本文出至 机器学习实战 这本书
算法使用的数据来至于 ML in Action 这本书提供的,算法也是,变成环境是在Eclipse,相关环境配置看我的上一篇博文 《Python学习系列0:配置Eclipse进行机器学习算法的准备工作》
这本系列记录了我在机器学习过程中的点滴,主要注释放在了代码中,系列不是为了书本的重复,更多的是自己 Action 中的 accumulation。
KNN 算法是机器学习中的有监督学习算法,该算法的优点是:分类算法中简单、有效的算法(有效2字我并不这么认为,因为我还没有与别的算法比较),理解起来比较简单;缺点是:1. 计算复杂度高,待分类特征向量需要与训练样本集中的每一个样本的向量进行比较; 2. KNN算法并不能反映出 训练数据集 中 内在结构信息,比如说训练样本集中特征的均值信息、分布律等其他的各种特征,在一些其他优秀的机器学习算法可以利用这些信息或者对距离进行一些优化。
该算法总体比较简单,思想是:
1. 待测样本的 特征向量 放在 numpy表示的 Matrix 中
2. 提取出测试样本的特征 放在 mat 中,并且将这些测试样本的分类放在labels的array中
3. 对带测试样本 和 训练集 的数据进行归一化 ( x-min) / (max-min)
4. 比较每一条待分类样本特征向量与训练样本集中每个样本的距离,这个距离是有欧式距离标定的(你也可以使用其他的方式)
5. 找到待测试验本与训练集的距离(即 相似度) 最高的 前 K 个 训练集的样本
6 找到这k 个训练样本的 Label(比如label中有A,B 两类), 统计K个样本的label ,A有 3个,B 有1 个,则认为待测试样本的分类为A
机器学习分类算法的一般步骤为:
1. 准备数据
2. 将数据表示成算法能够处理的形式
3. 写出 分类算法
4. 将训练样本 带入到 分类算法中进行训练
5.将测试样本带入到 分类算法中 测试,如果满意
6 将待分类样本 代入到 分类算法中 进行 预测
我在eclipse 中是采取 包的方式 进行不同算法的管理,如上图所示
1. 我将KNN算法放在了 KNN的包中
2. 测试样例 我放在了 KNN_test.py 中
KNN 分类算法、数据准备函数 我放在了model 文件 : mKNN.py中
尽力实现 测试 和 代码 分离的方式
数据都是放在D:/data/python/*** 路径下
3. 通过 书写这个 工程,让我 练习了 python
4. 我要将我 的代码 放在gitHub 上
以下是我的代码:
KNN_test.py
# -*- coding:utf-8 -*-
from KNN.mKNN import *
import matplotlib
import matplotlib.pyplot as plt
########### 用模拟数据 做KNN 实验
group,labels = createDate() #创建实验所需的数据集
print(group)
print(labels)
a = classify0([0,0], group, labels,3) # 调用KNN 的分类方法
print(a) # 获得所属的类型
####### 读取 dataSet.txt 进行KNN分类
datingDataMat,datingLabels = file2matrix("D:\Data\python\datingTestSet2.txt")
print(datingDataMat)
print(datingLabels[0:30])
# 把数据点 画出来
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:,1],datingDataMat[:,2])
#plt.legend()
plt.xlabel(u"飞行里程")
plt.ylabel(u"看电影时间")
plt.show() # 必须把 画的图 关闭了 才可以进行后续的处理
#对 数据集 进行 归一化
NordataMat ,rangs, minVals = autoNorm(datingDataMat)
print(NordataMat)
# 准备 KNN 分类器 可以使用的 数据
# start = time.time()
datingClassTest("D:\Data\python\datingTestSet2.txt")
# k = 3 errorRate = 5%
# k = 4 errorRate = 4%
######## 写一个真实 的应用程序 进行相亲的 配对
classifyPerson("D:\Data\python\datingTestSet2.txt")
#直接在控制台输入 10 10000 0.5 就可以了得到结果
############# 对 手写体图片 进行 KNN 分类
#测试一下 图片到向量
start = time.time()
vet = img2vec('D:\\Data\\python\\digits\\testDigits\\0_0.txt') # 使用 \\ 是为了避免转义字符! \t \0 都是转义字符
#print(vet[0,32:64])
handwriteClassify("D:\\Data\\python\\digits\\testDigits", "D:\\Data\\python\\digits\\trainingDigits")
ends = time.time() - start
print("一共消耗了 %.2f" %(ends))
在该注释的地方,我已经注释了
我用的是python 3.3 的环境,我在上一篇博文也介绍了 怎么 加入 3方包
mKNN.py:
# -*- coding:utf-8 -*-
from numpy import *
import operator
import time # 获得当前时间,用于计时
from operator import itemgetter
from pip.backwardcompat import raw_input
from os import listdir
# 这个模块是KNN 算法的 各种函数
# 创建 实验的 模拟数据集
def createDate():
group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels = ['A','A','B','B']
return group,labels
# KNN 分类器 intX 是输入测试数据的特征向量 dataSet 是训练样本集,labels 标签向量 , k 是选择最邻近的数目
def classify0(intX, dataSet, labels, k):
dataSetSize = dataSet.shape[0] # 获得 有多少行
difMat = tile(intX, (dataSetSize,1)) - dataSet # tile( ) 函数的用法
sqDiffMat = difMat**2
sqDistances = sqDiffMat.sum(axis=1)
distance = sqDistances**0.5
sortedDistIndecies = distance.argsort()
classCount = { } # 声明为一个 dic 类型
for i in range(k):
voteIlabel = labels[sortedDistIndecies[i]] #取出前n个距离最小的label值
classCount[voteIlabel] = classCount.get(voteIlabel,0) +1 #如果没有
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
# sorted 就是一个方法,里面穿值 3.3 版本中 classCount.items() 代替 classCount.iteritems()---应该已经弃用了 其实只要是iterable() 就可以的
return sortedClassCount[0][0]
# 读取 txt文本文件 的样本数据
def file2matrix(filename):
fr = open(filename)
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines)
returnMat = zeros((numberOfLines,3)) # 使用的numpy的 zeros() numberOfLines*3 的 零矩阵 3 是固定好的,可以通过 split 函数确定
classLabelVector = [ ]
index = 0
for line in arrayOLines:
line = line.strip()
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
classLabelVector.append(int(listFromLine[-1]))
index += 1
return returnMat, classLabelVector
# 数据的归一化
def autoNorm(dataSet):
minVals = dataSet.min(0) # 得到的是 列 中的最小值 是一个向量 Vector
maxVals = dataSet.max(0)
ranges = maxVals - minVals
NorMat = zeros(shape(dataSet))
m = dataSet.shape[0]
NorMat = dataSet - tile(minVals,(m,1))
NorMat = NorMat/tile(ranges,(m,1))
return NorMat,ranges,minVals
# Test the KNN classify algorithm 测试 KNN 算法
# 可以通过改变 hosRate K 来观测不同的 错误率大小
def datingClassTest(filename):
start = time.time()
hosRate = 0.10 # 选择 hosRate 比例作为 测试数据
dataMat,dataLab = file2matrix(filename)
norMat, ranges, minVals = autoNorm(dataMat)
testNum = int(dataMat.shape[0]*hosRate)
totNum = dataMat.shape[0]
errorRate = 0.0
for i in range(testNum):
# 选择 前 testNum 个样本作为测试数据, 后面 testNum-totNum 作为 训练样本 K 选择 3
classRes = classify0(norMat[i,:], norMat[testNum:totNum,:], dataLab[testNum:totNum], 5)
print("the classify result is %d, the real result is %d" %(classRes,dataLab[i]) )
if(classRes != dataLab[i]):
errorRate += 1.0
print("the error Rate is %.2f %%" %((errorRate/testNum)*100))
comTime = time.time() - start
print("一共耗时 %0.2f s" %(comTime))
# 真实的应用 程序 主要是对 输入 输出 函数的 练习
def classifyPerson(filename):
resultValue = ['little','small does','large does']
game = float(raw_input("how many hours you play games?"))
fly = float(raw_input("How many kilometers you fly?"))
iceCream = float(raw_input("How many iceCream you eat?"))
personVec = array([game,fly,iceCream])
dataSet,dataLab = file2matrix(filename)
norMat,ranges,minVals = autoNorm(dataSet)
classRes = classify0((personVec-minVals)/ranges, norMat, dataLab, 3)
print("The person is %s",resultValue[classRes-1])
# 对指定文件名下的图片转化为向量 准备数据
def img2vec(filename):
returnVec = zeros((1,1024))
fr = open(filename)
numLine = fr.readlines()
m = len(numLine)
for i in range(m):
line = numLine[i]
line = line.strip() # strip() 去掉 每一行的 换行符 等
for j in range(len(line)):
returnVec[0,i*m+j]=int(line[j])
return returnVec
# 测试 手写题的
def handwriteClassify(dirtest,dirtrain):
hwLabels = []
trainfilelist = listdir(dirtrain)
m = len(trainfilelist)
returnMat =zeros((m,1024)) # 扩展 结果矩阵 1024 可以通过读取第一个文件 得到
for i in range(m):
filenamestr = trainfilelist[i]
filename = filenamestr.split('.')[0]
hwtype = filename.split('_')[0]
hwLabels.append(hwtype)
fileloc = dirtrain+"\\"+filenamestr
returnMat[i,:] = img2vec(fileloc)
# 解析 testdir
errNum = 0.0
testfilelist = listdir(dirtest)
n = len(testfilelist)
for j in range(n):
filenamestr = testfilelist[j]
filename = filenamestr.split('.')[0]
filetype = filename.split('_')[0]
fileloc = dirtest +"\\"+filenamestr
returnVec = img2vec(fileloc)
classtype = classify0(returnVec, returnMat, hwLabels, 3)
if(classtype != filetype):
errNum += 1
print("error rate is :%f" % (errNum/n))
通过对KNN 对 手写体识别 的计算还是有些慢,一开始我以为程序死了
后来加入 time 包查看 运行时间, 发现 对4000多条 的数据进行比较 用了40多秒!!
----------------------------------------------------------------------------------------------------------------------------------------------------------------
让我收获的感觉是:1. 原来 Python 写 ML 的代码可以这么优美 , 易于构建,解析文件也很给力
2. 自己对 第三方包 的熟悉程度不够
3. 需要 根据 作者的思想 自己构建 算法, 而不是 照本宣科 的coding
--------------------------------- edit by Jamin