
简介《动手学大语言模型》Hands-On Large Language Models又被称为“袋鼠书”由 Jay Alammar 与 Maarten Grootendorst 合著是一本以高度视觉化方式讲解大型语言模型的实践指南适合正在入门或希望系统提升生成式 AI 能力的学生、研究者与工程技术人员。全书围绕模型的生成、表示与检索三大应用展开详细讲解 Transformer 架构、标记器、语义搜索、RAG、文本与视觉嵌入融合等关键主题并通过大量插图、时间线、经典论文引用和可运行代码实验室把抽象概念转化为可操作的工程方法。配套下载的压缩包共 1 个 PDF 文件大小约 21.37MBPDF 格式便于跨设备阅读、检索重点章节和做批注。该资源已有 899 人学习下载书中还包含多位业内专家的推荐语从算法演进、理论严谨性与工程实用性三个维度提供了完整参考既能辅助读者快速建立 LLM 知识框架也能作为日常开发与学习的案头手册。1. 袋鼠书不是来给你讲概念的它把大语言模型拆成了能上手改的模块书名里的 Hands-On 不是营销词。Jay Alammar 这本常被叫作“袋鼠书”的《动手学大语言模型》从头到尾都在逼你做同一件事把 Large Language Models 从“能聊天的黑匣子”拆成“能改代码的零件箱”。书里没有大段抄论文而是用大量示意图和可运行 notebook 带你从 token 切分一路走到微调与评估。适合两类人一类是已经调过 API、但不知道模型内部到底发生了什么的应用开发者另一类是准备做本地部署大语言模型、却被显存和训练成本卡住的研究生。你不需要数学博士背景但最好有 Python 基础。读完再回来看模型榜单你会明显感觉到自己看问题的层级变了——不再问“哪个模型强”而是问“这个模型在哪一层、用了什么策略变强”。2. 动手前先还清三笔认知债token、嵌入与注意力机制的工程视角袋鼠书前面的章节讲得很慢但慢得有道理。大语言模型的输入输出链路可以压缩成一句话文本 → 切分 → 索引 → 嵌入 → 多层注意力变换 → 概率输出。任何一步理解偏了后面调参都会变成玄学。2.1 分词粒度决定中文效果从 BPE 到 SentencePieceTokenizer 是模型真正“看到”的东西。英文按空格切词看着合理中文按字切又会把“机器学习”拆成“机 / 器 / 学 / 习”四个独立片段模型初期很难学到“机器学习”是一个稳定概念。常见做法是用字节级 BPE 或 Unigram 算法 SentencePiece 则是把语言无关的字节片段作为原子单位。from transformers import AutoTokenizer tok AutoTokenizer.from_pretrained(bert-base-chinese) text 机器学习是人工智能的核心 encoded tok(text, return_tensorspt) print(encoded[input_ids]) # 把 token id 还原成可见 token print(tok.convert_ids_to_tokens(encoded[input_ids][0]))这行代码能让你直观看到切分结果中文 BERT 多半会把“机器学习”切成[机, 器, 学, 习]四个 token而一个用中文语料训练过的 BPE 词表可能切出[机器学习, 是, 人工智能, 的, 核心]。后者对后续注意力计算更友好因为语义单元更完整。实际项目里换 tokenizer 会直接改变模型能处理的最大序列长度和训练收敛速度这不是一个能“顺手换掉”的组件。2.2 嵌入层与位置编码模型不是真的“读懂”文本嵌入层做的事情是把 token id 映射成稠密向量。这个向量初始是随机的经过预训练之后语义相近的词在向量空间里的距离会拉近。“国王 - 男性 女性 ≈ 女王”这种经典关系不是手工标出来的而是语言建模目标逼出来的副产品。但嵌入层解决不了词序问题。Transformer 结构本身没有循环对位置不敏感于是模型设计者加入了位置编码。这本书里对绝对位置编码和旋转位置编码的区别讲得比较清楚绝对位置打一个固定标签RoPE 则通过旋转矩阵把位置信息揉进注意力计算里这也是 LLaMA 等模型能外推更长上下文的关键设计。import torch import torch.nn.functional as F def rope(x, seq_len, dim, base10000): theta 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim)) pos torch.arange(seq_len).float() angles torch.outer(pos, theta) # [seq_len, dim/2] cos torch.cos(angles) sin torch.sin(angles) # 简化演示仅对偶数维度做旋转 return x, cos, sin需要理解的是RoPE 并不是什么高深数学它是在做向量旋转把两个 token 的注意力分数乘上它们位置差对应的旋转角让“距离越近”的 token 在位置维度上天然更相似。写代码时最常踩的坑是维度索引没配对导致 rotate 之后 cos 和 sin 形状对不上。调试这类问题时别去抠线性代数而是把每个中间矩阵的 shape 打印出来一步一步对。2.3 注意力机制是信息路由不是搜索功能很多人把注意力理解成“模型在文本里找关键词”这个直觉在单层时勉强成立但堆到几十层之后就完全失效了。注意力层做的事情是让每个 token 根据当前表示去“查询”其他 token再把有价值的信息加权汇总回来。它不是搜索更像是一个由数据学出来的路由系统——不同头负责不同关系有的头盯语法依赖有的头盯共指消解。书里建议的实践方式是拿一个预训练模型用transformers库把特定层的 attention weight 抽出来可视化。from transformers import AutoModel, AutoTokenizer model AutoModel.from_pretrained(bert-base-uncased, output_attentionsTrue) tok AutoTokenizer.from_pretrained(bert-base-uncased) inputs tok(The cat sat on the mat because it was tired, return_tensorspt) outputs model(**inputs) att outputs.attentions # 每一层都返回注意力矩阵 # att[layer] 的 shape: [batch, num_heads, seq_len, seq_len]参数说明output_attentionsTrue是这里的关键开关不打开的话注意力矩阵根本不会返回。看到[batch, num_heads, seq_len, seq_len]这个结构后你可以自己验证一个现象高层的注意力远比底层稀疏因为模型已经学会只路由关键信息。理解这一点后你再去看模型压缩、蒸馏、稀疏注意力这些改进方案就不会觉得它们是在“砍模型”而是在尊重注意力结构本身。3. 从零跑到第一次文本生成环境选型与最小复现路径原理看明白了就得动手。这一章解决的是“代码在我电脑上怎么跑起来”的问题顺便回应一个高频诉求本地部署大语言模型到底需要什么配置。3.1 显存不是唯一瓶颈理解算力约束下的资源搭配很多人以为跑大模型就是拼显存其实显存决定的是“能不能放下”内存和带宽决定的是“每秒能生成多少字”。CPU 推理不是不能做但词表投影层和注意力计算对内存带宽的需求非常高普通台式机跑 7B 模型生成速度可能只有每秒 2 到 4 个 token体验很痛苦。所以在动手之前先想清楚你的目标是验证代码逻辑、跑通训练流程还是真的要做本地服务。书里的代码大多数基于 PyTorch 和 HuggingFace Transformers对 GPU 的要求不算苛刻。一张 8GB 显存的卡就能跑通 7B 模型的推理和 LoRA 微调。真正吃显存的是优化器状态和梯度而不是模型权重本身。这也是我建议在读这本书时先跑小模型、再尝试大模型的原因——同样的代码逻辑1.5B 和 7B 之间差了可能 5 倍显存占用但你能学到的东西没有缩水。3.2 最小推理脚本从加载到文本生成先把环境装好。以 Python 3.10 为例创建虚拟环境后核心依赖只有两个库其余按书里 notebook 逐步补充即可python -m venv llm-book source llm-book/bin/activate pip install transformers torch --index-url https://download.pytorch.org/whl/cu121安装完成后用一个 1B 左右的小模型验证链路from transformers import AutoModelForCausalLM, AutoTokenizer model_name Qwen/Qwen2.5-1.5B-Instruct tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained( model_name, torch_dtypeauto, device_mapauto ) prompt 用一句话解释什么是注意力机制。 messages [{role: user, content: prompt}] inputs tokenizer.apply_chat_template( messages, add_generation_promptTrue, return_tensorspt ) outputs model.generate( inputs.to(model.device), max_new_tokens128, do_sampleTrue, temperature0.7, top_p0.9 ) print(tokenizer.decode(outputs[0], skip_special_tokensTrue))代码逻辑分三段tokenizer 负责把对话模板转成 token id 序列模型做前向推理最后 decode 把概率分布还原成文本。需要留意device_mapauto它能自动把权重均匀分配到多张卡或 CPU 与 GPU 之间单卡机器上不要乱改。do_sampleTrue开启随机采样配合temperature0.7让输出有一定多样性如果做的是确定性任务比如分类或者抽取建议关掉采样否则结果每次不一样。3.3 在 CPU 上也能跑量化推理的三种选择没有独显不代表不能动手。袋鼠书里提到的量化思路在本地部署大语言模型时是必须掌握的技能把 FP16 权重压缩成 INT8 或 INT4显存占用直接砍半甚至砍到四分之一。from transformers import BitsAndBytesConfig bnb_config BitsAndBytesConfig( load_in_4bitTrue, bnb_4bit_compute_dtypefloat16, bnb_4bit_quant_typenf4, bnb_4bit_use_double_quantTrue ) model AutoModelForCausalLM.from_pretrained( model_name, quantization_configbnb_config, device_mapauto )load_in_4bitTrue关键参数nf4是 4bit 量化里质量最好的数据格式use_double_quant会对量化常数再做一次量化能额外省几个百分点显存。代价是推理变慢一点但 7B 模型在 6GB 显存上跑起来对学习项目来说是值得的。4. 微调与对齐真正花钱的部分怎么省着做推理跑通只是开始。书中后半部分进入训练环节从文本生成模型到指令跟随模型中间隔着微调和偏好对齐。这一章是最容易劝退读者的地方也是含金量最高的部分。4.1 全量微调不是默认选项冻结权重和参数高效微调全量微调 7B 模型需要至少 60GB 显存这个量级已经超过了大多数个人开发者的硬件上限。所以 LoRA 类方法成为主流。LoRA 的思路非常简单冻结原始权重在每一层并行插入一个低秩矩阵训练时只更新这个小矩阵。from peft import LoraConfig, get_peft_model from transformers import AutoModelForCausalLM model AutoModelForCausalLM.from_pretrained(Qwen/Qwen2.5-1.5B-Instruct) lora_cfg LoraConfig( r8, lora_alpha16, lora_dropout0.05, biasnone, task_typeCAUSAL_LM ) model get_peft_model(model, lora_cfg) model.print_trainable_parameters() # 通常只有 0.5% 上下r8是低秩矩阵的秩lora_alpha16是缩放系数。这两个值之间有个经验配比alpha 一般是 r 的 1 到 2 倍过大的 alpha 会让微调后的输出偏离原模型分布。dropout0.05防止小数据量过拟合。这种配置下1.5B 参数模型的可训练参数可能不到 10M一张 8GB 显卡就能完成微调这正符合“算力约束下提升大语言模型能力的资源配置建模”的核心思路不是资源越堆越好而是把参数集中在最有效的低秩子空间里更新。4.2 评估要趁早避免只盯着 Loss 曲线很多人在训练时只关注 lossloss 降到 1.2 就觉得成了。可实际上语言模型的 loss 和下游真实任务质量之间不是单调关系。书里强调要自己做一个小规模评测集用精确匹配、ROUGE 或者人工抽测来验证。推荐的做法是训练过程中每隔固定步数做一次生成采样把 prompt 和模型输出打印到日志里。if step % 500 0: model.eval() with torch.no_grad(): sample tokenizer( 把这句话翻成英文模型正在学习。, return_tensorspt ).to(device) output model.generate(**sample, max_new_tokens64) print(fstep {step}: {tokenizer.decode(output[0])}) model.train()这段代码的作用是建立“主观质量”与训练步数的对应关系。你很快会发现某些步骤输出中文语病减少了但回答变得非常啰嗦再过几百步输出又开始退化。这是因为微调数据里存在分布噪音模型在某个局部区间过拟合了。提前建立采样机制能够在模型彻底跑偏前及时保存 checkpoint这就是训练流程里的后悔药。4.3 偏好对齐从 RLHF 到 DPO 的更轻路径书中谈到 RLHF 时点明了一个现实完整的 RLHF 需要训练奖励模型加上 PPO 四个模型交互显存和代码量都很大。容易上手的替代方案是 DPO——直接偏好优化。DPO 不需要单独训练奖励模型只需要准备包含“偏好回答”和“拒绝回答”的数据对就能用分类损失让模型学会选择更好的回答。from trl import DPOTrainer, DPOConfig dpo_config DPOConfig( output_dir./dpo_model, per_device_train_batch_size1, learning_rate5e-6, max_length512, max_prompt_length256, num_train_epochs3 ) dpo_trainer DPOTrainer( modelmodel, train_datasetdataset, tokenizertokenizer, argsdpo_config )注意learning_rate5e-6这个值比 SFT 低一个数量级。偏好对齐是在微调基础上的精调学习率过高很容易让模型忘记原本学到的基础能力。DPO 不生成偏好样本只负责学习偏好数据质量直接决定对齐结果。自己构造数据对的时候宁可每对只有一句话的差异也不要写成长篇大论——差异太大模型不知道该对齐哪一个维度。5. 避坑复现袋鼠书代码时最容易翻车的五个隐性错误边读边跑的最大障碍不是概念难懂而是代码能跑但结果不对或者昨天能跑今天报错。这一章的素材来自大量实际踩坑记录。5.1 现象加载模型时爆显存原因默认加载 FP32 权重7B 模型光权重就要 28GB加上激活值随手突破显存上限。解决加载时立刻指定torch_dtypetorch.float16或者直接用量化配置。如果你已经用了 FP16 还爆显存再看max_memory参数手动把部分层分配到 CPUfrom transformers import AutoModelForCausalLM model AutoModelForCausalLM.from_pretrained( model_name, torch_dtypeauto, device_mapauto, max_memory{0: 6GiB, cpu: 16GiB} )5.2 现象同一个 prompt 每次生成结果都不同原因do_sampleTrue时模型按概率采样输出天然有随机性。解决设置seed或加temperature0。如果你在写评测案例把do_sampleFalse改成贪心解码。我在写复现实验时固定了torch.manual_seed(42)加torch.backends.cudnn.deterministicTrue这样才能保证两次实验结果可比较。5.3 现象检查 tokenizer 分词时结果和书上截图不一样原因HuggingFace 的 tokenizer 文件更新过或者你加载的是不同 checkpoint 附带的不同词表。解决不要手动对比 token id对比 tokenizer 的vocab_size和词表 hash。书里的版本未必和你下载时一致这是正常现象重点看分词逻辑而不是具体 id 值。5.4 现象模型微调后 loss 一直在降但生成文本全是重复话原因学习率太高加上数据中包含大量重复模板模型陷入局部最优输出收敛到高频率 token 的循环。解决降低学习率到 2e-5 或 1e-5检查训练数据里重复度——如果同一句话出现超过 10 次先做去重或采样。from collections import Counter counter Counter(dataset[text]) dups [k for k, v in counter.items() if v 10] print(f重复样本数: {len(dups)})5.5 现象评估分数比论文报告低很多原因评估时没有使用和论文完全一致的 prompt 模板和生成参数。比如论文用max_new_tokens128你用了 256长输出中多余部分拉了平均分。解决先看论文或仓库里eval脚本的设置逐项对齐。把评估脚本的参数单独抽成配置项不要藏在代码里。评估脚本和训练脚本一样重要需要纳入版本管理。6. 最后一章把书读完后的第一个自测项目造一个能复述知识点的问答工具读完书不代表掌握动手设计一个小项目才算闭环。我推荐的自测路径是选一个你熟悉的垂直领域比如你正在用的开发框架准备 500 到 1000 条问答样本然后依次完成四件事数据清洗、LoRA 微调、推理效果测试、与基线模型做对比。这四个动作会强制你用上整本书的核心知识。验证方法有一个很实用的技巧拿一个完全没见过的问题去问微调前后的模型。base 使用PyTorch实现一个带dropout的两层全连接网络 print(微调前:, generate(base)) print(微调后:, generate_lora(base))对比输出时不要只看“是否提到 dropout”而要看“概念解释的顺序是否合理”“代码是否真的能跑”。如果微调后的输出能把 torch 的nn.Module复现得基本正确说明模型真的学到了领域结构而不是在背答案。我的习惯是给这个项目设置一个硬性交付物一个包含“训练日志、评测样本、配置文件”的文件夹提交到 Git 仓库。这样以后再看这本书随时可以回放那段时间的学习状态。大语言模型的学习路径很容易飘——一会儿想做智能体一会儿想做检索增强但读完一本动手型书籍后做出的落地作品才是抵御焦虑的锚点。别贪多先做一个只需要跑在一张消费级显卡上的小项目。希望帮到你。本文还有配套的精品资源点击获取