机器学习入门(二)半小时入门线性回归

为什么要学习机器学习?

      机器学习(maching learing)总是给人带来一种神秘的面纱,一是这个名字让人难免联想起人工智能。二是因为它过于强大和奇妙的应用。如人脸识别、ai换脸、语音助手、无人驾驶等。这些神奇而令人叹为观止的技术无疑让机器学习更显得更加“高大上”。但就因为这种高大上导致很多软件开发的同学看待这门技术,也觉得其像宇宙深处一样神秘且多变。
机器学习入门(二)半小时入门线性回归_第1张图片

      我相信很多同学在学习这门技术时,经常会望而却步。一是这门技术确实不是那么容易的入门,二是这门技术的从业者的门槛相对较高,一有挑战,二有顾虑所以很多初学者在学习这门技术时都会变成从入门到放弃。

      其实我们不妨简单一点,这是一个当下应用较多的技术,而且未来的应用也会越加广泛。不妨把他作为一本武功秘籍,学学也无妨。如果是从事机器学习、ai相关的工作那自然是好。再退一步来说,这些算法在自然界中有很多的实例。就算不从事相关的工作,生活中用到岂不美妙。正如上个世纪八九十年代,一个业余会修电器的工人常人被邻里称赞。而当下信息爆炸的时代,掌握机器学习将来的应用可能丝毫不亚于以前的"修电"。
      而线性回归作为机器学习最简单、又最基础且强大的算法,在生活中还是能得到很多应用的。是否学习机器学习,不妨花半个小时学完这篇文章,再做考量。

这个算法需要什么样的知识储备

  1. python基本语法
  2. numpy、skicent-learn库基本接口调用
  3. 基本数学知识

      这篇文章是以入门为主,简单的会列出线性回归的推导过程,但至少体现其思想,如对数学掌握不是很牢靠的同学,也丝毫不影响使用。

简单线性回归

什么是线性回归?

      生活中很多事物都是存在线性关系的,比如丈夫的收入与妻子颜值、电视的尺寸和价格、up主的粉丝数与视频播放量等等。简单的来说就是就是成一种直线的趋势,如图1.1所示:
机器学习入门(二)半小时入门线性回归_第2张图片

图1.1 某一线城市java程序员薪资与年限分布图

      从图中不难发现,java程序员的薪资跟工作年限大概是呈一种线性的关系。简单来说,就是java程序员的工资随着工作年限的增加而增加,而这种增加并不像指数式的喷发那么夸张。那么我们就想,能不能寻找一条直线,最大程度的拟合样本特征与样本输出标记之间的关系呢。

      就像是中学的数学应用题,用一条光滑的曲线均匀的穿过点的两边。得到如下图所示的一根直线,这就是整个线性回归的过程。但是这里面的值不再是中学数学中所说的自变量跟因变量。而是特征与输出标记,比如图中的程序员工作年限就是特征,而其的薪资则是输出标记。这里面的特征只有一个,而实际上可能有很多个。而这种只有一个特征的回归我们又称为简单线性回归。

线性回归模型如何建立

      图中的样本的个数,我们设为n个。而图中的直线的表达式我们也知道,是y=ax+b。而我们只要把这条曲线的斜率a跟截距b算出来。对于第i个样本,他的特征值值为x(i) ,他的标记值则为y(i)。
      而当我们求出这条直线的截图和斜率时,我们可以把x(i)代入得到一个值,我们把其称为 y ^ \hat{y} y^(i) ,这个值就代表的是样本i的预测值。这个预测值与真实的值会存在差距,我们希望的预测会比较准。而这个比较准就是希望 y ^ \hat{y} y^(i) 与y(i)(样本实际值)的差距尽量小。也就是使 ( y ( i ) − y ^ ( i ) ) 2 (y^{(i)}-\hat{y}^{(i)})^{2} (y(i)y^(i))2尽量小,那么考虑到所有的样本,则得出:
∑ i = 1 n ( y ( i ) − y ^ ( i ) ) 2 \sum_{i=1}^n (y^{(i)}-\hat{y}^{(i)})^{2} i=1n(y(i)y^(i))2
      所以我们应该找的一条直线使得上述式子的值最小。而同样不难得出 y ^ ( i ) = a x i + b \hat{y}^{(i)}=ax^{i}+b y^(i)=axi+b,则得到:
∑ i = 1 n ( y ( i ) − a x ( i ) − b ) 2 \sum_{i=1}^n (y^{(i)}-ax^{(i)}-b)^{2} i=1n(y(i)ax(i)b)2
      而简单线性回归就是求出a、b使得上述的值尽可能的小。而所谓的线性回归建模的过程就是找到a、b的过程。而上述的式子又称为损失函数(loss function),可以简写为cost(a,b)。其实机器学习的建模就是优化损失函数,获得机器学习模型的过程。

推导出最优解

其实推导出最优解就是求cost(a,b)的最小值,而求最小值也就是极值最直接的办法就是求导,即得到
∂ c o s ( a , b ) ∂ a = 0 {∂cos(a,b)\over∂a} =0 acos(a,b)=0
∂ c o s ( a , b ) ∂ b = 0 {∂cos(a,b)\over∂b} =0 bcos(a,b)=0
      先对b求偏导使其为0
∑ i = 1 n ( y ( i ) − a x ( i ) − b ) = 0 \sum_{i=1}^n (y^{(i)}-ax^{(i)}-b)=0 i=1n(y(i)ax(i)b)=0
易得出
∑ i = 1 n y ( i ) − a ∑ i = 1 n x ( i ) − ∑ i = 1 n b = 0 \sum_{i=1}^ny^{(i)} -a\sum_{i=1}^nx^{(i)}-\sum_{i=1}^nb=0 i=1ny(i)ai=1nx(i)i=1nb=0
n b = ∑ i = 1 n y ( i ) − a ∑ i = 1 n x ( i ) nb = \sum_{i=1}^ny^{(i)} -a\sum_{i=1}^nx^{(i)} nb=i=1ny(i)ai=1nx(i)
      求出 b = y ( 均 ) − a x ( 均 ) b=y(均)-ax(均) b=y()ax()
      这样b的值就求出来了,那么继续对a求偏导使其为0。
∑ i = 1 n ( y ( i ) − a x ( i ) − b ) x ( i ) = 0 \sum_{i=1}^n(y^{(i)}-ax^{(i)}-b)x^{(i)}=0 i=1n(y(i)ax(i)b)x(i)=0
      再把b的值代入得:
∑ i = 1 n ( y ( i ) − a x ( i ) − y ( 均 ) + a x ( 均 ) ) x ( i ) = 0 \sum_{i=1}^n(y^{(i)}-ax^{(i)}-y(均)+ax(均))x^{(i)}=0 i=1n(y(i)ax(i)y()+ax())x(i)=0
不难得出
∑ i = 1 n ( x ( i ) y ( i ) − x ( i ) y ( 均 ) ) − ∑ i = 1 n ( a ( x ( i ) ) 2 − a x ( 均 ) x ( i ) ) = 0 \sum_{i=1}^n(x^{(i)}y^{(i)}-x^{(i)}y(均))-\sum_{i=1}^n(a(x^{(i)})^{2}-ax(均)x^{(i)})=0 i=1n(x(i)y(i)x(i)y())i=1n(a(x(i))2ax()x(i))=0
这样就得到了a的表达式:
a = ∑ i = 1 n ( x ( i ) y ( i ) − x ( i ) y ( 均 ) ) ∑ i = 1 n ( ( x ( i ) ) 2 − x ( 均 ) x ( i ) ) a={\sum_{i=1}^n(x^{(i)}y^{(i)}-x^{(i)}y(均))\over\sum_{i=1}^n((x^{(i)})^{2}-x(均)x^{(i)})} a=i=1n((x(i))2x()x(i))i=1n(x(i)y(i)x(i)y())
再简化
∑ i = 1 n ( x ( i ) − x ( 均 ) ) ( y ( i ) − y ( 均 ) ) ∑ i = 1 n ( x ( i ) − x ( 均 ) ) 2 {\sum_{i=1}^n(x^{(i)}-x(均))(y^{(i)}-y(均))\over\sum_{i=1}^{n}(x^{(i)}-x(均))^{2}} i=1n(x(i)x())2i=1n(x(i)x())(y(i)y())

具体实现

import numpy as np

class SimpleLinearRegression:
    def __init__(self):
        self.a_ = None
        self.b_ = None

    def fit(self, x_train, y_train):
        """根据训练集训练模型"""
        x_mean = np.mean(x_train)
        y_mean = np.mean(y_train)
        num = 0.0
        d = 0.0
        for x,y in zip(x_train,y_train):
            num += (x-x_mean) * (y -y_mean)
            d += (x - x_mean) ** 2
        self.a_ = num / d
        self.b_ = y_mean - self.a_ * x_mean
        return self

    def predict(self,x_predict):

        return np.array([self._predict(x) for x in x_predict])

    def _predict(self, x_single):
        return self.a_ * x_single + self.b_
    
    def __repr__(self):
       return "简单线性回归"

      我们可以看到,如果b的值比较直接,而a的值则需要通过for循环遍历相加,这种效率在计算大数据集上的效率可想而知,所以有另一种向量化的运算,这里不一一推导,其实也只有fit函数的改动

import numpy as np

class SimpleLinearRegression:
    def __init__(self):
        self.a_ = None
        self.b_ = None

    def fit(self, x_train, y_train):
        """根据训练集训练模型"""
        x_mean = np.mean(x_train)
        y_mean = np.mean(y_train)
        num = (x_train - x_mean).dot(y_train-y_mean)
        d = (x_train-x_mean).dot(x_train-x_mean)

        self.a_ = num / d
        self.b_ = y_mean - self.a_ * x_mean
        return self

    def predict(self,x_predict):

        return np.array([self._predict(x) for x in x_predict])

    def _predict(self, x_single):
        return self.a_ * x_single + self.b_

    def __repr__(self):
        return "简单线性回归"

验证模型好坏

      每一种机器学习模型都会有好有坏,我们来验证模型的好坏,就是拿测试数据对模型进行验证。所以衡量模型好坏的参数极其重要,而对于线性回归算法,则有以下几类衡量方法。

  1. 均方根误差 RMSE (Root Mean Squared Error)
    1 n ∑ i = 1 n ( y ( i ) − y ^ ( i ) ) 2 \sqrt{{1\over n}\sum_{i=1}^n(y^{(i)}-\hat{y}^{(i)})^{2}} n1i=1n(y(i)y^(i))2
  2. 平均绝对误差 MAE (Mean Absolute Error)
    1 n ∑ i = 1 n ∣ y ( i ) − y ^ ( i ) ∣ {1\over n}\sum_{i=1}^{n}|{y^{(i)-\hat{y}^{(i)}}|} n1i=1ny(i)y^(i)

      不难看出这两种算法再验证模型好坏的时候都能达到不错的效果,但是这个值是无限大的,就是控制不了其的大小,这样在同类型的时候或者同一种模型可以比较模型的好坏,这样如果不同的模型计算不同类型的好坏如何比较呢。所以在线性回归中使用最广的是另一种验证方式。

  1. R Squared = R 2 R^{2} R2 R 2 = 1 − ∑ i ( y ^ ( i ) − y ( i ) ) 2 ∑ i ( y ( 均 ) − y ( i ) ) 2 R^{2}=1-{\sum_{i}(\hat{y}^{(i)}-y^{(i)})^{2}\over\sum_{i}(y(均)-y^{(i)})^{2}} R2=1i(y()y(i))2i(y^(i)y(i))2
    R 2 R^{2} R2越大越好, R 2 R^{2} R2<0 则说明不存在任何线性关系。
    由于该算法在sklearn已经封装,故在此不再叙述,具体的引用代码如下所示。
import numpy as np
from sklearn.metrics import r2_score

r2_score(y_true=np.array([]),y_pred=np.array([]))

多元线性回归

      多元线性回归,由于牵扯到多个维度,其函数表达式为:
y ^ ( i ) = θ 0 x 0 ( i ) + θ 1 x 1 ( i ) + θ 2 x 2 ( i ) + θ n x n ( i ) \hat{y}^{(i)}=\theta_{0}x^{(i)}_{0}+\theta_{1}x^{(i)}_{1}+\theta_{2}x^{(i)}_{2}+\theta_{n}x^{(i)}_{n} y^(i)=θ0x0(i)+θ1x1(i)+θ2x2(i)+θnxn(i)
      同样,也可对其进行简写。
y ^ = x ⋅ θ T \hat{y}=x\cdot\theta^{T} y^=xθT
θ = ( θ 1 , θ 2 , θ 3 , θ n ) \theta= (\theta_{1},\theta_{2},\theta_{3},\theta_{n} ) θ=(θ1,θ2,θ3,θn)
      同样求
∑ i = 1 n ( y ( i ) − y ^ ( i ) ) 2 \sum_{i=1}^n (y^{(i)}-\hat{y}^{(i)})^{2} i=1n(y(i)y^(i))2
      则得出:
( y − x b ⋅ θ ) T ( y − x b ⋅ θ ) (y-x_b\cdot\theta)^{T}(y-x_b\cdot\theta) (yxbθ)T(yxbθ)
      而我们的目标就是使上述式子尽可能小。
      这个推导结果什么复杂,在这里就不细加推敲
θ = ( X b T X b ) − 1 X b T y \theta=(X^{T}_bX_b)^{-1}X^{T}_by θ=(XbTXb)1XbTy

你可能感兴趣的:(机器学习入门(二)半小时入门线性回归)