KNN
监督学习是一种依赖输入数据来学习函数的算法,该函数在给定新的未标记数据时可以产生适当的输出。
监督学习用于解决分类或回归问题。
分类问题的输出是离散值。例如,“喜欢披萨上的菠萝”和“不喜欢披萨上菠萝”是离散的,没有中间立场。
回归问题的输出是实数(带小数点的数字)。例如,我们可以使用下表中的数据来估计给定身高的人的体重。
我们有一个自变量(或一组自变量)和一个因变量(给定自变量,我们试图猜测的事情)。例如,我们可以说身高是自变量,体重是因变量。
无监督的机器学习算法使用没有任何标签的输入数据——换句话说,没有老师(标签)告诉孩子(计算机)什么时候是正确的,什么时候犯了错误。
监督学习试图学习一个函数,该函数将允许我们在给定一些新的未标记数据的情况下进行预测;无监督学习试图学习数据的基本结构,让我们对数据有更多的了解。
KNN算法
KNN算法假设相似的事物存在于非常接近的地方,换句话说,相似的事物彼此接近。
“Birds of a feather flock together.”
在上图中,大多数情况下,相似的数据点彼此接近。KNN算法取决于这个假设是否足够真实,从而使算法有用。
**直线距离(Euclidean Distance)**是一个流行且熟悉的选择。
The KNN Algorithm:
- Load the data
- Initialize K to your chosen number of neighbors
- For each example in the data: Calculate the distance between the query example and the current example from the data; Add the distance and the index of the example to an ordered collection.
- Sort the ordered collection of distances and indices from smallest to largest (in ascending order) by the distances.
- Pick the first K entries from the sorted collection.
- Get the labels of the selected K entries.
- If regression, return the mean of the K labels.
- If classification, return the mode of the K labels.
# --*-- coding:utf-8 --*--
# @Author : 一只楚楚猫
# @File : 03KNN.py
# @Software : PyCharm
from collections import Counter
import math
def knn(data, query, k, distance_fn, choice_fn):
"""
:param data: Regression Data
:param query: Query Data
:param k: K Neighbours
:param distance_fn: Euclidean Distance
:param choice_fn:
:return:
"""
neighbor_distances_and_indices = []
# 3. For each example in the data
for index, example in enumerate(data):
# 3.1 Calculate the distance between the query example and the current
# example from the data.
distance = distance_fn(example[:-1], query)
# 3.2 Add the distance and the index of the example to an ordered collection
neighbor_distances_and_indices.append((distance, index))
# 4. Sort the ordered collection of distances and indices from
# smallest to largest (in ascending order) by the distances
sorted_neighbor_distances_and_indices = sorted(neighbor_distances_and_indices)
# 5. Pick the first K entries from the sorted collection
k_nearest_distances_and_indices = sorted_neighbor_distances_and_indices[:k]
# 6. Get the labels of the selected K entries
k_nearest_labels = [data[i][-1] for distance, i in k_nearest_distances_and_indices]
# 7. If regression (choice_fn = mean), return the average of the K labels
# 8. If classification (choice_fn = mode), return the mode of the K labels
return k_nearest_distances_and_indices, choice_fn(k_nearest_labels)
def mean(labels):
return sum(labels) / len(labels)
def mode(labels):
"""
most_common是Counter类的方法,用于返回出现频率最高的元素及其计数。
在这里,most_common(1)返回一个包含一个元组的列表,
元组的第一个元素是出现频率最高的元素,第二个元素是该元素出现的次数。
由于我们指定参数1,因此它返回出现频率最高的1个元素。
"""
return Counter(labels).most_common(1)[0][0]
def euclidean_distance(point1, point2):
sum_squared_distance = 0
for i in range(len(point1)):
sum_squared_distance += math.pow(point1[i] - point2[i], 2)
return math.sqrt(sum_squared_distance)
def main():
'''
# Regression Data
#
# Column 0: height (inches)
# Column 1: weight (pounds)
'''
reg_data = [
[65.75, 112.99],
[71.52, 136.49],
[69.40, 153.03],
[68.22, 142.34],
[67.79, 144.30],
[68.70, 123.30],
[69.80, 141.49],
[70.01, 136.46],
[67.90, 112.37],
[66.49, 127.45],
]
# Question:
# Given the data we have, what's the best-guess at someone's weight if they are 60 inches tall?
reg_query = [60]
reg_k_nearest_neighbors, reg_prediction = knn(
reg_data, reg_query, k=3, distance_fn=euclidean_distance, choice_fn=mean
)
print(f"reg_prediction: {reg_prediction}")
'''
# Classification Data
#
# Column 0: age
# Column 1: likes pineapple
'''
clf_data = [
[22, 1],
[23, 1],
[21, 1],
[18, 1],
[19, 1],
[25, 0],
[27, 0],
[29, 0],
[31, 0],
[45, 0],
]
# Question:
# Given the data we have, does a 33 year old like pineapples on their pizza?
clf_query = [33]
clf_k_nearest_neighbors, clf_prediction = knn(
clf_data, clf_query, k=3, distance_fn=euclidean_distance, choice_fn=mode
)
print(f"cls_prediction: {clf_prediction}")
if __name__ == '__main__':
main()
Advantages:
- 该算法简单,易于实现。
- 无需构建模型、调整多个参数或做出额外的假设。
- 该算法具有通用性。它可用于分类、回归和搜索。
Disadvantages:
- 随着自变量的增加,算法会明显变慢
KNN in practice
KNN 的主要缺点是随着数据量的增加而明显变慢,这使得它在需要快速做出预测的环境中成为不切实际的选择。
在推荐系统中可以使用KNN算法
推荐系统
我们想回答什么问题?
给定我们的电影数据集,与电影查询最相似的 5 部电影是什么?
探索、清理和准备数据
我们上面的 KNN 实现依赖于结构化数据。它需要采用表格格式。此外,该实现假设所有列都包含数字数据,并且数据的最后一列具有可以执行某些功能的标签。因此,无论我们从哪里获取数据,我们都需要使其符合这些约束。
该数据包含 30 部电影,包括七种类型的每部电影的数据及其 IMDB 评级。标签列全为零,因为我们不使用该数据集进行分类或回归。
此外,在使用 KNN 算法时,电影之间的关系(例如演员、导演和主题)不会被考虑在内,因为捕获这些关系的数据在数据集中丢失了。因此,当我们对数据运行 KNN 算法时,相似性将仅基于所包含的类型和电影的 IMDB 评级。
使用算法
想象一下。我们正在浏览 MoviesXb 网站(一个虚构的 IMDb 衍生产品),然后遇到了《华盛顿邮报》。我们不确定我们是否想看它,但它的类型引起了我们的兴趣;我们对其他类似的电影感到好奇。我们向下滚动到 “更多类似内容” 部分,看看 MoviesXb 会提出什么建议,算法齿轮开始转动。
MoviesXb 网站向其后端发送请求,获取与《华盛顿邮报》最相似的 5 部电影。后端有一个和我们一模一样的推荐数据集。它首先为《华盛顿邮报》创建行表示(更广为人知的名称是特征向量),然后运行类似于下面的程序来搜索与《华盛顿邮报》最相似的 5 部电影,最后将结果发送回 MoviesXb 网站。
import sys
"""
python import模块时, 是在sys.path里按顺序查找的。
sys.path是一个列表,里面以字符串的形式存储了许多路径。
使用KNN.py文件中的函数需要先将它的文件路径放到sys.path中
"""
sys.path.append(r"E:\楚楚猫\code\python\02NLP\01文本分析")
from KNN import knn, euclidean_distance
def recommend_movies(movie_query, k_recommendations):
raw_movies_data = []
with open('./data/movies_recommendation_data.csv', 'r') as md:
# Discard the first line (headings)
next(md)
# Read the data into memory
for line in md.readlines():
data_row = line.strip().split(',')
raw_movies_data.append(data_row)
# Prepare the data for use in the knn algorithm by picking
# the relevant columns and converting the numeric columns
# to numbers since they were read in as strings
movies_recommendation_data = []
for row in raw_movies_data:
data_row = list(map(float, row[2:]))
movies_recommendation_data.append(data_row)
# Use the KNN algorithm to get the 5 movies that are most
# similar to The Post.
recommendation_indices, _ = knn(
movies_recommendation_data, movie_query, k=k_recommendations,
distance_fn=euclidean_distance, choice_fn=lambda x: None
)
movie_recommendations = []
for _, index in recommendation_indices:
movie_recommendations.append(raw_movies_data[index])
return movie_recommendations
if __name__ == '__main__':
the_post = [7.2, 1, 1, 0, 0, 0, 0, 1, 0] # feature vector for The Post
recommended_movies = recommend_movies(movie_query=the_post, k_recommendations=5)
# Print recommended movie titles
for recommendation in recommended_movies:
print(recommendation[1])
movies_recommendation_data.csv数据获取链接
Summary
KNN算法是一种简单的监督学习算法,用于解决分类问题和回归问题。它很容易实现和理解,但有一个主要缺点,随着数据大小的增长,速度会明显变慢。
KNN的工作原理是查询Query与数据中所有示例之间的距离,选择最接近查询的指定数量(K)。然后投票选出最频繁的标签(在分类的情况下)或对标签进行平均(在回归的情况)。
参考文献
[1]Machine Learning Basics with the K-Nearest Neighbors Algorithm
[2]在一个.py文件中调用另一个py文件中的类或函数
[3]一篇文章入门python基础