《开源大模型食用指南》self-llm 实战:Llama-4-Scout 对话助手——环境搭建、ModelScope 模型下载与 Flask 多轮对话应用部署
【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm
本文是 self-llm(《开源大模型食用指南》)中 Llama 4 系列实战的第一篇,围绕models/Llama4/01-Llama4-对话助手教程文档,完整讲解 Meta Llama 4 系列的关键技术点(MoE、iRoPE、early fusion、1M/10M 长上下文),并手把手带你完成基于Llama-4-Scout-17B-16E-Instruct的对话助手:从环境准备、ModelScope 国内加速下载模型,到用 Flask 构建前后端分离的多轮对话 Web 应用并部署运行。读完本文,你将掌握一条可直接复制的"开源大模型 Web 应用"落地路径,并理解生成参数、对话历史、显存量化等工程细节。
一、Llama 4 系列模型简介
Llama 4 是 Meta 于 2025 年 4 月发布的模型系列,目前公开可用的包括Llama 4 Scout和Llama 4 Maverick,同时预发布了Llama 4 Behemoth。Scout 与 Maverick 均具备原生多模态能力:能够同时理解图像和文本输入,并以文本形式输出;支持多语言(200 种语言),但对中文的支持依然有限,这一点在实际对话中需要留意(下方对话截图中的助手也自述"我的中文不好")。
值得一提的是,这是 Meta 第一次在 Llama 系列上采用MoE(混合专家)架构。下表对比了 Llama 4 与上一代 Llama 3 的核心差异:
表 1:Llama 4 与上一代 Llama 3 的比较
| 特性 | Llama 4 (Scout/Maverick) | Llama 3 (8B/70B/405B) |
|---|---|---|
| 发布日期 | 2025.04.05 | 2024.04.18 |
| 知识截止日期 | 2024.08 | 2023.10 |
| 输入模态 | 文本和图像(最多 8 张) | 主要基于文本(后续 Llama 3.2 发布 11B 和 90B 的 Vision 版本,支持图像输入) |
| 输出模态 | 文本 | 文本 |
| 架构 | Sparse (MoE) | Dense |
| 训练数据 | 40T+ tokens(Scout)/ 22T+ tokens(Maverick) | 15T+ tokens |
| 最大上下文窗口 | 10M | 128k |
| 参数规模 | 17B/109B(Scout),17B/400B(Maverick) | 8B、70B、405B |
| 支持语言 | 多语言(200 languages) | 多语言 |
说明:Scout / Maverick 的"17B/109B"、"17B/400B"含义为"激活参数/总参数"。例如 Scout 每次推理激活约 17B 参数,总参数量 109B,通过 MoE 大幅降低单 token 的计算成本。
表 2:Llama 4 性能基准测试
| 基准测试类别 | 基准测试 | Llama 4 Scout | Llama 4 Maverick | GPT-4o | Gemini 2.0 Flash | DeepSeek v3 0324 |
|---|---|---|---|---|---|---|
| 图像推理 | MMMU | 69.4 | 73.4 | 69.1 | 71.7 | 不支持多模态 |
| MathVista | 70.7 | 73.7 | 63.8 | 73.1 | 不支持多模态 | |
| 图像理解 | ChartQA | 88.8 | 90.0 | 85.7 | 88.3 | 不支持多模态 |
| 编码 | LiveCodeBench | 32.8 | 43.4 | 32.3 | 34.5 | 45.8/49.2 |
| 推理与知识 | MMLU Pro | 74.3 | 80.5 | N/A | 77.6 | 81.2 |
| GPQA Diamond | 57.2 | 69.8 | 53.6 | 60.1 | 68.4 | |
| 多语言 | MGSM | 90.6 | 92.3 | N/A | N/A | N/A |
| 长上下文 | MTOB | 42.2 | 54.0 | N/A | 48.4 | N/A |
| ELO 评分 (LMArena) | N/A | 1417 | N/A | N/A | N/A |
1. 几个值得注意的点
原生多模态能力:Llama 4 采用early fusion,将文本和视觉 tokens 集成到统一的模型骨干中,从模型架构底层就将视觉与语言信息联系起来,从而对多模态输入实现更连贯的理解,生成更相关的响应。
扩展的上下文窗口:Llama 4 Scout 拥有高达 10M tokens 的上下文窗口(此前最大的开源模型为 1M),能够处理和理解极长的信息序列。这得益于 mid-training 阶段加入的特殊 long-context 数据集进行长上下文扩展。
MoE 架构:Scout 与 Maverick 均采用混合专家架构。对任意给定 token,只有一部分模型参数被激活,从而在保持较低计算成本的同时获得更大的模型容量。其整体架构与 DeepSeek MoE 架构非常相似(额外引入了多模态处理),核心思想可概括为"split more experts and shared expert"(划分更多专家 + 共享专家)。
训练:预训练过程采用FP8 精度高效训练,并使用MetaP技术设置超参数;后训练阶段则采用具有自适应数据过滤功能的持续在线强化学习策略,进一步提升模型性能。
2. Llama 4 中的新技术与新架构
- early fusion:从模型架构的开始就整合文本与视觉处理,把两种模态视为同一个 token 序列处理,使模型更早地学习文本与图像之间的关联。Llama 4 的 vision encoder 基于 MetaClip 改进,与冻结的 Llama 模型单独训练,以更好地适配 LLM encoder。
- iRoPE:Llama 4 Scout 采用"无位置编码的交错注意力层(interleaved attention layers without positional embeddings)",并在推理时对注意力做温度缩放(inference-time temperature scaling of attention)来增强长度泛化能力,这一架构被称为iRoPE,使其能高效处理超长上下文中的位置信息。
- MetaP:用于设置关键模型超参数(如每层学习率和初始化尺度),且所选超参数在不同批大小、模型宽度、深度和训练 token 数量之间具有良好的迁移性。
从数据规模来看,训练数据量依然在持续扩展——增量一部分来自更多语言的支持,一部分来自 Meta 产品中的真实用户数据,还有一部分来自 Llama 3 到 Llama 4 这一年期间公开互联网产生的新数据。Llama 4 相较 Llama 3 训练数据的显著增加,以及 Behemoth 高达 2T 的总参数量,都表明 Meta 依然坚信Scaling Law:更多的数据、更大的模型、更多的算力通常带来更好的性能。
3. Post-Training 阶段
对 Llama 4 Maverick 进行后训练时,最大的挑战在于维持多模态输入、推理和对话能力之间的平衡。其后训练 pipeline 为:
轻量级监督微调(SFT)→ 在线强化学习(RL)→ 轻量级直接偏好优化(DPO)
一个关键经验是:SFT 和 DPO 可能过度约束模型,限制在线 RL 阶段的探索,导致次优准确性(尤其在推理、编码和数学领域)。为此,团队使用 Llama Model as a Judge 去除了超过 50% 被标记为"简单"的数据,并在剩余的较难数据集上进行轻量级 SFT;随后的多模态在线 RL 阶段通过仔细挑选更难的 prompt 实现了性能阶跃式提升——具体做法是持续交替"训练模型 → 用该模型筛选并仅保留 medium 到 hard 的 prompt",实验证明这种策略在计算与准确性权衡方面非常有益;最后进行轻量级 DPO 处理响应质量的边缘情况,在模型智能与对话能力之间取得良好平衡。
二、环境准备
本教程的运行基础环境如下:
---------------- ubuntu 22.04 python 3.12 cuda 12.4 pytorch 2.6.0 ---------------- 另外:保证有足够的GPU显存首先为pip换源加速下载,然后安装依赖包:
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple pip install modelscope==1.20.0 pip install transformers==4.51.0 pip install accelerate==1.6.0 pip install compressed-tensors==0.9.3 pip install flask==3.1.0各依赖的作用与版本注意点:
modelscope==1.20.0:提供snapshot_download等模型下载接口,对国内用户十分友好,后续模型下载即依赖它。transformers==4.51.0:Llama 4 需要较新的 transformers 才能识别Llama4ForConditionalGeneration架构类,务必使用文档要求的 4.51.0。accelerate==1.6.0:配合device_map="auto"实现多卡/自动设备分配。compressed-tensors==0.9.3:用于压缩张量(FP8/INT4 等)量化推理格式的支持库,是显存不足时做 INT4 量化加载的关键依赖(详见"运行应用"小节)。flask==3.1.0:搭建 Web 后端服务。
仓库中 参考代码的 requirements.txt 给出了与本教程一致的完整版本约束(torch==2.6.0、transformers==4.51.0、accelerate==1.6.0、compressed-tensors==0.9.3、flask==3.1.0),可直接pip install -r requirements.txt安装。
三、模型下载
使用modelscope的snapshot_download函数下载模型:第一个参数为模型名称,参数cache_dir为模型下载路径。该方式对国内用户十分友好。新建model_download.py文件,写入以下代码并运行python model_download.py:
# model_download.py from modelscope import snapshot_download model_dir = snapshot_download('LLM-Research/Llama-4-Scout-17B-16E-Instruct', cache_dir='请修改我!', revision='master') # model_dir = snapshot_download('LLM-Research/Llama-4-Maverick-17B-128E-Instruct-FP8', cache_dir='请修改我!', revision='master')注意:请记得修改
cache_dir为你自己的模型下载路径。
代码中注释掉了 Maverick 的 FP8 版本下载语句——如果你打算使用 Maverick,可取消注释;两个模型都通过 ModelScope 的LLM-Research组织发布。下载完成后,将得到的本地路径记录下来,后续应用搭建中的MODEL_ID需要指向它。
四、应用搭建
本教程搭建的是一个基于Llama-4-Scout-17B-16E-Instruct的前后端分离对话助手:Flask 负责后端推理与会话管理,HTML/JS 负责前端交互界面。仓库中已提供完整参考代码,目录结构为:
models/Llama4/01-Llama4-对话助手/app/ ├── templates/ │ └── index.html # 前端页面 ├── README.md # 应用说明文档 ├── app.py # Flask 后端 └── requirements.txt # 依赖清单下面分别讲解后端与前端的关键实现。
1. 后端代码(app.py)
完整的后端代码位于 app.py,核心实现如下:
# app.py from flask import Flask, request, jsonify, render_template, session import torch from transformers import AutoTokenizer, Llama4ForConditionalGeneration import gc import re import uuid app = Flask(__name__) app.secret_key = "llama4-scout-chatbot-secret-key" # 用于session加密 # 全局变量存储预加载的模型和tokenizer MODEL_ID = "/pfs/mt-euDpOR/nlp/personal/shufan.jiang/models/LLM-Research/Llama-4-Scout-17B-16E-Instruct" tokenizer = None model = None # 用于存储对话历史的字典 chat_histories = {} # 默认值设置 DEFAULT_MAX_NEW_TOKENS = 1024 DEFAULT_MAX_HISTORY_LENGTH = 10 # 在应用启动前预加载模型 def load_model(): global tokenizer, model print("正在加载模型和tokenizer,请稍候...") # 加载tokenizer tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) # 加载模型 model = Llama4ForConditionalGeneration.from_pretrained( MODEL_ID, attn_implementation="eager", device_map="auto", torch_dtype=torch.bfloat16, ) print("模型加载完成!") def clean_response(text): """清理模型响应中的特殊标记""" # 根据截图中看到的标记,定义可能的标记形式 patterns = [ # 直接匹配具体的标记 '<|eot|>', ] # 应用所有模式 for pattern in patterns: text = text.replace(pattern, '') # 使用正则表达式处理可能的其他token text = re.sub(r'<[\|/]?eot[\|]?>', '', text) # 匹配形如 <eot>, </eot>, <|eot|> 等 return text.strip()后端设计要点(对应 app.py):
- 模型类选择:
transformers4.51 为 Llama 4 提供了专门的Llama4ForConditionalGeneration入口,而不是泛用的AutoModelForCausalLM。 attn_implementation="eager":Scout 的 iRoPE 采用交错注意力层设计,加载时显式指定 eager attention 实现,避免默认的 SDPA 等优化路径与模型结构不兼容。device_map="auto":由 accelerate 自动把各层分配到可用 GPU/CPU,多卡环境也能自动利用。torch_dtype=torch.bfloat16:以 BF16 精度加载,兼顾精度与显存占用(BF16 下该模型参考显存约 212910MB ≈ 208GB,见下文)。clean_response:模型输出中可能携带<|eot|>等结束标记,用replace加正则re.sub(r'<[\|/]?eot[\|]?>', '', text)一并剔除,保证展示给用户的文本干净。
接下来是三个路由(app.py):
@app.route('/') def home(): # 创建会话ID if 'chat_id' not in session: session['chat_id'] = str(uuid.uuid4()) # 如果是新会话,初始化聊天历史 chat_id = session['chat_id'] if chat_id not in chat_histories: chat_histories[chat_id] = [] return render_template('index.html', chat_id=chat_id) @app.route('/api/generate', methods=['POST']) def generate(): try: # 确保模型已加载 if tokenizer is None or model is None: return jsonify({"error": "模型正在加载中,请稍后再试"}), 503 data = request.json user_input = data.get('user_input', '') chat_id = data.get('chat_id', session.get('chat_id', str(uuid.uuid4()))) # 获取前端传递的参数,如果没有则使用默认值 max_new_tokens = int(data.get('max_new_tokens', DEFAULT_MAX_NEW_TOKENS)) max_history_length = int(data.get('max_history_length', DEFAULT_MAX_HISTORY_LENGTH)) # 参数限制,确保在合理范围内 max_new_tokens = max(256, min(max_new_tokens, 2048)) max_history_length = max(2, min(max_history_length, 20)) if not user_input: return jsonify({"error": "请输入问题"}), 400 # 获取或初始化聊天历史 if chat_id not in chat_histories: chat_histories[chat_id] = [] # 添加用户消息到历史记录 chat_histories[chat_id].append({"role": "user", "content": user_input}) # 从历史记录构建消息列表,使用前端传递的历史长度 messages = chat_histories[chat_id][-max_history_length*2:] # 用户和助手消息各算一条 # 应用chat模板 inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt", return_dict=True) # 生成响应,使用前端传递的token数量 with torch.no_grad(): outputs = model.generate(**inputs.to(model.device), max_new_tokens=max_new_tokens) response = tokenizer.batch_decode(outputs[:, inputs["input_ids"].shape[-1]:]) # 清理缓存 torch.cuda.empty_cache() gc.collect() # 清理响应,移除结束标记 cleaned_response = clean_response(response[0]) # 添加模型回复到历史记录 chat_histories[chat_id].append({"role": "assistant", "content": cleaned_response}) # 如果历史记录太长,保留最新的max_history_length条 if len(chat_histories[chat_id]) > max_history_length * 2: # 用户和助手消息各占一半 chat_histories[chat_id] = chat_histories[chat_id][-max_history_length*2:] return jsonify({ "response": cleaned_response, "chat_id": chat_id, "max_new_tokens": max_new_tokens, "max_history_length": max_history_length }) except Exception as e: return jsonify({"error": str(e)}), 500 @app.route('/api/clear_history', methods=['POST']) def clear_history(): try: data = request.json chat_id = data.get('chat_id', session.get('chat_id')) if chat_id and chat_id in chat_histories: chat_histories[chat_id] = [] return jsonify({"success": True, "message": "聊天历史已清除"}) else: return jsonify({"success": False, "error": "无效的会话ID"}), 400 except Exception as e: return jsonify({"success": False, "error": str(e)}), 500路由与核心逻辑解读:
GET /:为用户会话生成唯一chat_id(uuid.uuid4()),在chat_histories字典中初始化空历史,并渲染index.html。基于session的机制天然支持多用户同时使用。POST /api/generate:对话主接口。若模型尚未加载完成(tokenizer is None or model is None)返回503,前端据此轮询等待;接着读取前端传来的user_input、chat_id、max_new_tokens、max_history_length,其中后两个参数被强制限制在合理区间(256~2048 与 2~20)。- 多轮对话上下文管理:
chat_histories[chat_id]按{"role": "user"/"assistant", "content": ...}的 OpenAI 风格格式保存消息;每次请求只取最后max_history_length * 2条(用户与助手消息各占一半),通过tokenizer.apply_chat_template(messages, add_generation_prompt=True, ...)套用 Llama 4 的对话模板构造输入——这也是多轮记忆得以生效的关键。 - 推理:在
torch.no_grad()下调用model.generate(**inputs.to(model.device), max_new_tokens=max_new_tokens),随后用batch_decode只解码新增生成的 token(outputs[:, inputs["input_ids"].shape[-1]:])。 - 资源回收:每次生成后执行
torch.cuda.empty_cache()与gc.collect(),减少显存与内存碎片。 - 历史裁剪:若历史超出
max_history_length * 2条则从头部截断,防止输入无限变长。 POST /api/clear_history:按chat_id清空对应会话历史,用于"开始新对话"。
最后是应用入口(app.py):
if __name__ == '__main__': # 在另一个线程中预加载模型 import threading threading.Thread(target=load_model).start() app.run(debug=True, host='0.0.0.0', port=5000, use_reloader=False)模型加载被放到独立线程中执行,避免阻塞 Flask 主线程,因此服务可以秒级启动、模型在后台加载;use_reloader=False则是防止调试重载导致模型被重复加载。
2. 前端代码(index.html)
前端页面位于 templates/index.html,是一个单页聊天界面,主要功能点包括:
- 模型加载提示条:页面顶部固定一个橙色通知条,显示"模型正在加载中,请稍候... 首次加载可能需要1-2分钟"。
- 会话 ID 展示:渲染 Flask 传入的
{{ chat_id }}。 - 模型参数设置面板:可折叠的滑动条面板,包含两个参数:
- 模型生成长度上限(
max_new_tokens):范围 256~2048,步长 128,默认 1024; - 最大对话记忆轮数(
max_history_length):范围 2~20,步长 1,默认 10。 - 拖动滑块时前端实时更新显示值,并在发送请求时一并传给后端。
- 模型生成长度上限(
- 模型加载状态轮询:
checkModelStatus()每隔 5 秒向/api/generate发送一次探测请求,收到503(模型加载中)则继续等待;一旦模型就绪,便隐藏提示条并启用输入框、发送按钮与清除按钮。 - 消息渲染与清理:
addMessage()将消息以气泡形式插入对话区(用户消息右对齐蓝色气泡、助手消息左对齐灰色气泡),助手消息还会经过cleanResponse()剔除<|eot|>、<eot>、</eot>等结束标记。 - 交互细节:支持点击"发送"按钮或按 Enter 键发送;请求期间显示加载动画并禁用输入控件;"清除对话历史"按钮调用
/api/clear_history后清空界面并恢复欢迎语。
3. 运行应用
python app.py应用将在http://localhost:5000上运行。
注意:启动后模型会在后台自动加载,这可能需要 1-2 分钟。在此期间,界面会显示"模型正在加载中"的提示,加载完成后才能开始对话。
显存说明:以bfloat16精度加载模型时,参考显存占用约为212910MB = 208GB,请确保服务器 GPU 显存充足。如果显存不足,可以使用INT4 量化加载——这也是参考代码 requirements.txt 中特意固定compressed-tensors==0.9.3的原因:该库为 transformers 的压缩张量量化(FP8/INT4 等)推理提供支持,配合Llama4ForConditionalGeneration可实现 INT4 低精度加载,大幅降低显存需求。
4. 使用方法
- 在浏览器中打开
http://localhost:5000。 - 等待模型加载完成(顶部的橙色通知条消失)。
- 根据需要调整参数滑动条:
- 生成长度上限:控制每次回复生成的最大 token 数(范围:256-2048)。
- 历史记录长度:控制对话中保留的最大轮数(范围:2-20)。
- 在输入框中输入问题。
- 点击"发送"按钮或按 Enter 键发送问题。
- 等待模型生成回复。
- 继续进行多轮对话,模型会记住之前的对话内容。
- 如需清除对话历史,点击"清除对话历史"按钮。
5. 效果展示
启动应用后,页面顶部会先出现模型加载中的橙色提示条,模型加载完成后即可开始对话:
模型加载完成后,展开"模型参数设置"面板即可实时调节生成长度上限与对话记忆轮数。多轮对话时,模型会记住之前的上下文(例如用户提到"喜欢小狗"后,后续提问"你还记得我喜欢什么动物吗",模型能正确回答"小狗"),同时界面会通过加载动画提示生成状态:
五、参考代码及其使用
本次教程搭建了一个基于Llama-4-Scout-17B-16E-Instruct的前后端分离对话助手,仓库中额外提供了可直接运行的参考代码,供学习者对照本教程使用:
- 后端服务:app.py(模型预加载、对话生成、历史管理三个路由)
- 前端页面:templates/index.html
- 依赖清单:requirements.txt
- 应用说明:README.md(含特性、安装、运行、参数调整、技术实现与生产部署建议)
使用方法:先按上文"模型下载"小节把模型下载到本地并修改app.py中的MODEL_ID为你的模型路径,然后:
pip install -r requirements.txt # 安装依赖 python app.py # 启动应用随后打开http://localhost:5000即可开始对话。
六、总结与延伸
至此,你已经完整走通了"Llama 4 系列模型认知 → 环境准备 → ModelScope 国内下载 → Flask 前后端对话应用搭建 → 多轮对话调优"的整条链路。核心收获可以归纳为三点:
- 模型侧:理解了 Llama 4 的 MoE(激活/总参数分离)、iRoPE 长上下文、early fusion 原生多模态与 FP8/MetaP 训练策略,以及 SFT→在线 RL→DPO 的后训练 pipeline 设计逻辑。
- 工程侧:掌握了
Llama4ForConditionalGeneration+apply_chat_template的标准推理姿势,以及attn_implementation="eager"、device_map="auto"、torch_dtype等加载参数的实际影响;当显存不足时,可借助compressed-tensors走 INT4 量化路径。 - 应用侧:实现了一个支持多用户会话隔离、多轮记忆、参数实时可调、历史可清除的完整 Web 对话助手,其前后端交互模式(503 轮询等待模型、滑动条传参、
<|eot|>标记清理)可直接复用到其他开源大模型的 Web 化改造中。
如果你希望继续深入 Llama 4 的其他玩法,可以回到 models/Llama4 目录查看相关教程资源;self-llm 仓库中还有大量其他开源模型的部署与微调实战文档可供参考。
【免费下载链接】self-llm《开源大模型食用指南》针对中国宝宝量身打造的基于Linux环境快速微调(全参数/Lora)、部署国内外开源大模型(LLM)/多模态大模型(MLLM)教程项目地址: https://gitcode.com/GitHub_Trending/se/self-llm
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考