နားလည်ထားရမယ့် အချက်
Train ပြီးသား model တစ်ခုရဲ့ သင်ယူထားတဲ့ knowledge အားလုံးဟာ layer တစ်ခုချင်းစီထဲက weight နဲ့ bias parameter တွေထဲမှာ ရှိနေပါတယ်။ PyTorch က အကြံပြုထားတဲ့ သိမ်းနည်းက model.state_dict() ကို save လုပ်တာဖြစ်ပြီး၊ ဒါဟာ layer နာမည်တစ်ခုချင်းစီကို ၎င်းရဲ့ parameter tensor တွေနဲ့ map လုပ်ပေးထားတဲ့ dictionary ရိုးရိုးလေးပါ — model object တစ်ခုလုံးကို pickle လုပ်တာမျိုး မဟုတ်ပါဘူး။ Naive နည်းလမ်းဖြစ်တဲ့ torch.save(model) ဟာ save လုပ်တဲ့အချိန်က ရှိနေတဲ့ class definition နဲ့ file layout အတိအကျနဲ့ ချည်နှောင်ထားတာဖြစ်လို့၊ နောက်ပိုင်း code ရွှေ့ရင် (သို့) ပြောင်းလဲ (သို့) refactor လုပ်ရင် load လုပ်တဲ့အခါ တိတ်တဆိတ် ပျက်သွားနိုင်ပါတယ်။ state_dict ကိုပဲ save လုပ်ခြင်းက ဒီပြဿနာကို ရှောင်ပေးပါတယ် — architecture ကို code ထဲမှာ class အနေနဲ့ ထားရှိပြီး (အမြဲပြန် build လို့ရအောင်)၊ load လုပ်တဲ့အခါ လုပ်ငန်းစဉ် နှစ်ဆင့် ရှင်းရှင်းလင်းလင်း ဖြစ်သွားပါတယ် — architecture အသစ်တစ်ခု instantiate လုပ်ပြီး load_state_dict() ခေါ်ကာ save ထားတဲ့ tensor တွေကို ထဲကို copy လုပ်ပါ။ ဒါက checkpoint တွေကို code ပြောင်းလဲမှုတွေနဲ့ ကျော်လွှားနိုင်အောင်၊ inspect လုပ်ရလွယ်အောင်၊ share လုပ်ရတာ ပိုလုံခြုံအောင် လုပ်ပေးပါတယ်။ ဒါ့အပြင် မေ့လွယ်ပေမယ့် အရေးကြီးတာက fresh load လုပ်ထားတဲ့ model ဟာ prediction မလုပ်ခင် model.eval() လိုအပ်နေဆဲပါ — dropout (activation တွေကို random ဖျက်တဲ့) လို training-only behavior တွေကို ပိတ်ပေးပြီး batch normalization ကို သိမ်းထားတဲ့ running statistics ဘက်ကို ပြောင်းပေးပါတယ်။ ဒါကို ကျော်သွားရင် inference ဟာ မလိုအပ်ဘဲ nondeterministic (သို့) bias ဖြစ်နိုင်ပါတယ်။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
Tutorial Platform မှာ course review တွေအတွက် train ထားတဲ့ sentiment classifier နဲ့ search ရဲ့ နောက်ကွယ်က ranking model တွေကို server restart တိုင်း (သို့) request တိုင်း ပြန် train စရာ မလိုပါဘူး — တစ်ကြိမ်တည်း train လုပ်ပြီး checkpoint အဖြစ် save ထားကာ request ကို ဖြေတဲ့ API server က load လုပ်အသုံးပြုတာပါ။ state_dict() ကိုပဲ save လုပ်ခြင်း (object တစ်ခုလုံး မဟုတ်ဘဲ) က architecture ကို နောက်ပိုင်း file တွေကြား ရွှေ့ (သို့) refactor လုပ်လို့ရအောင် checkpoint အဟောင်းတွေ မပျက်စေပါဘူး။ Inference serving code path တိုင်း — review အသစ်ကို score ပေးတဲ့ (သို့) search result ကို rank လုပ်တဲ့ endpoint အားလုံး — load လုပ်ပြီးတာနဲ့ .eval() ကို ချက်ချင်း ခေါ်ရပါမယ်။ dropout သုံးထားတဲ့ model မှာ ဒါကို မေ့ရင် review တစ်ခုတည်းအတွက် request တိုင်း sentiment score မတူတာမျိုး ဖြစ်နိုင်ပါတယ် — testing မှာ မမြင်ရပေမယ့် production မှာ ထင်ရှားနေတဲ့ bug အမျိုးအစားပါပဲ။
အတူတူ စမ်းရေးကြည့်မယ်
import torch
import torch.nn as nn
class TinyNet(nn.Module):
def __init__(self):
super().__init__()
self.fc1 = nn.Linear(4, 8)
self.dropout = nn.Dropout(0.5)
self.fc2 = nn.Linear(8, 1)
def forward(self, x):
x = torch.relu(self.fc1(x))
x = self.dropout(x)
return self.fc2(x)
torch.manual_seed(0)
model = TinyNet()
# Quick "training" so weights aren't just fresh init
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
x_train = torch.randn(5, 4)
y_train = torch.randn(5, 1)
for _ in range(20):
optimizer.zero_grad()
loss = torch.nn.functional.mse_loss(model(x_train), y_train)
loss.backward()
optimizer.step()
# Save only the learned parameters, not the whole object
torch.save(model.state_dict(), "tiny_net.pt")
# Simulate a fresh process: recreate the architecture, then load weights
loaded_model = TinyNet()
loaded_model.load_state_dict(torch.load("tiny_net.pt"))
loaded_model.eval() # disable dropout for deterministic inference
model.eval()
sample = torch.randn(1, 4)
with torch.no_grad():
original_output = model(sample)
loaded_output = loaded_model(sample)
print("Original model output:", original_output)
print("Loaded model output: ", loaded_output)
print("Outputs match:", torch.allclose(original_output, loaded_output))Original model output: tensor([[0.0421]])
Loaded model output: tensor([[0.0421]])
Outputs match: True
(တိကျတဲ့ ဂဏန်းတွေက random seed နဲ့ PyTorch version ပေါ်မူတည်ပေမယ့် model နှစ်ခုစလုံးက weight တူတူ load ချထားပြီး eval mode မှာ run နေတာကြောင့် line နှစ်ကြောင်းစလုံး အမြဲတူညီပါတယ်)၅ မိနစ် စမ်းကြည့်
fc1 နောက်မှာ nn.BatchNorm1d ပါတဲ့ hidden layer ဒုတိယတစ်ခု ထပ်ထည့်ပြီး အလားတူ save/load လုပ်ကြည့်ပါ။ loaded_model.eval() ခေါ်တာ (train mode ထားတာနဲ့ ယှဉ်ရင်) output ကို ပြောင်းလဲသလားဆိုတာ dropout နဲ့ batch norm ရဲ့ ကွာခြားချက်ကို output နှစ်ခုလုံး print ထုတ်ပြီး စစ်ဆေးကြည့်ပါ။
သတိလေးတစ်ချက်
state_dict အစား torch.save(model) နဲ့ object တစ်ခုလုံးကို pickle လုပ်ခြင်း — class definition (သို့) file structure ပြောင်းသွားရင် နောက်ပိုင်း load လုပ်တဲ့အခါ ပျက်နိုင်ပါတယ်။
Load လုပ်ပြီးနောက် model.eval() ကို မေ့ခြင်း — dropout ကို active ထားခဲ့ရင် input တူတူအတွက် ခေါ်တိုင်း output မတူတော့ပါဘူး၊ batch norm ကလည်း train ထားတဲ့ running average အစား noisy per-batch statistics ကို သုံးမိပါတယ်။
PyTorch Docs — Saving and Loading Models — Deep Learning