從一道面試題認識普通最小二乘法(Ordinary Least Squares)

前言

昨天看到一道有意思的面試題,題目以下: html

3981570520978_.pic.jpg

評論大體分爲兩種:python

  • 認爲答案是 y = x + 1
  • 認爲這是一道線性迴歸題

僅憑直覺的話 y = x + 1 彷佛符合題目要求,畢竟此函數通過其中三個點,但顯然這是一個經典的使用線性迴歸的場景,那麼咱們該如何證實使用線性迴歸能獲得比 y = x + 1 更好的結果呢?git

使用庫實現線性迴歸

咱們說把大象放進冰箱只要三步,同理實現線性迴歸也只要三步,讓咱們先看看使用 scikit-learn 的線性迴歸模型擬合出的函數與 y = x + 1 有何不一樣,代碼實現以下。github

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


def plot():
    # 重塑爲 2D array 以知足模型輸入要求
    x = np.array([[-1, 0, 1, 0, 1]]).reshape((-1, 1))
    y = np.array([0, 1, 1, 2, 2])

    model = LinearRegression().fit(x, y)

    plt.plot(x, model.predict(x), label=f'y = {model.coef_[0]:.3f} * x + {model.intercept_:.3f}')
    plt.plot(x, list(map(lambda a: a + 1, x)), label='y = x + 1')

    plt.scatter(x, y)
    plt.title('Simple Linear Regression')
    plt.legend()
    plt.grid()
    plt.show()


if __name__ == '__main__':
    plot()
複製代碼

畫出來的圖長下面這樣,咋一看彷佛很反直覺,模型給出的擬合函數距其餘點的距離彷佛並不比 y = x + 1 小,爲啥模型會給出這個結果呢?不要着急繼續往下看。 面試

simple_linear_regression.png

查看官方文檔

對,沒錯,如今要作的就是了解 scikit-learn 的線性迴歸模型是使用何種方法實現的。 打開 sklearn.linear_model.LinearRegression — scikit-learn 0.21.3 documentation 能夠看到文檔中赫然寫着模型是用 Ordinary Least Squares 實現的(即普通最小二乘法)。函數

sklearndoc.jpg

接着再往下翻一翻,能夠看到模型自己提供了度量擬合優度的方法 score,使用的是決定係數(Coefficient of determination),被定義爲spa

SS_{res} = \sum_{i=1}^n (y_i - f_i)^2
\label{eq:res}
SS_{tot} = \sum_{i=1}^n (y_i - \overline{y})^2
\label{eq:tot}
R^2 = 1 - \frac{SS_{res}}{SS_{tot}}

其中 SS_{res} 爲均方偏差,與用何模型無關;SS_{tot}爲模型預測結果的均方偏差。所以這兩個值的比例表示模型仍存在的均方偏差比例,R^2 表示模型消除的均方偏差比例。3d

須要說明的是線性模型與實際值的偏差是如何定義的,在此場景中不管咱們使用哪一個函數,其自己均可以歸結爲:y = kx + b,其偏差爲實際數據到擬合線的豎直偏移或稱殘差(residual),對於某點 i的殘差可被計算爲:res_i = y_i - (kx + b)code

以下圖所示,紅點表明 x 與 y 的均值的座標表示,SS_{tot} 由數據點自己所決定。天然咱們能作的是使模型預測值與實際點的殘差儘量越小越好(如圖中虛線所示),咱們可使殘差的絕對值最小,平方值最小,甚至立方值最小,但爲何廣泛的作法是使殘差平方和最小呢?主要的理由有二:cdn

  • 平方值和殘差的正負沒有關係
  • 平方值使較大的殘差具備更多的權重,但又不至於使最大的殘差做用過大

故題目中求距離最短直線的方程即爲求預測值與實際值殘差平方和最小的方程。

仔細觀察你能夠發現2個函數都通過 x 與 y 的均值座標,這一點很重要,至於爲何後文會提。

slr_res.png

擬合優度評估

接下來咱們使用決定係數評估2個函數,這裏我使用 sckit-learn 自帶的函數計算,固然你也能夠本身實現,代碼:

from sklearn.metrics import r2_score
r2_score(y_true, y_pred)
複製代碼

最終結果我約了3位小數,能夠看到使用線性迴歸模型擬合的函數 R^2 分數爲 0.413,意味着其消除了約 41% 的偏差。

slr_r2.png

OLS 推導過程

根據上文咱們已經知道線性迴歸模型擬合的函數比 y = x + 1 更符合題意,且知道殘差計算公式:

res_i = y_i - (kx + b)

因此當線性模型的平方偏差和最小時,即 SE = \sum_{i=1}^n res_i ^ 2 取得最小值時,直線離其餘點的距離最小。

將公式展開分析:

SE_{line} = \sum_{i=1}^n (y_i - (kx_i + b))^2 \\
= \sum_{i=1}^n(y_i^2 - 2y_i(kx_i+b) + (kx_i+b)^2) \\
= \sum_{i=1}^n(y_i^2 - 2kx_iy_i - 2by_i + k^2x_i^2 + 2kx_ib + b^2) \\
= \sum_{i=1}^n y_i^2 - 2k\sum_{i=1}^nx_iy_i - 2b\sum_{i=1}^ny_i + k^2\sum_{i=1}^nx_i^2 + 2kb\sum_{i=1}^nx_i + \sum_{i=1}^nb^2

如今上式中共有 4 個自變量,但實際上能夠化簡爲 2 個自變量,咱們知道平均數的計算爲 \overline{x} = \frac{\sum_{i=1}^n x_i}{n} 因此 n\overline{x} = \sum_{i=1}^n x_i 。如今咱們繼續簡化公式:

SE_{line} = \sum_{i=1}^n y_i^2 - 2k\sum_{i=1}^nx_iy_i - 2b\sum_{i=1}^ny_i + k^2\sum_{i=1}^nx_i^2 + 2kb\sum_{i=1}^nx_i + \sum_{i=1}^nb^2 \\
= n\overline{y^2} - 2nk\overline{xy} - 2nb\overline{y} + nk^2\overline{x^2} + 2nkb\overline{x} + n(b)^2

能夠看到實際上線性模型的平方偏差和僅取決於斜率 k 及截距 b 的取值。因此其實是對二元函數 SE_{line} 求 k , b 的一階偏導,有:

\frac{\partial SE_{line}}{\partial k} = -2\sum_{i=1}^n x_iy_i + 2k\sum_{i=1}^nx_i^2 + 2b\sum_{i=1}^nx_i \\
= -2\sum_{i=1}^n (x_iy_i - kx_i^2 -bx_i)\\
\frac{\partial SE_{line}}{\partial b} = -2\sum_{i=1}^n y_i + 2k\sum_{i=1}^n x_i + 2\sum_{i=1}^n b \\
= -2\sum_{i=1}^n(y_i - kx_i - b)

\frac{\partial SE_{line}}{\partial k} = 0 , \frac{\partial SE_{line}}{\partial b} = 0, SE_{line} 取得最小值,即線性模型距此 k, b 取得最小平方偏差和,有:

\begin{cases}
-2\sum_{i=1}^n(x_iy_i - kx_i^2 - bx_i) = 0\\
-2\sum_{i=1}^n(y_i - kx_i -b) = 0
\end{cases}

進而:

-2\sum_{i=1}^n(y_i - kx_i -b) = 0 \\
n\overline{y} - nk\overline{x} - nb = 0 \\
\overline{y} - k\overline{x} - b = 0

可得 b= \overline{y} - k\overline{x}

顯然,最小二乘法獲得的函數一定通過點 (\overline{x}, \overline{y}),即若要使直線距全部數據點距離最小其一定通過點 (\overline{x}, \overline{y})

下面咱們將 \frac{\partial SE_{line}}{\partial k} 化簡後代入 b:

-2\sum_{i=1}^n(x_iy_i - kx_i^2 - bx_i) = 0\\
\sum_{i=1}^n x_iy_i - k\sum_{i=1}^nx_i^2 - b\sum_{i=1}^n x_i = 0 \\
\sum_{i=1}^n x_iy_i - k\sum_{i=1}^nx_i^2 - (\overline{y} - k\overline{x})\sum_{i=1}^nx_i = 0 \\
\sum_{i=1}^n x_iy_i - k\sum_{i=1}^nx_i^2 - \overline{y}\sum_{i=1}^nx_i + k\overline{x}\sum_{i=1}^nx_i = 0

可得

\sum_{i=1}^nx_iy_i - \overline{y}\sum_{i=1}^nx_i = k\sum_{i=1}^n x_i^2 - k\overline{x}\sum_{i=1}^nx_i \\
k = \frac{\sum_{i=1}^n x_iy_i - \overline{y}\sum_{i=1}^n x_i}{\sum_{i=1}^n x_i^2 - \overline{x}\sum_{i=1}^n x_i} \\
k = \frac{\sum_{i=1}^n (x_i - \overline{x})(y_i - \overline{y})}{\sum_{i=1}^n (x_I - \overline{x})^2}

利用總和運算子基本性質可證 \sum_{i=1}^n (x_i - \overline{x})(y_i - \overline{y}) = \sum_{i=1}^nx_iy_i - \overline{y}\sum_{i=1}^nx_i

\sum_{i=1}^n(x_i - \overline{x}) ^ 2 = \sum_{i=1}^nx_i^2 - \overline{x}\sum_{i=1}^nx_i

證實以下:

\sum_{i=1}^n(x_i - \overline{x})(y_i - \overline{y}) \\
= \sum_{i=1}^n(x_iy_i - x_i\overline{y} - \overline{x}y_i + \overline{x}\overline{y}) \\
= \sum_{i=1}^nx_iy_i - n\overline{x}\overline{y} - n\overline{x}\overline{y} + n\overline{x}\overline{y} \\
= \sum_{i=1}^nx_iy_i - n\overline{x}\overline{y} \\
= \sum_{i=1}^nx_iy_i - \overline{y}\sum_{i=1}^nx_i
\sum_{i=1}^n(x_i - \overline{x}) ^ 2 \\
= \sum_{i=1}^n(x_i^2 - 2\overline{x}x_i + \overline{x}^2)\\
= \sum_{i=1}^nx_i^2 - 2n(\overline{x})^2 + n(x)^2\\
= \sum_{i=1}^nx_i^2 - n(x)^2 \\
= \sum_{i=1}^nx_i^2 - \overline{x}\sum_{i=1}^nx_i

故最後可解得普通最小二乘法公式爲:

\begin{cases}
k = \frac{\sum_{i=1}^n (x_i - \overline{x})(y_i - \overline{y})}{\sum_{i=1}^n (x_I - \overline{x})^2} \\
b = \overline{y} - k\overline{x} \\
y = kx + b
\end{cases}

總結

推導出普通最小二乘法公式後咱們再畫出函數圖線看是否與模型給出的結果一致,可見線性模型函數圖線與使用普通最小二乘法得出的圖線一致。

從右側平方偏差和能夠看出 ols 實現的偏差比 y = x + 1 要小。

lr_tot.png

代碼地址點此

說說本身的心路歷程吧,這題的思路就是普通最小二乘法,但通常的文章都是告訴你能夠用最小二乘法實現線性迴歸。說實話看到給出的公式的時候我第一反應是不理解爲啥,因而用一天時間一點一點找資料本身推理出公式,發現其實用到的知識就是大學高數的內容。工做忙的時候老是趕着實現,老是知其然而不知其因此然,其實靜下心來了解問題背後的原理和本質反而感受收穫更多。

相關文章
相關標籤/搜索