coremltools: PyTorch unified convertor does not work with flexible input shapes
🐞Describe the bug
I made changes to my model so I could use the recommended unified convertor. Conversion is successful without issue and shows that flexible shapes are supported (in both Python and Xcode).
Running prediction with a shape in the supported ranges (and any shape other than the fixed shape) will fail with an error. The fixed shape input works as expected. I’ve tried both GPU and CPU only.
Trace
[espresso] [Espresso::handle_ex_plan] exception=Espresso exception: "Not implemented": axis -4 not implemented status=-9
[coreml] Failure dynamically resizing for sequence length.
[coreml] Failure in resetSizes.
prediction error: Error Domain=com.apple.CoreML Code=0 "Failure dynamically resizing for sequence length." UserInfo={NSLocalizedDescription=Failure dynamically resizing for sequence length.}
To Reproduce
The source code and model is in the attached archive.
import torch
import torch.nn as nn
import coremltools as ct
import coremltools.proto.FeatureTypes_pb2 as ft
from coremltools.models.neural_network import flexible_shape_utils
from model import TransformerNet
channels = 3
width = 1024
height = 1024
torch_model = TransformerNet()
#torch_model.load_state_dict(torch.load('TrainedModel.pth', map_location=torch.device('cpu')))
torch_model.eval()
example_input = torch.rand(1, channels, width, height)
traced_model = torch.jit.trace(torch_model, example_input)
mlmodel = ct.convert(
traced_model,
inputs=[ct.ImageType(name="input_1", shape=example_input.shape)],
minimum_ios_deployment_target='13'
)
#note if "input" is used for the name it creates a name collision
spec = mlmodel.get_spec()
# needed because documentation states:
# outputs must not be specified for PyTorch
output = spec.description.output[0]
output.type.imageType.colorSpace = ft.ImageFeatureType.RGB
output.type.imageType.height = height
output.type.imageType.width = width
ct.utils.rename_feature(spec, '782', 'output')
img_size_ranges = flexible_shape_utils.NeuralNetworkImageSizeRange(height_range=(256, 3072), width_range=(256, 3072))
flexible_shape_utils.update_image_size_range(spec, feature_name='input_1', size_range=img_size_ranges)
flexible_shape_utils.update_image_size_range(spec, feature_name='output', size_range=img_size_ranges)
ct.utils.save_spec(spec, "TransformerNet.mlmodel")
model.py:
import torch
import torch.nn as nn
class TransformerNet(torch.nn.Module):
def __init__(self):
super(TransformerNet, self).__init__()
# Initial convolution layers
self.conv1 = ConvLayer(3, 8, kernel_size=9, stride=1)
self.in1 = torch.nn.InstanceNorm2d(8, affine=True)
self.conv2 = ConvLayer(8, 16, kernel_size=3, stride=2)
self.in2 = torch.nn.InstanceNorm2d(16, affine=True)
self.conv3 = ConvLayer(16, 32, kernel_size=3, stride=2)
self.in3 = torch.nn.InstanceNorm2d(32, affine=True)
# Residual layers
self.res1 = ResidualBlock(32)
self.res2 = ResidualBlock(32)
self.res3 = ResidualBlock(32)
self.res4 = ResidualBlock(32)
self.res5 = ResidualBlock(32)
# Upsampling Layers
self.deconv1 = UpsampleConvLayer(32, 16, kernel_size=3, stride=1, upsample=2)
self.in4 = torch.nn.InstanceNorm2d(16, affine=True)
self.deconv2 = UpsampleConvLayer(16, 8, kernel_size=3, stride=1, upsample=2)
self.in5 = torch.nn.InstanceNorm2d(8, affine=True)
self.deconv3 = ConvLayer(8, 3, kernel_size=9, stride=1)
# Non-linearities
self.relu = torch.nn.ReLU()
def forward(self, X):
y = self.relu(self.in1(self.conv1(X)))
y = self.relu(self.in2(self.conv2(y)))
y = self.relu(self.in3(self.conv3(y)))
y = self.res1(y)
y = self.res2(y)
y = self.res3(y)
y = self.res4(y)
y = self.res5(y)
y = self.relu(self.in4(self.deconv1(y)))
y = self.relu(self.in5(self.deconv2(y)))
y = self.deconv3(y)
return y
class ConvLayer(torch.nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride):
super(ConvLayer, self).__init__()
reflection_padding = kernel_size // 2
#self.reflection_pad = torch.nn.ReflectionPad2d(reflection_padding)
self.reflection_pad = ReflectPad2d_rev(reflection_padding)
self.conv2d = torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride)
def forward(self, x):
out = self.reflection_pad(x)
out = self.conv2d(out)
return out
class ResidualBlock(torch.nn.Module):
def __init__(self, channels):
super(ResidualBlock, self).__init__()
self.conv1 = ConvLayer(channels, channels, kernel_size=3, stride=1)
self.in1 = torch.nn.InstanceNorm2d(channels, affine=True)
self.conv2 = ConvLayer(channels, channels, kernel_size=3, stride=1)
self.in2 = torch.nn.InstanceNorm2d(channels, affine=True)
self.relu = torch.nn.ReLU()
def forward(self, x):
residual = x
out = self.relu(self.in1(self.conv1(x)))
out = self.in2(self.conv2(out))
out = out + residual
return out
class UpsampleConvLayer(torch.nn.Module):
def __init__(self, in_channels, out_channels, kernel_size, stride, upsample=None):
super(UpsampleConvLayer, self).__init__()
self.upsample = upsample
reflection_padding = kernel_size // 2
#self.reflection_pad = torch.nn.ReflectionPad2d(reflection_padding)
self.reflection_pad = ReflectPad2d_rev(reflection_padding)
self.conv2d = torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride)
def forward(self, x):
x_in = x
if self.upsample:
x_in = torch.nn.functional.interpolate(x_in, mode='nearest', scale_factor=self.upsample)
out = self.reflection_pad(x_in)
out = self.conv2d(out)
return out
class ReflectPad2d_rev(nn.Module):
def __init__(self, size):
super().__init__()
self.size = size
def forward(self, x):
a = self.size
L_list, R_list = [], []
U_list, D_list = [], []
for i in range(a):#i:0, 1
l = x[:, :, :, (a-i):(a-i+1)]
L_list.append(l)
r = x[:, :, :, (i-a-1):(i-a)]
R_list.append(r)
L_list.append(x)
x = torch.cat(L_list+R_list[::-1], dim=3)
for i in range(a):
u = x[:, :, (a-i):(a-i+1), :]
U_list.append(u)
d = x[:, :, (i-a-1):(i-a), :]
D_list.append(d)
U_list.append(x)
x = torch.cat(U_list+D_list[::-1], dim=2)
return x
System environment (please complete the following information):
- coremltools 4.0
- OS MacOS
- macOS 10.15.7 (19H2)
- Version 12.1 (12A7403)
- virtualenv
- python version 3.7
- pytorch 1.70
Additional context
This issue severely restricts deploying MLModels across my workflow.
About this issue
- Original URL
- State: closed
- Created 4 years ago
- Comments: 21
They provide the tools, but it’s up to use to use them properly. There is documentation and places to find help.
It’s been a long time. Any progress on this?