KNN 的浅薄学习笔记
今天了解了一下机器学习,看到了KNN
这种算法,因为没搞懂,所以打算把学习过程写成博文辅助自己理解
代码参考 kaggle 上的 Handwritten digits Classification(Using KNN ) 和 Movie Ratings and Recommendation using KNN
这是啥?
KNN
,全称 “ k-nearest neighbor classification”,是一种机器学习算法,它是一种基于分类的有监督学习方式,核心思想是“相似样本有相似输出”,从一大堆数据集中进行训练,所以会有训练集,和测试集,一般是三七开或者二八开,训练集会把样本和标记全部给到模型,然后库比如 scikit-learn
或者 pytorch
会对数据进行处理,或者你可以手动实现,然后你给它个新的值就能做预测。其中KNN
中的 K
代表的是 k
值,表示这个新的东西临近的样本的个数,k
值会直接影响模型判断的准确率,如果 k
值过大可能就欠拟合,也就是没找到规律;k
值过小就是过拟合,简单说就是新数据的不认识,只认识训练数据,接下来是实现和使用过程
怎么实现?
使用现有库
以 Scikit-learn 举例,首先拿到数据,对数据进行预处理,学习的话一般数据会从数据集的库里拿,比如 scikit-learn
自己的数据集 ,具体数据集对应的使用方法都在API文档里面,我们拿这个Handwritten digits Classification(Using KNN )的代码来做例子:
首先是数据导入和预处理
1 | from sklearn import datasets # 从库里获取数据集 |
然后是使用 scikit-learn
库来做机器学习,第一步是弄个 k
值预设表 kVals
,之后的 k
值就会从这里一个一个试,试到满意为止
1 | kVals = range(1, 30, 2) # 1 ~ 30 之间的所有奇数,换成C#也就是 Enumerable.Range(1, 30).Where(x => x%2 != 0); |
这里是最重要的地方,接下来就是一个一个试 k
值了
1 | # 导入几个新的工具 |
试出来所有 k
值对应的准确率后就该正式训练模型了
1 | # 导入一下新的工具 |
差不多了,后面都是无关紧要的验证,最重要的是前面的实现,那么接下来就是手动实现的案例了
手动实现
Movie Ratings and Recommendation using KNN 的代码使用的是手动实现的方式,它实现了一个电影评价系统,开始它先导入了两张表
1 | import pandas as pd # 用来导入表的库 |
接着的部分是利用输出辨别数据结构,不是KNN讨论的重点,所以我们略过这一部分
接下来合并了两张表方便后续处理
1 | movies = movies.merge(credits,left_on='id',right_on='movie_id',how='left') |
下面是它核心的算法部分,用于从多个维度计算两个电影之间的距离
1 | from scipy import spatial |
所以我们可以用下面的方式来调用这个函数
1 | Similarity(3,160) #checking similarity between any 2 random movies |
下面,它定义了一个分数预测器 predic_score(name)
函数,这里面是模型训练和获取预测值的相关逻辑,详细拆解如下:
这里是最重要的地方,在这个函数里面,它先定义了一个“获取邻居”的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16def getNeighbors(baseMovie, K):
distances = []
# 遍历所有电影,计算与目标电影的距离
for index, movie in movies.iterrows():
if movie['new_id'] != baseMovie['new_id'].values[0]: # 排除自身
dist = Similarity(baseMovie['new_id'].values[0], movie['new_id'])
distances.append((movie['new_id'], dist)) # 以(电影ID,距离)的tuple来存储
# 按距离升序排序(距离越小越相似)
distances.sort(key=operator.itemgetter(1))
# 取前K个最近邻
neighbors = []
for x in range(K):
neighbors.append(distances[x])
return neighbors下面是获取预测值的步骤,这里我已经把
getNeighbors(baseMovie, K)
抽象到上面了,方便理解1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19def predict_score(name):
# 找到符合目标电影关键词的列表
new_movie = movies[movies['original_title'].str.contains(name)].iloc[0].to_frame().T
print('Selected Movie: ', new_movie.original_title.values[0])
K = 10 # 取10个邻居,拟定 k 值为 10,实际并没有经过过测试
neighbors = getNeighbors(new_movie, K) # 获取最近的10个邻居
# 输出匹配到的10个结果
print('Recommended Movies: ')
avgRating = 0
for neighbor in neighbors:
avgRating += movies.iloc[neighbor[0]]['vote_average'] # 累加近邻评分
print(f"{movies.iloc[neighbor[0]]['original_title']} | Genres: {str(movies.iloc[neighbor[0]]['genres']).strip('[]').replace(' ','')} | Rating: {movies.iloc[neighbor[0]]['vote_average']}") # 打印匹配到的电影的详情
# 预测评分(近邻平均评分)这步已经是多余了,主要是根据KNN的思想,假设相似的电影具有相似的属性,那么作用是,如果电影未上映,则可以预测出一个可能的评分
avgRating /= K
print(f'\nThe predicted rating for {new_movie["original_title"].values[0]} is: {avgRating}')
print(f'The actual rating for {new_movie["original_title"].values[0]} is {new_movie["vote_average"].values[0]}')这就是关于这个的全部解析了
下面是个人有关第二个手动实现的代码的一些想法
结合面向对象编程
那么我们现在看到,这个代码的遍历方式和比较方式是很笨拙的,可扩展性不高,要是再有多一点东西,基本上就能感觉到修改极其吃力,并且我们理解也需要花很高的成本
下面这个音乐分类的例子采用了面向对象的思想
1 | import random |
- 标题: KNN 的浅薄学习笔记
- 作者: 零风PLFJY
- 创建于 : 2025-07-22 17:32:37
- 更新于 : 2025-08-30 09:56:33
- 链接: https://blog.plfjy.top/KNN-shallow-learning-note/
- 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。