机器学习实战 - python3 学习笔记(一) - k近邻算法
一. 使用k近邻算法改进约会网站的配对效果
k-近邻算法的一般流程:
- 收集数据:可以使用爬虫进行数据的收集,也可以使用第三方提供的免费或收费的数据。一般来讲,数据放在txt文本文件中,按照一定的格式进行存储,便于解析及处理。
- 准备数据:使用Python解析、预处理数据。
- 分析数据:可以使用很多方法对数据进行分析,例如使用Matplotlib将数据可视化。
- 测试算法:计算错误率。
- 使用算法:错误率在可接受范围内,就可以运行k-近邻算法进行分类。
实战内容:
海伦女士一直使用在线约会网站寻找适合自己的约会对象。尽管约会网站会推荐不同的任选,但她并不是喜欢每一个人。经过一番总结,她发现自己交往过的人可以进行如下分类:
- 不喜欢的人
- 魅力一般的人
- 极具魅力的人
海伦收集约会数据已经有了一段时间,她把这些数据存放在文本文件datingTestSet.txt中,每个样本数据占据一行,总共有1000行。
海伦收集的样本数据主要包含以下3种特征:
- 每年获得的飞行常客里程数
- 玩视频游戏所消耗时间百分比
- 每周消费的冰淇淋公升数
完整代码:
1 import numpy as np 2 import operator 3 import matplotlib 4 import matplotlib.pyplot as plt 5 6 def classify0(inX, dataSet, labels, k): 7 '''距离计算''' 8 dataSetSize = dataSet.shape[0] 9 diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet 10 sqDiffMat = diffMat ** 2 11 sqDistances = sqDiffMat.sum(axis=1) 12 distances = sqDistances ** 0.5 13 sortedDistIndicies = distances.argsort() 14 classCount = {} 15 '''选择距离最小的k个点''' 16 for i in range(k): 17 voteIlabel = labels[sortedDistIndicies[i]] 18 classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 19 '''排序''' 20 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) 21 return sortedClassCount[0][0] 22 23 24 def file2matrix(filename): 25 fr = open(filename) # 打开文件 26 arrayOfLines = fr.readlines() # 读取文件所有内容 27 numberOfLines = len(arrayOfLines) # 返回文件的行数 28 returnMat = np.zeros((numberOfLines, 3)) 29 classLabelVector = [] 30 index = 0 31 for line in arrayOfLines: 32 line = line.strip() 33 listFromLine = line.split('\t') 34 returnMat[index, :] = listFromLine[0:3] 35 # 根据文本中标记的喜欢的程度进行分类,1代表不喜欢,2代表魅力一般,3代表极具魅力 36 if listFromLine[-1] == 'didntLike': 37 classLabelVector.append(1) 38 elif listFromLine[-1] == 'smallDoses': 39 classLabelVector.append(2) 40 elif listFromLine[-1] == 'largeDoses': 41 classLabelVector.append(3) 42 index += 1 43 return returnMat, classLabelVector 44 45 datingDataMat, datingLabels = file2matrix('datingTestSet.txt') 46 47 48 fig = plt.figure() 49 ax = fig.add_subplot(111) 50 ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 15.0 * np.array(datingLabels), 15.0 * np.array(datingLabels)) 51 plt.show() 52 53 54 def autoNorm(dataSet): 55 '''归一化特征值:利用公式 newValue = (oldValue - min) / (max - min), 这个公式可以将任意取值范围的特征值转化为0到1区间的值''' 56 normDataSet = np.zeros(np.shape(dataSet)) 57 m = dataSet.shape[0] 58 59 minVals = dataSet.min(0) # min(0) 表示返回矩阵中所有列的最小值,当min(1)时,则返回矩阵中所有行的最小值 60 maxVals = dataSet.max(0) 61 ranges = maxVals - minVals 62 63 normDataSet = dataSet - np.tile(minVals, (m, 1)) 64 normDataSet = normDataSet / np.tile(ranges, (m, 1)) 65 return normDataSet, ranges, minVals 66 normMat, ranges, minVals = autoNorm(datingDataMat) 67 68 69 def datingClassTest(): 70 hoRatio = 0.10 # 取所有数据的百分之10 71 72 filename = 'datingTestSet.txt' 73 datingDataMat, datingLabels = file2matrix(filename) 74 normMat, ranges, minVals = autoNorm(datingDataMat) 75 76 m = normMat.shape[0] 77 numTestVes = int(m * hoRatio) # 测试集数量 78 errorCount = 0.0 # 错误的次数 79 for i in range(numTestVes): 80 # 前100个数据作为测试集,后900个数据作为训练集 81 classifierResult = classify0(normMat[i, :], normMat[numTestVes:m, :], datingLabels[numTestVes:m], 4) 82 print('分类结果: %d, 真实类别: %d'%(classifierResult, datingLabels[i])) 83 84 if (classifierResult != datingLabels[i]): 85 errorCount += 1.0 86 print('错误率: %f%%' % (errorCount / float(numTestVes) * 100)) 87 datingClassTest() 88 89 90 def classifyPerson(): 91 resultList = ['讨厌', '有些喜欢', '非常喜欢'] 92 percentTats = float(input('玩视频游戏所耗时间百分比:')) 93 ffMiles = float(input('每年获得的飞行常客里程数:')) 94 iceCream = float(input('每周消费的冰淇淋公升数:')) 95 96 filename = 'datingTestSet.txt' 97 datingDataMat, datingLabels = file2matrix(filename) 98 normMat, ranges, minVals = autoNorm(datingDataMat) 99 100 inArr = np.array([ffMiles, percentTats, iceCream]) 101 classifierResult = classify0((inArr - minVals) / ranges, normMat, datingLabels, 4) 102 103 print('你可能%s这个人' % (resultList[classifierResult - 1])) 104 105 classifyPerson()
运行结果:
二. sklearn手写数字识别系统
为了简单起见,这里构造的系统只能识别数字0到9,需要识别的数字已经使用图形处理软件,处理成具有相同的色彩和大小①:宽高是32像素×32像素的黑白图像。尽管采用文本格式存储图像不能有效地利用内存空间,但是为了方便理解,我们还是将图像转换为文本格式。
接下来,我们将使用强大的第三方Python科学计算库Sklearn构建手写数字系统。
import numpy as np import operator from os import listdir from sklearn.neighbors import KNeighborsClassifier as KNN def img2Vector(filename): '''将32*32的二进制图像转换为1*1024向量''' # 创建1*1024 的零向量 returnVect = np.zeros((1, 1024)) # 打开文件 fr = open(filename) # 按行读取 for i in range(32): # 读一行数据 lineStr = fr.readline() # 每一行的前32个元素依次添加到returnVect中 for j in range(32): returnVect[0, 32 * i + j] = int(lineStr[j]) # 返回转换后的1*1024向量 return returnVect def handwritingClassTest(): '''手写数字分类测试''' # 测试集的Labels hwLabels = [] # 返回trainingDigits目录下的文件名 trainingFileList = listdir('trainingDigits') # 返回文件夹下文件的个数 m = len(trainingFileList) # 初始化训练的Mat矩阵,测试集 trainingMat = np.zeros((m, 1024)) # 从文件名中解析出训练集的类别 for i in range(m): # 获得文件的名字 fileNameStr = trainingFileList[i] # 获得分类的数字 classNumber = int(fileNameStr.split('_')[0]) # 将获得的类别添加到hwLabels中 hwLabels.append(classNumber) # 将每一个文件的1*1024数据存储到trainingMat矩阵中 trainingMat[i, :] = img2Vector('trainingDigits/%s' % (fileNameStr)) # 构建KNN分类器 neigh = KNN(n_neighbors=3, algorithm='auto') # 拟合模型,trainingMat为训练矩阵,hwLabels为对应的标签 neigh.fit(trainingMat,hwLabels) # 返回testDigits目录下的文件列表 testFileList = listdir('testDigits') # 错误检测计数 errorCount = 0.0 # 测试集的数量 mTest = len(testFileList) # 从文件中解析出测试集的类别并进行分类 for i in range(mTest): # 获得文件的名字 fileNameStr = testFileList[i] # 获得分类的数字 classNumber = int(fileNameStr.split('_')[0]) # 获得测试集的1*1024向量,用于训练 vectorUnderTest = img2Vector('testDigits/%s' % (fileNameStr)) # 获得预测结果 classifierResult = neigh.predict(vectorUnderTest) print('分类返回结果为:%d\t真是结果为:%d' % (classifierResult, classNumber)) if classifierResult != classNumber: errorCount += 1.0 print('总共错了 %d 个数据\n错误率为:%f%%' % (errorCount, errorCount / mTest * 100)) handwritingClassTest()
运行结果: