回归分析是一种预测性的建模技术,它研究的是因变量和自变量之间的关系。它能够表明自多个自变量对一个因变量的影响强度。这种技术通常用于预测分析、时间序列模型以及发现变量之间的因果关系。回归分析是一种通过建立模型来研究变量之间相互关系的密切程度、结构状态及进行模型预测的有效工具,是建模和分析数据的重要工具。
回归算法源于统计学理论,它可能是机器学习算法中产生最早的算法之一,其在现实中的应用非常广泛,包括使用其他经济指标预测股票市场指数,根据喷射流的特征预测区域内的降水量,根据公司的广告花费预测总销售额,或者根据有机物质中残留的碳-14的量来估计化石的年龄等等,只要一切基于特征预测连续型变量的需求,我们都使用回归技术。
多元线性回归基本原理
回归方程
线性回归(Linear Regression
)是机器学习中最简单的回归算法,多元线性回归指的就是一个样本有多个特征的线性回归问题。对于一个有 个特征的样本 而言,它的回归方程:
其中, 为模的参数,为截距(intercept
);为回归系数(regression coefficient
); 是目标变量,即标签; 是样本上的不同特征。
可以使用矩阵来表示这个方程,其中 可以被看做是一个结构为的列矩阵, 是一个结构为的特征矩阵,则有:
简写为:
在19世纪的英国,有一位著名的生物学家高尔顿,在研究父母和孩子身高的遗传关系时,发现了一个直线方程,通过这个方程,他几乎准确地拟合了被调查父母的平均身高 和子女平均身高 之前的关系:
y=3.78 0.516x
。它代表父母身高每增加1个单位,其成年子女的平均身高只增加0.516个单位,反映了一种"衰退"效应("回归"到正常人平均身高)。虽然之后的 与 变量之间并不总是具有"衰退"(回归)关系,但是为了纪念高尔顿这位伟大的统计学家,"线性回归" 这一名称就保留了下来。
损失函数
最小二乘法解释损失函数
一般情况误差 有正有负,如果直接对 求和,则会出现正负抵消的情况,反映不出整体误差情况。如果使用平方和,不仅可以避免正负抵消的缺陷,而且整体的误差分布不变,所以一般使用平方和做损失函数。
其中 是样本 对应的真实标签, (即)是样本 在一组参数 下的预测标签。
由于每个样本的数据里存在较大差异,为了消除样本间差异的影响,使用最小化均方误差MSE
拟合,并得到损失函数。
其中 是为了求导计算的便利,而 是将损失平均化,消除样本量m带来的影响。
极大似然估计解释损失函数
误差 中,误差是独立同分布的,并且服从 的高斯分布。其表达式为
由于误差服从均值,方差为 的高斯分布
从式中可以看出,误差 越小, 概率越大,说明预测值与真实值越接近。
因线性回归模型是一条直线(或超平面)拟合多个点,所以需要满足所有误差 取得最小值,即所有概率的乘积最大化,符合似然函数
式中第一项为定值,因此需要第二项最小化,于是得到损失函数
同样乘以一个 以消除样本量带来的影响, 将损失平均化。
损失函数衡量了所构造的模型的预测结果和真实标签的差异,希望预测结果和真实值差异越小越好,即求解目标函数可以转化为
其中右下角的2表示向量 的L2
范式,称其为SSE
(Sum of Sqaured Error
,误差平方和)或者RSS
(Residual Sum of Squares
残差平方和)。
多元线性回归的参数求解
最小二乘法
通过最小化真实值和预测值之间的RSS
来求解参数的方法叫做最小二乘法。
以上用到的规则:详情请见[https://blog.csdn.net/mounty_fsc/article/details/51588794]
令(2)求导后一阶导数为0:
在这里可以看出,注意到是一个矩阵乘以它自身的转置,得到的是一个实对称的方阵。如果它是可逆的,则等式左右两边同乘 ,从而得到 。因此,逆矩阵存在的充分必要条件是特征矩阵不存在多重共线性。下篇会详细介绍多重共线性问题及解决。
梯度下降法
前面使用最小二乘法求出使得损失函数最小化的参数 ,而实际问题中常使用梯度下降法。首先,最小二乘法是通过平方损失函数建立模型优化目标函数的一种思路,求解最优模型过程便具体化为最优化目标函数的过程;而梯度下降法是最优化目标函数的一种优化算法,具体求解使得目标函数能达到最优或者近似最优的参数集。再者,实际问题中 不是满秩矩阵,不可逆,最小二乘法无法解决,而梯度下降法可以。如果维度多、样本多,即便有逆矩阵,计算机求解的速度也会很慢。
损失函数为凸函数,其表达式
对参数 求导
将上述梯度带入随机梯度下降公式
这个式子中,
矩阵和
向量都是已知的,步长是人为设定的一个值,只有参数
是未知的,而每一步的
是由
决定的,也就是每一步的点坐标。
算法过程 1, 初始化
向量的值,即
,将其带入
得到当前位置的梯度; 2,当前梯度乘以步长
,得到从当前位置下降的距离; 3,更新得到新表达式
4,重复以上步骤,直到更新到
,直至触发停止条件,即得到目标函数能达到最优或者近似最优的参数向量。
梯度下降不一定能够找到全局的最优解,有可能是一个局部最优解。当然,如果损失函数是凸函数,梯度下降法得到的解就一定是全局最优解。
linear_model.LinearRegression
语法:
代码语言:javascript复制from sklearn.linear_model import LinearRegression LinearRegression(fit_intercept=True,normalize=False,copy_X=True,n_jobs=1)
普通最小二乘线性回归。线性回归拟合系数为w = (w1,…,wp)
的线性模型,以最小化数据集中观察到的目标和通过线性逼近预测的目标之间的残差平方和。
参数:
fit_intercept : boolean, optional, default True 是否计算此模型的截距。如果设置为False,则不会计算截距。 normalize : boolean, optional, default False 当
fit_intercept
设置为False
时,将忽略此参数。如果为True
,则特征矩阵X
在进入回归之前将会被减去均值(中心化)并除以L2
范式(缩放)。如果你希望进行标准化,请在fit
数据之前使用preprocessing
模块中的标准化专用类StandardScaler
。 copy_X : boolean, optional, default True 如果为真,将在X.copy()
上进行操作,否则的话原本的特征矩阵X
可能被线性回归影响并覆盖。 n_jobs : int, optional, default 1 用于计算的作业数。只在多标签的回归和数据量足够大的时候才生效。除非None
在joblib.parallel_backend
上下文中,否则None
统一表示为1
。如果输入-1
,则表示使用全部的CPU
来进行计算。
从参数可以看出,其参数较少,仅有四个参数就可以完成一个完整的算法。线性回归模型简单,其性能取决于数据本身,而非调参好坏。虽然线性回归对数据有着很高的要求,但大部分连续型变量之间,都存在着或多或少的联系。因此,在合适的数据集下,线性回归简单而强大。
属性:
coef_ : array of shape (n_features, ) or (n_targets, n_features) 数组,形状为
(n_features, )
或者(n_targets, n_features)
线性回归方程中估计出的系数。如果在fit
中传递多个标签(当y
为二维或以上的时候),则返回的系数是形状为(n_targets,n_features)
的二维数组,而如果仅传递一个标签,则返回的系数是长度为n_features
的一维数组。 intercept_ : float or array of shape (n_targets,) 数组,线性回归中的截距项。
例:
代码语言:javascript复制>>> import numpy as np
>>> from sklearn.linear_model import LinearRegression
>>> X = np.array([[1, 1], [1, 2], [2, 2], [2, 3]])
>>> # y = 1 x_0 2 x_1 3
>>> y = np.dot(X, np.array([1, 2])) 3
>>> reg = LinearRegression().fit(X, y)
>>> reg.score(X, y)
1.0
>>> reg.coef_
array([1., 2.])
>>> reg.intercept_
3.0000...
>>> reg.predict(np.array([[3, 5]]))
array([16.])
评估指标
回归类算法的模型评估与分类型算法的模型评估其实是相似的法则——找真实标签和预测值的差异。在分类型算法中,评判否预测到了正确的分类,而在我们的回归类算法中,评判是否预测到了正确的数值、以及是否拟合到了足够的信息。
绝对误差 MAE
---- 是否预测了正确的数值
sklearn
中使用RSS
的变体,均方误差MSE
(mean squared error
)来衡量我们的预测值和真实值的差异:
均方误差MSE
在sklearn
当中,我们有两种方式调用这个评估指标,一种是使用sklearn
专用的模型评估模块metrics
里的类mean_squared_error
,另一种是调用交叉验证的类cross_val_score
并使用里面的scoring
参数来设置使用均方误差。
sklearn.metrics.mean_squared_error(y_true, y_pred, *, sample_weight=None, multioutput='uniform_average', squared=True)
sklearn.model_selection.cross_val_score(reg,X,y,cv=10,scoring="neg_mean_squared_error")
squared bool, default=True If True returns MSE value, if False returns RMSE value.
这里注意
scoring="neg_mean_squared_error"
, 均方误差为负是因为sklearn
在计算模型评估指标的时,认为均方误差本身是一种误差,是模型的一种损失(loss
)。在sklearn
当中,所有的损失都使用负数表示,因此均方误差也被显示为负数了。
均方根误差RMSE
RMSE为MSE的算术平方根
缺点:因使用平均误差,平均误差对异常值比较敏感,异常值的出现,使得RMSE
的误差较大。
例:
代码语言:javascript复制>>> from sklearn.metrics import mean_squared_error
>>> y_true = [3, -0.5, 2, 7]
>>> y_pred = [2.5, 0.0, 2, 8]
>>> mean_squared_error(y_true, y_pred)
0.375
>>> y_true = [3, -0.5, 2, 7]
>>> y_pred = [2.5, 0.0, 2, 8]
>>> mean_squared_error(y_true, y_pred, squared=False)
0.612...
>>> y_true = [[0.5, 1],[-1, 1],[7, -6]]
>>> y_pred = [[0, 2],[-1, 2],[8, -5]]
>>> mean_squared_error(y_true, y_pred)
0.708...
>>> mean_squared_error(y_true, y_pred, squared=False)
0.822...
>>> mean_squared_error(y_true, y_pred, multioutput='raw_values')
array([0.41666667, 1. ])
>>> mean_squared_error(y_true, y_pred, multioutput=[0.3, 0.7])
0.825...
同样可以在sklearn
当中,使用命令from sklearn.metrics import mean_absolute_error
来调用MAE
;在交叉验证中的scoring = "neg_mean_absolute_error"
来调用MAE
。
sklearn.metrics.mean_absolute_error(y_true, y_pred, *, sample_weight=None, multioutput='uniform_average')
代码语言:javascript复制>>> from sklearn.metrics import mean_absolute_error
>>> y_true = [3, -0.5, 2, 7]
>>> y_pred = [2.5, 0.0, 2, 8]
>>> mean_absolute_error(y_true, y_pred)
0.5
>>> y_true = [[0.5, 1], [-1, 1], [7, -6]]
>>> y_pred = [[0, 2], [-1, 2], [8, -5]]
>>> mean_absolute_error(y_true, y_pred)
0.75
>>> mean_absolute_error(y_true, y_pred, multioutput='raw_values')
array([0.5, 1. ])
>>> mean_absolute_error(y_true, y_pred, multioutput=[0.3, 0.7])
0.85...
如果
multioutput
为'raw_values'
,则分别返回每个输出的绝对平均误差。 如果multioutpu
t是'uniform_average'
或权值的ndarray
,则返回所有输出错误的加权平均值。
(决定系数)
---- 是否拟合了足够的信息
方差的本质是任意一个值和样本均值的差异,差异越大,这些值所带的信息越多。由此可以用来衡量数据上的信息量。如果方差越大,代表数据上的信息量越多,而这个信息量不仅包括了数值的大小,还包括了希望模型捕捉的那些规律。 为衡量模型对数据上的信息量的捕捉而生。
总平方和(SST)= 回归平方和(SSR) 残差平方和(SSE)
其中, 为真实标签, 为预测结果, 为样本均值。 比 样本量 即为样本方差。
数学理解: 1, 衡量的是1 - 模型没有拟合到的信息占真实标签中所带的信息量。 2, 分母理解为原始数据的离散程度,方差,即真实标签所带的信息量。 3,分子为预测数据和原始数据的误差,残差平方和模型没有拟合到的信息。 4, 两者相除可以消除原始数据离散程度的影响。
理论上取值(−∞,1],正常取值范围为[0, 1]
- 越接近1,模型对数据拟合的越好。
- 越接近0,表明模型拟合的越差。
- 小于0(为负),说明模型拟合到的全部信息小于残差平方和
缺点:
- 数据集的样本越大, 越大。
- 不同数据集的模型结果比较会有一定的误差。
可以使用三种方式来调用,一是从metrics
中导入r2_score
,输入预测值和真实值后打分。第二是从线性回归LinearRegression
的接口score
来进行调用。第三是在交叉验证中,输入scoring = "r2"
来调用。
sklearn.metrics.r2_score(y_true, y_pred, *, sample_weight=None, multioutput='uniform_average')
代码语言:javascript复制>>> from sklearn.metrics import r2_score
>>> y_true = [3, -0.5, 2, 7]
>>> y_pred = [2.5, 0.0, 2, 8]
>>> r2_score(y_true, y_pred)
0.948...
>>> y_true = [[0.5, 1], [-1, 1], [7, -6]]
>>> y_pred = [[0, 2], [-1, 2], [8, -5]]
>>> r2_score(y_true, y_pred,
... multioutput='variance_weighted')
0.938...
>>> y_true = [1, 2, 3]
>>> y_pred = [1, 2, 3]
>>> r2_score(y_true, y_pred)
1.0
>>> y_true = [1, 2, 3]
>>> y_pred = [2, 2, 2]
>>> r2_score(y_true, y_pred)
0.0
>>> y_true = [1, 2, 3]
>>> y_pred = [3, 2, 1]
>>> r2_score(y_true, y_pred)
-3.0
multioutput: {'raw_values','uniform_average','variance_weighted'},array like of shape(n_output,) or None,default='uniform_average' 定义多个输出分数的聚合。类数组值定义用于平均得分的权重。默认设置是
"uniform_average"
。"raw_values"
: 在多输出输入的情况下返回完整的分数集。"uniform_average"
: 对所有输出的分数取平均值,权重一致。"variance_weighted"
: 所有输出的分数取平均值,用每个单个输出的方差加权。
简单总结
算法任务
- 构造一个预测函数来映射输入的特性矩阵和标签的线性关系。
- 线性回归使用最佳的拟合直线(也就是回归线)在因变量()和一个或多个自变量()之间建立一种关系。
在这种技术中,因变量是连续的,自变量可以是连续的也可以是离散的,回归线的性质是线性的。
优化目标
SSE(RSS)
算法求解
- 最小二乘法:通过最小化真实值和预测值之间的
RRS
(残差平方和)来求解参数的方法。 - 最小二乘法求解最佳拟合回归线:对于观测数据,它通过最小化每个数据点到线的垂直偏差平方和来计算最佳拟合线 。
- 梯度下降法是最优化目标函数的一种优化算法,具体求解使得目标函数能达到最优或者近似最优的参数集。
- 在常规数据集中,采用梯度下降法求解损失函数更为常见,其计算代价相对较低。
评估指标
- 是否预测正确的值
- 是否拟合到全部信息
—— 残差平方和,模型中没有拟合到的信息
—— 代表了模型中的全部信息
要点
- 自变量与因变量之间必须有线性关系。
- 多元回归存在多重共线性,自相关性和异方差性。
- 线性回归对异常值非常敏感。它会严重影响回归线,最终影响预测值。
案例
案例一
代码语言:javascript复制import matplotlib.pyplot as plt
import numpy as np
from sklearn import datasets, linear_model
from sklearn.metrics import mean_squared_error, r2_score
# 导入糖尿病数据集
diabetes_X, diabetes_y = datasets.load_diabetes(return_X_y=True)
# 仅使用一个特征
diabetes_X = diabetes_X[:, np.newaxis, 2]
# 划分训练集和测试集数据
diabetes_X_train = diabetes_X[:-20]
diabetes_X_test = diabetes_X[-20:]
# 划分训练集和测试集目标变量(标签)
diabetes_y_train = diabetes_y[:-20]
diabetes_y_test = diabetes_y[-20:]
# 创建线性回归模型
regr = linear_model.LinearRegression()
# 训练模型
regr.fit(diabetes_X_train, diabetes_y_train)
# 用测试集预测数据
diabetes_y_pred = regr.predict(diabetes_X_test)
# 系数
print('Coefficients: n', regr.coef_)
# 均方误差
print('Mean squared error: %.2f'
% mean_squared_error(diabetes_y_test, diabetes_y_pred))
# R2:1为完美预测
print('Coefficient of determination: %.2f'
% r2_score(diabetes_y_test, diabetes_y_pred))
# 可视化输出
plt.style.use('seaborn')
plt.figure(figsize=(8,6))
plt.scatter(diabetes_X_test, diabetes_y_test, color='black')
plt.plot(diabetes_X_test, diabetes_y_pred, color='blue', linewidth=3)
plt.xticks(())
plt.yticks(())
plt.show()
输出结果:
Coefficients: [938.23786125] Mean squared error: 2548.07 Coefficient of determination: 0.47
案例二
代码语言:javascript复制>>> from sklearn.datasets import fetch_california_housing # 数据集函数
>>> from sklearn.linear_model import LinearRegression # 线性回归模型
>>> from sklearn.metrics import mean_squared_error # MSE
>>> from sklearn.model_selection import cross_val_score # 交叉验证
>>> from sklearn.metrics import mean_absolute_error # MAE
>>> from sklearn.metrics import r2_score # metrics 模块
>>> house = fetch_california_housing() # 读入数据
>>> house = fetch_california_housing(data_home='/Users/mac/Downloads') # 读入数据
>>> X = pd.DataFrame(data = house.data , columns= house.feature_names)
>>> y = house.target
>>> """
... MedInc:该街区住户的收入中位数
... HouseAge:该街区房屋使用年代的中位数
... AveRooms:该街区平均的房间数目
... AveBedrms:该街区平均的卧室数目
... Population:街区人口
... AveOccup:平均入住率
... Latitude:街区的纬度
... Longitude:街区的经度
... """
>>> # 拆分训练集和测试机
>>> from sklearn.model_selection import train_test_split
>>> X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=666)
>>> # 实例化
>>> lr = LinearRegression()
>>> # 模型训练
>>> lr.fit(X_train,y_train)
>>> print('Coefficients: n', lr.coef_) # 系数
Coefficients:
[ 4.40228234e-01 9.48391521e-03 -1.12843342e-01 6.09712715e-01
-4.00482664e-06 -3.96962732e-03 -4.12468279e-01 -4.26552274e-01]
>>> print('Intercept: n', lr.intercept_)
Intercept:
-36.25068267909044
>>> # 生成模型预测值
>>> # 分别生成模型对训练集的预测值,和测试集的
>>> y_train_pred = lr.predict(X_train)
>>> y_test_pred = lr.predict(X_test)
>>> # 模型在训练集中的MSE
>>> print('Mean squared error: %.3f for train set' % mean_squared_error(y_train , y_train_pred))
Mean squared error: 0.527 for train set
>>> # 模型在测试集中的MSE
>>> print('Mean squared error: %.3f for test set' % mean_squared_error(y_test , y_test_pred))
Mean squared error: 0.521 for test set
>>> # neg_mean_squared_error
>>> print('Mean squared error: %.3f for train set in cross_val_score' % cross_val_score(lr, X_train, y_train, cv=5, scoring='neg_mean_squared_error').mean())
Mean squared error: -0.532 for train set in cross_val_score
>>> # 模型在训练集中的MAE
>>> print('Mean absolute error: %.3f for train set' % mean_absolute_error(y_train , y_train_pred))
Mean absolute error: 0.533 for train set
# 模型在测试集中的MAE
>>> print('Mean absolute error: %.3f for test set' % mean_absolute_error(y_test , y_test_pred))
Mean absolute error: 0.529 for test set
# r2
>>> print('R2: %.3f for train set in linear_model' % lr.score(X_train,y_train))
R2: 0.608 for train set in linear_model
>>> print('R2: %.3f for test set in linear_model' % lr.score(X_test,y_test))
R2: 0.599 for test set in linear_model
>>> print('R2: %.3f for train set in cross_val_score' % cross_val_score(lr, X_train, y_train, cv=5, scoring='r2').mean())
R2: 0.605 for train set in cross_val_score
>>> # 按照系数大小排序
>>> feature_values = pd.DataFrame(list(zip(house.feature_names , lr.coef_)), columns=['特征名称','系数值'])
>>> feature_values.sort_values(by='系数值',ascending=False)
>>> feature_values
特征名称 系数值
0 MedInc 0.440228
1 HouseAge 0.009484
2 AveRooms -0.112843
3 AveBedrms 0.609713
4 Population -0.000004
5 AveOccup -0.003970
6 Latitude -0.412468
7 Longitude -0.426552
因为数据的量纲不同,我们还不能简单的通过系数的大小来判断特征的重要程度,需要对数据进行标准化处理,更多标准化内容可点击数据标准化和归一化查看。
代码语言:javascript复制>>> from sklearn.preprocessing import StandardScaler # 标准化
>>> std = StandardScaler().fit(X_train)
>>> X_train_std = std.transform(X_train) # 训练集标准化
>>> X_test_std = std.transform(X_test) # 测试集标准化
>>> # 标准化前后数据对比
>>> X.head()
代码语言:javascript复制>>> pd.DataFrame(data = X_train_std , columns= house.feature_names).head()
标准化之后模型预测结果和标准化之前没有任何区别。
代码语言:javascript复制>>> # 采用标准化之后的数据进行建模
>>> lr2 = LinearRegression()
>>> lr2.fit(X_train_std , y_train)
>>> lr2.score(X_train_std,y_train)
>>> lr2.score(X_test_std,y_test)
>>> y_train_std_pre = lr2.predict(X_train_std)
>>> y_test_std_pre = lr2.predict(X_test_std)
>>> y_train_pred
array([2.17567626, 2.13052503, 1.17965784, ..., 1.00437331, 1.57254239,
1.47268172])
>>> y_train_std_pre
array([2.17567626, 2.13052503, 1.17965784, ..., 1.00437331, 1.57254239,
1.47268172])
标准化前后模型系数对比,因为消除量纲对模型的影响,因此标准化后的系数更加可信。
代码语言:javascript复制>>> print('Before StandardScalern', list(zip(house.feature_names, lr.coef_)))
Before StandardScaler
[('MedInc', 0.44022823447307646),
('HouseAge', 0.009483915211687824),
('AveRooms', -0.11284334216964081),
('AveBedrms', 0.6097127147709995),
('Population', -4.0048266407842215e-06),
('AveOccup', -0.00396962731746198),
('Latitude', -0.4124682789498445),
('Longitude', -0.4265522739083765)]
>>> print('After StandardScalern', list(zip(house.feature_names, lr2.coef_)))
After StandardScaler
[('MedInc', 0.8436986416007352),
('HouseAge', 0.11936774866894542),
('AveRooms', -0.26717565155245615),
('AveBedrms', 0.29269692762426314),
('Population', -0.004439817451324353),
('AveOccup', -0.04623086241312088),
('Latitude', -0.8799314407209977),
('Longitude', -0.8532772941241822)]
转载请联系笔者
点赞、关注、转发、在看
支持笔者,关注持续获取最新数据分析相关干货。