在解决回归问题中,很多数据集中输入空间与输出空间并非完全呈线性关系,使用线性回归无法解决此类问题。为了解决存在非线性关系的数据集的回归问题,需要进行多项式回归,但sklearn并未提供多项式回归模型的类。 多项式回归使用的还是线性回归的思路,它的关键在于我们为原来的数据样本添加新的特征,这些新的特征来源方式是对原来特征的多项式组合,采用这样的方式,便可以完美解决非线性问题,本文提供了三种方式进行多项式回归,在工程实践中,推荐方式三用pipeline封装一个多项式回归方法。
值得注意的是:
PCA算法是对数据集进行降维处理,而多项式回归算法与之相反,对数据集进行升维,添加新的特征之后,使算法能够更好拟合非线性关系的数据。
多项式回归与多元线性回归不同,多项式回归是将已有特征向量进行非线性组合后作为新的特征向量进行训练,多元线性回归是特征空间原本就有多个特征向量,且每个特征向量与输出空间呈线性关系。
# -*- coding: UTF-8 -*-
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
# 构建具有非线性关系的两个数据集 X 和 y
x = np.random.uniform(-3, 3, size=100)
X = x.reshape(-1, 1)
y = 0.5 * x ** 2 + x + 2 + np.random.normal(0, 0.5, 100)
plt.scatter(X, y)
# plt.show()
使用已有的特征值进行线性回归训练,可以直观的看到,其拟合效果并不佳,整个散点并不能被一条直线完美的拟合。
线性回归模型为:y = 0.9506727252503596x + 3.5467856620081837.
# 使用已有的特征值和结果数据做线性回归训练
lin_reg = LinearRegression()
lin_reg.fit(X, y)
y_predict = lin_reg.predict(X)
plt.plot(X, y_predict, color='r')
# plt.show()
a = lin_reg.coef_[0] # 查看斜率
b = lin_reg.intercept_ # 查看截距
print("线性回归模型为:y = {}x + {}.".format(a, b))
为了实现非线性拟合,可在原有特征空间中添加一个非线性向量(原有向量平方得到),得到新的数据集进行多元线性训练,能够较好的拟合数据集的散点。
线性回归模型为:y = 0.49836542422636915x**2 + 0.9847186942160173x + 2.031892702400074.
# 在原有的特征基础上,将一个特征值平方后作为新的特征添加到数据集X中进行线性回归训练
X2 = np.hstack([X ** 2, X])
lin_reg = LinearRegression()
lin_reg.fit(X2, y)
y_predict = lin_reg.predict(X2)
# 按大小将特征值第一列排序作为横坐标,y值取对应排序的序号作为纵坐标
plt.plot(np.sort(X2[:, 1]), y_predict[np.argsort(X2[:, 1])], color='r')
# plt.show()
a = lin_reg.coef_[0] # 查看x2系数
b = lin_reg.coef_[1] # 查看x 系数
c = lin_reg.intercept_ # 查看截距
print("线性回归模型为:y = {}x**2 + {}x + {}.".format(a, b, c))
改进:此方法虽然有效的实现非线性回归,但如果特征空间有多个特征向量,且需要进行更高次方的多项式组合,人工预处理特征向量空间数据集会非常麻烦,因此sklearn提供了对已知特征向量空间输出多项式组合特征向量空间数据集的方法,具体介绍如下。
通过sklearn自带的数据预处理模型,可将原有向量空间进行维度提升,基于现有向量空间,对各个特征向量进行多项式组合,新增加非线性特征向量。
如果方法PolynomialFeatures中参数degree=2,且特征空间只有一个变量时,表示输出小于等于二元的所有多项式组合,即:
如果方法PolynomialFeatures中参数degree=2,且特征空间有两个变量 ,则输出小于等于二元的所有多项式组合,即:
# 探索预处理机制
A = np.arange(1, 11).reshape(-1, 2)
print(A)
poly = PolynomialFeatures(degree=2)
poly.fit(A)
A1 = poly.transform(A)
print(A1)
原特征向量空间数据集为:
通过degree=2 对原特征向量空间数据集进行多项式组合预处理后,得到新的特征向量空间数据集:
如果degree=n 特征空间有 i 个向量,那么所对应的多项式的项,如下图所示:
通过上述方法对特征向量空间数据集预处理后再进行线性回归算法,得到的结果与手动预处理特征向量空间数据集一致:
线性回归模型为:y = 0.49836542422636904x**2 + 0.9847186942160173x + 2.031892702400074.
# 使用sklearn自带方法进行数据预处理
poly = PolynomialFeatures(degree=2)
poly.fit(X)
X3 = poly.transform(X)
lin_reg = LinearRegression()
lin_reg.fit(X3, y)
y_predict = lin_reg.predict(X3)
# 按大小将特征值第一列排序作为横坐标,y值取对应排序的序号作为纵坐标
plt.plot(np.sort(X3[:, 1]), y_predict[np.argsort(X3[:, 1])], color='b')
# plt.show()
a = lin_reg.coef_[2] # 查看x**2系数
b = lin_reg.coef_[1] # 查看x**1系数
c = lin_reg.intercept_ # 查看截距
print("线性回归模型为:y = {}x**2 + {}x + {}.".format(a, b, c))
再次改进:如果对特征向量空间数据集预处理时,添加了更高元次的多项式特征向量,且原始数据集的值大于1,则新添加的高次方多项式项的值将会远远超过原始数据向量的值,为了让各个特征向量保持在同一量纲水平,就需要做各个特征向量做归一化处理。
可以使用sklearn的管道方法,将上述三个步骤:
1.数据多项式非线性特征向量添加预处理
2.数据归一化预处理
3.线性回归模型训练
统一封装到了一个方法中,让其顺序执行以上三个步骤,这也是本文着重推荐的多项式非线性回归的方式。
# 使用pipeline进行多项式回归
poly_reg = Pipeline([("poly",PolynomialFeatures(degree=2)),
("std_scaler", StandardScaler()),
("lin_reg", LinearRegression())
])
poly_reg.fit(X,y)
y_predict = poly_reg.predict(X)
# 按大小将特征值第一列排序作为横坐标,y值取对应排序的序号作为纵坐标
plt.plot(np.sort(X[:, 0]), y_predict[np.argsort(X[:, 0])], color='g')
plt.show()
a = lin_reg.coef_[2] # 查看x**2系数
b = lin_reg.coef_[1] # 查看x**1系数
c = lin_reg.intercept_ # 查看截距
print("线性回归模型为:y = {}x**2 + {}x + {}.".format(a, b, c))
线性回归模型为:y = 0.5035575308426694x**2 + 1.0059744093074539x + 1.99139805183949.
本文通过举例说明,当特征向量与输出空间存在非线性关系时,直接使用线性回归并不能取得较好的拟合效果,通过在特征向量中添加多项式组合的非线性特征向量,然后再进行线性回归将会取得较好的拟合效果,此种方法即为多项式回归方法。
除此之外本文还介绍了三种python代码实现多项式回归的方式:
方式1:直接手动添加多项式特征向量,代码复杂,且容易出错;
方式2:通过PolynomialFeatures方法进行特征向量添加预处理,但没有对数据归一化处理;
方式3:通过pipeline将预处理、归一化、线性回归三个步骤封装一起,强烈推荐!!!