機器學習(迴歸三)——線性迴歸-多項式擴展

概述

前兩篇博客介紹的是線性迴歸,線性迴歸的一個問題是有可能出現欠擬合現象,因爲它求的是具有最小均方誤差的無偏估計。顯而易見,如果模型欠擬合將不能取得最好的預測效果。所以有些方法允許在估計中引入一些偏差,從而降低預測的均方誤差。其中的一個方法是本文的多項式擴展,還有一個是後續的博客會介紹的局部加權線性迴歸(Locally Weighted Linear Regression,LWLR)。在正式介紹多項式擴展之前,先來看一張對比圖
在這裏插入圖片描述
圖1,是真實的樣本數據,要做的是找到擬合這些點的線。
圖2,我們可以把這些點分成不同的區間段,區間段如果選的比較小,兩個點一段,均值作爲預測值。甚至還可以更小,小到一個點一段,類似於微分的思想。這樣肯定會很好的擬合這些樣本。
圖3,如果選的區間段的跨度比較大,還以均值爲預測值,會發現此時的誤差會特別大。
上面說的L(f)是預測值與實際值之間的差值,是期望誤差。Ω(f)表示方差誤差,指複雜度。error = Ω(f) + L(f),實際來講不僅僅是降低期望誤差,而是二者結合的一個最優。也就是圖4的效果。
換句話說,圖2是過擬合,圖3是欠擬合,圖4是比較理想的結果。

就像上圖,現實中的樣本數據往往都是非線性的,如果能直接找到曲線去擬合,那往往效果會更好。前兩篇博客介紹的是線性迴歸,線性迴歸針對的是θ而言的,即θjθ_j 的最高次項是1次。對於樣本本身而言,樣本可以是非線性的也就是說最終得到的函數f:x->y;函數f(x)可以是非線性的,比如:曲線等。
在這裏插入圖片描述
很顯然,如果用直線去擬合這種曲線的情況,結果會很不理想,後面的兩張圖的擬合效果好很多。這就是我們本文提到的多項式擴展。

代碼

這裏依然使用前面的“家庭用電預測”的樣本數據,通過代碼看一下時間與電壓的多項式關係。

# 引入所需要的全部包
import sklearn
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import PolynomialFeatures
from sklearn.pipeline import Pipeline

import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import pandas as pd
import time


# 創建一個時間字符串格式化字符串
def date_format(dt):
    import time
    t = time.strptime(' '.join(dt), '%d/%m/%Y %H:%M:%S')
    return (t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec)


## 設置字符集,防止中文亂碼
mpl.rcParams['font.sans-serif']=[u'simHei']
mpl.rcPar
ams['axes.unicode_minus']=False


# 加載數據
path = 'datas\household_power_consumption_200.txt' ## 200行數據
path = 'datas\household_power_consumption_1000.txt' ## 1000行數據
df = pd.read_csv(path, sep=';', low_memory=False)

# 日期、時間、有功功率、無功功率、電壓、電流、廚房用電功率、洗衣服用電功率、熱水器用電功率
names2=df.columns
names=['Date', 'Time', 'Global_active_power', 'Global_reactive_power', 'Voltage', 'Global_intensity', 'Sub_metering_1', 'Sub_metering_2', 'Sub_metering_3']

# 異常數據處理(異常數據過濾)
new_df = df.replace('?', np.nan)
datas = new_df.dropna(axis=0,how = 'any') # 只要有數據爲空,就進行刪除操作


## 時間和電壓之間的關係(Linear)
# 獲取x和y變量, 並將時間轉換爲數值型連續變量
X = datas[names[0:2]]
X = X.apply(lambda x: pd.Series(date_format(x)), axis=1)
Y = datas[names[4]].values

# 對數據集進行測試集合訓練集劃分 
X_train,X_test,Y_train,Y_test = train_test_split(X, Y, test_size=0.2, random_state=0)

# 數據標準化
ss = StandardScaler()
X_train = ss.fit_transform(X_train) # 訓練並轉換
X_test = ss.transform(X_test) ## 直接使用在模型構建數據上進行一個數據標準化操作 

# 模型訓練
lr = LinearRegression()
lr.fit(X_train, Y_train) ## 訓練模型

# 模型校驗
y_predict = lr.predict(X_test) ## 預測結果

# 模型效果
print("準確率:",lr.score(X_test, Y_test))

## 預測值和實際值畫圖比較
t=np.arange(len(X_test))
plt.figure(facecolor='w')
plt.plot(t, Y_test, 'r-', linewidth=2, label=u'真實值')
plt.plot(t, y_predict, 'g-', linewidth=2, label=u'預測值')
plt.legend(loc = 'lower right')
plt.title(u"線性迴歸預測時間和功率之間的關係", fontsize=20)
plt.grid(b=True)#網格
plt.show()

在這裏插入圖片描述
發現:時間和功能之間的關係,不是那的契合,就是說找的這條直線沒法很好擬合樣本點的。那樣本點是不是曲線形式,我們可不可以進行一下擴展?下面就採用了多項式擴展。
多項式擴展:將特徵與特徵之間進行整合,從而形成新的特徵的一個過程;從數學空間上來講,就是將低維度空間的點映射到高維度空間中。(比如說一維,平方後,就變成了二維。映射到高維之後,數據會更分散,一些特性就更容易體現出來)屬於特徵工程的一種操作。
作用:通過多項式擴展後,我們可以提高模型的準確率/效果。

## 時間和電壓之間的關係(Linear-多項式)
# Pipeline:管道的意思,講多個操作合併成爲一個操作
# Pipleline總可以給定多個不同的操作,給定每個不同操作的名稱即可,執行的時候,按照從前到後的順序執行
# Pipleline對象在執行的過程中,當調用某個方法的時候,會調用對應過程的對應對象的對應方法
# eg:在下面這個案例中,調用了fit方法,
# 那麼對數據調用第一步操作:PolynomialFeatures的fit_transform方法對數據進行轉換並構建模型
# 然後對轉換之後的數據調用第二步操作: LinearRegression的fit方法構建模型
# eg: 在下面這個案例中,調用了predict方法,
# 那麼對數據調用第一步操作:PolynomialFeatures的transform方法對數據進行轉換
# 然後對轉換之後的數據調用第二步操作: LinearRegression的predict方法進行預測
models = [
    Pipeline([
            ('Poly', PolynomialFeatures()), # 給定進行多項式擴展操作, 第一個操作:多項式擴展
            ('Linear', LinearRegression(fit_intercept=False)) # 第二個操作,線性迴歸
        ])
]

:這裏引入了Pipeline,管道的意思。之前每寫都先fit,再transform。通過管道就可以把他們放到一起。

  • Poly表示名字,是自定義的,這一行屬於第一步,也就是進行多項式擴展;
  • Linear也是表示名字,是自定義,這一行屬於第二步,也就是進行線性迴歸。
model = models[0]
# 獲取x和y變量, 並將時間轉換爲數值型連續變量
X = datas[names[0:2]]
X = X.apply(lambda x: pd.Series(date_format(x)), axis=1)
Y = datas[names[4]]

# 對數據集進行測試集合訓練集劃分
X_train,X_test,Y_train,Y_test = train_test_split(X, Y, test_size=0.2, random_state=0)

# 數據標準化
ss = StandardScaler()
X_train = ss.fit_transform(X_train) # 訓練並轉換
X_test = ss.transform(X_test) ## 直接使用在模型構建數據上進行一個數據標準化操作 

# 模型訓練
t=np.arange(len(X_test))
N = 5
d_pool = np.arange(1,N,1) # 階

:階,即表示擴展後的多項式的最高次

m = d_pool.size
clrs = [] # 顏色
for c in np.linspace(16711680, 255, m):
    clrs.append('#%06x' % int(c))
line_width = 3

plt.figure(figsize=(12,6), facecolor='w')#創建一個繪圖窗口,設置大小,設置顏色
#下面是對階數的迭代
for i,d in enumerate(d_pool):
    plt.subplot(N-1,1,i+1)
    plt.plot(t, Y_test, 'r-', label=u'真實值', ms=10, zorder=N)
    ### 設置管道對象中的參數值,Poly是在管道對象中定義的操作名稱, 後面跟參數名稱;中間是兩個下劃線
    model.set_params(Poly__degree=d) ## 設置多項式的階乘
    model.fit(X_train, Y_train) # 模型訓練
    # Linear是管道中定義的操作名稱
    # 獲取線性迴歸算法模型對象
    lin = model.get_params()['Linear']
    output = u'%d階,係數爲:' % d
    # 判斷lin對象中是否有對應的屬性
    if hasattr(lin, 'alpha_'):
        idx = output.find(u'係數')
        output = output[:idx] + (u'alpha=%.6f, ' % lin.alpha_) + output[idx:]
    if hasattr(lin, 'l1_ratio_'):
        idx = output.find(u'係數')
        output = output[:idx] + (u'l1_ratio=%.6f, ' % lin.l1_ratio_) + output[idx:]
    print (output, lin.coef_.ravel())
    
    # 模型結果預測
    y_hat = model.predict(X_test)
    # 計算評估值
    s = model.score(X_test, Y_test)
    
    # 畫圖
    z = N - 1 if (d == 2) else 0
    label = u'%d階, 準確率=%.3f' % (d,s)
    plt.plot(t, y_hat, color=clrs[i], lw=line_width, alpha=0.75, label=label, zorder=z)
    plt.legend(loc = 'upper left')
    plt.grid(True)
    plt.ylabel(u'%d階結果' % d, fontsize=12)

## 預測值和實際值畫圖比較
plt.suptitle(u"線性迴歸預測時間和功率之間的多項式關係", fontsize=20)
plt.grid(b=True)
plt.show()

看一下輸出的參數:
1階,係數爲: [2.39926650e+02 0.00000000e+00 0.00000000e+00 3.97781449e+00 8.73334650e-01 1.70647992e-01 0.00000000e+00]
2階,係數爲: [ 1.23998300e+02 3.55271368e-14 -7.81597009e-14 5.34497071e+01 2.95068077e+00 2.69407641e-01 -5.32907052e-15 -3.55271368e-15 8.88178420e-16 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 1.02461452e+02 -2.50100464e+01 -5.18469319e-01 0.00000000e+00 -1.02427364e+01 -4.65982219e-01 0.00000000e+00 -3.55472266e-02 0.00000000e+00 .00000000e+00]
3階,係數爲: [ 1.06303324e+12 -7.52773669e+11 2.12816760e+12 -9.53433863e+12 1.50224363e+11 1.24753680e+11 -2.10445177e+11 -2.86373371e+11 -2.73949767e+11 1.63670539e+11 5.97002023e+10 -2.79408605e+11 -3.55726203e+11 -2.59005902e+11 6.79712021e+10 -1.44760428e+10 5.20112328e+10 -9.76562500e-04 2.60610968e+12 -6.05309076e+10 -5.02678348e+10 0.00000000e+00 1.10827637e+00 3.45336914e-01 0.00000000e+00 5.85937500e-03 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 9.10600353e+12 -1.50224363e+11 -1.24753680e+11 0.00000000e+00 -6.41564941e+00 -6.71470642e-01 0.00000000e+00 -1.84478760e-01 0.00000000e+00 0.00000000e+00 4.48028564e+00 2.57629395e-01 0.00000000e+00 -2.65136719e-01 0.00000000e+00 0.00000000e+00 -2.44018555e-01 0.00000000e+00 0.00000000e+00 0.00000000e+00]
4階,係數爲: [ 1.98236950e+13 -6.36266368e+12 -8.19197778e+11 -2.41835460e+13 2.40054791e+12 1.10773332e+12 2.52245897e+12 2.26093380e+12 -4.64598235e+11 -3.10328767e+11 6.98913673e+11 9.69576533e+11 3.36682879e+11 5.31213939e+11 -1.93230766e+11 -7.71628230e+11 3.76054179e+11 1.38354357e+11 5.03666951e+12 -1.14372116e+13 -6.42706951e+11 -1.04958920e+11 9.69966926e+12 2.54696605e+11 -1.21838302e+11 5.77387250e+12 4.40390380e+10 -1.45775272e+10 -1.40971612e+10 -5.91093717e+08 2.89672374e+07 5.29743161e+06 -7.99508243e+05 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 8.80822476e+12 1.81817615e+12 -1.02861244e+12 0.00000000e+00 -3.90835262e+12 -1.02626607e+11 0.00000000e+00 -2.32650507e+12 0.00000000e+00 0.00000000e+00 -1.53422852e+01 -2.18774414e+00 0.00000000e+00 -5.88867188e-01 0.00000000e+00 0.00000000e+00 -2.44140625e-01 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00 -1.83344615e+13 1.04699427e+13 1.96360556e+11 0.00000000e+00 -9.69966926e+12 -2.54696605e+11 0.00000000e+00 -5.77387250e+12 0.00000000e+00 0.00000000e+00 1.53059082e+01 -6.72119141e+00 0.00000000e+00 -2.19726562e-01 0.00000000e+00 0.00000000e+00 3.61328125e-02 0.00000000e+00 0.00000000e+00 0.00000000e+00 -3.05761719e+00 -3.54296875e+00 0.00000000e+00 -4.14062500e-01 0.00000000e+00 0.00000000e+00 2.08007812e-01 0.00000000e+00 0.00000000e+00 0.00000000e+00 -2.53906250e-02 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00]
在這裏插入圖片描述
發現:隨着階數增加,準確率在提高。通過多項式擴展能進行很好的擬合

分析

再結合下面一張簡化的圖,來看一下數據更深層的內容。
在這裏插入圖片描述
對應的係數:
在這裏插入圖片描述
圖中紅色的點是樣本點,如果用線來擬合的話,近似一條二次函數曲線,而做線性迴歸,找的是直線,直線和實際分佈就不太擬合。通過提高階數,能對數據進行很好的擬合(圖2),但當階數特別大時(圖3),出現了很大的拐線。其實這是出現了嚴重的過擬合。
過擬合:如果模型在訓練集上效果非常好,而在測試集上效果不好,那麼認爲這個時候存在過擬合的情況,多項式擴展的時候,如果指定的階數比較大,那麼有可能導致過擬合。從線性迴歸模型中來講,我們認爲訓練出來的模型參數值越大,就表示越存在過擬合的情況。(對於過擬合的情況該如何解決,最常用的就是正則化,也就是加入懲罰項,詳情內容,下篇博客會介紹)
也就是越複雜,越會出現過擬合。

  • 對於線性迴歸,可能就是參數過多(階數過大、參數值比較大)
  • 對於決策樹,就是葉子節點過多

換一個角度來說:
在線性迴歸中,我們可以通過多項式擴展將低維度的數據擴展成爲高維度的數據,從而可以使用線性迴歸模型來解決問題。也就是說對於二維空間中不是線性可分的數據,將其映射到高維空間中後,變成了線性可分的數據。
兩維線性模型:
hθ(x1,x2)=θ0+θ1x1+θ2x2(x1,x2)(x1,x2,x12,x22,x1x2) h_θ (x_1,x_2 )=θ_0+θ_1 x_1+θ_2 x_2 \\ (x_1,x_2 ) \underrightarrow{多項式擴展} (x_1,x_2,x_1^2,x_2^2,x_1 x_2)
五維線性模型:
 hθ(x1,x2)=θ0+θ1x1+θ2x2+θ3x12+θ4x22+θ5x1x2hθ(x1,x2)=θ0+θ1z1+θ2z2+θ3z3+θ4z4+θ5z5 h_θ (x_1,x_2 )=θ_0+θ_1 x_1+θ_2 x_2+θ_3 x_1^2+θ_4 x_2^2+θ_5 x_1 x_2 \\ \underrightarrow{等價} h_θ (x_1,x_2 )=θ_0+θ_1 z_1+θ_2 z_2+θ_3 z_3+θ_4 z_4+θ_5 z_5
通過等價變化後,可以看出,從本質上講,多項式擬合也是一個線性模型。經過上面的分析可以知道,多項式擬合其實是兩個過程:

  1. 對原始特徵向量 x 做多項式特徵生成,得到新的特徵z
  2. 對新的特徵 z 做線性迴歸

其實多項式擴展的這種方法稍想一下就會發現,除了可能的過擬合的問題之外還有:
在最初的例子裏做了一個二階多項式的轉換,對一個二維空間做映射,選擇的新空間是原始空間的所有一階和二階的組合,得到了5個維度;如果原始空間是三維,那麼我們會得到9維的新空間;如果原始空間是n維,那麼我們會得到一個n(n+3)/2 維的新空間;這個數目是呈爆炸性增長的,這給計算帶來了非常大的困難。(SVM對於非線性可分就是用的升維的方式,只不過採用的是更有效的核函數)

總結

事實上,線性迴歸就是特殊的多項式迴歸,這很好理解,最後,我想說的是,不考慮過擬合之類的問題,理論上來說,多項式迴歸應該是可以擬合任何的數據的,因爲我們有泰勒定理,任意的函數都可以通過泰勒級數逼近,或許我們構建的多項式過於複雜,有很多項是不需要的(因爲多項式迴歸必須把同一次項的所有情況都要考慮,比如兩個特徵的三次多項式,只要是特徵的冪是三就需要稱爲多項式的項,而不僅僅是每個特徵的一二三次冪),但是我們也要知道,在訓練模型的過程中,對數據影響不大的項的參數是會最終趨於零的,所以不考慮計算量、過擬合等問題,哪怕我們只用多項式迴歸這個方法,也足夠擬合任意的數據集了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章