介绍一个完完整整的,与我们实际使用一摸一样的多层感知机的反向传播方式
多层感知机MLP & 反向传播
与多输出感知机相比
有多层节点(绿色的)
δkk是由Okk和tk得到的
所以δ一共有k个
我们此时求的是E对Wjk的导
这是输出节点前的最后一层,前面还有好多层
也就是我们需要求E对Wij的导数
其实δk定义为从当前节点开始,即从k层的节点开始,到最终的输出层的梯度的导数的信息,只要拿到了这个信息,再乘上O节点的输出,就可以得到E对Wjk的导数
假如本层是k层,上一层是j层,知道了δk就可以求δj
即
比如我们要求E对Wij的导
只需要知道这一层j层的的δj和上一层的一个节点i的输出Oi
而δj是怎么求出来的呢
当前节点j的输出和下一层k的δk
所以,通过这样的顺序,可以从后往前不停地迭代,就可以计算出前面所有层的偏微分的信息,得到之后就可以使用链式法则的梯度更新方式来更新权值W
优化问题实战
2D函数优化实例
himmelblau function 专门用来检测优化器的效果
有四个局部最小值,这四个也都是全局最小解,值都是0
import numpy as np from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import torch 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,y range: ', x.shape, y.shape) X, Y = np.meshgrid(x,y) print('X,Y maps:',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 numpy as np from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import torch 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,y range: ', x.shape, y.shape) X, Y = np.meshgrid(x,y) print('X,Y maps:',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() #Gradient Descent x = torch.tensor([0.,0.], requires_grad=True) #初始化值设定为0,0 optimizer = torch.optim.Adam([x], lr=1e-3) #优化器,目标是x,学习率是1e-3 for step in range(20000): pred=himmelblau(x) optimizer.zero_grad() #梯度清零 pred.backward() #会生成x和y的梯度信息 optimizer.step() #会将x和y更新为x'和y' if step % 2000 == 0: print('step {}: x = {}, f(x) = {}'.format(step, x.tolist(), pred.item()))
我们优化的不是loss(error)了,而是函数的预测值
optimizer = torch.optim.Adam([x], lr=1e-3)
这句话是 优化器,目标是x,学习率是1e-3
意思就是只要你得到梯度以后,这句话就可以自动完成这个操作
只要你下面调用一次optimizer.step() 它就会更新一次这个过程
正确找到一个点
下面我们改变一下初始化
import numpy as np from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import torch 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,y range: ', x.shape, y.shape) X, Y = np.meshgrid(x,y) print('X,Y maps:',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() #Gradient Descent x = torch.tensor([4.,0.], requires_grad=True) #初始化值设定为0,0 optimizer = torch.optim.Adam([x], lr=1e-3) #优化器,目标是x,学习率是1e-3 for step in range(20000): pred=himmelblau(x) optimizer.zero_grad() #梯度清零 pred.backward() #会生成x和y的梯度信息 optimizer.step() #会将x和y更新为x'和y' if step % 2000 == 0: print('step {}: x = {}, f(x) = {}'.format(step, x.tolist(), pred.item()))
找到了另一个局部最小值
再改一下
import numpy as np from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import torch 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,y range: ', x.shape, y.shape) X, Y = np.meshgrid(x,y) print('X,Y maps:',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() #Gradient Descent x = torch.tensor([-4.,0.], requires_grad=True) #初始化值设定为0,0 optimizer = torch.optim.Adam([x], lr=1e-3) #优化器,目标是x,学习率是1e-3 for step in range(20000): pred=himmelblau(x) optimizer.zero_grad() #梯度清零 pred.backward() #会生成x和y的梯度信息 optimizer.step() #会将x和y更新为x'和y' if step % 2000 == 0: print('step {}: x = {}, f(x) = {}'.format(step, x.tolist(), pred.item()))
又换了一个点