吴恩达深度学习系列笔记

文章目录

深度学习概论

利用神经网络进行监督学习

图像数据可以使用CNN(s)

序列数据(音频,语言等)可以使用RNN(可以训练成一个有监督的学习问题)、LSTM

image-20210301112720684

结构化数据、非结构化数据

image-20210301112828383

神经网络基础

二分类

符号定义 :

x x x
表示一个 n x n_{x} nx维数据,为输入数据,维度为 ( n x n_{x} nx,1);

y y y

表示输出结果,取值为 ( 0 , 1 )

( x ( i ) , y ( i ) ) (x^{(i)},y^{(i)}) (x(i),y(i))

表示第 i i i组数据,可能是训练数据,也可能是测试数据,此处默认为训练数据;

X = [ x ( 1 ) , x ( 2 ) , . . , x ( m ) ] X=[x^{(1)},x^{(2)},..,x^{(m)}] X=[x(1),x(2),..,x(m)]

表示所有的训练数据集的输入值,放在一个 n x n_{x} nx∗m 的矩阵中,其中 m表示样本数目;

Y = [ y ( 1 ) , y ( 2 ) , . . , y ( m ) ] Y=[y^{(1)},y^{(2)},..,y^{(m)}] Y=[y(1),y(2),..,y(m)]

对应表示所有训练数据集的输出值,维度为 1 ∗ m

Logistic 回归

下图是sigmoid函数的图像,如果我把水平轴作为 z z z轴,那么关于z的sigmoid函数是这样的,它是平滑地从0、走向1,让我在这里标记纵轴,这是0,曲线与纵轴相交的截距是0.5,这就是关于z的sigmoid函数的图像。我们通常都使用 z z z来表示 w T x + b w^{T}x+b wTx+b的值

img

关于sigmoid函数的公式是这样的, σ ( z ) = 1 1 + e − z \sigma(z)=\frac{1}{1+e^{-z}} σ(z)=1+ez1,在这里 z z z是一个实数,这里要说明一些要注意的事情,如果 z z z非常大那么 e − z e^{-z} ez将会接近于0,关于 z z zsigmoid函数将会近似等于1除以1加上某个非常接近于0的项,因为 e e e的指数如果是个绝对值很大的负数的话,这项将会接近于0,所以如果很大的话那么关于的sigmoid函数会非常接近1。相反地,如果 z z z非常小或者说是一个绝对值很大的负数,那么关于这项 e − z e^{-z} ez会变成一个很大的数,你可以认为这是1除以1加上一个非常非常大的数,所以这个就接近于0。实际上你看到当 z z z变成一个绝对值很大的负数,关于 z z zsigmoid函数就会非常接近于0,因此当你实现逻辑回归时,你的工作就是去让机器学习参数 w w w以及 b b b这样才使得 y ^ \widehat{y} y 成为对 y = 1 y=1 y=1这一情况的概率的一个很好的估计

img

在继续进行下一步之前,介绍一种符号惯例,可以让参数 w w w和参数 b b b分开。在符号上要注意的一点是当我们对神经网络进行编程时经常会让参数 w w w和参数 b b b分开,在这里参数 b b b对应的是一种偏置。在之前的机器学习课程里,你可能已经见过处理这个问题时的其他符号表示。比如在某些例子里,你定义一个额外的特征称之为 x 0 x_{0} x0,并且使它等于1,那么现在 X X X就是一个 n x n_{x} nx加1维的变量,然后你定义的 y ^ = σ ( θ T x ) \widehat{y}=\sigma(\theta^{T}x) y =σ(θTx)sigmoid函数。在这个备选的符号惯例里,你有一个参数向量 θ 0 , θ 1 , θ 2 , . . . , θ n \theta_{0},\theta_{1},\theta_{2},...,\theta{n} θ0,θ1,θ2,...,θn,这样 θ 0 \theta_{0} θ0就充当了 b b b,这是一个实数,而剩下的 θ 1 , θ 2 , . . . , θ n \theta_{1},\theta_{2},...,\theta{n} θ1,θ2,...,θn充当了 w w w,结果就是当你实现你的神经网络时,有一个比较简单的方法是保持 b b b w w w分开。但是在这节课里我们不会使用任何这类符号惯例,所以不用去担心。 现在你已经知道逻辑回归模型是什么样子了,下一步要做的是训练参数 b b b和参数 w w w,你需要定义一个代价函数,让我们在下节课里对其进行解释

逻辑回归的代价函数

为什么需要代价函数

为了训练逻辑回归模型的参数参数 w w w和参数 b b b我们,需要一个代价函数,通过训练代价函数来得到参数 w w w和参数 b b b。先看一下逻辑回归的输出函数:

1

为了让模型通过学习调整参数,你需要给予一个 m m m样本的训练集,这会让你在训练集上找到参数w和参数b,来得到你的输出

损失函数

损失函数又叫做误差函数,用来衡量算法的运行情况,Loss function: L ( y ^ , y ) L(\widehat{y},y) L(y ,y)

我们通过这个称为 L L L的损失函数,来衡量预测输出值和实际值有多接近。一般我们用预测值和实际值的平方差或者它们平方差的一半,但是通常在逻辑回归中我们不这么做,因为当我们在学习逻辑回归参数的时候,会发现我们的优化目标不是凸优化,只能找到多个局部最优值,梯度下降法很可能找不到全局最优值,虽然平方差是一个不错的损失函数,但是我们在逻辑回归模型中会定义另外一个损失函数

我们在逻辑回归中用到的损失函数是:

L ( y ^ , y ) = − y log ⁡ ( y ^ ) − ( 1 − y ) log ⁡ ( 1 − y ^ ) L(\widehat{y},y)=-y\log(\widehat{y})-(1-y)\log(1-\widehat{y}) L(y ,y)=ylog(y )(1y)log(1y )

损失函数是在单个训练样本中定义的,它衡量的是算法在单个训练样本中表现如何

了衡量算法在全部训练样本上的表现如何,我们需要定义一个算法的代价函数,算法的代价函数是对 m m m个样本的损失函数求和然后除以 m m m:

J ( w , b ) = 1 m ∑ i = 1 m L ( y ^ ( i ) , y ( i ) ) = 1 m ∑ i = 1 m ( − y ( i ) log ⁡ y ^ ( i ) − ( 1 − y ^ ( i ) ) ) J(w,b)=\frac{1}{m}\sum^{m}_{i=1}L(\widehat{y}^{(i)},y^{(i)})=\frac{1}{m}\sum^{m}_{i=1}(-y^{(i)}\log\widehat{y}^{(i)}-(1-\widehat{y}^{(i)})) J(w,b)=m1i=1mL(y (i),y(i))=m1i=1m(y(i)logy (i)(1y (i)))

损失函数只适用于像这样的单个训练样本,而代价函数是参数的总代价,所以在训练逻辑回归模型时候,我们需要找到合适的 w w w b b b,来让代价函数 J J J的总代价降到最低

梯度下降法

image-20210301150347998

逻辑回归的代价函数(成本函数) J ( w , b ) J(w,b) J(w,b)是含有两个参数的

img

∂ \partial 表示求偏导符号,可以读作round ∂ J ( w , b ) ∂ w \frac{ {\partial}J(w,b)}{ {\partial}w} wJ(w,b)就是对 w w w求偏导,在代码中我们会使用 d w dw dw表示这个结果;对应对 b b b求偏导就是 d b db db

计算图

可以说,一个神经网络的计算,都是按照前向或反向传播过程组织的。首先我们计算出一个新的网络的输出(前向过程),紧接着进行一个反向传输操作。后者我们用来计算出对应的梯度或导数。

img

逻辑回归中的梯度下降

image-20210301214313612

m个样本的梯度下降

img

当你应用深度学习算法,你会发现在代码中显式地使用for循环使你的算法很低效,同时在深度学习领域会有越来越大的数据集。所以能够应用你的算法且没有显式的for循环会是重要的,并且会帮助你适用于更大的数据集。所以这里有一些叫做向量化技术,它可以允许你的代码摆脱这些显式的for循环。

向量化

向量化是非常基础的去除代码中for循环的艺术,在深度学习安全领域、深度学习实践中,你会经常发现自己训练大数据集,因为深度学习算法处理大数据集效果很棒,所以你的代码运行速度非常重要,否则如果在大数据集上,你的代码可能花费很长时间去运行,你将要等待非常长的时间去得到结果。所以在深度学习领域,运行向量化是一个关键的技巧,让我们举个栗子说明什么是向量化。

在逻辑回归中你需要去计算 z = w T x + b z=w^{T}x+b z=wTx+b w w w x x x都是列向量。如果你有很多的特征那么就会有一个非常大的向量,所以 w ∈ R n x w{\in}R^{n_{x}} wRnx, x ∈ R n x x{\in}R^{n_{x}} xRnx,所以如果你想使用非向量化方法去计算 w T x w^{T}x wTx,你需要用如下方式(python

x
z=0
for i in range(n_x)
    z+=w[i]*x[i]
z+=b

这是一个非向量化的实现,你会发现这真的很慢,作为一个对比,向量化实现将会非常直接计算 w T x w^{T}x wTx,代码如下:

z=np.dot(w,x)+b

这是向量化计算的方法,你将会发现这个非常快

np.dot()

np.dot(a,b)
#如果ab为向量,则它返回的是向量的内积
#如果ab为数组矩阵,则返回的是矩阵乘法

构建神经网络的基本步骤

  1. 定义模型结构(例如输入特征的数量)

  2. 初始化模型的参数

  3. 循环:

    3.1 计算当前损失(正向传播)

    3.2 计算当前梯度(反向传播)

    3.3 更新参数(梯度下降)

def initialize_with_zeros(dim):
    """
        此函数为w创建一个维度为(dim,1)的0向量,并将b初始化为0。

        参数:
            dim  - 我们想要的w矢量的大小(或者这种情况下的参数数量)

        返回:
            w  - 维度为(dim,1)的初始化向量。
            b  - 初始化的标量(对应于偏差)
    """
    w = np.zeros(shape = (dim,1))
    b = 0
    #使用断言来确保我要的数据是正确的
    assert(w.shape == (dim, 1)) #w的维度是(dim,1)
    assert(isinstance(b, float) or isinstance(b, int)) #b的类型是float或者是int

    return (w , b)

def propagate(w, b, X, Y):
    """
    实现前向和后向传播的成本函数及其梯度。
    参数:
        w  - 权重,大小不等的数组(num_px * num_px * 3,1)
        b  - 偏差,一个标量
        X  - 矩阵类型为(num_px * num_px * 3,训练数量)
        Y  - 真正的“标签”矢量(如果非猫则为0,如果是猫则为1),矩阵维度为(1,训练数据数量)

    返回:
        cost- 逻辑回归的负对数似然成本
        dw  - 相对于w的损失梯度,因此与w相同的形状
        db  - 相对于b的损失梯度,因此与b的形状相同
    """
    m = X.shape[1]

    #正向传播
    A = sigmoid(np.dot(w.T,X) + b) #计算激活值,请参考公式2。
    cost = (- 1 / m) * np.sum(Y * np.log(A) + (1 - Y) * (np.log(1 - A))) #计算成本,请参考公式3和4。

    #反向传播
    dw = (1 / m) * np.dot(X, (A - Y).T) #请参考视频中的偏导公式。
    db = (1 / m) * np.sum(A - Y) #请参考视频中的偏导公式。

    #使用断言确保我的数据是正确的
    assert(dw.shape == w.shape)
    assert(db.dtype == float)
    cost = np.squeeze(cost)
    assert(cost.shape == ())

    #创建一个字典,把dw和db保存起来。
    grads = {
   
                "dw": dw,
                "db": db
             }
    return (grads , cost)

def optimize(w , b , X , Y , num_iterations , learning_rate , print_cost = False):
    """
    此函数通过运行梯度下降算法来优化w和b

    参数:
        w  - 权重,大小不等的数组(num_px * num_px * 3,1)
        b  - 偏差,一个标量
        X  - 维度为(num_px * num_px * 3,训练数据的数量)的数组。
        Y  - 真正的“标签”矢量(如果非猫则为0,如果是猫则为1),矩阵维度为(1,训练数据的数量)
        num_iterations  - 优化循环的迭代次数
        learning_rate  - 梯度下降更新规则的学习率
        print_cost  - 每100步打印一次损失值

    返回:
        params  - 包含权重w和偏差b的字典
        grads  - 包含权重和偏差相对于成本函数的梯度的字典
        成本 - 优化期间计算的所有成本列表,将用于绘制学习曲线。

    提示:
    我们需要写下两个步骤并遍历它们:
        1)计算当前参数的成本和梯度,使用propagate()。
        2)使用w和b的梯度下降法则更新参数。
    """

    costs = []

    for i in range(num_iterations):

        grads, cost = propagate(w, b, X, Y)

        dw = grads["dw"]
        db = grads["db"]

        w = w - learning_rate * dw
        b = b - learning_rate * db

        #记录成本
        if i % 100 == 0:
            costs.append(cost)
        #打印成本数据
        if (print_cost) and (i % 100 == 0):
            print("迭代的次数: %i , 误差值: %f" % (i,cost))

    params  = {
   
                "w" : w,
                "b" : b }
    grads = {
   
            "dw": dw,
            "db": db } 
    return (params , grads , costs)

def predict(w , b , X ):
    """
    使用学习逻辑回归参数logistic (w,b)预测标签是0还是1,

    参数:
        w  - 权重,大小不等的数组(num_px * num_px * 3,1)
        b  - 偏差,一个标量
        X  - 维度为(num_px * num_px * 3,训练数据的数量)的数据

    返回:
        Y_prediction  - 包含X中所有图片的所有预测【0 | 1】的一个numpy数组(向量)

    """

    m  = X.shape[1] #图片的数量
    Y_prediction = np.zeros((1,m)) 
    w = w.reshape(X.shape[0],1)

    #计预测猫在图片中出现的概率
    A = sigmoid(np.dot(w.T , X) + b)
    for i in range(A.shape[1]):
        #将概率a [0,i]转换为实际预测p [0,i]
        Y_prediction[0,i] = 1 if A[0,i] > 0.5 else 0
    #使用断言
    assert(Y_prediction.shape == (1,m))

    return Y_prediction

def model(X_train , Y_train , X_test , Y_test , num_iterations = 2000 , learning_rate = 0.5 , print_cost = False):
    """
    通过调用之前实现的函数来构建逻辑回归模型

    参数:
        X_train  - numpy的数组,维度为(num_px * num_px * 3,m_train)的训练集
        Y_train  - numpy的数组,维度为(1,m_train)(矢量)的训练标签集
        X_test   - numpy的数组,维度为(num_px * num_px * 3,m_test)的测试集
        Y_test   - numpy的数组,维度为(1,m_test)的(向量)的测试标签集
        num_iterations  - 表示用于优化参数的迭代次数的超参数
        learning_rate  - 表示optimize()更新规则中使用的学习速率的超参数
        print_cost  - 设置为true以每100次迭代打印成本

    返回:
        d  - 包含有关模型信息的字典。
    """
    w , b = initialize_with_zeros(X_train.shape[0])

    parameters , grads , costs = optimize(w , b , X_train , Y_train,num_iterations , learning_rate , print_cost)

    #从字典“参数”中检索参数w和b
    w , b = parameters["w"] , parameters["b"]

    #预测测试/训练集的例子
    Y_prediction_test = predict(w , b, X_test)
    Y_prediction_train = predict(w , b, X_train)

    #打印训练后的准确性
    print("训练集准确性:"  , format(100 - np.mean(np.abs(Y_prediction_train - Y_train)) * 100) ,"%")
    print("测试集准确性:"  , format(100 - np.mean(np.abs(Y_prediction_test - Y_test)) * 100) ,"%")

    d = {
   
            "costs" : costs,
            "Y_prediction_test" : Y_prediction_test,
            "Y_prediciton_train" : Y_prediction_train,
            "w" : w,
            "b" : b,
            "learning_rate" : learning_rate,
            "num_iterations" : num_iterations }
    return d

d = model(train_set_x, train_set_y, test_set_x, test_set_y, num_iterations = 2000, learning_rate = 0.005, print_cost = True)


浅层神经网络

神经网络的表示

img

我们有输入特征 x 1 x_{1} x1 x 2 x_{2} x2 x 3 x_{3} x3,它们被竖直地堆叠起来,这叫做神经网络的输入层。它包含了神经网络的输入;然后这里有另外一层我们称之为隐藏层(图中的四个结点)。在本例中最后一层只由一个结点构成,而这个只有一个结点的层被称为输出层,它负责产生预测值。解释隐藏层的含义:在一个神经网络中,当你使用监督学习训练它的时候,训练集包含了输入也包含了目标输出,所以术语隐藏层的含义是在训练集中,这些中间结点的准确值我们是不知道到的,也就是说你看不见它们在训练集中应具有的值。你能看见输入的值,你也能看见输出的值,但是隐藏层中的东西,在训练集中你是无法看到的。所以这也解释了词语隐藏层,只是表示你无法在训练集中看到他们

现在我们再引入几个符号,就像我们之前用向量 x x x表示输入特征。这里有个可代替的记号 a [ 0 ] a^{[0]} a[0]可以用来表示输入特征。 a a a表示激活的意思,它意味着网络中不同层的值会传递到它们后面的层中,输入层将 x x x传递给隐藏层,所以我们将输入层的激活值称为 a [ 0 ] a^{[0]} a[0];下一层即隐藏层也同样会产生一些激活值,那么我将其记作 a [ 1 ] a^{[1]} a[1],所以具体地,这里的第一个单元或结点我们将其表示为 a 1 [ 0 ] a_{1}^{[0]} a1[0],第二个结点的值我们记为 a 2 [ 0 ] a_{2}^{[0]} a2[0]以此类推。所以这里的是一个四维的向量如果写成Python代码,那么它是一个规模为4x1的矩阵或一个大小为4的列向量,如下公式,它是四维的,因为在本例中,我们有四个结点或者单元,或者称为四个隐藏层单元;

image-20210302103723059

最后输出层将产生某个数值 a a a,它只是一个单独的实数,所以 y ^ \widehat{y} y 值将取为 a [ 2 ] a^{[2]} a[2]。这与逻辑回归很相似,在逻辑回归中,我们有 y ^ \widehat{y} y 直接等于 a a a,在逻辑回归中我们只有一个输出层,所以我们没有用带方括号的上标。但是在神经网络中,我们将使用这种带上标的形式来明确地指出这些值来自于哪一层,有趣的是在约定俗成的符号传统中,在这里你所看到的这个例子,只能叫做一个两层的神经网络(图3.2.2)。原因是当我们计算网络的层数时,输入层是不算入总层数内,所以隐藏层是第一层,输出层是第二层。第二个惯例是我们将输入层称为第零层,所以在技术上,这仍然是一个三层的神经网络,因为这里有输入层、隐藏层,还有输出层。但是在传统的符号使用中,如果你阅读研究论文或者在这门课中,你会看到人们将这个神经网络称为一个两层的神经网络,因为我们不将输入层看作一个标准的层

w600

最后,我们要看到的隐藏层以及最后的输出层是带有参数的,这里的隐藏层将拥有两个参数 W W W b b b,我将给它们加上上标 [ 1 ] ^{[1]} [1]( W [ 1 ] W^{[1]} W[1], b [ 1 ] b^{[1]} b[1]),表示这些参数是和第一层这个隐藏层有关系的。之后在这个例子中我们会看到是一个4x3的矩阵,而不是一个4x1的向量,第一个数字4源自于我们有四个结点或隐藏层单元,然后数字3源自于这里有三个输入特征,我们之后会更加详细地讨论这些矩阵的维数,到那时你可能就更加清楚了。相似的输出层也有一些与之关联的参数以及。从维数上来看,它们的规模分别是1x4以及1x1。1x4是因为隐藏层有四个隐藏层单元而输出层只有一个单元,之后我们会对这些矩阵和向量的维度做出更加深入的解释,所以现在你已经知道一个两层的神经网络什么样的了,即它是一个只有一个隐藏层的神经网络

计算一个神经网络的输出

w600

上标表示神经网络的层数(隐藏层为1),下标表示该层的第几个神经元。这是神经网络的符号惯例

神经网络的计算

关于神经网络是怎么计算的,从我们之前提及的逻辑回归开始,如下图所示。用圆圈表示神经网络的计算单元,逻辑回归的计算有两个步骤,首先你按步骤计算出 z z z,然后在第二步中你以sigmoid函数为激活函数计算(得出),一个神经网络只是这样子做了好多次重复计算。

img

第一步,计算 z 1 [ 1 ] , z 1 [ 1 ] = w 1 [ 1 ] T x + b 1 [ 1 ] z_{1}^{[1]},z_{1}^{[1]}=w_{1}^{[1]T}x+b_{1}^{[1]} z1[1],z1[1]=w1[1]Tx+b1[1]

第二步,通过激活函数计算 a 1 [ 1 ] , a 1 [ 1 ] = σ ( z 1 [ 1 ] ) a_{1}^{[1]},a_{1}^{[1]}=\sigma(z_{1}^{[1]}) a1[1],a1[1]=σ(z1[1])

隐藏层的第二个以及后面两个神经元的计算过程一样,只是注意符号表示不同,最终分别得到 a 2 [ 1 ] a_{2}^{[1]} a2[1] a 3 [ 1 ] a_{3}^{[1]} a3[1] a 4 [ 1 ] a_{4}^{[1]} a4[1],详细结果见下:

image-20210302110849068

向量化计算 如果你执行神经网络的程序,用for循环来做这些看起来真的很低效。所以接下来我们要做的就是把这四个等式向量化。向量化的过程是将神经网络中的一层神经元参数纵向堆积起来,例如隐藏层中的 w w w纵向堆积起来变成一个 ( 4 , 3 ) (4,3) (4,3)的矩阵,用符号 W [ 1 ] W^{[1]} W[1]表示。另一个看待这个的方法是我们有四个逻辑回归单元,且每一个逻辑回归单元都有相对应的参数——向量 w w w,把这四个向量堆积在一起,你会得出这4×3的矩阵。

z [ n ] = w [ n ] x + b [ n ] z^{[n]}=w^{[n]}x+b^{[n]} z[n]=w[n]x+b[n]

a [ n ] = σ ( z [ n ] ) a^{[n]}=\sigma(z^{[n]}) a[n]=σ(z[n])

image-20210302111446368

image-20210302111848416

w600

总结 通过本视频,你能够根据给出的一个单独的输入特征向量,运用四行代码计算出一个简单神经网络的输出。接下来你将了解的是如何一次能够计算出不止一个样本的神经网络输出,而是能一次性计算整个训练集的输出。

多样本向量化

逻辑回归是将各个训练样本组合成矩阵,对矩阵的各列进行计算。神经网络是通过对逻辑回归中的等式简单的变形,让神经网络计算出输出值。这种计算是所有的训练样本同时进行的,以下是实现它具体的步骤:

w800

如果有一个非向量化形式的实现,而且要计算出它的预测值,对于所有训练样本,需要让 i i i从1到 m m m实现这四个等式:

image-20210302115123119

对于上面的这个方程中的 ( i ) ^{(i)} (i),是所有依赖于训练样本的变量,即将 ( i ) (i) (i)添加到 x x x z z z a a a。如果想计算 m m m个训练样本上的所有输出,就应该向量化整个计算,以简化这列

所以,希望通过这个细节可以更快地正确实现这些算法。接下来讲讲如何向量化这些:

image-20210302115248550

image-20210302115410203

image-20210302115539971

image-20210302115629996

将多个训练样本横向堆叠成一个矩阵 X X X,然后就可以推导出神经网络中前向传播(forward propagation)部分的向量化实现

激活函数

使用一个神经网络时,需要决定使用哪种激活函数用隐藏层上,哪种用在输出节点上。到目前为止,之前的视频只用过sigmoid激活函数,但是,有时其他的激活函数效果会更好

更通常的情况下,使用不同的函数 g ( z [ 1 ] ) g(z^{[1]}) g(z[1]) g g g可以是除了sigmoid函数以外的非线性函数。tanh函数或者双曲正切函数是总体上都优于sigmoid函数 a = t a n h ( z ) a=tanh(z) a=tanh(z)的值域是位于+1和-1之间。 a = t a n h ( z ) = e z − e − z e z + e − z a=tanh(z)=\frac{e^{z}-e^{-z}}{e^{z}+e^{-z}} a=tanh(z)=ez+ezezez

事实上,tanh函数是sigmoid的向下平移和伸缩后的结果。对它进行了变形后,穿过了(0,0)点,并且值域介于+1和-1之间。

结果表明,如果在隐藏层上使用函数 g ( z [ 1 ] ) = t a n h ( z [ 1 ] ) g(z^{[1]})=tanh(z^{[1]}) g(z[1])=tanh(z[1]) 效果总是优于sigmoid函数。因为函数值域在-1和+1的激活函数,其均值是更接近零均值的。在训练一个算法模型时,如果使用tanh函数代替sigmoid函数中心化数据,使得数据的平均值更接近0而不是0.5.这会使下一层学习简单一点

在讨论优化算法时,有一点要说明:我基本已经不用sigmoid激活函数了,tanh函数在所有场合都优于sigmoid函数。

但有一个例外:在二分类的问题中,对于输出层,因为 y y y的值是0或1,所以想让 y ^ \widehat{y} y 的数值介于0和1之间,而不是在-1和+1之间。所以需要使用sigmoid激活函数。这里的 公式: g ( z [ 2 ] ) = σ ( z [ 2 ] ) g(z^{[2]})=\sigma(z^{[2]}) g(z[2])=σ(z[2]) 在这个例子里看到的是,对隐藏层使用tanh激活函数,输出层使用sigmoid函数。

所以,在不同的神经网络层中,激活函数可以不同

为了表示不同的激活函数,在不同的层中,使用方括号上标来指出 g g g上标为 [ 1 ] [1] [1]的激活函数,可能会跟 g g g上标为 [ 2 ] [2] [2]不同。方括号上标 [ 1 ] [1] [1]代表隐藏层,方括号上标 [ 2 ] [2] [2]表示输出层

sigmoid函数和tanh函数两者共同的缺点是,在 z z z特别大或者特别小的情况下,导数的梯度或者函数的斜率会变得特别小,最后就会接近于0,导致降低梯度下降的速度。

在机器学习另一个很流行的函数是:修正线性单元的函数(ReLu),ReLu函数图像是如下图。 公式: a = m a x ( 0 , z ) a=max(0,z) a=max(0,z) 所以,只要z是正值的情况下,导数恒等于1,当是z负值的时候,导数恒等于0。从实际上来说,当使用z的导数时,z=0的导数是没有定义的。但是当编程实现的时候,z的取值刚好等于0.00000001,这个值相当小,所以,在实践中,不需要担心这个值,z是等于0的时候,假设一个导数是1或者0效果都可以

这有一些选择激活函数的经验法则:

如果输出是0、1值(二分类问题),则输出层选择sigmoid函数,然后其它的所有单元都选择Relu函数。

这是很多激活函数的默认选择,如果在隐藏层上不确定使用哪个激活函数,那么通常会使用Relu激活函数。有时,也会使用tanh激活函数,但Relu的一个优点是:当z是负值的时候,导数等于0。

这里也有另一个版本的Relu被称为Leaky Relu

当z是负值时,这个函数的值不是等于0,而是轻微的倾斜,如图

这个函数通常比Relu激活函数效果要好,尽管在实际中Leaky ReLu使用的并不多

w600

两者的优点是:

第一,在z的区间变动很大的情况下,激活函数的导数或者激活函数的斜率都会远大于0,在程序实现就是一个if-else语句,而sigmoid函数需要进行浮点四则运算,在实践中,使用ReLu激活函数神经网络通常会比使用sigmoid或者tanh激活函数学习的更快。

第二,sigmoidtanh函数的导数在正负饱和区的梯度都会接近于0,这会造成梯度弥散,而ReluLeaky ReLu函数大于0部分都为常数,不会产生梯度弥散现象。(同时应该注意到的是,Relu进入负半区的时候,梯度为0,神经元此时不会训练,产生所谓的稀疏性,而Leaky ReLu不会有这问题)

z在ReLu的梯度一半都是0,但是,有足够的隐藏层使得z值大于0,所以对大多数的训练数据来说学习过程仍然可以很快。

快速概括一下不同激活函数的过程和结论。

sigmoid激活函数:除了输出层是一个二分类问题基本不会用它。

tanh激活函数:tanh是非常优秀的,几乎适合所有场合。

ReLu激活函数:最常用的默认函数,如果不确定用哪个激活函数,就使用ReLu或者Leaky ReLu。公式3.23: a = m a x ( 0.01 z , z ) a=max(0.01z,z) a=max(0.01z,z) 为什么常数是0.01?当然,可以为学习算法选择不同的参数。

在选择自己神经网络的激活函数时,有一定的直观感受,在深度学习中的经常遇到一个问题:在编写神经网络的时候,会有很多选择:隐藏层单元的个数、激活函数的选择、初始化权值……这些选择想得到一个对比较好的指导原则是挺困难的。

为自己的神经网络的应用测试这些不同的选择,会在以后检验自己的神经网络或者评估算法的时候,看到不同的效果。如果仅仅遵守使用默认的ReLu激活函数,而不要用其他的激励函数,那就可能在近期或者往后,每次解决问题的时候都使用相同的办法。

为什么需要非线性激活函数

我们稍后会谈到深度网络,有很多层的神经网络,很多隐藏层。事实证明,如果你使用线性激活函数或者没有使用一个激活函数,那么无论你的神经网络有多少层一直在做的只是计算线性函数,所以不如直接去掉全部隐藏层。

在我们的简明案例中,事实证明如果你在隐藏层用线性激活函数,在输出层用sigmoid函数,那么这个模型的复杂度和没有任何隐藏层的标准Logistic回归是一样的,如果你愿意的话,可以证明一下。

总而言之,不能在隐藏层用线性激活函数,可以用ReLU或者tanh或者leaky ReLU或者其他的非线性激活函数,唯一可以用线性激活函数的通常就是输出层;除了这种情况,会在隐层用线性函数的,除了一些特殊情况,比如与压缩有关的,那方面在这里将不深入讨论。在这之外,在隐层使用线性激活函数非常少见。因为房价都是非负数,所以我们也可以在输出层使用ReLU函数这样你的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值