1. 项目背景与核心需求
花卉绽放状态识别是计算机视觉领域一个典型的分类问题,在农业生产、园艺管理和生态监测中具有广泛应用价值。传统的人工观察方法效率低下且主观性强,而基于深度学习的自动化识别方案能够实现高效、客观的持续监测。
这个毕业设计项目的核心是构建一个能够准确区分花卉绽放与未绽放状态的CNN模型。从技术角度看,这属于二分类问题,但相比普通图像分类任务更具挑战性:绽放状态判断不仅依赖整体形态特征,还需要捕捉花瓣展开程度、花蕊可见性等细微差异。我在实际测试中发现,即使是同一品种的花卉,其绽放与未绽放状态的差异可能比不同品种间的差异更微妙。
2. 技术方案设计
2.1 模型架构选型
经过对比测试LeNet、AlexNet和轻量化MobileNet三种架构后,最终选择在LeNet基础上进行改进:
python复制class FlowerCNN(nn.Module):
def __init__(self):
super(FlowerCNN, self).__init__()
self.conv1 = nn.Conv2d(3, 16, 5, padding=2) # 保持空间分辨率
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(16, 32, 5, padding=2)
self.fc1 = nn.Linear(32 * 56 * 56, 120) # 输入尺寸调整为224x224
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 2) # 二分类输出
def forward(self, x):
x = self.pool(F.relu(self.conv1(x)))
x = self.pool(F.relu(self.conv2(x)))
x = x.view(-1, 32 * 56 * 56)
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
x = self.fc3(x)
return x
选择这一架构主要基于三点考虑:
- 花卉图像通常包含清晰的局部特征(如花瓣纹理),适合CNN提取
- 相对于复杂模型,中等规模网络在有限训练数据下更不易过拟合
- 调整后的感受野能更好捕捉花朵局部细节
2.2 数据准备关键点
数据集构建是本项目成功的关键。我采用的自建数据集包含两个子集:
- Flower102扩展集:从Oxford 102花卉数据集中筛选出有明显绽放状态差异的品种
- 自采数据集:使用手机在不同光照条件下拍摄的本地花卉图片
数据增强策略:
python复制transform = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ColorJitter(brightness=0.2, contrast=0.2),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
特别需要注意的细节:
- 保持绽放/未绽放样本数量均衡(建议比例1:1到1:1.2)
- 对室外拍摄图片需进行阴影校正处理
- 同一花卉的绽放前后状态应归为同一ID的不同类别
3. 模型训练技巧
3.1 损失函数优化
测试交叉熵损失函数时发现类别不平衡问题,采用加权交叉熵损失:
python复制pos_weight = torch.tensor([1.5]) # 给未绽放样本更高权重
criterion = nn.BCEWithLogitsLoss(pos_weight=pos_weight)
3.2 学习率调度
采用余弦退火配合热重启:
python复制optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9)
scheduler = optim.lr_scheduler.CosineAnnealingWarmRestarts(optimizer,
T_0=10,
T_mult=2)
3.3 关键训练参数
| 参数 | 设置值 | 选择依据 |
|---|---|---|
| Batch Size | 32 | GPU显存与训练稳定性平衡 |
| 初始LR | 0.01 | 多次网格搜索确定 |
| Epochs | 100 | 早停法实际约70轮 |
| 动量 | 0.9 | 常规CNN设置 |
4. 性能优化实战
4.1 注意力机制增强
在第二个卷积层后添加CBAM模块:
python复制class CBAM(nn.Module):
def __init__(self, channels):
super(CBAM, self).__init__()
self.channel_attention = ChannelAttention(channels)
self.spatial_attention = SpatialAttention()
def forward(self, x):
x = self.channel_attention(x)
x = self.spatial_attention(x)
return x
实测可使准确率提升2-3%,但会增加约15%的训练时间。
4.2 模型量化部署
使用TorchScript量化模型便于部署:
python复制quantized_model = torch.quantization.quantize_dynamic(
model, {nn.Linear}, dtype=torch.qint8)
torch.jit.save(torch.jit.script(quantized_model), 'flower_cnn_quant.pt')
量化后模型大小缩减为原来的35%,CPU推理速度提升2倍。
5. 常见问题解决方案
5.1 过拟合处理
验证集表现远差于训练集时的对策:
- 增加MixUp数据增强:
python复制def mixup_data(x, y, alpha=0.4):
lam = np.random.beta(alpha, alpha)
batch_size = x.size()[0]
index = torch.randperm(batch_size)
mixed_x = lam * x + (1 - lam) * x[index]
y_a, y_b = y, y[index]
return mixed_x, y_a, y_b, lam
- 添加Dropout层(p=0.3)
- 使用Label Smoothing(ε=0.1)
5.2 样本混淆分析
通过混淆矩阵发现常见错误模式:
- 重瓣花卉的未绽放状态易误判为绽放
- 白色花卉在不同光照下判断不稳定
解决方案:
- 针对易混淆类别增加hard sample mining
- 引入HSV颜色空间特征辅助判断
6. 完整实现示例
环境配置:
bash复制conda create -n flower python=3.8
conda install pytorch==1.12.1 torchvision==0.13.1 -c pytorch
pip install opencv-python matplotlib
训练流程主函数:
python复制def train_model(dataloaders, model, criterion, optimizer, scheduler, num_epochs=25):
best_acc = 0.0
for epoch in range(num_epochs):
for phase in ['train', 'val']:
if phase == 'train':
model.train()
else:
model.eval()
running_loss = 0.0
running_corrects = 0
for inputs, labels in dataloaders[phase]:
inputs = inputs.to(device)
labels = labels.to(device)
optimizer.zero_grad()
with torch.set_grad_enabled(phase == 'train'):
outputs = model(inputs)
_, preds = torch.max(outputs, 1)
loss = criterion(outputs, labels)
if phase == 'train':
loss.backward()
optimizer.step()
running_loss += loss.item() * inputs.size(0)
running_corrects += torch.sum(preds == labels.data)
if phase == 'train':
scheduler.step()
epoch_loss = running_loss / len(dataloaders[phase].dataset)
epoch_acc = running_corrects.double() / len(dataloaders[phase].dataset)
print(f'{phase} Loss: {epoch_loss:.4f} Acc: {epoch_acc:.4f}')
if phase == 'val' and epoch_acc > best_acc:
best_acc = epoch_acc
torch.save(model.state_dict(), 'best_model.pth')
return model
7. 项目扩展方向
- 多阶段识别:增加"即将绽放"的中间状态
- 时序分析:结合视频流分析绽放过程
- 移动端优化:转换为TFLite部署到Android设备
- 品种适配:设计可迁移的少样本学习方案
在实际部署中发现,模型对拍摄角度较为敏感。建议在实际应用中固定摄像头位置或增加视角归一化预处理。另一个实用技巧是在花朵中心区域添加注意力权重,这可以通过在数据标注时标记花心位置来实现。
