mirror of
https://github.com/apachecn/ailearning.git
synced 2026-05-08 06:33:55 +08:00
197
docs/2.k-近邻算法.md
197
docs/2.k-近邻算法.md
@@ -76,7 +76,7 @@ knn 算法按照距离最近的三部电影的类型,决定未知电影的类
|
||||
```
|
||||
收集数据:提供文本文件
|
||||
准备数据:使用 Python 解析文本文件
|
||||
分析数据:使用 Matplotlib 画二维扩散图
|
||||
分析数据:使用 Matplotlib 画二维散点图
|
||||
训练算法:此步骤不适用于 k-近邻算法
|
||||
测试算法:使用海伦提供的部分数据作为测试样本。
|
||||
测试样本和非测试样本的区别在于:
|
||||
@@ -94,21 +94,38 @@ knn 算法按照距离最近的三部电影的类型,决定未知电影的类
|
||||
|
||||
> 准备数据:使用 Python 解析文本文件
|
||||
|
||||
将文本记录转换为 NumPy 的解析程序
|
||||
```Python
|
||||
def file2matrix(filename):
|
||||
"""
|
||||
Desc:
|
||||
导入训练数据
|
||||
parameters:
|
||||
filename: 数据文件路径
|
||||
return:
|
||||
数据矩阵 returnMat 和对应的类别 classLabelVector
|
||||
"""
|
||||
fr = open(filename)
|
||||
# 获得文件中的数据行的行数
|
||||
numberOfLines = len(fr.readlines())
|
||||
# 生成对应的空矩阵
|
||||
# 例如:zeros(2,3)就是生成一个 2*3的矩阵,各个位置上全是 0
|
||||
returnMat = zeros((numberOfLines, 3)) # prepare matrix to return
|
||||
classLabelVector = [] # prepare labels return
|
||||
fr = open(filename)
|
||||
arrayOLines = fr.readlines()
|
||||
numberOfLines = len(arrayOLines)
|
||||
returnMat = zeros((numberOfLines,3))
|
||||
classLabelVector = []
|
||||
index = 0
|
||||
for line in arrayOLines:
|
||||
for line in fr.readlines():
|
||||
# str.strip([chars]) --返回移除字符串头尾指定的字符生成的新字符串
|
||||
line = line.strip()
|
||||
# 以 '\t' 切割字符串
|
||||
listFromLine = line.split('\t')
|
||||
returnMat[index,:] = listFromLine[0:3]
|
||||
# 每列的属性数据
|
||||
returnMat[index, :] = listFromLine[0:3]
|
||||
# 每列的类别数据,就是 label 标签数据
|
||||
classLabelVector.append(int(listFromLine[-1]))
|
||||
index += 1
|
||||
return returnMat,classLabelVector
|
||||
# 返回数据矩阵returnMat和对应的类别classLabelVector
|
||||
return returnMat, classLabelVector
|
||||
```
|
||||
|
||||
> 分析数据:使用 Matplotlib 画二维散点图
|
||||
@@ -118,18 +135,106 @@ import matplotlib
|
||||
import matplotlib.pyplot as plt
|
||||
fig = plt.figure()
|
||||
ax = fig.add_subplot(111)
|
||||
ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2])
|
||||
ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 15.0*array(datingLabels), 15.0*array(datingLabels))
|
||||
plt.show()
|
||||
```
|
||||
|
||||
下图中采用矩阵的第一和第三列属性得到很好的展示效果,清晰地标识了三个不同的样本分类区域,具有不同爱好的人其类别区域也不同。
|
||||
|
||||

|
||||
|
||||
> 准备数据: 归一化数据
|
||||
|
||||
归一化特征值,消除属性之间量级不同导致的影响
|
||||
```Python
|
||||
def autoNorm(dataSet):
|
||||
"""
|
||||
Desc:
|
||||
归一化特征值,消除属性之间量级不同导致的影响
|
||||
parameter:
|
||||
dataSet: 数据集
|
||||
return:
|
||||
归一化后的数据集 normDataSet. ranges和minVals即最小值与范围,并没有用到
|
||||
|
||||
归一化公式:
|
||||
Y = (X-Xmin)/(Xmax-Xmin)
|
||||
其中的 min 和 max 分别是数据集中的最小特征值和最大特征值。该函数可以自动将数字特征值转化为0到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)) # element wise divide
|
||||
return normDataSet, ranges, minVals
|
||||
```
|
||||
|
||||
> 训练算法:此步骤不适用于 k-近邻算法
|
||||
|
||||
> 测试算法:使用海伦提供的部分数据作为测试样本。
|
||||
测试样本和非测试样本的区别在于:
|
||||
测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。
|
||||
> 测试算法:使用海伦提供的部分数据作为测试样本。测试样本和非测试样本的区别在于:测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。
|
||||
|
||||
kNN 分类器针对约会网站的测试代码
|
||||
```Python
|
||||
def datingClassTest():
|
||||
"""
|
||||
Desc:
|
||||
对约会网站的测试方法
|
||||
parameters:
|
||||
none
|
||||
return:
|
||||
错误数
|
||||
"""
|
||||
# 设置测试数据的的一个比例(训练数据集比例=1-hoRatio)
|
||||
hoRatio = 0.1 # 测试范围,一部分测试一部分作为样本
|
||||
# 从文件中加载数据
|
||||
datingDataMat, datingLabels = file2matrix('input/2.KNN/datingTestSet2.txt') # load data setfrom file
|
||||
# 归一化数据
|
||||
normMat, ranges, minVals = autoNorm(datingDataMat)
|
||||
# m 表示数据的行数,即矩阵的第一维
|
||||
m = normMat.shape[0]
|
||||
# 设置测试的样本数量, numTestVecs:m表示训练样本的数量
|
||||
numTestVecs = int(m * hoRatio)
|
||||
print 'numTestVecs=', numTestVecs
|
||||
errorCount = 0.0
|
||||
for i in range(numTestVecs):
|
||||
# 对数据测试
|
||||
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))
|
||||
print errorCount
|
||||
```
|
||||
|
||||
> 使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方是否为自己喜欢的类型。
|
||||
|
||||
约会网站预测函数
|
||||
```Python
|
||||
def clasdifyPerson():
|
||||
resultList = ['not at all', 'in small doses', 'in large doses']
|
||||
percentTats = float(raw_input("percentage of time spent playing video games ?"))
|
||||
ffMiles = float(raw_input("frequent filer miles earned per year?"))
|
||||
iceCream = float(raw_input("liters of ice cream consumed per year?"))
|
||||
datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
|
||||
normMat, ranges, minVals = autoNorm(datingDataMat)
|
||||
inArr = array([ffMils, percentTats, iceCream])
|
||||
classifierResult = classify0((inArr-minVals)/ranges,normMat,datingLabels, 3)
|
||||
print "You will probably like this person: ", resultList[classifierResult - 1]
|
||||
```
|
||||
|
||||
实际运行效果如下:
|
||||
|
||||
```Python
|
||||
>>> kNN.classifyPerson()
|
||||
percentage of time spent playing video games?10
|
||||
frequent flier miles earned per year?10000
|
||||
liters of ice cream consumed per year?0.5
|
||||
You will probably like this person: in small doses
|
||||
```
|
||||
|
||||
[完整代码地址](https://github.com/apachecn/MachineLearning/blob/master/src/python/2.KNN/kNN.py): <https://github.com/apachecn/MachineLearning/blob/master/src/python/2.KNN/kNN.py>
|
||||
|
||||
@@ -139,29 +244,35 @@ plt.show()
|
||||
|
||||
### 项目案例2: 手写数字识别系统
|
||||
|
||||
> 概述
|
||||
#### 项目概述
|
||||
|
||||
构造一个能识别数字 0 到 9 的基于 KNN 分类器的手写识别系统。
|
||||
|
||||
需要识别的数字是存储在文本文件中的具有相同的色彩和大小:宽高是 32 像素 * 32 像素的黑白图像。
|
||||
|
||||
> 使用 KNN 的手写识别系统
|
||||
#### 开发流程
|
||||
|
||||
```
|
||||
收集数据:提供文本文件。
|
||||
准备数据:编写函数 img2vector(), 将图像格式转换为分类器使用的向量格式
|
||||
分析数据:在 Python 命令提示符中检查数据,确保它符合要求。
|
||||
分析数据:在 Python 命令提示符中检查数据,确保它符合要求
|
||||
训练算法:此步骤不适用于 KNN
|
||||
测试算法:编写函数使用提供的部分数据集作为测试样本,测试样本与非测试样本的
|
||||
区别在于测试样本是已经完成分类的数据,如果预测分类与实际类别不同,
|
||||
则标记为一个错误。
|
||||
则标记为一个错误
|
||||
使用算法:本例没有完成此步骤,若你感兴趣可以构建完整的应用程序,从图像中提取
|
||||
数字,并完成数字识别,美国的邮件分拣系统就是一个实际运行的类似系统。
|
||||
数字,并完成数字识别,美国的邮件分拣系统就是一个实际运行的类似系统
|
||||
```
|
||||
|
||||
> 收集数据: 提供文本文件
|
||||
|
||||
目录 trainingDigits 中包含了大约 2000 个例子,每个例子内容如下图所示,每个数字大约有 200 个样本;目录 testDigits 中包含了大约 900 个测试数据。
|
||||
|
||||

|
||||
|
||||
* 将图像转换为向量
|
||||
> 准备数据: 编写函数 img2vector(), 将图像格式转换为分类器使用的向量格式
|
||||
|
||||
将图像转换为向量
|
||||
|
||||
```Python
|
||||
def img2vector(filename):
|
||||
@@ -174,6 +285,56 @@ def img2vector(filename):
|
||||
return returnVect
|
||||
```
|
||||
|
||||
> 分析数据:在 Python 命令提示符中检查数据,确保它符合要求
|
||||
|
||||
在 Python 命令行中输入下列命令测试 img2vector 函数,然后与文本编辑器打开的文件进行比较:
|
||||
|
||||
```Python
|
||||
>>> testVector = kNN.img2vector('testDigits/0_13.txt')
|
||||
>>> testVector[0,0:31]
|
||||
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
|
||||
>>> testVector[0,31:63]
|
||||
array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 1., 1., 1., 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
|
||||
```
|
||||
|
||||
> 训练算法:此步骤不适用于 KNN
|
||||
|
||||
> 测试算法:编写函数使用提供的部分数据集作为测试样本,测试样本与非测试样本的区别在于测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误
|
||||
|
||||
```Python
|
||||
def handwritingClassTest():
|
||||
# 1. 导入数据
|
||||
hwLabels = []
|
||||
trainingFileList = listdir('input/2.KNN/trainingDigits') # load the training set
|
||||
m = len(trainingFileList)
|
||||
trainingMat = zeros((m, 1024))
|
||||
# hwLabels存储0~9对应的index位置, trainingMat存放的每个位置对应的图片向量
|
||||
for i in range(m):
|
||||
fileNameStr = trainingFileList[i]
|
||||
fileStr = fileNameStr.split('.')[0] # take off .txt
|
||||
classNumStr = int(fileStr.split('_')[0])
|
||||
hwLabels.append(classNumStr)
|
||||
# 将 32*32的矩阵->1*1024的矩阵
|
||||
trainingMat[i, :] = img2vector('input/2.KNN/trainingDigits/%s' % fileNameStr)
|
||||
|
||||
# 2. 导入测试数据
|
||||
testFileList = listdir('input/2.KNN/testDigits') # iterate through the test set
|
||||
errorCount = 0.0
|
||||
mTest = len(testFileList)
|
||||
for i in range(mTest):
|
||||
fileNameStr = testFileList[i]
|
||||
fileStr = fileNameStr.split('.')[0] # take off .txt
|
||||
classNumStr = int(fileStr.split('_')[0])
|
||||
vectorUnderTest = img2vector('input/2.KNN/testDigits/%s' % fileNameStr)
|
||||
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3)
|
||||
print "the classifier came back with: %d, the real answer is: %d" % (classifierResult, classNumStr)
|
||||
if (classifierResult != classNumStr): errorCount += 1.0
|
||||
print "\nthe total number of errors is: %d" % errorCount
|
||||
print "\nthe total error rate is: %f" % (errorCount / float(mTest))
|
||||
```
|
||||
|
||||
> 使用算法:本例没有完成此步骤,若你感兴趣可以构建完整的应用程序,从图像中提取数字,并完成数字识别,美国的邮件分拣系统就是一个实际运行的类似系统
|
||||
|
||||
[完整代码地址](https://github.com/apachecn/MachineLearning/blob/master/src/python/2.KNN/kNN.py): <https://github.com/apachecn/MachineLearning/blob/master/src/python/2.KNN/kNN.py>
|
||||
|
||||
* * *
|
||||
|
||||
BIN
images/2.KNN/knn_matplotlib_2.png
Normal file
BIN
images/2.KNN/knn_matplotlib_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 138 KiB |
Reference in New Issue
Block a user