277 lines
12 KiB
Markdown
277 lines
12 KiB
Markdown
# 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,在给定鸢尾花特征值的情况下,给鸢尾花做花种分类,让你了解机器学习最基本的过程外,能够真正了解其背后的线性代数真相,为你进入后面课程的学习提供一个感性的认知。
|
||
|
||
机器学习中用到的线性代数知识点比比皆是,而且往往软件架构上看上去复杂的事情,在数学上反而很简单,希望你在学习了这门课程后,能够多从数学角度出发去构思解决问题的方案。
|
||
|
||
同时,欢迎你在留言区说说自己对机器学习的理解,也可以分享一下自己的线性代数学习经历,如果你有所收获,也欢迎你把这篇文章分享给你的朋友。
|
||
|