
如果让我回到 17 岁正在为“以后学什么”而迷茫我大概率会选择从头构建一个大型语言模型LLM。这个领域当时看起来像数学、编程、算法交织而成的“高墙”但真正走下来你会发现它比想象中更值得学也比想象中更有迹可循——不需要先读完整本《深度学习》不需要拥有一张顶级显卡也不需要一开始就能推导全部公式。本文会用“如果我 17 岁重新开始”的视角整理一份从零学习 LLM 的完整路径。内容包括先学什么、原理如何拆解、精度问题FP16、FP32、BF16为什么重要、怎么用开源代码训练一个微型 GPT、以及走向 Agent、RAG、MCP 等应用层时应该补哪些知识。文章偏工程实践适合刚接触 LLM 的读者也适合想梳理自己学习体系的人。1. 为什么要从零开始构建 LLM1.1 LLM 是什么为什么值得学大型语言模型Large Language ModelLLM是一类以 Transformer 为骨干、在海量文本上做自监督学习的模型。它的核心任务是预测文本序列中下一个 Token 是什么但通过足够大的参数量和足够多的训练数据模型会“涌现”出语法理解、知识记忆、逻辑推理甚至工具调用能力。从学习角度来看LLM 和普通深度学习模型有一个重要区别它跨越了数据工程、模型结构、分布式训练、推理优化、应用编排等多个领域。也就是说你学的不只是一个模型而是一整套现代 AI 工程方法论。即使以后不做大模型方向这套方法论里的 Python、PyTorch、数据处理、性能调优能力也都能迁移到其他技术方向。1.2 为什么“从头构建”是最好入门方式很多人学 LLM 是从调用 API 开始的比如用现成接口发一条 prompt拿到结果再研究提示词。这种方式能让你快速体验 LLM但很难理解模型为什么这么回答、为什么某些提示词有效、为什么同样的模型在不同框架下表现不一致。“从头构建”的意思是用一个小型数据集、一个小规模模型亲手把 Token 化、Embedding、注意力机制、训练循环、采样生成这些环节都写一遍。你会发现这些概念不再是大模型黑盒里的神秘东西而是自己代码里一个个可调试的函数。Karpathy 的 nanoGPT、llm.c 项目以及他提出的 “LLM wiki” 学习方式本质上都在强调同一件事不要只消费 LLM去亲手构建一个 LLM 的“最小版本”你会获得完全不同的理解深度。1.3 17 岁学习 LLM 的资源优势现在的学习条件比以前好很多PyTorch 和 Hugging Face 生态开源、大量免费教程和讲义、云 GPU 资源可以按小时租用、社区开源模型和训练日志公开可查。17 岁入局最大的优势不是知识储备而是时间和好奇心。哪怕每天只能抽出一小时先啃下 Python 基础再用三个月跑通一个小型 GPT就已经超过了绝大多数只停留在“调接口”阶段的开发者。2. 学习路线总览2.1 明确阶段目标从零到能独立训练和部署一个 LLM大致可以分为六个阶段。不必每个阶段都达到“精通”但每个阶段都要有可交付的成果。阶段核心学习内容可交付成果阶段一Python 基础、PyTorch 张量操作能写数据预处理和简单训练循环阶段二线性代数、概率论、Softmax、交叉熵能看懂损失函数和梯度下降阶段三Transformer 原理、注意力机制、GPT 结构能画出模型结构并说出数据流向阶段四用 nanoGPT 或类似项目训练“迷你 GPT”得到可运行的小模型并生成文本阶段五混合精度、LoRA、量化、部署推理能在有限显存下训练或微调模型阶段六RAG、Agent、MCP、编排框架能开发一个 LLM 应用2.2 值得长期跟进的资源这一节只推荐个人觉得很适合自学的资源不是广告也没有固定优先级。nanoGPTAndrej Karpathy 开源的极简 GPT 训练项目代码量不大适合精读和魔改。llm.cKarpathy 用纯 C/CUDA 实现的 LLM 训练适合想理解底层算子的人。LLM wiki 学习法利用 LLM 帮你整理、检索、对话式学习知识库。把笔记做成可交互的“个人知识库”是很好的学习方式。3Blue1Brown 的神经网络视频从直觉上理解梯度下降和反向传播。Hugging Face 课程偏应用适合学完基础后快速上手指南。2.3 学习节奏建议不要试图一口气学完所有理论。我的建议是“螺旋式上升”先花两周跑通一个最小例子哪怕完全不理解内部原理然后在实验过程中不断反问“为什么这里要除以 sqrt(d_k)”“为什么 loss 下降变慢了”再带着问题去查理论。学编程和学模型原理交替进行比先啃完一本大部头再动手高效得多。3. 基础准备编程、数学与深度学习3.1 Python 该掌握到什么程度构建 LLM 不需要你先成为 Python 高手但下面这些技能是跑通项目的最低要求变量、列表、字典、集合等基础数据结构函数定义、类定义、模块导入文件读写和文本处理简单的异常处理能读懂torch.Tensor的基本运算和.shape。如果你还不太熟悉 Python可以挑一本轻量教程快速过一遍然后直接开始写 PyTorch 代码。写代码时遇到不懂的语法再查效率比系统学完一轮高得多。3.2 数学只学真正用得上的部分LLM 入门用到的数学比很多人想象中少。线性代数主要用矩阵乘法。Transformer 里所有 Linear 层本质都是矩阵乘法。概率论主要用 Softmax 和采样。Softmax 把分数变成概率采样决定生成哪个 Token。信息论/统计主要用交叉熵损失用来衡量预测分布和真实标签的差距。这里给出一个 Softmax 的 Python 示例理解它对你后面读模型代码很有帮助import math def softmax(logits): # 先减去最大值防止指数运算溢出 max_logit max(logits) exp_logits [math.exp(x - max_logit) for x in logits] sum_exp sum(exp_logits) return [x / sum_exp for x in exp_logits] logits [2.0, 1.0, 0.1] print(softmax(logits)) # 输出概率之和约为 1.03.3 PyTorch 最小上手你不需要学完 PyTorch 全部 API重点是四组知识torch.Tensor的创建、形状变换、索引torch.nn.Linear、torch.nn.Embedding、torch.nn.LayerNorm这些基础模块torch.nn.functional.cross_entropy损失函数torch.optim.AdamW优化器。下面是最小训练循环的骨架它展示了 LLM 训练流程的基本套路import torch import torch.nn as nn model nn.Linear(10, 5) # 一个最简单的线性层 optimizer torch.optim.AdamW(model.parameters(), lr1e-3) loss_fn nn.CrossEntropyLoss() x torch.randn(32, 10) # 模拟一个 batch32 条样本 y torch.randint(0, 5, (32,)) # 模拟标签 logits model(x) loss loss_fn(logits, y) optimizer.zero_grad() loss.backward() optimizer.step() print(loss.item())这个循环里包含了全部核心逻辑前向计算、计算损失、反向传播、更新参数。后面训练 GPT 时只是把model换成了更复杂的结构数据预处理和训练主循环并没有本质变化。4. 核心原理Transformer 与 GPT4.1 从词嵌入到上下文表示LLM 无法直接处理文本所以第一步是把文本变成数字。常见做法是先建立词表Vocabulary把每个 Token 映射成一个整数 ID然后通过 Embedding 层把 ID 映射成向量。GPT 结构里还会加上位置编码让模型知道 Token 的顺序。大概的数据流向是原始文本 - Token ID - Token Embedding Position Embedding - Transformer Block - 输出向量 - 预测下一个 Token 的概率分布4.2 自注意力机制自注意力Self-Attention是 Transformer 的核心。它做的事情可以这样理解对于序列中的每个位置计算它和其他位置的相关性然后用相关性加权汇总其他位置的信息。实现时通常把输入向量通过三个 Linear 层投影为 Query、Key、Value。Query 和 Key 计算相似度相似度经过 Softmax 变成权重再用权重对 Value 加权求和。GPT 还使用因果掩码让第 i 个 Token 只能看到前 i 个 Token从而保证是“自回归”的。4.3 一个完整的 GPT 模型骨架为了不陷入长篇大论这里给出一个教学版 GPT 模型的核心结构。它参考 nanoGPT 的组织方式但为了可读性做了简化适合自己复现和修改。# 文件路径tinygpt/model.py import torch import torch.nn as nn from torch.nn import functional as F class CausalSelfAttention(nn.Module): def __init__(self, n_embd, n_head, block_size, dropout): super().__init__() self.n_head n_head self.key nn.Linear(n_embd, n_embd) self.query nn.Linear(n_embd, n_embd) self.value nn.Linear(n_embd, n_embd) self.proj nn.Linear(n_embd, n_embd) self.register_buffer( mask, torch.tril(torch.ones(block_size, block_size)).view(1, 1, block_size, block_size) ) self.dropout nn.Dropout(dropout) def forward(self, x): B, T, C x.shape k self.key(x) q self.query(x) v self.value(x) k k.view(B, T, self.n_head, C // self.n_head).transpose(1, 2) q q.view(B, T, self.n_head, C // self.n_head).transpose(1, 2) v v.view(B, T, self.n_head, C // self.n_head).transpose(1, 2) att (q k.transpose(-2, -1)) * (C // self.n_head) ** -0.5 att att.masked_fill(self.mask[:, :, :T, :T] 0, float(-inf)) att F.softmax(att, dim-1) att self.dropout(att) y att v y y.transpose(1, 2).contiguous().view(B, T, C) return self.proj(y) class Block(nn.Module): def __init__(self, n_embd, n_head, block_size, dropout): super().__init__() self.ln1 nn.LayerNorm(n_embd) self.attn CausalSelfAttention(n_embd, n_head, block_size, dropout) self.ln2 nn.LayerNorm(n_embd) self.mlp nn.Sequential( nn.Linear(n_embd, 4 * n_embd), nn.GELU(), nn.Linear(4 * n_embd, n_embd), nn.Dropout(dropout), ) def forward(self, x): x x self.attn(self.ln1(x)) x x self.mlp(self.ln2(x)) return x class TinyGPT(nn.Module): def __init__(self, vocab_size, n_embd, n_head, n_layer, block_size, dropout0.0): super().__init__() self.token_embedding nn.Embedding(vocab_size, n_embd) self.position_embedding nn.Embedding(block_size, n_embd) self.blocks nn.Sequential( *[Block(n_embd, n_head, block_size, dropout) for _ in range(n_layer)] ) self.ln_f nn.LayerNorm(n_embd) self.lm_head nn.Linear(n_embd, vocab_size, biasFalse) self.block_size block_size def forward(self, idx): B, T idx.shape assert T self.block_size tok self.token_embedding(idx) pos self.position_embedding(torch.arange(T, deviceidx.device)) x tok pos x self.blocks(x) x self.ln_f(x) logits self.lm_head(x) return logits这段代码把 GPT 中最关键的三个部分体现出来了因果自注意力、残差连接 LayerNorm、Token Embedding 与位置 Embedding 相加。读懂这段代码Transformer 就不再是抽象概念。4.4 大模型的训练三阶段从预训练到能用LLM 通常经历三个阶段预训练在海量文本上预测下一个 Token学会语言规律和世界知识。监督微调SFT用人工撰写的指令-回复数据让模型学会回答问题。对齐RLHF 或 DPO让模型输出更符合人类偏好减少有害或偏见内容。作为学习者第一阶段最值得亲手实现因为它不需要复杂的人工标注数据只需要文本语料就能跑通。这也是 nanoGPT 这类项目能成为入门首选的原因。5. 训练精度FP16、FP32 与 BF16 怎么选5.1 为什么精度问题这么重要很多初学者按默认方式训练 Transformer会突然遇到显存溢出或者 loss 变成 NaN。这背后往往就是精度问题。模型参数、梯度和中间激活值都需要显存而同样的模型在不同精度下占用显存结果差异很大。以大模型实际训练为例一个比较粗略的显存估算思路是模型参数量为 P 时如果用 FP32 保存参数和优化器状态再加上梯度训练阶段大约需要 16P 到 20P 字节如果用混合精度和 8 比特优化器这个数字会大幅下降。这就是为什么训练大模型必须考虑精度策略。5.2 FP32、FP16、BF16 对比数据类型指数位尾数位表示范围主要场景FP328 位23 位较大主权重、损失缩放、数值稳定要求高的地方FP165 位10 位较小容易溢出混合精度训练中的前向/反向计算BF168 位7 位和 FP32 相同大模型训练中的主流低精度格式FP16 容易在反向传播时出现梯度下溢或上溢所以需要损失缩放Loss Scaling。BF16 虽然尾数少、精度不如 FP32但指数范围和 FP32 一样大因此训练过程更稳定成为大量大模型训练的选择。5.3 混合精度训练的原理混合精度训练的思路很简单主权重和优化器状态保留在 FP32前向和反向计算用 FP16/BF16损失缩放负责防止梯度溢出。PyTorch 提供了torch.cuda.amp.autocast和GradScaler调用方式如下scaler torch.cuda.amp.GradScaler() for step in range(max_iters): optimizer.zero_grad() with torch.autocast(device_typecuda, dtypetorch.float16): logits model(x) loss F.cross_entropy(logits.view(-1, logits.size(-1)), y.view(-1)) scaler.scale(loss).backward() scaler.step(optimizer) scaler.update()5.4 推理阶段的量化训练完成后推理阶段还会用到量化技术。INT8、INT4 量化可以把模型体积进一步压缩但会带来一定精度损失。不同模型和任务对量化的敏感度不同所以生产环境要先用验证集测试。学习阶段不用太早陷入量化细节但要知道训练用 FP16/BF16推理用 INT8/INT4 是一种很常见的工程组合。等你训练出一个模型再尝试量化会更容易理解为什么量化后有些任务会变差。6. 从零训练一个微型 GPT完整实战6.1 环境准备与项目结构本节我们用 Python PyTorch 从零训练一个字符级 GPT。所谓“字符级”就是直接把每个字符当成一个 Token。模型比较小普通 CPU 也能完成训练只是速度慢一些。有 NVIDIA GPU 的话训练会快很多。建议 Python 3.10 以上PyTorch 2.x。版本不需要完全一致关键是能够正常导入torch并运行张量运算。项目结构如下tinygpt/ ├── input.txt # 训练语料 ├── data.py # 数据加载与字符级分词 ├── model.py # GPT 模型定义 ├── train.py # 训练脚本 └── generate.py # 文本生成脚本6.2 准备训练语料你可以用任何英文文本作为语料比如《爱丽丝梦游仙境》的开头段落。把文本保存到input.txt。6.3 编写数据预处理字符级模型只需要建立“字符到整数”的映射。# 文件路径tinygpt/data.py def load_text(path): with open(path, r, encodingutf-8) as f: return f.read() def build_vocab(text): chars sorted(list(set(text))) stoi {ch: i for i, ch in enumerate(chars)} itos {i: ch for i, ch in enumerate(chars)} return stoi, itos def encode(text, stoi): return [stoi[ch] for ch in text] def decode(ids, itos): return .join([itos[i] for i in ids])set(text)会把文本中所有不重复字符收集起来构建成词表。字符级模型的优点是代码简单缺点是模型很难学到较长的语义作为入门实验非常合适。6.4 训练脚本训练脚本把数据切成固定长度的输入块x和标签块yy是x向右平移一位的结果这样每个位置都在预测下一个字符。# 文件路径tinygpt/train.py import torch import torch.nn as nn from torch.nn import functional as F from data import load_text, build_vocab, encode from model import TinyGPT batch_size 64 block_size 128 max_iters 5000 eval_interval 500 learning_rate 3e-4 n_embd 128 n_head 4 n_layer 4 dropout 0.0 text load_text(input.txt) stoi, itos build_vocab(text) data encode(text, stoi) n int(0.9 * len(data)) train_data data[:n] val_data data[n:] vocab_size len(stoi) model TinyGPT( vocab_sizevocab_size, n_embdn_embd, n_headn_head, n_layern_layer, block_sizeblock_size, dropoutdropout, ) optimizer torch.optim.AdamW(model.parameters(), lrlearning_rate) def get_batch(split): d train_data if split train else val_data ix torch.randint(len(d) - block_size, (batch_size,)) x torch.stack([torch.tensor(d[i:i block_size]) for i in ix]) y torch.stack([torch.tensor(d[i 1:i block_size 1]) for i in ix]) return x, y for step in range(max_iters): model.train() x, y get_batch(train) logits model(x) loss F.cross_entropy(logits.view(-1, logits.size(-1)), y.view(-1)) optimizer.zero_grad() loss.backward() optimizer.step() if step % eval_interval 0: model.eval() with torch.no_grad(): vx, vy get_batch(val) vlogits model(vx) vloss F.cross_entropy(vlogits.view(-1, vlogits.size(-1)), vy.view(-1)) print(fstep {step}: train loss {loss.item():.4f}, val loss {vloss.item():.4f}) torch.save(model.state_dict(), tinygpt.pt) print(训练完成模型已保存)运行命令python train.py预期会看到 loss 逐步下降。如果语料很小loss 在早期就能明显降低。6.5 编写文本生成脚本训练完成后写一个采样脚本让模型续写文本。# 文件路径tinygpt/generate.py import torch from data import load_text, build_vocab, encode, decode from model import TinyGPT text load_text(input.txt) stoi, itos build_vocab(text) vocab_size len(stoi) block_size 128 model TinyGPT( vocab_sizevocab_size, n_embd128, n_head4, n_layer4, block_sizeblock_size, ) model.load_state_dict(torch.load(tinygpt.pt)) model.eval() prompt input(请输入起始文本: ) x torch.tensor([encode(prompt, stoi)], dtypetorch.long) with torch.no_grad(): for _ in range(300): x_cond x[:, -block_size:] logits model(x_cond) probs torch.softmax(logits[:, -1, :], dim-1) next_id torch.multinomial(probs, num_samples1) x torch.cat([x, next_id], dim1) print(decode(x[0].tolist(), itos))运行命令python generate.py输入一个起始字符比如Alice模型会逐字生成后续内容。因为模型规模小、训练数据少生成文本大概率不通顺但你已经完整跑通了一个 LLM 的最小闭环。6.6 为什么推荐继续看 nanoGPT 和 llm.c跑通上面的教学版之后强烈建议去读 nanoGPT 的源码。你会发现 nanoGPT 在工程上更完善加入了学习率调度、梯度裁剪、权重衰减、评估环节、模型保存逻辑。如果还想再往下挖一层可以试试 llm.c。它是用纯 C 和 CUDA 写的不依赖 PyTorch。读它能让你理解 GPU 上真正的计算是如何发生的以及为什么混合精度、内存布局会影响训练速度。这个阶段不需要全部读懂但能极大拓展你对大模型底层实现的认知。7. 应用层进阶Agent、RAG、MCP 与编排框架7.1 训练模型之后还要学会使用模型会训练和会应用 LLM 是两种能力。很多公司招聘 LLM 工程师更多是在做应用落地把模型接入业务系统、为模型补充外部知识、让模型调用工具。所以学完训练下一步要转向应用层。7.2 LLM Agent 是什么LLM Agent 指的是让大模型作为“大脑”根据目标自主规划、调用外部工具、观察结果并迭代调整。一个典型流程是用户提出目标LLM 拆解成子任务LLM 调用搜索、代码执行器或数据库接口获取结果后继续推理直到完成目标。Agent 的核心难点不是模型本身而是如何设计工具调用格式、如何管理多轮上下文、如何处理模型犯错的恢复逻辑。7.3 RAG给模型补充知识RAGRetrieval-Augmented Generation检索增强生成是目前最常用的 LLM 应用模式。它不重新训练模型而是先从一个外部知识库中检索与问题相关的文档片段把片段拼进 prompt再让模型基于这些材料回答。RAG 的优势是知识更新成本低、可控性强、能缓解幻觉问题。学习 RAG 时你会接触到 Embedding 模型、向量数据库、文本切片和召回排序等内容。它和 Agent 经常组合使用Agent 负责调度RAG 负责提供领域知识。7.4 MCP工具连接的标准化MCPModel Context Protocol可以理解为 LLM 与外部工具之间的标准化通信协议。它解决了一个实际问题不同应用都要为模型对接不同的工具协议如果不统一每接入一个工具都要写一套适配代码。MCP 当前还在快速演进不同框架的 API 变化也比较快。学习时不必追求记住所有细节重点是理解它的消息结构模型发出工具调用请求MCP Server 执行工具并返回结构化结果。7.5 为什么需要编排框架如果你只调用一个模型接口完全不需要框架。一旦应用涉及多步推理、多工具调用、多数据源代码会变得混乱这时就需要编排框架比如 LangChain、LlamaIndexJava 生态里的 Spring AI 也属于这一类。框架的价值是替你管理上下文、工具注册、调用流程和错误处理。代价是引入了抽象层出问题时排查难度会增加。建议先用原生代码实现一个简单的 Agent 或 RAG 流程再对比框架的写法这样你能更清楚框架帮你省掉了什么。8. 常见问题与排查思路问题现象常见原因解决思路本地没有 GPU训练太慢模型太大或数据太多减小n_embd、n_layer、block_size使用云 GPU 服务CUDA 显存不足batch_size 或 block_size 过大减小 batch_size使用梯度累积loss 变成 NaN学习率过高、FP16 溢出降低学习率使用 BF16 或混合精度损失缩放loss 不下降数据预处理错误、模型结构错误先用少量数据过拟合测试生成文本全是重复字符模型欠拟合或采样温度太低增加训练步数提高采样温度微调时基础能力退化学习率太高或数据分布偏差使用较小学习率混合原始数据应用接入外部工具失败工具返回格式与预期不一致打印工具原始返回检查解析逻辑向量检索结果不相关文本切片不合理或 Embedding 不匹配优化切片大小换用更合适的 Embedding 模型排查时最重要的原则是“逐步缩小范围”。先确认数据是否正确再确认模型前向输出形状是否正确最后再检查训练过程。打印中间张量的 shape 是最快的定位方式。9. 最佳实践与工程建议9.1 把每个实验都变成可复现记录训练 LLM 时参数组合非常多。我建议从第一天开始就为每个实验记录数据集来源、Token 化方式、模型参数、超参数、损失曲线、最终生成样例。记录越详细后面调优越省力。LLM wiki 这种用对话方式管理笔记的形式很适合做这件事——你可以把实验记录交给 LLM训练它用检索回答你的问题。9.2 先用小模型验证再扩大规模在正式训练较大模型之前先在一个非常小的数据集上跑通甚至做到“过拟合”这能验证你的代码链路是否正确。提高训练速度后再逐步增加数据量和模型规模。9.3 关注数据质量而不是只关注模型结构同一个模型在不同质量的数据上表现可能差距巨大。很多入门者花费大量时间调模型结构却忽略了数据清洗、去重、格式统一。越早意识到数据的重要性后面成长越快。9.4 安全和合规不能忽视训练数据和模型权重都有版权和许可要求。开源模型的权重有各自的开源协议使用前要确认是否允许商用、是否允许微调、是否要求在衍生作品中保留声明。应用开发时还要注意注入攻击恶意用户可能通过 prompt 让模型执行非预期行为服务端必须校验输入和输出。9.5 代码规范要从学习期养成即便只是学习项目也建议按工程标准组织代码配置文件与代码分离、日志清晰、函数职责单一、训练和评估脚本拆分。等你进入团队或参与开源项目会发现这些习惯能帮助你更快融入。10. 总结如果我在 17 岁开始学 LLM我会按“写代码 - 补原理 - 换更大模型 - 做应用”的顺序前进。先跑通一个最小 GPT理解 Token、Embedding、注意力、训练循环然后尝试混合精度再去了解 RAG、Agent 和 MCP。这个路径的最大好处是每一个新知识点都有刚跑过的代码作为锚点不会飘在概念层面。学习过程中最需要警惕的是“收藏了就是学会了”的错觉。看一百篇 Transformer 讲解不如自己把注意力矩阵打印出来看一次收藏十个 GitHub 仓库不如真正运行其中任何一个。把本文看做一个起点打开终端创建一个tinygpt文件夹放一段文本然后开始训练你的第一个模型。