一文徹底理解邏輯回歸:從公式推導到代碼實現


傳送門:點我

前言

大家好,這里是 Codeman 。這是本文的第二次修訂,在前文的基礎之上,這次我增加了很多公式的推導,從數學原理到代碼實現,本文提供了一站式服務,希望能幫助讀者從根上理解邏輯回歸,一勞永逸地解決問題!

多次修訂,只源於我精益求精的人生態度。寫博客,我是認真的!如果你覺得本文確實對你有幫助,請點個贊支持我一下吧 🌹

正文

邏輯回歸在社會和自然科學中應用非常廣泛,它其實是一種統計學習方法,因為它的底層方法就是線性回歸。它們不同的地方在於:線性回歸適用於解決回歸問題,而邏輯回歸適用於解決分類問題。這是為什么呢?別急,看完本文你就懂了。因此,我們可以說邏輯回歸是基於回歸的偽回歸算法!

image.png

在自然語言處理中,邏輯回歸是用於分類的基線監督機器學習算法,它與神經網絡也有非常密切的關系,神經網絡可以被視為一系列堆疊在一起的邏輯回歸分類器。

由於后文涉及到很多數學公式的推導,因此我們先做一個符號約定:

符號 含義
\(𝑚\) 訓練集中樣本的數量
\(𝑛\) 特征的數量
\(𝑥\) 特征/輸入變量
\(𝑦\) 目標變量/輸出變量
\((𝑥,𝑦)\) 訓練集中的樣本
\((𝑥^{(𝑖)},𝑦^{(𝑖)})\) \(𝑖\) 個觀察樣本
\(𝑥_j^{(i)}\) \(𝑖\) 個觀察樣本的第 \(j\) 個特征
\(ℎ\) 學習算法的解決方案或函數,也稱為假設(hypothesis)
\(\widehat{𝑦}=ℎ(𝑥)\) 預測值

我們先從線性回歸開始講起,一步一步地領略邏輯回歸的全貌。

1.線性回歸

線性回歸是一種使用特征屬性的線性組合來預測響應的方法。它的目標是找到一個線性函數,以盡可能准確地描述特征或自變量(\(x\))與響應值(\(y\))之間的關系,使得預測值與真實值之間的誤差最小化。

image.png

1.1 數學定義

在數學上,線性回歸要找的這個線性函數叫回歸方程,其定義如下:

\[h(x^{(i)})=\beta_0+\beta_1x^{(i)}\tag{1.1} \]

其中,\(h(x^{(i)})\)表示第 \(i\) 個樣本的預測響應值。\(b_0\)\(b_1\) 是回歸系數,分別代表回歸線的 \(y\) 軸截距和斜率。這種形式通常見於特征只有單個屬性的時候,也就是一元線性回歸,我們初高中所學的就是這種。

在機器學習中,通常每個樣本都有 \(n\) 個特征屬性,每個特征 \(x_i\) 都有一個對應的權值 \(w_i\),此時我們需要的就是多元線性回歸:

\[ℎ(𝑥)=𝑤_0x_0 +𝑤_1𝑥_1 +𝑤_2𝑥_2 +...+𝑤_𝑛𝑥_𝑛=w^TX\tag{1.2} \]

其中,\(x_0=1\) 沒有實義,只是為了方便寫成矩陣的形式,\(w_0\) 則等價於式 (1.1) 中的 \(\beta_0\),把 \(\beta_0\) 融入矩陣中,不僅為了看起來簡潔,也是為了方便計算。

損失函數采用平方和損失:

\[𝑙(𝑥^{(𝑖)})=\frac{1}{2}(ℎ(𝑥^{(𝑖)})−𝑦^{(𝑖)})^2\tag{1.3} \]

代價函數定義如下:

\[J(w)=\frac{1}{2}\sum_{𝑖=1}^𝑚(ℎ(𝑥^{(𝑖)})−𝑦^{(𝑖)})^2\tag{1.4} \]

損失函數(Loss Function)度量單樣本預測的誤差,代價函數(Cost Function)度量全部樣本的平均誤差。常用的代價函數有均方誤差、均方根誤差、平均絕對誤差等。

損失函數的系數 1/2 是為了便於計算,使對平方項求導后的常數系數為 1。

我們的目標是要找到一組 \(𝑤(𝑤_0,𝑤_1,𝑤_2,...,𝑤_𝑛)\),使得代價函數 \(J(w)\) 最小,即最小化 \(\frac{\partial {𝐽(𝑤)}}{\partial 𝑤}\)

下面我們將詳細描述用最小二乘法求 \(𝑤\) 的推導過程。

1.2 最小二乘法

為了方便敘述,我們將\(J(w)\)用矩陣形式表達,即:

\[J(𝑤)=\frac{1}{2}(𝑋𝑤−𝑌)^2=\frac{1}{2}(𝑋𝑤−𝑌)^𝑇(𝑋𝑤−𝑌)\tag{1.5} \]

其中,\(𝑋\)\(𝑚\)\(𝑛+1\) 列的矩陣(第一列全為 \(1\),即式 (1.2) 中的 \(x_0\)),\(𝑤\)\(𝑛+1\)\(1\) 列的矩陣(包含了 \(𝑤_0\) ),\(𝑌\)\(𝑚\)\(1\) 列的矩陣。

\[X=\left[\begin{array}{cccccc} 1 & x_{1}^{(1)} & x_{2}^{(1)} & x_{3}^{(1)} & \ldots & x_{n}^{(1)} \\ 1 & x_{1}^{(2)} & x_{2}^{(2)} & x_{3}^{(2)} & \ldots & x_{n}^{(2)} \\ \vdots & \vdots & \vdots & \vdots & \vdots & \vdots \\ 1 & x_{1}^{(m)} & x_{2}^{(m)} & x_{3}^{(m)} & \ldots & x_{n}^{(m)} \end{array}\right],\quad w=\left[\begin{array}{c}w_0\\w_2\\\vdots \\w_n \end{array}\right],\quad Y=\left[\begin{array}{c}y^{(1)} \\y^{(2)} \\\vdots \\y^{(m)}\end{array}\right] \]

對式 (1.5) 求導,可得:

\[\frac{\partial {𝐽(𝑤)}}{\partial 𝑤}=\frac{1}{2}\frac{\partial}{\partial 𝑤}(w^TX^TXw-Y^TXw-w^TX^TY+Y^TY)\tag{1.6} \]

\(Y^TXw=(w^TX^TY)^T\)\(\frac{𝑑𝑋^𝑇𝑋}{𝑑𝑋}=2𝑋\),所以

\[\begin{aligned} \frac{\partial {𝐽(𝑤)}}{\partial 𝑤} &=\frac{1}{2}\frac{\partial}{\partial 𝑤}(w^TX^TXw-2w^TX^TY+Y^TY)\\ &=\frac{1}{2}(2X^TXw-2X^TY+0)\\ &=X^TXw-X^TY \end{aligned}\tag{1.7} \]

\(\frac{\partial {𝐽(𝑤)}}{\partial 𝑤}=0\),則:

\[𝑤=(𝑋^𝑇𝑋)^{−1}𝑋^𝑇𝑌\tag{1.8} \]

由上式可知,最小二乘法需要計算\((𝑋^𝑇𝑋)^{−1}\),但是矩陣求逆的時間復雜度為 \(𝑂(𝑛3)\),因此當特征數量 \(𝑛\) 較大時,其運算代價非常大。所以這種方法只適用於特征數量較少的線性模型,不適用於其他模型。

現代機器學習中常用的參數更新方法是梯度下降法。

1.3 梯度下降法

根據梯度下降的每一步中用到的樣本數量,可以將梯度下降法分為以下三類:

類別 樣本數量 公式
批量梯度下降(Batch Gradient Descent,BGD) 所有 \(w_{j}:=w_{j}-\alpha\frac{1}{m}\sum_{i=1}^{m}\left(\left(h\left(x^{(i)}\right)-y^{(i)}\right)\cdot x_{j}^{(i)}\right)\)
隨機梯度下降(Stochastic Gradient Descent,SGD) 一個 \(w_{j}:=w_{j}-\alpha\left(h\left(x^{(i)}\right)-y^{(i)}\right)\cdot x_{j}^{(i)}\)
小批量梯度下降(Mini-Batch Gradient Descent,MBGD) 部分 \(w_{j}:=w_{j}-\alpha\frac{1}{b}\sum_{k=i}^{i+b-1}\left(h\left(x^{(k)}\right)-y^{(k)}\right) x_{j}^{(k)}\)

其中,\(\alpha\) 稱為學習率。根據公式,不難看出,BGD 和 SGD 其實是 MBGD 的 \(b\) 取值為 \(m\)\(1\) 時的特殊情況。我們以 SGD 為例,推導一遍參數的更新過程。

\[\begin{aligned} \frac{\partial}{\partial w_{j}} J(w) &=\frac{\partial}{\partial w_{j}} \frac{1}{2}\left(h\left(x^{(i)}\right)-y^{(i)}\right)^{2}\\ &=2 \cdot \frac{1}{2}\left(h\left(x^{(i)}\right)-y^{(i)}\right) \cdot \frac{\partial}{\partial w_{j}}\left(h\left(x^{(i)}\right)-y^{(i)}\right) \\ &=\left(h\left(x^{(i)}\right)-y^{(i)}\right) \cdot \frac{\partial}{\partial w_{j}}\left(\sum_{i=0}^{n}\left(w_{i} x_{i}^{(i)}-y^{(i)}\right)\right) \\ &=\left(h\left(x^{(i)}\right)-y^{(i)}\right) x_{j}^{(i)} \end{aligned}\tag{1.9} \]

\(w_j=w_j-\alpha\frac{\partial}{\partial w_{j}} J(w)\),所以

\[w_{j}:=w_{j}-\alpha\left(h\left(x^{(i)}\right)-y^{(i)}\right) \cdot x_{j}^{(i)}\tag{1.10} \]

1.4 回歸的評價指標

監督學習主要分回歸和分類兩種,二者的評價指標是截然不同的。我們先在此介紹回歸的評價指標。

首先是誤差要越小越好,主要是下面幾種:

指標 公式
均方誤差(Mean Square Error,MSE) \(MSE=\frac{1}{m} \sum_{i=1}^{m}\left(y^{(i)}-\widehat{y}^{(i)}\right)^{2}\)
均方根誤差(Root Mean Square Error,RMSE) \(RMSE(y, \widehat{y})=\sqrt{\frac{1}{m} \sum_{i=1}^{m}\left(y^{(i)}-\widehat{y}^{(i)}\right)^{2}}\)
平均絕對誤差(Mean Absolute Error,MAE) \(MAE(y, \widehat{y})=\frac{1}{m} \sum_{i=1}^{n} |y^{(i)}-\widehat{y}^{(i)} |\)

其次是 \(R^2\) 要越大越好,它越接近於 1,說明模型擬合得越好。計算公式如下:

\[R^{2}(y, \widehat{y})=1-\frac{\sum_{i=0}^{m}\left(y^{(i)}-\widehat{y}^{(i)}\right)^{2}}{\sum_{i=0}^{m}\left(y^{(i)}-\bar{y}\right)^{2}}=1-\frac{\mathrm{SSE}}{\mathrm{SST}}=\frac{\mathrm{SSR}}{\mathrm{SST}}\tag{1.11} \]

其中,\(SSR=\sum_{i=0}^{m}\left(\widehat{y}^{(i)}-\bar{y}\right)^{2}\),\(SSE=\sum_{i=0}^{m}\left(y^{(i)}-\widehat{y}^{(i)}\right)^{2}\),\(SST=\sum_{i=0}^{m}\left(y^{(i)}-\bar{y}\right)^{2}\)

SST 是 Sum of Squares Total 的縮寫,含義是總平方和。它是變量 \(y\) 與其平均值 \(\bar{y}\) 之差的平方和。我們可以將其視為對於變量 \(y\) 在其平均值 \(\bar{y}\) 周圍的分散程度的一種度量,很像描述性統計中的方差。

SSR 是 Sum of Squares Regression 的縮寫,含義是回歸的平方和。它是預測值 \(\widehat{y}\) 與平均值 \(\bar{y}\) 之差的平方和。我們可以將其視為描述回歸線與數據的擬合程度的一種度量。

SSE 是 Sum of Squares Error 的縮寫,含義是誤差的平方和。它是預測值 \(\widehat{y}\) 與觀測值 \(y\) 之差的平方和。

image.png

從圖中不難看出,三者的關系是:SST = SSR + SSE。如果 SSR 的值等於 SST,這意味着我們的回歸模型是完美的。

2.邏輯回歸

我們前面說過,邏輯回歸和線性回歸不同的地方在於:線性回歸適用於解決回歸問題,而邏輯回歸適用於解決分類問題。本節我們就講講造成這種差異的原因。

2.1 Sigmoid函數

我們知道邏輯回歸的目標是訓練一個分類器,該分類器可以對輸入數據的類別做出決策。以二元邏輯回歸為例,對於一個輸入 \(x(x_1,x_2,...,x_n)\),我們希望分類器能夠輸出 \(y\) 是 1(是某類的成員)或 0(不是某類的成員)。也就是說,我們想知道這個輸入 \(x\) 是該類成員的概率 \(P(y = 1|x)\)

我們知道邏輯回歸的底層就是線性回歸,但是線性回歸解決的是回歸問題,那我們如何將一個回歸問題轉化為一個分類問題呢?為了方便討論,我們再對式 (1.2) 做一點形式變換,即令

\[z=ℎ(𝑥)=𝑤_0 +𝑤_1𝑥_1 +𝑤_2𝑥_2 +...+𝑤_𝑛𝑥_𝑛=w^TX\tag{2.1} \]

有些地方寫的是 \(z=𝑤^𝑇𝑥+𝑏\),其實是一樣的,因為 \(b\) 可以融入到 \(w_0\) 中。

我們觀察式 (2.1),不難發現,\(z\) 的輸出范圍沒有任何限制,即 \((-∞, +∞)\)。而作為一個分類器,我們需要輸出的是位於 0 和 1 之間的合法概率值。而為了完成這一步轉變,我們就需要 Sigmoid函數 \(σ(z)\)。Sigmoid 函數(命名是因為它的圖像呈𝑆形)也稱為邏輯函數,邏輯回歸的名稱也由此而來。

image.png

Sigmoid 有許多優點,它能將任意實數映射到 \([0,1]\) 范圍內,而且任意階可導。它在 0 附近幾乎是線性的,但在兩端趨於平緩,它能將異常值壓向 0 或 1。

\[\sigma(z)=\frac{1}{1+e^{-z}}=\frac{1}{1+\exp (-z)}\tag{2.2} \]

我們將式 (2.1) 的值代入上式,就能得到一個介於 0 和 1 之間的概率。對於一個二元邏輯回歸,我們只需要確保 \(P(y = 1)+P(y = 0)=1\) 即可。我們可以這樣做:

\[\begin{aligned} P(y=1) &=\sigma(z)=\frac{1}{1+\exp (-z)} \\ P(y=0) &=1-\sigma(z)=1-\frac{1}{1+\exp (-z)}=\frac{\exp (-z)}{1+\exp (-z)} \end{aligned}\tag{2.3} \]

根據 Sigmoid 函數的性質:

\[1−σ(x)=σ(−x)\tag{2.4} \]

我們也可以將 \(P(y = 0)\) 表示為 \(σ(−z)\)

好了,現在我們已經有了概率值,那么概率值大於多少我們認為它是屬於 1 類呢?通常,如果概率 \(P(y = 1|x)\) 大於 \(0.5\),我們認為是,否則就不是。這個 0.5 被稱為決策邊界

\[\operatorname{decision}(x)= \begin{cases}1 & \text { if } P(y=1 \mid x)>0.5 \\ 0 & \text { otherwise }\end{cases}\tag{2.5} \]

總結:邏輯回歸的總體思路就是,先用邏輯函數把線性回歸的結果 (-∞,∞) 映射到 (0,1),再通過決策邊界建立與分類的概率聯系。

邏輯函數還有一個很好的特性就是,其導函數可以轉化成本身的一個表達式,推導過程如下:

\[\begin{aligned} \sigma^{\prime}(z)&=\left(\frac{1}{1+e^{-z}}\right)^{\prime} \\ &=\frac{e^{-z}}{\left(1+e^{-z}\right)^{2}} \\ &=\frac{1+e^{-z}-1}{\left(1+e^{-z}\right)^{2}} \\ &=\frac{1}{\left(1+e^{-z}\right)}\left(1-\frac{1}{\left(1+e^{-z}\right)}\right) \\ &=\sigma(z)(1-\sigma(z)) \end{aligned}\tag{2.6} \]

在二分類模型中,事件發生與不發生的概率之比 \(\frac{𝑝}{1−𝑝}\) 稱為事件的幾率(odds)。令\(\sigma(z)=p\),解得:

\[z=log(\frac{p}{1-p})\tag{2.7} \]

也就是說,線性回歸的結果(即 \(z\) )等於對數幾率

2.2 代價函數

下面我們講講模型的參數如何更新。我們使用極大似然估計法來求解。極大似然估計法利用已知樣本結果,反推最有可能導致這樣結果的原因。我們的目標就是找到一組參數,使得在這組參數下,我們的樣本的似然度(概率)最大。

對於一個二分類模型,已知 \(P(y=1)=\sigma(z),P(y=0)=1-\sigma(z)\), 則似然函數為:

\[L(w)=\prod_{i=1}^{m} P\left(y^{(i)} \mid x^{(i)} ; w\right)=\prod_{i=1}^{m}\left(\sigma\left(x^{(i)}\right)\right)^{y^{(i)}}\left(1-\sigma\left(x^{(i)}\right)\right)^{1-y^{(i)}}\tag{2.8} \]

等式兩邊同時取對數:

\[l(w)=\log L(w)=\sum_{i=1}^{m}\left(y^{(i)} \log \left(\sigma\left(x^{(i)}\right)\right)+\left(1-y^{(i)}\right) \log \left(1-\sigma\left(x^{(i)}\right)\right)\right)\tag{2.9} \]

則代價函數為:

\[J(w)=-\frac{1}{m} l(w)\tag{2.10} \]

其實,式(2.9)前面再加一個負號,就是我們常用的損失函數——交叉熵損失(cross-entropy loss)

代價函數之所以要加負號,是因為機器學習的目標是最小化損失函數,而極大似然估計法的目標是最大化似然函數。那么加個負號,正好使二者等價。

2.3 梯度下降法求解

求解邏輯回歸的方法有非常多,我們這里主要了解一下梯度下降法。

將式 (2.2) 代入式 (2.10),得:

\[\begin{aligned} \because & y^{(i)} \log \left(\sigma\left(x^{(i)}\right)\right)+\left(1-y^{(i)}\right) \log \left(1-\sigma\left(x^{(i)}\right)\right)\\ &=y^{(i)} \log \left(\frac{1}{1+e^{-w^{T} x^{(i)}}}\right)+\left(1-y^{(i)}\right) \log \left(1-\frac{1}{1+e^{-w^{T} x^{(i)}}}\right)\\ &=-y^{(i)} \log \left(1+e^{-w^{T} x^{(i)}}\right)-\left(1-y^{(i)}\right) \log \left(1+e^{w^{T} x^{(i)}}\right)\\\therefore J(w)&=-\frac{1}{m}\sum_{i=1}^{m}\left(-y^{(i)} \log \left(1+e^{-w^{T} x^{(i)}}\right)-\left(1-y^{(i)}\right) \log \left(1+e^{w^{T} x^{(i)}}\right)\right) \end{aligned}\tag{2.11} \]

\(J(w)\) 求偏導,得:

\[\begin{aligned} \frac{\partial}{\partial w_{j}} J(w)&=\frac{\partial}{\partial w_{j}}\left(-\frac{1}{m} \sum_{i=1}^{m}\left(-y^{(i)} \log \left(1+e^{-w^{T} x^{(i)}}\right)-\left(1-y^{(i)}\right) \log \left(1+e^{w^{T} x^{(i)}}\right)\right)\right) \\ &=-\frac{1}{m} \sum_{i=1}^{m}\left(-y^{(i)} \frac{-x_{j}^{(i)} e^{-w^{T} x^{(i)}}}{1+e^{-w^{T} x^{(i)}}}-\left(1-y^{(i)}\right) \frac{x_{j}^{(i)} e^{w^{T} x^{(i)}}}{1+e^{w^{T} x^{(i)}}}\right) \\ &=-\frac{1}{m} \sum_{i=1}^{m}\left(y^{(i)}-\sigma\left(x^{(i)}\right)\right) x_{j}^{(i)} \\ &=\frac{1}{m} \sum_{i=1}^{m}\left(\sigma\left(x^{(i)}\right)-y^{(i)}\right) x_{j}^{(i)} \end{aligned}\tag{2.12} \]

所以:

\[w_{j}:=w_{j}-\frac{\partial}{\partial w_{j}} J(w)=w_{j}-\alpha \frac{1}{m} \sum_{i=1}^{m}\left(\sigma\left(x^{(i)}\right)-y^{(i)}\right) x_{j}^{(i)}\tag{2.13} \]

2.4 邏輯回歸的分類

邏輯回歸對特征變量(x)和分類響應變量(y)之間的關系進行建模,在給定一組預測變量的情況下,它能給出落入特定類別響應水平的概率。也就是說,你給它一組數據(特征),它告訴你這組數據屬於某一類別的概率。根據分類響應變量(y)的性質,我們可以將邏輯回歸分為三類:

  • 二元邏輯回歸(Binary Logistic Regression)
    當分類結果只有兩種可能的時候,我們就稱為二元邏輯回歸。例如,考試通過或未通過,回答是或否,血壓高或低。
  • 名義邏輯回歸(Nominal Logistic Regression)
    當存在三個或更多類別且類別之間沒有自然排序時,我們就稱為名義邏輯回歸。例如,企業的部門有策划、銷售、人力資源等,顏色有黑色、紅色、藍色、橙色等。
  • 序數邏輯回歸(Ordinal Logistic Regression)
    當存在三個或更多類別且類別之間有自然排序時,我們就稱為序數邏輯回歸。例如,評價有好、中、差,身材有偏胖、中等、偏瘦。注意,類別的排名不一定意味着它們之間的間隔相等。

2.5 Softmax Regression

我們以上討論都是以二元分類為前提展開的,但有時可能有兩個以上的類,例如情緒分類有正面、負面或中性三種,手寫數字識別有 0-9 總共十種分類。

在這種情況下,我們需要多元邏輯回歸,也稱為 Softmax Regression。在多元邏輯回歸中,我們假設總共有 \(K\) 個類,每個輸入 \(x\) 只能屬於一個類。也就是說,每個輸入 \(x\) 的輸出 \(y\) 將是一個長度為 \(K\) 的向量。如果類 \(c\) 是正確的類,則設置\(\mathbf{y}_c=1\),其他元素設置為 0,即\(\mathbf{y}_{c}=1,\mathbf{y}_{j}=0\quad\forall j \neq c\)。這種編碼方式稱為 one-hot 編碼。此時,分類器的工作是產生一個估計向量。對於每個類 \(k\),產生一個概率估計 \(P(y_k = 1|x)\)

多元邏輯分類器采用 sigmoid 的泛化函數 softmax 來計算 \(P(y_k = 1|x)\)。它能將 \(K\) 個任意值的向量 \(z = [z_1,z_2,\dots,z_K]\) 映射到 (0,1) 范圍內的概率分布上,並且所有值的總和為 1 。其定義如下:

\[\operatorname{softmax}\left(\mathbf{z}_{i}\right)=\frac{\exp \left(\mathbf{z}_{i}\right)}{\sum_{j=1}^{K} \exp \left(\mathbf{z}_{j}\right)} 1 \leq i \leq K\tag{2.14} \]

因此,輸入向量 \(z = [z_1,z_2,\dots,z_K]\) 的 softmax輸出如下:

\[\operatorname{softmax}(\mathbf{z})=\left[\frac{\exp \left(\mathbf{z}_{1}\right)}{\sum_{i=1}^{K} \exp \left(\mathbf{z}_{i}\right)}, \frac{\exp \left(\mathbf{z}_{2}\right)}{\sum_{i=1}^{K} \exp \left(\mathbf{z}_{i}\right)}, \ldots, \frac{\exp \left(\mathbf{z}_{K}\right)}{\sum_{i=1}^{K} \exp \left(\mathbf{z}_{i}\right)}\right]\tag{2.15} \]

舉個例子,若 \(\mathbf{z}=[0.6,1.1,-1.5,1.2,3.2,-1.1]\),則

\[\operatorname{softmax}(\mathbf{z})=[0.055,0.090,0.006,0.099,0.74,0.010] \]

與 sigmoid 一樣,softmax 也具有將異常值壓向 0 或 1 的特性。因此,如果其中一個輸入遠大於其他輸入,它將傾向於將其概率推向 1,並抑制較小輸入的概率。

由於現在有 \(K\) 個類別,而且每一個類別都有一個單獨的權重向量 \(w_k\),則每個輸出類別的概率估計 \(\widehat{y_k}\) 應該這樣計算:

\[P\left(\mathbf{y}_{k}=1 \mid \mathbf{x}\right)=\frac{\exp \left(\mathbf{w}_{k}^T \cdot \mathbf{x}\right)}{\sum_{j=1}^{K} \exp \left(\mathbf{w}_{j}^T \cdot \mathbf{x}\right)}\tag{2.16} \]

上式看起來我們將分別每個類別計算輸出。但是,我們可以將 \(K\) 個權重向量表示為權重矩陣 \(W\)\(W\) 的第 \(k\) 列對應於權重向量 \(w_k\)。因此,\(W\) 的形狀為 \([(n+1) \times K]\),其中,\(K\) 是輸出類的數量,\(n\) 是輸入特征的數量,加 \(1\) 對應的就是偏置。這樣一來,我們還是可以通過一個優雅的公式計算 \(\widehat{y}\)

\[\widehat{y}=\operatorname{softmax}{(W^TX)}\tag{2.17} \]

3. 代碼實現(Pytorch)

class LogisticRegression(torch.nn.Module):

    def __init__(self, num_features):
        super(LogisticRegression, self).__init__() #調用父類的構造函數
        self.linear = torch.nn.Linear(num_features, 1)
        
        self.linear.weight.detach().zero_() #權值初始化為0
        self.linear.bias.detach().zero_() #偏置初始化為0
        
    def forward(self, x):
        logits = self.linear(x)
        probas = torch.sigmoid(logits)
        return probas

在 Pytorch 中可以通過繼承torch.nn.Module類來實現自定義模型,在__init__中定義每一層的構造,在forward中定義每一層的連接關系,是實現模型功能的核心,且必須重寫,否則模型將由於無法找到各層的連接關系而無法執行。

torch.nn.Linear對傳入的數據做線性變換,weightbias 是它的兩個變量,分別代表學習的權值和偏置。

4. 鳶尾花分類

我們以鳶尾花數據的分類為例,做一個簡單地用 Logistic 回歸進行分類的任務。

  • 數據集獲取與划分
import matplotlib.pyplot as plt
import numpy as np
from io import BytesIO

import torch
import torch.nn.functional as F

ds = np.lib.DataSource()
fp = ds.open('http://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data')

x = np.genfromtxt(BytesIO(fp.read().encode()), delimiter=',', usecols=range(2), max_rows=100)
y = np.zeros(100)
y[50:] = 1

np.random.seed(1)
idx = np.arange(y.shape[0])
np.random.shuffle(idx)
X_test, y_test = x[idx[:25]], y[idx[:25]]
X_train, y_train = x[idx[25:]], y[idx[25:]]
mu, std = np.mean(X_train, axis=0), np.std(X_train, axis=0)
X_train, X_test = (X_train - mu) / std, (X_test - mu) / std

fig, ax = plt.subplots(1, 2, figsize=(7, 2.5))
ax[0].scatter(X_train[y_train == 1, 0], X_train[y_train == 1, 1])
ax[0].scatter(X_train[y_train == 0, 0], X_train[y_train == 0, 1])
ax[1].scatter(X_test[y_test == 1, 0], X_test[y_test == 1, 1])
ax[1].scatter(X_test[y_test == 0, 0], X_test[y_test == 0, 1])
plt.show()

實際的數據共有152行,我們只取前100行。按照下標隨機打亂之后來划分訓練集和測試集。其中,訓練集有75行,測試集有25行。

image.png

  • 模型訓練
model = LogisticRegression(num_features=2).to(device)
cost_fn = torch.nn.BCELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

def custom_where(cond, x_1, x_2):
    return (cond * x_1) + ((1-cond) * x_2)
    
def comp_accuracy(label_var, pred_probas):
    pred_labels = custom_where((pred_probas > 0.5).float(), 1, 0).view(-1)
    acc = torch.sum(pred_labels == label_var.view(-1)).float() / label_var.size(0)
    return acc


num_epochs = 10

X_train_tensor = torch.tensor(X_train, dtype=torch.float32, device=device)
y_train_tensor = torch.tensor(y_train, dtype=torch.float32, device=device).view(-1, 1)


for epoch in range(num_epochs):
    
    #### Compute outputs ####
    out = model(X_train_tensor)
    
    #### Compute gradients ####
    cost = cost_fn(out, y_train_tensor)
    optimizer.zero_grad()
    cost.backward()
    
    #### Update weights ####  
    optimizer.step()
    
    #### Logging ####      
    pred_probas = model(X_train_tensor)
    acc = comp_accuracy(y_train_tensor, pred_probas)
    print('Epoch: %03d' % (epoch + 1), end="")
    print(' | Train ACC: %.3f' % acc, end="")
    print(' | Cost: %.3f' % cost_fn(pred_probas, y_train_tensor))


    
print('\nModel parameters:')
print('  Weights: %s' % model.linear.weight)
print('  Bias: %s' % model.linear.bias)

BCELoss計算目標值和預測值之間的二進制交叉熵損失函數,數學公式如下:

\[Loss = -w[ylog(y')+(1-y)log(1-y')] \]

其中,\(w\)\(y\)\(y'\) 分別表示權值、標簽(target)、預測概率值(input probabilities)。reduction取值為sum表明對樣本的損失值進行求和。

輸出如下:

Epoch: 001 | Train ACC: 0.987 | Cost: 5.581
Epoch: 002 | Train ACC: 0.987 | Cost: 4.882
Epoch: 003 | Train ACC: 1.000 | Cost: 4.381
Epoch: 004 | Train ACC: 1.000 | Cost: 3.998
Epoch: 005 | Train ACC: 1.000 | Cost: 3.693
Epoch: 006 | Train ACC: 1.000 | Cost: 3.443
Epoch: 007 | Train ACC: 1.000 | Cost: 3.232
Epoch: 008 | Train ACC: 1.000 | Cost: 3.052
Epoch: 009 | Train ACC: 1.000 | Cost: 2.896
Epoch: 010 | Train ACC: 1.000 | Cost: 2.758

Model parameters:
  Weights: Parameter containing:
tensor([[ 4.2267, -2.9613]], requires_grad=True)
  Bias: Parameter containing:
tensor([0.0994], requires_grad=True)
  • 模型評估
X_test_tensor = torch.tensor(X_test, dtype=torch.float32, device=device)
y_test_tensor = torch.tensor(y_test, dtype=torch.float32, device=device)

pred_probas = model(X_test_tensor)
test_acc = comp_accuracy(y_test_tensor, pred_probas)

print('Test set accuracy: %.2f%%' % (test_acc*100))

輸出如下:

Test set accuracy: 100.00%

image.png

結語

一個非常基礎的入門級的機器學習算法,但是要完全講透還是非常困難,尤其是公式的推導部分,光用文字描述很難講清楚,但是總體上公式的推導都沒問題,只是說第一眼看不是那么容易懂,多看幾遍或者自己在紙上寫一寫還是不難理解的。另外,也請讀者發現錯誤后能在評論區指出,我看到后會及時更正。

全文寫下來洋洋灑灑六千字,也花費了好幾天的時間,從自己理解到寫出來讓別人看懂,這之間差別真的很大,可能也是我不善於表達,詞不達意,也請大家見諒。

篇幅這么長,公式這么多,看完本文你可能什么也記不住,但是這兩點你一定要記住:

1. Logistic回歸不適用於解決回歸問題,它適用於解決分類問題。千萬不要被它的名稱迷惑!

2. Logistic回歸 = 線性回歸 + Sigmoid 函數。當然了,如果是多元回歸的話就是 softmax 函數。

例行公事😂:如果你覺得本文確實對你有幫助,請點個贊支持我一下吧 🌹


免責聲明!

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



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