從 0 開始機器學習 - 一文入門多維特徵梯度下降法!

今天登龍跟大家分享下我對多維特徵的讀取、縮放和多變量梯度下降算法的理解,文章不長,有理論也有實際的代碼,下面開始,Go!

一、如何表示多維特徵?

1.1 特徵縮放

實際項目中在讀取多維特徵之前需要先對數據進行縮放,爲什麼呢?

因爲在有了多維特徵向量和多變量梯度下降法後,爲了幫助算法更快地收斂,還需要對選用的特徵進行尺度縮放,其實就是縮小特徵值,將多個特徵的值都縮小到同樣大小的區間,通常是縮小到 [-1, 1] 周圍。

以下圖爲例:

在沒有進行特徵縮放之前,兩參數梯度下降的等高線圖呈豎的橢圓形,這是因爲橫軸和縱軸參數範圍不同,進而導致算法在尋找最小值時會迭代很多次。

而當進行縮放使得橫縱軸範圍大致相同後,等高線圖基本呈圓形,算法在迭代的時候往一個方向很快就能找到最小值,大大減少迭代次數。

縮放的最終結果不一定非要準確到 [-1, 1],比如 [-3, 3],[-2, 1] 這些範圍不是太大都是可以的,一個又常用有簡單的特徵縮放計算方法是:

xn=xnμnsn x_n=\frac{x_n - \mu_n}{s_n}

其中 μn{\mu_{n}} 是平均值,sn{s_{n}} 是(max - min),比如用這個公式將所有的房屋面積和臥室數量進行縮放:

  • x1=(size1000)/2000x_1 = (size - 1000) / 2000,其中 1000 是面積平均值,2000 是最大面積減最小面積。
  • x2=(bedrooms2)/5x_2 = (bedrooms - 2) / 5,其中 2 是臥室數量平均值,5 個最大臥室數量減去最小臥室數量。

理論學會後,再來學習下實際的特徵縮放代碼:

# 特徵縮放
def normalize_feature(df):
    # 對原始數據每一列應用一個 lambda 函數,mean() 求每列平均值,std() 求標準差
    return df.apply(lambda column: (column - column.mean()) / column.std())

我們用這個函數來實際縮放一下含有 2 個特徵的原始房價數據:

# 讀取原始數據
raw_data = pd.read_csv('ex1data2.txt', names = ['square', 'bedrooms', 'price'])

# 顯示前 5 行
raw_data.head()

# 對原始數據進行特徵縮放
data = normalize_feature(raw_data)

# 顯示縮放後的前 5 行數據
data.head()

可以看到縮放後的數據範圍基本都在 [1,1][-1, 1] 區間左右,說明我們的特徵縮放成功了 _!下面來學習如何讀取多維特徵!

1.2 讀取多維特徵

還記得上篇文章我們介紹的第一個機器學習算法嗎?

即通過房屋面積來預測價格,這個問題中只使用一個輸入特徵房屋面積,可現實生活中要解決的問題通常都含有多個特徵,因爲用多個特徵訓練出的模型準確度更高。

那麼如何機器學習算法如何處理多個特徵的輸入呢?

我們還用預測房價的例子,不過這次要增加另外 3 個特徵,臥室數量,房屋樓層,房屋年齡:

這樣一來,我們就有了 4 個輸入特徵了,特徵多了,表示的方法也要升升級了:

  • nn:輸入特徵的數量,即特徵矩陣列數,也即特徵向量的維度

  • x(i){x^{\left( i \right)}}:訓練集中第 ii 個實例向量,就是特徵矩陣的第 ii 行,比如列向量 x(2)=[14163240]T{x}^{(2)}\text{=}\begin{bmatrix} 1416 & 3 & 2 & 40 \end{bmatrix}^T

  • xj(i){x_j}^{\left( i \right)}:訓練集中第 ii 個實例的第 jj 個特徵,比如 x2(2)=3x_2^{\left( 2 \right)}=3

特徵數量增加了,之前的假設函數肯定也需要修改,要把增加的特徵變量和參數加上:

hθ(x)=θ0+θ1x1+θ2x2+...+θnxnh_{\theta}\left( x \right)={\theta_{0}}+{\theta_{1}}{x_{1}}+{\theta_{2}}{x_{2}}+...+{\theta_{n}}{x_{n}}

雖然這樣表示沒問題,但是卻不方便利用向量來計算,因爲參數 θ\theta 有 n + 1 個,但 xx 只有 n 個,那怎麼辦呢?

很簡單,我們額外增加一個 x0=1{x_0}=1,則上式變爲:

hθ(x)=θ0x0+θ1x1+θ2x2+...+θnxn h_{\theta} \left( x \right)={\theta_{0}}{x_{0}}+{\theta_{1}}{x_{1}}+{\theta_{2}}{x_{2}}+...+{\theta_{n}}{x_{n}}

這樣一來就可以寫成向量相乘的形式:

hθ(x)=θTX h_{\theta} \left( x \right)={\theta^{T}}X

你可能要問了爲何要寫成向量的形式?主要因爲 2 點:

  • 使用向量方便程序編寫,一句計算特徵向量的代碼就可以同時計算多個輸入參數,因爲一個特徵向量中包含所有輸入參數
  • 使用向量方便算法執行,梯度下降算法要求參數同時更新,如果不使用向量,那更新起來非常麻煩。

通過增加一個維度 x0=1x_0 = 1,最終訓練集的特徵矩陣的大小爲:m(n+1)m * (n + 1),其中 m 爲行數,n + 1 爲列數。

那來看下讀取多維數據並添加一列全 1 向量的函數代碼:

# 讀取原始數據,返回 m * (n + 1) 維特徵矩陣
def get_X(df):
    # 創建 m 行 1 列的數據幀
    ones = pd.DataFrame({'ones': np.ones(len(df))})
    
    # 合併全 1 向量作爲元素數據第一列,axis = 0 按行合併,anix = 1 按列合併
    data = pd.concat([ones, df], axis=1)
    
    # 返回特徵矩陣
    return data.iloc[:, :-1].iloc[:, :].values

爲了簡單點,這裏假設原始房價數據只有 2 個輸入特徵,即房屋面積和臥室數量:

我們用上面的函數來讀取下數據特徵到向量 X

# 讀取原始數據,增加第一列全 1 向量
X = get_X(data)

# 輸出數據、維度和類型
print(X.shape, type(X))
print(X)

輸出結果如下:

# 47 行,3 列 = 47 * (2 + 1)
(47, 3) <class 'numpy.ndarray'>

可以看到特徵矩陣的第一維列向量全爲 1,後兩列不變(數據換成科學計數法表示),這與我們上面介紹的對多維特徵的操作方法結果相同!

讀取多維特徵之後,我們就可以將特徵矩陣 X 的每一行作爲一個特徵向量(就是特徵組成的向量 =_=),並用它們來訓練機器學習算法啦!

以上就是我對多維特徵作爲機器學習算法輸入的一些理解,非常感謝吳恩達老師的公開課 _

上面的代碼都在文末我的 Github 倉庫,直接下載就能運行,記得給我個 star 哦!

二、多變量梯度下降法

多維特徵讀取後,就可以學習多變量梯度下降法了,其實與上一篇博客的單變量梯度下降原理是一樣的,只不過增加了特徵變量,相應地參數也就增加了。

比如線性迴歸的多變量假設函數、代價函數、梯度下降法分別如下:

  • 假設函數:

hθ(x)=θTX=θ0+θ1x1+θ2x2+...+θnxnh_{\theta}\left( x \right)=\theta^{T}X={\theta_{0}}+{\theta_{1}}{x_{1}}+{\theta_{2}}{x_{2}}+...+{\theta_{n}}{x_{n}}

  • 代價函數:

J(θ0,θ1...θn)=12mi=1m(hθ(x(i))y(i))2J \left( \theta_0, \theta_1 ... \theta_n\right) = \frac{1}{2m}\sum\limits_{i=1}^m \left( h_{\theta}(x^{(i)})-y^{(i)} \right)^{2}

  • 多變量梯度下降法

因爲參數增加到 n 個,所以梯度下降的偏導數也要分別對每個參數求一次,然後同時更新 n 個參數:

比如當 n>=1n>=1 時更新前 3 個參數:

我覺得挺好理解的,只需要按照單變量梯度下降的邏輯拓展下變量和參數的數量即可,前提一定要完全理解單變量的梯度下降。

那繼續來看下多變量梯度下降的算法代碼,與單變量梯度下降一毛一樣,先計算偏導數:

# 計算偏導數
def gradient(theta, X, y):
    m = X.shape[0]
    
    inner = X.T @ (X @ theta - y)
    
    return inner / m

再迭代下降:

# 批量梯度下降
# epoch: 下降迭代次數
# alpha: 初始學習率
def batch_gradient_decent(theta, X, y, epoch, alpha = 0.01):
    # 計算初始成本:theta 都爲 0
    cost_data = [lr_cost(theta, X, y)]
    
    # 創建新的 theta 變量,不與原來的混淆
    _theta = theta.copy()
    
    for _ in range(epoch):
      	# 新的 theta = 舊的 theta - 學習率 * 偏導數
        _theta = _theta - alpha * gradient(_theta, X, y)
        # 累加成本數據,用於可視化
        cost_data.append(lr_cost(_theta, X, y))
        
    return _theta, cost_data

來調用下這個梯度下降函數,初始學習率 alpha 設置爲 0.01,迭代 epoch = 500 次:

final_theta, cost_data = batch_gradient_decent(theta, X, y, epoch, alpha = alpha)

這是最終的成本和迭代次數的曲線,可以看到成本 cost 最終基本趨於不變,說明梯度下降算法收斂啦!

話說我之前忘記講解單變量梯度下降的代碼了,下次一定補上!文章內的代碼倉庫
AI-Notes

OK,今天就跟大家分享這些,喜歡的小夥伴記得關注下面的公衆號,持續關注我哦!

本文原創首發於 同名微信公號「登龍」,微信搜索關注回覆「1024」你懂的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章