一篇文章入门KNN算法

KNN

监督学习是一种依赖输入数据来学习函数的算法,该函数在给定新的未标记数据时可以产生适当的输出

监督学习用于解决分类或回归问题。

分类问题的输出是离散值。例如,“喜欢披萨上的菠萝”和“不喜欢披萨上菠萝”是离散的,没有中间立场。

回归问题的输出是实数(带小数点的数字)。例如,我们可以使用下表中的数据来估计给定身高的人的体重。

在这里插入图片描述
我们有一个自变量(或一组自变量)和一个因变量(给定自变量,我们试图猜测的事情)。例如,我们可以说身高是自变量,体重是因变量。

无监督的机器学习算法使用没有任何标签的输入数据——换句话说,没有老师(标签)告诉孩子(计算机)什么时候是正确的,什么时候犯了错误

监督学习试图学习一个函数,该函数将允许我们在给定一些新的未标记数据的情况下进行预测;无监督学习试图学习数据的基本结构,让我们对数据有更多的了解。

KNN算法

KNN算法假设相似的事物存在于非常接近的地方,换句话说,相似的事物彼此接近

“Birds of a feather flock together.”

在这里插入图片描述
在上图中,大多数情况下,相似的数据点彼此接近。KNN算法取决于这个假设是否足够真实,从而使算法有用。

**直线距离(Euclidean Distance)**是一个流行且熟悉的选择。

The KNN Algorithm:

  1. Load the data
  2. Initialize K to your chosen number of neighbors
  3. 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.
  4. Sort the ordered collection of distances and indices from smallest to largest (in ascending order) by the distances.
  5. Pick the first K entries from the sorted collection.
  6. Get the labels of the selected K entries.
  7. If regression, return the mean of the K labels.
  8. 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:

  1. 该算法简单,易于实现。
  2. 无需构建模型、调整多个参数或做出额外的假设。
  3. 该算法具有通用性。它可用于分类、回归和搜索。

Disadvantages:

  1. 随着自变量的增加,算法会明显变慢

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基础

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值