神经网络基础篇:详解逻辑回归 & m个样本梯度下降
逻辑回归中的梯度下降
本篇讲解怎样通过计算偏导数来实现逻辑回归的梯度下降算法。它的关键点是几个重要公式,其作用是用来实现逻辑回归中梯度下降算法。但是在本博客中,将使用计算图对梯度下降算法进行计算。必须要承认的是,使用计算图来计算逻辑回归的梯度下降算法有点大材小用了。但是,认为以这个例子作为开始来讲解,可以使更好的理解背后的思想。从而在讨论神经网络时,可以更深刻而全面地理解神经网络。接下来让开始学习逻辑回归的梯度下降算法。
假设样本只有两个特征\({{x}_{1}}\)和\({{x}_{2}}\),为了计算\(z\),需要输入参数\({{w}_{1}}\)、\({{w}_{2}}\) 和\(b\),除此之外还有特征值\({{x}_{1}}\)和\({{x}_{2}}\)。因此\(z\)的计算公式为:
\(z={{w}_{1}}{{x}_{1}}+{{w}_{2}}{{x}_{2}}+b\)
回想一下逻辑回归的公式定义如下:
\(\hat{y}=a=\sigma (z)\)
其中\(z={{w}^{T}}x+b\)
\(\sigma \left( z \right)=\frac{1}{1+{{e}^{-z}}}\)
损失函数:
\(L( {{{\hat{y}}}^{(i)}},{{y}^{(i)}})=-{{y}^{(i)}}\log {{\hat{y}}^{(i)}}-(1-{{y}^{(i)}})\log (1-{{\hat{y}}^{(i)}})\)
代价函数:
\(J\left( w,b \right)=\frac{1}{m}\sum\nolimits_{i}^{m}{L( {{{\hat{y}}}^{(i)}},{{y}^{(i)}})}\)
假设现在只考虑单个样本的情况,单个样本的代价函数定义如下:
\(L(a,y)=-(y\log (a)+(1-y)\log (1-a))\)
其中\(a\)是逻辑回归的输出,\(y\)是样本的标签值。现在让画出表示这个计算的计算图。
这里先复习下梯度下降法,\(w\)和\(b\)的修正量可以表达如下:
\(w:=w-a \frac{\partial J(w,b)}{\partial w}\),\(b:=b-a\frac{\partial J(w,b)}{\partial b}\)
如图:在这个公式的外侧画上长方形。然后计算:
\(\hat{y}=a=\sigma(z)\)
也就是计算图的下一步。最后计算损失函数\(L(a,y)\)。
有了计算图,就不需要再写出公式了。因此,为了使得逻辑回归中最小化代价函数\(L(a,y)\),需要做的仅仅是修改参数\(w\)和\(b\)的值。前面已经讲解了如何在单个训练样本上计算代价函数的前向步骤。现在让来讨论通过反向计算出导数。
因为想要计算出的代价函数\(L(a,y)\)的导数,首先需要反向计算出代价函数\(L(a,y)\)关于\(a\)的导数,在编写代码时,只需要用\(da\) 来表示\(\frac{dL(a,y)}{da}\) 。
通过微积分得到:
\(\frac{dL(a,y)}{da}=-y/a+(1-y)/(1-a)\)
如果非常熟悉微积分,建议主动推导前面介绍的代价函数的求导公式,使用微积分直接求出\(L(a,y)\)关于变量\(a\)的导数。如果不太了解微积分,也不用太担心。现在已经计算出\(da\),也就是最终输出结果的导数。
现在可以再反向一步,在编写Python代码时,只需要用\(dz\)来表示代价函数\(L\)关于\(z\) 的导数\(\frac{dL}{dz}\),也可以写成\(\frac{dL(a,y)}{dz}\),这两种写法都是正确的。
\(\frac{dL}{dz}=a-y\) 。
因为\(\frac{dL(a,y)}{dz}=\frac{dL}{dz}=(\frac{dL}{da})\cdot (\frac{da}{dz})\),
并且\(\frac{da}{dz}=a\cdot (1-a)\),
而 \(\frac{dL}{da}=(-\frac{y}{a}+\frac{(1-y)}{(1-a)})\),因此将这两项相乘,得到:
\({dz} = \frac{{dL}(a,y)}{{dz}} = \frac{{dL}}{{dz}} = \left( \frac{{dL}}{{da}} \right) \cdot \left(\frac{{da}}{{dz}} \right) = ( - \frac{y}{a} + \frac{(1 - y)}{(1 - a)})\cdot a(1 - a) = a - y\)
博客中为了简化推导过程,假设\({{n}_{x}}\) 这个推导的过程就是之前提到过的链式法则。如果对微积分熟悉,放心地去推导整个求导过程,如果不熟悉微积分,只需要知道\(dz=(a-y)\)已经计算好了。
现在进行最后一步反向推导,也就是计算\(w\)和\(b\)变化对代价函数\(L\)的影响,特别地,可以用:
\(d{{w}_{1}}=\frac{1}{m}\sum\limits_{i}^{m}{x_{1}^{(i)}}({{a}^{(i)}}-{{y}^{(i)}})\)
\(d{{w}_{2}}=\frac{1}{m}\sum\limits_{i}^{m}{x_{2}^{(i)}}({{a}^{(i)}}-{{y}^{(i)}})\)
\(db=\frac{1}{m}\sum\limits_{i}^{m}{({{a}^{(i)}}-{{y}^{(i)}})}\)
博客中,
\(d{{w}_{1}}\) 表示\(\frac{\partial L}{\partial {{w}_{1}}}={{x}_{1}}\cdot dz\),
\(d{{w}_{\text{2}}}\) 表示\(\frac{\partial L}{\partial {{w}_{2}}}={{x}_{2}}\cdot dz\),
\(db=dz\)。
因此,关于单个样本的梯度下降算法,所需要做的就是如下的事情:
使用公式\(dz=(a-y)\)计算\(dz\),
使用\(d{{w}_{1}}={{x}_{1}}\cdot dz\) 计算\(d{{w}_{1}}\), \(d{{w}_{2}}={{x}_{2}}\cdot dz\)计算\(d{{w}_{2}}\),
\(db=dz\) 来计算\(db\),
然后:
更新\({{w}_{1}}={{w}_{1}}-a d{{w}_{1}}\),
更新\({{w}_{2}}={{w}_{2}}-a d{{w}_{2}}\),
更新\(b=b-\alpha db\)。
这就是关于单个样本实例的梯度下降算法中参数更新一次的步骤。
现在已经知道了怎样计算导数,并且实现针对单个训练样本的逻辑回归的梯度下降算法。但是,训练逻辑回归模型不仅仅只有一个训练样本,而是有\(m\)个训练样本的整个训练集。因此在下面讲解中,将这些思想应用到整个训练样本集中,而不仅仅只是单个样本上。
m个样本的梯度下降
在之前的博客中,已经看到如何计算导数,以及应用梯度下降在逻辑回归的一个训练样本上。现在想要把它应用在\(m\)个训练样本上。
首先,让时刻记住有关于损失函数\(J(w,b)\)的定义。
\(J(w,b)=\frac{1}{m}\sum\limits_{i=1}^{m}{L({{a}^{(i)}},{{y}^{(i)}})}\)
当的算法输出关于样本\(y\)的\({{a}^{(i)}}\),\({{a}^{(i)}}\)是训练样本的预测值,即:\(\sigma ( {{z}^{(i)}})=\sigma( {{w}^{T}}{{x}^{\left( i \right)}}+b)\)。
所以在前面是对于任意单个训练样本,如何计算微分当只有一个训练样本。因此\(d{{w}_{1}}\),\(d{{w}_{\text{2}}}\)和\(db\) 添上上标\(i\)表示求得的相应的值。如果面对的是之前的那种情况,但只使用了一个训练样本\(({{x}^{(i)}},{{y}^{(i)}})\)。
现在知道带有求和的全局代价函数,实际上是1到\(m\)项各个损失的平均。 所以它表明全局代价函数对\({{w}_{1}}\)的微分,对\({{w}_{1}}\)的微分也同样是各项损失对\({{w}_{1}}\)微分的平均。
但之前已经演示了如何计算这项,即之前是如何对单个训练样本进行计算。所以真正需要做的是计算这些微分,如在之前的训练样本上做的。并且求平均,这会给全局梯度值,能够把它直接应用到梯度下降算法中。
所以这里有很多细节,但让把这些装进一个具体的算法。同时需要一起应用的就是逻辑回归和梯度下降。
初始化\(J=0,d{{w}_{1}}=0,d{{w}_{2}}=0,db=0\)
代码流程:
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=w-alpha*dw
b=b-alpha*db
这里只应用了一步梯度下降。因此需要重复以上内容很多次,以应用多次梯度下降。看起来这些细节似乎很复杂,但目前不要担心太多。希望明白,当继续尝试并应用这些在编程作业里,所有这些会变的更加清楚。
但这种计算中有两个缺点,也就是说应用此方法在逻辑回归上需要编写两个for循环。第一个for循环是一个小循环遍历\(m\)个训练样本,第二个for循环是一个遍历所有特征的for循环。这个例子中只有2个特征,所以\(n\)等于2并且\({{n}_{x}}\) 等于2。 但如果有更多特征,开始编写的因此\(d{{w}_{1}}\),\(d{{w}_{2}}\),有相似的计算从\(d{{w}_{3}}\)一直下去到\(d{{w}_{n}}\)。所以看来需要一个for循环遍历所有\(n\)个特征。
当应用深度学习算法,会发现在代码中显式地使用for循环使的算法很低效,同时在深度学习领域会有越来越大的数据集。所以能够应用的算法且没有显式的for循环会是重要的,并且会帮助适用于更大的数据集。所以这里有一些叫做向量化技术,它可以允许的代码摆脱这些显式的for循环。
想在先于深度学习的时代,也就是深度学习兴起之前,向量化是很棒的。可以使有时候加速的运算,但有时候也未必能够。但是在深度学习时代向量化,摆脱for循环已经变得相当重要。因为越来越多地训练非常大的数据集,因此真的需要的代码变得非常高效。所以在接下来的几个博客中,会谈到向量化,以及如何应用向量化而连一个for循环都不使用。所以学习了这些,希望有关于如何应用逻辑回归,或是用于逻辑回归的梯度下降,事情会变得更加清晰。当进行编程练习,但在真正做编程练习之前让先谈谈向量化。然后可以应用全部这些东西,应用一个梯度下降的迭代而不使用任何for循环。