飞道的博客

python深度学习入门-与学习相关的技巧

330人阅读  评论(0)

深度学习入门-与学习相关的技巧

目录

摘要

1. 参数的更新

1.1 SGD

1.2 SGD 的缺点

1.3 Momentum(动量)

1.4 AdaGrad

1.5 Adam

1.6 最优化方法的比较

1.7 基于 MNIST 数据集的更新方法的比较

2. 权重的初始值

3. Batch Normalization(批归一化)

4. 正则化

5. 超参数的验证


摘要

  • 参数更新方法:SGD、Momentum、AdaGrad、Adam 等。
  • 权重初始值的赋值方法对进行正确的学习非常重要。
  • 作为权重初始值,Xavier 初始值、He 初始值等比较有效。
  • 通过使用 Batch Normalization(批归一化),可以加速学习,并且对初始值变得健壮。
  • 抑制过拟合的正则化技术有:权值衰减、Dropot 等。
  • 逐渐缩小 “好值” 存在的范围是搜索超参数的一个有效方法。

1. 参数的更新

    最优化(optimization):

        神经网络的学习的目的是找到使损失函数的值尽可能小的参数。这是寻找最优参数的问题,解决这个问题的过程称为最优化(optimization)。

    随机梯度下降法(stochastic gradient descent):

        使用参数的梯度,沿梯度方向更新参数,并重复这个步骤多次,从而逐渐靠近最优参数,这个过程称为随机梯度下降法(stochastic gradient descent),简称 SGD

1.1 SGD

    SGD 用数学式表示如下式 (5.1)。

        

        :需要更新的权重参数;

        :损失函数关于  的梯度;

        :学习率(实际上会取 0.01 或 0.001 这些事先决定好的值);

        :表示用右边的值更新左边的值。

    Python 实现 SGD:


  
  1. class SGD(object):
  2. """随机梯度下降法(Stochastic Gradient Descent)"""
  3. def __init__(self, lr=0.01):
  4. self.lr = lr # 学习率
  5. def update(self, params, grads):
  6. for key in params.keys():
  7. params[key] -= self.lr * grads[key]

1.2 SGD 的缺点

    如果函数的形状非均向(anisotropic),比如呈延伸状,所有的路径就会非常低效。SGD 低效的根本原因是,梯度的方向并没有指向最小值的方向。

1.3 Momentum(动量)

    Momentum 用数学式表示如下式 (5.2)、(5.3)。

        

        

        :需要更新的权重参数;

        :损失函数关于  的梯度;

        :学习率(实际上会取 0.01 或 0.001 这些事先决定好的值);

        :表示了物体在梯度方向上受力,在这个力的作用下,物体的速度增加这一物理法则;

        :在物体不受任何力时,该项承担使物体逐渐减速的任务( 设定为 0.9 之类的值),对应物理上的地面摩擦力或空气阻力;

        :表示用右边的值更新左边的值。

    Python 实现 Momentum:


  
  1. import numpy as np
  2. class Momentum(object):
  3. """Momentum SGD"""
  4. def __init__(self, lr=0.01, momentum=0.9):
  5. self.lr = lr
  6. self.momentum = momentum
  7. self.v = None
  8. def update(self, params, grads):
  9. if self.v is None:
  10. self.v = {}
  11. for key, val in params.items():
  12. self.v[key] = np.zeros_like(val)
  13. for key in params.keys():
  14. self.v[key] = self.momentum * self.v[key] - self.lr*grads[key]
  15. params[key] += self.v[key]

1.4 AdaGrad

    学习率衰减(learning rate decay):

        随着学习的进行,使学习率逐渐减小。

     AdaGrad 用数学式表示如下式 (5.4)、(5.5)。

        

        

         :需要更新的权重参数;

        :损失函数关于  的梯度;

        :学习率(实际上会取 0.01 或 0.001 这些事先决定好的值);

        :保存了以前的所有梯度值的平方和;

        :表示用右边的值更新左边的值。

    使用 RMSProp 方法改善 AdaGrad 无止境学习时更新量变为 0 的情况:

        AdaGrad 会记录过去所有梯度的平方和。因此,学习越深入,更新的幅度就越小。实际上,如果无止境地学习,更新量就会变为 0,完全不再更新。

        RMSProp 方法并不是将过去所有的梯度一视同仁地相加,而是逐渐地遗忘过去的梯度,在做加法运算时将新梯度的信息更多地反映出来。这种操作从专业上讲,称为 “指数移动平均”,呈指数函数式地减小过去的梯度的尺度。

    Python 实现 AdaGrad 和 RMSProp :


  
  1. import numpy as np
  2. class AdaGrad(object):
  3. """AdaGrad"""
  4. def __init__(self, lr=0.01):
  5. self.lr = lr
  6. self.h = None
  7. def update(self, params, grads):
  8. if self.h is None:
  9. self.h = {}
  10. for key, val in params.items():
  11. self.h[key] = np.zeros_like(val)
  12. for key in params.keys():
  13. self.h[key] += grads[key] * grads[key]
  14. params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
  15. class RMSProp(object):
  16. """RMSProp"""
  17. def __init__(self, lr=0.01, decay_rate=0.99):
  18. self.lr = lr
  19. self.decay_rate = decay_rate
  20. self.h = None
  21. def update(self, params, grads):
  22. if self.h is None:
  23. self.h = {}
  24. for key, val in params.items():
  25. self.h[key] = np.zeros_like(val)
  26. for key in params.keys():
  27. self.h[key] *= self.decay_rate
  28. self.h[key] += ( 1 - self.decay_rate) * grads[key] * grads[key]
  29. params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

1.5 Adam

    Adam:直观理解,就是融合了 Momentum 和 AdaGrad 的方法。可以实现参数空间的高效搜索和进行超参数的 “偏置矫正”。论文地址:http://arxiv.org/abs/1412.6980v8

    Python 实现 Adam:


  
  1. import numpy as np
  2. class Adam(object):
  3. """Adam (http://arxiv.org/abs/1412.6980v8)"""
  4. def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
  5. self.lr = lr # 学习率
  6. self.beta1 = beta1 # 一次momentum系数
  7. self.beta2 = beta2 # 二次momentum系数
  8. self.iter = 0
  9. self.m = None
  10. self.v = None
  11. def update(self, params, grads):
  12. if self.m is None:
  13. self.m, self.v = {}, {}
  14. for key, val in params.items():
  15. self.m[key] = np.zeros_like(val)
  16. self.v[key] = np.zeros_like(val)
  17. self.iter += 1
  18. lr_t = self.lr * np.sqrt( 1.0 - self.beta2**self.iter) / ( 1.0 - self.beta1**self.iter)
  19. for key in params.keys():
  20. self.m[key] += ( 1 - self.beta1) * (grads[key] - self.m[key])
  21. self.v[key] += ( 1 - self.beta2) * (grads[key]** 2 - self.v[key])
  22. params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)

1.6 最优化方法的比较


  
  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. from collections import OrderedDict
  4. """优化器"""
  5. class SGD(object):
  6. """随机梯度下降法(Stochastic Gradient Descent)"""
  7. def __init__(self, lr=0.01):
  8. self.lr = lr # 学习率
  9. def update(self, params, grads):
  10. for key in params.keys():
  11. params[key] -= self.lr * grads[key]
  12. class Momentum(object):
  13. """Momentum SGD"""
  14. def __init__(self, lr=0.01, momentum=0.9):
  15. self.lr = lr
  16. self.momentum = momentum
  17. self.v = None
  18. def update(self, params, grads):
  19. if self.v is None:
  20. self.v = {}
  21. for key, val in params.items():
  22. self.v[key] = np.zeros_like(val)
  23. for key in params.keys():
  24. self.v[key] = self.momentum * self.v[key] - self.lr*grads[key]
  25. params[key] += self.v[key]
  26. class Nesterov:
  27. """Nesterov's Accelerated Gradient (http://arxiv.org/abs/1212.0901)"""
  28. def __init__(self, lr=0.01, momentum=0.9):
  29. self.lr = lr
  30. self.momentum = momentum
  31. self.v = None
  32. def update(self, params, grads):
  33. if self.v is None:
  34. self.v = {}
  35. for key, val in params.items():
  36. self.v[key] = np.zeros_like(val)
  37. for key in params.keys():
  38. self.v[key] *= self.momentum
  39. self.v[key] -= self.lr * grads[key]
  40. params[key] += self.momentum * self.momentum * self.v[key]
  41. params[key] -= ( 1 + self.momentum) * self.lr * grads[key]
  42. class AdaGrad:
  43. """AdaGrad"""
  44. def __init__(self, lr=0.01):
  45. self.lr = lr
  46. self.h = None
  47. def update(self, params, grads):
  48. if self.h is None:
  49. self.h = {}
  50. for key, val in params.items():
  51. self.h[key] = np.zeros_like(val)
  52. for key in params.keys():
  53. self.h[key] += grads[key] * grads[key]
  54. params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
  55. class RMSProp(object):
  56. """RMSProp"""
  57. def __init__(self, lr=0.01, decay_rate=0.99):
  58. self.lr = lr
  59. self.decay_rate = decay_rate
  60. self.h = None
  61. def update(self, params, grads):
  62. if self.h is None:
  63. self.h = {}
  64. for key, val in params.items():
  65. self.h[key] = np.zeros_like(val)
  66. for key in params.keys():
  67. self.h[key] *= self.decay_rate
  68. self.h[key] += ( 1 - self.decay_rate) * grads[key] * grads[key]
  69. params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
  70. class Adam(object):
  71. """Adam (http://arxiv.org/abs/1412.6980v8)"""
  72. def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
  73. self.lr = lr # 学习率
  74. self.beta1 = beta1 # 一次momentum系数
  75. self.beta2 = beta2 # 二次momentum系数
  76. self.iter = 0
  77. self.m = None
  78. self.v = None
  79. def update(self, params, grads):
  80. if self.m is None:
  81. self.m, self.v = {}, {}
  82. for key, val in params.items():
  83. self.m[key] = np.zeros_like(val)
  84. self.v[key] = np.zeros_like(val)
  85. self.iter += 1
  86. lr_t = self.lr * np.sqrt( 1.0 - self.beta2**self.iter) / ( 1.0 - self.beta1**self.iter)
  87. for key in params.keys():
  88. self.m[key] += ( 1 - self.beta1) * (grads[key] - self.m[key])
  89. self.v[key] += ( 1 - self.beta2) * (grads[key]** 2 - self.v[key])
  90. params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)
  91. """优化器比较"""
  92. def f(x, y):
  93. return x** 2 / 20.0 + y** 2
  94. def df(x, y):
  95. return x / 10.0, 2.0 * y
  96. init_pos = ( -7.0, 2.0)
  97. params = {}
  98. params[ "x"], params[ "y"] = init_pos[ 0], init_pos[ 1]
  99. grads = {}
  100. grads[ "x"], grads[ "y"] = 0, 0
  101. optimizers = OrderedDict()
  102. optimizers[ "SGD"] = SGD(lr= 0.95)
  103. optimizers[ "Momentum"] = Momentum(lr= 0.1)
  104. optimizers[ "AdaGrad"] = AdaGrad(lr= 1.5)
  105. optimizers[ "Adam"] = Adam(lr= 0.3)
  106. idx = 1
  107. for key in optimizers:
  108. optimizer = optimizers[key]
  109. x_history = []
  110. y_history = []
  111. params[ 'x'], params[ 'y'] = init_pos[ 0], init_pos[ 1]
  112. for i in range( 30):
  113. x_history.append(params[ "x"])
  114. y_history.append(params[ "y"])
  115. grads[ "x"], grads[ "y"] = df(params[ "x"], params[ "y"])
  116. optimizer.update(params, grads)
  117. x = np.arange( -10, 10, 0.01)
  118. y = np.arange( -5, 5, 0.01)
  119. X, Y = np.meshgrid(x, y)
  120. Z = f(X, Y)
  121. # for simple contour line
  122. mask = Z > 7
  123. Z[mask] = 0
  124. # plot
  125. plt.subplot( 2, 2, idx)
  126. idx += 1
  127. plt.plot(x_history, y_history, "o-", color= "red")
  128. plt.contour(X, Y, Z)
  129. plt.ylim( -10, 10)
  130. plt.xlim( -10, 10)
  131. plt.plot( 0, 0, "+")
  132. # colorbar()
  133. # spring()
  134. plt.title(key)
  135. plt.xlabel( "x")
  136. plt.ylabel( "y")
  137. plt.show()

 

图 5-1    最优化方法的比较:SGD、Momentum、AdaGram、Adam

1.7 基于 MNIST 数据集的更新方法的比较

    实验:

        以一个 5 层神经网络为对象,其中每层有 100 个神经元。激活函数使用 ReLU。

    Python 实现:


  
  1. """基于MNIST数据集的更新方法的比较"""
  2. import numpy as np
  3. import matplotlib.pyplot as plt
  4. from collections import OrderedDict
  5. from dataset.mnist import load_mnist
  6. def smooth_curve(x):
  7. """用于使损失函数的图形变圆滑
  8. 参考:http://glowingpython.blogspot.jp/2012/02/convolution-with-numpy.html
  9. """
  10. window_len = 11
  11. s = np.r_[x[window_len -1: 0: -1], x, x[ -1:-window_len: -1]]
  12. w = np.kaiser(window_len, 2)
  13. y = np.convolve(w/w.sum(), s, mode= 'valid')
  14. return y[ 5:len(y) -5]
  15. def sigmoid(x):
  16. return 1 / ( 1 + np.exp(-x))
  17. def softmax(x):
  18. if x.ndim == 2:
  19. x = x.T
  20. x = x - np.max(x, axis= 0)
  21. y = np.exp(x) / np.sum(np.exp(x), axis= 0)
  22. return y.T
  23. x = x - np.max(x) # 溢出对策
  24. return np.exp(x) / np.sum(np.exp(x))
  25. def cross_entropy_error(y, t):
  26. if y.ndim == 1:
  27. t = t.reshape( 1, t.size)
  28. y = y.reshape( 1, y.size)
  29. # 监督数据是one-hot-vector的情况下,转换为正确解标签的索引
  30. if t.size == y.size:
  31. t = t.argmax(axis= 1)
  32. batch_size = y.shape[ 0]
  33. return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
  34. def numerical_gradient(f, x):
  35. h = 1e-4 # 0.0001
  36. grad = np.zeros_like(x)
  37. it = np.nditer(x, flags=[ 'multi_index'], op_flags=[ 'readwrite'])
  38. while not it.finished:
  39. idx = it.multi_index
  40. tmp_val = x[idx]
  41. x[idx] = float(tmp_val) + h
  42. fxh1 = f(x) # f(x+h)
  43. x[idx] = tmp_val - h
  44. fxh2 = f(x) # f(x-h)
  45. grad[idx] = (fxh1 - fxh2) / ( 2 * h)
  46. x[idx] = tmp_val # 还原值
  47. it.iternext()
  48. return grad
  49. class SoftmaxWithLoss(object):
  50. def __init__(self):
  51. self.loss = None # 损失
  52. self.y = None # softmax 的输出
  53. self.t = None # 监督数据(one-hot vector)
  54. def forward(self, x, t):
  55. """
  56. 正向传播
  57. :param x: 输入
  58. :param t: 监督数据
  59. :return:
  60. """
  61. self.t = t
  62. self.y = softmax(x)
  63. self.loss = cross_entropy_error(self.y, self.t)
  64. return self.loss
  65. def backward(self, dout=1):
  66. """
  67. 反向传播
  68. :param dout: 上游传来的导数
  69. :return:
  70. """
  71. batch_size = self.t.shape[ 0]
  72. if self.t.size == self.y.size: # 监督数据是one-hot-vector的情况
  73. dx = (self.y - self.t) / batch_size
  74. else:
  75. dx = self.y.copy()
  76. dx[np.arange(batch_size), self.t] -= 1
  77. dx = dx / batch_size
  78. return dx
  79. class Sigmoid:
  80. def __init__(self):
  81. self.out = None
  82. def forward(self, x):
  83. out = sigmoid(x)
  84. self.out = out
  85. return out
  86. def backward(self, dout):
  87. dx = dout * ( 1.0 - self.out) * self.out
  88. return dx
  89. class Affine:
  90. def __init__(self, W, b):
  91. self.W = W # 权重参数
  92. self.b = b # 偏置参数
  93. self.x = None # 输入
  94. self.original_x_shape = None # 输入张量的形状
  95. self.dW = None # 权重参数的导数
  96. self.db = None # 偏置参数的导数
  97. def forward(self, x):
  98. """
  99. 正向传播
  100. :param x:
  101. :return:
  102. """
  103. # 对应张量
  104. self.original_x_shape = x.shape
  105. x = x.reshape(x.shape[ 0], -1)
  106. self.x = x
  107. out = np.dot(self.x, self.W) + self.b
  108. return out
  109. def backward(self, dout):
  110. """
  111. 反向传播
  112. :param dout: 上游传来的导数
  113. :return: 输入的导数
  114. """
  115. dx = np.dot(dout, self.W.T)
  116. self.dW = np.dot(self.x.T, dout)
  117. self.db = np.sum(dout, axis= 0)
  118. dx = dx.reshape(*self.original_x_shape) # 还原输入数据的形状(对应张量)
  119. return dx
  120. class Relu(object):
  121. def __init__(self):
  122. # 由True/False构成的NumPy数组
  123. # 正向传播时的输入x的元素中小于等于0的地方保存为True,其他地方(大于0的元素)保存为False
  124. self.mask = None
  125. def forward(self, x):
  126. """
  127. 正向传播
  128. :param x: 正向传播时的输入
  129. :return:
  130. """
  131. # 输入x的元素中小于等于0的地方保存为True,其他地方(大于0的元素)保存为False
  132. self.mask = (x <= 0)
  133. # 输入x的元素中小于等于0的值变换为0
  134. out = x.copy()
  135. out[self.mask] = 0
  136. return out
  137. def backward(self, dout):
  138. """
  139. 反向传播
  140. :param dout: 上游传来的导数
  141. :return:
  142. """
  143. # 将从上游传来的dout的mask中的元素为True的地方设为0
  144. dout[self.mask] = 0
  145. dx = dout
  146. return dx
  147. class MultiLayerNet:
  148. """全连接的多层神经网络
  149. Parameters
  150. ----------
  151. input_size : 输入大小(MNIST的情况下为784)
  152. hidden_size_list : 隐藏层的神经元数量的列表(e.g. [100, 100, 100])
  153. output_size : 输出大小(MNIST的情况下为10)
  154. activation : 'relu' or 'sigmoid'
  155. weight_init_std : 指定权重的标准差(e.g. 0.01)
  156. 指定'relu'或'he'的情况下设定“He的初始值”
  157. 指定'sigmoid'或'xavier'的情况下设定“Xavier的初始值”
  158. weight_decay_lambda : Weight Decay(L2范数)的强度
  159. """
  160. def __init__(self, input_size, hidden_size_list, output_size,
  161. activation='relu', weight_init_std='relu', weight_decay_lambda=0):
  162. self.input_size = input_size
  163. self.output_size = output_size
  164. self.hidden_size_list = hidden_size_list
  165. self.hidden_layer_num = len(hidden_size_list)
  166. self.weight_decay_lambda = weight_decay_lambda
  167. self.params = {}
  168. # 初始化权重
  169. self.__init_weight(weight_init_std)
  170. # 生成层
  171. activation_layer = { 'sigmoid': Sigmoid, 'relu': Relu}
  172. self.layers = OrderedDict()
  173. for idx in range( 1, self.hidden_layer_num+ 1):
  174. self.layers[ 'Affine和Softmax层的实现' + str(idx)] = Affine(self.params[ 'W' + str(idx)],
  175. self.params[ 'b' + str(idx)])
  176. self.layers[ 'Activation_function' + str(idx)] = activation_layer[activation]()
  177. idx = self.hidden_layer_num + 1
  178. self.layers[ 'Affine和Softmax层的实现' + str(idx)] = Affine(self.params[ 'W' + str(idx)],
  179. self.params[ 'b' + str(idx)])
  180. self.last_layer = SoftmaxWithLoss()
  181. def __init_weight(self, weight_init_std):
  182. """设定权重的初始值
  183. Parameters
  184. ----------
  185. weight_init_std : 指定权重的标准差(e.g. 0.01)
  186. 指定'relu'或'he'的情况下设定“He的初始值”
  187. 指定'sigmoid'或'xavier'的情况下设定“Xavier的初始值”
  188. """
  189. all_size_list = [self.input_size] + self.hidden_size_list + [self.output_size]
  190. for idx in range( 1, len(all_size_list)):
  191. scale = weight_init_std
  192. if str(weight_init_std).lower() in ( 'relu', 'he'):
  193. scale = np.sqrt( 2.0 / all_size_list[idx - 1]) # 使用ReLU的情况下推荐的初始值
  194. elif str(weight_init_std).lower() in ( 'sigmoid', 'xavier'):
  195. scale = np.sqrt( 1.0 / all_size_list[idx - 1]) # 使用sigmoid的情况下推荐的初始值
  196. self.params[ 'W' + str(idx)] = scale * np.random.randn(all_size_list[idx -1], all_size_list[idx])
  197. self.params[ 'b' + str(idx)] = np.zeros(all_size_list[idx])
  198. def predict(self, x):
  199. for layer in self.layers.values():
  200. x = layer.forward(x)
  201. return x
  202. def loss(self, x, t):
  203. """求损失函数
  204. Parameters
  205. ----------
  206. x : 输入数据
  207. t : 教师标签
  208. Returns
  209. -------
  210. 损失函数的值
  211. """
  212. y = self.predict(x)
  213. weight_decay = 0
  214. for idx in range( 1, self.hidden_layer_num + 2):
  215. W = self.params[ 'W' + str(idx)]
  216. weight_decay += 0.5 * self.weight_decay_lambda * np.sum(W ** 2)
  217. return self.last_layer.forward(y, t) + weight_decay
  218. def accuracy(self, x, t):
  219. y = self.predict(x)
  220. y = np.argmax(y, axis= 1)
  221. if t.ndim != 1 : t = np.argmax(t, axis= 1)
  222. accuracy = np.sum(y == t) / float(x.shape[ 0])
  223. return accuracy
  224. def numerical_gradient(self, x, t):
  225. """求梯度(数值微分)
  226. Parameters
  227. ----------
  228. x : 输入数据
  229. t : 教师标签
  230. Returns
  231. -------
  232. 具有各层的梯度的字典变量
  233. grads['W1']、grads['W2']、...是各层的权重
  234. grads['b1']、grads['b2']、...是各层的偏置
  235. """
  236. loss_W = lambda W: self.loss(x, t)
  237. grads = {}
  238. for idx in range( 1, self.hidden_layer_num+ 2):
  239. grads[ 'W' + str(idx)] = numerical_gradient(loss_W, self.params[ 'W' + str(idx)])
  240. grads[ 'b' + str(idx)] = numerical_gradient(loss_W, self.params[ 'b' + str(idx)])
  241. return grads
  242. def gradient(self, x, t):
  243. """求梯度(误差反向传播法)
  244. Parameters
  245. ----------
  246. x : 输入数据
  247. t : 教师标签
  248. Returns
  249. -------
  250. 具有各层的梯度的字典变量
  251. grads['W1']、grads['W2']、...是各层的权重
  252. grads['b1']、grads['b2']、...是各层的偏置
  253. """
  254. # forward
  255. self.loss(x, t)
  256. # backward
  257. dout = 1
  258. dout = self.last_layer.backward(dout)
  259. layers = list(self.layers.values())
  260. layers.reverse()
  261. for layer in layers:
  262. dout = layer.backward(dout)
  263. # 设定
  264. grads = {}
  265. for idx in range( 1, self.hidden_layer_num+ 2):
  266. grads[ 'W' + str(idx)] = self.layers[ 'Affine和Softmax层的实现' + str(idx)].dW + self.weight_decay_lambda * self.layers[ 'Affine和Softmax层的实现' + str(idx)].W
  267. grads[ 'b' + str(idx)] = self.layers[ 'Affine和Softmax层的实现' + str(idx)].db
  268. return grads
  269. class SGD(object):
  270. """随机梯度下降法(Stochastic Gradient Descent)"""
  271. def __init__(self, lr=0.01):
  272. self.lr = lr # 学习率
  273. def update(self, params, grads):
  274. for key in params.keys():
  275. params[key] -= self.lr * grads[key]
  276. class Momentum(object):
  277. """Momentum SGD"""
  278. def __init__(self, lr=0.01, momentum=0.9):
  279. self.lr = lr
  280. self.momentum = momentum
  281. self.v = None
  282. def update(self, params, grads):
  283. if self.v is None:
  284. self.v = {}
  285. for key, val in params.items():
  286. self.v[key] = np.zeros_like(val)
  287. for key in params.keys():
  288. self.v[key] = self.momentum * self.v[key] - self.lr * grads[key]
  289. params[key] += self.v[key]
  290. class Nesterov:
  291. """Nesterov's Accelerated Gradient (http://arxiv.org/abs/1212.0901)"""
  292. def __init__(self, lr=0.01, momentum=0.9):
  293. self.lr = lr
  294. self.momentum = momentum
  295. self.v = None
  296. def update(self, params, grads):
  297. if self.v is None:
  298. self.v = {}
  299. for key, val in params.items():
  300. self.v[key] = np.zeros_like(val)
  301. for key in params.keys():
  302. self.v[key] *= self.momentum
  303. self.v[key] -= self.lr * grads[key]
  304. params[key] += self.momentum * self.momentum * self.v[key]
  305. params[key] -= ( 1 + self.momentum) * self.lr * grads[key]
  306. class AdaGrad:
  307. """AdaGrad"""
  308. def __init__(self, lr=0.01):
  309. self.lr = lr
  310. self.h = None
  311. def update(self, params, grads):
  312. if self.h is None:
  313. self.h = {}
  314. for key, val in params.items():
  315. self.h[key] = np.zeros_like(val)
  316. for key in params.keys():
  317. self.h[key] += grads[key] * grads[key]
  318. params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
  319. class RMSProp(object):
  320. """RMSProp"""
  321. def __init__(self, lr=0.01, decay_rate=0.99):
  322. self.lr = lr
  323. self.decay_rate = decay_rate
  324. self.h = None
  325. def update(self, params, grads):
  326. if self.h is None:
  327. self.h = {}
  328. for key, val in params.items():
  329. self.h[key] = np.zeros_like(val)
  330. for key in params.keys():
  331. self.h[key] *= self.decay_rate
  332. self.h[key] += ( 1 - self.decay_rate) * grads[key] * grads[key]
  333. params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)
  334. class Adam(object):
  335. """Adam (http://arxiv.org/abs/1412.6980v8)"""
  336. def __init__(self, lr=0.001, beta1=0.9, beta2=0.999):
  337. self.lr = lr # 学习率
  338. self.beta1 = beta1 # 一次momentum系数
  339. self.beta2 = beta2 # 二次momentum系数
  340. self.iter = 0
  341. self.m = None
  342. self.v = None
  343. def update(self, params, grads):
  344. if self.m is None:
  345. self.m, self.v = {}, {}
  346. for key, val in params.items():
  347. self.m[key] = np.zeros_like(val)
  348. self.v[key] = np.zeros_like(val)
  349. self.iter += 1
  350. lr_t = self.lr * np.sqrt( 1.0 - self.beta2 ** self.iter) / ( 1.0 - self.beta1 ** self.iter)
  351. for key in params.keys():
  352. self.m[key] += ( 1 - self.beta1) * (grads[key] - self.m[key])
  353. self.v[key] += ( 1 - self.beta2) * (grads[key] ** 2 - self.v[key])
  354. params[key] -= lr_t * self.m[key] / (np.sqrt(self.v[key]) + 1e-7)
  355. """优化器比较"""
  356. # 0: 读入数据
  357. (x_train, y_train), (x_test, y_test) = load_mnist(normalize= True)
  358. train_size = x_train.shape[ 0]
  359. batch_size = 128
  360. max_iterations = 2000
  361. # 1: 进行实验的设置
  362. optimizers = {}
  363. optimizers[ "SGD"] = SGD()
  364. optimizers[ "Momentum"] = Momentum()
  365. optimizers[ "AdaGrad"] = AdaGrad()
  366. optimizers[ "Adam"] = Adam()
  367. networks = {}
  368. train_loss = {}
  369. for key in optimizers.keys():
  370. networks[key] = MultiLayerNet(input_size= 784,
  371. hidden_size_list=[ 100, 100, 100, 100],
  372. output_size= 10)
  373. train_loss[key] = []
  374. # 2: 开始训练
  375. for i in range(max_iterations):
  376. batch_mask = np.random.choice(train_size, batch_size)
  377. x_batch = x_train[batch_mask]
  378. y_batch = y_train[batch_mask]
  379. for key in optimizers.keys():
  380. grads = networks[key].gradient(x_batch, y_batch)
  381. optimizers[key].update(networks[key].params, grads)
  382. loss = networks[key].loss(x_batch, y_batch)
  383. train_loss[key].append(loss)
  384. if i % 100 == 0:
  385. print( f"============itrration: {i}============")
  386. for key in optimizers.keys():
  387. loss = networks[key].loss(x_batch, y_batch)
  388. print( f"{key}: {loss}")
  389. # 3: 绘制图形
  390. markers = { "SGD": "o", "Momentum": "x", "AdaGrad": "s", "Adam": "D"}
  391. x = np.arange(max_iterations)
  392. for key in optimizers.keys():
  393. plt.plot(x, smooth_curve(train_loss[key]), marker=markers[key], markevery= 100, label=key)
  394. plt.xlabel( "iterations")
  395. plt.ylabel( "loss")
  396. plt.ylim( 0, 1)
  397. plt.legend()
  398. plt.show()
图 5-2    基于 MNIST 数据集的 4 种更新方法的实现

2. 权重的初始值

    在神经网络的学习中,设定什么样的权重初始值,经常关系到神经网络的学习能否成功。

2.1 可以将权重初始值设为 0 吗

    权值衰减(weight decay): 一种以减小权重参数的值为目的进行学习的方法。通过减小权重参数的值来抑制过拟合的发生。

    为什么不能将权重初始值设为 0 呢?为什么不能将权重初始值设成一样的值呢?

    因为在误差反向传播中,所有的权重值都会进行相同的更新。比如,在 2 层神经网络中,假设第 1 层和第 2 层的权重为0。这样一来,正向传播时,因为输入层的权重为 0,所以第 2 层的神经元全部会被传递相同的值。第 2 层的神经元中全部输入相同的值,这意味着反向传播时第 2 层的权重全部都会进行相同的更新。因此,权重被更新为相同的值,并拥有了对称的值(重复的值)。这使得神经网络拥有许多不同的权重的意义丧失了。

2.2 隐藏层的激活值的分布

    实验:

        向一个 5 层神经网络(激活函数使用 sigmoid 函数)传入随机生成的输入数据,用直方图绘制各层激活值(激活函数的输出数据)的数据分布。

    实验 1:使用标准差为 1 的高斯分布作为权重初始值

图 5-3    使用标准差为 1 的高斯分布作为权重初始值时的各层激活值的分布

        各层的激活值呈偏向 0 和 1 的分布。这里使用的 sigmoid 函数是 S 型函数,随着输出不断地靠近 0(或者靠近 1),它的导数的值逐渐接近 0。

        梯度消失(gradient vanishing):偏向 0 和 1 的数据分布会造成反向传播中梯度的值不断变小,最后消失。这个问题称为梯度消失(gradient vanishing)。

    实验 2:使用标准差为 0.01 的高斯分布作为权重初始值

图 5-4    使用标准差为 0.01 的高斯分布作为权重初始值时的各层激活值的分布

        各层的激活值呈集中在 0.5 附近的分布。因为不像标准差为 1 时的那样偏向 0 和 1,所以不会发生梯度消失的问题。

        激活值的分布有所偏向,说明表现力上会有很大问题。为什么这么说呢?因为如果有多个神经元都输出几乎相同的值,那它们就没有存在的意义了。比如,如果 100 个神经元都输出几乎相同的值,那么也可以由 1 个神经元来表达基本相同的事情。因此,激活值在分布上有所偏向会出现 “表现力受限 ” 的问题。

    各层的激活值的分布都要求有适当的广度。为什么呢?

    因为通过在各层间传递多样性的数据,神经网络可以进行高效的学习。反过来,如果传递的是有所偏向的数据,就会出现梯度消失或者 “表现力受限” 的问题,导致学习可能无法顺利进行。

    实验 3:使用 Xavier 初始值作为权重初始值

        如果前一层的节点数为 n,则初始值使用标准差为  的分布。

图 5-5    使用 Xavier 初始值作为权重初始值时的各层激活值的分布

        使用 Xavier 初始值后,前一层的节点数越多,要设定为目标节点的初始值的权重尺度就越小。

        观察图 5-5 可知,越是后面的层,图像变得越歪斜,但是呈现了比之前更有广度的分布。因为各层间传递的数据有了适当的广度,所以 sigmoid 函数的表现力不受限制,有望进行高效地学习。

    实验 4:使用 Xavier 初始值作为权重初始值时使用 tanh 函数代替 sigmoid 函数

        tanh 和 sigmoid 函数都是 S型曲线函数。

        tanh 函数是关于原点 (0, 0) 对称的 S 型曲线,而 sigmoid 函数是关于 (x, y) = (0, 0.5) 对称的 S 型曲线。

图 5-6    使用 Xavier 初始值作为权重初始值时时使用 tanh 函数代替 sigmoid 函数的各层激活值的分布

    用 tanh 函数(双曲线函数)代替 sigmoid 函数,可以改善 sigmoid 函数作为激活函数时神经网络层的激活值分布呈现出稍微歪斜的形状的问题。

    

3. Batch Normalization(批归一化)

4. 正则化

5. 超参数的验证


转载:https://blog.csdn.net/weixin_38477351/article/details/116380361
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场