数学原理
回归分析
回归分析是用来评估变量之间关系的统计过程。用来解释自变量X与因变量Y的关系。即当自变量X发生改变时,因变量Y会如何发生改变。
线性回归
回归分析的一种,评估的自变量X与因变量Y之间是一种线性关系。当只有一个自变量时,称为一元线性回归,当具有多个自变量时,称为多元线性回归。
线性关系的理解:
- 画出来的图像是直的。
- 每个自变量的最高次项为1。
我们从简单的一元线性回归开始。这里,我们以房屋面积(x)与房屋价格(y)为例,显而易见,二者是一种线性关系,房屋价格正比于房屋面积,我们假设比例为w:
y
^
=
w
∗
x
\hat{y} = w * x
y^=w∗x
然而,这种线性方程一定是过原点的,即当x为0时,y也一定为0。这可能并不符合现实中某些场景。为了能够让方程具有更广泛的适应性,我们这里再增加一个截距,设为b,即之前的方程变为:
y
^
=
w
∗
x
+
b
\hat{y} = w * x + b
y^=w∗x+b
而以上方程,就是我们数据建模的模型。方程中的w与b,就是模型的参数。
假定数据集如下:
房屋面积 | 房屋价格 |
---|---|
30 | 100 |
40 | 120 |
40 | 115 |
50 | 130 |
50 | 132 |
60 | 147 |
线性回归是用来解释自变量与因变量之间的关系,但是,这种关系并非严格的函数映射关系。从数据集中,我们也看到了这一点。相同面积的房屋,价格并不完全相同,但是,也不会相差过大。
解决方法
我们现在的目的就是,从现有的数据(经验)中,去学习(确定)w与b的值。一旦w与b的值确定,我们就能够确定拟合数据的线性方程,这样就可以对未知的数据x(房屋面积)进行预测y(房屋价格)。
多元线性回归
然而,现实中的数据可能是比较复杂的,自变量也很可能不只一个。例如,影响房屋价格也很可能不只房屋面积一个因素,可能还有距地铁距离,距市中心距离,房间数量,房屋所在层数,房屋建筑年代等诸多因素。不过,这些因素,对房屋价格影响的力度(权重)是不同的,例如,房屋所在层数对房屋价格的影响就远不及房屋面积,因此,我们可以使用多个权重来表示多个因素与房屋价格的关系:
y
^
=
w
1
∗
x
1
+
w
2
∗
x
2
+
w
3
∗
x
3
+
…
…
+
w
n
∗
x
n
+
b
\hat{y} = w_{1} * x_{1} + w_{2} * x_{2} + w_{3} * x_{3} + …… + w_{n} * x_{n} + b
y^=w1∗x1+w2∗x2+w3∗x3+……+wn∗xn+b
其中,每个x为影响因素,每个w为对应的影响力度,y为房屋的价格。我们也可以使用向量的表示方式,设x与w为两个向量:
w
=
(
w
1
,
w
2
,
w
3
,
…
…
w
n
)
x
=
(
x
1
,
x
2
,
x
3
,
…
…
x
n
)
w = (w_{1}, w_{2}, w_{3}, …… w_{n})\\ x = (x_{1}, x_{2}, x_{3}, …… x_{n})
w=(w1,w2,w3,……wn)x=(x1,x2,x3,……xn)
则方程可表示为:
y
^
=
∑
j
=
1
n
w
j
∗
x
j
+
b
=
w
T
∗
x
+
b
\hat{y} = \sum_{j=1}^{n}w_{j} * x_{j} + b\\ \ \ = w ^ {T} * x + b
y^=∑j=1nwj∗xj+b =wT∗x+b
我们也可以令:
{
x
0
=
1
w
0
=
b
\left\{\begin{matrix}x_{0} = 1 & \\ w_{0} = b & \end{matrix}\right.
{x0=1w0=b
这样,就可以表示为:
y
^
=
w
0
∗
x
0
+
w
1
∗
x
1
+
w
2
∗
x
2
+
w
3
∗
x
3
+
…
…
+
w
n
∗
x
n
=
∑
j
=
0
n
w
j
∗
x
j
=
w
T
∗
x
\hat{y} = w_{0} * x_{0} + w_{1} * x_{1} + w_{2} * x_{2} + w_{3} * x_{3} + …… + w_{n} * x_{n}\\ \ \ = \sum_{j=0}^{n}w_{j} * x_{j}\\ \ \ = w ^ {T} * x
y^=w0∗x0+w1∗x1+w2∗x2+w3∗x3+……+wn∗xn =∑j=0nwj∗xj =wT∗x
说明:在机器学习中,习惯用上标表示样本,用下标表示特征。
多元线性回归在空间中,可以表示为一个超平面,去拟合空间中的数据点。
拟合
拟合,是指构建一种算法(数学函数),使得该算法能够符合真实的数据。从机器学习角度讲,线性回归就是要构建一个线性函数,使得该函数与目标值之间的拟合性最好。从空间的角度来看,就是要让函数的直线(面),尽可能穿过空间中的数据点。线性回归会输出一个连续值。
损失函数
通过之前的介绍,我们得知,对机器学习来讲,就是从已知数据(经验)去建立一个模型,使得该模型能够对未知的数据进行预测。实际上,机器学习的过程,就是确定(学习)模型参数(即模型的权重与偏置)的过程,因为只要模型的参数确定了,我们就可以利用模型进行预测(有参数模型)。
那么,模型的参数该如果求解呢?对于监督学习来说,我们可以通过建立损失函数来实现。损失函数,也称目标函数或代价函数,简单的说,就是关于误差的一个函数。损失函数用来衡量模型预测值与真实值之间的差异。机器学习的目标,就是要建立一个损失函数,使得该函数的值最小。
也就是说,损失函数是一个关于模型参数的函数(以模型参数作为自变量的函数),自变量可能的取值组合通常是无限的,我们的目标,就是要在众多可能的组合中,找到一组最合适的自变量组合(值),使得损失函数的值最小。
损失函数我们习惯使用J来表示,例如,J(w)则表示以w为自变量的函数。
损失函数分类
损失函数有很多种,常见的损失函数为:
- 平方和损失函数
- 交叉熵损失函数
参数求解
误差与分布
接下来,我们来看一下线性回归模型中的误差。正如我们之前所提及的,线性回归解释的变量(现实中存在的样本),是存在线性关系的。然而,这种关系并不是严格的函数映射关系,但是,我们构建的模型(方程)却是严格的函数映射关系的,因此,对于每个样本来说,我们拟合的结果会与真实值之间存在一定的误差,我们可以将误差表示为:
y
^
(
i
)
=
w
T
∗
x
(
i
)
y
(
i
)
=
y
^
(
i
)
+
ε
(
i
)
\hat{y} ^ {(i)} = w ^ {T} * x ^ {(i)}\\ y ^ {(i)} = \hat{y} ^ {(i)} + \varepsilon ^ {(i)}
y^(i)=wT∗x(i)y(i)=y^(i)+ε(i)
其中,
ε
(
i
)
\varepsilon ^ {(i)}
ε(i)表示每个样本与实际值之间的误差。
由于每个样本的误差
ε
\varepsilon
ε是独立同分布的,根据中心极限定理,
ε
\varepsilon
ε服从均值为0,方差为
σ
2
\sigma ^ {2}
σ2的正态分布。因此,根据正态分布的概率密度公式:
p
(
ε
(
i
)
)
=
1
σ
2
π
e
x
p
(
−
(
ε
(
i
)
)
2
2
σ
2
)
p
(
y
(
i
)
∣
x
(
i
)
;
w
)
=
1
σ
2
π
e
x
p
(
−
(
y
(
i
)
−
w
T
x
(
i
)
)
2
2
σ
2
)
p(\varepsilon ^ {(i)}) = \frac{1}{\sigma\sqrt{2\pi}}exp(-\frac{(\varepsilon ^ {(i)}) ^ {2}}{2\sigma ^ {2}})\\ p(y ^ {(i)}|x ^ {(i)};w) = \frac{1}{\sigma\sqrt{2\pi}}exp(-\frac{(y ^ {(i)} - w ^ {T}x ^ {(i)}) ^ {2}}{2\sigma ^ {2}})
p(ε(i))=σ2π1exp(−2σ2(ε(i))2)p(y(i)∣x(i);w)=σ2π1exp(−2σ2(y(i)−wTx(i))2)
为什么假定误差为标准正太分布?
- 1、计量分析中有一个因果关系,这个因果关系是我们进行计量的基础。我们容易知道变量X.Y之间,可能具有相关关系,但相关关系并不是因果关系。在计量中,因果关系是给定其他情况不变,这样才能被认为是因果关系。以一元线性回归为例。在线性回归的时候,我们考虑在变量X变化时,Y的变化,形成了一元线性的关系,Yi=β0+β1X+u。而u作为随机误差项,包含了一切的其他因素。因此,我们假定u的期望(均值)为0,以满足我们的假定。2、期望为0是随机误差项的经典假设,这是进行之后分析的大的假设前提。你可以想象一下如果不这样要求,那么你的回归完全可能不在散点图之间,全部在散点图下面(把原来的回归向下平移),这样数值会很大,不好算,也不利于之后的预测。3、之前的Yi=β0+β1X+u是总体回归模型。但我们之后需要的是总体回归方程,即E(Yi|Xi)=β0+β1X,如果u的期望不为0,就无法得到我们之后的总体回归方程
# 独立同分布变量之和的分布情况 --掷骰子
%matplotlib inline
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
result=[]
for i in range(10000):
result.append(np.sum(np.random.randint(1, 7, size=3)))
s = pd.Series(result)
s.plot(kind='kde')
<matplotlib.axes._subplots.AxesSubplot at 0x20d781700c8>
最大似然估计
以上是一个样本的概率密度,我们假设数据集中共有m个样本,每个样本具有n个特征,则多个样本的联合密度函数(似然函数)为:
L
(
w
)
=
∏
1
m
p
(
y
(
i
)
∣
x
(
i
)
;
w
)
=
∏
1
m
1
σ
2
π
e
x
p
(
−
(
y
(
i
)
−
w
T
x
(
i
)
)
2
2
σ
2
)
L(w) = \prod_{1}^{m}p(y ^ {(i)}|x ^ {(i)};w)\\ = \prod_{1}^{m}\frac{1}{\sigma\sqrt{2\pi}}exp(-\frac{(y ^ {(i)} - w ^ {T}x ^ {(i)}) ^ {2}}{2\sigma ^ {2}})
L(w)=∏1mp(y(i)∣x(i);w)=∏1mσ2π1exp(−2σ2(y(i)−wTx(i))2)
根据最大似然估计,我们让所有样本出现的联合概率最大,则此时参数w的值,就是我们要求解的值。不过,累计乘积的方式不利于求解,我们这里使用对数似然函数,即在联合密度函数上取对数操作(取对数操作不会改变函数的极值点),这样就可以将累计乘积转换为累计求和的形式。
l
n
(
L
(
w
)
)
=
l
n
∏
1
m
1
σ
2
π
e
x
p
(
−
(
y
(
i
)
−
w
T
x
(
i
)
)
2
2
σ
2
)
=
∑
1
m
l
n
1
σ
2
π
e
x
p
(
−
(
y
(
i
)
−
w
T
x
(
i
)
)
2
2
σ
2
)
=
m
l
n
1
σ
2
π
−
1
σ
2
∗
1
2
∑
1
m
(
y
(
i
)
−
w
T
x
(
i
)
)
2
ln(L(w)) = ln\prod_{1}^{m}\frac{1}{\sigma\sqrt{2\pi}}exp(-\frac{(y ^ {(i)} - w ^ {T}x ^ {(i)}) ^ {2}}{2\sigma ^ {2}})\\ = \sum_{1}^{m}ln\frac{1}{\sigma\sqrt{2\pi}}exp(-\frac{(y ^ {(i)} - w ^ {T}x ^ {(i)}) ^ {2}}{2\sigma ^ {2}})\\ = mln\frac{1}{\sigma\sqrt{2\pi}} - \frac{1}{\sigma ^ {2}} * \frac{1}{2}\sum_{1}^{m}(y ^ {(i)} - w ^ {T}x ^ {(i)}) ^ {2}
ln(L(w))=ln∏1mσ2π1exp(−2σ2(y(i)−wTx(i))2)=∑1mlnσ2π1exp(−2σ2(y(i)−wTx(i))2)=mlnσ2π1−σ21∗21∑1m(y(i)−wTx(i))2
上式中,前半部分都是常数,我们的目的是为了让联合密度概率值最大,因此,我们只需要让后半部分的值最小即可,因此,后半部分,就可以作为模型的损失函数。该函数是二次函数,具有唯一极小值。
最小二乘法
最小二乘法,即最小平方法(也许这样称呼或许更合理),通过让样本数据的预测值与真实值之间的误差平方和最小,进而求解参数的方法。
一方面,我们通过极大似然估计,寻找出目标函数,不过,我们也可以直观的进行分析。从简单的角度看,我们的目的,其实就是要寻找一条合适的直线(平面),使得所有样本距离直线(平面)的距离,达到最小化即可。因此,我们可以采用每个样本的预测值与真实值做差,然后取平方和的方式,能够让该平方和最小的w,就是我们需要求解的w)。思考:为什么要用平方和,而不是直接求和,或者绝对值求和?
J
(
w
)
=
1
2
∑
1
m
(
y
(
i
)
−
w
T
x
(
i
)
)
2
J(w) = \frac{1}{2}\sum_{1}^{m}(y ^ {(i)} - w ^ {T}x ^ {(i)}) ^ {2}
J(w)=21∑1m(y(i)−wTx(i))2
由于:
y
^
=
(
y
^
(
1
)
y
^
(
2
)
…
…
y
^
(
m
)
)
=
(
w
T
∗
x
(
1
)
w
T
∗
x
(
2
)
…
…
w
T
∗
x
(
m
)
)
=
(
w
1
x
1
(
1
)
+
w
2
x
2
(
1
)
+
…
…
+
w
n
x
n
(
1
)
w
1
x
1
(
2
)
+
w
2
x
2
(
2
)
+
…
…
+
w
n
x
n
(
2
)
…
…
w
1
x
1
(
m
)
+
w
2
x
2
(
m
)
+
…
…
+
w
n
x
n
(
m
)
)
=
(
x
1
(
1
)
,
x
2
(
1
)
,
…
…
x
n
(
1
)
x
1
(
2
)
,
x
2
(
2
)
,
…
…
x
n
(
2
)
…
…
x
1
(
m
)
,
x
2
(
m
)
,
…
…
x
n
(
m
)
)
⋅
(
w
1
w
2
…
…
w
n
)
=
X
⋅
w
y
=
(
y
(
1
)
y
(
2
)
…
…
y
(
m
)
)
e
r
r
o
r
=
(
e
r
r
o
r
(
1
)
e
r
r
o
r
(
2
)
…
…
e
r
r
o
r
(
m
)
)
=
(
y
^
(
1
)
−
y
(
1
)
y
^
(
2
)
−
y
(
2
)
…
…
y
^
(
m
)
−
y
(
m
)
)
=
y
^
−
y
=
X
⋅
w
−
y
=
>
∑
1
m
(
e
r
r
o
r
(
i
)
)
2
=
∑
1
m
(
y
^
(
i
)
−
y
(
i
)
)
2
=
∑
1
m
(
w
T
x
(
i
)
−
y
(
i
)
)
2
=
e
r
r
o
r
T
⋅
e
r
r
o
r
%y_hat定义 \hat{y} = \begin{pmatrix} \hat{y} ^ {(1)}\\ \hat{y} ^ {(2)}\\ ……\\ \hat{y} ^ {(m)} \end{pmatrix} = \begin{pmatrix} w ^ {T} * x ^ {(1)}\\ w ^ {T} * x ^ {(2)}\\ ……\\ w ^ {T} * x ^ {(m)} \end{pmatrix} = %wi * xi 展开式 \begin{pmatrix} w_{1}x_{1} ^ {(1)} + w_{2}x_{2} ^ {(1)} + …… + w_{n}x_{n} ^ {(1)}\\ w_{1}x_{1} ^ {(2)} + w_{2}x_{2} ^ {(2)} + …… + w_{n}x_{n} ^ {(2)}\\ ……\\ w_{1}x_{1} ^ {(m)} + w_{2}x_{2} ^ {(m)} + …… + w_{n}x_{n} ^ {(m)} \end{pmatrix} = \begin{pmatrix} x_{1} ^ {(1)}, x_{2} ^ {(1)}, ……x_{n} ^ {(1)}\\ x_{1} ^ {(2)}, x_{2} ^ {(2)}, ……x_{n} ^ {(2)}\\ ……\\ x_{1} ^ {(m)}, x_{2} ^ {(m)}, ……x_{n} ^ {(m)} \end{pmatrix} \cdot \begin{pmatrix} w_{1}\\ w_{2}\\ ……\\ w_{n} \end{pmatrix} = X\cdot w \\ %y的定义 y = \begin{pmatrix} y ^ {(1)}\\ y ^ {(2)}\\ ……\\ y ^ {(m)} \end{pmatrix}\qquad %error定义 error = \begin{pmatrix} error ^ {(1)}\\ error ^ {(2)}\\ ……\\ error ^ {(m)} \end{pmatrix} = \begin{pmatrix} \hat{y} ^ {(1)} - y ^ {(1)}\\ \hat{y} ^ {(2)} - y ^ {(2)}\\ ……\\ \hat{y} ^ {(m)} - y ^ {(m)} \end{pmatrix} = \hat{y} - y = X\cdot w - y\qquad => \\ %error总结 \sum_{1}^{m}(error ^ {(i)}) ^ {2} = \sum_{1}^{m}(\hat{y} ^ {(i)} - y ^ {(i)}) ^ {2} = \sum_{1}^{m}( w ^ {T}x ^ {(i)} - y ^ {(i)}) ^ {2} = error ^ {T} \cdot error
y^=⎝⎜⎜⎛y^(1)y^(2)……y^(m)⎠⎟⎟⎞=⎝⎜⎜⎛wT∗x(1)wT∗x(2)……wT∗x(m)⎠⎟⎟⎞=⎝⎜⎜⎜⎛w1x1(1)+w2x2(1)+……+wnxn(1)w1x1(2)+w2x2(2)+……+wnxn(2)……w1x1(m)+w2x2(m)+……+wnxn(m)⎠⎟⎟⎟⎞=⎝⎜⎜⎜⎛x1(1),x2(1),……xn(1)x1(2),x2(2),……xn(2)……x1(m),x2(m),……xn(m)⎠⎟⎟⎟⎞⋅⎝⎜⎜⎛w1w2……wn⎠⎟⎟⎞=X⋅wy=⎝⎜⎜⎛y(1)y(2)……y(m)⎠⎟⎟⎞error=⎝⎜⎜⎛error(1)error(2)……error(m)⎠⎟⎟⎞=⎝⎜⎜⎛y^(1)−y(1)y^(2)−y(2)……y^(m)−y(m)⎠⎟⎟⎞=y^−y=X⋅w−y=>∑1m(error(i))2=∑1m(y^(i)−y(i))2=∑1m(wTx(i)−y(i))2=errorT⋅error
因此:
J
(
w
)
=
1
2
∑
1
m
(
y
(
i
)
−
w
T
x
(
i
)
)
2
=
1
2
(
X
w
−
y
)
T
(
X
w
−
y
)
J(w) = \frac{1}{2}\sum_{1}^{m}(y ^ {(i)} - w ^ {T}x ^ {(i)}) ^ {2}\\ = \frac{1}{2}(Xw - y) ^ {T}(Xw - y)
J(w)=21∑1m(y(i)−wTx(i))2=21(Xw−y)T(Xw−y)
我们要求该目标函数的最小值,只需要对自变量w进行求导,导数为0时w的值,就是我们要求解的值。
▽
w
J
(
w
)
=
▽
w
(
1
2
(
X
w
−
y
)
T
(
X
w
−
y
)
)
=
▽
w
(
1
2
(
w
T
X
T
−
y
T
)
(
X
w
−
y
)
)
=
▽
w
(
1
2
(
w
T
X
T
X
w
−
w
T
X
T
y
−
y
T
X
w
+
y
T
y
)
)
\triangledown_{w} J(w) = \triangledown_{w}(\frac{1}{2}(Xw - y) ^ {T}(Xw - y))\\ =\triangledown_{w}(\frac{1}{2}(w ^ {T}X ^ {T} - y ^ {T})(Xw - y))\\ = \triangledown_{w}(\frac{1}{2}(w ^ {T}X ^ {T}Xw - w ^ {T}X ^ {T}y - y ^ {T}Xw + y ^ {T}y))
▽wJ(w)=▽w(21(Xw−y)T(Xw−y))=▽w(21(wTXT−yT)(Xw−y))=▽w(21(wTXTXw−wTXTy−yTXw+yTy))
根据矩阵与向量的求导公式,有:
∂
A
x
⃗
∂
x
⃗
=
A
T
∂
A
x
⃗
∂
x
⃗
T
=
A
∂
(
x
⃗
T
A
)
∂
x
⃗
=
A
∂
(
x
⃗
T
A
x
⃗
)
∂
x
⃗
=
(
A
T
+
A
)
x
⃗
\frac{\partial A\vec{x}}{\partial \vec{x}} = A ^ {T} \\ \frac{\partial A\vec{x}}{\partial \vec{x} ^ {T}} = A \\ \frac{\partial (\vec{x} ^ {T}A)}{\partial \vec{x}} = A \\ \frac{\partial (\vec{x} ^ {T}A\vec{x})}{\partial \vec{x}} = (A ^ {T} + A)\vec{x}
∂x∂Ax=AT∂xT∂Ax=A∂x∂(xTA)=A∂x∂(xTAx)=(AT+A)x
特别的,如果
A
=
A
T
A = A ^ {T}
A=AT(A为对称矩阵),则:
∂
(
x
⃗
T
A
x
⃗
)
∂
x
⃗
=
2
A
x
⃗
\frac{\partial (\vec{x} ^ {T}A\vec{x})}{\partial \vec{x}} = 2A\vec{x}
∂x∂(xTAx)=2Ax
因此:
▽
w
(
1
2
(
w
T
X
T
X
w
−
w
T
X
T
y
−
y
T
X
w
+
y
T
y
)
)
=
1
2
(
2
X
T
X
w
−
X
T
y
−
(
y
T
X
)
T
)
=
X
T
X
w
−
X
T
y
\triangledown_{w}(\frac{1}{2}(w ^ {T}X ^ {T}Xw - w ^ {T}X ^ {T}y - y ^ {T}Xw + y ^ {T}y))\\ = \frac{1}{2}(2X ^ {T}Xw - X ^ {T}y - (y ^ {T}X) ^ {T})\\ = X ^ {T}Xw - X ^ {T}y
▽w(21(wTXTXw−wTXTy−yTXw+yTy))=21(2XTXw−XTy−(yTX)T)=XTXw−XTy
令导函数的值为0,则:
w
=
(
X
T
X
)
−
1
X
T
y
w = (X ^ {T}X) ^ {-1}X ^ {T}y
w=(XTX)−1XTy
以上的求解方式,就是最小二乘法。使用最小二乘法求解的时候,要求矩阵
X
T
X
X ^ {T}X
XTX必须是可逆的。
代码推导
import numpy as np
def loadDataSet(filePath):
""" 加载数据
解析以tab键分隔的文件中的浮点数
Returns:
dataMat : feature 对应的数据集
labelMat : feature 对应的分类标签,即类别标签
"""
# 获取样本特征的总数,不算最后的目标变量
numFeat = len(open(filePath).readline().split('\t')) - 1
dataMat = []
labelMat = []
fr = open(filePath)
for line in fr.readlines():
# 读取每一行
lineArr = []
# 删除一行中以tab分隔的数据前后的空白符号
curLine = line.strip().split('\t')
# i 从0到2,不包括2
for i in range(numFeat):
# 将数据添加到lineArr List中,每一行数据测试数据组成一个行向量
lineArr.append(float(curLine[i]))
# 将测试数据的输入数据部分存储到dataMat 的List中
dataMat.append(lineArr)
# 将每一行的最后一个数据,即类别,或者叫目标变量存储到labelMat List中
labelMat.append(float(curLine[-1]))
return dataMat, labelMat
def standRegres(xArr, yArr):
'''
Description:
线性回归
Args:
xArr :输入的样本数据,包含每个样本数据的 feature
yArr :对应于输入数据的类别标签,也就是每个样本对应的目标变量
Returns:
ws:回归系数
'''
# mat()函数将xArr,yArr转换为矩阵 mat().T 代表的是对矩阵进行转置操作
xMat = np.mat(xArr)
yMat = np.mat(yArr).T
# 矩阵乘法的条件是左矩阵的列数等于右矩阵的行数
xTx = xMat.T * xMat
# 因为要用到xTx的逆矩阵,所以事先需要确定计算得到的xTx是否可逆,条件是矩阵的行列式不为0
# linalg.det() 函数是用来求得矩阵的行列式的,如果矩阵的行列式为0,则这个矩阵是不可逆的,就无法进行接下来的运算
if np.linalg.det(xTx) == 0.0:
print("This matrix is singular, cannot do inverse")
return
# 最小二乘法
# 书中的公式,求得w的最优解
ws = xTx.I * (xMat.T * yMat)
return ws
def regression():
xArr, yArr =loadDataSet('data.txt')
xMat = np.mat(xArr)
yMat = np.mat(yArr)
ws = standRegres(xArr, yArr)
fig = plt.figure()
ax = fig.add_subplot(111) # add_subplot(349)函数的参数的意思是,将画布分成3行4列图像画在从左到右从上到下第9块
ax.scatter([xMat[:, 1].flatten()], [yMat.T[:, 0].flatten().A[0]]) # scatter 的x是xMat中的第二列,y是yMat的第一列
xCopy = xMat.copy()
xCopy.sort(0)
yHat = xCopy * ws
ax.plot(xCopy[:, 1], yHat)
plt.show()
if __name__ == '__main__':
regression()
基于sklearn实现及参数说明
一元线性回归
# sklearn 命名惯例:
# 矩阵 使用大写字母
# 向量 使用小写字母
# 所有模型的拟合(训练)方法都叫fit。
# 所有模型的测试方法都叫predict。
import numpy as np
# LinearRegression 线性回归的类,我们可以使用该类实现线性回归。
# 底层就是使用最小二乘公式来进行求解的。
from sklearn.linear_model import LinearRegression
# 该方法可以用来对数据集进行切分,分为训练集与测试集。训练集用于训练模型(求解出模型的参数w与b),
# 测试集用来验证我们建立模型的效果如何。
from sklearn.model_selection import train_test_split
# 设置随机种子。
np.random.seed(0)
x = np.linspace(-10,10,1000)
# 将x转换为二维的格式。之所以要将x转换为二维矩阵的格式,是因为稍后的模型fit方法中,第一个
# 参数(X)需要是二维的格式。
X = x.reshape(-1,1)
# 构建一个方程,,生成y值。
y = 0.85 * X - 0.72
# 生成正态分布的噪音。
noise = np.random.normal(scale = 1.5,size=X.shape)
# 令y值加入噪音,这样,更符合现实中数据的情况。(并不是完美的函数映射关系。)
y+=noise
# 创建线性回归的对象。
lr = LinearRegression()
# 将样本数据切分为训练集与测试集。
# 该函数接收若干个数组,将每个数组切分为两部分,并返回。
# 相关参数:
# shuffle 是否进行洗牌,默认为True。
# random_state: 洗牌时候所使用的随机种子,相同的随机种子,一定可以产生相同的洗牌序列。
# test_size: 测试集数据所占有的比例。
train_X,test_X,train_y,test_y=train_test_split(X,y,test_size=0.25,random_state=0)
# 拟合数据,即使用训练数据(X与y)去训练模型。实际上,拟合(训练)就是求解模型的参数(w与b)。
lr.fit(train_X,train_y)
# coef_ 返回模型训练好的权重w值。
print(f"矩阵的权重是{lr.coef_}")
# intercept_ 返回模型训练好的截距(偏置)b。
print(f"矩阵的截距是{lr.intercept_}")
# 测试方法。模型经过拟合训练后,就可以得到(求解出)w与b。一旦w与b确定,模型就能够进行预测。
# 根据参数传递过来的X,返回预测的结果(目标值)y_hat。
y_hat=lr.predict(test_X)
print(f"实际值:{test_y.ravel()[:10]}")
print(f"预测值:{y_hat.ravel()[:10]}")
矩阵的权重是[[0.84850287]]
矩阵的截距是[-0.78380631]
实际值:[ 8.03907916 5.43572543 -4.74810247 -0.21098171 -0.70311919 8.75684748
-9.04131632 -6.02904889 -6.0141805 4.1986826 ]
预测值:[ 7.59930008 5.32303614 -4.20669575 0.12500056 2.14645884 7.22558511
-8.81018478 -5.34482773 -4.0707994 2.72401835]
import matplotlib as mpl
import matplotlib.pyplot as plt
mpl.rcParams["font.family"] = "SimHei"
mpl.rcParams["axes.unicode_minus"] =False
# 绘制数据集样本。
plt.scatter(X,y,s=2)
#绘制线性回归的拟合直线。即模型y = w * x + b的那条线。也就是 y = 0.84850287 * x + -0.78380631
plt.plot(X,lr.predict(X),"r-")
[<matplotlib.lines.Line2D at 0x20d7d398a08>]
多元线性回归
类似的,我们也可以实现多元线性回归。这里,我们需要创建多个特征(x),我们也可以像之前程序那样,随机生成多个特征,不过,这里,我们使用sklearn库提供的更方面的方法。
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_regression
# 参数说明:
# n_samples:生成的样本数量。
# n_features:生成的特征数量。(有几个x)
# coef:是否返回方程的权重(w)。如果该值为True,则返回X,y与coef。
# 如果该值为False,仅返回X与y。默认为False。
# bias:偏置值(截距),也就是方程中的b。
# random_state:设置随机种子。
# noise:噪声的值。该值会影响数据的波动情况。值越大,波动性越大。
X, y, coef = make_regression(n_samples=10000, n_features=2, coef=True,bias=1.1, random_state=0, noise=10)
train_X, test_X, train_y, test_y =train_test_split(X,y,test_size=0.25,random_state=0)
print(f"生成矩阵实际权重{coef}")
lr=LinearRegression()
lr.fit(train_X,train_y)
print(f"模型训练权重{lr.coef_}")
print(f"模型训练偏置{lr.intercept_}")
生成矩阵实际权重[49.9292395 98.65669029]
模型训练权重[49.60128928 98.63917225]
模型训练偏置1.0023002044412217
import matplotlib as mpl
import matplotlib.pyplot as plt
#Axes3D 用来绘制3D图形的类。
from mpl_toolkits.mplot3d import Axes3D
mpl.rcParams["font.family"] = "SimHei"
mpl.rcParams["axes.unicode_minus"]=False
# 设置绘图后,以弹出框的形式显示。因为嵌入显示(默认)的形式,图像为一张图片,
# 无法进行拖动而转换角度。这在三维图中不利于观察。
%matplotlib qt
# 分别返回x1与x2两个特征的最大值。
max1,max2 = np.max(X,axis=0)
# 分别返回x1与x2两个特征的最小值。
min1, min2 = np.min(X, axis=0)
#在x1轴的区间上取若干点。
x1 = np.linspace(min1, max1, 30)
# 在x2轴的区间上取若干点。
x2 = np.linspace(min2, max2, 30)
# 接收两个一维数组。进行网格扩展。过程:将x1沿着行进行扩展,扩展的行数与x2含有的元素
# 个数相同。将x2沿着列进行扩展,扩展的列数与x1含有的元素个数相同。返回扩展之后的X1与X2(并非
# 就地修改)。X1与X2的形状(shape)是相同的。
# 扩展的目的:依次对位取出X1与X2中的每个元素,就能够形成x1与x2(原始的一维数组x1与x2)中任意
# 两个元素的组合,进而就能够形成网格的结构。
X1, X2 = np.meshgrid(x1, x2)
# 创建一个画布对象,用于绘制图像。
fig = plt.figure()
# 创建Axes3D对象,用于绘制3D图。参数指定画布对象,表示要在哪个
# 画布上进行绘制。(要绘制图像,就不能离开画布对象的支持。)
ax = Axes3D(fig)
# 绘制3D散点图。
ax.scatter(X[:,0], X[:,1], y,color="b")
# 绘制3D空间平面(曲面)。预测的y_hat值需要与X1或X2的形状相同。
# rstride 在行(row)的方向上前进的距离。
# cstride 在列(column)的方向上前进的距离。
# 距离越大,网格越宽。
# cmap colormap,指定绘制的风格。可以通过plt.cm.颜色图对象进行指定。
# alpha 指定透明度。
surf = ax.plot_surface(X1, X2, lr.predict(np.array([X1.ravel(),X2.ravel()]).T).reshape(X1.shape),
rstride = 5, cstride = 5 ,cmap = plt.cm.rainbow, alpha = 0.5)
fig.colorbar(surf)
plt.show()