k-近邻算法 实战

一、算法的提出与目的

1、算法的提出

我们知道电影可以按照题材分类,比如分为爱情片和动作片。暂且不讨论种类是怎么分的。
假设,这时有一个新的电影,怎么根据已有的数据(部分已经定义为爱情片和动作片的电影)来判断新的电影是什么种类的?

2、算法的工作原理

假设每个电影都有一定的特征值(比如接吻镜头,打斗镜头),合理的办法是,计算新的这个电影的特征值和所有电影的特征值之间的差,从差值最小的前k个电影题材的比例,来判断新的电影是什么题材的。

这也是k-近邻算法(KNN)的工作原理:输入没有标签的新数据后,将新数据的每个特征与训练样本中对应的特征进行比较,然后提取出训练样本中特征最相似的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就算算法名称的由来。
最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。

3、算法的目的

可以应用k-近邻算法,根据样本数据,判断输入数据分别属于哪个分类。

二、算法的推导

k-近邻算法比较简单明了,就是计算已知类别数据集中的点与当前点之间的距离,用欧式距离公式来得到结果,没有什么推导的地方。

三、算法的代码实现

树立分块写代码的思想:
1、算法是一个函数,参数是特定的数据格式;
2、数据处理再单独写,把原数据处理成算法函数需要的形式。

1、算法的伪码(自己先想想伪码应该怎么写)

  • 1 计算当前点与已知类别的数据集中每个样本点之间的距离
  • 2 将已经计算出的距离,按照从小到大的顺序排列
  • 3 选中前k个最小的距离,并计算出前k个中每个点对应的类别的比例
  • 4 返回前k个点中类别比例最高的那一类作为当前点的预测分类

2、算法的代码

这是一个分类器,将其写成一个函数
所需要的数据:
一个矩阵(dataSet)~表示每个数据的每个特征值,
一个向量(labels)~每个数据对应的标签

代码经验

1、这里排列的思想比较有意思
先说argsort方法,返回“数组的值从小到大”的索引值
原本数据是按照dataSet中的顺序排列的(并没有什么大小的意思),只不过,dataSet中的第m个数据,对应的是labels中的第m个标签。这是前面处理数据的结果。
矩阵整体的加减乘除运算并不改变数据位置,所以distances表示的还是原来的顺序的距离。
这时用argsort方法来返回的是“数组的值从小到大”的索引值。也就是说,distances按照结果中(sortedDis)前k个值进行索引的话,是原数组中(即distances中)最小的k个值。
比如,sortedDis中第一个值是156,那么说明distances[156]是distances列表中最小的值。同样,labels[156]就是最小的值对应的标签。
于是,用labels向量对sortedDis中前k个值(对应了distances中最小的前k个值的索引)进行索引,就能得到距离最小的前k个值的标签。
一步到位了。毕竟最后的目的是最小值的标签。

2、sorted函数的应用
sorted(iterable,key,reverse)三个参数
其中iterable表示可以迭代的对象,例如可以是dict.items()、dict.keys()等,key是一个函数,用来选取参与比较的元素
这里的key函数是用operator模块中的itemgetter方法。
一般用的是lambda表达式,来表示取第几个元素用来比较。比如 key = lambda item:item[1]则表示取第一个元素。这是比较常用的sorted功能,见下面链接。
python的sorted函数对字典按key排序和按value排序

def classify0(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]#采用这种方法来获得矩阵行数,也就是数据大小
#1、距离计算
    #整体的计算距离,其中tile函数比较好,直接把当前点扩展成了相同结构的矩阵
    #矩阵相减,矩阵平方,矩阵内部各行求和,矩阵元素开方,得到与每个数据之间的距离
    diffMat = tile(inX, (dataSetSize,1)) - dataSet
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances**0.5

#2、将距离按照从小到大顺序排列,并且取出前k个最小的值的标签,都放到统计类数量的classcount中去  
    sortedDistIndicies = distances.argsort()     
    classCount={}          
    for i in range(k):#for循环和sortedDis的配合
        voteIlabel = labels[sortedDistIndicies[i]]
        #这里默认为0是比较好的,要是没有该标签,key的值为零以后加1,有标签,直接加1
        classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1

#3+4返回前k个点中类别比例最高的那一类作为当前点的预测分类
    #注意sorted函数的运用
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    #已经按照sorted排序好了, 种类出现最多的排在首位,所以可以按照【0】【0】索引了。
    return sortedClassCount[0][0]

四 实战

1 准备数据(准备数据是很重要很重要的事情)

我们需要把给定的训练数据,解析处理成算法需要的参数。简单点说,就是,给定的样本数据是不能直接用的,我们需要预处理一下,把这些文本数据处理成可以直接在函数里应用的参数的格式。

1.1单个文本文件_解析文本

这里应用《机器学习实战》这本书上的例子。
定义函数把数据预先处理成算法函数想要的结构,是第一步
定义一个函数,使得该函数的输入为文件名字符串,输出为训练样本矩阵类标签向量

将文本转化为算法需要参数的数据结构函数思路:

  • 1既然想得到一个矩阵还有一个类标签向量,就要先知道矩阵的shape,列数可以事先查看一下就可以得到
  • 2然而行数不好得到,可以用len(F.readlines)容易得到。
  • 3得到shape之后赋初值全为零。
  • 4解析得到的每行数据,有两个方面的数据,一个是输入的特征值,前面三个元素,最后一个是标签
  • 5所以,可以用for循环加上切片的方式,把特征值赋给矩阵每一行。再把每行的最后一个值添加进标签向量之中。
  • 6至此,得到一个矩阵还有一个向量。
def file2matrix(filename):
    fr = open(filename)
    numberOfLines = len(fr.readlines())  #得到行数
    returnMat = zeros((numberOfLines,3)) #矩阵赋值       
    classLabelVector = []                        
    index = 0
    for line in fr.readlines():
        line = line.strip()
        listFromLine = line.split('\t')
        returnMat[index,:] = listFromLine[0:3]#给矩阵每行赋特征值
        classLabelVector.append(int(listFromLine[-1]))#得到每行标签
        index += 1
    return returnMat,classLabelVector
1.2归一化数据

在应用KNN算法的时候,其中计算距离的法用的是欧氏距离公式,但是有一个缺点在于,如果特征的属性并不相同,可能造成权重一边倒的情况。比如这里用的例子,第一个特征是一年玩游戏的时间,第二个飞行常客里程数,那么与几十个冰淇淋相比,一定是成百上千的飞行里程数在公式中占据主导地位。这是不应该的。所以此时需要对每个特征进行归一化。

此处我们用的归一化,就是将取值范围的特征值转化到0-1之间。
newValue = (oldValue - min) / (max - min)

归一化特征值函数思路:
整体处理数据的思想,数据要按照整体进行处理

  • 1 每个特征的最大,最小值可以通过mat.max(0)和mat.min(0)来求得。
  • 2 构造一个新的归一化的矩阵,和原矩阵同shape。
  • 3 直接把矩阵应用到公式中即可,其中减去最小值的那一项,可以用上面提到的tile函数来实现。
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))
    normDataSet = normDataSet/tile(ranges, (m,1))   #element wise divide
    return normDataSet, ranges, minVals

2 测试算法

算法的代码已经完成,需要输入的数据参数也已经处理好,接下来就是根据训练数据测试算法即可。

3 使用算法 (构建一个完整的系统)

算法不是测试完了就OK了,最重要的是实现算法的功能:根据输入的特征值,判断此人是否是海伦喜欢的类型。

  • 1 应用input函数来使使用者能够输入数据特征
  • 2 别忘了用float把input返回的字符串变成数字
  • 3 对于输入的特征,千万别忘了也需要进行数据处理,此处需要归一化
  • 4 输出结果:like or dislike

五 KNN的优缺点

优点:简单有效
缺点:

  • 1必须保存全部数据集,可能会使用大量的存储空间。
  • 2必须对每个数据计算距离,耗时。
  • 3无法给出任何数据的基础结构信息,无法知道平均实例样本和典型实例样本具有什么特征。

PS1:

对于多文件(同一个文件夹下的多个文本文件)的数据处理方法。

  • 1 用listdir函数获得该文件夹下的所有文件名并返回一个列表
  • 2 用返回的列表,索引的方式获得每一个文件名(若需要的话,对每个文件名进行字符串处理得到想要的数据,比如类标签)
  • 3 测试算法(同样,在运用算法函数之前,需要处理好数据,可以直接赋值给参数。)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容