惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
Y
Y Combinator Blog
博客园 - 聂微东
GbyAI
GbyAI
H
Hackread – Cybersecurity News, Data Breaches, AI and More
月光博客
月光博客
T
The Blog of Author Tim Ferriss
爱范儿
爱范儿
宝玉的分享
宝玉的分享
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
雷峰网
雷峰网
MyScale Blog
MyScale Blog
Cloudbric
Cloudbric
T
Threatpost
Scott Helme
Scott Helme
N
News | PayPal Newsroom
Google DeepMind News
Google DeepMind News
Martin Fowler
Martin Fowler
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Engineering at Meta
Engineering at Meta
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
V2EX - 技术
V2EX - 技术
罗磊的独立博客
Attack and Defense Labs
Attack and Defense Labs
IT之家
IT之家
S
Secure Thoughts
C
Cyber Attacks, Cyber Crime and Cyber Security
D
Docker
V
V2EX
T
Troy Hunt's Blog
Forbes - Security
Forbes - Security
aimingoo的专栏
aimingoo的专栏
J
Java Code Geeks
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes
T
Tor Project blog
P
Proofpoint News Feed
Recorded Future
Recorded Future
B
Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Simon Willison's Weblog
Simon Willison's Weblog
A
About on SuperTechFans
The GitHub Blog
The GitHub Blog
MongoDB | Blog
MongoDB | Blog
O
OpenAI News
V
Vulnerabilities – Threatpost
P
Privacy International News Feed
云风的 BLOG
云风的 BLOG

StudyingLover's Blog

Diffusion Policy笔记 rwkv笔记 act笔记 nanovllm-block_manager opencode多智能体 nanobot-pre-train nanobot-rl nanobot-sft nanobot-checkpoint_manager nanobot-mid-train Vision Mamba (Vim)笔记 BPE演示 最后一遍学习Transformer YOLOv5 目标检测笔记 下载根服务器解析记录 Dynaseal A Backend-Controlled LLM API Key Distribution Scheme with Constrained Invocation Parameters 判断链表有环 王道25数据结构勘误 关于perplexity的open-sourcing-r1-1776 AI为什么不像人类一样进行多轮对话 新博客改造日记和功能测试 linuxqq只显示登陆背景图 数字设计和计算机体系结构(机械工业出版社)勘误(自制) Dynaseal:面向未来端侧llm agent的llm api key分发机制 A Definitive Guide to Markdown Style This post is using MDX, Where you can embed JSX and Astro components RT-Patch学习 pydantic实现的LLM ReAct fastapi 和 uvicorn 设置监听 ipv6 pydantic+openai+json 控制大模型输出的最佳范式 解决 Matplotlib Scatter 不支持 Marker 列表的问题:mscatter 实现 roofline model zhipuAI接口兼容openai 在docker部署fastapi宝塔里使用nginx反代套上cloudflare获取请求的真实ip clion搭建libbpf-bootstrap开发环境 coze+coze-discord-proxy+ChatNextWebUI实现AI自由 安卓内核时间使用的是UTC时间 colab运行google最新开源模型Gemma Sora技术报告 视频生成模型作为世界模拟器 笔记 archlinux flutter开发踩坑 fastapi集成google auth登录 linux下NTFS磁盘报错输入输出错误 Venn-Abers 预测器 基于Venn-Abers预测器的系统日志异常检测方法_顾兆军 手机平板远程访问kvm虚拟机的windows phi-2弱智吧测评 poe的gemini pro或是百度开发 google gemini api使用 google gemini api申请 构建用于复杂数据处理的高效UDP服务器和客户端 matplotlib中文字体渲染 TruFor笔记和代码复现 深入分析:GitHub Trending 项目 "multipleWindow3dScene" pua大模型 ggml教程|mnist手写体识别量化推理 xgboost2.0最佳实践 xgboost使用GPU最佳实践 马踏棋盘 cloudlflare推理llama2 docker搭建elasticsearch并使用python连接 FreeU-文字生成图片的免费午餐笔记 使用xgboost的c接口推理模型 Archlinux使用CMake调用xgboost的c接口 m2cgen生成机器学习c语言推理代码 xgboost模型序列化存储并推理 speculative-sampling笔记 prompt2model笔记 RoboTAP笔记 自建obsidian同步服务 MediaPipe即将推出图像生成服务 Dual-Stream Diffusion Net for Text-to-Video Generation笔记 ViT在DDPM取代UNet(DiT) arch4edu搞崩了我的flutter LISA(推理分割)笔记 在终端绘制GPU显存使用曲线 GPTBot介绍 arch蓝牙无法连接 GPU部署llama-cpp-python(llama.cpp通用) 花式求GCD 使用llama构建一个蜜罐(前端) 使用llama构建一个蜜罐(后端) llama-cpp-python快速上手 快速上手llama2.c(更新版) Paper Gestalt笔记 DINO-v2笔记 快速上手llama2.c AnyDoor笔记 Archlinux安装scrcpy加载共享库出错 error while loading shared libraries:libusb-1.0.so.0:wrong ELF class:ELFCLASS32 npc_gzip笔记 python调用c++函数 Filesystem type ntfs3,ntfs not configured in kernel open_clip编码图像和文本 PicGo配置CloudflareR2图片储存 ArchlinuxGnome快捷键打开终端 clip-interrogator代码解析 GroundingDINO安装报错解决 2023华为鲲鹏畅想日暨西安高新国际会议中心零食午饭测评 RoboMaster开源仓库汇总(长期更新) 没有手都可以在腾讯云创建镜像 I3D笔记
nanobot-gpt
About the Author StudyingLover · 2026-01-18 · via StudyingLover's Blog

这个文件定义了gpt模型,以及对应的generate方法

配置

首先是各项配置

@dataclass
class GPTConfig:
    sequence_len: int = 2048  # 最大序列长度
    vocab_size: int = 32768  # 词汇表大小
    n_layer: int = 12  # Transformer 的层数
    n_head: int = 6  # 查询头的数量
    n_kv_head: int = 6  # 键/值头的数量 (GQA)
    n_embd: int = 768  # 模型内部的隐藏维度
    window_pattern: str = "SSSL"

这里需要说下滑动窗口注意力模式,这是一个性能与质量的平衡方案。

  • 前面层用短窗口 → 推理速度快,计算量少
  • 每 3 层一个长窗口 → 定期收集完整上下文信息
  • 最后一层必须 L → 确保输出层有完整的语义理解
层数字符窗口大小含义
0S1024只看前 1024 个词元
1S1024只看前 1024 个词元
2S1024只看前 1024 个词元
3L2048看所有词元(完整上下文)
4S1024只看前 1024 个词元(模式重复)
5S1024只看前 1024 个词元
6S1024只看前 1024 个词元
7L2048看所有词元
8S1024只看前 1024 个词元
9S1024只看前 1024 个词元
10S1024只看前 1024 个词元
11L2048看所有词元(最后一层强制L)

方法

norm

这是 RMSNorm(Root Mean Square Normalization),一种更简单的归一化方法。

def norm(x):
    # Purely functional rmsnorm with no learnable params
    return F.rms_norm(x, (x.size(-1),))

传统的LayerNorm是这样的

# 有可学习参数
class LayerNorm(nn.Module):
    def __init__(self, dim):
        super().__init__()
        self.weight = nn.Parameter(torch.ones(dim))   # 可学习的缩放
        self.bias = nn.Parameter(torch.zeros(dim))     # 可学习的偏移

    def forward(self, x):
        mean = x.mean(-1, keepdim=True)
        std = x.std(-1, keepdim=True)
        return self.weight * (x - mean) / std + self.bias
特性LayerNormRMSNorm(本代码)
减均值✅ 有❌ 无
可学习参数✅ weight & bias❌ 无参数
计算量较多较少
效果相当或更好

has_ve

给某些层额外加强,但要聪明地加——只加奇数层(或偶数层),这样既能提升模型,又不会太浪费参数。而且不管有多少层,最后一层肯定要加强!

def has_ve(layer_idx, n_layer):
    """Returns True if GPT layer should have Value Embedding (alternating, last layer always included)."""
    return layer_idx % 2 == (n_layer - 1) % 2

apply_rotary_emb

rope编码, 旋转变换,x的shape不变,然后返回。以前不看代码一直以为是concat的

def apply_rotary_emb(x, cos, sin):
    assert x.ndim == 4  # multihead attention
    d = x.shape[3] // 2
    x1, x2 = x[..., :d], x[..., d:]  # split up last dim into two halves
    y1 = x1 * cos + x2 * sin  # rotate pairs of dims
    y2 = x1 * (-sin) + x2 * cos
    return torch.cat([y1, y2], 3)

CausalSelfAttention

可以认为,这个类就是transformer的注意力层,多个head包含在里面

QKV三个矩阵和输出投影矩阵就是四个nn.Linear

self.c_q = nn.Linear(self.n_embd, self.n_head * self.head_dim, bias=False)
        self.c_k = nn.Linear(self.n_embd, self.n_kv_head * self.head_dim, bias=False)
        self.c_v = nn.Linear(self.n_embd, self.n_kv_head * self.head_dim, bias=False)
        self.c_proj = nn.Linear(self.n_embd, self.n_embd, bias=False)

formard

输入x,通过QKV做投影,然后拆成多个头,后面会送进Flash Attention做运算

注意:拆成多个头计算不是显性的,nanochat未实现具体的计算,会通过fash attention的参数隐式完成

然后做旋转变换把位置信息揉进来

cos, sin = cos_sin
        q, k = apply_rotary_emb(q, cos, sin), apply_rotary_emb(k, cos, sin)
        q, k = norm(q), norm(k)  # QK norm
  • Q 和 K:参与点积计算相似度,需要控制数值范围
  • V:只是被加权求和,不影响注意力分数的数值稳定性

最后调用一下flash attention,这里算的就是Concat(head_1, …, head_n)

if kv_cache is None:
            # Training: causal attention with optional sliding window
            y = flash_attn.flash_attn_func(
                q, k, v, causal=True, window_size=window_size
            )
        else:
            # Inference: use flash_attn_with_kvcache which handles cache management
            k_cache, v_cache = kv_cache.get_layer_cache(self.layer_idx)
            y = flash_attn.flash_attn_with_kvcache(
                q,
                k_cache,
                v_cache,
                k=k,
                v=v,
                cache_seqlens=kv_cache.cache_seqlens,
                causal=True,
                window_size=window_size,
            )
            # Advance position after last layer processes
            if self.layer_idx == kv_cache.n_layers - 1:
                kv_cache.advance(T)

最后通过线性层映射回原始的嵌入维度

y = y.contiguous().view(B, T, -1)
y = self.c_proj(y)
return y

y.contiguous()是把内存变成连续的(fa返回的可能是非连续的),view需要连续内存

MLP

相当于encoder或者decoder的一个堆叠块

class Block(nn.Module):
    def __init__(self, config: GPTConfig, layer_idx: int):
        super().__init__()
        self.attn = CausalSelfAttention(config, layer_idx)
        self.mlp = MLP(config)

    def forward(self, x, ve, cos_sin, window_size, kv_cache):
        x = x + self.attn(norm(x), ve, cos_sin, window_size, kv_cache)
        x = x + self.mlp(norm(x))
        return x

GPT

元设备(Meta Device)

GPT 有很多参数,直接在 init 中初始化会很慢。我们可以在在init定义,然后再开一个函数初始化,init 阶段是虚拟结构,init_weights() 阶段是真实初始化:

阶段initinit_weights()
调用时机创建模型时,自动调用创建后,手动调用 model.init_weights()
运行环境Meta Device(虚拟)真实设备(GPU/CPU)
内存占用0字节(没有真实数据)真实参数的内存
做什么定义网络结构填充权重实际值

词表大小会做一些调整, 向上取整到 pad_vocab_size_to 的倍数.

为什么要pad?为了DDP和tensor core效率

padded_vocab_size = (
            (config.vocab_size + pad_vocab_size_to - 1) // pad_vocab_size_to
        ) * pad_vocab_size_to

相应的lm_head输出维度也是新的padded_vocab_size

self.lm_head = nn.Linear(
            config.n_embd, padded_vocab_size, bias=False
        )

_precompute_rotary_embeddings

预计算出sin和cos的值,然后再apply_rotary_emb() 直接使用

num_scaling_params

计算模型的总参数数量,用于缩放律分析

nparams = sum(p.numel() for p in self.parameters())
return nparams

setup_optimizers

这是作者在nanochat中多次实验调优出来的最佳实践,根据不同参数使用不同的优化器和学习率.这个属实是前沿大厂训练模型的不传之秘了,感恩开源

参数类型优化器学习率原因
矩阵权重 (768×768等)Muon0.02Muon对大矩阵效果好
EmbeddingAdamW0.2词嵌入需要大学习率
Value EmbeddingAdamW0.2同embedding
lm_headAdamW0.004输出层需要小学习率
resid_lambdasAdamW0.005最敏感! (0.5 × 0.01)
x0_lambdasAdamW0.5较敏感
  • 学习率缩放:作者在768维模型上调参,大模型需要更小的lr,小模型需要更大的lr。
  • resid_lambdas 超敏感:这个参数会在每一层都被乘一次,累积效应大

两个优化器:

  • AdamW:适合embedding和小参数,自适应学习率
  • Muon:专为大矩阵设计,在Transformer矩阵上特别有效

forward

先把token id序列转成向量.x0是后面用于skip connection

x = self.transformer.wte(idx)  # Token ID → Embedding向量
x = norm(x)
x0 = x

然后计算堆叠的Transformer Block

for i, block in enumerate(self.transformer.h):
    # 第i层的残差混合
    x = self.resid_lambdas[i] * x + self.x0_lambdas[i] * x0
    # 可选:加入Value Embedding增强(某些层)
    ve = self.value_embeds[str(i)](idx) if str(i) in self.value_embeds else None
    # 通过该层的Attention + FFN
    x = block(x, ve, cos_sin, self.window_sizes[i], kv_cache)
x = norm(x)

接下来就可以得到logits,先用self.lm_head(x) 线性层把隐藏状态映射到词汇表大小的logits(这里实际上是被pad过的词汇表大小),然后切掉多余的,删回原始大小,最后转成float32,压缩logits范围到 [-15, 15]

softcap = 15  # smoothly cap the logits to the range [-softcap, softcap]
logits = self.lm_head(
    x
)  # (B, T, padded_vocab_size) <- very big tensor, large amount of memory
logits = logits[..., : self.config.vocab_size]
logits = (
    logits.float()
)
logits = softcap * torch.tanh(logits / softcap)

最后根据训练还是推理返回不同的内容

  • 训练时返回损失
  • 推理时返回所有T个位置的logits (B, T, vocab_size)
if targets is not None:
    # training: given the targets, compute and return the loss
    # TODO experiment with chunked cross-entropy?
    loss = F.cross_entropy(
        logits.view(-1, logits.size(-1)),
        targets.view(-1),
        ignore_index=-1,
        reduction=loss_reduction,
    )
    return loss
else:
    # inference: just return the logits directly
    return logits

generate

top_k

Top-K采样:用 -∞ 来”屏蔽”低概率 token,只从高概率的 top_k 个里采样。

if top_k is not None:
    v, _ = torch.topk(
        logits, min(top_k, logits.size(-1))
    )  # v: (B, top_k)
    logits[logits < v[:, [-1]]] = -float(
        "Inf"
    )

temperature

采样出token,然后拼接到输入序列,转成普通的 Python int,继续下一个位置的预测

  1. 用温度调整 logits
    • temperature < 1: logits 被放大 → 分布更尖锐 → 采样更确定
    • temperature = 1: 不变
    • temperature > 1: logits 被缩小 → 分布更平坦 → 采样更随机
  2. softmax 转成概率分布,所有值都在 [0, 1],求和为 1
  3. 按照这个概率分布采样,从分布中随机抽一个token ID
if temperature > 0:
    logits = logits / temperature
    probs = F.softmax(logits, dim=-1)
    # shape: (B, vocab_size)
    next_ids = torch.multinomial(probs, num_samples=1, generator=rng)

贪婪采样

给出概率最高的 token ID

总结

GPT 中 norm 的位置?什么时候需要norm?

位置代码作用
Pre-Normnorm(x) → Attn/FFN稳定梯度,允许高学习率
QK NormQ/K 后 Attention 前稳定 attention scores 数值范围
Embedding 后wte() → norm()标准化初始表示
输出前最后 norm()标准化最终表示