参考:
从0开始词嵌入(Word embedding)
word2vec模型深度解析
词向量经典模型:从word2vec、glove、ELMo到BERT
基于TextRank算法的文本摘要(附Python代码)
文章目录
1 什么是词嵌入?
词嵌入是自然语言处理的一项核心技术,由于计算机无法理解人类的语言,词嵌入手段将词语转换成计算机能够理解的向量形式,同时也克服了传统方法中词语表示的稀疏性和高维度问题。
1.1定义
词嵌入是将自然语言中的词语映射为实数向量的过程。在这个过程中,每个词语都被转换成一个固定长度的稠密向量,这些向量构成了词语的向量空间模型。在该模型中,语义或语法性质相似的词语的向量会彼此靠近。
1.2目的
- 语义捕捉:通过学习词语之间的关系,词嵌入能够捕捉词语的同义性、反义性、上下位关系等语义信息。
- 降维: 相比于传统的词袋模型(Bag of Words)或One–hot编码,词嵌入生成的向量维度远低于词汇表的大小,有效缓解了维度灾难问题。
- 稠密表示: 词嵌入向量是稠密的,每个维度通常都有实际的数值,这与稀疏的One-hot向量形成对比。
2 发展历程
2.1 早期尝试
最早的词表示方法包括One-hot编码和基于统计的方法,如词共现矩阵(Co-occurrence Matrix)和点互信息(PMI)等,但这些方法存在维度高、无法有效捕捉词义等缺点。
2.2 神经网络方法
随着神经网络的发展,研究者开始使用浅层神经网络学习词嵌入,如Bengio等人提出的神经概率语言模型(NNLM)。
2.3 Word2Vec
2013年,Mikolov等人提出了Word2Vec,一个高效的词嵌入学习方法,包括Skip-gram和CBOW两种模型。Word2Vec的提出标志着词嵌入技术的一个重要发展阶段。
2.4 后续发展
随后,更多的模型和方法被提出,如GloVe(基于全局词共现统计的模型)和FastText(能够考虑词内字符级别信息的模型)。
3 实现方法
3.1 Word2Vec 模型
这是Google在2013年推出的NLP工具,它能够将所有词向量化,这样词与词之间就可以定量的去度量他们之间的关系。
相比于One-Hot方法,它的思路是通过训练将每个词都映射到较为短的词向量上,这些词向量就组成了向量空间。
Word2Vec具体的训练方法包含两个部分:CBOW模型和Skip-gram模型。
- CBOW模型:这是一个三层的神经网络,特点是输入已知的上下文,输出对当前单词的预测。
- Skip-Gram模型:与CBOW模型正好相反,可以由当前的词预测上下文,
3.2 N-gram 模型
它通过将文本分割成n个连续的项(可以是字母、音节或单词),来预测或识别语言中的一系列元素。这种模型的核心思想是,一个项的出现概率不仅取决于它自身,还取决于它前面的n-1个项。
N-gram定义
N-gram是一种基于统计的语言模型,用于建立一个项与其前面项序列之间的关联。在N-gram模型中,“N”指的是项的数量,表示在当前项出现的条件下,考虑前面多少个项。例如,一个2-gram(或bigram)模型会考虑当前项之前的1个项,3-gram(或trigram)模型会考虑当前项之前的2个项,以此类推。
N-gram类型
- Unigram(1-gram):模型中每个项都是独立的,不考虑任何上下文信息。
- Bigram(2-gram):模型中每个项的出现概率取决于它前面的一个项。
- Trigram(3-gram)及更高阶:模型中每个项的出现概率取决于它前面的两个或更多项。
代码演示 bigram
# 示例文本数据
text_data = [
"I feel happy because I am learning",
"learning is a joyful process",
"I am happy with my learning progress"
]
from collections import defaultdict, Counter
import numpy as np
def preprocess_text(text_data):
"""
简单的文本预处理:分词并小写化。
"""
return [sentence.lower().split() for sentence in text_data]
def build_bigram_model(text_data):
"""
构建Bigram模型,计算每个Bigram的概率。
"""
# 预处理文本数据
processed_text = preprocess_text(text_data)
# 构建Bigram计数
#可以创建一个自动初始化的字典,适合用来计数
bigram_counts = defaultdict(Counter)
for sentence in processed_text:
for i in range(len(sentence) - 1):
first_word, second_word = sentence[i], sentence[i + 1]
bigram_counts[first_word][second_word] += 1
# 转换计数为概率
bigram_model = defaultdict(dict)
for first_word, next_words in bigram_counts.items():
total_count = sum(next_words.values())
for second_word, count in next_words.items():
bigram_model[first_word][second_word] = count / total_count
return bigram_model
# 构建模型
bigram_model = build_bigram_model(text_data)
def predict_next_word(bigram_model, word):
"""
根据给定的单词,预测下一个单词的概率。
"""
next_words = bigram_model.get(word.lower(), {})
if not next_words:
return "No predictions available."
return next_words
# 示例预测
word = "I"
print(f"Given the word '{word}', the next word probabilities are:")
print(predict_next_word(bigram_model, word))
print('so the following word is ',end='')
max_key = max(predict_next_word(bigram_model, word), key=lambda k: predict_next_word(bigram_model, word)[k]) #获取最大值所对应的键
print(f'"{max_key}"')
3.3 GloVe模型
Word2Vec模型仅仅只能考虑于该词相临近窗口的局部信息,没有考虑到窗口外的其他信息,而glove模型利用共现矩阵考虑了全局信息
GloVe工作原理
该模型的核心思想是基于一个假设:单词的共现概率(两个单词同时出现的概率)能够反应单词之间的语义关系。模型训练主要包含以下几个步骤:
- 构建共现矩阵:首先,对一个大型语料库进行遍历,构建一个共现矩阵 X X X,矩阵中的元素 X i j X_{ij} Xij代表单词 i i i和 j j j在特定大小的上下文窗口内共同出现的次数。
- 定义目标函数:GloVe模型的目标是让单词向量的点积等于它们共现概率的对数值。具体来说,定义损失函数为所有单词对的权重函数 f ( X i j ) f(X_{ij}) f(Xij)与单词向量点积与 log ( X i j ) \log(X_{ij}) log(Xij)之间差的平方和,通过最小化这个损失函数来训练词向量。
- 权重函数 f ( X i j ) f(X_{ij}) f(Xij):为了解决稀疏性和频率差异大的问题,GloVe模型引入了一个权重函数 f ( X i j ) f(X_{ij}) f(Xij),它赋予低频单词对较高的权重,而对于高频的单词对则通过一个截断操作来限制它们的权重。
- 优化:通过梯度下降等优化算法调整每个单词的向量,以最小化上述损失函数。
3.4 TF-IDF 模型
当目标文本经过文本清洗和停用词的去除后,一般可以认为剩下的均为有着目标含义的词。下面如果需要对其特征进行更进一步的提取,那么提取的应该是那些能代表文章的元素,包括词、短语、句子、标点以及其他信息的词。从词的角度考虑,需要提取对文章表达贡献度大的词。有些对于文章意义不大的词相反会影响模型的性能,因此需要有一种方法来评估文章中词句的重要性。
TF-IDF模型基本原理
- TF (Term Frequency):词频,表示词语在文档中出现的频率。这反映了词语在文档中的重要性,但未考虑词语在语料库中的分布情况。
- IDF (Inverse Document Frequency):逆文档频率,用来衡量一个词语是否具有很好的类别区分能力。计算方式是语料库中的文档总数除以包含该词语之文档的数目,再将得到的商取对数。
TF-IDF=TF×IDF
3.5 TextRank算法
该算法的核心思想是来自著名的网页排名算啊PageRank。
PageRank:
- 当一个网页被越多网页所链接时,其排名会越靠前
- 排名高的网页应具有更大的表决权,即当一个网页被排名高的网页所链接时,其重要性也会对应提高。
TextRank算法步骤:
- 把给定的文本T安装完整句子进行分割。
- 对于每个句子,进行分词和词性标注,并且过滤掉停用词,值保留指定词性的单词,如名词,动词,形容词等。
- 构建候选关键词图G=(V,E),其中V为节点集,每个词之间的相似度作为连接的边值。
- 根据以下公式,迭代传播各节点的权重,直至收敛。
W S ( V i ) = ( 1 − d ) + d × ∑ V j ∈ ln ( V i ) w j i ∑ V k ∈ Out ( V j ) w j k W S ( V j ) W S\left(V_{i}\right)=(1-d)+d \times \sum_{V_{j} \in \ln \left(V_{i}\right)} \frac{w_{j i}}{\sum_{V_{k} \in \operatorname{Out}\left(V_{j}\right)} w_{j k}} W S\left(V_{j}\right) WS(Vi)=(1−d)+d×Vj∈ln(Vi)∑∑Vk∈Out(Vj)wjkwjiWS(Vj)
对节点按权重进倒排序,作为安重要程度排序的关键词
算法实现
#导入需要的包
import numpy as np
import pandas as pd
import nltk# 自然语言处理包
import re
#需要下载安装punkt模块
#nltk.download('punkt')
#获得数据集 链接:https://s3-ap-south-1.amazonaws.com/av-blog-media/wp-content/uploads/2018/10/tennis_articles_v4.csv
df=pd.read_csv(r".\tennis_articles_v4.csv")
#选择对所有文章进行内容摘要
from nltk.tokenize import sent_tokenize
sentences=[]
for s in df['article_text']:
#使用sent_tokenize 函数对文本分割成单个句子
sentences.append(sent_tokenize(s))
print(sentences[:5])
sentences=[y for x in sentences for y in x] #平整list,多维转低维度
#获得embeddding
'''
我们将使用预训练好的Wikipedia 2014 + Gigaword 5 (补充链接)GloVe向量,文件大小是822 MB。
GloVe词向量下载链接:
https://nlp.stanford.edu/data/glove.6B.zip
'''
word_embeddings={}
f=open(r".\glove.6B.100d.txt",encoding='utf-8')
for line in f:
values=line.split()
word=values[0]
coefs=np.asarray(values[1:],dtype='float32')
word_embeddings[word]=coefs
f.close()
#文本预处理
#移除标点符号、数字、特殊字符
clean_sentences=pd.Series(sentences).str.replace('[^a-zA-Z]'," ")
#统一成小字母
clean_sentences=[s.lower() for s in clean_sentences]
#除去常用的停用词如 is am of in 等,需要下载停用词
#nltk.download('stopwords')
from nltk.corpus import stopwords
stop_words=stopwords.words('english')
#定义移除数据集中的停用词的函数
def remove_stopwords(sen):
sen_new=' '.join([i for i in sen if i not in stop_words])
return sen_new
#移除停用词
clean_sentences=[remove_stopwords(r.split()) for r in clean_sentences]
#利用GloVe词向量为数据集生成特征向量
sentence_vectors=[]
for i in clean_sentences:
if len(i)!=0:
v=sum([word_embeddings.get(w,np.zeros((100,))) for w in i.split()])/(len(i.split())+0.001)
#防止分母为0
else:
v=np.zeros((100,))
sentence_vectors.append(v)
#利用余弦相似度来计算两个句子之间的相似度
from sklearn.metrics.pairwise import cosine_similarity
for i in range(len(sentences)):
for j in range(len(sentences)):
if i != j:
sim_mat[i][j]=cosine_similarity(sentence_vectors[i].reshape(1,100),sentence_vectors[j].reshape(1,100))[0,0]
#应用PageRank算法
import networkx as nx
#先转换成图结构
nx_graph=nx.from_numpy_array(sim_mat)
scores=nx.pagerank(nx_graph)
#摘要提取
ranked_sentences=sorted(((scores[i],s) for i,s in enumerate(sentences)),reverse=True)
for i in range(10):
print(ranked_sentences[i][1])