目录
引言:之前学习机器学习学的比较乱,而且也没有进行过系统的总结,这段时间打算重新学习机器学习,参考书籍--机器学习实战,机器学习西瓜书,这里我会根据自己的吸收消化加以总结,使得进一步加深理解。
总结不易,转载请注明本文出处或链接,感谢。
下面进入正题:
1、什么是K近邻算法?
我想先举一个例子:假如现在有三个人,这里我们称为A,B,C;这三个人位于同一个平面上,每个人都处在不同的位置上;这时又来了第四个人D,这个人和这三个人处在同一个平面上,我们现在的目标是计算这个人D和这三个人A,B,C谁离得最近,那我们需要分别计算出他们之间的距离,然后排序选出最小的距离,这时我们就可以说D离谁最近。距离怎么算呢,假设我们用坐标表示他们的位置,A(x1, y1), B(x2, y2), C(x3, y3), D(x4, y4)。那么我们可以用横坐标xi的差的平方加上纵坐标的差的平方然后再开平方,也就是我们所谓的欧氏距离公式。如果说他们不在一个平面上,而是在一个空间上呢,那就在多加一个坐标元素变为:A(x1, y1, z1), B(x2, y2, z2), C(x3, y3, z3), D(x4, y4, z4)。也可以拓展到多维,多维的时候对应的坐标相减求平方再求和开平方即可。计算公式如下。
二维平面上:欧式距离公式:
三维空间上:欧式距离公式:
上面说了那么多,可能就会有人觉得这个高中就学过,和K近邻算法有什么关系呢,其实K近邻算法我觉得是基于上面的计算距离引出的。
通俗定义:采用测量不同样本之间对应特征值之间的距离的方法进行分类。
那么上面举的例子A,B,C,D这四个人的坐标值就代表他们各自拥有的特征值。假设还是处在同一平面上的,即每个人的坐标有x和y构成,这里我们把x,y当作他们每个人的两个特征值,而每个人又是一个单独的样本,下面我们把人称作样本。假设现在还是A,B,C三个人处在不同位置,这三个人分别代表三个类别a,b,c。
假定我们现在所有的样本(D, E, F,......等等),只要位于这个平面中,我们的目的就是要把这个样本划分a,b,c其中一个类别,也就是推断我们新添加到这个平面上的样本是属于a,b,c中哪一个类别。我们还是依然计算每个样本到A,B,C这三个样本的距离,然后取出前K个最小的距离,也即将距离从小到大排序之后,取出前k个最小,这就是K近邻算法中K的由来。
然后呢,每一个距离它都对应一个样本所属的类别,我们就看这个前k个中样本所属的类别,出现的次数最多的那个类别,我们就把我们新添加到平面上的样本归为这个次数出现最多的那个类别。比如:我们现在有一个样本D,它到A,B,C的距离分别为:d1, d2, d3;按照从小到大的顺序排序之后为:d2, d1, d3,我们要取前k个最小的距离,假如k=1,即我们取到最小的距离为d2,d2是我们算的样本D到样本B的距离,样本B的类别为b,所以我们这里就推断说:样本D所属的类别为b。
2、电影分类实践
我们先举个简单的例子实现一下,这是机器学习实战上的例子,我觉得很不错,可以更好的理解我上面的那段话。
假设:我们现在要做电影分类任务,依据电影片段中的打斗情节和亲吻的情节出现的次数,来判断这个电影是属于爱情片还是动作片。数据如下:每一行代表一个样本对应的两个特征和一个标签(类别),就像是上面说到的一个人的坐标为特征,并且还有一个对应的标签。
电影名 | 特征1(打斗片段) | 特征2(接吻片段) | 标签(类别) |
电影1 | 3 | 87 | 爱情片 |
电影2 | 2 | 94 | 爱情片 |
电影3 | 4 | 102 | 爱情片 |
电影4 | 100 | 3 | 动作片 |
电影5 | 99 | 4 | 动作片 |
电影6 | 89 | 5 | 动作片 |
注意:这些数字是个人编辑的,并不是真实的电影分类的依据,我们做的只是假设,带大家去理解K紧邻算法。
从上面的数据中,我们可以看到每一个电影拥有两个特征值,然后对应一个标签,即所属的电影类别。依据这些数据,当给定我们一个电影7,只给定了电影7的两个特征值,然后我们预测电影7所属的类别。我们可以编写程序来实现,具体思路如下:
①、制定我们的数据集dataset
②、计算电影7与其他电影之间的特征值的距离
③、将距离进行排序,取出前k个最小距离
④、计算前k个最小距离中出现次数最多的类别
⑤、根据前四步推断出电影7所属的类别。
下面我们来进行实现,代码进行了详细的注释:
# 引入库
import numpy as np
# 第一步:根据现有的数据制作我们的数据集
def createDataset():
# 每个样本占一行,每一行有三列,我们把爱情片和动作片当作0和1来处理,
dataset = np.array([[3, 87, 0],
[2, 94, 0],
[4, 102, 0],
[100, 3, 1],
[99, 4, 1],
[89, 5, 1]])
# 创建好数据集之后,我们需要分别获得特征值和标签
features = dataset[:, :2]
labels = dataset[:, -1]
#打印标签和特征进行查看
print('features=', features)
print('labels=', labels)
return features, labels
# 定义一个字典便于我们取出数字化之后的标签对应的真实信息
labels_true = {'0':'爱情片', '1':'动作片'}
# 定义一个函数用于生成我们想要测试的数据
def testData():
# 创建一个数组用于存储两个特征值, 这里我们生成的行数等于上面已知的样本的数量,其每一行的值是相等的
testdata = np.zeros((6, 2), dtype=np.uint8)
# 输入第一个特征值
feature1 = input('请输入打斗场景次数:')
# 添加到第一列
testdata[:, 0] = feature1
# 输入第二个特征值
feature2 = input('请输入亲吻场景次数:')
# 添加到第二列
testdata[:, 1] = feature2
return testdata
# 定义一个函数,用于计算预测样本和一直样本之间的距离
def calculateDistance(features, testdata):
# 对应特征值先做减法
distance_mat = testdata - features
print('做减法之后=', distance_mat)
# 做差之后进行平方
distance_mat = distance_mat ** 2
print('平方之后等于=', distance_mat)
# 对每一行进行求和,即可得到当前预测样本到各个样本特征值的距离
distance_mat = distance_mat.sum(axis = 1)
print('对每一行求和=', distance_mat)
return distance_mat
# 对距离进行排序,然后取出前k个最小距离
# 统计前k个中哪个类别出现的次数最多
def get_class(distance, features, labels, k=3):
# 先按照距离由小到大进行排序
distance_sort = np.sort(distance)
print(distance_sort)
# 用于存储前k个最小距离所在样本数据的标签
list1 = []
for i in range(len(distance_sort)):
# 获得排序后的每一个元素
value = distance_sort[i]
# 找到其在排序之前的数组中的下标
index = np.where(distance==value)
# 将排序后的每一个元素在原始列表中的下标保存在一个列表中,用于后面找到其所属的类别
list1.append(index[0][0])
print(list1)
# 样本对应的标签有两种,爱情片--0,动作片--1
label1 = 0
label2 = 1
# 记录每一个类别出现的次数
cn1 = 0
cn2 = 0
# 查看其排序后的前k个元素所属的类别
for i in list1[:k]:
if labels[i] == label1:
cn1 += 1
else:
cn2 += 1
print(cn1, cn2)
# 判断哪个类别出现的多
if cn2 > cn1:
# labels_true是上面定义的字典,方便我们取出其真正的类别标签
print(labels_true[str(label2)])
else:
print(labels_true[str(label1)])
if __name__ == '__main__':
# 调用createDataset()函数,获得我们人工创造的数据集的样本特征和标签
features, labels = createDataset()
# 调用testData()函数,生成测试样本特征
testdata = testData()
# 调用calculateDistance(features, testdata)函数,计算测试样本到人工创造样本的距离
distance = calculateDistance(features, testdata)
# 调用get_class(distance, features, labels)函数,推断测试样本所属类别
get_class(distance, features, labels)
运行结果如下:
虽然这个数据集的意义不是很大,但是它很好的展现了K近邻算法的用法。
K近邻算法看起来比较简单粗暴,就是计算当前测试样本到每个已知样本的距离,然后取前k个最小距离,计算这前k个最小距离每个类别出现的次数,然后找出出现次数最多的那个类别,即我们当前测试样本的所属类别也就找到了。
K近邻算法不足之处:如果一个样本有多个特征,而且每个特征的数值都比较大,这就会导致计算效率低,计算复杂度高的问题。从上面的例子我们可以看出该算法是基于数据集,所以数据集如果庞大的话,这种简单粗暴的算法似乎性能不是太好。由此引出了归一化方法,以及其他算法。