CBOW模型算法实战:词嵌入矩阵调整算法实例与性能优化

1次阅读
没有评论

共计 1889 个字符,预计需要花费 5 分钟才能阅读完成。

image.webp

背景痛点分析

CBOW 模型在训练过程中,词嵌入矩阵的调整常遇到三个典型问题:

CBOW 模型算法实战:词嵌入矩阵调整算法实例与性能优化

  • 高频词主导 :语料中出现频率高的词(如 ”the”、”and”)会占据大部分梯度更新,导致低频词难以充分学习
  • 低频词欠拟合 :出现次数少的词因样本不足,其向量表示往往收敛到次优解
  • 维度爆炸 :当词表规模大(如 10 万 +)时,传统的 softmax 计算会带来巨大的计算开销

技术对比:优化方案选型

针对上述问题,主流优化技术有:

  1. Negative Sampling
  2. 适合:大规模词表、注重训练效率的场景
  3. 原理:通过采样负例简化计算,公式:
    $$
    \log\sigma(v_{w_o}^T v_{w_I}) + \sum_{i=1}^k \mathbb{E}{w_i \sim P_n(w)}[\log\sigma(-v)]
    $$}^T v_{w_I

  4. Hierarchical Softmax

  5. 适合:对低频词精度要求高的场景
  6. 原理:使用霍夫曼树将计算复杂度从 O(V) 降到 O(logV)

核心实现详解

矩阵初始化技巧

词向量初始值建议采用:

$$
W \in \mathbb{R}^{V\times d}, w_{ij} \sim U(-\frac{\sqrt{6}}{\sqrt{d + f_i}}, \frac{\sqrt{6}}{\sqrt{d + f_i}})
$$

其中 $f_i$ 为词频,实现词频敏感初始化:

import torch
import math

def init_embedding(vocab_size, embed_dim, word_freq):
    """
    vocab_size: 词表大小
    embed_dim: 向量维度 
    word_freq: 词频字典
    """
    embedding = torch.empty(vocab_size, embed_dim)
    for i in range(vocab_size):
        freq = word_freq.get(i, 1)  # 默认频率为 1
        scale = math.sqrt(6 / (embed_dim + freq))
        torch.nn.init.uniform_(embedding[i], -scale, scale)
    return embedding

动态学习率调整

采用反向词频加权学习率:

class AdaptiveLR:
    def __init__(self, base_lr=0.025, min_lr=0.0001):
        self.base_lr = base_lr
        self.min_lr = min_lr

    def get_lr(self, word_freq, epoch, max_epoch):
        """
        word_freq: 当前词的频率 (0~1)
        epoch: 当前训练轮次
        """
        # 随训练轮次线性衰减
        progress = 1 - epoch / max_epoch
        # 低频词获得更高学习率
        freq_factor = 1 - word_freq  
        return max(self.min_lr, self.base_lr * progress * freq_factor)

性能优化实验

在 WikiText- 2 数据集上的测试结果:

词表规模 维度 内存占用 (MB) 每秒训练词数
10,000 300 12 45,000
50,000 300 59 28,000
100,000 300 118 15,000
100,000 100 39 32,000

避坑实践指南

  1. 维度选择经验公式
    $$
    d = 100 \times \sqrt[4]{\frac{V}{1000}}
    $$
    其中 V 为词表大小

  2. 梯度消失诊断

  3. 检查 embedding 矩阵更新的 L2 范数:
    torch.norm(embedding.grad, p=2)
  4. 正常值范围:1e-3 ~ 1e-5

  5. OOV 处理方案

  6. 为未登录词分配随机子词向量(如 char-n-gram)
  7. 示例代码:
    def get_oov_embedding(word, char_ngrams, n=3):
        """通过字符 n -gram 组合生成 OOV 词向量"""
        ngrams = [word[i:i+n] for i in range(len(word)-n+1)]
        valid_ngrams = [ng for ng in ngrams if ng in char_ngrams]
        if not valid_ngrams:
            return torch.zeros(embed_dim)
        return torch.mean(char_ngrams[valid_ngrams], dim=0)

延伸思考

如何将 CBOW 的词嵌入优化方法适配到 BERT 的 Embedding 层? 考虑以下方向:

  1. 将动态学习率策略应用于 BERT 的 WordPiece 嵌入
  2. 借鉴词频敏感的初始化方法改进 BERT 的 position embedding
  3. 使用分层 softmax 加速 BERT 的 masked language model 预训练
正文完
 0
评论(没有评论)