02 LinerRegression

问题引入:这就是个简单的线性回归函数的计算问题

现在我们已知一次函数 y = 4x + 9,bias为一个服从标准正态分布的随机随机数值。那么通过 y = 4x + 9 + bias,当我们给定一系列x值:x1,x2,...,xn值后,我们可以得到一组离散的点集(x1, y1),(x2, y2),...,(x3, y3)。很明显这些点一定是围绕着y = 4x + 9 这条直线波动的。

上面的思维是我们已知直线方程 y = 4x +9 而后推导出一组离散点集(x,y)。那我们如何反过来,即通过观测得到的离散点集推测出 y = 4x + 9 ?

我们假设这些离散点符合一次方程 y = wx + b,我们需要做的就是推测出w的最大可能值与b的最大可能值,这是两个未知数,两个变量

由上一篇文章得出,想要推导未知变量的值,有以下步骤:

  1. 假定变量的初始值 w0、b0
  2. 确定总损失函数的表达形式
  3. 将总的损失函数拆解,计算各自的梯度
  4. 通过各自梯度得到用于更新变量值的平均梯度

但本文是两个未知变量,上一篇文章只是一个未知变量,何解?

  • 初始值的假定很简单,随心而定即可
  • totaolLoss = \sum_{i=1}^{n}\left \{ \left ( w_0x_i+b_0 \right ) - y_i \right \}^2
  • baseLoss = \left \{ \left ( w_0x_i+b_0 \right ) - y_i \right \}^2,不同之处在于,此时不再只是求导,而是求偏导,即:\frac{\partial baseLoss}{\partial w}\frac{\partial baseLoss}{\partial b}
  • 然后计算出各自的平均偏导梯度,与学习率相乘,各自更新各自的变量互不打扰即可

思路依然确立,接下来就是代码实现的问题了。需要实现的功能如下:

  1. 获得围绕直线 y = 4x + 9波动的离散点集
  2. 计算total_loss值
  3. 计算平均偏导梯度值
  4. 设置学习率,配合平均偏导梯度值完成一次对参数值 w,b 的更新
  5. 重复上述步骤 n 次,n即为训练次数

完整实例代码:

'''
0. 主要目的是通过随机点集,预测其对应的线性回归方程
1. 生成围绕线性方程 y=4*x+9 波动的离散坐标点的点集
2. 通过梯度求解线性回归方程 y=w*x+b 中的w,b最可能的近似值
3. 希望预测的a, b值各自接近4 和 9
'''
def producePoints(num):
    '''
    :param num: 指定生成随机点的个数
    :return data: 返回随机点坐标的集合[(x1,y1), (x2,y2)...]
    '''
    import torch #不导入torch,numpy用不了我直接无语
    import numpy as np
    points_x = [i+np.random.rand() for i in range(1, num+1)] #np.random.rand(d0, d1)生成[0, 1)之间的随机浮点数或者N维浮点数组
    points_y = [4*points_x[i]+9+np.random.rand() for i in range(num)] #这样的出的y值并不满足y=4*x+9, 另外加的np.random.rand()相当于是噪声

    points = zip(points_x, points_y)
    return list(points)

def compute_error_for_given_points(w, b, points):
    '''
    通过给定当前预测的w, b值计算各个点预测值与真实值之间的平均误差
    :param w: 当前预测的w值
    :param b: 当前预测的b的值
    :param points: 当前获得离散坐标点的点集
    :return:  由预测的w , b值获取各个点预测Y值与真实Y值之间的误差平方和的平均值: double
    '''
    totalError = 0
    for point in points:
        x, y = point[0], point[1]
        totalError += (w*x+b - y) ** 2  #(w*x+b - y) ** 2代表当前的w,b取值在(x, y)这个点处预测的Y值即w*x+b与真实值Y值的即y之间的误差的平方
    return totalError/len(points) #对误差取平均

def step_gradient(current_w, current_b, points, learningRate):
    '''
    根据误差函数loss = (w*x+b - y) ** 2,计算w, b于每个点上的梯度情况,从而更新w, b值
    :param current_w: 当前预测的w值
    :param current_b: 当前预测的b值
    :param points: 当前给定的离散坐标点的点集
    :param learningRate: 当前给定的学习率
    :return: 新预测的w, b值:[w, b]
    '''
    gradient_w_total = 0 # loss函数在w方向上每个点的梯度之和
    gradient_b_total = 0
    for point in points:
        x, y = point[0], point[1]
        gradient_w_total += 2 * x * (current_w*x+current_b - y) #loss函数对w求导
        gradient_b_total += 2 * (current_w*x+current_b - y) #loss函数对b求导
    gradient_w_average = gradient_w_total / len(points) #loss函数在所有点梯度的平均值
    gradient_b_average = gradient_b_total / len(points)
    new_w = current_w - (gradient_w_average * learningRate) #计算新的w的值
    new_b = current_b - (gradient_b_average * learningRate)
    return [new_w, new_b]

def gradient_descent_runner(start_w, start_b, points, learningRate, iterations_num):
    '''
    给定初始的w, b,离散点点集, 学习率,迭代次数,不停的更新w, b值
    :param start_w: 起始的w值
    :param start_b: 起始的b值
    :param points: 离散点的点集
    :param learningRate: 学习率
    :param iterations_num: 梯度下降的次数
    :return: 经过指定迭代次数后得到的w, b的值
    '''
    current_w = start_w
    current_b = start_b
    for i in range(iterations_num):
        print("第{:>6d}次梯度迭代".format(i))
        current_w, current_b = step_gradient(current_w, current_b, points, learningRate)
    return [current_w, current_b]

if __name__ == '__main__':
    start_w = 0
    start_b = 0
    points = producePoints(100)
    learningRate = 0.0001
    iterations_num = 100000 #经过测试,这个训练步数得到的结果还行

    final_w, final_b = gradient_descent_runner(start_w, start_b, points, learningRate, iterations_num)
    final_error = compute_error_for_given_points(final_w, final_b, points)

    print("最终的w值为:{} 最终的b值为:{} 最终的平均误差为:{}".format(final_w, final_b, final_error))

运行结果:

猜你喜欢

转载自blog.csdn.net/qq_40923413/article/details/108182928
02
02-
今日推荐