1. K-近邻算法
相关配置如下
- 系统
Ubuntu Kylin
- IDE
Pycharm Community
- 语言
Python3
2. 模块安装
在安装好了Python3和IDE之后,我们需要首先安装相关的模块(也就是函数库),以便我们能够完成接下来的学习。
1. pip3 安装
sudo apt-get install python3-pip
作用:用来提供python3所需要的函数库。
2. Numpy安装
pip3 install numpy
作用:用来提供矩阵运算的函数库
3. Matplotlib安装
pip3 install pillow
作用:安装python图像处理库
sudo apt-get install python3-tk
作用:安装GUI界面tkinter库
sudo apt-get install libfreetype6-dev libxft-dev
sudo pip3 install matplotlib
作用:安装matplotlib绘图库
4. Matplotlib的使用
import matplotlib
matplotlib.use('Qt5Agg')
作用:在实际使用中需要添加语句才能显示
3. k-近邻算法概述
k-NN is a type of instance-based learning, or lazy learning, where the function is only approximated locally and all computation is deferred until classification. The k-NN algorithm is among the simplest of all machine learning algorithms. —— [ 维基百科 ]
简单的说,k-近邻算法采用测量不同特征值之间的距离方法进行分类。
如图所示,在已知分类标签(图中红色A点和黑色B点)的情况下,通过计算待分类点(图中蓝色的*)和已知标签之间的距离,对其进行类别划分。
优点: | 精度高、对异常值不敏感、无数据输入假定 |
缺点: | 计算复杂度高、空间复杂度高 |
使用数据范围: | 数值型和标称型 |
4. 具体实例
下面以两个具体实例来讲解如何使用python实现kNN算法。
1. 约会网站配对效果
我的朋友海伦一直使用在线约会网站寻找适合自己的约会对象。尽管约会网站会推荐不同的人选,但她并不是喜欢每一个人。经过一番总结,她发现曾交往过三种类型的人:
- 不喜欢的人
- 魅力一般的人
- 极具魅力的人
和好多女孩一样,海伦希望自己可以在周一到周五约会魅力一般的人,而在周末则与极具魅力的人为伴。海伦希望可以有这样一款分类软件帮助她更好地将匹配对象划分到确切的分类中。此外,海伦还收集了一些约会网站未曾记录的数据信息,她认为这些数据更有助于匹配对象的归类。
海伦将这些数据存放在文本文件中,每个样本数据占据一行,总共有1000行。海伦的样本数据包含以下3种特征:
- 每年获得的飞行常客里程数
- 玩视频游戏所耗时间百分比
- 每周消费的冰淇淋公升数
首先,我们需要完成格式处理程序的编写,具体程序如下所示:
# ------------文本文件转为矩阵-------------
def file2matrix(filename):
fr = open(filename)
# 读取文本
arrayOLines = fr.readlines()
# 计算文本行数
numberOfLines = len(arrayOLines)
# 构造一个零矩阵,维数为:行数*3
returnMat = zeros((numberOfLines, 3))
# 构造一个标签向量,用于返回标签值
classLabelVector = []
# 构造一个index,用于选择矩阵的行
index = 0
for line in arrayOLines:
# 去除回车符
line = line.strip()
# 用\t作为文件分隔位,将字符串进行分割
listFromLine = line.split('\t')
# 填充矩阵
returnMat[index,:] = listFromLine[0:3]
# 将列表的最后一列添加到标签
classLabelVector.append(int(listFromLine[-1]))
# 处理下一行
index += 1
return returnMat, classLabelVector
接下来,我们使用Matplotlib制作原始数据的散点图,这样做的目的是为了让我们能够很好的观察数据特征:
# ---------------导入数据-----------------
datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
print(datingDataMat)
print(datingLabels[0:20])
# ---------------分析数据----------------
# 使用Matplotlib创建散点图
# matplotlib安装:
# 1. sudo apt-get install libfreetype6-dev libxft-dev
# 2. sudo pip3 install matplotlib
import matplotlib
# 显示散点图
matplotlib.use('Qt5Agg')
# matplotlib.matplotlib_fname()
import matplotlib.pyplot as plt
fig = plt.figure()
# 设置图片显示位置信息
ax = fig.add_subplot(111)
# 设置图片散点信息
ax.scatter(datingDataMat[:,1], datingDataMat[:,2])
plt.show()
# 调用scatter函数个性化标记散点图上的点
fig1 = plt.figure()
ax = fig1.add_subplot(111)
ax.scatter(datingDataMat[:,1], datingDataMat[:,2],
15.0*array(datingLabels), 15.0*array(datingLabels))
plt.show()
考虑到三类数据之间存在量级的差异,比如飞行常客里程数要远远大于视频游戏所耗时间百分比,因此我们需要对数据进行归一化处理:
# -------------准备数据------------
# 数据归一化处理
def autoNorm(dataSet):
# 列最小值(使用min(0)可以从列中选择最小值;使用min(1)可以从行中选择最小值)
minVals = dataSet.min(0)
# 列最大值
maxVals = dataSet.max(0)
# 区间
ranges = maxVals - minVals
# 构造零向量
normDataSet = zeros(shape(dataSet))
# 确定行数
m = dataSet.shape[0]
# 行列式相减(注:采用行列式的形式替代循环,可以提升速度)
normDataSet = dataSet - tile(minVals, (m,1))
# 行列式相除(优点同上)
normDataSet = normDataSet/tile(ranges, (m,1))
return normDataSet, ranges, minVals
最后,我们还要完成分类器的编写:
# inX:用于分类的输入向量;dataSet:输入的训练样本集;labels:标签向量;k:用于选择最近邻居的数目
def classify0(inX, dataSet, labels, k):
# 读取矩阵第一维的长度,参考网站:http://www.cnblogs.com/zuizui1204/p/6511050.html
dataSetSize = dataSet.shape[0]
# tile:重复维度,进行扩展。参考网站:http://blog.csdn.net/wy250229163/article/details/52453201
# diffMat:表示输入向量和训练样本集之间的距离
diffMat = tile(inX, (dataSetSize, 1)) - dataSet
# 分别计算平方:a^2,b^2(d=sqrt(a^2+b^2))
sqDiffMat = diffMat**2
# 将矩阵的每一行元素相加:a^2+b^2。若axis=0则为普通求和
sqDistances = sqDiffMat.sum(axis = 1)
# 计算距离开根号
distances = sqDistances**0.5
# 按照距离排序,并返回索引
sortedDistIndices = distances.argsort()
classCount = {}
# 选择距离最小的k个点
for i in range(k):
# 获取label值
voteIlabel = labels[sortedDistIndices[i]]
# 计算label出现次数
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
# 按照label出现的多少排序,降序排列。注:python3中使用items替代iteritems
sortedClassCount = sorted(classCount.items(),
key = operator.itemgetter(1), reverse = True)
return sortedClassCount[0][0]
下面,可以进行测试,看看我们分类的错误率有多少,能否让海伦得偿所愿:
# 测试算法
def datingClassTest():
# 此处代码之前有讲解
hoRatio = 0.10
datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
normMat, ranges, minVals = autoNorm(datingDataMat)
m = normMat.shape[0]
# 测试数据为10%
numTestVecs = int(m*hoRatio)
# 初始化错误率
errorCount = 0.0
# 针对每一个测试数据,判断其分类情况
for i in range(numTestVecs):
# 利用分类器训练,训练数据为90%
classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:],
datingLabels[numTestVecs:m], 3)
print("the classifier came back with: %d, the real answer is: %d"
% (classifierResult, datingLabels[i]))
if (classifierResult != datingLabels[i]):
errorCount += 1.0
print("the total error rate is: %f" % (errorCount/float(numTestVecs)))
# 输出结果
datingClassTest()
2. 手写数字识别系统
在手写识别系统中,我们通过训练分类器完成手写数字的判定,其中每个手写数字都可以用如下所示的二进制图像矩阵表示:
接下来通过图像转换和分类器判决,就可以完成手写数字识别。该部分的具体实现代码请参考:手写数字识别系统