You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何拼接ResNet输出与原始图像尺寸?PyTorch可行性及实现方法

方案合理性判断
  • 该方案适配你的数据集场景,具备可行性:预训练ResNet要求输入图像统一缩放至固定尺寸(通用配置为224×224),该预处理步骤会完全抹除原始图像的尺寸差异信息。你提到不同类别间平均尺寸差达10倍量级,原始尺寸本身就是和类别强关联的判别信号,作为补充特征加入可以降低卷积层的学习负担,理论上能带来分类精度提升。
  • 实现时需要注意3个问题,避免引入反效果:
    • 尺寸特征必须做归一化:禁止直接将原始宽高数值(如25、250)直接拼入特征向量,建议将宽、高分别除以训练集所有样本的最大宽度、最大高度,将数值压缩至0~1区间;如果尺寸跨度呈现量级差异,可以先对宽高做log变换再归一化,避免特征数值分布偏移干扰模型训练。
    • 注意规避数据集偏差风险:如果尺寸差异来自采集偏差(比如某一类样本全部是小尺寸截图,而非目标本身固有尺寸属性),加入尺寸特征可能导致模型过拟合到数据集偏差、跨域泛化性下降,建议通过消融实验对比加特征前后的验证集精度,确认特征有效性。
    • 尺寸特征维度不宜过多:一般使用「归一化宽度、归一化高度」2维特征即可,也可根据需求补充宽高比、图像面积特征,总维度控制在2~4维即可,冗余特征反而会干扰分类决策。
PyTorch实现步骤

整体实现逻辑分为3步:修改数据集类返回归一化尺寸特征、改造ResNet18结构适配拼接后的特征维度、调整训练循环的输入逻辑。

  1. 自定义数据集,在返回预处理后图像、标签的同时,返回归一化的原始尺寸特征:
from torch.utils.data import Dataset
from torchvision import transforms
from PIL import Image
import torch

class CustomImageDataset(Dataset):
    def __init__(self, file_paths, labels, max_w, max_h):
        self.file_paths = file_paths
        self.labels = labels
        # 提前统计训练集最大宽、高,用于尺寸特征归一化
        self.max_w = max_w
        self.max_h = max_h
        # ResNet标准图像预处理流程
        self.img_transform = transforms.Compose([
            transforms.Resize((224,224)),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        ])

    def __len__(self):
        return len(self.labels)

    def __getitem__(self, idx):
        img = Image.open(self.file_paths[idx]).convert('RGB')
        # 记录原始图像尺寸(宽、高)
        orig_w, orig_h = img.size
        # 生成归一化尺寸特征
        size_feat = torch.tensor([orig_w / self.max_w, orig_h / self.max_h], dtype=torch.float32)
        img_tensor = self.img_transform(img)
        label = self.labels[idx]
        return img_tensor, size_feat, label
  1. 改造ResNet18结构,截获卷积层输出的512维图像特征,与尺寸特征拼接后送入新的全连接层分类:
import torch
import torch.nn as nn
from torchvision import models

class ResNet18WithSizeFeat(nn.Module):
    def __init__(self, num_classes=17, size_feat_dim=2):
        super().__init__()
        # 加载预训练ResNet18
        backbone = models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
        # 拆分出特征提取部分(去掉原最后一层全连接层),输出为512维图像特征
        self.feat_extractor = nn.Sequential(*list(backbone.children())[:-1])
        # 新的全连接层,输入维度为图像特征维度+尺寸特征维度
        self.fc = nn.Linear(in_features=512 + size_feat_dim, out_features=num_classes, bias=True)

    def forward(self, img, size_feat):
        # 提取图像特征,输出形状为(batch_size, 512, 1, 1)
        img_feat = self.feat_extractor(img)
        # 展平为(batch_size, 512)
        img_feat = img_feat.flatten(1)
        # 沿特征维度拼接图像特征和尺寸特征
        combined_feat = torch.cat([img_feat, size_feat], dim=1)
        # 输出分类结果
        out = self.fc(combined_feat)
        return out
  1. 训练时调整前向传播逻辑,同时传入图像和尺寸特征:
# 初始化模型
model = ResNet18WithSizeFeat(num_classes=17, size_feat_dim=2)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
model = model.to(device)

criterion = nn.CrossEntropyLoss()
optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
epochs = 10

model.train()
for epoch in range(epochs):
    for imgs, size_feats, labels in train_loader:
        imgs = imgs.to(device)
        size_feats = size_feats.to(device)
        labels = labels.to(device)

        optimizer.zero_grad()
        outputs = model(imgs, size_feats)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

调参建议:可以先冻结ResNet backbone的预训练权重,仅训练新的全连接层,验证尺寸特征带来的精度增益,再决定是否微调整个网络。

内容的提问来源于stack exchange,提问作者Dobiks

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 14:06:28