Files
Kaggle/Faster-RCNN/main.py
T

146 lines
6.0 KiB
Python
Raw Normal View History

2026-08-18 23:21:35 +08:00
#这是一个RCNN骨架的搭建 我的水平真的很拉 但是也得逼自己写一下看能不能写出来
import torch,torchvision
from torch import nn
from torchvision.models.detection.image_list import ImageList
from torchvision.models.detection.anchor_utils import AnchorGenerator
#需要传入的参数有 图片 Xbatch3,800,600
class FasteRCNN(nn.Module):
def __init__(self,anchor_sizes,aspect_ratios,ntype):
super(FasteRCNN, self).__init__()
self.ntype=ntype
resnet = torchvision.models.resnet50(pretrained=True)
self.backbone = nn.Sequential(*list(resnet.children())[:-2]) # 去掉avgpool和fc
self.anchors_size=9
self.anchor_generator = AnchorGenerator(anchor_sizes, aspect_ratios)
self.RPN = RegionProposalNetwork(self.anchors_size)
self.roihead = ROIhead(self.ntype)
def forward(self, X):
features = self.backbone(X)
# 现在的大小就是(batch 2048 19 25)了
image_list = ImageList(X, [(X.shape[2], X.shape[3])] * X.shape[0])
anchors = self.anchor_generator(image_list, [features])
predbias,predclassify=self.RPN(features)
proposals = generate_proposals(anchors, predclassify, predbias, image_list.image_sizes)
return self.roihead(features,proposals)
class RegionProposalNetwork(nn.Module):
#anchors:anchorbox个数
def __init__(self,anchors):
super(RegionProposalNetwork, self).__init__()
#先做一个3x3的conv
self.conv1 = nn.Conv2d(2048, 2048, kernel_size=3, padding=1)
self.relu = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(2048,4*anchors , kernel_size=1)
self.conv3 = nn.Conv2d(2048, 2*anchors, kernel_size=1)
def forward(self,features):
features = self.relu(self.conv1(features))
#第一个分支生成偏移量
predbias = self.conv2(features)
#第二个分支判断是否为背景
predclassify = self.conv3(features)
B, _, H, W = predbias.shape
predbias = predbias.permute(0, 2, 3, 1).reshape(B, -1, 4)
predclassify = predclassify.permute(0, 2, 3, 1).reshape(B, -1, 2)
return predbias, predclassify
from torchvision.ops import nms
def generate_proposals(anchors, predclassify, predbias, image_sizes):
"""
anchors: List[Tensor],每个特征图对应一个 Tensor,形状 [A_i*H_i*W_i, 4]
predclassify: Tensor [B, total_anchors, 2] (尚未 softmax
predbias: Tensor [B, total_anchors, 4]
image_sizes: List[Tuple[int, int]],每张图片的 (H, W)
"""
# 1. 将所有层级的 anchor 合并成一个大的 Tensor
# 2. 对 predclassify 做 softmax,取前景分数(索引1)
scores = torch.softmax(predclassify, dim=-1)[:, :, 1] # [B, total_anchors]
batch_size = predclassify.shape[0]
proposals = []
for i in range(batch_size):
# 当前图片的 anchor 和预测值
cur_anchors = anchors[0]
deltas = predbias[i] # [total_anchors, 4]
score_i = scores[i] # [total_anchors]
# 3. 将偏移量应用到 anchor 上(中心点格式转换)
# anchor 格式: (x1, y1, x2, y2)
width = cur_anchors[:, 2] - cur_anchors[:, 0]
height = cur_anchors[:, 3] - cur_anchors[:, 1]
ctr_x = cur_anchors[:, 0] + 0.5 * width
ctr_y = cur_anchors[:, 1] + 0.5 * height
dx = deltas[:, 0]
dy = deltas[:, 1]
dw = deltas[:, 2]
dh = deltas[:, 3]
pred_ctr_x = dx * width + ctr_x
pred_ctr_y = dy * height + ctr_y
pred_w = torch.exp(dw) * width
pred_h = torch.exp(dh) * height
# 转回 (x1, y1, x2, y2)
pred_x1 = pred_ctr_x - 0.5 * pred_w
pred_y1 = pred_ctr_y - 0.5 * pred_h
pred_x2 = pred_ctr_x + 0.5 * pred_w
pred_y2 = pred_ctr_y + 0.5 * pred_h
boxes = torch.stack([pred_x1, pred_y1, pred_x2, pred_y2], dim=1)
# 4. 裁剪到图像边界
H, W = image_sizes[i]
boxes[:, 0] = torch.clamp(boxes[:, 0], min=0, max=W)
boxes[:, 1] = torch.clamp(boxes[:, 1], min=0, max=H)
boxes[:, 2] = torch.clamp(boxes[:, 2], min=0, max=W)
boxes[:, 3] = torch.clamp(boxes[:, 3], min=0, max=H)
# 5. 剔除宽高 <= 0 的框
keep = (boxes[:, 2] > boxes[:, 0]) & (boxes[:, 3] > boxes[:, 1])
boxes = boxes[keep]
score_i = score_i[keep]
# 6. 按分数排序取 top-N(训练时取 12000,测试取 6000)
pre_nms_top_n = 12000 # 可以根据 self.training 调整,但函数里无法获取,可以传入参数
if len(score_i) > pre_nms_top_n:
topk = torch.topk(score_i, pre_nms_top_n)
boxes = boxes[topk.indices]
score_i = score_i[topk.indices]
# 7. NMS(阈值 0.7
keep = nms(boxes, score_i, 0.7)
boxes = boxes[keep]
score_i = score_i[keep]
# 8. 最终取 top-N(训练取 2000,测试取 1000
post_nms_top_n = 2000
if len(score_i) > post_nms_top_n:
topk = torch.topk(score_i, post_nms_top_n)
boxes = boxes[topk.indices]
# score_i 可以丢弃,proposal 只需框
proposals.append(boxes)
return proposals # List[Tensor],每个 Tensor 形状 [N_i, 4]
import torchvision.ops.roi_align as roi_align
class ROIhead(nn.Module):
def __init__(self,ntype):
super(ROIhead, self).__init__()
self.conv1 = nn.Conv2d(2048, 512, kernel_size=3,padding=1)
self.fc1 = nn.Linear(512*7*7,4096)
self.fc2 = nn.Linear(4096,4096)
self.fc3_1 = nn.Linear(4096,ntype+1)
self.fc3_2 = nn.Linear(4096,(ntype+1)*4)
self.relu = nn.ReLU()
def forward(self,features ,proposals):
X=roi_align(features,proposals,output_size=7,spatial_scale=1.0/32)
X=self.conv1(X)
#(B,512,7,7)->(B,512*7*7)
X=X.flatten(start_dim=1,end_dim=3)
X = self.relu(self.fc2(self.relu(self.fc1(X))))
return self.fc3_1(X),self.fc3_2(X).reshape(features.shape[0],-1,4)
model = FasteRCNN(anchor_sizes = ((32, 64, 128),),aspect_ratios = ((0.5, 1.0, 2.0),),ntype=20)