KNN(kNN, k-NearestNeighbor)可以说是机器学习入门的算法,它是一种基本分类与回归的算法。
KNN通俗理解
KNN近邻算法实际上是利用的相似性来进行分类,简单来说和人以类聚,物以群分是一个道理,也可以说近朱者赤近墨者黑。比如你交了一个新朋友,想知道他的经济情况是哪个层次,但是中国人非常避讳这一点,就好像你不能问女孩子年龄一样,但是还是想知道,咋办呢?那就看他身边的朋友,如果他身边的朋友都是大款,那么十有八九他自己也属于资产阶级;如果他经常和一帮穷逼走在一起,一般这种人也是穷逼。
KNN工作原理
1.假设现有有带标签的样本数据集(训练集),注意可以有多个分类。
2.输入不带标签的数据后,将不带标签的数据与训练集中的每条数据进行比较
3.计算不带标签数据与训练集中每条数据的距离(我们自己定义),然后按距离从小到大进行排序。
4.取出前k个样本数据对应的标签值,这k个数据中出现次数最多的分类标签就作为不带标签数据的类别。
KNN特点
从上面的描述可以看到,KNN近邻算法实际上没有进行训练,也就是说它的训练复杂度为0,KNN近邻算法是用相似性来判断类别的,你和谁更像,那就认为你是哪种人。
KNN适用于数值型和标称型的数据,其优点是精度高、对异常值不敏感、无数据输入假定,但是缺点也很明显,我们每次使用都需要遍历整个训练集,计算复杂度和空间复杂度都很高。
KNN案例
案例还是使用经典的海伦小姐约会来说吧。场景是这样的,海伦小姐大龄单身,爸妈催着她结婚,没办法啊,可以租男朋友总不能租老公吧,那就相亲找对象吧。
海伦小姐是高级白领呀,时间很宝贵的,相亲多了,她发现有一部分人压根就是在浪费她的时间,因为这一部分人她压根不喜欢;还有一部分是不喜欢也不讨厌的,如果比较闲暇,去和这样的人去相亲也还能接受;当然还有一部分是比较吸引海伦小姐的,遇到这样的人,就算向公司请假也要去见见面。
那么问题来了,海伦小姐还没有见过对方,怎么就知道对方是哪一类人呢,但是海伦小姐提供了一份她以前相亲经历的所有案例,总共有1000场相亲经历(相亲好像有点多了),现在有机器学习,海伦小姐找到小绪想让他分析一下这些数据,让她以后不用见面就能先大致知道对方属于自己心目中的哪一类人。
海伦小姐总共就考察了三个指标,分别是:每年获得的飞行常客里程数、玩视频游戏所耗时间百分比、每周消费的冰淇淋公升数;可以看出来海伦小姐还是很精明的,第一个指标基本上就能确定对方的工作性质、经济状况等等情况了,第二个就不说了,花太多时间在游戏一般没出息(职业选手除外),第三个指标一看就知道对方的身材还不好,胖子大多喜欢吃冰淇淋。
KNN实现
KNN实现直接按上面说的步骤进行即可,第一步一般都是读取数据咯,把数据组织成自己喜欢的样子,也就是方便程序操纵的样子。
def get_data():
df = pd.read_csv('hailun.csv')
labels = np.array(df['label'])
df.drop('label', axis=1, inplace=True)
data = np.mat(df)
# 数据集和标签集
return data, labels
要注意一点的就是,这里要对数据进行归一化处理,所给的三个特征中,数据的取值范围相差很大,在机器学习中,这种情况就不得不选择很小的学习因子来避免成本函数发生震荡,而特别小的学习因子会让学习速率变得很慢,进行归一化操作可以方便后面的数据处理,也保证了程序收敛加快,归一化处理有多种方式,这里不再赘述。
def normalization(data):
"""
归一化处理,归一化公式为:Y = (X-Xmin)/(Xmax-Xmin)
:param data: 待处理数据
:return: 处理后的数据
"""
min_val = data.min(0)
max_val = data.max(0)
# 极差
ranges = max_val - min_val
norm_data = (data - min_val) / ranges
return norm_data, ranges, min_val
数据都处理好了,最后直接按前文说的KNN原理实现就好了。
def classify(inX, norm_data, labels, k):
"""
:param inX: 待分类数据
:param norm_data: 输入的训练样本集
:param labels: 标签向量
:param k: 选择最近邻居的数目
:return: 分类标签
"""
# 这里使用的是欧式距离
data_size = norm_data.shape[0]
diff_mat = np.tile(inX, (data_size, 1)) - norm_data
sq_diff_mat = np.array(diff_mat) ** 2
sq_distances = sq_diff_mat.sum(axis=1)
distances = np.array(sq_distances) ** 0.5
# 将距离排序:从小到大
sorted_dist_indicies = distances.argsort()
# 选择前k个,并选择最多的标签类别
clazz_count = {}
for i in range(k):
label = labels[sorted_dist_indicies[i]]
clazz_count[label] = clazz_count.get(label, 0) + 1
sorted_clazz = sorted(clazz_count.items(), key=operator.itemgetter(1), reverse=True)
return sorted_clazz[0][0]
当然,更专业一点的方法是把数据集划分为两部分,一部分作为训练集(这里没有训练),一部分作为验证集,习惯上是三七分,通过验证集可以进行模型的筛选,这里就不说了。
最后可以简单的测试一下,以后海伦小姐在相亲之前就可以通过下面的方式先看下自己是不是需要去参加,这个小程序是便利多多。
if __name__ == '__main__':
data, labels = get_data()
norm_data, ranges, min_val = normalization(data)
inX = np.array([26052, 1.441871, 0.805124])
a = classify((inX - min_val)/ranges, norm_data, labels, 5)
print(a)
# 结果为1,这一个极具魅力的男性
KNN邻近算法不仅仅能干这么简单的事,也可以用它玩玩房价预测、手写字体识别,甚至用它实现文本分类也是可以的,只是效果不太理想吧。
参考内容:k-近邻算法
数据集:约会数据