k近邻算法
来源:互联网 发布:网络延长器是什么 编辑:程序博客网 时间:2024/05/22 08:20
对在漫长人生中读过的书而言,人类的记忆力是有限的。如果想在读完每一本书后都有扎实的收获,就需要有技巧地写读书笔记,并养成长期的习惯。——《如何有效阅读一本书:超实用笔记读书法 ([日]奥野宣之)》
前言
文章为《机器学习实战》摘录笔记。
概述
简单地说,k近邻算法采用测量不同特征值之间的距离方法进行分类。
k近邻算法是分类数据最简单最有效的算法。
k近邻算法必须保存全部数据集,如果训练数据集的很大,必须使用大量的存储空间。此外,由于必须对数据集中的每个数据计算距离值,实际使用时可能非常耗时。K近邻算法的另一个缺陷是它无法给出任何数据的基础结构信息,因此我们也无法知晓平均实例样本和典型实例样本具有什么特征。
k-近邻算法
优点:精度高、对异常值不敏感、无数据输入假定。
缺点:计算复杂度高、空间复杂度高。 适用数据范围:数值型和标称型。
概念
一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
理论依据
在KNN中,通过计算对象间的距离来作为各个对象之间的非相似指标,避免了对象之间的匹配距离,在这里距离一般使用的是欧式距离或者是曼哈顿距离:
示例解释
假如有一部未看过的电影,如何确定它是爱情片还是动作片呢?
比如,我们通过以下的表格可以绘制出下面的坐标图:
现在我们得到了样本集中所有电影与未知电影的距离,按照距离递增排序,可以找到k个距离最近的电影。假定k=3,则三个最靠近的电影依次是He’s Not Really into Dudes
、Beautiful Woman
和California Man
。k近邻算法按照距离最近的三部电影的类型,决定未知电影的类型,而这三部电影全是爱情片,因此我们判定未知电影是爱情片。
KNN分类算法
对未知类别属性的数据集中的每个点依次执行以下操作:
1. 计算已知类别数据集中的点与当前点之间的距离;
2. 按照距离递增次序排序;
3. 选取与当前点距离最小的k个点;
4. 确定前k个点所在类别的出现频率;
5. 返回前k个点出现频率最高的类别作为当前点的预测分类。
import operatordef classify0(in_x, data_set, labels, k): """ 分类器 :param in_x:用于分类的输入向量(列表) :param data_set:训练样本集 :param labels:标签向量 :param k:距离样本最近的k个邻居 :return: """ # numpy方法 # shape表示各个维度大小的元组 # shape[0]表示在0维度上元组的大小 data_set_size = data_set.shape[0] # numpy方法 # 在对应维度上重复复制in_x,并去除样本集 diff_mat = tile(in_x, (data_set_size, 1)) - data_set """ 平方之后求和,求和完之后开方 """ # 对集合进行平方处理 sq_diff_mat = diff_mat ** 2 # sum(axis=1)表示对行的元素进行累加。 sq_distances = sq_diff_mat.sum(axis=1) # 对结果集进行开方 distances = sq_distances ** 0.5 # 对结果集升序排序之后返回对数组的索引 sorted_dist_indicies = distances.argsort() """ 统计 """ # 新建一个字典,用于保存数据 class_count = {} for i in range(k): vote_label = labels[sorted_dist_indicies[i]] # 如果vote_label不存在,就取默认值0 class_count[vote_label] = class_count.get(vote_label, 0) + 1 sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True) # 返回出现次数最多的分类 return sorted_class_count[0][0]
使用k近邻算法改进约会网站的配对效果
收集数据:提供文本文件。
准备数据:使用Python解析文本文件。
分析数据:使用Matplotlib画二维扩散图,并对数据进行归一化处理,让数据都在[0,1]范围内,方便进行矩阵运算。
训练算法:此步骤不适用于k近邻算法。(本案例不包含训练部分)
测试算法:使用部分数据作为测试样本。
测试样本和非测试样本的区别在于:测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。
使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方是否为自己喜欢的类型。
datingTestSet.txt 文件中有1000行的约会数据,样本主要包括以下3种特征:
- 每年获得的飞行常客里程数
- 玩视频游戏所耗时间百分比
- 每周消费的冰淇淋公升数
from os import listdirfrom numpy import *def file_matrix(filename): """ 使用Python解析文本文件。 该函数的输入为文件名字符串,输出为训练样本矩阵和类标签向量。 :param filename:文件名 :return:训练样本矩阵和类标签向量。 """ fr = open(filename) # 获取文件的行数 number_of_lines = len(fr.readlines()) # 创建返回的Numpy矩阵 return_mat = zeros((number_of_lines, 3)) # 准备返回的标签向量 class_label_vector = [] fr = open(filename) index = 0 for line in fr.readlines(): line = line.strip() # 去除多余的回车字符 list_from_line = line.split('\t') # 使用\t字符作为分隔符将数据拆分成一个列表 return_mat[index, :] = list_from_line[0:3] # 需要注意的是,我们必须明确地通知解释器,告诉它列表中存储的元素值为整型, # 否则Python语言会将这些元素当作字符串处理。 class_label_vector.append(int(list_from_line[-1])) index += 1 return return_mat, class_label_vectordef auto_norm(data_set): """ 对数据进行归一化处理,让数据都在[0,1]范围内,方便进行矩阵运算。 归一化特征值(将数据处理为0~1范围内) :param data_set:数据集合 :return: """ # 每列中的最小值 min_val = data_set.min(0) # 每列中的最大值 max_val = data_set.max(0) # 取值范围 ranges = max_val - min_val # data中的行的数量 m = data_set.shape[0] # 下面两行的逻辑:newValue = (oldValue-min)/(max-min) norm_data_set = data_set - tile(min_val, (m, 1)) # Numpy库中tile()函数将变量内容复制成输入矩阵同样大小的矩阵 norm_data_set = norm_data_set / tile(ranges, (m, 1)) # 特征值相除 return norm_data_set, ranges, min_valdef dating_class_test(): """ 使用部分数据作为测试样本。 :return: 无 """ # 使用10%的数据进行测试 ho_ratio = 0.90 # 从文件中读取数据集合 dating_data_mat, dating_label = file_matrix('datingTestSet2.txt') # 将数据进行归一化处理。 # 由于数据的大小不统一,所以需要进行归一化处理 norm_mat, ranges, minVal = auto_norm(dating_data_mat) # 获取归一化后一维数组的数量 m = norm_mat.shape[0] # 仅仅拿出m * ho_ratio%的数据进行分类 num_test_vec = int(m * ho_ratio) # 错误的数据的数量 error_count = 0.0 for i in range(num_test_vec): # 分类 classifier_result = classify0(norm_mat[i, :], # 输入向量 norm_mat[num_test_vec:m, :], # 样本数量 dating_label[num_test_vec:m], # 标签 3) # print("the classifier came back with: %d, the real answer is: %d" % # (classifier_result, dating_label[i])) if classifier_result != dating_label[i]: error_count += 1.0 print("the total error rate is: %d%%" % (error_count / float(num_test_vec) * 100)) print("the sum is %d and the error count is: %d" % (m, error_count))def classify_person(): result_list = ['可能性为0', '小几率', '大概率'] percent_tats = float(input("问题1:玩游戏事件所占的百分比?")) fly_miles = float(input("问题2:每年获得的飞行常客里程数?")) ice_cream = float(input("问题3:每周消费的冰淇淋公升数?")) dating_data_mat, dating_labels = file_matrix('datingTestSet2.txt') norm_mat, ranges, min_val = auto_norm(dating_data_mat) in_arr = array([fly_miles, percent_tats, ice_cream]) classifierResult = classify0((in_arr - min_val) / ranges, # 输入向量 norm_mat, # 样本数量 dating_labels, # 标签 3) print("You will probably like this person: ", result_list[classifierResult - 1])
手写识别系统
收集数据:提供文本文件。
准备数据:编写函数classify0(),将图像格式转换为分类器使用的list格式。
分析数据:在Python命令提示符中检查数据,确保它符合要求。(若是需要,必须进行归一化处理,详见auto_norm函数)
训练算法:此步骤不适用于k近邻算法。(本案例不包含训练部分。)
测试算法:编写函数使用提供的部分数据集作为测试样本,测试样本与非测试样本的区别在于测试样本是已经完成分类的数据,如果预测分类与实际类别不同,则标记为一个错误。
使用算法:本例没有完成此步骤,若你感兴趣可以构建完整的应用程序,从图像中提取数字,并完成数字识别,美国的邮件分拣系统就是一个实际运行的类似系统。
from os import listdirdef img_to_vector(filename): """ 将图片转换为一个向量 :param filename:图片文件名 :return: 返回图片向量 """ return_vec = zeros((1, 1024)) fr = open(filename) for i in range(32): line_str = fr.readline() for j in range(32): return_vec[0, 32 * i + j] = int(line_str[j]) return return_vecdef handwriting_class_test(): """ 手写数字识别系统的测试代码 :return:无 """ hw_labels = [] # 将digits/trainingDigits目录下的文件全部存到一个列表中 training_file_list = listdir('digits/trainingDigits') # 获取文件的数量 m = len(training_file_list) # 训练矩阵 training_mat = zeros((m, 1024)) # 分类 # 由于文本中的值已经在0和1之间了,所以我们不需要归一化处理了。 for i in range(m): # 文件名 file_name = training_file_list[i] file = file_name.split('.')[0] # take off .txt # 类别 class_num = int(file.split('_')[0]) # 将类别添加到标签列表中 hw_labels.append(class_num) training_mat[i, :] = img_to_vector('digits/trainingDigits/%s' % file_name) # 测试样本 test_file_list = listdir('digits/testDigits') # 错误率 error_count = 0.0 mTest = len(test_file_list) for i in range(mTest): file_name = test_file_list[i] # 将.txt去掉,仅仅需要的是第一个部分 file = file_name.split('.')[0] # 第一个部分中的_前面的数字是文本内显示的内容 # 第二个是编号 class_num = int(file.split('_')[0]) # 图像文本转向量 vector_under_test = img_to_vector('digits/testDigits/%s' % file_name) # 分类 classifier_result = classify0(vector_under_test, training_mat, hw_labels, 3) # print("the classifier came back with: %d, the real answer is: %d" % # (classifier_result, class_num)) if classifier_result != class_num: error_count += 1.0 print("\n the total number of errors is: %d" % error_count) print("\n the total error rate is: %f" % (error_count / float(mTest)))
实际使用这个算法时,算法的执行效率并不高。因为算法需要为每个测试向量做2000次距离计算,每个距离计算包括了1024个维度浮点运算,总计要执行900次,此外,我们还需要为测试向量准备2MB的存储空间。是否存在一种算法减少存储空间和计算时间的开销呢?k决策树就是k近邻算法的优化版,可以节省大量的计算开销。
- K近邻算法
- K近邻算法
- K近邻算法
- K近邻算法
- K近邻算法
- K近邻算法
- k近邻算法
- OpenCv K近邻算法
- k-近邻算法(kNN)
- k-近邻算法
- k-近邻算法(kNN)
- K近邻算法
- K近邻分类算法
- K近邻分类算法
- K近邻算法
- K近邻算法
- k最近邻算法
- K-近邻算法
- 使用Flask-Login实现token验证和超时失效使用体会
- 圆点与Viewpager联动
- C++ 字符串字面值拼接
- [iOS] 网络之概述
- YTU.1911: 完美数
- k近邻算法
- java selenium firefox (图形界面)爬取页面数据
- 【第三周项目2】 建设“顺序表”算法库
- Python3:《机器学习实战》之Logistic回归(3)预测病马死亡率
- Entity Framework(一)基础
- Python的py文件打包成exe
- 我的积累
- 走在路上的一些感悟
- Mac使用apt-get