多项式回归及过拟合欠拟合处理

回顾

  • 什么是回归问题
    • 如果需要处理的标签数据为连续性数据,则该问题为回归问题
  • 什么是线性回归
    • 就是在找寻特征数据和标签数据之间的线性关系,可以使用权重系数来表示。
    • 权重系数的个数一定是和特征维度保持一致。
  • 线性回归的矩阵表达
    • y = wx + b
    • Y = XWt
  • 损失函数
    • 误差平方和/残差平方和
  • 最小二乘法的作用
    • 可以使得损失值进行最小化,在损失值最小化的情况下求解出最优的权重系数W
    • 损失函数对W进行求导,且将一阶导数等于0.
  • 回归模型的评价指标
    • 模型是否能够预测到真实值或者接近真实的值
      • MSE:均方误差
    • 模型是否拟合到了足够的信息
      • R2

实战

  • 房地产估价数据集数据集(house.xlsx)
    • 数据集信息:
      • 房地产估值的市场历史数据集来自台湾新北市新店区。“房地产估价” 是一个回归问题。
    • 属性信息:
      • 输入如下:
        • X1 =交易日期(例如,2013.250 = 2013年3月,2013.500 = 2013年6月,等等)
        • X2 =房屋年龄(单位:年)
        • X3 =到最近的捷运站的距离(单位:米) )
        • X4 =步行生活圈中的便利店的数量(整数)
        • X5 =地理坐标,纬度。(单位:度)
        • X6 =地理坐标,经度。(单位:度)
      • 输出结果如下:
        • Y =单位面积的房价(10000新台币/ Ping,其中Ping是本地单位,1 Ping = 3.3米平方)
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
import pandas as pd
from sklearn.metrics import mean_squared_error as MSE
#读取数据
df = pd.read_excel('./datasets/house.xlsx')
#删除多余的列
df = df.drop(labels='No',axis=1)
#特征 标签
feature =  df.loc[:,df.columns != 'Y house price of unit area']
target = df['Y house price of unit area']
#数据集的切分
x_train,x_test,y_train,y_test = train_test_split(feature,target,test_size=0.2,random_state=2020)

linner = LinearRegression()
linner.fit(x_train,y_train)

y_pred = linner.predict(x_test)
MSE(y_test,y_pred) #55.334060500911804
y_test.max(),y_test.min() #(63.2, 13.8)
linner.score(x_test,y_test) #0.6108181277767183

linner.score(x_train,y_train) #0.5750984249253495
#在测试集和训练集表现都不好,因此为欠拟合

#出现问题:训练好的模型没有很好的拟合到原始的样本数据中标签和特征值之间的线性关系。
  • 使用多项式回归处理上述案例出现的欠拟合问题

二次项

feature =  df.loc[:,df.columns != 'Y house price of unit area']
target = df['Y house price of unit area']
#给原始特征增加高次项特征
from sklearn.preprocessing import PolynomialFeatures
p = PolynomialFeatures(degree=2)
d_2_feature = p.fit_transform(feature)
#数据集的切分
x_train,x_test,y_train,y_test = train_test_split(d_2_feature,target,test_size=0.2,random_state=2020)

linner = LinearRegression().fit(x_train,y_train)
y_pred = linner.predict(x_test)
MSE(y_test,y_pred) #30.83018292349415
linner.score(x_test,y_test) #0.7831616150606915

​ 三次项

feature =  df.loc[:,df.columns != 'Y house price of unit area']
target = df['Y house price of unit area']
#给原始特征增加高次项特征
from sklearn.preprocessing import PolynomialFeatures
p = PolynomialFeatures(degree=3)
d_3_feature = p.fit_transform(feature)
#数据集的切分
x_train,x_test,y_train,y_test = train_test_split(d_3_feature,target,test_size=0.2,random_state=2020)

linner = LinearRegression().fit(x_train,y_train)
y_pred = linner.predict(x_test)
MSE(y_test,y_pred) #34.37178091836353
linner.score(x_test,y_test) #0.7582524411119846

欠拟合&&过拟合

  • 案例1:
    • 现在有一组天鹅的特征数据然后对模型进行训练,然后模型学习到的内容是有翅膀,嘴巴长的就是天鹅。然后使用模型进行预测,该模型可能会将所有符合这两个特征的动物都预测为天鹅,则肯定会有误差的,因为鹦鹉,秃鹫都符合有翅膀和嘴巴长的特征。
      • 原因:模型学习到的天鹅的特征太少了,导致区分标准太粗糙,不能准确的识别出天鹅。
  • 案例2:
    • 更新了样本的特征数据了,增加了一些特征,然后训练模型。模型这次学习到的内容是,有翅膀、嘴巴长、白色、体型像2、脖子长且有弯度的就是天鹅。然后开始使用模型进行预测,现在一组测试数据为鹦鹉,因为鹦鹉的体型小,脖子短不符合天鹅的特征,则预测结果为不是天鹅。然后又有一组特征为黑天鹅,结果因为颜色不是白色,预测成了黑天鹅。
      • 原因:现在模型学习到的特征已经基本可以区分天鹅和其他动物了。但是学习到的特征中有一项是羽毛是白色,那么就会导致将黑天鹅无法识别出来。也就是机器学习到的特征太依赖或者太符合训练数据了。

1.欠拟合&&过拟合

  • 欠拟合:案例1中的场景就可以表示欠拟合
    • 一个假设在训练数据上不能获得很好的拟合,在训练数据以外的数据集上也不能很好的拟合数据,此时认为这个假设出现了欠拟合的现象。(模型过于简单)
  • 过拟合:案例2中的场景就可以表示过拟合
    • 一个假设在训练数据上能够获得比其他假设更好的拟合,但是在训练数据以外的数据集上却不能很好的拟合数据,此时认为这个假设出现了过拟合现象。(模型过于复杂)
欠拟合 过拟合.png

2.欠拟合和过拟合的解决

  • 欠拟合:

    • 原因:模型学习到样本的特征太少
    • 解决:增加样本的特征数量(多项式回归)
  • 过拟合:

    • 原因:原始特征过多,存在一些嘈杂特征。
    • 解决:
      • 进行特征选择,消除关联性大的特征(很难做)
      • 正则化之岭回归(掌握)
  • 模型的复杂度 --》回归出直线or曲线 :

    回归模型算法就是在寻找特征值和目标值之间存在的某种关系,那么这种关系越复杂则表示训练出的模型的复杂度越高,反之越低。

    模型的复杂度由特征和目标之间的关系导致的 , 特征和目标之间的关系不仅仅是线性关系

1.欠拟合的处理:多项式回归

为了解决欠拟合的情 经常要提高线性的次数(高次多项式)建立模型拟合曲线,次数过高会导致过拟合,次数不够会欠拟合。

  • y = w*x + b 一次多项式函数
  • y = w1x^2 + w2x + b 二次多项式函数
  • y = w1x^3 + w2x^2 + w3*x + b 三次多项式函数

高次多项式函数的表示为曲线

线性回归模型y=wx+b只能解决线性(回归出的为直线)问题,多项式回归能够解决非线性回归(回归出的为曲线)问题。

多项式回归可以看成特殊的线性模型

  • 建立二次多项式线性回归模型进行预测

    • 根据二次多项式公式可知,需要给原始特征添加更高次的特征数据x^2.

      • y=w1x∧2+w2x+b
    • 如何给样本添加高次的特征数据呢?

      • 使用sklearn.preprocessing.PolynomialFeatures来进行更高次特征的构造

        • 它是使用多项式的方法来进行的,如果有a,b两个特征,那么它的2次多项式为(1,a,b,a^2,ab, b^2)

        • PolynomialFeatures有三个参

          • degree:控制多项式的度

          • interaction_only: 默认为False,如果指定为True,上面的二次项中没有a2和b2。

          • include_bias:默认为True。如果为False的话,那么就不会有上面的1那一项

            #工具类的基本使用
            from sklearn.preprocessing import PolynomialFeatures
            p = PolynomialFeatures(degree=2)
            p.fit_transform([[3,5]])
            #array([[ 1.,  3.,  5.,  9., 15., 25.]])
            
示例
  • 下面模拟 根据蛋糕的直径大小 预测蛋糕价格
from sklearn.linear_model import LinearRegression
import numpy as np
import matplotlib.pyplot as plt
# 样本的训练数据,特征和目标值
x_train = [[6], [8], [10], [14], [18]] #大小
y_train = [[7], [9], [13], [17.5], [18]]#价格
linner = LinearRegression()
linner.fit(x_train,y_train)
#画图
y_pred = linner.predict(x_train)
plt.scatter(x_train,y_train)
plt.plot(x_train,y_pred)
plt.xlabel('size_x')
plt.ylabel('price_y')
蛋糕1.png
  • 建立二次多项式线性回归模型进行预测
#二次特征的构造
p = PolynomialFeatures(degree=2)
d_2_train = p.fit_transform(x_train)
linner = LinearRegression().fit(d_2_train,y_train)
#画图
y_pred = linner.predict(d_2_train)
plt.scatter(x_train,y_train)
plt.plot(x_train,y_pred)
plt.xlabel('size_x')
plt.ylabel('price_y')
蛋糕2.png
  • 建立3次多项式线性回归模型进行预测
# 3次特征的构造
p = PolynomialFeatures(degree=3)
d_3_train = p.fit_transform(x_train)
linner = LinearRegression().fit(d_3_train,y_train)
#画图
y_pred = linner.predict(d_3_train)
plt.scatter(x_train,y_train)
plt.plot(x_train,y_pred)
plt.xlabel('size_x')
plt.ylabel('price_y')
蛋糕3.png
2.过拟合处理:正则化
- 将过拟合的曲线的凹凸幅度减少就可以将过拟合曲线趋近于拟合曲线了。那么过拟合曲线的凹凸肯定是由y=wx**2+x**3+x**4中的高次项导致的,那么正则化就可以通过不断的尝试发现高次项的特征然后这些特征的权重w调小到0,则高次项的特征没有了,那么凹凸幅度就减少了,就越趋近于拟合曲线了!
- 可以使得高次项的w权重减小,趋近于0.
- LinnerRegression是没有办法进行正则化的,所以该算法模型容易出现过拟合,并且无法解决。
  • L2正则化:
    • 使用带有正则化算法的回归模型(Ridge岭回归)处理过拟合的问题。
3.Ridge岭回归:具备L2正则化的线性回归模型
  • API:from sklearn.linear_model import Ridge

  • Ridge(alpha=1.0):

    • alpha:正则化的力度,力度越大,则表示高次项的权重w越接近于0,导致过拟合曲线的凹凸幅度越小。
      • 取值:0-1小数或者1-10整数
    • coef_:回归系数
  • 使用岭回归可以通过控制正则化力度参数alpha降低高次项特征的权重

x_train = [[6], [8], [10], [14], [18]] #大小
y_train = [[7], [9], [13], [17.5], [18]]#价格
p = PolynomialFeatures(degree=3)
d_3_train = p.fit_transform(x_train)
linner = LinearRegression().fit(d_3_train,y_train)
linner.coef_
#array([[ 0.        , -1.42626096,  0.31320489, -0.01103344]])
from sklearn.linear_model import Ridge
p = PolynomialFeatures(degree=3)
d_3_train = p.fit_transform(x_train)
ridge=Ridge(alpha=0.1)
ridge.fit(d_3_train,y_train)
ridge.coef_
#array([[ 0.        , -0.54302902,  0.23512062, -0.00888958]])
  • 岭回归的优点:
    • 获取的回归系数更符合实际更可靠
    • 在病态数据(异常值多的数据)偏多的研究中有更大的存在意义
模型的保存和加载
  • from sklearn.externals import joblib

    • joblib.dump(model,'xxx.m'):保存
    • joblib.load('xxx.m'):加载
  • import pickle

    • with open('./123.pkl','wb') as fp:
      • pickle.dump(linner,fp)
    • with open('./123.pkl','rb') as fp:
      • linner = pickle.load(fp)
import pickle
with open('./ridge.pkl','wb') as fp:
    pickle.dump(ridge,fp)
ridge=None
with open('./ridge.pkl','rb') as fp:
    ridge=pickle.load(fp)
print(ridge)