如果让我回到 17 岁,正在为“以后学什么”而迷茫,我大概率会选择从头构建一个大型语言模型(LLM)。这个领域当时看起来像数学、编程、算法交织而成的“高墙”,但真正走下来你会发现,它比想象中更值得学,也比想象中更有迹可循——不需要先读完整本《深度学习》,不需要拥有一张顶级显卡,也不需要一开始就能推导全部公式。
本文会用“如果我 17 岁重新开始”的视角,整理一份从零学习 LLM 的完整路径。内容包括:先学什么、原理如何拆解、精度问题(FP16、FP32、BF16)为什么重要、怎么用开源代码训练一个微型 GPT、以及走向 Agent、RAG、MCP 等应用层时应该补哪些知识。文章偏工程实践,适合刚接触 LLM 的读者,也适合想梳理自己学习体系的人。
1. 为什么要从零开始构建 LLM
1.1 LLM 是什么,为什么值得学
大型语言模型(Large Language Model,LLM)是一类以 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 值得长期跟进的资源
这一节只推荐个人觉得很适合自学的资源,不是广告,也没有固定优先级。
- nanoGPT:Andrej Karpathy 开源的极简 GPT 训练项目,代码量不大,适合精读和魔改。
- llm.c:Karpathy 用纯 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(), lr=1e-3) loss_fn = nn.CrossEntropyLoss() x = torch.randn(32, 10) # 模拟一个 batch,32 条样本 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 与 GPT
4.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, dropout=0.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, bias=False) 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, device=idx.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 对比
| 数据类型 | 指数位 | 尾数位 | 表示范围 | 主要场景 |
|---|---|---|---|---|
| FP32 | 8 位 | 23 位 | 较大 | 主权重、损失缩放、数值稳定要求高的地方 |
| FP16 | 5 位 | 10 位 | 较小,容易溢出 | 混合精度训练中的前向/反向计算 |
| BF16 | 8 位 | 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_type="cuda", dtype=torch.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", encoding="utf-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和标签块y,y是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_size=vocab_size, n_embd=n_embd, n_head=n_head, n_layer=n_layer, block_size=block_size, dropout=dropout, ) optimizer = torch.optim.AdamW(model.parameters(), lr=learning_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(f"step {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_size=vocab_size, n_embd=128, n_head=4, n_layer=4, block_size=block_size, ) model.load_state_dict(torch.load("tinygpt.pt")) model.eval() prompt = input("请输入起始文本: ") x = torch.tensor([encode(prompt, stoi)], dtype=torch.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_samples=1) x = torch.cat([x, next_id], dim=1) 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:给模型补充知识
RAG(Retrieval-Augmented Generation,检索增强生成)是目前最常用的 LLM 应用模式。它不重新训练模型,而是先从一个外部知识库中检索与问题相关的文档片段,把片段拼进 prompt,再让模型基于这些材料回答。
RAG 的优势是知识更新成本低、可控性强、能缓解幻觉问题。学习 RAG 时你会接触到 Embedding 模型、向量数据库、文本切片和召回排序等内容。它和 Agent 经常组合使用:Agent 负责调度,RAG 负责提供领域知识。
7.4 MCP:工具连接的标准化
MCP(Model Context Protocol)可以理解为 LLM 与外部工具之间的标准化通信协议。它解决了一个实际问题:不同应用都要为模型对接不同的工具,协议如果不统一,每接入一个工具都要写一套适配代码。
MCP 当前还在快速演进,不同框架的 API 变化也比较快。学习时不必追求记住所有细节,重点是理解它的消息结构:模型发出工具调用请求,MCP Server 执行工具并返回结构化结果。
7.5 为什么需要编排框架
如果你只调用一个模型接口,完全不需要框架。一旦应用涉及多步推理、多工具调用、多数据源,代码会变得混乱,这时就需要编排框架,比如 LangChain、LlamaIndex,Java 生态里的 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文件夹,放一段文本,然后开始训练你的第一个模型。