收藏ShowMeAI查看更多精彩内容
本系列为吴恩达老师《深度学习专业课程》学习与总结整理所得,对应的课程视频可以在这里查看。
在ShowMeAI前一篇文章 深度学习概论 中我们对深度学习(Deep Learning)进行了简单介绍:
本节内容我们展开介绍神经网络的基础:逻辑回归(Logistic Regression)。我们将通过对逻辑回归模型结构的分析,过渡到后续神经网络模型。(关于逻辑回归模型,大家也可以阅读ShowMeAI的文章 图解机器学习 | 逻辑回归算法详解 学习)
逻辑回归(Logistic regression) 是一个用于二分类的算法。
二分类就是输出\(y\)只有 {0,1} 两个离散值(也有 {-1,1} 的情况)。我们以一个「图像识别」问题为例,判断图片是否是猫。识别是否是「猫」,这是一个典型的二分类问题——0代表「非猫(not cat)」,1代表「猫(cat)」。(关于机器学习基础知识大家也可以查看ShowMeAI文章 图解机器学习 | 机器学习基础知识)。
从机器学习的角度看,我们的输入\(x\)此时是一张图片,彩色图片包含RGB三个通道,图片尺寸为\((64,64,3)\)。
有些神经网络的输入是一维的,我们可以将图片\(x\)(维度\((64,64,3)\))展平为一维特征向量(feature vector),得到的特征向量维度为\((12288,1)\)。我们一般用列向量表示样本,把维度记为\(n_x\)。
如果训练样本有\(m\)张图片,那么我们用矩阵存储数据,此时数据维度变为\((n_x,m)\)。
我们可以对训练样本的标签\(Y\)也做一个规整化,调整为1维的形态,标签\(Y\)的维度为\((1,m)\)。
逻辑回归是最常见的二分类算法(详细算法讲解也可阅读ShowMeAI文章 图解机器学习 | 逻辑回归算法详解),它包含以下参数:
输出计算用到了Sigmoid函数,它是一种非线性的S型函数,输出被限定在 \([0,1]\) 之间,通常被用在神经网络中当作激活函数(Activation Function)使用。
Sigmoid函数的表达式如下:
\[s = \sigma(w^Tx+b) = \sigma(z) = \frac{1}{1+e^{-z}} \]实际上,逻辑回归可以看作非常小的一个神经网络。
在机器学习中,损失函数(loss function)用于量化衡量预测结果与真实值之间的差距,我们会通过优化损失函数来不断调整模型权重,使其最好地拟合样本数据。
在回归类问题中,我们会使用均方差损失(MSE):
\[L(\hat{y},y) = \frac{1}{2}(\hat{y}-y)^2 \]但是在逻辑回归中,我们并不倾向于使用这样的损失函数。逻辑回归使用平方差损失会得到非凸的损失函数,它会有很多个局部最优解。梯度下降法可能找不到全局最优值,从而给优化带来困难。
因此我们调整成使用对数损失(二元交叉熵损失):
\[L(\hat{y},y) = -(y\log\hat{y})+(1-y)\log(1-\hat{y}) \]刚才我们给到的是单个训练样本中定义的损失函数,它衡量了在单个训练样本上的表现。我们定义代价函数(Cost Function,或者称作成本函数)为全体训练样本上的表现,即\(m\)个样本的损失函数的平均值,反映了\(m\)个样本的预测输出与真实样本输出\(y\)的平均接近程度。
成本函数的计算公式如下:
\[J(w,b) = \frac{1}{m}\sum_{i=1}^mL(\hat{y}^{(i)},y^{(i)}) \]刚才我们了解了损失函数(Loss Function)与成本函数定义,下一步我们就要找到最优的\(w\)和\(b\)值,最小化\(m\)个训练样本的Cost Function。这里用到的方法就叫做梯度下降(Gradient Descent)算法。
在数学上,1个函数的梯度(gradient)指出了它的最陡增长方向。也就是说,沿着梯度的方向走,函数增长得就最快。那么沿着梯度的负方向走,函数值就下降得最快。
(更详细的最优化数学知识可以阅读ShowMeAI文章 图解AI数学基础 | 微积分与最优化)
模型的训练目标是寻找合适的\(w\)与\(b\)以最小化代价函数值。我们先假设\(w\)与\(b\)都是一维实数,则代价函数\(J\)关于\(w\)与\(b\)的图如下所示:
上图中的代价函数\(J\)是一个凸函数,只有一个全局最低点,它能保证无论我们初始化模型参数如何(在曲面上任何位置),都能够寻找到合适的最优解。
基于梯度下降算法,得到以下参数\(w\)的更新公式:
\[w := w - \alpha\frac{dJ(w, b)}{dw} \]公式中\(\alpha\)为学习率,即每次更新的\(w\)的步长。
成本函数\(J(w, b)\)中对应的参数\(b\)更新公式为:
\[b := b - \alpha\frac{dJ(w, b)}{db} \]对于神经网络而言,训练过程包含了两个阶段:前向传播(Forward Propagation)和反向传播(Back Propagation)。
下面,我们结合一个例子用计算图(Computation graph)的形式来理解这两个阶段。
假如我们的Cost Function为\(J(a,b,c)=3(a+bc)\),包含\(a\)、\(b\)、\(c\)三个变量。
我们添加一些中间变量,用\(u\)表示\(bc\),\(v\)表示\(a+u\),则\(J=3v\)。
整个过程可以用计算图表示:
在上图中,我们让\(a=5\),\(b=3\),\(c=2\),则\(u=bc=6\),\(v=a+u=11\),\(J=3v=33\)。
计算图中,这种从左到右,从输入到输出的过程,就对应着神经网络基于\(x\)和\(w\)计算得到Cost Function的前向计算过程。
我们接着上个例子中的计算图讲解反向传播,我们的输入参数有\(a\)、\(b\)、\(c\)三个。
① 先计算\(J\)对参数\(a\)的偏导数
从计算图上来看,从右到左,\(J\)是\(v\)的函数,\(v\)是\(a\)的函数。基于求导链式法则得到:
\[\frac{\partial J}{\partial a}=\frac{\partial J}{\partial v}\cdot \frac{\partial v}{\partial a}=3\cdot 1=3 \]② 计算\(J\)对参数\(b\)的偏导数
从计算图上来看,从右到左,\(J\)是\(v\)的函数,\(v\)是\(u\)的函数,\(u\)是\(b\)的函数。同样可得:
\[\frac{\partial J}{\partial b}=\frac{\partial J}{\partial v}\cdot \frac{\partial v}{\partial u}\cdot \frac{\partial u}{\partial b}=3\cdot 1\cdot c=3\cdot 1\cdot 2=6 \]③ 计算\(J\)对参数\(c\)的偏导数
此时从右到左,\(J\)是\(v\)的函数,\(v\)是\(u\)的函数,\(u\)是\(c\)的函数。可得:
\[\frac{\partial J}{\partial c}=\frac{\partial J}{\partial v}\cdot \frac{\partial v}{\partial u}\cdot \frac{\partial u}{\partial c}=3\cdot 1\cdot b=3\cdot 1\cdot 3=9 \]这样就完成了从右往左的反向传播与梯度(偏导)计算过程。
回到我们前面提到的逻辑回归问题,我们假设输入的特征向量维度为2(即\([x_1, x_2]\)),对应权重参数\(w_1\)、\(w_2\)、\(b\)得到如下的计算图:
① 求出\(L\)对于\(a\)的导数
② 求出\(L\)对于\(z\)的导数
③ 继续前推计算
④ 基于梯度下降可以得到参数更新公式
前面提到的是对单个样本求偏导和应用梯度下降算法的过程。对于有\(m\)个样本的数据集,Cost Function \(J(w,b)\)、\(a^{(i)}\) 和 权重参数\(w_1\) 的计算如图所示。
完整的Logistic回归中某次训练的流程如下,这里仅假设特征向量的维度为2:
J=0; dw1=0; dw2=0; db=0; for i = 1 to m z(i) = wx(i)+b; a(i) = sigmoid(z(i)); J += -[y(i)log(a(i))+(1-y(i))log(1-a(i)); dz(i) = a(i)-y(i); dw1 += x1(i)dz(i); dw2 += x2(i)dz(i); db += dz(i); J /= m; dw1 /= m; dw2 /= m; db /= m;
接着再对\(w_1\)、\(w_2\)、\(b\)进行迭代。
上述计算过程有一个缺点:整个流程包含两个for循环。其中:
如果有大量特征,在代码中显示使用for循环会使算法很低效。向量化可以用于解决显式使用for循环的问题。
继续以逻辑回归为例,如果以非向量化的循环方式计算\(z=w^Tx+b\),代码如下:
z = 0; for i in range(n_x): z += w[i] * x[i] z += b
基于向量化的操作,可以并行计算,极大提升效率,同时代码也更为简洁:
(这里使用到python中的numpy工具库,想了解更多的同学可以查看ShowMeAI的 图解数据分析 系列中的numpy教程,也可以通过ShowMeAI制作的 numpy速查手册 快速了解其使用方法)
z = np.dot(w, x) + b
不用显式for循环,实现逻辑回归的梯度下降的迭代伪代码如下:
\[Z=w^TX+b=np.dot(w.T, x) + b \]\[A=\sigma(Z) \]\[dZ=A-Y \]\[dw=\frac{1}{m}XdZ^T \]\[db=\frac{1}{m}np.sum(dZ) \]\[w:=w-\sigma dw \]\[b:=b-\sigma db \]