ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

Python神经网络学习(四)--机器学习--线性回归

2021-09-12 12:34:40  阅读:193  来源: 互联网

标签:直线 导数 误差 -- 梯度 学习 Python 求导 工资水平


前言

终于感觉我对这一章的理解比较深刻,并且也写出了像样的代码实现供大家参考,感觉自己可以写这篇文章,大家久等了。

线性回归

什么是线性回归?

线性回归常用于连续值的预测任务,最经典的例子就是:假设工资水平仅仅和工作时长有关,那么我们要找到一条直线,虽然这个直线不能穿过所有的样本点,但是能在误差尽量小的情况下给出一个预测,如下图所示:

而得到这个直线之后,我们就可以输入工作年限,然后通过计算得出一个大概的工资,虽然跟实际拿到的工资并不一定一致,但是也能给出一个大致的估计。

线性模型

很容易看出, 回归线是一条直线,直线的公式我们也都牢记于心:

y = a*x + b

 或者我们可以这样写:

y = a * x + b * 1

 这样的话,在本次例子中,根据图像,x 即工作年数,y 即工资水平。

目标就是:找到一条直线 y = ax+b ,尽可能准确的根据工作年数,预测工资水平。

分析

那么现在对这个公式进行分析,在此之前,我先提问一个问题:在上述的公式中,什么是已知的,什么是未知的?

计算这个线性回归模型的时候,是要针对已知样本的(输入,输出),通过计算得到目标直线,(输入,输出)即(工作年数,工资水平),目标直线的(斜率,截距)却不知道,要通过计算得到。所以,(x, y)是已知的,(a, b)是未知的。这个地方大家必须清楚。

现在我们弄明白了谁是已知,谁是未知,就该去确定怎么计算这两个未知数。

目标是找到一条尽可能准确的直线,尽可能准确就是对于所有的样本,误差要尽可能的小

假设,对于给定的数据,最理想,最好的直线是 y = a*x+b ,根据之前的思想:先初始化一条随机的直线,然后根据误差,慢慢修正参数,最终得到一条近似完美的参数作为最终参数。

随机一条初始的直线:h = \hat{a} * x + \hat{b} ,对于这个随机直线,输入一个工作年限,会得到一个不一样的工资水平h,这个工资水平h和真实的y是不一样的,有误差的.

那么, 单一的样本的误差就是:loss = h - y

当计算所有的样本的误差的时候,我们可以把各个样本的误差加起来,但这时候,正负的loss就会抵消,所以要消除其中的负号,消除负号有两种方法:

1. 绝对值。

2. 平方。

但是为了求导方便,这里选择方法2,取平方,即:(假设共 n 个样本)

Loss(a, b) = \sum_{i=i}^{n} (h_{i} - y_{i})^2 = \sum_{i=i}^{n}(a*x_{i}+b-y_{i})^2

这个里面的 a 和 b 是 h 中的 a 和 b。

再回顾一下我们的目标:找到一条尽可能准确的直线。也就是说,总和误差要达到最小,也就是说Loss(a, b) 要达到最小值。

此时问题转化为了二元函数求极值的问题,也就是说:找到Loss(a, b)的最小值。根据二元函数求极值的方法,我们需要分别对 a 和 b 求偏导数(此时 x 和 y 视为常数),求导结果如下:

\frac{\partial Loss}{\partial a} = 2\sum_{i=1}^{n}(a*x+b) * x ,\frac{\partial Loss}{\partial b} = 2\sum_{i=1}^{n}(a*x+b) * 1 = 2\sum_{i=1}^{n}(a*x+b)

求导之后,此时有两种方法:

1.最小二乘法

最小二乘法在求导后,另上面两个偏导数为0,联立解得 a 和 b 的值,然后直接计算就得到了这条直线,就是解方程的过程,这里就带过了。

2. 梯度下降法(推理过程仅帮助大家理解,未找专业人士求证,如果想要了解最正确的推理过程可以去百度百科或者去找课本。)

有人可能会不知道梯度什么意思,我这里给一个最浅显的理解方式,可能不太符合定义,但是帮助理解:

一元函数,求导之后有斜率,二元及以上函数,求导之后有梯度,可以理解为:梯度就是高维的斜率。如同:平面内是垂直,高维就是正交。

这里先暂停一下,线回顾一下一元方程:y = f(x) = x^2 + 2 ,那么这个 y = f(x) 是一个凹函数(如无特殊说明,这个文章中的凹凸性,按照2021考研数学中的定义,2021年9月12日。),那么它会在某处存在一个最小值,假设在x_{0} 的地方取得最小值,则这个地方导数为0。即:

f'(x_{0}) = 0。但是我们不看这个,看这个就是最小二乘法了,我们现在说梯度下降法,来看导数的定义:

f'(x_{0}) = \frac{f(x_{0} + \Delta x) - f(x_{0})}{\Delta x}

整理移项可得:f(x_{0}) = f(x_{0}+\Delta x) - \Delta x *f'(x_{0})

由于x_{0}处是极小值(最小值),我们的最终目标是求到x_{0},而x_{0}是未知的,但是x_{0}+\Delta x整个是一个已知的递推项,且\Delta x 无限趋近于0,那么我们可以对上式做一些小的修改:

f(x_{0}) = f(x_{0}+\Delta x) - \Delta x *f'(x_{0}+\Delta x)

那么我们可以得到一个递推公式:

f(x_{n+1}) = f(x_{n}) - \alpha f'(x_{n})

由于导函数f'(x_{n}) 是导数带入具体值,是一个常数,f(x_{n+1}) f(x_{n})是两个函数,进一步去掉函数,只看自变量和常数:

x_{n+1} = x_{n} - \alpha f'(x_{n})

即,给定一个初始的x,经过一定次数的迭代之后,就能慢慢的逼近最终的一个极小值的结果。

上面是一元方程中的问题,那么现在来到二元方程,帮助你们回顾:

误差的损失函数:Loss(a, b) = \sum_{i=i}^{n}(a*x_{i}+b-y_{i})^2

对于 a 的偏导数:\frac{\partial Loss}{\partial a} = 2\sum_{i=1}^{n}(a*x+b) * x

 对于 b 的偏导数:\frac{\partial Loss}{\partial b} = 2\sum_{i=1}^{n}(a*x+b)

 偏导数就是在某一个方向上的导数,对于每一个单独的 a 或者 b 都可以看作:Loss(a),Loss(b),那么可以接受一元方程中的扩展。

那么就得到了 a 和 b 的修正规则:

a_{n+1} = a_{n} - \alpha \frac{\partial Loss}{\partial a_{n}}b_{n+1} = b_{n} - \alpha \frac{\partial Loss}{\partial b_{n}}

前面的\alpha就可以认为是一个学习率。根据这两个公式,就能一步一步的求出目标的相对最优的直线参数了。

 代码实现

代码实现很简单,注释都在代码里,我感觉注释应该写清楚了。能简化的步骤我都给予了简化,希望大家能理解,不懂得欢迎评论区留言。

# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pyplot as plt

def main():
    """线性回归,梯度下降法"""
    # 随机编写一组点,使用numpy的数组可以进行加减乘的运算
    x = np.array([0.5, 0.7, 1.0, 1.5, 2.1, 2.3, 3.0, 3.3])
    y = np.array([5.0, 5.6, 5.3, 6.0, 7.0, 6.8, 9.1, 10.5])

    # 随机初始y = ax + b 的参数
    a, b = 1, 1
    times = 10000  # 迭代训练次数
    learning_rate = 0.001

    for i in range(times):  # 开始训练
        # 根据两个偏导数计算Loss/a的偏导数
        dloss_da = 2 * ((a * x + b - y)*x).sum()
        dloss_db = 2 * (a * x + b - y).sum()

        # 根据修正规则,修正参数
        a = a - learning_rate * dloss_da
        b = b - learning_rate * dloss_db

    # 得到最终的直线上x对应的点
    final_y = a * x + b
    # 画散点
    plt.scatter(x, y, label="test data point")
    plt.plot(x, final_y, label='final regression line')  # 训练完的回归线
    plt.legend()  # 将label贴到图片上
    plt.show()  # 展示这个图片

if __name__ == "__main__":
    main()

 最终的效果如图所示: 

 结束语

 这期距离上期时间间隔确实挺长,但是不会断更,因为我希望我能用最简洁的方式给大家说明白,如果我的解释哪些地方可能是错的,我会标注出,留给大家深入理解了并且数学知识足够了,再去自己探索(比如梯度下降算法的推导过程,实际情况肯定不是这样,但是这样我觉得是最容易理解的方式,需要的知识也仅仅是求导的知识而已)。

希望大家能学习到知识,这期就到这里,下期再见!欢迎评论区留言交流。

标签:直线,导数,误差,--,梯度,学习,Python,求导,工资水平
来源: https://blog.csdn.net/qq_38431572/article/details/120247790

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有