PyTorch卷积网络如何动态设置全连接层in_features?
解决PyTorch中CNN全连接层in_features动态确定的问题
在调整CNN超参数(如卷积核大小、通道数、dropout率)时,硬编码全连接层的in_features会非常繁琐,以下是几种无需硬编码的解决方案:
方法1:提前计算输入维度
根据输入序列长度、卷积和池化的参数,手动计算经过所有卷积池化操作后的扁平化维度,直接在初始化阶段确定全连接层的输入特征数。
import torch class Conv_v1(torch.nn.Module): def __init__(self, input_seq_len, in_channels=4, out_channels1=3, kernel_size1=17, pool_kernel_size=5, dropout_rate=0.5, num_classes=2): super(Conv_v1, self).__init__() self.conv1 = torch.nn.Conv1d(in_channels=in_channels, out_channels=out_channels1, kernel_size=kernel_size1) self.activation = torch.nn.ReLU() self.maxpool = torch.nn.MaxPool1d(kernel_size=pool_kernel_size) self.dropout = torch.nn.Dropout(p=dropout_rate) # 计算卷积池化后的扁平化维度 conv_out_len = input_seq_len - kernel_size1 + 1 pool_out_len = conv_out_len // pool_kernel_size # 若池化后有剩余可根据需求调整计算方式 fc_in_features = out_channels1 * pool_out_len self.fc = torch.nn.Linear(in_features=fc_in_features, out_features=num_classes) def forward(self, x): x = x.permute(0, 2, 1) x = self.conv1(x) x = self.activation(x) x = self.maxpool(x) x = x.view(x.size(0), -1) x = self.dropout(x) x = self.fc(x) return x
使用时只需传入输入序列的长度,超参数调整时自动适配全连接层的输入维度。
方法2:使用PyTorch的LazyLinear模块
PyTorch 1.8.0及以上版本提供了LazyLinear模块,无需提前指定in_features,第一次前向传播时会自动根据输入维度初始化权重,彻底省去手动计算的步骤。
import torch class Conv_v2(torch.nn.Module): def __init__(self, out_channels1=3, kernel_size1=17, pool_kernel_size=5, dropout_rate=0.5, num_classes=2): super(Conv_v2, self).__init__() self.conv1 = torch.nn.Conv1d(in_channels=4, out_channels=out_channels1, kernel_size=kernel_size1) self.activation = torch.nn.ReLU() self.maxpool = torch.nn.MaxPool1d(kernel_size=pool_kernel_size) self.dropout = torch.nn.Dropout(p=dropout_rate) # 使用LazyLinear,无需指定in_features self.fc = torch.nn.LazyLinear(out_features=num_classes) def forward(self, x): x = x.permute(0, 2, 1) x = self.conv1(x) x = self.activation(x) x = self.maxpool(x) x = x.view(x.size(0), -1) x = self.dropout(x) x = self.fc(x) return x
该模块会在第一次前向传播后固定权重维度,后续不会重新初始化,保证模型正常学习。
方法3:第一次前向传播时动态初始化全连接层
如果不想依赖Lazy模块,可以在第一次前向传播时计算输入维度,创建全连接层并将其参数注册到模型中,确保仅初始化一次。
import torch class Conv_v3(torch.nn.Module): def __init__(self, out_channels1, kernel_size1, out_channels2, kernel_size2, dropout_rate, num_classes=2): super(Conv_v3, self).__init__() self.conv1 = torch.nn.Conv1d(in_channels=4, out_channels=out_channels1, kernel_size=kernel_size1) self.conv2 = torch.nn.Conv1d(in_channels=out_channels1, out_channels=out_channels2, kernel_size=kernel_size2) self.dropout = torch.nn.Dropout(p=dropout_rate) self.fc = None # 先初始化为None self.num_classes = num_classes def forward(self, x): x = x.permute(0, 2, 1) x = torch.nn.functional.max_pool1d(torch.tanh(self.conv1(x)), 2) x = torch.nn.functional.max_pool1d(torch.tanh(self.conv2(x)), 2) x = x.view(x.size(0), -1) x = self.dropout(x) # 第一次前向时初始化fc层并注册参数 if self.fc is None: self.fc = torch.nn.Linear(in_features=x.size(1), out_features=self.num_classes) self.add_module('fc', self.fc) out = self.fc(x) return out
通过add_module将全连接层注册到模型后,优化器就能正常更新其权重,避免了重复初始化的问题。
内容的提问来源于stack exchange,提问作者Marina Thalassini
相关产品推荐
相关产品推荐

