深入理解吴恩达深度学习(02 改善深层神经网络:超参数调试、正则化以及优化 第一周深度学习的实践层面

0 本次课程重点内容

  1. L2正则化和理解为什么正则化起作用
  2. dropout正则化
  3. 梯度检验

1.1 训练,验证,测试集(Train / Dev / Test sets)

机器学习中再训练模型的时候,我们可以按照训练数据样本的多少来划分训练集,测试集,验证集。

  1. 训练集:顾名思义,用来训练模型的数据集。
  2. 验证集:用来测试训练集训练的模型的结果是否在测试数据中也表现优秀,可以理解为开发过程中的测试数据。
  3. 测试集:用来测试训练结果和实际情况中是否吻合,可以理解为抽取生产数据对模型进行测试。

划分方式:
按照所有数据的多少,我们训练,验证,测试集的分配比例不同,假如你拥有1W条数据,建议的划分比例是6:3:1,或者7:2:1,假如你有1000W条数据,建议的划分比例是99:0.8:0.2。

总之, 根据所有数量不同,我们的训练,验证,测试集分配比例也有所不同。

总的原则是:假如数据少(1W-10W级),测试和验证集分配比例就适当增大。假如数据量大,测试和验证集分配比例就适当减小。

1.2 偏差,方差(Bias /Variance)

偏差:描述的是预测值(估计值)的期望与真实值之间的差距。偏差越大,越偏离真实数据,如下图第二行所示。

方差:描述的是预测值的变化范围,离散程度,也就是离其期望值的距离。方差越大,数据的分布越分散,如下图右列所示。
在这里插入图片描述

偏差和方差,与拟合的关系:
偏差高:说明模型对测试数据欠拟合,此时模型的准确率不足,比如这样划分数据:
在这里插入图片描述
偏差低:说明模型对测试数据拟合程度好,准确率高,但是此时要注意过拟合的情况。
例如,中间这张图和右边的情况都是低偏差情况,而且右边图的偏差小于左边图的偏差,理论上来说右边的拟合训练数据情况要好于中间的图片,但是右边的数据过拟合了。
在这里插入图片描述
方差低:说明预测数据聚集程度高,大家都挤在一起,此时模型处于拟合程度恰好的情况。
方差高:说明预测数据聚集程度低,正确数据比较分散,此时模型处于过拟合阶段。
还是例如上图,中间的正确数据集比右边的聚集,因此中间图片的方差低,此时拟合程度好于右边图片的情况。

具体总结如下:

类型偏差低偏差高
方差低恰好合适欠拟合
方差高过拟合欠拟合

1.3 机器学习基础(Basic Recipe for Machine Learning)

上节已经讲了偏差和方差的影响,但是实际情况中很难有只降低偏差又不提升方差的方法,因此低偏差和低方差的寻找过程是一个不断尝试和妥协的过程。但是这里有一个原则要讲清楚:

  1. 高偏差 -->模型学习能力不足,或者训练数据不够–>增加模型学习能力(深度加深,神经元增加等)
  2. 高方差 -->模型学习能力过强,或者训练数据不够,数据噪声大–>降低模型学习能力(减小W权重,神经元减少)

其中数据不够,数据噪声大的问题,我们通过特征工程来解决,这里不再讨论。所以上述推理只从改变模型的角度来讲讲怎么优化我们的模型。

1.4 正则化(Regularization)

深度学习可能存在过拟合问题——高方差,有两个解决方法,一个是正则化,另一个是准备更多的数据,这是非常可靠的方法,但你可能无法时时刻刻准备足够多的训练数据或者获取更多数据的成本很高,但正则化通常有助于避免过拟合或减少你的网络误差。

正则化可以解决过拟合的问题,那么正则化到底是什么呢?

正则化其实就是在损失函数J后边加上一项恒为正的数,这个数的作用大小可调整,这个数也叫正则化项。
如果我们规定J<0.01结束训练,那么整个过程是这样的:

原来: 训练模型---->J<0.01---->训练结束

加正则化之后: 训练模型---->J + 正则化项 < 0.01 ---->训练结束

那么训练J的时候,因为我们一般训练时候会设置当J<设定值时候停止训练,我们相当于将J调大了,因此模型就会相对之前来说J比较大,也就减小了过拟合的可能性。

我们添加的正则化项分为L1正则化项和L2正则化项,下面我们分开来讨论一下:

1.4.1 L1正则化(Regularization)

L 1 ( w ) = J ( w ) + λ m ∑ l = 1 L ∣ w [ l ] ∣ L_1(w) = J(w)+\frac{\lambda}{m}\sum_{l= 1}^{L}{|w^{[l]}|} L1(w)=J(w)+mλl=1Lw[l]
参数说明:

  1. λ \lambda λ 是一个超参数,用来调整正则化项的大小。
  2. m 表示输入训练数据的样本数。
  3. L 表示神经网络层数。
  4. ∣ w [ l ] ∣ |w^{[l]}| w[l] 是L1正则化的精髓,表示w矩阵中每个元素都取绝对值。因此也就能保证了整个正则化项都恒为正。

为了保证我们的正则化项不能太大,因此我们对m个样本的w求和之后要乘以 1 m \frac{1}{m} m1,也就是用均值来计算正则化项。

为了我们能够方便的调整正则化项的大小,因此添加了可调参数(超参数) λ \lambda λ,来方便的调整正则化项数值。

实际过程中我们比较少使用L1正则化,虽然L1正则化相对L2计算简单,但是L1正则化项----->绝对值项是非连续函数,因此它是不可导函数,在梯度下降过程中,导数不太好求出来。

1.4.2 L2正则化(Regularization)

L 2 ( w ) = J ( w ) + λ 2 m ∑ l = 1 L ∣ w [ l ] ∣ 2 L_2(w) = J(w)+\frac{\lambda}{2m}{\sum_{l= 1}^{L}{|w^{[l]}|}^2} L2(w)=J(w)+2mλl=1Lw[l]2
参数说明:
1. ∣ w [ l ] ∣ 2 {|w^{[l]}|}^2 w[l]2 是L2正则化的精髓,表示对w矩阵中的每个元素求平方。
2.注意L2正则化的参数是除以2m,添加了 1 2 \frac{1}{2} 21是为了求导方便。
3.L2正则化的导数:
d L 2 ( w ) = d J ( w ) + λ m ∑ l = 1 L w [ l ] dL_2(w) = dJ(w)+\frac{\lambda}{m}{\sum_{l= 1}^{L}{w^{[l]}}} dL2(w)=dJ(w)+mλl=1Lw[l]

实际使用过程中,我们一般使用L2正则化,因为L2正则化项—>平方函数 是可导的凸函数,方便之后使用梯度下降法。
代码:L2正则化成本函数

# GRADED FUNCTION: compute_cost_with_regularization

def compute_cost_with_regularization(A3, Y, parameters, lambd):
    """
    Implement the cost function with L2 regularization. See formula (2) above.
    
    Arguments:
    A3 -- post-activation, output of forward propagation, of shape (output size, number of examples)
    Y -- "true" labels vector, of shape (output size, number of examples)
    parameters -- python dictionary containing parameters of the model
    
    Returns:
    cost - value of the regularized loss function (formula (2))
    """
    m = Y.shape[1]
    W1 = parameters["W1"]
    W2 = parameters["W2"]
    W3 = parameters["W3"]
    
    cross_entropy_cost = compute_cost(A3, Y) # This gives you the cross-entropy part of the cost
    
    ### START CODE HERE ### (approx. 1 line)
    """
    计算L2正则化的成本函数
	"""
    L2_regularization_cost = lambd/(m*2)*(np.sum(np.multiply(W1,W1))+np.sum(np.multiply(W2,W2))+np.sum(np.multiply(W3,W3)))
    ### END CODER HERE ###
    
    cost = cross_entropy_cost + L2_regularization_cost
    
    return cost

代码:L2正则化成本函数的导数
d L 2 ( w ) = d J ( w ) + λ m ∑ l = 1 L w [ l ] dL_2(w) = dJ(w)+\frac{\lambda}{m}{\sum_{l= 1}^{L}{w^{[l]}}} dL2(w)=dJ(w)+mλl=1Lw[l]


def backward_propagation_with_regularization(X, Y, cache, lambd):
    """
    Implements the backward propagation of our baseline model to which we added an L2 regularization.
    
    Arguments:
    X -- input dataset, of shape (input size, number of examples)
    Y -- "true" labels vector, of shape (output size, number of examples)
    cache -- cache output from forward_propagation()
    lambd -- regularization hyperparameter, scalar
    
    Returns:
    gradients -- A dictionary with the gradients with respect to each parameter, activation and pre-activation variables
    """
    
    m = X.shape[1]
    (Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3) = cache
    
    dZ3 = A3 - Y
    
    ### START CODE HERE ### (approx. 1 line)
    dW3 = 1./m * np.dot(dZ3, A2.T) + lambd/m * W3
    ### END CODE HERE ###
    db3 = 1./m * np.sum(dZ3, axis=1, keepdims = True)
    
    dA2 = np.dot(W3.T, dZ3)
    dZ2 = np.multiply(dA2, np.int64(A2 > 0))
    ### START CODE HERE ### (approx. 1 line)
    dW2 = 1./m * np.dot(dZ2, A1.T) + lambd/m * W2
    ### END CODE HERE ###
    db2 = 1./m * np.sum(dZ2, axis=1, keepdims = True)
    
    dA1 = np.dot(W2.T, dZ2)
    dZ1 = np.multiply(dA1, np.int64(A1 > 0))
    ### START CODE HERE ### (approx. 1 line)
    dW1 = 1./m * np.dot(dZ1, X.T) + lambd/m * W1
    ### END CODE HERE ###
    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
1.4.2 正则化(Regularization)的补充说明
  1. 为什么只使用W作为正则化参数,而不是使用b呢?
    实际上你可以这么做,只是我们习惯省略不写,因为 w w w通常是一个高维参数矢量,已经可以表达高偏差问题, w w w可能包含有很多参数,我们不可能拟合所有参数,而 b b b只是单个数字,所以 w w w几乎涵盖所有参数,而不是 b b b,如果加了参数 b b b,其实也没太大影响,因为 b b b只是众多参数中的一个,所以我通常省略不计,如果你想加上这个参数,完全没问题。

  2. 正则化到底在干什么?
    以L2正则化为例子,正则化实际上是使L2函数代替原来的J代价函数。考虑J=0.01和L2=0.01的情况,由于L2函数长这个样子:
    L 2 ( w ) = J ( w ) + λ 2 m ∑ l = 1 L ∣ w [ l ] ∣ 2 L_2(w) = J(w)+\frac{\lambda}{2m}{\sum_{l= 1}^{L}{|w^{[l]}|}^2} L2(w)=J(w)+2mλl=1Lw[l]2

当大家都等于0.01时候,因为L2正则化添加了一个正则化项---->所以L2中的J会比不添加正则化的J小---->J越小说明偏差越小---->模型训练效果更好

当大家都等于0.01时候,因为L2正则化添加了一个正则化项---->该正则化大小项由W大小决定---->所以L2正则化中的W会比不使用正则化时候小---->W越小说明对输出预测值的影响越小----->输出结果就会越集中---->说明模型方差减小—>得到低方差模型—>模型训练能够减小过拟合度。

从上述推理得出,正则化实现了减小模型偏差的同时,又能减小方差的效果,因此正则化有效。

1.5 为什么正则化有利于预防过拟合呢?(Why regularization reduces overfitting?)

为什么正则化有利于预防过拟合呢?为什么它可以减少方差问题?我们通过两个例子来直观体会一下。

在这里插入图片描述
在这里插入图片描述

左图是高偏差,右图是高方差,中间是Just Right,这几张图我们在前面课程中看到过。

在这里插入图片描述

现在我们来看下这个庞大的深度拟合神经网络。我知道这张图不够大,深度也不够,但你可以想象这是一个过拟合的神经网络。这是我们的代价函数 J J J,含有参数 W W W b b b。我们添加正则项,它可以避免数据权值矩阵过大,这就是弗罗贝尼乌斯范数,为什么压缩 L 2 L2 L2范数,或者弗罗贝尼乌斯范数或者参数可以减少过拟合?

回顾一下L2正则化函数:

L 2 ( w ) = J ( w ) + λ 2 m ∑ l = 1 L ∣ w [ l ] ∣ 2 L_2(w) = J(w)+\frac{\lambda}{2m}\sum_{l= 1}^{L}{|w^{[l]}|}^2 L2(w)=J(w)+2mλl=1Lw[l]2

还有我们的目标–>解决过拟合---->目标是减小模型的学习能力

1.5.1正则化有利于预防过拟合的第一个直观理解

直观上理解就是:

  1. 如果正则化 λ \lambda λ设置得足够大
  2. 权重矩阵 W W W被设置为接近于0的值
  3. 直观理解就是把多隐藏单元的权重设为0,于是基本上消除了这些隐藏单元的许多影响
  4. 也就是减小了模型学习能力
  5. 如果减小过度,这个被大大简化了的神经网络会变成一个很小的网络,小到如同一个逻辑回归单元,可是深度却很大,它会使这个网络从过度拟合的状态更接近左图的高偏差状态。
  6. 但是 λ \lambda λ会存在一个中间值,于是会有一个接近“Just Right”的中间状态。

直观理解就是 λ \lambda λ增加到足够大, W W W会接近于0,实际上是不会发生这种情况的,我们尝试消除或至少减少许多隐藏单元的影响,最终这个网络会变得更简单,这个神经网络越来越接近逻辑回归,我们直觉上认为大量隐藏单元被完全消除了,其实不然,实际上是该神经网络的所有隐藏单元依然存在,但是它们的影响变得更小了。神经网络变得更简单了,貌似这样更不容易发生过拟合,因此我不确定这个直觉经验是否有用,不过在编程中执行正则化时,你实际看到一些方差减少的结果。

在这里插入图片描述

1.5.2 正则化有利于预防过拟合的第二个直观理解

我们再来直观感受一下,正则化为什么可以预防过拟合,假设我们用的是这样的双曲线激活函数。

在这里插入图片描述
回顾一下前向传播公式: A [ L ] = g [ L ] ( w [ L ] A [ L − 1 ] + b [ L ] ) A^{[L]}=g^{[L]}(w^{[L]}A^{[L-1]}+b^{[L]}) A[L]=g[L](w[L]A[L1]+b[L])
g ( z ) g(z) g(z)表示 t a n h ( z ) tanh(z) tanh(z)

  1. 前边已经说过,添加L2正则化会让 w [ L ] w^{[L]} w[L]减小。
  2. w [ L ] w^{[L]} w[L]减小,在其他值不变的情况下, z = w [ L ] A [ L − 1 ] + b [ L ] z=w^{[L]}A^{[L-1]}+b^{[L]} z=w[L]A[L1]+b[L]也就减小。
  3. 从下图可知,当 z z z减小,激活函数 t a n h ( z ) tanh(z) tanh(z)就会接近线性函数。
  4. 深层神经网络当激活函数为线性函数时候,输出相当于输入的线性组合,也就是说可以理解为此时隐藏层失效,相当于没有隐藏层。
  5. 由此可知模型学习能力下降。
  6. 由此可知可以解决过拟合。

在这里插入图片描述

1.6 dropout 正则化(Dropout Regularization)

除了 L 2 L2 L2正则化,还有一个非常实用的正则化方法——“Dropout(随机失活)”,虽然这里翻译成为随机失活,不过Dropout的字面意思是丢弃,什么?丢弃神经元?楼主能够脑洞再大点嘛?

我们之前说过,不管你使用什么方法,解决过拟合的目标是降低模型的学习能力,那么减少每层神经元个数(随机丢弃,随机失活),也就能能够将原来的复杂的神经网络简单化,因此就能够降低模型学习能力啦!

然鹅,丢神经元也是一个技术活,丢多少合适,怎么丢,都是一个比较复杂的问题,下面我们来说说看Dropout的具体实现方法:

  1. 设置每层保留的神经元个数概率数组。
    例如我有三层神经元,数组是[0.5,0.6,0.7],分别表示第一层保留50%的神经元,第二层保留60%神经元,第三层保留70%神经元。
  2. 在前向传播时候,按照上述概率,逐层将神经元的输出设置为0(神经元失活),并且保存好相关参数。
  3. 在反向传播的时候,按照保存的参数,对相应位置神经元做失活处理,逐层更新w和b。
  4. 下一个样本到来时候,重新计算2-4步骤。

代码:dropout正则化前向传播

# GRADED FUNCTION: forward_propagation_with_dropout

def forward_propagation_with_dropout(X, parameters, keep_prob = 0.5):
    """
    Implements the forward propagation: LINEAR -> RELU + DROPOUT -> LINEAR -> RELU + DROPOUT -> LINEAR -> SIGMOID.
    
    Arguments:
    X -- input dataset, of shape (2, number of examples)
    parameters -- python dictionary containing your parameters "W1", "b1", "W2", "b2", "W3", "b3":
                    W1 -- weight matrix of shape (20, 2)
                    b1 -- bias vector of shape (20, 1)
                    W2 -- weight matrix of shape (3, 20)
                    b2 -- bias vector of shape (3, 1)
                    W3 -- weight matrix of shape (1, 3)
                    b3 -- bias vector of shape (1, 1)
    keep_prob - probability of keeping a neuron active during drop-out, scalar
    
    Returns:
    A3 -- last activation value, output of the forward propagation, of shape (1,1)
    cache -- tuple, information stored for computing the backward propagation
    """
    
    np.random.seed(1)
    
    # retrieve parameters
    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 = relu(Z1)
    ### START CODE HERE ### (approx. 4 lines)        # Steps 1-4 below correspond to the Steps 1-4 described above. 
    """
    计算0到1的随机数,维度是(n(l-1),n(l))
    """
    D1 = np.random.rand(Z1.shape[0],Z1.shape[1])     # Step 1: initialize matrix D1 = np.random.rand(..., ...)
    """
    随机值>keep_prob设置为1
    """
    D1[(D1>keep_prob)] = 1
    """
    随机值<=keep_prob设置为0
    """
    D1[(D1<=keep_prob)] = 0                           # Step 2: convert entries of D1 to 0 or 1 (using keep_prob as the threshold)
    """
    矩阵对应位置相乘,1保留该神经元输出结果,0删除神经元输出结果(随机失活)
    """
    A1 = np.multiply(D1,A1)                           # Step 3: shut down some neurons of A1
    """
    为了删除神经元之后,保证输出均值不变,因此需要除以keep_prob
    """
    A1 = A1/keep_prob                                 # Step 4: scale the value of neurons that haven't been shut down
    ### END CODE HERE ###
    Z2 = np.dot(W2, A1) + b2
    A2 = relu(Z2)
    ### START CODE HERE ### (approx. 4 lines)
    D2 = np.random.rand(Z2.shape[0],Z2.shape[1])      # Step 1: initialize matrix D2 = np.random.rand(..., ...)
    D2[(D2>keep_prob)] = 1
    D2[(D2<=keep_prob)] = 0                           # Step 2: convert entries of D2 to 0 or 1 (using keep_prob as the threshold)
    A2 = np.multiply(D2,A2)                                     # Step 3: shut down some neurons of A2
    A2 = A2/keep_prob                                 # Step 4: scale the value of neurons that haven't been shut down
    ### END CODE HERE ###
    Z3 = np.dot(W3, A2) + b3
    A3 = sigmoid(Z3)
    
    cache = (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3)
    
    return A3, cache

代码:dropout正则化反向传播

# GRADED FUNCTION: backward_propagation_with_dropout

def backward_propagation_with_dropout(X, Y, cache, keep_prob):
    """
    Implements the backward propagation of our baseline model to which we added dropout.
    
    Arguments:
    X -- input dataset, of shape (2, number of examples)
    Y -- "true" labels vector, of shape (output size, number of examples)
    cache -- cache output from forward_propagation_with_dropout()
    keep_prob - probability of keeping a neuron active during drop-out, scalar
    
    Returns:
    gradients -- A dictionary with the gradients with respect to each parameter, activation and pre-activation variables
    """
    
    m = X.shape[1]
    (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3) = cache
    
    dZ3 = A3 - Y
    dW3 = 1./m * np.dot(dZ3, A2.T)
    db3 = 1./m * np.sum(dZ3, axis=1, keepdims = True)
    dA2 = np.dot(W3.T, dZ3)
    ### START CODE HERE ### (≈ 2 lines of code)
    """
    前向传播时候,对应位置的神经元失活了,因此反向传播时候求导数也要对相应位置神经元失活。
    """
    dA2 = np.multiply(dA2,D2)       # Step 1: Apply mask D2 to shut down the same neurons as during the forward propagation
    """
    为了删除神经元之后,保证输出均值不变,因此需要除以keep_prob
    """
    dA2 = dA2/keep_prob              # Step 2: Scale the value of neurons that haven't been shut down
    ### END CODE HERE ###
    dZ2 = np.multiply(dA2, np.int64(A2 > 0))
    dW2 = 1./m * np.dot(dZ2, A1.T)
    db2 = 1./m * np.sum(dZ2, axis=1, keepdims = True)
    
    dA1 = np.dot(W2.T, dZ2)
    ### START CODE HERE ### (≈ 2 lines of code)
    dA1 = np.multiply(dA1,D1)           # Step 1: Apply mask D1 to shut down the same neurons as during the forward propagation
    dA1 = dA1/keep_prob              # Step 2: Scale the value of neurons that haven't been shut down
    ### END CODE HERE ###
    dZ1 = np.multiply(dA1, np.int64(A1 > 0))
    dW1 = 1./m * np.dot(dZ1, X.T)
    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

1.7 理解 dropout(Understanding Dropout)

Dropout可以随机删除网络中的神经单元,他为什么可以通过正则化发挥如此大的作用呢?

直观上理解:不要依赖于任何一个特征,因为该单元的输入可能随时被清除,因此该单元通过这种方式传播下去,并为单元的四个输入增加一点权重,通过传播所有权重,dropout将产生收缩权重的平方范数的效果,和之前讲的 L 2 L2 L2正则化类似;实施dropout的结果实它会压缩权重,并完成一些预防过拟合的外层正则化; L 2 L2 L2对不同权重的衰减是不同的,它取决于激活函数倍增的大小。

总结一下,dropout的功能类似于 L 2 L2 L2正则化,与 L 2 L2 L2正则化不同的是应用方式不同会带来一点点小变化,甚至更适用于不同的输入范围。
在这里插入图片描述

第二个直观认识是,我们从单个神经元入手,如图,这个单元的工作就是输入并生成一些有意义的输出。通过dropout,该单元的输入几乎被消除,有时这两个单元会被删除,有时会删除其它单元,就是说,我用紫色圈起来的这个单元,它不能依靠任何特征,因为特征都有可能被随机清除,或者说该单元的输入也都可能被随机清除。我不愿意把所有赌注都放在一个节点上,不愿意给任何一个输入加上太多权重,因为它可能会被删除,因此该单元将通过这种方式积极地传播开,并为单元的四个输入增加一点权重,通过传播所有权重dropout将产生收缩权重的平方范数的效果,和我们之前讲过的 L 2 L2 L2正则化类似,实施dropout的结果是它会压缩权重,并完成一些预防过拟合的外层正则化。、

dropout一大缺点就是代价函数 J J J不再被明确定义,每次迭代,都会随机移除一些节点,如果再三检查梯度下降的性能,实际上是很难进行复查的。定义明确的代价函数 J J J每次迭代后都会下降,因为我们所优化的代价函数 J J J实际上并没有明确定义,或者说在某种程度上很难计算,所以我们失去了调试工具来绘制这样的图片。我通常会关闭dropout函数,将keep-prob的值设为1,运行代码,确保J函数单调递减。然后打开dropout函数,希望在dropout过程中,代码并未引入bug。我觉得你也可以尝试其它方法,虽然我们并没有关于这些方法性能的数据统计,但你可以把它们与dropout方法一起使用。

总结一下:

  1. dropout 正则化通过调整每层的神经元保留概率数组 keepArray来实现超参数调整。
  2. 我们的目标永远是减小模型的学习能力,因此在过拟合情况下,如果某些层L的W值比较大,请适当将这一层的keep概率调小一点,让本层学习能力下降。
  3. dropout我们是每次迭代都随机删除各层节点,本次迭代的导数dw,db可以求出来,因此可以实现梯度下降
  4. dropout我们是每次迭代都随机删除各层节点,n次迭代中的导数dw,db都是不同的,因此很难实现梯度校验。
  5. 和L2正则化类似,dropout解决过拟合的手段就是,通过随机失活,减小各层w的值。

1.8 其他正则化方法(Other regularization methods)

除了 L 2 L2 L2正则化和随机失活(dropout)正则化,还有几种方法可以减少神经网络中的过拟合:
在这里插入图片描述
一.数据扩增

说白了就是通过各种手段添加训练数据量,训练数据多了,自然准确率就高了,就跟 岛国大片看多了,自然也就心中无码了 —啊哈,貌似暴露了什么。

数据扩增的手段有:

  1. 直接增加数据(还用你说呀,摔)。
  2. 数据变形,例如图片旋转一定度数,图片扭曲。
  3. 添加噪声,例如人为的在图片上打码(岛国片跟你有仇呀,摔),添加雾霾,添加椒盐噪声,对音频信号添加背景噪声等等。

二.early stopping

还有另外一种常用的方法叫作early stopping,early stopping 貌似很高端的样子,其实就是提前停止训练,早点收工下班……具体样例操作就是将J<0.00001停止训练,调整为J<0.00002时候停止训练。

提前停止训练,也就是说不让模型再学那么多东西了,否则把拟合数据也学进去了。貌似很有道理,但是,提前多少很难操作,提前早了,容易产生欠拟合(岛国片看不够,达不到心中无码)。晚了,又会导致过拟合(看多了又会在脑海里YY,容易犯罪)。因此这个适度,很难把握,一般不太实用。因此它不是重点。

1.9 归一化输入(Normalizing inputs)

训练神经网络,其中一个加速训练的方法就是归一化输入。假设一个训练集有两个特征,输入特征为2维,归一化需要两个步骤:

  1. 零均值

  2. 归一化方差;

    我们希望无论是训练集和测试集都是通过相同的 μ μ μ σ 2 σ^2 σ2定义的数据转换,这两个是由训练集得出来的。

在这里插入图片描述
第一步是零均值化, μ = 1 m ∑ i = 1 m x ( i ) \mu = \frac{1}{m}\sum_{i =1}^{m}x^{(i)} μ=m1i=1mx(i),它是一个向量, x x x等于每个训练数据 x x x减去 μ \mu μ,意思是移动训练集,直到它完成零均值化。

在这里插入图片描述

第二步是归一化方差,注意特征 x 1 x_{1} x1的方差比特征 x 2 x_{2} x2的方差要大得多,我们要做的是给 σ \sigma σ赋值, σ 2 = 1 m ∑ i = 1 m ( x ( i ) ) 2 \sigma^{2}= \frac{1}{m}\sum_{i =1}^{m}{({x^{(i)})}^{2}} σ2=m1i=1m(x(i))2,这是节点 y y y 的平方, σ 2 \sigma^{2} σ2是一个向量,它的每个特征都有方差,注意,我们已经完成零值均化, ( x ( i ) ) 2 ({x^{(i)})}^{2} (x(i))2元素 y 2 y^{2} y2就是方差,我们把所有数据除以向量 σ 2 \sigma^{2} σ2,最后变成上图形式。

x 1 x_{1} x1 x 2 x_{2} x2的方差都等于1。提示一下,如果你用它来调整训练数据,那么用相同的 μ μ μ σ 2 \sigma^{2} σ2来归一化测试集。尤其是,你不希望训练集和测试集的归一化有所不同,不论 μ μ μ的值是什么,也不论 σ 2 \sigma^{2} σ2的值是什么,这两个公式中都会用到它们。所以你要用同样的方法调整测试集,而不是在训练集和测试集上分别预估 μ μ μ σ 2 \sigma^{2} σ2。因为我们希望不论是训练数据还是测试数据,都是通过相同μ和 σ 2 \sigma^{2} σ2定义的相同数据转换,其中 μ μ μ σ 2 \sigma^{2} σ2是由训练集数据计算得来的。

我们为什么要这么做呢?为什么我们想要归一化输入特征,回想一下右上角所定义的代价函数。

J ( w , b ) = 1 m ∑ i = 1 m L ( y ^ ( i ) , y ( i ) ) J(w,b)=\frac{1}{m}\sum\limits_{i=1}^{m}{L({{{\hat{y}}}^{(i)}},{{y}^{(i)}})} J(w,b)=m1i=1mL(y^(i),y(i))

如果你使用非归一化的输入特征,代价函数会像这样:

在这里插入图片描述

这是一个非常细长狭窄的代价函数,你要找的最小值应该在这里。但如果特征值在不同范围,假如 x 1 x_{1} x1取值范围从1到1000,特征 x 2 x_{2} x2的取值范围从0到1,结果是参数 w 1 w_{1} w1 w 2 w_{2} w2值的范围或比率将会非常不同,这些数据轴应该是 w 1 w_{1} w1 w 2 w_{2} w2,但直观理解,我标记为 w w w b b b,代价函数就有点像狭长的碗一样,如果你能画出该函数的部分轮廓,它会是这样一个狭长的函数。

然而如果你归一化特征,代价函数平均起来看更对称,如果你在上图这样的代价函数上运行梯度下降法,你必须使用一个非常小的学习率。因为如果是在这个位置,梯度下降法可能需要多次迭代过程,直到最后找到最小值。但如果函数是一个更圆的球形轮廓,那么不论从哪个位置开始,梯度下降法都能够更直接地找到最小值,你可以在梯度下降法中使用较大步长,而不需要像在左图中那样反复执行。

当然,实际上 w w w是一个高维向量,因此用二维绘制 w w w并不能正确地传达并直观理解,但总地直观理解是代价函数会更圆一些,而且更容易优化,前提是特征都在相似范围内,而不是从1到1000,0到1的范围,而是在-1到1范围内或相似偏差,这使得代价函数 J J J优化起来更简单快速。
在这里插入图片描述

实际上如果假设特征 x 1 x_{1} x1范围在0-1之间, x 2 x_{2} x2的范围在-1到1之间, x 3 x_{3} x3范围在1-2之间,它们是相似范围,所以会表现得很好。

当它们在非常不同的取值范围内,如其中一个从1到1000,另一个从0到1,这对优化算法非常不利。但是仅将它们设置为均化零值,假设方差为1,就像上一张幻灯片里设定的那样,确保所有特征都在相似范围内,通常可以帮助学习算法运行得更快。

所以如果输入特征处于不同范围内,可能有些特征值从0到1,有些从1到1000,那么归一化特征值就非常重要了。如果特征值处于相似范围内,那么归一化就不是很重要了。执行这类归一化并不会产生什么危害,我通常会做归一化处理,虽然我不确定它能否提高训练或算法速度。

这就是归一化特征输入,下节课我们将继续讨论提升神经网络训练速度的方法。

1.10 梯度消失/梯度爆炸(Vanishing / Exploding gradients)

训练神经网络,尤其是深度神经所面临的一个问题就是梯度消失或梯度爆炸,也就是你训练神经网络的时候,导数或坡度有时会变得非常大,或者非常小,甚至于以指数方式变小,这加大了训练的难度。

假设我们不考虑激活函数,也不考虑偏置b (这样抓关键点就够了),只考虑深层神经网络每层之间只是简单的线性连接:

那么总体公式就是 y ^ = X W 1 W 2 … … W L \hat{y}=X W^{1}W^{2}……W^{L} y^=XW1W2WL

  1. 梯度爆炸:假设所有W中每个维度的 W i [ l ] W^{[l]}_i Wi[l]>1,再简单点,假设 W [ l ] W^{[l]} W[l]只有一维,每个 W i [ l ] W^{[l]}_i Wi[l]=2,那么输出值就等于
    y ^ = X W 1 W 2 … … W L = X ∗ 2 L \hat{y}=X W^{1}W^{2}……W^{L}=X*2^{L} y^=XW1W2WL=X2L
    假设 L=2, y = 4x
    假设 L=10,y = 1024x
    假设 L=16,y = 65536x
    假设 L=32,y = 4294967296x
    ……
    可以看见,当W中的所有元素都大于1时候,输出会随着L的层数和W的维度增大增多呈指数级增长……然后,如果我们最终要求预测值 y ^ &gt; = 0 \hat{y}&gt;=0 y^>=0 y ^ &lt; = 1 \hat{y}&lt;=1 y^<=1那么求解是非常非常困难的,这就是梯度爆炸。

  2. 梯度消失:假设所有W中每个维度的0< W i [ l ] W^{[l]}_i Wi[l]<1,再简单点,假设 W [ l ] W^{[l]} W[l]只有一维,每个 W i [ l ] W^{[l]}_i Wi[l]=0.9,那么输出值就等于
    y ^ = X W 1 W 2 … … W L = X ∗ 0. 9 L \hat{y}=X W^{1}W^{2}……W^{L}=X*0.9^{L} y^=XW1W2WL=X0.9L
    假设 L=2,y = 0.81x
    假设 L=10,y = 0.3486x
    假设 L=16,y = 0.1853x
    假设 L=32,y = 0.0343x
    ……
    可以看见,当W中的所有元素都小于1时候,输出会随着L的层数和W的维度增大增多呈指数级减小……然后,如果我们最终要求预测值 y ^ &gt; = 0 \hat{y}&gt;=0 y^>=0 y ^ &lt; = 1 \hat{y}&lt;=1 y^<=1并且我们的判断阈值是0.5,那么求解也是非常非常困难的,这就是梯度消失。
    在这里插入图片描述

1.11 神经网络的权重初始化(Weight Initialization for Deep NetworksVanishing / Exploding gradients)

上节课,我们学习了深度神经网络如何产生梯度消失和梯度爆炸问题,那么怎么解决梯度消失和梯度爆炸问题呢?

  1. W的选择不恰当会导致梯度消失或者梯度问题。
  2. 所以我们需要选择适当的W参数进行训练。
  3. 所以我们可以对W设置初值,来解决问题。
  4. 给W设置初值就是权重初始化。

初始化方法有:

方法特点有效性
全0初始化W全设置为0无效
随机初始化各层W值取随机数有效
HE初始化各层W值取随机数 * 2 上一层神经元个数 \sqrt{\frac{2}{\text{上一层神经元个数}}} 上一层神经元个数2 很有效
  1. 代码:全0初始化
def 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
    #layers_dims = [X.shape[0], 10, 5, 1] 
    for l in range(1, L):
        ### START CODE HERE ### (≈ 2 lines of code)
        ###erro : 
        """
        由于使用WX而不是W.TX,因此应该是 W维度是 (n(l),n(l-1)) X维度(n(l-1),m)
        使用zeros函数初始化为0
        """
        parameters['W' + str(l)] = np.zeros((layers_dims[l],layers_dims[l-1]))
        parameters['b' + str(l)] = np.zeros((layers_dims[l],1))
        ### END CODE HERE ###
    return parameters

实验结果:表明全0初始化的成本函数cost,在迭代1400次之后都没有变化过,因此全0初始化无效。
在这里插入图片描述

  1. 代码:随机初始化
# 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
    
    for l in range(1, L):
        ### START CODE HERE ### (≈ 2 lines of code)
        parameters["W" + str(l)] = np.random.randn(layers_dims[l], layers_dims[l-1]) 
        parameters['b' + str(l)] = np.zeros((layers_dims[l],1))
        ### END CODE HERE ###

    return parameters

在这里插入图片描述
实验结果:表明全0初始化的成本函数cost,在迭代之后逐渐减小,因此随机初始化有效。

  1. 代码:HE初始化
    特点: 各层W值取随机数 * 2 上一层神经元个数 \sqrt{\frac{2}{\text{上一层神经元个数}}} 上一层神经元个数2
# GRADED FUNCTION: initialize_parameters_random
# 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[l], layers_dims[l-1]) * np.sqrt(2. / layers_dims[l-1])
        parameters['b' + str(l)] = np.zeros((layers_dims[l],1))
        ### END CODE HERE ###
        
    return parameters

在这里插入图片描述
实验结果:表明HE初始化的成本函数cost,在迭代之后逐渐减小,因此HE初始化有效。

1.12 梯度的数值逼近(Numerical approximation of gradients)

在实施反向传播的时候,怎么知道我们的梯度下降是执行的完全正确的呢?
为了检验反向传播时候我的梯度计算正确,我们可以采用导数的定义来验证我们的梯度。
导数的定义:
d θ = lim ⁡ ε − &gt; 0 f ( θ + ε ) − ( θ − ε ) 2 ε d\theta=\lim_{\varepsilon-&gt;0} \frac{f(\theta + \varepsilon ) - (\theta -\varepsilon)}{2\varepsilon} dθ=ε>0lim2εf(θ+ε)(θε)
参数说明:

  1. ε \varepsilon ε表示一个很小很小的数,它趋近于0。
  2. f ( θ ) f(\theta ) f(θ) 表示一个以 θ \theta θ为参数的连续函数。
  3. 整个式子就是说, f ( θ ) f(\theta ) f(θ) θ \theta θ点的导数 = 在 ε \varepsilon ε趋近于0的时候,以 2 ε 2\varepsilon 2ε底边,以 f ( θ + ε ) − f ( θ − ε ) f(\theta + \varepsilon ) - f(\theta -\varepsilon) f(θ+ε)f(θε)为高的直角三角形的斜边的斜率。
    直观理解:
    在这里插入图片描述

ε \varepsilon ε足够小的时候,我们可以认为这个 f ( θ + ε ) − ( θ − ε ) 2 ε \frac{f(\theta + \varepsilon ) - (\theta -\varepsilon)}{2\varepsilon} 2εf(θ+ε)(θε)就是 f ( θ ) f(\theta) f(θ) θ \theta θ点的导数的精确值。

1.13 梯度检验(Gradient checking)

好了,我们现在已经知道了怎么求导数的精确值。那么如何实现梯度检验呢???

  1. 首先将二位矩阵 W [ 1 ] W^{[1]} W[1] b [ 1 ] b^{[1]} b[1]…… W [ l ] W^{[l]} W[l] b [ l ] b^{[l]} b[l]转换为一维向量,具体操作就是reshape一下就好。

  2. 把所有 W W W矩阵转换成向量之后,做连接运算,得到一个巨型向量 θ \theta θ,该向量表示为参数 θ \theta θ

  3. 计算代价函数 J ( θ ) J(\theta) J(θ)

  4. 同样把 d W [ 1 ] dW^{[1]} dW[1] d b [ 1 ] {db}^{[1]} db[1]…… d W [ l ] {dW}^{[l]} dW[l] d b [ l ] {db}^{[l]} db[l]转换成一个新的向量,用它们来初始化大向量 d θ d\theta dθ,它与 θ \theta θ具有相同维度。

  5. 每次迭代过程中对 d θ approx [ i ] = J ( θ 1 , θ 2 , … θ i + ε , … ) − J ( θ 1 , θ 2 , … θ i − ε , … ) 2 ε d\theta_{\text{approx}}\left[i \right] = \frac{J\left( \theta_{1},\theta_{2},\ldots\theta_{i} + \varepsilon,\ldots \right) - J\left( \theta_{1},\theta_{2},\ldots\theta_{i} - \varepsilon,\ldots \right)}{2\varepsilon} dθapprox[i]=2εJ(θ1,θ2,θi+ε,)J(θ1,θ2,θiε,) d θ d\theta dθ进行比较。

  6. 比较也就是求 d i f f e r e n c e = ∥ g r a d − g r a d a p p r o x ∥ 2 ∥ g r a d ∥ 2 + ∥ g r a d a p p r o x ∥ 2 &lt; 设 定 值 difference = \frac {\| grad - gradapprox \|_2}{\| grad \|_2 + \| gradapprox \|_2 } &lt;设定值 difference=grad2+gradapprox2gradgradapprox2<

  7. 其中, 第二范数的定义为: ∣ ∣ X ∣ ∣ 2 = ∑ i = 1 N x i 2 ||X||_2=\sqrt{\sum^N_{i=1}{x_i^2}} X2=i=1Nxi2

# GRADED FUNCTION: gradient_check_n

def gradient_check_n(parameters, gradients, X, Y, epsilon = 1e-7):
    """
    Checks if backward_propagation_n computes correctly the gradient of the cost output by forward_propagation_n
    
    Arguments:
    parameters -- python dictionary containing your parameters "W1", "b1", "W2", "b2", "W3", "b3":
    grad -- output of backward_propagation_n, contains gradients of the cost with respect to the parameters. 
    x -- input datapoint, of shape (input size, 1)
    y -- true "label"
    epsilon -- tiny shift to the input to compute approximated gradient with formula(1)
    
    Returns:
    difference -- difference (2) between the approximated gradient and the backward propagation gradient
    """
    
    # Set-up variables
    #print(str(parameters))
    """
    1.转换W,b为向量
    """
    parameters_values, _ = dictionary_to_vector(parameters)
    """
    2.转换dW,db为向量
    """
    grad = 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))
    
    #print(parameters_values.shape)
    #print(str(parameters_values))
    # Compute gradapprox
    for i in range(num_parameters):
        # Compute J_plus[i]. Inputs: "parameters_values, epsilon". Output = "J_plus[i]".
        # "_" is used because the function you have to outputs two parameters but we only care about the first one
        ### START CODE HERE ### (approx. 3 lines)
        thetaplus = np.copy(parameters_values)                                      # Step 1
        thetaplus[i][0] = thetaplus[i][0]+epsilon                               # Step 2
        J_plus[i], _ =  forward_propagation_n(X, Y, vector_to_dictionary(thetaplus))                                   # Step 3
        #print(str(gradapprox))
      
        ### END CODE HERE ###
        
        # Compute J_minus[i]. Inputs: "parameters_values, epsilon". Output = "J_minus[i]".
        ### START CODE HERE ### (approx. 3 lines)
        thetaminus = np.copy(parameters_values)            # Step 1
        thetaminus[i][0] =  thetaminus[i][0]-epsilon           # Step 2        
        J_minus[i], _ = forward_propagation_n(X, Y, vector_to_dictionary(thetaminus))                                          # Step 3
        ### END CODE HERE ###
        
        # Compute gradapprox[i]
        ### START CODE HERE ### (approx. 1 line)
        """
	    5.计算精确的导数值
	    """
        gradapprox[i] = (J_plus[i]-J_minus[i])/(2*epsilon)
        #print(str(gradapprox[i]))
        ### END CODE HERE ###
    
    # Compare gradapprox to backward propagation gradients by computing difference.
    ### START CODE HERE ### (approx. 1 line)
    """
	6.求出difference 
	"""
    numerator = np.linalg.norm(gradapprox-grad)                      # Step 1'
    denominator =  np.linalg.norm(grad)+np.linalg.norm(gradapprox)   # Step 2’
    difference = numerator/denominator                              # Step 3'
    ### END CODE HERE ###


    """
	7.比较difference是否小于设定值10^-7 
	"""
    if difference > 1e-7:
        print ("\033[93m" + "There is a mistake in the backward propagation! difference = " + str(difference) + "\033[0m")
    else:
        print ("\033[92m" + "Your backward propagation works perfectly fine! difference = " + str(difference) + "\033[0m")
    
    return difference

1.14 梯度检验应用的注意事项(Gradient Checking Implementation Notes)

这节课,分享一些关于如何在神经网络实施梯度检验的实用技巧和注意事项。

  1. 不要在训练中使用梯度检验,它只用于调试。

我的意思是,计算所有 i i i值的 d θ approx [ i ] d\theta_{\text{approx}}\left[i\right] dθapprox[i]是一个非常漫长的计算过程,为了实施梯度下降,你必须使用 W W W b b b backprop来计算 d θ d\theta dθ,并使用backprop来计算导数,只要调试的时候,你才会计算它,来确认数值是否接近 d θ d\theta dθ。完成后,你会关闭梯度检验,梯度检验的每一个迭代过程都不执行它,因为它太慢了。

  1. 如果算法的梯度检验失败,要检查所有项,检查每一项,并试着找出bug.

也就是说,如果 d θ approx [ i ] d\theta_{\text{approx}}\left[i\right] dθapprox[i]与dθ[i]的值相差很大,我们要做的就是查找不同的i值,看看是哪个导致 d θ approx [ i ] d\theta_{\text{approx}}\left[i\right] dθapprox[i] d θ [ i ] d\theta\left[i\right] dθ[i]的值相差这么多。举个例子,如果你发现,相对某些层或某层的 θ \theta θ d θ d\theta dθ的值相差很大,但是 dw [ l ] \text{dw}^{[l]} dw[l]的各项非常接近,注意 θ \theta θ的各项与 b b b w w w的各项都是一一对应的,这时,你可能会发现,在计算参数 b b b的导数 d b db db的过程中存在bug。反过来也是一样,如果你发现它们的值相差很大, d θ approx [ i ] d\theta_{\text{approx}}\left[i\right] dθapprox[i]的值与 d θ [ i ] d\theta\left[i\right] dθ[i]的值相差很大,你会发现所有这些项目都来自于 d w dw dw或某层的 d w dw dw,可能帮你定位bug的位置,虽然未必能够帮你准确定位bug的位置,但它可以帮助你估测需要在哪些地方追踪bug

  1. 在实施梯度检验时,如果使用正则化,请注意正则项。

如果代价函数 J ( θ ) = 1 m ∑ L ( y ^ ( i ) , y ( i ) ) + λ 2 m ∑ ∣ ∣ W [ l ] ∣ ∣ 2 J(\theta) = \frac{1}{m}\sum_{}^{}{L(\hat y^{(i)},y^{(i)})} + \frac{\lambda}{2m}\sum_{}^{}{||W^{[l]}||}^{2} J(θ)=m1L(y^(i),y(i))+2mλW[l]2,这就是代价函数 J J J的定义, d θ d\theta dθ等于与 θ \theta θ相关的 J J J函数的梯度,包括这个正则项,记住一定要包括这个正则项。

  1. 梯度检验不能与dropout同时使用。

因为每次迭代过程中,dropout会随机消除隐藏层单元的不同子集,难以计算dropout在梯度下降上的代价函数 J ​ J​ J。因此dropout可作为优化代价函数 J ​ J​ J的一种方法,但是代价函数J被定义为对所有指数极大的节点子集求和。而在任何迭代过程中,这些节点都有可能被消除,所以很难计算代价函数 J ​ J​ J。你只是对成本函数做抽样,用dropout,每次随机消除不同的子集,所以很难用梯度检验来双重检验dropout的计算,所以我一般不同时使用梯度检验和dropout。如果你想这样做,可以把dropout中的keepprob设置为1.0,然后打开dropout,并寄希望于dropout的实施是正确的,你还可以做点别的,比如修改节点丢失模式确定梯度检验是正确的。实际上,我一般不这么做,我建议关闭dropout,用梯度检验进行双重检查,在没有dropout的情况下,你的算法至少是正确的,然后打开dropout

5.当 w w w b b b接近0时,梯度下降的实施是正确的,在随机初始化过程中……,但是在运行梯度下降时, w w w b b b变得更大。可能只有在 w w w b b b接近0时,backprop的实施才是正确的。但是当 W W W b b b变大时,它会变得越来越不准确。你需要做一件事,我不经常这么做,就是在随机初始化过程中,运行梯度检验,然后再训练网络, w w w b b b会有一段时间远离0,如果随机初始化值比较小,反复训练网络之后,再重新运行梯度检验。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值