机器学习笔记(1): 梯度下降算法
本文作为我看过 # 吴恩达机器学习系列课程 的产物,并不适用于一无所知的学习者。
在机器学习中,有三个很重要的函数:
- \(h_\theta(x)\) 表示预测数据
- \(J(\theta)\) 代价函数,表示预测和实际的差距,\(J(\theta) \ge 0\),且 \(J(\theta)\) 值越小,差距越小。
- \(\frac {\partial}{\partial \theta} J(\theta)\) 也就是其偏导数,用于梯度下降算法的拟合。
由于本人没有系统的学习偏导数,为了方便表示,这里认为 \(\theta' = \frac {\partial}{\partial \theta} J(\theta)\) 是一个与 \(\theta\) 同样大小的向量,其中 \(\theta'_i\) 表示在第 \(i\) 个维度平面内的斜率。故 \(\frac {\partial}{\partial \theta} J(\theta)\) 实际上就表示的是 \(J\) 函数图像在 \(\theta\) 处的斜率。
一个优秀的代价函数是梯度下降算法的核心。
一般来说,需要具有如下特性:
- 不存在局部最小值 %% 也就是 \(\not \exists \theta(J'(\theta) = 0 \wedge \exists \alpha(J(\theta) > J(\alpha))\) %%
- 没有平坦的部分,也就是没有 \(J'(\theta) = 0\) 但是 \(J(\theta)\) 不是最小值的地方。
梯度下降算法的目标很简单:
其过程也很简单:
其中 \(\alpha\) 是学习速率。如果 \(\alpha\) 过小,则时间成本过大;如果 \(\alpha\) 过大,则容易跳过最优解。
如何理解 跳过 ?梯度下降算法的过程,实际上就是沿着斜率不断向下跳的过程。而学习速率决定了向下跳的距离,所以说如果 \(\alpha\) 过大,则容易跳过最优解。
线性回归
线性回归,即使用一次函数对于数据进行拟合:
我们一般使用的是平方损失函数:
其中 \(x_i\) 表示输入特征,而 \(y_i\) 表示真实值。
我们不妨将 \(x_i\) 简单修改,变成 \((1, x_i)\),记为 \(X_i\),则 \(X\) 是一个 \(2 \times m\) 的矩阵:
那么自然,\(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\) 的矩阵。
原本计算式:
变成矩阵的写法:
非常的优美。
简单代码
利用
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
这么一看核心代码也就一点点……但是不得不说确实高级。
正则化参数
是一种防止“过拟合出现”的重要方式。
一般来说,为了限制参数的大小,我们会小小修改损失函数:
那么其偏导数:
自然参数学习部分变成了:
不过值得注意的是,一般来说,我们的 \(\theta\) 是增广的矩阵,所以需要注意常数项不应该被减少!
也就是说设 \(\theta'\) 表示 \(\theta\) 常数项被设为 \(0\),那么: