机器学习实战 K-近邻算法(超详细注释)

机器学习实战第二章

使用K-近邻算法改进约会网站的匹配效果

  • 完整的机器学习实战第二章的约会匹配
  • 原书是py2版本的,已将源码转成py3版本

修改了部分代码:

修改了字典的创建函数:classCount[voteIlabel] = classCount.setdefault(voteIlabel, 0) + 1
修改了字典的排序方式:sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
添加了绘表函数:def pltFig(datingDataMat, datingLabels)
利用了numpy的广播机制:diffMat = inX - dataSet 而非 diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
将英文输入打印,改成中文输入打印
修改了每年获得飞行常客里程数、玩视频游戏所耗时间、每周消费冰淇淋公升数的数据输入方式
将原码的m变量改名为dataSetRowNum变量
在def autoNorm(dataSet)返回的变量中添加了dataSetRowNum变量,减少了原码的冗余

代码

import numpy as np
import operator
import matplotlib
import matplotlib.pyplot as plt

'''绘制表格'''
def pltFig(datingDataMat, datingLabels):
	# 创建画表
	# plt.figure()中有个参数figsize = (2,2),用来设置表格的大小
	fig = plt.figure(figsize=(10, 10))
	# 分配表位
	ax1 = fig.add_subplot(311)
	ax2 = fig.add_subplot(312)
	ax3 = fig.add_subplot(313)
	# 导入数据
	ax1.scatter(datingDataMat[:, 0], datingDataMat[:, 1], 15.0 * np.array(datingLabels), 15.0 * np.array(datingLabels))
	ax2.scatter(datingDataMat[:, 0], datingDataMat[:, 2], 15.0 * np.array(datingLabels), 15.0 * np.array(datingLabels))
	ax3.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 15.0 * np.array(datingLabels), 15.0 * np.array(datingLabels))
	ax1.set(ylabel='玩视频游戏所耗时间', xlabel='每年获得飞行常客里程数')
	ax2.set(ylabel='每周消费冰淇淋公升数', xlabel='每年获得飞行常客里程数')
	ax3.set(ylabel='每周消费冰淇淋公升数', xlabel='玩视频游戏所耗时间')
	plt.show()


#创造矩阵
def createDataSet():
	# 生成矩阵
	group = np.array([1., 1.1], [1., 1.], [0, 0], [0, 0.1])
	# 标记特征值
	labels = ['a', 'a', 'b', 'b']
	return group, labels

'''inX为样本,dataSet为训练集,labels为标签向量,k为聚类个数'''
def classIfY0(inX, dataSet, labels, k):
	# 返回dataSet训练集的行数;*shape[0]为行数、shape[1]为列数
	dataSetSize = dataSet.shape[0]
	# np.tile为拷贝,将 inX 拷贝行dataSetSize次,列1次
	# 拷贝完的 inX' 减去dataSet矩阵 
	# 其实python具有广播机制也是可以的    
	# diffMat = inX - dataSet
	diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
	# 做完减法后得平方
	sqDiffMat = diffMat ** 2
	# sqDiffMat矩阵按行相加,得到dataSetSize列矩阵;*axis=1按行相加、axis=0按列相加
	sqDistances = sqDiffMat.sum(axis=1)
	# 对sqDistances矩阵开方
	distances = sqDistances**0.5
	# 从小到大排列,提取其对应的index(索引)
	sortedDistIndicies = distances.argsort()
	# 创建空字典,用来记录K个邻居点的特征值名称(键)-数量(值)
	classCount = {}
	for i in range(k):
		# 找到[0, k)区间较小的值所对应的特征值
		# sortedDistIndicies[i]返回数值较小的索引号
		voteIlabel = labels[sortedDistIndicies[i]]
		# 在字典中查找voteIlabel键的特征值个数
		# 若找到则返回voteIlabel键下对应的值并加1;
		# 若找不出,则新建voteIlabel(键)- 0(值)并加1
		classCount[voteIlabel] = classCount.setdefault(voteIlabel, 0) + 1 #**
	# classCount.items()返回键值对迭代器,字典形式:{'a':1,'b':3}-》列表形式[('a',1),('b',3)]
	# key=operator.itemgetter(1)使用元素第二维的数据进行排序
	# reverse=True决定了排序方式,从大到小
	sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) #**
	return sortedClassCount[0][0]

'''读取训练文件'''
def file2matrix(fileName):
	# 读取文件
	with open(fileName) as fr:
		# 按行读取
		arrayOlinges = fr.readlines()
		# 获得长度
		numberOfLines = len(arrayOlinges)
		# 生成N * 3的矩阵
		returnMat = np.zeros((numberOfLines, 3))
		classLabelVector = []
		index = 0
		for line in arrayOlinges:
			line = line.strip()
			# 将行数据写入列表中
			listFromLine = line.split('\t')
			# 将文件的前三列写入矩阵中
			returnMat[index, :] = listFromLine[0: 3]
			# 记录第四列内容
			classLabelVector.append(int(listFromLine[-1]))
			# 行+1
			index += 1
			#print(line)
		'''返回前三列的数据矩阵和一列特征矩阵'''
		return returnMat, classLabelVector


'''归一化特征值'''
def autoNorm(dataSet):
	# 得到最大、最小值和差值
	minVals = dataSet.min(0)
	maxVals = dataSet.max(0)
	ranges = maxVals - minVals
	# 得到和训练集的同型矩阵
	normDataSet = np.zeros(np.shape(dataSet))
	# 得到行数
	dataSetRowNum = dataSet.shape[0]
	# 做矩阵数的减法和除法,实现归一化
	normDataSet = dataSet - np.tile(minVals, (dataSetRowNum, 1))
	normDataSet = normDataSet / np.tile(ranges, (dataSetRowNum, 1))
	return normDataSet, ranges, minVals, dataSetRowNum

'''分类器测试'''
def datingClassTest(datingDataMat, datingLabels):
	hoRatio = 0.1
	# 归一化,得到归一矩阵、差值矩阵、最小值矩阵和行数
	normMat, ranges, minVals, dataSetRowNum = autoNorm(datingDataMat)
	# 取百分之10作为测试数据
	numTestVecs = int(dataSetRowNum * hoRatio)
	# 错误统计
	errorCount = 0.0
	
	for i in range(numTestVecs):
		# 测试矩阵(0->numTestVecs-1), 训练矩阵(numTestVecs->dataSetRowNum),特征矩阵, K聚类的个数
		classIfierResult = classIfY0(normMat[i, :], \
			normMat[numTestVecs: dataSetRowNum, :], \
				datingLabels[numTestVecs: dataSetRowNum], \
					3)
		print(f'the classifier came back with : {classIfierResult}, the real answer is : {datingLabels[i]}')
		if classIfierResult != datingLabels[i]:
			errorCount += 1
	print(f'the total error rate is : {errorCount/float(numTestVecs)}')
	pltFig(datingDataMat, datingLabels)

'''分类器个人'''
def datingClassPerson(datingDataMat, datingLabels):
	resultList = ['不喜欢', '魅力一般', '极具魅力']
	ffMiles = float(input('每年获得飞行常客里程数:'))
	percentTats = float(input('玩视频游戏所耗时间:'))
	iceCream = float(input('每周消费冰淇淋公升数:'))
	# 创建单个小矩阵
	inArr = np.array([ffMiles, percentTats, iceCream])
	# 调用np.row_stack函数,将小矩阵并入大矩阵中
	datingDataMat = np.row_stack((datingDataMat, inArr))
	# 归一化,得到归一矩阵、差值矩阵、最小值矩阵和行数
	normMat, ranges, minVals, dataSetRowNum = autoNorm(datingDataMat)
	# 进入预测
	classIfierResult = classIfY0(normMat[-1, :], normMat[:-1, :], datingLabels, 3)
	print(resultList[int(classIfierResult) - 1])
	

if __name__ == '__main__':
	num = input('1:测试KNN的正确性\n2:自行输入数据,预测约会网站的匹配结果\n')
	mapData = r'G:\机器学习实战\机器学习实战资料\Ch02\datingTestSet2.txt'
	# 读取文件
	datingDataMat, datingLabels = file2matrix(mapData )

	if num == '1':
		datingClassTest(datingDataMat, datingLabels)
	else:
		datingClassPerson(datingDataMat, datingLabels)

测试效果

图片经过剪切

1-1:自带文件测试(图片经过剪切)
=======================================================

在这里插入图片描述

1-2:个人数据测试

手写识别系统

  • 完整的机器学习实战第二章的手写识别系统
  • 原书是py2版本的,已将源码转成py3版本

修改了部分代码:

简化了文件遍历的代码
和约会的代码差不多,核心还是小矩阵和大矩阵的匹配,找到最相近的匹配项目

代码:

import numpy as np
import operator
import os

'''读取文件内容到矩阵中'''
def img2vector(fileName):
    # 创建1*1024的矩阵
    returnVect = np.zeros((1, 1024))
    with open(fileName) as fr:
        # 共计32行
        for i in range(32):
            # 返回字符串
            lineStr = fr.readline()
            # 共计32列
            for j in range(32):
                # 将文件的数据写入到变量returnVect中,并转为整形
                returnVect[0, 32*i+j] = int(lineStr[j])
    return returnVect

'''inX为样本,dataSet为训练集,labels为标签向量,k为聚类个数'''
def classIfY0(inX, dataSet, labels, k):
	# 返回dataSet训练集的行数;*shape[0]为行数、shape[1]为列数
	dataSetSize = dataSet.shape[0]
	# np.tile为拷贝,将 inX 拷贝行dataSetSize次,列1次
	# 拷贝完的 inX' 减去dataSet矩阵 
	# 其实python具有广播机制也是可以的    
	# diffMat = inX - dataSet
	diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
	# 做完减法后得平方
	sqDiffMat = diffMat ** 2
	# sqDiffMat矩阵按行相加,得到dataSetSize列矩阵;*axis=1按行相加、axis=0按列相加
	sqDistances = sqDiffMat.sum(axis=1)
	# 对sqDistances矩阵开方
	distances = sqDistances**0.5
	# 从小到大排列,提取其对应的index(索引)
	sortedDistIndicies = distances.argsort()
	# 创建空字典,用来记录K个邻居点的特征值名称(键)-数量(值)
	classCount = {}
	for i in range(k):
		# 找到[0, k)区间较小的值所对应的特征值
		# sortedDistIndicies[i]返回数值较小的索引号
		voteIlabel = labels[sortedDistIndicies[i]]
		# 在字典中查找voteIlabel键的特征值个数
		# 若找到则返回voteIlabel键下对应的值并加1;
		# 若找不出,则新建voteIlabel(键)- 0(值)并加1
		classCount[voteIlabel] = classCount.setdefault(voteIlabel, 0) + 1 #**
	# classCount.items()返回键值对迭代器,字典形式:{'a':1,'b':3}-》列表形式[('a',1),('b',3)]
	# key=operator.itemgetter(1)使用元素第二维的数据进行排序
	# reverse=True决定了排序方式,从大到小
	sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) #**
	return sortedClassCount[0][0]


def handwritingClassTest(trainingFolderMap, testFolderMap):
    # 存放数字的特征值的特征值列表
    hwLabels = []
    # 读取训练列表
    for fatherFolder, sonFolders, sonFiles in os.walk(trainingFolderMap):
        # 创建 len(sonFiles)*1024矩阵
        trainingMat = np.zeros((len(sonFiles), 1024))
        # 统计次数,与trainingMat相对应
        flagCount = 0
        # 训练
        for file in sonFiles:
            # 将数字特征值写入特征值列表
            hwLabels.append(file[0])
            # 将文件内容小矩阵写入到大矩阵表中
            trainingMat[flagCount, :] = img2vector(os.path.join(fatherFolder, file))
            flagCount += 1
    # 错误累计
    errorCount = 0.0
    # 读取预测列表
    for fatherFolder, sonFolders, sonFiles in os.walk(testFolderMap):
        for file in sonFiles:
            # 读取数字的特征值
            classNumStr = file[0]
            # 读取文件的数据,生成1*1024的列表
            vectorUnderTest = img2vector(os.path.join(fatherFolder, file))
            # 将小矩阵的数据作为预测数据,大矩阵的数据作为训练数据,hwLabels为特征值
            classIfierResult = classIfY0(vectorUnderTest, trainingMat, hwLabels, 3)
            # 打印预测结果
            print(f'the classifier came back with : {classIfierResult}, the real answer is : {classNumStr}')
            # 错误统计
            if classIfierResult != classNumStr:
                errorCount += 1
        print(f'the total error rate is : {errorCount/float(len(sonFiles))}')

trainingFolderMap = r'G:\机器学习实战\机器学习实战资料\Ch02\trainingDigits'
testFolderMap = r'G:\机器学习实战\机器学习实战资料\Ch02\testDigits'
print(handwritingClassTest(trainingFolderMap, testFolderMap))

测试效果

在这里插入图片描述

2-1:自带文件测试(图片经过剪切)

资料

测试文件资料:约会+手写

©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页