機器學習筆記(1): 梯度下降演算法

jeefy發表於2024-06-02

本文作為我看過 # 吳恩達機器學習系列課程 的產物,並不適用於一無所知的學習者。

在機器學習中,有三個很重要的函式:

  • \(h_\theta(x)\) 表示預測資料
  • \(J(\theta)\) 代價函式,表示預測和實際的差距,\(J(\theta) \ge 0\),且 \(J(\theta)\) 值越小,差距越小。
  • \(\frac {\delta}{\delta \theta} J(\theta)\) 也就是其偏導數,用於梯度下降演算法的擬合。

由於本人沒有系統的學習偏導數,為了方便表示,這裡認為 \(\theta' = \frac {\delta}{\delta \theta} J(\theta)\) 是一個與 \(\theta\) 同樣大小的向量,其中 \(\theta'_i\) 表示在第 \(i\) 個維度平面內的斜率。故 \(\frac {\delta}{\delta \theta} J(\theta)\) 實際上就表示的是 \(J\) 函式影像在 \(\theta\) 處的斜率。

一個優秀的代價函式是梯度下降演算法的核心。
一般來說,需要具有如下特性:

  • 不存在區域性最小值 %% 也就是 \(\not \exists \theta(J'(\theta) = 0 \wedge \exists \alpha(J(\theta) > J(\alpha))\) %%
  • 沒有平坦的部分,也就是沒有 \(J'(\theta) = 0\) 但是 \(J(\theta)\) 不是最小值的地方。

梯度下降演算法的目標很簡單:

\[\min J(\theta) \]

其過程也很簡單:

\[\theta = \theta - \alpha \frac {\delta}{\delta \theta} J(\theta) \]

其中 \(\alpha\) 是學習速率。如果 \(\alpha\) 過小,則時間成本過大;如果 \(\alpha\) 過大,則容易跳過最優解。

如何理解 跳過 ?梯度下降演算法的過程,實際上就是沿著斜率不斷向下跳的過程。而學習速率決定了向下跳的距離,所以說如果 \(\alpha\) 過大,則容易跳過最優解。

線性迴歸

線性迴歸,即使用一次函式對於資料進行擬合:

\[h_\theta(x_i) = \theta_0 + \theta_1 x_i \]

我們一般使用的是平方損失函式:

\[J(\theta) = \frac 1 {2m} \sum_{i = 1}^m (h_\theta(x_i) - y_i) ^2 \]

其中 \(x_i\) 表示輸入特徵,而 \(y_i\) 表示真實值。

我們不妨將 \(x_i\) 簡單修改,變成 \((1, x_i)\),記為 \(X_i\),則 \(X\) 是一個 \(2 \times m\) 的矩陣:

\[X = \begin{pmatrix} 1 & x_0 \\ 1 & x_1 \\ \vdots & \vdots \\ 1 & x_{m - 1} \\ \end{pmatrix} \]

那麼自然,\(J(\theta) = \frac 1 {2m} \mathrm{ones} (1, m) {\large (}(X \theta - y) \cdot (X \theta - y){\large )}\)

其中 \({\rm ones}(n, m)\) 表示一個 \(n \times m\) 的全為 \(1\) 的矩陣。

原本計算式:

\[\theta_j = \theta_j - \alpha \frac 1 m\sum_{i = 1}^m X_{i, j} (X_i *\theta - y) \]

變成矩陣的寫法:

\[\theta = \theta - \frac \alpha m X^T (X \times \theta - y) \]

非常的優美。

簡單程式碼

利用 Octave 寫的。

% X 是輸入資料,y 是目標資料資料

% 標準化輸入
[X mu sigma] = featureNormalize(X);

% 新增一列常數 1
X = [ones(m, 1) X];

% 設定訓練引數
alpha = 0.01;
num_iters = 400;

% 開始訓練
theta = zeros(3, 1);
theta = gradientDescentMulti(X, y, theta, alpha, num_iters);

% 預測函式,這裡的 x 是沒有常數項的
function [predict] = multiPredict(x, theta, mu, sigma)
	normData = ([1 x] - [0 sigma]) ./ [1 mu];
	predict = normData * theta;
end

一些函式

% 標準化每一列
function [X_norm, mu, sigma] = featureNormalize(X)
X_norm = X;
mu = zeros(1, size(X, 2));
sigma = zeros(1, size(X, 2));

for i = 1:length(X(1, :))
	V = X_norm(:, i);
	sigma(i) = mean(V);
	mu(i) = std(V) * std(V);
	V = (V - sigma(i)) / mu(i);
	X_norm(:, i) = V;
end

end

% 開始梯度下降
function theta = gradientDescentMulti(X, y, theta, alpha, num_iters)
m = length(y); % 樣本量

for iter = 1:num_iters
	theta = theta - alpha / m * (X' * (X * theta - y));
end

end

% 計算代價函式 J(\theta)
function J = computeCostMulti(X, y, theta)
m = length(y); % 樣本量
diffCost = X * theta - y;
J = sum(diffCost .* diffCost) / 2 / m;

end

這麼一看核心程式碼也就一點點……但是不得不說確實高階。

相關文章