个人博客文章链接:http://www.huqj.top/article?id=162
梯度下降法(gradient descent),是机器学习中最常用的参数调优算法,所谓梯度下降,就是对于一个模型的代价函数而言,从某个初始参数开始,逐渐将参数朝“使得代价函数减小最快”的方向调整,使得代价函数最终稳定在某个值左右。
举个例子,对于训练集数据:
1 | training_data = [ 1 7.6 ; 2 15.8 ; 3 29 ; 4 45 ; 5 66.5 ; 6 91 ; 10 225 ]; |
它的离散图像大致如下:
如果我们需要用一个函数来拟合它,那么最好是用一个二次或者更高次函数,假设我们使用二次函数:
y = θ0 + θ1x + θ2x2
来作为模型的函数表达式,那么我们就需要确定三个参数分别是多少才能够最大程度的符合训练数据,对于一次函数而言,我们知道可以使用最小二乘法来计算参数,同样,对于二次函数也有相应的数学方法可以确定参数值,但是一方面这样做不具有通用性,另一方面,也可能会出现没有最优解的情况,因此梯度下降法成为了一个较好的选择,它使用迭代的方式使得代价函数逐步减小,直到稳定在最小值附近,这样就可以得到参数的较优解。
梯度下降的数学原理如下:
①假设模型函数为 y = hθ(X),其中θ和X都是n维向量。
②代价函数表示当取某个θ向量作为参数时,模型计算出的结果和实际结果的误差,通常使用如下的函数来表示:
J(θ) = 1/2m * ∑i=1:m(hθ(Xi) - yi)2
其中m为训练集的数据个数,Xi表示第i个数据的自变量向量,yi为第i组数据的因变量,θ为某个参数向量。
③参数θ的迭代调整方法:
带入上面的J(θ)和hθ(X)的定义可推导:
θj := θj - α(1/m) * ∑i=1:m(hθ(Xi) - yi)*Xi,j
这个写的可能有点不清楚,可以看下面这张图片里的公式:
这里描述了只有一个训练数据的情况,当有多个训练数据时,x和y都需要加上下标并求和,而j表示的是当前求偏导的自变量是xj, 需要和第几组训练数据相区分。
迭代中的α称为“学习率”,它揭示了梯度下降中的“下降速度”,也就是每次朝着代价函数减小的方向移动多少,但是学习率不能太大,太大可能会导致迭代发散。如下图所示:
因为学习率过大可能导致第一次迭代的时候就跳过了最小值从而代价函数越来越大。但是学习率过小也会导致训练速度缓慢,因此把握好学习率也是梯度下降中比较重要的一点。
迭代终止的条件通常是两次参数调节导致的代价函数值变化不大,或者迭代到了一定次数。
有了以上数学原理的铺垫,我们就可以写出梯度下降的代码了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | function coe=GradientDecent(training_data, maxIndex, alpha, threshold, maxTimes) %测试梯度下降算法调整模型参数,参数为训练数据集和模型函数的最大系数,和学习率 %测试参数预设 training_data = [1 7.6;2 15.8; 3 29;4 45;5 66.5;6 91;10 225]; maxIndex = 2; alpha = 0.0001; threshold = 0.0001; maxTimes = 5000; dataSize = length(training_data); dataLen = dataSize(1); plot(training_data(:,1), training_data(:,2), 'r*'); hold on; paramLen = maxIndex + 1; theta = zeros(paramLen, 1); theta0 = theta; times = 0; cost0=0; cost1=1; % 迭代直到一定次数或者两次参数误差小于一定阈值 while abs(cost1-cost0) > threshold && times<=maxTimes theta0 = theta; times = times + 1; cost0 = costFunction(theta', maxIndex, training_data); tmp = zeros(paramLen, 1); for j = 1:dataLen X = zeros(paramLen, 1); for k = 1:paramLen X(k) = training_data(j, 1) ^ (k - 1); end for i = 1:paramLen tmp(i) = tmp(i) + (theta0' * X - training_data(j, 2)) * X(i); end end for i = 1:paramLen tmp(i) = tmp(i) / dataLen; theta(i) = theta0(i) - alpha * tmp(i); end cost1 = costFunction(theta', maxIndex, training_data); end theta0 theta X = 0:0.01:10; Y = X; for i = 1:length(X) Y(i) = theta' * [1; X(i); X(i)^2]; end plot(X, Y, 'b'); coe = theta; function delta=costFunction(theta, maxCoe, data) %代价函数,给定参数θ、模型变量指数最大值、训练数据,求代价函数值 X = zeros(maxCoe + 1, 1); delta = 0; [len, ~] = size(data); for i = 1:len for j = 0:maxCoe X(j+1) = data(i, 1)^j; end delta = delta + (theta * X - data(i, 2))^2; end delta = delta/(2 * len); |
我们可以调节模型的自变量最大指数和学习率、阈值等来看看最终训练出来的效果有什么不同。
二次模型:
一次模型:
三次模型:
值得注意的是:对于不同的模型,学习率可能是不同的,例如这里对于一次模型而言,0.001的学习率就可以使得梯度下降收敛,但是对于二次模型却不行。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)