1.单输出的单层感知机实例:
import torch
from torch.nn import functional as F
# 输入有10个特征
x = torch.rand(1,10)
w = torch.rand(1,10,requires_grad=True)
# 对输出用sigmoid函数激活处理
o = torch.sigmoid(x @ w.t())
print('输出值o为:',o)
print('输出值o的shape为:',o.shape)
# 计算损失mse
loss = F.mse_loss(torch.ones(1,1),o)
print('损失值为:',loss)
print('损失值的shape为:',loss.shape)
# 计算梯度,得到loss对w的导数
loss.backward()
print('loss对w的导数为:',w.grad)
结果:
输出值o为: tensor([[0.9597]], grad_fn=<SigmoidBackward>)
输出值o的shape为: torch.Size([1, 1])
损失值为: tensor(0.0016, grad_fn=<MeanBackward1>)
损失值的shape为: torch.Size([])
loss对w的导数为: tensor([[-0.0024, -0.0008, -0.0013, -0.0026, -0.0027, -0.0004, -0.0030, -0.0014,-0.0021, -0.0017]])
2.多输出的单层感知机实例:
import torch
from torch.nn import functional as F
# 输入有10个特征
x = torch.rand(1,10)
# 因为要实现多输出,因此w的维度不再是1,本次为2个输出
w = torch.rand(2,10,requires_grad=True)
# 对输出用sigmoid函数激活处理
o = torch.sigmoid(x @ w.t())
print('输出值o为:',o)
print('输出值o的shape为:',o.shape)
# 计算损失mse
loss = F.mse_loss(torch.ones(1,2),o)
print('损失值为:',loss)
print('损失值的shape为:',loss.shape)
# 计算梯度,得到loss对w的导数
loss.backward()
print('loss对w的导数为:',w.grad)
结果:
输出值o为: tensor([[0.9151, 0.8192]], grad_fn=<SigmoidBackward>)
输出值o的shape为: torch.Size([1, 2])
损失值为: tensor(0.0200, grad_fn=<MeanBackward1>)
损失值的shape为: torch.Size([])
loss对w的导数为: tensor([[-0.0054, -0.0041, -0.0005, -0.0012, -0.0039, -0.0020, -0.0027, -0.0003,
-0.0019, -0.0019],
[-0.0219, -0.0167, -0.0019, -0.0049, -0.0159, -0.0079, -0.0109, -0.0012,
-0.0077, -0.0076]])
3.链式法则实例:
链式法则是求复合函数的导数(偏导数)的法则,可以表示为如下式子:
其中的x相当于输入,y相当于输出,u相当于神经网络中的hidden layer
。
使用链式法则会让神经网络的求解变得非常简洁和清晰。
import torch
x = torch.tensor(1.1)
w1 = torch.tensor(2.2,requires_grad=True)
b1 = torch.tensor(1.1)
w2 = torch.tensor(2.2,requires_grad=True)
b2 = torch.tensor(1.1)
y1 = x*w1 + b1
y2 = y1*w2 + b2
# 使用链式法则求解
dy2_dw1 = torch.autograd.grad(y2,[w1],retain_graph=True)[0]
print(dy2_dw1)
# 使用逐步求解
dy2_dy1 = torch.autograd.grad(y2,[y1],retain_graph=True)[0]
dy1_dw1 = torch.autograd.grad(y1,[w1],retain_graph=True)[0]
print(dy2_dy1*dy1_dw1)
结果:
tensor(2.4200)
tensor(2.4200)
4.2D函数(Himmelblau函数)优化实例:
Himmelblau函数表达式如下所示:
该函数有四处极值点,为:
Himmelblau函数用于检测优化器效果。
- 首先将函数可视化出来。
import numpy as np
from matplotlib import pyplot as plt
# matplotlib版本在1.0.x以上时需导入下面工具,否则会报错ValueError: Unknown projection '3d'
from mpl_toolkits.mplot3d import Axes3D
def Himmelblau(x):
return (x[0]**2 + x[1] - 11)**2 + (x[0] + x[1]**2 -7)**2
x = np.arange(-6,6,0.1)
y = np.arange(-6,6,0.1)
print(x.shape,y.shape)
# meshgrid()方法从一个坐标向量中返回一个坐标矩阵
X,Y = np.meshgrid(x,y)
print(X.shape,Y.shape)
Z = Himmelblau([X,Y])
fig = plt.figure('Himmelblau')
ax = fig.gca(projection='3d')
ax.plot_surface(X,Y,Z)
ax.view_init(60,-30)
ax.set_xlabel('X')
ax.set_ylabel('Y')
plt.show()
得到如下图形:
- 使用随机梯度下降优化
import torch
def himmelblau(x):
return (x[0] ** 2 + x[1] - 11) ** 2 + (x[0] + x[1] ** 2 - 7) ** 2
# 初始化
x = torch.tensor([0., 0.], requires_grad=True)
# 优化目标是找到使himmelblau函数值最小的坐标x[0],x[1]
# 这里使用Adam优化器,优化目标为x,学习率为1e-3
optimizer = torch.optim.Adam([x], lr=1e-3)
for i in range(20000):
# 每次计算出当前的函数值
pred = himmelblau(x)
# 因为梯度是被积累的而不是被替换掉,每次需将梯度设置为0
optimizer.zero_grad()
# 求得梯度信息
pred.backward()
# 使用上一步得到的梯度更新参数
optimizer.step()
# 每2000次输出下
if i % 2000 == 0:
print("step={},x={},f(x)={}".format(i, x.tolist(), pred.item()))
结果如下:
step=0,x=[0.0009999999310821295, 0.0009999999310821295],f(x)=170.0
step=2000,x=[2.3331806659698486, 1.9540692567825317],f(x)=13.730920791625977
step=4000,x=[2.9820079803466797, 2.0270984172821045],f(x)=0.014858869835734367
step=6000,x=[2.999983549118042, 2.0000221729278564],f(x)=1.1074007488787174e-08
step=8000,x=[2.9999938011169434, 2.0000083446502686],f(x)=1.5572823031106964e-09
step=10000,x=[2.999997854232788, 2.000002861022949],f(x)=1.8189894035458565e-10
step=12000,x=[2.9999992847442627, 2.0000009536743164],f(x)=1.6370904631912708e-11
step=14000,x=[2.999999761581421, 2.000000238418579],f(x)=1.8189894035458565e-12
step=16000,x=[3.0, 2.0],f(x)=0.0
step=18000,x=[3.0, 2.0],f(x)=0.0
可以看出很容易找到局部最小值,改变初始化状态将会得到不同的结果。
说明初始化状态对机器学习其实是很重要的。