二. 线性模型


线性回归

所有机器学习方法论里,线性回归绝对是最简单且最实用的方法。只要涉及到回归问题,第一时间需要想到的就是线性回归。

一元线性回归针对的是只包含一个自变量的数据。

线性回归作为一个必不可少的方法论,在几乎所有的 回归 问题上都可以派上用场,线性回归具有简单、有效、 可解释性强等特点。

线性回归是机器学习中最为经典的模型,总结起来:

  • 几乎任何的回归问题可以使用线性回归来解决
  • 线性回归训练效率高,可用于数据量特别大的情况
  • 线性回归的可解释性强,这也是很多其他模型不具备的有点

1. 一元线性回归模型

误差

上图中黄线对数据的拟合度最高, 肉眼很容易看出哪一条线对样本的拟合度最高。这个拟合的标准如何量化?  可以通过计算的方式来求解出拟合度最高的那条线了。

量化的本质其实是设计一个评估标注, 并通过这个标准来寻求最好的那条线。

”离样本点越近的线条越适合“, 把这句话转换成一套可量化的评估标准,即误差 的概念, 就是实际值和预测值之间的差异。如果一条线产生的误差较小,就可以认为这条线拟合度较高。

误差表示成了包含? 的式子,用其来表示真实值和预测值之间的差异。一条线可以由两个参数来表示,分别是斜率w和偏移量b,所以寻求拟合度最好的那条线实际上是求解最好的w和b值。

为了求解这两个值,我们需要把?表示成包含wb的函数,这样一来整个误差就可以表示成包含 wb的函数。

目标函数

 有了目标函数(objective function)之后,剩下的工作就是求解让这个目标函数最小的参数w,b了,这实际上就是模型训练的过程。

求解一元函数的最优解

 给定一个包含若干个参数的函数, 如果想寻求这个函数的最小值或者最大值,这种问题称之为优化问题。

优化问题贯穿整个机器学习领域, 通常所说的”模型的训练“,其实指的就是函数的优化过程。

求解函数的最大最小值,就是函数的导数为0得到的x值代入求解即可得。

对于函数f(x)=x3?3x2?9x+10 的最小值为?

把导数设置为0之后,可以求出两个极值点,分别为3和-1。当x=3是获得最小值?17。

一元线性回归的最小二乘法

对于一元线性回归的求解, 首先把样本误差的平方作为 目标函数 , 再通过优化算法去寻求最优参数w,b, 整个过程称之为普通最小二乘法(ordinary least-square method)。

利用 求导的方法来求解极值点 (extreme point)是解决最小二乘的一个经典的优化方法。

代码:

在实际的工作中,我们只需要直接调用sklearn里的LinearRegression函数。

# 创建数据集,把数据写入到numpy数组
import numpy as np # 引用numpy库,主要用来做科学计算
import matplotlib.pyplot as plt # 引用matplotlib库,主要用来画图
data = np.array([[152,51],[156,53],[160,54],[164,55],
        [168,57],[172,60],[176,62],[180,65],
        [184,69],[188,72]])
# 打印大小
x, y = data[:,0], data[:,1]


#print (x.shape, y.shape)
# 1. 手动实现一个线性回归算法
# TODO: 实现w和b参数, w是斜率, b是偏移量

w = ((sum(x*y) / x.size) - (sum(x)/x.size * sum(y)/y.size))  / ((sum(x**2) / x.size) - (sum(x) / x.size) ** 2)
b = (sum(y)/y.size) - (w * sum(x)/x.size)
print ("通过手动实现的线性回归模型参数: %.5f %.5f"%(w,b))

# 2. 使用sklearn来实现线性回归模型, 比较跟手动实现的结果
from sklearn.linear_model import LinearRegression
model = LinearRegression().fit(x.reshape(-1,1),y)
print ("基于sklearn的线性回归模型参数:%.5f %.5f"%(model.coef_, model.intercept_))


---->>
通过手动实现的线性回归模型参数: 0.57576 -38.07879
基于sklearn的线性回归模型参数:0.57576 -38.07879


对于一元线性回归做一个简单总结

  •     一元线性回归用来拟合一维的特征向量
  •     一元线性回归的求解可以采用把导数设置为0的方法
  •     一条线由两个参数来组成,分别是 斜率w和偏移量b,训练一元线性回归模型实际上是寻找最优参数

2. 向量、矩阵等的概念

向量以及它的基本运算

对于数据的表示的几个概念:

标量 (scalar), 向量 (vector), 矩阵 (matrix)和 张量 (tensor)。不管使用 sklearn , Pytorch ,还是 Tensorflow 也好,一定要掌握这几个概念。即便工具化趋势越来越明显,而

且我们可以把模型当作黑盒子来对待,但唯一绕不开的是这个“黑盒子的”输入和输出,而且输入和输出必须要以标量、向量、矩阵或者张量的方式来表示。

 向量的运算、向量的转置(横着的给它逆时针旋转90° );

L2范数(Norm)

矩阵以及基本运算

除了向量,还有一个很重要的概念是矩阵。矩阵也有一些常用的运算如 矩阵 加法、 矩阵 乘法、 矩阵 转置、 矩阵 求逆 ,

 A ∈ Rm * n,表示这个矩阵有m行、n列;在数据集中的表示方法:X ∈ RN * O,N个样本,O个特征维度

矩阵的加减法跟向量的加减法一样;(都要保证两个矩阵的大小是一样的

 矩阵的乘法跟它的加减法不太一样, 两个矩阵如果相乘 Rm * *  Rn * d  =  Rm * d第一个矩阵的第二个维度n 要等于第二个矩阵的第一个维度n;

 矩阵的加减法跟向量的加减法类似,确保矩阵大小是一样的;

 矩阵的转置跟向量的转置是类似的

单位矩阵

逆矩阵 A-1

   A . A-1 = I (单位矩阵)

3. 多元线性回归模型

绝大多数情况下,需要处理的自变量可能有多个(特征向量维度大于1)。对于这类数据,假如要拟合线性回归模型,则使用的模型称之为多元线性回归模型。

在大多数情况下,给定的数据通常会包含多个 特征 ,所以更实用的还是多元线性回归模型。 除了特征个数的增加,从本质上跟 一元线性回归 是没有区别的。


 跟一元线性回归没有本质的区别,唯一的区别是原来的参数w是标量,现在变成了向量的形式  多维向量;

多元线性回归的目标函数

多元线性回归的目标函数(矩阵形态)

   

有了目标函数之后,我们就可以通过优化算法去寻找让目标函数最小的参数w了。跟一元线性回归类似,首先需要把导数设置为0,之后在求解它的极值点。 唯一不同的点在于,对于多元线性回归我们处理

的参数变成了向量,需要用到向量的求导法则。

对于向量的求导

多元线性回归的求解涉及到对向量的求导, 以及跟向量有关的基本运算。首先, 我们有必要把基础知识巩固一下,之后再去试着求解多元线性回归的解。 通常来讲,对向量和矩阵的求导会比较复杂。即便从事AI研究

很多年的学者也未必能把这些求导法则记得很清楚,所以很多时候还是要依赖于类似词典的参考资料。

The Matrix Cookbook

对向量、矩阵的求导 

 上图中分子有2个w,属于二阶求导;参数w的转置 * 某个值 * w ,查找刚工具书中的二阶求导法则:

 第二个求导是属于一阶导,查找公式得到

L2范数的平方

转置的运算


求多元线性回归的解

有了对向量和矩阵的基本概念, 以及理解了对它们的求导法则之后,便可以开始来解决多元线性回归的问题了, 先回到之前多元线性回归的目标函数。

多元线性回归的解析解

对于多元线性回归的目标函数,我们是通过求导的方式直接求出了对应的最优参数。这种方法,也称之为解析解(Analytical Solution)。

但需要注意的一点是,并不是所有的目标函数我们都可以通过把导数设置为0的方式来求出极值点的。(比如逻辑回归)

 

从零实现多元线性回归模型的参数求解。

import numpy as np
from sklearn.linear_model import LinearRegression
# 生成样本数据, 特征维度为2
X = np.array([[1, 1], [1, 2], [2, 2], [2, 3]])
# y = 1 * x_0 + 2 * x_1 + 3
y = np.dot(X, np.array([1, 2])) + 3
# 先使用sklearn自带的库来解决
model = LinearRegression().fit(X, y)
# 打印参数以及偏移量(bias)
print ("基于sklearn的线性回归模型参数为 coef: ", model.coef_, " intercept: %.5f" %model.intercept_)
# TODO: 手动实现多元线性回归的参数估计, 把最后的结果放在res变量里。 res[0]存储的是偏移量,res[1:]存储的是模型参数
a = np.ones(X.shape[0]).reshape(-1, 1)
X = np.concatenate((a, X),axis=1)

res = np.matmul(np.linalg.inv(np.matmul(X.T, X)), np.matmul(X.T, y))
# 打印参数以偏移量(bias)
print ("通过手动实现的线性回归模型参数为 coef: ", res[1:], " intercept: %.5f"%res[0])


--->>>
基于sklearn的线性回归模型参数为 coef:  [1. 2.]  intercept: 3.00000
通过手动实现的线性回归模型参数为 coef:  [1. 2.]  intercept: 3.00000
import numpy as np
from sklearn.linear_model import LinearRegression
# 生成样本数据, 特征维度为2
X = np.array([[1, 1, 2], [2, 2, 3], [3, 3, 2], [5, 5, 2]])
# y = 1 * x_0 + 2 * x_1 + 3 * x_2 + 3
y = np.dot(X, np.array([1, 2, 3])) + 3
# 手动实现参数的求解。先把偏移量加到X里
a = np.ones(X.shape[0]).reshape(-1,1)
X = np.concatenate((a, X), axis=1)
# 通过矩阵、向量乘法来求解参数, res已经包含了偏移量
res = np.matmul(np.linalg.inv(np.matmul(X.T, X)), np.matmul(X.T, y))
# 打印参数以偏移量(bias)
print ("通过手动实现的线性回归模型参数为 coef: ", res[1:], " intercept: %.5f"%res[0])

----->>
numpy.linalg.LinAlgError: Singular matrix
当我们运行上述代码时出现了“Singular Matrix”的错误,导致没有办法进行下去。

  问题主要出现在求解逆矩阵的过程中,因为并不是所有的矩阵都具有逆矩阵。

  并不是所有的矩阵都具有逆矩阵, 对于某些矩阵我们是无法计算出逆矩阵的。那对于什么样的矩阵它没有逆矩阵呢?

答案是这个矩阵的行列式(Determinant)的值为0的时候,那什么时候行列式为0呢?

答案是矩阵为满秩(not full rank)的时候。那为什么对于上述的数据,矩阵X是非满秩呢?

 它的答案是: 因为特征之间线性相关!


很多概念包括行列式、秩、满秩等等。现阶段不要纠结这些概念,它并不会影响后续对内容的理解,暂时把上述的观点当作是已知的就可以。

但如果感兴趣,可以查看相应的资料去学习。

逆矩阵无法求解时: 结合单位矩阵来求解无法求逆的问题

 

加了单位矩阵,其实跟加入正则(regularization)项起到的作用是一样的。

在现实问题中,在预处理阶段,我们通常会做特征之间的相关性检测,然后把那些相关性比较大的特征从中去掉, 这种操作也可以有效地避免无法求逆的问题。

两种常用的优化方法:

  • 解析解:通过把导数设置为0来求解;
  • 梯度下降法:通过循环迭代的方式来求解;

总结:

  • 向量、矩阵、张量是建模中最常用的概念,一定要好好理解
  • 多元线性回归解决多变量的问题
  • 对于多元线性回归,可通过求导的方法来求出解析解
  • 当特征线性相关的时候,无法求出解析解。一种处理方法是加入单位矩阵

4. 案例:股价预测

股价预测问题

首先,股价预测本身可以看作是回归问题,因为被预测的股价可以看作是具体的数值。但如果我们想预测的是未来股价的涨或者跌,那就变成分类问题了。股价其实是有一定预

测性的,但由于受到的外部因素很多,预测具有极高的挑战性。

理解了股票数据之后,该定义具体的预测任务了。 在这里,制定的任务是:根据历史的股价数据,预测出5天之后的股价。有了这个预测值之后就可以设计这样的买卖策略:

如果5天之后的预测值大于现在的价格,就买进。当然,实操中用的买卖策略远比这个复杂得多。

数据清洗

对于上面的数据,我们首先希望以时间的维度做一个排序,因为我们的任务是预测未来5天之后的股价。所以,下面按照date字段,把所有的样本以时间的升序做个排序。 具体排序的代码可以参考如下部分:

df['date'] = pd.to_datetime(df['date'])  # 把字符串转换成时间的格式 

df = df.set_index('date') # 把date字段作为index

# 按照时间升序排列

df.sort_values(by=['date'], inplace=True, ascending=True)

df.head()

构造训练数据

由于问题本身是回归问题,所以需要预测值。对于这次的任务,我们的目标是通过当日的数据特征去预测5天之后的股价。在数据中,暂时还没有包含这5天之后的股价。但构造的方式很简单,比如对于2016-11-29当天的数据,根据5天的规定,预测值设定为9.49(5天之后的收盘价),对于2016-11-30当天的数据,预测值设定为9.48,以此类推。所以在预测值的构造上,我们只需要把原来数据中的close字段的值往前挪5位就可以了,可通过以下的代码来实现:

df['label'] = df['close'].shift(-num)  # 构造预测值,这里的num=5 

处理NA字段

df.dropna(inplace=True) 

数据中每个字段的值

  • 有些字段的值很大(如volume),有些字段的值很小 (open)
  • 字段值的大小不一样,会影响模型的训练
  • 数据值大,不代表这个特征就越重要

数据中明显可以看到不同数据字段的值的区间是不一样的, 这会影响后续模型的训练(但也要看具体是什么类型的模型,以及用什么样的优化方法)。

数据归一化

为了解决字段值之间大小的差异,我们通常在训练模型前做归一化的操作。归一化的目的就是把每个字段转换到同等大小的区间来消除字段大小所带来的影响。

归一化通常有两种方法:

  • 第一种是 min-max归一化 ,
  • 第二种是 z-score归一化 。

 

构建模型

  处理完数据之后,就可以进入 建模 的阶段了。 针对于建模,相对比较简单,我们首先需要把数据分为训练数据和测试数据,之后在训练数据上训练模型,最后在测试数据上 评估模型 即可。

实现时,可以直接使用LinearRegression来构建线性回归模型,并用模型的fit()函数在训练数据上做训练,最后可以通过它的score()函数来评估模型的 准确率 。

5. 平方误差和高斯噪音 

从噪声的角度来理解一下线性回归模型。线性回归的目标函数实际就是平方误差。对于平方误差,有没有可能从噪声模型来做解释呢?

线性回归的最小二乘法是基于高斯误差的假设得来的。

中心极限定理:这些误差的变量服从一个正态分布;


 高斯分布

均值u、方差(曲线上的点--样本跟均值的距离有多大,方差越大图像越扁平,越小图像越高瘦;方差越小表示预测能力越强(很多样本集中在均值附近))

概率密度函数;

 多元高斯分布

向量: X ∈ Rd  ;高斯分布 N( u, ∑ ) u为均值, ∑ 协方差矩阵(衡量两两维度之间的相关性);

 每个样本的似然概率

 所有样本的似然概率

条件概率来表示每一个样本, 这也称之为似然概率(likelihood)。那如何表示整体样本的似然概率呢?其实很简单,只需要把每个样本的似然概率相乘即可。得到所有样本的似然概率之后,我们接着可以通过

优化算法去求解最合适的参数,这个过程也称之为最大似然估计(Maximum Likelihood Estimation)。


当我们把误差分布假定为高斯分布的时候,最终得出来的所有样本的 似然函数 其实就是一开始讨论的平方误差。这种分析方法也叫作概率解释(Probabilistic Interpretation)的方法。

如果我们把误差的分布假定为另外一种分形态(如拉普拉斯分布),则最后得出来的结果跟平方误差结果是不一样的;

基于不同的分布假设,最后得出来的结果是不一样的。如果我们使用了拉普拉斯分布,对应的目标函数是真实值x1)和预测值(x2)之间的绝对距离(x1?x2),而不是平方距离。