Pytorch搭建神经网络

作者:ZZY

1. 搭建一个简单的神经网络

1.1 导入Pytorch



import torch



1.2 初始化参数

首先我们明确这次搭建的背景:希望将若干个二位平面的点分为两类。

对于平面上的点,我们将其x轴,y轴作为输入数据的特征。对于将要被分为的两类作为输出的节点。对于隐层,将其特征数量设置为50,为了将低维数据映射高维,便于分类的实现。(这里只是我自己的想法,欢迎纠正)



data_nums = 100         #输入训练集个数
input_data = 2          #每个训练集的特征数量
output_data = 2         #输出的分类数量
hidden_layer = 50       #隐层的特征数量
epoch_max = 50          #最大迭代次数
learning_rate = 1e-6    #学习速度(dx)



1.3 生成数据

根据神经网络的模型,我们得以得到四个需要的数据,输入数据集x,输出标签y,由输入层到隐层的参数w1,由隐层到输出层参数w2。根据四个数据的矩阵维度,随机化生成。



x = torch.randn(data_nums, input_data)
y = torch.randn(data_nums, output_data)

w1 = torch.randn(input_data, hidden_layer)
w2 = torch.randn(hidden_layer, output_data)



1.4 通过梯度下降优化神经网络



for epoch in range(0, epoch_max):
    h1 = x.mm(w1)
    h1.clamp_(min=0) #模拟激活函数RuLu
    Y = h1.mm(w2)

    loss = (Y-y).pow(2).sum()
    print("Eopch:{}, Loss:{:.4f}".format(epoch+1, loss))

    '''
    h1 = x*w1,y = h1*w2,loss = f(y)
    d(loss)/d(w2) = d(loss)/d(y) * d(y)/d(w2)
    d(loss)/d(w1) = d(loss)/d(y) * d(y)/d(h1) * d(h1)/d(w1)

    d(loss)/d(y) = 2*(Y-y)
    d(y)/d(w2) = h1
    d(y)/d(h1) = w2
    d(h1)/d(w1) = x
    '''
    gray_Y = 2*(Y-y)
    gray_w2 = h1.t().mm(gray_Y)

    gray_h = gray_Y.clone()
    gray_h = gray_h.mm(w2.t())
    gray_h.clamp_(min=0)
    gray_w1 = x.t().mm(gray_h)

    w1 -= learning_rate*gray_w1
    w2 -= learning_rate*gray_w2



上述代码中,通过FOR循环进行迭代模拟。

通过矩阵相乘模拟了前向传播,得到了预测结果。其中通过torch.clamp对矩阵进行裁剪,来模拟RuLe函数,将线性变成非线性。更有利于分类问题的实现。

其中loss函数定义为最常见的平方差和函数。然后根据链式反应写出对参数w1,w2的求导方程完成反向传播对参数的优化。对w1,w2求导的方法在代码注释部分已经给出。

1.5 打印结果



Eopch:1, Loss:11480.7578
Eopch:2, Loss:11365.5840
Eopch:3, Loss:11251.7920
Eopch:4, Loss:11139.1611
Eopch:5, Loss:11027.8447
Eopch:6, Loss:10917.8174
Eopch:7, Loss:10809.0947
Eopch:8, Loss:10701.6602
Eopch:9, Loss:10595.4980
Eopch:10, Loss:10490.5674
...
Eopch:45, Loss:7501.0620
Eopch:46, Loss:7432.1606
Eopch:47, Loss:7363.9067
Eopch:48, Loss:7296.3892
Eopch:49, Loss:7229.5928
Eopch:50, Loss:7163.5239

Process finishe d with exit code 0



通过上述打印结果我们可以发现,loss函数在不断的减小。说明根据神经网络优化参数后的模型,得到的预测值越来越准确。

1.6 完整代码



import torch

data_nums = 100         #输入训练集个数
input_data = 2          #每个训练集的特征数量
output_data = 2         #输出的分类数量
hidden_layer = 50       #隐层的特征数量
epoch_max = 50          #最大迭代次数
learning_rate = 1e-6    #学习速度(dx)

x = torch.randn(data_nums, input_data)
y = torch.randn(data_nums, output_data)

w1 = torch.randn(input_data, hidden_layer)
w2 = torch.randn(hidden_layer, output_data)

for epoch in range(0, epoch_max):
    h1 = x.mm(w1)
    h1.clamp_(min=0) #模拟激活函数RuLu
    Y = h1.mm(w2)

    loss = (Y-y).pow(2).sum()
    print("Eopch:{}, Loss:{:.4f}".format(epoch+1, loss))

    '''
    h1 = x*w1,y = h1*w2,loss = f(y)
    d(loss)/d(w2) = d(loss)/d(y) * d(y)/d(w2)
    d(loss)/d(w1) = d(loss)/d(y) * d(y)/d(h1) * d(h1)/d(w1)

    d(loss)/d(y) = 2*(Y-y)
    d(y)/d(w2) = h1
    d(y)/d(h1) = w2
    d(h1)/d(w1) = x
    '''
    gray_Y = 2*(Y-y)
    gray_w2 = h1.t().mm(gray_Y)

    gray_h = gray_Y.clone()
    gray_h = gray_h.mm(w2.t())
    gray_h.clamp_(min=0)
    gray_w1 = x.t().mm(gray_h)

    w1 -= learning_rate*gray_w1
    w2 -= learning_rate*gray_w2



2. 利用pytorch中的包对代码进行“减负”

相信写完上面的代码后,对于链式反应的部分觉得十分的麻烦,不但需要自己在草稿纸上推演,在代码实现中也需要小心翼翼。特别是当隐层的数量增加之后,复杂的数学方程式会大大减慢搬砖的速度。

Pytorch中的torch.autograd提供了自动计算梯度的方法,可以让模型参数自动计算在优化过程中需要的梯度值。大大提升了搬砖速度,减少了“重复造轮子”的冗余过程。

2.1 torch.autograd.Variable

通过Pytorch建立神经网络,获取梯度信息主要为以下几个阶段:

  1. 构建一个计算图,用Variable将Tensor包装,形成计算图中的节点。
  2. 通过.backward()来自动计算出所有需要的梯度
  3. 来针对某个变量x执行x.grad获得想要的梯度。

2.1.1 Variable

autograd.Variable 是autograd中最核心的类。 它包装了一个Tensor,并且几乎支持所有在其上定义的操作。一旦完成了你的运算,你可以调用 .backward()来自动计算出所有的梯度。




pytorch pt模型 pytorch模型搭建_pytorch pt模型


在Pytorch的一个重要特点就是动态计算图(Dynamic Computational Graphs)。计算图中每一个节点代表一个Variable,Variable之间的运算可以修改。

Variable用来构建一个计算图中的节点。将Tensor包装转换成Variable后,该Tensor就成了计算图中的一个节点。对于该节点,如上图中所示,有两个特性:

  • .data:该节点的Tensor数据。
  • .grad:获得该节点的梯度信息。

而对于Variable两个参数“requires_grad”和“grad_fn“,与创建人有关。

  • requires_grad:表示该Variable是否需要计算梯度值,有True和Flase两个值。默认为False。
  • grad_fn:得知该变量是否是一个计算结果,比如人为创建的Variable x中grad_fn = None,但是y=x+2中y的grad_fn是存在的,指向这个该函数y=x+2的相关对象。

来源:https://zhuanlan.zhihu.com/p/29904755

2.2 构建神经网络

2.2.1 导入库

对比开始,多从导入Variable


import torch
from torch.autograd import Variable


2.2.2 生成Variable


x = Variable(torch.randn(data_nums, input_data), requires_grad=False)
y = Variable(torch.randn(data_nums, output_data))
#y中没有初始化requires_gread参数,因为其默认值为False,效果和Variable x一样。

w1 = Variable(torch.randn(input_data, hidden_layer), requires_grad=True)
w2 = Variable(torch.randn(hidden_layer, output_data), requires_grad=True)


2.2.3 优化梯度下降代码


for epoch in range(0, epoch_max):
    Y = x.mm(w1).clamp(min=0).mm(w2)

    loss = (Y-y).pow(2).sum()
    print("Eopch:{}, Loss:{:.4f}".format(epoch + 1, loss.data))

    loss.backward()
    w1.data -= learning_rate * w1.grad.data
    w2.data -= learning_rate * w2.grad.data

    w1.grad.data.zero_()
    w2.grad.data.zero_()