Похоже, что исходная версия кода, использованного вами в этом вопросе, вела себя иначе. Последняя версия кода, который у вас здесь, дает мне ошибку, отличную от той, которую вы опубликовали, а точнее - это:
RuntimeError: Calculated padded input size per channel: (20 x 1). Kernel size: (3 x 768). Kernel size can't be greater than actual input size
Прошу прощения, если я неправильно понял ситуацию, но мне кажется, что ваше понимание Что именно делает слой nn.Conv2d, не на 100% понятно, и это главный источник вашей борьбы. Я интерпретирую запрошенную вами часть «подробное объяснение двухслойной CNN в Pytorch» как просьбу подробно объяснить, как работает этот слой, и надеюсь, что после того, как это будет сделано, не возникнет проблем с его применением 1, 2 или более раз. .
Вы можете найти всю документацию по слою здесь , но позвольте мне дать вам резюме, которое, надеюсь, поможет лучше понять ошибки, которые вы получаете. Прежде всего, nn.Conv2d
входы - это 4-мерные тензоры формы (BatchSize, ChannelsIn, Height, Width)
, а выходы - 4-мерные тензоры формы (BatchSize, ChannelsOut, HeightOut, WidthOut)
. Самый простой способ представить себе nn.Conv2d
- это что-то, применяемое к 2d-изображениям с пиксельной сеткой размером Height x Width
и имеющей ChannelsIn
разных цветов или функций на пиксель. Даже если ваши входные данные не имеют ничего общего с реальными изображениями, поведение слоя остается прежним. Самая простая ситуация - когда nn.Conv2d
не использует заполнение (как в вашем коде). В этом случае аргумент kernel_size=(kernel_height, kernel_width)
указывает прямоугольник, который вы можете представить, проходя через прямоугольник Height x Width
ваших входных данных и создавая один пиксель для каждой допустимой позиции. Без заполнения координата точки прямоугольника может быть любой парой знаков (x, y)
с x между 0
и Height - kernel_height
и y между 0
и Width - kernel_width
. Таким образом, вывод будет выглядеть как двумерное изображение размером (Height - kernel_height + 1) x (Width - kernel_width + 1)
и будет иметь столько выходных каналов, сколько указано в конструкторе nn.Conv2d
, поэтому выходной тензор будет иметь форму (BatchSize, ChannelsOut, Height - kernel_height + 1, Width - kernel_width + 1)
.
Параметр groups
не влияет на то, как формы изменяются слоем - это только управление тем, какие входные каналы используются в качестве входов для выходных каналов (groups=1
означает, что каждый входной канал используется как вход для каждого выходного канала, в противном случае вход и выход каналы разделены на соответствующее количество групп, и только входные каналы из группы i
используются как входы для выходных каналов из группы i
).
Теперь в вашей текущей версии кода у вас есть BatchSize = 16, а на выходе предварительно обученной модели - (BatchSize, DynamicSize, 768)
с DynamicSize
в зависимости от ввода, например 22. Затем вы вводите дополнительное измерение в качестве оси 1 с помощью unsqueeze
и повторяете значения вдоль этого измерения, преобразуя тензор формы (16, 22, 768)
в (16, 12, 22, 768)
. Фактически вы используете выходные данные предварительно обученной модели как 12-канальные (с каждым каналом, имеющим те же значения, что и другие) здесь 2-мерные изображения размером (22, 768)
, где 22 не фиксировано (зависит от пакета). Затем вы применяете nn.Conv2d с размером ядра (3, 768)
- это означает, что нет "места для маневра" для ширины, и выводимые двумерные изображения будут иметь размер (20, 1)
, а поскольку ваш слой имеет 192 канала, окончательный размер Выход первого сверточного слоя имеет вид (16, 192, 20, 1)
. Затем вы снова пытаетесь применить второй слой свертки поверх этого с размером ядра (3, 768)
, но, поскольку ваше двумерное «изображение» теперь всего лишь (20 x 1), нет допустимого положения для размещения (3, 768)
прямоугольника ядра. внутри прямоугольника (20 x 1)
, что приводит к сообщению об ошибке Kernel size can't be greater than actual input size
.
Надеюсь, это объяснение поможет. Теперь о вариантах, которые вам нужно избежать:
- (a) - это добавить отступы таким образом, чтобы размер вывода не изменялся по сравнению с вводом (я не буду go подробнее здесь, потому что я не думаю, что это то, что вам нужно)
- (b) Используйте меньшее ядро как для первой, так и / или второй сверток (например, если вы не меняете первую свертку, единственно допустимую ширина для второго ядра будет
1
). - (c) Глядя на то, что вы пытаетесь сделать, я предполагаю, что вы на самом деле не хотите использовать 2-мерную свертку, вы хотите 1d свертка (в последовательности) с каждой позицией, описываемой 768 значениями. Когда вы используете один слой свертки с ядром шириной 768 (и такой же входной шириной 768), вы фактически делаете то же самое, что и 1d свертка с 768 входными каналами, но затем, если вы попытаетесь применить второй, у вас возникнет проблема. Вы можете указать ширину ядра как
1
для следующего слоя (слоев), и это будет работать для вас, но более правильным способом было бы транспонировать выходной тензор предварительно обученной модели, переключив последние измерения - получив форму (16, 768, DynamicSize)
из (16, DynamicSize, 768)
, а затем примените слой nn.Conv1d с 768 входными каналами и произвольными ChannelsOut
в качестве выходных каналов и 1d kernel_size=3
(то есть вы смотрите на 3 последовательных элемента последовательности для свертки). Если вы сделаете это, то без заполнения входная форма (16, 768, DynamicSize)
станет (16, ChannelsOut, DynamicSize-2)
, а после применения второго Conv1d, например, с теми же настройками, что и первый, вы получите тензор формы (16, ChannelsOut, DynamicSize-4)
, et c. (каждый раз длина 1d будет уменьшаться на kernel_size-1
). Вы всегда можете изменить количество каналов / размер ядра для каждого последующего слоя свертки.