FasterRCNN训练自己的数据集

2016年提出的Faster RCNN目标检测模型是深度学习现代目标检测算法的开山之作,也是第一个真正全流程都是神经网络的目标检测模型。

其主要步骤如下:

1,使用CNN对输入图片提取feature map.

2,对feature map上的每个点设计一套不同大小和长宽比的anchor作为先验框。

3,设计RPN网络从大量的anchor中筛选出一些作为目标框的proposals并用回归分支纠正它们的位置。

4,使用ROI Pooling技术对不同大小的proposals获取相同大小的对应特征图,以便后续分类模型一并处理。

5,在proposals的feature map上使用分类分支和回归分支进一步预测目标类别和更精确的定位。

anchor技巧ROI Pooling技术 是非常值得学习的技巧,在许多目标检测模型中都能看到他们的身影。

b1c2e63ddc895a32120c5b0ddb03a614.jpeg

尽管FasterRCNN历史悠久,但依然是一个非常重要的目标检测任务的baseline.

一般会把它叫做two-stage的目标检测模型,主要是如果train from scratch,   RPN网络提取proposals和后续对propasals的定位分类 这两个步骤是要分开训练的,但在微调的时候,通常可以一起训练。

本文我们主要演示调用torchvision中的faster-rcnn模型在自己的数据集上微调来检测螺丝螺母。

#!pip install torchvision,torchkeras
import numpy as np
import pandas as pd 
from matplotlib import pyplot as plt
from PIL import Image,ImageColor,ImageDraw,ImageFont 

import torch
from torch import nn
import torchvision
from torchvision import datasets, models, transforms

import datetime
import os
import copy
import json 

print(torch.__version__)
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
2.0.0+cu117

〇,预训练模型

from torchkeras.data import get_example_image
img = get_example_image('park.jpg')
img.save('park.jpg')
from torchkeras.plots import vis_detection 

# 准备数据
inputs = []
img = Image.open('park.jpg').convert("RGB")
img_tensor = torch.from_numpy(np.array(img)/255.).permute(2,0,1).float()
if torch.cuda.is_available():
    img_tensor = img_tensor.cuda()
inputs.append(img_tensor)    

# 加载模型
num_classes = 91
model = torchvision.models.detection.fasterrcnn_resnet50_fpn(
    weights=torchvision.models.detection.FasterRCNN_ResNet50_FPN_Weights.COCO_V1,
    num_classes = num_classes)

if torch.cuda.is_available():
    model.to("cuda:0")
model.eval()

# 预测结果
with torch.no_grad():
    predictions = model(inputs)


# 结果可视化
class_names = torchvision.models.detection.FasterRCNN_ResNet50_FPN_Weights.COCO_V1.meta['categories']

vis_detection(img,predictions[0],class_names,min_score = 0.8)

ed52a62468ca74dbcbd773733d61e7c5.png

下面代码我们演示使用我开发的优雅的torchkeras工具在自己的数据集上对Faster-RCNN模型进行finetune。

我们使用一个非常简单的螺丝(bolt)螺母(nut)数据集作为示范。

公众号 算法美食屋 后台回复关键词:torchkeras,获取本文notebook代码和 bolt nut 数据集 下载地址。

一,准备数据

data_path = "./data/bolt_nut"

train_images_path = "./data/bolt_nut/train"
train_targets_path = './data/bolt_nut/train.txt'

val_images_path = "./data/bolt_nut/val"
val_targets_path = './data/bolt_nut/val.txt'

class_names = ['__background__','bolt','nut']
class BoltNut(torch.utils.data.Dataset):
    def __init__(self, images_path, targets_path, 
                 class_names = class_names,
                 transforms = None
                ):
        self.images_path = images_path
        self.targets_path = targets_path
        self.transforms = transforms
        self.infos_list = open(targets_path,"r").readlines()
        self.class_names = class_names

    def __getitem__(self, idx):
        
        info_str = self.infos_list[idx]
        info_arr = info_str.replace("\n","").replace("\t ","").split("\t")
        
        img_path = info_arr.pop(0)
        
        info_arr = [x for x in info_arr if x.strip()] 
        infos = [json.loads(x) for x in info_arr]

        img= Image.open(os.path.join(self.images_path,img_path)).convert("RGB")

        target = {}
        target["image_id"] = torch.tensor([int(img_path.split(".")[0])],dtype = torch.int64)  
        target["labels"] = torch.tensor([self.class_names.index(x["value"]) for x in infos],
                                        dtype = torch.int64)

        coords = [x["coordinate"]  for x in infos]
        boxes = torch.tensor([[xmin,ymin,xmax,ymax] for (xmin,ymin), (xmax,ymax)  in coords])
        target["boxes"] = boxes

        target["area"] = (boxes[:, 3] - boxes[:, 1]) * (boxes[:, 2] - boxes[:, 0])
        target["iscrowd"] = torch.zeros((len(infos) ,), dtype=torch.int64)
        
        if self.transforms is not None:
            img, target = self.transforms(img, target)
  
        return img, target

    def __len__(self):
        return len(self.infos_list)
# 可视化数据集
ds_train = BoltNut(train_images_path,train_targets_path)
img,target = ds_train[12]

target["scores"] = torch.ones_like(target["labels"])
img_result = vis_detection(img,target,class_names,min_score = 0.8)
img_result

0f773c51efefd49c9ef239c74d8c1302.png

下面我们设计数据增强模块

import random 
from torchvision import transforms as T

class Compose(object):
    def __init__(self, transforms):
        self.transforms = transforms

    def __call__(self, image, target):
        for t in self.transforms:
            image, target = t(image, target)
        return image, target


class RandomHorizontalFlip(object):
    def __init__(self, prob):
        self.prob = prob

    def __call__(self, image, target):
        if random.random() < self.prob:
            height, width = image.shape[-2:]
            image = image.flip(-1)
            bbox = target["boxes"]
            bbox[:, [0, 2]] = width - bbox[:, [2, 0]]
            target["boxes"] = bbox
            if "masks" in target:
                target["masks"] = target["masks"].flip(-1)
        return image, target


class ToTensor(object):
    def __call__(self, image, target):
        image = T.ToTensor()(image)
        return image, target
transforms_train = Compose([ToTensor(),RandomHorizontalFlip(0.5)])
transforms_val = ToTensor()

ds_train = BoltNut(train_images_path,train_targets_path,transforms=transforms_train)
ds_val = BoltNut(val_images_path,val_targets_path,transforms=transforms_val)
def collate_fn(batch):
      return tuple(zip(*batch))

dl_train = torch.utils.data.DataLoader(ds_train, batch_size=2, 
          shuffle=True, num_workers=4,collate_fn= collate_fn)

dl_val = torch.utils.data.DataLoader(ds_val, batch_size=2, 
          shuffle=True, num_workers=4,collate_fn= collate_fn)
for batch in dl_train:
    features,labels = batch  
    break

二,定义模型

import torchvision
from torchvision.models.detection.faster_rcnn import FastRCNNPredictor

num_classes = 3  # 3 classes (bult,nut) + background
model = torchvision.models.detection.fasterrcnn_resnet50_fpn(
    weights=torchvision.models.detection.FasterRCNN_ResNet50_FPN_Weights.COCO_V1)
in_features = model.roi_heads.box_predictor.cls_score.in_features
model.roi_heads.box_predictor = FastRCNNPredictor(in_features, num_classes)

三,训练模型

from torchkeras import KerasModel
class StepRunner:
    def __init__(self, net, loss_fn, accelerator, stage = "train", metrics_dict = None, 
                 optimizer = None, lr_scheduler = None
                 ):
        self.net,self.loss_fn,self.metrics_dict,self.stage = net,loss_fn,metrics_dict,stage
        self.optimizer,self.lr_scheduler = optimizer,lr_scheduler
        self.accelerator = accelerator
        if self.stage=='train':
            self.net.train() 
        else:
            self.net.train() #attention here
    
    def __call__(self, batch):
        features,labels = batch 
        
        #loss
        loss_dict = self.net(features,labels)
        loss = sum(loss_dict.values())
        
        #backward()
        if self.optimizer is not None and self.stage=="train":
            self.accelerator.backward(loss)
            self.optimizer.step()
            if self.lr_scheduler is not None:
                self.lr_scheduler.step()
            self.optimizer.zero_grad()
            
        #all_preds = self.accelerator.gather(preds)
        #all_labels = self.accelerator.gather(labels)
        all_loss = self.accelerator.gather(loss).sum()
        
        #losses
        step_losses = {self.stage+"_loss":all_loss.item()}
        
        #metrics
        step_metrics = {}
        
        if self.stage=="train":
            if self.optimizer is not None:
                step_metrics['lr'] = self.optimizer.state_dict()['param_groups'][0]['lr']
            else:
                step_metrics['lr'] = 0.0
        return step_losses,step_metrics
    
KerasModel.StepRunner = StepRunner
params = [p for p in model.parameters() if p.requires_grad]
optimizer = torch.optim.SGD(params, lr=0.005,
                             momentum=0.9, weight_decay=0.0005)
lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer,T_max=4)

keras_model = KerasModel(model,
                         loss_fn = None,
                         metrics_dict=None,
                         optimizer= optimizer,
                         lr_scheduler=lr_scheduler
                        )

keras_model.fit(train_data=dl_train,val_data=dl_val,
    epochs=20,patience=5,
    monitor='val_loss',
    mode='min',
    ckpt_path ='faster-rcnn.pt',
    plot=True
)

d299f12f273b84c75b01062c0740f2b0.png

024eaa1bd0ac0807dfeea0bdc3963d7c.png

四,评估模型

import torch 

from PIL import Image 
from tqdm import tqdm
from ultralytics.yolo.utils import set_logging
set_logging(verbose=False)
from ultralytics.yolo.utils.metrics import  DetMetrics, box_iou
def process_batch(predictions, targets, 
                  iouv = torch.linspace(0.5, 0.95, 10) # iou vector for mAP@0.5:0.95
                 ):
    ...
    return metrics
model.eval()
list_predictions = [model(x[0].to('cuda')[None,...])[0] for x in ds_val]
list_targets = [x[1] for x in ds_val]

names = {0:'bolt',1:'nut'}
metrics = eval_metrics(list_predictions,
                       list_targets,
                       names =  names)
display(metrics.results_dict)
{'metrics/precision(B)': 0.9976781395819151,
 'metrics/recall(B)': 1.0,
 'metrics/mAP50(B)': 0.995,
 'metrics/mAP50-95(B)': 0.8542317510036526,
 'fitness': 0.8683085759032874}
import pandas as pd 
df = pd.DataFrame()
df['metric'] = metrics.keys
for i,c in names.items():
    df[c] = metrics.class_result(i)
df

9dfe2e3a0af99c86ab178b6e85870002.png

五,使用模型

# 准备数据
inputs = []
img_path = os.path.join(val_images_path,os.listdir(val_images_path)[5])
img = Image.open(img_path).convert("RGB")
img_tensor = torch.from_numpy(np.array(img)/255.).permute(2,0,1).float()
if torch.cuda.is_available():
    img_tensor = img_tensor.cuda()
inputs.append(img_tensor)    

model.eval()

# 预测结果
with torch.no_grad():
    predictions = model(inputs)

# 结果可视化
vis_detection(img,predictions[0],list(idx2names.values()),min_score = 0.8)

38fed89e054034ac169df0251abc66d5.png

公众号 算法美食屋 后台回复关键词:torchkeras,获取本文notebook代码和 bolt nut 数据集 下载地址。

万水千山总是情,点个赞赞行不行?😋😋

### 使用 Faster R-CNN 训练自定义数据集 为了使用 Faster R-CNN 进行目标检测训练自定义数据集,可以按照以下方法操作: #### 1. 准备环境和依赖项 确保安装了必要的 Python 库以及深度学习框架 PyTorch。可以通过 pip 安装所需的包: ```bash pip install torch torchvision torchaudio ``` 此外,还需要准备 COCO API 或类似的工具来处理标注文件。 --- #### 2. 数据集格式化 Faster R-CNN 的输入通常需要遵循特定的格式。常见的做法是将数据转换为 COCO 格式或 Pascal VOC 格式。COCO 格式的 JSON 文件结构如下所示[^3]: ```json { "images": [ {"id": 0, "file_name": "image_0.jpg", ...}, ... ], "annotations": [ {"id": 1, "image_id": 0, "category_id": 0, "bbox": [x_min, y_min, width, height], ...}, ... ], "categories": [ {"id": 0, "name": "object_class"}, ... ] } ``` 如果数据不是这种格式,则需编写脚本将其转化为上述标准形式。 --- #### 3. 创建 Dataset 类 在 PyTorch 中,通过继承 `torch.utils.data.Dataset` 来创建一个新的类用于加载图像及其对应的标签信息。以下是示例代码片段[^4]: ```python import os from PIL import Image import torch from torch.utils.data import Dataset class CustomDataset(Dataset): def __init__(self, root_dir, annotation_file, transforms=None): self.root_dir = root_dir self.annotations = json.load(open(annotation_file)) self.transforms = transforms def __len__(self): return len(self.annotations['images']) def __getitem__(self, idx): img_info = self.annotations['images'][idx] anns = [ann for ann in self.annotations['annotations'] if ann['image_id'] == img_info['id']] image_path = os.path.join(self.root_dir, img_info['file_name']) img = Image.open(image_path).convert('RGB') boxes = [] labels = [] for ann in anns: xmin, ymin, w, h = ann['bbox'] xmax = xmin + w ymax = ymin + h boxes.append([xmin, ymin, xmax, ymax]) labels.append(ann['category_id']) target = {} target["boxes"] = torch.as_tensor(boxes, dtype=torch.float32) target["labels"] = torch.as_tensor(labels, dtype=torch.int64) if self.transforms is not None: img, target = self.transforms(img, target) return img, target ``` 此部分实现了从本地路径读取图片与边界框坐标的功能,并返回张量形式的数据供后续模型调用。 --- #### 4. 加载预训练模型 可以直接基于官方提供的 Faster R-CNN 预训练权重初始化模型。例如,在 PyTorch 中有现成的方法获取这些资源[^2]: ```python import torchvision.models as models def get_model(num_classes): model = models.detection.fasterrcnn_resnet50_fpn(pretrained=True) in_features = model.roi_heads.box_predictor.cls_score.in_features model.roi_heads.box_predictor = models.detection.faster_rcnn.FastRCNNPredictor(in_features, num_classes) return model ``` 这里调整分类器层以适应新的类别数量(包括背景),从而完成迁移学习的过程。 --- #### 5. 设置优化器与损失函数 对于目标检测任务来说,默认情况下会采用 SGD 作为优化策略之一;同时由于 Faster R-CNN 自身已经包含了多任务联合训练机制,因此无需额外指定单独的 Loss Function 表达式。 ```python params = [p for p in model.parameters() if p.requires_grad] optimizer = torch.optim.SGD(params, lr=0.005, momentum=0.9, weight_decay=0.0005) lr_scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=3, gamma=0.1) ``` --- #### 6. 开始训练循环 最后一步就是执行完整的 epoch 循环逻辑,期间不断更新参数直至收敛为止。下面给出简化版伪代码表示整个流程: ```python num_epochs = 10 for epoch in range(num_epochs): train_one_epoch(model, optimizer, data_loader_train, device, epoch, print_freq=10) lr_scheduler.step() evaluate(model, data_loader_val, device=device) ``` 其中涉及到了辅助函数如 `train_one_epoch()` 和评估指标计算等内容均来自 Torchvision 工具箱内部实现细节。 --- ### 总结 综上所述,借助于开源社区的力量加上适当修改即可轻松部署属于自己的定制版本 Faster R-CNN 解决方案[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值