问题:

在deeplearning.ai的课程1『神经网络与深度学习』中的第4周的编程作业『Building your Deep Neural Network: Step by Step』中,为什么多层神经网络的权重初始化是
parameters["W" + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) / np.sqrt(layers_dims[l-1])? 即:W^{[l]} = np.random.randn(shape) * np.sqrt(\frac{1}{n^{[l-1]}} )。其中,W^{[l]}是第l层网络的权重,n^{[l-1]}是第l-1层的神经元数量。

为什么不是parameters["W" + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) * 0.01?

答案:

1、在课程2『改善深层神经网络:超参数调试、正则化以及优化』中第一周的第11讲『神经网络的权重初始化』中有详细解释:
2、只有2层及以下神经网络的权重初始化才是*0.01,而多层神经网络都要 /np.sqrt(layers_dims[l-1]),即要除以其前一层神经元数量的平方根。
2、原因:因为Z^{[l]} = \sum\limits_{i = 1}^{n^{[l-1]}} W^{[l](i)}A^{[l-1](i)},我们希望初始时Z^{[l]}要足够小,不能太大,否则会进入激活函数的饱和区使得梯度下降很慢,产生梯度消失。 但是如果第l-1层神经元数量n越大,那么W^{[l]}值必须设置越小,所以某层W值一定要同它的输入神经元数(即前一层神经元数)挂钩,因此才会有:W^{[l]} = np.random.randn(shape) * np.sqrt(\frac{1}{n^{[l-1]}} )

说白了就是除个根号下n[l-1]来平衡W值

总结:

1、Xavier初始化:如果激活函数是tanh函数,初始化时:也是要乘以np.sqrt(\frac{1}{n^{[l-1]}} ),这叫Xavier初始化。在一篇论文中提到出了这个。而bengio提出的初始化方法是乘以np.sqrt(\frac{2}{n^{[l-1]+n^{[l]}}} )
2、He初始化:如果激活函数是relu函数,初始化时:是要乘以np.sqrt(\frac{2}{n^{[l-1]}} ),因为它的方差是这个,这种初始化叫He初始化。

 

 

 

 

正则化的时候

 

 所以在L2的正则化成本计算中

cross_entropy_cost = reg_utils.compute_cost(A3,Y)
L2_regularization_cost = lambd * (np.sum(np.square(W1)) + np.sum(np.square(W2)) + np.sum(np.square(W3))) / (2 * m)
cost = cross_entropy_cost + L2_regularization_cost

 

因为J(w,b)=原始+lambda/2m ||w||^2

所以求导后,dw[l]项因为正则化影响,变成 原式+lambda/W(l)

dW3 = (1 / m) * np.dot(dZ3,A2.T) + ((lambd * W3) / m )

 

梯度检验

 

 

 

 

 

 

 1 def gradient_check(x,theta,epsilon=1e-7):
 2     """
 3     
 4     实现图中的反向传播。
 5     
 6     参数:
 7         x  - 一个实值输入
 8         theta  - 参数,也是一个实数
 9         epsilon  - 使用公式(3)计算输入的微小偏移以计算近似梯度
10     
11     返回:
12         近似梯度和后向传播梯度之间的差异
13     """
14     
15     #使用公式(3)的左侧计算gradapprox。
16     thetaplus = theta + epsilon                               # Step 1
17     thetaminus = theta - epsilon                              # Step 2
18     J_plus = forward_propagation(x, thetaplus)                # Step 3
19     J_minus = forward_propagation(x, thetaminus)              # Step 4
20     gradapprox = (J_plus - J_minus) / (2 * epsilon)           # Step 5
21     
22     
23     #检查gradapprox是否足够接近backward_propagation()的输出
24     grad = backward_propagation(x, theta)
25     
26     numerator = np.linalg.norm(grad - gradapprox)                      # Step 1'
27     denominator = np.linalg.norm(grad) + np.linalg.norm(gradapprox)    # Step 2'
28     difference = numerator / denominator                               # Step 3'
29     
30     if difference < 1e-7:
31         print("梯度检查:梯度正常!")
32     else:
33         print("梯度检查:梯度超出阈值!")
34     
35     return difference

解释一下np.linalg.norm()

1、linalg=linear(线性)+algebra(代数),norm则表示范数。

2、函数参数

x_norm=np.linalg.norm(x, ord=None, axis=None, keepdims=False)
①x: 表示矩阵(也可以是一维)

②ord:范数类型

 

 多维:

 

 

def forward_propagation_n(X,Y,parameters):
    """
    实现图中的前向传播(并计算成本)。
    
    参数:
        X - 训练集为m个例子
        Y -  m个示例的标签
        parameters - 包含参数“W1”,“b1”,“W2”,“b2”,“W3”,“b3”的python字典:
            W1  - 权重矩阵,维度为(5,4)
            b1  - 偏向量,维度为(5,1)
            W2  - 权重矩阵,维度为(3,5)
            b2  - 偏向量,维度为(3,1)
            W3  - 权重矩阵,维度为(1,3)
            b3  - 偏向量,维度为(1,1)
   
    返回:
        cost - 成本函数(logistic)
    """
    m = X.shape[1]
    W1 = parameters["W1"]
    b1 = parameters["b1"]
    W2 = parameters["W2"]
    b2 = parameters["b2"]
    W3 = parameters["W3"]
    b3 = parameters["b3"]
    
    # LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID
    Z1 = np.dot(W1,X) + b1
    A1 = gc_utils.relu(Z1)
    
    Z2 = np.dot(W2,A1) + b2
    A2 = gc_utils.relu(Z2)
    
    Z3 = np.dot(W3,A2) + b3
    A3 = gc_utils.sigmoid(Z3)
    
    #计算成本
    logprobs = np.multiply(-np.log(A3), Y) + np.multiply(-np.log(1 - A3), 1 - Y)
    cost = (1 / m) * np.sum(logprobs)
    
    cache = (Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3)

    return cost, cache

def backward_propagation_n(X,Y,cache):
    """
    实现图中所示的反向传播。
    
    参数:
        X - 输入数据点(输入节点数量,1)
        Y - 标签
        cache - 来自forward_propagation_n()的cache输出
    
    返回:
        gradients - 一个字典,其中包含与每个参数、激活和激活前变量相关的成本梯度。
    """
    m = X.shape[1]
    (Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3) = cache
    
    dZ3 = A3 - Y
    dW3 = (1. / m) * np.dot(dZ3,A2.T)
    dW3 = 1. / m * np.dot(dZ3, A2.T)
    db3 = 1. / m * np.sum(dZ3, axis=1, keepdims=True)
    
    dA2 = np.dot(W3.T, dZ3)
    dZ2 = np.multiply(dA2, np.int64(A2 > 0))
    #dW2 = 1. / m * np.dot(dZ2, A1.T) * 2  # Should not multiply by 2
    dW2 = 1. / m * np.dot(dZ2, A1.T)
    db2 = 1. / m * np.sum(dZ2, axis=1, keepdims=True)
    
    dA1 = np.dot(W2.T, dZ2)
    dZ1 = np.multiply(dA1, np.int64(A1 > 0))
    dW1 = 1. / m * np.dot(dZ1, X.T)
    #db1 = 4. / m * np.sum(dZ1, axis=1, keepdims=True) # Should not multiply by 4
    db1 = 1. / m * np.sum(dZ1, axis=1, keepdims=True)
    
    gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3,
                 "dA2": dA2, "dZ2": dZ2, "dW2": dW2, "db2": db2,
                 "dA1": dA1, "dZ1": dZ1, "dW1": dW1, "db1": db1}
 
    return gradients

如果想比较“gradapprox”与反向传播计算的梯度

θ\thetaθ不再是标量。 这是一个名为“parameters”的字典。 我们为你实现了一个函数“dictionary_to_vector()”。 它将“parameters”字典转换为一个称为“values”的向量,通过将所有参数(W1,b1,W2,b2,W3,b3)整形为向量并将它们连接起来而获得。

反函数是“vector_to_dictionary”,它返回“parameters”字典。

 

 

 

 

def gradient_check_n(parameters,gradients,X,Y,epsilon=1e-7):
    """
    检查backward_propagation_n是否正确计算forward_propagation_n输出的成本梯度
    
    参数:
        parameters - 包含参数“W1”,“b1”,“W2”,“b2”,“W3”,“b3”的python字典:
        grad_output_propagation_n的输出包含与参数相关的成本梯度。
        x  - 输入数据点,维度为(输入节点数量,1)
        y  - 标签
        epsilon  - 计算输入的微小偏移以计算近似梯度
    
    返回:
        difference - 近似梯度和后向传播梯度之间的差异
    """
    #初始化参数
    parameters_values , keys = gc_utils.dictionary_to_vector(parameters) #keys用不到,把parameters参数转成向量传给p_v
    grad = gc_utils.gradients_to_vector(gradients) #从梯度字典变成一个单一的向量
    num_parameters = parameters_values.shape[0]
    J_plus = np.zeros((num_parameters,1))
    J_minus = np.zeros((num_parameters,1))
    gradapprox = np.zeros((num_parameters,1))
    
    #计算gradapprox
    for i in range(num_parameters):
        #计算J_plus [i]。输入:“parameters_values,epsilon”。输出=“J_plus [i]”
        thetaplus = np.copy(parameters_values)                                                  # Step 1
        thetaplus[i][0] = thetaplus[i][0] + epsilon                                             # Step 2
        J_plus[i], cache = forward_propagation_n(X,Y,gc_utils.vector_to_dictionary(thetaplus))  # Step 3 ,cache用不到
        
        #计算J_minus [i]。输入:“parameters_values,epsilon”。输出=“J_minus [i]”。
        thetaminus = np.copy(parameters_values)                                                 # Step 1
        thetaminus[i][0] = thetaminus[i][0] - epsilon                                           # Step 2        
        J_minus[i], cache = forward_propagation_n(X,Y,gc_utils.vector_to_dictionary(thetaminus))# Step 3 ,cache用不到
        
        #计算gradapprox[i]
        gradapprox[i] = (J_plus[i] - J_minus[i]) / (2 * epsilon)
        
    #通过计算差异比较gradapprox和后向传播梯度。
    numerator = np.linalg.norm(grad - gradapprox)                                     # Step 1'
    denominator = np.linalg.norm(grad) + np.linalg.norm(gradapprox)                   # Step 2'
    difference = numerator / denominator                                              # Step 3'
    
    if difference < 1e-7:
        print("梯度检查:梯度正常!")
    else:
        print("梯度检查:梯度超出阈值!")
    
    return difference

 

posted on 2020-03-09 18:29  路途陌客  阅读(887)  评论(0)    收藏  举报