大模型底层架构与源码解读:从Transformer到LoRA微调部署实战
这两年我被问得最多的一句话,基本就是“大模型到底是怎么工作的?”。我早期一直在做传统深度学习和后端架构,真正把大模型源码翻了一遍之后,才发现市面上大多数所谓“原理讲解”不是讲得太浅就是太虚。今天这篇准备以“底层架构+源码解读”为主线,把一个大模型从预训练到微调、再到部署落地的完整链路,掰开揉碎讲清楚。
这篇内容适合正在入门大模型的算法工程师、准备转行做AI应用开发的后端工程师,也包括那些已经在用模型API、但始终搞不清“本地部署大模型”时为啥要配显卡、要配多少显存的技术人。我会尽量少堆术语,关键概念都拿生活化类比垫一下,同时给出可以直接上手的参数和命令。
1. 大模型底层架构的整体拆解
先明确一个概念:我们现在讨论的“大模型”,绝大多数是基于 Transformer 架构的 decoder-only 语言模型。无论是 GPT、LLaMA、Qwen、DeepSeek,还是各类国产开源基座,底层骨架差别不大,核心都是“堆叠解码器层 + 多头注意力 + 前馈网络”。
1.1 从 N-Gram 到 Transformer:解决“长距离依赖”的关键一跃
在 Transformer 出现之前,老牌语言模型走的是 RNN/LSTM 路线,可以理解成“一个木桶,边防边往后传”。问题是木桶每传一次信息就会损失一点,传到第几十个 token 时,最早的信息已经变形得认不出来了。Transformer 的做法完全不同,它允许序列里的每个 token 直接“看到”序列里所有其他 token,说白了就是开会时每个人可以直接跟任何人交换意见,而不是只能挨个悄悄话。这个机制叫自注意力(Self-Attention),它让“长距离依赖”从工程难点变成了自带能力。
对应到源码,你在 Hugging Face Transformers 里随便打开一个 Llama 模型文件,看到的核心目录无非就是 modeling_llama.py、configuration_llama.py。前者是模型数学结构,后者是超参配置。很多人一打开源码就晕,其实只要抓住几个数据结构就能顺下来:Embedding、DecoderLayer、RMSNorm、Attention、MLP。
# 伪代码风格,LLaMA-like decoder layer 的核心结构 class LlamaDecoderLayer(nn.Module): def __init__(self, config): self.self_attn = LlamaAttention(config) self.mlp = LlamaMLP(config) self.input_layernorm = LlamaRMSNorm(config.hidden_size) self.post_attention_layernorm = LlamaRMSNorm(config.hidden_size) def forward(self, hidden_states, attention_mask, position_ids): residual = hidden_states hidden_states = self.input_layernorm(hidden_states) hidden_states = self.self_attn(hidden_states, attention_mask, position_ids) hidden_states = residual + hidden_states # 残差连接 residual = hidden_states hidden_states = self.post_attention_layernorm(hidden_states) hidden_states = self.mlp(hidden_states) return residual + hidden_states这个伪代码基本就是把一个解码器层讲完了。不用管里面多少行细节,你只需要记住两点:残差连接把每层输出叠加回输入,避免深层网络梯度消失;每个层包含一个注意力子层和一个 MLP 子层,中间夹着归一化。就是这三个组件反复堆了 32 层甚至更多,才有了“深度”。
1.2 参数量到底怎么估算:70B 是怎么来的
很多人对“7B”“13B”“70B”没有直观感觉。其实参数量可以大体估算:一个 decoder-only 模型的参数主要来自三块,Embedding 矩阵、注意力层、MLP 层。以 LLaMA 7B 为例,隐藏维度 4096、层数 32、注意力头 32、词表大小约 32000,光 Embedding 就有 32000 × 4096 ≈ 1.3 亿参数,约占 2%。注意力层的 Q/K/V/O 投影加上 MLP 的 gate/up/down 投影才是大头,这里的计算逻辑是每层约隐藏维度平方的 12 倍出头,再乘以层数,量级就出来了。
算明白这笔账,对你后面决定“能不能微调”“能不能本地部署”特别重要。很多人以为自己买一张 24GB 的 3090 就能训 7B 模型,实际上一旦开始全参数微调,优化器状态、梯度、激活值都要额外吃显存。量化一下:单张 24GB 显卡用全参数微调一个 7B 模型,基本没戏。所以行业里普遍采用 LoRA 这类参数高效微调方法,后面专门说。
初次接触源码时不要一上来就盯 fp16/bf16 算子实现,先看 forward 函数里 tensor 的 shape 变化,那才是主干道。
2. 核心源码解析:一次前向计算里到底发生了什么
很多文章会给你画一张大图,但你已经看烦了,今天我直接把前向计算拆成四个阶段:Token 编码、位置编码、注意力计算、生成循环。每一段配合代码逻辑说清楚为什么要这么设计。
2.1 Token 化与 Embedding:文本怎么变成向量
模型不是直接读文字的。原始文本进来以后,先经过 Tokenizer 切分成 token 序列,再把每个 token 映射成整数 ID。注意,不同 Tokenizer 的切分逻辑会影响模型表现:中英文混合场景用 llama tokenizer 会把中文切成多个 token,导致长文本上下文被快速占满;所以后来有 Qwen 等模型专门优化了词表。实际工程里经常遇到“同一个 Prompt 在 A 模型里上下文塞得下,在 B 模型里直接超出限制”,根源就在这里。
Tokenizer 之后是 Embedding 层,其实就是一张查找表,每个 token ID 对应一个向量。这个向量是训练中学习出来的。很多源码新手会问“为什么 Embedding 还要做 scale”,比如有的实现里会把 hidden state 乘上一个缩放因子,这是因为注意力计算在维度高时点积结果容易过大,softmax 会饱和到极值。所以要在架构层面做稳定化处理。
继续说 Attention mask。在自回归模型里,每个 token 只能看到自己和之前的 token,不能看未来的 token。实现上就是用一个上三角 mask 矩阵,把未来的位置置为负无穷,经过 softmax 后权重趋近于零,未来信息就被挡掉了。这个 mask 的构造与拼接在源码实现里随处可见,理解它,你才能理解为什么 batch 推理里 padding token 的处理那么麻烦。
# 注意力 mask 的简化逻辑 mask = torch.triu(torch.ones(seq_len, seq_len), diagonal=1).bool() mask = mask.masked_fill(mask == 1, float("-inf"))这段代码虽然只有三行,但却是整个自回归机制的核心表达。如果你在阅读推理源码时看到 scores 张量被填入了大量极小值,不用慌,这就是在“关窗户”。
2.2 注意力计算:多头、GQA 与 KV Cache 的真实角色
注意力公式是 Attention(Q, K, V) = softmax(QK^T / sqrt(d_k))V。这里 Q、K、V 分别代表查询、键、值,可以通俗理解成:每个 token 发出一个“查找请求”Q,所有 token 都亮出自己的“索引标牌”K,最后按匹配度去“取内容”V。
多头注意力(Multi-Head Attention)就是把这一套操作做多份,每份在不同子空间里抓不同模式,然后拼接起来。近两年很多模型已经不用原始的 MHA,而是换成了 GQA(分组查询注意力),比如 LLaMA 2/3、Qwen 等。GQA 之所以流行,核心在于减少了 K/V 头的数量,同样参数量下缓存占用大幅下降,而效果损失很小。这属于架构层面对推理成本做了主动优化。
解码阶段还有一个特别重要的概念叫 KV Cache。自回归生成时每生成一个新 token,都要把之前 token 的 K、V 重新算一遍吗?如果重新算,那成本就是 O(n²),等于每次都比上一次更慢。有了 KV Cache 之后,之前算过的 K/V 直接存在显存里,新 token 只算跟当前位置相关的部分,速度会快上一个数量级。当然代价是显存被缓存吃掉。7B 模型在 2048 token 长度下,光 KV Cache 可能吃 2GB 以上。这也是为什么 vLLM 这类推理框架值得用:它把 KV Cache 做了分页管理,避免显存碎片化。
2.3 生成循环与采样策略:输出为什么有时候“胡说八道”
自回归生成在代码里非常简单,一个 while 循环,每次喂入已有的 token 序列,取最后一个位置的 logits,然后做采样,再把采样结果拼回序列,直到生成结束符或达到最大长度。
但真正影响输出质量和风格的是采样策略。温度参数 temperature 用来控制概率分布的“锐利程度”,温度越低,越倾向于概率高的 token,输出越稳定保守;温度太高则开始乱跑。Top-k 是从概率最高的 k 个候选里采样,Top-p(核采样)是选累计概率达到 p 的一个最小集合。实际工程里,我自己的默认配置是 temperature=0.7、top_p=0.9,然后看场景再做微调。写代码、数学推导这类任务,温度调到 0.1 以下效果更好;创意写作可以调高到 0.8 以上。
源码里采样逻辑一般情况下集中在 generation_utils.py 或 sampling 目录。看一遍这个文件,你能比看十篇“如何让AI更像人”的文章都有收获。
3. 大模型微调实战:不到 30GB 显存也能微调 7B
这是很多开发者最关心的部分,也是热搜里“大模型微调实战”最频繁被搜到的一个方向。先说结论:当下的实际工程标准不是全参数微调,而是 LoRA 或 QLoRA。主流开源工具链是 Hugging Face PEFT + Transformers + TRL,底层微调原理可以解释为“冻结原模型绝大部分参数,只训练少量增量矩阵”。
3.1 全参数微调与 LoRA 的成本对比
全参数微调为什么贵?因为大模型里的每个参数都要计算梯度,而且要由优化器维护一阶动量、二阶动量等状态。以 7B 参数为例,就算是混合精度训练,仅优化器状态就要占几十 GB 显存。普通实验室或者个人开发者根本没有这种资源。
LoRA 的核心逻辑是给原始权重旁挂一条低秩分支。原始矩阵 W 保持冻结,新增两个小矩阵 A 和 B,前向计算时输出变成 Wx + BAx。A 的维度是输入维度 × rank,B 的维度是 rank × 输出维度,rank 往往设置在 8~64,参数量一下子从百亿级别降到几百万甚至更低。这就像是给一本厚重典籍加了几个便利贴,而不是把整本书重写。
QLoRA 就是在 LoRA 基础上进一步把模型权重量化到 4bit,这样加载 7B 模型时显存占用大幅下降。实际场景里,我用 QLoRA 在 24GB 的 3090 上微调过 7B 模型,训练序列长度 1024、batch size 1、gradient accumulation 8,可以稳定跑起来。如果你只有 16GB 显存,把序列长度降到 512,或者换成更小规模的 3B 模型,问题也不大。
不要觉得 LoRA 效果一定不如全参。生成类任务里,LoRA 拟合能力已经足够强,真正决定微调上限的是数据质量和对话格式的一致性,而不是你是不是“全参”。
3.2 关键训练参数解读与踩坑记录
使用 PEFT 做 LoRA 微调时,有一个参数很容易踩坑:target_modules。它决定要把 LoRA 挂在哪些子模块上。拿 LLaMA 家族来说,常见的设置是把 q_proj、k_proj、v_proj、o_proj、gate_proj、up_proj、down_proj 全部加上。默认只改查询和值投影的话,模型学习新任务的“改写强度”往往不够,尤其做领域指令微调时效果打折比较明显。
然后是 LoRA 超参里最核心的两个:rank 和 alpha。rank 是低秩矩阵的秩,决定新增参数量的大小,一般从 8 起步。alpha 是缩放系数,最终的实际缩放是 alpha / rank。经验上,如果任务是通用指令服从,rank=16 够用;如果是特定领域知识注入,比如你要让模型变成一个法律问答助手,rank 可以提高到 32 甚至 64。很多人以为 rank 越大越好,实际上 rank 过大反而会破坏基座模型的基础能力,也就是俗称的“灾难性遗忘”。
数据格式方面也需要特别注意。模型微调不是直接喂“问题和答案”两列就能出好效果,而是要组织成对话模板。以 Qwen 系列为例,微调数据需要有 system、user、assistant 角色,并且每轮 assistant 回复是训练目标。很多从零开始做微调的人把训练数据做成“一句是一句”,结果模型学完以后不会多轮对话,或者答非所问,问题基本都出在这里。
# 一个典型的 LoRA 微调命令行配置(以 LLaMA-Factory 为例) CUDA_VISIBLE_DEVICES=0 python src/train_bash.py \ --stage sft \ --model_name_or_path meta-llama/Llama-2-7b-hf \ --finetuning_type lora \ --quantization_bit 4 \ --lora_target q_proj,k_proj,v_proj,o_proj,gate_proj,up_proj,down_proj \ --lora_rank 16 \ --lora_alpha 32 \ --dataset my_dataset \ --dataset_dir data \ --per_device_train_batch_size 1 \ --gradient_accumulation_steps 8 \ --learning_rate 2e-4 \ --num_train_epochs 3 \ --max_source_length 1024 \ --max_target_length 1024 \ --output_dir output/llama2_lora \ --fp16这条命令在单卡 3090 上实测能跑通。注意学习率 2e-4 比全参微调常用的 1e-5 高出一个数量级,这是因为 LoRA 新加的参数从一开始就要“用力学”,而基座参数几乎不动,所以不存在破坏原有权重的问题。
3.3 训练过程的“一眼诊断法”
微调过程中最容易遇到的一个问题是 loss 曲线看起来正常,但模型输出变成乱码或者不断复读。这时候先检查两件事:第一,tokenizer 对特殊 token 的处理是否正确,很多微调框架要求数据里不能有裸的 padding token 参与训练,否则模型会把 padding token 当作合法输出来学;第二,看数据里是否大量存在截断现象,用户 Prompt 超过 max_source_length 时会被硬切,切到一半的语义学出来当然乱七八糟。
另一个高频问题是 loss 下降很快但验证效果不佳。这通常说明模型在“背题”而不是在“学方法”,即训练集和验证集文本太相似或者数据量太少。行业内在这个阶段会做数据增强,把一个意思用多种风格重写,保证模板也能泛化。
4. 大模型部署与推理优化:从能跑到跑得快
微调之后必然要落地。很多人卡在“模型能跑”和“服务能上线”中间,原因是训练框架和推理框架的思路完全不同。训练时我们恨不得大 batch 把显存吃满,推理时却要管理 QPS、延迟、上下文长度。所以我单开一节来讲部署选型和优化。
4.1 推理框架选型:为什么优先 vLLM
聊到推理框架,你大概率会听到 vLLM、SGLang、TensorRT-LLM、llama.cpp 这几种。个人开发者和中小团队接入局域网或公网服务,我默认第一个建议就是 vLLM。核心原因是它的 PagedAttention 机制把 KV Cache 换成了不连续的分页存储,显存利用率可以提升非常多。这在长上下文场景尤其重要,传统推理框架每请求预留固定长度的 KV Cache,来十个并发请求就可能在无效保留区浪费一半显存。
vLLM 另一个杀手特性是 Continuous Batching。传统逐请求生成是“一批必须全部结束后才能进新请求”,Continuous Batching 允许每个请求独立进行到不同步,一个请求中断生成时,GPU 线程立刻去跑另一个请求,吞吐率能提升数倍。部署起来也简单,一条命令就能起一个兼容 OpenAI 接口的服务:
python -m vllm.entrypoints.openai.api_server \ --model /path/to/your_model \ --tensor-parallel-size 1 \ --gpu-memory-utilization 0.9 \ --max-model-len 8192这里的几个参数值得解释。tensor-parallel-size 是多卡 deployment 时用,比如两张 A100 就把模型参数切分到两张卡上,每张算一半。gpu-memory-utilization 是 vLLM 独家的显存预算比例,你设为 0.9 意思是把 90% 的显存都交给 vLLM 调度,而不是预留给别的东西。Max-model-len 直接决定设备能支持的最大上下文,设得越高,KV Cache 预留越多,能支持的并发就越少。所以要结合业务真实需要的长度去配置,别一味拉长。
4.2 量化:到底要不要做 4bit 推理
大模型量化的本质是“用更少的 bit 表达权重数值”。量化方式分为训练后量化和量化感知训练两类,前者简称为 PTQ,后者为 QAT。绝大多数部署场景使用 PTQ 就够。我经常用的 GPTQ 和 AWQ 都是 PTQ。做 4bit 量化之后,7B 模型权重从约 14GB 降到约 4GB,显存压力骤减,推理速度因为访存减少反而更快。
但量化不是没有代价。权重被压缩之后会有少量精度损失,敏感业务场景下,回答的法律条款、数值计算等结果可能出错。如果模型部署目的是代码生成或数学推理,我会优先选择保持 8bit 或直接不量化,搭配 KV Cache 优化去解决显存瓶颈。AWQ 相比 GPTQ 的优势在于它考虑了激活值的分布,只保护对最终输出影响最大的那些权重通道,所以在很多任务上精度保留比 GPTQ 好。
4.3 本地部署的前置资源评估
很多开发者不知道从何评估“本地部署大模型要多少显存”。这里给一个经验范围。7B 模型在 fp16 下加载权重就需要约 14GB 显存,加上 KV Cache 和中间激活值,正式推理至少要 16GB 起步。13B 模型要 28GB 以上。34B 模型哪怕做 4bit 量化,前期加载也要 18GB 左右。所以“让个人电脑智能化”这件事不是不行,而是要先看显卡。
如果只有普通笔记本 CPU,没有独显,也不是不能跑。llama.cpp 提供了纯 CPU 推理方案,对 7B 模型做 4bit 量化后,内存占用可以压到 6GB 左右,速度大概是每秒几个 token,当个文档分析小助手完全够用。我自己就是拿一台旧笔记本跑了个量化模型,用来做本地文本分类和草稿润色,彻底摆脱了对云端 API 的依赖,数据隐私也稳了。
5. 多模态大模型与 AI Agent:底层架构在做什么
多模态大模型的技术热点在标题里也多次出现。很多人以为多模态是“模型同时学会了看图、看字、听声音”,这句话不能算错,但不准确。抛开训练阶段,多模态模型在推理时的核心是把视觉、文本映射到同一个语义空间里,让模型对图片就像对一段文字那样做注意力计算。
5.1 图文对齐架构:LLaVA 式方案的典型脉络
LLaVA 类模型的典型结构包含三块:视觉编码器、投影层、语言模型主干。视觉编码器通常沿用 CLIP 或者 SigLIP,把图片切成固定块再做编码;投影层把视觉向量转换成长度和语言模型隐藏维度一致的向量;语言模型继续做自回归解码生成回复。
这种架构的好处是复用已有单模态基座能力,训练成本相对低。开源社区里大量图文对话模型走的就是这条路线。如果你想训练一个私有领域的看图问答模型,最稳妥的做法不是重新训练整个多模态模型,而是在 LLaVA 基础模型上做 LoRA 微调。具体做法是把图文对数据组织成对话格式,图像路径作为输入前缀,模型的权重冻结大部分,只用 LoRA 去适配你的领域指令。
跨模态对齐这块有一个难点值得注意:视觉编码器本身的分辨率敏感度。很多视觉编码器在 224×224 上预训练,但用户上传的图片往往是长宽不一的高清图,如果直接把大图缩到 224×224,细节信息全丢,推理效果自然很差。最新方案里通常会做动态分辨率处理,把图片切片成多个 tile,再把多个 tile 对应的视觉 token 一并交给语言模型。这种细节才是决定多模态产品好不好用的关键,比选哪个基座重要得多。
5.2 Agent 与工具调用背后的架构设计
AI Agent 是当前最热的落地方向,底层同样离不开大模型的推理链路。一个 Agent 的工作过程可以概括为让模型反复执行“思考 → 调用工具 → 观察结果 → 再思考”的循环。对模型来说,工具调用不是外挂魔法,而是通过把工具描述、参数 schema、历史观察结果组装进上下文,然后让模型按约定格式输出一个“调用动作”。
这里和上一节微调是相通的。如果你用的基座模型本身没经过很好的工具调用训练,即便你给它再详细的工具说明,它也会胡编参数。解决办法有两个:要么选用原生支持 tool calling 的基座(比如 Qwen 系列),要么用数据集中做工具调用格式的监督微调。数据里要包含完整的多轮工具调用过程,模型才学得会“先观察工具返回结果再给最终答复”这件事。
当前工程化领域还出现了一个值得关注的基础设施协议,就是 MCP,它把工具调用接口做成了标准协议。Agent 里的所有工具,包括数据库查询、搜索、文件读写、代码执行,都可以挂到统一网关下供模型调用。我自己在处理企业内部 Agent 项目时优先用这一套,最大好处是新增工具不必改 Agent 代码,只要实现一次 MCP 服务端注册即可。
6. 常见问题与源码级排查技巧实录
这部分是我最想写的,因为占了真实开发过程中一半以上的时间。下面这些坑,基本都是我反复踩过并且确认过根因的。
6.1 显存不足:是谁吃光了我的显卡
显存爆掉几乎是每个人都会遇到的。最粗暴的排查方式是看报错信息里是 OOM 还是 GPU memory error。OOM 多发生在训练阶段,表现为 CUDA out of memory。常见原因和解决方案如下:
| 现象 | 大概率原因 | 处理建议 |
|---|---|---|
| 加载模型就 OOM | 模型权重太大 | 切换 4bit/8bit 量化加载 |
| 训练时前几个 step 就爆 | batch size 过大 | 调小 batch,开 gradient accumulation |
| 推理长文本时中途爆 | KV Cache 超预算 | 降低 max-model-len,换 GQA 架构模型 |
| 多卡训练时单卡爆 | 数据并行负载不均 | 检查 batch 是否正确分配到各卡 |
还有一个容易被忽略的细节:transformers 的 from_pretrained 默认会把模型参数加载到 GPU 显存里,但如果显存不够,系统可能会先尝试分配内存到 CPU 再复制,反而导致共享内存空间被大量占用。这种情况下可以先加载到 CPU,再模型并行切片地搬上 GPU。
6.2 输出全是重复 token 怎么办
模型输出变成无限复读,基本可以锁定三个方向:采样参数设置过于激进、训练阶段数据存在重复、生成长度上限击穿了模型的正常输出能力。排在第一位的是采样参数。top_p 如果设得太高、temperature 设得太高,极端情况下采样会趋向于高频循环词。这种问题的排查思路是先调回保守参数,比如 temperature=0.5、top_p=0.8。第二步,检查模型的 repetition_penalty 配置,像 vLLM 里可以直接开 repetition_penalty=1.3,对抑制中文复读有奇效。
在微调模型上,如果训练数据里有大段重复回复,模型会把重复当成一种“正确风格”来学。这种情况单靠采样参数已经救不回来,必须清洗数据。凡是助手回复里出现连续 3 个相同句子的样本,建议直接删掉或者重新生成。
6.3 loss 失控与 NaN 的源头排查
训练时 loss 突然变成 NaN,这事我也遇到过。因果关系通常是学习率过大导致梯度爆炸。另一种可能是混合精度训练时 fp16 的计算溢出,尤其是 loss scale 控制不到位。处理手段很简单:学习率降到原来的五分之一,或者把 fp16 换成 bf16,如果显卡是 Ampere 以后架构,bf16 会更稳,因为它的指数范围和 fp32 一样大,数值溢出风险低得多。
还有一个隐蔽的坑是数据本身含异常特征。训练文本里如果出现了无法用 UTF-8 解码的字符,Tokenizer 可能会把它 encode 成空 token 或者 unknown token,这部分数据如果量太大,模型会把这些异常 token 当作正常模式去学习。所以做大规模数据处理时,在清洗阶段一定要过滤掉无效字符,tokenizer encode 之后检查是否有大量 unknown id。
说道排查思路,我还有一个习惯是“从后往前读代码”。很多人遇到 transformer 库里的报错会直接搜报错信息,其实大多数问题在前向传播源码里一眼就能定位。如果你把 modeling_llama.py 里的 forward 参数列表打印出来,很多 shape 不匹配的错误原因就暴露了。
7. 源码阅读路线与个人总结
最后想聊点实在的建议。对于准备深入大模型源码的人,我个人推荐的阅读顺序是先读 model 源码,再读 generate 循环,再读 trainer 和 data collator,最后再去追算子层。这个顺序的逻辑是:先从全局掌握输入输出流,再理解采样和停止条件,最后才去扣数据流细节。很多人一开始就扎进 Kernel、CUDA 算子优化,结果看了一个星期连 embedding 和 attention mask 都对不上号,这种阅读方式极其劝退。
如果基础偏薄弱,先从 Hugging Face Transformers 源码开始,不用直接碰训练框架。把 LlamaForCausalLM 的前向计算完整走一遍,你会建立对大模型整体的认知。之后再看 PEFT 和 vLLM 源码,你会发现大模型相关的开源生态高度依赖同一套抽象:模型层、采样层、调度层。
实际上,我自己在带团队时发现一个规律:那些能快速定位问题、能独立完成部署调优的人,并不是记了多少论文结论,而是真的把源码走了两遍。读第一遍是跟着注释理解流程,读第二遍是带着自己的疑问去验证,比如“KV Cache 到底存在哪个变量里”“padding token 在 mask 里怎么被屏蔽的”。第二个阶段才是真正沉淀经验的过程。希望这篇内容能帮你把第一遍的路走得顺一点,至少不再对着那几个重复出现的文件名发怵。