一、背景

在 SFT(Supervised Fine-Tuning)阶段为 LLM 添加 special_token(例如用于分隔用户和助手的 <|user|>,<|assistant|> 等)是一个非常常见的操作,但如果处理不当,确实会严重影响模型原有的性能。

核心问题在于:新添加的 token 在模型的词向量矩阵(Embedding Matrix)和输出层(LM Head)中没有对应的、经过预训练的向量,它们是“从零开始”的,这会给模型带来巨大的扰动。

为了尽可能保持原模型的训练效果,你需要遵循一套系统性的策略,核心思想是:让新 token 的初始状态尽可能“平滑”地融入现有模型,并通过高效的微调方式让模型“温柔”地学会其用法。

以下是详细的步骤、策略和代码示例:

核心问题所在:

  • 随机初始化的 Embedding:当你调用 model.resize_token_embeddings() 时,新增的 token 向量默认是随机初始化的。这个随机向量与模型中其他经过数十亿 token 预训练、具有丰富语义信息的向量相比,完全是“噪声”。模型在处理它时会感到困惑。

  • 随机初始化的 LM Head:同样,在模型的输出层(通常是 lm_head),也增加了一个新的、随机初始化的 logit,模型在预测这个新 token 时完全是瞎猜。

  • 训练初期的梯度震荡:在 SFT 初期,当模型遇到这些新 token 时,会产生巨大的 loss 和梯度,这些梯度会反向传播并剧烈地更新模型参数,可能会破坏掉模型在预训练阶段学到的通用知识,导致“灾难性遗忘”(Catastrophic Forgetting)。

二、核心步骤

步骤一:向 Tokenizer 中添加新 Token

这是基础操作,确保你的 tokenizer 能够识别和编码新的特殊 token。


from transformers import AutoTokenizer, AutoModelForCausalLM
model_name = "meta-llama/Llama-2-7b-hf"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
# 定义新的特殊token
special_tokens_to_add = ["<|user|>", "<|assistant|>", "<|endoftext|>"] 
# 添加到tokenizer
tokenizer.add_special_tokens({
    "additional_special_tokens": special_tokens_to_add,
    "pad_token": "<|endoftext|>" # 也可以用已有的,或新加一个
})
# 验证一下
print(f"Tokenizer vocabulary size: {len(tokenizer)}")

步骤二:调整模型 Embedding 和 LM Head 的大小

模型需要知道词汇表变大了,并为新 token 分配空间。

model.resize_token_embeddings(len(tokenizer))
print(f"Model input embedding size: {model.get_input_embeddings().weight.shape[0]}")
print(f"Model output head size: {model.get_output_embeddings().weight.shape[0]}")

步骤三:【最关键】初始化新 Token 的 Embedding

不要使用默认的随机初始化!这是保持模型性能的关键。你有以下几种优质选择:

策略 A:使用现有 Token Embedding 的均值(最推荐、最通用)

这种方法假设新 token 的语义应该是一个“中性”的、不带来强烈偏见的起始点。

取所有或部分现有 token embedding 的平均值,可以有效地将新 token 放置在语义空间的中心位置,减少对模型的初始冲击。

import torch
# 获取旧的embedding矩阵和lm_head矩阵
input_embeddings = model.get_input_embeddings()
output_embeddings = model.get_output_embeddings()
# 计算旧词汇表的平均embedding
old_vocab_size = input_embeddings.weight.shape[0] - len(special_tokens_to_add)
avg_embedding = input_embeddings.weight.data[:old_vocab_size].mean(dim=0, keepdim=True)
# 将这个平均值赋给所有新的token
with torch.no_grad():
    # 初始化input embeddings
    input_embeddings.weight.data[old_vocab_size:] = avg_embedding.clone()
    # 如果lm_head和input_embeddings是绑定的,这一步可能不是必须的,但为了保险起见可以做
    if output_embeddings is not None and output_embeddings.weight.shape[0] == len(tokenizer):
         output_embeddings.weight.data[old_vocab_size:] = avg_embedding.clone()
print("New special token embeddings have been initialized with the average of old embeddings.")

策略 B:使用语义相近的 Token Embedding

如果你的新 token 有明确的语义,可以找一个或多个现有的、语义相近的 token 来初始化它。 

例如,对于 <|user|>,你可以使用 “user”, “User”, “human” 等 token 的 embedding 的平均值。


# 示例:用 "user" 和 "assistant" 的embedding来初始化
user_token_id = tokenizer.encode("user", add_special_tokens=False)[0]
assistant_token_id = tokenizer.encode("assistant", add_special_tokens=False)[0]
user_embedding = input_embeddings.weight.data[user_token_id].clone()
assistant_embedding = input_embeddings.weight.data[assistant_token_id].clone()
new_user_token_id = tokenizer.convert_tokens_to_ids("<|user|>")
new_assistant_token_id = tokenizer.convert_tokens_to_ids("<|assistant|>")
with torch.no_grad():
    input_embeddings.weight.data[new_user_token_id] = user_embedding
    input_embeddings.weight.data[new_assistant_token_id] = assistant_embedding
    # ... 对其他新token和lm_head做同样操作

这种方法更精确,但操作更繁琐,且不一定总能找到合适的对应词。

步骤四:选择合适的微调方法

初始化完成后,如何训练也至关重要。

策略 A:使用 PEFT(如 LoRA)进行微调(强烈推荐)

Parameter-Efficient Fine-Tuning (PEFT) 是保持原模型性能的利器。

LoRA(Low-Rank Adaptation)通过在模型的线性层旁边增加小型的、可训练的“适配器”矩阵来进行微调,而原始模型的绝大部分权重保持冻结。

为什么有效? 因为它不会去剧烈改动预训练好的权重,从而最大程度地保留了模型的通用能力。训练的重点放在了学习新 token 的用法和遵循 SFT 指令上。

操作:在使用 peft 库时,你可以选择将 embed_tokens 和 lm_head 也作为 LoRA 的目标模块(target_modules),这样模型在学习 LoRA 适配器的同时,也能微调新 token 的 embedding。


from peft import LoraConfig, get_peft_model
# 配置LoRA
lora_config = LoraConfig(
    r=16,
    lora_alpha=32,
    target_modules=["q_proj", "v_proj", "k_proj", "o_proj", "gate_proj", "up_proj", "down_proj"], # 根据模型结构调整
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM",
    # modules_to_save=["embed_tokens", "lm_head"] # 这是一个选项,可以让embedding层和输出层也参与训练
)
# 应用LoRA到模型
peft_model = get_peft_model(model, lora_config)
peft_model.print_trainable_parameters()

注意:modules_to_save 会让指定的模块(如 embedding 层)进行全参数微调,而不是 LoRA 微调。

这对于新 token 是必要的,因为它们需要从头学习。LoRA 则保护了其他层的权重。

策略 B:分阶段微调(如果不用 LoRA)

如果你坚持全量微调(Full Fine-tuning),可以考虑一个“预热”阶段:

第一阶段:冻结模型绝大部分层,只训练 embed_tokens 和 lm_head 层。使用包含新 token 的语料进行少量步数的训练。这能让新 token 的 embedding 先“稳定下来”,找到一个比较合理的位置。

第二阶段:解冻所有层,进行完整的 SFT。

这种方法比直接全量微调要平滑,但比 LoRA 更耗资源且风险更高。

三、总结与最佳实践

添加 Token:使用 tokenizer.add_special_tokens。

调整模型大小:使用 model.resize_token_embeddings。

智能初始化(关键):强烈推荐使用所有旧 token embedding 的均值来初始化新 token 的 embedding。这是效果和简便性之间最好的平衡。

高效微调(关键):强烈推荐使用 LoRA 进行 SFT。它能有效防止灾难性遗忘,将训练的“火力”集中在任务适应和新 token 学习上,而不是破坏原有知识。确保 SFT 数据中充分、正确地使用了这些新 token。

数据质量:确保你的 SFT 数据格式统一,新添加的 special_token 被正确地用作结构分隔符。模型需要通过大量高质量的样本来学习它们的“语法”功能。

评估:在 SFT 后,不仅要评估模型在目标任务上的表现,还应该在一些通用的基准(如 MMLU、C-Eval)上进行测试,以检查模型的通用能力是否出现大幅下降。

遵循以上策略,你就可以在 SFT 阶段平稳地引入 special_token,同时最大程度地保留大模型强大的基础能力。

四、AI大模型从0到精通全套学习大礼包

我在一线互联网企业工作十余年里,指导过不少同行后辈。帮助很多人得到了学习和成长。

只要你是真心想学AI大模型,我这份资料就可以无偿共享给你学习。大模型行业确实也需要更多的有志之士加入进来,我也真心希望帮助大家学好这门技术,如果日后有什么学习上的问题,欢迎找我交流,有技术上面的问题,我是很愿意去帮助大家的!

如果你也想通过学大模型技术去帮助就业和转行,可以扫描下方链接👇👇
大模型重磅福利:入门进阶全套104G学习资源包免费分享!
在这里插入图片描述

01.从入门到精通的全套视频教程

包含提示词工程、RAG、Agent等技术点
在这里插入图片描述

02.AI大模型学习路线图(还有视频解说)

全过程AI大模型学习路线

在这里插入图片描述
在这里插入图片描述

03.学习电子书籍和技术文档

市面上的大模型书籍确实太多了,这些是我精选出来的

在这里插入图片描述
在这里插入图片描述

04.大模型面试题目详解

在这里插入图片描述

在这里插入图片描述

05.这些资料真的有用吗?

这份资料由我和鲁为民博士共同整理,鲁为民博士先后获得了北京清华大学学士和美国加州理工学院博士学位,在包括IEEE Transactions等学术期刊和诸多国际会议上发表了超过50篇学术论文、取得了多项美国和中国发明专利,同时还斩获了吴文俊人工智能科学技术奖。目前我正在和鲁博士共同进行人工智能的研究。

所有的视频由智泊AI老师录制,且资料与智泊AI共享,相互补充。这份学习大礼包应该算是现在最全面的大模型学习资料了。

资料内容涵盖了从入门到进阶的各类视频教程和实战项目,无论你是小白还是有些技术基础的,这份资料都绝对能帮助你提升薪资待遇,转行大模型岗位。

在这里插入图片描述
在这里插入图片描述

智泊AI始终秉持着“让每个人平等享受到优质教育资源”的育人理念‌,通过动态追踪大模型开发、数据标注伦理等前沿技术趋势‌,构建起"前沿课程+智能实训+精准就业"的高效培养体系。

课堂上不光教理论,还带着学员做了十多个真实项目。学员要亲自上手搞数据清洗、模型调优这些硬核操作,把课本知识变成真本事‌!

在这里插入图片描述
如果说你是以下人群中的其中一类,都可以来智泊AI学习人工智能,找到高薪工作,一次小小的“投资”换来的是终身受益!

应届毕业生‌:无工作经验但想要系统学习AI大模型技术,期待通过实战项目掌握核心技术。

零基础转型‌:非技术背景但关注AI应用场景,计划通过低代码工具实现“AI+行业”跨界‌。

业务赋能 ‌突破瓶颈:传统开发者(Java/前端等)学习Transformer架构与LangChain框架,向AI全栈工程师转型‌。

👉获取方式:
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓

在这里插入图片描述

Logo

中国智能体开发者社区,聚焦智能体与大模型开发,提供前沿资讯、实用工具链、开源项目及行业案例。通过技术沙龙、开发者大赛等活动,促进经验交流与协作,助力开发者快速构建创新智能应用。

更多推荐