手把手从零实现MoE、RoPE、GQA:打造下一代推理大模型实战教程
2026/5/8 21:27:31 网站建设 项目流程

本文深入剖析了现代大模型中MoE、RoPE、GQA等核心组件的原理与实现,详细阐述了这些技术如何提升模型的推理能力与效率。通过从基础Transformer架构出发,逐步介绍架构改进与训练技术的结合,使读者理解早期Transformer设计的局限性以及现代架构的突破。文章不仅提供了完整的代码实现,还涵盖了预训练流程、数据集构建、学习率调度和训练循环等关键环节,为读者构建下一代推理大模型提供了全面的实战指导。


零废话指南:从零手写 MoE、RoPE、分组查询注意力构建大语言模型

有一个词叫**“时刻”(moment),在这十年里既让 AI 投资者心惊胆战,又让他们兴奋不已。对一些人来说,这是像 2022 年末**“ChatGPT 时刻”**之后那样疯狂印钞,最终直到今天,美股大盘仍靠七大 AI 巨头支撑。而对另一些人来说,2025 年开局则是一记冰冷的警钟——“DeepSeek 时刻”。**

2025 年 1 月下旬,一家名不见经传的中国初创公司在“地下室”里造出了世界级大模型,性能超越当时所有模型,而耗电量却低得令人怀疑。这让投入巨型数据中心的数十亿美元沦为笑柄,纳斯达克暴跌 3.1%,标普 500 跌 1.5%,AI 宠儿英伟达暴跌 16.9%。

DeepSeek-R1 凭借其推理能力真正震撼了整个行业。从那以后,推理模型成为复杂智能体、多步任务等场景的首选。随后 Gemini 2.5 Pro、OpenAI o3、Qwen 2.5,以及最近的 Gemini 3 Pro 相继推出。但从普通语言模型到推理模型的转变,并非只靠新架构,而是架构改进 + 全新训练方式的结合,赋予了大模型天然的多步思考能力。

在本文中,我们将拆解让现代 Transformer 更强大、更高效的架构突破,并剖析实现推理行为的训练技术。你将从零实现**混合专家(MoE)、旋转位置编码(RoPE)、分组查询注意力(GQA)**等核心组件,并理解为什么早期 Transformer 设计远比如今模型低效。


快速回顾

新读者可以先阅读我之前的文章,其中详细讲解了基础 Transformer,为本文打下基础。

我们讲过自注意力模块:通过三个可学习矩阵——查询矩阵 Q、键矩阵 K、值矩阵 V——学习序列中不同词之间的关系。

模型被拆分为多个并行注意力头(多头注意力),在不同子空间和位置上捕捉关系,从而学习更丰富、更复杂的模式。你已经了解模型如何通过在庞大词表中选择概率最高的若干 token 来正确预测下一个词,这一过程称为**解码(decoding)。**我们还介绍了预训练及其数据集结构。

# 预训练数据结构input_ids: [101, 102, 103, 104, 105]untokenized input_ids: ["The", "cat", "sat", "on", "the"]target_ids: [102, 103, 104, 105, 106]untokenized target_ids: ["cat", "sat", "on", "the", "mat"]

最后,你学习了如何对模型进行**领域微调,**使其适配具体任务。


需要解决的局限性

尽管 Transformer 架构远超以往所有模型,但要用于关键场景(低延迟、高能效、长上下文、严格性能约束),仍需大幅改进。主要局限如下:

可扩展性与计算开销

注意力矩阵需要计算并存储输入 token 之间的关系。我们简单算一笔账:

内存占用若使用 FP32 存储,注意力矩阵的内存消耗如下:

以常用上下文长度 4096 token 为例: 注意力矩阵恒为context_length × context_length,内存呈平方级增长 O(n²)。

4096² = 16777216 个数值 内存 = 1670 万 × 4 字节 ≈67MB/头若有 16–48 个头,单层可达 GB 级。

再加上 Q/K/V 投影矩阵。设序列长度 L、隐层维度 d_model:

  • Q = X × W_q
  • K = X × W_k
  • V = X × W_v

每个投影矩阵 W 形状为d_model × d_head,Q/K/V 激活形状为L × d_head。

以标准配置: d_model=768,d_head=64,48 个注意力头:

768×64 = 49152 参数 × 4 字节 ≈196 KBQ/K/V 共 3 个:3×196 KB =588 KB/头48 头:588 KB × 48 ≈28 MB(仅投影矩阵)

训练/推理时还要保存 Q/K/V 激活:

单张量内存 = L × d_head × 4 = 4096×64×4 =1 MBQKV 共 3 MB,48 头 =144 MB

计算量每次前向传播包含两次矩阵乘法:QKᵀsoftmax(QKᵀ)V。

4096² × 64 =10.7 亿次操作/头/层

一次前向传播接近1.5 万亿次运算。这就是原始 Transformer 推理慢、资源极高的根本原因。

静态位置编码

静态编码为 token 分配固定位置,推理时遇到长于训练长度的上下文泛化能力差。且它对相对位置信息保留极少,而相对位置对语言任务往往更重要。

常用的正弦位置编码用不同频率的正弦/余弦函数表示位置,而非可学习嵌入。因其确定性,模型可外推到更长序列。且正弦余弦的周期性允许模型通过线性变换从绝对位置推导相对位置。

但问题是:**位置信息只在输入层注入,不直接修改注意力机制。**因此建模相对位置的能力依然有限,尤其长上下文推理。

上下文窗口限制

原始 Transformer 上下文窗口固定(512–2048 token),只能“看到”有限文本。输入超长时必须截断或分块,导致**上下文丢失,**长文档理解性能大幅下降。


RoPE 旋转位置编码

在讲正式内容前,先看一个简单思想: 对两个夹角为 θ 的向量 A、B,它们的点积:

关键点:点积只取决于向量的角度差,与绝对角度无关。

注意力分数正是 Q 与 K 的点积。 与其显式加入绝对位置,不如直接将相对位置融入 Q、K 向量本身。

论文RoFormer: Enhanced Transformer with Rotary Position Embedding提出这一思想: 将每个向量(Q、K)视为 2D 平面上的点,并根据位置旋转一定角度。

  • 位置 1 → 旋转 θ₁
  • 位置 2 → 旋转 θ₂
  • 位置 3 → 旋转 θ₃ ……

每一对维度(如 [x,y])独立以不同频率旋转,但核心思想不变:位置 = 旋转

旋转频率公式:

  • d_model:嵌入维度
  • i:维度下标
  • θ_base:通常为 10000(来自正弦编码)
  • **高频对:**旋转快 → 擅长局部关系
  • **低频对:**旋转慢 → 擅长长距离关系

可以类比时钟: 秒针快(局部)、分针中(中等距离)、时针极慢(全局结构)。

RoPE(旋转位置编码)正是如此,使注意力分数天然成为相对位置的函数。

旋转频率可直接写成 PyTorch 代码:

assert head_dim % 2 == 0inv_freq = 1.0 / (theta_base ** ( torch.arange(0, head_dim, 2)[: (head_dim // 2)].float() / head_dim))positions = torch.arange(context_length, dtype=dtype)angles = positions[:, None] * inv_freq[None, :]angles = torch.cat([angles, angles], dim=1)cos = torch.cos(angles)sin = torch.sin(angles)

向量旋转可在注意力中直接调用:

defrope(x, cos, sin, offset=0):# x: (batch_size, num_heads, seq_len, head_dim) batch_size, num_heads, seq_len, head_dim = x.shapeassert head_dim % 2 == 0 x1 = x[..., : head_dim // 2] # 前半部分 x2 = x[..., head_dim // 2:] # 后半部分 cos = cos[offset:offset + seq_len, :].unsqueeze(0).unsqueeze(0) sin = sin[offset:offset + seq_len, :].unsqueeze(0).unsqueeze(0) rotated = torch.cat((-x2, x1), dim=-1) x_rotated = (x * cos) + (rotated * sin)return x_rotated.to(dtype=x.dtype)

分组查询注意力 GQA

解决多头注意力性能问题的思路:减少不同头之间冗余的 K、V 向量。

《Fast Transformer Decoding》提出多查询注意力 MQA:所有头共享一组 K、V,每个头有独立 Q。大幅降低 KV Cache 内存,但精度下降明显。

后来分组查询注意力 GQA取得完美平衡: 将多个查询头分为若干组,**每组共享一套 KV 头。**在保持传统多头表达能力的同时,显著降低内存与带宽需求。

与 MQA 只用一组 KV 不同,GQA 有多组 KV,满足:num_heads % num_kv_groups == 0

原始自注意力实现(回顾)

classSelfAttention(torch.nn.Module):def__init__(self, embed_dim, attention_dim, bias=False, dropout=0.1):super().__init__()self.w_key = nn.Linear(embed_dim, attention_dim, bias=bias)self.w_query = nn.Linear(embed_dim, attention_dim, bias=bias)self.w_value = nn.Linear(embed_dim, attention_dim, bias=bias)self.dropout = nn.Dropout(dropout)defforward(self, x): B, T, _ = x.size() k = self.w_key(x) q = self.w_query(x) v = self.w_value(x) scores = (q @ k.transpose(-2, -1)) / (k.size(-1) ** 0.5) mask = torch.triu(torch.ones(T, T, device=x.device), diagonal=1).bool() scores = scores.masked_fill(mask, float('-1e10')) attn = scores.softmax(dim=-1) attn = self.dropout(attn)return attn @ v

GQA 关键区别

Q 投影不变,但 K、V 投影维度变为:num_kv_groups * head_dim

self.W_query = nn.Linear(d_in, self.d_out, bias=False, dtype=dtype)self.W_key = nn.Linear(d_in, num_kv_groups * head_dim, bias=False, dtype=dtype)self.W_value = nn.Linear(d_in, num_kv_groups * head_dim, bias=False, dtype=dtype)

现代架构还会对 Q、K 做 **RMSNorm,**防止数值过大:

self.q_norm = nn.RMSNorm(head_dim, eps=1e-6)self.k_norm = nn.RMSNorm(head_dim, eps=1e-6)queries = self.q_norm(queries)keys = self.k_norm(keys)

V 不做归一化:因为 V 不参与 softmax,归一化会限制表达能力且无助于稳定性。

之后对 Q、K 施加 RoPE:

queries = rope(queries, cos, sin, offset=start_pos)keys = rope(keys, cos, sin, offset=start_pos)

完整 GQA 实现

classGroupedQueryAttention(nn.Module):def__init__(self, d_in, num_heads, num_kv_groups, head_dim, qk_norm=False, dtype=None):super().__init__()assert num_heads % num_kv_groups == 0assert d_in % num_heads == 0self.num_heads = num_headsself.num_kv_groups = num_kv_groupsself.group_size = num_heads // num_kv_groupsself.head_dim = head_dimself.d_out = num_heads * head_dimself.W_query = nn.Linear(d_in, self.d_out, bias=False, dtype=dtype)self.W_key = nn.Linear(d_in, num_kv_groups * head_dim, bias=False, dtype=dtype)self.W_value = nn.Linear(d_in, num_kv_groups * head_dim, bias=False, dtype=dtype)self.out_proj = nn.Linear(self.d_out, d_in, bias=False, dtype=dtype)if qk_norm:self.q_norm = nn.RMSNorm(head_dim, eps=1e-6)self.k_norm = nn.RMSNorm(head_dim, eps=1e-6)else:self.q_norm = self.k_norm = Nonedefforward(self, x, mask, cos, sin, start_pos=0): b, num_tokens, _ = x.shape queries = self.W_query(x) keys = self.W_key(x) values = self.W_value(x) queries = queries.view(b, num_tokens, self.num_heads, self.head_dim).transpose(1, 2) keys = keys.view(b, num_tokens, self.num_kv_groups, self.head_dim).transpose(1, 2) values = values.view(b, num_tokens, self.num_kv_groups, self.head_dim).transpose(1, 2)ifself.q_norm: queries = self.q_norm(queries)ifself.k_norm: keys = self.k_norm(keys) queries = rope(queries, cos, sin, offset=start_pos) keys = rope(keys, cos, sin, offset=start_pos)# 扩维匹配头数 keys = keys.repeat_interleave(self.group_size, dim=1) values = values.repeat_interleave(self.group_size, dim=1) attn_scores = queries @ keys.transpose(2, 3) attn_scores = attn_scores.masked_fill(mask, -torch.inf) attn_weights = torch.softmax(attn_scores / self.head_dim**0.5, dim=-1) context = (attn_weights @ values).transpose(1, 2) context = context.reshape(b, num_tokens, self.d_out)returnself.out_proj(context)

混合专家 MoE

原始 Transformer 中,**每个 token 都要经过整个模型。**好比医院里所有医生(外科、牙科、神经科)同时治疗每一个病人——非常荒谬。

要模拟智能,结构应更像人脑:特定脑区负责特定认知任务,而非全脑同时激活。

MoE 混合专家的思想: 网络内包含一组专家(expert),由门控(gate/router)为每个 token 只激活最相关的前 K 个专家。这让万亿参数模型在保持质量的同时,**仅少量参数实际参与计算,**带来巨大效率与性能提升。

思想早在 90 年代就已提出,但真正在 Transformer 中规模化落地是:

  • GShard(2020)
  • Switch Transformer(2021)首个万亿参数稀疏 MoE
  • Mixtral(2024)证明稀疏 MoE 可工业化落地

工作机制示例

Tokens:T0,T1,T2,T3Experts:E0,E1,E2Top-k (K):2expertspertoken

门控网络gate(x)输出每个专家的分数,选取Top-2:

topk_scores, topk_expert_indices = torch.topk(scores, self.num_experts_per_tok, dim=-1)topk_probs = torch.softmax(topk_scores, dim=-1)

形状:

  • topk_scores: (b, seq_len, k)
  • topk_expert_indices: (b, seq_len, k)

为避免逐 token 循环低效,采用

按专家并行处理。

展平后:

topk_expert_indices_flat = topk_expert_indices.reshape(-1, K)topk_probs_flat = topk_probs.reshape(-1, K)

对每个专家 E0/E1/E2,只计算分配给它的 token,并乘以门控概率:

E0(T0)→y0E0(T2)→y2E0(T3)→y3output[T0]+=0.88·y0output[T2]+=0.27·y2output[T3]+=0.62·y3

最终每个 token 输出是所选专家的加权和:

output[T0]=0.88·E0(T0)+0.12·E1(T0)output[T1]=0.82·E1(T1)+0.18·E2(T1)output[T2]=0.73·E2(T2)+0.27·E0(T2)output[T3]=0.62·E0(T3)+0.38·E1(T3)

激活函数:SiLU 而非 ReLU

ReLU 负部分硬归零,会导致**门控死亡、梯度稀疏脆弱。**LLaMA、Mistral 均使用 **SiLU,**下降更平滑,适合专家平滑专业化。

hidden = F.silu(self.w_gate[e](sslocal://flow/file_open?url=x&flow_extra=eyJsaW5rX3R5cGUiOiJjb2RlX2ludGVycHJldGVyIn0=)) * self.w_up[e](sslocal://flow/file_open?url=x&flow_extra=eyJsaW5rX3R5cGUiOiJjb2RlX2ludGVycHJldGVyIn0=)

完整 MoE 模块

classSparseMoeBlock(nn.Module):def__init__(self, params):super().__init__()self.num_experts_per_tok = params["num_experts_per_tok"]self.num_experts = params["num_experts"]self.emb_dim = params["emb_dim"]self.gate = nn.Linear(params["emb_dim"], params["num_experts"], bias=False, dtype=params["dtype"])self.w_gate = nn.ModuleList([ nn.Linear(params["emb_dim"], params["moe_intermediate_size"], bias=False, dtype=params["dtype"])for _ inrange(params["num_experts"]) ])self.w_up = nn.ModuleList([ nn.Linear(params["emb_dim"], params["moe_intermediate_size"], bias=False, dtype=params["dtype"])for _ inrange(params["num_experts"]) ])self.w_down = nn.ModuleList([ nn.Linear(params["moe_intermediate_size"], params["emb_dim"], bias=False, dtype=params["dtype"])for _ inrange(params["num_experts"]) ])defforward(self, x): scores = self.gate(x) topk_scores, topk_expert_indices = torch.topk(scores, self.num_experts_per_tok, dim=-1) topk_probs = torch.softmax(topk_scores, dim=-1) batch, seq_len, _ = x.shape x_flat = x.reshape(batch * seq_len, -1) out_flat = torch.zeros(batch * seq_len, self.emb_dim, device=x.device, dtype=x.dtype) topk_indices_flat = topk_expert_indices.reshape(-1, self.num_experts_per_tok) topk_probs_flat = topk_probs.reshape(-1, self.num_experts_per_tok) selected_experts = torch.unique(topk_indices_flat)for expert_id_tensor in selected_experts: expert_id = int(expert_id_tensor.item()) mask = topk_indices_flat == expert_idifnot mask.any():continue token_mask = mask.any(dim=-1) selected_idx = token_mask.nonzero(as_tuple=False).squeeze(-1)if selected_idx.numel() == 0:continue expert_input = x_flat.index_select(0, selected_idx) hidden = F.silu(self.w_gate[expert_id](sslocal://flow/file_open?url=expert_input&flow_extra=eyJsaW5rX3R5cGUiOiJjb2RlX2ludGVycHJldGVyIn0=)) * self.w_up[expert_id](sslocal://flow/file_open?url=expert_input&flow_extra=eyJsaW5rX3R5cGUiOiJjb2RlX2ludGVycHJldGVyIn0=) expert_out = self.w_down[expert_id](sslocal://flow/file_open?url=hidden&flow_extra=eyJsaW5rX3R5cGUiOiJjb2RlX2ludGVycHJldGVyIn0=) mask_selected = mask[selected_idx] slot_indices = mask_selected.int().argmax(dim=-1, keepdim=True) selected_probs = torch.gather( topk_probs_flat.index_select(0, selected_idx), dim=-1, index=slot_indices ).squeeze(-1) out_flat.index_add_(0, selected_idx, expert_out * selected_probs.unsqueeze(-1))return out_flat.reshape(batch, seq_len, self.emb_dim)

Decoder 模块

LLaMA、Qwen 等大模型均为**纯解码器架构。**本文采用类似 Qwen 结构:用 MoE 替换标准 FFN 前馈网络。

注意:每个子层(注意力/前馈)都有**残差连接,**原始输入直接加回输出,防止梯度消失。

classDecoder(nn.Module):def__init__(self, params):super().__init__()self.emb_dim = params["emb_dim"]self.num_heads = params["num_heads"]self.num_kv_groups = params["num_kv_groups"]self.head_dim = self.emb_dim // self.num_headsself.dtype = params["dtype"]self.qk_norm = params["qk_norm"] cos, sin = get_rope_params(self.head_dim, context_length=params.get("context_length", 4096))self.register_buffer('cos', cos)self.register_buffer('sin', sin)self.attention_block = GroupedQueryAttention( d_in=self.emb_dim, num_heads=self.num_heads, num_kv_groups=self.num_kv_groups, head_dim=self.head_dim, qk_norm=self.qk_norm, dtype=self.dtype )self.feed_forward = SparseMoeBlock(params)self.norm1 = nn.RMSNorm(self.emb_dim, eps=1e-6)self.norm2 = nn.RMSNorm(self.emb_dim, eps=1e-6)defforward(self, x, mask, start_pos=0): prev = x x = self.norm1(x) x = self.attention_block(x, mask, self.cos, self.sin, start_pos) + prev prev = x x = self.norm2(x) x = self.feed_forward(x) + prevreturn x

最终架构:完整模型

  • 输入:token embedding
  • 位置信息:完全由 RoPE 提供,无外部位置嵌入
  • 堆叠 Decoder Layer
  • 最终 RMSNorm
  • 线性层投影到词表(LM Head)输出 logits
classQwen(nn.Module):def__init__(self, params):super().__init__()self.embed = nn.Embedding(params["vocab_size"], params["emb_dim"], dtype=params["dtype"])self.decoder = nn.ModuleList([Decoder(params) for _ inrange(params["num_layers"])])self.norm = nn.RMSNorm(params["emb_dim"], eps=1e-6)self.lm_head = nn.Linear(params["emb_dim"], params["vocab_size"], bias=False, dtype=params["dtype"])self.context_length = params.get("context_length", 4096)defforward(self, x, start_pos=0): batch_size, seq_len = x.shape mask = torch.triu(torch.ones(seq_len, seq_len, device=x.device), diagonal=1).bool() mask = mask.unsqueeze(0).unsqueeze(0) x = self.embed(x)for layer inself.decoder: x = layer(x, mask, start_pos=start_pos) x = self.norm(x) logits = self.lm_head(x)return logits

分词器实现

使用 Qwen2.5-7B 分词器,支持特殊 token(包括 `` 推理标记)。

import refrom pathlib import Pathfrom tokenizers import TokenizerclassTokenizer: special_tokens = ["<|endoftext|>","<|im_start|>", "<|im_end|>","<|object_ref_start|>", "<|object_ref_end|>","<|box_start|>", "<|box_end|>","<|quad_start|>", "<|quad_end|>","<|vision_start|>", "<|vision_end|>","<|vision_pad|>", "<|image_pad|>", "<|video_pad|>","", "" ] CHAT_AND_THINK_RE = re.compile(r"(<\|[^>]+?\|>||)")def__init__(self, tokenizer_file_path="tokenizer.json", apply_chat_template=True, add_generation_prompt=False, add_thinking=False):self.apply_chat_template = apply_chat_templateself.add_generation_prompt = add_generation_promptself.add_thinking = add_thinkingself._tok = Tokenizer.from_file(str(Path(tokenizer_file_path)))self._special_to_id = {}for t inself.special_tokens: tid = self._tok.token_to_id(t)if tid isnotNone:self._special_to_id[t] = tidself.pad_token_id = self._special_to_id.get("<|endoftext|>", 0)self.eos_token_id = self._special_to_id.get("<|im_end|>", self.pad_token_id)defencode(self, text, chat_wrapped=None):if chat_wrapped isNone: chat_wrapped = self.apply_chat_template stripped = text.strip()if stripped inself._special_to_id and"\n"notin stripped:return [self._special_to_id[stripped]]if chat_wrapped: text = self._wrap_chat(text) ids = []for part infilter(None, self.CHAT_AND_THINK_RE.split(text)):if part inself._special_to_id: ids.append(self._special_to_id[part])else: ids.extend(self._tok.encode(part).ids)return idsdefdecode(self, ids):returnself._tok.decode(ids, skip_special_tokens=False)def_wrap_chat(self, text): parts = ["<|im_start|>user\n", text.strip(), "\n<|im_end|>\n"]ifself.add_generation_prompt: parts.append("<|im_start|>assistant\n")ifself.add_thinking: parts.append("")return"".join(parts)

重点特殊 token:

  • <|endoftext|>:

    序列结束

  • ...:思维链推理,

    让模型显式生成中间思考步骤


测试生成

import torchimport randomdeftest_model_generation(model, tokenizer, device="cuda"if torch.cuda.is_available() else"cpu"): prompts = ["The future of artificial intelligence is","def fibonacci(n):","Once upon a time in a galaxy far, far away,","The capital of France is","Explain the theory of relativity in simple terms:", ] text = random.choice(prompts)print(f"Input Prompt: '{text}'") input_ids = tokenizer.encode(text) idx = torch.tensor(input_ids, dtype=torch.long).unsqueeze(0).to(device) model.to(device) generated_ids = generate( model=model, idx=idx, max_new_tokens=50, temperature=0.8, top_k=50 ) output_text = tokenizer.decode(generated_ids[0].tolist())print(f"\nGenerated Output:\n{'-'*20}\n{output_text}\n{'-'*20}\n")test_model_generation(model, tokenizer)

未训练模型会输出**杂乱文本,**属于正常现象。


预训练

预训练让模型学习**语言基本规律:**词法、句法、连贯性。

数据集

使用 Andrej Karpathy 整理的FineWeb-Edu教育语料,以流式加载节约内存。

from torch.utils.data import IterableDataset, DataLoaderfrom datasets import load_datasetclassPretrainingDataset(IterableDataset):def__init__(self, tokenizer, seq_length=512, split="train"):self.tokenizer = tokenizerself.seq_length = seq_lengthself.dataset = load_dataset("karpathy/fineweb-edu-100b-shuffle", split=split, streaming=True ).take(10000)def__iter__(self):for item inself.dataset: text = item['text'] token_ids = self.tokenizer.encode(text) token_ids = torch.tensor(token_ids, dtype=torch.long)for i inrange(0, len(token_ids) - self.seq_length, self.seq_length): input_chunk = token_ids[i:i+self.seq_length] target_chunk = token_ids[i+1:i+self.seq_length+1]iflen(input_chunk) == self.seq_length:yield input_chunk, target_chunkdefget_batch_iterator(tokenizer, batch_size=1, seq_length=2048): dataset = PretrainingDataset(tokenizer, seq_length=seq_length)return DataLoader(dataset, batch_size=batch_size, num_workers=0)

数据格式: 输入右移一位作为目标。

损失函数:交叉熵

defcompute_loss(logits, targets, ignore_index=-100): batch_size, seq_len, vocab_size = logits.shape logits_flat = logits.reshape(-1, vocab_size) targets_flat = targets.reshape(-1)return F.cross_entropy(logits_flat, targets_flat, ignore_index=ignore_index)

学习率调度:预热 + 余弦衰减

defget_lr(step, warmup_steps, max_steps, max_lr, min_lr):if step < warmup_steps:return max_lr * (step+1) / warmup_stepselif step > max_steps:return min_lrelse: decay_ratio = (step - warmup_steps) / (max_steps - warmup_steps) coeff = 0.5 * (1.0 + math.cos(math.pi * decay_ratio))return min_lr + coeff * (max_lr - min_lr)

策略:小起点 → 稳步上升 → 平滑余弦衰减

超参数配置

settings = {"learning_rate":3e-4,"weight_decay":0.1,"max_steps":100_000,"batch_size":32,"warmup_steps":2000,"max_lr":3e-4,"min_lr":3e-5,"beta1":0.9,"beta2":0.95,"grad_clip":1.0,}params = {"vocab_size":151936,"emb_dim":3584,"num_heads":28,"num_kv_groups":7,"num_layers":28,"num_experts":4,"num_experts_per_tok":2,"moe_intermediate_size":14336,"context_length":32768,"qk_norm": True,}

完整训练循环(含混合精度、梯度累积、早停)

代码较长,已完整保留原文逻辑:混合精度、GradScaler、梯度裁剪、余弦调度、早停、 checkpoint 保存。

训练后示例输出:

Input Prompt: 'The capital of France is'Generated Output:The capital of France is Paris which is a prime example of their ancient world in Europe, which it is the oldest European country, a major city in the world, and a center that will make it an area of rich history and culture. The city is also a state of ancient Greece,

总结

你已经**从零完整实现现代推理大模型,**包含当前 SOTA 模型(DeepSeek-R1、Qwen 2.5、LLaMA 4、Gemini 3)的全部核心技术:

  • RoPE 旋转位置编码
  • GQA 分组查询注意力
  • MoE 稀疏混合专家
  • RMSNorm、SiLU、残差连接
  • 解码器-only 架构
  • 预训练流程 + 数据集 + 调度器 + 训练循环

本文从原理到代码无黑盒、无高级库依赖,是理解下一代推理 LLM最完整的实战教程之一。


结语:抓住大模型时代的职业机遇

AI大模型的发展不是“替代人类”,而是“重塑职业价值”——它淘汰的是重复性、低附加值的工作,却催生了更多需要“技术+业务”交叉能力的高端岗位。对于求职者而言,想要在这波浪潮中立足,不仅需要掌握Python、TensorFlow/PyTorch等技术工具,更要深入理解目标行业的业务逻辑(如金融的风险控制、医疗的临床需求),成为“懂技术、懂业务”的复合型人才。

无论是技术研发岗(如算法工程师、研究员),还是业务落地岗(如产品经理、应用工程师),大模型都为不同背景的职场人提供了广阔的发展空间。只要保持学习热情,紧跟技术趋势,就能在AI大模型时代找到属于自己的职业新蓝海。

最近两年大模型发展很迅速,在理论研究方面得到很大的拓展,基础模型的能力也取得重大突破,大模型现在正在积极探索落地的方向,如果与各行各业结合起来是未来落地的一个重大研究方向

大模型应用工程师年包50w+属于中等水平,如果想要入门大模型,那现在正是最佳时机

2025年Agent的元年,2026年将会百花齐放,相应的应用将覆盖文本,视频,语音,图像等全模态

如果你对AI大模型入门感兴趣,那么你需要的话可以点击这里大模型重磅福利:入门进阶全套104G学习资源包免费分享!

扫描下方csdn官方合作二维码获取哦!

给大家推荐一个大模型应用学习路线

这个学习路线的具体内容如下:

第一节:提示词工程

提示词是用于与AI模型沟通交流的,这一部分主要介绍基本概念和相应的实践,高级的提示词工程来实现模型最佳效果,以现实案例为基础进行案例讲解,在企业中除了微调之外,最喜欢的就是用提示词工程技术来实现模型性能的提升

第二节:检索增强生成(RAG)

可能大家经常会看见RAG这个名词,这个就是将向量数据库与大模型结合的技术,通过外部知识来增强改进提升大模型的回答结果,这一部分主要介绍RAG架构与组件,从零开始搭建RAG系统,生成部署RAG,性能优化等

第三节:微调

预训练之后的模型想要在具体任务上进行适配,那就需要通过微调来提升模型的性能,能满足定制化的需求,这一部分主要介绍微调的基础,模型适配技术,最佳实践的案例,以及资源优化等内容

第四节:模型部署

想要把预训练或者微调之后的模型应用于生产实践,那就需要部署,模型部署分为云端部署和本地部署,部署的过程中需要考虑硬件支持,服务器性能,以及对性能进行优化,使用过程中的监控维护等

第五节:人工智能系统和项目

这一部分主要介绍自主人工智能系统,包括代理框架,决策框架,多智能体系统,以及实际应用,然后通过实践项目应用前面学习到的知识,包括端到端的实现,行业相关情景等

学完上面的大模型应用技术,就可以去做一些开源的项目,大模型领域现在非常注重项目的落地,后续可以学习一些Agent框架等内容

上面的资料做了一些整理,有需要的同学可以下方添加二维码获取(仅供学习使用)

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询