您的位置:首页 > 其它

基于文本向量空间模型的文本聚类算法

2015-06-22 11:05 573 查看

基于文本向量空间模型的文本聚类算法

@[vsm|向量空间模型|文本相似度]

本文源地址http://www.houzhuo.net/archives/51.html

vsm概念简单,把对文本内容的处理转化为向量空间中的
向量
计算,以空间上的相似度来直观表达语义上的相似度。

目录

基于文本向量空间模型的文本聚类算法
文本聚类

向量空间模型vsm
文本预处理

获取每篇文档词频

获得相同长度的向量

归一化

idf频率加权

tf-idf加权并归一化

计算向量间的夹角

文本聚类

文本聚类
主要依据聚类假设:同类的文档相似度较大,非同类的文档相似度较小。作为一种
无监督的机器学习
方法,聚类由于不需要训练过程、以及不需要预先对文档手工标注类别,因此具有较高的灵活性和自动化处理能力,成为对文本信息进行有效组织、摘要和导航的重要手段。

向量空间模型vsm

所有的文本都可表现成向量的形式:



向量中的每一维都代表在文档中出现的一个独立词组或单个词,并且我们给每个词组赋予一个
权值
(最简单就是词频,或者广为人知的tf_idf权重)。所以一个文档就会转换成一个n维的向量。

向量夹角公式



接下来就是利用中学所学的的公式来计算向量之间的夹角,夹角越小即代表较高的相似度。当然,我们比较之前需要将两个向量转化为同一维度(下面的代码中将加以演示)

文本预处理:

__author__ = 'iothz'

import string
from string import *
list_of_all_file =[]
str_of_file1 = ""
str_of_file2 = ""

file1 = open('science.txt', 'r')
for line in file1.readlines():
nopunc =line.replace(",", "").replace(".", "").replace("?", "").replace("\"", "").replace("\'", "").replace(")", "").replace("(", "").replace("[", " ").replace("]", " ").replace("\n", " ")
str_of_file1 +=nopunc
list_of_all_file.append(str_of_file1)
file2 = open('science2.txt', 'r')
for line in file2.readlines():
nopunc =line.replace(",", "").replace(".", "").replace("?", "").replace("\"", "").replace("\'", "").replace(")", "").replace("(", "").replace("[", " ").replace("]", " ").replace("\n", " ")
str_of_file2 +=nopunc

list_of_all_file.append(str_of_file2)


文本预处理方法各不相同,上面代码去除两个文本的标点,并添加到一个list中方便下面处理

获取每篇文档词频

from collections import Counter
def build_lexicon(corpus):
lexicon = set()
for doc in corpus:
lexicon.update([word for word in doc.split()])
return lexicon

vocabulary = build_lexicon(list_of_all_file)
print 'the vector of two file is [' + ', '.join(list(vocabulary)) + ']'


the vector of two file is [and, nlp, basketball, love, her, i, baseball, you, cins]


这里引入了一个新的Python对象Counter用来在一个循环中进行计数。结果统计出每个单词出现的次数,但是我们现在还不能比较,因为他们的不在同一词汇空间中。

获得相同长度的向量

def freq(term, document):
return document.split().count(term)

def tf(term, document):
return freq(term, document)

doc_term_matrix = []

for doc in list_of_all_file:
#print '****'
print 'the doc is "' + doc + ' " '
tf_vector = [tf(word, doc) for word in vocabulary]
print tf_vector
tf_vector_string = ', '.join(format(freq, 'd') for freq in tf_vector)
print 'the tf vector for Document %d is [%s]' % ((list_of_all_file.index(doc)+1), tf_vector_string)

doc_term_matrix.append(tf_vector)
print 'All combined, here is out master document term matrix: '
print doc_term_matrix


the doc is "i love basketball i love you and nlp "
[1, 1, 1, 2, 0, 2, 0, 1, 0]
the tf vector for Document 1 is [1, 1, 1, 2, 0, 2, 0, 1, 0]
the doc is "i love baseball i love her and cins "
[1, 0, 0, 2, 1, 2, 1, 0, 1]
the tf vector for Document 2 is [1, 0, 0, 2, 1, 2, 1, 0, 1]
All combined, here is out master document term matrix:
[[1, 1, 1, 2, 0, 2, 0, 1, 0], [1, 0, 0, 2, 1, 2, 1, 0, 1]]


根据这段代码我们得到了相同长度的量化结果,
量化结果的长度是由语料库决定的
。有过接触机器学习经验的人都知道,为了避免个别单词在文档中过于频繁的出现,影响分析结果,我们要对每个词频向量进行比例缩放(
归一化
)。

归一化

import math
import numpy as np

def normalizer(vec):
denom = np.sum([el**2 for el in vec])
return [(el / math.sqrt(denom)) for el in vec]

doc_term_matrix_normalizer = []
for vec in doc_term_matrix:
doc_term_matrix_normalizer.append(normalizer(vec))
print 'A regular old document term matrix '
print np.matrix(doc_term_matrix)
print '\nA document term matrix with row-wise  norms of 1:'
print np.matrix(doc_term_matrix_normalizer)


A regular old document term matrix
[[1 1 1 2 0 2 0 1 0]
[1 0 0 2 1 2 1 0 1]]

A document term matrix with row-wise norms of 1:
[[ 0.28867513  0.28867513  0.28867513  0.57735027  0.          0.57735027
0.          0.28867513  0.        ]
[ 0.28867513  0.          0.          0.57735027  0.28867513  0.57735027
0.28867513  0.          0.28867513]]


我们就这样得到了一个归一化过后的向量,并且没有丢失过多信息。但是比如在一篇文章中,“我”,“的”这类高频词汇对我们的做相似性比较似乎并没有什么作用,因为每篇文章中都会出现,反而会影响结果。所以我们将引入最通用的一种文本权值计算方法
tf-idf


idf频率加权

def numDocsContaining(word, doclist):
docCount = 0
for doc in doclist:
if(freq(word, doc) > 0):
docCount +=1
return docCount
def idf(word, doclist):
n_samples = len(doclist)
df = numDocsContaining(word, doclist)
return np.log(n_samples / 1+df)

my_idf_vector = [idf(word, list_of_all_file) for word in vocabulary]

print 'Our vocabulary vector is [' + ', '.join(list(vocabulary)) + ']'
print 'The inverse document frequency vector is [' + ', '.join(format(freq, 'f') for freq in my_idf_vector) + ']'


Our vocabulary vector is [and, nlp, basketball, love, her, i, baseball, you, cins]
The inverse document frequency vector is
[1.098612, 1.098612, 1.098612, 1.098612, 0.693147, 1.098612, 0.693147, 1.098612, 0.693147]


tf-idf
是一种统计方法,它的主要思想是:如果某个词或短语在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。

TF词频(Term Frequency)
易于理解



IDF逆向文件频率(Inverse Document Frequency)
是一个词语普遍重要性的度量。某一特定词语的IDF,可以由总文件数目除以包含该词语之文件的数目,再将得到的商取对数得到:



其中

|D|:语料库中的
文件总数


:包含词语的文件数目(即的文件数目)如果该词语不在语料库中,就会导致分母为零,因此一般情况下使用

作为
分母


我们快得到想要的结果了。为了得到TF-IDF加权词向量,你必须做一个简单的计算:tf * idf。

如果你用一个AxB的向量乘以另一个AxB的向量,你将得到一个大小为AxA的向量,或者一个标量。我们不会那么做,因为我们想要的是一个具有相同维度(1 x词数量)的词向量,向量中的每个元素都已经被自己的idf权重加权了,所以:

def build_idf_matrix(idf_vector):
idf_mat = np.zeros((len(idf_vector),len(idf_vector)))
np.fill_diagonal(idf_mat,idf_vector)
return idf_mat

my_idf_matrix = build_idf_matrix(my_idf_vector)
print my_idf_matrix


[[ 1.09861229  0.          0.          0.          0.          0.          0.
0.          0.        ]
[ 0.          1.09861229  0.          0.          0.          0.          0.
0.          0.        ]
[ 0.          0.          1.09861229  0.          0.          0.          0.
0.          0.        ]
[ 0.          0.          0.          1.09861229  0.          0.          0.
0.          0.        ]
[ 0.          0.          0.          0.          0.69314718  0.          0.
0.          0.        ]
[ 0.          0.          0.          0.          0.          1.09861229
...]]


这样我们就将IDF向量转化为BxB的矩阵了,矩阵的
对角线就是IDF向量
。这意味着我们现在可以用逆文档词频矩阵乘以每一个词频向量了。当然,我们在其中还是要做一次
归一化
操作

tf-idf加权并归一化

doc_term_tfidf__matrix = []
for tf_vector in doc_term_matrix:
doc_term_tfidf__matrix.append(np.dot(tf_vector, my_idf_matrix))
doc_term_tfidf__matrix_normalizer = []
for tf_vector in doc_term_tfidf__matrix:
doc_term_tfidf__matrix_normalizer.append(normalizer(tf_vector))
print vocabulary
print np.matrix(doc_term_tfidf__matrix_normalizer)


[[ 0.28867513  0.28867513  0.28867513  0.57735027  0.          0.57735027
0.          0.28867513  0.        ]
[ 0.31320094  0.          0.          0.62640189  0.19760779  0.62640189
0.19760779  0.          0.19760779]]


由此已经计算出了tf-idf权值,最后一步便是计算向量间的夹角了

计算向量间的夹角

x = np.array(doc_term_tfidf__matrix_normalizer[0][:])
y = np.array(doc_term_tfidf__matrix_normalizer[1][:])

Lx = np.sqrt(x.dot(x))
Ly = np.sqrt(y.dot(y))
print Lx, Ly

cos_angle = x.dot(y) / (Lx*Ly)
print  'cos_value: ', cos_angle

angle = np.arccos(cos_angle)
angle2 = angle*360/2/np.pi
print 'angle: ', angle2

similarity = (90-angle2) / 90
print 'similarity is: ' ,similarity


1.0 1.0
cos_value:  0.8137199207
angle:  35.5390135166
similarity is: 0.605122072038


得到最终结果!当然还有最简的方式,就是利用
scikit-learn
来计算,但是为了夯实基础还是要从最基本的了解起。

from sklearn.feature_extraction.text import TfidfVectorizer

tfidf_vectorizer = TfidfVectorizer(min_df=1)
tfidf_matrix = tfidf_vectorizer.fit_transform(list_of_all_file)

print tfidf_matrix.todense()
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: