多项式线性回归

作者: 抹茶不加茶 | 来源:发表于2020-03-08 14:44 被阅读0次

原理

前面我们学习了线性回归,顾明思议,我们拟合的结果是线性的函数,而对于有一些数据,其分布明显是属于非线性的,这时我们应该怎么办呢?

非线性分布
其实我们整体上还是采用线性回归的方法,我们将x**2理解成一个新的特征,然后去做线性回归即可

基本实现

import numpy as np
import matplotlib.pyplot as plt
X=np.random.uniform(-3,3,size=100)
x=X.reshape(-1,1)
y=0.5*X**2+X+2+np.random.normal(0,1,size=100)

我们先观察一下散点图


散点图-非线性

我们先用线性回归进行拟合

from sklearn.linear_model import LinearRegression
lin_reg=LinearRegression()
lin_reg.fit(x,y)
y_predict= lin_reg.predict(x)
plt.scatter(x,y)
plt.plot(x,y_predict,color='r')
用线性回归拟合

然后我们把x**作为新特征加入

#采用添加一个特性的方案来变成多项式回归
(x**2).shape
x2=np.hstack([x,x**2])
lin_reg2=LinearRegression()
lin_reg2.fit(x2,y)
y_predict2=lin_reg2.predict(x2)
plt.scatter(x,y)
plt.plot(np.sort(X),y_predict2[np.argsort(X)],color='r')
#x是无序的,我们如果想生成一个平滑曲线,而非无限根线交织在一起的话就应该像上面一样使用sort排序,然后y_predict使用其返回的索引
#可以试试直接使用plot(x,y_predict2)看看会是什么样的图
拟合结果
结果2

sklearn中的多项式线性回归以及pipeline初识

import numpy as np
import matplotlib.pyplot as plt
X=np.random.uniform(-3,3,size=100)
x=X.reshape(-1,1)
y=0.5*X**2+X+2+np.random.normal(0,1,size=100)
from sklearn.preprocessing import PolynomialFeatures
poly=PolynomialFeatures(degree=2)#最高添加几次幂
poly.fit(x)
x2=poly.transform(x)

这里我们查看x2注意到


x2

第一列全为1,是额外增加上的,第三列是第二列的平方

from sklearn.linear_model import LinearRegression
lin_reg2=LinearRegression()
lin_reg2.fit(x2,y)
y_predict2= lin_reg2.predict(x2)
plt.scatter(x,y)
plt.plot(np.sort(X),y_predict2[np.argsort(X)],color='r')
结果1
结果2
那么你可能会想,若degree不为2呢,或者若特征不止一个的情况呢?

因为高中知识告诉我们,若存在两个自变量,阶数是由两个自变量共同决定的,若有X1,X2 ,那么X1*X2也是二阶,所以在使用 PolynomialFeatures 时我们注意到

  • degree=3时


    degree=3
我们在处理相关问题的时候,往往需要几个固定的步骤,我们有没有办法把这些步骤联合起来呢?——答案就是pipeline
pipeline用法
import numpy as np
import matplotlib.pyplot as plt
X=np.random.uniform(-3,3,size=100)
x=X.reshape(-1,1)
y=0.5*X**2+X+2+np.random.normal(0,1,size=100)
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
poly_reg=Pipeline([
    ("poly",PolynomialFeatures(degree=2)),
    ("std_scacler",StandardScaler()),
    ("lin_reg",LinearRegression())
])
poly_reg.fit(x,y)
y_predict3=poly_reg.predict(x)
plt.scatter(x,y)
plt.plot(np.sort(X),y_predict3[np.argsort(X)],color='r')
得到结果 结果

过拟合与欠拟合

其实多项式相关内容已经可以结束了,但这里需要额外补充一下在ML中很重要的一些知识。
首先我们一步一步从线性回归到不同阶数的多项式回归,观察其拟合程度,这里选用均方误差进行判断。

X=np.random.uniform(-3,3,size=100)
x=X.reshape(-1,1)
y=0.5*X**2+X+2+np.random.normal(0,1,size=100)
from sklearn.linear_model import LinearRegression
lin_reg=LinearRegression()
lin_reg.fit(x,y)
m=lin_reg.score(x,y)
y_predict= lin_reg.predict(x)
plt.scatter(x,y)
plt.plot(np.sort(X),y_predict[np.argsort(X)],color='r')

得到散点图、准确率、均方误差如下:


1

下面使用多项式回归进行拟合

#使用多项式回归
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import PolynomialFeatures  
def PolynomialRegression(degree):
    return Pipeline([
    ("poly",PolynomialFeatures(degree=degree)),
    ("std_scacler",StandardScaler()),
    ("lin_reg",LinearRegression())
])
poly_reg2=PolynomialRegression(degree=2)
poly_reg2.fit(x,y)
y_predict2=poly_reg2.predict(x)
mean_squared_error(y,y_predict2)
2

这时我们会考虑试试其他阶数的情况(10阶)

poly_reg10=PolynomialRegression(degree=10)
poly_reg10.fit(x,y)
y_predict10=poly_reg10.predict(x)
mean_squared_error(y,y_predict10)
10

我们发现均方误差进一步减小,但曲线开始弯曲不平了,至于这于模型而言是好是坏我们暂且不得而知,我们进一步增大阶数,100阶:

poly_reg100=PolynomialRegression(degree=100)
poly_reg100.fit(x,y)
y_predict100=poly_reg100.predict(x)
mean_squared_error(y,y_predict100)
100

这时均方误差又进一步减小,但曲线弯曲程度更厉害了
那么我们来分析一下这几个阶的情形
首先我们先将100阶情形下的曲线更加准确化,使用linspace生成连续的自变量进行绘图,如下:

#我们更加准确地看看100阶情况下曲线图
x_plot=np.linspace(-3,3,100).reshape(100,1)
y_plot=poly_reg100.predict(x_plot)
plt.scatter(x,y)
plt.plot(x_plot[:,0],y_plot,color='r')
plt.axis([-3,3,-1,10])
100阶精确图

其实我们稍作分析,容易得出结论,虽然100阶的情形对于训练集拟合的更好,但是对于未知数据的预测,很可能出现偏离原来数据较远的情况,也就是说,不能准确预测,我们将其称为过拟合。而对于我们上面使用线性回归的情况,称之为欠拟合。


图示
  • 欠拟合:算法所训练的模型不能完整表述数据关系。
  • 过拟合:算法所训练的模型过多表达了数据间的噪音关系。
训练、测试、验证数据集
  • 如果我们仅有训练数据集,那么我们常常会对于是否过拟合欠拟合而不自知
  • 如果我们仅有训练、测试数据集会不会有问题呢?
    如果仅有训练测试数据集的话,相当于整个训练集训练的模型,在针对这一个测试数据集进行调参,有可能对于这个测试数据集过拟合。即,对这个测试数据集符合的很好,然而对于我们要预测的数据,又没法很好的预测了。
  • 所以我们需要再引入验证数据集


    引入验证

    这时,我们使用验证数据集,替代我们前文中所述的“测试数据集”,然后这里的测试数据集仅用来做测试,相当于我们即将要用来预测的数据。

  • 那么如何保证随机性呢?——交叉验证


    交叉验证

    使用这k个模型进行平均,作为结果来进行调参。


    k-folds交叉验证
import numpy as np
from sklearn import datasets
digits=datasets.load_digits()
x=digits.data
y=digits.target
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.4,random_state=6)
from sklearn.neighbors import KNeighborsClassifier
#使用直接尝试的方法去调参
best_score,best_k,best_p=0,0,0
for k in range(2,11):
    for p in range(1,6):
        knn_clf=KNeighborsClassifier(weights="distance",n_neighbors=k,p=p)
        knn_clf.fit(x_train,y_train)
        score=knn_clf.score(x_test,y_test)
        if score>best_score:
            best_score,best_k,best_p=score,k,p
print("best score=",best_score)
print("best k",k)
print("best p",p)

我们得到结果


直接搜寻

我们先来看看如何使用sklearn中的交叉验证

from sklearn.model_selection import cross_val_score
knn_clf=KNeighborsClassifier()
cross_val_score(knn_clf,x_train,y_train)
#默认分成三类进行交叉验证,不过编译时提示以后默认会改成cv=5

我们将上面的fit变为cross_val_score然后score变成平均值即可

for k in range(2,11):
    for p in range(1,6):
        knn_clf=KNeighborsClassifier(weights="distance",n_neighbors=k,p=p)
        scores=cross_val_score(knn_clf,x_train,y_train,cv=5)
        score=np.mean(scores)
        if score>best_score:
            best_score,best_k,best_p=score,k,p
print("best score=",best_score)
print("best k",k)
print("best p",p)
交叉验证结果

偏差与方差

模型误差=偏差+方差+不可避免的误差
偏差->欠拟合
方差->过拟合

  • 机器学习主要挑战便是降低方差(高偏差可以通过改用更合适的模型解决)
    1、降低模型复杂度
    2、减小数据维度
    3、增加样本数
    4、使用验证集

模型正则化:限制参数的大小

对于之前过拟合的图形,我们看到,部分曲线上下起伏很大,这是部分seata值过大造成的,我们进行如下分析:

原来的目标
加入模型正则化的目标
容易分析得到,加入模型正则化,可以使得在J尽可能小的同时,seata值也尽可能小,这样就解决了上述问题。
上面的这种模型正则化的方式称为岭回归。
import numpy as np
import matplotlib.pyplot as plt
X=np.random.uniform(-3.0,3.0,size=100)
x=X.reshape(-1,1)
y=0.5*X**2+X+3+np.random.normal(0,1,size=100)
#使用多项式回归
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import PolynomialFeatures  
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error
def PolynomialRegression(degree):
    return Pipeline([
    ("poly",PolynomialFeatures(degree=degree)),
    ("std_scacler",StandardScaler()),
    ("lin_reg",LinearRegression())
])
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.2,random_state=6)

poly1_reg=PolynomialRegression(degree=20)
poly1_reg.fit(x,y)
y1_predict=poly1_reg.predict(x_test)
mean_squared_error(y_test,y1_predict)
使用多项式回归20阶
#使用岭回归
from sklearn.linear_model import Ridge
def RidgeRegression(degree,alpha):
    return Pipeline([
    ("poly",PolynomialFeatures(degree=degree)),
    ("std_scacler",StandardScaler()),
        ("ridge_reg",Ridge(alpha=alpha))
])#类似于前面,仅将用于fit的函数替换掉
ridge_reg1=RidgeRegression(20,0.0001)
ridge_reg1.fit(x_train,y_train)
y_predict_1=ridge_reg1.predict(x_test)
mean_squared_error(x_test,y_predict_1)
岭回归20阶
可以看到,曲线变平滑了很多,这里的alpha选的很小的值,若增大其值,会提升模型正则化所占比例,这时候曲线平滑程度会增大,最值成为一条横线。
除了岭回归,还有其他的正则化方式,这里介绍LASSO回归 岭回归和LASSO回归

实例中,相较于使用岭回归,所作出的改变,仅仅是将Pipline中的Ridge变成LASSO即可


比较

最后提及一下L1正则、L2正则


Lp范数
L0正则
弹性网
弹性网将L1L2结合,可调节两者所占比例,利用两者的优缺点

总结

这章的内容可以说是非常多了,所以码字加代码学习花了比较长的时间,不过其实最为核心的内容还是前面那一部分关于多项式回归的,若觉得内容繁多,可以仅看前面的内容。

相关文章

网友评论

    本文标题:多项式线性回归

    本文链接:https://www.haomeiwen.com/subject/qpswlhtx.html