AI初学笔记 3
一、神经网络
在上一节进行了梯度下降的代码实现,以自动求导的方式进行梯度下降,取得简单曲线的最优解,但是这里存在一个问题,就是梯度函数是人工计算导数后写入的,如果是简单模型,确实可以人工计算,但模型复杂则无法进行人工计算,比如下图所示的一般情况下的神经网络。
X为输入层,H为输入和输出中间的隐藏层,H与X间的每一条连线都是一个权重,每层H的个数往往都与输入X的个数不同,所以之间要进行矩阵的乘法,以图中矩阵乘法为例,如果要得到 [6 * 1] 大小的H矩阵,那么 W 的行数必须为 6 ,如果输入X的行数为 5, 那么 W 的列数必须为5。
简而言之,相乘矩阵的前项列与后项行必须相等,结果项行数等于前项行数,结果项列数等于后项列数。更多矩阵运算规则详见线性代数同济版。
可见但从H层到X层就有30次运算,H(7单元)到H(6单元)有42次运算,这么多的计算量明显不能用人工进行完成,所以需要找到一种方式,让神经网络自动计算梯度并保存。
二、激活函数
前向传播的函数在前面定义过,计算公式为: y_pred = w * x + b (b为偏置) ,如上图所示,在一般的神经网络中,需要经历多层的前向传播,假设有两层网络,则:y_pred = w2(w1 * x1 + b1)+b2,经过化简,y_pred = (w1w2) * x1 + (b1w2 + b2),括号里部分可以进一步看成:y_pred = w3 * x +b3。
此时多层神经网络将没有意义,因为无论多少层网络最终都将化简成一层,所以在每层网络后,需要再添加激活函数,至于sigmod、relu等激活函数的用法原理,日后再进行讨论学习。
三、反向传播
反向传播过程如图下半部分红色箭头流程所示,需要注意的是反向传播中的链式法则,在第二节中有提到。
四、代码实现
1.数据
import numpy as np
import torch
x_data = [1, 2, 3]
y_data = [2, 4, 6]
w = torch.tensor([1.0])
w.requires_grad_(True)
这里w的定义要定义为张量tensor的形式,因为后续要保存梯度,并进行张量计算。w.requires_grad_(True)打开自动计算梯度的开关,因为反向传播需要自动计算保存梯度,一般反向传播计算都需要这一步。
2.定义函数
def foward(x):
return w * x
def loss(x, y):
y_pred = foward(x)
return (y_pred - y) ** 2
前向传播函数与损失函数没有变化,因为要进行反向传播自动求导,所以省去了求梯度函数。
2.主要流程
for epoch in range(100):
for x, y in zip(x_data, y_data):
l = loss(x, y)
l.backward()
w.data = w.data - 0.01 * w.grad.data
w.grad.data.zero_()
print("epoch = ", epoch, "loss = ", l, "w = ", w.data)
使用 l.backward() 可以对l进行自动求导,可以使用 .grad 直接调用梯度,如w.grad.data 使用data的原因是保存数据形式,如果不用则会释放张量导致错误(暂时这么理解的,日后有待更进一步学习)。
最后不要忘记使用w.grad.data.zero_()清零。(说是不清零会占用空间)
总结
反向传播非常重要,但是在pytorch里实现比较简单。
- 开始的时候打开梯度开关w.requires_grad_(True)
- 对损失 l 直接调用.backward()就可以实现梯度计算。
- 不要忘记最后 w.grad.data.zero_() 进行梯度清零。