1. 程式人生 > 其它 >方法介紹:迴歸(regression)

方法介紹:迴歸(regression)

迴歸可用於做實證研究,研究自變數和因變數之間的內在聯絡和規律,常見於社會科學研究中。迴歸也可用來做預測,根據已知的資訊去準確預測未知的事情。

1. 迴歸(regression)

1.1 起源與定義

迴歸最早是被高爾頓提出的。他通過研究發現:如果父母都比較高一些,那麼生出的子女身高會低於父母的平均身高;反之,如果父母雙親都比較矮一些,那麼生出的子女身高要高於父母平均身高。他認為,自然界有一種約束力,使得身高的分佈不會向高矮兩個極端發展,而是趨於回到中心,所以稱為迴歸。
目前,從用法角度將其定義為一種數值(scalar)預測的技術,區別於分類(類別預測技術)。

1.2 不同的用法

1.2.1 解釋(Explanation)

迴歸可用於做實證研究,研究自變數和因變數之間的內在聯絡和規律,常見於社會科學研究中。

  • 網際網路的普及降低了教育不平等程度嗎?
  • 大學生就業選擇的影響因素有哪些?
  • 醫療電子商務場景下客戶滿意度的影響因素有哪些?

1.2.2 預測(Prediction)

迴歸也可用來做預測,根據已知的資訊去準確預測未知的事情。

  • 股市預測:根據過去10年股票的變動、新聞諮詢、公司併購諮詢等,預測股市明天的平均值。
  • 商品推薦:根據使用者過去的購買記錄和候選的商品資訊,預測使用者購買某個商品的可能性。
  • 自動駕駛:根據汽車的各個sensor的資料,例如路況和車距等,預測正確的方向盤角度。

1.3 模型的構建

無論目的是解釋還是預測,都需要掌握與任務相關的規律(認識世界),即建立合理的模型。
不同的一點是,解釋模型只需要基於訓練集構建,一般具備解析解(計量經濟模型)。 預測模型必須在測試集上做檢驗和調整,一般不具備解析解,需要通過機器學習的方法去調整引數。因此,同樣的模型框架和資料集,最優的解釋模型和預測模型很可能是不相同的。
本文主要關注預測模型的構建,不進一步涉及解釋模型相關的內容。

2. 基於機器學習的模型構建

我們以Pokemon精靈攻擊力預測(Combat Power of a pokemon)這個任務為例,梳理機器學習三個步驟的詳細內容。

  • 輸入:進化前的CP值、物種(Bulbasaur)、血量(HP)、重量(Weight)、高度(Height)
  • 輸出:進化後的CP值

2.1 模型假設 - 線性模型

為了方便,我們選擇最簡單的線性模型來作為完成迴歸任務的模型框架。我們可以使用單特徵或者多特徵的線性迴歸模型,後者會更加複雜,模型集合會更大。

為選擇合理的模型框架,提前對資料集進行探索,觀察變數間的關係是很有必要的,這將決定最終將哪些變數放入模型,以及是否需要對變數進行再次處理(二次項、取倒數等)。

可以看出,橫軸和縱軸主要呈直線關係,也有一些二次關係(可考慮加二次項)。
模型框架(預先設定) + 引數(待估計) = 模型(目標)
目前模型的引數包括各個特徵的權重 \(w_i\) 以及偏移量 \(b\)

2.2 模型評價 - 損失函式

本文闡述的迴歸任務屬於有監督學習場景,因此需要收集足夠的輸入輸出對以指導模型的構建。

有了這些真實的資料,那我們怎麼衡量模型的好壞呢?從數學的角度來講,我們使用損失函式(Loss function) 來衡量模型的好壞。Loss function基於模型預測值和實際值的差異來設定。

在本文中,我們選擇常用的均方誤差作為損失函式。

2.3 模型調優 - 梯度下降

當模型非凸時,是沒有解析解的,只能通過啟發式的方式迭代優化,常用的方法是梯度下降。

首先,我們隨機選擇一個 \(w^0\),然後計算微分判定移動的方向,再更新對應引數,迴圈往復,直到找到最低點(兩次更新之間差異小於閾值或者達到預先設定好的迭代次數)。
對於有多個待更新引數的模型,步驟是基本一致的,只不過做的是偏微分。

在梯度下降的過程中,會遇到一些問題,導致無法達到最優點。

這些問題如何解決以後會涉及到。

3. 模型構建中的問題和解決

3.1 評價模型的泛用性(Generalization)

好模型不僅要在訓練集中表現優異,在未知的資料集(測試集,真實應用場景)中也應該一樣。
因此,我們必須要計算模型在測試機上的效能,理想情況下不能有較大的下滑。

3.2 提高模型的擬合度

若模型過於簡單,則模型集合較小,可能無法包含真實的模型,即出現欠擬合問題。
我們可以選擇更復雜的模型去優化效能。以使用1元2次方程舉例,顯著提高了預測效能。

我們還可以在模型中增加調節項(Pokemon種類)來改進模型。

模型在訓練集和測試集的效能表現如下所示:

3.3 防止過擬合(Overfiting)的出現

如果我們繼續使用更高次的模型,可能會出現過擬合問題。

我們可以通過加入正則項來防止過擬合問題的出現。

正則項權重變化對模型效能的影響如下所示:

4. 迴歸 - 程式碼演示

現在假設有10個x_data和y_data,x和y之間的關係是y_data=b+w*x_data。b,w都是引數,是需要學習出來的。現在我們來練習用梯度下降找到b和w。

import numpy as np
import matplotlib.pyplot as plt
from pylab import mpl

# matplotlib沒有中文字型,動態解決
plt.rcParams['font.sans-serif'] = ['Simhei']  # 顯示中文
mpl.rcParams['axes.unicode_minus'] = False  # 解決儲存影象是負號'-'顯示為方塊的問題

# 生成實驗資料
x_data = [338., 333., 328., 207., 226., 25., 179., 60., 208., 606.]
y_data = [640., 633., 619., 393., 428., 27., 193., 66., 226., 1591.]
x_d = np.asarray(x_data)
y_d = np.asarray(y_data)
x = np.arange(-200, -100, 1) # 引數的候選項,指偏移項b
y = np.arange(-5, 5, 0.1) # 引數的候選項,指權重w
Z = np.zeros((len(x), len(y)))
X, Y = np.meshgrid(x, y)

# 得出每種可能組合下的loss,共需要計算100*100=10000次
for i in range(len(x)):
    for j in range(len(y)):
        b = x[i]
        w = y[j]
        Z[j][i] = 0  # meshgrid吐出結果:y為行,x為列
        for n in range(len(x_data)):
            Z[j][i] += (y_data[n] - b - w * x_data[n]) ** 2
        Z[j][i] /= len(x_data)

以上程式碼生成了實驗資料,並用窮舉法計算出了所有可能組合的loss,其中最小值為10216。
接下來我們嘗試使用梯度下降法來快速尋找到較小的loss值。

# linear regression
b=-120
w=-4
lr = 0.000005
iteration = 10000 #先設定為10000

b_history = [b]
w_history = [w]
loss_history = []
import time
start = time.time()
for i in range(iteration):
    m = float(len(x_d))
    y_hat = w * x_d  +b
    loss = np.dot(y_d - y_hat, y_d - y_hat) / m
    grad_b = -2.0 * np.sum(y_d - y_hat) / m
    grad_w = -2.0 * np.dot(y_d - y_hat, x_d) / m
    # update param
    b -= lr * grad_b
    w -= lr * grad_w

    b_history.append(b)
    w_history.append(w)
    loss_history.append(loss)
    if i % 1000 == 0:
        print("Step %i, w: %0.4f, b: %.4f, Loss: %.4f" % (i, w, b, loss))
end = time.time()
print("大約需要時間:",end-start)
# Step 0, w: 1.6534, b: -119.9839, Loss: 3670819.0000
# Step 1000, w: 2.4733, b: -120.1721, Loss: 11492.1941
# Step 9000, w: 2.4776, b: -121.6771, Loss: 11435.5676

可以發現,梯度下降法可以快速從初始值迭代到合適的引數組合,接近最優引數。但我們發現,達到最優值的過程卻非常緩慢。使用下面的程式碼可以對尋優過程進行視覺化。

# plot the figure
plt.contourf(x, y, Z, 50, alpha=0.5, cmap=plt.get_cmap('jet'))  # 填充等高線
plt.plot([-188.4], [2.67], 'x', ms=12, mew=3, color="orange") # 最優引數
plt.plot(b_history, w_history, 'o-', ms=3, lw=1.5, color='black')
plt.xlim(-200, -100)
plt.ylim(-5, 5)
plt.xlabel(r'$b$')
plt.ylabel(r'$w$')
plt.title("線性迴歸")
plt.show()

如下圖所示,引數最終尋優的方向是正確的,但是因為迭代次數不夠所以提前停止。

我們將迭代次數更改為10萬次,結果如下所示:

迭代次數仍然不足,我們繼續將迭代次數更改為100萬次,結果接近最優,如下所示:

迭代次數太多會消耗過多的計算資源,我們可以通過調整學習率來加快速度。當我們將學習率設定為之前的兩倍(0.00001)時,迭代10萬次即可達到接近最優的結果,如下所示;

但需要注意的是,學習率如果設定得太高,可能會發生振盪,無法收斂。下圖是我們將學習率設定為0.00005時的情況。

總而言之,我們要清楚機器學習的強大能力以及不穩定性,然後學習相關原理進而熟練使用。

參考文獻

  1. Datawhale 開源學習資料 李巨集毅機器學習
  2. 到底什麼是實證研究?