diff --git a/index.md b/index.md
index 928e35e..dda79ca 100644
--- a/index.md
+++ b/index.md
@@ -29,4 +29,11 @@ marp: true
6. [大语言模型解析 III: Attention Layer](lecture6.html)
7. [大语言模型解析 IV: Flash Attention](lecture7.html)
8. [大语言模型解析 V: MoE, LoRA](lecture8.html)
- 9. [大语言模型解析 VI: Fine-tuning](lecture9.html)
\ No newline at end of file
+ 9. [大语言模型解析 VI: Fine-tuning](lecture9.html)
+
+
+---
+
+课程目录
+ 10. [大语言模型解析 VII: LLM save/load](lecture10.html)
+ 11. [大语言模型解析 VII: LLM 推理, 解码策略, KVCache](lecture11.html)
\ No newline at end of file
diff --git a/lecture10.md b/lecture10.md
new file mode 100644
index 0000000..39cbc28
--- /dev/null
+++ b/lecture10.md
@@ -0,0 +1,429 @@
+---
+theme: gaia
+_class: lead
+paginate: true
+backgroundColor: #fff
+# backgroundImage: url('https://marp.app/assets/hero-background.svg')
+marp: true
+---
+
+
+
+
+
+![bg left:45% 80%](images/course.webp)
+
+# **LLM智能应用开发**
+
+第10讲: 大语言模型解析 VII
+
+基于HF LlaMA实现的讲解
+
+
+
+---
+
+# LLM结构的学习路径
+
+* LLM结构解析(开源LlaMA)
+* 自定义数据集构造
+* 自定义损失函数和模型训练/微调
+
+---
+
+# LLM封装和参数装载(load)
+
+* One basic PyTorch model
+* LLM base model
+* LoRA adapters
+
+---
+
+# One basic PyTorch model
+
+```python
+import torch
+
+class MyNetwork(torch.nn.Module):
+ def __init__(self):
+ super(MyNetwork, self).__init__()
+ self.conv1 = torch.nn.Conv2d(3, 6, 5)
+ self.pool = torch.nn.MaxPool2d(2, 2)
+ self.conv2 = torch.nn.Conv2d(6, 16, 5)
+ self.fc1 = torch.nn.Linear(16 * 5 * 5, 120)
+ def forward(self, x):
+ x = self.pool(torch.relu(self.conv1(x)))
+ x = self.pool(torch.relu(self.conv2(x)))
+ x = x.view(-1, 16 * 5 * 5)
+ x = torch.relu(self.fc1(x))
+ return x
+```
+
+---
+
+# 一个model实例的初始
+
+当一个model被创建
+```python
+model = MyNetwork()
+```
+* 伴随而“建”的有什么?
+* MyNetwork继承了```torch.nn.Module```
+ * 回想```init```函数做了些什么?
+ * 定义了每个基础模块
+ * 每个模块亦继承了```torch.nn.Module```
+ * 通常所说的参数存放在基础模块中
+
+
+---
+
+# nn.Linear: LLM的核心基本基础模块
+
+
+nn.Linear的[实现](https://pytorch.org/docs/stable/_modules/torch/nn/modules/linear.html#Linear)
+
+```python
+class Linear(Module):
+ __constants__ = ["in_features", "out_features"]
+ in_features: int
+ out_features: int
+ weight: Tensor
+```
+
+---
+
+# nn.Linear的init方法
+```python
+def __init__(
+ self, in_features: int, out_features: int, bias: bool = True, device=None, dtype=None,) -> None:
+ factory_kwargs = {"device": device, "dtype": dtype}
+ super().__init__()
+ self.in_features = in_features
+ self.out_features = out_features
+ self.weight = Parameter(
+ torch.empty((out_features, in_features), **factory_kwargs)
+ )
+ if bias:
+ self.bias = Parameter(torch.empty(out_features, **factory_kwargs))
+ else:
+ self.register_parameter("bias", None)
+ self.reset_parameters()
+```
+
+---
+
+# nn.Linear的reset_parameters方法
+
+```python
+def reset_parameters(self) -> None:
+ # Setting a=sqrt(5) in kaiming_uniform is the same as initializing with
+ # uniform(-1/sqrt(in_features), 1/sqrt(in_features)). For details, see
+ # https://github.com/pytorch/pytorch/issues/57109
+ init.kaiming_uniform_(self.weight, a=math.sqrt(5))
+ if self.bias is not None:
+ fan_in, _ = init._calculate_fan_in_and_fan_out(self.weight)
+ bound = 1 / math.sqrt(fan_in) if fan_in > 0 else 0
+ init.uniform_(self.bias, -bound, bound)
+```
+
+
+---
+
+# nn.Linear的forward方法
+
+```python
+def forward(self, input: Tensor) -> Tensor:
+ return F.linear(input, self.weight, self.bias)
+```
+
+* 其中```F```是```torch.nn.functional```
+ * ```from torch.nn import functional as F```
+
+
+
+---
+
+# nn.Linear中weight的定义和初始化
+weight定义
+```python
+self.weight = Parameter(
+ torch.empty((out_features, in_features), **factory_kwargs)
+)
+self.reset_parameters()
+```
+weight初始化,详见[torch.nn.init](https://pytorch.org/docs/stable/nn.init.html#torch.nn.init.kaiming_uniform_)
+```python
+init.kaiming_uniform_(self.weight, a=math.sqrt(5))
+```
+
+---
+
+# model如何存储和装载
+
+* model保存,核心为保存参数
+* PyTorch提供的保存方法
+ * ```torch.save```
+* model里都有什么, 可以用```print(model)```查看
+
+
+
+
+```python
+MyNetwork(
+ (conv1): Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))
+ (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
+ (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
+ (fc1): Linear(in_features=400, out_features=120, bias=True)
+)
+```
+
+
+
+---
+
+# model.state_dict()
+
+* model参数存储在内部的字典结构```model.state_dict()```中
+ * ```print(model.state_dict().keys())```
+
+
+
+
+```python
+odict_keys(['conv1.weight', 'conv1.bias', 'conv2.weight', 'conv2.bias', 'fc1.weight', 'fc1.bias'])
+```
+
+
+
+
+
+可通过```torch.save```存储模型至磁盘
+```python
+torch.save(model.sate_dict(), "model_weights.pt")
+```
+
+
+
+---
+
+# model加载
+
+* ```torch.save```存储的是一个模型的```state_dict```,那么加载的话
+ * 创建model
+ * 调用
+
+
+
+
+```python
+model.load_state_dict(torch.load('model_weights.pt', weights_only=True))
+```
+
+
+
+* 存储/装载state_dict针对模型参数,也可直接存储/装载模型结构+模型参数
+ * ```torch.save(model, 'model.pt')```
+ * ```model = torch.load('model.pt', weights_only=False)```
+
+
+---
+
+# 基于PyTorch的参数装载过程
+
+* torch.save
+* torch.load
+* torch.nn.Module.load_state_dict
+* torch.nn.Module.state_dict
+
+
+---
+
+
+# HuggingFace对model的封装
+
+* tensor的存储结构, [safetensors](https://github.com/huggingface/safetensors)
+ * Storing tensors safely (as opposed to pickle) and that is still fast (zero-copy).
+* ```from_pretrained```和```save_pretrained```
+
+
+
+
+```python
+import transformers
+model_id = '/Users/jingweixu/Downloads/Meta-Llama-3.1-8B-Instruct'
+llama = transformers.LlamaForCausalLM.from_pretrained(model_id)
+llama.save_pretrained('/Users/jingweixu/Downloads/llama_test', from_pt=True)
+```
+
+
+
+
+
+---
+
+# safetensors的其他存储/加载方式
+
+```python
+import torch
+from safetensors import safe_open
+from safetensors.torch import save_file
+
+tensors = {
+ "weight1": torch.zeros((1024, 1024)),
+ "weight2": torch.zeros((1024, 1024))
+}
+save_file(tensors, "model.safetensors")
+
+tensors = {}
+with safe_open("model.safetensors", framework="pt", device="cpu") as f:
+ for key in f.keys():
+ tensors[key] = f.get_tensor(key)
+```
+
+
+
+---
+
+# HuggingFace中的LoRA
+
+* PEFT库提供LoRA实现
+* LoRA是建立在一个已有的base model之上
+* LoRA中的参数是base model的参数的一部分
+ * 先加载base model
+ * 再加载/创建对应的LoRA adapters
+
+---
+
+# HF加载LoRA的过程
+
+```python
+import transformers
+
+model_id = '/Users/jingweixu/Downloads/Meta-Llama-3.1-8B-Instruct'
+llama = transformers.LlamaForCausalLM.from_pretrained(model_id)
+```
+
+
+
+
+```python
+from peft import get_peft_model, LoraConfig, TaskType
+
+peft_config = LoraConfig(task_type=TaskType.CAUSAL_LM,
+ inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1)
+
+peft_model = get_peft_model(llama, peft_config)
+
+```
+
+
+
+---
+
+# 原始的LlamaForCausalLM结构
+
+```python
+LlamaForCausalLM(
+ (model): LlamaModel(
+ (embed_tokens): Embedding(128256, 4096)
+ (layers): ModuleList(
+ (0-31): 32 x LlamaDecoderLayer(
+ (self_attn): LlamaSdpaAttention(
+ (q_proj): Linear(in_features=4096, out_features=4096, bias=False)
+ (k_proj): Linear(in_features=4096, out_features=1024, bias=False)
+ (v_proj): Linear(in_features=4096, out_features=1024, bias=False)
+ (o_proj): Linear(in_features=4096, out_features=4096, bias=False)
+ (rotary_emb): LlamaRotaryEmbedding()
+ )
+ (mlp): LlamaMLP(
+ (gate_proj): Linear(in_features=4096, out_features=14336, bias=False)
+ (up_proj): Linear(in_features=4096, out_features=14336, bias=False)
+ (down_proj): Linear(in_features=14336, out_features=4096, bias=False)
+ (act_fn): SiLU()
+ )
+ (input_layernorm): LlamaRMSNorm((4096,), eps=1e-05)
+ (post_attention_layernorm): LlamaRMSNorm((4096,), eps=1e-05)
+ )
+ )
+ (norm): LlamaRMSNorm((4096,), eps=1e-05)
+ (rotary_emb): LlamaRotaryEmbedding()
+ )
+ (lm_head): Linear(in_features=4096, out_features=128256, bias=False)
+)
+```
+
+
+---
+
+# PEFT的PeftModelForCausalLM结构
+
+```python
+PeftModelForCausalLM(
+ (base_model): LoraModel(
+ (model): LlamaForCausalLM(
+ (model): LlamaModel(
+ (embed_tokens): Embedding(128256, 4096)
+ (layers): ModuleList(
+ (0-31): 32 x LlamaDecoderLayer(
+ (self_attn): LlamaSdpaAttention(
+ (q_proj): lora.Linear(
+ (base_layer): Linear(in_features=4096, out_features=4096, bias=False)
+ (lora_dropout): ModuleDict(
+ (default): Dropout(p=0.1, inplace=False)
+ )
+ (lora_A): ModuleDict(
+ (default): Linear(in_features=4096, out_features=8, bias=False)
+ )
+ (lora_B): ModuleDict(
+ (default): Linear(in_features=8, out_features=4096, bias=False)
+ )
+```
+
+
+---
+
+# 读懂PEFT加载LoRA的过程
+
+* 入口: ```get_peft_model```方法
+ * ```peft_model.py```中的方法
+
+
+
+
+```python
+self.base_model = cls(model, {adapter_name: peft_config}, adapter_name)
+```
+```class BaseTuner(nn.Module, ABC):```中的```inject_adapter```方法和```_create_and_replace```方法(LoRA.model.py中实现)
+
+
+
+* 入口: ```peft_model.py```中的```PeftModel.from_pretrained```方法
+
+
+
+---
+
+
+移步代码
\ No newline at end of file
diff --git a/lecture11.md b/lecture11.md
new file mode 100644
index 0000000..4db7706
--- /dev/null
+++ b/lecture11.md
@@ -0,0 +1,339 @@
+---
+theme: gaia
+_class: lead
+paginate: true
+backgroundColor: #fff
+# backgroundImage: url('https://marp.app/assets/hero-background.svg')
+marp: true
+---
+
+
+
+
+
+![bg left:45% 80%](images/course.webp)
+
+# **LLM智能应用开发**
+
+第11讲: 大语言模型解析 VIII
+
+基于HF LlaMA实现的讲解
+
+
+
+---
+
+# LLM结构的学习路径
+
+* LLM结构解析(开源LlaMA)
+* 自定义数据集构造
+* 自定义损失函数和模型训练/微调
+* **让我们再次动起来:LLM推理过程**
+
+---
+
+# LLM推理过程二三事
+
+* LLM二阶段推理
+* KV-caching机制
+
+---
+
+# LLM的输入输出
+
+![w:1000 center](images/l11/pipeline.png)
+
+---
+
+# LLM推理过程中实际涉及的步骤
+
+![w:1000 center](images/l11/pipeline_with_tokens.png)
+
+* LLM的一次推理输出logits,并非token
+* 要得到token,还需通过Decoding strategy对logits进行解码
+
+---
+
+
+# LLM推理过程中实际涉及的步骤
+
+* LlaMAModel获得最后一层DecoderLayer的输出
+* LM_head获得logits
+* Decoding strategy解码logits得到token
+
+* 常用的Decoding strategy有:
+ * Greedy decoding
+ * Sampling
+ * Beam search
+
+---
+
+# LLM的解码(decoding)策略
+
+* 如果我们把logits(通过softmax转换为token的概率分布)作为输入,通常有如下解码策略:
+ * 贪婪解码(Greedy Decoding):每次直接选择概率最高的token,简单高效,但并非全局最优
+ * 采样(Sampling):按一定的采样策略选择一个单词,增加生成过程的多样性,但可能会导致生成的文本不连贯
+ * Beam Search:通过维护一个长度为k的候选序列集,每一步(单token推理)从每个候选序列的概率分布中选择概率最高的k个token,再考虑序列概率,保留最高的k个候选序列
+
+---
+
+# 采样策略
+
+* 一切从随机出发,叠加控制
+ * 随机采样
+ * Top-k采样
+ * Top-p采样(核采样,Nucleus sampling)
+ * Top-k+Top-p采样
+
+
+---
+
+# 采样策略: top-k采样
+
+输入:南京大学计算机学院的课程有
+概率分布: {算法:0.4, 操作系统:0.3, 计算机:0.2, 数据:0.05, ...}
+* top-k采样,每次从概率最高的k个单词中进行随机采样
+* 例如k=2,有可能生成的输出有
+ * 南京大学计算机学院的课程有算法
+ * 南京大学计算机学院的课程有操作系统
+* 贪婪解码本质是一种top-k采样(k=1)
+
+---
+
+# 采样策略: top-p采样
+
+* top-p采样,源自[The Curious Case of Neural Text Degeneration](https://arxiv.org/pdf/1904.09751)
+* 核心思路,重构采样集合
+ * 给定token分布$P(x\mid x_{x_{1:i-1}})$,top-p集合$V^{(p)}\subset V$,使得$P(x\mid x_{x_{1:i-1}}\geq p)$
+ * 和top-k很像,区别在于在什么位置对分布进行截断
+
+---
+
+# HF关于采样策略的实现
+
+* 参考:[top_k_top_p_filtering](https://github.com/huggingface/transformers/blob/c4d4e8bdbd25d9463d41de6398940329c89b7fb6/src/transformers/generation_utils.py#L903) (老版本)
+* 参考:
+ * src/transformers/generation/logits_process.py
+ * TopPLogitsWarper
+ * TopKLogitsWarper
+ * src/transformers/generation/utils.py
+ * _get_logits_processor
+ * 先topk,再topp
+
+---
+
+# LLM推理之两大阶段
+
+* 基于LLM自回归生成(autoregressive generation)的特点
+ * 逐token生成,生成的token依赖于前面的token
+ * 一次只能生成一个token,无法同时生成多个token
+* LLM生成过程分为两个阶段
+ * Prefill phase
+ * Decoding phase
+
+---
+
+# LLM推理第一阶段: Prefill
+
+输入token序列,输出下一个token
+
+![w:900 center](images/l11/prefill.jpg)
+
+---
+
+# LLM推理第二阶段: Decoding
+
+![w:700 center](images/l11/decoding1.jpg)
+![w:700 center](images/l11/decoding2.jpg)
+
+---
+
+# LLM推理第二阶段: Decoding
+
+![w:700 center](images/l11/decoding2.jpg)
+![w:700 center](images/l11/decoding4.jpg)
+
+
+---
+
+# LLM完成推理后,解码
+
+将生成的token序列解码成文本
+
+![w:700 center](images/l11/decodingAll.jpg)
+
+---
+
+# LLM二阶段推理解析
+
+* 将LLM当作函数,输入是token序列,输出是下一个token
+* LLM通过自回归(autoregressive generation)不断生成"下一个token"
+* 脑补下当LLM接收到输入的token序列后如何进行下一个token的推理
+
+
+
+![w:1000 center](images/l11/pipeline_with_tokens.png)
+
+
+
+---
+
+# LLM推理过程会产生一些中间变量
+
+第一个"下一个token"生成: 输入token序列"经过"(调用forward方法)N层Decoder layer后,的到结果
+细看其中一层Decoder layer,frward方法会返回若干中间输出,被称之为激活(activation)
+![w:700 center](images/l11/pipeline.png)
+
+
+---
+
+# Prefill phase
+
+* 第一个"下一个token"生成过程被称之为Prefill阶段
+* 为何特殊对待?
+ * 计算开销大
+* 简单推导一下一次LLM的推理过程的计算开销
+
+
+---
+
+# 计算开销
+
+* 符号约定
+ * b: batch size
+ * s: sequence length
+ * h: hidden size/dimension
+ * nh: number of heads
+ * hd: head dimension
+
+---
+
+# 计算开销
+
+
+* 给定矩阵$A\in R^{1\times n}$和矩阵$B\in R^{n\times 1}$,计算$AB$需要$n$次乘法操作和$n$次加法操作,总计算开销为$2n$ (FLOPs)
+ * FLOPs: floating point operations
+* 给定矩阵$A\in R^{m\times n}$和矩阵$B\in R^{n\times p}$,计算$AB$中的一个元素需要$n$次乘法操作和$n$次加法操作,一共有$mp$个元素,总计算开销为$2mnp$
+
+---
+
+# Self-attn模块
+
+* 第一步计算: $Q=xW_q$, $K=xW_k$, $V=xW_v$
+ * 输入x的shape: $(b,s,h)$,weight的shape: $(h,h)$
+ * Shape视角下的计算过程: $(b,s,h)(h,h)\rightarrow(b,s,h)$
+ * 如果在此进行多头拆分(reshape/view/einops),shape变为$(b,s,nh,hd)$,其中$h=bh*hd$
+ * 计算开销: $3\times 2bsh^2\rightarrow 6bsh^2$
+
+---
+
+# Self-attn模块
+
+* 第二步计算: $x_{\text{out}}=\text{softmax}(\frac{QK^T}{\sqrt{h}})VW_o+x$
+ * $QK^T$计算: $(b,nh,s,hd)(b,nh,hd,s)\rightarrow (b,nh,s,s)$
+ * 计算开销: $2bs^2h$
+ * $\text{softmax}(\frac{QK^T}{\sqrt{h}})V$计算: $(b,nh,s,s)(b,bh,s,hd)\rightarrow(b,nh,s,hd)$
+ * 计算开销: $2bs^2h$
+* 第三步$W_o$计算: $(b,s,h)(h,h)\rightarrow(b,s,h)$
+ * 计算开销: $2bsh^2$
+* Self-attn模块总计算开销: $8bsh^2+4bs^2h$
+---
+
+# MLP模块
+
+$$x=f_\text{activation}(x_{\text{out}}W_{\text{up}})W_{\text{down}}+x_{\text{out}}$$
+* 第一步计算,假设上采样到4倍
+ * Shape变化:$(b,s,h)(h,4h)\rightarrow(b,s,4h)$
+ * 计算开销: $8bsh^2$
+* 第二步计算,假设下采样回1倍
+ * Shape变化:$(b,s,4h)(4h,h)\rightarrow(b,s,h)$
+ * 计算开销: $8bsh^2$
+* MLP模块总计算开销: $16bsh^2$
+
+
+---
+
+# Decoder layer模块计算开销
+
+* Self-attn模块计算开销: $8bsh^2+4bs^2h$
+* MLP模块计算开销: $16bsh^2$
+* Decoder layer模块计算开销: $24bsh^2+4bs^2h$
+
+* 以上为一次推理的计算开销,开销为sequence的平方级别
+
+---
+
+# Decoding phase
+
+* 当第一个"下一个token"生成完毕后,LLM开始"自回归推理"生成
+* 第二个"下一个token"
+ * 输入x的shape: $(b,s+1,h)$,继续以上推理过程
+* 第三个"下一个token"
+ * 输入x的shape: $(b,s+2,h)$,继续以上推理过程
+* 第n个"下一个token"
+ * 输入x的shape: $(b,s+n-1,h)$,继续以上推理过程
+* 自回归推理过程的计算开销
+* 每次自回归推理过程,都需要平方级别的开销?
+ * 且包含了计算开销和内存开销
+
+
+---
+
+# 回顾Self-attn中$QK^T$的计算过程
+
+* 第一个"下一个token"
+ * $QK^T$计算: $(b,nh,s,hd)(b,nh,hd,s)\rightarrow (b,nh,s,s)$
+* 第二个"下一个token"
+ * $QK^T$计算: $(b,nh,s+1,hd)(b,nh,hd,s+1)\rightarrow (b,nh,s+1,s+1)$
+* 考虑自回归特性,$(s,s)$和$(s+1,s+1)$为下三角阵
+ * $(s+1,s+1)$的前$s$行就是$(s,s)$
+* 考虑复用$(s,s)$?
+
+---
+
+# LLM自回归过程中的复用
+
+* 要复用什么,还得从需求出发
+* 需求: 生成"下一个token"
+* Decoder layers之后的lm_head计算
+ * shape视角: $(b,s,h)(h,V)\rightarrow (b,s,V)$
+* 生成第二个"下一个token"
+ * shape视角: $(b,s+1,h)(h,V)\rightarrow (b,s+1,V)$
+ * 第二个"下一个token"的logits在$(b,s+1,V)$中第二个维度index $s+1$处,该logits只受$(b,s+1,h)$中第二个维度index $s+1$处的值影响
+
+---
+
+# LLM自回归过程中的复用
+
+* 真正要复用的是用于计算$(b,s+1,h)$中第二维度index $s+1$的数值
+ * shape的视角: $(b,s+1,h)\rightarrow (b,1,V)$
+* 整个self-attn计算过程中,只有$QK^T$中的$K$和$\text{softmax}(\frac{QK^T}{\sqrt(h)})V$中的$V$需要复用
+ * 为K和V构建缓存: 即KVCache
+
+---
+
+# Self-attn例
+
+![w:500 center](images/l11/attn_example.jpg)
diff --git a/lecture12.md b/lecture12.md
new file mode 100644
index 0000000..4db7706
--- /dev/null
+++ b/lecture12.md
@@ -0,0 +1,339 @@
+---
+theme: gaia
+_class: lead
+paginate: true
+backgroundColor: #fff
+# backgroundImage: url('https://marp.app/assets/hero-background.svg')
+marp: true
+---
+
+
+
+
+
+![bg left:45% 80%](images/course.webp)
+
+# **LLM智能应用开发**
+
+第11讲: 大语言模型解析 VIII
+
+基于HF LlaMA实现的讲解
+
+
+
+---
+
+# LLM结构的学习路径
+
+* LLM结构解析(开源LlaMA)
+* 自定义数据集构造
+* 自定义损失函数和模型训练/微调
+* **让我们再次动起来:LLM推理过程**
+
+---
+
+# LLM推理过程二三事
+
+* LLM二阶段推理
+* KV-caching机制
+
+---
+
+# LLM的输入输出
+
+![w:1000 center](images/l11/pipeline.png)
+
+---
+
+# LLM推理过程中实际涉及的步骤
+
+![w:1000 center](images/l11/pipeline_with_tokens.png)
+
+* LLM的一次推理输出logits,并非token
+* 要得到token,还需通过Decoding strategy对logits进行解码
+
+---
+
+
+# LLM推理过程中实际涉及的步骤
+
+* LlaMAModel获得最后一层DecoderLayer的输出
+* LM_head获得logits
+* Decoding strategy解码logits得到token
+
+* 常用的Decoding strategy有:
+ * Greedy decoding
+ * Sampling
+ * Beam search
+
+---
+
+# LLM的解码(decoding)策略
+
+* 如果我们把logits(通过softmax转换为token的概率分布)作为输入,通常有如下解码策略:
+ * 贪婪解码(Greedy Decoding):每次直接选择概率最高的token,简单高效,但并非全局最优
+ * 采样(Sampling):按一定的采样策略选择一个单词,增加生成过程的多样性,但可能会导致生成的文本不连贯
+ * Beam Search:通过维护一个长度为k的候选序列集,每一步(单token推理)从每个候选序列的概率分布中选择概率最高的k个token,再考虑序列概率,保留最高的k个候选序列
+
+---
+
+# 采样策略
+
+* 一切从随机出发,叠加控制
+ * 随机采样
+ * Top-k采样
+ * Top-p采样(核采样,Nucleus sampling)
+ * Top-k+Top-p采样
+
+
+---
+
+# 采样策略: top-k采样
+
+输入:南京大学计算机学院的课程有
+概率分布: {算法:0.4, 操作系统:0.3, 计算机:0.2, 数据:0.05, ...}
+* top-k采样,每次从概率最高的k个单词中进行随机采样
+* 例如k=2,有可能生成的输出有
+ * 南京大学计算机学院的课程有算法
+ * 南京大学计算机学院的课程有操作系统
+* 贪婪解码本质是一种top-k采样(k=1)
+
+---
+
+# 采样策略: top-p采样
+
+* top-p采样,源自[The Curious Case of Neural Text Degeneration](https://arxiv.org/pdf/1904.09751)
+* 核心思路,重构采样集合
+ * 给定token分布$P(x\mid x_{x_{1:i-1}})$,top-p集合$V^{(p)}\subset V$,使得$P(x\mid x_{x_{1:i-1}}\geq p)$
+ * 和top-k很像,区别在于在什么位置对分布进行截断
+
+---
+
+# HF关于采样策略的实现
+
+* 参考:[top_k_top_p_filtering](https://github.com/huggingface/transformers/blob/c4d4e8bdbd25d9463d41de6398940329c89b7fb6/src/transformers/generation_utils.py#L903) (老版本)
+* 参考:
+ * src/transformers/generation/logits_process.py
+ * TopPLogitsWarper
+ * TopKLogitsWarper
+ * src/transformers/generation/utils.py
+ * _get_logits_processor
+ * 先topk,再topp
+
+---
+
+# LLM推理之两大阶段
+
+* 基于LLM自回归生成(autoregressive generation)的特点
+ * 逐token生成,生成的token依赖于前面的token
+ * 一次只能生成一个token,无法同时生成多个token
+* LLM生成过程分为两个阶段
+ * Prefill phase
+ * Decoding phase
+
+---
+
+# LLM推理第一阶段: Prefill
+
+输入token序列,输出下一个token
+
+![w:900 center](images/l11/prefill.jpg)
+
+---
+
+# LLM推理第二阶段: Decoding
+
+![w:700 center](images/l11/decoding1.jpg)
+![w:700 center](images/l11/decoding2.jpg)
+
+---
+
+# LLM推理第二阶段: Decoding
+
+![w:700 center](images/l11/decoding2.jpg)
+![w:700 center](images/l11/decoding4.jpg)
+
+
+---
+
+# LLM完成推理后,解码
+
+将生成的token序列解码成文本
+
+![w:700 center](images/l11/decodingAll.jpg)
+
+---
+
+# LLM二阶段推理解析
+
+* 将LLM当作函数,输入是token序列,输出是下一个token
+* LLM通过自回归(autoregressive generation)不断生成"下一个token"
+* 脑补下当LLM接收到输入的token序列后如何进行下一个token的推理
+
+
+
+![w:1000 center](images/l11/pipeline_with_tokens.png)
+
+
+
+---
+
+# LLM推理过程会产生一些中间变量
+
+第一个"下一个token"生成: 输入token序列"经过"(调用forward方法)N层Decoder layer后,的到结果
+细看其中一层Decoder layer,frward方法会返回若干中间输出,被称之为激活(activation)
+![w:700 center](images/l11/pipeline.png)
+
+
+---
+
+# Prefill phase
+
+* 第一个"下一个token"生成过程被称之为Prefill阶段
+* 为何特殊对待?
+ * 计算开销大
+* 简单推导一下一次LLM的推理过程的计算开销
+
+
+---
+
+# 计算开销
+
+* 符号约定
+ * b: batch size
+ * s: sequence length
+ * h: hidden size/dimension
+ * nh: number of heads
+ * hd: head dimension
+
+---
+
+# 计算开销
+
+
+* 给定矩阵$A\in R^{1\times n}$和矩阵$B\in R^{n\times 1}$,计算$AB$需要$n$次乘法操作和$n$次加法操作,总计算开销为$2n$ (FLOPs)
+ * FLOPs: floating point operations
+* 给定矩阵$A\in R^{m\times n}$和矩阵$B\in R^{n\times p}$,计算$AB$中的一个元素需要$n$次乘法操作和$n$次加法操作,一共有$mp$个元素,总计算开销为$2mnp$
+
+---
+
+# Self-attn模块
+
+* 第一步计算: $Q=xW_q$, $K=xW_k$, $V=xW_v$
+ * 输入x的shape: $(b,s,h)$,weight的shape: $(h,h)$
+ * Shape视角下的计算过程: $(b,s,h)(h,h)\rightarrow(b,s,h)$
+ * 如果在此进行多头拆分(reshape/view/einops),shape变为$(b,s,nh,hd)$,其中$h=bh*hd$
+ * 计算开销: $3\times 2bsh^2\rightarrow 6bsh^2$
+
+---
+
+# Self-attn模块
+
+* 第二步计算: $x_{\text{out}}=\text{softmax}(\frac{QK^T}{\sqrt{h}})VW_o+x$
+ * $QK^T$计算: $(b,nh,s,hd)(b,nh,hd,s)\rightarrow (b,nh,s,s)$
+ * 计算开销: $2bs^2h$
+ * $\text{softmax}(\frac{QK^T}{\sqrt{h}})V$计算: $(b,nh,s,s)(b,bh,s,hd)\rightarrow(b,nh,s,hd)$
+ * 计算开销: $2bs^2h$
+* 第三步$W_o$计算: $(b,s,h)(h,h)\rightarrow(b,s,h)$
+ * 计算开销: $2bsh^2$
+* Self-attn模块总计算开销: $8bsh^2+4bs^2h$
+---
+
+# MLP模块
+
+$$x=f_\text{activation}(x_{\text{out}}W_{\text{up}})W_{\text{down}}+x_{\text{out}}$$
+* 第一步计算,假设上采样到4倍
+ * Shape变化:$(b,s,h)(h,4h)\rightarrow(b,s,4h)$
+ * 计算开销: $8bsh^2$
+* 第二步计算,假设下采样回1倍
+ * Shape变化:$(b,s,4h)(4h,h)\rightarrow(b,s,h)$
+ * 计算开销: $8bsh^2$
+* MLP模块总计算开销: $16bsh^2$
+
+
+---
+
+# Decoder layer模块计算开销
+
+* Self-attn模块计算开销: $8bsh^2+4bs^2h$
+* MLP模块计算开销: $16bsh^2$
+* Decoder layer模块计算开销: $24bsh^2+4bs^2h$
+
+* 以上为一次推理的计算开销,开销为sequence的平方级别
+
+---
+
+# Decoding phase
+
+* 当第一个"下一个token"生成完毕后,LLM开始"自回归推理"生成
+* 第二个"下一个token"
+ * 输入x的shape: $(b,s+1,h)$,继续以上推理过程
+* 第三个"下一个token"
+ * 输入x的shape: $(b,s+2,h)$,继续以上推理过程
+* 第n个"下一个token"
+ * 输入x的shape: $(b,s+n-1,h)$,继续以上推理过程
+* 自回归推理过程的计算开销
+* 每次自回归推理过程,都需要平方级别的开销?
+ * 且包含了计算开销和内存开销
+
+
+---
+
+# 回顾Self-attn中$QK^T$的计算过程
+
+* 第一个"下一个token"
+ * $QK^T$计算: $(b,nh,s,hd)(b,nh,hd,s)\rightarrow (b,nh,s,s)$
+* 第二个"下一个token"
+ * $QK^T$计算: $(b,nh,s+1,hd)(b,nh,hd,s+1)\rightarrow (b,nh,s+1,s+1)$
+* 考虑自回归特性,$(s,s)$和$(s+1,s+1)$为下三角阵
+ * $(s+1,s+1)$的前$s$行就是$(s,s)$
+* 考虑复用$(s,s)$?
+
+---
+
+# LLM自回归过程中的复用
+
+* 要复用什么,还得从需求出发
+* 需求: 生成"下一个token"
+* Decoder layers之后的lm_head计算
+ * shape视角: $(b,s,h)(h,V)\rightarrow (b,s,V)$
+* 生成第二个"下一个token"
+ * shape视角: $(b,s+1,h)(h,V)\rightarrow (b,s+1,V)$
+ * 第二个"下一个token"的logits在$(b,s+1,V)$中第二个维度index $s+1$处,该logits只受$(b,s+1,h)$中第二个维度index $s+1$处的值影响
+
+---
+
+# LLM自回归过程中的复用
+
+* 真正要复用的是用于计算$(b,s+1,h)$中第二维度index $s+1$的数值
+ * shape的视角: $(b,s+1,h)\rightarrow (b,1,V)$
+* 整个self-attn计算过程中,只有$QK^T$中的$K$和$\text{softmax}(\frac{QK^T}{\sqrt(h)})V$中的$V$需要复用
+ * 为K和V构建缓存: 即KVCache
+
+---
+
+# Self-attn例
+
+![w:500 center](images/l11/attn_example.jpg)
diff --git a/lecture8.md b/lecture8.md
index a60eb24..68c9d9e 100644
--- a/lecture8.md
+++ b/lecture8.md
@@ -37,7 +37,7 @@ a[href='red'] {
# **LLM智能应用开发**
-第7讲: 大语言模型解析 V
+第8讲: 大语言模型解析 V
基于HF LlaMA实现的讲解
diff --git a/lecture9.md b/lecture9.md
index 042535d..4151728 100644
--- a/lecture9.md
+++ b/lecture9.md
@@ -37,7 +37,7 @@ a[href='red'] {
# **LLM智能应用开发**
-第7讲: 大语言模型解析 VI
+第9讲: 大语言模型解析 VI
基于HF LlaMA实现的讲解
@@ -111,7 +111,7 @@ encoded_input = tokenizer("Tell me a story about Nanjing University.")
# 字典结构
-* 基本元素:input_ids 和 attention_mask
+基本元素:input_ids 和 attention_mask
```python
encoded_input = tokenizer("Tell me a story about Nanjing University.")
```
@@ -214,7 +214,8 @@ encoded_input = tokenizer(batch_sentences, padding=True)
* 指定长度进行padding
-*
+
+
```python
encoded_input = tokenizer(batch_sentences, padding="max_length", max_length=20, truncation=True)
@@ -275,6 +276,7 @@ def tokenize_function(dataset):
```
+
* 调用预处理方法