机器学习是一门多领域交叉学科,涉及概率论、统计学、逼近论、凸分析、算法复杂度理论等多门学科。专门研究计算机怎样模拟或实现人类的学习行为,以获取新的知识或技能,重新组织已有的知识结构使之不断改善自身的性能。它是人工智能的核心,是使计算机具有智能的根本途径。


【机器学习】K-近邻算法(KNN)_java


那么多余的内容就不说了,想必大家都明白,网上内容实在太多了。接下来进入正题,第一个机器学习算法——KNN。


01

原理透析 


KNN算法即K-近邻算法,是机器学习算法当中相对较为简单的一个分类算法,它属于上图分类中的监督学习算法,监督学习是指利用一组已知类别的样本进行训练,来推断新的数据,也就是用打过标签的数据来训练


它的判决是通过特征值的欧几里得距离:

【机器学习】K-近邻算法(KNN)_java_02

欧式距离是最常见的距离量度,衡量的是多维空间中各个点之间的绝对距离。


具体判别方法是:一个样本在其特征空间里的K个最邻近的样本中进行投票,该样本属于投票数量较多的那个类别,K的取值要结合具体内容来调节。单凭文字看的很懵,那就上图吧:


【机器学习】K-近邻算法(KNN)_java_03

上图描述的就是一个KNN算法的过程(数据位置随机,仅为介绍原理),其中五角星是待测数据,三角形和正方形分别是两个类别,K-近邻算法是在K个最近的样本中进行投票。例如,设置K=3,那么就计算得所有样本的欧氏距离,进行排序,取最近的3个,那么也就是其中的蓝色圈范围。其中包括两个三角形,一个正方形,其概率也就是:三角形 2/3,正方形 1/3,因此可以判别出该五角星属于三角形一类。


下面通过一个例子告诉你,KNN算法该如何实现:


# 导入包:KNN方法在sklearn下neighbors中
import numpy as np
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier


补充:KNN可以做分类问题,以及回归问题

分类问题:from sklearn.neighbors import KNeighborsClassifier
回归问题:from sklearn.neighbors import KNeighborsRegressor


为了容易理解,编造少量数据进行演示:


data = pd.DataFrame([['大话西游',45,1,'动作片'],['杀破狼',33,3,'动作片'],['前任3',0,10,'爱情片'],['战狼',59,1,'动作片'],['泰坦尼克号',1,15,'爱情片'],['纯真漫画',2,19,'爱情片']], columns=['电影名称','武打镜头','接吻镜头','分类情况'])


【机器学习】K-近邻算法(KNN)_java_04


那么接下来就可以通过特征值来训练模型,当然电名称不能用来判断,就选用“武打镜头”和“接吻镜头”来分析,“分类情况”是已经打好的标签。


X = data[['武打镜头','接吻镜头']]
y = data['分类情况']


接下来开始训练模型,至于参数问题,我使用的jupyter,通过shift+tab可以显示需要传的参数,其他环境也有自己的方式,可以试着摸索一下。


# 创建对象并训练模型
knn = KNeighborsClassifier(n_neighbors=5) # 参数为K值
knn.fit(X, y) # 参数为特征和标签


训练好模型就可以测试一下了,看看到底准确与否。


# 武打镜头:100,接吻镜头:3 显然我们自己可以判断出这是个动作片
X_test = pd.DataFrame({'武打镜头':[100], '接吻镜头':[3]})
knn.predict(X_test) # 进行预测

[out] array(['动作片'], dtype=object) # 结果正确


下面可以解析一下它是怎么做到的。已知KNN是利用欧式距离判别,那么就手动的计算下。


# 欧式距离公式
s = ((data['武打镜头']-100)**2 + (data['接吻镜头']-3)**2)**0.5
index = s.sort_values().index
data['分类情况'][index[:5]] # 前面设置的n_neighbors为5,取前5个进行投票,动作片有三票,则是动作片

[out]
3 动作片
0 动作片
1 动作片
5 爱情片
4 爱情片
Name: 分类情况, dtype: object


可以看到动作片的投票占到了0.6,爱情片占0.4,knn的预测还有一个函数predict_proba,即为预测概率值。


# 上述可以计算得,该片为动作片的概率为0.6,爱情片的概率为0.4
knn.predict_proba(X_test)

[out] array([[0.6, 0.4]]) # 概率吻合


上述内容已经完整的走了一遍KNN算法的过程。但是其中有一些缺陷,再拿回来前面的一幅图看一下。

【机器学习】K-近邻算法(KNN)_java_05


可以看到,我还画了一个红色的圈,也就是选取K=7的时候,所取到的最近邻样本,这时候再看投票比例:正方形 4/7,三角形 3/7,五角星判决为正方形,不再是K取3时的三角形。这就是它的一个缺陷:不稳定,而且KNN的时间复杂度和空间复杂度都很高,因为它要遍历所有的样本,计算欧氏距离。那么它解决不了的问题还有其他算法来解决,所以也不用担心啦。


02

实战演练 


实战演练选取的是一个十分经典的案例——手写数字识别(MNIST)。不出意外,后续讲解其他算法同样会用到这个例子。


数据集包括训练集 6w 条数据,测试集 1w 条数据。(需要数据可以公众号后台添加小田微信)


【机器学习】K-近邻算法(KNN)_java_06


import numpy as np
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier


# pandas读取训练数据,并查看其内容
data = pd.read_csv('mnist_dataset/mnist_train.csv', header=None)
data.head()


【机器学习】K-近邻算法(KNN)_java_07


查看数据,可以观察到,表中第一列是对应数字的标签,后面的就是该数字图像的像素点(因为照片四周是空白的,也即是0)。


【机器学习】K-近邻算法(KNN)_java_08


数据共 6w 条,第一列是标签,那么后面784列便是数字的特征值(即28X28的图片展开为一维)。接下来就是拆分成上面所提到的特征值和标签:


# 拆分为第一列的标签值和后面784列的特征值
X_train = data.iloc[:,1:]
y_train = data[0]


下面开始训练数据:


%%time
knn = KNeighborsClassifier(n_neighbors=10) # 数据量较大,适当放大K值
knn.fit(X_train, y_train)


【机器学习】K-近邻算法(KNN)_java_09


高估了我电脑的性能,尽然用了一分钟。后面进行预测的时候耗时将更多,大家想测试的话可以截取其中部分数据进行建模。下面开始检验模型:


test_data = pd.read_csv('mnist_dataset/mnist_test.csv', header=None)
X_test = test_data.iloc[:,1:]
y_test = test_data[0]


%%time
y_predict = knn.predict(X_test)


Wall time: 20min 42s(不出意外,尽然二十分钟,同时也可以看到该算法的复杂度,曾经用神经网络跑这个数据集用了近十分钟)。

# 计算一下上述模型的准确率
(y_test == y_predict).mean()

[out0.9665 # 结果并不算差


还有一个命令knn.scorere(X_test, y_test),它既进行了预测,同时计算出准确率。上述就是一个完整的过程。不过其中还有可以优化的地方,毕竟上面的耗时、准确度还有待提高。

【机器学习】K-近邻算法(KNN)_java_10


jupyter中shift+tab,查看其中的参数,包括例如投票的权重(weights)、算法(algorithm)、多进程(n_jobs)等,以上参数需要自己协调,找到最合适的,可以降低计算时间,提高准确率。