版权声明:本文为博主原创文章,未经博主允许不得转载。 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