方法介紹:回歸(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. 到底什么是實證研究?


免責聲明!

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



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