python机器学习实战之最近邻kNN分类器

时间:2021-12-25 13:47:48

K近邻法是有监督学习方法,原理很简单,假设我们有一堆分好类的样本数据,分好类表示每个样本都一个对应的已知类标签,当来一个测试样本要我们判断它的类别是, 就分别计算到每个样本的距离,然后选取离测试样本最近的前K个样本的标签累计投票, 得票数最多的那个标签就为测试样本的标签。

源代码详解:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#-*- coding:utf-8 -*-
#!/usr/bin/python
 
# 测试代码 约会数据分类 import KNN  KNN.datingClassTest1() 标签为字符串  KNN.datingClassTest2() 标签为整形
# 测试代码 手写字体分类 import KNN  KNN.handwritingClassTest()
 
from numpy import *  # 科学计算包
import operator    # 运算符模块
from os import listdir # 获得指定目录中的内容(手写字体文件夹下样本txt) 类型命令行 ls
 
import matplotlib         # 画图可视化操作
import matplotlib.pyplot as plot
 
# 显示一个 二维图
def myPlot(x, y, labels):
  fig = plot.figure()#创建一个窗口
  ax = fig.add_subplot(111)# 画一个图
  #ax.scatter(x,y)
  ax.scatter(x,y,15.0*array(labels),15.0*array(labels)) # 支持 分类颜色显示
  ax.axis([-2,25,-0.2,2.0])
  plot.xlabel('Percentage of Time Spent Playing Video Games')# 坐标轴名称
  plot.ylabel('Liters of Ice Cream Consumed Per Week')
  plot.show()
   
 
# 创建假 的数据测试
def createDataSet():
  groop = array([[1.0, 1.1],[1.0, 1.0],[0, 0],[0, 0.1]]) # numpy的array 数组格式
  labels = ['A','A','B','B']# 标签 list
  return groop, labels
 
# 定义 KNN 分类函数
def knnClassify0(inX, dataSet, labels, k):
  # inX 待分类的点 数据集和标签 DataSet, label 最近领域个数 k
  dataSetSize = dataSet.shape[0] # 数据集大小(行数)  
  # tile(A,(行维度,列维度)) A沿各个维度重复的次数
  # 点A 重复每一行 到 数据集大小行
  differeMat = tile(inX, (dataSetSize,1)) - dataSet # 求 待分类点 与个个数据集点的 差值
  sqDiffMat = differeMat**2              # 求 平方
  sqDistances = sqDiffMat.sum(axis=1)         # 求 和(各行求和)
  distances = sqDistances**0.5            # 开方 得到 点A 与 数据集个点 的欧式距离
  sortedDistIndicies = distances.argsort()      # 返回 递增排序后 的 原位置序列(不是值)  
  # 取得最近的 k个点 统计 标签类出现的频率
  classCount={} # 字典
  for i in range(k):
    voteIlabel = labels[sortedDistIndicies[i]]#从小到大 对应距离 数据点 的标签
    classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 # 对于类标签 字典单词 的 值 + 1    
  # 对 类标签 频率(字典的 第二列(operator.itemgetter(1))) 排序 从大到小排序 reverse=True
  sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True)
  return sortedClassCount[0][0] # 返回 最近的 对应的标签
 
 
# 真实数据的处理  输入TXT文本文件 返回 数据集和标签(已转化成数字) 列表 list
def file2matrix(filename):
  fr = open(filename)         # 打开文件       
  numberOfLines = len(fr.readlines()) # 得到文件所有的行数
  returnMat = zeros((numberOfLines,3))
 # 创建一个用于存储返回数据的矩阵 数据集 每个数据的大小根据实际情况!! 即是 3 列数应根据 数据维度确定
  classLabelVector = []        # 对应标签
  fr = open(filename)
  index = 0
  for line in fr.readlines():     # 每一行
    line = line.strip()       # 默认删除空白符(包括'\n', '\r', '\t', ' ')
    listFromLine = line.split('\t') # 按 制表符(\t) 分割字符串 成 元素列表
    returnMat[index,:] = listFromLine[0:3]     # 前三个为 数据集数据
    classLabelVector.append(int(listFromLine[-1])) # 最后一个 为 标签 整形
    index += 1
  return returnMat,classLabelVector
 
 
# 真实数据的处理  输入TXT文本文件 返回 数据集和标签(为字符串) 列表 list
def file2matrix2(filename):
  fr = open(filename)         # 打开文件       
  numberOfLines = len(fr.readlines()) # 得到文件所有的行数
  returnMat = zeros((numberOfLines,3))
 # 创建一个用于存储返回数据的矩阵 数据集 每个数据的大小根据实际情况!! 即是 3 列数应根据 数据维度确定
  classLabelVector = []        # 对应标签
  fr = open(filename)
  index = 0
  for line in fr.readlines():     # 每一行
    line = line.strip()       # 默认删除空白符(包括'\n', '\r', '\t', ' ')
    listFromLine = line.split('\t') # 按 制表符(\t) 分割字符串 成 元素列表
    returnMat[index,:] = listFromLine[0:3]     # 前三个为 数据集数据
    classLabelVector.append(str(listFromLine[-1])) # 最后一个 为 标签 字符串型
    index += 1
  return returnMat,classLabelVector
 
 
# 数据集 各个类型数据归一化 平等化 影响权值
def dataAutoNorm(dataSet):
  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))  # 扩展 minVals 成 样本总数行m行 1列(属性值个数)
  normDataSet = normDataSet/tile(ranges, (m,1))  # 矩阵除法 每种属性值 归一化 numpy库 为(linalg.solve(matA,matB))
  return normDataSet, ranges, minVals       # 返回 归一化后的数组 和 个属性范围以及最小值
 
# 约会数据 KNN分类 测试
# 标签为 字符串型
def datingClassTest1(test_ret=0.1):
  hoRatio = test_ret       # 测试的样本比例 剩下的作为 训练集
  datingDataMat,datingLabels = file2matrix2('datingTestSet.txt')        #载入数据集
  normMat, ranges, minVals = dataAutoNorm(datingDataMat)
  m = normMat.shape[0]      # 总样本数量
  numTestVecs = int(m*hoRatio)  # 总测试样本数
  errorCount = 0.0        # 错误次数记录
  for i in range(numTestVecs):  # 对每个测试样本
    # KNN 分类            测试样本    剩下的作为数据集        数据集对应的标签 最近 的三个
    classifierResult = knnClassify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
    print "分类结果: %s,\t真实标签: %s" % (classifierResult, datingLabels[i])
    if (classifierResult != datingLabels[i]): errorCount += 1.0  
  print "总错误次数: %d" % errorCount
  print "测试总数:  %d" % numTestVecs
  print "总错误率:  %f" % (errorCount/float(numTestVecs))
 
# 标签为 整形 int
def datingClassTest2(test_ret=0.1):
  hoRatio = test_ret       # 测试的样本比例 剩下的作为 训练集
  datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')        #载入数据集
  normMat, ranges, minVals = dataAutoNorm(datingDataMat)
  m = normMat.shape[0]      # 总样本数量
  numTestVecs = int(m*hoRatio)  # 总测试样本数
  errorCount = 0.0        # 错误次数记录
  for i in range(numTestVecs):  # 对每个测试样本
    # KNN 分类            测试样本    剩下的作为数据集        数据集对应的标签 最近 的三个
    classifierResult = knnClassify0(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
    print "分类结果: %d, 真实标签: %d" % (classifierResult, datingLabels[i])
    if (classifierResult != datingLabels[i]): errorCount += 1.0  
  print "总错误次数: %d" % errorCount
  print "测试总数:  %d" % numTestVecs
  print "总错误率:  %f" % (errorCount/float(numTestVecs))
 
 
# 根据用户输入的 样本的属性值 判断用户所倾向的类型(有点问题??)
def classifyPerson():
  resultList = ['讨厌','一般化','非常喜欢']
  percent = float(raw_input("打游戏所花时间比例: "))
  mile  = float(raw_input("每年飞行的里程数量: "))
  ice   = float(raw_input("每周消费的冰淇淋量: "))
  datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')        #载入数据集
  normMat, ranges, minVals  = dataAutoNorm(datingDataMat)
  # 新测试样本 归一化
  print ranges, minVals
  testSampArry   = array([mile, percent, ice])  # 用户输入的 测试样例
  testSampArryNorm = (testSampArry-minVals)/ranges # 样例归一化
  print testSampArry ,testSampArryNorm
  # 分类
  classifierResult = knnClassify0(testSampArryNorm,normMat,datingLabels,3)
  print classifierResult
  print "他是不是你的菜: ", resultList[classifierResult-1]
   
 
# 手写字体 图像 32*32 像素转化成 1*1024 的向量 
def img2vector(filename):
  returnVect = zeros((1,1024)) # 创建空的 返回向量
  fr = open(filename)     # 打开文件
  for i in range(32):     # 对每一行
    lineStr = fr.readline() # 每一行元素
    for j in range(32):   # 每一行的每个值
      returnVect[0,32*i+j] = int(lineStr[j])
  return returnVect
 
 
# 手写字体的 KNN识别 每个数字图片被转换成 32*32 的 0 1 矩阵
def handwritingClassTest(k=3):
  # 得到训练数据集
  hwLabels = []                # 识别的标签
  trainingFileList = listdir('trainingDigits') # 加载手写字体训练数据集 (所有txt文件列表)
  m = len(trainingFileList)          # 总训练样本数
  trainingMat = zeros((m,1024))        # 训练数据集
  for i in range(m):
    fileNameStr = trainingFileList[i]    # 每个训练数据样本文件 0_0.txt 0_1.txt 0_2.txt
    fileStr = fileNameStr.split('.')[0]   # 以.分割 第一个[0]为文件名  第二个[1]为类型名 txt文件
    classNumStr = int(fileStr.split('_')[0]) # 以_分割,第一个[0]为该数据表示的数字 标签
    hwLabels.append(classNumStr)                   # 训练样本标签
    trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr) # 训练样本数据
     
  # 得到测试数据集  
  testFileList = listdir('testDigits')     # 测试数据集
  errorCount = 0.0               # 错误次数计数
  mTest = len(testFileList)          # 总测试 数据样本个数
  for i in range(mTest):
    fileNameStr = testFileList[i]      # 每个测试样本文件
    fileStr = fileNameStr.split('.')[0]   # 得到文件名
    classNumStr = int(fileStr.split('_')[0]) # 得到对应的真实标签
    vectorUnderTest = img2vector('testDigits/%s' % fileNameStr)        # 测试样本数据
    classifierResult = knnClassify0(vectorUnderTest, trainingMat, hwLabels, k) # 分类
    print "KNN分类标签: %d, 真实标签: %d" % (classifierResult, classNumStr)
    if (classifierResult != classNumStr): errorCount += 1.0
  print "\n总的错误次数: %d" % errorCount
  print "\n总的错误比例: %f" % (errorCount/float(mTest))

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:http://blog.csdn.net/xiaoxiaowenqiang/article/details/76726763