第一步:安装Pytorch
我的电脑在pip
上下载Pytorch报错,因此使用Anaconda下载,在此不再赘述。
第二步:准备数据、构建框架
首先我们约定尝试搭建两层神经网络,数据量$N=64$,输入层、隐藏层、输出层的维度分别是:$d_{in}=1000,H=100,d_{out}=10$
因为是一个Demo
,所以尝试直接使用一些随机数据进行测试:
# 64个数据,1000-dim,,中间层100-dim,输出10-dim
x = torch.randn(N, d_in, requires_grad=True)
y = torch.randn(N, d_out, requires_grad=True)
注意在这个时候需要写出requires_grad=True
,方便后续使用.grad
属性直接计算梯度。
接下来就是要定义一个模型,这个模型我们是打算使用两层的神经网络,首先在官方的Doc中是这样定义torch.nn.module
的用法的:
Base class for all neural network modules.
Your models should also subclass this class.
查阅Doc,主要有几个点是应该会用到的:
-
torch.nn.module
应当是所有神经网络的基类,并且你要是写一个神经网络也应该继承自此。 - 如果要应用这个模型到GPU,还可以使用
.cuda()
函数。
对于其中的__init__
,就是勾勒出整个模型的框架,我们首先需要把基类的构造函数进行调用,然后把本模型所需要的函数进行定义,因为我们想要做两层的神经网络,所以分别声明两层线性的函数。
对于其中的forward
,是所有的子类都必须得进行override,代表了前向传播的逐层映射关系。激活函数我们约定使用ReLu
也就是.clamp(min=0)
。
所以我们可以写成下面这个样子:
class TwoLayerNet(torch.nn.module):
# 定义了init和forward就想到于给定了一个模型
# init相当于给了这个框架
def __init__(self, d_in, H, d_out):
super(TwoLayerNet, self).__init__()
self.linear1 = nn.Linear(d_in, H)
self.linear2 = nn.Linear(H, d_out)
# forward是定义前向传播的部分
def forward(self, x):
y_pred = self.linear2(self.linear1(x).clamp(min=0))
return y_pred
第三步:应用模型
前向传播部分
主要是两个方面,第一是把上面定义好的框架应用起来,第二是给定学习率和loss function。
# 初始化model
model = TwoLayerNet(d_in, H, d_out)
# 规定loss function
loss_fn = nn.MSELoss(reduction='sum')
# 规定学习率
learning_rate = 1e-4
反向传播部分
首先,我们约定反向传播的模型是SGD
这种比较简单的模型:
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
loss_list = [] # 为了画图
训练迭代部分
至此,我们需要对模型有个直观认识,对于整个模型而言应当是$x \rightarrow H \rightarrow y_{pred}$为正向传播路径,而损失函数为:
$$loss = \sqrt{y_{pred}^2-y^2} $$
对于各层神经网络,我借用网图来说明一下关系:
第一层神经网络是输入部分:所以权重矩阵$w_1$应该是$d_{in}\times H$的维度,同理,第二层神经网络输出权重矩阵应当是$H \times d_{out}$维度。
我们接下来就是进行训练迭代,我们先约定训练$200$代,观察收敛效果。
for it in range(200):
# forward pass
y_pred = model(x)
# compute loss
loss = loss_fn(y_pred.float(), y.float())
print(it, loss.item())
loss_list.append(loss)
# backward pass
optimizer.zero_grad()
loss.backward()
optimizer.step()
forward pass
部分是把自变量$x$输入到模型中,而compute loss
是计算损失数值,我们需要注意以下这些点:
-
loss_fn(y_pred.float(), y.float())
不可以写成loss_fn(y_pred- y)
,否则会缺失一个target
参数 -
loss.item()
是用来输出loss
这个tensor
包含的数值
而backward pass
中,我们需要注意,每次求梯度下降之前,都需要把之前的梯度清零,原因就是这个梯度如果不被清零,在每次迭代的时候都会被自动叠加,这样自动叠加的好处是对于一些连锁的梯度运算的时候更为方便了,但是对于每一代而言都需要手动进行清零。
第四步:结尾和简单分析
最后我们只要简单的把迭代的图画出来即可,效果大概是这样的:
plt.plot(range(200), loss_list)
plt.show()
大概在$10$代附近就已经收敛了,效果还是蛮不错的。可以通过第一次运行的结果调整迭代次数。
下面附上整个代码:
import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
N, d_in, H, d_out = 64, 1000, 100, 10
# 随机创建一些训练数据
x = torch.randn(N, d_in, requires_grad=True)
y = torch.randn(N, d_out, requires_grad=True)
class TwoLayerNet(torch.nn.module):
# 定义了init和forward就想到于给定了一个模型
# init相当于给了这个框架
def __init__(self, D_in, H, D_out):
super(TwoLayerNet, self).__init__()
self.linear1 = nn.Linear(D_in, H)
self.linear2 = nn.Linear(H, D_out)
# forward是
def forward(self, x):
y_pred = self.linear2(self.linear1(x).clamp(min=0))
return y_pred
# 初始化model
model = TwoLayerNet(d_in, H, d_out)
# 规定loss function
loss_fn = nn.MSELoss(reduction='sum')
# 规定学习率
learning_rate = 1e-4
# 定义optimizer做优化
optimizer = torch.optim.Adam(model.parameters(), lr=learning_rate)
loss_list = []
for it in range(200):
# forward pass
y_pred = model(x)
# compute loss
loss = loss_fn(y_pred.float(), y.float())
print(it, loss.item())
loss_list.append(loss)
# backward pass
optimizer.zero_grad()
loss.backward()
optimizer.step()
plt.plot(range(200), loss_list)
plt.show()
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。