预测问题-机器学习思考

假设空间与泛化能力。泛化能力的含义在上文也有说明,为强调起见,这里再叙述一遍:

  • 泛化能力针对的其实是学习方法,它用于衡量该学习方法学习到的模型在整个样本空间上的表现。

这一点当然是十分重要的,因为我们拿来训练模型的数据终究只是样本空间的一个很小的采样,如果只是过分专注于它们,就会出现所谓的过拟合Over Fitting)的情况。当然,如果过分罔顾训练数据,又会出现欠拟合Under Fitting)。可以用一张图来直观地感受过拟合和欠拟合(如图1所示,左为欠拟合,右为过拟合)。


1 欠拟合与过拟合

所以需要张弛有度,找到最好的那个平衡点。统计学习中的结构风险最小化(Structural Risk MinimizationSRM)就是研究这个的,它和传统的经验风险最小化(Empirical Risk MinimizationERM)相比,注重于对风险上界的最小化,而不是单纯地使经验风险最小化。它有一个原则:在使风险上界最小的函数子集中挑选出使经验风险最小的函数。而这个函数子集,正是我们之前提到过的假设空间。

注意:所谓经验风险,可以理解为训练数据集上的风险。相对应的,ERM则可以理解为只注重训练数据集的学习方法,它的理论基础是经验风险在某种足够合理的数学意义上一致收敛于期望风险,亦即所谓的真正的风险。

关于SRMERM的详细讨论会涉及诸如VC维和正则化的概念,这里不进行详细展开,但需要有这么一个直观的认识:为了使我们学习方法训练出的模型泛化能力足够好,需要对模型做出一定的限制,而这个限制就表现在假设空间的选取上。一个非常普遍的做法是对模型的复杂度做出一定的惩罚,从而使模型趋于精简。这与所谓的奥卡姆剃刀原理不谋而合:如无必要,勿增实体”“切勿浪费较多的东西去做,用较少的东西、同样可以做好事情

相比起通过选取合适的假设空间来规避过拟合,进行交叉验证(Cross Validation)则可以让我们知道过拟合的程度,从而帮助我们选择合适的模型。常见的交叉验证有以下三种。

  • S-fold Cross Validation:中文可翻译成S折交叉验证,它是应用最多的一种方法,其方法大致如下。 
    • 将数据分成S份:D={D_1,D_2,…,D_S},一共做S次试验。
    • 在第i次试验中,使用D-D_i作为训练集,D_i作为测试集对模型进行训练和评测。
    • 最终选择平均测试误差最小的模型。
  • 留一交叉验证(Leave-one-out Cross Validation):这是S折交叉验证的特殊情况,此时S=N。
  • 简易交叉验证:这种实现起来最简单,也是本书(在进行交叉验证时)所采用的方法。它简单地将数据进行随机分组,最后达到训练集约占原数据70%的程度(这个比例可以视情况改变),选择模型时使用测试误差作为标准。

 

该问题来自Coursera上的斯坦福大学机器学习课程,其叙述如下:现有47个房子的面积和价格,需要建立一个模型对新的房价进行预测。稍微翻译问题,可以得知:

  • 输入数据只有一维,亦即房子的面积。
  • 目标数据也只有一维,亦即房子的价格。
  • 需要做的,就是根据已知的房子的面积和价格的关系进行机器学习。

下面我们就来一步步地进行操作。

获取与处理数据

原始数据集的前10个样本如表1.1所示,这里房子面积和房子价格的单位可以随意定夺,因为它们不会对结果造成影响。

1.1 房价数据集 

2104,399900
1600,329900
2400,369000
1416,232000
3000,539900
1985,299900
1534,314900
1427,198999
1380,212000
1494,242500
1940,239999
2000,347000
1890,329999
4478,699900
1268,259900
2300,449900
1320,299900
1236,199900
2609,499998
3031,599000
1767,252900
1888,255000
1604,242900
1962,259900
3890,573900
1100,249900
1458,464500
2526,469000
2200,475000
2637,299900
1839,349900
1000,169900
2040,314900
3137,579900
1811,285900
1437,249900
1239,229900
2132,345000
4215,549000
2162,287000
1664,368500
2238,329900
2567,314000
1200,299000
852,179900
1852,299900
1203,239500

但一般而言,我们应该对它做简单的处理以期望降低问题的复杂度。在这个例子里,采取常用的将输入数据标准化的做法,其数学公式为:


# 导入需要用到的库
import numpy as np
import matplotlib.pyplot as plt

# 定义存储输入数据(x)和目标数据(y)的数组
x, y = [], []
# 遍历数据集,变量sample对应的正是一个个样本
for sample in open("./11.txt", "r"):
  # 由于数据是用逗号隔开的,所以调用Python中的split方法并将逗号作为参数传入
      _x, _y = sample.split(",")
      # 将字符串数据转化为浮点数
      x.append(float(_x))
      y.append(float(_y))
#读取完数据后,将它们转化为Numpy数组以方便进一步的处理
x, y = np.array(x), np.array(y)
# 标准化
x = (x - x.mean()) / x.std()
# 将原始数据以散点图的形式画出
plt.figure()
plt.scatter(x, y, c="g", s=6)
plt.show()

2 预处理后的数据散点图

这里横轴是标准化后的房子面积,纵轴是房子价格。以上我们已经比较好地完成了机器学习任务的第一步:数据预处理。

选择与训练模型

在弄好数据之后,下一步就要开始选择相应的学习方法和模型了。幸运的是,通过可视化原始数据,可以非常直观地感受到:很有可能通过线性回归(Linear Regression)中的多项式拟合来得到一个不错的结果。其模型的数学表达式如下。

注意:用多项式拟合散点只是线性回归的很小的一部分,但是它的直观意义比较明显。考虑到问题比较简单,我们才选用了多项式拟合。线性回归的详细讨论超出了本书的范围,这里不再赘述。


其中f(x|p;n)就是我们的模型,pn都是模型的参数,其中p是多项式f的各个系数,n是多项式的次数。L(p;n)则是模型的损失函数,这里我们采用了常见的平方损失函数,也就是所谓的欧氏距离(或说向量的二范数)。xy则分别是输入向量和目标向量;在我们这个样例中,xy这两个向量都是47维的向量,分别由47个不同的房子面积、房子价格所构成。

在确定好模型后,就可以开始编写代码来进行训练了。对于大多数机器学习算法,所谓的训练正是最小化某个损失函数的过程,这个多项式拟合的模型也不例外:我们的目的就是让上面定义的L(p;n)最小。在数理统计领域里有专门的理论研究这种回归问题,其中比较有名的正规方程更是直接给出了一个简单的解的通式。不过由于有Numpy的存在,这个训练过程甚至变得还要更加简单一些。

# 在(-2,4)这个区间上取100个点作为画图的基础
x0 = np.linspace(-2, 4, 100)
# 利用Numpy的函数定义训练并返回多项式回归模型的函数
# deg参数代表着模型参数中的n,亦即模型中多项式的次数
# 返回的模型能够根据输入的x(默认是x0),返回相对应的预测的y
def get_model(deg):
    return lambda input_x=x0: np.polyval(np.polyfit(x, y, deg), input_x)

这里需要解释Numpy里面带的两个函数:polyfitpolyval的用法。

  • polyfit(x, y, deg):该函数会返回使得上述(注:该公式中的x和y就是输入的x和y)最小的参数p,亦即多项式的各项系数。换句话说,该函数就是模型的训练函数。
  • polyval(p, x):根据多项式的各项系数p和多项式中x的值,返回多项式的值y。

评估与可视化结果

模型做好后,我们就要尝试判断各种参数下模型的好坏了。为简洁起见,我们采用n=1,4,10这三组参数进行评估。由于我们训练的目的是最小化损失函数,所以用损失函数来衡量模型的好坏似乎是一个合理的做法。

# 根据参数n、输入的x、y返回相对应的损失
def get_cost(deg, input_x, input_y):
    return 0.5 * ((get_model(deg)(input_x) - input_y) ** 2).sum()
# 定义测试参数集并根据它进行各种实验
test_set = (1, 4, 10)
for d in test_set:
# 输出相应的损失
    print(get_cost(d, x, y))

所得的结果是:当n=1,4,10时,损失的头两位数字分别为969475。这么看来似乎是n=10优于n=4,而n=1最差,但从图3可以看出,似乎直接选择n=1作为模型的参数才是最好的选择。这里矛盾的来源正是前文所提到过的过拟合情况。


3 线性回归的可视化

那么,怎么最直观地了解是否出现过拟合了呢?当然还是画图了。

# 画出相应的图像
plt.scatter(x, y, c="g", s=20)
for d in test_set:
    plt.plot(x0, get_model(d)(), label="degree = {}".format(d))
# 将横轴、纵轴的范围分别限制在(-2,4)、(〖10〗^5,8×〖10〗^5)
plt.xlim(-2, 4)
plt.ylim(1e5, 8e5)
# 调用legend方法使曲线对应的label正确显示
plt.legend()
plt.show()

上面这段代码的运行结果如图3所示。

其中,三条线分别代表n=1n=4n=10的情况(图1.10的右上角亦有说明)。可以看出,从n=4开始模型就已经开始出现过拟合现象了,到n=10时模型已经变得非常不合理。

至此,可以说这个问题就已经基本上解决了。在这个样例里面,除了交叉验证,我们涵盖了机器学习中的大部分主要步骤(之所以没有进行交叉验证是因为数据太少了……)。代码部分加起来总共40~50行,应该算是一个比较合适的长度。希望大家能够通过这个样例对机器学习有个大概的了解,也希望它能引起大家对机器学习的兴趣。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值