| import torch |
| import torch.nn as nn |
| import torch.nn.functional as F |
| from torch.quantization import QuantStub, DeQuantStub, fuse_modules |
| class ConvBNReLU(nn.Sequential): |
| """ |
| 三个层在计算过程中应当进行融合 |
| 使用ReLU作为激活函数可以限制 |
| 数值范围,从而有利于量化处理。 |
| """ |
| def __init__(self, n_in, n_out, |
| kernel_size=3, stride=1, |
| groups=1, norm_layer=nn.BatchNorm2d): |
| |
| padding = (kernel_size - 1) // 2 |
| |
| super(ConvBNReLU, self).__init__( |
| nn.Conv2d(n_in, n_out, [1, kernel_size], |
| stride, [0, padding], groups=groups, |
| bias=False), |
| nn.BatchNorm2d(n_out), |
| nn.ReLU(inplace=True) |
| ) |
|
|
| class QInvertedResidual(nn.Module): |
| """ |
| 本个模块为MobileNetV2中的可分离卷积层 |
| 中间带有扩张部分,如图10-2所示 |
| """ |
| def __init__(self, n_in, n_out, |
| stride, expand_ratio, norm_layer=nn.BatchNorm2d): |
| super().__init__() |
| self.stride = stride |
| self.conv = ConvBNReLU(n_in, n_out, 5, stride=stride) |
| if n_in == n_out and stride==1: |
| self.use_res = True |
| else: |
| self.use_res = False |
| def forward(self, x): |
| if self.use_res: |
| return x + self.conv(x) |
| else: |
| return self.conv(x) |
|
|
| class ConvTBNReLU(nn.Sequential): |
| """ |
| 三个层在计算过程中应当进行融合 |
| 使用ReLU作为激活函数可以限制 |
| 数值范围,从而有利于量化处理。 |
| """ |
| def __init__(self, n_in, n_out, |
| kernel_size=3, stride=1, padding=1, output_padding=1, bias=True, dilation=1, |
| groups=1, norm_layer=nn.BatchNorm2d): |
| |
| |
| super(ConvTBNReLU, self).__init__( |
| nn.UpsamplingNearest2d(scale_factor=tuple(stride)), |
| QInvertedResidual(n_in, n_out, 1, 2), |
| ) |
|
|
| class Model(nn.Module): |
| def __init__(self, n_stride=8): |
| super().__init__() |
| self.n_stride = n_stride |
| F = 16 |
| self.layers = nn.Sequential( |
| ConvBNReLU(3, F*2**0), |
| QInvertedResidual(F*2**0, F*2**1, 2, 2), |
| QInvertedResidual(F*2**1, F*2**1, 1, 2), |
| QInvertedResidual(F*2**1, F*2**2, 2, 2), |
| QInvertedResidual(F*2**2, F*2**2, 1, 2), |
| QInvertedResidual(F*2**2, F*2**3, 2, 2), |
| QInvertedResidual(F*2**3, F*2**3, 1, 2) |
| ) |
| self.class_encoder = nn.Sequential( |
| QInvertedResidual(F*2**3, F*2**3, 2, 2), |
| QInvertedResidual(F*2**3, F*2**3, 2, 2), |
| QInvertedResidual(F*2**3, F*2**3, 2, 2), |
| ConvTBNReLU(F*2**3, F*2**3, [1, 5], stride=[1, 2], padding=[ |
| 0, 2], output_padding=[0, 1], bias=False, dilation=1), |
| ConvTBNReLU(F*2**3, F*2**3, [1, 5], stride=[1, 2], padding=[ |
| 0, 2], output_padding=[0, 1], bias=False, dilation=1), |
| ConvTBNReLU(F*2**3, F*2**3, [1, 5], stride=[1, 2], padding=[ |
| 0, 2], output_padding=[0, 1], bias=False, dilation=1), |
| ) |
| self.cl = nn.Conv2d(F * 2 ** 3 * 2, 3, 1) |
| self.tm = nn.Conv2d(F * 2 ** 3 * 2, 1, 1) |
| self.quant = torch.quantization.QuantStub() |
| self.dequant = torch.quantization.DeQuantStub() |
| self.qfunc = nn.quantized.FloatFunctional() |
|
|
| def fuse_model(self): |
| for m in self.modules(): |
| if type(m) == ConvBNReLU: |
| fuse_modules(m, ['0', '1', '2'], inplace=True) |
| |
| |
| |
| |
|
|
| def forward(self, x): |
| x = x.unsqueeze(2) |
| x1 = self.layers(x) |
| x2 = self.class_encoder(x1) |
| x = torch.cat([x1, x2], dim=1) |
| out_class = self.cl(x).squeeze(dim=2) |
| out_time = self.tm(x) |
| out_time = out_time.sigmoid().squeeze() * self.n_stride |
| if self.training: |
| return out_class, out_time |
| else: |
| out_class = F.softmax(out_class, dim=1) |
| return out_class, out_time |
|
|
| class Loss(nn.Module): |
| """损失函数""" |
| def __init__(self): |
| super().__init__() |
| self.mse = nn.MSELoss(reduction="none") |
| |
| self.ce = nn.CrossEntropyLoss(reduction="sum", ignore_index=-1) |
| def forward(self, pred, label): |
| pclass, ptime = pred |
| dclass = label[:, 0, :].long() |
| dtime = label[:, 1, :].float() |
| loss_class = self.ce(pclass, dclass) |
| loss_time_none = self.mse(ptime, dtime) * (dclass).clamp(0, 1).float() |
| loss_time = loss_time_none.sum() |
| loss = loss_class + loss_time / 10 |
| return loss |