着手学习机器学习了,打算根据《机器学习实战》这本书从头到尾把这书里的代码撸个遍,想必就应该能入门了吧。写笔记目的一是记录,二是加深记忆,三是分享。cd尽量减少摘抄性文字。
书中的所有代码来源可到官网下载:https://www.manning.com/books/machine-learning-in-action
这里博客的代码及相关数据放在了cd的github上:https://github.com/ChenDdon/mechine_learning_practice.git
k-近邻算法
文章目录
一 定义、优缺点及工作原理
定义:k-近邻算法采用测量不同特征值的距离方法进行分类。
优点:进度稿、对异常值不敏感、无数据输入假定。
缺点:计算复杂度高、空间复杂度高、
适用数据范围:数值型和标称型。
工作原理:
存在一个样本数据集合,也称作为训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系。
输入没有标签的新数据后,将新的数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-近邻算法中k的出处,通常k是不大于20的整数,且最好为奇数(涉及到判定分类时的投票过程)。
最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
二 k-近邻算法实践
2.1 k-近邻算法的一般流程
1、收集数据:任意方法。
2、准备数据:距离计算所需要的数值,最好是格式化的数据格式。
3、分析数据:可以使用任何方法。
4、测试算法:计算错误率。
5、使用算法:首先输入样本数据和结构化的输出结果,然后进行k-近邻算法判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续处理。
2.2 示例说明(任务说明)
这里cd主要基于书本中给的使用k-近邻算法改进约会网站的配对结果这个示例来写的。
示例背景: 海伦一直用一个约会平台在找对象。海伦将约会的众多对象分成了三类:
- didntLike;
- smallDoses;
- largeDoses
然后,海伦将之前每个约会对象数据记录下来,每个人主要是三个数据:
- 玩视频游戏所耗时间百分比
- 每年获取的飞行常客里程数
- 每周消费的冰淇淋公升数
海伦将每个人的数据以及分类整理在了datingTestSet.txt这个文件里了。希望能有个分类程序,每当她拿到一个没约会过的人的前述三个数据,就能知道这个人她会不会喜欢。
给出的数据:datingTestSet.txt与datingTestSet2.txt这两个文件。其中我们要用的是datingTestSet2.txt这个文件与datingTestSet.txt的区别就在于,数据文件中的第四列数值做了如下转换:
第四列数值意义 |
---|
largeDoses ——> 3 |
smallDoses ——> 2 |
didntLike ——> 1 |
任务:得到一个基于k-近邻算法的分类器,对一行新的向量能进行正确的分类判定。
分类器需要达到的效果,即:input为1*4的向量;output为相应的分类结果。
2.3 代码实践
首先,创建名为kNN.py的python文件。
然后,要清楚我们要做哪些事情,需要哪些功能,从而确定框架。
#1收集数据
#这里提供的datingTestSet2.txt文件就是收集到的数据
#准备数据:将收集的到的数据转换成格式化的数据格式
def img2matrix():
pass
#分析数据,可以用matplotlib创建可视化的图形
def analyseShow():
pass
#分类器构建
def classify0():
pass
#测试算法部分
def datingClassTest():
pass
#使用算法部分
def classifyPerson():
pass
2.3.1 收集数据
因为这个例子里的数据是准备好了的,所以直接拿来用就行了。代码和数据都同意放在文末的连接里吧。
2.3.2 准备数据
准备数据其实也是为了后续处理方便,我们首先看一下这个例子中的数据特征:
40920 8.326976 0.953952 3
14488 7.153469 1.673904 2
26052 1.441871 0.805124 1
75136 13.147394 0.428964 1
38344 1.669788 0.134296 1
72993 10.141740 1.032955 1
35948 6.830792 1.213192 3
... ... ... ...
这里罗列的数据前几行,前三列是三个特征分别是:每年获得的飞行常客里程数;玩视频游戏所耗时间百分比;每周消费的冰淇淋公升数。
第四列是分类结果,用数字1,2,3表示三种结果。
那么可以将特征数据,与分类结果数据分开来,代码如下:
import numpy as np
def file2matrix(filename):
with open(filename) as fr:
lines = fr.readlines()
numberOfLines = len(lines) #获取文件行数
returnMat = np.zeros((numberOfLines,3)) #准备回传的矩阵-零矩阵
classLabelVector = [] #准备返回的结果数值-空list
index = 0
for line in lines:
line = line.strip()
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
classLabelVector.append(int(listFromLine[-1]))
index += 1
return returnMat,classLabelVector
结果如以下截图所示:
此外,我们可以看到三个特征属性的数值存在巨大差异,那么在计算两个样本之间的差距时,数值大的特征将占到主导地位,而对于这个示例而言,三种特征是同等重要的。因此,在准备数据阶段,我们还需要将特征值的数据进行归一化处理,如将数值取值均归一化到0到1,或者-1到1之间。(不归一化容易导致欠拟合的情况)
下面公式是将特征值转化为0到1区间的值:
newValue = (oldValue-min)/(max-min)
因此,在准备数据这个模块里我们还需要加进去归一化处理的功能:
#准备数据:将收集的到的数据转换成格式化的数据格式
import numpy as np
def file2matrix(filename):
with open(filename) as fr:
lines = fr.readlines()
numberOfLines = len(lines) #获取文件行数
returnMat = np.zeros((numberOfLines,3)) #准备回传的矩阵-零矩阵
classLabelVector = [] #准备返回的结果数值-空list
index = 0
for line in lines:
line = line.strip()
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
classLabelVector.append(int(listFromLine[-1]))
index += 1
#数据归一化的部分
dataSet = returnMat
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals - minVals
normDataSet = np.zeros(np.shape(dataSet))
m = dataSet.shape[0]
normDataSet = dataSet - np.tile(minVals, (m,1))
normDataSet = normDataSet/np.tile(ranges, (m,1)) #element wise divide
# return returnMat,classLabelVector
return normDataSet,classLabelVector,ranges, minVals
这样一来,输出的normDataSet就是经过归一化之后的数据了。
由于后续会用到每列的数值范围,因此也一起返回了取值范围(ranges)和最小值(minVals)。
2.3.3 分析数据
这里我们使用matplotlib制作原始数据的散点图:
#分析数据,可以用matplotlib创建可视化的图形
'''
Parameters:
datingDataMat: 数据特征矩阵(包含n个样本的数据集合)
datingLabels: 样本标签集合(各个样本的结果的集合)
'''
def analyseShow(datingDataMat, datingLabels):
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['Microsoft YaHei'] #用来正常显示中文标签
plt.rcParams['axes.unicode_minus']=False #用来正常显示负号
fig = plt.figure(figsize=