陈颂光
全栈工程师,承接从编译器到网站的各类软件开发与咨询,也可以聊历史哲学。
关注我的 GitHub

机器学习怎样做预测

对于比较明确的需求如排序和计税,我们通常会直接编写满足要求的“函数”。但对于一些更多地涉及人文背景的问题如翻译和人脸识别,准确地转化为计算机术语可以认为是不可能的。在过往,为了解决这些问题,我们往往要为每个问题单独设计非常专门的解法,但人手设计的启发式规则很快会变得难以维护,又因重复劳动不利于总结提高而难以达到理想的效果。近年,随着收集大量数据变得容易,基于(有监督)机器学习的方法取得了主导地位,它的思想是用统一的框架解决不同的问题:给定一些键值对然后设法自动找出一个(近似)包含这些键值对的映射。

基本概念

映射是数学最核心的概念,众多问题(但不是全部)都可以视为映射:

  • 邮件分类可以视为一个从字符串集到布尔值集的映射,把字符串对应到它是否无用信息
  • 机器翻译可以视为一个从字符串集到字符串集的映射,把来自一个语言的字符串对应到另一语言中语义相同的字符串
  • 人脸识别可以视为一个从人脸图片集到人集的映射,把人脸图片对应到人脸所属的人
  • 天气预测可以视为一个从数列集到实数的映射,把过往若干天的气温(或其它天气数据)对应到明天的气温(或其它天气数据)

对于给定的映射$f:X\to Y$,我们希望对于每个定义域中的值$x$,可以用计算机算出$f(x)$。对于一些对计算机来说是精确叙述的问题如排序,这是可能严格做到的:我们可以直接设计具体的计算方法。但对于涉及到计算机以外复杂概念的问题,通常不能期望能完全准确地算出。于是,我们退而求其次,找一个能够计算的映射$g$去近似$f$。

为了用统一的框架自动地构造近似映射$g$,当然我们必须给出关于$f$的一些信息。机器学习要求的信息就是映射在部分点处的值$(x_1,f(x_1)),\dots,(x_M,f(x_M))$。显然即使给定了一个映射在一些点的值,它在其它点处的值仍然可以是任意的,根据世界是简单的信念,机器学习企图用相对“简单”的映射$g$去逼近。更准确地说,我们假定$g$形如$g(\cdot;\theta_1,\dots,\theta_N)$,然后我们算出算出参数$\theta_1,\dots,\theta_N$的估计值使$f$与$g$在已知点处的值在某种意义下最接近,如$\sum^M_{i=1}d(f(x),g(x))$最小(其中$d$为某种“距离”),这是一个典型的优化问题并可以用常规的数值方法求解。与基于大数定律的统计方法一样,只有在数据足够多时我们才能指望学习得出的映射$g$确实能近似$f$。幸运的是,随着互联网的兴起,数据源源不断地从人和各种传感器产生,收集数据变得容易,机器学习因而在许多问题变得可行。

由于数学方法一般在欧氏空间中最好处理,因此往往设计一种编码$e: X\to \mathbb{R}^k$把$f$定义域中的对象对应到某个固定维数的欧氏空间,再设计一种解码单射$d: \mathbb{R}^l\to Y$把某个固定维数的欧氏空间中向量对应到$f$值域中的对象,从而可以把问题归结为寻找近似映射$h:\mathbb{R}^k\to\mathbb{R}^l$再令$g=d\circ h\circ e$即可。以下是一些表示方式:

  • 对于图像识别问题,图像可以对应到各像素亮度组成的向量,反之亦然
  • 对于文本分类问题,文章可以编码为各单词频率组成的向量(这会丢失单词顺序信息)
  • 对于序列预测问题,序列数据可以编码为最近若干个数据(这会丢失长时依赖信息)
  • 对于一般的$n$类分类问题,各类可以分别对应于$(1,0,\dots,0),(0,1,0,\dots,0),\dots,(0,0,\dots,0,1)$

顺便指出,虽然上面主要谈监督学习。但某些非监督学习问题可转化为监督学习问题。例如有损压缩问题相当于寻找压缩函数$f:X\to Y$和解压函数$g:Y\to X$使$g\circ f$在某种意义下接近恒同映射,于是我们可以设计一个神经网络,各层中神经元个数先是递减再递增,输入数与输出数相同,数据集中数据同时用作输入和输出去训练网络,最后前半个神经网络就可作为压缩器而后半个神经网络就可作为解压器。类似技术还可以用于生成文本或图像之类。

流程

在一个开发迭代中,典型地需要经历以下过程:

  1. 理解问题。和其它软件项目一样,我们首先需要理解需求:
    • 了解应用场景,包括数据的来源和结果的用途
    • 明确需要预测的是什么量,它是什么性质的(如数值/枚举)
    • 明确我们能用哪些量去预测它,它们是什么性质的(如数值/枚举),比如我们可能想用过去10天的价格预测明天的价格(时间序列)
  2. 收集数据。对于有监督学习,需要一些数据(包括自变量和因变量的值)供训练和测试。收集数据有不同渠道,例如:
    • 寻找现有的数据集
    • 记录来自传感器的数据(如物联网应用)
    • 在网站、应用程序加入跟踪代码
    • 用爬虫抓取数据(如文本或语义标记)
    • 生成模拟数据(如对文本识别可用字体生成各字符的样本)
    • 加工数据以生成更多数据(如对于图像检测可对图像进行各种旋转、模糊等变换来生成更多图片)
  3. 准备数据
    • 提取数据。
      • 从数据库、日志、文件系统导入数据
    • 探索数据。通过观察(部分)数据,可以得到一些启发:
      • 通过计算字段值的分布,可能可找出重要的字段。
      • 通过计算字段间的相关性,可能可发现字段间的关系。
      • 通过可视化数据,可能可发现离群值,它们可能对应于诈骗等异常情况。
    • 清理数据。现实世界中收集到的数据质量往往参差不齐,需要经过一些预处理才适合作为机器学习的数据集。
      • 删除重复记录。有时由于各种原因导致同一记录被收集了多次,这时往往应该删去主键相同的重复记录。
      • 删除多余字段。有的字段对建立模型用处不大甚至有误导性,同时也导致建模过程中浪费更多空间和时间,以下字段可能应该删去:
        • 大部分记录值相同的字段。因为能提供的信息较少。
        • 缺失值太多的字段。因为能提供的信息较少,而且往往说明字段的值不易得。
        • 相关性强的变量。因为当两个字段能大致相互决定时,保留一个已经足够。
        • 主键。因为主键只应用于区分记录。
      • 填补缺失值。部分模型不容许数据集有缺失值,这时可以考虑用以下方法补上缺失值:
        • 使用一个普通记录不使用的特殊值(主要适用于枚举型变量)
        • 使用其它记录计算字段的均值、中位数或众数
        • 使用随机值
        • 建立一个用其它字段去预测本字段的模型,然后用预测结果代替缺失值。实际上,所谓的半监督学习就在干这事情,如可以先用已有棋谱训练两个下棋机器人,通过让这两个机器人下棋去得到更多棋谱。
      • 正规化。部分模型要求不同数值型字段的数量级可比,于是可能需要标准化,例如:
        • 0-1正规化。令某字段的值分别为$x_i(i=1,\dots,m)$,则分别正规化为$\frac{x_i-\min_jx_j}{\max_jx_j-\min_jx_j}\in [0,1]$。
        • Z正规化。令某字段的值分别为$x_i(i=1,\dots,m)$,它们的均值为$\mu$、方差为$\sigma$,则分别正规化为$\frac{x_i-\mu}{\sigma}$,有时也可用协方差同时正规化多个变量。
      • 重新分类。
        • 重新划分。对于枚举型字段,有时字段的值太多或者不同值的频率相差太大,这时一些模型的效果会较差,因而可能需要合并一些类别,甚至可能要为此抛弃部分记录。
        • 数值分箱。对于数值型字段,在希望用离散方法处理时要把数值分为若干类,这可以借助聚类方法,或者粗暴地把分为等长的区间或等频的区间。
      • 审查离群值。数据中可能存在录入错误,以下观察往往有助找出部分可疑记录:
        • 低频值。对于枚举型变量,如果发现一些出现频率特别低的值,则可能有错。
        • 极端值。对于数值型变量,如果发现一些特别大或特别小的值(比如Z正规化后绝对值很大或在特定的分位数范围外),则可能有错。
      • 其它变换。另外还有一些因问题而异的数据变换,比如年龄与出生日期间的转换或者各种格式转换。
    • 降维。有时变量很多但其中有的纯属噪声或者是多余的,不但会浪费空间和时间,还可能起干扰作用(例如多重共线性会使线性回归不稳定),于是可能需要减少变量的个数。
      • 特征选择。即仅保留部分变量而删去其它,原则上我们想选择变量组使模型精度最高或者损失信息与删去变量数相比最小,但由于可能的变量子集太多,通常不会遍历而用启发式策略如:
        • 后向删除。由全部变量出发,每步删掉一个“最无用变量”
        • 前身选择。由没有变量出发,每步加入一个“最有用变量”
      • 特征提取。利用现有的字段去计算另一组字段,比较常见的方法都取现有字段的一些线性组合:
        • 主成分分析。当以协方差矩阵的特征向量组为正交基,则首个分量方差最大(即最“有用”)。通过选择前面若干个主成分(特征向量)使方差(特征值之和)贡献超过某个比例(如50%)作为新的变量组,可以在保持较多信息的情况下减少变量数。另外,应该确保由训练集和测试集求出的主成分近似对应。
        • 因子分析。假设自变量是另一组变量的线性组合。
    • 划分数据。数据集中的记录通常要分为训练集(用于建立模型)、验证集(用于选择模型,不是所有模型类型都需要)和测试集(用于检验模型)。
      • 比例划分。把数据集随机划分为二到三部分使它们的记录数约为7:3或8:1:1之类的比例。
      • k折交叉验证。把数据集划分为k等份(通常k=10),然后做k个模型,每个模型分别用k-1份数据做训练集而用余下的一份做测试集,最后用这k个测试结果平均出总测试结果。这样可以使测试集更大,并且让每个记录都用于测试一次相对公平。
  4. 训练模型。
    • 选择模型类型。
    • 利用训练集去训练模型。
  5. 评估模型。因为模型通常对“见过”的数据更为准确(正如一些学生能背出老问题的答案,但题目改一点就不会做了,其实她并没有学会),需要使用它未“见过”的记录去测试才能检验模型的泛化能力,而不是过度拟合。
    • 对于分类问题,常用的指标有:
      • $\text{准确率}=\frac{\text{被正确分类的记录数}}{\text{记录数}}$越高越好
      • 对于每个类,以下指标越高越好:
        • $\text{召回率}=\frac{\text{类中被判定为此类的记录数}}{\text{类中的记录数}}$
        • $\text{精度}=\frac{\text{类中被判定为此类的记录数}}{\text{被判定为此类的记录数}}$
        • $\text{F1}=\frac{2\times\text{精度}\times\text{召回率}}{\text{精度}+\text{召回率}}$,有时也对精度和召回率加不同权(如杀错和放过的危害可能不同)
    • 对于回归问题,常用指标有:
      • $\text{均方差}=\frac{\sum\vert \text{预测值}-\text{实际值}\vert^2}{\text{记录数}}$
    • 有时候除准确度外,也要考虑与时间和空间代价的平衡
  6. 部署模型。把模型应用到产品中以满足需求,而利用产品往往可收集更多数据(特别是用户反馈)以进一步改进模型。

分类

预测的量是枚举型(即$f(X)$有限)的预测问题称为分类问题,这时我们可要求拟合映射$g:X\to f(X)$的值域也是枚举型的。

常用的分类器

最近邻算法

对于每个待分类点,在训练集中找出与之最接近的一个样本点,然后把待分类点判定为这样本点所属的类。最近邻算法非常简单,还可以通过调整距离函数来控制不同变量的重要性。最近邻算法的缺点在于分类时需与所有训练样本比对(虽然用kd树或R树之类的数据结构往往可避免大部分比较),训练集较大的话时间和空间复杂度比较高。

决策树

决策树是一棵树,每个叶子标上类,而每边标上一个条件。分类时从树根出发,检验各出边对应的条件,沿满足的边走,直到到达叶子即知该判定为哪个类。决策树的优点在于可解释性,缺点在于容易产生过度拟合。构造决策树的有多种方法,但基本方法为递归划分训练集,以下以C4.5为例说明:

  1. 把所有训练记录都分配给根
  2. 对每个结点
    • 若结点中所有记录属于同一类,则把结点标记为该类
    • 否则,
      1. 找出对结点中记录信息增益最大的变量
      2. 对变量的每个取值建立一个结点并把当前结点对应于该取值的记录都分配给这子结点,并设立当前结点到新结点的边,条件为上述变量有对应取值
  3. 重复2.直到树不再变化
  4. 利用验证集剪枝:把错误率太高的子树换成叶子,标记为其中样本最常见的类

支持向量机

支持向量机的想法是用某个函数$\phi:\mathbb{R}^n\to\mathbb{R}^l$把样本投射到高维空间,然后企图用高维空间中超平面把两类($y\in\{1,-1\}$)的点分开。更准确地,它求解以下的优化问题:

$\begin{align} \min_{b,\omega,\xi} &\frac{1}{2}\Vert\omega\Vert^2+C\sum^m_{i=1}\xi_i\\ y_i(\omega^T\phi(x_i)+b)&\geq 1-\xi_i\\\xi_i&\geq 0\end{align}$

然后对判决函数由$y=\mathrm{sgn}(\omega^T\phi(x)+b)$给出。对于多类分类问题,需要化为一些二类分类问题再用支持向量机,对于$n$类分类问题:

  • 一对多方法训练$n$个分类器,分别把一类的记录作为正例而其它所有记录作为负类,分类时分别计算这些分类器的决策值,取决策值最高分类器对应的正分类
  • 一对一方法训练$\frac{n(n-1)}{2}$个分类器,分别把一类的记录作为正例而另一类的记录作为负类,分类时分别计算这些分类器的决策值,选取在最多分类器胜出的分类

支持向量机需要调的参数不多(系数$C$和核),适合制作分类器原型。为了对支持向量机形成更直观的认识,建议玩一下这玩具,感受一下在二维情况下它如何划分平面。

人工神经网络

人工神经网络实际上是通过对“简单映射”进行复合来构造逼近函数族的方法,其中的“简单映射”称为神经元。人工神经网络可以用图直观地表示,其中每个顶点是神经元,值沿着有向边在神经元间流动,直至到达输出神经元成为整个逼近映射的值的一个分量。设计人工神经网络时把它分为若干层,大多数边从一层指向下一层。反馈神经网络中也可能存在反向的边,和时序电路中用类似方法实现记忆类似,这种设计被认为可模拟人“越想越像”的记忆,有利于上下文感知。所谓深度学习说是就是层数较多的意思,通常认为后面的层次保存了较高层次(更整体)的信息。

更准确地说,一个典型的神经元对输入$x_1,\dots,x_k$输出$y=\frac{1}{1+\exp(-(\sum^k_{i=1}\omega_ix_i+\omega_0))}$,其中各系数$\omega_i$是训练时求出的参数。训练实际上是一个最优化问题,求出参数$\Theta$使损失函数如$\sum^M_{i=1}\vert y_i-f(x_i;\Theta)\vert^2$最小(应用中为避免把所有样本放进内存,每步中只考虑一小批样本对应的损失),最优化通常采用梯度下降法,它的基本形式是数值计算损失函数关于参数$\Theta$的梯度,然后让参数走负梯度的一个倍数(学习率,通常是越来越短),重复这过程到几乎收敛。

人工神经网络模型可以非常强大,目前许多最好的模型都是人工神经网络。然而,网络的设计非常关键,是一门艺术。

概率方法

与其它模型相比,概率模型一般需要对数据做假设,从而能在理论上分析。

  • 最大似然模型。求$\arg\max_{y}P(y\vert x)=\arg\max_{y}P(y\cap x)$作为预测的分类。在假设各自变量独立(朴素贝叶斯情况)时前式化为$P(y\vert x_1)\dots P(y\vert x_n)$,在更一般情况则假设贝叶斯网络再行计算。最大似然模型比较适合按大量同质变量分类。
  • 最大熵模型。求$\arg\max_{ {E_p}(f_i(x))=E_{\hat{p}}(f_i(x))}H(p)$,其中$H(p)=-\sum_{x}p(x)\ln p(x)$为熵、$f_i$为一些特征、$\hat{p}$为频率。最大熵模型比较适合按异质变量分类。

元分类器

通过组合多个分类器,可能可以使分类结果更稳定,因为多个分类器同时失误的可能性比一个分类器失误的可能性低。表决方法就是用多个子分类器分别进行分类,然后通过投票方法得出最终判定的类别。投票通常采用简单多数规则,有时也容许不同子分类器有不同的票数。以下是一些例子:

  • 装包方法通过放回抽样从原训练集生成多个训练集,分别用于训练多个子分类器,分类时表决这些子分类器分别得到的分类结果。
  • 提升方法依次构建多个分类器,为被上一分类器错判的记录赋予更高的权重(初始时所有记录权重相等)去训练下一分类器,并为分类器赋予与其错误率有关的权重。
  • k-近邻算法找出训练集中与待分类点之间最接近的k个样本点,然后通过表决选取这k个样本对应的类之一。它的一些变种用与距离有关的系数加权,使越近的样本有越大的影响力。
  • 随机森林算法选取不同的变量子集分别构造决策树,分类时表决这些决策树分别得到的分类结果。

回归

当要预测的变量是数值型而不是枚举型时,这种问题称为回归而不是分类。以下是一些回归的方法:

  • 线性回归
  • 人工神经网络本身就可以产生数值型输出
  • 支持向量机修改为回归形式求出回归关系$b\sim \omega^T\phi(x)+b$: $\begin{align} \min &\frac{1}{2}\Vert\omega\Vert^2+C\sum^m_{i=1}(\xi^+_i+\xi^-_i)\\ y_i-\omega^T\phi(x_i)-b&\leq\epsilon+\xi^+_i\\+\omega^T\phi(x_i)+b-y_i&\leq\epsilon+\xi^-_i\\\xi^+,\xi^-&\geq 0\end{align}$

软件包

以下列举Java平台中一些常用的机器学习工具包:

  • Weka是一个提供大量不同算法的机器学习库,包括从数据加载、预处理、分类、聚类、关联规则挖掘、评价到可视化的工具链,支持本文提到的所有分类器
  • libsvm是一个支持向量机包
  • liblinear是一个线性分类器包,比libsvm的线性核快
  • deeplearning4j是一个深度学习库

应用

监督机器学习已经取得了一些应用,以下是一些典型的例子:

  • 语音技术
    • 语音唤醒。助听器或录音笔可能应在只有环境噪声时关闭而在有对话声时开启。
    • 语音识别。把语音转换为文字并按句子/说话人分解后才适合用其它自然语言处理技术继续处理。
    • 语音合成。播报系统或失明人士辅助系统需要把文本读出。
  • 图像技术
    • 对象检测。找出图像中的特定对象(如文字或人)。
    • 图像识别。确定图像中的对象是什么类型的,有时也作更细的分类:
      • 文字识别。把文本图像转换为文字后才适合用其它自然语言处理技术继续处理。
      • 人脸识别。门禁系统需要知道抓拍到的人是谁。
    • 图像标注。为图像加上注释。
  • 视频技术
    • 视频内容审核。容许用户上传视频的平台会检测视频是否含有不良内容或涉及盗版。
    • 视频内容分析。运动跟踪系统需要确定视频中的人在干什么。
    • 视频封面选图。在视频网站的搜索结果页中呈现有吸引力的截图可提高点击率。
  • 自然语言处理
    • 基础技术
      • 分词
      • 命名实体检测
      • 词性标注
      • 文本语法解析
      • 文本生成
    • 文本分类
      • 内容审核。各大支持评论的网站如新闻平台、社交平台和讨论区会判定留言是否涉及敏感内容然后删除可疑留言。
      • 情感分析。一些电商网站会把评论分为正面或负面再分别呈现。
      • 通顺程度。编辑软件可以检测行文的质量,甚至用于批改作文。
    • 人机对话
      • 语音控制。一些手机、导航仪和其它电器可以用口语控制。
      • 客服系统。一些网站的客服功能可自动回答常见的疑问。
    • 机器翻译
  • 排名
    • 搜索结果排名。搜索引擎需要确定两个结果的相对排名或者各种排序指标的相对重要性。

结语

已经爆破过至少两次的“人工智能”泡沫在2010年代又再鼓起来了,这次靠的就是机器学习。什么才叫具备智能?既然人们自认为人有最高的智能,那么与人越接近就越智能。于是图灵提出了一个测试智能的方法:让人通过文字传送设备与另一方聊一会天,然后猜测对方是人还是机器。当重复这试验时,如果前者能以接近100%的准确率判定对方是否机器,则说明该机器不具备智能;反之,如果前者只能以约50%的准确率判定对方是否机器,即机器与人几乎不可区分,则说明该机器具备智能。既然人机对话直接是典型的机器学习问题,这一次的确可能在图灵测试上更为成功。不过可以肯定的是,当机器真正具备智能时,机器也将失去它相对于人的最大的优点。

我们承认,机器学习方法比过去的方法更好地解决了一些问题。与此同时,我们必须认清机器学习方法的局限性。在过度吹捧技术的潮流下,更应当始终冷静地谨记,经济基础才是根,技术什么的都只能是配角,只有能带来经济效益的技术才是好技术,相反整个社会都会为发展亏本技术这种资源错配付出代价。这个危险已经客观存在,已经有号称“人工智能”的服务被揭发其实只是让大批廉价劳工在后台代劳,这说明雇用真人可能比发展“人工智能”技术更划算,押在“人工智能”上的投资是风险甚高的豪赌。

机器学习方法大多是暗箱操作的,基于机器学习方法用到锦上添花的场合也许无伤大雅,但如果应用到关键的场合则可能引致严重的后果:一个人可能因人脸识别系统出错而被视为杀人犯继而被执行死刑。试想一个更实际的情况是,求职、入学时用人单位都用回归预测申请者日后表现并由此决定是否录取,于是一个人的种族、性别、地域、家庭背景注定了其能否接受良好的教育,继而决定就业机会,这将会使社会恢复到一个充满对出身的歧视的状态,个人的努力无力阻止跨代贫穷。这种不公平对于坐在空调房间看这篇文章的程序员来说,可能觉得是好事,因为你们很可能就是既得利益者,直至无产阶级革命的到来。

关键词 人工智能