μ μ: Ankith Gunapal, Jordi Ramon, Marcos Carranza λ²μ: μ΄νμ€
Introduction to torch.export Tutorial μμ, torch.export λ₯Ό μ¬μ©νλ λ°©λ²μ λ°°μ μ΅λλ€.
μ΄ νν 리μΌμ μ΄μ νν 리μΌμ νμ₯νλ©°, λ리 μ¬μ©λλ λͺ¨λΈλ€μ μ½λμ ν¨κ» λ΄λ³΄λ΄λ κ³Όμ κ³Ό torch.export μ¬μ©μ€ λ§μ£ΌμΉ μ μλ λ¬Έμ λ€μ λ€λ£Ήλλ€.
μ΄ νν 리μΌμ λ€μκ³Ό κ°μ μ¬μ© μ¬λ‘μ λ§κ² λͺ¨λΈμ λ΄λ³΄λ΄λ λ°©λ²μ λ°°μλλ€.
- μμ λΆλ₯ (MViT)
- μλ μμ± μΈμ (OpenAI Whisper-Tiny)
- μ΄λ―Έμ§ μΊ‘μ λ (BLIP)
- ν둬ννΈ κΈ°λ° μ΄λ―Έμ§ λΆν (SAM2)
κ° λ€ κ°μ§ λͺ¨λΈμ torch.export μ κ³ μ ν κΈ°λ₯μ 보μ¬μ£Όκ³ , ꡬν κ³Όμ μμμ μ€μ§μ μΈ κ³ λ €μ¬νκ³Ό λ°μν μ μλ λ¬Έμ λ€μ ν¨κ» λ€λ£¨κΈ° μν΄ μ μ λμμ΅λλ€.
- PyTorch 2.4 μ΄μ λ²μ
torch.exportλ° PyTorch Eager μΆλ‘ μ λν κΈ°λ³Έμ μΈ μ΄ν΄
torch.compile μ JITλ₯Ό νμ©ν΄ PyTorch μ½λλ₯Ό μ΅μ νλ 컀λλ‘ μ»΄νμΌν¨μΌλ‘μ¨ μ€ν μλλ₯Ό ν₯μμν΅λλ€. μ£Όμ΄μ§ λͺ¨λΈμ TorchDynamo λ₯Ό νμ©νμ¬ μ΅μ ννκ³ , μ΅μ νλ κ·Έλνλ₯Ό λ§λ λ€, APIμμ μ§μ ν λ°±μλλ₯Ό ν΅ν΄ νλμ¨μ΄μ λ§κ² μ€νλλλ‘ λ³νν©λλ€.
TorchDynamo κ° μ§μνμ§ μλ Pythonμ κΈ°λ₯μ λ§λλ©΄, κ³μ° κ·Έλνλ μ€λ¨νκ³ ν΄λΉ μ½λλ κΈ°λ³Έ Python μΈν°ν리ν°κ° μ²λ¦¬νλλ‘ νκ³ , κ·Έλν μΊ‘μ³λ₯Ό μ΄μ΄λκ°λλ€.
μ΄λ¬ν μ€λ¨λ κ³μ° κ·Έλνλ₯Ό graph break λΌκ³ μΉν©λλ€.
torch.export μ torch.compile μ μ£Όμν μ°¨μ΄μ μ€ νλλ torch.export λ κ·Έλν λΆμ μ μ§μνμ§ μλλ€λ κ²μ
λλ€. μ¦, λ΄λ³΄λ΄λ €λ μ 체 λͺ¨λΈ λλ λͺ¨λΈμ μΌλΆλ λ¨μΌ κ·Έλν ννμ¬μΌ ν©λλ€.
μ΄λ κ·Έλν λΆμ μ μ²λ¦¬νλ €λ©΄ μ§μλμ§ μλ μ°μ°μ κΈ°λ³Έ PythonμΌλ‘ νκ°ν΄μΌνλλ°, μ΄λ¬ν λ°©μμ΄ torch.export μ μ€κ³μ νΈνλμ§ μκΈ° λλ¬Έμ
λλ€.
λ€μν PyTorch νλ μμν¬λ€μ μ°¨μ΄μ μ λν μΈλΆμ μΈ μ 보λ link μμ νμΈν μ μμ΅λλ€.
μλμ 컀맨λλ₯Ό μ¬μ©ν΄μ νλ‘κ·Έλ¨ λ΄μ κ·Έλν λΆμ μ νμΈν μ μμ΅λλ€.
TORCH_LOGS="graph_breaks" python <file_name>.pyνλ‘κ·Έλ¨ λ΄μ κ·Έλν λΆμ μ μ κ±°νλλ‘ μ½λλ₯Ό μμ ν΄μΌ ν©λλ€. λ¬Έμ κ° ν΄κ²°λλ€λ©΄, λͺ¨λΈμ λ΄λ³΄λΌ μ€λΉκ° λ κ²μ
λλ€.
PyTorchλ μΈκΈ° μλ HuggingFaceμ TIMM λͺ¨λΈμμ torch.compile μ μν΄μ nightly benchmarks λ₯Ό μ€νν©λλ€.
μ΄λ¬ν λͺ¨λΈ λλΆλΆμ κ·Έλν λΆμ μ΄ μμ΅λλ€.
ν΄λΉ λ μνΌμ ν¬ν¨λ λͺ¨λΈλ€μ κ·Έλν λΆμ μ΄ μμ§λ§, torch.export λ μ€ν¨ν©λλ€.
MViTλ MultiScale Vision Transformers μ κΈ°λ°μΌλ‘ν λͺ¨λΈμ ν΄λμ€μ λλ€. μ΄ λͺ¨λΈμ Kinetics-400 Dataset μ μ¬μ©νμ¬ μ¬μ νλ ¨λ μμ λΆλ₯ λͺ¨λΈμ λλ€. μ΄ λͺ¨λΈμ μ μ ν λ°μ΄ν° μ κ³Ό ν¨κ» μ¬μ©νλ€λ©΄, κ²μ νκ²½μμμ λμ μΈμμ νμ©ν μ μμ΅λλ€.
μλμ μ½λλ MViTλ₯Ό batch_size=2 λ‘ νΈλ μ΄μ±νμ¬ λ΄λ³΄λ΄κ³ , μ΄ν batch_size=4 λ‘ λ΄λ³΄λΈ νλ‘κ·Έλ¨μ΄ μ μμ μΌλ‘ μ€νλλμ§ νμΈν©λλ€.
import numpy as np
import torch
from torchvision.models.video import MViT_V1_B_Weights, mvit_v1_b
import traceback as tb
model = mvit_v1_b(weights=MViT_V1_B_Weights.DEFAULT)
# 2κ°μ λΉλμ€μ λ°°μΉλ₯Ό λ§λ€λ©°, κ°κ°μ ννλ 224x224x3μ 16 νλ μμ κ°μ§λλ€.
input_frames = torch.randn(2, 16, 224, 224, 3)
# Transpose to get [1, 3, num_clips, height, width].
input_frames = np.transpose(input_frames, (0, 4, 1, 2, 3))
# λͺ¨λΈμ λ΄λ³΄λ
λλ€.
exported_program = torch.export.export(
model,
(input_frames,),
)
# 4κ°μ λΉλμ€μ λ°°μΉλ₯Ό λ§λ€λ©°, κ°κ°μ ννλ 224x224x3μ 16 νλ μμ κ°μ§λλ€.
input_frames = torch.randn(4, 16, 224, 224, 3)
input_frames = np.transpose(input_frames, (0, 4, 1, 2, 3))
try:
exported_program.module()(input_frames)
except Exception:
tb.print_exc() raise RuntimeError(
RuntimeError: Expected input at *args[0].shape[0] to be equal to 2, but got 4κΈ°λ³Έμ μΌλ‘ λ΄λ³΄λ΄λ κ³Όμ μμλ λͺ¨λ μ λ ₯ ννκ° κ³ μ λμ΄ μλ€κ³ κ°μ νκ³ νΈλ μ΄μ€(trace) ν©λλ€, λ°λΌμ νΈλ μ΄μ±(tracing)μ ν λ μ¬μ©ν μ λ ₯ ννμ λ€λ₯Έ ννλ‘ νλ‘κ·Έλ¨μ μ€ννλ©΄ μ€λ₯κ° λ°μν©λλ€.
μ΄ μ€λ₯λ₯Ό ν΄κ²°νκΈ° μν΄, μ
λ ₯μ 첫 λ²μ§Έ μ°¨μ (batch_size)μ λμ μΌλ‘ μ§μ νκ³ , νμ©λλ batch_size μ λ²μλ₯Ό μ§μ ν©λλ€.
μλμ μμ λ μμ μμλ, batch_size μ νμ© λ²μλ₯Ό 1λΆν° 16κΉμ§λ‘ μ§μ ν©λλ€.
μ¬κΈ°μ μλ €λ릴 μΈλΆμ¬νμ min=2 μ λ²κ·Έκ° μλλΌλ κ²μ΄κ³ μ΄μ λν μ€λͺ
μ The 0/1 Specialization Problem λ¬Έμμμ νμΈν μ μμ΅λλ€.
λν torch.export μ λμ μ
λ ₯ ννμ λν μμΈν μ€λͺ
μ export νν 리μΌμμ μ°Ύμλ³Ό μ μμ΅λλ€.
μλμ μ½λλ λμ λ°°μΉ μ¬μ΄μ¦λ₯Ό μ¬μ©νμ¬ mViTλ₯Ό λ΄λ³΄λ΄λ λ°©λ²μ 보μ¬μ€λλ€.
import numpy as np
import torch
from torchvision.models.video import MViT_V1_B_Weights, mvit_v1_b
import traceback as tb
model = mvit_v1_b(weights=MViT_V1_B_Weights.DEFAULT)
# 2κ°μ λΉλμ€μ λ°°μΉλ₯Ό λ§λ€λ©°, κ°κ°μ ννλ 224x224x3μ 16 νλ μμ κ°μ§λλ€.
input_frames = torch.randn(2,16, 224, 224, 3)
# μ°¨μμ λ°κΏ [1, 3, num_clips, height, width] ννλ‘ λ³νν©λλ€.
input_frames = np.transpose(input_frames, (0, 4, 1, 2, 3))
# λͺ¨λΈμ λ΄λ³΄λ
λλ€.
batch_dim = torch.export.Dim("batch", min=2, max=16)
exported_program = torch.export.export(
model,
(input_frames,),
# Specify the first dimension of the input x as dynamic
dynamic_shapes={"x": {0: batch_dim}},
)
# 4κ°μ λΉλμ€μ λ°°μΉλ₯Ό λ§λ€λ©°, κ°κ°μ ννλ 224x224x3μ 16 νλ μμ κ°μ§λλ€.
input_frames = torch.randn(4,16, 224, 224, 3)
input_frames = np.transpose(input_frames, (0, 4, 1, 2, 3))
try:
exported_program.module()(input_frames)
except Exception:
tb.print_exc()μλ μμ± μΈμμ κΈ°κ³νμ΅μ νμ©νμ¬ μμ±μ ν
μ€νΈλ‘ λ³ννλ κΈ°μ μ
λλ€.
Whisper λ OpenAIμμ κ°λ°ν μΈμ½λ-λμ½λ ꡬ쑰μ νΈλμ€ν¬λ¨Έ λͺ¨λΈλ‘, ASRκ³Ό μμ± λ²μμ μν΄ 68λ§ μκ°μ λΌλ²¨λ§λ λ°μ΄ν°λ₯Ό μ¬μ©ν΄ νμ΅λμμ΅λλ€.
μλμ μ½λλ‘ μλ μμ± μΈμμ μν whisper-tiny λͺ¨λΈμ λ΄λ³΄λΌ μ μμ΅λλ€.
import torch
from transformers import WhisperProcessor, WhisperForConditionalGeneration
from datasets import load_dataset
# λͺ¨λΈμ κ°μ Έμ΅λλ€.
model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-tiny")
# λͺ¨λΈ λ΄λ³΄λ΄κΈ°λ₯Ό μν λλ―Έ μ
λ ₯μ
λλ€.
input_features = torch.randn(1,80, 3000)
attention_mask = torch.ones(1, 3000)
decoder_input_ids = torch.tensor([[1, 1, 1 , 1]]) * model.config.decoder_start_token_id
model.eval()
exported_program: torch.export.ExportedProgram= torch.export.export(model, args=(input_features, attention_mask, decoder_input_ids,))torch._dynamo.exc.InternalTorchDynamoError: AttributeError: 'DynamicCache' object has no attribute 'key_cache'κΈ°λ³Έμ μΌλ‘ torch.export λ TorchDynamo λΌλ λ°μ΄νΈμ½λ λΆμ μμ§μ μ¬μ©νμ¬ μ½λλ₯Ό μ²λ¦¬ν©λλ€, μ΄λ μ½λλ₯Ό μ¬λ³Όλ¦νκ² λΆμνμ¬ κ·Έλνλ₯Ό μμ±ν©λλ€.
μ΄ λΆμμ μμ μ± λ³΄μ₯μ κ°νν΄μ£Όμ§λ§, λͺ¨λ Python μ½λλ₯Ό μ§μνλ κ²μ μλλλ€. whisper-tiny λͺ¨λΈμ κΈ°λ³Έ strict λͺ¨λλ‘ λ΄λ³΄λΌ λ, Dynamoμμ μ§μλμ§ μλ κΈ°λ₯ λλ¬Έμ μΌλ°μ μΌλ‘ μ€λ₯κ° λ°μν©λλ€.
Dynamoμμ μ΄ μλ¬κ° λ°μνλ μ΄μ λ₯Ό μ΄ν΄νλ €λ©΄, GitHub issue ν΄λΉ κΉνλΈ μ΄μλ₯Ό μ°Έκ³ νμΈμ.
μμ μλ¬λ₯Ό ν΄κ²°νκΈ° μν΄, torch.export λ Python μΈν°ν리ν°λ₯Ό μ¬μ©ν΄ νλ‘κ·Έλ¨μ νΈλ μ΄μ±νλ non_strict λͺ¨λλ₯Ό μ 곡νλ©°, μ΄λ PyTorch eager μ€νκ³Ό μ μ¬νκ² λμν©λλ€.
μ μΌν μ°¨μ΄μ μ λͺ¨λ Tensor κ°μ²΄κ° ProxyTensors λ‘ λ체λλ©°, μ΄λ λͺ¨λ μ°μ°μ΄ κ·Έλνμ κΈ°λ‘λλ€λ κ²μ
λλ€.
strict=False μ μ¬μ©νλ©΄, νλ‘κ·Έλ¨μμ λ΄λ³΄λΌ μ μμ΅λλ€.
import torch
from transformers import WhisperProcessor, WhisperForConditionalGeneration
from datasets import load_dataset
# λͺ¨λΈμ κ°μ Έμ΅λλ€.
model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-tiny")
# λͺ¨λΈ λ΄λ³΄λ΄κΈ°λ₯Ό μν λλ―Έ μ
λ ₯μ
λλ€.
input_features = torch.randn(1,80, 3000)
attention_mask = torch.ones(1, 3000)
decoder_input_ids = torch.tensor([[1, 1, 1 , 1]]) * model.config.decoder_start_token_id
model.eval()
exported_program: torch.export.ExportedProgram= torch.export.export(model, args=(input_features, attention_mask, decoder_input_ids,), strict=False)μ΄λ―Έμ§ μΊ‘μ
λ μ μ΄λ―Έμ§μ μλ λ¨μ΄μ λ΄μ©μ μ μνλ μ
무λ₯Ό μννλ€. κ²μ νκ²½μμ μ΄λ―Έμ§ μΊ‘μ
λμ μ₯λ©΄ λ΄ λ€μν κ²μ κ°μ²΄μ λν ν
μ€νΈ μ€λͺ
μ λμ μΌλ‘ μμ±νλ©°, κ²μ΄λ¨Έμκ² μΆκ°μ μΈ μ 보λ₯Ό μ 곡ν¨μΌλ‘μ¨ κ²μ νλ μ΄ κ²½νμ ν₯μμν€λλ° νμ©λ μ μμ΅λλ€.
BLIP λ μ΄λ―Έμ§ μΊ‘μ
λ λΆμΌμμ λ리 μ¬μ©λλ λͺ¨λΈλ‘, released by SalesForce Research μμ 곡κ°λμμ΅λλ€.
μλ μ½λλ batch_size=1 λ‘ BLIPλ₯Ό λ΄λ³΄λ΄λ €κ³ μλν©λλ€.
import torch
from models.blip import blip_decoder
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
image_size = 384
image = torch.randn(1, 3,384,384).to(device)
caption_input = ""
model_url = 'https://storage.googleapis.com/sfr-vision-language-research/BLIP/models/model_base_capfilt_large.pth'
model = blip_decoder(pretrained=model_url, image_size=image_size, vit='base')
model.eval()
model = model.to(device)
exported_program: torch.export.ExportedProgram= torch.export.export(model, args=(image,caption_input,), strict=False)λͺ¨λΈμ λ΄λ³΄λΌ λ, λͺ¨λΈ ꡬνμμ torch.export μμ μμ§ μ§μνμ§ μλ νΉμ Python μ°μ°μ΄ ν¬ν¨μ΄ λ μ μκΈ° λλ¬Έμ μ€ν¨ν μ μμ΅λλ€.
μ΄ μ€ν¨ μ¬λ‘λ€ μ€ μΌλΆλ ν΄κ²° λ°©λ²μ΄ μμ μ μμ΅λλ€. BLIPλ μλ λͺ¨λΈμμ μ€λ₯κ° λ°μνλ μμμ΄μ§λ§, μ½λμ μμ μμ μ νλ©΄ ν΄κ²°ν μ μμ΅λλ€.
torch.export λ ExportDB μμ μ§μνλ μ°μ°κ³Ό μ§μνμ§ μλ μ°μ°μ μΌλ°μ μΈ μ¬λ‘λ€μ λμ΄νκ³ , μ½λμμ λ΄λ³΄λΌ μ μλλ‘ μμ νλ λ°©λ²μ 보μ¬μ€λλ€.
File "/BLIP/models/blip.py", line 112, in forward
text.input_ids[:,0] = self.tokenizer.bos_token_id
File "/anaconda3/envs/export/lib/python3.10/site-packages/torch/_subclasses/functional_tensor.py", line 545, in __torch_dispatch__
outs_unwrapped = func._op_dk(
RuntimeError: cannot mutate tensors with frozen storageλ΄λ³΄λ΄κΈ°κ° μ€ν¨νλ μμΉμ μλ tensor λ₯Ό 볡μ ν©λλ€.
text.input_ids = text.input_ids.clone() # clone the tensor
text.input_ids[:,0] = self.tokenizer.bos_token_idNote
This constraint has been relaxed in PyTorch 2.7 nightlies. This should work out-of-the-box in PyTorch 2.7 μ΄ μ μ½μ PyTorch 2.7 nightliesμμ μνλμμ΅λλ€. PyTorch 2.7μμλ λ³λμ μ€μ μμ΄ λ°λ‘ λμν κ²μ λλ€.
μ΄λ―Έμ§ λΆν μ λμ§νΈ μ΄λ―Έμ§λ₯Ό ν½μ λ¨μμ νΉμ§μ λ°λΌ μλ‘ λ€λ₯Έ κ·Έλ£Ή, μ¦ μΈκ·Έλ¨ΌνΈλ‘ λλλ μ»΄ν¨ν° λΉμ κΈ°μ μ λλ€. Segment Anything Model (SAM) μ ν둬ννΈ κΈ°λ° μ΄λ―Έμ§ λΆν μ λμ ν λͺ¨λΈλ‘, μ¬μ©μκ° μνλ κ°μ²΄λ₯Ό μ§μ νλ ν둬ννΈλ₯Ό μ λ ₯νλ©΄ ν΄λΉ κ°μ²΄μ λ§μ€ν¬λ₯Ό μμΈ‘ν©λλ€. SAM 2 λ μ΄λ―Έμ§μ λΉλμ€μμ κ°μ²΄λ₯Ό λΆν νκΈ° μν μ΅μ΄μ ν΅ν© λͺ¨λΈμ λλ€. SAM2ImagePredictor ν΄λμ€λ λͺ¨λΈμ ν둬ννΈλ₯Ό μ λ ₯ν μ μλ κ°νΈν μΈν°νμ΄μ€λ₯Ό μ 곡ν©λλ€. μ΄ λͺ¨λΈμ ν¬μΈνΈμ λ°μ€ ν둬ννΈλ λ¬Όλ‘ , μ΄μ μμΈ‘μμ μμ±λ λ§μ€ν¬λ μ λ ₯μΌλ‘ λ°μ μ μμ΅λλ€. SAM2λ κ°μ²΄ μΆμ μμ κ°λ ₯ν μ λ‘μ· μ±λ₯μ μ 곡νλ―λ‘, μ₯λ©΄ λ΄ κ²μ κ°μ²΄λ₯Ό μΆμ νλ λ° νμ©ν μ μμ΅λλ€.
SAM2ImagePredictor μ μμΈ‘ λ©μλμμ λ°μνλ ν μ μ°μ°μ μ€μ λ‘ _predict λ©μλ μμμ μνλ©λλ€. λ°λΌμ μλμ κ°μ΄ λ΄λ³΄λ΄κΈ°λ₯Ό μλν©λλ€.
ep = torch.export.export(
self._predict,
args=(unnorm_coords, labels, unnorm_box, mask_input, multimask_output),
kwargs={"return_logits": return_logits},
strict=False,
)torch.export λ λͺ¨λμ΄ torch.nn.Module νμ
μ΄μ΄μΌ ν©λλ€. νμ§λ§, λ΄λ³΄λ΄κΈ° νλ €λ λͺ¨λμ ν΄λμ€ λ©μλμ΄κΈ° λλ¬Έμ μ€λ₯κ° λ°μν©λλ€.
Traceback (most recent call last):
File "/sam2/image_predict.py", line 20, in <module>
masks, scores, _ = predictor.predict(
File "/sam2/sam2/sam2_image_predictor.py", line 312, in predict
ep = torch.export.export(
File "python3.10/site-packages/torch/export/__init__.py", line 359, in export
raise ValueError(
ValueError: Expected `mod` to be an instance of `torch.nn.Module`, got <class 'method'>.λμ°λ―Έ ν΄λμ€λ₯Ό μμ±νμ¬ torch.nn.Module μ μμνκ³ , ν΄λμ€μ forward λ©μλ μμμ _predict method λ₯Ό νΈμΆν©λλ€. μ 체 μ½λλ here μμ νμΈν μ μμ΅λλ€.
class ExportHelper(torch.nn.Module):
def __init__(self):
super().__init__()
def forward(_, *args, **kwargs):
return self._predict(*args, **kwargs)
model_to_export = ExportHelper()
ep = torch.export.export(
model_to_export,
args=(unnorm_coords, labels, unnorm_box, mask_input, multimask_output),
kwargs={"return_logits": return_logits},
strict=False,
)μ΄ νν 리μΌμμλ torch.export λ₯Ό νμ©νμ¬ λ€μν λνμ μΈ μ¬μ© μ¬λ‘μ λͺ¨λΈμ λ΄λ³΄λ΄λ λ°©λ²μ νμ΅νμκ³ , μ¬λ°λ₯Έ μ€μ κ³Ό κ°λ¨ν μ½λ μμ μΌλ‘ λ°μν μ μλ μ¬λ¬ λ¬Έμ λ€μ ν΄κ²°νλ λ°©λ²λ ν¨κ» λ€λ€μ΅λλ€.
λͺ¨λΈμ μ±κ³΅μ μΌλ‘ λ΄λ³΄λΈ νμ, μλ² νκ²½μμλ AOTInductor λ₯Ό, μ£μ§ λλ°μ΄μ€ νκ²½μμλ ExecuTorch λ₯Ό μ¬μ©νμ¬ ExportedProgram μ νλμ¨μ΄μ λ§κ² λ³νν μ μμ΅λλ€.
AOTInductor (AOTI)μ λν μμΈν λ΄μ©μ AOTI tutorial μ, ExecuTorch μ λν μμΈν λ΄μ©μ ExecuTorch tutorial μ μ°Έκ³ νμΈμ.