机器学习入门笔记(三):梯度下降算法具体实现(python)

0x01.大致实现思路:

  • 通过numpy做矩阵运算和产生训练数据集.
  • 再将训练集导入scipy的stats.linregress,利用其线性回归算法作结果比对验证.
  • 最后使用matplotlib作图,更直观的展示结果。

0x02.具体代码

#coding:utf-8
import numpy as np
from scipy import stats
import matplotlib.pyplot as plt


'''构造训练数据 h(x) = thata0 * x0 + thata1 * x'''
x = np.arange(0., 10., 0.2)
m = len(x)                          
x0 = np.full(m, 1.0)                 
train_data = np.vstack([x0, x]).T   # 通过矩阵变化得到测试集 [x0 x1]
y = 4 * x + 1 + np.random.randn(m)  # 通过随机增减一定值构造'标准'答案 h(x)=4*x+1+random_float(小于一,正负)

def Bgd(alpha, loops, epsilon):
    '''
    [批量梯度下降]
    alpha:步长, 
    loops:循环次数,
    epsilon:收敛精度
    '''
    count = 0 # loop的次数
    thata = np.random.randn(2) # 随机thata向量初始的值,也就是随机起点位置
    err = np.zeros(2)  # 上次thata的值,初始为0向量
    finish = 0 # 完成标志位

    while count < loops:
        count += 1
        # 所有训练数据的期望更新一次thata
        sum = np.zeros(2) # 初始化thata更新总和
        for i in xrange(m):
            cost = (np.dot(thata, train_data[i]) - y[i]) * train_data[i]
            sum += cost
        thata = thata - alpha * sum
        if np.linalg.norm(thata - err) < epsilon: # 判断是否收敛
            finish = 1
            break
        else:
            err = thata # 没有则将当前thata向量赋值给err,作为下次判断收敛的参数之一
    print u'Bgd结果:\tloop_counts: %d\tthata[%f, %f]' % (count, thata[0], thata[1])
    return thata

def Sgd(alpha, loops, epsilon):
    '''
    [增量梯度下降]
    alpha:步长, 
    loops:循环次数,
    epsilon:收敛精度
    '''
    count = 0 # loop的次数
    thata = np.random.randn(2) # 随机thata向量初始的值,也就是随机起点位置
    err = np.zeros(2)  # 上次thata的值,初始为0向量
    finish = 0 # 完成标志位

    while count < loops and finish == 0:
        count += 1
        # 每组训练数据都会更新thata
        for i in xrange(m):
            cost = (np.dot(thata, train_data[i]) - y[i]) * train_data[i]
            thata = thata - alpha * cost
            if np.linalg.norm(thata - err) < epsilon: # 判断是否收敛
                finish = 1
        else:
            err = thata # 没有则将当前thata向量赋值给err,作为下次判断收敛的参数之一
    print u'Sgd结果:\tloop_counts: %d\tthata[%f, %f]' % (count, thata[0], thata[1])
    return thata

if __name__ == '__main__':
    # thata = Sgd(alpha=0.001, loops=10000, epsilon=1e-4)
    thata = Bgd(alpha=0.0005, loops=10000, epsilon=1e-4)

    # 将训练数据导入stats的线性回归算法,以作验证
    slope, intercept, r_value, p_value, slope_std_error = stats.linregress(x, y)
    print u'Stats结果:\tintercept(截距):%s\tslope(斜率):%s' % (intercept, slope)
        
    plt.plot(x, y, 'k+')
    plt.plot(x, thata[1] * x + thata[0], 'r')
    plt.show()

0x03. 最终结果如下

输入的对比结果与stats算法结果基本一致:

更直观的作图结果:

其中移动的步长我设置为固定的,其实还可以继续优化.究竟为多少比较合适,却没有一个通用的标准,太长使用BGD容易产生震荡,太短时候达到收敛的时间会增长。因此,步长的选择是一门基于经验的艺术。

0x04. 参考链接

机器学习入门笔记(三):梯度下降算法具体实现(python)》有1个想法

  1. 你好,刚拜读了你的代码,想请问多元情况下,np.vstack()应该如何调整,才能使得cost循环中不出现错误?

发表评论

电子邮件地址不会被公开。 必填项已用*标注