0x00 前言

线性回归模型看起来非常简单,简单到让人怀疑其是否有研究价值以及使用价值。但实际上,线性回归模型可以说是最重要的数学模型之一,很多模型都是建立在它的基础之上,可以被称为是“模型之母”。

0x01 简单线性回归

1.1 什么是简单线性回归

之前我们介绍的kNN算法属于分类(Classification),即label为离散的类别型(categorical variable),如:颜色类别、手机品牌、是否患病等。
而简单线性回归是属于回归(regression),即label为连续数值型(continuous numerical variable),如:房价、股票价格、降雨量等。

那么什么是简单线性回归?
所谓简单,是指只有一个样本特征,即只有一个自变量;所谓线性,是指方程是线性的;所谓回归,是指用方程来模拟变量之间是如何关联的。
简单线性回归,其思想简单,实现容易(与其背后强大的数学性质相关。同时也是许多强大的非线性模型(多项式回归、逻辑回归、SVM)的基础。并且其结果具有很好的可解释性。

1.2 一种基本推导思路

我们所谓的建模过程,其实就是找到一个模型,最大程度的拟合我们的数据。 在简单线回归问题中,模型就是我们的直线方程:y = ax + b 。
要想最大的拟合数据,本质上就是找到没有拟合的部分,也就是损失的部分尽量小,就是损失函数(loss function)(也有算法是衡量拟合的程度,称函数为效用函数(utility function)):
image.png
因此,推导思路为:

  1. 通过分析问题,确定问题的损失函数或者效用函数;
  2. 然后通过最优化损失函数或者效用函数,获得机器学习的模型

近乎所有参数学习算法都是这样的套路,区别是模型不同,建立的目标函数不同,优化的方式也不同。
回到简单线性回归问题,目标:
已知训练数据样本、 ,找到和的值,使 image.png 尽可能小
这是一个典型的最小二乘法问题(最小化误差的平方)
通过最小二乘法可以求出a、b的表达式:

image.png

0x02 最小二乘法

2.1 由损失函数引出一堆“风险”

2.1.1 损失函数

在机器学习中,所有的算法模型其实都依赖于最小化或最大化某一个函数,我们称之为“目标函数”。
最小化的这组函数被称为“损失函数”。什么是损失函数呢?

损失函数描述了单个样本预测值和真实值之间误差的程度。用来度量模型一次预测的好坏。

损失函数是衡量预测模型预测期望结果表现的指标。损失函数越小,模型的鲁棒性越好。。
常用损失函数有:

  • 0-1损失函数:用来表述分类问题,当预测分类错误时,损失函数值为1,正确为0
    image.png
  • 平方损失函数:用来描述回归问题,用来表示连续性变量,为预测值与真实值差值的平方。(误差值越大、惩罚力度越强,也就是对差值敏感)
    image.png
  • 绝对损失函数:用在回归模型,用距离的绝对值来衡量
    image.png
  • 对数损失函数:是预测值Y和条件概率之间的衡量。事实上,该损失函数用到了极大似然估计的思想。P(Y|X)通俗的解释就是:在当前模型的基础上,对于样本X,其预测值为Y,也就是预测正确的概率。由于概率之间的同时满足需要使用乘法,为了将其转化为加法,我们将其取对数。最后由于是损失函数,所以预测正确的概率越高,其损失值应该是越小,因此再加个负号取个反。
    image.png

以上损失函数是针对于单个样本的,但是一个训练数据集中存在N个样本,N个样本给出N个损失,如何进行选择呢?
这就引出了风险函数。

2.1.2 期望风险

期望风险损失函数的期望,用来表达理论上模型f(X)关于联合分布P(X,Y)的平均意义下的损失。又叫期望损失/风险函数

image.png

2.1.3 经验风险

模型f(X)关于训练数据集的平均损失,称为经验风险或经验损失
其公式含义为:模型关于训练集的平均损失(每个样本的损失加起来,然后平均一下)

image.png

经验风险最小的模型为最优模型。在训练集上最小经验风险最小,也就意味着预测值和真实值尽可能接近,模型的效果越好。公式含义为取训练样本集中对数损失函数平均值的最小。

image.png

2.1.4 经验风险最小化和结构风险最小化

期望风险是模型关于联合分布的期望损失,经验风险是模型关于训练样本数据集的平均损失。根据大数定律,当样本容量N趋于无穷时,经验风险趋于期望风险。
因此很自然地想到用经验风险去估计期望风险。但是由于训练样本个数有限,可能会出现过度拟合的问题,即决策函数对于训练集几乎全部拟合,但是对于测试集拟合效果过差。因此需要对其进行矫正:

  • 结构风险最小化:当样本容量不大的时候,经验风险最小化容易产生“过拟合”的问题,为了“减缓”过拟合问题,提出了结构风险最小理论。结构风险最小化为经验风险与复杂度同时较小。
    image.png

通过公式可以看出,结构风险:在经验风险上加上一个正则化项(regularizer),或者叫做罚项(penalty) 。正则化项是J(f)是函数的复杂度再乘一个权重系数(用以权衡经验风险和复杂度)

2.1.5 小结

1、损失函数:单个样本预测值和真实值之间误差的程度。
2、期望风险:是损失函数的期望,理论上模型f(X)关于联合分布P(X,Y)的平均意义下的损失。
3、经验风险:模型关于训练集的平均损失(每个样本的损失加起来,然后平均一下)。
4、结构风险:在经验风险上加上一个正则化项,防止过拟合的策略

2.2 最小二乘法

2.2.1 什么是最小二乘法

言归正传,进入最小二乘法的部分。
大名鼎鼎的最小二乘法,虽然听上去挺高大上,但是思想还是挺朴素的,符合大家的直觉。
最小二乘法源于法国数学家阿德里安的猜想:

对于测量值来说,让总的误差的平方最小的就是真实值。这是基于,如果误差是随机的,应该围绕真值上下波动。

image.png
那么为了求出这个二次函数的最小值,对其进行求导,导数为0的时候取得最小值:
image.png

正好是算数平均数(算数平均数是最小二乘法的特例)。
这就是最小二乘法,所谓“二乘”就是平方的意思。
(高斯证明过:如果误差的分布是正态分布,那么最小二乘法得到的就是最有可能的值。)

0x03 简单线性回归的代码实现

3.1 简单线性回归算法的实现

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. x = np.array([1.,2.,3.,4.,5.])
  4. y = np.array([1.,3.,2.,3.,5,])
  5. plt.scatter(x,y)
  6. plt.axis([0,6,0,6])
  7. plt.show()

image.png

  1. x_mean = np.mean(x)
  2. y_mean = np.mean(y)
  3. num = 0.0 # 分子
  4. d = 0.0 # 分母
  5. for x_i, y_i in zip(x, y):
  6. num += (x_i - x_mean) * (y_i - y_mean)
  7. d += (x_i - x_mean) ** 2
  8. a = num / d
  9. b = y_mean - a * x_mean
  10. y_hat = a * x + b
  11. plt.scatter(x, y)
  12. plt.plot(x, y_hat, color='r')
  13. plt.axis([0, 6, 0, 6])
  14. plt.show()

image.png

  1. # 进行预测:
  2. x_predict = 6
  3. y_predict = a * x_predict + b
  4. y_predict # 5.2

3.2 向量化运算
使用我们自己封装的简单线性回归算法

  1. import numpy as np
  2. class SimpleLinearRegression1:
  3. def __init__(self):
  4. """初始化 Simple Linear Regression 模型"""
  5. self.a_ = None
  6. self.b_ = None
  7. def fit(self, x_train, y_train):
  8. """根据训练数据集x_train,y_train训练 Simple Linear Regression 模型"""
  9. assert x_train.ndim == 1, "Simple Linear Regressor can only solve single feature training data"
  10. assert len(x_train) == len(y_train), "the size of x_train must be equal to the of y_train"
  11. x_mean = np.mean(x_train)
  12. y_mean = np.mean(y_train)
  13. num = 0.0 # 分子
  14. d = 0.0 # 分母
  15. for x_i, y_i in zip(x_train, y_train):
  16. num += (x_i - x_mean) * (y_i - y_mean)
  17. d += (x_i - x_mean) ** 2
  18. self.a_ = num / d
  19. self.b_ = y_mean - self.a_ * x_mean
  20. return self
  21. def predict(self, x_predict):
  22. """给定待预测数据集x_predict, 返回x_predict的结果向量"""
  23. assert x_predict.ndim == 1, "Simple Linear Regressor can only solve single feature training data"
  24. assert self.a_ is not None and self.b_ is not None, "must fit before predict!"
  25. return np.array([self._predict(x) for x in x_predict])
  26. def _predict(self, x_single):
  27. """给定单个待预测数据x_single, 返回x_single的预测结果值"""
  28. return self.a_ * x_single + self.b_
  29. def __repr__(self):
  30. return "SimpleLinearRegression1()"
import numpy as np
import matplotlib.pyplot as plt

x = np.array([1., 2., 3., 4., 5.])
y = np.array([1., 3., 2., 3., 5.])

from playML.SimpleLinearRegression import SimpleLinearRegression1

reg1 = SimpleLinearRegression1()
reg1.fit(x, y)
y_hat1 = reg1.predict(x)
plt.scatter(x, y)
plt.plot(x, y_hat1, color='r')
plt.axis([0, 6, 0, 6])
plt.show()

image.png

向量化实现SimpleLinearRegression

import numpy as np


class SimpleLinearRegression1:

    def __init__(self):
        """初始化 Simple Linear Regression 模型"""
        self.a_ = None
        self.b_ = None

    def fit(self, x_train, y_train):
        """根据训练数据集x_train,y_train训练 Simple Linear Regression 模型"""
        assert x_train.ndim == 1, "Simple Linear Regressor can only solve single feature training data"
        assert len(x_train) == len(y_train), "the size of x_train must be equal to the of y_train"

        x_mean = np.mean(x_train)
        y_mean = np.mean(y_train)

        num = 0.0  # 分子
        d = 0.0  # 分母
        for x_i, y_i in zip(x_train, y_train):
            num += (x_i - x_mean) * (y_i - y_mean)
            d += (x_i - x_mean) ** 2

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

        return self

    def predict(self, x_predict):
        """给定待预测数据集x_predict, 返回x_predict的结果向量"""
        assert x_predict.ndim == 1, "Simple Linear Regressor can only solve single feature training data"
        assert self.a_ is not None and self.b_ is not None, "must fit before predict!"

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

    def _predict(self, x_single):
        """给定单个待预测数据x_single, 返回x_single的预测结果值"""
        return self.a_ * x_single + self.b_

    def __repr__(self):
        return "SimpleLinearRegression1()"


class SimpleLinearRegression2:

    def __init__(self):
        """初始化 Simple Linear Regression 模型"""
        self.a_ = None
        self.b_ = None

    def fit(self, x_train, y_train):
        """根据训练数据集x_train,y_train训练 Simple Linear Regression 模型"""
        assert x_train.ndim == 1, "Simple Linear Regressor can only solve single feature training data"
        assert len(x_train) == len(y_train), "the size of x_train must be equal to the of y_train"

        x_mean = np.mean(x_train)
        y_mean = np.mean(y_train)

        # num = 0.0  # 分子
        # d = 0.0  # 分母
        # for x_i, y_i in zip(x_train, y_train):
        #     num += (x_i - x_mean) * (y_i - y_mean)
        #     d += (x_i - x_mean) ** 2
        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):
        """给定待预测数据集x_predict, 返回x_predict的结果向量"""
        assert x_predict.ndim == 1, "Simple Linear Regressor can only solve single feature training data"
        assert self.a_ is not None and self.b_ is not None, "must fit before predict!"

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

    def _predict(self, x_single):
        """给定单个待预测数据x_single, 返回x_single的预测结果值"""
        return self.a_ * x_single + self.b_

    def __repr__(self):
        return "SimpleLinearRegression2()"
from playML.SimpleLinearRegression import SimpleLinearRegression2

reg2 = SimpleLinearRegression2()
reg2.fit(x, y)

reg2.a_  # 0.8
reg2.b_  # 0.39999999999999947

y_hat2 = reg2.predict(x)
plt.scatter(x, y)
plt.plot(x, y_hat2, color='r')
plt.axis([0, 6, 0, 6])
plt.show()

image.png