机器学习
1 线性回归问题
1.1 回归问题
回归问题,用于研究数据集中的特征数据(自变量)与标签数据(因变量)之间的关系或趋势。回归问题的目标是找到拟合函数,能够将每一个特征数据映射到标签数据上。
与分类问题相比,回归问题的目标值是连续的。
1.2 线性回归问题
回归问题中,如果特征数据与标签数据之间呈线性关系,则这个回归问题称为线性回归(Linear Regression)问题。线性回归,就是寻找数据中特征与目标之间的线性关系,在二维平面中,这种关系可以用一条直线表示。
线性回归模型中数据的每个特征都有各自的权重,若所有特征的权重都能确定,线性回归模型就构建完成了。回归模型的构建就是获取各个特征的权重系数。
1.3 案例 房价预估
根据已知的房屋面积和售价数据,构建线性回归模型来预测房屋价格。
import numpy as np
import pandas as pd
from pandas import DataFrame
from pylab import mpl
import matplotlib.pylab as plt
dic = {
'面积': [55, 76, 80, 100, 120, 150],
'售价': [110, 152, 160, 200, 240, 300]
}
df = DataFrame(data=dic)
# 默认不支持中文,通过设置RC参数字体让其支持中文。
mpl.rcParams['font.sans-serif'] = ['FangSong']
# 设置RC参数编码,让其支持负号。
mpl.rcParams['axes.unicode_minus'] = False
plt.scatter(df['面积'], df['售价'])
plt.xlabel('面积')
plt.ylabel('售价')
plt.scatter(df['面积'], df['售价'])
plt.xlabel('面积')
plt.ylabel('售价')
plt.title('面积和价钱的分布图')
plt.scatter(np.linspace(0, 180, num=100), np.linspace(0, 180, num=100) * 2, alpha=0.3)
上图中,用一条直线可以穿过所有的样本数据点,这条直线表示房屋面积与售价之间的线性关系,可以基于这条线对房屋价格进行预测。
1.4 线性方程
在线性回归问题中,使用线性方程来定量表示特征与标签之间的线性关系。
线性方程
其中x是自变量(特征),y是因变量(标签),w是斜率,b是截距。
变换,令,,得到:
上面的线性方程表示1条数据,具有n个特征。
假设样本中有m条数据,每条数据有n个特征,使用矩阵表示:,其中
2 误差与损失函数
2.1 介绍
实际上,能够使所有的数据点都落在拟合线上的理想模型是不存在的,误差一定存在,但是可以通过选取合适的参数使所有数据点的误差最小化,即保证所有的数据点距离拟合线的“距离”之和最小。
2.2 迭代
迭代是重复反馈过程的活动,其目的是为了逼近目标值,每次对过程的重复称为一次迭代,每次迭代得到的结果会作为下一次迭代的初始值,理论上每次迭代都会使系统更加完美。
回归算法是一种迭代算法,是通过不断进行自身迭代来减少误差,使得回归算法的预测结果逐渐逼近真实结果。
训练线性回归模型的过程
- 开始时将样本数据逐个输入到模型中,每次都会计算出一组参数,使得已输入的数据点产生的误差之和尽量小;
- 随着样本数据不断输入,理论上每次迭代得到的参数应该趋于稳定。在这组稳定的参数下,数据点产生的误差之和最小,模型的预测值能够非常逼近真实值。
2.3 损失函数
损失函数(Loss Function)可以定量地评估误差大小,通过具体数据反映出预测结果与实际值之间的差异程度。这里主要介绍平方损失函数。
2.3.1 平方损失函数
其中表示实际值,表示预测值。
平方损失函数是在计算实际值与预测值之间的欧氏距离。
2.3.2 范数
范数是向量空间中表示向量“长度”概念的函数。
向量,向量的p-范数为
- 范数
范数表示向量中非0元素的个数。 - 范数
范数表示向量中各个元素的绝对值之和。 - 范数
范数表示向量中各个元素的平方和,然后求平方根。
平方损失函数也可以称为L2损失函数。
根据范数的定义,计算参数向量使平方损失函数最小可以表示为
2.3.3 残差平方和
数据点与回归线上相应位置的差异称为残差,将每个残差平方之后加起来称为残差平方和(Residual Sum of Squares,RSS),也可以称为误差平方和(Sum of Sqaured Error,SSE)。残差平方和与平方损失函数在表达形式上相同。
残差平方和:
3 最小二乘法
最小二乘法(the least-squares solution),用于求解使欧式误差距离(即平方损失函数)最小时的方程参数。
3.1 推导过程
最小二乘法
其中,伪逆
3.2 sklearn - LinearRegression
最小二乘法求解线性回归问题。
sklearn.linear_model.LinearRegression(fit_intercept=True, normalize=False,copy_X=True, n_jobs=1)
参数介绍
fit_intercept - 是否计算截距,默认为True,如果使用中心化的数据,可以不考虑截距;
normalize - 标准化开关,指定进入回归前是否对特征矩阵进行标准化处理,即减去均值(中心化)+除以l2范式(缩放),默认为False;
copy_X - 是否在特征矩阵的副本上进行操作,默认为True,如果copy_X=False,原始的特征矩阵数据在回归过程中可能会被影响并覆盖;
n_jobs - 用于计算的作业数量,默认为1,如果n_jobs=-1,表示使用全部CPU资源进行计算。
4 评价指标
对于线性回归问题,一般通过衡量模型的预测值与标签的真实值之间差异程度来对模型做出评估。
主要通过以下两个角度来评估模型:
- 模型输出的预测值是否达到或接近真实值;
- 模型是否拟合到了足够的信息。
上面的两个角度分别对应两种评价指标,即均方误差MSE和R方。
4.1 均方误差 MSE
残差平方和RSS能够表示模型的预测值与真实值之间的差异,但是RSS会随着样本数量的增加而不断增长,而模型的评价指标一般不能受到样本数量的影响,因此使用RSS的均值作为回归问题模型的一个评价指标,即均方误差(Mean Squared Error,MSE)。
一般可以将均方误差与样本数据的最大值和最小值放在一起比较,评估误差的严重程度。
sklearn提供了两种方式计算均方误差
- 模型评估模块metrics的类mean_squared_error
from sklearn.metrics import mean_squared_error as MSE
# y_true: 测试数据的实际标签
# y_pred: 模型对测试集的预测结果
y_pred = lr.predict(x_test)
MSE(y_test,y_pred) # 0.531397330705225
- 交叉验证类的方法cross_val_score
设置参数scoring=‘neg_mean_squared_error’,得到的是负均方误差。
from sklearn.model_selection import cross_val_score
cross_val_score(lr, x_train, y_train, cv=5, scoring='neg_mean_squared_error').mean()
# -0.5309250456411255
实际的均方误差不可能为负数,但sklearn在计算模型评估指标时会考虑指标本身的意义,均方误差作为一种误差,被认为是一种损失,sklearn中的损失均以负数表示,因此这里得到的是负均方误差。
4.2 平均绝对误差 MAE
平均绝对误差(Mean Absolute Error,MAE),与均方误差MSE类似。
from sklearn.metrics import mean_absolute_error as MAE
# y_true: 测试数据的实际标签
# y_pred: 模型对测试集的预测结果
y_pred = lr.predict(x_test)
MSE(y_test,y_pred) # 0.531657868842987
from sklearn.model_selection import cross_val_score
cross_val_score(lr, x_train, y_train, cv=5, scoring='neg_mean_absolute_error').mean()
# -0.5316347823387871
4.3 R方
对于回归问题,仅评判模型的预测是否准确是不够的,还希望知道模型是否能发现并正确捕捉到数据中隐藏的规律,例如数据服从的分布规律,未来走势等,因此仅仅使用MSE或MAE衡量模型质量是不够的。
R方是回归模型建立后对数据拟合效果的一个评价指标,可以反映出模型(回归方程)对所有因变量的方差的解释程度,即能够解释的因变量方差占总体因变量的比例。越接近1,则模型的拟合效果越好。
例如表示模型能够解释85%的因变量方差,还有15%无法做出解释。
5 案例 加利福尼亚房价预测
5.1 数据介绍
目标:预测加利福尼亚房屋价值
特征介绍
AveBedrms - 该街区平均的卧室数目
Population - 街区人口
AveOccup - 平均入住率
Latitude - 街区的纬度
Longitude - 街区的经度
MedInc - 街区住户收入的中位数
HouseAge - 房屋使用年数中位数
AveRooms - 街区平均房屋的数量
5.2 训练模型
导入数据
import sklearn.datasets as datasets
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
house = datasets.fetch_california_housing(data_home='./datasets')
house.feature_names
'''
['MedInc',
'HouseAge',
'AveRooms',
'AveBedrms',
'Population',
'AveOccup',
'Latitude',
'Longitude']
'''
样本数据提取
# 样本数据提取
feature = house.data
target = house.target
feature.shape # (20640, 8)
回归模型一般不需要做特征预处理。
回归模型的构建本质上是为了寻找标签与特征之间的关系,模型中每个特征都有自己的权重系数,权重系数对特征数据具有调节作用,特征数据与权重系数一起反映了这个特征对结果产生的影响。
数据集切分
x_train, x_test, y_train, y_test = train_test_split(feature, target, test_size=0.2, random_state=2020)
训练模型
lr = LinearRegression(normalize=True, copy_X=False)
lr.fit(x_train, y_train)
查看模型参数
# 查看特征的权重系数
lr.coef_
'''
array([ 4.40832770e-01, 9.61642244e-03, -1.14781227e-01, 6.75973842e-01,
-5.10337874e-06, -3.47558983e-03, -4.19478386e-01, -4.32477249e-01])
'''
# 查看截距
lr.intercept_ # -36.77944310594462
将系数和特征结合在一起
[*zip(house.feature_names, linner.coef_)]
'''
[('MedInc', 0.44083277039517044),
('HouseAge', 0.009616422444042549),
('AveRooms', -0.1147812274695966),
('AveBedrms', 0.6759738423151265),
('Population', -5.103378739590361e-06),
('AveOccup', -0.003475589832444512),
('Latitude', -0.41947838567694345),
('Longitude', -0.43247724904169327)]
'''
5.3 评估模型
使用测试集评估模型
lr.score(x_test, y_test) # 0.6077878946185038
主要的评价指标
- 均方误差MSE
MSE表示每个样本的平均误差,用来衡量预测值和真实值的差异。
from sklearn.metrics import mean_squared_error as MSE
# y_test: 测试集的真实标签数据
# y_pred: 使用模型对测试集的预测结果
y_pred = lr.predict(x_test)
MSE(y_test, y_pred) # 0.531397330705225
y_test.min(), y_test.max() # (0.14999, 5.00001)
- R方
R方用于评估是否拟合到了足够的信息。
from sklearn.metrics import r2_score
# y_test: 测试集的真实标签数据
# y_pred: 使用模型对测试集的预测结果
y_pred = lr.predict(x_test)
r2_score(y_test, y_pred) # 0.6077878946185038
可以看出,模型的MSE很小,但R方却不高,说明模型虽然能较好地进行数据拟合,却未能正确捕捉到数据的分布情况。
通过绘制曲线说明,一条曲线表示真实数据y_true,另一条曲线表示模型预测结果y_predict。如果两条曲线的重叠程度越多,则说明模型拟合程度越好。
%matplotlib inline
import matplotlib.pyplot as plt
y_pred = lr.predict(x_test)
plt.plot(range(len(y_test)), sorted(y_test), c="black", label="y_true")
plt.plot(range(len(y_pred)), sorted(y_pred), c="red", label="y_predict")
plt.legend()
plt.show()
从图中可以看出,模型对大部分数据的拟合程度较好,但曲线的头部和尾部存在较大的误差。假设曲线右侧分布更多的数据,模型的预测值很可能越来越偏离真实值。
结果表明,模型在有限的数据范围上能够做出准确的预测,但无法正确地拟合出真实数据的分布情况,如果有更多的数据输入到模型中,模型预测出错误结果的可能性会很大。