文本聚类教程
来源:互联网 发布:身骑白马 知乎 编辑:程序博客网 时间:2024/06/14 18:56
文本聚类教程
本人曾做机器学习方向,由于实习需要转做文本聚类、分类的工作,虽然大致相似,但仍是新手,过程和结果也仅供大神指教。本博包含了作者两周的专心研究调试及由数千行测试得到了300余行代码精华,如需转载,请注明出处。
什么是文本聚类?
文本聚类是将一个个文档由原有的自然语言文字信息转化成数学信息,以高维空间点的形式展现出来,通过计算那些点距离比较近来将那些点聚成一个簇,簇的中心叫做簇心。一个好的聚类要保证簇内点的距离尽量的近,但簇与簇之间的点要尽量的远。
文本聚类的难点是什么?
聚类是一种非监督学习,也就是说聚成几类,怎么聚,我们都不知道,只能一点点试出来。但是有时候机器认为这两堆点可以认为是两个簇,但人理解可能是一个簇,文本聚类就就难在了这里,机器与人的理解不太一样。一般能看到这个博的人都学过基本的聚类算法,拿k-means为例,簇心的选取是个非常随机的过程,导致k值相同的情况下聚类的结果每次都不一样,又不好取个平均,所以聚类的好坏很难被评价出来。
如何评价聚类的好坏?
我在上一篇博:http://blog.csdn.net/chixujohnny/article/details/51852633 中讲到完爆一切的S_Dbw评价指标,我目前还没试过,有兴趣的同学可以试试,总之早晚都要用的。
文本聚类过程
![](http://img.blog.csdn.net/20160708151824436?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
词赋权方法还有textrate,还没用,jieba自带的,肉眼看不错,可以试试。
上面的图说几个部分,一般生成文档向量矩阵的格式是,每一行代表一个文档,每一列是一个维度代表该文档这个词的权重,没出现这个词就是0,几千个文件维度在10多w左右(看文档的大小),这么大的维度人脑想也想到了,矩阵将是及其稀疏的,也就是说,在一个高维空间中,几千个点几乎都聚在了一起,虽说彼此之间有距离,但是距离非常之小,很明显这样聚类效果肯定非常差,实测过,跟抛硬币的概率一样。于是将矩阵稠密一点就想到了pca降维,pca是主成分分析的缩写,大致意思就是取这个高维向量中方差最大的方向经过一些数学变换将有用的部分保留,没用的部分舍弃,这种办法同样适合分类算法中寻找最吊的特征,具体细节我在《机器学习实战》这本书看到的(一个小哥背麻袋的那本书),讲的不详细但是大致懂了。为啥不用SVD降维呢,SVD适合稠密型矩阵,比如图像矩阵或者推荐系统中,取80%有用的信息,适合做图像压缩算法(懂得不深入,请打脸)。
轮廓系数这个概念我实在这个北邮同学的博客里看到的:buptguo.com/2016/05/31/learn-ml-from-scikit-learn-silhouette-analysis/ 直接点进去看就行了,他讲的比我好。
聚类这块我看了些资料,百度的那些用的都是k-means做的文本聚类,我就想问你们做的是不是学校的作业应付了事?上千维的向量用k-means做?搞笑吗?我实测了一下,效果很差,扔筛子的准确度。后来开始查文献资料,有一种叫BIRCH的层次聚类算法,该算法可以比较好的解决k-means每次聚类结果偏差太大的问题,相比dbscan有可以设置聚类的个数(当然阈值也可以设置),最重要的是sklearn有现成的库调用,速度还很快。试了一下,比kmeans好,但是还不是特别满意,查了下还是不太适合高维空间,准备再查查资料。
还有几个点可能还需要尝试一下:
聚类算法的阈值可以设置一下,还有什么聚类算法都试试,尤其是高维的聚类算法。
用textrate赋值一下权重,看看效果咋样
最后送上代码:
函数干啥的我就不在博客解释了,下面代码注释的非常详细
-
-
-
-
-
-
-
- import logging
- import time
- import os
- import jieba
- import glob
- import random
- import copy
- import chardet
- import gensim
- from gensim import corpora,similarities, models
- from pprint import pprint
- import jieba.analyse
- from sklearn import feature_extraction
- from sklearn.feature_extraction.text import TfidfTransformer
- from sklearn.feature_extraction.text import CountVectorizer
- import os
- from sklearn.decomposition import PCA
-
-
-
-
-
- start = time.clock()
-
- print '#----------------------------------------#'
- print '# #'
- print '# 载入语料库 #'
- print '# #'
- print '#----------------------------------------#\n'
- def PreprocessDoc(root):
-
- allDirPath = []
- fileNumList = []
-
- def processDirectory(args, dirname, filenames, fileNum=0):
- allDirPath.append(dirname)
- for filename in filenames:
- fileNum += 1
- fileNumList.append(fileNum)
- os.path.walk(root, processDirectory, None)
- totalFileNum = sum(fileNumList)
- print '总文件数为: ' + str(totalFileNum)
-
- return allDirPath
-
-
- print '#----------------------------------------#'
- print '# #'
- print '# 合成语料文档 #'
- print '# #'
- print '#----------------------------------------#\n'
-
-
-
- def SaveDoc(allDirPath, docPath, stopWords):
-
- print '开始合成语料文档:'
-
- category = 1
- f = open(docPath,'w')
-
- for dirParh in allDirPath[1:]:
-
- for filePath in glob.glob(dirParh + '/*.txt'):
-
- data = open(filePath, 'r').read()
- texts = DeleteStopWords(data, stopWords)
- line = ''
- for word in texts:
- if word.encode('utf-8') == '\n' or word.encode('utf-8') == 'nbsp' or word.encode('utf-8') == '\r\n':
- continue
- line += word.encode('utf-8')
- line += ' '
- f.write(line + '\n')
- category += 1
-
- return 0
-
-
- print '#----------------------------------------#'
- print '# #'
- print '# 分词+去停用词 #'
- print '# #'
- print '#----------------------------------------#\n'
- def DeleteStopWords(data, stopWords):
-
- wordList = []
-
-
- cutWords = jieba.cut(data)
- for item in cutWords:
- if item.encode('utf-8') not in stopWords:
- wordList.append(item)
-
- return wordList
-
-
- print '#----------------------------------------#'
- print '# #'
- print '# tf-idf #'
- print '# #'
- print '#----------------------------------------#\n'
- def TFIDF(docPath):
-
- print '开始tfidf:'
-
- corpus = []
-
-
- lines = open(docPath,'r').readlines()
- for line in lines:
- corpus.append(line.strip())
-
-
- vectorizer = CountVectorizer()
-
-
- transformer = TfidfTransformer()
-
-
- tfidf = transformer.fit_transform(vectorizer.fit_transform(corpus))
-
-
- word = vectorizer.get_feature_names()
-
-
- weight = tfidf.toarray()
- print weight
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- return weight
-
-
- print '#----------------------------------------#'
- print '# #'
- print '# PCA #'
- print '# #'
- print '#----------------------------------------#\n'
- def PCA(weight, dimension):
-
- from sklearn.decomposition import PCA
-
- print '原有维度: ', len(weight[0])
- print '开始降维:'
-
- pca = PCA(n_components=dimension)
- X = pca.fit_transform(weight)
- print '降维后维度: ', len(X[0])
- print X
-
- return X
-
-
- print '#----------------------------------------#'
- print '# #'
- print '# k-means #'
- print '# #'
- print '#----------------------------------------#\n'
- def kmeans(X, k):
-
- from sklearn.cluster import KMeans
-
- print '开始聚类:'
-
- clusterer = KMeans(n_clusters=k, init='k-means++')
-
-
-
-
-
-
- y = clusterer.fit_predict(X)
- print y
-
-
-
-
-
-
-
-
- return y
-
-
- print '#----------------------------------------#'
- print '# #'
- print '# BIRCH #'
- print '# #'
- print '#----------------------------------------#\n'
- def birch(X, k):
-
- from sklearn.cluster import Birch
-
- print '开始聚类:'
-
- clusterer = Birch(n_clusters=k)
-
- y = clusterer.fit_predict(X)
- print '输出聚类结果:'
- print y
-
- return y
-
-
- print '#----------------------------------------#'
- print '# #'
- print '# 轮廓系数 #'
- print '# #'
- print '#----------------------------------------#\n'
- def Silhouette(X, y):
-
- from sklearn.metrics import silhouette_samples, silhouette_score
-
- print '计算轮廓系数:'
-
- silhouette_avg = silhouette_score(X, y)
- sample_silhouette_values = silhouette_samples(X, y)
-
- pprint(silhouette_avg)
-
- return silhouette_avg, sample_silhouette_values
-
-
- print '#----------------------------------------#'
- print '# #'
- print '# 画图 #'
- print '# #'
- print '#----------------------------------------#\n'
- def Draw(silhouette_avg, sample_silhouette_values, y, k):
-
- import matplotlib.pyplot as plt
- import matplotlib.cm as cm
- import numpy as np
-
-
- fig, ax1 = plt.subplots(1)
- fig.set_size_inches(18, 7)
-
-
-
- ax1.set_xlim([-0.2, 0.5])
-
-
- ax1.set_ylim([0, len(X) + (k + 1) * 10])
-
- y_lower = 10
-
- for i in range(k):
-
- ith_cluster_silhouette_values = sample_silhouette_values[y == i]
- ith_cluster_silhouette_values.sort()
-
- size_cluster_i = ith_cluster_silhouette_values.shape[0]
- y_upper = y_lower + size_cluster_i
-
- color = cm.spectral(float(i)/k)
- ax1.fill_betweenx(np.arange(y_lower, y_upper),
- 0,
- ith_cluster_silhouette_values,
- facecolor=color,
- edgecolor=color,
- alpha=0.7)
-
-
- ax1.text(-0.05, y_lower + 0.5 * size_cluster_i, str(i))
-
-
- y_lower = y_upper + 10
-
-
- ax1.axvline(x=silhouette_avg, color='red', linestyle="--")
-
- plt.show()
-
-
-
-
-
-
- if __name__ == "__main__":
-
- root = '/Users/John/Desktop/test'
- stopWords = open('/Users/John/Documents/NLPStudy/stopwords-utf8', 'r').read()
- docPath = '/Users/John/Desktop/test/doc.txt'
- k = 3
-
- allDirPath = PreprocessDoc(root)
- SaveDoc(allDirPath, docPath, stopWords)
-
- weight = TFIDF(docPath)
- X = PCA(weight, dimension=800)
-
- y = birch(X, k)
- silhouette_avg, sample_silhouette_values = Silhouette(X, y)
- Draw(silhouette_avg, sample_silhouette_values, y, k)
-
-
- end = time.clock()
- print '运行时间: ' + str(end - start)