共计 3103 个字符,预计需要花费 8 分钟才能阅读完成。
数据集背景介绍
ChineseFoodNet 是一个包含 108 种中式菜品、总计 18.5 万张图像的数据集。它最早由中科院在 2018 年发布,主要用于食品识别和分类研究。数据集的特点是菜品类别覆盖广(从宫保鸡丁到阳春面),但存在以下挑战:

- 图像尺寸不统一(从 300×400 到 2000×3000 不等)
- 光照条件差异大(部分拍摄于餐厅,部分来自网络)
- 标注文件采用 JSON 格式,需要特殊解析
典型应用场景包括:
- 智能餐厅的自动结账系统
- 健康饮食 APP 的热量估算
- 外卖平台的图像搜索功能
数据预处理实战
1. 环境准备
首先安装必要依赖(建议使用 conda 环境):
conda create -n food python=3.8
conda install pytorch==1.10 torchvision -c pytorch
pip install opencv-python pandas tqdm
2. 数据加载
数据集解压后通常包含以下结构:
dataset_root/
├── annotations/
│ ├── train.json
│ └── test.json
└── images/
├── 000001.jpg
├── 000002.jpg
...
使用以下代码加载标注信息:
import json
import pandas as pd
with open('annotations/train.json') as f:
train_data = json.load(f)
# 转换为 DataFrame 方便处理
train_df = pd.DataFrame({'image_path': [f"images/{x['image_id']}.jpg" for x in train_data],
'label': [x['category_id'] for x in train_data]
})
3. 图像预处理
关键预处理步骤包括:
- 统一缩放到 256×256 尺寸
- 随机水平翻转增强
- 归一化到 [0,1] 范围
PyTorch 实现示例:
from torchvision import transforms
# 定义训练集变换
train_transform = transforms.Compose([transforms.Resize(256), # 先调整短边为 256
transforms.CenterCrop(224), # 中心裁剪
transforms.RandomHorizontalFlip(), # 数据增强
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
# 验证集只需要基础变换
val_transform = transforms.Compose([transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406],
std=[0.229, 0.224, 0.225])
])
模型训练示例
基础 CNN 模型
以下是一个适合新手的简单架构:
import torch.nn as nn
import torch.nn.functional as F
class SimpleCNN(nn.Module):
def __init__(self, num_classes=108):
super().__init__()
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
self.fc1 = nn.Linear(64 * 56 * 56, 512) # 224/2/2=56
self.fc2 = nn.Linear(512, num_classes)
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(-1, 64 * 56 * 56) # 展平
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
训练循环
完整训练流程示例:
from torch.utils.data import DataLoader
model = SimpleCNN()
criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
for epoch in range(10):
model.train()
for images, labels in train_loader:
optimizer.zero_grad()
outputs = model(images)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
# 验证阶段
model.eval()
correct = 0
with torch.no_grad():
for images, labels in val_loader:
outputs = model(images)
_, predicted = torch.max(outputs.data, 1)
correct += (predicted == labels).sum().item()
print(f'Epoch {epoch}: Acc {correct/total:.2f}')
性能优化技巧
批量大小选择
- 显存充足时:使用较大 batch(如 64/128)加速训练
- 显存有限时:尝试梯度累积(accumulate gradients)
# 梯度累积示例
accum_steps = 4
for i, (images, labels) in enumerate(train_loader):
outputs = model(images)
loss = criterion(outputs, labels)
loss = loss / accum_steps # 平均损失
loss.backward()
if (i+1) % accum_steps == 0:
optimizer.step()
optimizer.zero_grad()
学习率调整
推荐使用余弦退火策略:
from torch.optim.lr_scheduler import CosineAnnealingLR
scheduler = CosineAnnealingLR(optimizer, T_max=10) # 10 个 epoch 周期
避坑指南
- 内存溢出问题:
- 现象:训练时突然崩溃
-
解决:减小 batch_size 或使用更小的图像尺寸
-
标签错位:
- 现象:准确率始终接近 0
-
检查:确认 DataLoader 返回的标签与图像对应
-
梯度爆炸:
- 现象:loss 变为 NaN
-
解决:添加梯度裁剪
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0) -
数据泄漏:
- 现象:验证集准确率异常高
-
检查:确保训练集和验证集没有重叠样本
-
预处理不一致:
- 现象:部署时效果下降
- 解决:训练和推理使用完全相同的 transform
延伸学习
- 进阶模型:尝试 ResNet18 等预训练模型
- 更复杂的数据增强:MixUp、CutMix
- 类别不平衡处理:Focal Loss
- 在线学习:使用少量新菜品样本微调模型
完整代码示例已上传 GitHub 仓库(假设链接),包含更多注释和可视化工具。建议从简单模型开始,逐步增加复杂度,祝各位训练顺利!
正文完
发表至: 未分类
近两天内
