本节以视频的观看次数预测为例介绍机器学习的运作过程.假设有人想要通过视频平台赚钱,他会在意频道有没有流量,这样他才会知道自己能不能获利.假设从后台可以看到很多相关的信息,比如每天点赞的人数、订阅人数、观看次数.根据一个频道过往所有的信息,可以预测明天的观看次数. 我们想寻找一个函数,该函数的输入是后台的信息,输出是次日这个频道的预计观看次数.
机器学习的过程分为3个步骤.第1个步骤是写出一个带有未知参数的函数 ,它能预测未来观看次数. 比如将函数 写成
(1.1)
其中, 是要预测的值,假设这里要预测的是2月26日这个频道总的观看次数. 是这个频道前一天(2月25日)的观看次数. 和 是数值; 和 是未知的参数,我们只能隐约地猜测它们的值. 猜测往往来自对这个问题本质上的了解,即领域知识(domain knowledge).机器学习需要一些领域知识,比如一天的观看次数总是会跟前一天的观看次数有点关联,所以不妨把前一天的观看次数乘以一个数值,再加上一个 做修正,当作对2月26日观看次数的预测. 这只是一个猜测,不一定是对的,稍后我们再来修正这个猜测.
带有未知 参数(parameter) 的函数称为 模型(model) . 模型在机器学习里面,就是一个带有未知参数的函数, 特征(feature) 是这个函数里面已知的来自后台的信息——2月25日的观看次数 是已知的. 称为 权重(weight) , 称为 偏置(bias) .
第2个步骤是定义损失(loss),损失也是一个函数,记为 .损失函数输出的值意味着,当把模型参数设定为某个数值时,这个数值好还是不好.举一个具体的例子,如果我们猜测 , ,则函数 就变成了 .对于从训练数据中计算损失这个问题,训练数据是这个频道过去的观看次数.如果我们已经掌握2017年1月1日~ 2020年12月31日的观看次数(如图1.1 所示)接下来就可以计算损失了.
图1.1 2017年1月1日~ 2020年12月31日的观看次数(此处的观看次数是随机生成的)
把2017年1月1日的观看次数代入函数,得到
(1.2)
根据我们的猜测, 预测次日的观看次数为 ,但真实的观看次数为4900,我们高估了这个频道的观看次数. 可以评估一下估测值 跟真实值 间的差距.评估差距其实有多种方式,比如取二者差的绝对值:
(1.3)
真实值称为标签(label).
我们不仅可以用2017年1月1日的值来预测2017年1月2日的值,也可以用2017年1月2日的值来预测2017年1月3日的值.根据2017年1月2日的观看次数4900,我们预测2017年1月3日的观看次数是5400.接下来计算 5400跟标签(7500)之间的差距:
(1.4)
以此类推,把接下来每一天的差距通通加起并取平均,得到损失 :
(1.5)
其中, 代表训练数据数,即4年来所有的训练数据. 是每一笔训练数据的误差 平均以后的结果. 越大,代表我们猜测的这组参数越差; 越小,代表这组参数越好.
估测值跟真实值之间的差距,其实有不同的评估方法,比如计算二者差的绝对值,如式(1.6) 所示,这个评估方式所计算的值称为 平均绝对误差(Mean Absolute Error,MAE) .
(1.6)
也可以计算 二者差的平方,如式(1.7) 所示,称为这个评估方式所计算的值 均方误差(Mean Squared Error,MSE) .
(1.7)
在有些任务中, 和 都是概率分布,这时候可能会选择计算 交叉熵(cross entropy) .}刚才举的那些数字不是真实的例子,以下数字才是真实的例子,是用一个频道真实的后台数据计算出来的结果.我们可以调整不同的 和不同的 ,计算损失,并画出图1.2 所示的等高线图.在这个等高线图中,色相越红,代表计算出来的损失越大,也就代表这一对 越差;色相越蓝,就代表损失越小,也就代表这一对 越好. 将损失小的 放到函数里面,预测会更精准. 从图1.2 中我们得知,如果为 代入一个接近1的值,并为 代入一个较小的正值(比如100),则预测较精准,这跟大家的预期可能比较接近:相邻两天观看次数总是差不多的.
在图1.2 中,我们尝试了不同的参数,并且计算了损失. 这样画出来的等高线图称为 误差表面(error surface) .
图1.2 误差表面
机器学习的第3个步骤是解一个优化问题,即找到最好的一对 ,使损失 的值最小. 我们用符号 ,代表最好的一对 .
对于这个问题, 梯度下降(gradient descent) 是最常用的优化方法.为了简化起见,先假设只有参数 是未知的,而 是已知的.当为 代入不同的数值时,就会得到不同的损失,从而绘制出误差表面,只是刚才在前一个例子里面,误差表面是二维的,而这里只有一个参数,所以误差表面是一维的.怎么才能找到一个 让损失值最小呢?如图1.3 所示,首先要随机选取一个初始的点 .接下来计算 ,也就是 时,损失 对参数 的微分,得到 处误差表面的切线(即蓝色虚线)的斜率. 如果斜率是负的,就代表左边比较高、右边比较低,此时把 的值变大,就可以让损失变小. 相反,如果斜率是正的,就代表把 变小可以让损失变小.我们可以想象有一个人站在这个地方左右环视,看看左边比较高还是右边比较高,并往比较低的地方跨出一步.步伐的大小取决于以下两件事情.
图1.3 优化过程
● 这个地方的斜率,斜率大,步伐就跨大一点;斜率小,步伐就跨小一点.
● 学习率(learning rate) .学习率可以自行设定,如果 设大一点,每次参数更新就会量大,学习可能就比较快;如果 设小一点,参数更新就很慢,每次只改变一点点参数的值. 这种需要自己设定,而不是由机器找出来的参数称为 超参数(hyperparameter) .
Q: 为什么损失可以是负的?
A: 根据刚才损失的定义,损失是估测值和真实值的差的绝对值,不可能是负的.但如何定义损失函数是我们自己决定的,比如设置一个损失函数为差的绝对值再减100,它就可以为负了.损失曲线并不反映真实的损失,也不是真实任务的误差表面.因此,损失曲线可以是任何形状.
把 往右移一步,新的位置为 ,这一步的步伐是 乘上微分的结果,即
(1.8)
接下来反复执行刚才的操作,计算一下 的微分结果,再决定要把 移动多少,得到 ,继续执行同样的操作,不断地移动 的位置,最后我们会停下来.这往往对应两种情况.
● 第一种情况是在一开始就设定,在调整参数的时候,微分最多计算几次,如100万次,参数更新100万次后,就不再更新了,我们就会停下来. 更新次数也是一个超参数.
● 另一种情况是在不断调整参数的过程中,我们遇到了微分的值是0的情况,此时哪怕继续迭代,参数的位置也不再更新,我们因此而停下来.
梯度下降有一个很大的问题,就是有可能既没有找到真正最好的解,也没有找到可以让损失最小的 .在图1.4 所示的例子中,把 设定在最右侧红点附近可以让损失最小.但如果在梯度下降过程中, 是随机初始的位置,则也很有可能走到 时,训练就停住了,我们无法再移动 的位置.右侧红点这个位置是真的可以让损失最小的地方,称为 全局最小值(globalminimum) ;而 这个地方称为 局部最小值(local minimum) ,其左右两边都比这个地方的损失还要高一点,但它不是整个误差表面上的最低点.所以我们常常可能会听到有人讲梯度下降法不是什么好方法,无法真正找到全局最小值.
图1.4 局部最小值
在有两个参数的情况下使用梯度下降法,跟只有一个参数的情景没有什么不同.
假设有两个参数 和 ,它们的随机初始值分别为 和 . 在 、 的位置,分别计算 对 的微分以及 对 的微分:
(1.9)
计算完毕后,更新 和 . 把 减掉学习率和微分结果的积,得到 ;把 减掉学习率和微分结果的积,得到 .
(1.10)
在深度学习框架(如 PyTorch)中,微分都是由程序自动计算的.程序会不断地更新 和 ,试图找到一对最好的 . 可以将这个计算过程绘制成一系列点,如图1.5 所示,随便选一个初始值,计算一下 和 ,就可以决定更新的方向. 这是一个向量,见图1.5 中红色的箭头.沿箭头方向不断移动,应该就可以找出一组不错的 和 .实际上,在真正用梯度下降进行一番计算以后,有 .计算损失 ,结果是480. 也就是说,在2017年~ 2020年的数据上,如果使用这个函数,为 代入0.97,为 代入100,则平均误差是480.
图1.5 梯度下降优化的过程