免費Python機器學習課程七:如何應對算法效果不佳
我們花了很多時間來開發(fā)機器學習算法。但是在部署后,如果該算法性能不佳,那將令人沮喪。問題是,如果算法無法按預期工作,下一步應該怎么做。什么地方出了錯?訓練數(shù)據(jù)的數(shù)量是否足夠?我們使用了正確的功能嗎?我們是否應該繼續(xù)收集更多數(shù)據(jù)?我們可以,但是那是非常耗時且昂貴的。我們應該添加更多功能嗎?那也可能很昂貴。
往哪個方向走?
如果您的機器學習算法無法正常工作,下一步該怎么做?有幾種選擇:
- 獲取更多的訓練數(shù)據(jù)非常耗時。甚至可能需要數(shù)月的時間才能獲得更多的研究數(shù)據(jù)。
- 獲得更多的訓練特征。也可能需要很多時間。但是,如果添加一些多項式特征可以工作,那就太酷了。
- 選擇較小的一組訓練特征。
- 增加正則項
- 減少正則項。
那么,接下來您應該嘗試哪一個呢?開始嘗試任何操作都不是一個好主意。因為您可能最終會花太多時間在無用的事情上。您需要先發(fā)現(xiàn)問題,然后采取相應措施。學習曲線有助于輕松檢測問題,從而節(jié)省大量時間。
學習曲線對于確定如何提高算法性能非常有用。確定算法是否遭受偏差或擬合不足,方差或擬合過度,或兩者兼而有之,這很有用。
學習曲線的工作原理
學習曲線是成本函數(shù)的圖。在同一圖中,訓練數(shù)據(jù)的成本函數(shù)和交叉驗證數(shù)據(jù)的成本函數(shù)為算法提供了重要的見解。提醒一下,這是成本函數(shù)的公式:


換句話說,它是預測輸出減去原始輸出的平方除以訓練數(shù)據(jù)數(shù)量的兩倍。要繪制學習曲線,我們需要將這些成本函數(shù)繪制為訓練數(shù)據(jù)數(shù)量(m)的函數(shù)。代替使用所有訓練數(shù)據(jù),我們將僅使用訓練數(shù)據(jù)的較小子集來訓練數(shù)據(jù)。
看看下面的圖片:

如果我們使用太少的數(shù)據(jù)來訓練數(shù)據(jù),則該算法將完全適合訓練數(shù)據(jù),并且成本函數(shù)將返回0。
在上面的圖片中清楚地表明,當我們僅使用一個,兩個或三個數(shù)據(jù)算法來訓練數(shù)據(jù)時,就可以很好地了解到很少的數(shù)據(jù),并且訓練成本為零或接近于零。但是,這種類型的算法無法在其他數(shù)據(jù)上很好地執(zhí)行。
當您嘗試使交叉驗證數(shù)據(jù)適合此算法時,在交叉驗證數(shù)據(jù)上執(zhí)行效果很差的可能性很高。因此,交叉驗證數(shù)據(jù)的成本函數(shù)將返回非常高的值。
另一方面,當我們將需要越來越多的數(shù)據(jù)來訓練算法時,它將不再完全適合訓練數(shù)據(jù)。因此,培訓成本將變得更高。
同時,由于該算法針對大量數(shù)據(jù)進行訓練,因此在交叉驗證數(shù)據(jù)上的性能會更好,并且交叉驗證數(shù)據(jù)的成本函數(shù)將返回較低的值。這是如何建立學習曲線的方法。
開發(fā)學習算法
我將演示如何逐步繪制學習曲線。為了繪制學習曲線,我們首先需要機器學習算法。為簡單起見,我將使用線性回歸算法。首先,我們開發(fā)一個線性回歸算法。
首先,導入包和數(shù)據(jù)集。我在這里使用的數(shù)據(jù)集取材于安德魯·伍(Andrew Ng)的Coursera機器學習課程。在此數(shù)據(jù)集中,X值和y值在Excel文件中的單獨工作表中進行組織。
提醒一下,X是我們將用來開發(fā)和訓練機器學習算法的功能。y是我們需要預測的輸出特征。
交叉驗證數(shù)據(jù)的X和y值也被組織在同一Excel文件中的其他兩個工作表中。我在本文結(jié)尾處提供了到數(shù)據(jù)集的鏈接。請隨時下載數(shù)據(jù)集并進行練習。
- %matplotlib inline
- import pandas as pd
- import numpy as np
- import matplotlib.pyplot as plt
- file = pd.ExcelFile('dataset.xlsx')
- df = pd.read_excel(file, 'Xval', header=None)
- df.head()

以相同的方式,導入訓練集的y值:
- y = pd.read_excel(file, 'yval', header=None)
- y.head()

讓我們快速開發(fā)線性回歸算法。
(1) 定義假設
線性回歸使用非?;镜木€性方程式進行我們在學校學習的預測。公式如下:
Y = C + BX
對于機器學習,我們使用不同的術(shù)語。

在這里," h"是假設或預測值,theta0和theta1是系數(shù),X是輸入特征。
在這里,我們已經(jīng)有了X。我們必須計算" h",并且期望它與y的值匹配。因為我們的目標是能夠預測y的值。
Theta0和theta1在開始時是隨機初始化的。我們將通過迭代不斷完善theta0和theta1的值。
在每次迭代中,我們將使用成本函數(shù)和梯度公式來計算成本以更新theta值
(2) 成本函數(shù)和梯度下降
成本函數(shù)為我們提供了有關(guān)我們的預測值與原始輸出特征有何不同的想法。在這里,我們的輸出特征為y,預測輸出為" h"。因此,成本函數(shù)將告訴我們" h"與" y"的偏離量。我們希望成本函數(shù)值盡可能低。
這是成本函數(shù)的公式:

在成本函數(shù)最小之前,我們將不斷淘汰算法。在每次迭代中,我們使用梯度下降來更新theta值。
要更新theta值,我們將從先前的theta值中減去梯度下降。當我們對其進行編碼時,它將更加清晰。

此處,m是訓練數(shù)據(jù)的數(shù)量,而alpha是學習率。
(3) 開發(fā)線性回歸算法
使用上述公式開發(fā)假設和成本函數(shù)。
- m = len(df)
- def hypothesis(theta, X):
- return theta[0] + theta[1]*X
- def cost_calc(theta, X, y):
- return (1/2*m) * np.sum((hypothesis(theta, X) - y)**2)
現(xiàn)在,我們將定義梯度下降以優(yōu)化參數(shù)theta0和theta1。在每次迭代中,我們將更新theta值并跟蹤成本函數(shù)和theta值。
最后,它將返回每個迭代theta值中的成本列表。代碼很簡單。請在這里檢查。
- def gradient_descent(theta, X, y, epoch, alpha):
- cost = []
- theta_hist = []
- i = 0
- while i < epoch:
- hx = hypothesis(theta, X)
- theta[0] -= alpha*(sum(hx-y)/m)
- theta[1] -= (alpha * np.sum((hx - y) * X))/m
- cost.append(cost_calc(theta, X, y))
- i += 1
- return theta, cost
完成了線性回歸算法。我們需要一種預測輸出的方法。在預測方法中,我們將使用來自梯度下降函數(shù)和假設函數(shù)的最終theta進行預測。
- def predict(theta, X, y, epoch, alpha):
- theta, cost = gradient_descent(theta, X, y, epoch, alpha)
- return hypothesis(theta, X), cost, theta
現(xiàn)在,將參數(shù)初始化為零,并使用預測函數(shù)預測輸出變量。
- theta = [0,0]
- y_predict, cost, theta = predict(theta, df[0], y[0], 1400, 0.001)
現(xiàn)在,在同一圖中繪制df或X的預測輸出(h)和原始輸出(y)。
- plt.figure()
- plt.scatter(df, y)
- plt.scatter(df, y_predict)

看來算法運作良好。預測的輸出線從中間位置開始。
是時候建立學習曲線了!!!
畫出學習曲線
現(xiàn)在,我們可以畫出學習曲線。首先,讓我們?yōu)槲覀兊慕徊骝炞C數(shù)據(jù)集導入X和y值。如前所述,我們將它們組織在單獨的Excel工作表中。
- file = pd.ExcelFile('dataset.xlsx')
- cross_val = pd.read_excel(file, 'X', header=None)
- cross_val.head()

- cross_y = pd.read_excel(file, 'y', header=None)
- cross_y.head()

為此,我想稍微修改一下gradient_descent函數(shù)。
在之前的gradient_descent函數(shù)中,我們計算了每次迭代的成本。我這樣做是因為這是傳統(tǒng)機器學習算法開發(fā)中的一種很好的做法。
但是對于學習曲線,我們不需要每次迭代的成本。因此,為了節(jié)省運行時間,我將在每個時期中排除計算成本函數(shù)。我們將僅返回更新的參數(shù)。
- def grad_descent(theta, X, y, epoch, alpha):
- i = 0
- while i < epoch:
- hx = hypothesis(theta, X)
- theta[0] -= alpha*(sum(hx-y)/m)
- theta[1] -= (alpha * np.sum((hx - y) * X))/m
- i += 1
- return theta
如前所述,要開發(fā)學習曲線,我們需要使用訓練數(shù)據(jù)的不同子集來訓練學習算法。
在我們的訓練數(shù)據(jù)集中,我們有21個數(shù)據(jù)。我將僅使用一個數(shù)據(jù),然后使用兩個數(shù)據(jù),然后使用三個數(shù)據(jù)一直到21個數(shù)據(jù)來訓練算法。
因此,我們將在21個訓練數(shù)據(jù)子集上對算法進行21次訓練。我們還將跟蹤每個訓練數(shù)據(jù)子集的成本函數(shù)。請仔細看一下代碼,它將更加清晰。
- j_tr = []
- theta_list = []
- for i in range(0, len(df)):
- theta = [0,0]
- theta_list.append(grad_descent(theta, df[0][:i], y[0][:i], 1400, 0.001))
- j_tr.append(cost_calc(theta, df[0][:i], y[0][:i]))
- theta_list
以下是每個訓練數(shù)據(jù)子集的訓練參數(shù):

這是每個訓練子集的費用:

查看每個子集的成本。當訓練數(shù)據(jù)僅為1或2時,成本為零或幾乎為零。隨著我們不斷增加培訓數(shù)據(jù),成本也上升了,這是預期的。
現(xiàn)在,對訓練數(shù)據(jù)的所有子集使用上面的參數(shù)來計算交叉驗證數(shù)據(jù)的成本:
- j_val = []
- for i in theta_list:
- j_val.append(cost_calc(i, cross_val[0], cross_y[0]))
- j_val

剛開始時,成本確實很高,因為訓練參數(shù)來自太少的訓練數(shù)據(jù)。但是隨著參數(shù)的增加和更多訓練數(shù)據(jù)的改進,交叉驗證錯誤不斷下降。
讓我們在同一圖中繪制訓練誤差和交叉驗證誤差:
- %matplotlib inline
- import matplotlib.pyplot as plt
- plt.figure()
- plt.scatter(range(0, 21), j_tr)
- plt.scatter(range(0, 21), j_val)

這是我們的學習曲線。
從學習曲線中得出決策
上面的學習曲線看起來不錯。它以我們預期的方式流動。最初,訓練誤差太小,驗證誤差太高。
慢慢地,它們彼此完全重疊。太完美了!但是在現(xiàn)實生活中,這種情況并不經(jīng)常發(fā)生。
大多數(shù)機器學習算法并不是第一次都能完美運行。它幾乎始終都遭受一些我們需要解決的問題的困擾。在這里,我將討論一些問題。
我們可能會發(fā)現(xiàn)學習曲線如下所示:

訓練誤差和驗證誤差之間是否存在重大差異,表明存在高方差問題。也可以稱為過度擬合問題。
獲取更多的訓練數(shù)據(jù)或選擇較小的功能集或同時使用這兩種功能都可以解決此問題。

如果傾斜曲線看起來像這樣,則意味著開始時訓練誤差太小而驗證誤差太高。緩慢地,訓練誤差變高而驗證誤差變低。但在某種程度上,它們變得平行。您可以從圖片中看到一點,即使有了更多訓練數(shù)據(jù),交叉驗證錯誤也不再減少。
在這種情況下,獲取更多訓練數(shù)據(jù)將不會改善機器學習算法。
這表明學習算法正在遭受高偏差問題。在這種情況下,獲得更多訓練功能可能會有所幫助。
修正學習算法
假設我們正在執(zhí)行線性回歸。但是該算法無法正常工作。
該怎么辦?
首先,畫出一個學習曲線,如我在此處演示的。
- 如果檢測到高方差問題,請根據(jù)要素的重要性選擇一組較小的要素。如果有幫助,可以節(jié)省一些時間。如果不是,請嘗試獲取更多訓練數(shù)據(jù)。
- 如果您從學習曲線中發(fā)現(xiàn)高偏差問題,那么您已經(jīng)知道獲得附加功能是一種可能的解決方案。您甚至可以嘗試添加一些多項式特征。大量時間可以幫助您節(jié)省大量時間。
- 如果您要使用正則化項lambda實現(xiàn)算法,請在該算法遇到高偏差的情況下嘗試降低lambda,并在該算法遇到高方差問題的情況下嘗試增加lambda。
在神經(jīng)網(wǎng)絡的情況下,我們也可能遇到這種偏差或方差問題。
對于高偏差或欠擬合問題,我們需要增加神經(jīng)元的數(shù)量或隱藏層的數(shù)量。為了解決高方差或過度擬合的問題,我們應該減少神經(jīng)元的數(shù)量或隱藏層的數(shù)量。我們甚至可以使用不同數(shù)量的神經(jīng)元繪制學習曲線。
非常感謝您閱讀本文。我希望這可以幫到你。