第一步:安装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} $$

对于各层神经网络,我借用网图来说明一下关系:

timg.jpg

第一层神经网络是输入部分:所以权重矩阵$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()

Figure_1.png
大概在$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()

参考资料

pytorch官方文档

最好的PyTorch的入门与实战教程(16小时实战)


喵喵狂吠
6 声望5 粉丝

假发消费者,计科小学生。