向量
描述向量
向量($Vector$)是有大小和方向的量, 只有大小没有方向的量称为标量($Scalar$), 或者数量/纯量.
对于向量$\vec{u}=\overrightarrow{OA}$, 我们用有向线段$OA$表示, 线段$OA$的长度表示向量的大小, 端点的顺序$O\to A$表示了向量的方向.
大小相等方向一致的向量称为相等向量.
在直角坐标系中, 向量$\overrightarrow{OA}$唯一对应一个有向线段, 唯一对应一对有序数字$(x,y)$.
在线性代数中, 用数字序列表示向量的写法有两种:
- 行向量: 例如$\vec{a}=(a_1,a_2,a_3)$
- 列向量: 例如$\vec{b}=\begin{pmatrix}b_1\\b_2\\b_3\end{pmatrix}$
我们一般使用列向量来表述
三维空间$\mathbb{R}^3$中有$\vec u=\begin{pmatrix}x_1\\x_2\\x_3\end{pmatrix}=(x_1,x_2,x_3)^T$, $T$表示转置.
$n$维空间$\mathbb{R}^n$中有$\vec u=\begin{pmatrix}x_1\\\vdots\\x_n\end{pmatrix}=(x_1,\cdots,x_n)^T$
在Python中有多种表示向量的方法, 比如用numpy
库中用数组对象表示向量:
import numpy as np
u=np.array([1,3,5])
print(u) #输出[1,3,5]
上面创建了一维数组$\boldsymbol{u}$, 表示的就是行向量, 如果想要表示列向量, 则:
v=u.reshape(-1,1) #事实上[[x1],[x2],[x3]]的写法就是列向量的写法
print(v)
'''
输出
[[1]
[3]
[5]]
'''
在机器学习中, 用数组表示的向量存在广泛, 这是因为向量能够提升运算速度, 例如创建一个由随机整数组成的列表, 计算列表中每个整数的平方:
import random,time
list1 = [random.randint(1,1000) for i in range(100000)]
start1 = time.time()
list2 = [i * i for i in list1] #列表解析来计算每个数的平方
end1 = time.time()
print(f"列表解析用时: {end1 - start1}s")
vlist=np.array(list1) #将列表转换为数组
start2 = time.time()
vlist2 = vlist1 * vlist1 #用数组相乘计算每个数的平方
end2 = time.time()
print(f"向量运算用时: {end1 - start1}s")
print(f"列表解析用时是向量运算用时的 {round((end1-start1)/(end2-start2), 3)} 倍")
'''
列表解析用时: 0.001995086669921875s
向量运算用时: 0.0009984970092773438s
列表解析用时是向量运算用时的 1.998 倍
'''
输出结果视电脑性能有所差距( 本来写的randint(1,100)
结果算的太快输出给了个0.0秒没绷住), 由此可见向量化的优势!
向量加法
向量加法的集合描述就是三角形法则和平行四边形法则, 解析法的话, 就是各个坐标相加
例如$\boldsymbol{u}=\begin{pmatrix}1\\2\end{pmatrix},\boldsymbol{v}=\begin{pmatrix}3\\4\end{pmatrix}$,则$\boldsymbol{u}+\boldsymbol{v}=\begin{pmatrix}1\\2\end{pmatrix}+\begin{pmatrix}3\\4\end{pmatrix}=\begin{pmatrix}1+2\\3+4\end{pmatrix}=\begin{pmatrix}3\\7\end{pmatrix}$
具体的原理就是正交分解, 这里不再赘述
定义: $n$维空间$\mathbb{R}^n$的两个向量$\boldsymbol{u}=\begin{pmatrix}x_1\\\vdots\\x_n\end{pmatrix}$和$\boldsymbol{v}=\begin{pmatrix}y_1\\\vdots\\y_n\end{pmatrix}$相加:
$$
\boldsymbol{u+v}=\begin{pmatrix}x_1+y_1\\\vdots\\x_n+y_n\end{pmatrix}
$$
同理可定义向量减法:
$$
\boldsymbol{u-v}=\boldsymbol{u}+(-\boldsymbol{v})
$$
$-\boldsymbol{v}$即将$\boldsymbol{v}$反向.
在numpy
中可以用数组的加减实现向量的加减:
print(np.array([[1],[2]]) + np.array([[3],[4]])
print(np.array([[1],[2]]) - np.array([[3],[4]])
'''
[[4]
[6]]
[[-2]
[-2]]
'''
向量的数乘
向量的数乘就是用一个标量乘一个向量, 此时向量的大小得到了缩放, 即$k\boldsymbol{r}$.
在numpy
中可以通过标量和数组的乘积实现数乘运算:
print(2 * np.array([[1], [3]]))
print(-1 * np.array([[1],[3]]))
'''
[[2]
[6]]
[[-1]
[-3]]
'''
定义: $k$为实数, $n$维空间$\mathbb{R}^n$的向量$\boldsymbol{u}=\begin{pmatrix}x_1\\\vdots\\x_n\end{pmatrix}$的数乘:
$$
k\boldsymbol{u}=\begin{pmatrix}kx_1\\\vdots\\kx_n\end{pmatrix}
$$
向量空间
什么是向量空间
对于二维空间中的向量:
- 两个向量相加得到的向量仍然在这个二维平面空间中
- 通过数乘得到的向量仍然在这个二维平面空间中
那有没有其他情况呢, 显然是有的, 这里我们引入一个定义, 即叉乘(矢量积): $\boldsymbol{u}$和$\boldsymbol{v}$是二维空间中的两个向量, 则
$$
\boldsymbol{u}\times\boldsymbol{v}=(uv\sin\theta)\boldsymbol{k}
$$
这里$u$和$v$是向量$\boldsymbol{u}$和$\boldsymbol{v}$的大小, $\boldsymbol{k}$是通过右手定则所确定的叉积方向, $\theta$是向量的夹角(箭头夹的角).
这时得到的向量已经不在这个平面, 而是垂直于这个平面.
在线性代数中,我们把如同向量加法和数乘一样, 计算结果的向量和已知向量在同一个空间, 称为加法和数乘封闭.
抽象为严格的数学定义, 即:
定义: 设$\mathbb{V}$是一个非空集合, $\mathbb{K}$是一个数域, 在$\mathbb{V}$中定义有:
- 加法: $\forall\boldsymbol{a},\boldsymbol{b}\in\mathbb{V}$, 则$\boldsymbol{a}+\boldsymbol{b}\in\mathbb{V}$;
- 数乘:$\forall\boldsymbol{a}\in\mathbb{V}$, $\forall k\in\mathbb{K}$, 则$k\boldsymbol{a}\in\mathbb{V}$
并且满足以下运算法则:
- $\boldsymbol{a}+\boldsymbol{b}=\boldsymbol{b}+\boldsymbol{a}$
- $\forall{\boldsymbol{c}}\in\mathbb{V}, (\boldsymbol{a}+\boldsymbol{b})+\boldsymbol{c}=\boldsymbol{a}+(\boldsymbol{b}+\boldsymbol{c})$
- $\mathbb{V}$中有一个零元素$\mathbf{0}$, 使得$\boldsymbol{a}+\mathbf{0}=\boldsymbol{a}$
- 对于$\forall \boldsymbol{a}\in \mathbb{V}$, 存在$\boldsymbol{a}$的负元素$\boldsymbol{d}$, 使得$\boldsymbol{a}+\boldsymbol{d}=\mathbf{0}$
- $1\boldsymbol{a}=\boldsymbol{a}$
- $(kl)\boldsymbol{a}=k(l\boldsymbol{a}),\forall k,l\in\mathbb{K}$
- $(k+l)\boldsymbol{a}=k\boldsymbol{a}+l\boldsymbol{a}$
- $k(\boldsymbol{a}+\boldsymbol{b})=k\boldsymbol{a}+k\boldsymbol{b}$
那么我们称$\mathbb{V}$是数域$\mathbb{K}$的一个线性空间. 如果线性空间的元素为向量, 那么线性空间又叫向量空间.
如无特殊说明, 以后我们谈论的数域皆为实数域.
线性组合
如果一个向量可以用其他向量的加减法或者数乘来表示, 我们把这个表达式称为这些向量的线性组合.
定义: 设$\boldsymbol{v_1},\boldsymbol{v_2},\cdots,\boldsymbol{v_n}$是$\mathbb{R^n}$中有限个向量, 称作一个向量组, 则
$$
c_1\boldsymbol{v_1}+c_2\boldsymbol{v_2}+\cdots+c_n\boldsymbol{v_n}
$$
称为向量组$\boldsymbol{v_1},\boldsymbol{v_2},\cdots,\boldsymbol{v_n}$的一个线性组合, 这里$c_1,c_2,\cdots,c_n$是标量, 称为系数.
我们可以利用线性方程组来理解线性组合:
$$
\begin{cases}
x_1-x_2+x_3&=0\\
x_1-3x_2+2x_3&=0\\
2x_1-x_2-2x_3&=0
\end{cases}
$$
我们可以改写为:
$$
x_1\begin{pmatrix}1\\1\\2\end{pmatrix}+x_2\begin{pmatrix}-1\\-3\\-1\end{pmatrix}+x_3\begin{pmatrix}1\\2\\-2\end{pmatrix}=\begin{pmatrix}0\\0\\0\end{pmatrix}
$$
左式显然是一个线性组合.
线性无关
定义: 设$\boldsymbol{v_1},\boldsymbol{v_2},\cdots,\boldsymbol{v_m}$是$\mathbb{R}^n$中的$m$个向量(即向量组),如果存在一组不全为零的常数$k_1,k_2,\cdots,k_m$使得
$$
k_1\boldsymbol{v_1}+k_2\boldsymbol{v_2}+\cdots+k_m\boldsymbol{v_m}=0
$$
则称$\boldsymbol{v_1},\boldsymbol{v_2},\cdots,\boldsymbol{v_m}$线性相关, 否则称为线性无关.
即如果根据$k_1\boldsymbol{v_1}+k_2\boldsymbol{v_2}+\cdots+k_m\boldsymbol{v_m}=0$只能得到$k_1=k_2=\cdots=k_m$, 则向量组$\boldsymbol{v_1},\boldsymbol{v_2},\cdots,\boldsymbol{v_m}$是线性无关的.
由线性无关的定义可以推出很多结论, 这里列出一些, 具体请参阅线性代数/高等代数类教材.
- 如果$\boldsymbol{v_1},\boldsymbol{v_2},\cdots,\boldsymbol{v_m}$线性无关, 那么它的任意部分向量也线性无关;
- 如果$\boldsymbol{v_1},\boldsymbol{v_2},\cdots,\boldsymbol{v_m}$有一部分向量线性相关, 则这个向量组线性相关;
- 含有零向量的向量组一定线性相关.
向量组中向量的线性相关或线性无关分析是线性代数和机器学习中的重要内容, 限于篇幅这里不再介绍, 读者大多是有线性代数基础的, 这里不再阐述.
子空间
任何维度的空间都包含包含了无穷个向量, 假设$\boldsymbol{v_1},\boldsymbol{v_2},\cdots,\boldsymbol{v_m}$是$\mathbb{R}^n$中的一个向量组, $k_1,k_2,\cdots,k_m$是实数, 那么有:
$$
\mathbb{R}^m=\{k_1\boldsymbol{v_1}+k_2\boldsymbol{v_2}+\cdots+k_m\boldsymbol{v_m}|k_1,k_2,\cdots,k_m\in\mathbb{R}\}
$$
由于$\mathbf{0}\in\mathbb{R}^n$,则$\mathbb{R}^m$是$\mathbb{R}^n$的非空子集.
$\mathbb{R}^m$符合加法封闭和数乘封闭(这里不再证明), 且它是由向量$\boldsymbol{v_1},\boldsymbol{v_2},\cdots,\boldsymbol{v_m}$张成的,故称$\mathbb{R}^m$是$\mathbb{R}^n$的一个线性子空间, 简称子空间.
基和维数
极大线性无关组
定义:如果向量组$A$中的每个向量都可以用向量组$B$中的向量以线性组合的形式表出(线性表出), 反之, 向量组$B$也可用向量组$A$中的向量线性表出, 那么这两个向量组等价.
定义: 某向量组的一个部分组, 如果满足:
- 这个部分组线性无关;
- 从向量组的其余向量中(如果有)任意添加一个进去, 得到的新的部分组线性相关.
那么我们称这个部分组为此向量组的一个极大线性无关组.
定义:向量组的一个极大线性无关组所含向量的个数, 称为这个向量组的秩(rank), 如向量组${\boldsymbol{u_1},\cdots,\boldsymbol{u_n}}$的秩记为$rank\{\boldsymbol{u_1},\cdots,\boldsymbol{u_n}\}$
基
对于二维空间, 任何一个向量都可以用两个线性无关的向量线性表出.
三维空间也有类似的结论, 我们可以把这个结论推广到任何向量空间.
如果我们可以找到一个极大线性无关的向量组, 用它可线性表出这个向量空间的任何一个向量, 那么称这个极大线性无关组为该向量空间的一个基.
对于一个基, 其向量长度都为1, 且彼此正交(垂直), 那么将其称为标准基(标准正交基). 例如$\mathbb{R}^n$的一个标准基为:
$$
\boldsymbol{v_1}=\begin{pmatrix}1\\0\\0\\\vdots\\0\\0\end{pmatrix},\boldsymbol{v_2}=\begin{pmatrix}0\\1\\0\\\vdots\\0\\0\end{pmatrix},\cdots,\boldsymbol{v_n}=\begin{pmatrix}0\\0\\0\\\vdots\\0\\1\end{pmatrix}
$$
向量空间的基不止一个, 但类似于该式的基称为向量空间的默认基.
以基中每个向量所在方向的直线为坐标轴就创建了一个坐标系, 不同的基创建的坐标系是不同的.
如果${\boldsymbol{\alpha_1},\cdots,\boldsymbol{\alpha_n}}$是某个向量空间的一个基, 则该空间的一个向量$\overrightarrow{OA}$可以描述为:
$$
\overrightarrow{OA}=x_1\boldsymbol{\alpha_1}+x_2\boldsymbol{\alpha_2}+\cdots+x_n\boldsymbol{\alpha_n}
$$
其中$(x_1,\cdots,x_n)$即为向量$\overrightarrow{OA}$在基${\boldsymbol{\alpha_1},\cdots,\boldsymbol{\alpha_n}}$的坐标.
如果这里存在另外一个基${\boldsymbol{\beta_1},\cdots,\boldsymbol{\beta_n}}$, 向量$\overrightarrow{OA}$又可以描述为:
$$
\overrightarrow{OA}=x_1’\boldsymbol{\beta_1}+x_2’\boldsymbol{\beta_2}+\cdots+x_n’\boldsymbol{\beta_n}
$$
由于他们两个在同一个向量空间, 那么${\boldsymbol{\beta_1},\cdots,\boldsymbol{\beta_n}}$可以用${\boldsymbol{\alpha_1},\cdots,\boldsymbol{\alpha_n}}$线性表出, 即:
$$
\begin{cases}
\boldsymbol{\beta_1}=b_{11}\boldsymbol{\alpha_1}+\cdots+b_{1n}\boldsymbol{\alpha_n}\\
\vdots\\
\boldsymbol{\beta_n}=b_{11}\boldsymbol{\alpha_1}+\cdots+b_{nn}\boldsymbol{\alpha_n}
\end{cases}
$$
用矩阵表示, 即:
$$
\begin{pmatrix}\boldsymbol{\beta_1}\\\vdots\\\boldsymbol{\beta_n}\end{pmatrix}=\begin{pmatrix}b_{11}\quad\cdots\quad b_{1n}\\\vdots\quad\ddots\quad\vdots\\b_{n1}\quad\cdots\quad b_{nn}\end{pmatrix}\begin{pmatrix}\boldsymbol{\alpha_1}\\\vdots\\\boldsymbol{\alpha_n}\end{pmatrix}
$$
其中:
$$
\boldsymbol{P}=\begin{pmatrix}b_{11}\quad\cdots\quad b_{1n}\\\vdots\quad\ddots\quad\vdots\\b_{n1}\quad\cdots\quad b_{nn}\end{pmatrix}
$$
称为基${\boldsymbol{\alpha_1},\cdots,\boldsymbol{\alpha_n}}$向${\boldsymbol{\beta_1},\cdots,\boldsymbol{\beta_n}}$的过渡矩阵. 过渡矩阵实现了一个基向另一个基的变换.
在同一个向量空间, 由基$\boldsymbol{[\alpha]}$向基$\boldsymbol{[\beta]}$的过渡矩阵是$\boldsymbol{P}$, 则
$$
\boldsymbol{[\beta]}=\boldsymbol{P}\boldsymbol{[\alpha]}
$$
在同一个向量空间, 由基$\boldsymbol{[\alpha]}$向基$\boldsymbol{[\beta]}$的过渡矩阵是$\boldsymbol{P}$, 某向量在基$\boldsymbol{[\alpha]}$下的坐标是$\boldsymbol{x}$, 在基$\boldsymbol{[\beta]}$下的坐标是$\boldsymbol{x’}$, 则他们之间的关系是:
$$
\boldsymbol{x}=\boldsymbol{P}\boldsymbol{x’}
$$
维数
空间中的基的向量个数称为此空间的维数(Dimension)
在向量空间中, 根据基所创建的坐标轴的数量与该空间的维数相同.
在机器学习中, 维这个词与线性代数的维略有不同, 接下来我们将阐述.
三维空间的维数是3, 其中某个向量可以写为$\boldsymbol{u}=\begin{pmatrix}2\\3\\4\end{pmatrix}$, 用numpy
的数组对象有两种方式可以表示:
import numpy as np
u1=np.array([2,3,4])
print(u1.ndim) #输出数组的维数是1
u2=np.array([[2],[3],[4]])
print(u2.ndim) #输出数组的维数是2
注意上面两种创建数组的方法有所不同.
可以看出数组的维数是针对数组对象而言的(简单理解即嵌套层数), 而不是数组对象对应的向量维数.
我们在进行数据清理和特征工程操作时, 有一种说法叫做数组降维, 这里的维指的是什么呢?
iris
是机器学习领域有名的鸢尾花数据集. 我们在后面会进行讲解. 由于其太过经典, 一些库都进行了集成, 如可视化库 seaborn
, 机器学习库sklearn
import seaborn
iris=seaborn.load_dataset('iris') # 加载iris数据集
print(iris.head())
输出
sepal_length sepal_width petal_length petal_width species
0 5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
3 4.6 3.1 1.5 0.2 setosa
4 5.0 3.6 1.4 0.2 setosa
这里输出的是从数据集中随机抽取的样本, 每一列表示了鸢尾花的一个属性, 例如sepal_length
表示花萼长度, 在机器学习中称这些属性为特征或者维, 所谓降维指的就是减少列的数量.
内积空间
什么是内积空间
定义: 实数域上的向量空间$\mathbb{V}$是一个函数, 用$\langle\boldsymbol{u},\boldsymbol{v}\rangle$表示, 且$\boldsymbol{u},\boldsymbol{v}$是$\mathbb{V}$中的向量,$\langle\boldsymbol{u},\boldsymbol{v}\rangle$得到的是一个实数. 称$\langle\boldsymbol{u},\boldsymbol{v}\rangle$为内积.
且内积有如下公理:
- $\langle\boldsymbol{u},\boldsymbol{v}\rangle=\langle\boldsymbol{v},\boldsymbol{u}\rangle$
- $\langle\boldsymbol{u+v},\boldsymbol{w}\rangle=\langle\boldsymbol{u},\boldsymbol{w}\rangle+\langle\boldsymbol{v},\boldsymbol{w}\rangle$
- $\langle c\boldsymbol{u},\boldsymbol{v}\rangle=c\langle\boldsymbol{u},\boldsymbol{v}\rangle$
- $\langle\boldsymbol{u},\boldsymbol{v}\rangle\geq0$, 当且仅当$\boldsymbol{u}=\boldsymbol{0}$时$\langle\boldsymbol{u},\boldsymbol{v}\rangle$=0
一个赋予了以上内积的向量空间称为内积空间
不过我们并没有定义这个函数的具体形式, 只要符合公理的我们都称为内积.
点积和欧几里得空间
记内积空间的两个向量$\boldsymbol{u}=\begin{pmatrix}u_1\\\vdots\\u_n\end{pmatrix},\boldsymbol{v}=\begin{pmatrix}v_1\\\vdots\\v_n\end{pmatrix}$,我们将他们的内积定义为:
$$
\langle\boldsymbol{u},\boldsymbol{v}\rangle=u_1v_1+u_2v_2+\cdots+u_nv_n
$$
这个内积也是符合内积的定义的, 这样就构成了一个内积空间, 也就是我们常说的欧几里得空间
通常我们也将这个内积写为:
$$
\boldsymbol{u\cdot v}=u_1v_1+u_2v_2+\cdots+u_nv_n
$$
我们也将其称为点积, 不要混淆点积和内积哦.
在二维空间, 点积可以写为$\boldsymbol{u\cdot v}=uv\cos\theta$,$\theta$是两个向量的夹角.
手工计算点积相信各位已经相当熟练, 这里演示如何用程序实现点积运算.
import numpy as np
a = np.array([1,3,5])
b = np.array([2,4,6])
print(np.dot(a,b)) #输出44
numpy
的inner
函数也能实现点积运算
print(np.inner(a,b)) #输出44
但是对于非一维数组np.inner()
和np.dot()
的计算结果又有所不同:
c = np.array([[1,3],[5,7]])
d = np.array([[2,4],[6,8]])
print(np.dot(c,d))
'''
[[20 28]
[52 76]]
'''
print(np.inner(c,d))
'''
[[14 30]
[38 86]]
'''
这是由于他们的运算法则有所不同, np.dot()
计算过程如下(与矩阵乘法一致):
$$
\begin{pmatrix}1\quad2\\3\quad4 \end{pmatrix}\cdot\begin{pmatrix}5\quad6\\7\quad 8\end{pmatrix}=\begin{pmatrix}1\times5+2\times7\quad1\times6+2\times8\\3\times5+4\times7\quad3\times6+4\times8\end{pmatrix}=\begin{pmatrix}19\quad22\\43\quad50\end{pmatrix}
$$np.inner()
的计算过程如下:
$$
\begin{pmatrix}1\quad2\\3\quad4 \end{pmatrix}\cdot\begin{pmatrix}5\quad6\\7\quad 8\end{pmatrix}=\begin{pmatrix}1\times5+2\times6\quad1\times7+2\times8\\3\times5+4\times6\quad3\times7+4\times8\end{pmatrix}=\begin{pmatrix}17\quad23\\39\quad53\end{pmatrix}
$$
注意比较二者差异.
此外, pandas
库的Series
对象和DataFrame
对象也有dot()
方法来实现点积运算.
距离和角度
距离
向量之间的距离指的是它们的终点(对应于坐标系中的两个点)之间的距离.
定义:内积空间中的向量$\boldsymbol{u,v}$之间的距离记为$d(\boldsymbol{u},\boldsymbol{v})=\boldsymbol{u}-\boldsymbol{v}$, 定义为:
$$
\boldsymbol{u}-\boldsymbol{v}=\sqrt{\langle(\boldsymbol{u}-\boldsymbol{v}),(\boldsymbol{u}-\boldsymbol{v})\rangle}
$$
由此可见距离的具体计算方法是由内积函数的形式确定的.
欧几里得距离
根据点积的定义, 得到的两个向量之间的距离称为欧几里得距离.
定义: 设$\boldsymbol{u}=\begin{pmatrix}u_1\\\vdots\\u_n\end{pmatrix}$和$\boldsymbol{v}=\begin{pmatrix}v_1\\\vdots\\v_n\end{pmatrix}$是欧几里得空间的两个向量, 它们之间的距离为
$$
d(\boldsymbol{u},\boldsymbol{v})=\left|\boldsymbol{u}-\boldsymbol{v}\right|=\sqrt{(\boldsymbol{u}-\boldsymbol{v})\cdot(\boldsymbol{u}-\boldsymbol{v})}=\sqrt{(u_1-v_1)^2+\cdots+(u_n-v_n)^2}
$$
在三维空间和二维空间中这个就是我们熟悉的两点间的距离.
程序计算的方法如下:
import numpy as np
vec1 = np.array([1,2])
vec2 = np.array([4,6])
dist = np.linalg.norm(vec1-vec2)
print(dist) #输出欧几里得距离即5.0
我们还有其他类型的距离.
曼哈顿距离
曼哈顿距离也称为出租车距离或者城市街区距离.
想象一下在方方正正的街区从一个点到不在同一条直线的另一个点, 需要经过其他的端点, 依据以此闵可夫斯基命名了曼哈顿距离.
定义:设$\boldsymbol{u}=\begin{pmatrix}u_1\\\vdots\\u_n\end{pmatrix}$和$\boldsymbol{v}=\begin{pmatrix}v_1\\\vdots\\v_n\end{pmatrix}$是两个向量, 这两个向量之间的曼哈顿距离为
$$
d(\boldsymbol{u},\boldsymbol{v})=u_1-v_1+\cdots+u_n-v_n=\sum_{i=1}^{n}|u_i-v_i|
$$
在Python中有个同numpy
一样重要的库SciPy
, 里面有曼哈顿距离计算函数cityblock()
from scipy.spatial.distance import cityblock
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(cityblock(a, b)) # 输出曼哈顿距离即9
切比雪夫距离
定义: 设$\boldsymbol{u}=\begin{pmatrix}u_1\\\vdots\\u_n\end{pmatrix}$和$\boldsymbol{v}=\begin{pmatrix}v_1\\\vdots\\v_n\end{pmatrix}$是两个向量, 这两个向量之间的切比雪夫距离为
$$
d(\boldsymbol{u},\boldsymbol{v})=\max_{i}({|u_i-v_i|})
$$
即$\boldsymbol{u}$和$\boldsymbol{v}$的对应坐标差的绝对值集合的最大值.
切比雪夫距离的另一种等价形式为
$$
d(\boldsymbol{u},\boldsymbol{v})=\lim_{p\to\infty}\left(\sum_{i=1}^n|u_i-v_i|^p\right)^{\frac{1}{p}}
$$
可以用scipy.spatial.distance
中的chebyshev()
来实现.
闵可夫斯基距离
更一般的我们有闵可夫斯基距离:
定义: 设$\boldsymbol{u}=\begin{pmatrix}u_1\\\vdots\\u_n\end{pmatrix}$和$\boldsymbol{v}=\begin{pmatrix}v_1\\\vdots\\v_n\end{pmatrix}$是两个向量, 这两个向量之间的闵可夫斯基距离为
$$
d(\boldsymbol{u},\boldsymbol{v})=\left(\sum_{i=1}^n|u_i-v_i|^p\right)^{\frac{1}{p}}
$$
- $p=1$, $d(\boldsymbol{u},\boldsymbol{v})=\sum_{i=1}^n|u_i-v_i|$, 即曼哈顿距离.
- $p=2$, $d(\boldsymbol{u},\boldsymbol{v})=(\sum_{i=1}^n|u_i-v_i|^2)^{\frac{1}{2}}$, 即欧几里得距离.
- $p\to \infty$, $(\boldsymbol{u},\boldsymbol{v})=\lim_{p\to\infty}(\sum_{i=1}^n|u_i-v_i|^p)^{\frac{1}{p}}$, 即切比雪夫距离.
基于距离的分类
在机器学习中分类是一项重要工作, 有一种重要的方法是根据两个样本的数据计算它们的距离, 距离越近代表他们的相似度越高, 归为一类的概率就越大.
下面从鸢尾花数据集中, 我们选取三个样本, 两个样本的species值都是setosa, 即同一种花卉, 另外一个样本的species值是versicolor
import seaborn as sns
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
iris = sns.load_dataset('iris')
seto1 = iris.iloc[7]
seto2 = iris.iloc[28]
vers = iris.iloc[72]
df = pd.DataFrame([seto1, seto2, vers])
print(df)
输出
sepal_length sepal_width petal_length petal_width species
7 5.0 3.4 1.5 0.2 setosa
28 5.2 3.4 1.4 0.2 setosa
72 6.3 2.5 4.9 1.5 versicolor
然后分别计算两个setosa的样本的欧几里得距离和versicolor与其中一个setosa的欧几里得距离
X = df.iloc[:,:-1] # 剔除标签特征, 得到除了标签特征 species 之外的数据
# 两个 setosa 间的距离
dist_seto = np.linalg.norm(X.iloc[1] - X.iloc[0])
print(dist_seto) #0.22360679774997916
# versicolor 与一个 setosa 的距离
dist_vers = np.linalg.norm(X.iloc[2] - X.iloc[0])
print(dist_vers) #3.968626966596886
不同类别的花卉之间的欧几里得距离不同, 为了更直观的观察, 选取两个特征petal_length和petal_width, 绘制出每个样本:
sns.scatterplot(data=iris, x="petal_length", y="petal_width", hue="species", style="species")
plt.show()
如图, 同一类别的鸢尾花聚集到一起. 我们在每个类别中选取一个相对较为中心的位置画一个圆, 如果某个样本到中心的距离不超过圆的半径, 我们就认为该样本属于这个中心所在的类别.
机器学习中的$k-NN$算法($k-$最邻近算法)就是由此发展起来的, 我们以后也会进行介绍, 这里仅通过sklearn
库进行演示
KNeighborsClassifier(algorithm='auto', leaf_size=30, metric='minkowski',
metric_params=None, n_jobs=None, n_neighbors=5, p=2,
weights='uniform')
这里KNeighborClassifier
就是实现$k-NN$算法的模型. 参数metric='minkowski'
表示默认使用闵可夫斯基距离, p=2
表示使用欧几里得距离.
from sklearn.datasets import load_iris #鸢尾花是经典的数据集,很多库都集成
from sklearn.neighbors import KNeighborsClassifier # 引入k-NN模型
iris = load_iris()
X = iris.data
y = iris.target
knn_l1 = KNeighborsClassifier(p=1) # 基于曼哈顿距离
knn_l2 = KNeighborsClassifier(p=2) # 基于欧几里得距离
# 训练模型
knn_l1.fit(X, y)
knn_l2.fit(X, y)
flower_l1 = knn_l1.predict([[2.7, 5.2, 6.3, 0.2]])
flower_l2 = knn_l2.predict([[2.7, 5.2, 6.3, 0.2]])
flower_l1_name = iris.target_names[flower_l1]
flower_l2_name = iris.target_names[flower_l2]
print("the instance [2.7, 5.2, 6.3, 0.2] is:")
print(flower_l1_name.item(), " by Manhattan Distance;")
print(flower_l2_name.item(), " by Euclidean Distance.")
输出
the instance [2.7, 5.2, 6.3, 0.2] is:
virginica by Manhattan Distance;
versicolor by Euclidean Distance.
范数和正则化
向量的终点到起点的距离称为向量的长度即范数, 即$\left|\boldsymbol{u}\right|=\sqrt{\langle\boldsymbol{u}, \boldsymbol{u}\rangle}$
在欧几里得空间, 由点积的定义, 欧几里得范数 (又称$l_2$范数) 的计算方法为
$$
\left|\boldsymbol{u}\right|_2=\sqrt{\boldsymbol{u}\cdot \boldsymbol{u}}=\sqrt{u_1^2+\cdots+u_n^2}
$$
同理, 曼哈顿范数 ($l_1$范数) , 闵可夫斯基范数, 切比雪夫范数为
$$
\left|\boldsymbol{u}\right|_1=u_1+\cdots+u_n=\sum_{i=1}^nu_i\
\left|\boldsymbol{u}\right|_q=\left(\sum_{i=1}^{n}\left|u_i\right|^q\right)^{\frac{1}{q}}\
\left|\boldsymbol{u}\right|_{\infty}=\max_{i}|u_i|
$$
在Python中, numpy
中的numpy.linalg.norm()
可以计算范数
import numpy as np
a = np.array([[3], [4]])
L1 = np.linalg.norm(a, ord=1) #ord=1表示计算l_1范数即曼哈顿范数, 不设置就默认为ord=2
print(L1) #输出7.0
在机器学习中模型参数过多或者过于复杂会出现一种过拟合的现象, 如何解决过拟合呢, 一个简单的想法就是引入一个正则项, 这里仅讨论引入$l_1,l_2$范数作为正则项的结果:
我们有一个机器学习模型$f$, 数据集$\boldsymbol{D}=[(\boldsymbol{x_1},y_1),\cdots,(\boldsymbol{x_n},y_n)]$, 其中$\boldsymbol{x_i}$是观测得到的数据作为模型自变量, $y_i$是每个样本的观测标签作为模型响应变量. 如果将$\boldsymbol{x_i}$输入给模型$f$, 所得到的就是观测结果(预测值) $\hat y_i$, 即
$$
\hat y_i=f(\boldsymbol{x_i},\boldsymbol{\theta})
$$
这里的$\boldsymbol\theta$就是模型的参数.
我们可以定义一个损失函数考察预测值和观测值的差异, 例如:
$$
\mathrm{loss}(y_i,\hat y_i)=(y_i-\hat y_i)^2
$$
我们希望平均损失函数越小越好, 即:
$$
\min_\theta\frac{1}{n}\sum_{i=1}^{n}(y_i-f(\boldsymbol{x_i},\boldsymbol{\theta}))^2
$$
为了使损失参数最小化人就会不断调整参数进而导致了过拟合, 实际中避免过拟合的方法比较多, 如增加数据量, 交叉验证等, 这里我们选择建立一个简单的线性回归模型$f(\boldsymbol{x},\boldsymbol{\theta})=\boldsymbol{X\theta}$, 其中$\boldsymbol{X}$是由$\boldsymbol{x_i}$组成的矩阵, 此时有
$$
\min_\theta\frac{1}{n}\left|\boldsymbol{y}-\boldsymbol{X\theta}\right|^2
$$
正则化是针对此类模型最常用的避免过拟合的方法, 我们加入惩罚项$\lambda J(\boldsymbol{\theta})$:
$$
\min_\theta\frac{1}{n}\left|\boldsymbol{y}-\boldsymbol{X\theta}\right|^2+\lambda J(\boldsymbol{\theta})
$$
对于不同的模型, $J(\boldsymbol{\theta})$也会有不同的形式:
- $J(\boldsymbol{\theta})=\left|\boldsymbol{\theta}\right|_1$的线性回归称为$\textbf{LASSO}$回归;
- $J(\boldsymbol{\theta})=\left|\boldsymbol{\theta}\right|_2$的线性回归称为岭回归(Ridge回归)
更进一步的讨论我们以后再说.
角度
定义: 设欧几里得空间中的两个非零向量$\boldsymbol{u},\boldsymbol{v}$, 它们的夹角余弦值为
$$
\cos\theta=\frac{\boldsymbol{u}\cdot\boldsymbol{v}}{\left|\boldsymbol{u}\right|\left|\boldsymbol{v}\right|}, (0\leq \theta\leq\pi)
$$
推广到内积空间我们有
$$
\cos\theta=\frac{\langle\boldsymbol{u}, \boldsymbol{v}\rangle}{\left|\boldsymbol{u}\right|\left|\boldsymbol{v}\right|}
$$scipy.spatial.distance
中的函数cosine()
可以计算两个向量的余弦值(实际计算的是$1-\cos\theta$)
import numpy as np
from scipy.spatial.distance import cosine
a = np.array([1,0,0])
b = np.array([0,1,0])
print(cosine(a, b)) #1.0
上面的向量的夹角余弦值为0, 在欧几里得空间中称为正交.