仍然是一篇入门文,用以补充以前文章中都有意略过的部分。 之前的系列中,我们期望对数学并没有特别喜好的程序员,也可以从事人工智能应用的开发。但走到比较深入之后,基本的数学知识,还是没办法躲过的。
所有的深度学习,始于一个最简单的公式:
\[ y=ax+b \]
如果不理解的,可以去看一下房价预测的例子。
简单说:y是要预测的房价,x是房子的平米数。a是每平米的房价,b是基本费用。
这个公式每当有一个房子的平米数,预测出来一个y,就是房子的总价格。当然作为简化的举例,这里排除了很多更复杂的因素。
所以,作为小学数学的课程,这是一个很简单的房价方程式。
然后机器学习的重点来了。 在常见的方程式中,y是计算结果不用说了,x从来都当做未知数,a/b是常量,常量在方程式中也是已知量的意思。 而在机器学习中,我们会有一大批用于学习的数据。仍以这个杜撰的房价预测为例,我们手头会有大量房间平米数、房价的对应样本。所以房价y是已知量,房间平米数x也不再是未知数,而成为了已知量。 反过来作为常量的a/b,我们并不知道,也就是未知数。 单纯从符号系统来看,这个公式太诡异了,已知数变成了未知数,未知数变成了已知数。 但也不过是就这么一点点不适应,作为程序员,用惯了各种奇怪的变量名,x/y声明为常量,a/b当做求解的未知数,也没有什么好奇怪的对不对?
只要适应了这一点点,接下来就没有什么好神奇的了。 公式\(y=ax+b\)中,有a/b两个未知数,常识告诉我们,其实不需要很多样本,有两组样本,足以求得两个未知数了。比如两组样本为: 公寓A,30平米,房价69万。公寓B,90平米,房价195万。还是老话,先别在意这两个数据合不合理,以及房价包含的很多复杂因素。 那么,列出的方程组为:
\[ \begin{equation} \begin{cases} 69=30a+b \\ 195=90a+b \end{cases} \end{equation} \]
这样的方程,应当可以秒解吧?万一忘了怎么解方程也没关系,再附送一个python版本的解方程:
from sympy import *
a,b = symbols("a b")
s1 = solve([Eq(69,30*a+b),Eq(195,90*a+b)],[a,b])
print("a=",s1[a].evalf()," b=",s1[b].evalf())
最后的结果:
a= 2.10000000000000 b= 6.00000000000000
现在a/b两个常量,终于是真正的常量了。之后再利用这个公式,就能够用来预测房价了。
小结一下: 机器学习,就是利用样本中的已知量,求解方程中常量系数的过程。 机器学习完成后,人工智能的预测过程,是使用在学习过程中求得的常量,通过计算输入的特征值x,得出预测值y的过程。
那说了这么多,这跟梯度下降有啥关系呢? 事情是这样的,在上面简单的例子中,只有一个特征值x,和两个未知数(两个常量系数需要求解),我们很容易就能解方程。 但在人工智能系统中,特征值可能有很多,比如一幅224x224的彩色图片,就是224x224x3(色深)=150528个特征值。至少有150529个常量需要求解。 用公式来表示会是这样:
\[ y = a_1x_1+a_2x_2+a_3x_3+ ... +a_{150528}x_{150528}+b \]
推广一下:
\[ y = a_1x_1+a_2x_2+a_3x_3+ ... +a_nx_n+b \]
这个公式还能化简,现在特征值是\(x_1\)一直到\(x_n\)。
我们人为设置一个内置的特征值\(x_0\),值一直是1。这样常量b也就能统一进来了,因为\(bx_0 = 1b = b\)。而把b换一个名字换成\(a_0\),不用担心这个名字换来换去,因为求解出来,都是一个固定的常量,叫什么名字都不重要。那整个方程就等于:
\[ \begin{align} y &= a_0x_0+a_1x_1+a_2x_2+a_3x_3+ ... +a_nx_n \\ &= \sum_{i=0}^na_ix_i \end{align} \]
这样看起来更舒服,也更有代表性。
这样情况下,要求的未知数可多了,使用通常的解方程方式已经无法满足这个要求。此时,梯度下降法已经可以粉墨登场了。
我们首先要引入两个概念,先说第一个:假设函数。 假设函数的意思是指,我们使用一组特征值x(x是简写,实际上是\(x_1\)一直到\(x_n\)),通过上述的计算公式,可以得到一个结果y'。为什么是y'而不直接是y呢?因为我们公式中所有的权重值\(a_0\)至\(a_n\)还没有确定的值,所以求得的y',跟实际上的y必然还不同。 整理一下,我们把假设函数列出来,公式中h,是英语假设Hypothesis的缩写:
\[ y' = h_a(x) = \sum_{i=0}^na_ix_i \]
刚才的数学公式,我们一直延续了小学生的习惯,为了跟机器学习课程的统一,我们再次重命名一下常量名称,用希腊字母θ代替我们一直使用的英文字母a。这是为了便于我们跟同行的交流,以及学习其它课程。那么再次列出改名后的公式:
\[ y' = h_θ(x) = \sum_{i=0}^nθ_ix_i \]
上面说到,我们使用一组样本的特征值x,可以求得一个目标值y'。因为公式中,我们的常量值尚未求得正确结果,所以此时y'跟正式的y值,肯定是不同的。那么两者的差异,就是“损失”。求得两者差异的函数,就是“损失函数”。也即:
\[ l(θ) = (y' - y)^2 = (h_θ(x) - y)^2 \]
l是损失(loss)的缩写。这里的差值再取平方就是大名鼎鼎的“方差”,是为了保证结果是一个正数。因为我们肯定希望这个“损失”值越小越好,最好是0,0表示无差别。如果出现了负数,那负的越多,结果实际上更恶劣了。
如果只有一组样本,我们不可能求得很多的未知数,但幸运的是,我们通常都有很多组数据样本。
这也是很多大佬口中说:拥有数据就拥有未来的意思。
在多组样本的情况下,科学表达损失值的方法有很多,常见的是均方差(Mean Squared Error)。均方差并非最好,但易懂易用,就是累计m组样本的方差,再求平均值:
\[ J(θ) = \frac1m\sum_{i=1}^m(h_θ(x^{(i)}) - y^{(i)})^2 \]
这里的J是均方差损失的意思,汇总了所有样本的表现。
损失函数也经常被称为代价函数Cost Function。但两者实际还是有细微区别。不过现在混用已经如此普及,我们就不吹毛求疵了。
为了后续的计算方便,我们把上面公式前面再增加一个1/2,后续化简的时候你会看到这个1/2的作用。在这里,我们期望的J(θ)是无限接近于0,所以前面增加1/2不会影响J(θ)的结果。
\[ J(θ) = \frac1{2m}\sum_{i=1}^m(h_θ(x^{(i)}) - y^{(i)})^2 \]
对每一个要求解的量θ,同损失函数值之间,都有一个函数关系图示如下:
当然这是一个极度理想化的图,只有一个全局的最低点。实际上大多复杂的机器学习问题,其图示关系都如同重峦叠嶂,有很多个低谷,不理解的可以参考以下题头图。那会导致我们很容易到达了一个局部最优解之后陷入在那里,而不是全局最优,这种情况不在本文讨论。 梯度下降是微分中的一个概念,在这里应用的大意是,我们把每一个θ的取值范围,都划分为很多份,每一份的宽度我们称为动态∂,其实际宽度是由微分步长α决定的,我们一步步尝试改变θ的值,直至求得的损失值J(θ)最小,无限接近于0。 根据微分公式变形得到的θ迭代公式为:
\[ θ_j := θ_j - α\frac∂{∂θ_j}J(θ) \]
这个公式的原理不详细解释了,你知道是由微分公式推导得来的就好。这里面的J是表示第J个未知数θ的意思。
我们看公式中α步长后面的部分:
\[ \begin{align} \frac∂{∂θ_j}J(θ) & = \frac∂{∂θ_j}\frac1{2m}\sum_{i=1}^m(h_θ(x^{(i)}) - y^{(i)})^2 \\ 化简后 \\ & = \sum_{i=1}^m(h_θ(x^{(i)}) - y^{(i)})x_j^i \end{align} \]
因为有人为添加的1/2的原因,这个化简让公式变得相对简单。所以对于确定的每一个θ,我们的梯度下降求值公式为:
\[ \begin{cases} θ_0 := θ_0 - α\sum_{i=1}^m(h_θ(x^{(i)}) - y^{(i)})^i \\ θ_1 := θ_1 - α\sum_{i=1}^m(h_θ(x^{(i)}) - y^{(i)})x_1^i \\ θ_2 := θ_2 - α\sum_{i=1}^m(h_θ(x^{(i)}) - y^{(i)})x_2^i \\ ...... \\ θ_j := θ_j - α\sum_{i=1}^m(h_θ(x^{(i)}) - y^{(i)})x_j^i \\ θ_n := θ_n - α\sum_{i=1}^m(h_θ(x^{(i)}) - y^{(i)})x_n^i \\ \end{cases} \]
上面公式第一行还记得\(x_0\)恒定为1吗?所以那里并没有\(x_0\)。随后有多少个要求解的θ,就有多少行公式。
从数学角度上说,这些公式没有疑问。从编程上说,有一个提醒。那就是这些公式在求取权重系数θ的时候,每一行求取一个新的θ的过程中,所使用计算假设函数的θ,是在上一个循环中固定下来的那个θ值,所有行的θ均为如此。直到所有这一个批次的θ值计算完成后,整体θ才可以代入到公式,从而参与下一个循环的计算。不能每计算一个θ值,就单独的代入到公式中,那样梯度下降就永远找不到方向了。
说了这么多,梯度下降就是一种解方程的方法,特别对应于机器学习这种,因为数据集特征维度超多导致的方程式权重系数量大,无法使用传统方式求解的问题。 公式的推导和解释只是为了对机器学习的底层逻辑理解的更为清楚,实际上在各个机器学习框架中,这些工作都已经由框架帮我们完成了,并且封装了很多种经典的算法,以适应不同的习惯和不同的工作。我们更多的是灵活运用即可。 当然还是期望出现更多的基础数学专家,在基础方程和解方程方面取得突破,相信每一次的收获,对于这个计算密集的领域来说都是里程碑式的。