本文目录
一、网络模型构建
二、手动实现梯度下降
三、自动梯度
四、使用torch.nn包进行模型搭建
五、使用torch.optim实现梯度下降过程中的参数优化
一、网络模型构建
本文将使用pytorch构建一个简易的神经网络,并使用梯度下降算法对模型进行监督学习训练。
本简易神经网络有一个数据输入层、一个隐藏层和一个数据输出层构成。其中,每个输入数据包含十个特征,可以认为输入数据是一个十维向量;经隐藏层处理后保留十五个特征,最后经数据输出层输出十个特征值。
无需考虑该神经网络的实际意义,本文仅对搭建神经网络作一示例。
二、手动实现梯度下降
可以说是最朴素的方法了,手动实现梯度下降。batch_n是在一个批次中输入数据的数量,hidden_layer定义经过隐藏层后保留的数据特征的个数,input_data是每个数据包含的特征个数,output_data是输出数据所包含的特征个数。
#7~8(#是行号)使用torch中的randn函数随机生成训练集<x, y>,虽然这并不是一个十分恰当的方法,但用来做测试是足够的。
#10~11使用randn函数初始化权值w1、w2。
epoch_n指定训练次数,learning_rate定义学习率。接下来我们用一个循环对神经网络做20次训练。我们使用torch中自带的矩阵乘法方式实现神经网络中的前向传播,然后通过链式求导法则分别求偏导数手动实现后向传播。
注意,#18对Tensor作clamp操作相当于一个ReLU激活函数。
import torch
batch_n = 100
hidden_layer = 15
input_data = 10
output_data = 5
x = torch.randn(batch_n, input_data)
y = torch.randn(batch_n, output_data)
w1 = torch.randn(input_data, hidden_layer)
w2 = torch.randn(hidden_layer, output_data)
epoch_n = 20
learning_rate = 1e-6
for epoch in range(epoch_n):
h1 = x.mm(w1)
h1 = h1.clamp(min = 0)
y_pred = h1.mm(w2)
loss = (y_pred-y).pow(2).sum()
print("Epoch:{}, Loss:{:.4f}".format(epoch, loss))
grad_y_pred = 2*(y_pred-y)
grad_w2 = h1.t().mm(grad_y_pred)
grad_h = grad_y_pred.clone()
grad_h = grad_h.mm(w2.t())
grad_h.clamp(min = 0)
grad_w1 = x.t().mm(grad_h)
w1 -= learning_rate*grad_w1
w2 -= learning_rate*grad_w2
以下是运行效果。
Epoch:0, Loss:1.0563
Epoch:1, Loss:1.0350
Epoch:2, Loss:1.0142
Epoch:3, Loss:0.9938
Epoch:4, Loss:0.9740
Epoch:5, Loss:0.9547
Epoch:6, Loss:0.9358
Epoch:7, Loss:0.9174
Epoch:8, Loss:0.8995
Epoch:9, Loss:0.8820
Epoch:10, Loss:0.8649
Epoch:11, Loss:0.8482
Epoch:12, Loss:0.8318
Epoch:13, Loss:0.8158
Epoch:14, Loss:0.8003
Epoch:15, Loss:0.7850
Epoch:16, Loss:0.7700
Epoch:17, Loss:0.7553
Epoch:18, Loss:0.7409
Epoch:19, Loss:0.7269
Process finished with exit code 0
三、自动梯度
手动计算偏导数有时是非常费解的(尤其是神经网络非常复杂的情况下)。torch.autograd包所提供的函数可以实现神经网络后向传播中的链式求导,我们可以调用这些函数使问题大大简化。
在手动计算梯度代码的基础上,我们只需要把我们之前定义的Tensor封装到Variable类当中。封装之后,计算图中的每一个节点就是一个Variable对象。如果用X表示我们选中的对象,X.data代表Tensor数据类型的变量,X.grad代表对象的梯度,通过X.grad.data可以访问梯度的值。
改进后的代码如下。
#8-12中requires_grad参数的值决定了这个Variable对象是否需要计算梯度信息。#18进行前向传播,pytorch自动构建计算图,#19计算损失,#22调用loss.backward()函数进行后向传播,pytorch即可按照构建好的计算图自动计算梯度,#24-25更新权值(即让神经网络进行“学习”,#27-28将梯度置0。
import torch
from torch.autograd import Variable
batch_n = 100
hidden_layer = 15
input_data = 10
output_data = 5
x = Variable(torch.randn(batch_n, input_data), requires_grad = False)
y = Variable(torch.randn(batch_n, output_data), requires_grad = False)
w1 = Variable(torch.randn(input_data, hidden_layer), requires_grad = True)
w2 = Variable(torch.randn(hidden_layer, output_data), requires_grad = True)
epoch_n = 20
learning_rate = 1e-6
for epoch in range(epoch_n):
y_pred = x.mm(w1).clamp(min = 0).mm(w2)
loss = (y_pred-y).pow(2).sum()
print("Epoch:{}, Loss:{:.4f}".format(epoch, loss))
loss.backward()
w1.data -= learning_rate * w1.grad.data
w2.data -= learning_rate * w2.grad.data
w1.grad.data.zero_()
w2.grad.data.zero_()
以下是运行结果。
Epoch:0, Loss:1.0718
Epoch:1, Loss:1.0501
Epoch:2, Loss:1.0289
Epoch:3, Loss:1.0082
Epoch:4, Loss:0.9880
Epoch:5, Loss:0.9683
Epoch:6, Loss:0.9490
Epoch:7, Loss:0.9302
Epoch:8, Loss:0.9118
Epoch:9, Loss:0.8938
Epoch:10, Loss:0.8763
Epoch:11, Loss:0.8591
Epoch:12, Loss:0.8423
Epoch:13, Loss:0.8260
Epoch:14, Loss:0.8099
Epoch:15, Loss:0.7943
Epoch:16, Loss:0.7790
Epoch:17, Loss:0.7641
Epoch:18, Loss:0.7495
Epoch:19, Loss:0.7352
Process finished with exit code 0
四、使用torch.nn包进行模型搭建
torch.nn.Sequential类是torch.nn中的一种序列容器,通过在容器中嵌套各种实现神经网络中具体功能的类,来完成对神经网络模型的搭建。
torch.nn.Sequential括号内的内容就是我们搭建的神经网络的具体结构,这里先通过torch.nn.Linear(input_data, hidden_layer)完成输入层到隐藏层的线性变换,然后经过激活函数及torch.nn.Linear(hidden_layer, output_data)完成隐藏层到输出层的线性变换。这于上文中手动构建的神经网络模型是相一致的。
models = torch.nn.Sequential(
torch.nn.Linear(input_data, hidden_layer),
torch.nn.ReLU(),
torch.nn.Linear(hidden_layer, output_data)
)
五、使用torch.optim实现梯度下降过程中的参数优化
使用torch.nn.MSELoss()均方误差函数来计算损失,使用torch.optim.Adam类作为我们的模型参数优化函数。
import torch
from torch.autograd import Variable
batch_n = 100
hidden_layer = 15
input_data = 10
output_data = 5
x = Variable(torch.randn(batch_n, input_data), requires_grad=False)
y = Variable(torch.randn(batch_n, output_data), requires_grad=False)
models = torch.nn.Sequential(
torch.nn.Linear(input_data, hidden_layer),
torch.nn.ReLU(),
torch.nn.Linear(hidden_layer, output_data)
)
epoch_n = 20
learning_rate = 1e-6
loss_f = torch.nn.MSELoss()
optimzer = torch.optim.Adam(models.parameters(), lr = learning_rate)
for epoch in range(epoch_n):
y_pred = models(x)
loss = loss_f(y_pred, y)
print("Epoch:{}, Loss:{:.4f}".format(epoch, loss))
optimzer.zero_grad()
loss.backward()
optimzer.step()
以下是运行效果
Epoch:0, Loss:0.9989
Epoch:1, Loss:0.9781
Epoch:2, Loss:0.9578
Epoch:3, Loss:0.9380
Epoch:4, Loss:0.9187
Epoch:5, Loss:0.8999
Epoch:6, Loss:0.8816
Epoch:7, Loss:0.8637
Epoch:8, Loss:0.8462
Epoch:9, Loss:0.8291
Epoch:10, Loss:0.8126
Epoch:11, Loss:0.7965
Epoch:12, Loss:0.7808
Epoch:13, Loss:0.7655
Epoch:14, Loss:0.7505
Epoch:15, Loss:0.7359
Epoch:16, Loss:0.7217
Epoch:17, Loss:0.7077
Epoch:18, Loss:0.6942
Epoch:19, Loss:0.6808
Process finished with exit code 0