目录
原始转换脚本
脚本运行报错
基于reset50 模型的自定义网络
基本网络结构
卷积模块定义示例
Bottleneck定义示例
网络定义示例
改进的转换脚本
脚本运行报错channels不匹配
脚本运行报错维度不匹配
模型输入数据的类型
tensor size
NCHW和NHWC
自定义网络的通道数目
输入维度修改
onnx转换om文件
带参数images
带参数input
总结
之前转换yolov8 官方的模型,命令很简单,只有几行就可以搞定。
而自定的reset50 则显得更为复杂些。在这个转换过程中,我们可以了解一些模型相关的基本概念。
原始转换脚本
model = models.torch.load('ok.pt')model.to(device)model.eval()dummy_img = torch.Tensor(torch.randn(10, 2, 95))) torch.onnx.export(model = model, #pth模型args=dummy_img,)
脚本运行报错
pth2onnx('resnet18')File "E:\project\10\model_trasfer.py", line 12, in pth2onnxmodel.eval()
AttributeError: 'collections.OrderedDict' object has no attribute 'eval'
提示没有这个eval这个属性。
这个eval属性将模型设置为推理模式(参考: 将 PyTorch 训练模型转换为 ONNX | Microsoft Learn),是必须调用的。此属性是针对模型的,需要先定义一个模型再进行转换。
基于reset50 模型的自定义网络
由前述这个脚本转换的报错,我们需要一个模型。
基本网络结构
参考:ResNets: Why do they perform better than Classic ConvNets? (Conceptual Analysis) | Towards Data Science
【DL系列】ResNet网络结构详解、完整代码实现-CSDN博客
主要包括
1)卷积模块,包括含卷积层(青色)、批归一化层(浅蓝色)、ReLU 激活函数(橙黄色)和最大池化层
2) 几个bottleneck模块
卷积模块定义示例
def Conv1(in_places, places, stride=2):return nn.Sequential(nn.Conv2d(in_channels=in_places, out_channels=places, kernel_size=7, stride=stride, padding=3, bias=False),nn.BatchNorm2d(places),nn.ReLU(inplace=True),nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
参数
in_places : 输入通道数。
places : 输出通道数。
stride: 卷积步长,默认为 2.
作用: 作为 ResNet 的第一个卷积层,用于对输入图像进行初步特征提取
Bottleneck定义示例
class Bottleneck(nn.Module):def __init__(self, in_places, places, stride=1, downsampling=False, expansion=4):super(Bottleneck, self).__init__()self.expansion = expansionself.downsampling = downsamplingself.bottleneck = nn.Sequential(nn.Conv2d(in_channels=in_places, out_channels=places, kernel_size=1, stride=1, bias=False),nn.BatchNorm2d(places),nn.ReLU(inplace=True),nn.Conv2d(in_channels=places, out_channels=places, kernel_size=3, stride=stride, padding=1, bias=False),nn.BatchNorm2d(places),nn.ReLU(inplace=True),self.relu = nn.ReLU(inplace=True)def forward(self, x):residual = x# print("bot input shape:",x.shape)out = self.bottleneck(x)if self.downsampling:residual = self.downsample(x)out += residualout = self.relu(out)# print("bot output shape:",out.shape)return out
in_places: 输入通道数
places: 中间层的通道数
stride : 卷积步长,默认为 1
downsampling: 是否进行下采样(用于调整残差连接的维度)
expansion: 扩展因子,用于调整输出通道数。
作用 通过 1x1、3x3、1x1 的卷积层组合,减少计算量并提取特征。
使用残差连接 ( out += residua1 ) 解决深层网络的梯度消失问题
网络定义示例
class ResNet(nn.Module):def __init__(self, blocks, num_classes=10, expansion=4):super(ResNet, self).__init__()self.expansion = expansionself.conv1 = Conv1(in_places=2, places=64)self.layer1 = self.make_layer(in_places=64, places=64, block=blocks[0], stride=1)self.layer2 = self.make_layer(in_places=256, places=128, block=blocks[1], stride=2)self.layer3 = self.make_layer(in_places=512, places=256, block=blocks[2], stride=2)self.layer4 = self.make_layer(in_places=1024, places=512, block=blocks[3], stride=2)self.avgpool = nn.AdaptiveAvgPool2d((1, 1))self.fc = nn.Linear(512 * expansion, num_classes)def make_layer(self, in_places, places, block, stride):layers = []layers.append(Bottleneck(in_places, places, stride, downsampling=True))for i in range(1, block):# print(i,in_places,places,self.expansion)layers.append(Bottleneck(places * self.expansion, places))return nn.Sequential(*layers)def forward(self, x):# print(x.shape)x1 = self.conv1(x)# print("conv1:",x1.shape)x2 = self.layer1(x1)return F.normalize(x7, dim=-1), F.normalize(x8, dim=-1)
功能:定义了完整的 ResNet 模型
参数
blocks : 每个阶段的 Bottleneck 模块数量(例如 ResNet-50 为 [3,4,6,3])。
num_classes : 分类任务的类别数,默认为 10
expansion :Bottleneck 模块的扩展因子,默认为 4
作用:
通过 make_layer 方法构建多个 Bottleneck 层。
使用全局平均池化 ( AdaptiveAvgPoo12d ) 将特征图转换为向量
通过全连接层 ( fc) 输出分类结果
返回归一化后的特征向量和分类结果
改进的转换脚本
主要在于model变量由上述自定义网络模型实例化而来。
model = Resnet50_2d() //这里引入自定义网络model.load_state_dict(torch.load('ok.pt')) #model = models.torch.load('ok.pt')#model.to(device)model.eval()dummy_img = torch.Tensor(torch.randn(10, 2, 95)) # (batchsize, channels, width, height)
脚本运行报错channels不匹配
return F.conv2d(
RuntimeError: Given groups=1, weight of size [64, 2, 7, 7], expected input[1, 10, 2, 95] to have 2 channels, but got 10 channels instead
脚本运行报错维度不匹配
raise ValueError("expected 4D input (got {}D input)".format(input.dim()))
ValueError: expected 4D input (got 3D input)
期望4D,但实际3D的输入。
模型输入数据的类型
任何程序都需要数据输入,只是数据输入的形式不同。
tensor size
tensor : 在模型转换中,指的是张量的形状或维度信息。张量是多维数组,广泛应用于深度学习框架中,用于表示输入数据、权重、中间结果等。
- 标量:0维张量(单个数值)
- 向量:1维张量(一维数组)
- 矩阵:2维张量(二维数组)
- 更高维张量:如3D、4D等
tensor size 表示张量在每个维度上的大小。 一个形状为 [3, 224, 224] 的张量表示一个3通道、224x224像素的图像。
NCHW和NHWC
多维数据通过多维数组存储,比如卷积神经网络的特征图(Feature Map)通常用四维数组保存,即4D,4D格式解释如下:
- N:Batch数量,例如图像的数目。
- H:Height,特征图高度,即垂直高度方向的像素个数。
- W:Width,特征图宽度,即水平宽度方向的像素个数。
- C:Channels,特征图通道,例如彩色RGB图像的Channels为3。
自定义网络的通道数目
通过2.2节及2.4节网络定义传输的参数
self.conv1 = Conv1(in_places=2, places=64)
据此我们将转换脚本修改为:
dummy_img = torch.Tensor(torch.randn(2, 2, 95)) # (batchsize, channels, width, height)
输入维度修改
由原先3维修改为4维
dummy_img = torch.Tensor(torch.randn(2,2, 2, 95)) # (batchsize, channels, width, height)
至此,已经可以将pt模型文件转换到onnx文件。
onnx转换om文件
带参数images
atc --model=ok.onnx --framework=5 --output=resnet --input_shape="images:1,3,640,640" --soc_version=Ascend310P3 --insert_op_conf=aipp.cfg
ATC start working now, please wait for a moment.
...
ATC run failed, Please check the detail log, Try 'atc --help' for more information
E10016: 1970-01-01-08:05:58.540.117 Opname [images] specified in [--input_shape] is not found in the model, confirm whether this node name exists, or node is not split with the specified delimiter ';'
提示模型中没有images。这里就特别注意
带参数input
atc --model=ok.onnx --framework=5 --output=resnet50_2d --soc_version=Ascend310P3 --input_format=NCHW --input_shape="input:2,2,2,95" --log=info
ATC start working now, please wait for a moment.
...
ATC run success, welcome to the next use.
总结
1) 自定义网络的模型转换,需要转换时实例化网络模型。
2) 明确模型输入数据的维度及各维度的大小。毕竟模型的主要工作即处理这些数据。
示例常用的输入一般为 4D 张量,形状为 (batch_size, channels, height, width)
。
1)batch_size:每次输入网络的样本数量。根据硬件内存和训练需求选择,常见值为 32、64、128 等。
在推理阶段,batch_size
表示一次性输入模型的样本数量。与训练阶段不同,推理时通常不需要反向传播和梯度计算,因此显存占用较低,可以支持较大的 batch_size。
常见的 batch_size
选择
-
batch_size = 1
:-
适用于实时推理任务(如摄像头视频流处理)。
-
延迟最低,但 GPU 利用率可能较低。
-
-
batch_size = 8/16/32
:-
适用于批量处理任务(如离线图像分类、目标检测)。
-
在显存允许的情况下,提高 GPU 利用率。
-
-
batch_size = 动态调整
:-
根据输入数据的数量动态调整
batch_size
。 -
例如,如果有 100 张图像需要处理,可以一次性输入 100 张(如果显存允许)。
-
2)channels:图像的通道数(如 RGB 图像为 3,灰度图像为 1)。由图像类型决定,RGB 为 3,灰度图为 1。
3)height 和 width:图像的高度和宽度。通常将图像调整为固定大小(如 224x224),以适应网络输入。