Как правильно задать размер входного изображения для модели LPRNet? ‘Ошибка времени выполнения: Размеры тензоров должны совпадать, за исключением измерения 1..’

#python #computer-vision #pytorch

Вопрос:

Пытаюсь адаптировать модель LPRNet для моего собственного набора данных. Оригинальная модель была установлена для изображений китайских номерных знаков размером 24×94 пикселя. Мой набор данных состоит из табличек только с номерами, изображения имеют размеры 64×128. Я получил ошибку при попытке заменить»сводка(lprnet, (3,24,94), устройство=» процессор») » на » сводка(lprnet, (3,64,128), устройство=»процессор»)». Первый код работает хорошо, второй получает ошибку времени выполнения: размеры тензоров должны совпадать, за исключением измерения 1. Получил 25 и 24 в измерении 3 (индекс нарушения равен 1)», это последняя строка кода. Я не вижу, где еще в коде я должен изменять параметры. Буду благодарен за любую подсказку! Оригинал: https://github.com/xuexingyu24/License_Plate_Detection_Pytorch/blob/master/LPRNet/model/LPRNET.py

 # LPRNET model
import torch
import torch.nn as nn
from torchsummary import summary

class small_basic_block(nn.Module):
    def __init__(self, ch_in, ch_out):
        super(small_basic_block, self).__init__()
        self.block = nn.Sequential(
            nn.Conv2d(ch_in, ch_out // 4, kernel_size=1),
            nn.ReLU(),
            nn.Conv2d(ch_out // 4, ch_out // 4, kernel_size=(3, 1), padding=(1, 0)),
            nn.ReLU(),
            nn.Conv2d(ch_out // 4, ch_out // 4, kernel_size=(1, 3), padding=(0, 1)),
            nn.ReLU(),
            nn.Conv2d(ch_out // 4, ch_out, kernel_size=1),
        )
    def forward(self, x):
        return self.block(x)

class LPRNet(nn.Module):
    def __init__(self, class_num, dropout_rate):
        super(LPRNet, self).__init__()
        self.class_num = class_num
        self.backbone = nn.Sequential(
            nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, stride=1), # 0
            nn.BatchNorm2d(num_features=64),
            nn.ReLU(),  # 2
            nn.MaxPool3d(kernel_size=(1, 3, 3), stride=(1, 1, 1)),
            small_basic_block(ch_in=64, ch_out=128),    # *** 4 ***
            nn.BatchNorm2d(num_features=128),
            nn.ReLU(),  # 6
            nn.MaxPool3d(kernel_size=(1, 3, 3), stride=(2, 1, 2)),
            small_basic_block(ch_in=64, ch_out=256),   # 8
            nn.BatchNorm2d(num_features=256),
            nn.ReLU(),  # 10
            small_basic_block(ch_in=256, ch_out=256),   # *** 11 ***
            nn.BatchNorm2d(num_features=256),   # 12
            nn.ReLU(),
            nn.MaxPool3d(kernel_size=(1, 3, 3), stride=(4, 1, 2)),  # 14
            nn.Dropout(dropout_rate),
            nn.Conv2d(in_channels=64, out_channels=256, kernel_size=(1, 4), stride=1),  # 16
            nn.BatchNorm2d(num_features=256),
            nn.ReLU(),  # 18
            nn.Dropout(dropout_rate),
            nn.Conv2d(in_channels=256, out_channels=class_num, kernel_size=(13, 1), stride=1), # 20
            nn.BatchNorm2d(num_features=class_num),
            nn.ReLU(),  # *** 22 ***
        )
        self.container = nn.Sequential(
            nn.Conv2d(in_channels=256 class_num 128 64, out_channels=self.class_num, kernel_size=(1,1), stride=(1,1)),
            # nn.BatchNorm2d(num_features=self.class_num),
            # nn.ReLU(),
            # nn.Conv2d(in_channels=self.class_num, out_channels=self.lpr_max_len 1, kernel_size=3, stride=2),
            # nn.ReLU(),
        )

    def forward(self, x):
        keep_features = list()
        for i, layer in enumerate(self.backbone.children()):
            x = layer(x)
            if i in [2, 6, 13, 22]: # [2, 4, 8, 11, 22]
                keep_features.append(x)

        global_context = list()
        for i, f in enumerate(keep_features):
            if i in [0, 1]:
                f = nn.AvgPool2d(kernel_size=5, stride=5)(f)
            if i in [2]:
                f = nn.AvgPool2d(kernel_size=(4, 10), stride=(4, 2))(f)
            f_pow = torch.pow(f, 2)
            f_mean = torch.mean(f_pow)
            f = torch.div(f, f_mean)
            global_context.append(f)

        x = torch.cat(global_context, 1)
        x = self.container(x)
        logits = torch.mean(x, dim=2)

        return logits
    
CHARS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']
    
if __name__ == "__main__":
     
    lprnet = LPRNet(class_num=len(CHARS), dropout_rate=0)
    print(lprnet)
    
    summary(lprnet, (3,24,94), device="cpu")
    #summary(lprnet, (3,64,128), device="cpu")
 

Ответ №1:

Проблема с вашим кодом заключается в том, что формы четырех тензоров в global_context различаются по размеру ввода (64, 128). Для фигуры (24, 94), в LRPNet , авторы делают все тензоры (4, 18) размером со средним объединением, которое не относится к размеру вашего изображения. Чтобы сделать их одинакового размера, вам необходимо изменить конфигурации nn.AvgPool2d при перечислении keep_features . Вы можете обновлять их различными способами. Я обновил avgpool с приведенными ниже конфигурациями, и это сработало:

             if i in [0, 1]:
                f = nn.AvgPool2d(kernel_size=(11, 24), stride=(7, 4))(f)

            elif i == 2:
                f = nn.AvgPool2d(kernel_size=(9, 11), stride=(7, 2))(f)
            elif i == 3:
                f = nn.AvgPool2d(kernel_size=(7, 2), stride=(5, 1))(f)
            f_pow = torch.pow(f, 2)
            f_mean = torch.mean(f_pow)
            f = torch.div(f, f_mean)
            global_context.append(f)```
 

Комментарии:

1. Большое тебе спасибо, Лабиба! Это работает и для меня, не могли бы вы объяснить более подробно, как вы нашли решение? Я пытаюсь понять, как входной размер (24,94) связан с размером тензоров (4,18). И как вы узнали, что эти тензоры имеют размер (4,18)?

2. Во-первых, я получил формы тензоров global_context. В исходном коде они принимают все тензоры global_context для формирования (4, 18), выполняя среднее объединение. Однако в вашем случае фигуры были больше. Чтобы сделать их все (4, 18), потребуется сжать много информации. Поэтому я изменил все фигуры на (8, 26), выполнив усреднение. Выполняя среднее объединение, чтобы определить размер ядра и скорость выполнения каждой операции, я просто следовал математике для H_{out} и W_{out} с этой страницы: pytorch.org/docs/stable/generated/… Надеюсь, это поможет.

3. Это действительно помогает, большое вам спасибо за объяснение. Я использовал эти формулы и для решения другой проблемы!