ml 梯度下降算法学习笔记
梯度下降
梯度下降是迭代法的一种,可以用于求解最小二乘问题(线性和非线性都可以)。在求解机器学习算法的模型参数,即无约束优化问题时,梯度下降(Gradient Descent)是最常采用的方法之一,另一种常用的方法是最小二乘法。在求解损失函数的最小值时,可以通过梯度下降法来一步步的迭代求解,得到最小化的损失函数和模型参数值。反过来,如果我们需要求解损失函数的最大值,这时就需要用梯度上升法来迭代了。在机器学习中,基于基本的梯度下降法发展了两种梯度下降方法,分别为随机梯度下降法和批量梯度下降法。
梯度下降原理:将函数比作一座山,我们站在某个山坡上,往四周看,从哪个方向向下走一小步,能够下降的最快。

在求极小值时,在数据量很小的时候,可以使用矩阵求逆的方式求最优的θ值。但当数据量和特征值非常大,例如几万甚至上亿时,使用矩阵求逆根本就不现实。而梯度下降法就是很好的一个选择了。
使用梯度下降算法的步骤:
1)对θ赋初始值,这个值可以是随机的,也可以让θ是一个全零的向量。
2)改变θ的值,使得目标损失函数J(θ)按梯度下降的方向进行减少。

其中为学习率或步长,需要人为指定,若过大会导致震荡即不收敛,若过小收敛速度会很慢。
3)当下降的高度小于某个定义的值,则停止下降。
另外,对上面线性回归算法损失函数求梯度,结果如下:

在实际应用的过程中,梯度下降算法有三类,它们不同之处在于每次学习(更新模型参数)使用的样本个数,每次更新使用不同的样本会导致每次学习的准确性和学习时间不同。

下面主要介绍小批量梯度下降法
小批量梯度下降法(Mini-batch梯度下降算法)
Mini-batch梯度下降综合了batch梯度下降与stochastic梯度下降,在每次更新速度与更新次数中间取得一个平衡,其每次更新从训练集中随机选择b,b<m个样本进行学习,即:

下面是最重要的,python代码实现三种梯度下降算法
1 #!/usr/bin/python 2 3 #coding=utf-8 4 5 import numpy as np 6 7 from scipy import stats 8 9 import matplotlib.pyplot as plt 10 11 12 13 # 构造训练数据 14 15 x = np.arange(0., 10., 0.2) 16 17 m = len(x) # 训练数据点数目 18 19 print m 20 21 x0 = np.full(m, 1.0) 22 23 input_data = np.vstack([x0, x]).T # 将偏置b作为权向量的第一个分量 24 25 target_data = 2 * x + 5 + np.random.randn(m) 26 27 28 29 # 两种终止条件 30 31 loop_max = 10000 # 最大迭代次数(防止死循环) 32 33 epsilon = 1e-3 34 35 36 37 # 初始化权值 38 39 np.random.seed(0) 40 41 theta = np.random.randn(2) 42 43 44 45 alpha = 0.001 # 步长(注意取值过大会导致振荡即不收敛,过小收敛速度变慢) 46 47 diff = 0. 48 49 error = np.zeros(2) 50 51 count = 0 # 循环次数 52 53 finish = 0 # 终止标志 54 55 56 57 while count < loop_max: 58 59 count += 1 60 61 62 63 # 标准梯度下降是在权值更新前对所有样例汇总误差,而随机梯度下降的权值是通过考查某个训练样例来更新的 64 65 # 在标准梯度下降中,权值更新的每一步对多个样例求和,需要更多的计算 66 67 sum_m = np.zeros(2) 68 69 for i in range(m): 70 71 dif = (np.dot(theta, input_data[i]) - target_data[i]) * input_data[i] 72 73 sum_m = sum_m + dif # 当alpha取值过大时,sum_m会在迭代过程中会溢出 74 75 76 77 theta = theta - alpha * sum_m # 注意步长alpha的取值,过大会导致振荡 78 79 # theta = theta - 0.005 * sum_m # alpha取0.005时产生振荡,需要将alpha调小 80 81 82 83 # 判断是否已收敛 84 85 if np.linalg.norm(theta - error) < epsilon: 86 87 finish = 1 88 89 break 90 91 else: 92 93 error = theta 94 95 print 'loop count = %d' % count, '\tw:',theta 96 97 print 'loop count = %d' % count, '\tw:',theta 98 99 100 101 # check with scipy linear regression 102 103 slope, intercept, r_value, p_value, slope_std_error = stats.linregress(x, target_data) 104 105 print 'intercept = %s slope = %s' % (intercept, slope) 106 107 108 109 plt.plot(x, target_data, 'g*') 110 111 plt.plot(x, theta[1] * x + theta[0], 'r') 112 113 plt.show()
运行结果截图:

1 #!/usr/bin/python 2 3 #coding=utf-8 4 5 import numpy as np 6 7 from scipy import stats 8 9 import matplotlib.pyplot as plt 10 11 12 13 # 构造训练数据 14 15 x = np.arange(0., 10., 0.2) 16 17 m = len(x) # 训练数据点数目 18 19 x0 = np.full(m, 1.0) 20 21 input_data = np.vstack([x0, x]).T # 将偏置b作为权向量的第一个分量 22 23 target_data = 2 * x + 5 + np.random.randn(m) 24 25 26 27 # 两种终止条件 28 29 loop_max = 10000 # 最大迭代次数(防止死循环) 30 31 epsilon = 1e-3 32 33 34 35 # 初始化权值 36 37 np.random.seed(0) 38 39 theta = np.random.randn(2) 40 41 # w = np.zeros(2) 42 43 44 45 alpha = 0.001 # 步长(注意取值过大会导致振荡,过小收敛速度变慢) 46 47 diff = 0. 48 49 error = np.zeros(2) 50 51 count = 0 # 循环次数 52 53 finish = 0 # 终止标志 54 55 ######-随机梯度下降算法 56 57 while count < loop_max: 58 59 count += 1 60 61 62 63 # 遍历训练数据集,不断更新权值 64 65 for i in range(m): 66 67 diff = np.dot(theta, input_data[i]) - target_data[i] # 训练集代入,计算误差值 68 69 70 71 # 采用随机梯度下降算法,更新一次权值只使用一组训练数据 72 73 theta = theta - alpha * diff * input_data[i] 74 75 76 77 # ------------------------------终止条件判断----------------------------------------- 78 79 # 若没终止,则继续读取样本进行处理,如果所有样本都读取完毕了,则循环重新从头开始读取样本进行处理。 80 81 82 83 # ----------------------------------终止条件判断----------------------------------------- 84 85 # 注意:有多种迭代终止条件,和判断语句的位置。终止判断可以放在权值向量更新一次后,也可以放在更新m次后。 86 87 if np.linalg.norm(theta - error) < epsilon: # 终止条件:前后两次计算出的权向量的绝对误差充分小 88 89 finish = 1 90 91 break 92 93 else: 94 95 error = theta 96 97 print 'loop count = %d' % count, '\tw:',theta 98 99 100 101 102 103 # check with scipy linear regression 104 105 slope, intercept, r_value, p_value, slope_std_error = stats.linregress(x, target_data) 106 107 print 'intercept = %s slope = %s' % (intercept, slope) 108 109 110 111 plt.plot(x, target_data, 'g*') 112 113 plt.plot(x, theta[1] * x + theta[0], 'r') 114 115 plt.show()
运行结果截图:

#!/usr/bin/python #coding=utf-8 import numpy as np from scipy importstats import matplotlib.pyplot as plt # 构造训练数据 x = np.arange(0.,10.,0.2) m = len(x) # 训练数据点数目 print m x0 = np.full(m, 1.0) input_data = np.vstack([x0, x]).T # 将偏置b作为权向量的第一个分量 target_data = 2 *x + 5 +np.random.randn(m) # 两种终止条件 loop_max = 10000 #最大迭代次数(防止死循环) epsilon = 1e-3 # 初始化权值 np.random.seed(0) theta = np.random.randn(2) alpha = 0.001 #步长(注意取值过大会导致振荡即不收敛,过小收敛速度变慢) diff = 0. error = np.zeros(2) count = 0 #循环次数 finish = 0 #终止标志 minibatch_size = 5 #每次更新的样本数 while count < loop_max: count += 1 # minibatch梯度下降是在权值更新前对所有样例汇总误差,而随机梯度下降的权值是通过考查某个训练样例来更新的 # 在minibatch梯度下降中,权值更新的每一步对多个样例求和,需要更多的计算 for i inrange(1,m,minibatch_size): sum_m = np.zeros(2) for k inrange(i-1,i+minibatch_size-1,1): dif = (np.dot(theta, input_data[k]) - target_data[k]) *input_data[k] sum_m = sum_m + dif #当alpha取值过大时,sum_m会在迭代过程中会溢出 theta = theta- alpha * (1.0/minibatch_size) * sum_m #注意步长alpha的取值,过大会导致振荡 # 判断是否已收敛 if np.linalg.norm(theta- error) < epsilon: finish = 1 break else: error = theta print 'loopcount = %d'% count, '\tw:',theta print 'loop count = %d'% count, '\tw:',theta # check with scipy linear regression slope, intercept, r_value, p_value,slope_std_error = stats.linregress(x, target_data) print 'intercept = %s slope = %s'% (intercept, slope) plt.plot(x, target_data, 'g*') plt.plot(x, theta[1]* x +theta[0],'r') plt.show()
运行结果:

还有参考了一些博主的博客,有一些可用代码:
import matplotlib.pyplot as plt #数据是我自己编的,故意编的很像一个单调递增的一次函数 x_train = [100,80,120,75,60,43,140,132,63,55,74,44,88] y_train = [120,92,143,87,60,50,167,147,80,60,90,57,99] #步长 alpha = 0.00001 diff = [0,0] cnt = 0 #计算样本个数 m = len(x_train) #初始化参数的值,拟合函数为 y=theta0+theta1*x theta0 = 0 theta1 = 0 #误差 error0=0 error1=0 #退出迭代的两次误差差值的阈值 epsilon=0.000001 def h(x): return theta1*x+theta0 #开始迭代 while True: cnt=cnt+1 diff = [0,0] #梯度下降 for i in range(m): diff[0]+=h(x_train[i])-y[i] diff[1]+=(h(x_train[i])-y[i])*x_train[i] theta0=theta0-alpha/m*diff[0] theta1=theta1-alpha/m*diff[1] error1=0 #计算两次迭代的误差的差值,小于阈值则退出迭代,输出拟合结果 for i in range(len(x_train)): error1 += (y[i] - (theta0 + theta1 * x_train[i])) ** 2 / 2 if abs(error1 - error0) < epsilon: break else: error0 = error1 plt.plot(x_train,[h(x) for x in x_train]) plt.plot(x_train,y_train,'bo') print(theta1,theta0) plt.show()
输出:

#x = [100,80,120,75,60,43,140,132,63,55,74,44,88] #y = [120,92,143,87,60,50,167,147,80,60,90,57,99] from numpy import * import matplotlib.pyplot as plt import pandas as pd # y = mx + b # m is slope, b is y-intercept #求误差函数,就是预测值和实际值相减然后求平方,最后再取平均值 def compute_error_for_line_given_points(b, m, points): totalError = 0 #将数据分别赋值为x,y for i in range(0, len(points)): x = points[i, 0] y = points[i, 1] totalError += (y - (m * x + b)) ** 2 return totalError / float(len(points)) #梯度下降算法实现,迭代一次后输出一组参数值 def step_gradient(b_current, m_current, points, learningRate): b_gradient = 0 m_gradient = 0 N = float(len(points)) for i in range(0, len(points)): x = points[i, 0] y = points[i, 1] b_gradient += -(2/N) * (y - ((m_current * x) + b_current)) m_gradient += -(2/N) * x * (y - ((m_current * x) + b_current)) new_b = b_current - (learningRate * b_gradient) new_m = m_current - (learningRate * m_gradient) return [new_b, new_m] def gradient_descent_runner(points, starting_b, starting_m, learning_rate, num_iterations): b = starting_b m = starting_m for i in range(num_iterations): b, m = step_gradient(b, m, array(points), learning_rate) return [b, m] def run(): points = genfromtxt('/Users/cailei/Cai_Lei/AI/Testdata/GradientDescentData1.csv', delimiter=",") data = pd.read_csv('/Users/cailei/Cai_Lei/AI/Testdata/GradientDescentData1.csv',names=['x','y']) learning_rate = 0.0001 initial_b = 0 # initial y-intercept guess initial_m = 0 # initial slope guess num_iterations = 1000 print "Starting gradient descent at b = {0}, m = {1}, error = {2}".format(initial_b, initial_m, compute_error_for_line_given_points(initial_b, initial_m, points)) print "Running..." [b, m] = gradient_descent_runner(points, initial_b, initial_m, learning_rate, num_iterations) print "After {0} iterations b = {1}, m = {2}, error = {3}".format(num_iterations, b, m, compute_error_for_line_given_points(b, m, points)) plt.plot(data.x,data.y,'bo') plt.plot(data.x,data.x*m+b) plt.show() if __name__ == '__main__': run()
输出:


浙公网安备 33010602011771号