重生之我成为模型 番外 · 饲养员厨房上——标准简餐

2 阅读5分钟

系列: 正文①钩子兑现 · 下篇 番外-饲养员厨房-下|大厨古法餐


正文那边,是模型自己在说话。

每一个模型的背后,都有一位兢兢业业准备食材的饲养员。
我就是这个饲养员,但名片上写的是 图像算法工程师

其实养育模型,是为了给我分担掉一些活计。
有时候不难的话,我自己就能干完了——我自己干活的事情,在下篇会全部展开。

模型的配餐,其实要求非常严格。
设计师把配餐要求写好了;我只需要照着要求,把食材做到对应的尺寸大小,到模型能吃的程度就行。
这篇我只负责做这件事。接下来,介绍一下我平时的配餐过程。


1. 进货:先把「图」读成像素板

先说清楚身份:饲养员也是打工仔。

设计师把配餐要求写在图纸上——考卷多大、吃几路颜色、mean/std 怎么配。
我不是在发明「图该长啥样」,我是照单进货、照单切配。
进货这一刀,就是把磁盘上的照片,读成内存里一块大约 0~255 的整数像素板。

人看图是风景、宠物、路牌;我看的是数组。形状常写成 H × W × C

字母人话
H高——多少行
W宽——多少列
C通道——每个像素带几路信息

基础读图大概长这样:

import cv2

img = cv2.imread("cat.jpg")          # 默认彩色;失败时是 None
if img is None:
    raise FileNotFoundError("图没读到,先查路径")

print(img.shape)   # 例如 (H, W, 3) —— OpenCV 默认 BGR
print(img.dtype)   # 常见 uint8,约 0–255

彩色还是灰度,不是审美,是任务。

任务大概要啥常怎么进货为啥
认种类、看颜色(AlexNet / ImageNet 这一路)彩色,C=3毛色、花纹、牌子颜色都是线索
找边缘、看明暗跳变、很多古典分割灰度,C=1 就够边≈亮度突然变;多通道反而吵
按颜色圈区域(比如蓝天 HSV)先彩色,再转 HSV 等空间要的是「什么色」,不是灰度亮度

喂他家那位认一千类:主路径是彩色。
下篇古典灶台找边、做 mask:经常先转灰,或转 HSV——那是另一套单。

OpenCV 默认读进来是 BGR,不是很多人以为的 RGB:

b, g, r = cv2.split(img)                    # 通道顺序:B→G→R
rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)  # 给 matplotlib / 部分流水线用
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
print(gray.shape)  # (H, W) —— 单通道,没有 C 那一维,或理解为 C=1

颜色顺序搞错了,后面一切都像隔着一层奇怪滤镜——打工仔翻车,多半翻在这种「好像小事」上。

技术翻译:

文件 → cv2.imread
→ uint8,约 0255
→ 彩色常是 H×W×3(BGR);灰度常是 H×W
→ 还不是考生吃的 NCHW

到这一步,还只是「图」的近亲。他家那位还没开吃。


2. 洗切:我怎么把图画布切到能吃

挑剔食客在正文第一篇就讲过这块了——256 舞台、裁 224、训随机推中心、裁不是整图缩到位。这边一笔带过,主要讲我怎么操作。

我常干啥小心别翻车
resize 到 256,再 crop 出 224一步缩成 224;224/227 混用,对不齐他家权重
训练加随机裁 / 翻转;推理切中心训推两套预处理对不上 → 分数飘
交出去的布,对上设计师画的门洞布太大扫不动,布太歪分布对不上

手搓一刀可以用 OpenCV(概念示意):

stage = cv2.resize(img, (256, 256))   # 非正方形会被扯;接受这代价换统一台面
# 推理常见:中心裁 224
y0 = (256 - 224) // 2
patch = stage[y0:y0+224, y0:y0+224]   # 仍是 HWC、约 0–255

正经喂他家那位,更多时候几何也写进 transformsResize / RandomCrop / CenterCrop),和下一节的 ToTensor、Normalize 串成一条。
口诀照旧:256 是舞台,224 是上台那一块布。


3. 调味装盘:数值链 + 按批上桌

几何过后还是 0–255 像素板。ToTensor、Normalize、堆成 NCHW——挑剔食客正文里也说过「是什么」。这边同样一笔带过,讲我装盘时钉死的两件事:

  1. 配方跟权重走——mean/std 对不上,等于给兔子喂猫粮。
  2. 这些活落在 __getitem__ / transforms 里——Conv 不会自动帮你 /255 或 Normalize。
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms

# ImageNet 这一路常见配方(跟对应预训练权重配套)
train_tf = transforms.Compose([
    transforms.Resize(256),
    transforms.RandomCrop(224),
    transforms.RandomHorizontalFlip(),
    transforms.ToTensor(),
    transforms.Normalize(
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225],
    ),
])

class ExamSet(Dataset):
    def __len__(self): ...
    def __getitem__(self, i):
        # 读图 → 必要时 BGR→RGB → train_tf → (张量, 标签)
        ...

loader = DataLoader(ExamSet(), batch_size=32, shuffle=True)
# 取出的 batch:N×C×H×W —— N 是一批几道题

一批多几张,只是 N 变大。灶台尺寸和口粮配方,仍然是定死的——我照单装盘,不替他发明菜单。


4. 答案从哪来(点到为止)

说清楚一件事,免得跟下篇搅在一起:

我这盘,主要是 题面
类号多半是人标的;检测框、分割 mask 也各有来源。
滤波、边缘、阈值那些工艺手法——不是喂 AlexNet 认 ImageNet 时的必经主菜。它们有时自己就是考生,有时帮人写出答案。那是下篇的灶台。

所以这一篇,其实是 简餐出餐标准:统一工序,只制作题面。
标准答案另一本账;古法大餐,下篇再说。


5. 收束

正文那边把「口粮必须合规」讲清楚了;厨房这篇补的是:我怎么操作、代码落哪、题面≠答案。

做完这套简餐工序,他才第一次发言。
我不负责替他看见世界;我负责把题印清楚、按批上桌。

下一篇是饲养员的专属时间——真正完整的大餐制作,古法工艺:滤波、边缘、轮廓、阈值。有时我自己交卷,根本轮不到深度考生上场。


下一篇:成文/番外-饲养员厨房-下|饲养员厨房(下):大厨古法餐