《机器学习实战》萌新入门笔记 ① — — K近邻算法 趣味讲解和书本实例详细注释后代码
开坑前言:大一在读新生开启了自己的机器学习之旅,从接触到现在已经有快两个月了,现在回过头为夯实基础,从开始看起《机器学习实战》这本书,即使是最简单的KNN算法,详细看过作者用python实现的代码后还能感觉到不少收获。开这个坑希望能分享一些经验给到更多一同在学习这本书的朋友,共同进步。
若本文有漏洞或者希望交流的朋友可以添加企鹅好友:27157812,欢迎大家指正。
本系列笔记正式开始:
故事引入:
首先!有一只小牛在网上冲浪,它正在找伴侣,于是在一个约会网站上搜啊搜。
由于约会网站很是发达也有些缺点,这只小牛在这个网站上找到了1000个可叫来约会的对象,但是对于这些对象只知道三个信息,分别是身高,体重,颜值(满分为10分)。
于是小牛把这1000个对象的信息储存在一个表格里,小牛和这1000个对象逐个叫来约会……
过了N年,小牛和1000个对象一一约会完毕。每个对象小牛都给出了一个喜爱程度,1是不喜爱,2是一般般,3是特别喜爱。
约会完以后,约会网站又给小牛分配了1000个新对象,但是这个小牛不想再通过约会来知道这些对象是不是他喜爱的了,他想通过约会网站给出的下一个对象的三个信息,来预估自己喜不喜欢下一个对象(如果不喜欢或者一般就不去约会了 )。
小牛根据之前1000个老对象的三个信息,和约会得出的喜不喜欢,建立了一个三维坐标轴,这三维分别是身高 体重 颜值,通过这三个维度,点出了每个对象在三维坐标系中所在的位置。
而每个对象都有对应的喜欢程度,而喜欢程度都对应一个颜色,不喜欢的就是灰色,一般般的就是蓝色,特别喜欢的就是红色,小牛由此建立了一个装载了1000个老对象的三维坐标空间。
小牛把新对象一个个放进坐标系中进行比较,根据三个信息确定了他们在空间里的位置,计算了新对象和所有老对象在空间坐标系中的距离。
再找到离这个新对象最近的10个老对象,看看他们是什么颜色的,如果距离最近的老对象们大多是红色的,那么新对象一定也差不到哪去,小牛笃定这个新对象一定是值得约会的!如果最近的老对象们大多是黑色的,小牛笃定这个新对象肯定很差劲,于是就根本不打算和这个差劲的新对象约会。
是不是非常简单易懂呀,实际上就是对于训练集(老对象们)建立了一组三维空间坐标系,根据几何距离的关系来对测试样本(新对象)的类别做出判断。
而KNN的算法是在本故事的基础上将问题上升到了n维空间中,而故事里边寻找的距离新对象最近的老对象的数量,指代的就是KNN中的K,如果K=1,算法就叫最近邻,K=3,算法就叫3近邻。
这里将书本上算法实现流程码在下面仅供参考:
对未知类别属性的数据集中的每个点依次执行以下操作:
(1)计算已知类别数据集中的点与当前点之间的距离
(2)按照距离递增次序排序
(3)选取与当前点距离最小的k个点
(4)确定前K个点所在类别的出现频率
(5)返回前K个点出现频率最高的类别作为当前点的预测分类
下面给出《机器学习实战》中,对于datingTestSet2.txt这一数据集实现绘制数据分析散点图和使用KNN算法预判类别功能的代码,(运行于版本最新的Python3.9):
这个代码需要结合书本来看,也就是说该代码对于有书的同学帮助较大。它是在原代码的基础上进行了优化和整合,将我注释的代码和源代码一一对应和比较更容易看懂~
这里给出各个函数对应的功能方便查阅:
- file_matrix 读入数据集
- classify0 实现KNN算法
- vasual_map 绘制数据分析图
- create_dataset 无视即可
from numpy import *
import operator
import matplotlib.pyplot as plt
def file_matrix(filename):
fr = open(filename)
# open()函数用于打开一个文件,创建一个file对象,相关的方法才可以调用它进行读写
array_in_line = fr.readlines()
# file.readlines([size]) :返回包含size行的列表, size 未指定则返回全部行
number_of_lines = len(array_in_line)
# Python len() 方法返回对象(字符、列表、元组等)长度或项目个数。
return_mat = zeros((number_of_lines, 3))
# return_mat为len行,一行三元素的由零组成的数组,视为初始化样本数组
class_label_vector = []
index = 0
for line in array_in_line:
line = line.strip()
# 去除该行的首尾空格
list_from_line = line.split('\t')
# \t为制表符,意为分割有空格分隔开的各元素
return_mat[index, :] = list_from_line[0:3]
# 注意,这里只取0 1 2个元素,不包括第三个元素,该数组即为读入的数据
class_label_vector.append(int(list_from_line[-1]))
# -1为最后一个元素,是每个样本的标签值
# 注:必须有int注明为整数变量,否则将读入字符串型
index += 1
return return_mat, class_label_vector
def create_dataset():
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(in_x, dataset, labels, kling):
dataset_size = dataset.shape[0]
# 获取训练集的大小
diff_mat = tile(in_x, (dataset_size, 1)) - dataset
# 将样本in_x按照dataset的大小纵向复制,(行,列),减去dataset代表各维度相减
sq_diff_mat = diff_mat**2
# 样本与测试集的各维度之差做平方
sq_distances = sq_diff_mat.sum(axis=1)
# axis=1代表跨行,axis=0代表跨列,这里表示计算样本与训练样本的各维度距离平方和
distances = sq_distances**0.5
# 计算样本与所有训练样本的距离
sorted_dist_index = distances.argsort()
# 返回对所有距离从小到大排序的索引值
class_count = {}
for i in range(kling):
vote_label = labels[sorted_dist_index[i]]
class_count[vote_label] = class_count.get(vote_label, 0) + 1
# get(key, default=None)
# 参数
# key -- 字典中要查找的键。
# default -- 如果指定键的值不存在时,返回该默认值。
# 这里是选择距离最小的k个点
sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)
# sorted是对是所有可迭代的对象进行排序操作,items(原iteritems)是将字典以列表的形式返回,itemgetter是返回对象第一个域的值,reverse=Ture是是数据
return sorted_class_count[0][0]
# 最后返回排序后的第一个点
def vasual_map(datamat, datalabels):
fig = plt.figure()
# 创建自定义图像
ax = fig.add_subplot(1, 2, 1)
ay = fig.add_subplot(1, 2, 2)
# add_subplot(x,y,z) 表示将图像划分为x行y列,此子图占据从左到右从上到下的第z个位置
ax.scatter(datamat[:, 1], datamat[:, 2], 15.0*array(datalabels), 15.0*array(datalabels))
ay.scatter(datamat[:, 0], datamat[:, 1], 15.0*array(datalabels), 15.0*array(datalabels))
# 使用数据矩阵的第二列第三列数据来构建ax子图像,并利用datalabels储存的不同标签,在散点图上绘制了色彩不等,尺寸不同的点
plt.show()
datingdatamat, datinglabels = file_matrix('datingTestSet2.txt')
vasual_map(datingdatamat, datinglabels)
forecast_class = classify0([35483, 12.273169, 1.508053], datingdatamat, datinglabels, 5)
print(forecast_class)
附上书上用代码绘制出的两个散点图:
后续的笔记很快就会更新!也请用这本书学习的同学持续关注我哦。
上一篇: python实现数据可视化