from torchvision.models import resnet34
import torch.nn as nn
import torch
class YOLOv1_resnet(nn.Module):
def __init__(self, num_box, class_num):
super(YOLOv1_resnet,self).__init__()
self.num_box = num_box
self.class_num = class_num
resnet = resnest_model()
# 调用torchvision里的resnet34预训练模型
resnet_out_channel = resnet.fc.in_features
# 记录resnet全连接层之前的网络输出通道数,方便连入后续卷积网络中
self.resnet = nn.Sequential(*list(resnet.children())[:-2])
# 去除resnet的最后两层
'''定义YOLO的最后的卷积层'''
self.conv_layer = nn.Sequential(
nn.Conv2d(resnet_out_channel,1024,3,padding=1),
nn.BatchNorm2d(1024),
nn.LeakyReLU(),
nn.Conv2d(1024,1024,3,stride=2,padding=1),
nn.BatchNorm2d(1024),
nn.LeakyReLU(),
nn.Conv2d(1024, 1024, 3, padding=1),
nn.BatchNorm2d(1024),
nn.LeakyReLU(),
)
'''全连接层,三维变量拉平进行处理'''
self.dense_layers = nn.Sequential(
nn.Linear(7*7*1024,4096),
nn.LeakyReLU(),
nn.Linear(4096,7*7*30))
def forward(self, x):
out = self.resnet(x)
out = self.conv_layer(out)
out = out.view(out.size()[0],-1)
out = self.dense_layers(out)
return out.reshape(-1, (5*self.num_box+self.class_num), 7, 7)
def set_parameter_requires_grad(model):
for param in model.parameters():
param.requires_grad = False
# ResNeSt模型
def resnest_model():
model_ft = resnet34(pretrained=True)
set_parameter_requires_grad(model_ft)
num_ftrs = model_ft.fc.in_features
### 问题:冻结参数是对已有的层,新定义的层不冻结?
return model_ft
if __name__ == '__main__':
x = torch.randn((1,3,448,448))
net = YOLOv1_resnet(2, 20)
print(net)
y = net(x)
print(y.size())
def box_iou(boxes1, boxes2):
'''
args:
boxes1: tensor
[num_boxes, 4]
boxes2: 同上
'''
box_area = lambda boxes:((boxes[:,2] - boxes[:,0]) * (boxes[:,3] - boxes[:,1]))
areas1 = box_area(boxes1)
areas2 = box_area(boxes2)
inter_upperlefts = torch.max(boxes1[:, None, :2], boxes2[:, :2])
inter_lowerrights = torch.min(boxes1[:, None, 2:], boxes2[: 2:])
inters = (inter_lowerrights - inter_upperlefts).clamp(min=0)
inter_areas = inters[:, :, 0] * inters[:, :, 1]
union_areas = areas1[:, None] + areas2 - inter_areas
return inter_areas / union_areas
class Loss_yolov1(nn.Module):
def __init__(self):
super(Loss_yolov1,self).__init__()
def box_center_to_conner(self, boxes, n, m, grid_num):
"""转换 YOLO v1的bounding box
从中间,宽度,高度)转换(左上,右下)"""
cx, cy, w, h = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
x1 = (cx + n)/grid_num - w/2
y1 = (cy + m)/grid_num - h/2
x2 = (cx + n)/grid_num + w/2
y2 = (cy + m)/grid_num + w/2
boxes = torch.stack((x1, y1, x2, y2), axis=-1)
return boxes
def forward(self, pred, labels):
"""
:param pred: (batchsize,30,7,7)的网络输出数据
:param labels: (batchsize,30,7,7)的样本标签数据
:return: 当前批次样本的平均损失
"""
num_gridx, num_gridy = labels.size()[-2:] # 划分网格数量
num_b = 2 # 每个网格的bbox数量
num_cls = 20 # 类别数量
noobj_confi_loss_2 = 0. # 不含目标的网格损失(只有置信度损失)
coor_loss = 0. # 含有目标的bbox的坐标损失
obj_confi_loss = 0. # 含有目标的bbox的置信度损失
class_loss = 0. # 含有目标的网格的类别损失
n_batch = labels.size()[0] # batchsize的大小
iloss_1 = 0.
for i in range(n_batch): # batchsize循环
for n in range(7): # x方向网格循环
for m in range(7): # y方向网格循环
if labels[i,4,m,n]==1:#如果包含物体
bbox1_pred_xyxy = self.box_center_to_conner(
pred[i,:4,m,n], n, m, num_gridx)
bbox2_pred_xyxy = self.box_center_to_conner(
pred[i,4:8,m,n], n, m, num_gridx)
bbox_gt_xyxy = self.box_center_to_conner(
labels[i,:4,m,n], n, m, num_gridx)
iou1 = box_iou(bbox1_pred_xyxy,bbox_gt_xyxy)
iou2 = box_iou(bbox2_pred_xyxy,bbox_gt_xyxy)
# 选择iou大的bbox作为负责物体
box_pred = pred[i, :4, m, n] if iou1 >= iou2 else pred[i, 4:8, m, n]
icoor_loss = (
torch.sum((box_pred[0:2,:] - labels[i,0:2,m,n])**2) + torch.sum((pred[i,2:4,m,n].sqrt()-labels[i,2:4,m,n].sqrt())**2))
iobj_confi_loss = (pred[i,4,m,n] - iou1)**2
# iou比较小的bbox不负责预测物体,因此confidence loss算在noobj中,注意,对于标签的置信度应该是iou2
inoobj_confi_loss_1 = ((pred[i,9,m,n]-iou2)**2)
iclass_loss = torch.sum((pred[i,10:,m,n] - labels[i,10:,m,n])**2)
iloss_1 += 5 * icoor_loss + iobj_confi_loss + iclass_loss + 0.5*inoobj_confi_loss_1
else: # 如果不包含物体
noobj_confi_loss_2 += 0.5 * torch.sum(pred[i,[4,9],m,n]**2)
loss = iloss_1 + 0.5 * noobj_confi_loss_2
# 此处可以写代码验证一下loss的大致计算是否正确,这个要验证起来比较麻烦,比较简洁的办法是,将输入的pred置为全1矩阵,再进行误差检查,会直观很多。
return loss/n_batch
###############
### 验证代码 ###
###############
loss_fn = Loss_yolov1()
pred = torch.randn((4,30,7,7))
label = torch.randn((4,30,7,7))
loss = loss_fn(pred, label)
print(loss)