zoukankan      html  css  js  c++  java
  • 从逻辑回归开始入门深度学习

    从逻辑回归开始入门深度学习

    本文主要来源于吴恩达《深度学习与神经网络》。本文根据课程内容做一个串联。

    本文内容安排如下:

    • 符号定义
    • 逻辑回归LR:定义、实现、高效实现
    • 浅层神经网络(2层):实现、优化
    • 深度神经网络:实现、优化、应用

    我们以一个分类问题作为研究背景。研究问题为判断输入图片是否为猫咪的二分类。

    符号定义

    在解决问题之前,我们先对使用的数学符号做一个定义:

    • (x, y): 输入样本; x ∈ (R^{n_x}), y ∈ {0, 1}
    • {((x^{(1)}, y^{(1)}), (x^{(2)}, y^{(2)})... (x^{(m)}, y^{(m)}))}: 训练数据集,包含m个训练样本
    • [a,b,c,.....,z].T: 向量,默认情况下,向量指的是列向量
    • (m = m_{train}), (m_{test})=#test examples
    • $X in R^{n_x * m} (: 训练集,训练样本以**列的方式**进行堆叠,换言之,X矩阵的每一列是一个样本,而不是行; X.shape = ()n_x$, m)
    • (Y in R^{1*m}): 训练标签,标签以列的方式进行堆叠, (Y.shape = (1,m))

    逻辑回归

    在介绍逻辑回顾处理图片分类。我们处理的问题是二分类,输入一张图片判断图片中是否有猫。输入图片格式为RGB三色图,像素取值为0~255。

    img

    原理介绍

    逻辑回归用于处理二分类问题。逻辑回归中(hat{h} = P(y=1|x))用于计算输入样本为1的概率。以单个样本为例,其计算公式为

    [hat{y} = sigmoid(w^Tx+b) ]

    其中,(x in R^{n_x}), (w in R^{n_x}) ,(b in R)。输出结果的取值范围为[0, 1]。

    逻辑回归其实是线性回归的进一步加工,线性回归计算结果的取值范围为((-infty, +infty)),我们将线性回归的计算结果使用sigmoid将范围压缩到[0, 1].

    Sigmoid是一种非线性的S型函数,取值范围在[0, 1],这种输出值可以别理解为概率表示。Sigmoid函数的计算公式和曲线如下。

    [Sigmoid(z) = frac{1}{1+e^{-z}} ]

    Sigmoid

    从上图可以看出,sigmoid取值范围为[0, 1],当自变量z非常小时,sigmoid趋近于0;当z非常大时,sigmoid趋近于1(实际上当z=10时,输出值为0.9999,非常趋近于1)。

    Loss function

    我们现在知道了如何使用逻辑回归计算一个样本为正例的概率,那么如何评估模型的好坏呢?这就依赖于损失函数。

    给定一个样本((x^{(i)}, y^{(i)})),使用逻辑回归计算这个样本为正例的概率P(y=1|x),

    [hat{y}^{(i)} = sigma(w^Tx^{(i)} + b), where sigma(z)=frac{1}{1+e^{-z}} ]

    理想情况下,输出结果(hat y)应该和样本标签y尽可能相等,即(hat y^{(i)} approx y^{(i)})

    [L(hat y, y) = -(yloghat y + (1-y)log(1-hat y)) ]

    当y=1时,(L(hat y, y)=-loghat y);当y=0时,(L(hat y, y) = -log(1-hat y)).

    在全部训练样本上,损失函数cost function为

    [J(w, b) = frac1{m}sum_{i=1}^m L(hat y, y) = -frac1{m}sum_{i=1}^m[y^{(i)}loghat y^{(i)} + (1-y^{(i)})log(1-hat y^{(i)})] ]

    损失函数是参数w,b的函数,我们想要通过最小化损失函数找到最佳的参数w,b,最后用于样本的预测[通过最小化损失函数,我们可以保证预测结果与真实样本标签之间差距尽可能小,进而保证预测结果的准确性]。

    LR损失函数可以使用最大似然估计来进行推导。

    Gradient Descent

    知道了模型的损失函数,接下来就是通过最小化损失函数得到最终的参数w,b。常用的方法是使用梯度下降法,使用当前位置的偏导数对参数进行更新,反复多次后,损失函数到达最低点,此时w,b即为最终结果。

    Gradient Descent

    使用梯度下降算法,w,b的更新公式如下:

    [w = w - alpha frac{partial J(w, b)}{partial w} \b = b - alpha frac{partial J(w, b)}{partial b} ]

    其中,(alpha)为学习率,含义是每次参数更新的步幅;如果(alpha)过大,导致参数更新幅度过大,可能会错过模型的最优值;如果(alpha)过下,导致每次更新幅度很小,模型需要更多的迭代次数才能收敛。在程序代码中,我们使用dw表示(frac{partial J(w, b)}{partial w}), db表示(frac{partial J(w, b)}{partial b}).

    计算图

    神经网络的计算过程一般分为两个阶段:前向传播和反向传播。使用计算图来描述计算过程更清晰:将整个计算过程分步骤进行计算。

    假设J(a, b, c) = 3(a + bc),其中a=5, b=3, c=2.这个计算过程可以使用计算图来描述,如:

    设定u = bc, v = a + u, J=3v.

    反向传播过程,需要计算da, db, dc.此时,可以通过计算图依据链式法则进行计算。

    [frac{partial J}{partial a} = frac{partial J}{partial v} * frac{partial v}{partial a} = 3 * 1 = 3 ]

    [frac{partial J}{partial b} = frac{partial J}{partial v} * frac{partial v}{partial u} * frac{partial u}{partial b}= 3 * 1 * c= 6 ]

    [frac{partial J}{partial c} = frac{partial J}{partial v} * frac{partial v}{partial u} * frac{partial u}{partial c}= 3 * 1 * b= 9 ]

    在计算图中,蓝色线为前向传播计算过程,红色线为反向传播计算过程。

    LR的优化计算

    通过上面的计算图我们知道了神经网络的计算流程。下面我们使用计算图来描述单个样本的逻辑回归的计算过程,然后扩展到m个样本上;之后介绍LR的优化过程,即向量化。

    单个样本的计算

    单个样本的逻辑回归计算公式如下:

    [hat y = sigma(w^Tx+b), where sigma(z)=frac1{1+e^{-z}} \L(hat y, y) = -[yloghat y + (1-y)log(1-hat y)] ]

    我们假设训练样本x的维度为2(有两个特征x1、x2)。

    描述逻辑回归的loss函数的计算图将运算过程分为3步, 分别为计算z、(hat y),以及L(a,y)。

    逻辑回归的参数更新法则如下:

    [w_1 = w_1 - alpha * frac{partial L}{partial w_1} \w_2 = w_2 - alpha * frac{partial L}{partial w_2}\b = b - alpha * frac{partial L}{partial b} ]

    因此,接下来的计算过程主要集中在偏导数(frac{partial L}{partial w_1}), (frac{partial L}{partial w_2})以及(frac{partial L}{partial b})的计算。

    从计算图来看:

    [frac{partial J}{partial a} = frac{partial -[yloga + (1-y)log(1-a)]}{partial a} = -[frac{y}{a} - frac{1-y}{1-a}] ]

    [frac{partial a}{partial z} = frac{partial frac{1}{1+e^{-z}}}{partial z} = frac{1}{1+e^{-z}} * frac{e^{-z}}{1+e^{-z}} = a * (1 - a) \frac{partial z}{partial w_1} = frac{partial (w_1x_1+w_2x_2+b)}{partial w_1} = x_1 \frac{partial z}{partial w_2} = frac{partial (w_1x_1+w_2x_2+b)}{partial w_2} = x_2 \frac{partial z}{partial b} = frac{partial (w_1x_1+w_2x_2+b)}{partial b} = 1 ]

    在反向传播过程中根据链式法则,我们可以知道

    [frac{partial J}{partial w_1} = frac{partial J}{partial a} * frac{partial a}{partial z} * frac{partial z}{partial w_1} = -[frac{y}{a} - frac{1-y}{1-a}] * a * (1 - a) * x_1 \= (a-y)*x_1 ]

    [frac{partial J}{partial w_2} = frac{partial J}{partial a} * frac{partial a}{partial z} * frac{partial z}{partial w_2} = -[frac{y}{a} - frac{1-y}{1-a}] * a * (1 - a) * x_2 \= (a-y)*x_2 ]

    [frac{partial J}{partial b} = frac{partial J}{partial a} * frac{partial a}{partial z} * frac{partial z}{partial b} = -[frac{y}{a} - frac{1-y}{1-a}] * a * (1 - a) * 1 \= a-y ]

    知道了单个样本的反向传播过程,接下来我们将样本数扩展到m个,看看计算有什么变化。

    m个样本的计算

    对于m个样本,逻辑回归的cost function计算过程如下:

    [z^{(i)} = w^Tx^{(i)} + b ]

    [hat y^{(i)} = a^{(i)} = sigma(z^{(i)}) ]

    [J(w, b) = frac1{m}sum_{i=1}^m L(hat y, y) = -frac1{m}sum_{i=1}^m[y^{(i)}loghat y^{(i)} + (1-y^{(i)})log(1-hat y^{(i)})] ]

    对应的偏导数为:

    (frac{partial J}{partial w_1} = frac1{m}*sum_{i=1}^m(a^{(i)}-y^{(i)})*x_1^{(i)})

    (frac{partial J}{partial w_2} = frac1{m}*sum_{i=1}^m(a^{(i)}-y^{(i)})*x_2^{(i)})

    (frac{partial J}{partial b} = frac1{m}*sum_{i=1}^m(a^{(i)}-y^{(i)}))

    其实就是将m个样本的偏导数求和,然后取平均。

    使用伪代码描述这个过程如下:

    J=0; dw1=0; dw2=0; db=0
    for i = 1 to m:
        # 前向传播计算损失函数
        z(i) = w * x(i) + b
        a(i) = sigmoid(z(i))
        J += -[y(i)loga(i) + (1-y(i))log(1-a(i))]
        # 反向传播计算导数
        dz(i) = a(i) - y(i)
        dw1 += dz(i)*x1(i) # x1(i):第i个样本的第一个特征
        dw2 += dz(i)*x2(i)
        db += dz(1)
    # 遍历完m个样本,计算梯度均值
    J /= m
    dw1 /= m
    dw2 /= m
    db /= m
    

    梯度计算完成后,对参数进行一次更新:

    w1 -= alpha * dw1
    w2 -= alpha * dw2
    b -= alpha * db
    

    这个过程是对样本集的一次遍历,梯度下降算法可以规定遍历的次数,遍历n次后整个梯度下降过程就完成了。

    优化

    整个计算过程中,使用的是显示的for循环,我们可以使用矩阵运算来对整个计算过程进行优化。

    Z计算: (z = w^Tx+b)

    对于单个样本,(z = w^Tx + b); 其中, (w in R^{n_x},x in R^{n_x}, b in R)

    for-loop形式

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

    向量形式

    import numpy as np
    z = np.dot(w.T, x) + b
    

    对于m个样本,z的计算结果为一个向量。$X in R^{n_x * m},w in R^{n_x} $

    for-loop 方法

    z = np.zeros((1, m))
    for i in range(m):
        for j in range(n_x):
            z[i] += w[j]*X[j][i]
    

    向量形式

    z = np.dot(w.T, X)
    

    使用矩阵运算后的LR计算过程如下:

    # w: [n_x, 1]; x: [n_x, m], b: float
    Z = np.dot(w.T, X) + b # [1, m]
    A = sigmoid(Z)
    # 反向传播计算梯度dw, db
    dZ = A -Y
    dw = 1./m * X * dZ.T
    db = 1./m * np.sum(dZ)
    # 参数更新
    w -= learning_rate * dw
    b -= learning_rate * db
    

    使用矩阵运算,减少了对样本的for训练遍历以及对梯度计算过程中对参数的遍历。

    Whenever possible, avoid explicit for-loops.

    值得注意的是,这里关于非参数的矩阵表示,如训练样本矩阵X,标签矩阵Y都是以列的方式进行堆叠而成的。矩阵运算将for循环集中在一次计算过程中完成。

    浅层神经网络(2层)

    从某种角度上说,逻辑回归LR也可以看作一种神经网络,示意图如下。

    中间的神经元完成两种运算,分别为z和a。

    浅层神经网络示意图如下,其中每个“圆圈”的运算类似于LR,区别在于第二步a的计算中使用的激活函数不同。LR激活函数为sigmoid,这里可以为relu、tanh、sigmoid等等。

    这个神经网络有输入层、隐藏层和输出层三层组成,但是一般情况下输入层忽略不计,所以这个神经网络有2层组成。

    前向传播

    我们这里设定(w_i^{[l]})表示神经网络第l层的第i个神经元的权重参数。对于单个样本而言,这个浅层神经网络的计算过程如下:

    在隐藏层:

    (z_1^{[1]} = w_1^{[1]T}*x + b_1^{[1]}, a_1^{[1]} = sigma(z_1^{[1]});)

    (z_2^{[1]} = w_2^{[1]T}*x + b_2^{[1]}, a_2^{[1]} = sigma(z_2^{[1]});)

    (z_3^{[1]} = w_3^{[1]T}*x + b_3^{[1]}, a_3^{[1]} = sigma(z_3^{[1]});)

    (z_4^{[1]} = w_4^{[1]T}*x + b_4^{[1]}, a_4^{[1]} = sigma(z_4^{[1]});)

    输出结果得到一个[4, 1]向量:[(a_1^{[1]};a_2^{[1]};a_3^{[1]};a_4^{[1]})]. 我们使用(a^{[1]})表示这个结果,同时作为输出层的输入继续计算。

    输出层计算结果:

    (z_1^{[2]} = w_1^{[2]T}*a^{[1]} + b_1^{[2]}, hat y=a^{[2]} = sigma(z^{[2]}))

    使用矩阵表示整个计算过程如下,隐藏层的参数用(W^{[1]})表示,由隐藏层各个神经元对应权重参数以行的方式堆叠而成,这里形状为[4, 3] (权重参数的第一维度为本层神经元的数目,另一个维度为上一层网络的神经元数目)

    单个样本的矩阵表示计算过程如下:

    (z^{[1]} = W^{[1]}x + b^{[1]})

    (a^{[1]} = sigma(z^{[1]}))

    (z^{[2]} = W^{[2]}a^{[1]} + b^{[2]})

    (a^{[2]}=sigma(z^{[2]}))

    如果将样本数扩展到m个,使用for循环的计算过程为:

    for i = 1 to m:
        z[1](i) = W[1]x(i) + b[1]
    	a[1](i) = sigma(z[1](i))
    	z[2](i) = W[2]a[1](i) + b[2]
    	a[2](i) = sigma(z[2](i))
    

    如果使用矩阵运算,我们设定m个训练样本以列的方式进行堆叠,用X表示,形状为[3, m]。使用矩阵运算过程如下,

    Z[1] = W[1]X + b[1]
    A[1] = sigma(Z[1])
    Z[2] = W[2]A[1] + b[2]
    A[2] = sigma(Z[2])
    

    示意图如下:

    反向传播

    反向传播主要用于计算梯度dw1, dw2, dw3, db.为了方便理解,我们先用for循环进行介绍,之后再使用矩阵进行计算优化。

    单个样本

    计算过程类似于逻辑回归的反向传播过程。

    (dz^{[2]} = a^{[2]} - y)

    (dW^{[2]} = dz^{[2]} * frac{partial dz^{[2]}}{partial W^{[2]}} = dz^{[2]} * a^{[1]T})

    $db^{[2]} = dz^{[2]} * frac{partial dz^{[2]}}{partial b^{[2]}} = dz^{[2]} $

    (dz^{[1]} = dz^{[2]} * frac{partial dz^{[2]}}{partial a^{[1]}} * frac{partial a^{[1]}}{partial z^{[1]}} = W^{[2]T}*dz^{[2]} * g^{[1]^{'}}(z^{[1]}))

    (dW^{[1]} = dz^{[1]} * frac{partial dz^{[1]}}{partial W^{[1]}} = dz^{[1]} * x^T)

    (db^{[1]} = dz^{[1]} * frac{partial dz^{[1]}}{partial b^{[1]}} = dz^{[1]} * 1 = dz^{[1]})

    对应的向量形式为(将m个样本堆叠在一起,一同计算):

    深层神经网络

    深层神经网络是指包括多个隐藏的神经网络模型。如

    深层神经网络计算过程类似于浅层神经网络,分为前向传播计算loss,反向传播计算梯度,然后更新权重;反复更新直到模型收敛。

    模型的前向传播郭恒比较简单,我们接下来主要介绍模型的反向传播。以神经网络的某一层为例:


    Input: (da^{[l]})

    Output: (da^{[l-1]}, dW^{[l]}, db^{[l]})


    我们知道前向传播过程中:

    (z^{[l]} = W^{[l]}*a^{[l-1]} + b^{[l]})

    (a^{[l]} = g(z^{[l]}))

    那么,

    (dz^{[l]} = da^{[l]} * g^{[l]'}(z^{[l]}))

    (da^{[l-1]} = W^{[l]T} * dz^{[l]})

    (dW^{[l-1]} = dz^{[l]} * a^{[l-1]})

    (db^{[l-1]} = dz^{[l]})

    得到(da^{[l-1]})之后,我们可以继续往前传播。


    为了方便运算,我们可以在前向传播过程中保存计算结果(g(z^{[l]}))

    深度神经网络的计算图可以描述如下。

    神经网络的前向传播和反向传播计算过程可以总结如下:

    总结

    本文自下而上的对神经网络进行了介绍。首先,从逻辑回归开始介绍其计算过程、反向传播、更新方法,在介绍过程中先以单个样本的计算开始,然后扩展到m个样本,之后为了提高计算速度,采用向量化方法进行计算;我们了解了逻辑回归之后,介绍浅层神经网络。浅层神经网络是一个2层神经网络,每层神经网络的神经元可以看做是一个“逻辑回归”计算单元,区别在于使用的激活函数不同。浅层神经网络的介绍也是先从单个样本开始,通过单个样本明白其计算过程,然后扩展到m个样本,最终使用向量化方式完成计算。最后,介绍深层神经网络,深层神经网络只是增加了隐藏层的数目,其计算过程和浅层神经网络十分相似。


    欢迎关注我的公众号,一同学习成长。
    公众号


  • 相关阅读:
    Android网络框架OkHttp之get请求(源码初识)
    Android Studio运行报错,Cannot find System Java Compiler. Ensure that you have installed a JDK......
    Android基于xmpp的即时通讯应用
    Android 基于Http的多线程下载的实现
    1.3 Quick Start中 Step 3: Create a topic官网剖析(博主推荐)
    1.3 Quick Start中 Step 2: Start the server官网剖析(博主推荐)
    1.3 Quick Start中 Step 1: Download the code官网剖析(博主推荐)
    1.2 Use Cases中 Commit Log官网剖析(博主推荐)
    1.2 Use Cases中 Event Sourcing官网剖析(博主推荐)
    1.2 Use Cases中 Stream Processing官网剖析(博主推荐)
  • 原文地址:https://www.cnblogs.com/ysugyl/p/12853631.html
Copyright © 2011-2022 走看看