初始化

欢迎来到“改进深度网络”课程作业的第一周第一部分。

训练你自己的深度网络需要指定权重的初始值,精心挑选的初始化方法将有助于学习的进度。

如果你完成了之前的课程,你可以按照我们的指导进行权重初始化,并且现在已经完成了。但是,你如何对一个新的神经网络进行初始化?在本次作业,你会看到不同的初始化会带来不同的结果。

一个良好的初始化能够:

l  加速梯度收敛

l  增加梯度下降收敛到较低的训练(和泛化)错误的几率

为了开始本作业,请运行下面的单元格来加载用来尝试分类的packages和planar dataset。

import numpy as npimport matplotlib.pyplot as pltimport sklearnimport sklearn.datasetsfrom init_utils import sigmoid,relu,compute_loss,forward_propagationfrom init_utils import update_parameters, predict,backward_propagationfrom init_utils import predict_dec,load_dataset, plot_decision_boundary%matplotlib inline#使用%matplotlib命令可以将matplotlib的图表直接嵌入到Notebook之中,#或者使用指定的界面库显示图表,它有一个参数指定matplotlib图表的显示方式#inline表示将图表嵌入到Notebook中plt.rcParams['figure.figsize'] = (7.0, 4.0) # set default size of plotsplt.rcParams['image.interpolation'] = 'nearest'plt.rcParams['image.cmap'] = 'gray'# load image dataset: blue/red dots in circlestrain_X, train_Y, test_X, test_Y = load_dataset()

1.jpg


1 – 深度网络模型

你将使用三层网络模型(该模型已经提供给你)。这里有三种可以使用的初始化方式:

  • Zeros initialization — setting initialization = "zeros"      in the input argument.

  • Random initialization — setting initialization = "random"      in the input argument. This initializes the weights to large random      values.

  • He initialization — setting initialization = "he" in      the input argument. This initializes the weights to random values scaled      according to a paper by He et al., 2015.

Instructions: 请快速阅读下面的代码,并运行它。 在下一部分中,您将实现这个model()调用的三个初始化方法。

def model(X,Y,learning_rate=0.01,num_iterations=15000,print_cost=True,initialization="he"):    """    Implements a three-layer neural network:    LINEAR->RELU->LINEAR->RELU->LINEAR->SIGMOID.    """            grads = {}    costs = [] # to keep track of the loss    m = X.shape[1] # number of examples    layers_dims = [X.shape[0], 10, 5, 1]        # Initialize parameters dictionary.    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)     # Loop (gradient descent)     for i in range(0, num_iterations):         # Forward propagation:        #LINEAR->RELU->LINEAR->RELU->LINEAR->SIGMOID.        a3, cache = forward_propagation(X, parameters)                # Loss        cost = compute_loss(a3, Y)         # Backward propagation.        grads = backward_propagation(X, Y, cache)                # Update parameters.        parameters = update_parameters(parameters, grads, learning_rate)                # Print the loss every 1000 iterations        if print_cost and i % 1000 == 0:            print("Cost after iteration {}: {}".format(i, cost))            costs.append(cost)                # plot the loss    plt.plot(costs)    plt.ylabel('cost')    plt.xlabel('iterations (per hundreds)')    plt.title("Learning rate =" + str(learning_rate))    plt.show()    return parameters

2 – 零初始化

两种在深度网络中进行初始化的参数:

  • 深度网络(W[1],W[2],W[3],…,W[L1],W[L])(W[1],W[2],W[3],…,W[L1],W[L])

  • 偏差值向量(b[1],b[2],b[3],…,b[L1],b[L])(b[1],b[2],b[3],…,b[L1],b[L])

    Exercise: Implement the following function to initialize all parameters to zeros. You'll see later that this does not work well since it fails to "break symmetry", but lets try it anyway and see what happens. Use np.zeros((..,..)) with the correct shapes.

    用以下代码来做测试:

  • # GRADED FUNCTION: initialize_parameters_zerosdef initialize_parameters_zeros(layers_dims):    """    Arguments:    layer_dims -- python array (list) containing the size of each layer.    Returns:    parameters --     python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":     W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])      b1 -- bias vector of shape (layers_dims[1], 1)                    ...      WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])     bL -- bias vector of shape (layers_dims[L], 1)    """    parameters = {}    L = len(layers_dims)# number of layers in the network       for l in range(1, L):        ### START CODE HERE ### (≈ 2 lines of code)               parameters['W' + str(l)] = np.zeros((layers_dims[1], layers_dims[0]))         if l == 1 else np.zeros((layers_dims[l], layers_dims[l-1]))        parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))               ### END CODE HERE ###    return parameters
  • 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.6931471805599453
Cost after iteration 1000: 0.6931471805599453
Cost after iteration 2000: 0.6931471805599453
Cost after iteration 3000: 0.6931471805599453
Cost after iteration 4000: 0.6931471805599453
Cost after iteration 5000: 0.6931471805599453
Cost after iteration 6000: 0.6931471805599453
Cost after iteration 7000: 0.6931471805599453
Cost after iteration 8000: 0.6931471805599453
Cost after iteration 9000: 0.6931471805599453
Cost after iteration 10000: 0.6931471805599455
Cost after iteration 11000: 0.6931471805599453
Cost after iteration 12000: 0.6931471805599453
Cost after iteration 13000: 0.6931471805599453
Cost after iteration 14000: 0.6931471805599453

2.jpg

On the train set:

Accuracy: 0.5

On the test set:

Accuracy: 0.5

可以看出,性能表现非常差,代价函数也没有任何下降,这个算法的表现都不如自己随机猜一个好。这是为什么呢?让我们来看一下预测和决策边界的详细情况:

print ("predictions_train = " + str(predictions_train))print ("predictions_test = " + str(predictions_test))

 
该模型预测每个例子均为0。
一般来说,将所有权重初始化为零将导致网络无法破坏对称性。 这意味着每一层中的每一个神经元都会学到相同的东西,而且你也可以用n [l] = 1n [l] = 1来训练一个神经网络,而且网络没有如逻辑回归之类的线性分类器那么强大。

你需要记住的是:

  • 为了打破对称,权重W[l]应当被随机初始化。

  • 将b[l]初始化为0是可以的,只要权重矩阵随机初始化就可以打破对称性。

3 – 随机初始化

为了打破对称,现在随机初始化权重矩阵。随机初始化之后,每个神经元可以继续学习其输入的不同功能。 在这个练习中,你会看到如果权重是随机初始化会发生什么情况,但是会看到非常大的值。

Exercise: 执行接下来的函数来用较大的数值进行随机初始化,将偏差值初始化为0。Use np.random.randn(..,..) * 10 for weights and np.zeros((.., ..)) for biases. We are using a fixed np.random.seed(..) to make sure your "random" weights match ours, so don't worry if running several times your code gives you always the same initial values for the parameters.

# GRADED FUNCTION: initialize_parameters_random def initialize_parameters_random(layers_dims):    """    Arguments:    layer_dims -- python array (list) containing the size of each layer.        Returns:    parameters--python dictionary containing your parameters"W1","b1",...,"WL","bL":        W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])        b1 -- bias vector of shape (layers_dims[1], 1)                    ...         WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])        bL -- bias vector of shape (layers_dims[L], 1)    """        np.random.seed(3)     # This seed makes sure your "random" numbers will be the as ours    parameters = {}    L = len(layers_dims) # integer representing the number of layers    print("shuzhi is " + str(L))    for l in range(1, L):#range(1,L)={1,2,3...,L-1}        ### START CODE HERE ### (≈ 2 lines of code)    parameters['W'+str(l)]=np.random.randn(layers_dims[1],layers_dims[0])*10     if l==1 else np.random.randn(layers_dims[l],layers_dims[l-1])*10        parameters['b' + str(l)] = np.zeros((layers_dims[l], 1))        ### END CODE HERE ###return parameters

测试代码:

parameters= initialize_parameters_random([4, 2, 1])print("W1 = " + str(parameters["W1"]))print("b1 = " + str(parameters["b1"]))print("W2 = " + str(parameters["W2"]))print("b2 = " + str(parameters["b2"]))

做15000次迭代代码:

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)

Cost after iteration 0: inf
Cost after iteration 1000: 0.6237287551108738
Cost after iteration 2000: 0.5981106708339466
Cost after iteration 3000: 0.5638353726276827
Cost after iteration 4000: 0.550152614449184
Cost after iteration 5000: 0.5444235275228304
Cost after iteration 6000: 0.5374184054630083
Cost after iteration 7000: 0.47357131493578297
Cost after iteration 8000: 0.39775634899580387
Cost after iteration 9000: 0.3934632865981078
Cost after iteration 10000: 0.39202525076484457
Cost after iteration 11000: 0.38921493051297673
Cost after iteration 12000: 0.38614221789840486
Cost after iteration 13000: 0.38497849983013926
Cost after iteration 14000: 0.38278397192120406

3.jpg

On the train set:
Accuracy: 0.83
On the test set:
Accuracy: 0.86

如果您看到“inf”作为迭代0之后的成本,这是因为数值舍入,一个更复杂的实现可以解决这个问题。 但是这不值得为我们的目的而担心。

无论如何,看起来已经失去了对称性,这样会有更好的结果。相比以前来说,该模型不再输出全0。

观察:

     代价函数在开始的时候非常高。 这是因为对于大的随机值权重,最后一次激活(sigmoid)输出的结果非常接近于0或1,并且当这个例子错误时,这个例子会导致很高的损失。 事实上,当log(a [3])= log(0)log(a [3])=log(0)时,损失趋于无穷大。

     初始化不良会导致渐变/爆炸渐变,这也会减慢优化算法的速度。

     如果你长时间训练这个网络,你会看到更好的结果,但是用过大的随机数初始化会减慢优化速度。

总结:

  • 初始化权重矩阵的值非常大的结果并不好。

  • 较小随机值的理想随机表现的更好。

但是现在有一个关键问题:这些值多小才是真正的小呢?让我们在下一节来完成这个问题。

4 – He初始化

最后,尝试使用“HE初始化”。If you have heard of "Xavier initialization", this is similar except Xavier initialization uses a scaling factor for the weights W[l] of sqrt(1./layers_dims[l-1]) where He initialization would use sqrt(2./layers_dims[l-1]).)

Exercise: Implement the following function to initialize your parameters with He initialization.

Hint: This function is similar to the previous initialize_parameters_random(...). The only difference is that instead of multiplying np.random.randn(..,..) by 10, you will multiply it byof the previous layer, which is what He initialization recommends for layers with a ReLU activation.

# GRADED FUNCTION: initialize_parameters_he def initialize_parameters_he(layers_dims):    """    Arguments:    layer_dims -- python array (list) containing the size of each layer.        Returns:  parameters-  python dictionary containing your parameters "W1","b1",...,"WL","bL":   W1 -- weight matrix of shape (layers_dims[1], layers_dims[0])   b1 -- bias vector of shape (layers_dims[1], 1)                    ...   WL -- weight matrix of shape (layers_dims[L], layers_dims[L-1])   bL -- bias vector of shape (layers_dims[L], 1)    """        np.random.seed(3)    parameters = {}    L = len(layers_dims) - 1 # integer representing the number of layers         for l in range(1, L + 1):        ### START CODE HERE ### (≈ 2 lines of code)        parameters['W'+str(l)]=    np.random.randn(layers_dims[1],layers_dims[0])*np.sqrt(2./layers_dims[0])     if l == 1 else 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[1], 1))        ### END CODE HERE ###        return parameters

测试代码:

parameters = initialize_parameters_he([2, 4, 1])print("W1 = " + str(parameters["W1"]))print("b1 = " + str(parameters["b1"]))print("W2 = " + str(parameters["W2"]))print("b2 = " + str(parameters["b2"]))

5 – 结论

在相同迭代次数和相同超参数下,其结果如表所示:

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

结论:
(1)不同的初始化导致不同的结果
(2)随机初始化用于破坏对称性,并确保不同的隐藏单元可以学习不同的东西
(3)不要初始化太大的值
(4)HE初始化适用于ReLU激活的网络。