说明:
一个全连接ReLU神经网络,一个隐藏层,没有bias。用来从x预测y,使用L2 Loss。
这一实现完全使用numpy来计算前向神经网络,loss,和反向传播。
numpy ndarray是一个普通的n维array。它不知道任何关于深度学习或者梯度(gradient)的知识,也不知道计算图(computation graph),只是一种用来计算数学运算的数据结构。
import numpy
length, input_size, hidden_size, out_size = 64, 1000, 100, 10 # 定义数据的维度
x = numpy.random.randn(length, input_size) # 构造数据x
y = numpy.random.randn(length, out_size) # 构造标签y
w1 = numpy.random.randn(input_size, hidden_size) # 初始化权重参数w1
w2 = numpy.random.randn(hidden_size, out_size) # 初始化权重参数w2
learning_rate = 1e-6
for it in range(500):
# 前向传播
hidden = x.dot(w1)# N * H
hidden_relu = numpy.maximum(hidden, 0) # N * H
y_pred = hidden_relu.dot(w2)# N * D_out
# 计算loss
loss = numpy.square(y_pred - y).mean() # item()是将一个数的tensor转换为标量
print(it, loss)
# 反向传播
# 计算梯度,链式法则 ∂loss/∂w1 = (∂loss/∂y_pred) * (∂y_pred/∂a) * ∂a/∂h * ∂h/∂w1 = 2.0 * (y_pred - y) * W_2T * [0|1]* xT
grad_y_pred = 2.0 * (y_pred - y)
grad_w2 = hidden_relu.T.dot(grad_y_pred)
grad_hidden_relu = grad_y_pred.dot(w2.T)
grad_hidden = grad_hidden_relu.copy() # 为什么要复制呢,因为不复制就会改变原有的梯度
# 因为在对relu求导时,大于0的数为本身即*1,<0 的数梯度为0,即*0,结果为0,所以在这里我们将隐层的梯度值小于0的数据置0
grad_hidden[hidden<0] = 0
grad_w1 = x.T.dot(grad_hidden)
# 更新 w1 和 w2
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w2
print(w1)
print(w2)
import torch
length, input_size, hidden_size, out_size = 64, 1000, 100, 10
x = torch.randn(length, input_size)
y = torch.randn(length, D_out)
w1 = torch.randn(input_size, hidden_size)
w2 = torch.randn(hidden_size, out_size)
learning_rate = 1e-6
for it in range(500):
hidden = x.mm(w1)
hidden_relu = hidden.clamp(min=0)
y_pred = hidden_relu.mm(w2) # N * D_out
loss = (y_pred - y).pow(2).sum().item()
print(it, loss)
grad_y_pred = 2.0 * (y_pred - y)
grad_w2 = h_relu.t().mm(grad_y_pred)
grad_hidden_relu = grad_y_pred.mm(w2.t())
grad_hidden = grad_hidden_relu.clone()
grad_hidden[hidden<0] = 0
grad_w1 = x.t().mm(grad_hidden)
w1 -= learning_rate * grad_w1
w2 -= learning_rate * grad_w2
print(w1)
print(w2)
length, input_size, hidden_size, out_size = 64, 1000, 100, 10
# 随机创建一些训练数据
x = torch.randn(length, input_size)
y = torch.randn(length, out_size)
w1 = torch.randn(input_size, hidden_size, requires_grad=True)
w2 = torch.randn(hidden_size, out_size, requires_grad=True)
learning_rate = 1e-6
for it in range(500):
# 前向传播
y_pred = x.mm(w1).clamp(min=0).mm(w2)
# computation graph,注意这里把item()去掉了,因为使用了这个之后loss就是一个标量,不是图了,就不能使用.backward()
loss = (y_pred - y).pow(2).sum()
print(it, loss.item())
# 反向传播
loss.backward()
with torch.no_grad(): # 为了让计算图不占内存,并且忘记w1和w2的梯度,需要做这个操作
w1 -= learning_rate * w1.grad
w2 -= learning_rate * w2.grad
w1.grad.zero_() # 计算完参数的梯度之后,需要将这一步的梯度清零,不然会越来越大,他是叠加的
w2.grad.zero_()
print(w1)
print(w2)