1、算法概述
最简单的分类器是把全部的训练数据所对应的特征和标签都记录下来,当测试数据的特征和某个训练样本的特征完全匹配时,便可以给测试数据打上该训练样本的标签,但是我们无法保证所有的测试对象都能在训练数据集中找到与之唯一对应的标签,可能该标签不存在或者存在多个不同的标签,基于这些问题产生了K近邻算法(KNN)。
2、工作原理
存在一个样本数据集,并且每个样本数据都存在标签(称作训练数据集),当输入不带标签的数据后,将新数据和训练数据集的特征进行比较,如果新数据与特征空间中的k个训练样本属于同一个类别,则该样本也属于这个类别,通常K的取值不大于20。
3、算法原理
对未知标签的数据集(测试数据集)中的每个点依次执行如下的操作;
1) 计算已知类别数据集中的点与当前点之间的距离;2)按照距离递增次序排序 ;3) 选取与当前点距离最小的K个点;4) 确定前K个点所在类别的出现频率;5) 返回前K个点出现频率最高的类别标签作为当前点的预测分类。
4、实战
1、对未知数据进行分类
from numpy import *
import operator
# 训练样本数据
def createDataSet():
group = array([[1.0, 1.1], [1.0, 1.0], [0, 0], [0, 0.1]])
labels = ["A", "A", "B", "B"]
return group, labels
def classify0(inX, dataSet, labels, k):
"""
# int_x, 分类数据
# data_set, 训练样本集
# labels, 标签向量
# K, 用于选择最近邻居的数目
"""
# dataSet训练数据的行数
dataSetSize = dataSet.shape[0]
# 将数据拓展成和训练数据相同行数和列数的矩阵。计算差值矩阵
diffMat = tile(inX, (dataSetSize, 1)) - dataSet
# 差值矩阵的平方
sqDiffMat = diffMat ** 2
# 计算矩阵中每一行元素之和,axis=1表示矩阵跨列求和
sqDistance = sqDiffMat.sum(axis=1)
# 对每一行元素求平方根
Distance = sqDistance ** 0.5
# 每一行元素按照从小到大排序,返回每个元素所对应的下标
sortedDistanceIndex = Distance.argsort()
# 保存最后结果并统计每个结果的投票数(字典)
classCount = {}
# 统计前k个样本元素的所属类别包含的标签个数
for i in range(k):
index = sortedDistanceIndex[i]
# votellabel表示樣本index对应的分类结果
voteiLable = labels[index]
# 字典获取每一个分类结果对应的value值
classCount[voteiLable] = classCount.get(voteiLable, 0) + 1
# 对classCount字典,按照第二行元素从大到小进行排序
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1),
reverse=True)
return sortedClassCount[0][0]
if __name__ == '__main__':
k = 3
group, label = createDataSet()
x = array([0, 0])
print(classify0(x, group, label, k))
2、使用KNN改进约会网站的配对效果
训练数据特征:每年获得的飞行常客里程数、玩视频游戏所耗时间百分比、每周消费的冰淇淋公升数,
训练数据标签:不喜欢的人、魅力一般的人、极具魅力的人
工作流程:1)收集文本数据;2)解析文本数据(输入为字符串,输出为训练样本矩阵和类标签向量;3)用matplotlib画散点图分析数据;4)训练算法;5)测试算法;6)输入特征值,给出标签。
import matplotlib.pyplot as plt
import numpy as np
# 将文本转换成Numpy类型array数组
def file2matrix(filename):
# 打开文件
fr = open(filename)
# 获取文件的所有行
arrayLines = fr.readlines()
# 获取文件的行数
numberLines = len(arrayLines)
# 创建零矩阵
retureMax = np.zeros([numberLines, 3])
# 把文本转换成数字
labelVector = {"largeDoses": 3, "smallDoses": 2, "didntLike": 1}
# 保存训练数据的标签数据
classLabelVector = []
index = 0
for line in arrayLines:
# 删除首尾空行
line = line.strip()
# split函数通过"\t"分割字符串
listFromLine = line.split("\t")
# 把listFromLine中的前三个元素添加到returnMax中
retureMax[index, :] = listFromLine[0:3]
# 把listFromLine数组的最后一行标签添加到标签数组中
classLabelVector.append(labelVector[listFromLine[-1]])
# returnMax矩阵行数加1
index += 1
# 返回训练样本矩阵和类标签
return retureMax, classLabelVector
# 数据可视化
def showDateSet_(datingDataMat, datingLabels):
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:, 0], datingDataMat[:, 2], c=10 * np.array(datingLabels), s=10 * np.array(datingLabels),
label='散点图')
plt.xlabel('玩视频游戏所耗时间百分比', fontproperties='SimHei', fontsize='14')
plt.ylabel('每周消费的冰淇淋公升数', fontproperties='SimHei', fontsize='14')
plt.show()
# 归一化特征值
def autoNorm(dataSet):
# 获取数据中每列的最大值(0:列)
maxValues = dataSet.max(0)
# 获取数据中每列的最小值
minValues = dataSet.min(0)
# 计算最大值和最小值的差值
ranges = maxValues - minValues
# 创建一个与dataSet相同shape的0矩阵,用于存放归一化后的数据
normDataSet = np.zeros(dataSet.shape)
# 获取数据集的行数
data_row = dataSet.shape[0]
# 计算数据集和最小值矩阵的差值
normDataSet = dataSet - np.tile(minValues, (data_row, 1))
# 把最大值和最小值的差值扩充到与dataSet同shape的矩阵,计算商值
normDataSet = normDataSet / np.tile(ranges, (data_row, 1))
return normDataSet, ranges, minValues
# KNN算法
def classify0(intX, dataSet, labels, k):
# intX: 输入测试样本(x,y)
# dataSet: 训练样本集([a1,a2]...[n1,n2])
# labels: 训练样本标签
# k: 分类前k个值
# 获取训练样本集的行数
data_row = dataSet.shape[0]
# 把测试样本转换成和训练样本同shape的数组,并计算差值
diffMat = np.tile(intX, (data_row, 1)) - dataSet
# 计算差值矩阵的平方值
sqDiffMat = diffMat ** 2
# 计算平方之后的数组每行的和
sqDistance = sqDiffMat.sum(axis=1)
# 计算平方之后差值矩阵的平方根
distance = sqDistance ** 0.5
# distance中的元素从大到小排序,得到排序之后的下标值
sortDistance = distance.argsort()
# 统计每个标签出现的次数
labelsCount = {}
for i in range(k):
# 获取对应下标的标签属性
voteLabels = labels[sortDistance[i]]
# 若labelsCout中不存在该元素,则+1,否则再原基础+1
labelsCount[voteLabels] = labelsCount.get(voteLabels, 0) + 1
# 对labelsCount字典进行从大到小排序,sortLabelsCount是一个list,每个元素是一个tuple
sortLabelsCount = sorted(labelsCount.items(), key=lambda x: x[1], reverse=True)
return sortLabelsCount[0][0]
def dataingClassTest(normMat, dataLabels, k):
# 测试数据,训练数据比例为1:9
hoRatio = 0.1
# 获取归一化之后数据集的行数
m = normMat.shape[0]
# 测试数据集行数
numTestVecs = int(m * hoRatio)
# 错误分类计数器
errorCount = 0
for i in range(numTestVecs):
classifyResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], dataLabels[numTestVecs:m], k)
if classifyResult != dataLabels[i]:
errorCount += 1
return errorCount / numTestVecs
if __name__ == '__main__':
datingDataMat, datingLabels = file2matrix(r"C:\Users\chen\Desktop\datingTestSet.txt")
# showDateSet_(datingDataMat, datingLabels)
normMat, ranges, minValues = autoNorm(datingDataMat)
errorRate = dataingClassTest(datingDataMat, datingLabels, 8)
print(errorRate)