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} ⎝⎜⎜⎜⎛y1−1y2−1⋮yn−1y1−2y2−2⋮yn−2⋯⋯⋱⋯y1−50y2−50⋮yn−50⎠⎟⎟⎟⎞
矩阵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}
⎝⎜⎜⎜⎛x1x1⋮x1x2x2⋮x2⋯⋯⋱⋯x50x50⋮x50⎠⎟⎟⎟⎞
第二步——进行运算:
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}
⎝⎜⎜⎜⎛x1−y1−1x2−y2−1⋮x1−yn−1x2−y1−2x2−y2−2⋮x2−yn−2⋯⋯⋱⋯x50−y1−50x50−y2−50⋮xn−yn−50⎠⎟⎟⎟⎞
第三步——各行的所有元素分别平方后放在一起求和,然后开根号,现在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}
⎝⎜⎜⎜⎛(x1−y1−1)2+(x2−y1−2)2+⋯+(x50−y1−50)2(x1−y2−1)2+(x2−y2−2)2+⋯+(x50−y2−50)2⋮(x1−yn−1)2+(x2−yn−2)2+⋯+(x50−yn−50)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]]
# ])