first commit

This commit is contained in:
陈赣
2026-06-03 11:00:50 +08:00
commit 322b72ac5b
18 changed files with 1821 additions and 0 deletions

481
tokenizer.md Normal file
View File

@@ -0,0 +1,481 @@
# DETR 的视觉 token 化过程说明
本文基于当前项目代码 `app/detector.py` 中的实现说明 DETR 的“token 化”过程。
当前代码使用的是 Hugging Face Transformers
```python
self.processor = DetrImageProcessor.from_pretrained(model_name)
self.model = DetrForObjectDetection.from_pretrained(model_name)
```
默认模型为:
```text
facebook/detr-resnet-50
```
需要注意DETR 这里没有文本 tokenizer。它处理的是图像因此所谓“token 化”指的是把图像经过 CNN backbone 后得到的二维视觉特征图,展开成 Transformer 可以处理的一维视觉 token 序列。
## 当前代码中的入口
`app/detector.py` 中,检测入口是:
```python
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 编码解码和目标检测。
## 总体流程
完整流程可以理解为:
```text
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 tokens: [batch, H'×W', 256]
加入二维位置编码
Transformer Encoder
Object Queries + Transformer Decoder
类别 logits + 边界框 boxes
post_process_object_detection 还原到原图坐标
```
## 第 1 步:图像预处理
代码:
```python
inputs = self.processor(images=image, return_tensors="pt")
```
`DetrImageProcessor` 主要做这些事情:
- 调整图像尺寸。
- 转换为 PyTorch tensor。
- 归一化像素值。
- 生成 `pixel_values`
- 必要时生成 `pixel_mask`,用于标记 padding 区域。
输出通常包含:
```python
{
"pixel_values": Tensor[batch, 3, H, W],
"pixel_mask": Tensor[batch, H, W]
}
```
其中:
- `pixel_values` 是送入模型的图像张量。
- `pixel_mask` 用于告诉模型哪些区域是真实图像,哪些区域是 padding。
这一步不是文本 token 化,不会产生 `input_ids``attention_mask` 这类 NLP tokenizer 输出。
## 第 2 步ResNet-50 提取特征图
模型内部首先使用 ResNet-50 backbone 处理图像:
```text
pixel_values: [batch, 3, H, W]
↓ ResNet-50
feature map: [batch, 2048, H', W']
```
`H'``W'` 是下采样后的空间尺寸。ResNet 通常会把图像下采样约 32 倍。
例如输入图像尺寸为:
```text
[batch, 3, 800, 1280]
```
经过 ResNet 后,特征图可能接近:
```text
[batch, 2048, 25, 40]
```
这里的每个空间位置 `(y, x)` 都是一个高层视觉特征向量。
## 第 3 步1×1 卷积投影通道
ResNet 输出通道数通常是 `2048`,而 DETR Transformer 默认隐藏维度通常是 `256`
因此模型会使用一个 `1×1 convolution` 做通道投影:
```text
[batch, 2048, H', W']
↓ 1×1 conv
[batch, 256, H', W']
```
这一步不会改变空间大小,只改变每个空间位置的特征维度。
可以理解为:
```text
每个网格点的 2048 维向量 → 256 维向量
```
## 第 4 步flatten 成视觉 token 序列
这是“视觉 token 化”的核心步骤。
投影后的特征图形状是:
```text
[batch, 256, H', W']
```
模型会把二维空间维度 `H' × W'` 展开成一维序列:
```text
[batch, 256, H', W']
↓ flatten H' 和 W'
[batch, 256, H'×W']
↓ 调整维度顺序
[batch, H'×W', 256]
```
如果特征图是:
```text
[batch, 256, 25, 40]
```
那么 token 数是:
```text
25 × 40 = 1000
```
最终得到:
```text
[batch, 1000, 256]
```
也就是说:
```text
每个特征图网格位置 = 1 个视觉 token
每个视觉 token = 1 个 256 维向量
```
伪代码可以写成:
```python
# 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 格式:
```python
# 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 步:加入二维位置编码
Transformer 本身不理解图像中的二维空间位置。
flatten 后,模型只看到一串 token
```text
token_0, token_1, token_2, ..., token_N
```
如果不加入位置编码,模型不知道某个 token 原来位于图像左上角、中心还是右下角。
因此 DETR 会为特征图每个 `(y, x)` 位置生成二维位置编码:
```text
position encoding: [batch, 256, H', W']
```
然后同样 flatten
```text
[batch, 256, H', W']
[batch, H'×W', 256]
```
Transformer Encoder 接收的是:
```text
visual token + positional encoding
```
位置编码让模型知道 token 的空间布局。
## 第 6 步Transformer Encoder 处理视觉 token
经过 flatten 和位置编码后,视觉 token 序列进入 Transformer Encoder
```text
[batch, H'×W', 256]
↓ Transformer Encoder
[batch, H'×W', 256]
```
Encoder 会通过 self-attention 建模图像中不同区域之间的关系。
例如:
- 车头区域可以关注车身区域。
- 道路区域可以影响车辆判断。
- 远处小目标可以和周围上下文一起被理解。
## 第 7 步Object Queries 和 Transformer Decoder
DETR 与传统检测器不同,它不是先生成大量 anchor box。
它使用一组可学习的 object queries。常见数量是
```text
100 个 object queries
```
这些 queries 进入 Transformer Decoder并关注 Encoder 输出的视觉 token
```text
object queries: [batch, 100, 256]
encoder tokens: [batch, H'×W', 256]
↓ Transformer Decoder
object features: [batch, 100, 256]
```
每个 query 最终预测一个候选目标:
```text
类别 + 边界框
```
因此 DETR 输出通常可以理解为:
```text
最多 100 个候选目标
```
每个候选目标会包含:
- 类别 logits。
- 归一化边界框。
## 第 8 步:后处理成车辆检测结果
当前代码中的后处理是:
```python
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]
```
这一步会:
- 把模型输出的归一化框还原为原图坐标。
- 根据置信度阈值过滤低分检测。
- 返回 `scores``labels``boxes`
然后代码过滤车辆类别:
```python
label_name = self.model.config.id2label[label.item()]
if label_name not in self.vehicle_labels:
continue
```
默认车辆类别为:
```text
car, motorcycle, bus, truck, bicycle
```
最终输出格式:
```python
{
"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 通常是:
```text
原图 -> 切成 16×16 patch -> 线性投影 -> token
```
DETR-ResNet 是:
```text
原图 -> ResNet 特征图 -> 每个特征图网格点 -> token
```
因此 DETR 的每个 token
- 对应特征图上的一个空间位置。
- 感受野来自 ResNet 深层网络。
- 通常覆盖原图中一片较大的区域。
- 相邻 token 的感受野会重叠。
## 一个具体尺寸例子
假设预处理后图像大小为:
```text
800 × 1280
```
ResNet-50 下采样约 32 倍:
```text
H' = 800 / 32 ≈ 25
W' = 1280 / 32 ≈ 40
```
Backbone 输出:
```text
[batch, 2048, 25, 40]
```
1×1 卷积投影:
```text
[batch, 256, 25, 40]
```
flatten
```text
[batch, 1000, 256]
```
所以该图像大约会生成:
```text
1000 个视觉 token
```
每个 token 是:
```text
256 维连续向量
```
## 当前项目中的实际检测路径
当前项目的整体路径是:
```text
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.py`DETR 图像预处理、模型推理、后处理。
- `app/stream_worker.py`视频帧读取、推理调用、OSD 画框。
- `app/main.py`MJPEG 视频流和检测结果接口。
## 小结
当前项目中的 DETR 不包含文本 tokenizer。
它的视觉 token 化具体是:
```text
图像经过 ResNet-50 得到二维特征图
1×1 卷积把通道投影到 Transformer hidden size
把 H' × W' 个空间位置 flatten 成 H'×W' 个视觉 token
给每个 token 加二维位置编码
送入 Transformer Encoder
```
因此“token 数”主要由输入分辨率和 backbone 下采样比例决定:
```text
视觉 token 数 ≈ (输入高度 / 32) × (输入宽度 / 32)
```
实际数量会受到图像预处理 resize、padding 和特征图尺寸取整影响。