【学习参考链接】,讲得非常棒,视频更详细。
# 只需 shift+回车 运行本单元格,就可以让jupyter notebook宽屏显示
from IPython.core.display import display, HTML
display(HTML('<style>.container { width:100% !important; }</style>'))
1 神经网络结构介绍
x 1 , x 2 , x 3 , x 4 x_1,x_2,x_3,x_4 x1,x2,x3,x4为输入层所输入的参数, h i j h_{ij} hij为隐藏层中所对应的输入,其维度与每一层的个数有关。如这里 x ∈ R 4 x\in \mathbb{R}^4 x∈R4, h i d d e n 1 ∈ R 5 hidden_1 \in \mathbb{R}^5 hidden1∈R5,由 h i d d e n 1 = ω X hidden_1 = \omega X hidden1=ωX可以确定其维度为: ω 5 × 4 \omega_{5 \times 4} ω5×4。由此规律可得 I n p u t Input Input与 h i d d e n 1 hidden_1 hidden1之间的权重是 ω 5 × 4 \omega_{5 \times 4} ω5×4, h i d d e n 1 hidden_1 hidden1与 h i d d e n 2 hidden_2 hidden2之间的权重是 ω 4 × 5 \omega_{4 \times 5} ω4×5, h i d d e n 2 hidden_2 hidden2与 O u t p u t Output Output之间的权重为 ω 3 × 4 \omega_{3 \times 4} ω3×4。即每相邻两层之间的权重维度为后一层的个数作为行数与前一层的个数作为列数。
2 计算图的构建
假设有一个两层的神经网络的表达式为: y ^ = W 2 ( W 1 ⋅ X + b 1 ) + b 2 \hat y = W_2(W_1\cdot X + b_1) + b_2 y^=W2(W1⋅X+b1)+b2
将 W 1 ⋅ X + b 1 W_1\cdot X + b_1 W1⋅X+b1看作为第一层,整体但作为第二层。其中 b 1 b_1 b1、 b 2 b_2 b2为偏置,为非线性函数根据输入所计算出的值,假设非线性函数为: s i g m o i d = 1 1 + e − x sigmoid = \frac1{1 + e^{-x}} sigmoid=1+e−x1,则 x 1 x_1 x1所对应的偏置为 1 1 + e − x 1 \frac1{1 + e^{-x_1}} 1+e−x11,通过计算每一个输入所对应的值构成列向量相加即可。第二层同理,其计算图如下所示:
加入非线性函数的原因就是防止能够将原表达式展开,导致增加的权重的没有意义,因此需要加入非线性函数使其不能够展开进行线性运算。
3 前馈和反馈的计算练习
4 Pytorch实现前馈和反馈
Tensor:用来存储数据的,前面的图中的所有数据,存的数据包括标量、矩阵、向量和高阶数据。
Data:储存权重值 w w w;grad:损失函数对权重的导数。
y ^ = x w \hat y = x w y^=xw
# 导入torch库
import torch
# GPU与CPU的切换:如果你系统中没有可用的cuda,直接切换到cpu来运行
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 查看GPU的名称
torch.cuda.get_device_name()
'GeForce RTX 2060'
# 构建数据集
x_data = [1,2,3]
y_data = [2,4,6]
# 选择初始权重,这里权重为一个数,也要用数组表示
w = torch.Tensor([1]) # 相当于Numpy数组
# 设置需要计算随机梯度
w.requires_grad = True
def forward(x):
return x * w
# w 是一个Tensor ,通过*自动将x转为Tensor,从而构成一个计算图
def loss(x,y):
y_pred = forward(x)
return (y_pred - y) ** 2
# 调动loss即构建出了计算图,看见代码见计算图
计算梯度用张量,更新梯度用标量。
print("predic(before training)",4,forward(4).item())
for epoch in range(100):
for x,y in zip(x_data,y_data):
# 前馈过程只需要计算loss,l表示算出的张量Tensor
l = loss(x,y)
# 通过调用backward,获取计算图中的所有梯度变量
l.backward() # 只要一做backward,计算图就释放了
# 这也是Pytorch的一个特点,w.grad.item()表示将其变为Python的标量,防止产生张量
print("\tgrad:",x,y,w.grad.item())
# 获取所需要的梯度,w.data表示取的标量,避免计算张量
w.data = w.data - 0.01 * w.grad.data
# 权重中的梯度数据清零,若不清零则为梯度的累加
w.grad.data.zero_()
print("pregress:",epoch,l.item())
print("predic(after training)",4,forward(4).item())
结果太多,这里删掉了一些。
predic(before training) 4 4.0
pregress: 95 9.094947017729282e-13
grad: 1 2 -7.152557373046875e-07
grad: 2 4 -2.86102294921875e-06
grad: 3 6 -5.7220458984375e-06
pregress: 96 9.094947017729282e-13
grad: 1 2 -7.152557373046875e-07
grad: 2 4 -2.86102294921875e-06
grad: 3 6 -5.7220458984375e-06
pregress: 97 9.094947017729282e-13
grad: 1 2 -7.152557373046875e-07
grad: 2 4 -2.86102294921875e-06
grad: 3 6 -5.7220458984375e-06
pregress: 98 9.094947017729282e-13
grad: 1 2 -7.152557373046875e-07
grad: 2 4 -2.86102294921875e-06
grad: 3 6 -5.7220458984375e-06
pregress: 99 9.094947017729282e-13
predic(after training) 4 7.999998569488525
视频课后练习:
# 导入torch库
import torch
# GPU与CPU的切换:如果你系统中没有可用的cuda,直接切换到cpu来运行
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 查看GPU的名称
print(torch.cuda.get_device_name())
# 构建数据集
x_data = [1,2,3]
y_data = [2,4,6]
# 选择初始权重,这里权重为一个数,也要用数组表示
w1 = torch.Tensor([1])
w2 = torch.Tensor([1])
b = torch.Tensor([1])
# 设置需要计算随机梯度
w1.requires_grad = True
w2.requires_grad = True
b.requires_grad = True
GeForce RTX 2060
# 构建计算图
def forward(x):
return w1 * x * x + w2 * x + b
def loss(x,y):
y_pred = forward(x)
return (y_pred - y) ** 2
print("predic(before training)",4,forward(4).item())
for epoch in range(100):
for x,y in zip(x_data,y_data):
# 前馈过程只需要计算loss,l表示算出的张量Tensor
l = loss(x,y)
# 通过调用backward,获取计算图中的所有梯度变量
l.backward() # 只要一做backward,计算图就释放了
# 这也是Pytorch的一个特点,w.grad.item()表示将其变为Python的标量,防止产生张量
print(f"x:{x}\ty:{y}\tw1:{w1.grad.item()}\tw2:{w2.grad.item()}\tb:{b.grad.item()}")
# 获取所需要的梯度,w.data表示取的标量,避免计算张量
w1.data = w1.data - 0.01 * w1.grad.data
w2.data = w2.data - 0.01 * w2.grad.data
b.data = b.data - 0.01 * b.grad.data
# 权重中的梯度数据清零,若不清零则为梯度的累加
w1.grad.data.zero_()
w2.grad.data.zero_()
b.grad.data.zero_()
print("pregress:",epoch,l.item())
print("predic(after training)",4,forward(4).item())
结果太多,这里删除一些。
predic(before training) 4 21.0
x:1 y:2 w1:2.0 w2:2.0 b:2.0
x:2 y:4 w1:22.880001068115234 w2:11.440000534057617 b:5.720000267028809
x:3 y:6 w1:77.04720306396484 w2:25.682401657104492 b:8.560800552368164
pregress: 0 18.321826934814453
x:1 y:2 w1:0.31661510467529297 w2:0.31661510467529297 b:0.31661510467529297
x:2 y:4 w1:-1.7297420501708984 w2:-0.8648710250854492 b:-0.4324355125427246
x:3 y:6 w1:1.4307546615600586 w2:0.47691822052001953 b:0.15897274017333984
pregress: 99 0.00631808303296566
predic(after training) 4 8.544172286987305