手擼機器學習演算法 - 多項式迴歸

JesusBilly發表於2021-06-17

系列文章目錄:

演算法介紹

今天我們來一起學習一個除了線性迴歸外最最最簡單的迴歸演算法:多項式迴歸

從線性迴歸到多項式迴歸

首先我們一起來學習下多項式迴歸,事實上與線性迴歸相比,沒有增加任何需要推導的東西,唯一增加的就是對原始資料進行多項式特徵轉換,這有點類似我們在非線性問題中對特徵的處理:將\(x_1\)轉換為\(x_1^2\),之前我們是通過對資料的探索來決定如何進行轉換,在多項式迴歸中,則是簡單的指定一個階,然後對所有列構建N元N次的方程中的所有項即可,這麼說有點抽象,下面舉個簡單的例子:
對有兩個特徵的資料做三階的多項式特徵轉換:\(x_1 + x_2\) 轉換為 \(x_1^3 + x_2^3 + x_1^2*x_2 + x_2^2*x_1 + x_1^2 + x_2^2 + x_1*x_2 + x_1 + x_2\),可以看到,通過做三階變換,特徵數從兩個增長到了九個,多項式特徵轉換是非常簡單且實用的構建特徵手段之一,它不僅能構建特徵自身的高階版,同時還能構建特徵與特徵之間的組合特徵,通常效果都不錯哦;

程式碼實現

上面說了,多項式迴歸與線性迴歸唯一區別就在多項式特徵構建上,因此程式碼部分也主要關注這一點,關於多項式特徵構建,大家既可以基於sklearn庫中的方法實現,也可以自己實現,都很簡單哈;

sklearn實現多項式特徵構建

from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(degrees)
X = poly.fit_transform(X)

自己實現多項式特徵構建

def build_combs(self,elements,times):
    '''
    構建多項式的元組合
    elements 元數
    times 次數
    '''
    x_list = sum([[i]*times for i in range(elements)],[])
    combs = sum([list(set(combinations(x_list,i))) for i in range(1,times+1)],[])
    return [list(comb) for comb in combs]

def polynomial(self,x):
    '''
    x shape = [1 N]
    '''
    fun = lambda x,y:x*y
    return [reduce(fun,x[comb]) for comb in self.combs]

執行結果

全部程式碼

多項式迴歸程式碼

import numpy as np
from itertools import combinations
from functools import reduce
from 線性迴歸最小二乘法矩陣實現 import LinearRegression as LR

class PolynomialRegression(LR):
    def __init__(self,X,y,degrees=1):
        self.combs = self.build_combs(X.shape[1],degrees)
        X = np.array([self.polynomial(x) for x in X])
        super(PolynomialRegression,self).__init__(X,y)

    def predict(self,x):
        x = self.polynomial(x)
        return super(PolynomialRegression,self).predict(x)

    def build_combs(self,elements,times):
        '''
        構建多項式的元組合
        elements 元數
        times 次數
        '''
        x_list = sum([[i]*times for i in range(elements)],[]) # 二元二次 [1 1 2 2]
        combs = sum([list(set(combinations(x_list,i))) for i in range(1,times+1)],[]) # 二元二次 [[1 1] [2 2] [1 2] [1] [2]]
        return [list(comb) for comb in combs]

    def polynomial(self,x):
        '''
        x shape = [1 N]
        '''
        fun = lambda x,y:x*y
        return [reduce(fun,x[comb]) for comb in self.combs]

測試程式碼

import matplotlib.pyplot as plt
import numpy as np
from sklearn.model_selection import train_test_split as tts
from 多項式迴歸 import PolynomialRegression as PR

rnd = np.random.RandomState(3)
x_min, x_max = 0, 10

def pain(pos=141,xlabel='x',ylabel='y',title='',x=[],y=[],line_x=[],line_y=[]):
    plt.subplot(pos)
    plt.title(title)
    plt.xlabel(xlabel)
    plt.ylabel(ylabel)
    plt.scatter(x,y)
    plt.plot(line_x,line_y)

# 上帝函式 y=f(x)
def f(x):
    return x**5-22*x**4+161*x**3-403*x**2+36*x+938

# 上帝分佈 P(Y|X)
def P(X):
    return f(X) + rnd.normal(scale=30, size=X.shape)

# 通過 P(X, Y) 生成資料集 D
X = rnd.uniform(x_min, x_max, 50)   # 通過均勻分佈產生 X
y = P(X)                            # 通過 P(Y|X) 產生 y

plt.subplot(332)
plt.scatter(x=X, y=y)
xx = np.linspace(x_min, x_max)
plt.plot(xx, f(xx), 'k--')

X_train,X_test,y_train,y_test = tts(X,y,test_size=0.3,random_state=10086)
X_train,X_test,y_train,y_test = X_train.reshape(-1,1),X_test.reshape(-1,1),y_train.reshape(-1,1),y_test.reshape(-1,1)

for pos,deg in zip([334,335,336,337,338,339],[1,3,5,8,15,20]):
    model = PR(X=X_train,y=y_train,degrees=deg)
    w,b = model.train()
    x_min,x_max = min(X_train),max(X_train)
    line_x = [x_min+(x_max-x_min)*(i/100) for i in range(100)]
    line_y = [model.predict(x) for x in line_x]
    pain(pos,'x','y','DEG='+str(deg),X_train[:,0],y_train[:,0],line_x,line_y)

plt.tight_layout()
plt.show()

最後

可以看到,實際上多項式迴歸是非常簡單的,實際應用上對於很多簡單任務的擬合效果也非常好,解釋性也不錯;

相關文章