機器學習3- 一元線性回歸+Python實現


1. 線性模型

給定 \(d\) 個屬性描述的示例 \(\boldsymbol{x} = (x_1; x_2; ...; x_d)\),其中 \(x_i\)\(\boldsymbol{x}\) 在第 \(i\) 個屬性上的取值,線性模型linear model)試圖學得一個通過屬性的線性組合來進行預測的函數,即:

\[f(\boldsymbol{x}) = w_1x_1 + w_2x_2 + ... + w_dx_d +b \tag{1.1} \]

使用向量形式為:

\[f(\boldsymbol{x}) = \boldsymbol{w}^T\boldsymbol{x}+b \tag{1.2} \]

其中 \(\boldsymbol{w} = (w_1;w_2;...;w_d)\),表達了各屬性在預測中的重要性。

2. 線性回歸

給定數據集 \(D = \lbrace(\boldsymbol{x}_1,{y}_1), (\boldsymbol{x}_2,{y}_2), ..., (\boldsymbol{x}_m,{y}_m)\rbrace\),其中 \(\boldsymbol{x}_i = (x_{i1}; x_{i2}; ...; x_{id})\)\(y_i \in \mathbb{R}\)線性回歸linear regression)試圖學得一個能盡可能准確地預測真實輸出標記的線性模型,即:

\[f(\boldsymbol{x}_i) = \boldsymbol{w}^T\boldsymbol{x}_i+b \text{,使得} f(\boldsymbol{x}_i) \simeq y_i\tag{2.1} \]

2.1 一元線性回歸

先只考慮輸入屬性只有一個的情況,\(D = \lbrace({x}_1,{y}_1), ({x}_2,{y}_2), ..., ({x}_m,{y}_m)\rbrace\)\(x_i \in \mathbb{R}\)。對離散屬性,若屬性值存在order)關系,可通過連續化將其轉化為連續值。

如”高度“屬性的取值“高”、“中”、“低”,可轉化為\(\{1.0, 0.5, 0.0\}\)

若不存在序關系,則假定有 \(k\) 種可能的屬性值,將其轉化為 \(k\) 維向量。

如“瓜類”屬性的取值有“冬瓜”、“西瓜”、“南瓜”,可轉化為 \((0,0,1),(0,1,0),(1,0,0)\)

線性回歸試圖學得:

\[f(x_i) = wx_i+b\text{,使得}f(x_i)\simeq y_i \tag{2.2} \]

為使 \(f(x_i)\simeq y_i\),即:使 \(f(x)\)\(y\) 之間的差別最小化。
考慮回歸問題的常用性能度量——均方誤差(亦稱平方損失(square loss)),即讓均方誤差最小化:

\[\begin{aligned} (w^*,b^*) = \underset{(w,b)}{arg\ min}\sum_{i=1}^m(f(x_i)-y_i)^2 \\ = \underset{(w,b)}{arg\ min}\sum_{i=1}^m(y_i-wx_i-b)^2 \end{aligned} \tag{2.3} \]

\(w^*,b^*\) 表示 \(w\)\(b\) 的解。
均方誤差對應了歐幾里得距離,簡稱歐氏距離(Euclidean distance)。
基於均方誤差最小化來進行模型求解的方法稱為最小二乘法least square method)。在線性回歸中,就是試圖找到一條直線,使得所有樣本到直線上的歐氏距離之和最小。

下面需要求解 \(w\)\(b\) 使得 \(E_{(w,b)} = \sum\limits_{i=1}^m(y_i-wx_i-b)^2\) 最小化,該求解過程稱為線性回歸模型的最小二乘參數估計parameter estimation)。

\(E_{(w,b)}\) 為關於 \(w\)\(b\) 的凸函數,當它關於 \(w\)\(b\) 的導數均為 \(0\) 時,得到 \(w\)\(b\) 的最優解。將 \(E_{(w,b)}\) 分別對 \(w\)\(b\) 求導數得:

\[\frac{\partial{E_{(w,b)}}}{\partial(w)} = 2\Big(w\sum_{i=1}^m x_i^2 - \sum_{i=1}^m (y_i-b)x_i\Big) \tag{2.4} \]

\[\frac{\partial{E_{(w,b)}}}{\partial(b)} = 2\Big(mb - \sum_{i=1}^m (y_i-wx_i)\Big) \tag{2.5} \]

令式子 (2.4) 和 (2.5) 為 \(0\) 得到 \(w\)\(b\) 的最優解的閉式(closed-form)解:

\[w = \frac{\sum_\limits{i=1}^m y_i(x_i-\overline{x})}{\sum\limits_{i=1}^m x_i^2 - \frac{1}{m}\Big(\sum\limits_{i=1}^m x_i\Big)^2} \tag{2.6} \]

\[b = \frac{1}{m}\sum_{i=1}^m (y_i-wx_i) \tag{2.7} \]

其中 \(\overline{x} = \frac{1}{m}\sum\limits_{i=1}^m x_i\)\(x\) 的均值。

其他解法:

\[方差\ var(x) = \frac{\sum\limits_{i=1}^m(x_i-\bar{x})^2}{m-1} \]

\[協方差\ cov(x,y) = \frac{\sum_\limits{i=1}^m (x_i-\overline{x})(y_i-\overline{y})}{n-1} \]

\[w = \frac{cov(x,y)}{var(x)} = \frac{\sum_\limits{i=1}^m (x_i-\overline{x})(y_i-\overline{y})}{\sum\limits_{i=1}^m (x_i-\overline{x})^2} \]

\[b = \bar{y} - w\bar{x} \]

3. 一元線性回歸的Python實現

現有如下訓練數據,我們希望通過分析披薩的直徑與價格的線性關系,來預測任一直徑的披薩的價格。

其中 Diameter 為披薩直徑,單位為“英寸”;Price 為披薩價格,單位為“美元”。

3.1 使用 stikit-learn

3.1.1 導入必要模塊

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

3.1.2 使用 Pandas 加載數據

pizza = pd.read_csv("pizza.csv", index_col='Id')
pizza.head()  # 查看數據集的前5行

3.1.3 快速查看數據

我們可以使用 matplotlib 畫出數據的散點圖,x 軸表示披薩直徑,y 軸表示披薩價格。

def runplt():
    plt.figure()
    plt.title("Pizza price plotted against diameter")
    plt.xlabel('Diameter')
    plt.ylabel('Price')
    plt.grid(True)
    plt.xlim(0, 25)
    plt.ylim(0, 25)
    return plt

dia = pizza.loc[:,'Diameter'].values
price = pizza.loc[:,'Price'].values
print(dia)
print(price)
plt = runplt()
plt.plot(dia, price, 'k.')
plt.show()
[ 6  8 10 14 18]
[ 7.   9.  13.  17.5 18. ]

3.1.4 使用 stlearn 創建模型

model = LinearRegression()  # 創建模型
X = dia.reshape((-1,1))
y = price
model.fit(X, y)  # 擬合

X2 = [[0], [25]] # 取兩個預測值
y2 = model.predict(X2)  # 進行預測
print(y2)  # 查看預測值

plt = runplt()
plt.plot(dia, price, 'k.')
plt.plot(X2, y2, 'g-')  # 畫出擬合曲線
plt.show()
[ 1.96551724 26.37284483]

這里 fit()方法學得了一元線性回歸模型 \(f(x) = wx+b\),這里 \(x\) 指披薩的直徑,\(f(x)\) 為預測的披薩的價格。

fit() 的第一個參數 X 為 shape(樣本個數,屬性個數) 的數組或矩陣類型的參數,代表輸入空間;
第二個參數 y 為 shape(樣本個數,) 的數組類型的參數,代表輸出空間。

3.1.5 模型評估

成本函數(cost function)也叫損失函數(lost function),用來定義模型與觀測值的誤差。

模型預測的價格和訓練集數據的差異稱為訓練誤差training error)也稱殘差residuals)。

plt = runplt()
plt.plot(dia, price, 'k.')
plt.plot(X2, y2, 'g-')

# 畫出殘差
yr = model.predict(X)
for index, x in enumerate(X):
    plt.plot([x, x], [y[index], yr[index]], 'r-')
    
plt.show()

根據最小二乘法,要得到更高的性能,就是讓均方誤差最小化,而均方誤差就是殘差平方和的平均值。

print("均方誤差為: %.2f" % np.mean((model.predict(X)-y) ** 2))
均方誤差為: 1.75

3.2 手動實現

3.2.1 計算 w 和 b

\(w\)\(b\) 的最優解的閉式(closed-form)解為:

\[w = \frac{\sum_\limits{i=1}^m y_i(x_i-\overline{x})}{\sum\limits_{i=1}^m x_i^2 - \frac{1}{m}\Big(\sum\limits_{i=1}^m x_i\Big)^2} \tag{2.6} \]

\[b = \frac{1}{m}\sum_{i=1}^m (y_i-wx_i) \tag{2.7} \]

其中 \(\overline{x} = \frac{1}{m}\sum\limits_{i=1}^m x_i\)\(x\) 的均值。

\[方差\ var(x) = \frac{\sum\limits_{i=1}^m(x_i-\bar{x})^2}{m-1} \]

\[協方差\ cov(x,y) = \frac{\sum_\limits{i=1}^m (x_i-\overline{x})(y_i-\overline{y})}{n-1} \]

\[w = \frac{cov(x,y)}{var(x)} = \frac{\sum_\limits{i=1}^m (x_i-\overline{x})(y_i-\overline{y})}{\sum\limits_{i=1}^m (x_i-\overline{x})^2} \]

\[b = \bar{y} - w\bar{x} \]

下面使用 Python 計算 \(w\)\(b\) 的值:

# 法一:
# w = np.sum(price * (dia - np.mean(dia))) / (np.sum(dia**2) - (1/dia.size) * (np.sum(dia))**2)
# b = (1 / dia.size) * np.sum(price - w * dia)

#法二:
variance = np.var(dia, ddof=1)  # 計算方差,doff為貝塞爾(無偏估計)校正系數
covariance = np.cov(dia, price)[0][1]  # 計算協方差
w = covariance / variance
b = np.mean(price) - w * np.mean(dia)

print("w = %f\nb = %f" % (w, b))

y_pred = w * dia + b

plt = runplt()
plt.plot(dia, price, 'k.')  # 樣本點
plt.plot(dia, y_pred, 'b-')  # 手動求出的線性回歸模型
plt.plot(X2, y2, 'g-.')  # 使用LinearRegression.fit()求出的模型
plt.show()
w = 0.976293
b = 1.965517

可以看到兩條直線重合,我們求出的回歸模型與使用庫求出的回歸模型相同。

3.2.2 功能封裝

將上述代碼封裝成類:

class LinearRegression:
    """
    擬合一元線性回歸模型

    Parameters
    ----------
    x : shape 為(樣本個數,)的 numpy.array
        只有一個屬性的數據集

    y : shape 為(樣本個數,)的 numpy.array
        標記空間

    Returns
    -------
    self : 返回 self 的實例.
    """
    def __init__(self):
        self.w = None
        self.b = None

    def fit(self, x, y):
        variance = np.var(x, ddof=1)  # 計算方差,doff為貝塞爾(無偏估計)校正系數
        covariance = np.cov(x, y)[0][1]  # 計算協方差
        self.w = covariance / variance
        self.b = np.mean(y) - w * np.mean(x)
#         self.w = np.sum(y * (x - np.mean(x))) / (np.sum(x**2) - (1/x.size) * (np.sum(x))**2)
#         self.b = (1 / x.size) * np.sum(y - self.w * x)
        return self

    def predict(self, x):
        """
        使用該線性模型進行預測

        Parameters
        ----------
        x : 數值 或 shape 為(樣本個數,)的 numpy.array
            屬性值

        Returns
        -------
        C : 返回預測值
        """
        return self.w * x + self.b

使用:

# 創建並擬合模型
model = LinearRegression()
model.fit(dia, price)

x2 = np.array([0, 25])  # 取兩個預測值
y2 = model.predict(x2)  # 進行預測
print(y2)  # 查看預測值

runplt()
plt.plot(dia, price, 'b.')
plt.plot(x2, y2, 'y-')  # 畫出擬合
plt.show()
[ 1.96551724 26.37284483]


此文原創禁止轉載,轉載文章請聯系博主並注明來源和出處,謝謝!
作者: Raina_RLN https://www.cnblogs.com/raina/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM