gitbook/重学线性代数/docs/265757.md
2022-09-03 22:05:03 +08:00

277 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 01 | 导读:如何在机器学习中运用线性代数工具?
你好,我是朱维刚。欢迎你跟我一起重学线性代数!
在开篇词中,我和你大致讲过我自己的经历,从2006年开始到现在14年的时间里,我都专注于机器学习领域。对于**线性代数**在机器学习中的应用,我非常了解。而这也是线性代数最主要的应用场景之一。因此,今天第一节课,我想先和你聊一聊,如何在机器学习中运用线性代数工具,在我们开始自下而上的学习之前,先从上层来看一看。
我们都知道,“数据”是机器学习的前提,机器学习的第一步就是要进行**数据**的收集、预处理和特征提取;而**模型**就是通过数据来学习的算法;**学习**则是一个循环过程,一个自动在数据中寻找模式,并不停调优模型参数的过程。那我们就从机器学习的三个核心概念:数据、模型和学习说起。
![](https://static001.geekbang.org/resource/image/3a/32/3a2a7433d5d13b676abe05041a1bcd32.png)
你看,不论是模型,还是学习,都涉及数据,而数据加上模型和学习,就是数学的一般过程了,也就是:观察、实验、推理和抽象。所以,我认为学好数学,不仅有利于理解复杂的机器学习系统,还能调优算法参数,甚至能帮助你创建新的机器学习解决方案。
## 从机器学习到线性代数
那机器学习和线性代数之间到底有着怎样的关系呢?我想,用一个实际的机器学习算法的例子来解释,你可能更容易搞清楚。接下来,我使用KNN(K-Nearest Neighbor,K最近邻分类算法)来让你简单了解一下机器学习,以及它和线性代数之间的关系。
之所以选KNN分类算法,因为它是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。这个方法的思路是:如果一个样本在特征空间中的K个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。
这里有个前提,KNN算法中,所选择的“邻居”都是已经正确分类的对象。KNN分类算法在分类决策上只依据最邻近的一个或者几个样本的类别,来决定待分样本所属的类别。我们通过图来理解的话或许更容易一些。
![](https://static001.geekbang.org/resource/image/43/aa/439cefee464eb01ed110e70515f94eaa.png)
假设图片中那个绿色圆就要是我们要决策的对象,那么根据KNN算法它属于哪一类?是红色三角形还是蓝色四方形?
如果K=3(实线圆),也就是包含离绿色圆最近的3个,由于红色三角形所占比例为2/3,绿色圆就属于红色三角形那个类。但如果K=5(虚线圆),就是包含离绿色圆最近的5个,由于蓝色四方形比例为3/5,绿色圆就属于蓝色四方形那个类。
## 鸢尾花分类问题中的线性代数
通过前面这个小例子,你应该已经理解了KNN算法的概念。那么接下来,我们就试着使用KNN在给定鸢尾花特征值的情况下,给鸢尾花做花种分类,带你来实际看一下线性代数在这里起到的作用。
特别说明一下,**鸢尾花分类问题**是一个国际上通用的案例,一般都被作为机器学习入门来使用,所以它的数据集也是公开的。
### 1.数据集的收集、加载和分析
首先,我们要做的是数据集的收集、加载和分析,你也可以点击[这里](https://www.kaggle.com/notlir/iriscsv)下载原始数据集,来看看原始数据长什么样,下面是获取和加载数据的代码,sklearn数据集已经包含了样本数据,你可以直接用。
```
import pandas as pd
from sklearn import datasets
iris = datasets.load_iris()
species = [iris.target_names[x] for x in iris.target]
iris = pd.DataFrame(iris['data'], columns = ['Sepal_Length', 'Sepal_Width', 'Petal_Length', 'Petal_Width'])
iris['Species'] = species
```
从显示的结果,我们能够看出鸢尾花有四个特征:花萼的长、宽和花瓣的长、宽。我们来看下这四个特征的数据类型:
```
iris.dtypes
Sepal_Length float64
Sepal_Width float64
Petal_Length float64
Petal_Width float64
Species object
dtype: object
```
这些特征都是数值型,而且标签Species表示的是花种,是一个字符串类型的变量。我们继续看一下鸢尾花的分类统计:
```
iris['count'] = 1
iris[['Species', 'count']].groupby('Species').count()
```
![](https://static001.geekbang.org/resource/image/a7/ce/a7ff740c15de327cfd8c1c9a4b681cce.png)
这里我们直接能够看到,鸢尾花有三个花种,每个种类有50个实例,或者说50条数据,我们再用图来更直观地显示这三种鸢尾花。
```
%matplotlib inline
def plot_iris(iris, col1, col2):
import seaborn as sns
import matplotlib.pyplot as plt
sns.lmplot(x = col1, y = col2,
data = iris,
hue = "Species",
fit_reg = False)
plt.xlabel(col1)
plt.ylabel(col2)
plt.title('Iris species shown by color')
plt.show()
plot_iris(iris, 'Petal_Width', 'Sepal_Length')
plot_iris(iris, 'Sepal_Width', 'Sepal_Length')
```
![](https://static001.geekbang.org/resource/image/c2/93/c216f676f59e00cae4b52481fdf88293.png)![](https://static001.geekbang.org/resource/image/a8/0a/a8337b9d13c23ef18e3bd8a4dbb91b0a.png)
蓝、黄、绿,这三种颜色分别代表了三种鸢尾花,显示还是很清楚的。
### 2.数据集的准备
接下来的第二步就是数据集的准备了。在训练任何机器学习模型前,数据准备都相当重要,这里也要涉及两步准备。
第一步,特征数值标准化。如果我们不做标准化,后果就是大数值特征会主宰模型训练,这会导致更有意义的小数值特征被忽略。这里我们用Z Score标准化,使每一类特征平均值为0,方差为1.0,我们可以通过代码实现来看下效果。
```
from sklearn.preprocessing import scale
import pandas as pd
num_cols = ['Sepal_Length', 'Sepal_Width', 'Petal_Length', 'Petal_Width']
iris_scaled = scale(iris[num_cols])
iris_scaled = pd.DataFrame(iris_scaled, columns = num_cols)
print(iris_scaled.describe().round(3))
```
![](https://static001.geekbang.org/resource/image/1f/da/1f7bbea1c93dcdbbcd9c1ba4e32178da.png)
你可以看到,每一列平均值为0,标准差大约是1.0。为了分类需要,我们用字典把花种从字符串类型转换成数字表示。
```
levels = {'setosa':0, 'versicolor':1, 'virginica':2}
iris_scaled['Species'] = [levels[x] for x in iris['Species']]
iris_scaled.head()
```
![](https://static001.geekbang.org/resource/image/bc/5e/bc14b245ab9076d3a8911dyy2da8895e.png)
第二步,把数据集随机分割成样本训练集和评估数据集,训练集用来训练KNN模型,评估集用来测试和评估KNN的分类结果。
```
from sklearn.model_selection import train_test_split
import numpy as np
np.random.seed(3456)
iris_split = train_test_split(np.asmatrix(iris_scaled), test_size = 75)
iris_train_features = iris_split[0][:, :4]
iris_train_labels = np.ravel(iris_split[0][:, 4])
iris_test_features = iris_split[1][:, :4]
iris_test_labels = np.ravel(iris_split[1][:, 4])
print(iris_train_features.shape)
print(iris_train_labels.shape)
print(iris_test_features.shape)
print(iris_test_labels.shape)
```
通过代码,我们得到了下面这样的结果。
```
(75, 4)
(75,)
(75, 4)
(75,)
```
### 3.训练模型
数据准备好后,就是第三步训练模型了。这里我们使用K=3来训练KNN模型,当然你也可以调整这个参数来进行观察和调优。
```
from sklearn.neighbors import KNeighborsClassifier
KNN_mod = KNeighborsClassifier(n_neighbors = 3)
KNN_mod.fit(iris_train_features, iris_train_labels)
```
### 4.模型测试
执行KNN训练后,我们来到了最后一步,模型测试,这里我们使用测试集来测试模型。
```
iris_test = pd.DataFrame(iris_test_features, columns = num_cols)
iris_test['predicted'] = KNN_mod.predict(iris_test_features)
iris_test['correct'] = [1 if x == z else 0 for x, z in zip(iris_test['predicted'], iris_test_labels)]
accuracy = 100.0 * float(sum(iris_test['correct'])) / float(iris_test.shape[0])
print(accuracy)
```
```
96.0
```
最终,我们得到的准确率是96.0,说明了KNN的训练模型不错,适用这类场景。我们通过代码把其中的两个分类setosa和versicolor打印出来看看。
```
levels = {0:'setosa', 1:'versicolor', 2:'virginica'}
iris_test['Species'] = [levels[x] for x in iris_test['predicted']]
markers = {1:'^', 0:'o'}
colors = {'setosa':'blue', 'versicolor':'green',}
def plot_shapes(df, col1,col2, markers, colors):
import matplotlib.pyplot as plt
import seaborn as sns
ax = plt.figure(figsize=(6, 6)).gca() # define plot axis
for m in markers: # iterate over marker dictioary keys
for c in colors: # iterate over color dictionary keys
df_temp = df[(df['correct'] == m) & (df['Species'] == c)]
sns.regplot(x = col1, y = col2,
data = df_temp,
fit_reg = False,
scatter_kws={'color': colors[c]},
marker = markers[m],
ax = ax)
plt.xlabel(col1)
plt.ylabel(col2)
plt.title('Iris species by color')
return 'Done'
plot_shapes(iris_test, 'Petal_Width', 'Sepal_Length', markers, colors)
plot_shapes(iris_test, 'Sepal_Width', 'Sepal_Length', markers, colors)
```
![](https://static001.geekbang.org/resource/image/9e/7f/9e2c398552558a970ff1644905f6347f.png)![](https://static001.geekbang.org/resource/image/10/47/1057ba92123f1b3faa7d98b3162a4c47.png)
从显示的效果来说,分类还是挺明显的,熟悉了最基础的机器学习过程后,你可能会问,讲了半天,线性代数到底在哪里呢?关键就在KNeighborsClassifier模块上,这个模型算法的实现背后,其实用到了线性代数的核心原理。
首先,因为每种鸢尾花都有四个特征:花萼的长、宽和花瓣的长、宽,所以每条数据都是四维向量。
接着,量化样本之间的相似度,也就是计算向量之间的距离。而向量之间距离的运算有很多方式,比如:曼哈顿距离、欧式距离、切比雪夫距离、闵可夫斯基距离等等。其中,欧式距离你应该很熟悉了,因为我们初中都学过,在二维平面上计算两点之间的距离公式:
$$d=\\sqrt{\\left(x\_{1}-x\_{2}\\right)^{2}+\\left(y\_{1}-y\_{2}\\right)^{2}}$$
扩展到我们实例中的四维向量,也是同样的算法。
你看,这就是线性代数在机器学习中的一种应用场景。KNN是一种监督学习算法,因为在样本集中有分类信息,通过计算距离来衡量样本之间相似度,算法简单,易于理解和实现。还有另一种机器学习算法是无监督学习,底层的数学原理其实也是差不多的,总的思想就是“物以类聚”。
现在,你是不是有一种豁然开朗的感觉?终于看到了线性代数原来那么有意义,而且再简单的公式也是美的。
## 本节小结
好了,到这里导读这一讲就结束了,最后我再总结一下前面讲解的内容。
这一讲我使用机器学习的监督学习算法KNN,在给定鸢尾花特征值的情况下,给鸢尾花做花种分类,让你了解机器学习最基本的过程外,能够真正了解其背后的线性代数真相,为你进入后面课程的学习提供一个感性的认知。
机器学习中用到的线性代数知识点比比皆是,而且往往软件架构上看上去复杂的事情,在数学上反而很简单,希望你在学习了这门课程后,能够多从数学角度出发去构思解决问题的方案。
同时,欢迎你在留言区说说自己对机器学习的理解,也可以分享一下自己的线性代数学习经历,如果你有所收获,也欢迎你把这篇文章分享给你的朋友。