美文网首页人工智能
梯度下降算法

梯度下降算法

作者: 小沛2016 | 来源:发表于2023-08-11 00:07 被阅读0次

梯度下降算法是众多人工智能算法的基石,它究竟有何特殊之处呢?

梯度下降的主要步骤

1.定义预测函数

首先,我们要确定一个小目标预测函数,这是机器学习的一个常见任务。通过学习算法,我们可以自动地发现数据背后的规律,并不断改进模型以做出更准确的预测。为了更好地理解,我们举一个简单的例子:在二维直角坐标系中,我们有一组样本点,横纵坐标分别代表一组有因果关系的变量,比如房房价和面积。常识告诉我们,它们的分布应该是正比例的,也就是一条通过原点的直线,即 y = w * x。我们的任务是设计一个算法,使机器能够拟合这些数据,从而帮助我们计算出直线的参数 w。一个简单的方法是,先随机选取一条通过原点的直线,然后计算所有样本点与该直线的偏离程度。接着,我们可以根据误差的大小来调整直线的斜率 w。总之,在这个问题中,直线 y = w * x 就是我们所谓的预测函数。

2.计算梯度

其次,我们需要量化数据的偏离程度,也就是所谓的误差。在这方面,最常见的方法是使用 均方误差,其含义是将误差的平方和求取平均值。设第一个点 为p1。这个点的坐标为 x1 和 y1,相应的误差记为 e1,以此类推,最终我们可以得到

image.png image.png image.png image.png

这个就是误差函数,意思是学习所需要付出的代价。
因为二次项系数 a 是 x 的平方和大于 0,所以这个函数所对应的图像是一个开口向上的抛物线。当 w 的取值发生变化时,相应的直线会绕着原点进行旋转。在抛物线图像中,这相当于取值点沿着曲线的运动轨迹。通过定义预测函数,并根据误差公式推导出代价函数,我们成功地将样本点的拟合过程映射到了一个函数图像上。

image.png

在找到代价函数的图像后,接下来的问题是,我们应该朝着哪个方向前进呢?我们的目标是找到最贴近训练数据分布的直线,也就是寻找使误差代价最小的参数 w。在代价函数图像上,这相当于找到了代价函数的最低点。寻找这一最低点的过程,就是梯度下降所要执行的任务。设想在曲线上的任意一点作为起始点,根据我们的经验,选择朝着陡峭程度最大的方向前进,以更快地接近最低点。这种陡峭程度就是梯度,也就是导数或斜率。

3.设置学习率

接下来,我们需要考虑学习率。一旦方向确定,就需要前进了,但是步子应该迈得多大呢?
以往的研究结论告诉我们,如果步子太大,会导致左右反复跳动;而如果步子太小,则会在计算和时间方面付出较大的代价。

4.重复第2、第3步直到找到最低点

这个流程就是所谓的梯度下降算法

为什么不直接求解?

也许你在这里会产生一些疑问,既然我们已经知道代价函数是一个一元二次抛物线,那为什么不使用数学方法直接求解呢?
这是因为在实际问题中,训练样本的分布千差万别,而代价函数的形态也可能变化多端。绝大多数的时候它不仅仅是一条简单的抛物线。
比如我们上面的例子,房价不仅仅和面积有关,还有地段、朝向、周围配套设施有关,甚至是当时的政策或销售的颜值都有关。
代价函数可能会在10维、甚至更高维度中变化。这使得将其可视化展示出来变得十分困难。但无论维度有多高,我们都可以通过梯度下降法来寻找使误差最小化的点。

示例代码

# -*- coding: utf-8 -*-  
import numpy as np

def create_data():
  data_array = np.array([
    [1.0, 15.0],
    [2.0, 18.0],
    [3.0, 21.0], 
    [4.0, 24.0],
    [5.0, 27.0], 
    [6.0, 30.0],
    [7.0, 33.0], 
    [8.0, 36.0],
    [9.0, 39.0], 
    [10.0, 42.0],
    [11.0, 45.0], 
    [12.0, 48.0],
    [13.0, 51.0], 
    [14.0, 54.0],
    [15.0, 57.0]
  ])

  x_value_array = data_array[:, 0:1]
  y_value_array = data_array[:, 1:2]

  return x_value_array, y_value_array

class my_train_class:
    # 初始化方法
    def __init__(self, k, b, learning_rate = 0.01, show_log = False) -> None:
       self.k = k
       self.b = b
       self.learning_rate = learning_rate
       self.show_log = show_log
    
    # 计算预测值
    def get_calculat_value_array(self, x_value_array):
        calculate_value_array = np.dot(x_value_array, self.k) + self.b

        if self.show_log:
            print(f"x_value_array == {x_value_array}")
            print(f"calculate_value_array == {calculate_value_array}")

        return calculate_value_array
    
    # 计算均值平方
    def get_MSE(self, reality_value_array, calculate_value_array):
        
        if len(reality_value_array) != len(calculate_value_array):
            raise Exception('2个数组个数不一致')
        
        difference = reality_value_array - calculate_value_array
        mse = np.mean(difference * difference)
        
        if self.show_log:
            print(f"MSE == {mse}")
        
        return mse
            
    # 计算梯度
    def gradient(self, x_value_array, reality_value_array, calculate_value_array):
        
        if len(reality_value_array) != len(calculate_value_array):
            raise Exception('reality_value_array 和 calculate_value_array 个数不一致')

        dk = np.mean((calculate_value_array - reality_value_array) * x_value_array)
        db = np.mean(calculate_value_array - reality_value_array)
            
        if self.show_log:
            print(f"dk == {dk}")
            print(f"db == {db}")
            
        return dk, db

    # 更新k和b的值
    def update_param(self, dk, db):
      self.k = self.k - self.learning_rate * dk
      self.b = self.b - self.learning_rate * db
        
    # 训练
    def train(self, x_value_array, y_value_array, trainCount = 2000):
        
        for i in range(trainCount):
            # 获取预测值的数组
            calculate_array = self.get_calculat_value_array(x_value_array)
            
            # 计算均值平方
            mse = self.get_MSE(y_value_array, calculate_array)
            
            # 计算梯度
            dk, db = self.gradient(x_value_array, y_value_array, calculate_array)
            
            # 更新k和b的值
            self.update_param(dk, db)
            
            if(i % 1000 == 0):
                print('第{}次mse:'.format(i+1), mse)
                print('第{}次梯度值:'.format(i+1), dk, db)
                print('第{}次k和b的值:'.format(i+1), self.k, self.b, "\n")
                
            
        print(f"训练结束, k == {self.k},  b == {self.b}")
    

x_value_array, y_value_array = create_data()

train_obj = my_train_class(1, 1, show_log = False)

train_obj.train(x_value_array, y_value_array, len(x_value_array) * 600)

结果

image.png

相关文章

网友评论

    本文标题:梯度下降算法

    本文链接:https://www.haomeiwen.com/subject/jmozpdtx.html