K-近邻算法(初级:电影分类)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/apollo_miracle/article/details/89208284

使用k-近邻算法分类爱情片和动作片

有人曾经统计过 很多电影的打斗镜头和接吻镜头,下图显示了6部电影的打斗和接吻镜头数。假如有一部未看过的电影,如何确定它是爱情片还是动作片呢?我们可以使用kNN来解决这个问题。

首先我们需要知道这个未知电影存在多少个打斗镜头和接吻镜头,图中问号位置是该未知电影出现的镜头数图形化展示,具体数据如下:

即使不知道未知电影属于哪种类型,我们也可以通过某种方法计算出来。首先计算未知电影与样本集中其他电影的距离,如表所示。此处暂时不要关心如何计算得到这些距离值,使用Python实现电影分类应用时,会提供具体的计算方法。

现在我们得到了样本集中所有电影与未知电影的距离,按照距离递增排序,可以找到k个距离最近的电影。假定k=3,则三个最靠近的电影依次是He’sNotReallyintoDudes、BeautifulWoman和CaliforniaMan。k-近邻算法按照距离最近的三部电影的类型,决定未知电影的类型,而这三部电影全是爱情片,因此我们判定未知电影是爱情片。

k-近邻算法的一般流程

(1)收集数据:可以使用任何方法。

(2)准备数据:距离计算所需要的数值,最好是结构化的数据格式。

(3)分析数据:可以使用任何方法。

(4)训练算法:此步骤不适用于k-近邻算法。

(5)测试算法:计算错误率。(本次实验数据太少,故没有进行算法测试)

(6)使用算法:首先需要输入样本数据和结构化的输出结果,然后运行k-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理。

具体代码如下:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
from sklearn.preprocessing import MinMaxScaler
import operator


def file_matrix(file_name):
    """
    准备数据,从文本文件中解析数据
    :param file_name: 文件名称
    :return: 特征值矩阵(数据集)、目标值向量
    """
    with open(file_name, "r") as f:
        # 以行读取文件内容
        lines_data = f.readlines()
    # 统计文件行数
    lines_num = len(lines_data)
    # 返回的numpy矩阵,解析完成的数据:lines_num行,2列
    data_set = np.zeros((lines_num, 2))
    # 返回的分类标签向量
    labels = []
    # 行索引index
    index = 0
    for line in lines_data:
        # 去除行数据两端的空格
        line = line.strip()
        # 使用s.split(str="",num=string,cout(str))将字符串根据','分隔符进行切片。
        line = line.split(",")
        # 将数据前两列提取出来,存放到return_mat的NumPy矩阵中,也就是特征矩阵
        data_set[index, :] = line[0:2]
        # 将目标值放入分类标签向量中
        labels.append(line[-1])
        index += 1
    return data_set, labels


def show_data(data_set, labels):
    """
    分析数据,数据可视化,使用Matplotlib创建散点图
    :param data_mat: 特征值矩阵(数据集)
    :param labels: 目标值向量
    :return: None
    """
    # 画布的大小为(13, 9)
    plt.figure(figsize=(8, 4))
    # 设置点的颜色
    labels_colors = []
    for data in labels:
        if data == "love":
            labels_colors.append("red")
        elif data == "action":
            labels_colors.append("green")
    # 画出散点图,x轴打斗镜头次数、y轴为接吻镜头次数,散点大小为15,透明度为0.5
    plt.scatter(x=data_set[:, 0], y=data_set[:, 1], marker=".", color=labels_colors, s=15, alpha=0.5)
    # 设置标题,x轴label,y轴label
    title_text = plt.title("每部电影的接吻镜头次数与打斗镜头次数")
    xlabel_text = plt.xlabel("打斗镜头次数")
    ylabel_text = plt.ylabel("接吻镜头次数")
    # 设置标题,x轴label,y轴label的字体大小、是否加粗、颜色
    plt.setp(title_text, size=12, weight='bold', color='red')
    plt.setp(xlabel_text, size=10, weight='bold', color='black')
    plt.setp(ylabel_text, size=10, weight='bold', color='black')
    # 设置图例
    love = mlines.Line2D([], [], color='red', marker='.', markersize=6, label='爱情片')
    action = mlines.Line2D([], [], color='green', marker='.', markersize=6, label='动作片')
    # 添加图例
    plt.legend(handles=[love, action])
    # 显示图片
    plt.show()


def auto_normal(data_set):
    """
    准备数据,数据归一化处理
    :param data_set: 需要进行归一化的数据集
    :return: 归一化是数据集、最小值与最大值的范围、最小值
    """
    # 获得每列数据的最小值
    min_val = data_set.min(0)
    # 最大值和最小值的范围
    ranges = data_set.max(0) - min_val
    # feature_range=(0, 1) 数据返回值在(0,1)之间
    # 对数据进行归一化处理
    mms = MinMaxScaler(feature_range=(0, 1))
    normal_data = mms.fit_transform(data_set)
    return normal_data, min_val, ranges


def kNN(inX, data_set, labels, k):
    """
    KNN算法分类器
    :param inX: 用于分类的数据(测试集)
    :param data_set: 用于训练的数据(训练集)
    :param labels: 训练数据的分类标签
    :param k: kNN算法参数,选择距离最小的k个点
    :return: 分类结果
    """
    # numpy函数shape[0]返回data_set的行数
    data_num = data_set.shape[0]
    # 在列向量方向上重复inX共1次(横向),行向量方向上重复inX共data_num次(纵向)
    diff_mat = np.tile(inX, (data_num, 1)) - data_set
    # 二维特征相减后平方
    distances_sq = diff_mat ** 2
    # sum()所有元素相加,sum(0)列相加,sum(1)行相加
    distances_sq_sum = distances_sq.sum(axis=1)
    # 开方,计算出距离
    distances = distances_sq_sum ** 0.5
    # 返回distances中元素从小到大排序后的索引值
    dist_index_sort = distances.argsort()
    # 定一个记录类别次数的字典
    class_count = {}
    for i in range(k):
        # 取出前k个元素的类别
        label = labels[dist_index_sort[i]]
        # dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
        # 计算类别次数
        class_count[label] = class_count.get(label, 0) + 1
    # python2中用iteritems()替换python3中的items()
    # key=operator.itemgetter(1)根据字典的值进行排序
    # key=operator.itemgetter(0)根据字典的键进行排序
    # reverse降序排序字典
    class_count_sorted = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)
    # 返回次数最多的类别,即所要分类的类别
    return class_count_sorted[0][0]


def data_deal(data, min_val, ranges):
    """
    # 测试集归一化
    :param data: 将要归一化处理的数据
    :param min_val: 训练集的每列最小值
    :param ranges: 训练集的每列取值范围
    :return:归一化处理后的数据
    """
    data_normal = (data - min_val) / ranges
    return data_normal


def movie_class(normal_data, labels, min_val, ranges):
    # 二维特征用户输入
    action_num = int(input("请输入本电影出现的打斗镜头次数:"))
    love_num = int(input("请输入本电影出现的接吻镜头次数:"))
    # 生成NumPy数组,测试集
    data = np.array([action_num, love_num])
    # 测试集归一化
    data_normal = data_deal(data, min_val, ranges)
    # 返回分类结果
    classify_result = kNN(data_normal, normal_data, labels, 3)
    # 输出结果
    if classify_result == "love":
        classify_result = "爱情片"
    elif classify_result == "action":
        classify_result = "动作片"
    print("本部电影可能是%s" % classify_result)


if __name__ == '__main__':
    # 文件名称
    file_name = "movies.txt"
    # 读取文件数据集
    data_set, labels = file_matrix(file_name)
    # 数据可视化
    show_data(data_set, labels)
    # 数据集归一化处理
    normal_data, min_val, ranges = auto_normal(data_set)
    # 使用分类器
    movie_class(normal_data, labels, min_val, ranges)

运行结果:

请输入本电影出现的打斗镜头次数:18
请输入本电影出现的接吻镜头次数:90
本部电影可能是爱情片

代码数据:

3,104,love
2,100,love
1,81,love
101,10,action
99,5,action
98,2,action

猜你喜欢

转载自blog.csdn.net/apollo_miracle/article/details/89208284
今日推荐