一、相关概念
相似性是指两个文本之间内容的相似程度。
现有关于文本相似性度量的研究,主要采用以下方法:词频-逆文档频率(Term Frequency–Inverse Document Frequency,TF-IDF)、潜在狄利克雷分布(Latent Dirichlet Allocation,LDA)主题模型、Word2vec词向量模型。
TF-IDF是一种用以评估词对文档集或语料库中文件重要程度的统计方法。
LDA是一种描述文档-主题-词之间关系的概率生成模型,能够在一定程度上捕捉文档的语义信息。
Word2vec词向量模型是根据上下文语义信息,将词表示成多维向量的神经网络语义模型。它通过词嵌入方式训练得到稠密词向量,不仅可定量地研究文本之间的关系、捕获文本语义层面的信息,还能够考虑词对整个文本的影响。
二、研究设计
尽管TF-IDF和LDA在文本相似性计算中有广泛应用,但它们仍存在以下不足:词语顺序与上下文信息的忽略、缺乏深层次语义理解、无法处理同义词与多义词、对长文本的效果较差、对噪音词的敏感性等。因此,在实际应用中,通常需结合其它方面来弥补这些不足,以提高文本相似性计算的准确性和鲁棒性。
基于上述分析,笔者采用“TF-IDF + Word2vec词向量模型”思路,来更准确地计算文本相似性。基本步骤如下:1)对文本进行分词处理,并对分词结果中的停用词、标点符号、图片表格进行剔除;2)计算文本中已分好的词的TF-IDF值;3)基于财经文本语料库,训练Word2vec词向量模型;4)从训练好的Word2vec模型中获取每个词的词向量,并进行TF-IDF加权处理,得到单个文本的向量;5)通过余弦函数,计算两个文本之间的相似性。其中,相似性的数值越大,表明两个文本之间内容的相似程度越高;反之亦然。
三、案例分析
由于笔者的研究方向处于经管领域,故以自身所使用的数据进行案例阐述。下文以计算企业社会责任报告之间的相似性进行分析:
1、导入第三方库
import jieba
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from gensim.models import Word2Vec
需说明的是,计算两个向量的相似性,有许多数学公式,如曼哈顿距离、欧式距离、皮尔逊相关系数等。然而,笔者使用的是被广泛应用的余弦函数。
2、加载文本数据
# 加载数据
texts_path = [r"C:\Users\22892\Desktop\草稿\000001_平安银行_2012年度社会责任报告.txt",
r"C:\Users\22892\Desktop\草稿\000001_平安银行_2013年度社会责任报告.txt",
]
texts_list = []
for i in range(len(texts_path)):
with open(texts_path[i], mode='r', encoding='utf-8') as file:
text = file.read()
texts_list.append(text)
3、分词并删除停用词、标点符号、图片表格
# 分词
words_1 = jieba.lcut(texts_list[0])
words_2 = jieba.lcut(texts_list[1])
# 删除停用词
stop_words = open(r"C:\Users\22892\Desktop\停用词.txt", mode='r', encoding='utf-8')
stop_words_list = stop_words.read().splitlines()
clean_words_1 = [word for word in words_1 if word not in stop_words_list and word.strip() and word.isalnum()]
clean_words_2 = [word for word in words_2 if word not in stop_words_list and word.strip() and word.isalnum()]
其中,word.strip() 表示删除空白字符,并过滤掉空元素和空行;word.isalnum() 表示仅保留词、字母、数字。
4、计算每个文本的TF-IDF值
# 计算TF-IDF值
documents = [' '.join(clean_words_1), ' '.join(clean_words_2)]
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(documents)
features = vectorizer.get_feature_names_out()
注意,documents 列表是二维的,第一维表示每个文本,第二维表示文本分好的词。
5、训练Word2vec词向量模型,并对词向量进行加权处理
# 训练Word2vec模型
model = Word2Vec(sentences=[clean_words_1, clean_words_2], vector_size=100, window=5, min_count=1, workers=4)
# 定义函数,通过对词向量进行TF-IDF加权,获得文档向量
def get_weighted_doc_vector(words, model, tfidf_vector):
doc_vector = np.zeros((model.vector_size, )) # 一维向量,100维
for word in words:
if word in model.wv:
word_index = vectorizer.vocabulary_.get(word)
if word_index is not None:
doc_vector += model.wv[word] * tfidf_vector[word_index]
return doc_vector
# 获取每个文档的TF-IDF向量
tfidf_1 = tfidf_matrix.toarray()[0]
tfidf_2 = tfidf_matrix.toarray()[1]
# 通过对词向量进行TF-IDF加权来获取文档向量
doc_vector_1 = get_weighted_doc_vector(words=clean_words_1, model=model, tfidf_vector=tfidf_1)
doc_vector_2 = get_weighted_doc_vector(words=clean_words_2, model=model, tfidf_vector=tfidf_2)
这里Word2vec模型的输入可换成自己的特定类型的语料库进行训练。
6、根据余弦函数计算文本相似性
# 计算两个文档向量之间的余弦相似度
cosine_sim = cosine_similarity(X=[doc_vector_1], Y=[doc_vector_2])[0][0]
注意,cosine_similarity() 的输入X、Y都必须是二维数据,否则程序会报错!最后,得到上述两个文本之间的相似度为0.9931。
7、整个代码
import jieba
import numpy as np
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from gensim.models import Word2Vec
# 加载数据
texts_path = [r"C:\Users\22892\Desktop\草稿\000001_平安银行_2012年度社会责任报告.txt",
r"C:\Users\22892\Desktop\草稿\000001_平安银行_2013年度社会责任报告.txt"]
texts_list = []
for i in range(len(texts_path)):
with open(texts_path[i], mode='r', encoding='utf-8') as file:
text = file.read()
texts_list.append(text)
# 分词
words_1 = jieba.lcut(texts_list[0])
words_2 = jieba.lcut(texts_list[1])
# 删除停用词
stop_words = open(r"C:\Users\22892\Desktop\停用词.txt", mode='r', encoding='utf-8')
stop_words_list = stop_words.read().splitlines()
clean_words_1 = [word for word in words_1 if word not in stop_words_list and word.strip() and word.isalnum()]
clean_words_2 = [word for word in words_2 if word not in stop_words_list and word.strip() and word.isalnum()]
# 计算TF-IDF值
documents = [' '.join(clean_words_1), ' '.join(clean_words_2)]
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(documents)
features = vectorizer.get_feature_names_out()
# 训练Word2vec模型
model = Word2Vec(sentences=[clean_words_1, clean_words_2], vector_size=100, window=5, min_count=1, workers=4)
# 定义函数,通过对词向量进行TF-IDF加权,获得文档向量
def get_weighted_doc_vector(words, model, tfidf_vector):
doc_vector = np.zeros((model.vector_size, )) # 一维向量,100维
for word in words:
if word in model.wv:
word_index = vectorizer.vocabulary_.get(word)
if word_index is not None:
doc_vector += model.wv[word] * tfidf_vector[word_index]
return doc_vector
# 获取每个文档的TF-IDF向量
tfidf_1 = tfidf_matrix.toarray()[0]
tfidf_2 = tfidf_matrix.toarray()[1]
# 通过对词向量进行TF-IDF加权来获取文档向量
doc_vector_1 = get_weighted_doc_vector(words=clean_words_1, model=model, tfidf_vector=tfidf_1)
doc_vector_2 = get_weighted_doc_vector(words=clean_words_2, model=model, tfidf_vector=tfidf_2)
# 计算两个文档向量之间的余弦相似度
cosine_sim = cosine_similarity(X=[doc_vector_1], Y=[doc_vector_2])[0][0]
print(cosine_sim)