当前位置: 首页 > news >正文

【16】Transformers快速入门:Token Embedding


课程模块:新 Token Embedding 的智慧初始化

目标: 理解为何需要精心初始化新 Token 的 Embedding,掌握多种初始化策略(包括零初始化、复制已有 Embedding、语义组合初始化),并了解其适用场景。

核心概念: Embedding 初始化,小样本学习 (Few-shot Learning),微调 (Fine-tuning),torch.no_grad()requires_grad,语义描述平均


1. 为什么不能总是随机初始化?—— 小样本学习的困境

当我们通过 model.resize_token_embeddings(len(tokenizer)) 添加新 Token 时,它们的 Embedding 会被随机初始化。这在有充足训练数据进行充分微调的场景下通常没问题——模型有足够的机会学习这些新 Token 的正确表示。

但是!在以下场景,随机初始化会带来问题:

  • 小样本学习 (Few-shot Learning): 只有非常少的标注数据(例如几十条甚至几条)可用于微调。
  • 资源受限的微调: 训练轮次 (epochs) 很少,或者学习率设置得很小。
  • 新 Token 出现频率低: 即使数据总量不少,但新 Token 本身在训练数据中出现次数极少。

问题本质:

  • 随机初始化的 Embedding 起点是“乱码”。
  • 训练数据少或训练不充分意味着模型调整 Embedding 的幅度非常有限
  • 研究表明,在这些情况下,新 Token 的 Embedding 最终值会非常接近其随机初始值,几乎没有学到有意义的语义信息。
  • 模型对这些新 Token 的处理效果会很差,因为它们本质上还是“随机的噪音”。

结论: 在小样本或训练不充分的场景下,为关键的新 Token(尤其是功能性的特殊 Token)提供合理的初始 Embedding 至关重要! 这相当于给模型一个“好起点”,让它更容易在有限的训练中学会使用这些 Token。


2. 初始化策略:从简单到智能

我们有多种策略来初始化新 Token 的 Embedding:

  • 策略一:零初始化 (Zero Initialization)

    • 做法: 将新 Token 的 Embedding 向量所有维度设置为 0
    import torch
    from transformers import AutoModelmodel = AutoModel.from_pretrained("bert-base-uncased")
    # ... (假设已添加新 Token 并 resize_token_embeddings)with torch.no_grad():  # 重要!初始化操作不参与梯度计算# 假设新添加的两个 Token 在 Embedding 矩阵的最后两行model.embeddings.word_embeddings.weight[-2:, :] = torch.zeros(2, model.config.hidden_size, requires_grad=True)
    
    • 优点: 极其简单。
    • 缺点:
      • 0 向量在向量空间中通常没有特殊意义。
      • 模型需要从头开始学习其表示,起点较差。
    • 适用场景: 当新 Token 纯粹作为占位符,或者你确实没有更好的先验知识时。不推荐作为首选。
  • 策略二:复制已有 Token (Copy from Existing Token)

    • 做法: 选择一个与新 Token 语义或功能相关的已有 Token,将其 Embedding 复制给新 Token。
    import torch
    from transformers import AutoTokenizer, AutoModeltokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
    model = AutoModel.from_pretrained("bert-base-uncased")
    # ... (假设已添加新 Token `[ENT_START]`, `[ENT_END]` 并 resize_token_embeddings)# 选择一个语义相关的词,比如 "entity"
    source_token = "entity"
    source_token_id = tokenizer.convert_tokens_to_ids(source_token)
    source_embedding = model.embeddings.word_embeddings.weight[source_token_id].clone().detach()with torch.no_grad():# 将 [ENT_START] 和 [ENT_END] 都初始化为 "entity" 的 Embeddingmodel.embeddings.word_embeddings.weight[-2:, :] = source_embedding.unsqueeze(0).repeat(2, 1)# 或者分别初始化(如果需要不同)# model.embeddings.word_embeddings.weight[-2, :] = source_embedding  # [ENT_START]# model.embeddings.word_embeddings.weight[-1, :] = source_embedding  # [ENT_END]
    
    • 优点:
      • 简单有效,为新 Token 提供了一个有语义基础的起点
      • 模型更容易理解新 Token 的大致角色(例如,[ENT_START] 初始化为 "entity",暗示它与实体概念相关)。
    • 缺点:
      • 可能过于简化,忽略了新 Token 的特殊性(如 [ENT_START] 是一个标记符,而 "entity" 是一个名词)。
    • 适用场景: 当新 Token 与词表中某个已有 Token 语义高度相关时。这是最常见且推荐的简单策略
  • 策略三:语义描述平均 (Semantic Description Averaging)

    • 做法: 为新 Token 定义一个描述其含义或功能的短语。计算这个短语中所有单词 Embedding 的平均值,作为新 Token 的初始 Embedding。
    import torch
    from transformers import AutoTokenizer, AutoModeltokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
    model = AutoModel.from_pretrained("bert-base-uncased")
    # ... (假设已添加新 Token `[ENT_START]`, `[ENT_END]` 并 resize_token_embeddings)# 为每个新 Token 定义描述短语
    descriptions = {"[ENT_START]": "start of entity",  # 描述 [ENT_START] 的含义"[ENT_END]": "end of entity"        # 描述 [ENT_END] 的含义
    }with torch.no_grad():# 倒序处理新 Token(假设它们添加在词表末尾)for i, (token, desc) in enumerate(reversed(descriptions.items()), start=1):# 分词描述短语desc_tokens = tokenizer.tokenize(desc)desc_ids = tokenizer.convert_tokens_to_ids(desc_tokens)# 获取描述短语中所有 Token 的 Embeddingdesc_embeddings = model.embeddings.word_embeddings.weight[desc_ids]# 计算平均 Embeddingnew_embedding = desc_embeddings.mean(dim=0)# 赋值给新 Token (注意索引:-i 表示倒数第 i 个 Token)model.embeddings.word_embeddings.weight[-i, :] = new_embedding.clone().detach()
    
    • 优点:
      • 提供更丰富、更精确的语义起点。
      • 能够捕捉新 Token 的复合含义(例如 [ENT_START] 结合了 "start""entity" 的概念)。
      • 特别适合功能标记复杂概念的初始化。
    • 缺点: 实现稍复杂,需要为每个新 Token 精心设计描述短语。
    • 适用场景: 当新 Token 的含义无法用单个已有词完美表达,或者需要更精细控制其初始语义时。这是最智能、效果通常最好的策略

3. 技术细节与最佳实践

  • torch.no_grad() 是必须的:

    with torch.no_grad():# 在这里进行 Embedding 赋值操作
    
    • 为什么? Embedding 初始化是一个设置参数初始值的操作,不是模型学习过程的一部分。我们不希望这个操作产生梯度(grad)或者影响反向传播。torch.no_grad() 上下文管理器确保赋值操作在无梯度计算的环境中进行,避免不必要的计算开销和潜在错误。
  • requires_grad=True 是必要的:

    new_embedding = ... # 从其他地方获取的 Embedding 向量
    model.embeddings.word_embeddings.weight[-i, :] = new_embedding.clone().detach().requires_grad_(True)
    
    • 为什么?
      1. .clone().detach():确保我们赋值的是一个新的、独立的 Tensor,并且断开了它可能来自原始 Embedding 的计算图(避免意外影响)。
      2. .requires_grad_(True):明确设置这个新 Embedding 需要计算梯度。这是关键!它告诉 PyTorch 在后续的微调训练中,这个新 Token 的 Embedding 参数是需要被优化(更新)的。如果不设置 requires_grad=True,模型在训练时就不会更新这个 Embedding,它就永远停留在初始值了。
  • 获取 Embedding 层:

    • 对于 transformers 库提供的模型(如 BertModel, AutoModel),Embedding 层通常位于 model.embeddings.word_embeddings
    • 对于自定义模型或某些特定架构,可能需要通过 model.get_input_embeddings() 来获取 Embedding 层。
    embedding_layer = model.get_input_embeddings()
    with torch.no_grad():embedding_layer.weight[-1, :] = ...  # 修改最后一个 Token 的 Embedding
    
  • 新 Token 的位置:

    • 新 Token 通过 add_tokensadd_special_tokens 添加后,通常位于词表的末尾
    • 因此,在 model.resize_token_embeddings(len(tokenizer)) 后,新 Token 的 Embedding 对应着 Embedding 矩阵的最后几行weight[-num_new_tokens:, :])。
    • 可以通过 tokenizer.convert_tokens_to_ids("[NEW_TOKEN]") 获取新 Token 的确切 ID 来验证其位置。
  • 何时初始化?

    • 初始化操作必须在调用 model.resize_token_embeddings(len(tokenizer)) 之后进行。
    • 推荐在添加 Token、调整 Embedding 大小后立即进行初始化,然后再开始模型的微调训练。
  • 微调依然必要:

    • 切记! 无论采用哪种初始化策略,都不能替代后续的微调训练
    • 初始化只是提供了一个更好的起点(Prior)。
    • 模型仍然需要根据你的具体任务数据调整(Adapt) 这些新 Token 的 Embedding,使其更好地服务于任务目标(例如,让 [ENT_START] 更精确地标记实体开始的位置)。

4. 策略选择指南与总结

初始化策略优点缺点推荐场景
零初始化极其简单起点差,模型学习负担重无更好选择时的备用方案;纯粹占位符
复制已有 Token简单有效,提供语义基础起点可能过于简化,忽略特殊性新 Token 与某个已有词语义高度相似时(最常见)
语义描述平均提供更丰富、精确的语义起点需设计描述短语,实现稍复杂新 Token 含义复杂或需精细控制初始语义时

核心原则:

  1. 避免纯随机初始化: 尤其是在小样本训练不充分的场景下。
  2. 选择有意义的起点: 利用模型预训练阶段学到的丰富语义知识(通过复制或平均已有 Embedding),为新 Token 提供一个合理的初始表示。
  3. 精心设计描述短语: 对于语义描述平均法,描述短语的质量直接影响初始化效果。短语应准确、简洁地反映新 Token 的核心含义或功能。
  4. 技术细节要严谨: 使用 torch.no_grad() 和正确设置 requires_grad=True
  5. 微调不可或缺: 初始化只是开始,必须通过任务数据的微调让模型学会如何有效地使用新 Token。

提示:

  1. 实验对比: 对一个分类任务(如情感分析),添加一个特殊 Token [REVIEW] 放在每个评论开头。分别尝试:
    • 随机初始化(默认)
    • 初始化为 "review" 词的 Embedding
    • 初始化为 "text""opinion" 的平均 Embedding
    • 初始化为 "this is a review text" 的平均 Embedding
      极少量训练数据(如 20 条)下微调模型,比较验证集准确率。观察哪种初始化策略在小样本下表现最好?
  2. 理解梯度: 在初始化新 Token Embedding 时,故意省略 .requires_grad_(True)。训练模型几个 epoch 后,打印出新 Token 的 Embedding。观察它是否被更新了?为什么?
  3. 设计描述: 假设你要添加一个 Token [MEDICAL_TERM] 来标记医学专业术语。设计 2-3 个不同的描述短语(如 "medical term", "specialized medical vocabulary", "clinical terminology")。用代码实现语义描述平均初始化,并打印出不同描述得到的初始 Embedding(取前 5 维)。它们相似吗?为什么?
  4. 思考: 如果添加的新 Token 是一个表情符号,比如 "😊",哪种初始化策略最合适?为什么?(提示:考虑表情符号的语义通常与“开心”、“微笑”相关)
http://www.dtcms.com/a/329865.html

相关文章:

  • JavaSE高级-01
  • cuDNN详解,从什么是cuDNN到实际应用过程
  • 肖臻《区块链技术与应用》第十二讲:比特币是匿名的吗?—— 深入解析匿名性、隐私风险与增强技术
  • 区块链DApp:颠覆未来的去中心化应用
  • 【Redis笔记】Redis 的通用命令
  • 字符串匹配算法
  • 认知系统的架构: 认知残余三角形、认知主体意识 和认知演进金字塔
  • UniApp开发常见问题及解决办法
  • 摆脱例行 SQL 报表的隐性成本:用 n8n 构建四节点自动化报告流程
  • 锂电池自动化生产线:智能制造重塑能源产业格局
  • ECCV-2018《Variational Wasserstein Clustering》
  • 【HTML】在页面中画一条0.5px的线
  • 聚焦用户价值,腾讯音乐Q2实现坚实增长
  • c++的运算符优先级
  • 嵌入式第二十七天(UI相关技术(framebuffer))
  • 如何通过api访问SearXNG
  • Open3d:从mesh中采样点云的两个函数
  • 不止于GET:掌握POST报错注入的精髓
  • HTML第二次作业
  • wandb: Network error (SSLError), entering retry loop
  • JavaWeb-XML、HTTP协议和Tomcat服务器
  • TF - IDF算法面试与工作常见问题全解析
  • 51单片机-51单片机最小系统
  • 基于大模型增强的知识图谱的嵌入学习模型的研究
  • 人工智能——CNN基础:卷积和池化
  • 【DL】最优化理论和深度学习
  • nginx匹配规则
  • 2023 年全国硕士研究生招生考试真题笔记
  • 部署在linux上的java服务老是挂掉[排查日志]
  • Spring Boot调用优化版AI推理微服务 集成 NVIDIA NIM指南