【代码学习笔记】KNN算法的实现详解,并附上测试代码及结果

KNN算法解析

根据《机器学习实战》P19
1.重新自己实现K邻近算法 并 2.生成随机数据测试

算法步骤


1. 数据形状:

为方便理解,设该数据集样本有50个数据label50个(代表各个数据样本所属的类别),50个特征

Dataset shape:

input_data.shape = (50, 50)
label.shape=(50, 1)

设有1个新的数据,要使用 KNN算法 以及以上 50个数据集样本 预测分类,也一样有 50个特征

test data shape:

test.shape = (1, 50)


2. 算法流程:

1.前1~3步,算出 新的数据的特征50个数据中每个数据的特征 之间分别的 欧氏距离


部分步骤用数学公式表示:

第一步——设置矩阵:
矩阵A——数据集特征矩阵: 行:数据集数据个数(n个)列:数据的特征数(50个)

A = ( y 1 − 1 y 1 − 2 ⋯ y 1 − 50 y 2 − 1 y 2 − 2 ⋯ y 2 − 50 ⋮ ⋮ ⋱ ⋮ y n − 1 y n − 2 ⋯ y n − 50 ) \begin {pmatrix} y_{1-1} & y_{1-2} & \cdots & y_{1-50} \\ y_{2-1} & y_{2-2} & \cdots & y_{2-50} \\ \vdots & \vdots & \ddots & \vdots \\ y_{n-1} & y_{n-2} & \cdots & y_{n-50}\end{pmatrix} y11y21yn1y12y22yn2y150y250yn50

矩阵B——需要预测分类的数据: 行:对应数据集的数据个数(n个)列:数据的特征数(50个)

B = ( x 1 x 2 ⋯ x 50 x 1 x 2 ⋯ x 50 ⋮ ⋮ ⋱ ⋮ x 1 x 2 ⋯ x 50 ) \begin {pmatrix} x_1 & x_2 & \cdots & x_{50} \\ x_1 & x_2 & \cdots & x_{50} \\ \vdots & \vdots & \ddots & \vdots \\ x_1 & x_2 & \cdots & x_{50}\end{pmatrix} x1x1x1x2x2x2x50x50x50

第二步——进行运算:

B - A= ( x 1 − y 1 − 1 x 2 − y 1 − 2 ⋯ x 50 − y 1 − 50 x 2 − y 2 − 1 x 2 − y 2 − 2 ⋯ x 50 − y 2 − 50 ⋮ ⋮ ⋱ ⋮ x 1 − y n − 1 x 2 − y n − 2 ⋯ x n − y n − 50 ) \begin {pmatrix} x_1-y_{1-1} & x_2-y_{1-2} & \cdots & x_{50}-y_{1-50} \\ x_2-y_{2-1} &x_2 - y_{2-2} & \cdots & x_{50}-y_{2-50} \\ \vdots & \vdots & \ddots & \vdots \\ x_1-y_{n-1} & x_2-y_{n-2} & \cdots & x_n-y_{n-50}\end{pmatrix} x1y11x2y21x1yn1x2y12x2y22x2yn2x50y150x50y250xnyn50

第三步——各行的所有元素分别平方后放在一起求和,然后开根号,现在shape=(n, 1)

( ( x 1 − y 1 − 1 ) 2 + ( x 2 − y 1 − 2 ) 2 + ⋯ + ( x 50 − y 1 − 50 ) 2 ( x 1 − y 2 − 1 ) 2 + ( x 2 − y 2 − 2 ) 2 + ⋯ + ( x 50 − y 2 − 50 ) 2 ⋮ ( x 1 − y n − 1 ) 2 + ( x 2 − y n − 2 ) 2 + ⋯ + ( x 50 − y n − 50 ) 2 ) \begin {pmatrix} \sqrt{(x_1-y_{1-1})^2 + (x_2-y_{1-2})^2 + \cdots + (x_{50}-y_{1-50})^2} \\ \sqrt{(x_1-y_{2-1})^2 + (x_2-y_{2-2})^2 + \cdots + (x_{50}-y_{2-50})^2} \\ \vdots \\ \sqrt{(x_1-y_{n-1})^2 + (x_2-y_{n-2})^2 + \cdots + (x_{50}-y_{n-50})^2}\end{pmatrix} (x1y11)2+(x2y12)2++(x50y150)2 (x1y21)2+(x2y22)2++(x50y250)2 (x1yn1)2+(x2yn2)2++(x50yn50)2

第四步——从各行的元素中找出前k个小的值
第五步——从这k个小的值对应的数据的类别,该类别最多的,便成为KNN算法预测这个输入数据应该分去的类别




3.代码实现:

设输入KNN算法函数的参数有:

(把 “1. 数据形状” 中的 shape=(50, 50),换成 shape=(n, m))

1.inputX : 输入的数据 ——(1, m),假设每个数据样本有m个特征
2.dataSet:数据集样本 ——(n, m),假设此处有n个数据
3.labels:标签样本——(n,1)
4.k:表示取前k个样本做统计


以下用代码表述算法流程:
import numpy as np

	def KNN(inputX, dataSet,labels , k):
		
		dataset_num = dataSet.shape[0]
		#这里 input.shape = (dataset_num, eign_num), 对应的是算法流程的矩阵B
		input = np.tile(inputX,(dataset_num, 1))
		
		#对应算法流程第二步
		distance = input - dataSet
		
		#对应算法流程第三步的矩阵
		sqdistance = distance ** 2
		#各行分别对所有列元素求和成一列
		sqdisSum = sqdistance.sum(axis=1)
		#距离大小从小到大排序 并且列出相应位置的index号 
		argsortlist = sqdisSum.argsort()
		
		#用字典存放
		classCount = {}
		for i in range(k):
			this_index = argsortlist[i]
			
			# thiskey: thisvalue + 1
			#classCount.get(key,defaultreturn=0)
			#defaultreturn:如果找不到这个value,那返回0并且+1
			#变成 thiskey:0+1
			classCount[labels[this_index]] = classCount.get(this_index,0) + 1
			
			# 原来书上代码:python2.7可用,python 3不可用
    		# sortedclassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1), reverse=True)

    		# python3方法,iteritems()方法不再适用,改成items()
    		# dict.items():key:value变成[(key(1),value(1)), ... ,(key(n),value(n))]
    		sortedclassCount = sorted(classCount.items(),key = lambda item:item[1],reverse=True)
			
			#sortedClassCount[keyindex][valueindex]
			return 	sortedClassCount[0][0]

模拟数据以测试代码
1. 准备工作,设置数据形状:

设该数据集样本有50个数据label50个(设分成3类,“1/2/3”)50个特征

Dataset shape:

dataset.shape = (50, 50)
label.shape=(50, 1)


设有1个新的数据,要使用 KNN算法 以及以上 50个数据集样本 预测分类,也一样有 50个特征

test data shape:

test.shape = (1, 50)

# 测试KNN函数,完整代码

import numpy as np

	def KNN(inputX, dataSet,labels , k):
		
		dataset_num = dataSet.shape[0]
		#这里 input.shape = (dataset_num, eign_num), 对应的是算法流程的矩阵B
		input = np.tile(inputX,(dataset_num, 1))
		
		#对应算法流程第二步
		distance = input - dataSet
		
		#对应算法流程第三步的矩阵
		sqdistance = distance ** 2
		#各行分别对所有列元素求和成一列
		sqdisSum = sqdistance.sum(axis=1)
		#距离大小从小到大排序 并且列出相应位置的index号 
		argsortlist = sqdisSum.argsort()
		
		#用字典存放
		classCount = {}
		for i in range(k):
			this_index = argsortlist[i]
			
			# thiskey: thisvalue + 1
			#classCount.get(key,defaultreturn=0)
			#defaultreturn:如果找不到这个key以及它的value,那创建key,并且设置value为0。后对这个key的value+1
			#变成 thiskey:0+1
			classCount[labels[this_index]] = classCount.get(labels[this_index],0) + 1
			
			# 原来书上代码:python2.7可用,python 3不可用
    		# sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1), reverse=True)

    		# python3方法,iteritems()方法不再适用,改成items()
    		# dict.items():key:value变成[(key(1),value(1)), ... ,(key(n),value(n))]
    		sortedClassCount= sorted(classCount.items(),key = lambda item:item[1],reverse=True)
			
			#sortedClassCount[keyindex][valueindex]
			return 	sortedClassCount[0][0]

#测试部分
# 50个样本,有50个特征
dataset = np.array([np.random.randint(1, 100, 50) for n in range(50)])
# 50个样本标签,标签有3类,1/2/3
label = np.random.randint(1, 4, 50)

#打印查看data
print(f'dataset Matrix\n:{dataset}\n')
print(f'label Martix\n:{label}\n')

test_data = np.random.randint(1, 100, 50)
print(f"this is test data martix:\n{test_data}")

# 参数`10`:代表取前10个最近距离的数据的label来统计,帮test_data分类
KNN(test_data, dataset, label, 10)



关于numpy.tile()函数
	# 关于tile函数
	import numpy as np
	# 先创建一个向量和一个矩阵来进行使用
	a = np.array([1, 2, 3, 4])  # 经过测试(Python3下1.13.0版本numpy),这里使用python中的列表形式也可以,最终也会被tile()函数转化成numpy中的array形式。
	# 第一种使用方法
	x1 = np.tile(a, 2)
	#    ->  array([1, 2, 3, 4, 1, 2, 3, 4])
	
	# 第二种方法
	# (2,1)第一行里面重复1次,然后有两行和第一行一样的矩阵
	x2 = np.tile(a, (2, 1))
	#    -> array([[1, 2, 3, 4], [1, 2, 3, 4]])
	
	# (2,2,1)第一行里面重复1次(参数里面第三个数字“1”),
	# "这个矩阵"里面然后 有两行 和第一行一样的矩阵(参数里面第二个“2”)
	# "这个矩阵"有两个 (参数里面第一个"2")
	x2 = np.tile(a, (2, 2, 1))
	#    -> array([
	#              [[1, 2, 3, 4], [1, 2, 3, 4]]
	#			   ,
	#              [[1, 2, 3, 4], [1, 2, 3, 4]]
	#             ])
  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值