机器学习系列笔记七:多项式回归[上]

机器学习系列笔记七:多项式回归[上]

文章目录

  • 机器学习系列笔记七:多项式回归[上]
    • Intro
    • 简单实现
    • scikit-learn中的多项式回归和Pipeline
      • 关于PolynomialFeatures
      • Pipeline
    • 过拟合与欠拟合
      • 概念引入
      • train test split的意义
    • 学习曲线
      • 绘制学习曲线

Intro

相比较线性回归所拟合的直线,多线性回归可以拟合的曲线更具有表达能力,能够有效提高模型的精度。区别如下图所示。
机器学习系列笔记七:多项式回归[上]_第1张图片

多项式回归在回归分析中很重要,因为任意一个函数至少在一个较小的范围内都可以用多项式任意逼近,因此在比较复杂的实际问题中,有时不问y与诸元素的确切关系如何,而用回归分析进行分析运算。

比如,对房屋成交信息建立多项式回归方程,并依据回归方程对房屋价格进行预测 。 机器学习系列笔记七:多项式回归[上]_第2张图片

多项式回归在机器学习算法上并没有什么新的地方,完全是使用线性回归的思路,它的关键在于:

  • 为原来的数据样本添加合理的新的特征

  • 这些新的特征是源于原有特征的

    比如原有特征为X,则通过将 X 2 X^2 X2 作为新的特征

在PCA中是想办法将数据降维,而多项式回归则相反,是想办法将数据维度提升。在升维后,原始数据集具有了一下新的特征,可以使得线性回归算法可以更好的拟合这个高维度的数据。

简单实现

其实要理解多项式回归的思路还是得通过例子来。

首先我们模拟一个数据集

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)
plt.scatter(X,y)
plt.title("original data")
plt.show()

机器学习系列笔记七:多项式回归[上]_第3张图片
然后我们先使用线性回归模型来实现拟合

from sklearn.linear_model import LinearRegression

lin_reg = LinearRegression()
lin_reg.fit(X,y)
LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None, normalize=False)
y_predict = lin_reg.predict(X)
plt.scatter(x,y)
plt.plot(X,y_predict,'r')
plt.show()

机器学习系列笔记七:多项式回归[上]_第4张图片

显然想过是不够好的,我们采用多项式回归的思路,人为的为该样本添加一个特征

(X**2).shape # 一个新的特征
(100, 1)
X_new = np.hstack([X,X**2]) # 将原来的样本横向展开,添加新的特征X**2
X_new.shape
(100, 2)

使用拓展了特征空间的新的数据集进行线性回归

lin_reg2 = LinearRegression()
lin_reg2.fit(X_new,y)
y_predict_new = lin_reg2.predict(X_new)
plt.scatter(x,y)
plt.plot(X,y_predict_new,'r')
plt.show()

机器学习系列笔记七:多项式回归[上]_第5张图片

可以看到生成了一大堆直线,很乱,这是因为我们的x不是顺序递增的,可能是3,2,6,1之类的顺序

我们可以通过对x进行排序来平滑曲线

plt.scatter(x,y)
plt.plot(np.sort(x),y_predict_new[np.argsort(x)],'r')
plt.show()

机器学习系列笔记七:多项式回归[上]_第6张图片

查看模型训练出的系数

lin_reg2.coef_ 
array([1.08034107, 0.4707525 ])

与之前生成的多项式中系数基本相似

lin_reg2.intercept_ 
2.1819245672677283

与之前生成的多项式截距基本相似

scikit-learn中的多项式回归和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)

scikit-leran将数据升维的过程封装到了preprocessing包中的PolynomialFeatures类中

from sklearn.preprocessing import PolynomialFeatures

在其构造函数中有一个重要参数degree

  • 表示需要为原本的数据集添加相应的degree次幂相应的特征
poly = PolynomialFeatures(degree=2)
poly.fit(X)
X2 = poly.transform(X)
X2.shape
(100, 3)
X2[:5]
array([[ 1.        ,  1.52415023,  2.32303391],
       [ 1.        ,  2.60525966,  6.7873779 ],
       [ 1.        , -2.58263766,  6.67001727],
       [ 1.        , -0.09536093,  0.00909371],
       [ 1.        ,  2.20542548,  4.86390153]])
X[:5]
array([[ 1.52415023],
       [ 2.60525966],
       [-2.58263766],
       [-0.09536093],
       [ 2.20542548]])

可以看到X2的第一列表示X的特征的0次幂,第二列表示1次幂,第三列表示2次幂(degree=2)

from sklearn.linear_model import LinearRegression

lin_reg = LinearRegression()
lin_reg.fit(X2,y)
y_predict = lin_reg.predict(X2)
plt.scatter(x,y)
plt.plot(np.sort(x),y_predict[np.argsort(x)],'r')
plt.show()

机器学习系列笔记七:多项式回归[上]_第7张图片

关于PolynomialFeatures

我们尝试设置X具有两个特征

X = np.arange(1,11).reshape(-1,2)
X.shape
(5, 2)

现在我们尝试使用scikit-leran中的PolynomialFeatures类来处理X

poly = PolynomialFeatures(degree=2)
poly.fit(X)
X2 = poly.transform(X)
X2.shape
(5, 6)

本身是5行2列的数据集被升维到了5行6列,增加了4个维度,共6个维度分别是:

  • X 0 X^0 X0
  • X [ : , 0 ] 1 X[:,0]^1 X[:,0]1
  • X [ : , 1 ] 1 X[:,1]^1 X[:,1]1
  • X [ : , 0 ] 2 X[:,0]^2 X[:,0]2
  • X [ : , 0 ] ∗ X [ : , 1 ] X[:,0]*X[:,1] X[:,0]X[:,1]
  • X [ : , 1 ] 2 X[:,1]^2 X[:,1]2
X
array([[ 1,  2],
       [ 3,  4],
       [ 5,  6],
       [ 7,  8],
       [ 9, 10]])
X2
array([[  1.,   1.,   2.,   1.,   2.,   4.],
       [  1.,   3.,   4.,   9.,  12.,  16.],
       [  1.,   5.,   6.,  25.,  30.,  36.],
       [  1.,   7.,   8.,  49.,  56.,  64.],
       [  1.,   9.,  10.,  81.,  90., 100.]])

生成的规则如下,以degree=3为例:
机器学习系列笔记七:多项式回归[上]_第8张图片

Pipeline

如果我们使用多项式回归,需要经过以下步骤

  • 对原始样本数据通过PolynomialFeature(degree=n)生成相应的多项式特征样本数据
  • 对生成的高维数据进行归一化处理(StandardScaler)得到归一化后的高维数据
  • 最后送入线性回归

而pipline可以帮助我们将这三个步骤合在一起,减少重复性的代码

from sklearn.pipeline import Pipeline

在Pipeline的构造中我们需要传入一个列表,表示这个管道中需要的每一个步骤所对应的类,想象一个工厂里的流水线生产过程

from sklearn.preprocessing import StandardScaler

poly_reg = Pipeline([
    ("poly",PolynomialFeatures(degree=2)),
    ("std_scaler",StandardScaler()),
    ("lin_reg",LinearRegression())
])

我们通过如上代码所示的方式创建了一个poly_reg的这样一个管道,我们送给这个poly_reg对象的数据就会沿着这个管道内部的三个类(三个步骤)依次的进行下去

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)
poly_reg.fit(X,y)
y_predict = poly_reg.predict(X)
plt.scatter(x,y)
plt.plot(np.sort(x),y_predict[np.argsort(x)],'r')
plt.show()

机器学习系列笔记七:多项式回归[上]_第9张图片
可以看到,pipline(管道)还是非常好用的,可以很好的帮助我们快速完成一个预测或回归任务。

过拟合与欠拟合

概念引入

在这里可以使用均分误差来衡量线性回归拟合和多项式回归拟合的准确度。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

np.random.seed(666)
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)

def plot_regression(X,y,y_predict,x=x):
    plt.scatter(X,y)
    plt.plot(np.sort(x),y_predict[np.argsort(x)],'r')
    plt.show()

使用线性回归拟合的误差

使用均分误差进行衡量

from sklearn.metrics import mean_squared_error
lin_reg = LinearRegression()
lin_reg.fit(X,y)
y_predict = lin_reg.predict(X)

plot_regression(X,y,y_predict)

mean_squared_error(y,y_predict) 

机器学习系列笔记七:多项式回归[上]_第10张图片

3.0750025765636577

使用多项式回归拟合的误差

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_scaler",StandardScaler()),
        ("lin_reg",LinearRegression())
    ])
poly2_reg = PolynomialRegression(degree=2)
poly2_reg.fit(X,y)
Pipeline(memory=None,
         steps=[('poly',
                 PolynomialFeatures(degree=2, include_bias=True,
                                    interaction_only=False, order='C')),
                ('std_scaler',
                 StandardScaler(copy=True, with_mean=True, with_std=True)),
                ('lin_reg',
                 LinearRegression(copy_X=True, fit_intercept=True, n_jobs=None,
                                  normalize=False))],
         verbose=False)
y2_predict = poly2_reg.predict(X)
plot_regression(X,y,y2_predict)
mean_squared_error(y,y2_predict)

机器学习系列笔记七:多项式回归[上]_第11张图片

1.0987392142417856

显然使用多项式回归产生的误差要比线性回归的要小

我们不妨试试让degree=10,看看生成的多项式回归模型的预测均分误差

poly10_reg = PolynomialRegression(degree=10)
poly10_reg.fit(X,y)
y10_predict = poly10_reg.predict(X)
plot_regression(X,y,y10_predict)
mean_squared_error(y,y10_predict)

机器学习系列笔记七:多项式回归[上]_第12张图片

1.0508466763764202

可以看到虽然误差没有减少多少,但是拟合出来的曲线有些歪歪曲曲了

我们继续把degree调到100,再看看误差与拟合曲线

poly100_reg = PolynomialRegression(degree=100)
poly100_reg.fit(X,y)
y100_predict = poly100_reg.predict(X)
plot_regression(X,y,y100_predict)
mean_squared_error(y,y100_predict)

机器学习系列笔记七:多项式回归[上]_第13张图片

0.6870911922673567

可以看到,拟合的曲线非常的"活跃"

# 使用新的样本作为测试集进行预测
X_plot = np.linspace(-3, 3, 100).reshape(100, 1)
y_plot = poly100_reg.predict(X_plot)
plt.scatter(x, y)
plt.plot(X_plot[:,0], y_plot, color='r')
plt.axis([-3, 3, 0, 10])
plt.show()

机器学习系列笔记七:多项式回归[上]_第14张图片

可以看到虽然使用当时样本所得到的误差变小了,但是一旦使用新的样本,就会产生极大的误差。

而且误差比之前还要大很多,甚至不如线性回归,这就是传说中的过拟合

而回到最开始通过线性回归拟合的直线也无法准确表达样本的分布,称之为欠拟合

对于这样的模型,我们称之泛化能力很弱

为了能及早地发现模型过拟合或欠拟合的问题,所以才有了测试数据集的意义

train test split的意义

from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=666)
lin_reg = LinearRegression()
lin_reg.fit(X_train,y_train)
y_predict = lin_reg.predict(X_test)
mean_squared_error(y_test,y_predict)
2.2199965269396573
poly2_reg = PolynomialRegression(degree=2)
poly2_reg.fit(X_train,y_train)
y2_predict = poly2_reg.predict(X_test)
mean_squared_error(y_test,y2_predict)
0.8035641056297901
poly10_reg = PolynomialRegression(degree=10)
poly10_reg.fit(X_train,y_train)
y10_predict = poly10_reg.predict(X_test)
mean_squared_error(y_test,y10_predict)
0.9212930722150697
poly100_reg = PolynomialRegression(degree=100)
poly100_reg.fit(X_train,y_train)
y100_predict = poly100_reg.predict(X_test)
mean_squared_error(y_test,y100_predict)
14075780347.739939

机器学习系列笔记七:多项式回归[上]_第15张图片

机器学习系列笔记七:多项式回归[上]_第16张图片

学习曲线

学习曲线可以表征随着样本的逐渐增多,算法训练出的模型的表现能力。

根据学习曲线,我们还可以判别当前模型是否欠拟合,是否过拟合。

接下来,我们通过代码的形式来绘制学习曲线。

绘制学习曲线

import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

np.random.seed(666)
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)
plt.scatter(X,y)
plt.show()

机器学习系列笔记七:多项式回归[上]_第17张图片

首先分割数据集

from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=10)
X_train.shape
(75, 1)

然后根据训练集和测试集样本的数量递增地训练模型,记录各个拥有不同数量训练样本的模型分别在当前训练集以及所有测试集上的表现

先考虑使用线性回归算法

from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_squared_error

train_score = []
test_score = []
for i in range(1,76):
    lin_reg = LinearRegression()
    lin_reg.fit(X_train[:i],y_train[:i]) # 取出训练集中前i个样本进行训练,i从1递增至75
    # 对于不同样本数训练的模型,获得其预测结果并将模型精度记录
    y_train_predict = lin_reg.predict(X_train[:i])
    train_score.append(mean_squared_error(y_train[:i],y_train_predict))
    
    y_test_predict = lin_reg.predict(X_test)
    test_score.append(mean_squared_error(y_test,y_test_predict))

plt.plot([i for i in range(1,76)],np.sqrt(train_score),label="train")
plt.plot([i for i in range(1,76)],np.sqrt(test_score),label="test")
plt.legend()
plt.title("learning curve") 
plt.show()

机器学习系列笔记七:多项式回归[上]_第18张图片

为方便对比其他算法的学习曲线,将上面的代码封装为一个函数

def plot_learning_curve(algo,X_train,X_test,y_train,y_test):
    train_score=[]
    test_score=[]
    for i in range(1,len(X_train)+1):
        algo.fit(X_train[:i],y_train[:i])
        y_train_predict = algo.predict(X_train[:i])
        train_score.append(mean_squared_error(y_train[:i],y_train_predict))
        
        y_test_predict = algo.predict(X_test)
        test_score.append(mean_squared_error(y_test,y_test_predict))

    plt.plot([i for i in range(1,len(X_train)+1)],np.sqrt(train_score),label="train")
    plt.plot([i for i in range(1,len(X_train)+1)],np.sqrt(test_score),label="test")
    plt.legend()
    plt.axis([0,len(X_train)+1,0,4]) # 限定显示范围
    
    plt.title("learning curve") 
    plt.show()
plot_learning_curve(LinearRegression(),X_train,X_test,y_train,y_test)

机器学习系列笔记七:多项式回归[上]_第19张图片

绘制多项式回归的学习曲线

from sklearn.preprocessing import PolynomialFeatures
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

def PolynomialRegression(degree):
    return Pipeline([
        ('poly',PolynomialFeatures(degree=degree)),
        ('std_scaler',StandardScaler()),
        ('lin_reg',LinearRegression())
    ])
poly2_reg = PolynomialRegression(degree=2)
plot_learning_curve(poly2_reg,X_train,X_test,y_train,y_test)

机器学习系列笔记七:多项式回归[上]_第20张图片

poly20_reg = PolynomialRegression(degree=20)
plot_learning_curve(poly20_reg,X_train,X_test,y_train,y_test)

机器学习系列笔记七:多项式回归[上]_第21张图片

可以看到,随着degree的增加,训练集和测试集的间隔越来越大,当出现如上图所示两条学习曲线间隔过大且没有要重合的趋势时,往往出现了过拟合

最后把三张图放一块做一个比较

def plot_learning_curve(algo,X_train,X_test,y_train,y_test,subplot,title):
    
    train_score=[]
    test_score=[]
    for i in range(1,len(X_train)+1):
        algo.fit(X_train[:i],y_train[:i])
        y_train_predict = algo.predict(X_train[:i])
        train_score.append(mean_squared_error(y_train[:i],y_train_predict))
        
        y_test_predict = algo.predict(X_test)
        test_score.append(mean_squared_error(y_test,y_test_predict))
    
    subplot1,subplot2,subplot3 =subplot
    
    plt.subplot(subplot1,subplot2,subplot3)
    plt.plot([i for i in range(1,len(X_train)+1)],np.sqrt(train_score),label="train")
    plt.plot([i for i in range(1,len(X_train)+1)],np.sqrt(test_score),label="test")
    plt.axis([0,len(X_train)+1,0,4]) # 限定显示范围   
    
#     plt.legend()
    plt.title(title) 
#     plt.show()
plt.figure(figsize=(20,8))

plot_learning_curve(LinearRegression(),X_train,X_test,y_train,y_test,(1,3,1),"under-fiting")

poly2_reg = PolynomialRegression(degree=2)
plot_learning_curve(poly2_reg,X_train,X_test,y_train,y_test,(1,3,2),"best-fiting")

poly20_reg = PolynomialRegression(degree=20)
plot_learning_curve(poly20_reg,X_train,X_test,y_train,y_test,(1,3,3),"over-fiting")

plt.legend()
plt.show()

机器学习系列笔记七:多项式回归[上]_第22张图片

可以看到

  • 欠拟合时,训练集的误差曲线和测试集的间隔不是很大,但是误差都在1.5以上
  • 过拟合时,训练集的误差很小但是测试集的误差较大,而且两条误差曲线的间隔很大。

你可能感兴趣的:(神经网络机器学习,python,算法,机器学习,人工智能)