Files
tokenresearch/tokenizer.md
2026-06-04 14:09:16 +08:00

11 KiB
Raw Blame History

DETR 的视觉 token 化过程说明

本文基于当前项目代码 app/detector.py 中的实现说明 DETR 的“token 化”过程。

当前代码使用的是 Hugging Face Transformers

self.processor = DetrImageProcessor.from_pretrained(model_name)
self.model = DetrForObjectDetection.from_pretrained(model_name)

默认模型为:

facebook/detr-resnet-50

需要注意DETR 这里没有文本 tokenizer。它处理的是图像因此所谓“token 化”指的是把图像经过 CNN backbone 后得到的二维视觉特征图,展开成 Transformer 可以处理的一维视觉 token 序列。

当前代码中的入口

app/detector.py 中,检测入口是:

image = Image.fromarray(frame_rgb)
inputs = self.processor(images=image, return_tensors="pt")
inputs = {key: value.to(self.device) for key, value in inputs.items()}

outputs = self.model(**inputs)

这里分成两部分:

  1. DetrImageProcessor:做图像预处理。
  2. DetrForObjectDetection在模型内部完成视觉特征提取、flatten、位置编码、Transformer 编码解码和目标检测。

总体流程

完整流程可以理解为:

OpenCV RGB 帧
  ↓
PIL Image
  ↓
DetrImageProcessor 图像预处理
  ↓
pixel_values: [batch, 3, H, W]
pixel_mask:   [batch, H, W]
  ↓
ResNet-50 backbone 提取视觉特征
  ↓
feature map: [batch, 2048, H', W']
  ↓
1×1 convolution 投影通道
  ↓
projected feature map: [batch, 256, H', W']
  ↓
flatten 空间维度 H' × W'
  ↓
visual token embeddings: [batch, H'×W', 256]
  ↓
加入二维位置 embedding
  ↓
Transformer Encoder
  ↓
Object query embeddings + Transformer Decoder
  ↓
类别 logits + 边界框 boxes
  ↓
post_process_object_detection 还原到原图坐标

Embedding 在 1-11 个环节中的位置

在这个 DETR 流程里embedding 不是单独只有一步,而是出现在 3 个关键环节:

页面步骤 名称 embedding 含义
第 6 步 visual token embedding projected feature map 经过 flatten 后,每个空间网格点变成一个 256 维视觉 token embedding。
第 7 步 position embedding 给每个视觉 token 加入二维位置 embedding让 Transformer 知道 token 原本在图像中的位置。
第 9 步 object query embedding DETR 使用一组可学习的 object query embeddings 进入 Decoder每个 query 最终预测一个候选目标。

所以如果问“embedding 在 1-11 哪个环节”,最核心的是:

第 6 步:产生视觉 token embedding
第 7 步:加入位置 embedding
第 9 步object query embedding 进入 Decoder

第 6 步是图像内容 embedding第 7 步是空间位置 embedding第 9 步是检测目标查询 embedding。

第 1 步:图像预处理

代码:

inputs = self.processor(images=image, return_tensors="pt")

DetrImageProcessor 主要做这些事情:

  • 调整图像尺寸。
  • 转换为 PyTorch tensor。
  • 归一化像素值。
  • 生成 pixel_values
  • 必要时生成 pixel_mask,用于标记 padding 区域。

输出通常包含:

{
    "pixel_values": Tensor[batch, 3, H, W],
    "pixel_mask": Tensor[batch, H, W]
}

其中:

  • pixel_values 是送入模型的图像张量。
  • pixel_mask 用于告诉模型哪些区域是真实图像,哪些区域是 padding。

这一步不是文本 token 化,不会产生 input_idsattention_mask 这类 NLP tokenizer 输出。

第 2 步ResNet-50 提取特征图

模型内部首先使用 ResNet-50 backbone 处理图像:

pixel_values: [batch, 3, H, W]
  ↓ ResNet-50
feature map: [batch, 2048, H', W']

H'W' 是下采样后的空间尺寸。ResNet 通常会把图像下采样约 32 倍。

例如输入图像尺寸为:

[batch, 3, 800, 1280]

经过 ResNet 后,特征图可能接近:

[batch, 2048, 25, 40]

这里的每个空间位置 (y, x) 都是一个高层视觉特征向量。

第 3 步1×1 卷积投影通道

ResNet 输出通道数通常是 2048,而 DETR Transformer 默认隐藏维度通常是 256

因此模型会使用一个 1×1 convolution 做通道投影:

[batch, 2048, H', W']
  ↓ 1×1 conv
[batch, 256, H', W']

这一步不会改变空间大小,只改变每个空间位置的特征维度。

可以理解为:

每个网格点的 2048 维向量 → 256 维向量

第 4 步flatten 成视觉 token 序列

这是“视觉 token 化”的核心步骤。

投影后的特征图形状是:

[batch, 256, H', W']

模型会把二维空间维度 H' × W' 展开成一维序列:

[batch, 256, H', W']
  ↓ flatten H' 和 W'
[batch, 256, H'×W']
  ↓ 调整维度顺序
[batch, H'×W', 256]

如果特征图是:

[batch, 256, 25, 40]

那么 token 数是:

25 × 40 = 1000

最终得到:

[batch, 1000, 256]

也就是说:

每个特征图网格位置 = 1 个视觉 token
每个视觉 token = 1 个 256 维向量

伪代码可以写成:

# x: [batch, 256, h, w]
x = x.flatten(2)          # [batch, 256, h*w]
x = x.transpose(1, 2)     # [batch, h*w, 256]

原始 DETR 论文和部分 PyTorch 实现中也常见 sequence-first 格式:

# x: [batch, 256, h, w]
x = x.flatten(2)          # [batch, 256, h*w]
x = x.permute(2, 0, 1)    # [h*w, batch, 256]

两种写法本质相同,只是 Transformer 接口期望的维度顺序不同。

第 5 步:加入二维位置 embedding

Transformer 本身不理解图像中的二维空间位置。

flatten 后,模型只看到一串 token

token_0, token_1, token_2, ..., token_N

如果不加入位置编码,模型不知道某个 token 原来位于图像左上角、中心还是右下角。

因此 DETR 会为特征图每个 (y, x) 位置生成二维位置编码:

position encoding: [batch, 256, H', W']

然后同样 flatten

[batch, 256, H', W']
  ↓
[batch, H'×W', 256]

Transformer Encoder 接收的是:

visual token + positional encoding

位置编码让模型知道 token 的空间布局。

第 6 步Transformer Encoder 处理视觉 token

经过 flatten 和位置编码后,视觉 token 序列进入 Transformer Encoder

[batch, H'×W', 256]
  ↓ Transformer Encoder
[batch, H'×W', 256]

Encoder 会通过 self-attention 建模图像中不同区域之间的关系。

例如:

  • 车头区域可以关注车身区域。
  • 道路区域可以影响车辆判断。
  • 远处小目标可以和周围上下文一起被理解。

第 7 步Object query embedding 和 Transformer Decoder

DETR 与传统检测器不同,它不是先生成大量 anchor box。

它使用一组可学习的 object query embeddings。常见数量是

100 个 object queries

这些 queries 进入 Transformer Decoder并关注 Encoder 输出的视觉 token

object queries: [batch, 100, 256]
encoder tokens: [batch, H'×W', 256]
  ↓ Transformer Decoder
object features: [batch, 100, 256]

每个 query 最终预测一个候选目标:

类别 + 边界框

因此 DETR 输出通常可以理解为:

最多 100 个候选目标

每个候选目标会包含:

  • 类别 logits。
  • 归一化边界框。

第 8 步:后处理成车辆检测结果

当前代码中的后处理是:

target_sizes = torch.tensor([image.size[::-1]], device=self.device)
results = self.processor.post_process_object_detection(
    outputs,
    target_sizes=target_sizes,
    threshold=self.confidence,
)[0]

这一步会:

  • 把模型输出的归一化框还原为原图坐标。
  • 根据置信度阈值过滤低分检测。
  • 返回 scoreslabelsboxes

然后代码过滤车辆类别:

label_name = self.model.config.id2label[label.item()]
if label_name not in self.vehicle_labels:
    continue

默认车辆类别为:

car, motorcycle, bus, truck, bicycle

最终输出格式:

{
    "label": "car",
    "score": 0.9132,
    "box": [x1, y1, x2, y2]
}

与文本 tokenizer 的区别

对比项 文本 tokenizer DETR 视觉 token 化
输入 文本字符串 图像张量
输出 token id 序列 视觉特征向量序列
token 来源 词、子词、字符片段 CNN 特征图空间网格
token 内容 离散整数 id 连续浮点向量
位置编码 一维位置编码 二维图像位置编码
当前代码中的类 DetrImageProcessor + DetrForObjectDetection

为什么说它是 patch-like features

它们可以叫做 patch-like features,但不是 ViT 那种直接切原图 patch。

ViT 通常是:

原图 -> 切成 16×16 patch -> 线性投影 -> token

DETR-ResNet 是:

原图 -> ResNet 特征图 -> 每个特征图网格点 -> token

因此 DETR 的每个 token

  • 对应特征图上的一个空间位置。
  • 感受野来自 ResNet 深层网络。
  • 通常覆盖原图中一片较大的区域。
  • 相邻 token 的感受野会重叠。

一个具体尺寸例子

假设预处理后图像大小为:

800 × 1280

ResNet-50 下采样约 32 倍:

H' = 800 / 32 ≈ 25
W' = 1280 / 32 ≈ 40

Backbone 输出:

[batch, 2048, 25, 40]

1×1 卷积投影:

[batch, 256, 25, 40]

flatten

[batch, 1000, 256]

所以该图像大约会生成:

1000 个视觉 token

每个 token 是:

256 维连续向量

当前项目中的实际检测路径

当前项目的整体路径是:

RTSP/HLS 视频帧
  ↓
OpenCV 读取 BGR frame
  ↓
cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
  ↓
Image.fromarray(frame_rgb)
  ↓
DetrImageProcessor 图像预处理
  ↓
DetrForObjectDetection 内部完成视觉 token 化和目标检测
  ↓
post_process_object_detection
  ↓
过滤车辆类别
  ↓
OpenCV OSD 画框
  ↓
FastAPI /video 输出动态打标画面

对应代码位置:

  • app/detector.pyDETR 图像预处理、模型推理、后处理。
  • app/stream_worker.py视频帧读取、推理调用、OSD 画框。
  • app/main.pyMJPEG 视频流和检测结果接口。

小结

当前项目中的 DETR 不包含文本 tokenizer。

它的视觉 token 化具体是:

图像经过 ResNet-50 得到二维特征图
  ↓
1×1 卷积把通道投影到 Transformer hidden size
  ↓
把 H' × W' 个空间位置 flatten 成 H'×W' 个视觉 token
  ↓
给每个 token 加二维位置编码
  ↓
送入 Transformer Encoder

因此“token 数”主要由输入分辨率和 backbone 下采样比例决定:

视觉 token 数 ≈ (输入高度 / 32) × (输入宽度 / 32)

实际数量会受到图像预处理 resize、padding 和特征图尺寸取整影响。