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()
Mini-batch梯度下降

运行结果:

 

还有参考了一些博主的博客,有一些可用代码:

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()
大佬

输出:

 

posted @ 2018-09-03 12:11  ivanthor  阅读(431)  评论(0)    收藏  举报