线性回归适用于数据呈线性分布的回归问题。如果数据样本呈明显非线性分布,线性回归模型就不再适用(下图左),而采用多项式回归可能更好(下图右)。
多项式模型定义 与线性模型相比,多项式模型引入了高次项,自变量的指数大于1,例如一元二次方程: $$ y = w_0 + w_1x + w_2x^2 $$ 一元三次方程: $$ y = w_0 + w_1x + w_2x^2 + w_3x ^ 3 $$ 推广到一元n次方程: $$ y = w_0 + w_1x + w_2x^2 + w_3x ^ 3 + … + w_nx^n $$ 上述表达式可以简化为: $$ y = \sum_{i=1}^N w_ix^i $$
与线性回归的关系 多项式回归可以理解为线性回归的扩展,在线性回归模型中添加了新的特征值.例如,要预测一栋房屋的价格,有$x_1, x_2, x_3$三个特征值,分别表示房子长、宽、高,则房屋价格可表示为以下线性模型: $$ y = w_1 x_1 + w_2 x_2 + w_3 x_3 + b $$ 对于房屋价格,也可以用房屋的体积,而不直接使用$x_1, x_2, x_3$三个特征: $$ y = w_0 + w_1x + w_2x^2 + w_3x ^ 3 $$ 相当于创造了新的特征$x, x$ = 长 * 宽 * 高. 以上两个模型可以解释为:
房屋价格是关于长、宽、高三个特征的线性模型
房屋价格是关于体积的多项式模型
因此,可以将一元n次多项式变换成n元一次线性模型.
多项式回归实现 对于一元n次多项式,同样可以利用梯度下降对损失值最小化的方法,寻找最优的模型参数$w_0, w_1, w_2, …, w_n$。可以将一元n次多项式,变换成n元一次线性模型,求线性回归。多项式回归的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 import numpy as npimport sklearn.linear_model as lmimport sklearn.metrics as smimport matplotlib.pyplot as mpimport sklearn.pipeline as plimport sklearn.preprocessing as sptrain_x, train_y = [], [] with open ("poly_sample.txt" , "rt" ) as f: for line in f.readlines(): data = [float (substr) for substr in line.split("," )] train_x.append(data[:-1 ]) train_y.append(data[-1 ]) train_x = np.array(train_x) train_y = np.array(train_y) model = pl.make_pipeline(sp.PolynomialFeatures(3 ), lm.LinearRegression()) model.fit(train_x, train_y) pred_train_y = model.predict(train_x) err4 = sm.r2_score(train_y, pred_train_y) print (err4)test_x = np.linspace(train_x.min (), train_x.max (), 1000 ) pre_test_y = model.predict(test_x.reshape(-1 , 1 )) mp.figure('Polynomial Regression' , facecolor='lightgray' ) mp.title('Polynomial Regression' , fontsize=20 ) mp.xlabel('x' , fontsize=14 ) mp.ylabel('y' , fontsize=14 ) mp.tick_params(labelsize=10 ) mp.grid(linestyle=':' ) mp.scatter(train_x, train_y, c='dodgerblue' , alpha=0.8 , s=60 , label='Sample' ) mp.plot(test_x, pre_test_y, c='orangered' , label='Regression' ) mp.legend() mp.show()
打印输出:
执行结果:
过拟合与欠拟合 什么是欠拟合、过拟合 在上一小节多项式回归示例中,多项特征扩展器PolynomialFeatures()进行多项式扩展时,指定了最高次数为3,该参数为多项式扩展的重要参数,如果选取不当,则可能导致不同的拟合效果。下图显示了该参数分别设为1、20时模型的拟合图像:
这两种其实都不是好的模型。前者没有学习到数据分布规律,模型拟合程度不够,预测准确度过低,这种现象称为“欠拟合”;后者过于拟合更多样本,以致模型泛化能力(新样本的适应性)变差,这种现象称为“过拟合”。欠拟合模型一般表现为训练集、测试集下准确度都比较低;过拟合模型一般表现为训练集下准确度较高、测试集下准确度较低 。一个好的模型,不论是对于训练数据还是测试数据,都有接近的预测精度,而且精度不能太低。
第一个模型欠拟合;第三个模型过拟合;第二个模型拟合较好.
如何处理欠拟合、过拟合
欠拟合:提高模型复杂度,如增加特征、增加模型最高次幂等等;
过拟合:降低模型复杂度,如减少特征、降低模型最高次幂等等.
正则化 过拟合还有一个常见的原因,就是模型参数值太大,所以可以通过抑制参数的方式来解决过拟合问题。如上图所示,右图产生了一定程度过拟合,可以通过弱化高次项的系数(但不删除)来降低过拟合。
例如,可以通过在$\theta_3, \theta_4$的系数上添加一定的系数,来压制这两个高次项的系数,这种方法称为正则化。但在实际问题中,可能有更多的系数,且并不知道应该压制哪些系数,所以,可以通过收缩所有系数来避免过拟合。
正则化的定义 正则化是指,在目标函数后面添加一个范数,来防止过拟合的手段,这个范数定义为:
$$ ||x||_{p}=(\sum _{i=1}^{N}|x|^{p})^{\frac{1}{p}} $$
当p=1时,称为L1范数(即所有系数绝对值之和):
$$ ||x||_{1}=(\sum _{i=1}^N |x|) $$
当p=2是,称为L2范数(即所有系数平方之和再开方):
$$ ||x||_{2}=(\sum _{i=1}^N |x|^2)^{\frac{1}{2}} $$
通过对目标函数添加正则项,整体上压缩了参数的大小,从而防止过拟合。
Lasso回归与岭回归 Lasso回归和岭回归(Ridge Regression)都是在标准线性回归的基础上修改了损失函数的回归算法。Lasso回归全称为 Least absolute shrinkage and selection operator,又译“最小绝对值收敛和选择算子”、“套索算法”,其损失函数如下所示:
$$ E = \frac{1}{n}(\sum _{i=1}^N y_i - y_i’)^2 + \lambda ||w||_1 $$
岭回归损失函数为: $$ E = \frac{1}{n}(\sum _{i=1}^N y_i - y_i’)^2 + \lambda ||w||_2 $$
从逻辑上说,Lasso回归和岭回归都可以理解为通过调整损失函数,减小函数的系数,从而避免过于拟合于样本,降低偏差较大的样本的权重和对模型的影响程度。
线性模型变种模型:
损失函数后面 + 正则项
Lasso回归:损失函数 + L1范数
岭回归:损失函数 + L2范数
以下关于Lasso回归于岭回归的sklearn实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 import numpy as npimport sklearn.linear_model as lmimport sklearn.metrics as smimport matplotlib.pyplot as mpx, y = [], [] with open ("abnormal.txt" , "rt" ) as f: for line in f.readlines(): data = [float (substr) for substr in line.split("," )] x.append(data[:-1 ]) y.append(data[-1 ]) x = np.array(x) y = np.array(y) model = lm.LinearRegression() model.fit(x, y) pred_y = model.predict(x) model_2 = lm.Ridge(alpha=200 , max_iter=1000 ) model_2.fit(x, y) pred_y2 = model_2.predict(x) model_3 = lm.Lasso(alpha=0.5 , max_iter=1000 ) model_3.fit(x, y) pred_y3 = model_3.predict(x) mp.figure('Linear & Ridge & Lasso' , facecolor='lightgray' ) mp.title('Linear & Ridge & Lasso' , fontsize=20 ) mp.xlabel('x' , fontsize=14 ) mp.ylabel('y' , fontsize=14 ) mp.tick_params(labelsize=10 ) mp.grid(linestyle=':' ) mp.scatter(x, y, c='dodgerblue' , alpha=0.8 , s=60 , label='Sample' ) sorted_idx = x.T[0 ].argsort() mp.plot(x[sorted_idx], pred_y[sorted_idx], c='orangered' , label='Linear' ) mp.plot(x[sorted_idx], pred_y2[sorted_idx], c='limegreen' , label='Ridge' ) mp.plot(x[sorted_idx], pred_y3[sorted_idx], c='blue' , label='Lasso' ) mp.legend() mp.show()
执行结果:
模型保存与加载 可使用Python提供的功能对模型对象进行保存。
1 2 3 4 5 import picklepickle.dump(模型对象, 文件对象) model_obj = pickle.load(文件对象)
保存训练模型应该在训练完成或评估完成之后,完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 import numpy as npimport sklearn.linear_model as lm import picklex = np.array([[0.5 ], [0.6 ], [0.8 ], [1.1 ], [1.4 ]]) y = np.array([5.0 , 5.5 , 6.0 , 6.8 , 7.0 ]) model = lm.LinearRegression() model.fit(x, y) print ("训练完成." )with open ('linear_model.pkl' , 'wb' ) as f: pickle.dump(model, f) print ("保存模型完成." )
执行完成后,可以看到与源码相同目录下多了一个名称为linear_model.pkl的文件,这就是保存的训练模型。使用该模型代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 import numpy as npimport sklearn.linear_model as lm import sklearn.metrics as sm import matplotlib.pyplot as mpimport picklex = np.array([[0.5 ], [0.6 ], [0.8 ], [1.1 ], [1.4 ]]) y = np.array([5.0 , 5.5 , 6.0 , 6.8 , 7.0 ]) with open ('linear_model.pkl' , 'rb' ) as f: model = pickle.load(f) print ("加载模型完成." ) pred_y = model.predict(x) mp.figure('Linear Regression' , facecolor='lightgray' ) mp.title('Linear Regression' , fontsize=20 ) mp.xlabel('x' , fontsize=14 ) mp.ylabel('y' , fontsize=14 ) mp.tick_params(labelsize=10 ) mp.grid(linestyle=':' ) mp.scatter(x, y, c='blue' , alpha=0.8 , s=60 , label='Sample' ) mp.plot(x, pred_y, c='orangered' , label='Regression' ) mp.legend() mp.show()
执行结果和训练模型预测结果一样.