深度学习神经网络中权重的初始化
前言
良好的初始化权重有以下的好处:
- 加快梯度下降的收敛速度
- 增加梯度下降收敛到较低训练(和泛化)错误的几率
所以一个良好的初始化也是非常重要的,这里尝试三种初始化化方式:
- 零初始化,将权重参数初始化为零。
- 随机初始化,使用随机的方式,初始化权重参数。
- He初始化,这个公式的初始化方式。
我们来尝试这个三种方法吧。
模型函数
编写一个 model
函数,使用这个函数可以测试各种初始化权重参数的效果。
在编写 model
函数之前,我们先要导入对于依赖包,其中有些依赖包可以在这里下载。
# coding=utf-8
import numpy as np
from init_utils import compute_loss, forward_propagation, backward_propagation
from init_utils import update_parameters, predict, load_dataset
# 加载图像数据集:蓝色/红点在圆圈中
train_X, train_Y, test_X, test_Y = load_dataset()
其中使用的数据集是这个样子的:
然后就可以编写 model
函数了。
def model(X, Y, learning_rate=0.01, num_iterations=15000, print_cost=True, initialization="he"):
"""
实现一个三层神经网络:LINEAR>RELU->LINEAR->RELU->LINEAR->SIGMOID。
Arguments:
X -- 输入数据,形状(2,样本数量)
Y -- 真正的“标签”向量(包含0代表红点;(1)形状(1,样本数目)
learning_rate -- 梯度下降的学习速率。
num_iterations -- 运行梯度下降的迭代次数。
print_cost -- 如果是真的,打印每1000次迭代的成本。
initialization -- 选择使用哪个初始化(“0”、“随机”或“He”)
Returns:
parameters -- 由模型学习的参数。
"""
global parameters
grads = {}
costs = [] # to keep track of the loss
m = X.shape[1] # number of examples
layers_dims = [X.shape[0], 10, 5, 1]
# 字典初始化参数。
if initialization == "zeros":
parameters = initialize_parameters_zeros(layers_dims)
elif initialization == "random":
parameters = initialize_parameters_random(layers_dims)
elif initialization == "he":
parameters = initialize_parameters_he(layers_dims)
# 循环(梯度下降)
for i in range(0, num_iterations):
# 前向传播: LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID.
a3, cache = forward_propagation(X, parameters)
# Loss
cost = compute_loss(a3, Y)
# 反向传播。
grads = backward_propagation(X, Y, cache)
# 更新参数。
parameters = update_parameters(parameters, grads, learning_rate)
# 每1000次迭代打印一次损失。
if print_cost and i % 1000 == 0:
print("Cost after iteration {}: {}".format(i, cost))
costs.append(cost)
return parameters
零初始化
在神经网络中初始化的参数有两种类型:
- 权重矩阵 $(W^{[1]}, W^{[2]}, W^{[3]}, ..., W^{[L-1]}, W^{[L]})$
- 偏差向量 $(b^{[1]}, b^{[2]}, b^{[3]}, ..., b^{[L-1]}, b^{[L]})$
def initialize_parameters_zeros(layers_dims):
"""
Arguments:
layer_dims——python数组(list),包含每个层的大小。
Returns:
parameters -- 包含参数的python字典 "W1", "b1", ..., "WL", "bL":
W1 -- 形状权重矩阵(layers_dims[1], layers_dims[0])
b1 -- 形状偏置向量(layers_dims[1], 1)
...
WL -- 形状权重矩阵(layers_dims[L], layers_dims[L-1])
bL -- 形状的偏置向量(layers_dims[L], 1)
"""
parameters = {}
L = len(layers_dims) # 网络中的层数
for l in range(1, L):
parameters['W' + str(l)] = np.zeros((layers_dims[l], layers_dims[l - 1]))
parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
return parameters
然后运行测试一下,会看cost并不怎么收敛,这是因为它没有“破坏对称性”。
if __name__ == "__main__":
parameters = model(train_X, train_Y, initialization="zeros")
print ("On the train set:")
predictions_train = predict(train_X, train_Y, parameters)
print ("On the test set:")
predictions_test = predict(test_X, test_Y, parameters)
输出的日志信息:
Cost after iteration 0: 0.69314718056
Cost after iteration 1000: 0.69314718056
Cost after iteration 2000: 0.69314718056
Cost after iteration 3000: 0.69314718056
Cost after iteration 4000: 0.69314718056
Cost after iteration 5000: 0.69314718056
Cost after iteration 6000: 0.69314718056
Cost after iteration 7000: 0.69314718056
Cost after iteration 8000: 0.69314718056
Cost after iteration 9000: 0.69314718056
Cost after iteration 10000: 0.69314718056
Cost after iteration 11000: 0.69314718056
Cost after iteration 12000: 0.69314718056
Cost after iteration 13000: 0.69314718056
Cost after iteration 14000: 0.69314718056
On the train set:
Accuracy: 0.5
On the test set:
Accuracy: 0.5
如果使用图表来显示的话,是这样子的:
随机初始化
随机初始化可以打破对称,让我们随机初始化权重。在随机初始化之后,每个神经元可以继续学习其输入的不同功能。我们只是随机初始化权重参数,偏差还是继续初始化为零。
def initialize_parameters_random(layers_dims):
"""
Arguments:
layer_dims -- python数组(list),包含每个层的大小。
Returns:
parameters -- 包含参数的python字典 "W1", "b1", ..., "WL", "bL":
W1 -- 形状权重矩阵(layers_dims[1], layers_dims[0])
b1 -- 形状偏置向量(layers_dims[1], 1)
...
WL -- 形状权重矩阵(layers_dims[L], layers_dims[L-1])
bL -- 形状的偏置向量(layers_dims[L], 1)
"""
parameters = {}
L = len(layers_dims) # 表示层数的整数。
for l in range(1, L):
parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) * 10
parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
return parameters
运行一下,可以看到已经破坏了对称性,模型并开始收敛了。
if __name__ == "__main__":
parameters = model(train_X, train_Y, initialization="random")
print ("On the train set:")
predictions_train = predict(train_X, train_Y, parameters)
print ("On the test set:")
predictions_test = predict(test_X, test_Y, parameters)
输出的日志如下,看到在0次迭代的时候是“inf”,这是因为数值舍入的:
Cost after iteration 0: inf
Cost after iteration 1000: 0.386009576858
Cost after iteration 2000: 0.276065073598
Cost after iteration 3000: 0.267517603676
Cost after iteration 4000: 0.261640832949
Cost after iteration 5000: 0.254131922349
Cost after iteration 6000: 0.31200805047
Cost after iteration 7000: 0.243035498545
Cost after iteration 8000: 0.22011133348
Cost after iteration 9000: 0.217258906612
Cost after iteration 10000: 0.214850990347
Cost after iteration 11000: 0.212760572669
Cost after iteration 12000: 0.210936153771
Cost after iteration 13000: 0.209300028106
Cost after iteration 14000: 0.207868192424
On the train set:
Accuracy: 0.883333333333
On the test set:
Accuracy: 0.85
如果用图表来显示的话,是这个样子的:
He初始化
He初始化跟上面的随机初始化类似 initialize_parameters_random(...)
。唯一的区别是,不是 np.random.randn(..,..)*10
的乘以10,而是乘以$\sqrt{\frac{2}{\text{dimension of the previous layer}}}$,这是He初始化为具有ReLU激活的图层推荐的内容。
def initialize_parameters_he(layers_dims):
"""
Arguments:
layer_dims -- python数组(list),包含每个层的大小。
Returns:
parameters -- 包含参数的python字典 "W1", "b1", ..., "WL", "bL":
W1 -- 形状权重矩阵(layers_dims[1], layers_dims[0])
b1 -- 形状偏置向量(layers_dims[1], 1)
...
WL -- 形状权重矩阵(layers_dims[L], layers_dims[L-1])
bL -- 形状的偏置向量(layers_dims[L], 1)
"""
np.random.seed(3)
parameters = {}
L = len(layers_dims) - 1 # 表示层数的整数
for l in range(1, L + 1):
parameters['W' + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) * np.sqrt(
2. / layers_dims[l - 1])
parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))
return parameters
运行这个初始化方式。
if __name__ == "__main__":
parameters = model(train_X, train_Y, initialization="he")
print ("On the train set:")
predictions_train = predict(train_X, train_Y, parameters)
print ("On the test set:")
predictions_test = predict(test_X, test_Y, parameters)
输出的日志信息是:
Cost after iteration 0: 0.883053746342
Cost after iteration 1000: 0.687982591973
Cost after iteration 2000: 0.675128626452
Cost after iteration 3000: 0.652611776889
Cost after iteration 4000: 0.608295897057
Cost after iteration 5000: 0.530494449172
Cost after iteration 6000: 0.413864581707
Cost after iteration 7000: 0.311780346484
Cost after iteration 8000: 0.236962153303
Cost after iteration 9000: 0.185972872092
Cost after iteration 10000: 0.150155562804
Cost after iteration 11000: 0.123250792923
Cost after iteration 12000: 0.0991774654653
Cost after iteration 13000: 0.0845705595402
Cost after iteration 14000: 0.0735789596268
On the train set:
Accuracy: 0.993333333333
On the test set:
Accuracy: 0.96
如果用图表来显示的话,应该是:
总结
最后使用一个表格的方式来总结一下我们使用三个不同的初始化方式的训练准确率。
Model | Train accuracy | Problem/Comment |
3-layer NN with zeros initialization | 50% | fails to break symmetry |
3-layer NN with large random initialization | 83% | too large weights |
3-layer NN with He initialization | 99% | recommended method |
参考资料
该笔记是学习吴恩达老师的课程写的。初学者入门,如有理解有误的,欢迎批评指正!