计算机视觉领域最让人着迷的,莫过于让机器学会"看"懂世界。作为从业多年的CV工程师,我见过太多初学者在入门阶段被各种术语和框架绕晕。今天我们就从最基础的图像识别(Image Recognition)和目标检测(Object Detection)讲起,用最接地气的方式带你理解这两个核心概念的区别与联系。
简单来说,图像识别解决"是什么"的问题——比如判断图片中是猫还是狗;而目标检测则要回答"在哪里"和"是什么"——不仅要识别物体类别,还要用边界框标出位置。这就像教孩子认东西:先学会叫出名字(识别),再能指着说"那个红色的球在桌子下面"(检测)。实际应用中,从手机相册自动分类到自动驾驶感知环境,都离不开这两项技术。
传统图像识别主要依赖特征工程+SVM/随机森林等分类器。2012年AlexNet横空出世后,CNN成为绝对主流。其核心在于:
以ResNet为例,其残差连接解决了深层网络梯度消失问题。实际训练时,我们常采用:
python复制model = ResNet50(weights='imagenet')
model.trainable = False # 迁移学习常用技巧
从传统方法到深度学习,目标检测经历了三次浪潮:
| 时期 | 代表算法 | 核心思想 |
|---|---|---|
| 2014前 | HOG+SVM | 滑动窗口+手工特征 |
| 2014-2016 | R-CNN系列 | 区域提议+CNN分类 |
| 2016至今 | YOLO/SSD | 端到端统一框架 |
YOLOv3的Darknet-53 backbone在COCO数据集上能达到57.9% mAP,同时保持45FPS的实时性能。其创新点在于:
根据项目规模推荐配置:
重要提示:使用CUDA前务必检查驱动版本与CUDA Toolkit的兼容性,这是新手最常见的环境问题
推荐使用conda创建隔离环境:
bash复制conda create -n obj_det python=3.8
conda install pytorch torchvision cudatoolkit=11.3 -c pytorch
pip install opencv-python matplotlib tqdm
验证安装:
python复制import torch
print(torch.cuda.is_available()) # 应输出True
print(torch.__version__) # 建议≥1.8.0
使用LabelImg进行标注时要注意:
标注质量检查脚本示例:
python复制def check_annotation(img_path, label_path):
img = cv2.imread(img_path)
h, w = img.shape[:2]
with open(label_path) as f:
for line in f:
cls, xc, yc, bw, bh = map(float, line.split())
# 转换为像素坐标
x1 = int((xc - bw/2) * w)
y1 = int((yc - bh/2) * h)
x2 = int((xc + bw/2) * w)
y2 = int((yc + bh/2) * h)
cv2.rectangle(img, (x1,y1), (x2,y2), (0,255,0), 2)
cv2.imshow('Check', img)
cv2.waitKey(0)
使用Albumentations库实现专业级增强:
python复制import albumentations as A
transform = A.Compose([
A.HorizontalFlip(p=0.5),
A.RandomBrightnessContrast(p=0.2),
A.ShiftScaleRotate(shift_limit=0.05, scale_limit=0.1, rotate_limit=15),
A.Cutout(num_holes=8, max_h_size=16, max_w_size=16, fill_value=0, p=0.5)
], bbox_params=A.BboxParams(format='yolo'))
以Faster R-CNN为例,冻结backbone训练技巧:
python复制from torchvision.models.detection import fasterrcnn_resnet50_fpn
model = fasterrcnn_resnet50_fpn(pretrained=True)
# 冻结所有骨干网络参数
for param in model.backbone.parameters():
param.requires_grad = False
# 仅训练RPN和分类头
optimizer = torch.optim.SGD(
[p for p in model.parameters() if p.requires_grad],
lr=0.005, momentum=0.9, weight_decay=0.0005
)
使用TensorBoard记录关键指标:
python复制from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter()
for epoch in range(epochs):
# ...训练代码...
writer.add_scalar('Loss/total', total_loss, epoch)
writer.add_scalar('LR', optimizer.param_groups[0]['lr'], epoch)
# 可视化预测结果
if epoch % 5 == 0:
writer.add_images('Predictions', pred_imgs, epoch)
将FP32模型转为INT8提升推理速度:
python复制model = torch.quantization.quantize_dynamic(
model, # 原始模型
{torch.nn.Linear}, # 要量化的模块类型
dtype=torch.qint8 # 量化类型
)
转换ONNX模型为TensorRT引擎:
bash复制trtexec --onnx=model.onnx --saveEngine=model.plan \
--fp16 --workspace=2048
实测性能对比(Tesla T4):
| 框架 | 延迟(ms) | 吞吐量(FPS) |
|---|---|---|
| PyTorch | 45.2 | 22.1 |
| TensorRT | 11.7 | 85.5 |
当遇到CUDA out of memory时:
python复制optimizer.zero_grad()
for i, (images, targets) in enumerate(dataloader):
loss = model(images, targets)
loss.backward()
if (i+1) % 4 == 0: # 每4个batch更新一次
optimizer.step()
optimizer.zero_grad()
python复制scaler = torch.cuda.amp.GradScaler()
with torch.cuda.amp.autocast():
loss = model(images, targets)
scaler.scale(loss).backward()
scaler.step(optimizer)
scaler.update()
检查清单:
调试代码示例:
python复制# 检查梯度流动
for name, param in model.named_parameters():
if param.grad is None:
print(f"No gradient for {name}")
else:
print(f"{name} grad mean: {param.grad.mean().item():.4f}")
某PCB板检测需求:
解决方案:
最终指标:
超市货架监测方案:
python复制class ShelfAnalyzer:
def __init__(self, model_path):
self.model = load_model(model_path)
self.class_map = {0: 'cola', 1: 'juice', ...}
def analyze(self, img):
results = self.model(img)
counts = defaultdict(int)
for box, cls, conf in results:
if conf > 0.7: # 置信度阈值
counts[self.class_map[cls]] += 1
return dict(counts)
关键创新点:
掌握基础后可以深入:
推荐实验路线:
mermaid复制graph LR
A[基础模型] --> B[改进损失函数]
B --> C[尝试不同backbone]
C --> D[模型蒸馏]
D --> E[部署优化]
最后分享一个实用技巧:当遇到难样本时,可以先用Grad-CAM可视化模型关注区域,这能快速发现是数据问题还是模型问题。比如检测漏标的缺陷时,热力图显示模型其实"看到"了异常区域,这时就该检查标注质量而非调整模型。