利用余弦相似度做文本分类:
在数学中余弦相似度的公式:cos(a,b)=a*b/(|a|+|b|),而在文本上,我们的余弦相似度通常是这样计算而成:
(文本a,b共同出现的词条数目)/(文本a出现的词条数目+文本b出现的词条数目)
处理两个纯文本的相似度可以这样处理,但对于文本分类来说,我们更多使用的是“训练集”与“测试集”的概念,具体我们会抽取训练集的词条,形成语料库,然后统计这些语料库中的词条在测试集中出现的频数以及在训练集中出现的频数,形成词条词频特征码,进而计算相似度。
在现实中,文本a,b都出现相同而不重要的词条,如:“的,地,我们,你”等。我们的解决的方案可以是,建立停用词库,对文本先预处理,去除文本的不重要词条。或者,用TF-IDF,TextRank等算法提取重要的特征词。而我在多次实验中发现,文本中较为重要的往往集中于实体名词,也就是说,我们可以提取带有vn,n等词性的词条,然后统计相同出现的数目,计算余弦相似度。
具体的步骤如下:
步骤一:提取文本
提取文本的词条我是用结巴分词,因为我的环境是linux平台,所以只需sudopip-install jieba即可,代码如下:
import jieba
import jieba.posseg as pseg
f1=open(sampfn) #打开训练集文本
test_words={}
all_words={}
try:
f1_text= f1.read()
f1_text= unicode(f1_text,'utf-8') #文本解码为utf-8,支持中文
finally:
f1.close()
f1_seg_list=pseg.cut(f1_text) #将文本分词,得到每一个词条以及其词性
for w in f1_seg_list:
if 'n' in w.flag : #抽取实体名词
test_words.setdefault(w.word,0) #构建语料库字典
all_words.setdefault(w.word,0) #创建训练集词条字典,并统计词条词频
all_words[w.word]+=1
ftest1= open(ftest1fn) #打开测试样本1
mytest1_words= copy.deepcopy(test_words) #复制语料库
try:
ftest1_text= ftest1.read()
ftest1_text= unicode(ftest1_text,'utf-8')
finally:
ftest1.close()
ftest1_seg_list=pseg.cut(ftest1_text) #将文本分词,得到每一个词条以及其词性
for w in ftest1_seg_list:
if 'n' in w.flag :
if mytest1_words.has_key(w.word):
mytest1_words[w.word]+=1 #统计语料库中的词条在测试集中的频数
步骤二:形成词条词频特征码
sampdata=[] #特征码用列表,方便使用numpy计算
test1data=[]
for key in all_words.keys():
sampdata.append(all_words[key])#训练集的特征码
test1data.append(mytest1_words[key]) #测试集的特征码
test1simi=get_cossimi(sampdata,test1data) #计算余弦相似度
步骤三:计算余弦相似度
我们可以借助numpy类,这是一个专门计算复杂数学公式的类,具体的函数如下:
def get_cossimi(x,y):
myx=np.array(x) #将列表转化为数组,更好的数学理解是向量
myy=np.array(y) #将列表转化为数组,更好的数学理解是向量
cos1=np.sum(myx*myy) #cos(a,b)=a*b/(|a|+|b|)
cos21=np.sqrt(sum(myx*myx))
cos22=np.sqrt(sum(myy*myy))
return cos1/(float(cos21*cos22))