梯度下降法

线性回归中参数解析式的求解涉及到矩阵的求逆,当特征矩阵数据量过大,求逆是一个很耗时的过程,我们可以使用先对特征矩阵做奇异值分解(SVD)后再求广义逆 ,但更多情况下,是使用梯度下降法绕过求逆的过程。

根据梯度反方向 J(θ)θ 是函数值下降最快的方向,随机初始化参数 θ ,让 θ 沿负梯度方向迭代,更新后的 θ 使损失函数 J(θ) 更小:

θ=θαJ(θ)θ

其中 α 是学习率,步长,表示每次向着 J(θ) 最陡峭的方向迈步的大小。这是梯度下降的基本思想。

以平方损失为例,参数 θj 的梯度方向 为:

J(θ)θj=(hθ(x)y)xj

批量梯度下降法(BGD)

批量梯度下降法(Batch Gradient Descent,简称BGD)是梯度下降法最原始的形式,具体思路是在更新每一参数时使用所有的样本来进行更新。BGD一定可以得到一个局部极值,在线性回归中可以得到全局最优解。

样本预测值为 hθ(xi) 的样本 (xi,yi) 的参数 θj 更新:

θj=θj+αmi=1m(yihθ(xi))xij

代码实现

for j in range(n):
        for i in range(m):
            diff += (y[i]- np.dot(theta,x[i])) * x[i][j]
        theta[j] = theta[j] + alpha *1/m* diff

这里写图片描述

随机梯度下降法(SGD)

批量梯度下降法更新一个参数的时候需要用到所有样本,所以训练速度会随着样本数量增加而变得非常缓慢,随机梯度(Stochastic Gradient Decent ,简称SGD)的解决办法就是通过单个样本的损失函数对参数求偏导来更新梯度。

参数更新:

θj=θj+α(yihθ(xi))xij

代码实现:

i = random.randint(0, m - 1)
diff = y[i] - np.dot(theta, x[i])
for j in range(n):
    theta[j] = theta[j] + alpha * diff * x[i][j]

这里写图片描述

比较图发现,SGD震荡下降,在极值点附近震荡,因为不需要用到所有样本进行更新参数,所以下降更快,但可能迭代次数会更多。
由于SGD是震荡下降,所以有可能在震荡下降过程中可能跳出次局部极值,找到全局极小值。SGD因为不需要用到所有的样本,所以更适合于做在线学习。

小批量梯度下降法(MBGD)

SGD的缺点也很明显,噪音较BGD要多,虽然整体往最优值方向移动,但每次迭代并非都向整体最优方向。

为了保证算法的训练速度,同时也保证参数训练的准确率,于是就有了小批量梯度下降法(Mini-batch Gradient Decent ,简称MBGD),抽取部分样本进行参数更新,视训练集规模人为给定抽取的样本个数,代码可参考BGD与SGD。

附录

①:若 A 为非奇异矩阵,则线性方程组Ax=b的解为

x=(ATA)1ATb

从方程的直观意义上可定义广义逆矩阵:
A+=(ATA)1AT

②:梯度方向
这里写图片描述

③:

# coding=utf-8
# !/usr/bin/python

import matplotlib.pyplot as plt
import matplotlib as mpl
import numpy as np
import warnings
import random

np.set_printoptions(suppress=True)
np.set_printoptions(precision=1)
warnings.filterwarnings("ignore")

# 构造数据 样本x 目标值y
N = 100
xNew = []
x = np.linspace(0, 6, N) + np.random.randn(N)
x = np.sort(x)
print(x)
z = [1 for i in range(len(x))]
y = 4 * x - 3 + np.random.randn(N)  # 通过一次函数加入随机扰动构造值
x1 = x
for i in range(len(x)):
    tempMat = [z[i], round(x[i], 2)]
    xNew.append(tempMat)
x = xNew

epsilon = 0.01
alpha = 0.05  # 学习率
error1 = 0  # 当前权重损失值
error0 = 0  # 上一次权重损失值
time = 0  # 迭代次数
errors = []  # 损失值数组
times = []

n = len(x[0])  # 变量个数 考虑截距项
m = len(x)  # 样本数
theta = [0 for i in range(n)]


while True:
    diff = 0
    # 随机梯度下降 begin
    # i = random.randint(0, m - 1)
    # diff = y[i] - np.dot(theta, x[i])
    # for j in range(n):
    #     theta[j] = theta[j] + alpha * diff * x[i][j]
        # 随机梯度下降 end
        #批量梯度下降 begin
    for j in range(n):
        for i in range(m):
            diff += (y[i]- np.dot(theta,x[i])) * x[i][j]
        theta[j] = theta[j] + alpha *1/m* diff
    #批量梯度下降 end
    error1 = 0
    for i in range(m):
        error1 += (y[i] - (np.dot(theta, x[i]))) ** 2 / 2  # 计算损失值

    errors.append(error1)
    times.append(time)
    time += 1

    if abs(error1 - error0) < epsilon:
        break
    else:
        error0 = error1

    if time == 100:  # 迭代到达100次停止
        break

# 画图
mpl.rcParams['font.sans-serif'] = [u'simHei']
mpl.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(12, 9))
plt.subplot(1, 2, 1)
plt.plot(times, errors, 'g-', linewidth=2)
plt.title(u'损失曲线', fontsize=18)
plt.xlabel(u'次数', fontsize=15)
plt.ylabel(u'损失值', fontsize=15)
plt.legend(loc='upper right')
plt.grid()
plt.subplot(1, 2, 2)
plt.plot(x1, y, 'bo', linewidth=2, label=u'原始数据')
x = np.arange(-2, 8)
y = theta[0] + theta[1] * x
plt.plot(x, y, 'r-', linewidth=2, label=u'回归模型')
plt.xlabel(u'X', fontsize=15)
plt.ylabel(u'Y', fontsize=15)
plt.title(u'回归拟合', fontsize=18)
plt.legend(loc='upper left')
plt.grid()
plt.show()
print('Done: theta0 : %f, theta1 : %f' % (theta[0], theta[1]))

git clone代码地址:https://code.csdn.net/snippets/2552953.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值