နားလည်ထားရမယ့် အချက်
PyTorch သည် image တစ်ခုကို channel dimension ကို ရှင်းလင်းစွာ ပါဝင်သော tensor တစ်ခုအဖြစ် ကိုယ်စားပြုပြီး၊ convention အရ ထို dimension ကို ရှေ့ဆုံးတွင် ထားသည် — (H, W, C) မဟုတ်ဘဲ (C, H, W) ပုံစံ ဖြစ်သည်။ ဓာတ်ပုံအရောင်တစ်ပုံသည် (3, height, width) ပုံစံ tensor ဖြစ်လာသည် — red, green, blue channel တစ်ခုစီအတွက် 2D grid တစ်ခုစီကို အတူတကွ stack လုပ်ထားခြင်းဖြစ်သည်။ ဤ channels-first layout သည် ကျပန်းမဟုတ်ပါ — convolution operate လုပ်ပုံနှင့် ကိုက်ညီသည် — kernel သေးငယ်တစ်ခုသည် H နှင့် W dimension များပေါ်တွင် slide လုပ်ပြီး C ကိုတော့ kernel က အပြည့်အဝ span လုပ်သည့် dimension အဖြစ် သတ်မှတ်ထားသောကြောင့် C ကို ရှေ့ဆုံးထားခြင်းက conv-heavy code များတွင် indexing နှင့် broadcasting ကို ပိုမို consistent ဖြစ်စေသည်။ disk မှ image များကို load လုပ်သည့် library များ (PIL, image codec အများစု) သည် ပုံမှန်အားဖြင့် (H, W, C) ပုံစံဖြင့် ပေးလေ့ရှိသည် — ၎င်းသည် file တစ်ခုအတွင်း pixel များ ဘယ်လို layout ဖြစ်သည်နှင့် ကိုက်ညီသောကြောင့် ဖြစ်သည် — pixel position တစ်ခုစီအတွက် RGB triplet အပြည့်အစုံ တစ်ခုစီ။ ထို mismatch ကြောင့်ပင် torchvision transform များနှင့် dataset loader များ ရှိနေခြင်းဖြစ်ပြီး၊ ၎င်းတို့က file ၏ သဘာဝ layout ဖြင့် load လုပ်ပြီး PyTorch ၏ tensor convention ဆီ ပြောင်းလဲပေးသောကြောင့် pipeline တစ်ခု setup လုပ်ပြီးသည်နှင့် ဤ conversion ကို လက်ဖြင့် ပြုလုပ်ရန် ရှားပါးသော်လည်း၊ shape mismatch တစ်ခုကို debug လုပ်နေချိန်တွင် ၎င်းကို သိရှိထားရန် အလွန်အရေးကြီးသည်။
grayscale image တစ်ခုသည် channel dimension ကို 3 အစား 1 အဖြစ် ချုံ့ထားသည့် idea အတူတူပင်ဖြစ်သည် — H နှင့် W အတူတူပင်၊ pixel တစ်ခုချင်းစီအတွက် color value သုံးခု အစား intensity value တစ်ခုသာ ရှိသည်။ RGB ကို grayscale ပြောင်းခြင်းသည် channel သုံးခုကို ညီမျှစွာ average လုပ်ရုံသက်သက် မဟုတ်ပါ — အဘယ်ကြောင့်ဆိုသော် လူ့မျက်စိသည် red, green, blue ကို perceived brightness တွင် ညီမျှစွာ ဂရုမစိုက်သောကြောင့် ဖြစ်သည် — physical intensity တူညီသည့်တိုင် green သည် blue ထက် ပိုမိုတောက်ပသည်ဟု မြင်ရသည်။ standard conversion များ (ITU-R BT.601 weight, R/G/B အတွက် ခန့်မှန်း 0.299/0.587/0.114) သည် ထိုအချက်ကို ထည့်သွင်းစဉ်းစားပေးကာ naive numeric average ထက် perceptually accurate brightness map ကို ထုတ်ပေးသည်။ နှစ်ခုစလုံးသည် ပုံမှန်အားဖြင့် တူညီသော်လည်း တစ်ထပ်တည်း မဟုတ်ပါ — အကြောင်းမှာ image အမှန်များတွင် R, G, B channel များသည် နေရာတိုင်းတွင် တူညီနေတတ်ခြင်း ရှားပါးသောကြောင့် ဖြစ်သည်။ ၎င်းသည် model တစ်ခု ဘာကို အမှန်တကယ် မြင်သင့်သလဲ ဆုံးဖြတ်သည့်အခါတိုင်း လက်တွေ့ကျသည် — vision task အချို့ (OCR, edge-based analysis) သည် color အမှန်တကယ် မလိုအပ်ဘဲ grayscale ဖြင့် train လုပ်ခြင်းက memory နှင့် compute ကို ချွေတာပေးနိုင်သော်လည်း၊ အခြား task များ (အရေပြားအရောင် detection, အရွယ်ရောက်မှု classification) ကတော့ color ကို ပစ်ချလိုက်လျှင် လုံးဝပျက်စီးသွားနိုင်သည်။
လက်တွေ့ scenario နဲ့ ချိတ်ကြည့်မယ်
Tutorial Platform ပေါ်တွင် upload လုပ်လိုက်သော profile avatar တစ်ခုချင်းစီသည် သိမ်းဆည်းမီ shape/channel-count စစ်ဆေးမှု တစ်ခု ဖြတ်သန်းရသည် — PNG အဖြစ် သိမ်းထားသော screenshot တစ်ခုသည် RGB အစား RGBA (channel 4 ခု၊ alpha layer ပါ) အဖြစ် ရောက်ရှိလာနိုင်ပြီး၊ scan လုပ်ထားသော ဓာတ်ပုံဟောင်းတစ်ပုံကတော့ single-channel grayscale အဖြစ် ရောက်ရှိလာနိုင်သည် — ၎င်းနှစ်ခုစလုံးကို ((3, H, W) float tensor မျှော်လင့်ထားသော) platform ၏ avatar-appropriateness classifier ထဲ တိုက်ရိုက် ထည့်လိုက်ပါက crash ဖြစ်စေနိုင်သည် (သို့) channel များကို color data အဖြစ် တိတ်တဆိတ် မှားယွင်းစွာ ဖတ်မိစေနိုင်သည် — ထို့ကြောင့် upload pipeline သည် image.shape[0] ကို တိတိကျကျ စစ်ဆေးပြီး၊ alpha channel မရှိမပျောက် ဖယ်ရှားကာ၊ grayscale channel ကို tensor သည် model ဆီ မရောက်မီ သုံးကြိမ် ထပ်ခါထပ်ခါ ပြန်လုပ်ပေးသည်။
အတူတူ စမ်းရေးကြည့်မယ်
import torch
torch.manual_seed(0)
# A synthetic "image" the way PyTorch expects it: channels first.
# 3 color channels (RGB), 8 pixels tall, 8 pixels wide.
rgb_image = torch.rand(3, 8, 8) # values in [0, 1), float32
print("RGB image shape (C, H, W):", rgb_image.shape)
# torchvision/PIL usually hand you (H, W, C) instead -- flip between
# the two layouts with permute (never reshape/view for this!).
hwc_image = rgb_image.permute(1, 2, 0)
print("Same image as (H, W, C):", hwc_image.shape)
# Standard luminance-weighted grayscale conversion (ITU-R BT.601 weights).
# This collapses the channel dimension from 3 down to 1.
weights = torch.tensor([0.299, 0.587, 0.114]).view(3, 1, 1)
grayscale = (rgb_image * weights).sum(dim=0, keepdim=True)
print("Grayscale shape (1, H, W):", grayscale.shape)
# A naive plain average gives a similar but not identical result,
# because it ignores that human eyes are more sensitive to green.
naive_gray = rgb_image.mean(dim=0, keepdim=True)
print("Naive average vs weighted differ:", not torch.allclose(grayscale, naive_gray))ဤသို့ print ထုတ်သည်: RGB image shape (C, H, W): torch.Size([3, 8, 8])၊ Same image as (H, W, C): torch.Size([8, 8, 3])၊ Grayscale shape (1, H, W): torch.Size([1, 8, 8])၊ နှင့် Naive average vs weighted differ: True — နောက်ဆုံးလိုင်းသည် True ဖြစ်ရသည့်အကြောင်းမှာ BT.601 weight များ (0.299/0.587/0.114) သည် ညီမျှသော 1/3 average နှင့် မတူညီသောကြောင့်ဖြစ်ပြီး၊ ကျပန်း generate လုပ်ထားသော image တစ်ခုအတွက် ရလဒ်နှစ်ခုသည် အတိအကျ ကိုက်ညီနိုင်ခြေ အလွန်နည်းသောကြောင့် ဖြစ်သည်။၅ မိနစ် စမ်းကြည့်
code ကို extend လုပ်ပြီး .unsqueeze(0) သုံးကာ batch dimension ထည့်ကာ (3, H, W) image ကို (1, 3, H, W) batch တစ်ခုအဖြစ် ပြောင်းပါ၊ ပြီးလျှင် weights tensor ၏ shape ကို မပြောင်းဘဲ image N ခု batch တစ်ခုလုံး (shape (N, 3, H, W) → (N, 1, H, W)) အတွက် တစ်ပြိုင်နက် အလုပ်လုပ်နိုင်သော grayscale conversion ဗားရှင်းတစ်ခု ရေးပါ။
သတိလေးတစ်ချက်
(C, H, W) နှင့် (H, W, C) ကြား ပြောင်းရန် .permute() အစား .view() (သို့) .reshape() ကို သုံးခြင်း — reshape သည် pixel value များကို မရွှေ့ဘဲ flat memory buffer အတူတူကိုသာ ပြန်ဖတ်ခြင်းဖြစ်၍ transpose လုပ်မည့်အစား image ကို တိတ်တဆိတ် ရောရှက်ပစ်လိုက်သည်။
image tensor တစ်ခုစီတွင် channel 3 ခု အတိအကျ ရှိမည်ဟု hardcode လုပ်ယူဆခြင်း — upload အမှန်များသည် channel 4 ခုရှိသော RGBA (alpha ပါ) (သို့) channel 1 ခုရှိသော grayscale အဖြစ် ရောက်ရှိလာနိုင်ပြီး၊ grayscale tensor တစ်ခုပေါ်တွင် 'blue' အတွက် image[2] ကို index လုပ်ခြင်းသည် index error ဖြင့် crash ဖြစ်စေသည် (သို့) အချည်းနှီး data ကို ဖတ်မိစေသည်။
Wikipedia — Digital image — Computer Vision