目录
前言
一、参数的更新
1. SGD的缺点
2. Momentum
3. AdaGrad
4. Adam
5. 基于MNIST数据集地更新方法比较
二、权重的初始值
1. 权重初始值不能设为0
总结
前言
本节介绍权重参数的优化方法,即寻找最优权重参数的最优化方法。
一、参数的更新
1. SGD的缺点
如果函数的形状飞军向,比如呈延伸状,搜索的路径会非常低效。究其根本原因是梯度的方向并没有指向最小值的方向。
2. Momentum
W表示权重参数,yita表示学习率,v表示在梯度上的受力。
av这一项表示在物体不受任何力时,收到的阻力。
代码实现如下:
class Momentum:
"""Momentum SGD"""
def __init__(self, lr=0.01, momentum=0.9):
self.lr = lr
self.momentum = momentum
self.v = None
def update(self, params, grads):
if self.v is None:
self.v = {}
for key, val in params.items():
self.v[key] = np.zeros_like(val)
for key in params.keys():
self.v[key] = self.momentum*self.v[key] - self.lr*grads[key]
params[key] += self.v[key]
分析:
(1)初始化时,v中什么都不保存,当第一次调用update()时,v会以字典变量的形式保存与参数结构相同的数据。
(2)np.zeros_like(val):
import numpy as np
a = np.arange(12)
a = a.reshape(2,2,3)
b = np.zeros_like(a)
print(a)
print(b)
结果:
[[[ 0 1 2]
[ 3 4 5]]
[[ 6 7 8]
[ 9 10 11]]]
[[[0 0 0]
[0 0 0]]
[[0 0 0]
[0 0 0]]]
(3)
这一部分代码的意义是什么?params.items()是什么?此处存疑
。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
Momentum的优点 :
与SGD相比,它的“之”字程度减轻了。这是因为虽然x轴上受到的力非常小,但是一直是在同一个方向上,所以会朝一个方向加速;y轴方向虽然受力大,但是交互地受到正反方向的力,积分为0.与SGD相比,可以更快地找到最小值。
3. AdaGrad
AdaGrad会为参数的每个元素适当地调整学利率,与此同时进行学习。即随着学习的进行,使学习率逐渐减小。
⚪表示对应矩阵元素地乘积。
参数的元素中变动较大地元素地学习率更小。
代码实现:
class AdaGrad:
"""AdaGrad"""
def __init__(self, lr=0.01):
self.lr = lr
self.h = None
def update(self, params, grads):
if self.h is None:
self.h = {}
for key, val in params.items():
self.h[key] = np.zeros_like(val)
for key in params.keys():
self.h[key] += grads[key] * grads[key]
params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
权重更新路径如下图所示:
函数的取值高效地向最小值移动。
4. Adam
Adam是将前两种(AdaGrad和Momentum)融合在一起。
class Adam:
"""Adam (http://arxiv.org/abs/1412.6980v8)"""
def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
self.lr = lr
self.beta1 = beta1
self.beta2 = beta2
self.iter = 0
self.m = None
self.v = None
def update(self, params, grads):
if self.m is None:
self.m, self.v = {}, {}
for key, val in params.items():
self.m[key] = np.zeros_like(val)
self.v[key] = np.zeros_like(val)
self.iter += 1
lr_t = self.lr * np.sqrt(1.0 - self.beta2**self.iter) / (1.0 - self.beta1**self.iter)
for key in params.keys():
#self.m[key] = self.beta1*self.m[key] + (1-self.beta1)*grads[key]
#self.v[key] = self.beta2*self.v[key] + (1-self.beta2)*(grads[key]**2)
self.m[key] += (1 - self.beta1) * (grads[key] - self.m[key])
self.v[key] += (1 - self.beta2) * (grads[key]**2 - self.v[key])
params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)
#unbias_m += (1 - self.beta1) * (grads[key] - self.m[key]) # correct bias
#unbisa_b += (1 - self.beta2) * (grads[key]*grads[key] - self.v[key]) # correct bias
#params[key] += self.lr * unbias_m / (np.sqrt(unbisa_b) + 1e-7)
Adam会设置三个超参数:学习率、beta1、beta2.根据论文,beta1 = 0.9, beta2 = 0.999.大多数情况下可以顺利进行。
5. 基于MNIST数据集地更新方法比较
# coding: utf-8
import os
import sys
sys.path.append(os.pardir) # 为了导入父目录的文件而进行的设定
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.util import smooth_curve
from common.multi_layer_net import MultiLayerNet
from common.optimizer import *
# 0:读入MNIST数据==========
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)
train_size = x_train.shape[0] #把x_train化成一维
batch_size = 128
max_iterations = 2000
# 1:进行实验的设置==========
optimizers = {}
optimizers['SGD'] = SGD()
optimizers['Momentum'] = Momentum()
optimizers['AdaGrad'] = AdaGrad()
optimizers['Adam'] = Adam()
#optimizers['RMSprop'] = RMSprop()
networks = {}
train_loss = {}
for key in optimizers.keys():
networks[key] = MultiLayerNet(
input_size=784, hidden_size_list=[100, 100, 100, 100],
output_size=10)
train_loss[key] = []
# 2:开始训练==========
for i in range(max_iterations):
batch_mask = np.random.choice(train_size, batch_size)
x_batch = x_train[batch_mask]
t_batch = t_train[batch_mask]
for key in optimizers.keys():
grads = networks[key].gradient(x_batch, t_batch)
optimizers[key].update(networks[key].params, grads)
loss = networks[key].loss(x_batch, t_batch)
train_loss[key].append(loss)
if i % 100 == 0: #i与100取余结果等于0
print( "===========" + "iteration:" + str(i) + "===========")
for key in optimizers.keys():
loss = networks[key].loss(x_batch, t_batch)
print(key + ":" + str(loss))
# 3.绘制图形==========
markers = {"SGD": "o", "Momentum": "x", "AdaGrad": "s", "Adam": "D"}
x = np.arange(max_iterations)
for key in optimizers.keys():
plt.plot(x, smooth_curve(train_loss[key]), marker=markers[key], markevery=100, label=key)
plt.xlabel("iterations")
plt.ylabel("loss")
plt.ylim(0, 1)
plt.legend()
plt.show()
输出结果:
分析:
(1)
np.random.choice():
import numpy as np
a = np.random.choice(10, 8)
#从[0, 10)内输出8个数字并组成一维数组a
print(a)
b = np.random.choice(a, 5)
#从一维数组a中随意抽取5个数组成一维数组b
#注意:a必须是一维的
print(b)
结果:
[1 3 7 5 7 5 1 7]
[5 7 7 5 1]
二、权重的初始值
设置什么样的初始值关系到神经网络学习能否成功。
1. 权重初始值不能设为0
权重的初始值绝对不能设为0
之前一段时间,权重初始值设为:
import numpy as np
a = 0.01 * np.random.randn(10, 100)
补充:np.random.randn的介绍:
import numpy as np
a = 0.01 * np.random.randn(2, 4, 3)#2*3*4的数组,即表示生成数组的维度
b = np.random.randn(2, 4)
print(f'a is {a}')
print(f'b is {b}')
结果:
a is [[[-0.01141521 0.00021992 -0.00668211]
[-0.00799102 -0.01430591 0.00065054]
[ 0.00253524 -0.01118892 -0.01097236]
[-0.00580513 0.00963655 -0.00336067]]
[[ 0.00232957 -0.00983508 0.00066577]
[-0.01303359 0.02022611 -0.00138892]
[-0.00026297 -0.00356707 -0.01244644]
[ 0.00965091 0.00946335 0.00834518]]]
b is [[ 1.3743193 -1.40996427 0.11132154 -0.37661421]
[ 0.61963745 -0.37448273 -0.69203084 -1.4140828 ]]
总结
关于参数更新方法个人而言没有特别理解,但是在具体的应用中似乎不需要明白其机理。因此不再此处过多停留。下一节将重点介绍权重的初始值。