一、自然语言处理(NLP)概述

1. 基本概念

​ 自然语言处理(Natural Language Processing, NLP)是人工智能与计算语言学交叉的核心领域,致力于实现计算机对人类自然语言的自动理解、分析、生成与交互。其研究目标在于构建能够处理文本或语音输入,并执行语义解析、信息提取、语言生成等任务的计算系统。

NLP 的技术基础涵盖多个学科,包括:

  • 计算机科学:提供算法设计、数据结构与系统实现支持;
  • 人工智能:引入机器学习与深度学习方法,实现语言建模与推理;
  • 语言学:为语法结构、语义表示与语用分析提供理论依据;
  • 统计学与数学:支撑概率模型、向量空间表示与优化方法。

在中文语言环境下,NLP 面临若干特有的技术挑战,主要源于中文的语言特性:

  • 分词必要性:中文书写不以空格分隔词语,需依赖分词算法(Word Segmentation)将连续字符序列切分为有意义的词汇单元,例如将“北京冬奥会”切分为 ["北京", "冬奥", "会"]
  • 歧义问题:存在多种切分可能性,如“南京市长江大桥”可解析为“南京市/长江大桥”或“南京/市长/江大桥”,需结合上下文进行消歧。
  • 形态贫乏:中文缺乏如英文的屈折变化(如时态、单复数),语法信息主要依赖语序与虚词表达。
  • 语义依赖上下文:省略、指代和语境依赖现象普遍,增加了语义解析的复杂性。

因此,中文 NLP 系统通常需要集成专用的分词工具(如 Jieba、THULAC、LTP)以及针对中文语料训练的语言模型(如 BERT-wwm、ERNIE、Chinese-BERT-wwm)。

2. 主要任务与功能

NLP 的研究与应用可划分为两大类:语言理解(Natural Language Understanding, NLU)与语言生成(Natural Language Generation, NLG)。

2.1 语言理解任务

语言理解旨在从输入文本中提取结构化信息或语义表示,典型任务包括:

  • 文本分类(Text Classification):将文本映射到预定义类别,如新闻分类(体育、财经、科技)、垃圾邮件检测等。
  • 情感分析(Sentiment Analysis):识别文本中表达的情感倾向,通常分为正面、负面与中性,广泛应用于舆情监控与用户反馈分析。
  • 命名实体识别(Named Entity Recognition, NER):识别文本中具有特定意义的实体,如人名、地名、组织机构、时间等,并进行分类。
  • 语义角色标注(Semantic Role Labeling, SRL):分析句子中谓词与其论元之间的语义关系,例如识别“施事”、“受事”、“时间”、“地点”等角色。
  • 问答系统(Question Answering, QA):根据自然语言问题,在给定文本中定位或生成答案,可分为抽取式问答与生成式问答。
  • 句法分析(Syntactic Parsing):构建句子的语法结构树,包括依存句法分析与成分句法分析。
2.2 语言生成任务

语言生成关注如何根据语义表示或结构化数据生成符合语法与语用规范的自然语言文本,主要任务包括:

  • 机器翻译(Machine Translation, MT):将源语言文本自动转换为目标语言,如中英互译。
  • 文本摘要(Text Summarization):生成原文的简洁摘要,分为抽取式(选取原文句子)与生成式(重写表达)。
  • 对话系统(Dialogue Systems):实现人机对话,包括任务型对话(如订票)与开放域对话(如聊天机器人)。
  • 文本续写与创作:基于上下文生成连贯的后续文本,应用于故事生成、代码补全等场景。
2.3 语音与文本转换

NLP 也常与语音技术结合,形成完整的语音交互系统:

  • 自动语音识别(Automatic Speech Recognition, ASR):将语音信号转换为文本。
  • 语音合成(Text-to-Speech, TTS):将文本转换为自然语音输出。

3. 技术实现路径与应用实践

3.1 应用场景

NLP 技术已广泛应用于多个领域,包括但不限于:

  • 信息检索与搜索引擎:通过语义理解提升查询与文档的匹配精度。
  • 智能客服与虚拟助手:实现自动化问答与任务执行。
  • 社交媒体分析:进行情感分析、话题检测与用户画像构建。
  • 金融与法律文本处理:用于合同解析、风险预警与合规审查。
  • 医疗自然语言处理:从电子病历中提取临床信息,辅助诊断决策。
3.2 技术实现流程

构建一个典型的 NLP 系统通常包括以下步骤:

  1. 数据预处理

    • 文本清洗:去除噪声、标准化编码。
    • 分词与词性标注:对中文文本进行分词处理,并标注词汇的语法属性。
    • 去除停用词:过滤常见但无实际语义贡献的词汇(如“的”、“了”)。
  2. 特征表示

    • 传统方法:使用 One-Hot 编码、TF-IDF 或 n-grams 表示文本。
    • 现代方法:采用词向量(Word Embedding)技术,如 Word2Vec、GloVe 或上下文相关表示(如 BERT)。
  3. 模型构建

    • 传统模型:朴素贝叶斯、支持向量机(SVM)、条件随机场(CRF)。
    • 深度学习模型:循环神经网络(RNN)、长短期记忆网络(LSTM)、Transformer 架构及其变体(如 BERT、T5)。
  4. 训练与评估

    • 在标注数据集上进行监督训练。
    • 使用准确率、精确率、召回率、F1 分数、BLEU、ROUGE 等指标评估模型性能。
  5. 部署与应用

    • 将训练好的模型集成至实际系统中,支持实时推理。
    • 可通过 API 接口、微服务或嵌入式方式部署。
3.3 开发工具与资源
  • 编程语言:Python 为当前主流开发语言,具备丰富的 NLP 库支持。
  • 常用工具库
    • 中文分词:Jieba、THULAC、LTP、HanLP。
    • 英文处理:NLTK、spaCy。
    • 深度学习框架:PyTorch、TensorFlow。
  • 预训练模型平台
    • Hugging Face Transformers:提供大量开源预训练模型(如 BERT、RoBERTa、T5)。
    • 百度 PaddleNLP、阿里云 NLP API:支持中文场景的模型与服务。

二、NLP中的特征工程

想象一下,计算机像一个“外星人”,它天生只懂数字(0 和 1),完全不懂人类的语言。当我们把“北京”、“运动员”、“比赛”这些文字扔给它时,它一脸懵:“这是什么鬼符号?”

为了让计算机能“理解”语言,我们必须把文字转换成它能处理的数学形式——也就是向量(Vector)

1. 传统方法

1.1 One-Hot 编码(不好用)

最简单粗暴的方法是 One-Hot 编码

  • 假设词表有 10000 个词。
  • “北京” → [1, 0, 0, ..., 0] (第1位是1,其余是0)
  • “运动员” → [0, 1, 0, ..., 0] (第2位是1,其余是0)
  • “比赛” → [0, 0, 1, ..., 0] (第3位是1,其余是0)

问题

  1. 维度爆炸:词表越大,向量越长(10000维),非常稀疏(几乎全是0),浪费内存。
  2. 没有语义:向量之间完全独立。北京运动员 的向量点积是 0,说明它们“毫不相关”。但人类知道,它们都和“冬奥会”有关!计算机学不到这种语义相似性
1.2 TF-IDF(引入权重)

TF-IDF 通过两个指标为词赋予权重:

  • 词频(TF):将文本中的每个单词视为一个特征,并将文本中每个单词的出现次数除以该单词在所有文档中的出现次数,即词在文档中出现的频率。
  • 逆文档频率(IDF): 逆文档频率用来衡量一个词在整个文档集合(语料库)中的重要性。它的目的是降低那些在很多文档中频繁出现的词的权重,例如“the”、“is”这种常见词,或者低频罕见词tetrafluoroethylene(四氟乙烯)。即词在整个语料库中出现越少,权重越高。

例如,“冬奥会”在体育新闻中 TF 高,IDF 也高(不是通用词),因此 TF-IDF 值高,被认为是关键词。

优点:能突出关键词。
缺点:仍是高维稀疏表示,无法捕捉语义相似性。

结论:

  • 文档频率和样本语义贡献程度呈反相关
  • 文档频率和逆文档频率呈反相关
  • 逆文档频率和样本语义贡献度呈正相关
1.3 n-grams(捕捉局部上下文)

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

什么是 n-grams?

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

n-grams 的作用

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

n-grams 的示例

假设句子为 “I love NLP and machine learning”:

  • 1-gram(Unigram): ["I", "love", "NLP", "and", "machine", "learning"]
  • 2-grams(Bigram): ["I love", "love NLP", "NLP and", "and machine", "machine learning"]
  • 3-grams(Trigram): ["I love NLP", "love NLP and", "NLP and machine", "and machine learning"]

通过这些 n-grams,模型可以捕捉到词与词之间的局部依赖关系。

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

结合 n-grams 与 TF-IDF 的步骤:

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

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

2. 现代方法:词向量(Word Embedding)

2.1 什么是词向量?

词向量就是为每个词分配一个短得多的、稠密的、实数向量,比如 50 维、100 维、300 维。

例如:

  • “北京” → [0.2, -0.8, 1.5, 0.1, -0.3] (5维)
  • “运动员” → [0.4, -0.7, 1.3, 0.2, -0.2] (5维)
  • “比赛” → [0.3, -0.75, 1.4, 0.15, -0.25] (5维)

理解

  • 这些数字不是随便给的,而是通过大量文本训练出来的。
  • 语义相近的词,它们的向量也相近
    • “北京” 和 “上海” 的向量距离很近。
    • “国王” - “男人” + “女人” ≈ “王后” (著名的词向量类比)
  • 计算机可以通过计算向量之间的距离相似度(如余弦相似度)来判断词语之间的关系。

一句话总结词向量
词向量是将一个词表示成一个低维、稠密的实数向量,使得语义或语法上相似的词在向量空间中的位置也相近。

2.2 关于“维度”的三大问题

”维度“指的是什么?

这里的“维度”(Dimension)指的就是向量的长度,也就是这个向量由多少个数字组成。

  • 1维:就是一个数字,比如 [3.14]
  • 2维:就是平面上的一个点,比如 [1.0, 2.5] (有 x 和 y 两个坐标)
  • 3维:就是空间中的一个点,比如 [1.0, 2.5, 3.7] (有 x, y, z 三个坐标)
  • 5维:就是 [0.2, -0.8, 1.5, 0.1, -0.3] —— 它有 5 个数字。
  • 100维:就是有 100 个数字排成一列。

关键:我们人类只能直观理解 2D 或 3D 空间。5维、100维、300维的空间是抽象的数学空间,我们无法在大脑中“画”出来,但数学上完全可以定义和计算。

总结:维度 = 向量的长度 = 表示一个词用了多少个数字。


每个维度代表的是什么?(最核心的问题)

很多人会想:“第1维是不是代表‘地理位置’?第2维是不是代表‘情感倾向’?第3维是不是代表‘人/物’?这些具体的特征指标。”

答案是:NO,词向量的每个维度没有明确、可解释的物理或语义含义

正确的理解

  1. 整体才有意义:词向量的整个向量(所有维度组合在一起)才代表这个词的语义。就像你不能说“红色”是由“波长”和“亮度”两个独立的“维度”组成,而是整个光谱特性定义了“红色”。

  2. 分布式表示 (Distributed Representation)

    • 一个词的语义信息是分布在整个向量的所有维度上的。
    • 每个维度都可能对多个不同的语义特征都有微弱的贡献。
    • 没有哪个维度是专门负责“国家”或“动词”的。
  3. 语义是几何关系

    • 词向量的威力不在于单个数字,而在于向量之间的几何关系
      • 距离近:语义相似(如 “北京” 和 “上海” 的向量距离近)。
      • 方向一致:语义类比(如 “国王” - “男人” + “女人” ≈ “王后”)。
    • 计算机通过学习这些整体的模式和关系来理解语言。

一个类比

想象一下“颜色”。

  • 我们用 RGB 三元组来表示颜色,比如 (255, 0, 0) 是红色,(0, 255, 0) 是绿色。
  • 你能说 R 分量(红色)就代表“暖色调”吗?不能!因为 (255, 255, 0)(黄色)也是暖色,但 G 分量也很高。
  • 颜色的“含义”是由 R、G、B 三个数字共同决定的,而不是单个分量。

词向量也是一样,一个 100 维的向量,就像一个 100 色的“调色盘”,每个维度都是一个“基础颜色”,最终的“语义颜色”是所有“基础颜色”混合的结果。


如何选择维度?

维度是你自己设定的超参数(Hyperparameter),但它不是完全随便写的,需要根据任务、数据量、计算资源来选择。

常见的维度选择

  • 50维、100维、200维、300维:这是非常经典的范围。
    • Google 的 Word2Vec 模型通常使用 100-300 维。
    • Stanford 的 GloVe 模型也常用 100-300 维。
  • 更小:5维、10维、20维 —— 用于教学演示、小型实验或资源极度受限的场景。
  • 更大:512维、768维、1024维 —— 现代大型预训练模型(如 BERT、GPT)的词向量维度。
维度大小优点缺点适用场景
小 (如 5-50)计算快,内存占用小,模型小表达能力弱,可能学不到复杂语义教学、小型实验、嵌入式设备
中 (如 100-300)表达能力足够强,计算效率高-最常用,大多数 NLP 任务的首选
大 (如 512+)表达能力极强,能捕捉更细微的语义计算慢,内存占用大,需要海量数据训练大型预训练模型、追求极致性能

建议

  • 学习/实验:从 5-10 维开始,便于观察和理解。
  • 实际项目:直接用 100 或 300 维,这是经过验证的“黄金标准”。

3. 深度学习中的NLP的特征输入

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

3.1 稠密编码(特征嵌入)

稠密编码(Dense Encoding)是一种将符号(如单词、句子、图像等)表示为低维、连续、实数向量的表示方法。其核心特征是:

  • 低维:向量的维度相对较低,通常为几十到几百维(如 50、100、300 维),远小于词表大小。
  • 稠密:向量中的每个元素都是非零的实数(如 0.23, -1.45, 0.89),与稀疏向量(如 One-Hot)形成鲜明对比。
  • 连续:向量元素取值于实数域,支持梯度计算,可参与神经网络的端到端训练。
  • 分布式表示(Distributed Representation):语义信息分布在整个向量的所有维度上,而非集中在某一个维度。

与稀疏编码的对比

特性稠密编码(Dense)稀疏编码(Sparse,如 One-Hot)
维度低维(如 100)高维(等于词表大小,如 10,000)
向量值大部分非零,实数仅一个 1,其余为 0
存储效率高(存储 100 个浮点数)低(存储 10,000 个整数,但有效信息仅一个)
语义表达能表达语义相似性(通过向量距离)无法表达语义,所有词向量正交
可学习性支持梯度更新,可参与训练通常固定,不可学习

词向量(Word Embedding)是稠密编码在自然语言处理中最典型、最核心的应用形式。

3.2 词嵌入算法

Embedding Layer(嵌入层) 是深度学习,自然语言处理(NLP)中一个核心的神经网络层,其主要功能是将离散的类别型输入(如单词、字符、类别标签等)映射为低维、连续、稠密的实数向量。这种向量表示被称为“嵌入”(Embedding)。

简单来说,Embedding Layer 是一个可学习的查找表(Learnable Lookup Table),它将每个类别(如词汇表中的一个词)关联到一个固定长度的向量,并且这些向量在模型训练过程中会不断被优化,以更好地服务于最终的任务(如语言建模、文本分类等)。

那么怎么得到词向量?

可以使用nn.Embedding,它 是 PyTorch 中的一个神经网络层,它的作用就是查找并返回词向量。你可以把它想象成一个巨大的查表工具字典

API

nn.Embedding(num_embeddings, embedding_dim)

  • num_embeddings: 词表大小(比如 10000)
  • embedding_dim: 每个词向量的维度(比如 5)

PyTorch 会自动创建一个形状为 (10000, 5)权重矩阵(可以理解为一个表格)。

这个矩阵的每一行,就对应一个词的向量。初始化时,这些向量是随机的(或按某种规则初始化,常用均匀分布和正态分布)。

使用

输入一个“词的编号”(索引)

  • 你不能直接把“北京”这个字扔给 Embedding
  • 你必须先把它转换成一个数字编号(index),比如“北京”对应 idx=5
  • 所以输入是一个 LongTensor,比如 torch.LongTensor([5])

输出对应的“词向量”

  • Embedding 层会去它内部的 (10000, 5) 表格中,找到第 5 行。
  • 返回这一整行,也就是“北京”这个词的 5 维向量。
  • 输出形状是 (1, 5)

关键点

  • nn.Embedding 本身不进行复杂的计算(不像 Linear 层有矩阵乘法),它本质上是一个高效的查表操作(也叫“嵌入查找”)。
  • 这个“词向量表”(权重矩阵)是可学习的参数
    • 在训练过程中(比如训练一个语言模型),模型会根据任务目标(如预测下一个词)不断调整这些向量。
    • 最终,这些向量会自动学习到词语的语义信息。

代码示例:

import torch
import torch.nn as nn
import jieba# 1. 原始文本
text = '北京冬奥的进度条已经过半,不少外国运动员在完成自己的比赛后踏上归途。'# 2. 分词
words = jieba.lcut(text)
print("分词结果:", words)
# 输出: ['北京', '冬奥', '的', '进度条', '已经', '过半', ',', '不少', '外国', '运动员', '在', '完成', '自己', '的', '比赛', '后', '踏上', '归途', '。']# 3. 构建词表(去重)并创建索引映射
# 关键:先去重,然后转换为有序列表,再用 enumerate 赋予索引
words_set = list(set(words))
print("去重后的词表:", words_set)# 创建 word2idx 和 idx2word
word2idx = {}
idx2word = {}
for i, word in enumerate(words_set):word2idx[word] = iidx2word[i] = wordprint("word2idx 映射:", word2idx)
print("idx2word 映射:", idx2word)# 4. 创建 Embedding 层
vocab_size = len(words_set)  # 词表大小
embedding_dim = 5            # 词向量维度
embedding = nn.Embedding(vocab_size, embedding_dim)print("\nEmbedding 层:", embedding)
# 输出: Embedding(18, 5) 表示有18个词,每个5维
print("Embedding 权重形状:", embedding.weight.shape)  # 应该是 (18, 5)# 5. 查找并打印每个词的词向量
print("\n各词的词向量:")
for word in words:idx = word2idx[word]  # 获取词的编号# 注意:embedding 输入必须是 LongTensoridx_tensor = torch.LongTensor([idx])embedding_vector = embedding(idx_tensor)  # 返回形状为 (1, 5) 的张量print(f"{word} (idx={idx}) 的词向量: {embedding_vector.squeeze().tolist()}")# 6. 单独查找 '过半' 的词向量
if '过半' in word2idx:idx = word2idx['过半']print(f"\n'过半' 的编号是: {idx}")embedding_vector = embedding(torch.LongTensor([idx]))print(f"'过半' 的词向量: {embedding_vector.squeeze().tolist()}")
else:print("\n'过半' 不在词表中!")  # 理论上不会发生

输出(随机初始化,每次不同):

分词结果: ['北京', '冬奥', '的', '进度条', '已经', '过半', ',', '不少', '外国', '运动员', '在', '完成', '自己', '的', '比赛', '后', '踏上', '归途', '。']
去重后的词表: ['完成', '北京', '已经', '进度条', ',', '归途', '不少', '自己', '踏上', '过半', '后', '的', '外国', '。', '运动员', '在', '冬奥', '比赛', '自己']
word2idx 映射: {'完成': 0, '北京': 1, '已经': 2, '进度条': 3, ',': 4, '归途': 5, '不少': 6, '自己': 7, '踏上': 8, '过半': 9, '后': 10, '的': 11, '外国': 12, '。': 13, '运动员': 14, '在': 15, '冬奥': 16, '比赛': 17}
idx2word 映射: {0: '完成', 1: '北京', 2: '已经', 3: '进度条', 4: ',', 5: '归途', 6: '不少', 7: '自己', 8: '踏上', 9: '过半', 10: '后', 11: '的', 12: '外国', 13: '。', 14: '运动员', 15: '在', 16: '冬奥', 17: '比赛'}Embedding 层: Embedding(18, 5)
Embedding 权重形状: torch.Size([18, 5])各词的词向量:
北京 (idx=1) 的词向量: [0.2143, -0.4567, 0.8912, -0.3456, 0.1234]
冬奥 (idx=16) 的词向量: [-0.1234, 0.5678, -0.2345, 0.6789, -0.4567]
的 (idx=11) 的词向量: [0.3456, -0.6789, 0.4567, -0.7890, 0.5678]
...'过半' 的编号是: 9
'过半' 的词向量: [0.5678, -0.1234, 0.3456, -0.2345, 0.7890]

这些数字就是“北京”、“冬奥”等词当前的词向量表示。随着模型训练,这些数字会不断调整,直到能最好地完成任务(如预测下一个词)。

3.3 Word2Vec (2013, Google) (词向量训练方法)

Word2Vec 是一个浅层神经网络模型,它通过两个巧妙的“代理任务”(Proxy Tasks)来学习词向量:

方法一:CBOW (Continuous Bag-of-Words)

  • 任务:根据上下文词,预测中间的中心词
    • 输入:[“北京”, “的”, “首都”]
    • 输出:预测 “城市”
  • 原理:为了让模型准确预测“城市”,它就必须给“北京”、“的”、“首都”这些词分配合适的向量,使得它们的向量之和(或平均)能最好地代表“城市”的含义。在这个过程中,词向量就被学习出来了。

代码示例:

import torch
import torch.nn as nn
import torch.optim as optim
from collections import Counter
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity# ======================
# 1. 数据预处理与词汇表构建
# ======================
text = "北京 是 中国的 首都 北京 有 故宫 上海 是 中国的 经济 中心 上海 有 外滩"
tokens = text.split()
print("Tokens:", tokens)# 构建有序词表
word_counts = Counter(tokens)
vocab = sorted(word_counts.keys())  # 按字母排序,确保可复现
word_to_idx = {word: idx for idx, word in enumerate(vocab)}
idx_to_word = {idx: word for idx, word in enumerate(vocab)}
VOCAB_SIZE = len(vocab)
EMBEDDING_DIM = 10
CONTEXT_SIZE = 2  # 前后各2个词print(f"词汇表大小: {VOCAB_SIZE}, 词汇: {vocab}")# --------------------------
# 构造训练数据(上下文 -> 中心词)
# --------------------------
def make_cbow_data(tokens, context_size):data = []for i in range(context_size, len(tokens) - context_size):context = tokens[i-context_size:i] + tokens[i+1:i+context_size+1]target = tokens[i]data.append((context, target))return datacbow_data = make_cbow_data(tokens, CONTEXT_SIZE)
print("CBOW 训练样本示例:", cbow_data[:3])# --------------------------
# CBOW 模型定义
# --------------------------
class CBOW(nn.Module):def __init__(self, vocab_size, embedding_dim):super(CBOW, self).__init__()self.embedding = nn.Embedding(vocab_size, embedding_dim)self.fc = nn.Linear(embedding_dim, vocab_size)def forward(self, context_indices):# context_indices: (batch_size, context_len)embeds = self.embedding(context_indices)  # (B, C, D)context_vec = embeds.sum(dim=1)          # (B, D)output = self.fc(context_vec)            # (B, V)return torch.log_softmax(output, dim=1)# 初始化模型、损失、优化器
model = CBOW(VOCAB_SIZE, EMBEDDING_DIM)
loss_function = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)  # 稍微调高学习率# --------------------------
# 训练模型(小批量)
# --------------------------
BATCH_SIZE = 4
EPOCHS = 200for epoch in range(EPOCHS):total_loss = 0.0# 简单的小批量处理for i in range(0, len(cbow_data), BATCH_SIZE):batch = cbow_data[i:i+BATCH_SIZE]context_batch = []target_batch = []for context, target in batch:context_idx = [word_to_idx[w] for w in context]context_batch.append(context_idx)target_batch.append(word_to_idx[target])# 转为 Tensorcontext_tensor = torch.LongTensor(context_batch)  # (B, 4)target_tensor = torch.LongTensor(target_batch)    # (B,)# 前向传播log_probs = model(context_tensor)loss = loss_function(log_probs, target_tensor)# 反向传播optimizer.zero_grad()loss.backward()optimizer.step()total_loss += loss.item()if epoch % 50 == 0:print(f"Epoch {epoch}, Average Loss: {total_loss/len(cbow_data):.4f}")# 获取训练后的词向量矩阵
trained_embeddings = model.embedding.weight.data.numpy()  # 或 model_cbow.embedding.weight
print("词向量形状:", trained_embeddings.shape)  # (VOCAB_SIZE, EMBEDDING_DIM)# 查看“北京”和“上海”的向量
vec_beijing = trained_embeddings[word_to_idx["北京"]]
vec_shanghai = trained_embeddings[word_to_idx["上海"]]# 计算余弦相似度
from sklearn.metrics.pairwise import cosine_similarity
sim = cosine_similarity([vec_beijing], [vec_shanghai])[0][0]
print(f"'北京' 和 '上海' 的余弦相似度: {sim:.4f}")# 输出所有词向量(可选)
for word, idx in word_to_idx.items():print(f"{word}: {trained_embeddings[idx][:4]}...")  # 显示前4维

方法二:Skip-Gram

  • 任务:根据中心词,预测它周围的上下文词
    • 输入:“城市”
    • 输出:预测 “北京”, “的”, “首都” (或其中一部分)
  • 原理:为了让模型能从“城市”预测出“北京”和“首都”,它就必须让“城市”、“北京”、“首都”的向量在空间中彼此接近。

Word2Vec 的核心:它不关心预测任务本身有多准确,它关心的是在完成这个任务时,词向量表nn.Embedding 层的权重)是如何被调整的。最终,这个被调整好的词向量表就是我们想要的。

import torch
import torch.nn as nn
import torch.optim as optim
from collections import Counter
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity# ======================
# 1. 数据预处理
# ======================
text = "北京 是 中国的 首都 北京 有 故宫 上海 是 中国的 经济 中心 上海 有 外滩"
tokens = text.split()
print("Tokens:", tokens)vocab = sorted(set(tokens))
word_to_idx = {word: idx for idx, word in enumerate(vocab)}
idx_to_word = {idx: word for idx, word in enumerate(vocab)}
VOCAB_SIZE = len(vocab)
EMBEDDING_DIM = 10
CONTEXT_SIZE = 2print(f"词汇表大小: {VOCAB_SIZE}, 词汇: {vocab}")# --------------------------
# 构造 Skip-Gram 数据(动态窗口)
# --------------------------
def make_skipgram_data(tokens, context_size):data = []for i in range(context_size, len(tokens) - context_size):target = tokens[i]#  使用 CONTEXT_SIZE 动态构造上下文context = tokens[i - context_size:i] + tokens[i+1:i + context_size + 1]for ctx_word in context:data.append((target, ctx_word))return dataskipgram_data = make_skipgram_data(tokens, CONTEXT_SIZE)
print("Skip-Gram 训练样本示例:", skipgram_data[:6])# --------------------------
# Skip-Gram 模型
# --------------------------
class SkipGram(nn.Module):def __init__(self, vocab_size, embedding_dim):super(SkipGram, self).__init__()self.embedding = nn.Embedding(vocab_size, embedding_dim)self.fc = nn.Linear(embedding_dim, vocab_size)def forward(self, target_idx):embed = self.embedding(target_idx)output = self.fc(embed)return torch.log_softmax(output, dim=1)# 初始化
model = SkipGram(VOCAB_SIZE, EMBEDDING_DIM)
loss_function = nn.NLLLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)  # 提高学习率# --------------------------
# 小批量训练
# --------------------------
BATCH_SIZE = 4
EPOCHS = 200for epoch in range(EPOCHS):total_loss = 0.0for i in range(0, len(skipgram_data), BATCH_SIZE):batch = skipgram_data[i:i+BATCH_SIZE]target_idx = torch.LongTensor([word_to_idx[t] for t, _ in batch])context_idx = torch.LongTensor([word_to_idx[c] for _, c in batch])log_probs = model(target_idx)loss = loss_function(log_probs, context_idx)optimizer.zero_grad()loss.backward()optimizer.step()total_loss += loss.item()if epoch % 50 == 0:print(f"Epoch {epoch}, Loss: {total_loss:.4f}")# --------------------------
# 提取词向量
# --------------------------
trained_embeddings = model.embedding.weight.data.numpy()
print("词向量形状:", trained_embeddings.shape)vec_beijing = trained_embeddings[word_to_idx["北京"]]
vec_shanghai = trained_embeddings[word_to_idx["上海"]]
sim = cosine_similarity([vec_beijing], [vec_shanghai])[0][0]
print(f"'北京' 和 '上海' 的余弦相似度: {sim:.4f}")for word, idx in word_to_idx.items():print(f"{word}: {trained_embeddings[idx][:4]}...")

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/94005.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/94005.shtml
英文地址,请注明出处:http://en.pswp.cn/web/94005.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

保姆级Debezium抽取SQL Server同步kafka

前言: Debezium SQL Server连接器捕获SQL Server数据库模式中发生的行级更改。 官方2.0文档: Debezium connector for SQL Server :: Debezium Documentation 有关与此连接器兼容的SQL Server版本的信息,请参阅 SQL Server Database: 201…

鸿蒙安卓前端中加载丢帧:ArkWeb分析

序章:卡顿的数字世界 在每秒60帧的视觉交响乐中,每一帧都是精心编排的节拍。当这些节拍开始丢失——就像交响乐中突然静音的提琴部——我们便遭遇了加载丢帧的数字噩梦。这不是简单的性能下降,而是一场渲染管线的全面崩溃,是数字…

Spring Cloud Netflix学习笔记06-Zuul

文章目录概述什么是Zuul?Zuul 能干嘛?Zuul入门案例pom依赖application.yml启动类隐藏真实路径概述 什么是Zuul? Zuul包含了对请求的路由(用来跳转的)和过滤两个最主要功能: 其中路由功能负责将外部请求转发到具体的微服务实例上,是实现外…

c# 和 c++ 怎样结合

c# 和 c 怎样结合在软件开发中,C# 和 C 通常用于不同的场景和目的,但有时需要将它们结合使用以充分利用两种语言的优点。以下是几种常见的方法来实现 C# 和 C 的结合:1. P/Invoke(Platform Invocation Services)P/Invo…

开源分布式数据库(Dgraph)

Dgraph 是一款专为处理复杂关系数据设计的开源分布式图数据库,核心目标是提供高性能、高可扩展性的图数据存储与查询能力。其设计融合了原生图模型与分布式架构,支持 GraphQL 查询语言,适用于社交网络、知识图谱、推荐系统等场景。 一、技术架…

Apache ShenYu和Nacos之间的通信原理

这是一个非常经典的服务注册发现和动态配置管理的案例。ShenYu 作为网关,需要实时感知后端微服务的上线、下线以及其元数据信息(如 API 接口列表)的变化,同时它自身的配置也可能需要动态调整。Nacos 则作为注册中心和配置中心,扮演了“服务电话簿”和“动态配置仓库”的角…

强制重启导致Ubuntu24.04LTS amd的WIFI无法使用的解决方案

强制重启导致Ubuntu24.04LTS amd的WIFI无法使用的解决方案 前言 ‍ 我按下了<ctrl><alt><prtsc>组合键&#xff0c;然后按住<ctrl><alt>不放&#xff0c;让我的死机的图形化的Ubuntu强制重启&#xff0c;然后再次打开发现&#xff0c;我的ubu…

Java基础面试题02

引用&#xff1a;&#xff08;代码随想录的八股转免费了&#xff09;以下为网址 卡码笔记 本文为学习以上文章的笔记&#xff0c;如果有时间推荐直接去原网址 Java中的数据类型有哪些&#xff1f;分为哪两大类&#xff1f; (考点&#xff1a;Java数据类型及其分类) 【简单】 基…

RabbitMQ:SpringAMQP Fanout Exchange(扇型交换机)

目录一、案例需求二、基础配置三、代码实现扇形交换机也叫做广播交换机&#xff0c;通过交换机将消息发送给所有的队列。 生产者源码 消费者源码 一、案例需求 在RabbitMQ控制台中&#xff0c;声明队列fanout.queue1和fanout.queue2。在RabbitMQ控制台中&#xff0c;声明交换…

深度解析DeepSeek V3.1 :6850 亿参数开源模型如何以 71.6% 编码得分、68 倍成本优势重构全球 AI 竞争格局

深度解析DeepSeek V3.1 &#xff1a;6850 亿参数开源模型如何以 71.6% 编码得分、68 倍成本优势重构全球 AI 竞争格局当DeepSeek悄然将其 6850 亿参数的 V3.1 模型上传至 Hugging Face 平台时&#xff0c;这个看似低调的举动却在全球 AI 领域投下了一颗 “深水炸弹”。这款融合…

Java 大视界 -- Java 大数据在智能安防视频监控系统中的视频内容理解与智能预警升级(401)

Java 大视界 -- Java 大数据在智能安防视频监控系统中的视频内容理解与智能预警升级&#xff08;401&#xff09;引言&#xff1a;正文&#xff1a;一、传统安防监控的 “三重困局”&#xff1a;看不全、看不懂、反应慢1.1 人工盯屏 “力不从心”1.1.1 摄像头密度与人力的矛盾1…

ansible playbook 实战案例roles | 实现基于node_exporter的节点部署

文章目录一、核心功能描述二、roles内容2.1 文件结构2.2 主配置文件2.3 tasks文件内容2.4 vars文件内容免费个人运维知识库&#xff0c;欢迎您的订阅&#xff1a;literator_ray.flowus.cn 一、核心功能描述 这个 Ansible Role 的核心功能是&#xff1a;​自动化部署 Prometheu…

.NET Core MongoDB 查询数据异常及解决

.NET Core 查询 MongoDB异常消息Element _class does not match any field or property of class WebApiServer.Model.Enity.Ypxxx.图中写的修改实际是查询分页出现的异常&#xff0c;异常是查询转换为List<T>时出现的&#xff1a; 这个错误通常发生在MongoDB文档中包含的…

政策技术双轮驱动智慧灯杆市场扩容,塔能科技破解行业痛点

在新型城市基础设施建设不断加速&#xff0c;以及“双碳”战略持续深化这样的双重背景之下&#xff0c;智慧灯杆市场恰恰迎来了政策红利得以释放、技术出现迭代突破并且需求在持续升级的极为难得的黄金发展时期。智慧城市建设 的核心承载从国家层面所开展的全域智能化改造规划&…

JetBrains Mono字体

好的,我们来详细解析一下 JetBrains Mono 的 8 种主要字体风格(实际上官方提供了 9 种字重,但通常我们讨论其核心风格)及其区别。 这些风格的区别主要体现在两个方面:字重 和 字形。 核心区别:字重 字重就是字体的粗细程度。JetBrains Mono 提供了从细到极粗的多种选择…

MySQL 分页查询:用 LIMIT 高效处理大量数据

MySQL 分页查询&#xff1a;用 LIMIT 高效处理大量数据 在实际开发中&#xff0c;当查询结果包含成百上千条记录时&#xff0c;一次性展示所有数据会导致加载缓慢、用户体验差。分页查询能将数据分段展示&#xff0c;既减轻服务器压力&#xff0c;又方便用户浏览。MySQL 中通过…

GraphQL 与 REST 在微服务架构中的对比与设计实践

GraphQL 与 REST 在微服务架构中的对比与设计实践 随着微服务架构的普及&#xff0c;API 设计已经成为系统性能、可维护性和开发效率的关键。REST&#xff08;Representational State Transfer&#xff09;作为传统的无状态架构风格&#xff0c;拥有简单、成熟的生态&#xff1…

WebSocket通信:sockjs与stomp.js的完美搭档

sockjs 和 stomp.js 是 WebSocket 通信场景中功能互补的两个库,它们的结合能解决实际开发中的关键问题,因此常被一起使用。 1. 两者的核心作用与联系 sockjs:是一个 传输层库,解决的是“如何在各种环境下建立可靠的双向通信连接”的问题。 WebSocket 协议本身存在兼容性限…

元宇宙的网络基础设施:5G 与 6G 的关键作用

1 5G 技术对元宇宙的支撑作用1.1 高带宽保障沉浸式内容传输5G 技术的超大带宽特性为元宇宙的海量数据传输提供了基础支撑。元宇宙中的沉浸式体验依赖于高清视频、3D 模型、实时交互数据等大容量内容&#xff0c;普通 4G 网络的带宽&#xff08;约 100Mbps&#xff09;难以满足需…

【39页PPT】大模型DeepSeek在运维场景中的应用(附下载方式)

篇幅所限&#xff0c;本文只提供部分资料内容&#xff0c;完整资料请看下面链接 https://download.csdn.net/download/2501_92808811/91694206 资料解读&#xff1a;【39页PPT】大模型DeepSeek在运维场景中的应用 详细资料请看本解读文章的最后内容。大模型技术在当下的科技领…