2019/11/12 Caesar
前言
今天开始,我将扎进机器学习的大坑。选择一个经典、容易理解的机器学习算法--- K近邻算法来热身,我将用 K近邻算法来做一个常见的应用,给豆瓣文章分类。现在开始:
1.1场景
想象自己是这幅图中的绿色圆圈,我是谁?我是什么?k近邻算法解答这个问题的依据是:,图中所有图形到绿色圆圈的距离,选出距离最短的k个,然后统计这k个图形的所属类别,投票出类占比最高的那个类别,就是绿色圆圈的类别。
1.2基本概念
k近邻算法实现简单,缺点是当训练样本数大、特征向量维数高时计算复杂度很大。算法复杂度主要在于,每次预测时要计算待预测样本和每个测试样本之间的距离,而且要对距离进行排序找到最近的
k个样本。因此我们有一种解决方案,用 k-d树来实现快速的近邻样本查找。
k近邻算法的实现依赖样本之间的距离值,因此,我们必须给出定义距离的计算方式。我们常用的距离函数是欧式距离,
但是,我们还需做点改动,也就是将应用欧式距离的特征向量的每个分量进行归一化,目的是减少因为特征值的尺度范围不同所带来的干扰,否则数值小的特征向量会被数值大的特征向量淹没。举个栗子,特征向量包含两个分量,分别为身高和肺活量,但是正常成年人的身高是 150 cm ~ 200 cm ,肺活量为 2000 ~ 9000 ml。如果不做归一化,直接揉在一起计算,那么身高带来的个体差异将荡然无存。
1.3算法流程
接下来我们唠唠,工程师是怎么一步步实现这个k近邻算法。
1.计算测试样本到训练集所有样本之间的距离;
2.将所有距离进行远近排序,选出与测试样本最近的k个样本,即邻居;
3.统计这k个的样本类别的频率;
4.取这k个样本中最高频率的样本类别为类别结果,完成kNN算法实验。
1.4算法实现
Python在手,撸它!
1.数据准备
#数据
def createDataSet():
group = np.array([[1, 1.1], [1, 1], [0, 0], [0, 0.1]])
labels = ['A', 'A', 'B', 'B'] return group, labels
2.计算各训练样本到测试样本的欧式距离
# knn classify
def classify(inX,dataSet,labels,k):
m, n = dataSet.shape
distance = []
for i in range(m):
tmp = np.sqrt(np.sum(np.square(inX-dataSet[i])))
distance.append(tmp)
sortedDist = sorted(distance)
classCount = {}
for i in range(k):
voteLabel = labels[distance.index(sortedDist[i])]
classCount[voteLabel] = classCount.get(voteLabel, 0) + 1 # 默认0:map
sortedClass = sorted(classCount.items(), key=lambda d: d[1], reverse=True)
return sortedClass[0][0]
3.测试
if __name__ == '__main__':
dataSet, labels = createDataSet()
r = classify([0,0.2], dataSet, labels, 1)
print(r)
1.5案例分析
假设我们已经有了一些豆瓣帖子,并知道他们是哪些类型的,比如旅游相关的,租房相关的。这个时候有个新的帖子,我们的任务是利用kNN算法判断它属于哪一类。做法:只要计算它与其它帖子的距离,然后选出 k 个最近的帖子样本,就可以知道这个帖子应该分到哪个类中。
1.5案例实现
1.数据处理
首先我们需要将文本内容向量化,这里用到了 jieba 分词。
def cut_word(content):
tags = jieba.analyse.extract_tags(content, withWeight=True, topK=20)
return tags
def merge_tags(*tags):
tag_dict_list = []
v_list = []
for tag in tags:
tag_dict_list.append({i[0]: i[1] for i in tag})
merged_tag = []
for i in tag_dict_list:
merged_tag.extend(i.keys())
merged_tag = set(merged_tag)
for tag_dict in tag_dict_list:
v = []
for i in merged_tag:
if i in tag_dict:
v.append(tag_dict[i])
else:
v.append(0) v_list.append(v)
return v_list
2.kNN算法
这里....当然不必重复造轮子啦!用我们前面实现的kNN分类器即可。
def classify(inX, dataSet, labels, k):
"""
定义knn算法分类器函数
:param inX: 测试数据
:param dataSet: 训练数据
:param labels: 分类类别
:param k: k值
:return: 所属分类
"""
m, n = dataSet.shape # shape(m, n)m列n个特征
# 计算测试数据到每个点的欧式距离
distances = []
for i in range(m):
sum = 0
for j in range(n):
sum += (inX[j] - dataSet[i][j]) ** 2
distances.append(sum ** 0.5)
sortDist = sorted(distances)
# k个最近的值所属的类别
classCount = {}
for i in range(k):
voteLabel = labels[distances.index(sortDist[i])] #初始化首下标值
classCount[voteLabel] = classCount.get(voteLabel, 0) + 1 #默认0:map
sortedClass = sorted(classCount.items(), key = lambda d:d[1], reverse=True)
return sortedClass[0][0]
3.测试
# 租房相关
content1 = u"""
可月付 无中介 方庄地铁附近 芳城园一区单间出租
我的房子在方庄地铁附近的芳城园一区,正规小区楼房,
三家合住,现出租一间主卧和一间带小阳台次卧,室内家电齐全,
冰箱,洗衣机等都有,可洗澡上网,做饭都可以,小区交通便利,四通八达,
希望入住的是附近正常上班的朋友
"""
content2 = u"""
可月付 无中介 方庄地铁附近 芳城园一区主卧次卧出租
我的房子在方庄地铁附近的芳城园一区,正规小区楼房,
三家合住,现出租一间主卧和一间带小阳台次卧,室内家电齐全,
冰箱,洗衣机等都有,可洗澡上网,做饭都可以,小区交通便利,四通八达,
希望入住的是附近正常上班的朋友
"""
content3 = u"""方庄地铁附近 芳城园一区次卧出租
我的房子在方庄地铁附近的芳城园一区,正规小区楼房,
三家合住,现出租一间主卧和一间带小阳台次卧,室内家电齐全,
冰箱,洗衣机等都有,可洗澡上网,做饭都可以,小区交通便利,四通八达,
希望入住的是附近正常上班的朋友
"""
test_content4 = u"""二环玉蜓桥旁下月27号后可入住二居
方庄方古园一区5号楼下月27日到期出租,
我是房主无中介费 ,新一年租6000元每月押一付三,主次卧可分开住。
距地铁5号线蒲黄榆站5分钟路程。房屋60平正向,另有看守固定车位。
"""
# 旅游相关
content5 = u"""世界尽头和冷酷仙境 去南极大部分都是从乌斯怀亚出发,
这是南美大陆的最南端,所以也被称为世界尽头。在这里,有很多东西都已经到了终点:
世界最尽头的邮局;世界最尽头的徒步线路;还有传说中“泛美公路”的终点,
这条路从阿拉斯加一路向南开过来,17848公里,开到这里居然也到头了,没路了…然而,
世界尽头也可以是起点,这里距离南极大陆只有不到1000公里的距离,世界尽头之后就是冷酷仙境。
"""
content6 = u"""走 走停停,看看世界上其中一条最美丽最浪漫的海岸线:那不勒斯湾到amalfi 海岸。
欧洲 最美是小镇,意大利南部小镇与北部山区小镇完全不一样的风格但又一样是浪漫和美丽的世 外桃源。
这个海岸线之所以美,原因跟托斯卡纳的山区一样, 建筑与自然的完美融合。海、 山与小屋群形成了一副美丽的风景画。
沿着铁路和公路,陆路从那不勒斯走到Amalfi。
"""
content7 = u"""仙本娜是一个几乎由潜水业支撑的小镇,走几步路就是潜店,
来自世界各地的游客每个人都在说着潜水。不大,但是比起马布就丰富多了,
有旅馆,酒吧,咖啡店,饭店,小卖部,还有一个大超市。到了仙本娜,
去一间叫龙门客栈(DRAGON ING)的旅馆CHECK IN。这是一件建在水上的树皮旅馆,
不论室内室外的墙面全部是树皮,很有原始的风味,房间内有冷风机,价格也不贵,66马币,
一个大床,有电视机,带卫生间。从马布搬过来,觉得这个环境真是天堂。旅馆由水上的很多长廊组成,
连成一条网,我几乎迷路。
"""
test_content8 = """ 环游世界当然不能真地把旅行都抛掉啦,伟大的古文明和异国文化是环游世界的其中一个重要目的。
美洲的玛雅文明,非洲的埃及文明,亚洲的印度文明,一个个从人类起源就可以追溯的古文明。
一个个让人目定口呆的世界文化遗产。感受人类的力量同时感受时间的伟大和残酷。 还有令人陶醉的异国文化,去捷克感受的波西米亚文化,
到伦敦西区和纽约百老汇看歌舞剧等等,品味澳大利亚的葡萄酒苏格兰的威士忌墨西哥的龙舌兰。有时候,小资也是一种情调啊!
"""
tag1 = cut_word(content1)
tag2 = cut_word(content2)
tag3 = cut_word(content3)
tag4 = cut_word(test_content4)
tag5 = cut_word(content5)
tag6 = cut_word(content6)
tag7 = cut_word(content7)
tag8 = cut_word(test_content8)
v = [tag1, tag2, tag3, tag4, tag5, tag6, tag7, tag8]
data = merge_tags(*v)
v4 = data.pop(3)
v8 = data.pop()
dataSet = array(data)
labels = array(['租房', '租房', '租房', '旅游', '旅游', '旅游'])
k = 3
v4 = array(v4)
outputLabel = kNNClassify(v4, dataSet, labels, k)
print "Your input is: v4 and classified to class: ", outputLabel
v8 = array(v8)
outputLabel = kNNClassify(v8, dataSet, labels, k)
print "Your input is: v8 and classified to class: ", outputLabel