机器学习之梯度下降算法

梯度下降算法就是函数沿着梯度的方向下降的算法,这里有两个概念,第一是梯度,第二是倒数。

1.首先什么是导数:相信学过高等数学的人都知道,什么是倒数,倒数也就是描述函数在这一点附近的变化率,也可以认为函数在某一点的倒数就是代表曲线在这一点的斜率。

2.梯度:高等数学里面的梯度是一个向量,表示某一函数在该点处的方向倒数沿着该方向取的最大值,也就是该点沿着该方向变化最快,变化率最大。当函数为一维函数时,梯度其实也就是倒数。

函数斜率

二维图像梯度

梯度下降算法:

梯度方法有梯度下降算法和梯度上升算法,所谓梯度下降算法也就是数据沿着当前梯度的负方向进行搜索,因为该方向为当前下降最快的方向。我们可以更好的找到函数的最小值。这种方法也是最快下降法。当快要接近最小值时,函数的变化量会非常小。公式如下:

梯度下降算法公式

上式中α被称为步长或者学习率,表示自变量θ/x每次变化的大小。当目标函数的数值变数非常小时,或者达到迭代次数时,就会结束循环。

梯度下降算法这里有几个问题,就是在负梯度方向作为变化的方向,有可能导致的求解的值是局部最优解而不是全局最优解。所以在运用时需要注意。

  • 学习率的选择:学习率过大和过小都会影响函数收敛的过程。当过大时有可能,会使函数跳过最优解,当学习率过小时,就会影响函数收敛的速度,会使它收敛过慢
  • 算法初始参数的选择:初始值不同有可能获取的全局最小值也可能不同,因为梯度下降算法求的是局部最优解。一般情况下还是需要多选几个初始值进行运算,并最终返回损失函数最小情况下的结果值。
  •  

梯度下降算法分为几种:

  1. 批量梯度下降算法: 
    BGD 使用所有样本在当前点的梯度值来对变量进行更新操作。
  2. 随机梯度下降法: 
    SGD 在更新变量的时候,选取一个样本的梯度值来更新参数。
  3. 小批量梯度下降法:集合BGD和SGD的特性,从原始数据中选取n个样本来更新参数。

批量梯度下降

随机梯度下降

小批量梯度下降

1

1

下面有一个随机梯度下降的例子:

目标函数:
   y = x**2 + b * x + c
  需求:求解最小值对应的x和y
  数据:
  b: 服从均值为-1,方差为10的随机数
  c:服从均值为0,方差为1的随机数
假定a、b、c这样的数据组合总共10、100、10w、100w条数据,求解在现在的数据情况下,目标函数的取最小值的时候,x和y分别对应多少?

# -- encoding:utf-8 --
"""
Create by ibf on 2018/6/21
"""

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

# 设置字符集,防止中文乱码
mpl.rcParams['font.sans-serif'] = [u'simHei']
mpl.rcParams['axes.unicode_minus'] = False

# 1. 随机数据产生
# 给定随机数种子:当程序多次运行的时候,可以保证每次运行时候随机的数据都是一样的
np.random.seed(28)
n = 100000
b_values = np.random.normal(loc=-1.0, scale=10.0, size=n)
c_values = np.random.normal(loc=0.0, scale=1.0, size=n)


# # 随机数据可视化查看
plt.figure(facecolor='w')
plt.subplot(1, 2, 1)
plt.hist(b_values, 1000, color='#FF0000')
plt.subplot(1, 2, 2)
plt.hist(c_values, 1000, color='#00FF00')
plt.suptitle(u'随机数据可视化', fontsize=22)
plt.show()

def calc_min_value_with_one_sample(b_values, c_values, max_iter=1000, tol=0.00001, alpha=0.01):
    """
    计算最小值时候对应的x和y的值
    :param b_values: 样本对应的b值
    :param c_values: 样本对应的c值
    :param max_iter: 最大迭代次数
    :param tol: 当变量小于该值的时候收敛
    :param alpha: 梯度下降学习率
    :return:
    """

    def f(x, b, c):
        """
        原始函数
        :param x:
        :param b:
        :param c:
        :return:
        """
        return x ** 2 + b * x + c

    def h(x, b, c):
        """
        原始函数对应的导函数
        :param x:
        :param b:
        :param c:
        :return:
        """
        return 2 * x + b

        # 定义变量

    step_channge = 1.0 + tol
    step = 0

    # 获取第一个样本
    b = b_values[0]
    c = c_values[0]

    # 给定一个初始的x值
    current_x = np.random.randint(low=-10, high=10)
    current_y = f(current_x, b, c)

    print("当前参数为:")
    print("b={}".format(b))
    print("c={}".format(c))

    # 开始迭代循环
    while step_channge > tol and step < max_iter:
        # 1. 计算梯度值
        current_d_f = h(current_x, b, c)
        # 2. 更新参数
        current_x = current_x - alpha * current_d_f
        # 3. 计算更新x之后的y值
        tmp_y = f(current_x, b, c)
        # 4. 记录y的变换大小、更新迭代次数、更新当前的y值
        step_channge = np.abs(current_y - tmp_y)
        step += 1
        current_y = tmp_y
    print("最终更新的次数:{}, 最终的变化率:{}".format(step, step_channge))
    print("最终结果为:{}---->{}".format(current_x, current_y))


def calc_min_value_with_ten_sample(n, b_values, c_values, max_iter=1000, tol=0.00001, alpha=0.01):
    """
    计算最小值时候对应的x和y的值
    :param n: 样本数量
    :param b_values: 样本对应的b值
    :param c_values: 样本对应的c值
    :param max_iter: 最大迭代次数
    :param tol: 当变量小于该值的时候收敛
    :param alpha: 梯度下降学习率
    :return:
    """
    # 要求n必须等于10
    assert n == 10 and len(b_values) == n and len(c_values) == n

    def f(x, b_values, c_values):
        """
        原始函数
        :param x:
        :param b_values:
        :param c_values:
        :return:
        """
        sample_1 = x ** 2 + b_values[0] * x + c_values[0]
        sample_2 = x ** 2 + b_values[1] * x + c_values[1]
        sample_3 = x ** 2 + b_values[2] * x + c_values[2]
        sample_4 = x ** 2 + b_values[3] * x + c_values[3]
        sample_5 = x ** 2 + b_values[4] * x + c_values[4]
        sample_6 = x ** 2 + b_values[5] * x + c_values[5]
        sample_7 = x ** 2 + b_values[6] * x + c_values[6]
        sample_8 = x ** 2 + b_values[7] * x + c_values[7]
        sample_9 = x ** 2 + b_values[8] * x + c_values[8]
        sample_10 = x ** 2 + b_values[9] * x + c_values[9]
        return sample_1 + sample_2 + sample_3 + sample_4 + sample_5 + sample_6 + sample_7 + sample_8 + sample_9 + sample_10

    def h(x, b_values, c_values):
        """
        原始函数对应的导函数
        :param x:
        :param b_values:
        :param c_values:
        :return:
        """
        sample_1 = x * 2 + b_values[0]
        sample_2 = x * 2 + b_values[1]
        sample_3 = x * 2 + b_values[2]
        sample_4 = x * 2 + b_values[3]
        sample_5 = x * 2 + b_values[4]
        sample_6 = x * 2 + b_values[5]
        sample_7 = x * 2 + b_values[6]
        sample_8 = x * 2 + b_values[7]
        sample_9 = x * 2 + b_values[8]
        sample_10 = x * 2 + b_values[9]
        return sample_1 + sample_2 + sample_3 + sample_4 + sample_5 + sample_6 + sample_7 + sample_8 + sample_9 + sample_10

    # 定义变量
    step_channge = 1.0 + tol
    step = 0

    # 给定一个初始的x值
    current_x = np.random.randint(low=-10, high=10)
    current_y = f(current_x, b_values, c_values)

    print("当前参数为:")
    print("b_values={},b的均值为:{}".format(b_values, np.mean(b_values)))
    print("c_values={},c的均值为:{}".format(c_values, np.mean(c_values)))

    # 开始迭代循环
    while step_channge > tol and step < max_iter:
        # 1. 计算梯度值
        current_d_f = h(current_x, b_values, c_values)
        # 2. 更新参数
        current_x = current_x - alpha * current_d_f
        # 3. 计算更新x之后的y值
        tmp_y = f(current_x, b_values, c_values)
        # 4. 记录y的变换大小、更新迭代次数、更新当前的y值
        step_channge = np.abs(current_y - tmp_y)
        step += 1
        current_y = tmp_y
    print("最终更新的次数:{}, 最终的变化率:{}".format(step, step_channge))
    print("最终结果为:{}---->{}".format(current_x, current_y))


def calc_min_value_with_n_sample(n, b_values, c_values, max_iter=1000, tol=0.00001, alpha=0.01):
    """
    计算最小值时候对应的x和y的值
    :param n: 样本数量
    :param b_values: 样本对应的b值
    :param c_values: 样本对应的c值
    :param max_iter: 最大迭代次数
    :param tol: 当变量小于该值的时候收敛
    :param alpha: 梯度下降学习率
    :return:
    """

    def f1(x, b, c):
        return x ** 2 + b * x + c

    def f(x, b_values, c_values):
        """
        原始函数
        :param x:
        :param b_values:
        :param c_values:
        :return:
        """
        result = 0
        for b, c in zip(b_values, c_values):
            # 遍历所有b和c的组合,这里求均值(防止数据量太大,计算困难)
            result += f1(x, b, c) / n
        return result

    def h1(x, b, c):
        return x * 2 + b

    def h(x, b_values, c_values):
        """
        原始函数对应的导函数
        :param x:
        :param b_values:
        :param c_values:
        :return:
        """
        result = 0
        for b, c in zip(b_values, c_values):
            # 遍历求解每个b、c组合对应的梯度值,这里求均值(防止数据量太大,计算困难)
            result += h1(x, b, c) / n
        return result

    # 定义变量
    step_channge = 1.0 + tol
    step = 0

    # 给定一个初始的x值
    current_x = np.random.randint(low=-10, high=10)
    current_y = f(current_x, b_values, c_values)

    print("当前参数为:")
    print("b_values={},b的均值为:{}".format(b_values, np.mean(b_values)))
    print("c_values={},c的均值为:{}".format(c_values, np.mean(c_values)))

    # 开始迭代循环
    while step_channge > tol and step < max_iter:
        # 1. 计算梯度值
        current_d_f = h(current_x, b_values, c_values)
        # 2. 更新参数
        current_x = current_x - alpha * current_d_f
        # 3. 计算更新x之后的y值
        tmp_y = f(current_x, b_values, c_values)
        # 4. 记录y的变换大小、更新迭代次数、更新当前的y值
        step_channge = np.abs(current_y - tmp_y)
        step += 1
        current_y = tmp_y
    print("最终更新的次数:{}, 最终的变化率:{}".format(step, step_channge))
    print("最终结果为:{}---->{}".format(current_x, current_y))


# print("*" * 50)
# calc_min_value_with_one_sample(b_values, c_values)
# print("*" * 50)
# calc_min_value_with_ten_sample(n, b_values, c_values)
print("*" * 50)
calc_min_value_with_n_sample(n, b_values, c_values)

分类: 机器学习

标签:机器学习梯度下降

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值