一个简单回归案例:初识机器学习过程
7016字,阅读需时24分钟

【摘要: 应用身高—体重数据集,生成预测模型,该模型通过身高预测体重,通过这个案例了解编写机器学习程序的过程。】


人类学习就是从经验中获得知识和技能,人们通过阅读、沟通、听讲、研究、实践获取经验,然后再对经验进行梳理、分析和研究,最后形成知识和技能。

机器学习类似于人类学习,它也需要从外部获得经验,这里的经验是指输入到程序的经验数据,程序通过学习算法分析经验数据并从中学习,学习结果会形成一个模型(模型可以理解为程序从经验数据学到的知识和技能),程序使用该模型完成设定的工作,如预测和控制两个变量间的相互变化、机器翻译、语音识别等工作。

下面我们通过一个简单机器学习的案例,来理解机器学习的过程。案例内容是建立一个预测模型,该模型可以根据人的身高来预测人的体重,若人的身高和体重是线性关系,该模型将会正确地工作,当然一些特例数据可能会让模型预测失误。

编写机器学习程序的第一步是要搜集和整理用于建立预测模型的经验数据,现在我们手上有数据集SOCR-HeightWeight.csv(数据集仅限于学习使用),该数据集记录了25,000个18岁不同人的身高(英寸)和体重(磅),利用这个数据集,可以建立身高—体重预测模型。

数据集规模比较大,简单起见,我们抽取两个子集:一个子集作为经验数据(即训练数据);一个子集作为为测试数据。经验数据用于模型的建立和调试,测试数据验证模型的正确性。

训练数据子集为:train_hw.csv

测试数据子集为:test_hw.csv

编写机器学习程序的第二步是开发者根据经验数据确定大致的预测模型,可以使用matplotlib绘制经验数据的散点图,观察数据点的分布情况,发现身高和体重两个变量间的因果关系。

例1  绘制train_hw.csv数据子集散点图

案例代码见课程资源(unit1/case01.py)

# 导入numpy库
import numpy as np
import matplotlib.pyplot as plt
 程序入口
if __name__ == '__main__':
 
   # 从数据集文件读取1、2列
   data = np.genfromtxt('train_hw.csv',delimiter=',',dtype='float',usecols=[1,2])
   x = data[::,1]
   y = data[::,0]
   fig, ax = plt.subplots(1, 1)
   # 绘制散点图
   ax.scatter(x,y, s=20, c=x)
   plt.show()

train_hw.csv数据子集散点图如下图所示:

01.png

散点图X轴为身高,Y轴为体重。观察散点图发现,身高和体重呈现一定的线性关系,大致的线性关系可以用下面的直线进行拟合:

 02.png

拟合直线方程为:

f(x) = ax + b

其中a和b是待定系数,机器学习的主要工作就是依据给出的经验数据确定a和b的值,从而确定f(x)函数,也就是确定预测模型。

这种方法也称为线性回归,目标是建立一个系统,将向量x作为输入,预测标量y作为输出,线性回归的输出是输入的线性函数,令y表示模型预测y应该取的值,回归输出为:

y = ax + b

其中y是模型预测y的结果值,a是参数向量,其分量个数和向量x的分量个数相同,在本案例中a和x仅有一个分量,b是在y轴的截距,若b为0,该直线会通过坐标轴的原点,b也称为偏置参数。

现在问题的关键是如何确定a和b的值,让y(预测值)最接近y(真实值)。

y最接近y值,即预测值与真实值的差值最小,也就是预测值与真实值的偏差最小。

我们前面建立的测试数据子集就是用来度量预测模型的性能,度量方法是计算预测模型在测试数据集上的偏差。如果用y(test)表示预测模型在测试集上的预测值,那么总偏差表示为:

03.PNG 

 其中M是预测值与真实值的总偏差,y(test)是预测值,y(test)是真实值。

编写机器学习程序的第三步就是构建一个机器学习算法,通过学习训练集获得经验,减少M以改进系数a和b,最小化训练集上的总偏差M。

这种根据总偏差作为最小的条件来选择系数a、b的方法叫做最小二乘法,是线性回归经常采用的方法。

下面的问题是如何改进a和b的值,可以使M取得最小值。将预测模型代入总偏差公式:

04.PNG

在上面的公式中,我们希望使所有偏差的平方和最小,如何求最小值M呢?可以通过微积分的方法得到,把偏差的平方和看作函数,它有a和b两个变量,求这个函数的最小值。

该函数是二元二次函数,分别求变量a和b的偏导函数,令偏导数为0,M取得最小值。下面的Python程序求变量a和b的偏导函数。

例2  求变量a和b的偏导函数

案例代码见课程资源(unit1/case02.py)

from sympy import diff
from sympy import symbols
import numpy as np
# 定义计算偏导的函数
def func(data,a,b):
    exp = ""
    for i,item in enumerate(data):
       exp += "(" + str(item[0]) + "-" + "(" + str(item[1]) + "*a+b)) ** 2"
       if i != len(data) - 1:
           exp += "+"
    return eval(exp)
 
# 程序入口
if __name__ == '__main__':
 
    # 从训练数据集读取数据
    data = np.genfromtxt('train_hw.csv',delimiter=',',dtype='float',usecols=[1,2])
    a = symbols("a")
    b = symbols("b")
    # 计算变量a的偏导函数
    print(diff(func(data,a,b),a))
    # 计算变量b的偏导函数
    print(diff(func(data,a,b),b))

程序执行后,得到下面的方程组:

3368087.42631065*a + 25841.82342*b - 1763213.64534135
25841.82342*a + 200.0*b - 13628.21132

求解上面的方程组,即可求得a和b的值,确定回归输出的预测模型。我们使用Python的NumPy库来求解上面的方程组。

例3  求解方程组

案例代码见课程资源(unit1/case03.py)

# 导入numpy库
import numpy as np
# 程序入口
if __name__ == '__main__':
 
    # 建立2X2矩阵
    ta = np.array([[3368087.42631065,25841.82342],[25841.82342,200.0]])
    # 建立2维向量
    tb = np.array([1763213.64534135, 13628.21132])
    # 解线性方程组
    x = np.linalg.solve(ta,tb)
    # 输出x和y的值
    print("a = %.2f  b = %.2f" % (x[0],x[1]))

运行上面的Python程序,求得:

a = 0.08, b = 57.82

回归输出预测模型为:

y = 0.08x + 57.82

编写机器学习程序的第三步是开发者度量预测模型的性能,可以先直观上了解一下预测模型是否合适,使用matplotlib绘制训练数据和测试数据的散点图,同时绘制预测模型的直线方程。

例4  绘制预测模型效果图

案例代码见课程资源(unit1/case04.py)

# 导入numpy库
import numpy as np
import matplotlib.pyplot as plt
 
# 定义绘图函数
def plotter(ax, data1, data2, param_dict,type):
    if type == 0:
       ax.scatter(x,y,**param_dict)
    else:
       ax.plot(data1, data2, **param_dict)
  
 
# 获取预测模型直线方程数据
def get_line_data(x):
   y = 0.08 * x + 57.82
   return (x,y)
 
# 程序入口
if __name__ == '__main__':
 
   # 读取训练数据集
   data = np.genfromtxt('train_hw.csv',delimiter=',',dtype='float',usecols=[1,2])
   x = data[::,1]
   y = data[::,0]
  
   fig, ax = plt.subplots(1, 1)
   # 设置图例中文显示
   plt.rcParams['font.sans-serif'] = ['SimHei']
   # 绘制训练数据
   plotter(ax,x,y,{'color': 'b','label':'训练集'},0)
 
   # 获取预测模型直线方程数据
   x1,y1 = get_line_data(x)
   plotter(ax,x1,y1,{'color': 'm','label':'预测模型直线方程'},1)
 
   # 读取测试数据集
   data = np.genfromtxt('test_hw.csv',delimiter=',',dtype='float',usecols=[1,2])
   x = data[::,1]
   y = data[::,0]
   # 绘制测试数据
   plotter(ax,x,y,{'color': 'r','label':'测试集'},0)
 
   # 显示图例
   plt.legend()
 
   plt.show()

预测效果图如下:

05.png

从上图可以看出,预测模型的直线较好拟合了测试集合数据集。

度量回归预测模型的性能最常用的方法是计算预测值与真实值的均方误差(MSE),均方误差(MSE)的计算方法为总偏差除以样本个数,计算公式为:

06.PNG

例5  计算均方误差(MSE)

案例代码见课程资源(unit1/case05.py)

# 导入numpy库
import numpy as np
import matplotlib.pyplot as plt
 
# 定义计算MSE的函数
def calculation_mse(data,a,b,m):
    exp = ""
    for i,item in enumerate(data):
       exp += "(" + str(item[0]) + "-" + "(" + str(item[1]) + "*a+b)) ** 2"
       if i != len(data) - 1:
           exp += "+"
    mse = eval(exp) / m
    return mse
   
# 定义绘图函数
def plotter(ax, data1, data2, param_dict,type):
    if type == 0:
       ax.scatter(data1,data2,**param_dict)
    else:
       ax.plot(data1, data2, **param_dict)
  
 
# 获取预测模型直线方程数据
def get_line_data(x):
   y = 0.08 * x + 57.82
   return (x,y)
 
# 程序入口
if __name__ == '__main__':
 
   # 读取测试数据集
   data = np.genfromtxt('test_hw.csv',delimiter=',',dtype='float',usecols=[1,2])
   # 区间[0.01,0.2]创建50个数据点 
   x = np.linspace(0.01,0.2,50)
   y = []
   # 对每个x取值计算MSE
   for x1 in x:
       mse = calculation_mse(data,x1,57.82,len(data))
       y.append(mse)
   fig, ax = plt.subplots(1, 1)
   plotter(ax,x,y,{'color': 'm','label':'MSE'},1)
   # 计算x取值0.08的MSE
   a_mse = calculation_mse(data,0.08,57.82,len(data))
   plotter(ax,0.08,a_mse,{'color': 'b'},0)
   plt.text(0.08,a_mse,(0.08,a_mse),color='r')
   plt.grid(True)
   # 显示图例
   plt.legend()
   plt.show()

计算结果如下图所示:

07.png

上图是预测模型系数a在区间[0.01,0.2]的MSE曲线,从图中可以看出系数a在0.08处取得MSE最小值,均方误差(MSE)约为2.87,说明预测模型与测试数据集有较好的拟合度。

通过编写简单回归案例,我们现在已经了解了编写机器学习程序的基本过程。若把机器学习的目的看作是任务,完成该任务首先要准备数据集,数据集由训练和测试两个数据集构成,然后再设计一个学习算法,该算法从训练集的数据中学习,找出与任务适配的模型,还要设计一个度量模型性能的方法,用来度量模型在任务上完成的好坏。

读者留言
最新
推荐
郎宏林
授课老师
授课老师简介
项目经理,系统分析和架构师,从事多年中文信息处理技术。熟悉项目管理、擅长项目需求分析和设计、精通Java、C#、Python等编程语言。