今天就给大家分享一个从DeepSeek-R1-1.5B到Qwen-2.5-1.5B的模型蒸馏案例,帮助刚接触的小伙伴轻松学明白。

一、模型蒸馏目标

模型蒸馏,简单来说,就是把大模型(教师模型)里有用的知识“传授”给小模型(学生模型)。这次从DeepSeek-R1-1.5B到Qwen-2.5-1.5B的模型蒸馏,主要有这几个目标:

  • 知识迁移:让Qwen-2.5-1.5B学到DeepSeek-R1-1.5B的推理能力,像多轮逻辑推理、代码生成这些厉害的本事。
  • 效率优化:在保证性能不咋下降的情况下,减少模型推理时的成本,比如降低内存占用、缩短延迟时间。
  • 兼容性:确保蒸馏后的Qwen-2.5-1.5B还能和原来一样,支持对话、多语言这些功能。

二、环境搭建准备

要进行模型蒸馏,先得把“战场”布置好,也就是搭建合适的环境。

(一)安装PyCharm

PyCharm是咱们开发的好帮手,去这个地址下载:https://www.jetbrains.com.cn/en-us/pycharm/download/?section=windows 。建议选择PyCharm Community Edition版本,下载完按照提示一步步安装就行。

(二)安装依赖库

这几个Python库是必不可少的,在命令行里运行下面的代码安装:

pip install torch torchvision transformers datasets pip install accelerate # 加速分布式训练 pip install evaluate # 评估指标 

(三)硬件要求

理想情况下,最好有NVIDIA GPU,像V100、A100这些,而且显存至少得24GB。CUDA也要安装和PyTorch兼容的版本,比如CUDA 11.7。要是硬件条件有限,像我这次用的是2核Intel CPU(Intel(R) Core(TM) i7 – 10700F CPU @ 2.90GHz 2.90 GHz) ,搭配16G内存,再设置了20G虚拟内存,也能做模型蒸馏,就是时间会长点,大概30天左右。设置虚拟内存的方法大家可以自行搜索一下。

(四)下载模型与数据集

  • 教师模型下载:DeepSeek-R1-1.5B得从官方或者可信的地方下载。离线下载的话,可以在命令行这么操作:
$env:HF_ENDPOINT = "https://hf-mirror.com" huggingface-cli download deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B --local-dir ./models/DeepSeek-R1-Distill-Qwen-1.5B --local-dir-use-symlinks False 
  • 学生模型下载:Qwen-2.5-1.5B可以从阿里云或者Hugging Face获取。离线下载同样在命令行操作:
$env:HF_ENDPOINT = "https://hf-mirror.com" huggingface-cli download Qwen/Qwen2.5-1.5B --local-dir ./models/qwen2.5-1.5B --local-dir-use-symlinks False 
  • 数据集下载:推荐用大规模文本数据集,像wikitex、Wikipedia、BooksCorpus、OpenWebText这些。这里以从https://www.kaggle.com/datasets/jayanthbontha/wikitext下载为例,大家根据实际情况下载就行。

三、日志记录与路径设置

在整个模型蒸馏过程中,记录日志能帮我们及时发现问题,知道程序运行到哪一步了。下面这段代码就是用来配置日志,同时获取当前脚本文件的路径和所在目录的:

import os import logging # 配置日志,设置日志级别为INFO,指定日志格式 logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logger = logging.getLogger(__name__) # 获取当前脚本文件的绝对路径 current_script_path = os.path.abspath(__file__) logger.info(f"Current script path: {current_script_path}") # 获取当前脚本文件所在的目录 current_script_dir = os.path.dirname(current_script_path) logger.info(f"Current script directory: {current_script_dir}") 

四、模型加载与配置

(一)加载教师模型

加载教师模型DeepSeek-R1-1.5B时,AutoTokenizer.from_pretrainedAutoModelForCausalLM.from_pretrained这两个函数很关键,它们能帮我们把模型和分词器加载到项目里。这里设置local_files_only=True,表示只用本地文件,不联网下载。

# 加载教师模型(DeepSeek-R1:1.5B) teacher_model_name = os.path.join(current_script_dir, "../models/DeepSeek-R1-Distill-Qwen-1.5B") logger.info(f"Loading teacher model: {teacher_model_name}") teacher_tokenizer = AutoTokenizer.from_pretrained(teacher_model_name, local_files_only=True ) teacher_model = AutoModelForCausalLM.from_pretrained(teacher_model_name, local_files_only=True ) 

下面是一些关键参数的说明:

参数名描述示例值
pretrained_model_name_or_path预训练模型名称(如bert-base-uncased)或本地路径“DeepSeek/r1-1.5b”
use_fast是否使用基于tokenizers库的快速分词器(默认True)True / False
tokenizer_type手动指定分词器类型(如BertTokenizer)“BertTokenizer”
revision指定模型版本(如”v1.0″)“main”
subfolder模型仓库中的子目录路径(若模型文件不在根目录)“models/tokenizer”
cache_dir指定缓存目录(默认为~/.cache/huggingface/transformers)“/path/to/cache”
force_download是否强制重新下载模型文件(覆盖现有文件)False
local_files_only仅使用本地文件,不尝试从网络下载False
trust_remote_code允许执行远程代码(如自定义模型需要时)False

(二)加载学生模型

加载学生模型Qwen-2.5-1.5B的过程和教师模型类似:

# 加载学生模型(Qwen) student_model_name = os.path.join(current_script_dir, "../models/qwen2.5-1.5B") # 确保模型名称正确 logger.info(f"Loading student model: {student_model_name}") student_tokenizer = AutoTokenizer.from_pretrained(student_model_name, local_files_only=True ) student_model = AutoModelForCausalLM.from_pretrained(student_model_name, local_files_only=True ) 

参数说明和教师模型加载时基本一样,就不再重复啦。

(三)数据预处理函数

数据预处理很重要,它能把原始数据处理成模型能“看懂”的格式。dataset.map()函数可以对数据集进行批量预处理,设置batched=True,它就会把数据集分批处理,这样效率更高。

# 数据预处理 logger.info(f"Preprocess_function") def preprocess_function(examples): return teacher_tokenizer(examples["text"], truncation=True, padding="max_length", max_length=512) logger.info("Preprocessing train dataset") train_dataset = train_dataset.map(preprocess_function, batched=True) logger.info("Preprocessing eval dataset") eval_dataset = eval_dataset.map(preprocess_function, batched=True) 

要注意,preprocess_function必须返回一个字典,而且里面每个值对应的列表长度要和输入的batch大小一样。比如输入batch有3个样本,返回的每个键对应的列表长度也得是3。

(四)数据收集器

DataCollatorForLanguageModeling是用来整理数据的,它能根据任务需求对输入数据进行预处理,比如生成训练样本。

# 数据收集器 logger.info("DataCollatorForLanguageModeling") data_collator = DataCollatorForLanguageModeling(tokenizer=teacher_tokenizer, mlm=False) 

这里的mlm参数很关键:

  • mlm=True:会随机把输入里的部分token变成[MASK]标记,这是像BERT训练时用的方法。
  • mlm=False:不会做掩码,适合因果语言模型(CLM),像GPT训练时就用这种方式,输入和标签都是原始的token序列。

(五)定义训练参数

训练参数设置得好不好,对模型训练效果影响很大。下面这段代码定义了训练参数:

# 定义训练参数 logger.info("Creating trainer") training_args = TrainingArguments( output_dir="./results", # 训练结果保存路径 eval_strategy="epoch", # 每个epoch结束时评估 learning_rate=5e-5, # 学习率(默认5e-5是常见选择) per_device_train_batch_size=2, # 每个设备的训练batch size(GPU单卡) per_device_eval_batch_size=2, # 每个设备的评估batch size num_train_epochs=3, # 训练轮次(3轮可能较短,需根据任务调整) weight_decay=0.01, # 权重衰减(L2正则化) logging_dir="./logs", # 日志保存路径 logging_steps=100, # 每100步记录一次日志 fp16=False, # 是否启用混合精度训练(建议开启) gradient_accumulation_steps=4, # 梯度累积步数(等效batch_size=8) report_to="tensorboard", # 使用TensorBoard记录训练过程 # tensorboard_dir="./tensorboard" # 可选:指定TensorBoard日志目录 ) 

核心优化方向是调整batch size、学习率、显存策略和保存策略,让它们更适合蒸馏任务。像fp16gradient_accumulation_stepssave_strategymetric_for_best_model这些关键参数,要根据硬件条件和任务特点来调整。最好结合TensorBoard监控训练过程,定期评估模型性能,然后调整超参数。

(六)定义蒸馏配置

蒸馏配置决定了模型蒸馏的具体方式和参数:

# 定义蒸馏配置 weight:添加权重,"loss": "mse" logger.info("Creating distillation config") distill_config = DistillationConfig( temperature=2.0, # 温度参数,控制软标签的平滑程度 hard_label_weight=0.5, # 真实标签损失权重 kd_loss_type="ce", # 知识蒸馏损失类型(交叉熵) intermediate_matches=[ # 中间层匹配配置 { "layer_T": 6, # 教师模型的第6层 "layer_S": 6, # 学生模型的第6层 "feature": "hidden", # 匹配隐藏层特征 "weight": 1.0, # 中间层损失权重 "loss": "mse" # 使用均方误差损失 } ] ) 

这里的temperature参数可以控制软标签的平滑程度,hard_label_weight是真实标签损失的权重,kd_loss_type指定了知识蒸馏损失的类型,intermediate_matches里配置了教师模型和学生模型中间层的匹配设置。

(七)定义训练配置

训练配置主要是设置训练过程中的一些基本信息,比如使用的设备、日志和模型输出的目录等:

# 定义训练配置 logger.info("Creating training config") train_config = TrainingConfig( device="cuda" if torch.cuda.is_available() else "cpu", # 设备选择 log_dir="./logs", # 日志目录 output_dir="./outputs" # 模型输出目录 # save_best_model=True, # 是否保存最佳模型(注释状态) # save_last_model=True, # 是否保存最后模型(注释状态) # save_model_every_epoch=True, # 是否每轮保存模型(注释状态) # tensorboard_dir="./tensorboard" # TensorBoard日志目录(注释状态) ) 

如果有GPU,就用cuda设备,没有的话就用cpu。其他几个参数大家根据实际需求决定是否开启和修改。

(八)创建蒸馏器

有了前面的各种配置,现在可以创建蒸馏器了:

# 创建蒸馏器 logger.info("Creating distiller") distiller = GeneralDistiller( train_config=train_config, # 训练配置(包含设备、路径等) distill_config=distill_config, # 蒸馏配置(温度、损失权重等) model_T=teacher_model, # 教师模型 model_S=student_model, # 学生模型 adaptor_T=None, # 教师模型适配器(未配置) adaptor_S=None # 学生模型适配器(未配置) ) 

这个蒸馏器会把前面设置的训练配置、蒸馏配置,还有教师模型、学生模型都整合起来,为后续的模型蒸馏做准备。

(九)开始蒸馏

万事俱备,终于可以开始蒸馏啦!

# 开始蒸馏 with distiller: # 使用蒸馏器上下文管理器,确保资源正确初始化和释放 logger.info("Starting training") # 记录训练开始日志 # 初始化Trainer,集成模型蒸馏配置 trainer = Trainer( model=student_model, # 学生模型(需要训练的小模型) args=training_args, # 训练参数(如学习率、批次大小、设备等) train_dataset=train_dataset, # 训练数据集(包含输入和标签) eval_dataset=eval_dataset, # 验证数据集(用于评估模型性能) data_collator=data_collator, # 数据批量处理函数(将单条数据组合成批次) # processing_class=teacher_tokenizer # 注意:此处可能存在问题(见下方说明) # 正确做法:适配器或数据处理逻辑应在蒸馏配置中处理 ) # 开始模型训练 trainer.train() # 启动训练循环,包含前向传播、损失计算、反向传播等 trainer.save_model() logger.info("Training finished") # 记录训练结束日志 

这里用了蒸馏器的上下文管理器,能保证资源正确初始化和释放。Trainer初始化后,调用train()方法就开始训练了,训练结束后还可以用save_model()保存模型。

五、结果分析

完成模型蒸馏后,我们来看看效果。从下面这个表格可以看出,蒸馏后的模型在验证损失、生成文本质量和推理速度上都有变化:

指标教师模型(DeepSeek-R1-1.5B)学生模型(Qwen-2.5-1.5B)蒸馏后模型
验证损失1.232.151.45
生成文本质量中等接近教师模型
推理速度慢(150ms/样本)快(80ms/样本)70ms/样本

可以看到,蒸馏后的模型验证损失降低了,生成文本质量接近教师模型,推理速度也更快了。不过在实际应用中,还得根据具体任务调整超参数和数据集,同时要注意模型架构差异、任务适配性和法律合规性这些问题,找到性能和成本之间的平衡点。