এই পর্বে যা শিখবেন
- Augmentation কেন কাজ করে: label যেসব বদলে বদলায় না (invariance), সেগুলো ডেটা দিয়ে শেখানো
- Flip, crop, rotation, brightness আর contrast কী করে, আর কোন রূপান্তর label ভেঙে দেয়
torchvision.transforms.v2-এ pipeline, RandAugment আর TrivialAugmentWide- Mixup আর CutMix-এর সূত্র, মেশানো label হাতে হিসাব, আর batch-এ
v2.MixUp/v2.CutMix - Test-time augmentation, train-only নিয়ম, আর CIFAR-10-এ চার রকম augmentation-এর সত্যিকারের তুলনা
পর্ব ১৬ (Deep learning-এ regularization)-এর শেষে দেখেছিলেন, ১০০০টি ছবিতে dropout বা weight decay accuracy প্রায় নড়াতে পারেনি; আসল সীমা ছিল ডেটার পরিমাণ। নতুন ছবি তোলা দামি। Data augmentation একটি সস্তা বিকল্প: হাতের ছবিগুলো থেকেই এমন নতুন রূপ বানানো, যার label জানা। পর্ব ১১-এ transforms.v2-এর এক ঝলক দেখেছেন; ছবি যে pixel-এর array, তা AI-এর ভিত্তি কোর্সের পর্ব ১৮-এ। Convolution কীভাবে ছবি পড়ে, তা পর্ব ২০ (Convolution — filter, stride, padding আর receptive field)-এ। এই পর্বের চারটি ব্লক ব্রাউজারে চলে (NumPy আর matplotlib; প্রথমবার প্যাকেজ নামতে কয়েক সেকেন্ড লাগে)। PyTorch-এর সব আউটপুট ডেস্কটপের সত্যিকারের run থেকে: Python 3.14, PyTorch 2.15.0, torchvision 0.29.2 (CPU build), লেখার সময়ের সংস্করণ।
১ · Augmentation মানে invariance শেখানো
কারওয়ান বাজারের একজন অভিজ্ঞ সবজিওয়ালার কথা ভাবুন। ভোরের আবছা আলোয়, দুপুরের কড়া রোদে, ঝুড়িতে উল্টো করে রাখা, অর্ধেক পাতায় ঢাকা: সব অবস্থায় তিনি লাউ চেনেন। বছরের পর বছর তিনি একই লাউ নানা রূপে দেখেছেন। নতুন শিক্ষানবিশ যদি শুধু সকালের আলোয় সোজা রাখা লাউ দেখে শেখে, বিকেলে সে ভুল করবে। Augmentation হলো শিক্ষানবিশকে ইচ্ছা করে নানা আলো আর কোণ দেখানো।
গাণিতিক ভাষায়, ধরুন T একটি রূপান্তর (যেমন আয়নার মতো উল্টানো)। যদি প্রতিটি ছবি x-এর জন্য label(T(x)) = label(x) হয়, তাহলে বলা হয় label এই রূপান্তরে invariant, অর্থাৎ অপরিবর্তিত থাকে। Model নিজে থেকে এটা জানে না। Training-এর সময় x-এর বদলে এলোমেলো T(x) দিলে আমরা model-কে সেই জ্ঞানটুকু ডেটার মাধ্যমে দিই। তাই augmentation আসলে আপনার ডোমেইন-জ্ঞান: কোন বদল গুরুত্বহীন, তা আপনিই ঠিক করেন। ভুল জ্ঞান দিলে (যেমন অঙ্ক ঘোরানো) model ভুল জিনিস শেখে।
Convolution layer কিছু invariance নিজেই কাঠামোয় বহন করে: ছবির বস্তু একটু সরলে feature map-ও সরে (পর্ব ২০-এ বিস্তারিত)। কিন্তু উল্টানো, ঘোরানো, আলো বা আকারের বদল CNN-এর কাঠামোয় নেই; সেগুলো ডেটা থেকে শিখতে হয়। নিচের ছবিতে পুরো পরিকল্পনা: train-এর পথে random রূপান্তর, validation আর test-এর পথে নয়।
Train আর test-এর দুই পথ
Random transforms only on the training path; normalization statistics come from training data only
২ · জ্যামিতি আর রং — NumPy-তে প্রতিটি রূপান্তর
ছবির রূপান্তর দুই দলে পড়ে। জ্যামিতিক: pixel-এর জায়গা বদলায়, মান নয় (উল্টানো, ঘোরানো, কাটা, সরানো)। রঙের (photometric): জায়গা একই, মান বদলায় (brightness, contrast, saturation-এর jitter). নিচের ব্লকে বাজারঘরের একটি কৃত্রিম ১৬ × ১৬ “ব্যাগ”-এর ছবি: কমলা শরীর, বাঁ দিকে হাতল আর ডান দিকে নীল logo, যাতে উল্টানো চোখে পড়ে। প্রতিটি রূপান্তর NumPy-র এক-দুই লাইন।
import numpy as np, matplotlib.pyplot as plt
img = np.full((16, 16, 3), 0.92) # বাজারঘরের কৃত্রিম "ব্যাগ": সাদা পটভূমি
img[6:15, 3:13] = [0.85, 0.35, 0.15] # কমলা ব্যাগের শরীর
img[2:7, 4] = img[2, 4:9] = img[2:7, 8] = 0.2 # বাঁ দিকে হাতল (তাই ছবিটি প্রতিসম নয়)
img[8:11, 9:12] = [0.15, 0.35, 0.8] # ডান দিকে নীল logo
rng = np.random.default_rng(42)
def crop(x, pad=2): # চারপাশে pad, তারপর এলোমেলো 16x16 টুকরো
p = np.pad(x, ((pad, pad), (pad, pad), (0, 0)), mode="edge")
i, j = rng.integers(0, 2 * pad + 1, 2)
return p[i:i + 16, j:j + 16]
def jitter(x, b, c): # brightness যোগ, contrast গড়ের চারপাশে গুণ
m = x.mean()
return np.clip((x - m) * c + m + b, 0, 1)
views = {"original": img, "h-flip": img[:, ::-1], "v-flip": img[::-1],
"rot90": np.rot90(img), "pad+crop": crop(img),
"brightness +0.2": jitter(img, 0.2, 1.0), "contrast x1.6": jitter(img, 0.0, 1.6),
"random jitter": jitter(img, rng.uniform(-0.2, 0.2), rng.uniform(0.6, 1.4))}
for name, v in views.items():
print(f"{name:15s} {v.shape} গড় {v.mean():.3f} std {v.std():.3f} logo-র কলাম {np.argwhere(v[..., 2] - v[..., 0] > 0.3)[:, 1].mean():.1f}")
fig, ax = plt.subplots(2, 4, figsize=(8, 4.4))
for a, (name, v) in zip(ax.ravel(), views.items()):
a.imshow(v, interpolation="nearest"); a.set_title(name, fontsize=9); a.axis("off")
fig.suptitle("One product image, eight training views (same label: bag)"); plt.show()আউটপুট (পাতার Python-এ চালানো; নিচে আটটি ছবির grid):
original (16, 16, 3) গড় 0.721 std 0.304 logo-র কলাম 10.0
h-flip (16, 16, 3) গড় 0.721 std 0.304 logo-র কলাম 5.0
v-flip (16, 16, 3) গড় 0.721 std 0.304 logo-র কলাম 10.0
rot90 (16, 16, 3) গড় 0.721 std 0.304 logo-র কলাম 9.0
pad+crop (16, 16, 3) গড় 0.740 std 0.296 logo-র কলাম 9.0
brightness +0.2 (16, 16, 3) গড় 0.843 std 0.256 logo-র কলাম 10.0
contrast x1.6 (16, 16, 3) গড় 0.725 std 0.422 logo-র কলাম 10.0
random jitter (16, 16, 3) গড় 0.701 std 0.384 logo-র কলাম 10.0
আকার সবসময় (16, 16, 3): model-এর input-এর আকার বদলায় না। Flip আর rot90-এ গড় আর std হুবহু একই, কারণ শুধু pixel-এর জায়গা বদলেছে; logo-র কলাম ১০ থেকে h-flip-এ ৫, অর্থাৎ ডান থেকে বাঁয়ে গেল। Pad+crop ছবিকে একটু সরায় (কিনারার pixel দিয়ে ভরাট, তাই গড় একটু বদলায়)। Brightness +০.২ গড় বাড়ায় ০.৭২১ থেকে ০.৮৪৩, কিন্তু std কমে ০.২৫৬-এ, কারণ সাদা পটভূমি ১-এ আটকে যায় (clip). Contrast ×১.৬ গড় প্রায় একই রেখে std বাড়ায় ০.৪২২-এ। চিত্রে আটটি ছবিই ব্যাগ; label একটিই।
নিচের stepper একটি ৩ × ৩ ধূসর ছবিতে প্রতিটি রূপান্তরের হিসাব দেখায়, শেষে normalization সহ।
| রূপান্তর | |
| ছবি | |
| গড় | |
| কী বদলাল |
- আসল ছবি. নয়টি pixel, গড় ০.৫. এটিই প্রতিটি পরের ধাপের শুরু।
- h-flip. প্রতিটি সারি উল্টো: প্রথম সারি ০.১ ০.২ ০.৩ থেকে ০.৩ ০.২ ০.১. মান একই, তাই গড়ও ০.৫.
- rot90.
np.rot90ঘড়ির কাঁটার উল্টো দিকে ৯০° ঘোরায়: শেষ কলাম (০.৩ ০.৬ ০.৯) হয়ে যায় প্রথম সারি। গড় এখনো ০.৫. - pad + crop. চারপাশে ০-এর এক ঘর বাড়িয়ে ৫ × ৫, তারপর ওপর-বাঁ কোণ থেকে ৩ × ৩ কাটা। ছবি নিচে-ডানে এক ঘর সরে গেল, ডান আর নিচের সারি হারাল; গড় ১.২/৯ = ০.১৩৩. Random crop প্রতিবার আলাদা কোণ বাছে।
- brightness +০.২. সব মানে ০.২ যোগ; ০.৯ + ০.২ = ১.১, কিন্তু pixel ১-এর বেশি হতে পারে না, তাই ১.০ (clip). গড় (৪.৫ + ১.৮ − ০.১)/৯ = ০.৬৮৯.
- contrast ×১.৫. সূত্র (x − গড়) × ১.৫ + গড়: ০.১ → −০.১ → clip ০, ০.৯ → ১.১ → clip ১. দুই দিকে সমান clip, তাই গড় ০.৫ থাকল; অন্ধকার আরও অন্ধকার, উজ্জ্বল আরও উজ্জ্বল।
- normalize. শেষ ধাপ, train আর test দুই পথেই: (x − ০.৫)/০.২৫. ০.১ → −১.৬, ০.৯ → ১.৬, গড় ০. μ আর σ আসে শুধু train ছবি থেকে; এটি augmentation নয়, প্রতিবার একই হিসাব।
৩ · যে augmentation label ভেঙে দেয়
প্রতিটি রূপান্তরের আগে একটি প্রশ্ন: এই বদলের পরে একজন মানুষ কি একই label দেবেন? বাজারঘরের ব্যাগ উল্টালে ব্যাগ। কিন্তু অঙ্ক “6” ১৮০° ঘোরালে “9”, ডান তীর আয়নায় বাঁ তীর, আর বাংলা “ব” আয়নায় উল্টালে কোনো অক্ষরই থাকে না। নিচের ব্লকে ৭ × ৫ pixel-এর “6” আর “9”, আর একটি সরল template classifier, যা প্রতিটি ছবিকে সবচেয়ে বেশি মেলা template-এর label দেয়।
import numpy as np, matplotlib.pyplot as plt
def bitmap(rows): return np.array([[c == "#" for c in r] for r in rows], float)
six = bitmap([".###.", "#....", "#....", "####.", "#...#", "#...#", ".###."])
nine = bitmap([".###.", "#...#", "#...#", ".####", "....#", "....#", ".###."])
right = bitmap(["..#..", "...#.", "#####", "...#.", "..#.."]) # তীর: "ডানে"
left = bitmap(["..#..", ".#...", "#####", ".#...", "..#.."]) # তীর: "বাঁয়ে"
templates = {"6": six, "9": nine}
def nearest(x): # সবচেয়ে মেলে এমন template (pixel মিল %)
s = {k: (x == t).mean() for k, t in templates.items()}
return max(s, key=s.get), {k: round(v * 100) for k, v in s.items()}
rng = np.random.default_rng(42)
views = {"6 (original)": six,
"6 + noise": np.clip(six + (rng.random(six.shape) < 0.06), 0, 1),
"6 shifted": np.roll(six, 1, axis=1),
"6 rotated 180": np.rot90(six, 2)}
for name, v in views.items():
label, score = nearest(v)
print(f"{name:14s} -> template বলছে '{label}' মিল% {score}")
print("rot180(6) হুবহু 9?", np.array_equal(np.rot90(six, 2), nine))
print("h-flip(ডান তীর) হুবহু বাঁ তীর?", np.array_equal(right[:, ::-1], left))
pics = list(views.items()) + [("arrow 'right'", right), ("h-flip: 'left'!", right[:, ::-1])]
fig, ax = plt.subplots(1, 6, figsize=(9, 2.2))
for a, (name, v) in zip(ax, pics):
a.imshow(v, cmap="gray_r", interpolation="nearest"); a.set_title(name, fontsize=8); a.axis("off")
fig.suptitle("Some transforms keep the label, some change it"); plt.show()আউটপুট (পাতার Python-এ চালানো; নিচে ছয়টি ছবি):
6 (original) -> template বলছে '6' মিল% {'6': 100, '9': 83}
6 + noise -> template বলছে '6' মিল% {'6': 97, '9': 80}
6 shifted -> template বলছে '6' মিল% {'6': 60, '9': 54}
6 rotated 180 -> template বলছে '9' মিল% {'6': 83, '9': 100}
rot180(6) হুবহু 9? True
h-flip(ডান তীর) হুবহু বাঁ তীর? True
Noise আর এক ঘর সরানো “6”-কে classifier এখনো “6” বলে (মিল ৯৭% আর ৬০%). কিন্তু ১৮০° ঘোরানো “6” হুবহু “9”-এর সমান (True), মিল ১০০%. এখন যদি এমন ঘোরানো ছবি “6” label নিয়ে train-এ ঢোকে, তাহলে আমরা model-কে নিজেই ভুল শেখাচ্ছি: একই ছবির দুই label. ডান তীরের h-flip হুবহু বাঁ তীর; ট্রাফিক চিহ্ন বা “ডানে যান” ধরনের নির্দেশনার ছবিতে h-flip তাই নিষিদ্ধ। শিক্ষা: ছোট ঘোরানো (±১৫°-এর মতো) অঙ্কে সাধারণত নিরাপদ, ১৮০° নয়। কতটুকু নিরাপদ, তা কাজ আর ডেটা দেখে ঠিক করতে হয়।
| কাজ | সাধারণত নিরাপদ | ঝুঁকিপূর্ণ বা নিষিদ্ধ |
|---|---|---|
| পণ্যের ছবি (বাজারঘর) | h-flip, crop, হালকা রং | খুব জোরালো রং (লাল জামা নীল হলে “রং” শ্রেণি ভাঙে) |
| অঙ্ক, হাতের লেখা, বাংলা টেক্সট | ছোট ঘোরানো, সরানো, কালির পুরুত্ব, blur | flip, বড় ঘোরানো (6/9, ক/ক-এর আয়না) |
| চিকিৎসার ছবি (যেমন বুকের X-ray) | ছোট সরানো, contrast | h-flip: হৃদপিণ্ডের দিক বদলায়; রোগনির্ণয়ের তথ্য হারাতে পারে |
| উপগ্রহ বা ওপর থেকে তোলা ছবি | সব দিকে ঘোরানো ও flip | ছায়ার দিক যদি সময়ের সংকেত হয় |
| Object detection (পর্ব ২৫) | flip, crop, scale | Box আর mask না বদলে শুধু ছবি বদলানো |
বাংলা লেখার ছবি (OCR): দোকানের রসিদ বা হাতে লেখা ঠিকানা পড়ার OCR model-এ flip সবসময় নিষিদ্ধ। কাজে আসে ছোট ঘোরানো (কাগজ বাঁকা রাখা), সামান্য perspective, blur, কালির পুরুত্ব বদল, কাগজের দাগ বা noise, উজ্জ্বলতার বদল। মাত্রা (ওপরের রেখা) আর কার-চিহ্নের অবস্থানে অর্থ বদলায়, তাই জোরালো elastic বিকৃতি বা বেশি crop অক্ষরের অংশ কেটে label ভাঙতে পারে। Crop করলে লেখার লাইন যেন না কাটে, তা দেখে নিন। এটি একটি সাধারণ নির্দেশনা; নির্দিষ্ট ডেটায় কোনটি কাজ করে, তা মেপে দেখতে হয়।
৪ · torchvision.transforms.v2 — pipeline, RandAugment আর TrivialAugment
বাস্তবে এই রূপান্তর হাতে লেখা হয় না। torchvision.transforms.v2-এর প্রতিটি রূপান্তর একটি object; v2.Compose সেগুলো ক্রমে চালায়, আর প্রতিবার ডাকলে নতুন random মান টানে। প্রচলিত কয়েকটি: RandomResizedCrop (ছবির একটি এলোমেলো অংশ, এলোমেলো আকার-অনুপাতে কেটে নির্দিষ্ট মাপে আনে), RandomCrop (padding দিয়ে সরানো), RandomHorizontalFlip, RandomRotation, ColorJitter. v2-এর একটি বড় সুবিধা: ছবির সঙ্গে bounding box আর segmentation mask (tv_tensors) দিলে একই flip বা crop সেগুলোতেও প্রয়োগ হয়, যা পর্ব ২৫ (Object detection আর segmentation)-এ লাগবে।
কোন রূপান্তর কত জোরে দেবেন, তা বাছা ঝামেলার। AutoAugment (Cubuk ও সহকর্মী, CVPR ২০১৯) reinforcement learning দিয়ে একটি “নীতি” খুঁজেছিল, যার জন্য অনেক GPU-ঘণ্টা লেগেছিল বলে প্রবন্ধে উল্লেখ আছে। RandAugment (Cubuk ও সহকর্মী, ২০২০) সেই খোঁজ বাদ দিয়ে মাত্র দুটি সংখ্যা রাখে: প্রতিটি ছবিতে কয়টি রূপান্তর (N) আর কত জোরে (M); তালিকা থেকে N টি এলোমেলো বাছাই। TrivialAugment (Müller আর Hutter, ICCV ২০২১) আরও সরল: প্রতিটি ছবিতে একটিই রূপান্তর, এলোমেলো শক্তিতে, কোনো knob নেই। তালিকায় থাকে rotate, shear, translate, brightness, contrast, sharpness, posterize, solarize, equalize ইত্যাদি। নিচের run একটি CIFAR-10 ছবিতে এগুলো চালায়, আর batch-এ MixUp/CutMix-এর আউটপুটের আকার দেখায় (অংশ ৫-এ ব্যাখ্যা)।
ds = torchvision.datasets.CIFAR10("data", train=True, download=False)
img = tv_tensors.Image(torch.from_numpy(ds.data[0]).permute(2, 0, 1)) # (3, 32, 32) uint8
print("ছবি:", ds.classes[ds.targets[0]], tuple(img.shape), img.dtype)
torch.manual_seed(0)
train_tf = v2.Compose([
v2.RandomResizedCrop(32, scale=(0.6, 1.0), antialias=True),
v2.RandomHorizontalFlip(p=0.5),
v2.ColorJitter(brightness=0.3, contrast=0.3, saturation=0.3),
v2.ToDtype(torch.float32, scale=True), # 0..255 -> 0..1
])
a, b = train_tf(img), train_tf(img)
print("দুবার ডাকলে:", tuple(a.shape), a.dtype, "| একই?", torch.equal(a, b),
f"| গড় {a.mean():.3f} বনাম {b.mean():.3f}")
for t in [v2.TrivialAugmentWide(), v2.RandAugment(num_ops=2, magnitude=9), v2.AutoAugment()]:
out = t(img)
print(type(t).__name__, "->", tuple(out.shape), out.dtype, f"| বদলানো pixel {(out != img).float().mean():.2f}")
batch = default_collate([(torch.rand(3, 32, 32), y) for y in [0, 1, 2, 3]])
for m in (v2.MixUp(alpha=1.0, num_classes=10), v2.CutMix(alpha=1.0, num_classes=10)):
x, y = m(*batch)
print(type(m).__name__, tuple(x.shape), "label", tuple(y.shape), "প্রথম সারি:", [round(v, 3) for v in y[0].tolist()[:4]])সত্যিকারের run (ডেস্কটপ):
~/bazarghar-dl$ uv run python tf_demo.py
torch 2.15.0+cpu | torchvision 0.29.2+cpu
ছবি: frog (3, 32, 32) torch.uint8
দুবার ডাকলে: (3, 32, 32) torch.float32 | একই? False | গড় 0.391 বনাম 0.350
TrivialAugmentWide -> (3, 32, 32) torch.uint8 | বদলানো pixel 0.48
RandAugment -> (3, 32, 32) torch.uint8 | বদলানো pixel 0.99
AutoAugment -> (3, 32, 32) torch.uint8 | বদলানো pixel 0.99
MixUp (4, 3, 32, 32) label (4, 10) প্রথম সারি: [0.992, 0.0, 0.0, 0.008]
CutMix (4, 3, 32, 32) label (4, 10) প্রথম সারি: [0.984, 0.0, 0.0, 0.016]
এই CIFAR-10 ছবিটি একটি ব্যাঙ, আকার (3, 32, 32), dtype uint8. একই train_tf দুবার ডাকলে ফল আলাদা (False; গড় ০.৩৯১ বনাম ০.৩৫০): প্রতিবার নতুন crop, flip আর রং। ToDtype(..., scale=True) ০–২৫৫-কে ০–১-এ আনে। TrivialAugmentWide এবার pixel-এর প্রায় অর্ধেক বদলেছে, RandAugment আর AutoAugment প্রায় সব (০.৯৯); কোন রূপান্তর পড়বে, তা প্রতিবার এলোমেলো, তাই এই সংখ্যাও প্রতিবার আলাদা। এই তিনটি uint8 ছবিতে চলে, তাই pipeline-এ ToDtype-এর আগে বসে। শেষ দুই লাইন: MixUp আর CutMix-এর পরে label (4,) থেকে (4, 10) হয়েছে। প্রথম নমুনার label-এ শ্রেণি ০-তে ০.৯৯২ আর শ্রেণি ৩-এ ০.০০৮: সে জোড়া পেয়েছে batch-এর শেষ নমুনার (শ্রেণি ৩) সঙ্গে, আর α = ১-এ এবারের λ ঘটনাক্রমে প্রায় ১.
৫ · Mixup আর CutMix — ছবিও মেশে, label-ও মেশে
এ পর্যন্ত প্রতিটি রূপান্তর একটি ছবিকে বদলেছে, label অপরিবর্তিত। Mixup (Zhang ও সহকর্মী, ICLR ২০১৮) দুটি আলাদা ছবি আর তাদের one-hot label একই অনুপাতে মেশায়:
x̃ = λ·xA + (1 − λ)·xB, ỹ = λ·yA + (1 − λ)·yB
এখানে λ (lambda) ০ আর ১-এর মধ্যে একটি সংখ্যা, প্রতিবার Beta(α, α) distribution থেকে টানা; α ছোট (যেমন ০.২) হলে λ বেশিরভাগ সময় ০ বা ১-এর কাছে, অর্থাৎ বেশিরভাগ ছবি প্রায় আসল। ỹ আর one-hot নয়, একটি soft label; cross-entropy তা সরাসরি নিতে পারে (পর্ব ১৩-এ label smoothing-এর soft target-এর মতো)। CutMix (Yun ও সহকর্মী, ICCV ২০১৯) মেশানোর বদলে A-এর ভেতরে B থেকে একটি আয়তক্ষেত্র কেটে বসায়; বাক্সের ক্ষেত্রফল হয় মোটের (১ − λ) ভাগ, আর label-এর অনুপাত সেই ক্ষেত্রফল থেকে। পুরোনো Cutout (DeVries আর Taylor, ২০১৭) বাক্সটি শুধু কালো করত; CutMix সেখানে অন্য ছবির তথ্য বসায়।
import numpy as np, matplotlib.pyplot as plt
classes = ["shirt", "mug", "bag"] # বাজারঘরের তিনটি শ্রেণি
A = np.full((16, 16, 3), 0.9); A[3:14, 4:12] = [0.2, 0.5, 0.9]; A[3:7, 1:15] = [0.2, 0.5, 0.9] # নীল জামা
B = np.full((16, 16, 3), 0.9); B[5:14, 3:11] = [0.8, 0.2, 0.2]; B[7:11, 11:14] = [0.8, 0.2, 0.2] # লাল মগ
yA, yB = np.eye(3)[0], np.eye(3)[1] # one-hot label
lam = 0.7 # Mixup: পুরো ছবি আর label একই অনুপাতে মেশানো
mix = lam * A + (1 - lam) * B
y_mix = lam * yA + (1 - lam) * yB
H = W = 16; r = np.sqrt(1 - lam) # CutMix: B থেকে (1 - λ) ক্ষেত্রফলের বাক্স
h, w = int(H * r), int(W * r); cy, cx = 4, 13 # বাক্সের কেন্দ্র (সাধারণত এলোমেলো)
y0, y1 = max(cy - h // 2, 0), min(cy + h // 2, H); x0, x1 = max(cx - w // 2, 0), min(cx + w // 2, W)
cut = A.copy(); cut[y0:y1, x0:x1] = B[y0:y1, x0:x1]
lam_cut = 1 - (y1 - y0) * (x1 - x0) / (H * W) # কিনারায় কাটা পড়লে λ নতুন করে হিসাব
y_cut = lam_cut * yA + (1 - lam_cut) * yB
np.set_printoptions(precision=3, suppress=True)
print("শ্রেণি", classes, "| Mixup λ =", lam, "label:", y_mix)
print(f"CutMix বাক্স {y1 - y0}x{x1 - x0} (চাওয়া {h}x{w}), λ = {lam_cut:.3f} label: {y_cut}")
p = np.array([0.6, 0.3, 0.1]) # model-এর একটি অনুমান
ce = lambda y: -(y * np.log(p)).sum()
print(f"CE(p, y_mix) = {ce(y_mix):.4f} = 0.7·CE(p,yA) + 0.3·CE(p,yB) = {lam * ce(yA) + (1 - lam) * ce(yB):.4f}")
lams = np.random.default_rng(42).beta(0.2, 0.2, 10000) # α = 0.2 হলে λ প্রায়ই 0 বা 1-এর কাছে
print("Beta(0.2, 0.2) থেকে λ: গড়", lams.mean().round(3), "| 0.1-এর কম বা 0.9-এর বেশি:", ((lams < 0.1) | (lams > 0.9)).mean().round(3))
fig, ax = plt.subplots(1, 4, figsize=(8, 2.4))
for a, im, t in zip(ax, [A, B, mix, cut], ["A: shirt", "B: mug", "Mixup 0.7/0.3", f"CutMix {lam_cut:.2f}/{1 - lam_cut:.2f}"]):
a.imshow(im, interpolation="nearest"); a.set_title(t, fontsize=9); a.axis("off")
plt.show()আউটপুট (পাতার Python-এ চালানো; নিচে চারটি ছবি):
শ্রেণি ['shirt', 'mug', 'bag'] | Mixup λ = 0.7 label: [0.7 0.3 0. ]
CutMix বাক্স 8x7 (চাওয়া 8x8), λ = 0.781 label: [0.781 0.219 0. ]
CE(p, y_mix) = 0.7188 = 0.7·CE(p,yA) + 0.3·CE(p,yB) = 0.7188
Beta(0.2, 0.2) থেকে λ: গড় 0.501 | 0.1-এর কম বা 0.9-এর বেশি: 0.675
Mixup-এর label [০.৭, ০.৩, ০]: ৭০% জামা, ৩০% মগ, ব্যাগ শূন্য। CutMix-এ চাওয়া ক্ষেত্রফল ০.৩, তাই বাক্সের বাহু ১৬ × √০.৩ ≈ ৮.৭৬, পূর্ণসংখ্যায় ৮. কিন্তু কেন্দ্র (৪, ১৩) ডান কিনারার কাছে, তাই বাক্স কেটে ৮ × ৭ হলো, ক্ষেত্রফল ৫৬/২৫৬ = ০.২১৯. তাই আসল λ = ০.৭৮১, ০.৭ নয়; torchvision-ও ঠিক এভাবে কাটা বাক্স থেকে λ নতুন করে হিসাব করে। তৃতীয় লাইন একটি দরকারি সত্য: মেশানো label-এর cross-entropy = দুই label-এর cross-entropy-র ওজনযুক্ত গড় (দুটিই ০.৭১৮৮)। তাই অনেক কোডে loss দুবার হিসাব করে λ দিয়ে মেশানো হয়; ফল একই। শেষ লাইন: Beta(০.২, ০.২) থেকে টানা λ-এর ৬৭.৫% হয় ০.১-এর কম নয়তো ০.৯-এর বেশি।
| ধাপ | |
| হিসাব | |
| ফল |
- দুটি নমুনা. জামার ছবি A আর মগের ছবি B, প্রতিটির one-hot label.
- λ বাছাই. বাস্তবে প্রতিবার Beta(α, α) থেকে এলোমেলো; হিসাবের সুবিধায় এখানে λ = ০.৭.
- Pixel মেশানো. একই জায়গার একটি pixel: A-তে ০.২, B-তে ০.৮. মিশ্রণ ০.৭ × ০.২ + ০.৩ × ০.৮ = ০.৩৮. প্রতিটি pixel-এ একই হিসাব; ছবিটি দেখতে দুটি ছবির স্বচ্ছ উপরিপাতনের মতো।
- Label মেশানো. একই λ দিয়ে: [০.৭, ০.৩, ০]. Model-এর কাছে চাওয়া: “৭০% জামা, ৩০% মগ বলো”, পুরো নিশ্চিত হওয়া নয়।
- Loss. −(০.৭ × ln ০.৬ + ০.৩ × ln ০.৩) = ০.৩৫৭৬ + ০.৩৬১২ = ০.৭১৮৮. ব্যাগের ভাগ ০, তাই ০.১-এর কোনো ভূমিকা নেই।
- CutMix. একই λ = ০.৭ মানে B থেকে মোটের ০.৩ ক্ষেত্রফলের বাক্স। ১৬ × ১৬ ছবিতে বাহু ৮.৭৬, পূর্ণসংখ্যায় ৮; আসল ক্ষেত্রফল ০.২৫, তাই label [০.৭৫, ০.২৫, ০]. বাক্স কিনারায় কাটা পড়লে λ আরও বাড়ে।
- Batch-এ.
v2.MixUp/v2.CutMixপুরো batch-এ একটি λ টানে আর batch-কে এক ঘর ঘুরিয়ে (roll) জোড়া বানায়: নমুনা ০-এর সঙ্গী শেষ নমুনা। Label হয়ে যায় (batch, শ্রেণি) আকারের soft label.
PyTorch-এ এগুলো একটি ছবিতে নয়, পুরো batch-এ কাজ করে, তাই Dataset-এর transform-এ নয়, DataLoader-এর collate_fn-এ বসে (collate-এর কাজ পর্ব ১১-এ)। torchvision-এর ডকুমেন্টেশনের ধরন:
from torch.utils.data import DataLoader, default_collate
from torchvision.transforms import v2
mixer = v2.RandomChoice([v2.MixUp(alpha=0.2, num_classes=10), # প্রতি batch-এ দুটির একটি
v2.CutMix(alpha=1.0, num_classes=10)])
def mix_collate(batch):
return mixer(*default_collate(batch)) # ছবি (B, 3, H, W), label (B, 10)
train_dl = DataLoader(train_ds, batch_size=128, shuffle=True, collate_fn=mix_collate)
# loop-এ: loss = F.cross_entropy(model(x), y) # y নরম label, এটিও চলে
# validation DataLoader-এ mix_collate নয়৬ · Test-time augmentation — একই ছবি কয়েক রূপে দেখে গড়
Augmentation-এর ধারণা inference-এও কাজে লাগানো যায়: test-time augmentation (TTA). একটি test ছবির কয়েকটি স্থির, আগে থেকে ঠিক করা রূপ (যেমন আসল আর আয়না) model-কে দেখিয়ে probability-র গড় নেওয়া। এটি এক ধরনের ছোট ensemble, কিন্তু model একটিই; খরচ view-এর সংখ্যার গুণ। ২০১২ সালের AlexNet প্রবন্ধে test-এ পাঁচটি crop আর তাদের আয়নার মোট দশটি view-এর গড় নেওয়া হয়েছিল। নিচে ৮ × ৮ pixel-এর তিনটি আকৃতির (আড়াআড়ি দাগ, খাড়া দাগ, ফাঁপা বাক্স) উপর একটি ছোট linear (softmax) model; train ছবি ±১ pixel সরানো আর noise-যুক্ত।
import numpy as np
rng = np.random.default_rng(42)
def shape(k): # 8x8-এর তিন শ্রেণি: আড়াআড়ি দাগ, খাড়া দাগ, ফাঁপা বাক্স
x = np.zeros((8, 8))
if k == 0: x[3:5, 1:7] = 1
elif k == 1: x[1:7, 3:5] = 1
else: x[2:6, 2:6] = 1; x[3:5, 3:5] = 0
return x
def make(n, s): # এলোমেলো সরানো (±s pixel) আর noise
y = rng.integers(0, 3, n)
X = np.stack([np.roll(shape(k), tuple(rng.integers(-s, s + 1, 2)), (0, 1)) for k in y])
return X + rng.normal(0, 0.3, X.shape), y
Xtr, ytr = make(300, 1) # train: ±1 pixel সরানো
W = np.zeros((64, 3)); b = np.zeros(3) # ছোট linear (softmax) model
def proba(X):
z = X.reshape(len(X), -1) @ W + b; z -= z.max(1, keepdims=True)
return np.exp(z) / np.exp(z).sum(1, keepdims=True)
for _ in range(300): # সাধারণ gradient descent
G = proba(Xtr) - np.eye(3)[ytr]
W -= 0.5 * Xtr.reshape(300, -1).T @ G / 300; b -= 0.5 * G.mean(0)
views = [lambda X: X, lambda X: X[:, :, ::-1], lambda X: X[:, ::-1]] + [
lambda X, d=d, a=a: np.roll(X, d, a) for d in (-1, 1) for a in (1, 2)] # আসল, দুই flip, চার দিকে সরানো
print(f"train acc {(proba(Xtr).argmax(1) == ytr).mean():.3f} | TTA-তে forward {len(views)} গুণ")
for s in (1, 2): # test: train-এর মতো (±1) আর বেশি সরানো (±2)
Xte, yte = make(600, s)
P1 = proba(Xte); Pt = np.mean([proba(v(Xte)) for v in views], axis=0)
print(f"test ±{s}: একবার {(P1.argmax(1) == yte).mean():.3f} | TTA {(Pt.argmax(1) == yte).mean():.3f}")
if s == 1:
i = np.flatnonzero((P1.argmax(1) != yte) & (Pt.argmax(1) == yte))[0]
print(f" উদাহরণ #{i} (শ্রেণি {yte[i]}): একবার {P1[i].round(2)} -> TTA {Pt[i].round(2)}")আউটপুট (পাতার Python-এ চালানো):
train acc 0.970 | TTA-তে forward 7 গুণ
test ±1: একবার 0.843 | TTA 0.908
উদাহরণ #17 (শ্রেণি 0): একবার [0.12 0. 0.88] -> TTA [0.58 0.01 0.4 ]
test ±2: একবার 0.522 | TTA 0.518
Train-এর মতো সরানো (±১) test-এ TTA accuracy ০.৮৪৩ থেকে ০.৯০৮-এ তুলল। উদাহরণ #১৭: একবার দেখে model ৮৮% নিশ্চিত যে এটি বাক্স (শ্রেণি ২), অথচ আসলে আড়াআড়ি দাগ; সাতটি view-এর গড়ে তা ০.৫৮ আর ০.৪০, এবার ঠিক। কিন্তু test-এ সরানো ±২ হলে, যা model train-এ কখনো দেখেনি, TTA কিছুই করতে পারল না (০.৫২২ থেকে ০.৫১৮)। TTA শুধু model-এর ছোট খামখেয়ালি অস্থিরতা গড় করে কমায়; নতুন ধরনের ডেটা (distribution shift, মেশিন লার্নিং কোর্সের পর্ব ৫৯) সামলায় না। আর TTA-র view-ও label রক্ষাকারী হতে হবে: এখানে তিনটি আকৃতিই আয়নায় একই থাকে বলে flip চলেছে।
৭ · Augmentation শুধু train-এ, normalization-এর পরিসংখ্যান শুধু train থেকে
Validation আর test-এর কাজ একটাই: model বাস্তবে কেমন করবে, তার স্থির, পুনরুৎপাদনযোগ্য মাপ। বাস্তবের ছবিতে কেউ ইচ্ছা করে flip বা jitter দেয় না। তাই validation-এর transform-এ শুধু নির্ধারিত (deterministic) ধাপ: আকার ঠিক করা, tensor-এ রূপান্তর, normalize. দ্বিতীয় নিয়ম normalization নিয়ে: channel-প্রতি গড় μ আর মানবিচ্যুতি σ শুধু train ছবি থেকে মাপা হয়, তারপর সেই সংখ্যা দিয়েই validation আর test normalize হয়। Test থেকে মাপা মানে test-এর তথ্য training-এ ঢোকানো, অর্থাৎ data leakage (AI-এর ভিত্তি কোর্সের পর্ব ৫৬)। ছবির গড়ে এর প্রভাব সাধারণত ছোট, কিন্তু অভ্যাসটি অন্য জায়গায় (যেমন tabular feature-এর standardization) বড় ক্ষতি করে। নিচের stepper সংখ্যাগুলো অংশ ৮-এর সত্যিকারের run থেকে।
| ধাপ | |
| কী ঘটে |
- Train-এর পথ. প্রতিটি ছবি প্রতি epoch-এ নতুন রূপে আসে; এটাই চাই, কারণ model যত বেশি রূপ দেখে, তত কম মুখস্থ করে।
- ভুল: test-এ random. Validation-এ ভুল করে
train_tfদিলে ১০,০০০টি test ছবিতে accuracy ০.৫৮৯. Model একই, weight একই; শুধু ছবিগুলো এলোমেলোভাবে বিকৃত। - আবার মাপা. আবার চালালে ০.৫৮৩: একই model-এর মাপ ০.০০৬ বদলে গেল, শুধু random রূপান্তরের কারণে। দুটি config-এর পার্থক্য এর চেয়ে ছোট হলে এমন তুলনা অর্থহীন।
- ফল কী বোঝায়. দুটিই সঠিক মাপ ০.৭১৩-এর চেয়ে প্রায় ০.১২ কম, কারণ TrivialAugment-এর জোরালো বিকৃতি বাস্তবের ছবির চেয়ে কঠিন। এভাবে validation model-কে অন্যায্যভাবে খারাপ দেখায়, আর জোরালো augmentation-এর config-কে বেশি শাস্তি দেয়।
- μ আর σ. ৫০০০টি train ছবি থেকে তিন channel-এর μ আর σ (০–১ স্কেলে)। এই সংখ্যাই train, validation আর test তিন পথে; test থেকে আলাদা করে মাপা হয় না।
- সঠিক পথ. শুধু ToDtype আর Normalize: accuracy ০.৭১৩, যতবার চালান ততবার একই। এটাই রিপোর্ট করার সংখ্যা।
- ব্যতিক্রম: TTA. TTA-ও test-এ রূপান্তর, কিন্তু স্থির আর label রক্ষাকারী (আসল + আয়না), তারপর probability-র গড়: ০.৭১৯. এটি ইচ্ছাকৃত inference কৌশল; সাধারণ মাপের পাশে আলাদা করে লিখুন।
৮ · CIFAR-10-এ সত্যিকারের ablation — চার রকম augmentation
এবার আসল মাপ। CIFAR-10 (Krizhevsky, ২০০৯; টরন্টো বিশ্ববিদ্যালয়ের সাইট থেকে নামানো) হলো ১০ শ্রেণির ৩২ × ৩২ রঙিন ছবি: বিমান, গাড়ি, পাখি, বিড়াল, হরিণ, কুকুর, ব্যাঙ, ঘোড়া, জাহাজ, ট্রাক। বাজারঘরের পণ্যের বদলে এটিই, কারণ একটি সৎ, পরিচিত ডেটাসেট। কম ডেটার অবস্থা বানাতে train-এ প্রতি শ্রেণির ৫০০টি, মোট ৫০০০টি ছবি; মাপা হয়েছে আলাদা ১০,০০০টি test ছবিতে। Model: ছয়টি 3 × 3 convolution (BatchNorm, ReLU), তিনটি max-pool, global average pooling আর একটি Linear (CNN-এর গঠন পর্ব ২০–২২-এ); SGD, OneCycle schedule (পর্ব ১৫), ৩০ epoch (আর চারটি config ৯০ epoch-এও). একবারে একটিই জিনিস বদলানো হয়েছে, augmentation; একে ablation বলে। মূল অংশ:
Xtr, ytr = Xall[idx], yall[idx] # প্রতি শ্রেণির ৫০০টি, seed 42
mean = (Xtr.float() / 255).mean((0, 2, 3)); std = (Xtr.float() / 255).std((0, 2, 3)) # শুধু train থেকে
norm = [v2.ToDtype(torch.float32, scale=True), v2.Normalize(mean.tolist(), std.tolist())]
basic = [v2.RandomCrop(32, padding=4, padding_mode="reflect"), v2.RandomHorizontalFlip()]
train_tf = v2.Compose({"none": [], "basic": basic, "mix": basic,
"trivial": basic + [v2.TrivialAugmentWide()],
"randaug": basic + [v2.RandAugment(num_ops=2, magnitude=9)]}[args.aug] + norm)
test_tf = v2.Compose(norm) # test-এ কোনো random রূপান্তর নেই
mixer = v2.RandomChoice([v2.MixUp(alpha=0.2, num_classes=10), v2.CutMix(alpha=1.0, num_classes=10)])
def mix_collate(batch): return mixer(*default_collate(batch))
train_dl = DataLoader(Images(Xtr, ytr, train_tf), batch_size=128, shuffle=True, generator=lg,
collate_fn=mix_collate if args.aug == "mix" else None)
...
for ep in range(1, args.epochs + 1):
model.train()
for xb, yb in train_dl:
loss = F.cross_entropy(model(xb), yb) # mix-এ yb হলো (B, 10) নরম label
opt.zero_grad(); loss.backward(); opt.step(); sched.step()
# শেষে model.eval() আর no_grad(): train acc পরিষ্কার (augmentation ছাড়া) train ছবিতে,
# test acc ১০,০০০টি test ছবিতে, আর TTA = আসল + আয়নার probability-র গড়সত্যিকারের run (ডেস্কটপ, CPU-তে, দুটি run একসঙ্গে):
~/bazarghar-dl$ uv run python aug.py --aug none --epochs 30 --seed 0
[none s0] train 1.000 | test 0.698 (TTA 0.719) | gap 0.302 | CPU-তে 229 s
~/bazarghar-dl$ uv run python aug.py --aug basic --epochs 30 --seed 0
[basic s0] train 0.898 | test 0.749 (TTA 0.758) | gap 0.148 | CPU-তে 249 s
… (একইভাবে randaug, trivial, mix; তারপর সব --seed 1; তারপর চারটি --epochs 90)
~/bazarghar-dl$ uv run python summary.py
30 epoch, seed 0 আর 1 (গড়) train │ test │ gap │ TTA
no aug (0.698 / 0.702) 1.000 │ 0.700 │ 0.300 │ 0.719
crop+flip (0.749 / 0.740) 0.889 │ 0.745 │ 0.144 │ 0.753
+ RandAugment (0.736 / 0.743) 0.828 │ 0.739 │ 0.089 │ 0.745
+ TrivialAugment (0.713 / 0.709) 0.777 │ 0.711 │ 0.066 │ 0.717
+ mixup/cutmix (0.731 / 0.715) 0.815 │ 0.723 │ 0.092 │ 0.727
90 epoch, seed 0 train │ test │ gap
no aug 1.000 │ 0.703 │ 0.297
crop+flip 0.997 │ 0.784 │ 0.213
+ TrivialAugment 0.946 │ 0.800 │ 0.145
+ mixup/cutmix 0.974 │ 0.803 │ 0.171
summary.py-এর টেবিলে সময়ের কলাম বাদ দেওয়া হয়েছে; প্রতিটি ৩০-epoch run CPU-তে প্রায় ৩–৫ মিনিট, ৯০-epoch run ১১–১৪ মিনিট লেগেছে (দুটি run একসঙ্গে চলায় সময় তুলনীয় নয়)। Train accuracy মাপা হয়েছে augmentation ছাড়া পরিষ্কার train ছবিতে, eval mode-এ; mixup-এর নরম label-এ “ঠিক উত্তর” গোনার অর্থ নেই বলেই এই নিয়ম। টেবিলটি এভাবে পড়ুন। প্রথম, augmentation ছাড়া model মুখস্থ করে: train ১.০০০, test ০.৭০০, ফারাক ০.৩০; ৯০ epoch-এও test ০.৭০৩, অর্থাৎ বেশি সময় দিয়ে কিছু হয় না। দ্বিতীয়, শুধু crop + flip-ই সবচেয়ে বড় লাভ: ৩০ epoch-এ test ০.৭৪৫, ফারাক অর্ধেক (০.১৪৪); দুই seed-এর মান (০.৭৪৯ আর ০.৭৪০) একে অন্যের কাছে, তাই লাভটি seed-এর খেলা নয়। তৃতীয়, জোরালো augmentation ফারাক সবচেয়ে কমায়, কিন্তু ৩০ epoch-এ test-এ পিছিয়ে: TrivialAugment-এ ফারাক মাত্র ০.০৬৬, test ০.৭১১; model এখনো শেখা শেষ করেনি (train-ও ০.৭৭৭)। ৯০ epoch দিলে ছবি উল্টে যায়: TrivialAugment ০.৮০০ আর mixup/cutmix ০.৮০৩, crop + flip ০.৭৮৪. ছোট ফারাক মানেই ভালো model নয়; underfit model-এরও ফারাক ছোট। চতুর্থ, TTA (আসল + আয়না) প্রতিটি সারিতে test-এ ০.০০৪–০.০১৯ যোগ করল, augmentation ছাড়া model-এ সবচেয়ে বেশি। সীমা মনে রাখুন: ৯০-epoch-এর সারি একটি seed-এর, mixup আর TrivialAugment-এর ০.০০৩ পার্থক্য seed-এর ওঠানামার (৩০ epoch-এ mixup-এর দুই seed-এ ০.০১৬) চেয়ে ছোট, তাই এ দুটির মধ্যে বিজয়ী বলা যায় না। আর test set এখানে শুধু প্রদর্শনের জন্য, কোনো config বাছাইয়ে ব্যবহার হয়নি; নিজের প্রকল্পে এই তুলনা validation-এ করবেন।
৯ · কোনটি কখন — বাছাইয়ের নিয়ম
| কৌশল | কী শেখায় | খরচ | কখন আগে ভাববেন |
|---|---|---|---|
| Crop + flip | অবস্থান আর আয়নায় অপরিবর্তিত | প্রায় শূন্য | প্রায় সব ছবির কাজে প্রথম ধাপ |
| ColorJitter | আলো আর ক্যামেরার পার্থক্য | কম | বিভিন্ন ফোনে তোলা ছবি; রং যেখানে label নয় |
| RandAugment / TrivialAugment | নানা বিকৃতি একসঙ্গে | CPU-তে বেশি সময় | বড় model, লম্বা training |
| Mixup / CutMix | শ্রেণির মাঝে মসৃণ সীমা, কম অতি-নিশ্চয়তা | কম, কিন্তু ধীরে শেখে | লম্বা training; ছোট বা ছোট-সময়ের run-এ সাবধানে |
| TTA | inference-এ অস্থিরতা কমানো | inference × view | সময়ের চাপ নেই এমন batch কাজ |
আগে প্রতিটি রূপান্তর চোখে দেখুন: কয়েকটি augmented ছবি আঁকুন আর জিজ্ঞেস করুন, label কি একই আছে? তারপর crop + flip দিয়ে baseline, তারপর একবারে একটি করে যোগ, কয়েকটি seed-এ validation-এ তুলনা। জোরালো augmentation আর mixup সাধারণত বেশি epoch চায়; অল্প epoch-এ এগুলো খারাপ দেখাতে পারে। Test set শেষে একবার।
আরও গভীরে: Augmentation-এর পুরোনো উদাহরণ Simard, Steinkraus আর Platt-এর ২০০৩ সালের হাতের লেখার কাজ, যেখানে elastic বিকৃতি ব্যবহৃত হয়। Chen, Dobriban আর Lee (JMLR ২০২০) augmentation-কে group theory দিয়ে ব্যাখ্যা করেন: label যদি সত্যিই রূপান্তরে invariant হয়, তাহলে augmentation-এ variance কমে। Mixup-কে “vicinal risk minimization” হিসেবে দেখা হয়: প্রতিটি train বিন্দুর আশপাশের বিন্দুতেও ঠিক উত্তর চাওয়া। AugMix (Hendrycks ও সহকর্মী, ICLR ২০২০) কয়েকটি augmentation-এর শৃঙ্খল মিশিয়ে distribution shift-এ robustness বাড়ানোর কথা বলে। Augmentation-এর সঙ্গে architecture-এর invariance মেলানো (equivariant network) গবেষণার আলাদা ধারা।
পরীক্ষা ও ইন্টারভিউতে:
- λ = ০.৬-এ Mixup, A শ্রেণি ২, B শ্রেণি ০, মোট ৪টি শ্রেণি। label? [০.৪, ০, ০.৬, ০].
- ৩২ × ৩২ ছবিতে CutMix, λ = ০.৭৫; বাক্সের বাহু? ৩২ × √০.২৫ = ১৬, অর্থাৎ ১৬ × ১৬.
- Validation-এ augmentation দেওয়া কেন ভুল? মাপ এলোমেলো আর বাস্তবের ছবির চেয়ে কঠিন; model বাছাই পক্ষপাতদুষ্ট হয়।
- Normalization-এর μ, σ কোথা থেকে? শুধু train থেকে; একই সংখ্যা val আর test-এ।
- অঙ্কের ছবিতে কোন augmentation দেবেন না? Flip আর বড় ঘোরানো: “6” ঘোরালে “9”.
১০ · AI-এর যুগে — AI-এর লেখা augmentation pipeline যাচাই
“CIFAR-এর জন্য augmentation দাও” বললে AI assistant প্রায় সবসময় একটি লম্বা তালিকা দেয়। পরিচিত ভুলগুলো: validation-এ একই train_tf দেওয়া; ImageNet-এর mean/std হুবহু বসানো, যদিও ডেটা আলাদা; পুরোনো transforms আর v2-এর API মেশানো, বা নেই এমন argument বানানো; অঙ্ক বা লেখার ছবিতে flip; Mixup-এর soft label-এর সঙ্গে accuracy-র কোডে y == pred লেখা, যা (B, 10) label-এ ভেঙে যায়; detection-এ ছবি flip করে box না বদলানো; আর একটি seed-এর একটি run দেখে “RandAugment ৫% বাড়ায়” ধরনের দাবি।
AI-কে এভাবে জিজ্ঞেস করুন:
AI-এর augmentation কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।
প্র ০১AlexNet-এর সাফল্যে augmentation-এর ভূমিকা কতটা ছিল, আর তখন থেকে কী বদলেছে?
২০১২ সালের AlexNet প্রবন্ধে (Krizhevsky, Sutskever আর Hinton) augmentation-কে overfitting কমানোর দুটি প্রধান উপায়ের একটি বলা হয়েছিল, অন্যটি dropout. তাঁরা ২৫৬ × ২৫৬ ছবি থেকে এলোমেলো ২২৪ × ২২৪ অংশ কেটে আর আয়নায় উল্টে train-এর ডেটা কার্যত বহু গুণ বাড়ান, আর রঙের জন্য RGB-এর principal component বরাবর এলোমেলো বদল যোগ করেন। Test-এ পাঁচটি crop আর তাদের আয়নার গড় নেওয়া হয়, যা আজকের TTA. প্রবন্ধে বলা হয়েছিল, augmentation ছাড়া তাদের network অনেক বেশি overfit করত; সংখ্যাগুলো প্রবন্ধের নিজস্ব পরীক্ষার, অন্য সেটআপে একই হবে এমন নয়।
এর পরের দশকে তিনটি বদল আসে। প্রথম, ঠিক কোন রূপান্তর, তা হাতে বাছার বদলে খোঁজা শুরু হয়: AutoAugment (২০১৮–২০১৯) অনেক GPU-ঘণ্টা খরচ করে নীতি খোঁজে, তারপর RandAugment আর TrivialAugment দেখায় যে এত খোঁজ ছাড়াও কাছাকাছি ফল পাওয়া যায় বলে তাদের প্রবন্ধে রিপোর্ট। দ্বিতীয়, label মেশানো কৌশল (Mixup, CutMix) আসে, যা একটি ছবির ভেতরের রূপান্তরের বাইরে যায়। তৃতীয়, Vision Transformer-এর মতো কম inductive bias-এর model-এ জোরালো augmentation আর regularization প্রায় অপরিহার্য হয়ে ওঠে বলে অনেক কাজে দেখা যায়, বিশেষ করে মাঝারি আকারের ডেটায়।
আরেকটি পরিবর্তন হলো ডেটার আকার। খুব বড় ডেটায় pretrain করা model-এ augmentation-এর আপেক্ষিক লাভ কমে, তবে fine-tuning-এর ছোট ডেটায় আবার ফিরে আসে (পর্ব ২৩, Transfer learning). অংশ ৮-এর ছোট পরীক্ষায় ৫০০০টি ছবিতে crop আর flip-এর বড় লাভ ঠিক এই প্রবণতার সঙ্গে মেলে।
মূল উপলব্ধি: Augmentation শুরু থেকেই deep learning-এর সাফল্যের অংশ; বদলেছে কীভাবে বাছা হয় (হাতে, খুঁজে, এলোমেলো) আর label-ও মেশানো হয় কি না।
প্র ০২Mixup-এর মেশানো ছবি মানুষের চোখে অর্থহীন; তবু তা model-কে সাহায্য করে কেন?
Mixup-এর ছবি দেখতে দুটি ছবির ভৌতিক উপরিপাতন, বাস্তবে এমন ছবি কখনো আসবে না। তাই প্রথম প্রতিক্রিয়া হয়: এটি নিশ্চয় ক্ষতি করবে। Zhang ও সহকর্মীরা (ICLR ২০১৮) যুক্তি দেন, সাধারণ training (empirical risk minimization) শুধু train বিন্দুগুলোতে ঠিক উত্তর চায়, মাঝখানে model যা খুশি করতে পারে। Mixup দুটি বিন্দুর সংযোগরেখার উপরের বিন্দুতেও ঠিক উত্তর চায়: রেখার ৭০% দূরে থাকলে label-ও ৭০/৩০. ফলে শ্রেণির মাঝের সীমা মসৃণ হয়, আর model দুই শ্রেণির মাঝখানে হঠাৎ ১০০% নিশ্চিত হয়ে ওঠে না।
এর পরোক্ষ ফল calibration-এ: model-এর probability কম অতি-আত্মবিশ্বাসী হয়। Thulasidasan ও সহকর্মীরা (NeurIPS ২০১৯) mixup-এ train করা network-এ calibration ভালো হওয়ার কথা রিপোর্ট করেন। প্রবন্ধে label noise আর কিছু adversarial আক্রমণের বিরুদ্ধে robustness-এর উন্নতিও দেখানো হয়েছিল। তবে সীমাও আছে: অংশ ৮-এর মতো অল্প epoch-এ mixup প্রায়ই পিছিয়ে থাকে, কারণ লক্ষ্য কঠিন হয়ে যায়; আর যে কাজে শ্রেণির মিশ্রণের কোনো অর্থ নেই (যেমন অঙ্কের মাঝামাঝি “৬.৩”), সেখানে ফল অনিশ্চিত।
CutMix এই অর্থহীনতা কিছুটা কমায়: প্রতিটি pixel কোনো একটি আসল ছবির, শুধু জায়গা ভাগ করা। Yun ও সহকর্মীরা (ICCV ২০১৯) যুক্তি দেন, এতে model বস্তুর একটি অংশ দেখেই চিনতে শেখে, যা localization-এও সাহায্য করে বলে তাঁদের পরীক্ষায় দেখা যায়। এক নজরে অদ্ভুত লাগলেও দুটিই একটি নীতি মানে: input যতটা মেশে, label-ও ঠিক ততটা।
মূল উপলব্ধি: Mixup বাস্তব ছবি বানায় না; এটি train বিন্দুর মাঝের জায়গায় model-এর আচরণ নিয়ন্ত্রণ করে, তাই সীমা মসৃণ হয় আর অতি-নিশ্চয়তা কমে।
প্র ০৩AutoAugment-এর মতো খোঁজা নীতির পরে RandAugment আর TrivialAugment-এর মতো সরল পদ্ধতি এত জনপ্রিয় হলো কেন?
AutoAugment (Cubuk ও সহকর্মী, CVPR ২০১৯) একটি controller network-কে reinforcement learning দিয়ে শেখায় কোন রূপান্তর, কত সম্ভাবনায়, কত জোরে দিতে হবে। প্রতিটি প্রস্তাবিত নীতি যাচাই করতে একটি ছোট model পুরো train করতে হয়, তাই খরচ বিশাল; প্রবন্ধে হাজারের ঘরের GPU-ঘণ্টার কথা আছে। খুঁজে পাওয়া নীতি অন্য ডেটাসেটে নিয়ে গেলে কাজ করে কি না, সেটিও প্রশ্ন ছিল।
RandAugment (Cubuk ও সহকর্মী, ২০২০) একটি সরল পর্যবেক্ষণ থেকে আসে: খোঁজার জায়গা ছোট করলে, অর্থাৎ শুধু N (কয়টি রূপান্তর) আর M (একটি সাধারণ শক্তি), সাধারণ grid search-এই কাছাকাছি ফল মেলে। আর ছোট proxy model-এ খোঁজা নীতি বড় model-এর জন্য সেরা নাও হতে পারে, কারণ বড় model জোরালো augmentation সহ্য করে। TrivialAugment (Müller আর Hutter, ICCV ২০২১) আরও এক ধাপ: প্রতি ছবিতে একটি রূপান্তর, শক্তি এলোমেলো, কোনো hyperparameter নেই; লেখকদের পরীক্ষায় এটি জটিল পদ্ধতির সমান বা কাছাকাছি ছিল বলে রিপোর্ট।
এখানে একটি বড় শিক্ষা আছে, যা deep learning গবেষণায় বারবার দেখা যায়: জটিল পদ্ধতির লাভের একটি অংশ আসে পরীক্ষার সেটআপ থেকে, যেমন বেশি epoch বা আলাদা baseline. সমান শর্তে তুলনা করলে সরল পদ্ধতি অনেক সময় টিকে থাকে। ব্যবহারিক দিক থেকে সরল পদ্ধতির সুবিধা স্পষ্ট: বোঝা সহজ, খোঁজার খরচ নেই, নতুন ডেটায় সহজে চালানো যায়। তবে অংশ ৮-এর ছোট run-এ দেখেছেন, জোরালো augmentation ছোট model আর অল্প epoch-এ সবসময় লাভ দেয় না; নিজের ডেটায় মেপে দেখতে হয়।
মূল উপলব্ধি: Augmentation-এর নীতি খোঁজায় বড় খরচের চেয়ে সরল এলোমেলো বাছাই প্রায়ই যথেষ্ট; জোর আর epoch-এর সংখ্যা model-এর আকারের সঙ্গে মেলাতে হয়।
প্র ০৪Augmentation কি distribution shift-এর সমাধান? বাজারঘরের ছবি যদি হঠাৎ নতুন ফোনের ক্যামেরা থেকে আসে?
Augmentation দিয়ে আমরা শুধু সেই বদলগুলো শেখাতে পারি, যা আগে থেকে ভাবতে পেরেছি আর হাতের ছবিতে বানাতে পেরেছি। ধরুন বাজারঘরের বিক্রেতারা আগে একটি পুরোনো ফোনে উজ্জ্বল আলোয় ছবি তুলতেন, এখন নতুন ফোনে, অন্য রঙের ভারসাম্য আর বেশি sharpening নিয়ে। Brightness আর color jitter train-এ থাকলে model রঙের এই বদলের কিছুটা সহ্য করবে। কিন্তু নতুন ফোন যদি ছবি অন্যভাবে compress করে, বা বিক্রেতারা এখন পণ্য হাতে ধরে তোলেন (পেছনে মানুষের হাত, নতুন পটভূমি), তাহলে এমন বদল augmentation-এর তালিকায় ছিল না।
অংশ ৬-এর TTA পরীক্ষা এর ছোট উদাহরণ: train-এ ±১ সরানো দেখা model ±২ সরানো ছবিতে ০.৫২-এ নামল, আর TTA-ও বাঁচাতে পারল না। Hendrycks আর Dietterich (ICLR ২০১৯) ImageNet-C নামে noise, blur, আবহাওয়া আর digital বিকৃতির একটি benchmark বানিয়ে দেখান যে সাধারণ model এসব বদলে অনেক খারাপ করে; AugMix-এর মতো কৌশল কিছুটা উন্নতি দেয় বলে রিপোর্ট, কিন্তু সমস্যা মেটে না। Recht ও সহকর্মীরা (২০১৯) একই নিয়মে নতুন করে ImageNet-এর test set বানিয়েও accuracy-তে লক্ষণীয় পতন দেখেন, যা দেখায় যে ছোট, অদেখা পার্থক্যও গুরুত্বপূর্ণ।
তাই ব্যবহারিক পরিকল্পনা হলো: সম্ভাব্য বদল (ক্যামেরা, আলো, পটভূমি) ভেবে augmentation দেওয়া; কিন্তু তার সঙ্গে production-এর ছবির নমুনা নিয়মিত দেখা, input-এর পরিসংখ্যান monitor করা, আর নতুন ধরনের ছবি এলে label করে আবার train করা। Shift ধরার আর সামলানোর পদ্ধতি মেশিন লার্নিং কোর্সের পর্ব ৫৯-এ আছে।
মূল উপলব্ধি: Augmentation জানা ও কল্পনাযোগ্য বদলের বিরুদ্ধে বীমা; অজানা distribution shift ধরতে monitoring আর নতুন ডেটা লাগে।
প্র ০৫Augmentation-এর তুলনা নিয়ে একটি run-এর ফল কেন বিশ্বাস করা কঠিন, আর সৎ ablation কেমন হওয়া উচিত?
অংশ ৮-এর মতো একটি ছোট পরীক্ষায় কয়েকটি জিনিস একসঙ্গে ফলকে নাড়ায়। প্রথম, seed: weight-এর শুরু, batch-এর ক্রম আর augmentation-এর random মান সব seed-এর ওপর নির্ভর করে; একই config দুই seed-এ কয়েক দশমাংশ শতাংশ থেকে এক শতাংশের বেশি আলাদা হতে পারে। দ্বিতীয়, epoch-এর সংখ্যা: জোরালো augmentation আর mixup ধীরে শেখে; অল্প epoch-এ পিছিয়ে, বেশি epoch-এ এগিয়ে থাকতে পারে। তৃতীয়, অন্য hyperparameter: augmentation বাড়ালে weight decay বা learning rate-এর সেরা মানও বদলায়। এক config-এর জন্য tune করা learning rate অন্যটিতে বসালে তুলনা অন্যায্য।
Bouthillier ও সহকর্মীরা (MLSys ২০২১) দেখান, ডেটার ভাগ, augmentation-এর random মান আর weight-এর শুরুর মতো উৎস থেকে আসা variance অনেক প্রকাশিত উন্নতির আকারের কাছাকাছি হতে পারে। Deep learning-এর অনেক প্রবন্ধে একটি run-এর ফল রিপোর্ট করা হতো বলে সমালোচনা আছে; এখন কয়েকটি seed-এর গড় আর মানবিচ্যুতি দেখানো ভালো চর্চা হিসেবে বেশি গৃহীত।
সৎ ablation-এর নীতিগুলো তাই সরল: একবারে একটি বদল; প্রতিটি config কয়েকটি seed-এ; তুলনা validation-এ, test set শুধু শেষে একবার; প্রতিটি config-কে ন্যায্য সুযোগ (যথেষ্ট epoch বা অন্তত একই compute); আর সংখ্যার পাশে খরচ (সময়)। আর ফল লেখার সময় সীমা বলা: “৫০০০টি CIFAR-10 ছবি, একটি ছোট CNN, এতগুলো epoch, এই কয়টি seed”. এই পর্বের run-এর সংখ্যাও ঠিক এভাবে পড়তে হবে, সাধারণ নিয়ম হিসেবে নয়। পরীক্ষার হিসাব রাখা আর তুলনার পদ্ধতি পর্ব ১৯-এ।
মূল উপলব্ধি: Augmentation-এর লাভ seed, epoch আর অন্য hyperparameter-এর সঙ্গে জড়িয়ে থাকে; কয়েকটি seed, ন্যায্য compute আর স্পষ্ট সীমা ছাড়া একটি সংখ্যা প্রায় কিছুই প্রমাণ করে না।
অনুশীলন
প্রথমে খাতায় করুন, তারপর উত্তর মেলান।
-
পূর্ণ numerical: (ক) চারটি শ্রেণির একটি কাজে Mixup, λ = ০.৬৫; A-এর শ্রেণি ১, B-এর শ্রেণি ৩ (০ থেকে গোনা)। মেশানো label কী? Model-এর অনুমান p = [০.১, ০.৫, ০.১, ০.৩] হলে cross-entropy কত? (খ) ৩২ × ৩২ ছবিতে CutMix, λ = ০.৮৪. বাক্সের বাহু কত (পূর্ণসংখ্যায় নিচে নামিয়ে), আর বাক্স পুরো ছবির ভেতরে থাকলে আসল λ কত? (গ) তিনটি train ছবির একটি channel-এর গড় ০.৪, ০.৫, ০.৬ আর একটি test ছবির ০.৯. Normalization-এর μ কত হবে, আর test ধরে নিলে কত হতো?
(ক) ỹ = ০.৬৫ × [০, ১, ০, ০] + ০.৩৫ × [০, ০, ০, ১] = [০, ০.৬৫, ০, ০.৩৫]. CE = −(০.৬৫ ln ০.৫ + ০.৩৫ ln ০.৩) = ০.৬৫ × ০.৬৯৩১ + ০.৩৫ × ১.২০৪০ = ০.৪৫০৫ + ০.৪২১৪ = ০.৮৭১৯. (খ) বাক্সের ক্ষেত্রফলের ভাগ ১ − ০.৮৪ = ০.১৬, বাহু ৩২ × √০.১৬ = ৩২ × ০.৪ = ১২.৮ → ১২. আসল ক্ষেত্রফল ১৪৪/১০২৪ = ০.১৪০৬, তাই আসল λ = ০.৮৫৯, ০.৮৪ নয়। (গ) শুধু train থেকে μ = (০.৪ + ০.৫ + ০.৬)/৩ = ০.৫. Test ধরলে (১.৫ + ০.৯)/৪ = ০.৬: test-এর তথ্য training-এর হিসাবে ঢুকে গেল, যা leakage.
-
সমালোচনা ও নকশা: বাজারঘরের একটি দল হাতে লেখা ডেলিভারি ঠিকানার ছবি (বাংলা) থেকে থানার নাম পড়ার model বানাচ্ছে। AI assistant-এর পরিকল্পনা: (১)
RandomHorizontalFlipআরRandomVerticalFlip; (২)RandomRotation(180); (৩) validation-এও একইtrain_tf, “যাতে মাপ বাস্তবসম্মত হয়”; (৪) ImageNet-এর mean/std; (৫) Mixup, যাতে “দুই থানার মাঝামাঝি নাম” শেখে; (৬) এক seed-এ তিনটি config চালিয়ে test set-এ সেরাটি বাছাই। কী ভুল, আর আপনার নকশা কী?১–২: লেখার ছবিতে flip আর বড় ঘোরানো label ভাঙে; উল্টানো বাংলা অক্ষর অর্থহীন, ১৮০°-এ শব্দ উল্টো। ছোট ঘোরানো (±৫°–১০°), সামান্য perspective, blur, কালির পুরুত্ব আর noise নিন। ৩: validation-এ random রূপান্তর মাপকে এলোমেলো আর কঠিন করে; validation-এ শুধু resize, tensor আর normalize. ৪: ধূসর কাগজের লেখার পরিসংখ্যান ImageNet-এর মতো নয়; নিজের train ছবি থেকে μ, σ মাপুন। ৫: দুই লেখার উপরিপাতনের “মাঝামাঝি নাম” বলে কিছু নেই; sequence label-এ Mixup-এর অর্থ পরিষ্কার নয়, তাই বাদ বা খুব সাবধানে। ৬: test-এ বাছাই মানে test-এ overfitting; কয়েকটি seed, validation-এ তুলনা, test শুধু শেষে। নকশা: কয়েকটি augmented ছবি চোখে দেখা, ছোট-জ্যামিতি আর কালির বদলের baseline, তারপর একটি করে যোগ।
-
আউটপুট অনুমান করুন: চালানোর আগে লিখে ফেলুন নিচের NumPy কোড কী ছাপবে।
import numpy as np x = np.array([[1, 2], [3, 4]]) print(x[:, ::-1].tolist()) print(np.rot90(x, 2).tolist()) print(np.clip((x - x.mean()) * 2 + x.mean(), 1, 4).tolist()) yA, yB, lam = np.eye(3)[2], np.eye(3)[0], 0.25 print((lam * yA + (1 - lam) * yB).tolist())[[2, 1], [4, 3]] [[4, 3], [2, 1]] [[1.0, 1.5], [3.5, 4.0]] [0.75, 0.0, 0.25](ডেস্কটপে সত্যিকারের run, NumPy 2.5.4.) প্রথম লাইন h-flip: প্রতিটি সারি উল্টো। দ্বিতীয়টি ১৮০° ঘোরানো: সারি আর কলাম দুটিই উল্টো, যা “6”-কে “9” বানায়। তৃতীয়টি contrast ×২: গড় ২.৫, তাই ১ → −০.৫, ৪ → ৫.৫, তারপর ১ আর ৪-এ clip; মাঝের ২ → ১.৫, ৩ → ৩.৫. তৃতীয় লাইনে float, কারণ
x.mean()float. শেষটি Mixup-এর label: ০.২৫ শ্রেণি ২-এর, ০.৭৫ শ্রেণি ০-এর। -
কোড লিখুন: NumPy-তে একটি
cutmix(xa, xb, lam, rng)function লিখুন, যা H × W × C আকারের দুটি ছবি নেয়, এলোমেলো কেন্দ্রে (১ − λ) ক্ষেত্রফলের বাক্স বসায়, কিনারায় কাটে, আর মেশানো ছবি ও আসল λ ফেরায়। পরীক্ষা করুন: λ = ১ হলে ছবি অপরিবর্তিত আর λ = ১ থাকে কি না।def cutmix(xa, xb, lam, rng): H, W = xa.shape[:2] r = np.sqrt(1 - lam) h, w = int(H * r), int(W * r) cy, cx = rng.integers(0, H), rng.integers(0, W) y0, y1 = max(cy - h // 2, 0), min(cy + h // 2, H) x0, x1 = max(cx - w // 2, 0), min(cx + w // 2, W) out = xa.copy() out[y0:y1, x0:x1] = xb[y0:y1, x0:x1] return out, 1 - (y1 - y0) * (x1 - x0) / (H * W)λ = ১ হলে r = ০, তাই h = w = ০, বাক্স ফাঁকা: ছবি অপরিবর্তিত আর ফেরত λ = ১. কেন্দ্র কিনারার কাছে পড়লে বাক্স ছোট হয়, তাই ফেরত λ চাওয়া λ-এর চেয়ে বড়; label সবসময় এই ফেরত λ দিয়ে মেশাবেন। torchvision-এর
v2.CutMixএকই নীতি মেনে কাটা বাক্স থেকে λ নতুন করে হিসাব করে।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি ছবির classifier-এ augmentation যোগ করার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- Train, validation আর test ভাগ করা
- শুধু train ছবি থেকে normalization-এর μ আর σ মাপা
- প্রতিটি রূপান্তরের কয়েকটি ছবি এঁকে label অপরিবর্তিত কি না দেখা
- Augmentation ছাড়া baseline train করে validation-এ মাপা
- একবারে একটি augmentation যোগ করে কয়েকটি seed-এ validation-এ তুলনা
- বাছাই করা config test set-এ একবার মাপা
তিন শ্রেণির কাজে Mixup, λ = ০.৭; ছবি A-এর label [১, ০, ০], ছবি B-এর label [০, ০, ১]. মেশানো label কী?
- [১, ০, ০]
- [০.৩, ০, ০.৭]
- [০.৭, ০, ০.৩]
- [০.৫, ০, ০.৫]
৩২ × ৩২ ছবিতে CutMix, চাওয়া λ = ০.৭৫, আর বাক্সটি পুরো ছবির ভেতরে পড়ল। বাক্সের আকার আর label-এ B-এর ভাগ কত?
- ৮ × ৮, B-এর ভাগ ০.০৬২৫
- ১৬ × ১৬, B-এর ভাগ ০.২৫
- ২৪ × ২৪, B-এর ভাগ ০.৭৫
- ১৬ × ৩২, B-এর ভাগ ০.৫
হাতে লেখা অঙ্ক (০–৯) চেনার model-এর জন্য কোন augmentation সবচেয়ে ঝুঁকিপূর্ণ?
- ১–২ pixel এলোমেলো সরানো
- কালির পুরুত্ব সামান্য বদলানো
- ±১০° ঘোরানো
- ১৮০° ঘোরানো
একটি ছবির classifier-এর validation pipeline-এ কোনটি থাকা উচিত?
- নির্দিষ্ট resize, tensor-এ রূপান্তর আর train-এর μ, σ দিয়ে normalize
- train-এর মতোই RandomResizedCrop আর ColorJitter
- validation ছবি থেকে আলাদা μ, σ মেপে normalize
- MixUp আর CutMix-এর collate_fn
Test-time augmentation (TTA) সম্পর্কে কোন কথাটি সঠিক?
- TTA নতুন weight-সহ আরেকটি model train করে
- TTA যেকোনো distribution shift ঠিক করে দেয়
- একই ছবির কয়েকটি স্থির view-এর probability গড় করা হয়, আর inference-এর খরচ ততগুণ বাড়ে
- TTA-তে যেকোনো রূপান্তর চলে, label রক্ষার দরকার নেই
v2.MixUp ব্যবহার করলে training loop-এ label-এর আকার আর accuracy মাপার ব্যাপারে কোনটি ঠিক?
- label আগের মতো (B,) আকারের পূর্ণসংখ্যা থাকে
- label (B, শ্রেণি) আকারের soft label হয়; train accuracy পরিষ্কার ছবিতে বা argmax নিয়ে মাপতে হয়
- cross_entropy soft label নিতে পারে না, তাই MSE লাগে
- প্রতিটি label সবসময় দুই শ্রেণিতে ০.৫ করে
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- Augmentation = ডোমেইন-জ্ঞান: যে বদলে label বদলায় না, সেগুলো ডেটা দিয়ে শেখানো। প্রতিটি রূপান্তর আগে চোখে দেখুন।
- Label ভাঙা রূপান্তর: অঙ্ক আর লেখায় flip ও বড় ঘোরানো নয় (“6” → “9”); detection-এ ছবির সঙ্গে box-ও বদলাতে হয়।
- Mixup আর CutMix: ỹ = λ·yA + (১ − λ)·yB; CutMix-এ λ আসে বাক্সের আসল ক্ষেত্রফল থেকে। Batch-এ
collate_fn-এ বসে। - শুধু train-এ: validation আর test-এ কোনো random রূপান্তর নয়; normalization-এর μ, σ শুধু train থেকে। TTA ইচ্ছাকৃত, স্থির view-এর গড়, আলাদা করে রিপোর্ট।
- মেপে দেখুন: Train আর test-এর ফারাক আর test accuracy দুটিই দেখুন; জোরালো augmentation বেশি epoch চায়। একটি seed-এর একটি run কিছু প্রমাণ করে না।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ২৫ · Object detection আর segmentation-এর পরিচয় — bounding box, IoU আর U-Netপরবর্তী পর্বIoU, NMS, mAP, pretrained detector আর ছোট U-Net দিয়ে segmentation শেখা।
- ডিপ লার্নিং, NLP আর Generative AI · কোর্স হোম৯১টি পর্ব১৪টি মডিউলের পুরো রোডম্যাপ; পর্ব ২৫ (Object detection আর segmentation) আর পর্ব ২৩ (Transfer learning) এখান থেকে খুলবেন।
- পর্ব ১৬ · Deep learning-এ regularizationআগের পর্বDropout, weight decay, EMA আর ensemble: augmentation-এর পাশের regularizer.
- পর্ব ১১ · Dataset আর DataLoaderআগের পর্বtransforms.v2-এর প্রথম পরিচয়, collate_fn আর worker-এর random seed.
- AI-এর ভিত্তি · পর্ব ১৮ · ছবি মানেই arrayআগের ভিত্তিPixel, channel আর NumPy দিয়ে ছোট image processing.
- AI-এর ভিত্তি · পর্ব ৫৬ · Train, validation আর testআগের ভিত্তিData leakage-এর ফাঁদ: কেন μ, σ শুধু train থেকে।
- মেশিন লার্নিং · পর্ব ৫৯ · Distribution shiftপাশের ট্র্যাকAugmentation যে বদল সামলাতে পারে না, তা ধরা আর সামলানো।