CEShJNj6H 发表于 2025-2-27 09:34:34

探索基于Qwen2.5实现DeepSeek推理的奇妙之旅

<div id="container" data-v-1d7a5742="" data-element="root" contentScore="3866">
作为一名互联网技术爱好者,我一直对大型语言模型和高效推理技术充满热情。本文基于基于Qwen2.5实现DeepSeek推理功能。
本文使用unsloth框架,这个轻量高效、易于上手的工具,加上SFT中文数据集的加持,测试了在医疗领域的推理应用。
当然,过程中还遇到了诸如GRPO等新概念的挑战与启示,这一切都让我对整个系统有了更深的认识。接下来,我就以亲历者的角度,带大家走进这个既枯燥又充满乐趣的技术世界。
Qwen2.5模型简介

Qwen2.5是近年来备受瞩目的大语言模型之一。相较于传统模型,它在语义理解、生成能力以及推理效率上都有明显提升。作为一个以中文为主要应用场景的模型,Qwen2.5在处理复杂语言任务时表现得游刃有余。我的初衷正是希望借助这一强大的模型,为实现DeepSeek推理功能提供坚实的底层支撑。
在实际使用过程中,我发现Qwen2.5不仅能够快速响应,还具备一定的自适应能力。尤其是在面对专业领域——如医疗场景——的应用时,它表现出的逻辑严谨性和数据敏感性,让人不得不对其刮目相看。当然,这背后离不开大规模预训练和精心设计的架构。
DeepSeek推理功能概述

DeepSeek是一种新兴的推理功能,其核心目标是实现对输入数据的高效、准确解析,并基于模型预训练的知识进行深度推理。简单来说,DeepSeek的优势在于它不仅能回答常见问题,还能通过复杂的逻辑链条,推断出更为隐含的信息。说白了,就是让模型不仅会“答题”,还会“思考”。
在项目中,我利用DeepSeek实现了医疗领域内的一系列推理任务。在医疗诊断中形成一个较为完整的推理链条,从而辅助医生进行诊断决策。整个过程既考验模型本身的语言理解能力,也对后端推理算法提出了挑战。
unsloth框架的选择与优势

谈到技术实现,就不得不提unsloth框架。这款框架虽然名字听起来“懒散”,但实际上它专为高并发、低延迟的推理任务而设计。以下几点是我选择unsloth框架的重要原因:
高效性:unsloth框架在处理大规模并发请求时表现稳定,并且在资源利用率上做了诸多优化。对于我这样需要实时推理的系统来说,这无疑是一个大优势。
易扩展性:框架的模块化设计使得我可以根据实际需求灵活调整架构。例如,在后期需要增加新的推理策略时,只需轻微改动即可。
兼容性:unsloth框架与Qwen2.5等主流大语言模型兼容性极高,这为我后续的调试和优化提供了极大的便利。
在实际开发过程中,我曾遇到一些资源瓶颈的问题,正是依靠unsloth的灵活扩展能力,我才能快速定位问题并进行相应的调整。
本次使用的数据集简介

数据是人工智能的燃料。在医疗领域,精确的中文数据尤为关键。
FreedomIntelligence/medical-o1-reasoning-SFT中文数据集正是在这种背景下应运而生。它包含了大量经过精细标注的医疗案例和推理路径,使得模型在进行医疗相关推理时能够借鉴真实场景下的数据逻辑。
我在项目中采用该数据集进行微调,目的是让Qwen2.5不仅具备通用的语言理解能力,更能深入理解医学术语和专业知识。数据集的丰富性和多样性为模型提供了足够的训练样本,从而提升了其在医疗场景下的表现。
事实上,经过这一轮微调后,我发现模型在面对复杂病症描述时,给出的推理结果更加合理且具有说服力。
数据集地址(本次我们使用的是中文数据集):
https://huggingface.co/datasets/FreedomIntelligence/medical-o1-reasoning-SFT/viewer/zh

实现代码

01、环境准备

%%capture# 该魔法命令用于在 Jupyter Notebook中隐藏输出# 避免输出干扰执行结果,例如 `pip install` 过程中的信息# 安装 Unsloth 和 vLLM 这两个库!pip install unsloth vllm# 升级 Pillow(Python Imaging Library),确保使用最新版本!pip install --upgrade pillow

[*]1.
[*]2.
[*]3.
[*]4.
[*]5.
[*]6.
[*]7.
[*]8.
[*]9.
[*]10.
[*]11.





from unsloth import FastLanguageModel, PatchFastRL# 从 unsloth 库中导入 FastLanguageModel(加速的语言模型)和 PatchFastRL(加速补丁)PatchFastRL("GRPO", FastLanguageModel)# 对 FastLanguageModel 应用 "GRPO" 方案的补丁优化,以提高推理效率

[*]1.
[*]2.
[*]3.
[*]4.
[*]5.
[*]6.





02、加载基础模型

from unsloth import is_bfloat16_supported# 检测是否支持 bfloat16(更节省显存的数值格式)import torch# 导入 PyTorch 进行深度学习计算# 设置模型参数max_seq_length = 1024# 最大序列长度,可以增大以支持更长的推理lora_rank = 64# LoRA 低秩适配矩阵的秩,较大值提高智能性但降低推理速度# 加载 FastLanguageModel 预训练模型model, tokenizer = FastLanguageModel.from_pretrained(    model_name="Qwen/Qwen2.5-3B-Instruct",# 使用 Qwen2.5-3B-Instruct 作为基础模型    max_seq_length=max_seq_length,# 设置最大序列长度    load_in_4bit=True,# 使用 4-bit 量化(减少显存消耗),若为 False 则使用 16-bit LoRA    fast_inference=True,# 启用 vLLM 加速推理(优化并行计算)    max_lora_rank=lora_rank,# 设置 LoRA 适配的秩    gpu_memory_utilization=0.5,# 限制 GPU 内存占用,降低该值可减少显存溢出)# 为模型应用 PEFT(参数高效微调)和 LoRA(低秩适配)model = FastLanguageModel.get_peft_model(    model,    r=lora_rank,# LoRA 低秩矩阵的秩,可选 8, 16, 32, 64, 128    target_modules=[      "q_proj", "k_proj", "v_proj", "o_proj",# 量化关键模块:QKV 投影和输出投影      "gate_proj", "up_proj", "down_proj",# MLP 相关模块    ],# 若显存不足,可以移除 QKVO 相关模块    lora_alpha=lora_rank,# LoRA 适配器的 alpha 参数,通常设为与 r 相同    use_gradient_checkpointing="unsloth",# 启用梯度检查点,减少显存占用,适用于长上下文微调    random_state=3407,# 设置随机种子,保证实验可复现)

[*]1.
[*]2.
[*]3.
[*]4.
[*]5.
[*]6.
[*]7.
[*]8.
[*]9.
[*]10.
[*]11.
[*]12.
[*]13.
[*]14.
[*]15.
[*]16.
[*]17.
[*]18.
[*]19.
[*]20.
[*]21.
[*]22.
[*]23.
[*]24.
[*]25.
[*]26.
[*]27.
[*]28.
[*]29.
[*]30.
[*]31.
[*]32.





# 输出 ==((====))==Unsloth 2025.2.12: Fast Qwen2 patching. Transformers: 4.48.3.   \\   /|    GPU: Tesla T4. Max memory: 14.741 GB. Platform: Linux.O^O/ \_/ \    Torch: 2.5.1+cu124. CUDA: 7.5. CUDA Toolkit: 12.4. Triton: 3.1.0\      /    Bfloat16 = FALSE. FA "-____-"   Free Apache license: http://github.com/unslothai/unslothUnsloth: Fast downloading is enabled - ignore downloading bars which are red colored!Unsloth: vLLM loading unsloth/qwen2.5-3b-instruct-unsloth-bnb-4bit with actual GPU utilization = 49.66%Unsloth: Your GPU has CUDA compute capability 7.5 with VRAM = 14.74 GB.Unsloth: Using conservativeness = 1.0. Chunked prefill tokens = 1024. Num Sequences = 192.Unsloth: vLLM's KV Cache can use up to 4.9 GB. Also swap space = 2 GB.WARNING 02-17 07:17:06 config.py:2386] Casting torch.bfloat16 to torch.float16.INFO 02-17 07:17:19 config.py:542] This model supports multiple tasks: {'classify', 'generate', 'embed', 'reward', 'score'}. Defaulting to 'generate'.Unsloth: vLLM Bitsandbytes config using kwargs = {'load_in_8bit': False, 'load_in_4bit': True, 'bnb_4bit_compute_dtype': 'float16', 'bnb_4bit_quant_storage': 'uint8', 'bnb_4bit_quant_type': 'nf4', 'bnb_4bit_use_double_quant': True, 'llm_int8_enable_fp32_cpu_offload': False, 'llm_int8_has_fp16_weight': False, 'llm_int8_skip_modules': ['lm_head', 'multi_modal_projector', 'merger', 'modality_projection', 'model.layers.2.mlp', 'model.layers.3.mlp', 'model.layers.30.mlp'], 'llm_int8_threshold': 6.0}INFO 02-17 07:17:19 llm_engine.py:234] Initializing a V0 LLM engine (v0.7.2) with config: model='unsloth/qwen2.5-3b-instruct-unsloth-bnb-4bit', speculative_cnotallow=None, tokenizer='unsloth/qwen2.5-3b-instruct-unsloth-bnb-4bit', skip_tokenizer_init=False, tokenizer_mode=auto, revisinotallow=None, override_neuron_cnotallow=None, tokenizer_revisinotallow=None, trust_remote_code=False, dtype=torch.float16, max_seq_len=1024, download_dir=None, load_format=LoadFormat.BITSANDBYTES, tensor_parallel_size=1, pipeline_parallel_size=1, disable_custom_all_reduce=False, quantizatinotallow=bitsandbytes, enforce_eager=False, kv_cache_dtype=auto,device_cnotallow=cuda:0, decoding_cnotallow=DecodingConfig(guided_decoding_backend='xgrammar'), observability_cnotallow=ObservabilityConfig(otlp_traces_endpoint=None, collect_model_forward_time=False, collect_model_execute_time=False), seed=0, served_model_name=unsloth/qwen2.5-3b-instruct-unsloth-bnb-4bit, num_scheduler_steps=1, multi_step_stream_outputs=True, enable_prefix_caching=True, chunked_prefill_enabled=False, use_async_output_proc=True, disable_mm_preprocessor_cache=False, mm_processor_kwargs=None, pooler_cnotallow=None, compilation_cnotallow={"level":0,"splitting_ops":[],"compile_sizes":[],"cudagraph_capture_sizes":,"max_capture_size":192}, use_cached_outputs=False, tokenizer_config.json: 100% 7.36k/7.36k :    """宽松格式检查:允许不严格换行"""    pattern = r".*?\s*.*?"    responses = ["content"] for completion in completions]    return def count_xml(text) -> float:    """检查 XML 结构完整性"""    count = 0.0    if text.count("\n") == 1:      count += 0.125    if text.count("\n\n") == 1:      count += 0.125    if text.count("\n\n") == 1:      count += 0.125      count -= len(text.split("\n\n")[-1]) * 0.001    if text.count("\n") == 1:      count += 0.125      count -= (len(text.split("\n")[-1]) - 1) * 0.001    return countdef xmlcount_reward_func(completions, **kwargs) -> list:    """计算 XML 结构完整性分数"""    contents = ["content"] for completion in completions]    return

[*]1.
[*]2.
[*]3.
[*]4.
[*]5.
[*]6.
[*]7.
[*]8.
[*]9.
[*]10.
[*]11.
[*]12.
[*]13.
[*]14.
[*]15.
[*]16.
[*]17.
[*]18.
[*]19.
[*]20.
[*]21.
[*]22.
[*]23.
[*]24.
[*]25.
[*]26.
[*]27.
[*]28.
[*]29.
[*]30.
[*]31.
[*]32.
[*]33.
[*]34.
[*]35.
[*]36.
[*]37.
[*]38.
[*]39.
[*]40.
[*]41.
[*]42.
[*]43.
[*]44.
[*]45.
[*]46.
[*]47.
[*]48.
[*]49.
[*]50.
[*]51.
[*]52.
[*]53.
[*]54.
[*]55.
[*]56.
[*]57.
[*]58.
[*]59.
[*]60.
[*]61.
[*]62.
[*]63.
[*]64.
[*]65.
[*]66.
[*]67.
[*]68.
[*]69.
[*]70.
[*]71.
[*]72.
[*]73.
[*]74.
[*]75.
[*]76.
[*]77.
[*]78.





#输出medical_o1_sft_Chinese.json: 100% 64.8M/64.8M [00:00
页: [1]
查看完整版本: 探索基于Qwen2.5实现DeepSeek推理的奇妙之旅