本人也只是刚刚开始接触机器学习实战这本书,之前好多python的语法知识都有些模糊,所以注释写的比较详细,多谅解。
书中所用的代码用的是python2.x,但是现在python3的用的比较多,所以多出会对代码指出两者不同,这也是很多刚开始按照书上代码会报错的原因,都会在下面进行总结。
第一次写博客,内容好多地方自己可能也有理解错误的地方希望指出,里面的代码,文字,图片都是自己一点一点打出来,截出来的,希望支持,谢谢。
内容
1.算法简介......对KNN算法有一个具体的模型概念
2.代码实现......对机器学习实战书中约会网站实例的代码实现
3.算法扩展......参数选择及kd树
1.算法简介
在统计学习方法上是如此定义:给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的k个实例,这k个实例多数属于某个类,就把该输入实例归为这个类。是一种基本分类与回归方法。
输入:训练数据集T
输出:实例 x 所属的类 y
k-近邻算法的一般流程:
收集数据—>准备数据—>分析数据—>测试算法—>使用算法
2.代码实现
实例:改进约会网站的配对效果
2.1 文本转换
因为机器学习实战附带的数据是txt文本,需要将其进行转换成NumPy中能处理的数组(矩阵)形式
def file2matrix(filename): fr = open(filename) arrayOnlines = fr.readlines()#readlines() 自动将文件内容分析成一个行的列表,该列表可以由 Python 的 for ... in ... 结构进行处理。 numberOflines = len(arrayOnlines)#得到文件行数 returnMat = zeros((numberOflines,3))#创建numpy矩阵 classLableVector = []#定义其为一个列表 index = 0 #解析文件数据到列表 for line in arrayOnlines: line = line.strip()#根据()里的参数分割内容,默认是空格 listFromLine = line.split('\t')#返回分割后的字符串列表。 returnMat[index,:] = listFromLine[0:3]#将文件的内容矩阵化,并读取前三个内容 classLableVector.append(int(listFromLine[-1]))#读取最后一个内容,必须明确告诉解释器,列表中存储的元素为整形,否则会当作字符串进行处理 index += 1 return returnMat,classLableVector要说明的是 训练集T是一个M*N的矩阵 M代表的是样本的个数,N代表的是特征值的个数
2.2归一化数值
#归一化处理 def autoNorm(dataSet):#参数为输入的特征值矩阵 minVals = dataSet.min(0)#找出每一特征值的最小值 maxVals = dataSet.max(0)#找出每一特征值的最大值 ranges = maxVals - minVals normDataSet = zeros(shape(dataSet))#初始化尺寸归一化后的矩阵 m = dataSet.shape[0]#行数即样本个数 normDataSet = dataSet - tile(minVals, (m,1))#对应公式的 (oldValue - min) normDataSet = normDataSet/tile(ranges, (m,1))#可直接相除,相当于每个元素除以对应元素 return normDataSet, ranges, minVals在代码中,tile()函数的应用有些模糊,查询了资料发现是将函数内第一个参数(一般为一个矩阵(数组))进行扩展
2.3分类器
利用KNN进行分类,先写出分类器的伪代码:
(1) 计算已知类别数据集中的点与当前点的之间距离
(2) 按照距离递增次序排序
(3) 选取与当前点距离最小的k个点
(4) 确定前k个点所在的类别的出现频率
(5) 返回前k个点出现频率最高的类别作为当前点的预测分类根据伪代码写出代码:
def classify0(inX, dataSet, lables, k):#定义一个分类器 dataSetSize = dataSet.shape[0] #得到训练集样本的个数 diffMat = tile(inX, (dataSetSize,1)) - dataSet #将输入向量X进行扩展成训练集的行数,并减去训练集中的值 进行差值运算 #进行欧式距离的运算 sqDiffMat = diffMat ** 2 sqDistances = sqDiffMat.sum(axis=1)#axis=1 是将矩阵或数组的行进行相加 distances = sqDistances ** 0.5 sortedDistIndicies = distances.argsort() #argsort()按照一定的大小顺序进行排列 默认是按照升序进行排列 classCount = {}#字典类型 #对最近的k个训练集中的实例进行标签的计数工作 for i in range(k): voteIlabel = lables[sortedDistIndicies[i]] classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)#按每个标签对应的数量升序进行排列 return sortedClassCount[0][0]#从k个标签数中最多的那个标签作为输入x的预估值
在分类器这个程序中,有好多要注意的地方,自上而下来说:
(1).四个参数,前三个在前面也表述是三个矩阵,而参数k由定义知是选择的距离最近的k个点,机器学习实战选择了3,关于k的具体在第四部分在叙述。
(2).shape 函数 shape[0]返回的是行数,shape[1]返回的是列数,shape是返回行数列数的一个数组。
(3).argsort()函数 按照一定的大小顺序进行排列,默认是按照升序进行排列
(4).在倒数第二行的代码中,我第一次按照书中代码执行发现会报错 AttributeError: 'dict' object has no attribute 'iteritems' 然后发现代码没有问题,分析报错原因和iteritems这个迭代器有关,而且自己学python时不记得有用到这个(我学的书用的python3),就觉得是不是python2和3的差,就去百度了下,发现又一个博客下写到:
Python 3.x里面,iteritems()和viewitems()这两个方法都已经删除,可用item()替代,于是我就尝试用items()替代,发现没有了错误,而且功能成功实现。
2.4测试程序
def datingClassTest(): hoRitio = 0.1 datingDataMat, datingLabels = file2matrix('datingTestSet.txt') normMat, ranges, minVals = autoNorm(datingDataMat) m = normMat.shape[0] numTestVecs = int(m*hoRitio) errorCount = 0.0 for i in range(numTestVecs): classifierResult = classify0(normMat[i,:],normMat[numTestVecs:m,:],\ datingLabels[numTestVecs:m],3) print("the classifierResult came back with : %d, the real answer is: %d"\ %(classifierResult,datingLabels[i])) if (classifierResult != datingLabels[i]): errorCount +=1.0 print("the total error rate is: %f"%(errorCount/float(numTestVecs)))
其中 hoRitio 是将得到的数据集分为训练集与测试集,在Ng的Machine Learning中他推荐的是3:7的分法,在这里是按1:9的分法。
2.5预测程序
def classifyPerson(): resuleList = ['not at all', 'in a small doses', 'in large doses']#最后分类结果的所有可能 percentTats = 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?"))#输入三个特征值 datingDataMat,datingLables = file2matrix('datingTestSet.txt')#导入数据集及其对应的类别集 normMat, ranges, minVals = autoNorm(datingDataMat)#将输入的三个特征值进行归一化 inArr = array([ffMiles, percentTats, iceCream]) classifierResult = classify0((inArr-\ minVals)/ranges,normMat,datingLables,3)#利用分类器进行预测 print("You will probably like this person : ",\ resuleList[classifierResult - 1])#分类器得到的是一个数字,将数字转换成文字结果其中,书中的程序输入的时候用的是raw_input() 在python3中将 raw_input() 和input()进行了合并,通用input()。