You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

277 lines
12 KiB
Markdown

2 years ago
# 01 | 导读:如何在机器学习中运用线性代数工具?
你好,我是朱维刚。欢迎你跟我一起重学线性代数!
在开篇词中我和你大致讲过我自己的经历从2006年开始到现在14年的时间里我都专注于机器学习领域。对于**线性代数**在机器学习中的应用,我非常了解。而这也是线性代数最主要的应用场景之一。因此,今天第一节课,我想先和你聊一聊,如何在机器学习中运用线性代数工具,在我们开始自下而上的学习之前,先从上层来看一看。
我们都知道,“数据”是机器学习的前提,机器学习的第一步就是要进行**数据**的收集、预处理和特征提取;而**模型**就是通过数据来学习的算法;**学习**则是一个循环过程,一个自动在数据中寻找模式,并不停调优模型参数的过程。那我们就从机器学习的三个核心概念:数据、模型和学习说起。
![](https://static001.geekbang.org/resource/image/3a/32/3a2a7433d5d13b676abe05041a1bcd32.png)
你看,不论是模型,还是学习,都涉及数据,而数据加上模型和学习,就是数学的一般过程了,也就是:观察、实验、推理和抽象。所以,我认为学好数学,不仅有利于理解复杂的机器学习系统,还能调优算法参数,甚至能帮助你创建新的机器学习解决方案。
## 从机器学习到线性代数
那机器学习和线性代数之间到底有着怎样的关系呢我想用一个实际的机器学习算法的例子来解释你可能更容易搞清楚。接下来我使用KNNK-Nearest NeighborK最近邻分类算法来让你简单了解一下机器学习以及它和线性代数之间的关系。
之所以选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在给定鸢尾花特征值的情况下给鸢尾花做花种分类让你了解机器学习最基本的过程外能够真正了解其背后的线性代数真相为你进入后面课程的学习提供一个感性的认知。
机器学习中用到的线性代数知识点比比皆是,而且往往软件架构上看上去复杂的事情,在数学上反而很简单,希望你在学习了这门课程后,能够多从数学角度出发去构思解决问题的方案。
同时,欢迎你在留言区说说自己对机器学习的理解,也可以分享一下自己的线性代数学习经历,如果你有所收获,也欢迎你把这篇文章分享给你的朋友。