之前听过前向传播和反向传播,也看到项目中有这两个步骤,但是不知道具体是干什么的,在看PyTorch官方教程的时候看到一个用numpy实现的2层神经网络,里面实现了前向传播和反向传播,这里记录一下,同时也记录一下我的PyTorch学习过程。
Numpy
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| import numpy as np
N, D_in, H, D_out = 64, 1000, 100, 10
x = np.random.randn(N, D_in) y = np.random.randn(N, D_out)
w1 = np.random.randn(D_in, H) w2 = np.random.randn(H, D_out)
learning_rate = 1e-6 for t in range(500): h = x.dot(w1) h_relu = np.maximum(h, 0) y_pred = h_relu.dot(w2)
loss = np.square(y_pred - y).sum() print t, loss
grad_y_pred = 2.0 * (y_pred - y) grad_w2 = h_relu.T.dot(grad_y_pred) grad_h_relu = grad_y_pred.dot(w2.T) grad_h = grad_h_relu.copy() grad_h[h < 0] = 0 grad_w1 = x.T.dot(grad_h) w1 -= learning_rate * grad_w1 w2 -= learning_rate * grad_w2
|
PyTorch:Tensor
numpy是一个很棒的框架,但是它没有利用GPU加速计算,对现代的深度神经网络,GPU经常提供50倍以上的加速,所以numpy不适用于现在的深度学习。
PyTorch里一个基本的概念是Tensor
,他和numpy里的array概念上是一样的,一个Tensor就是一个n维数组,PyTorch提供了大量的函数来操作这些Tensor。和numpy不同的是,PyTorch的Tensor可以利用GPU来加速计算。
下面用PyTorch的Tensor来实现刚才的2层神经网络:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import torch
dtype = torch.float device = torch.device("cpu")
N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in, device=device, dtype=dtype) y = torch.randn(N, D_out, device=device, dtype=dtype)
w1 = torch.randn(D_in, H, device=device, dtype=dtype) w2 = torch.randn(H, D_out, device=device, dtype=dtype)
learning_rate = 1e-6 for t in range(500): h = x.mm(w1) h_relu = h.clamp(min=0) y_pred = h_relu.mm(w2)
loss = (y_pred - y).pow(2).sum().item() print t, loss
grad_y_pred = 2.0 * (y_pred - y) grad_w2 = h_relu.t().mm(grad_y_pred) grad_h_relu = grad_y_pred.mm(w2.t()) grad_h = grad_h_relu.clone() grad_h[h < 0] = 0 grad_w1 = x.t().mm(grad_h) w1 -= learning_rate * grad_w1 w2 -= learning_rate * grad_w2
|
PyTorch:autograd
上面我们手动实现了反向传播,对于2层的神经网络还可以,但是对更大的复杂网络就会非常困难,PyTorch提供了自动微分(automatic differentiation)
功能来自动计算神经网络中的反向传播。这个功能在PyTorch的autograd包中,当使用autograd,网络的前向传播会定义一个计算图,图中的节点为Tensor,边是从输入Tensor产生输出Tensor的函数。这个图中的反向传播可以让我们很容易地计算梯度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import torch
dtype = torch.float
N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in, dtype=dtype) y = torch.randn(N, D_out, dtype=dtype)
w1 = torch.randn(D_in, H, dtype=dtype, requires_grad=True) w2 = torch.randn(H, D_out, dtype=dtype, requires_grad=True)
learning_rate = 1e-6 for t in range(500): y_pred = x.mm(w1).clamp(min=0).mm(w2) loss = (y_pred - y).pow(2).sum() print t, loss.item() loss.backward() with torch.no_grad(): w1 -= learning_rate * w1.grad w2 -= learning_rate * w2.grad w1.grad.zero_() w2.grad.zero_()
|
PyTorch:forward backward
PyTorch中我们可以定义自己的autograd操作符,定义torch.autograd.Function
的子类,并实现forward
和backward
函数,虽然还不是很明白,但是也先记录一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| import torch
class MyReLU(torch.autograd.Function): """ 通过继承Function来实现自定义autograd函数 """ @staticmethod def forward(ctx, input): """ 在前向传播中,我们接收一个Tensor包含输入,返回一个Tensor包含输出。ctx是一个上下文对象, 可以用来存放反向计算的信息,可以用ctx.save_for_backward方法缓存任意在反向传播中用到的对象 """ ctx.save_for_backward(input) return input.clamp(min=0)
@staticmethod def backward(ctx, grad_output): """ 反向传播中,我们接收一个Tensor包含loss对于输出的梯度,需要计算loss对于输入的梯度 """ input, = ctx.saved_tensors grad_input = grad_output.clone() grad_input[input < 0] = 0 return grad_input
dtype = torch.float
N, D_in, H, D_out = 64, 1000, 100, 10
x = torch.randn(N, D_in, dtype=dtype) y = torch.randn(N, D_out, dtype=dtype)
w1 = torch.randn(D_in, H, dtype=dtype, requires_grad=True) w2 = torch.randn(H, D_out, dtype=dtype, requires_grad=True)
learning_rate = 1e-6 for t in range(500): relu = MyReLU.apply
y_pred = relu(x.mm(w1)).mm(w2) loss = (y_pred - y).pow(2).sum() loss.backward() with torch.no_grad(): w1 -= learning_rate * w1.grad w2 -= learning_rate * w2.grad w1.grad.zero_() w2.grad.zero_()
|