Bootstrap

NLP知识点

基本概念

        自然语言处理(Natural Language Processing,简称NLP)是人工智能和语言学领域的一个分支,它涉及到计算机和人类(自然)语言之间的相互作用。它的主要目标是让计算机能够理解、解释和生成人 类语言的数据。NLP结合了计算机科学、人工智能和语言学的技术和理论,旨在填补人与机器之间的交 流隔阂。

        总结:人类语言与计算机语言之间的互通

相关的概念

1.语言(Language):是人类用于沟通的一种结构化系统,可以包括声音、书写符号或手势。

2.自然语言(Natural Language):是指自然进化中通过使用和重复,无需有意计划或预谋而形成的 语言。

3.计算机语言学(Computational Linguistics):是语言学计算机科学之间的跨学科领域,主要包括

  •  计算机辅助语言学(Computer-aided Linguistics):利用计算机研究语言的学科,主要为语言学家所实践。
  • 自然语言处理(NLP):使计算机能够解决以自然语言表达的数据问题的技术,主要由工程师和计算机科学家实践

研究范畴

        研究范围广泛,包括但不限于语言理解(让计算机理解输入的语言)、语言生成(让计算机生成人类可以理解的语言)、机器翻译(将一种语言翻译成另一种语言)、情感分析(分析文本中的情绪 倾向)、语音识别和语音合成等。

中文处理差异

        在中文环境下,自然语言处理的定义和应用也与英文环境相似,但需要考虑中文的特殊性,如中文分 词、中文语法和语义分析等,因为中文与英文在语言结构上有很大的不同,这对NLP技术的实现提出了特殊的挑战。自然语言处理使计算机不仅能够理解和解析人类的语言,还能在一定程度上模仿人类的语言使用方式,进行有效的沟通和信息交换。

NLP可选性

  • 自然语言处理(NLP)就是在机器语言和人类语言之间沟通的桥梁,以实现人机交流的目的。
  • NLP 就是让机器学会处理我们的语言!

NLP的应用方向

自然语言理解

典型的自然语言理解(NLU)任务包括

  • 情感分析:对给定的文本输入,在给定的选项范围内分析文本的情绪是正面还是负面;
  • 文本分类:对给定的文本输入,在给定的选项范围内对文本进行二分类或多分类;
  • 信息检索:搜索引擎依托于多种技术,如网络爬虫技术、检索排序技术、网页处理技术、大数据处 理技术、自然语言处理技术等,为信息检索用户提供快速、高相关性的信息服务;
  • 抽取式阅读理解:对给定的文本输入,用文本中的内容回答问题;
  • 语义匹配:对给定的两个文本输入,判断是否相似;
  • 自然语言推理:对给定的两个文本输入,判断是蕴涵、矛盾还是无关;
  • 命名实体识别:对给定的文本输入,返回含有命名实体及其对应标签的映射,例如{'糖尿病':'疾 病'};
  • 文本摘要:对给定的文本输入,用文本中的内容对文本进行摘要

自然语言转换

自然语言转换(NLT)任务包括但不限于

  •  机器翻译:将一种自然语言转换为另一种自然语言,包括从源语言到目标语言的文本或语音的转 换; 机器学习模型目前常用seq2seq模型来实现,由EncoderDecoder组成。

  • 非抽取式阅读理解:接受给定文本的输入,能够理解自然语言问题,并回答问题;
  • 文本风格转换:将文本从一种风格转换为另一种风格,如将正式文本转换为非正式文本;
  • 语音识别:将人类的语音转换为文本,用于语音指令、口述文本、会议记录等。 
  • 意图改写:对给定的文本输入,将原始文本中的意图或核心信息重新表述,以不同的词汇和句式表 达相同的意思,同时保持原意的准确性和完整性;

自然语言生成

自然语言生成(NLG)任务则更为灵活了。

  • 文本生成:根据给定的上下文或提示,自动生成文本,如自动写作、诗歌创作、故事生成等。https://github.com/yanqiangmiffy/char-rnn-writer

  • 语音合成:将文本转换为听起来自然的语音,用于有声书、导航系统、虚拟助手等。
  • 聊天机器人:能够与人类实现多轮对话的聊天助手;
  • 文本到知识:从文本中提取知识,构建知识图谱或语义网络;
  • 语义解析:将自然语言表达转换为形式化的逻辑表示,用于命令解析、查询理解等。
应用方向
  • 序列到序列(Sequence-to-Sequence, Seq2Seq)问题是指模型将一个输入序列转换为一个输出序列 的任务。这类问题可以同步或异步进行,具体取决于输入序列和输出序列的处理方式。

同步序列到序列(Synchronous Seq2Seq)

  • 特点:输入序列和输出序列是严格对齐的,即在处理一个输入序列的元素时,模型会即时产生相应 的输出序列的元素。
  • 典型应用:通常应用在那些输入和输出之间存在一对一对齐关系的任务中。例如,逐词对齐、词性 标注(POS tagging)、命名实体识别(NER)。
  • 优点:处理速度快,因为输入和输出同步生成,不需要等待整个输入处理完毕。
  • 缺点:要求输入和输出严格对齐,不适合处理输入输出之间存在较复杂映射关系的任务。

异步序列到序列(Asynchronous Seq2Seq)

  • 特点:输入序列和输出序列之间不需要严格对齐,模型可以在处理完整个输入序列后,再开始生成 输出序列。大多数的Seq2Seq模型都是异步的。
  • 典型应用:用于需要对整个输入序列进行全局上下文理解后,才能生成输出的任务。例如,机器翻 译(句子级别)、文本摘要生成、问答系统等。经典的Seq2Seq模型,如基于RNN或Transformer 的模型,通常都属于异步类型。
  • 优点:模型可以利用输入序列的全局上下文信息进行更准确的输出生成。
  • 缺点:生成速度较慢,因为需要等待整个输入序列处理完毕后才开始生成输出。

NLP基础概念

1.些NLP中常用的概念名词

  • 词表/词库(Vocabulary):文本数据集中出现的所有单词的集合。
  • 语料库(Corpus):用于NLP任务的文本数据集合,可以是大规模的书籍、文章、网页等。
  • 词嵌入(Word Embedding):将单词映射到低维连续向量空间的技术,用于捕捉单词的语义 和语法信息。
  • 停用词(Stop Words):在文本处理中被忽略的常见单词,如"a"、"the"、"is"等,它们通常对 文本的意义贡献较 小。
  • 分词(Tokenization):将文本分割成一个个单词或标记的过程,为后续处理提供基本的单位。 
  • 词频(Term Frequency):在给定文档中,某个单词出现的次数。
  • 逆文档频率(Inverse Document Frequency):用于衡量一个单词在整个语料库中的重要性, 是将词频取倒数并取 对数的值。
  • TF-IDF(Term Frequency-Inverse Document Frequency):一种常用的文本特征表示方法, 综合考虑了词频和逆文档频率。
  • 词袋模型(Bag of Words):将文本表示为一个单词的集合,忽略了单词的顺序和语法结构。
  • N-gram:连续的N个单词构成的序列,用于捕捉文本中的局部特征和上下文信息。
  • 序列:指的是一个按顺序排列的元素集合。这些元素可以是字符、单词、句子,甚至更抽象的 结构。序列的每个元素都有特定的顺序和位置,这意味着它们不能随意重排,否则会影响其意义或功能。

2.序列的常见类型

字符序列

  • 一个字符串就是一个字符序列,每个字符按顺序排列。
  • 例子: "hello" 是一个由 h、e、l、l、o 组成的字符序列。

单词序列

  • 一句话可以看作是一个单词序列,每个单词按照一定顺序排列。
  • 例子: "I love NLP" 是一个由 I、love 、 NLP 组成的单词序列。

3. 时序数据

  • 在时间序列中,元素是按时间顺序排列的,常用于预测问题。
  • 例子:股票价格数据可以看作是随时间变化的数值序列。

4. 语音序列

  • 在语音处理任务中,语音信号可以被分解为按时间顺序排列的帧序列(特征向量序列)。

5. 其他序列

  • 序列还可以表示一些更抽象的结构,比如DNA序列(由碱基组成的序列)、事件序列等

NLP的基本流程(概念梳理)

        中文NLP的基本流程和英文相比有一些特殊性,主要表现在文本预处理环节。首先,中文文本没有像 英文单词那样用空格隔开,因此不能像英文一样直接用最简单的空格和标点符号完成分词,一般需要用分词 算法完成分词。其次,中文的编码不是utf-8,而是Unicode,因此在预处理的时候,有编码处理的问题。中文NLP的基本流程由语料获取、语料预处理、文本向量化、模型构建、模型训练和模型评价等六 部分组成。

语料获取

在进行NLP之前,人们需要得到文本语料。文本语料的获取一般有以下几种方法。

  • 利用已经建好的数据集或第三方语料库,这样可以省去很多处理成本。
  • 获取网络数据。很多时候要解决的是某种特定领域的应用,仅靠开放语料库无法满足需求,这时就需 要通过爬虫技术获取需要的信息。
  • 与第三方合作获取数据。通过购买的方式获取部分需求文本数据。

语料预处理

获取语料后还需要对语料进行预处理,常见的语料预处理如下。

  • 去除数据中非文本内容。大多数情况下,获取的文本数据中存在很多无用的内容,如爬取的一些 HTML代码、CSS标签和不需要的标点符号等,这些都需要分步骤去除。少量非文本内容可以直接用 Python的正则表达式删除,复杂的非文本内容可以通过 Python的 Beautiful Soup库去除。
  • 中文分词。常用的中文分词软件有很多,如jieba、FoolNLTK、HanLP、THULAC、NLPIR、LTP 等。其中jieba是使用 Python语言编写的,其安装方法很简单,使用“pip install jieba”命令即可完成安 装。
  • 词性标注。词性标注指给词语打上词类标签,如名词、动词、形容词等,常用的词性标注方法有基于规则的算法、基于统计的算法等。
  • 去停用词。停用词就是句子中没必要存在的词,去掉停用词后对理解整个句子的语义没有影响。中 文文本中存在大量的虚词、代词或者没有特定含义的动词、名词,在文本分析的时候需要去掉。

文本向量化(特征工程)

        文本数据经过预处理去除数据中非文本内容、中文分词、词性标注和去停用词后,基本上是干净的文 本了。但此时还是无法直接将文本用于任务计算,需要通过某些处理手段,预先将文本转化为特征向 量。一般可以调用一些模型来对文本进行处理,常用的模型有词袋模型(Bag of Words Model)、独热 表示、TF-IDF 表示、n元语法(n-gram)模型和 Word2Vec模型等。

模型构建

        文本向量化后,根据文本分析的需求选择合适的模型进行模型构建,同类模型也需要多准备几个备选 用于效果对比。过于复杂的模型往往不是最优的选择,模型的复杂度模型训练时间呈正相关,模型复杂度越高,模型训练时间往往也越长,但结果的精度可能与简单的模型相差无几。

  • NLP中使用的模型包括机器学习模型深度学习模型两种。
  • 常用的机器学习模型有SVM、Naive Bayes、决策树、K-means 等。
  • 常用的深度学习模型有TextCNN、RNN、LSTM、GRM、Seq2Seq、transformer等。

模型训练

模型构建完成后,需要进行模型训练,其中包括模型微调等。训练时可先使用小批量数据进行试验, 这样可以避免直接使用大批量数据训练导致训练时间过长等问题。

在模型训练的过程中要注意两个问 题:

  • 一个为在训练集上表现很好,但在测试集上表现很差的过拟合问题;
  • 另一个为模型不能很好地拟合 数据的欠拟合问题。

同时;还要避免出现梯度消失梯度爆炸问题。

仅训练一次的模型往往无法达到理想的精度与效果,还需要进行模型调优迭代,提升模型的性能。模型调优往往是一个复杂、冗长且枯燥的过程,需要多次对模型的参数做出修正;调优的同时需要权衡模型的精度与泛用性,在提高模型精度的同时还需要避免过拟合。

在现实生产与生活中,数据的分布会随着时间的推移而改变,有时甚至会变化得很急剧,这种现象称为分布漂移(Distribution Drift)

当一个模型随着时间的推移,在新的数据集中的评价不断下降时,就意味着这个模型无法适应新的数据的变化,此时模型需要进行重新训练

模型评价

模型训练完成后,还需要对模型的效果进行评价

模型的评价指标指主要有准确率(Accuracy)、精确率(Precision)、召回率、F1值、ROC曲线、AUC线等。

针对不同类型的模型,所用的评价指标往往也不同,例如分类模型常用的评价方法有准确率、精确率AUC曲线等。

同一种评价方法也往往适用于多种类型的模型。

在实际的生产环境中,模型性能评价的侧重点可能会不一样,不同的业务场景对模型的性能有不同的要求,如可能造成经济损失的预测结果会要求模型的精度更高

NLP中的特征工程

  • 特征:数据中抽取出来的对结果预测有用的信息
  • 特征工程:在自然语言处理(NLP)中,特征工程是指将文本数据转换为适合机器学习模型使用的数值表示过 程
  • 选择原因:文本是一种非结构化数据,机器学习模型无法直接处理,因此必须通过特征工程来提取有用的信息。
  • 目的:通过特征工程让机器学习到文本数据中的一些特征,比如词性、语法、相似度等。
  • 列如:英语学习英语词性的分类

  • 从向量角度进行观察:方向、大小的相似性(程度)

词向量

词向量(Word Vector)是对词语义含义数值向量表示,包括字面意义隐含意义

词向量可以捕捉到词的内涵,将这些含义结合起来构成一个稠密浮点数向量,这个稠密向量支持查询逻辑推理

词向量也称为词嵌入,其英文均可用 Word Embedding,是自然语言处理中的一组语言建模和特征学习技术的统称,其中来自词表的单词或短语被映射为实数的向量,这些向量能够体现词语之间的语义关 系

从概念上讲,它涉及从每个单词多维的空间到具有更低维度的连续向量空间数学嵌入

当用作底层输入表示时,单词和短语嵌入已经被证明可以提高 NLP 任务的性能,例如文本分类、命名实体识别、关系抽取等。 词嵌入实际上是一类技术,单个词在预定义的向量空间中被表示为实数向量,每个单词都映射到一个向量。

举个例子,比如在一个文本中包含“猫”“狗”“爱情”等若干单词,而这若干单词映射到向量空间中, “猫”对应的向量为(0.1 0.2 0.3),“狗”对应的向量为(0.2 0.2 0.4),“爱情”对应的映射为(-0.4 -0.5 -0.2)(本数据仅为示意)。

像这种将文本X{x1,x2,x3,x4,x5……xn}映射到多维向量空间 Y{y1,y2,y3,y4,y5……yn },这个映射的过程就叫做词嵌入

之所以希望把每个单词都变成一个向量,目的还是为了方便计算,比如“猫”,“狗”,“爱情”三个词。对于我们人而言,我们可以知道“猫”和“狗”表示的都是动物,而“爱情”是表示的一种情感,但是对于机器而言,这三个词都是用0,1表示成二进制的字符串而已,无法对其进行计算。

而通过词嵌入这种方式将单词 转变为词向量,机器便可对单词进行计算,通过计算不同词向量之间夹角余弦值cosine而得出单词之间 的相似性

此外,词嵌入还可以做类比,比如:v(“国王”)-v(“男人”)+v(“女人”)≈v(“女王”),v(“中国”)+v(“首 都”)≈v(“北京”),当然还可以进行算法推理。有了这些运算,机器也可以像人一样“理解”词汇的意思了。

传统NLP中的特征工程

独热编码 one - hot

        独热编码(One-Hot Encoding) 是一种常见的特征表示方法,通常用于将离散的类别型数据转换为数值型表示,以便输入到机器学习模型中。它的特点是将每个类别表示为一个向量,在该向量中,只有 一个元素为1,其余元素全部为0。

One-Hot Encoding 的工作原理

去重后的词数量决定了矩阵的维度,在对应向量维度上,如果词存在,对应向量的编码为1,反之为0

在NLP当中

  • Time flies like an arrow.
  • Fruit flies like a banana.
  • 构成词库{time, fruit, flies, like, a, an, arrow, banana}
  • banana的one-hot表示就是:[0,0,0,0,0,0,0,1],"like a banana” 的one-hot表示就是:[0, 0,0,1,1,0,0,1]。

词频-逆文档频率(TF-IDF)

词频

        一个词语在文档中出现的次数除以文档中的总词数。这确实如你所说,分母是文档中的总词数,不论词语是否重复。这样计算出来的值反映了某个词在一个特定文档中的相对重要性。

注意:在计算词频(TF)时,分母是文档中的总词数,而不考虑每个词是否重复。这意味着无论词是否 重复,分母始终是文档中所有词的数量总和

逆文档频率(Inverse Document Frequency, IDF)

        逆文档频率用来衡量一个词在整个文档集合(语料库)中的重要性。它的目的是降低那些在很多文档 中频繁出现的词的权重,例如“the”、“is”这种常见词,或者低频罕tetrafluoroethylene(四氟乙烯)词。 

其中,D 表示文档集合,t 是要计算的词。+1 是为了避免分母为 0 的情况。 

TF-IDF 计算

        “将文本中每个单词的出现次数除以该单词在所有文档中的出现次数”,实际上是在描述一种结合了 TF 和 IDF 的方法,通常称为 TF-IDF(Term Frequency-Inverse Document Frequency)。这是信息检索和文本挖掘中非常流行的一种加权技术,用于评估一个词对于一个文档或者一个语料库中的其中一份文档的重要性。

最终,TF-IDF 是将 TF 和 IDF 相乘得出的结果,公式如下

通过这个方法,一个词在特定文档中出现的频率越高(TF高),并且在整个语料库中出现得越少(IDF 高),它的 TF-IDF 值就越高。这样可以使模型更加关注那些在某篇文档中特别重要但不常见的词。

结论
  • 文档频率和样本语义贡献程度呈反相关
  • 文档频率和逆文档频率呈反相关
  • 逆文档频率和样本语义贡献度呈正相关

n-grams

        n-grams特征工程中的一种技术,它通过将文本中的连续 n 个词(或字符)组合起来,形成一个短语来捕捉文本中的局部上下文信息。n 可以为 1、2、3 等,具体取决于希望捕捉的上下文范围。

  • 1-gram(Unigram):每个单独的词作为一个单位。例如,"I love NLP" 的 1-gram 是 "love", "NLP"]。
  • 2-grams(Bigram):相邻的两个词组合成一个短语。例如,"I love NLP" 的 2-grams 是 love", "love NLP"]。
  • 3-grams(Trigram):相邻的三个词组合成一个短语。例如,"I love NLP" 的 3-grams 是 love NLP"]。
n-grams 的作用

        使用 n-grams 可以捕捉词之间的局部上下文关系。例如,1-gram 只关心词的独立出现频率,而 bigram 和 trigram 能捕捉到词之间的顺序关系。例如,bigram "love NLP" 表示词 "love" 和 "NLP" 是一起出现的,这种信息在建模中会比仅仅知道 "love" 和 "NLP" 出现频率更有价值。        

        将 n-grams 与 TF-IDF 相结合是文本特征工程中非常常见的做法,它不仅能够捕捉词与词之间的局部关系,还能通过 TF-IDF 来衡量这些短语在整个语料库中的重要性。结合的过程基本上是先生成 n-grams,然后对这些 n-grams 计算 TF-IDF 权重。

结合 n-grams 与 TF-IDF 的步骤
  • 生成 n-grams:首先从文本中生成 n-grams(n 可以是 1, 2, 3 等)。这些 n-grams 就像是词的组合,通常使用 CountVectorizer 或类似的工具生成。
  •  计算词频 (TF):统计每个 n-gram 在文本中出现的频率。
  • 计算逆文档频率 (IDF):计算 n-gram 在所有文档中出现的频率,稀有的 n-grams 会得到较高的权重,而常见的 n-grams 权重较低。
  • 计算 TF-IDF:将每个 n-gram 的 TF 和 IDF 相乘,得到 TF-IDF 权重,表示该 n-gram 对特定文本的重要性。

注意:当使用 2-grams 时, I love 和 love NLP 被看作是两个单独的特征,总共有两个特征(总特 征数 = 2)。

深度学习中NLP的特征输入

        深度学习使用分布式单词表示技术(也称词嵌入表示),通过查看所使用的单词的周围单词(即上下文) 来学习单词表示。这种表示方式将词表示为一个粘稠的序列,在保留词上下文信息同时,避免维度过大导致的计算困难。

稠密编码(特征嵌入)

        稠密编码(Dense Encoding),在机器学习和深度学习中,通常指的是将离散或高维稀疏数据转化为低维的连续、密集向量表示。这种编码方式在特征嵌入(Feature Embedding)中尤为常见。

        稠密向量表示:不再以one-hot中的一维来表示各个特征,而是把每个核心特征(词,词性,位置等) 都嵌入到d维空间中,并用空间中的一个向量表示。通常空间维度d远小于每个特征的样本数(40000的词表,100/200维向量)。嵌入的向量(每个核心特征的向量表示)作为网络参数与神经网络中的其他参数一 起被训练。

特征嵌入(Feature Embedding)

        特征嵌入,也成为词嵌入,是稠密编码的一种表现形式,目的是将离散的类别、对象或其他类型的特征映射到一个连续的向量空间。通过这种方式,嵌入后的向量可以捕捉不同特征之间的语义关系,并且便于在后续的机器学习模型中使用。

特点

  • 低维度:相比稀疏表示(如独热编码),稠密编码的维度更低,能够减少计算和存储成本。
  • 语义相似性:嵌入向量之间的距离(如欧氏距离或余弦相似度)可以表示这些对象之间的语义相似性。
  • 可微学习:嵌入表示通常通过神经网络进行学习,并且通过反向传播算法进行优化。

词嵌入算法

Embedding Layer

        由于缺乏更好的名称,Embedding Layer是与特定自然语言处理上的神经网络模型联合学习的单词嵌入。该嵌入方法将清理好的文本中的单词进行one hot编码(独热编码)向量空间的大小或维度被指定为模型的一部分,例如50、100或200维。向量以小的随机数进行初始化。        

        Embedding Layer用于神经网络的前端,并采用反向传播算法进行监督

用法
  • 比如我们的目标是希望神经网络发现如下这样的规律:已知一句话的前几个字,预测下一个字是什么,于是有了NNLM 语言模型搭建的网络结构图

实施
  • 先用最简单的方法来表示每个词,one-hot 表示为

  • 加入矩阵映射,弥补one-hot编码的缺陷

  • One-hot表示法这时候就作为一个索引字典了,通过映射矩阵对应到具体的词向量。

词嵌入层的使用

1.词嵌入层首先会根据输入的词的数量构建一个词向量矩阵,例如: 我们有 100 个词,每个词希望转换 成 128 维度的向量,那么构建的矩阵形状即为: 100*128,输入的每个词都对应了一个该矩阵中的一个 向量。

2。在 PyTorch 中,我们可以使用 nn.Embedding 词嵌入层来实现输入词的向量化,流程为

  • 先将语料进行分词,构建词与索引映射,我们可以把这个映射叫做词表,词表中每个词都对应了 一个唯一的索引;
  • 然后使用 nn.Embedding 构建词嵌入矩阵,词索引对应的向量即为该词对应的数值化后的向量表示。

3.例如,我们的文本数据为: "北京冬奥的进度条已经过半,不少外国运动员在完成自己的比赛后踏上归 途。",

使用词嵌入层将其进行转换为向量表示,实现流程

  • 将文本进行分词;
  • 根据词构建词表;
  • 使用嵌入层将文本转换为向量表示。

nn.Embedding 对象构建时,最主要有两个参数

  • num_embeddings 表示词的数量
  • embedding_dim 表示用多少维的向量来表示每个词

API

nn.Embedding(num_embeddings=10, embedding_dim=4)

代码实现

  • Embedding类的本质是一个大小为 (num_embeddings, embedding_dim)的矩阵,每一行是某个词汇的嵌入向量。
  • 通过索引,可以高效地从这个矩阵中提取对应词汇的向量表示,因为 nn.Embedding 在内部通过 索引直接查找矩阵中的行,这种操作非常快速且方便。
import jieba
import torch.nn as nn
import torch
t = "北京冬奥的进度条已经过半,不少外国运动员在完成自己的比赛后踏上归途。"
# 使用jiba分词,返回的是列表
# ['北京', '冬奥', '的', '进度条', '已经', '过半', ',', '不少', '外国', 
# '运动员', '在', '完成', '自己', '的', '比赛', '后', '踏上', '归途', '。']  
l = jieba.lcut(t)
# 构建此表
index_to_word = {}
word_to_index = {}
# 对文本数据进行去重,set()返回的是列表,<class 'set'>
unique_words = set(l) # 18
# 填充词表内容
for i, word in enumerate(unique_words):
    if word not in word_to_index:
        word_to_index[word] = i
        index_to_word[i] = word
# word_to_index: {'已经': 0, '不少': 1, '比赛': 2, ',': 3, '北京': 4, 
# '过半': 5, '外国': 6, '的': 7, '在': 8, '完成': 9, '。': 10, 
# '运动员': 11, '踏上': 12, '进度条': 13, '归途': 14, '后': 15, 
# '冬奥': 16, '自己': 17}
# index_to_word: {0: '已经', 1: '不少', 2: '比赛', 3: ',', 4: '北京',
#  5: '过半', 6: '外国', 7: '的', 8: '在', 9: '完成', 10: '。', 
# 11: '运动员', 12: ' headed', 13: 'progress', 14: 'bar',
#  15: 'has', 16: 'Olympics', 17: 'its'}
# 构建Embedding对象
# Embedding(18, 4)
embedding = nn.Embedding(len(word_to_index), 4)

for i in l:
    # 获取词在词表中对应索引
    index = word_to_index[i]
    # 获取词对应的Embedding向量(词嵌入向量)
    word_vec = embedding(torch.tensor(index))

"""
tensor([ 0.8825, -0.6389, -0.2894,  1.5997], grad_fn=<EmbeddingBackward0>) 
tensor([ 0.3891, -0.5539, -0.8674,  0.6056], grad_fn=<EmbeddingBackward0>) 
tensor([-0.0467,  0.5574, -0.8281,  1.4655], grad_fn=<EmbeddingBackward0>) 
tensor([ 1.7195, -0.1542,  0.1673, -0.1536], grad_fn=<EmbeddingBackward0>) 
tensor([-1.1996, -1.3826, -1.6861,  0.3747], grad_fn=<EmbeddingBackward0>) 
tensor([-1.4873, -0.6304,  0.4751, -1.8516], grad_fn=<EmbeddingBackward0>) 
tensor([ 2.3931, -0.2807, -1.5455, -0.1739], grad_fn=<EmbeddingBackward0>) 
tensor([ 0.4386, -0.0943, -1.0730, -1.2707], grad_fn=<EmbeddingBackward0>) 
tensor([ 0.0422, -0.7731, -0.7996,  1.4030], grad_fn=<EmbeddingBackward0>) 
tensor([ 0.6415,  1.0944, -0.6016,  1.4144], grad_fn=<EmbeddingBackward0>) 
tensor([ 0.3816, -0.4310,  0.0394, -2.8283], grad_fn=<EmbeddingBackward0>) 
tensor([-0.6128, -0.3577, -0.1243, -0.9008], grad_fn=<EmbeddingBackward0>) 
tensor([-0.5590,  0.4077, -0.7216,  0.4109], grad_fn=<EmbeddingBackward0>)
tensor([-0.0467,  0.5574, -0.8281,  1.4655], grad_fn=<EmbeddingBackward0>) 
tensor([-0.2985,  0.3905,  0.7987,  0.2177], grad_fn=<EmbeddingBackward0>) 
tensor([ 0.2912,  0.9763,  1.2942, -0.5256], grad_fn=<EmbeddingBackward0>) 
tensor([-1.3085, -0.7446,  0.7673,  1.4198], grad_fn=<EmbeddingBackward0>) 
tensor([-0.9636,  0.9365,  0.4900,  0.0753], grad_fn=<EmbeddingBackward0>) 
tensor([-0.6429,  0.0599,  1.6040, -0.5258], grad_fn=<EmbeddingBackward0>) 
"""
案列
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim

sentences = ["i like dog", "i love coffee", "i hate milk", "i do nlp"]
# 将所有句子合并并分割成单词列表
# i like dog i love coffee i hate milk i do nlp
# print(' '.join(sentences))
# ['i', 'like', 'dog', 'i', 'love', 'coffee', 'i', 'hate', 'milk', 'i', 'do', 'nlp']
word_list = ' '.join(sentences).split()  
# 去重操作,set类型
# {'love', 'like', 'dog', 'i', 'hate', 'milk', 'coffee', 'nlp', 'do'}
word_list = set(word_list)
# 使用列表推导式生成所需的列表
# {'dog': 0, 'hate': 1, 'coffee': 2, 'i': 3, 'like': 4, 'do': 5, 'love': 6, 'milk': 7, 'nlp': 8}
word_dict = {w: i for i, w in enumerate(word_list)}
# {0: 'dog', 1: 'hate', 2: 'coffee', 3: 'i', 4: 'like', 5: 'do', 6: 'love', 7: 'milk', 8: 'nlp'}
index_dict = {i: w for i, w in enumerate(word_list)}


# 语料库的一个大小,这个文章的字(词)的数量(去重后的词)
n_class = len(word_dict) # 9
# 特征维度
n_emb = 2 # 每个字(词)会生成的字(词)向量维度,也是一个初始的权重
# 输入的字(词)的数量
n_step = 2
# 隐藏层的输出神经元数量
n_hidden = 2

# input_batch:获取输入字在语料库里的索引
# target_batch:输出目标字在语料库里的索引
def make_batch(sentences):
    input_batch = []
    target_batch = []
    # 遍历句子
    for sen in sentences:
        # 切分,返回列表
        word = sen.split()
        # 取出前n-1个词(每个句子的前两个单词)的下标,返回列表
        input = [word_dict[n] for n in word[:-1]]
        # 取出最后一个词(每个句子的最后一个单词)的下标,返回列表
        target = word_dict[word[-1]]
        # 添加到列表中
        input_batch.append(input)
        target_batch.append(target)
        # python中的列表是没有shape这个属性的
    # 转为tensor张量
    """
    tensor([[6, 5],
        [6, 8],
        [6, 7],
        [6, 4]])
    """
    input_batch = torch.LongTensor(input_batch)
    """
    tensor([4, 1, 2, 3])
    """
    target_batch = torch.LongTensor(target_batch)       
    return input_batch, target_batch

class NNLM(nn.Module):
    def __init__(self, n_step, n_hidden, n_class, n_emb):
        super(NNLM, self).__init__()
        # 生成嵌入向量,输入的字词数量n_class, 词向量维度n_emb, 获取到嵌入向量
        self.embed = nn.Embedding(n_class, n_emb)
        # 第二层输入的字词数量n_step * n_emb,如果有批次,还需要乘以批次, 输出隐藏层维度n_hidden
        # 全连接要输入神经元的对应完整的个数,最终一个神经元输出n_hidden个神经元
        self.W1 = nn.Linear(n_step * n_emb, n_hidden)
        # 隐藏层维度n_hidden, 输出字词数量n_class,词向量的数量决定了输出结果的分类可能数量
        self.W2 = nn.Linear(n_hidden, n_class)


    def forward(self, x):
         # torch.Size([4, 2]) 预料库的数量和每条语料库输入的词数量
        #  print(x.size())
         # torch.Size([4, 2, 2]),每个词的维度是n_emb(2)
         x = self.embed(x)  # 通过嵌入层得到嵌入向量,形状为 batch_size x n_step x m
         # torch.Size([4, 4])
         x = x.view(-1, x.size(1) * x.size(2))  # 将嵌入向量展平成二维,大小为 batch_size x (n_step * m)
         tanh = torch.tanh(self.W1(x))  # 通过第一层线性层,并使用tanh激活函数
         output = self.W2(tanh)  # 通过第二层线性层,得到分类输出
         return output  # 返回输出
    
model = NNLM(n_step, n_hidden, n_class, n_emb)

# 损失函数
criterion = nn.CrossEntropyLoss()
# 优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 数据
# input_batch [[7, 0], [7, 4], [7, 8], [7, 1]]
# target_batch [5, 3, 6, 2]
input_batch, target_batch = make_batch(sentences)
for epoch in range(5000):
    # 前向传播
    # torch.Size([4, 9])
    output = model(input_batch)
    # 计算损失 预测值output,理解重点是这个损失函数的计算方法
    # target_batch.squeeze(0) 删除第一维
    loss = criterion(output, target_batch)
    if (epoch + 1) % 1000 == 0:  # 每1000个epoch打印一次损失
        print(f'epoch: {epoch + 1:04d}, cost = {loss.item():.6f}')
    
    # 反向传播
    loss.backward()
    # 更新参数
    optimizer.step()
    # 每1000次迭代打印一次损失值
# 预测
"""
tensor([[8],
        [0],
        [5],
        [8]])
"""
predict = model(input_batch).max(1, keepdim=True)[1]
# 设置keepdim=True是为了保持输出张量的维度数不变,即原本的二维张量仍然是二维的。
# predict = model(input_batch).max(1)[1]
# tensor([2, 7, 3, 2])
# 去掉第一维
# tensor([8, 0, 5, 8]) squeeze() 函数会去掉张量中所有大小为 1 的维度。
print(predict.squeeze())
print([sen.split()[:2] for sen in sentences], '->', [index_dict[n.item()] for n 
in predict.squeeze()])

word2vec

word2vec是Google研究团队里的Tomas Mikolov等人于2013年的《Distributed Representations ofWords and Phrases and their Compositionality》以及后续的《Efficient Estimation of Word Representations in Vector Space》两篇文章中提出的一种高效训练词向量的模型,基本出发点是上下文相似的两个词,它们的词向量也应该相似,比如香蕉和梨在句子中可能经常出现在相同的上下文中, 因此这两个词的表示向量应该就比较相似。(理解上下文)

word2vec一般分为CBOW(Continuous Bag-of-Words)与 Skip-Gram 两种模型(二者结构十分相似)

  • CBOW:根据中心词周围的词来预测中心词,有negative sampleHuffman两种加速算法
  • Skip-Gram:根据中心词来预测周围词

Skip-gram 模型

Skip-gram 模型的优点是:由于它是基于目标单词来预测上下文单词的,因此它可以利用目标单词的语义和语法特征来预测上下文单词;模型能够生成更多的训练数据,因此可以更好地训练低频词汇的表 示;Skip-gram 模型在处理大规模语料库时效果比 CBOW 模型更好。

案例
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from tqdm import tqdm
from torch.autograd import Variable
import matplotlib.pyplot as plt
from gensim.models import Word2Vec
import nltk
# nltk.download('punkt_tab')  # 如果未安装nltk的punkt分词器,请先下载

# 定义数据类型为浮点数
dtype = torch.FloatTensor

# 语料库,包含训练模型的句子
sentences = ["i like dog", "i like cat", "i like animal",
            "dog cat animal", "apple cat dog like", "cat like fish",
            "dog like meat", "i like apple", "i hate apple",
            "i like movie book music apple", "dog like bark", "dog friend cat"]

# 将所有句子拼接为一个字符串并按空格分词
# ' '.join(sentences) 是一个用于字符串操作的方法。
# 这个表达式的作用是将列表 sentences 中的所有元素连接成一个新的字符串,
# 而这些元素之间会用空格 ' ' 分隔。
word_sequence = ' '.join(sentences).split()
 # 获取词汇表中的所有唯一词,去重后随机排列
word_list = list(set(word_sequence))
 # 创建词典,词汇表中的每个词都分配一个唯一的索引
word_dict = {w: i for i, w in enumerate(word_list)}
# 创建反向词典,将索引映射回对应的词
index_dict = {i: w for i, w in enumerate(word_list)}

# 创建跳字节模型的训练数据,根据一个词,输出他的前后一个词
skip_grams = [] # 存储训练数据

# range(1, len(word_sequence) - 1),因为是根据中心词来推测周围词,所以遍历的词周围需要有其他的词
for i in range(1, len(word_sequence) - 1):
    # 当前词对应的id索引
    target_idx = word_dict[word_sequence[i]]
    # 前一个词和后一个词的索引
    context_idx = [word_dict[word_sequence[i - 1]],
                   word_dict[word_sequence[i + 1]]]
    # 将当前词和前后两个词的索引组合成一个元组,并添加到skip_grams列表中
    for c in context_idx:
        skip_grams.append([c, [target_idx]])
"""
[[0, [2]], [8, [2]], [2, [8]], [0, [8]], [8, [0]], [2, [0]], 
[0, [2]], [3, [2]], [2, [3]], [0, [3]], [3, [0]], [2, [0]], 
[0, [2]], [12, [2]], [2, [12]], [8, [12]], [12, [8]], [3, [8]],
[8, [3]], [12, [3]], [3, [12]], [9, [12]], [12, [9]], [3, [9]], 
[9, [3]], [8, [3]], [3, [8]], [2, [8]], [8, [2]], [3, [2]],
[2, [3]], [2, [3]], [3, [2]], [13, [2]], [2, [13]], [8, [13]], 
[13, [8]], [2, [8]], [8, [2]], [4, [2]], [2, [4]], [0, [4]], [4, [0]], 
[2, [0]], [0, [2]], [9, [2]], [2, [9]], [0, [9]], [9, [0]], [6, [0]], 
[0, [6]], [9, [6]], [6, [9]], [0, [9]], [9, [0]], [2, [0]], [0, [2]], 
[10, [2]], [2, [10]], [5, [10]], [10, [5]], [1, [5]], [5, [1]], [9, [1]], 
[1, [9]], [8, [9]], [9, [8]], [2, [8]], [8, [2]], [7, [2]], [2, [7]], [8, [7]], 
[7, [8]], [11, [8]], [8, [11]], [3, [11]]]
"""
# print(skip_grams)

# 定义嵌入维度(嵌入向量的大小)为2
embedding_size = 2
# 词汇表大小
voc_size = len(word_list)
 # 每次训练的批量大小
batch_size = 5

 # 定义随机批量生成函数
def random_batch(data, size):
    random_inputs = []  # 输入批次
    random_labels = []  # 标签批次
    # 从数据中随机选择size个索引
    # np.random.choice()
        # 一个参数是要从中抽取元素的数组或整数(如果是整数,则会创建一个从0到该整数减一的范围)。
        # size 参数指定了输出样本的数量。
        # replace 参数是一个布尔值,表示是否允许有放回抽样。如果设置为 False,则意味着一旦某个元素被抽取后就不会再被考虑,从而确保不会重复选取同一个元素。
    random_index = np.random.choice(range(len(data)), size, replace=False)
    # [47 74  6 75 17]
    # print(random_index)
    # 根据随机索引生成输入和标签批次
    for i in random_index:
        # np.eye 是 NumPy 库中的一个函数,用于创建单位矩阵(identity matrix),即对角线上的元素为1,其他位置的元素为0。
        # voc_size 表示词汇表的大小,所以 np.eye(voc_size) 将创建一个形状为 (voc_size, voc_size) 的单位矩阵。
        # 目标词one-hot编码 voc_size词的数量 data[i][0]输入词的下标
        random_inputs.append(np.eye(voc_size)[data[i][0]])
        # 上下文词的索引作为标签 data[i][1]目标词的下标
        random_labels.append(data[i][1])
    """
    random_inputs:
    [array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.]), array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.]), array([0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0., 0., 0.]), array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.]), array([0., 0., 0., 0., 
0., 0., 0., 0., 0., 0., 0., 0., 0., 1.])]
    """
    """
    random_labels:
    [[10], [13], [11], [11], [6]]
    """
    return random_inputs, random_labels

 # 定义Word2Vec模型
class Word2Vec(nn.Module):
    def __init__(self):
        super(Word2Vec, self).__init__()
        # 定义词嵌入矩阵W,随机初始化,大小为(voc_size, embedding_size)
        # nn.Parameter 是一个特殊的 Tensor 子类,当它被分配为模块的属性时,
        # 它会被自动注册为模型的参数。也就是说,这个张量将会被视为模型的一部分,
        # 并且会在训练过程中通过反向传播算法更新其值。
        # 将随机初始化的张量包裹在 nn.Parameter 中,使其成为模型的一个可训练参数。
        self.W = nn.Parameter(torch.rand(voc_size, embedding_size)).type(dtype)
        # 定义上下文矩阵WT,随机初始化,大小为(embedding_size, voc_size)
        self.WT = nn.Parameter(torch.rand(embedding_size, voc_size)).type(dtype)
    # 前向传播
    def forward(self, x):
        """
        tensor([[0., 0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0.],
        [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1., 0., 0., 0.]])
        """
        # print(x)
        # 通过乘以嵌入矩阵W得到词向量
        weight_layer = torch.matmul(x, self.W)
        # torch.Size([5, 2])
        # print(weight_layer.shape)
        # 通过上下文矩阵WT得到输出
        output_layer = torch.matmul(weight_layer, self.WT)
        # torch.Size([5, 14])五条样本的输出,每个样本输出的可能性为word_dict的长度
        # print(output_layer.shape)
        return output_layer
# 创建模型实例
model = Word2Vec()

# 定义损失函数为交叉熵损失
criterion = nn.CrossEntropyLoss()
 # 使用Adam优化器
optimizer = optim.Adam(model.parameters(), lr=0.001)

for epoch in range(5000):
    # 获取第一批随机的输入和目标
    inputs, labels = random_batch(skip_grams, batch_size)
    # 转化为张量
    inputs = torch.FloatTensor(inputs)
    labels = torch.LongTensor(labels)
    """
    tensor([[3],
        [4],
        [4],
        [4],
        [7]]) tensor([3, 4, 4, 4, 7])
    """
    # print(labels, labels.squeeze(1))

    # 梯度清零
    optimizer.zero_grad()
    # 前向传播
    output = model(inputs)
    # 计算损失
    loss = criterion(output, labels.squeeze(1))
    # 每1000个epoch打印一次损失
    if (epoch + 1) % 1000 == 0:
        print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.6f}'.format(loss))
    # 反向传播
    loss.backward()
    # 更新参数
    optimizer.step()

# 可视化
for i, label in enumerate(word_list):
    # 获取模型参数  torch.Size([14, 2]) torch.Size([2, 14])
    W, WT = model.parameters()
    # 获取词嵌入的二维坐标
    x, y = float(W[i][0]), float(W[i][1])
    # 绘制散点图
    plt.scatter(x, y)
    # 标注词汇
    # label: 这是你希望显示在图表上的文本内容,即注释的具体文字信息。
    # xy=(x, y): 指定了注释所指向的数据点的位置。这里的x和y是该点在图表上的坐标值。

    plt.annotate(label, xy=(x, y), xytext=(5, 2), textcoords='offset points', ha='right', va='bottom')
    # 显示图像
    plt.show()

CBOW模型

        连续词袋模型(CBOW)是一种根据上下文单词来预测目标单词的模型。具体来说,给定一个窗口大小为 n上下文单词序列,连续词袋模型的任务是预测中间的目标单词

        CBOW模型∶使用文本的中间词作为目标词(标签),去掉了隐藏层。用上下文各词的词向量的均值代替NNLM拼接起来的词向量

        连续词袋模型的优点是:由于它是基于上下文单词来预测目标单词的,因此它可以利用上下文单词的信息来推断目标单词的语义和语法特征;模型参数较少,训练速度相对较快

CBOW对小型数据库比较合适。

输入∶上下文的语义表示

输出∶中间词是哪个词

案例
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
from tqdm import tqdm
import matplotlib.pyplot as plt
dtype = torch.FloatTensor
 # 语料库
sentences = ["i like dog", "i like cat", "i like animal",
            "dog cat animal", "apple cat dog like", "cat like fish",
            "dog like meat", "i like apple", "i hate apple",
            "i like movie book music apple", "dog like bark", "dog friend cat"]
word_sequence = ' '.join(sentences).split()  # 将所有句子合并成一个长的词序列
word_list = list(set(word_sequence))  # 获取去重的词汇列表
word_dict = {w: i for i, w in enumerate(word_list)}  # 创建词典,词对应的索引
# 创建CBOW训练数据
cbow_data = []
for i in range(1, len(word_sequence) - 1):
    context = [word_dict[word_sequence[i - 1]], word_dict[word_sequence[i + 1]]] 
    # 上下文词的索引
    target = word_dict[word_sequence[i]]  # 当前词(目标词)的索引
    cbow_data.append([context, target])  # 将上下文和目标词加入训练数据
"""
[[[6, 11], 3], [[3, 6], 11], [[11, 3], 6], [[6, 0], 3], [[3, 6], 0], [[0, 3], 6], 
[[6, 13], 3], [[3, 11], 13], [[13, 0], 11], [[11, 13], 0], [[0, 2], 13], [[13, 0], 2], 
[[2, 11], 0], [[0, 3], 11], [[11, 0], 3], [[3, 3], 0], [[0, 1], 3], [[3, 11], 1], 
[[1, 3], 11], [[11, 8], 3], [[3, 6], 8], [[8, 3], 6], [[6, 2], 3], [[3, 6], 2], [[2, 12], 6], 
[[6, 2], 12], [[12, 6], 2], [[2, 3], 6], [[6, 4], 3], [[3, 5], 4], [[4, 10], 5], [[5, 2], 10], 
[[10, 11], 2], [[2, 3], 11], [[11, 7], 3], [[3, 11], 7], [[7, 9], 11], [[11, 0], 9]]
"""
# print(cbow_data)
embedding_size = 2  # 词向量的维度
voc_size = len(word_list)  # 词汇表的大小
batch_size = 5  # 每个批次的大小
# 定义CBOW模型
class CBOW(nn.Module):
    def __init__(self):
        super(CBOW, self).__init__()
        self.embeddings = nn.Embedding(voc_size, embedding_size)  # 词嵌入层
        self.linear1 = nn.Linear(embedding_size, voc_size)  # 输出层,将嵌入映射回词汇表大小
    def forward(self, x):
        # torch.Size([5, 2])
        # print(x.shape)
        embeds = self.embeddings(x)  # 查找上下文词的嵌入向量
        # torch.Size([5, 2, 2])
        # print(embeds.shape)
        avg_embeds = torch.mean(embeds, dim=1)  # 对上下文词向量取平均值
        """
        tensor([[ 1.9557,  1.8476],
        [ 0.7933, -0.8557],
        [-1.4357,  0.3240],
        [-1.8752,  1.1240],
        [-1.0851, -0.3865]], grad_fn=<MeanBackward1>)
        """
        # print(avg_embeds)
        out = self.linear1(avg_embeds)  # 通过全连接层,预测词汇表中的每个词的概率
        # torch.Size([5, 14])
        # print(out.shape)
        return out
# 实例化模型
model = CBOW()
criterion = nn.CrossEntropyLoss()  # 使用交叉熵损失
optimizer = optim.Adam(model.parameters(), lr=0.001)  # 使用Adam优化器
# 随机批次生成函数
def random_batch(data, size):
    random_inputs = []  # 上下文词
    random_labels = []  # 目标词
    random_index = np.random.choice(range(len(data)), size, replace=False)  # 随机选择数据索引
    for i in random_index:
        random_inputs.append(data[i][0])  # 上下文词加入输入列表
        random_labels.append(data[i][1])  # 目标词加入标签列表
    return random_inputs, random_labels
 # 训练模型
for epoch in range(10000):
    input_batch, target_batch = random_batch(cbow_data, batch_size)  # 获取随机批次数据
    # [[11, 6], [4, 11], [3, 11], [4, 2], [11, 8]]
    # print(input_batch)
    # [10, 8, 8, 11, 12]
    # print(target_batch)
    input_batch = torch.LongTensor(input_batch)  # 转换为LongTensor,因为Embedding需要整数索引
    target_batch = torch.LongTensor(target_batch)  # 转换为LongTensor
    optimizer.zero_grad()  # 梯度清零
    output = model(input_batch)  # 前向传播,预测输出
    loss = criterion(output, target_batch)  # 计算损失
    if (epoch + 1) % 1000 == 0:  # 每1000个epoch打印一次损失
        print('Epoch:', '%04d' % (epoch + 1), 'cost =', 
        '{:.6f}'.format(loss.item()))
    loss.backward()  # 反向传播
    optimizer.step()  # 更新参数
# 可视化嵌入结果
for i, label in enumerate(word_list):
 W = model.embeddings.weight.data.numpy()  # 获取词嵌入的权重
x, y = W[i][0], W[i][1]  # 获取每个词的嵌入向量的两个维度
plt.scatter(x, y)  # 在二维空间中绘制点
plt.annotate(label, xy=(x, y), xytext=(5, 2), textcoords='offset points', 
ha='right', va='bottom')  # 标注词汇
plt.show()  # 显示图形

gensim API调用

        Word2vec是一个用来产生词向量的模型。是一个将单词转换成向量形式的工具。 通过转换,可以把对文本内容的处理简化为向量空间中的向量运算,计算出向量空间上的相似度,来表 示文本语义上的相似度

参数说明
sentences可以是一个list,对于大语料集,建议使用BrownCorpus,Text8Corpus或 lineSentence构建。
vector_sizeword向量的维度,默认为100。大的size需要更多的训练数据,但是效果会 更好。推荐值为几十到几百。
alpha学习率
window表示当前词与预测词在一个句子中的最大距离是多少。
min_count可以对字典做截断。词频少于min_count次数的单词会被丢弃掉,默认值为 5。
max_vocab_size设置词向量构建期间的RAM限制。如果所有独立单词个数超过这个,则就消 除掉其中最不频繁的一个。每一千万个单词需要大约1GB的RAM。设置成 None则没有限制。
sample高频词汇的随机降采样的配置阈值,默认为1e-3,范围是(0,1e-5)
seed用于随机数发生器。与初始化词向量有关。
workers参数控制训练的并行数。
sg用于设置训练算法,默认为0,对应CBOW算法;sg=1则采用skip-gram算 法。
hs如果为1则会采用hierarchica·softmax技巧。如果设置为0(default),则 negative sampling会被使用。
negative如果>0,则会采用negative samping,用于设置多少个noise words。
cbow_mean如果为0,则采用上下文词向量的和,如果为1(default)则采用均值。只有 使用CBOW的时候才起作用。
hashfxnhash函数来初始化权重。默认使用python的hash函数。
epochs迭代次数,默认为5。
trim_rule用于设置词汇表的整理规则,指定那些单词要留下,哪些要被删除。可以设 置为None(min_count会被使用)或者一个接受()并返回RULE_DISCARD, utils。RULE_KEEP或者utils。RULE_DEFAULT的函数。
sorted_vocab如果为1(default),则在分配word index 的时候会先对单词基于频率降序 排序。
batch_words每一批的传递给线程的单词的数量,默认为10000
min_alpha随着训练的进行,学习率线性下降到min_alpha
常用方法

model.wv: 这个对象包含了所有单词的词嵌入向量。常用的方法有:

  • model.wv[word]:返回某个特定单词的向量。
  • model.wv.most_similar(word):获取与某个单词最相似的词。
  • model.wv.similarity(word1, word2):计算两个词之间的相似度。

案列
import numpy as np
from gensim.models import Word2Vec
import matplotlib.pyplot as plt
 # 语料库
sentences = ["i like dog", "i like cat", "i like animal",
            "dog cat animal", "apple cat dog like", "cat like fish",
            "dog like meat", "i like apple", "i hate apple",
            "i like movie book music apple", "dog like bark", "dog friend cat"]
 # 将每个句子分成单词列表(Gensim的Word2Vec API要求输入格式为二维列表:每个句子是一个单词列表)
tokenized_sentences = [sentence.split() for sentence in sentences]
"""
[['i', 'like', 'dog'], ['i', 'like', 'cat'], ['i', 'like', 'animal'], ['dog', 'cat', 'animal'], ['apple', 'cat', 'dog', 'like'], ['cat', 'like', 'fish'], ['dog', 'like', 'meat'], ['i', 'like', 'apple'], ['i', 'hate', 'apple'], ['i', 
'like', 'movie', 'book', 'music', 'apple'], ['dog', 'like', 'bark'], ['dog', 'friend', 'cat']]
"""
# print(tokenized_sentences)
# 定义Word2Vec模型
model = Word2Vec(sentences=tokenized_sentences, vector_size=2, window=2, min_count=1, sg=0)
 # 获取词汇表
word_list = list(model.wv.index_to_key)
# ['like', 'dog', 'i', 'cat', 'apple', 'animal', 'friend', 'bark', 'music', 'book', 'movie', 'hate', 'meat', 'fish']
# print(word_list)
 # 可视化嵌入结果
for i, word in enumerate(word_list):
    W = model.wv[word]  # 获取每个单词的词向量
    x, y = W[0], W[1]  # 由于向量是二维的,提取前两个维度
    plt.scatter(x, y)  # 在二维空间中绘制点
    plt.annotate(word, xy=(x, y), xytext=(5, 2), textcoords='offset points', 
    ha='right', va='bottom')  # 标注词汇
plt.show()  # 显示图形

import numpy as np
from gensim.models import Word2Vec
from nltk.tokenize import word_tokenize
import nltk
nltk.download('punkt')  # 如果未安装nltk的punkt分词器,请先下载

# 示例文本数据
sentence = "Word embedding is the collective name for a set of language modeling and feature learning techniques in natural language processing (NLP) where words or phrases from the vocabulary are mapped to vectors of real numbers."
 # 使用NLTK进行分词
tokenized_text = word_tokenize(sentence.lower())  # 将文本转换为小写并进行分词
print(tokenized_text)
# 定义Word2Vec模型并进行训练
model = Word2Vec(sentences=[tokenized_text], vector_size=100, window=5, 
min_count=1, workers=4)
 # 计算整句话的嵌入向量
def sentence_embedding(sentence, model,tokenized_text):
    # 获取每个单词的嵌入向量
    word_embeddings = [model.wv[word] for word in tokenized_text if word in model.wv]
    # 如果句子中没有任何一个单词在词汇表中,则返回None
    if len(word_embeddings) == 0:
        return None
    # 计算平均单词嵌入
    sentence_embedding = np.mean(word_embeddings, axis=0)
    return sentence_embedding
# 获取整句话的嵌入向量
sentence_emb = sentence_embedding(sentence, model,tokenized_text)
print("Embedding for the sentence:", sentence_emb)
;