0 前言
本文主要讲述以下几点:
1.通过scikit-learn计算文本内容的tfidf并构造N*M矩阵(N个文档 M个特征词);
2.调用scikit-learn中的K-means进行文本聚类;
3.使用PAC进行降维处理,每行文本表示成两维数据;
4.最后调用Matplotlib显示聚类效果图。
1 输入
文本输入是读取本地的01_All_BHSpider_Content_Result.txt文件,里面包括1000行数据,其中001~400行为景区、401~600为动物、601~800为人物明星、801~1000为国家地理文本内容(百度百科摘要信息)。
该内容可以自定义爬虫进行爬取,同时分词采用Jieba进行。
免费下载包括代码py文件和01_All_BHSpider_Content_Result.txt。
下载地址:http://download.csdn.net/detail/eastmount/9410810
2 源代码
代码如下,详见注释和后面的学习笔记推荐:
[python] view plain copy
- # coding=utf-8
- """
- Created on 2016-01-16 @author: Eastmount
- 输入:打开txt 对应1000个文本
- 001~400 5A景区 401~600 动物 601~800 人物 801~1000 国家
- 输出:txt tfidf值聚类图形 1000个类标
- 参数:weight权重这是一个重要参数
- """
- importtime
- importre
- importos
- importsys
- importcodecs
- importshutil
- importnumpy as np
- importmatplotlib
- importscipy
- importpyplot as plt
- fromsklearn importfeature_extraction
- fromfeature_extraction.text importTfidfTransformer
- fromfeature_extraction.text importCountVectorizer
- fromfeature_extraction.text importHashingVectorizer
- if__name__ == "__main__":
- #########################################################################
- # 第一步计算TFIDF
- #文档预料空格连接
- corpus = []
- #读取预料一行预料为一个文档
- forline in open('01_All_BHSpider_Content_Result.txt', 'r').readlines():
- #print line
- append(line.strip())
- #print corpus
- #参考: http://blog.csdn.net/abcjennifer/article/details/23615947
- #vectorizer = HashingVectorizer(n_features = 4000)
- #将文本中的词语转换为词频矩阵矩阵元素a[i][j] 表示j词在i类文本下的词频
- vectorizer = CountVectorizer()
- #该类会统计每个词语的tf-idf权值
- transformer = TfidfTransformer()
- #第一个fit_transform是计算tf-idf 第二个fit_transform是将文本转为词频矩阵
- tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus))
- #获取词袋模型中的所有词语
- word = vectorizer.get_feature_names()
- #将tf-idf矩阵抽取出来,元素w[i][j]表示j词在i类文本中的tf-idf权重
- weight = tfidf.toarray()
- #打印特征向量文本内容
- print'Features length: ' + str(len(word))
- resName = "BHTfidf_Result.txt"
- result = codecs.open(resName, 'w', 'utf-8')
- forj in range(len(word)):
- write(word[j] + ' ')
- write('\r\n\r\n')
- #打印每类文本的tf-idf词语权重,第一个for遍历所有文本,第二个for便利某一类文本下的词语权重
- fori in range(len(weight)):
- #print u"-------这里输出第", i, u"类文本的词语tf-idf权重------"
- forj in range(len(word)):
- #print weight[i][j],
- write(str(weight[i][j]) + ' ')
- write('\r\n\r\n')
- close()
- ########################################################################
- # 第二步聚类Kmeans
- print'Start Kmeans:'
- fromcluster importKMeans
- clf = KMeans(n_clusters=4) #景区动物 人物 国家
- s = clf.fit(weight)
- prints
- '''''
- print 'Start MiniBatchKmeans:'
- from sklearn.cluster import MiniBatchKMeans
- clf = MiniBatchKMeans(n_clusters=20)
- s = clf.fit(weight)
- print s
- '''
- #中心点
- print(clf.cluster_centers_)
- #每个样本所属的簇
- label = [] #存储1000个类标4个类
- print(clf.labels_)
- i = 1
- whilei <= len(clf.labels_):
- printi, clf.labels_[i-1]
- append(clf.labels_[i-1])
- i = i + 1
- #用来评估簇的个数是否合适,距离越小说明簇分的越好,选取临界点的簇个数137281791
- print(clf.inertia_)
- ########################################################################
- # 第三步图形输出 降维
- fromdecomposition importPCA
- pca = PCA(n_components=2) #输出两维
- newData = pca.fit_transform(weight) #载入N维
- printnewData
- #5A景区
- x1 = []
- y1 = []
- i=0
- whilei<400:
- append(newData[i][0])
- append(newData[i][1])
- i += 1
- #动物
- x2 = []
- y2 = []
- i = 400
- whilei<600:
- append(newData[i][0])
- append(newData[i][1])
- i += 1
- #人物
- x3 = []
- y3 = []
- i = 600
- whilei<800:
- append(newData[i][0])
- append(newData[i][1])
- i += 1
- #国家
- x4 = []
- y4 = []
- i = 800
- whilei<1000:
- append(newData[i][0])
- append(newData[i][1])
- i += 1
- #四种颜色红 绿 蓝 黑
- plot(x1, y1, 'or')
- plot(x2, y2, 'og')
- plot(x3, y3, 'ob')
- plot(x4, y4, 'ok')
- show()
3 输出结果
采用Kmeans中设置类簇数为4,分别表示景区、动物、明星和国家。
其中运行结果如下图所示,包括17900维tfidf特征向量:
聚类输出结果如下图所示:其中"红-景区 绿-动物 蓝-人物 黑-国家"。由于数据集比较小,文本聚类效果还是很明显的,而LDA算法是计算每个主题分布的算法,推荐你也去学习下。
4 性能评估
这里我想结合文本聚类简单叙述下最常用的评估方法:
正确率 Precision = 正确识别的个体总数 / 识别出的个体总数
召回率 Recall = 正确识别的个体总数 / 测试集中存在的个体总数
F值 F-measure = 正确率 * 召回率 * 2 / (正确率 + 召回率)
由于"clf.labels_"会返回聚类每个样本所属的簇,比如1000行数据,就会返回1000个label值。同时,clf = KMeans(n_clusters=4)设置了类簇为4,故每个值对应在0、1、2、3中的一个,统计结果如下:
其中以世界国家为例,label1数目为198,同时识别出的个体数=198(世界国家)+2(动物)=200,故:
准确率=198/200=0.990
其中动物里面有两个聚类到了世界国家中。而召回率我以人物明星为例,因为知道测试集中601~800这200个数据对应人物明星,故测试集中存在个体数为200,而正确识别数目为185个,故:
召回率=185/200=0.925
最后计算F值即可。同时可以计算宏平均聚类准确率(Macro-Prec)和宏平均召回率(Macro-Rec)。
5 总结及推荐学习资料
代码中有几个问题我没有实现,包括:
(1) 使用HashingVectorizer(n_features = n)设置维数,如何选择更合理的特征;
(2) 调用plt.legend([plot1, plot2, plot3, plot4], (u'景区', u'动物', u'明星', u'国家') )
报错"AttributeError: 'NoneType' object has no attribute 'tk'";
(3) sklearn其它聚类算法以及设置聚类中心点。
但是对那些刚接触Python聚类算法的同学 ,这篇文章还是有一定帮助的!