機器學習回顧篇(4):邏輯回歸


注:本系列所有博客將持續更新並發布在github上,您可以通過github下載本系列所有文章筆記文件

1 引言

邏輯不邏輯,回歸非回歸。

回想當年初次學習邏輯回歸算法時,看到”邏輯回歸“這個名字,第一感覺是這是一個與線性回歸類似的回歸類別的算法,只不過這個算法突出”邏輯“,或者與某個以”邏輯“命名的知識點有關。可后來卻發現,這是一個坑死人不償命的名字——邏輯回歸算法不是回歸算法,是分類算法,也與邏輯無關,要說有關也僅是因為它的英文名字是Loginstics,音譯為邏輯而已(所以也有資料稱之為邏輯斯蒂回歸)。

2 邏輯回歸原理

2.1 從線性回歸到邏輯回歸

在上一篇博文中,我們詳細說過回歸算法與分類算法的區別。邏輯回歸既然是分類算法,為什么不叫邏輯分類而是邏輯回歸呢?在我看來,這是因為邏輯回歸用回歸的思路去解決分類的問題。

假設有如下圖所示的一個數據集,使用線性回歸算法,我們可以找到大致如黑線的一個線性模型對其進行擬合。對於回歸算法,需要做的是對數據集中每一個${{x}_{i}}$,都能通過模型找到一個${{y}_{i}}$(預測值)與之對應。

獲得了預測值${{y}_{i}}$,我們就可以做很多事情了,例如:分類。我們可以對${{y}_{i}}$進行分段,例如,在${{y}}$軸上取一值$M$,當${{y}_{i}}<M$時,我們將其標記到類0中,當${{y}_{i}}>M$時,我們將其標記到另一類1中,如下圖所示:

這就實現了以回歸的思路來實現分類。

但邏輯回歸可不止在線性回歸的基礎上做這些事情。在上一篇介紹線性回歸的博文的末尾,我們提到,線性回歸有一個很致命的缺陷——對異常值很敏感,如果數據集中出現異常值,擬合出來的線性模型也將出現很大變化,預測出來的結果也將不在那么准確,從而到導致分類錯誤。如下圖所示,數據集中出現一個異常點(綠點),那么擬合出來的模型就可能從原來的黑線變為綠線,此時,當數據集中有某一點$x\in ({{x}_{1}},{{x}_{2}})$時,該點就回被誤判,例如圖中橙色點,在原本黑線模型中,該點預測出來的${{y}}$值大於$M$,被標記到1類中,但在綠線模型中,其${{y}}$值就小於$M$,就回被誤標記到0類中。

邏輯回歸算法對線性回歸對異常數據敏感的不足進行了優化改進。怎么改進呢?最直觀的方法就是將直線“掰彎”。“掰彎”之后,就算出現異常數據,模型主體部分也不會出現太多改變,從而解決線性回歸模型對異常值敏感的問題,如下圖所示:

而我們所用的“掰彎”方法就是用sigmod函數與線性函數進行擬合。

2.2 sigmod函數

sigmoid函數也叫Logistic函數,函數表達式如下:

$g(z)=\frac{1}{1+{{e}^{-x}}}$

其中,$e$為自然對數,是一個常數,值約為$2.71828$

函數圖像如下:

從函數圖像可以看出, sigmoid函數可以很好地將$(-\infty ,+\infty )$內的數映射到$(0,1)$ 上,於是當$g(z)\ge 0.5$時我們可以將該條數據標記為1類, $g(z)<0.5$時標記為0類。即:

\[y=\left\{ _{0,\text{    }g(x)<0.5}^{1,\text{    }g(x)\ge 0.5} \right.\]

其中$y$表示分類結果。

通常,在邏輯回歸算法應用中,模型可不會如同上面的sigmoid函數那么簡單,而是sigmoid函數與線性函數的組合:

\[g(x)=\frac{1}{1+{{e}^{-z}}}\]

其中,$z$就是線性回歸中的預測值,即:

\[z=f(x)={{\theta }_{0}}+{{\theta }_{1}}{{x}_{1}}+{{\theta }_{2}}{{x}_{2}}+\cdots +{{\theta }_{n}}{{x}_{n}}\]

所以有:

\[h(x)=\frac{1}{1+{{e}^{-({{\theta }_{0}}+{{\theta }_{1}}{{x}_{1}}+{{\theta }_{2}}{{x}_{2}}+\cdots +{{\theta }_{n}}{{x}_{n}})}}}\]

用矩陣方式表示:

\[h(x)=g(z)=g({{\theta }^{T}}x)=\frac{1}{1+{{e}^{-{{\theta }^{T}}x}}}\]

其中,$\theta =\left[ \begin{matrix}
   {{\theta }_{0}}  \\
   {{\theta }_{1}}  \\
   \vdots   \\
   {{\theta }_{n}}  \\
\end{matrix} \right]$,$x=\left[ \begin{matrix}
   {{x}_{0}}  \\
   {{x}_{1}}  \\
   \vdots   \\
   {{x}_{n}}  \\
\end{matrix} \right]$

3 損失函數

下一步我們要做的就是如何求取最佳擬合模型的問題了。在線性回歸算法中,我們使用誤差平方和來作為損失函數,但是在邏輯回歸中,這個方法不再使用,因為已被證明,在邏輯回歸模型中使用誤差平方和作為損失函數的話,會存在許多局部最小值點,在求解參數的過程中很容易陷入局部最小值點,而無法求得真正的最小值點。

上面說過,$h(x)\in (0,1)$,這一點的性質剛好與概率$p\in [0,1]$的性質吻合(當做概率使用的理由不止這點),故而我們可以將其當做$h(x)$值當做數據被標記為1類的概率,即:

\[p(y=1|x;\theta )=h(x)\]

\[p(y=0|x;\theta )=1-h(x)\]

當給定$y$為1時,即屬於1類時,$h(x)$越趨近於1,被預測為1類的概率就越大,損失(誤差)就越小;反之,當給定$y$為0時,即屬於0類時,$h(x)$越趨近於1,被預測為0類的概率就越小,損失(誤差)就越大,於是,我們可以定義損失函數:

\[\cos t(h(x),y)=\left\{ _{-\log (1-h(x)),\text{ }y=0}^{-\log (h(x)),\text{ }y=1} \right.\]

對所有數據集中$x$損失累加然后求平均,有:

\[J(\theta )=-\frac{1}{m}\sum\limits_{i=1}^{m}{\cos (h(x),y)}\]

由於$y$的取值為0或1,結合上面兩個公式可以得到:

\[J(\theta )=-\frac{1}{m}\sum\limits_{i=1}^{m}{({{y}_{i}}\log (h({{x}_{i}}))+(1-{{y}_{i}})\log (1-h({{x}_{i}})))}\]

這個函數就是我們邏輯回歸的損失函數,我們把它稱為交叉熵損失函數。

接下來就是針對的優化問題,也就是求得最小值,在這位大佬的博客里推導過程寫得很詳細,我自愧不如,就不獻丑了。

4 代碼實現

import torch
from torch import nn
from torch.autograd import Variable
import matplotlib.pyplot as plt
import numpy as np
 
# 假數據
n_data = torch.ones(100, 2)         # 數據的基本形態
x0 = torch.normal(2*n_data, 1)      # 類型0 x data (tensor), shape=(100, 2)
y0 = torch.zeros(100)               # 類型0 y data (tensor), shape=(100, 1)
x1 = torch.normal(-2*n_data, 1)     # 類型1 x data (tensor), shape=(100, 1)
y1 = torch.ones(100)                # 類型1 y data (tensor), shape=(100, 1)
 
# 注意 x, y 數據的數據形式是一定要像下面一樣 (torch.cat 是在合並數據)
x = torch.cat((x0, x1), 0).type(torch.FloatTensor)  # FloatTensor = 32-bit floating
y = torch.cat((y0, y1), 0).type(torch.FloatTensor)    # LongTensor = 64-bit integer
 
# 畫圖
# plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=y.data.numpy(), s=100, lw=0, cmap='RdYlGn')
# plt.show()
 
class LogisticRegression(nn.Module):
    def __init__(self):
        super(LogisticRegression, self).__init__()
        self.lr = nn.Linear(2, 1)
        self.sm = nn.Sigmoid()
 
    def forward(self, x):
        x = self.lr(x)
        x = self.sm(x)
        return x
 
logistic_model = LogisticRegression()
if torch.cuda.is_available():
    logistic_model.cuda()
 
# 定義損失函數和優化器
criterion = nn.BCELoss()
optimizer = torch.optim.SGD(logistic_model.parameters(), lr=1e-3, momentum=0.9)
 
# 開始訓練
for epoch in range(10000):
    if torch.cuda.is_available():
        x_data = Variable(x).cuda()
        y_data = Variable(y).cuda()
    else:
        x_data = Variable(x)
        y_data = Variable(y)
 
    out = logistic_model(x_data)
    loss = criterion(out, y_data)
    print_loss = loss.data.item()
    mask = out.ge(0.5).float()  # 以0.5為閾值進行分類
    correct = (mask == y_data).sum()  # 計算正確預測的樣本個數
    acc = correct.item() / x_data.size(0)  # 計算精度
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    # 每隔20輪打印一下當前的誤差和精度
    if (epoch + 1) % 20 == 0:
        print('*'*10)
        print('epoch {}'.format(epoch+1)) # 訓練輪數
        print('loss is {:.4f}'.format(print_loss))  # 誤差
        print('acc is {:.4f}'.format(acc))  # 精度
 
# 結果可視化
w0, w1 = logistic_model.lr.weight[0]
w0 = float(w0.item())
w1 = float(w1.item())
b = float(logistic_model.lr.bias.item())
plot_x = np.arange(-7, 7, 0.1)
plot_y = (-w0 * plot_x - b) / w1
plt.scatter(x.data.numpy()[:, 0], x.data.numpy()[:, 1], c=y.data.numpy(), s=100, lw=0, cmap='RdYlGn')
plt.plot(plot_x, plot_y)
plt.show()

4 總結

總結一下邏輯回歸的優缺點:

優點:

1)預測結果是介於0和1之間的概率;

2)可以適用於連續性和離散型變量;

3)容易使用,解釋性強。

缺點:

1)對模型中自變量多重共線性較為敏感,例如兩個高度相關自變量同時放入模型,可能導致較弱的一個自變量回歸符號不符合預期,符號被扭轉。需要利用因子分析或者變量聚類分析等手段來選擇代表性的自變量,以減少候選變量之間的相關性;

2)預測結果呈“S”型,因此從log(odds)向概率轉化的過程是非線性的,在兩端隨着log(odds)值的變化,概率變化很小,邊際值太小,slope太小,而中間概率的變化很大,很敏感。 導致很多區間的變量變化對目標概率的影響沒有區分度,無法確定閥值。

參考:

https://blog.csdn.net/out_of_memory_error/article/details/81275651

https://www.cnblogs.com/yiduobaozhiblog1/p/8872903.html

https://blog.csdn.net/ligang_csdn/article/details/53838743

https://baijiahao.baidu.com/s?id=1620514366177013756&wfr=spider&for=pc


免責聲明!

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



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