이전 버전의 컨버터는 pyTorch를 변환하기 위해서는 ONNX를 거쳐야 했다.
PyTorch 변환
import coremltools as ct
model = ct.convert(
source_model,// TorchScript model, 혹은 TorchScript model이 있는 경로
inputs=[ct.ImageType(name="input", shape(3, 224, 224))]
)
커스텀 오퍼레이션이 필요하다면
그래서 TorchScript를 어떻게 얻나?
trace 로 얻기
예제 입력을 넣어서 실행하면서, 이를 추척해서 스크립트 생성
validation 데이터, 혹은 랜덤 데이터를 예제 입력으로 쓰면 된다.
import torch
traced_model = torch.jit.trace(model, example_input)
실제
# # Converting a Segmentation Model via CoreML
# ### Imports
import urllib
import torch
import torch.nn as nn
import torchvision
import json
from torchvision import transforms
import coremltools as ct
from PIL import Image
# ### Load Sample Model and Image
# Load model
model = torch.hub.load('pytorch/vision:v0.6.0', 'deeplabv3_resnet101', pretrained=True).eval()
# Load sample image
input_image = Image.open("dog_and_cat.jpg")
display(input_image)
# ### Image Preprocessing
to_tensor = transforms.ToTensor()
input_tensor = to_tensor(input_image)
input_batch = input_tensor.unsqueeze(0)
# ### Trace the Model with PyTorch
# First attempt at tracing
trace = torch.jit.trace(model, input_batch)
# ### Wrap the Model to Allow Tracing
# only tensor and tuple can be output. dictionaries are not allowed
class WrappedDeeplabv3Resnet101(nn.Module):
def __init__(self):
super(WrappedDeeplabv3Resnet101, self).__init__()
self.model = torch.hub.load('pytorch/vision:v0.6.0', 'deeplabv3_resnet101', pretrained=True).eval()
def forward(self, x):
res = self.model(x)
x = res["out"]
return x
# ### Trace the Wrapped Model
traceable_model = WrappedDeeplabv3Resnet101().eval()
trace = torch.jit.trace(traceable_model, input_batch)
# ### Convert to Core ML
# Define input
_input = ct.ImageType(
name="input_1",
shape=input_batch.shape,
bias=[-0.485/0.229,-0.456/0.224,-0.406/0.225],
scale= 1./(255*0.226)
)
# Convert model
mlmodel = ct.convert(
trace,
inputs=[_input],
)
# ### Set the Model Metadata
labels_json = {"labels": ["background", "aeroplane", "bicycle", "bird", "board", "bottle", "bus", "car", "cat", "chair", "cow", "diningTable", "dog", "horse", "motorbike", "person", "pottedPlant", "sheep", "sofa", "train", "tvOrMonitor"]}
mlmodel.type = 'imageSegmenter'
mlmodel.user_defined_metadata['com.apple.coreml.model.preview.params'] = json.dumps(labels_json)
# ### Save the Model for Visualization
mlmodel.save("SegmentationModel.mlmodel")
scripting
class MyModel(nn.Module):
def forward(self, x):
x = self.conv(x)
x = self.relu(x)
x = self.avg_pool(x)
x = self.dropout(x)
x = self.linear(x)
return x
import torch
model = MyModel()
scripted_model = torch.jit.script(model)
tracing vs scripting
Language Model
# # Converting a Language Model via Core ML
# ### Imports
import torch
import numpy as np
from transformers import GPT2LMHeadModel, GPT2Tokenizer
import coremltools as ct
# ### Model
class FinishMySentence(torch.nn.Module):
def __init__(self, model=None, eos=198):
super(FinishMySentence, self).__init__()
self.eos = torch.tensor([eos])
self.next_token_predictor = model
self.default_token = torch.tensor([0])
def forward(self, x):
sentence = x
token = self.default_token
while token != self.eos:
predictions, _ = self.next_token_predictor(sentence)
token = torch.argmax(predictions[-1, :], dim=0, keepdim=True)
sentence = torch.cat((sentence, token), 0)
return sentence
# ### Initialize the Token Predictor
token_predictor = GPT2LMHeadModel.from_pretrained("gpt2", torchscript=True).eval()
# ### Trace the Token Predictor
#
random_tokens = torch.randint(10000, (5,))
traced_token_predictor = torch.jit.trace(token_predictor, random_tokens)
# ### Script the Outer Loop
#
model = FinishMySentence(model=traced_token_predictor)
scripted_model = torch.jit.script(model)
# ### Convert to Core ML
#
mlmodel = ct.convert(
scripted_model,
# Range for the sequence dimension to be between [1, 64]
inputs=[ct.TensorType(name="context", shape=(ct.RangeDim(1, 64),), dtype=np.int32)],
)
# ### Encode the Sentence Fragment
#
sentence_fragment = "The Manhattan bridge is"
tokenizer = GPT2Tokenizer.from_pretrained("gpt2")
context = torch.tensor(tokenizer.encode(sentence_fragment))
# ### Run the Model
coreml_inputs = {"context": context.to(torch.int32).numpy()}
prediction_dict = mlmodel.predict(coreml_inputs)
generated_tensor = prediction_dict["sentence:2"]
generated_text = tokenizer.decode(generated_tensor)
print("Fragment: {}".format(sentence_fragment))
print("Completed: {}".format(generated_text))
output 형식 문제: output은 dictionary가 될 수 없다.
Trace warning: Python value에 대해서는 trace를 할 수 없다.
JIT script의 타입 문제: JIT scripter는 타입을 필요로 한다.
class TestNet(nn.Module):
def forward(self, x):
_list = []
for i in range(10):
_list.append(i)
return list
model = torch.jit.script(TestNet())
RuntimeError: Argument for call are not valid
...
Expected a value of type 'Tensor' ... but instead found type 'int'
...
class TestNet(nn.Module):
def forward(self, x):
_list: List[int] = [0]
for i in range(10):
_list.append(i)
return _list[1:]
model = torch.jit.script(TestNet())
evaluation 모드로의 변경: 특정 레이어는 evaluation모드에서 비활성화되고, 컨버터는 이를 무시한다.
더 자세한 정보는 coremltool과 pytorch사이트 참조