词向量与分布式语义

文档概述

本文档系统阐述词向量的理论基础、实现原理及认知科学联系。重点介绍Word2Vec、GloVe、FastText等经典模型,以及ELMo、BERT等上下文词向量技术,并探讨Embedding空间的几何性质及其认知意义。

关键词速览

术语英文核心定义
分布式假说Distributional Hypothesis词语由其上下文定义
词向量Word Vector/Embedding词语的稠密向量表示
Word2VecWord2Vec经典的词嵌入训练模型
GloVeGlobal Vectors全局共现统计词嵌入
FastTextFastText子词嵌入模型
上下文词向量Contextualized Embedding随上下文变化的词表示
ELMoEmbeddings from Language Models双向LSTM词向量
BERTBidirectional Encoder RepresentationsTransformer编码器
语义空间Semantic Space词向量张成的空间
认知科学Cognitive Science研究人类认知的学科

一、分布式假说(Distributional Hypothesis)

1.1 理论起源与核心思想

分布式假说是现代计算语言学和词向量研究的理论基石,由英国语言学家John Rupert Firth于1957年首次系统性地提出。Firth的经典论述至今仍是NLP领域最重要的指导思想之一:

“You shall know a word by the company it keeps” (观其伴,知其义)

这一假说的核心洞见在于:语义相似的词语倾向于出现在相似的上下文中。因此,我们可以通过分析一个词的上下文分布来推断其语义。

1.2 形式化定义

从数学角度,分布式假说可以形式化为:

其中上下文相似度可以通过以下方式度量:

这里 表示词语 的上下文向量。

1.3 上下文的定义

窗口上下文(Window-based) 最常用的上下文定义是基于词语周围的窗口:

原始句子: "The quick brown fox jumps over the lazy dog"
窗口大小=2时的上下文:
- "quick" 的上下文: [The, brown, fox]
- "fox" 的上下文: [quick, brown, jumps, over]

更广义的上下文定义

上下文类型定义应用场景
词汇窗口固定窗口内的词句法/语义分析
文档级同一文档中的词主题建模
依赖结构依存语法关系中的词句法语义
语义框架同一框架中的词框架语义学

二、经典词向量模型

2.1 Word2Vec系列模型

Word2Vec由Tomas Mikolov等人于2013年提出,是词向量研究的里程碑式工作。该模型通过浅层神经网络学习词的高维稠密表示。

2.1.1 Skip-gram模型

Skip-gram模型的核心思想是:用中心词预测上下文词

模型架构

输入层: 中心词 one-hot 向量 [V × 1]
    ↓ W [V × d]
隐藏层: 词嵌入 [d × 1]
    ↓ W' [d × V]
输出层: Softmax → 上下文词概率分布 [V × 1]

目标函数

给定训练语料中的词序列 ,Skip-gram的目标是最大化:

其中 是上下文窗口大小,条件概率定义为:

这里 是输入词嵌入, 是输出词嵌入。

高效训练技术:负采样(Negative Sampling)

由于完整softmax需要遍历整个词表,计算代价极高。负采样通过二分类近似解决这个问题:

其中 是sigmoid函数, 是负样本数量, 是负采样分布。

2.1.2 CBOW模型

CBOW(Continuous Bag-of-Words)与Skip-gram互补,用上下文预测中心词

class CBOW(nn.Module):
    def __init__(self, vocab_size, embedding_dim):
        super().__init__()
        self.embeddings = nn.Embedding(vocab_size, embedding_dim)
        self.linear = nn.Linear(embedding_dim, vocab_size)
    
    def forward(self, context_words):
        # context_words: [batch_size, window_size * 2]
        # 形状: [batch_size, 2c]
        
        # 获取上下文词的嵌入并求平均
        embedded = self.embeddings(context_words)  # [batch, 2c, embed_dim]
        averaged = torch.mean(embedded, dim=1)    # [batch, embed_dim]
        
        # 预测中心词
        output = self.linear(averaged)            # [batch, vocab_size]
        return output

2.1.3 Skip-gram完整实现

import torch
import torch.nn as nn
import torch.optim as optim
from collections import Counter
import numpy as np
 
class SkipGramModel(nn.Module):
    """
    Skip-gram词向量模型
    
    核心思想:用中心词预测上下文词
    训练目标:最大化真实上下文词的概率,最小化负采样词的概率
    """
    def __init__(self, vocab_size, embedding_dim):
        super().__init__()
        self.vocab_size = vocab_size
        self.embedding_dim = embedding_dim
        
        # 词嵌入层
        self.target_embeddings = nn.Embedding(vocab_size, embedding_dim)
        self.context_embeddings = nn.Embedding(vocab_size, embedding_dim)
        
        # 初始化
        nn.init.uniform_(self.target_embeddings.weight, -0.5/embedding_dim, 0.5/embedding_dim)
        nn.init.zeros_(self.context_embeddings.weight)
    
    def forward(self, target, context, negative):
        """
        Args:
            target: 中心词ID [batch_size]
            context: 上下文词ID [batch_size]
            negative: 负采样词ID [batch_size, num_negative]
        Returns:
            正样本损失 + 负样本损失
        """
        # 正样本得分
        target_emb = self.target_embeddings(target)      # [batch, dim]
        context_emb = self.context_embeddings(context)    # [batch, dim]
        pos_score = torch.sum(target_emb * context_emb, dim=1)  # [batch]
        pos_loss = torch.nn.functional.binary_cross_entropy_with_logits(
            pos_score, torch.ones_like(pos_score)
        )
        
        # 负样本损失
        neg_emb = self.context_embeddings(negative)     # [batch, k, dim]
        neg_score = torch.bmm(neg_emb, target_emb.unsqueeze(2)).squeeze()  # [batch, k]
        neg_loss = torch.nn.functional.binary_cross_entropy_with_logits(
            neg_score, torch.zeros_like(neg_score)
        )
        
        return pos_loss + neg_loss
 
def train_word2vec(corpus, embedding_dim=100, window_size=5, 
                    min_count=5, negative_samples=5, epochs=5):
    """
    Word2Vec训练流程
    """
    # 1. 构建词表
    word_counts = Counter(corpus)
    vocab = [word for word, count in word_counts.items() if count >= min_count]
    word_to_idx = {word: idx for idx, word in enumerate(vocab)}
    idx_to_word = {idx: word for word, idx in word_to_idx.items()}
    
    # 2. 生成训练样本
    def generate_training_data(corpus, word_to_idx, window_size):
        pairs = []
        for i, word in enumerate(corpus):
            if word not in word_to_idx:
                continue
            center_idx = word_to_idx[word]
            
            # 窗口内上下文
            start = max(0, i - window_size)
            end = min(len(corpus), i + window_size + 1)
            
            for j in range(start, end):
                if i != j and corpus[j] in word_to_idx:
                    pairs.append((center_idx, word_to_idx[corpus[j]]))
        return pairs
    
    # 3. 训练模型
    model = SkipGramModel(len(vocab), embedding_dim)
    optimizer = optim.Adam(model.parameters(), lr=0.01)
    
    training_pairs = generate_training_data(corpus, word_to_idx, window_size)
    
    for epoch in range(epochs):
        total_loss = 0
        for target, context in training_pairs:
            # 负采样
            neg_samples = np.random.choice(
                len(vocab), size=negative_samples, replace=False
            )
            
            target_t = torch.tensor([target])
            context_t = torch.tensor([context])
            neg_t = torch.tensor(neg_samples)
            
            optimizer.zero_grad()
            loss = model(target_t, context_t, neg_t)
            loss.backward()
            optimizer.step()
            
            total_loss += loss.item()
        
        print(f"Epoch {epoch+1}, Loss: {total_loss/len(training_pairs):.4f}")
    
    return model, word_to_idx

2.2 GloVe模型

GloVe(Global Vectors)由Pennington等人于2014年提出,融合了全局矩阵分解和局部上下文窗口两种方法的优点

2.2.1 核心思想

GloVe基于全局词共现矩阵进行训练,其损失函数设计结合了:

  1. 全局统计信息:利用词共现矩阵 ,其中 表示词 在词 的上下文中出现的次数
  2. 局部上下文:保持Skip-gram的窗口概念

2.2.2 损失函数

其中权重函数 用于处理稀有和常见词:

通常取

2.2.3 GloVe与Word2Vec的对比

特性Word2VecGloVe
训练目标预测概率(条件)重构共现概率
语料利用局部上下文全局共现矩阵
训练速度快(在线)慢(需要矩阵分解)
语义任务表现相似词效果好类比推理更优

2.3 FastText模型

FastText由Mikolov等人于2016年提出,核心创新是引入子词(subword)信息

2.3.1 子词嵌入原理

FastText将每个词表示为其字符n-gram的集合:

词 "where" 的n-gram表示 (n=3):
- <wh, whe, her, ere, re>
- 特殊边界符号: <where>

词表大小 = 所有字符n-gram的集合

词嵌入计算

2.3.2 FastText的优势

  1. 处理未登录词(OOV):可以通过组合子词表示未知词
  2. 处理形态丰富语言:如德语、土耳其语等
  3. 捕捉词缀信息:自动学习词根、词缀的语义关联
class FastTextModel(nn.Module):
    """
    FastText模型 - 使用子词嵌入
    """
    def __init__(self, vocab_size, embedding_dim, ngram_min=3, ngram_max=6):
        super().__init__()
        self.ngram_min = ngram_min
        self.ngram_max = ngram_max
        
        # 子词嵌入
        self.subword_embeddings = nn.Embedding(vocab_size, embedding_dim)
        
        # 词级别嵌入(用于完整词)
        self.word_embeddings = nn.Embedding(vocab_size, embedding_dim)
    
    def forward(self, word_ids, subword_ids):
        """
        Args:
            word_ids: 完整词ID [batch_size]
            subword_ids: 子词ID列表 [batch_size, num_subwords]
        """
        # 完整词嵌入
        word_emb = self.word_embeddings(word_ids)  # [batch, dim]
        
        # 子词嵌入平均
        subword_emb = self.subword_embeddings(subword_ids)  # [batch, num_sub, dim]
        subword_avg = torch.mean(subword_emb, dim=1)        # [batch, dim]
        
        # 组合
        combined = (word_emb + subword_avg) / 2
        
        return combined

三、上下文词向量(Contextualized Embeddings)

3.1 传统词向量的局限性

传统词向量(如Word2Vec、GloVe)的主要问题是词义消歧能力不足

传统词向量的问题示例:
"bank" 只有一个向量表示,无法区分:
- "bank account" (银行账户)
- "river bank" (河岸)

3.2 ELMo:双向LSTM词向量

ELMo(Embeddings from Language Models)由Peters等人于2018年提出,首次实现了上下文相关的词表示。

3.2.1 架构设计

输入: The mouse ate the cheese
         ↓
字符卷积层 + Highway网络
         ↓
前向LSTM: [The] → [mouse] → [ate] → [the] → [cheese]
后向LSTM: [cheese] ← [the] ← [ate] ← [mouse] ← [The]
         ↓
双向LSTM各层输出
         ↓
加权组合(学习得到)→ 最终表示

3.2.2 预训练目标

ELMo通过预测下一个词来预训练:

3.2.3 上下文表示的获取

对于每个词 ,其ELMo表示为:

其中 是可学习的层权重, 是任务特定的缩放因子。

3.3 BERT:Transformer编码器

BERT(Bidirectional Encoder Representations from Transformers)由Devlin等人于2018年提出,是NLP领域的里程碑式模型。

3.3.1 核心创新

  1. 双向Transformer编码器:同时利用左右上下文
  2. 掩码语言模型(MLM):随机mask输入词进行预测
  3. 下一句预测(NSP):学习句子间关系

3.3.2 BERT架构

class BERTModel(nn.Module):
    """
    BERT模型核心结构
    
    关键特性:
    1. Transformer编码器(双向注意力)
    2. 子词tokenization(WordPiece)
    3. 位置嵌入(可学习)
    4. 层归一化 + 残差连接
    """
    def __init__(self, config):
        super().__init__()
        self.config = config
        
        # 嵌入层
        self.embeddings = nn.ModuleDict({
            'token': nn.Embedding(config.vocab_size, config.hidden_size),
            'position': nn.Embedding(config.max_position_embeddings, config.hidden_size),
            'segment': nn.Embedding(config.type_vocab_size, config.hidden_size)
        })
        self.embed_layer_norm = nn.LayerNorm(config.hidden_size)
        
        # Transformer编码器层
        self.encoder_layers = nn.ModuleList([
            TransformerEncoderLayer(config) for _ in range(config.num_hidden_layers)
        ])
        
        # 输出层
        self.pooler = nn.Linear(config.hidden_size, config.hidden_size)
        self.pooler_activation = nn.Tanh()
    
    def forward(self, input_ids, attention_mask=None, token_type_ids=None):
        # 1. 嵌入
        seq_len = input_ids.size(1)
        position_ids = torch.arange(seq_len, device=input_ids.device)
        position_ids = position_ids.unsqueeze(0).expand_as(input_ids)
        
        token_emb = self.embeddings.token(input_ids)
        position_emb = self.embeddings.position(position_ids)
        segment_emb = self.embeddings.segment(token_type_ids or torch.zeros_like(input_ids))
        
        embeddings = token_emb + position_emb + segment_emb
        embeddings = self.embed_layer_norm(embeddings)
        
        # 2. Transformer编码
        hidden_states = embeddings
        for layer in self.encoder_layers:
            hidden_states = layer(hidden_states, attention_mask)
        
        # 3. 池化
        pooled = self.pooler(hidden_states[:, 0])  # [CLS] token
        pooled = self.pooler_activation(pooled)
        
        return {
            'last_hidden_state': hidden_states,
            'pooled_output': pooled
        }
 
class TransformerEncoderLayer(nn.Module):
    """Transformer编码器层"""
    def __init__(self, config):
        super().__init__()
        self.attention = nn.MultiheadAttention(
            config.hidden_size, config.num_attention_heads, 
            dropout=config.attention_dropout
        )
        self.attention_norm = nn.LayerNorm(config.hidden_size)
        
        self.ffn = nn.Sequential(
            nn.Linear(config.hidden_size, config.intermediate_size),
            nn.GELU(),
            nn.Linear(config.intermediate_size, config.hidden_size),
            nn.Dropout(config.hidden_dropout)
        )
        self.ffn_norm = nn.LayerNorm(config.hidden_size)
    
    def forward(self, x, attention_mask):
        # 自注意力 + 残差
        attn_out, _ = self.attention(x, x, x, key_padding_mask=attention_mask)
        x = self.attention_norm(x + attn_out)
        
        # 前馈网络 + 残差
        ffn_out = self.ffn(x)
        x = self.ffn_norm(x + ffn_out)
        
        return x

3.3.3 BERT的预训练任务

掩码语言模型(MLM)

随机选择15%的token进行mask:

  • 80%替换为[MASK]
  • 10%替换为随机词
  • 10%保持不变
def mask_tokens(inputs, tokenizer, mlm_probability=0.15):
    """
    BERT式掩码处理
    """
    labels = inputs.clone()
    
    # 特殊token不mask
    probability_matrix = torch.full(labels.shape, mlm_probability)
    special_tokens_mask = torch.tensor(
        [tokenizer.get_special_tokens_mask(val) for val in labels.tolist()]
    )
    probability_matrix.masked_fill_(special_tokens_mask.bool(), value=0.0)
    
    # 随机mask
    masked_indices = torch.bernoulli(probability_matrix).bool()
    labels[~masked_indices] = -100  # 不计算损失的标记
    
    # 实际替换操作(80/10/10策略)
    indices_replaced = torch.bernoulli(torch.full(labels.shape, 0.8)).bool() & masked_indices
    inputs[indices_replaced] = tokenizer.mask_token_id
    
    # 随机替换
    indices_random = torch.bernoulli(torch.full(labels.shape, 0.5)).bool() & masked_indices & ~indices_replaced
    random_words = torch.randint(len(tokenizer), labels.shape, dtype=torch.long)
    inputs[indices_random] = random_words[indices_random]
    
    return inputs, labels

下一句预测(NSP)

def create_nsp_data(sentences, tokenizer, seq_length):
    """
    创建NSP训练数据
    """
    for i in range(len(sentences) - 1):
        # 正样本:连续句子对
        tokens_a = tokenizer.encode(sentences[i])
        tokens_b = tokenizer.encode(sentences[i + 1])
        is_next = 1
        
        # 负样本:随机句子对
        if random.random() < 0.5:
            tokens_b = tokenizer.encode(random.choice(sentences))
            is_next = 0
        
        # 截断并组合
        ...

四、Embedding空间的几何性质

4.1 语义空间的数学结构

词向量空间具有丰富的几何结构,这些结构编码了语言学规律。

4.1.1 线性关系

词向量空间中存在系统性的线性关系:

Mikolov等人发现这种关系广泛存在:

  • 城市-国家关系:Paris - France + Italy ≈ Rome
  • 动词时态关系:walk - walked + think ≈ thought
  • 复数关系:apple - apples + car ≈ cars

这种线性可加性的数学解释

词的语义可以分解为多个语义特征的线性组合:

其中 表示语义特征向量, 是权重。

4.1.2 语义距离与相似度

词向量空间中的距离度量:

度量方法公式适用场景
余弦相似度方向相似性
欧氏距离绝对距离
曼哈顿距离稀疏向量
内积非归一化相似度
def compute_similarity(vecs1, vecs2, method='cosine'):
    """
    词向量相似度计算
    """
    if method == 'cosine':
        # 余弦相似度
        norm1 = vecs1 / np.linalg.norm(vecs1, axis=1, keepdims=True)
        norm2 = vecs2 / np.linalg.norm(vecs2, axis=1, keepdims=True)
        return np.dot(norm1, norm2.T)
    elif method == 'euclidean':
        # 欧氏距离(转相似度)
        distances = np.linalg.norm(vecs1[:, np.newaxis] - vecs2[np.newaxis, :], axis=2)
        return 1 / (1 + distances)

4.2 语义类别的空间分布

4.2.1 聚类结构

词向量空间中的语义类别呈现聚类分布:

Embedding空间示意图(2D降维可视化)

                    [水果类]
                     🍎 🍊 🍋
                   [动物类]
                 🐕 🐈 🐇 🐁
              [颜色类]
             🔴 🔵 🟢 🟡
    [数字类]                    [动作类]
      1 2 3 4 5               跑 跳 飞 游

4.2.2 类别边界的模糊性

语义类别之间的边界往往是模糊的,这反映了语义原型理论:

# 使用GMM建模语义类别的概率分布
from sklearn.mixture import GaussianMixture
 
def model_semantic_categories(word_embeddings, category_labels, n_components=3):
    """
    使用高斯混合模型建模语义类别
    
    假设每个语义类别不是严格的聚类,
    而是服从高斯分布的概率分布
    """
    categories = set(category_labels)
    category_models = {}
    
    for cat in categories:
        mask = np.array(category_labels) == cat
        cat_embeddings = word_embeddings[mask]
        
        # 每个类别用多个高斯建模(捕捉子类变体)
        gmm = GaussianMixture(n_components=min(n_components, len(cat_embeddings)))
        gmm.fit(cat_embeddings)
        category_models[cat] = gmm
    
    return category_models

4.3 语义空间的异常现象

4.3.1 性别偏差

词向量中系统性存在的性别偏见:

职业词的性别向量投影:

                    男性方向
                        ↑
            医生  ──────┼──────  护士
                        │
           工程师 ──────┼──────  保育员
                        │
                    女性方向

偏见校正方法

def debias_embeddings(embeddings, definitional_pairs, equalize_pairs):
    """
    Hard Debias算法
    
    步骤:
    1. 计算性别方向向量
    2. 对中性词去偏
    3. 均衡化性别词
    """
    # 1. 计算性别方向
    gender_direction = compute_gender_direction(embeddings, definitional_pairs)
    
    # 2. 中性词去偏
    for word in neutral_words:
        v = embeddings[word]
        v_bias = (v @ gender_direction) * gender_direction
        embeddings[word] = v - v_bias
    
    # 3. 均衡化性别对
    for (word1, word2) in equalize_pairs:
        v1, v2 = embeddings[word1], embeddings[word2]
        v_mean = (v1 + v2) / 2
        v1_new = v_mean + (v1 - v_mean).dot(gender_direction) * gender_direction
        v2_new = v_mean - (v1 - v_mean).dot(gender_direction) * gender_direction
        embeddings[word1] = v1_new
        embeddings[word2] = v2_new
 
def compute_gender_direction(embeddings, definitional_pairs):
    """
    从定义性词对计算性别方向向量
    """
    directions = []
    for word1, word2 in definitional_pairs:
        if word1 in embeddings and word2 in embeddings:
            directions.append(embeddings[word1] - embeddings[word2])
    return np.mean(directions, axis=0)

五、与认知科学的联系

5.1 分布式表征与人类语义记忆

词向量的分布式表征假说与认知科学中的分布式记忆理论高度一致。

5.1.1 特征理论 vs. 分布式表征

经典特征理论

  • 词义由一组二元或数值特征定义
  • 特征如:[+ANIMATE, +HUMAN, +MALE]等
  • 问题:无法解释语义模糊性和语境依赖

分布式表征理论

  • 词义由整个向量空间的模式定义
  • 每个维度可能对应多个相关特征的加权组合
  • 更好地解释语义泛化和原型效应

5.1.2 语义网络的神经基础

人类语义记忆的神经表征

                 前颞叶
              ┌──────────┐
              │  语义    │
              │  知识    │
              └──────────┘
         梭状回        角回
           ↓            ↓
    ┌──────┴────────────┴──────┐
    │      分布式语义表征       │
    │  (词向量空间类比)         │
    └─────────────────────────┘

研究表明,语义知识在前颞叶(ATL)区域以分布式方式存储,这与词向量的分布式表征相符。

5.2 语义启动效应

语义启动效应(Semantic Priming)现象与词向量的语义相似度计算密切相关:

实验范式

  • 启动词:“医生”
  • 目标词:“医院” vs “面包”
  • 结果:识别”医院”更快

向量空间解释

def simulate_priming(prime_embedding, target_embedding, distractor_embedding):
    """
    模拟语义启动效应
    
    假设:语义相关词对的处理更快
    因为它们在语义空间中距离更近
    """
    prime_target_distance = cosine_distance(prime_embedding, target_embedding)
    prime_distractor_distance = cosine_distance(prime_embedding, distractor_embedding)
    
    # 相关词对距离更近 → 处理更快
    return prime_target_distance < prime_distractor_distance

5.3 原型效应与最佳示例

人类在分类时表现出原型效应(Prototype Effect):某些成员比其他人更能代表类别。

词向量空间中也存在类似现象:

def compute_prototypicality(word_embeddings, category_members):
    """
    计算词的原型性
    
    原型词 = 距离类别中心最近的词
    """
    category_center = np.mean(word_embeddings[category_members], axis=0)
    
    distances = [cosine_distance(w, category_center) for w in word_embeddings[category_members]]
    prototypicality = 1 / (1 + np.array(distances))
    
    return dict(zip(category_members, prototypicality))
 
# 示例:水果类的原型性
# 原型词可能是"苹果"或"橙子"(更典型)
# 而非常见水果如"牛油果"原型性较低

5.4 概念组合与语义合成

人类能够灵活组合概念生成新意义。词向量的组合性研究探索这一能力:

简单组合(向量加法)

属性组合(向量乘法/ Hadamard积)

乘法操作可以更好地捕捉属性修饰关系。

复杂组合(神经网络组合器)

class NeuralComposition(nn.Module):
    """
    神经网络语义组合器
    比简单线性组合更能处理复杂语义组合
    """
    def __init__(self, embed_dim, hidden_dim):
        super().__init__()
        self.f = nn.Sequential(
            nn.Linear(embed_dim * 2, hidden_dim),
            nn.ReLU(),
            nn.Linear(hidden_dim, embed_dim)
        )
    
    def forward(self, head, modifier):
        """
        组合两个词的语义表示
        
        Args:
            head: 被修饰词 [batch, embed_dim]
            modifier: 修饰词 [batch, embed_dim]
        """
        combined = torch.cat([head, modifier], dim=-1)
        result = self.f(combined)
        return result

六、实践应用与工具

6.1 主流词向量工具

工具语言特点预训练模型
gensimPythonWord2Vec/FastText实现多语言
spaCyPython集成词向量en_core_web_md
TensorFlow Hub多语言Universal Sentence Encoder17种语言
HuggingFacePythonBERT系列模型100+模型
fastTextC++/Python快速子词嵌入157种语言

6.2 词向量可视化

# 使用t-SNE/UMAP可视化词向量
from sklearn.manifold import TSNE
import plotly.express as px
 
def visualize_embeddings(word_embeddings, words, labels=None):
    """
    词向量可视化
    """
    # 降维
    tsne = TSNE(n_components=2, random_state=42)
    coords = tsne.fit_transform(word_embeddings)
    
    # 绘制
    df = pd.DataFrame({
        'x': coords[:, 0],
        'y': coords[:, 1],
        'word': words,
        'label': labels or ['unknown'] * len(words)
    })
    
    fig = px.scatter(df, x='x', y='y', text='word', color='label')
    fig.update_traces(textposition='top center')
    fig.show()

参考文献与推荐阅读

  1. Mikolov, T., et al. (2013). Distributed representations of words and phrases and their compositionality. NeurIPS.
  2. Pennington, J., Socher, R., & Manning, C. D. (2014). GloVe: Global vectors for word representation. EMNLP.
  3. Bojanowski, P., et al. (2017). Enriching word vectors with subword information. TACL, 5, 135-146.
  4. Peters, M. E., et al. (2018). Deep contextualized word representations. NAACL-HLT.
  5. Devlin, J., et al. (2019). BERT: Pre-training of deep bidirectional transformers for language understanding. NAACL-HLT.
  6. Rogers, A., et al. (2020). A primer in BERTology: What we know about how BERT works. TACL, 8, 842-866.

关联文档