经过前几篇博文的分步骤讲解,现在可以使用K近邻算法分类器来读人们进行分类。通过输入约会网站上的某个人的信息,该程序会给出对对方的喜欢程度的预测值。
前几篇博文:
《机器学习实战》第2章阅读笔记3 使用K近邻算法改进约会网站的配对效果—分步骤详细讲解1——数据准备:从文本文件中解析数据(附详细代码及注释)
《机器学习实战》第2章阅读笔记3 使用K近邻算法改进约会网站的配对效果—分步骤详细讲解2——分析数据:可视化分析数据(附详细代码及注释)
《机器学习实战》第2章阅读笔记3 使用K近邻算法改进约会网站的配对效果—分步骤详细讲解3——准备数据:归一化数值(附详细代码及注释)
《机器学习实战》第2章阅读笔记3 使用K近邻算法改进约会网站的配对效果—分步骤详细讲解4——测试算法:验证分类器(附详细代码及注释)
本篇使用的数据存放在文本文件datingTestSet2.txt中,每个样本数据占据一行,总共有1000行。
样本主要包含以下3中特征:
(1)每年获得飞行常客里程数
(2)玩视频游戏所耗时间百分比
(3)每周消费的冰淇淋公升数
下边为实现代码及详细注释:
主要包括以下几个部分:
(1)数据准备:从文本文件中解析数据
(2)分析数据:使用Matplotlib创建散点图
(3)准备数据:归一化数值(避免某些特征值过大,影响过大,通过归一化,将特征值作为等权重特征值)
(4)使用算法:构建完整可用系统
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from numpy import *
import operator
# inX 用于分类的输入向量
# dataSet表示训练样本集
# 标签向量为labels,标签向量的元素数目和矩阵dataSet的行数相同
# 参数k表示选择最近邻居的数目
def classify0(inx, data_set, labels, k):
"""实现k近邻"""
diff_mat = inx - data_set # 各个属性特征做差
sq_diff_mat = diff_mat**2 # 各个差值求平方
sq_distances = sq_diff_mat.sum(axis=1) # 按行求和
distances = sq_distances**0.5 # 开方
sorted_dist_indicies = distances.argsort() # 按照从小到大排序,并输出相应的索引值
class_count = {} # 创建一个字典,存储k个距离中的不同标签的数量
for i in range(k):
vote_label = labels[sorted_dist_indicies[i]] # 求出第i个标签
# 访问字典中值为vote_label标签的数值再加1,
# class_count.get(vote_label, 0)中的0表示当为查询到vote_label时的默认值
class_count[vote_label] = class_count.get(vote_label, 0) + 1
# 将获取的k个近邻的标签类进行排序
sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)
# 标签类最多的就是未知数据的类
return sorted_class_count[0][0]
def file2matrix(filename):
"""将文本记录转换为Numpy的解析程序"""
fr = open(filename)
array_lines = fr.readlines() # 读取文件
number_lines = len(array_lines) # 获取文件行数
return_mat = zeros((number_lines, 3)) # 创建返回的Numpy矩阵,大小为number_lines*3
class_label_vector = [] # 类别标签列表
index = 0
for line in array_lines:
line = line.strip() # 截取掉所有的回车符
list_from_line = line.split('\t') # 使用tab字符\t将上一步得到的整行数据分割成一个元素列表
return_mat[index, :] = list_from_line[0:3] # 取前3个元素,将它们存储到特征矩阵中
# 将列表的最后一列存储在类别便签列表内,必须明确表示存储的是整型,不然默认当成字符串处理
class_label_vector.append(list_from_line[-1]) # 将列表的最后一列存储在类别便签列表内
index += 1
return return_mat, class_label_vector
def auto_norm(data_set):
"""归一化特征值"""
# 从列中选取最小值和最大值,大小为1*3
min_vals = data_set.min(0)
max_vals = data_set.max(0)
# 可能取值的范围
ranges = max_vals - min_vals
# 复制min_vals,max_vals,使得大小为1000*3
m = data_set.shape[0] # 求行数
min_vals = tile(min_vals, (m, 1))
ranges = tile(ranges, (m, 1))
# 创建归一化矩阵,大小与data_set相同
norm_data_set = zeros(shape(data_set))
norm_data_set = data_set - min_vals
norm_data_set = norm_data_set/ranges
return norm_data_set, min_vals, ranges
def classfy_person():
"""预测分类"""
rusult_list = ['not at all', 'in small doses', 'in large doses']
# 输入三个特征值
percent_tats = float(input("percentage of time spent playing video games?"))
ffmiles = float(input("frequent flier miles earned per year?"))
icecream = float(input("liters of ice cream consumed per year?"))
# 读取文件
dating_data_mat, dating_labels = file2matrix("datingTestSet2.txt")
# 归一化数据,输出归一化数,最小值,和取值范围值。这里输出最小值和取值范围值是为了归一化输入的特征值
norm_dating_data_mat, min_vals, ranges = auto_norm(dating_data_mat)
# 输入列表
in_arr = array([ffmiles, percent_tats, icecream])
# 分类
classifier_result = classify0((in_arr-min_vals)/ranges, norm_dating_data_mat, dating_labels, 3)
print("You will probably like this person:", end='')
print(rusult_list[int(classifier_result)-1])
classfy_person()
运行结果: