第一个机器学习样例——学习笔记

Python 与机器学习实战 何宇健 2018年10月第8次印刷

该问题来自Coursera上的斯坦福大学机器学习课程:

现有47个房子的面积和价格,需要建立一个模型对新的房价进行预测

即有这样的理解:

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

虽然机器学习算法很多,但通常而言,进行机器学习的过程会包含以下三步:

  • 获取与处理数据
  • 选择与训练模型
  • 评估与可视化结果

1.获取与处理数据

数据集为房子面积和房子价格的,保存为txt文件

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

可以看到数据集中的数字都相当大,为了降低问题的复杂度,我们对房子面积数据进行标准化处理,公式为:

                                                 第一个机器学习样例——学习笔记

std(X)为X的标准差,第一个机器学习样例——学习笔记为表示X(房子面积)的均值

代码实现为:

# 导入需要用到的库,并定义为简写引用
import numpy as np
import matplotlib.pyplot as plt

# 定义存储输入数据(x)和目标数据(y)的数组,事先分配空间
x, y = [ ], [ ]
# 遍历数据集,变量sample对应的正是一个个样本
for sample in open("../_Data/prices.txt","r"):
# 由于数据是用逗号隔开的,所以调用Python中的split方法,以逗号(,)为分割,逗号前赋值给xx,逗号后赋值给yy
    xx, yy = sample.split(",")
# 将字符串数据xx和yy转化为浮点数,并依次添加到列表x和y的最后
    x.append(float(xx))
    y.append(float(yy))
# 读取完数据后,将列表x和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)=p0x^n+p1x^(n-1)+…+pn-1x+pn
L(p;n)=0.5∑[f(x|p;n)-y]^2

        其中f(x|p;n)就是我们的模型,p、n是模型参数,p是多项式f的各个系数,n是多项式次数。

        L(p;n)是模型的损失函数(平方损失),也就是所谓的欧氏距离(或说是向量的二范数)。在这个例子中,x,y这两个向量都是47维,分别由47个不同房子的面积、房子价格所构成。我们的目的是使L(p;n)最小,这样的模型是最优的。

实现代码如下:

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

#理解该训练模块:np.polyfit(x, y, deg)代表通过x,y和deg拟合出一个多项式函数,np.polyval(np.polyfit(x, y, deg), input_x)表示通过此拟合出的多项式函数和input_x(即x0)输出返回x0对应的y值。:之前的内容,表示后面的式子成立条件
def get_model(deg):
    return lambda input_x=x0: np.polyval(np.polyfit(x, y, deg), input_x)

3.评估和可视化结果

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

代码实现:

# 根据参数n、输入的deg、x和y返回相应的损失

#理解该计算损失函数模块:get_model(deg)()默认表示输入为input_x=x0,get_model(deg)(input_x)表示输入为input_x的矩阵得到对应输出预测y的矩阵,(get_model(deg)(input_x) - input_y)通过拟合函数计算得到的预测y的矩阵和实际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))

输出结果:

96732238800.35297
94112406641.67743
75874846680.09282

可视化结果

第一个机器学习样例——学习笔记

可视化代码如下:

# 画出相应的图像
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()

4.结论

        按照损失函数所得到的结果看,似乎n=10优于n=4,而n=1最差,但是从图中可以看出选择n=1比较好,n=4开始模型就出现过拟合现象了,n=10时已经十分不合理了。至此这个问题基本解决。