এই পর্বে যা শিখবেন
- Autoencoder-এর encoder, bottleneck আর decoder; undercomplete বনাম overcomplete
- Linear autoencoder আর PCA-র সম্পর্ক সংখ্যায় যাচাই; MSE বনাম BCE loss
- Convolutional autoencoder আর transposed convolution-এর output-এর মাপ ও parameter হাতে হিসাব
- Denoising autoencoder, sparse autoencoder, আর reconstruction error দিয়ে anomaly detection, ROC-AUC-সহ
- Latent code-এর মান probe দিয়ে মাপা, আর সাধারণ autoencoder কেন ভালো generator নয়
পর্ব ৭৪ (Generative modeling-এর মানচিত্র) বলেছে, একটি generative model-এর তিন রকম কাজ: নতুন নমুনা বানানো, একটি নমুনা কতটা সম্ভব তা মাপা, আর ডেটার একটি কাজের representation শেখা। এই পর্বের autoencoder মূলত তৃতীয় কাজের যন্ত্র, আর পরের পর্বগুলোর (৭৬ VAE, ৮০ latent diffusion) ভিত্তি। এখানে লাগবে convolution (পর্ব ২০ · Convolution), U-Net-এর decoder-এ দেখা transposed convolution (পর্ব ২৫ · Detection আর segmentation), আর PCA, যা AI-এর ভিত্তি কোর্সের পর্ব ৬৫-এ আর মেশিন লার্নিং কোর্সের পর্ব ৯-এ আছে। ডেটা Fashion-MNIST (Zalando Research, MIT লাইসেন্স): ২৮ × ২৮ মাপের ধূসর পোশাক আর জুতার ছবি, ১০টি শ্রেণি, train-এ ৬০,০০০ আর test-এ ১০,০০০। বাজারঘরের পণ্যের ছবির বদলে এটি ব্যবহার করছি; এগুলো আসল দোকানের ছবি নয়। Desktop-এর run লেখার সময়ে (নভেম্বর ২০২৬) এই version-এ: Python 3.14.4, torch 2.15.0, NumPy 2.5.4, scikit-learn 1.9.1. এই model-গুলো এত ছোট যে সব training CPU-তে (৩টি thread) চালানো হয়েছে; সময়গুলো এই এক যন্ত্রের একটি run-এর, সাধারণ নিয়ম নয়।
১ · Autoencoder কী — encoder, bottleneck আর decoder
বাজারঘরের গুদামের মালিক ফোনে ম্যানেজারকে একটি নতুন শার্টের বর্ণনা দিচ্ছেন। পুরো ছবি পাঠানোর উপায় নেই, শুধু কয়েকটি কথা: “লম্বা হাতা, গাঢ় রং, বুকে পকেট”। ম্যানেজার সেই কয়েকটি কথা থেকে মনে মনে শার্টটি আবার আঁকেন। যদি আঁকা ছবি আসলের কাছাকাছি হয়, তবে ওই কয়েকটি কথাই শার্টের আসল তথ্য ধরেছে। কথা যত কম, বাছাই তত কঠিন।
Autoencoder ঠিক এই খেলাটি শেখে। দুটি network: encoder f input x-কে ছোট একটি code z = f(x)-এ নেয়, আর decoder g সেই code থেকে x̂ = g(z) বানায়। শেখানো হয় x̂ যেন x-এর কাছাকাছি হয়, অর্থাৎ reconstruction error L(x, x̂) কমাতে। কোনো label লাগে না, input নিজেই লক্ষ্য; তাই একে প্রায়ই self-supervised শেখার শুরুর উদাহরণ বলা হয়। Code-এর মাপ k-কে বলে bottleneck: বোতলের সরু গলা, যেখান দিয়ে সব তথ্য যেতে হয়।
k যদি input-এর মাপ d-এর চেয়ে ছোট হয়, model-টি undercomplete: সব তথ্য রাখা যায় না, তাই model বাধ্য হয়ে গুরুত্বপূর্ণটুকু বাছে। k ≥ d হলে overcomplete: তখন সবচেয়ে সহজ সমাধান হলো input হুবহু কপি করা (identity), যাতে কিছুই শেখা হয় না। তাই overcomplete model-এ অন্য কোনো বাধা লাগে: input-এ noise দেওয়া (অংশ ৫), বা code-এর বেশির ভাগ মান শূন্য রাখতে বাধ্য করা। একেবারে ছোট একটি উদাহরণে একটি forward pass হাতে করে দেখুন: তিনটি pixel, এক সংখ্যার code, কোনো activation নেই।
| ধাপ | |
| হিসাব | |
| ফল |
- Input. তিনটি pixel-এর উজ্জ্বলতা x = [১, ২, ৩]. ওজন আগে থেকে দেওয়া: encoder We = [০.২, ০.৪, ০.৪], decoder Wd = [০.৫, ১.০, ১.৫].
- Encoder. z = We·x = ০.২ + ০.৮ + ১.২ = ২.২। তিনটি সংখ্যা এখন একটি সংখ্যায়: এটিই bottleneck-এর code.
- Decoder. x̂ = Wd × z = [১.১, ২.২, ৩.৩]. Decoder শুধু একটি দিক জানে, [০.৫, ১, ১.৫]; code বলে সেই দিকে কতদূর যেতে হবে।
- ত্রুটি. x̂ − x = [০.১, ০.২, ০.৩]. আকার মিলেছে, কিন্তু প্রতিটি pixel একটু বেশি।
- বর্গ. ০.০১ + ০.০৪ + ০.০৯ = ০.১৪, এটি squared error-এর যোগফল।
- MSE. তিনটি pixel-এর গড়: ০.১৪ ÷ ৩ ≈ ০.০৪৬৭। Training এই সংখ্যা কমাতে We আর Wd বদলাবে (gradient-এর হিসাব পর্ব ৪-এর মতোই)।
- Parameter. ৩ + ৩ = ৬টি ওজন, code ৩ থেকে ১-এ। x যদি [১, ২, ৩]-এর কোনো গুণিতক হতো, যেমন [২, ৪, ৬], তাহলে নিখুঁত ফেরানো সম্ভব; এক সংখ্যার code মানে ডেটা একটি সরলরেখার কাছে আছে বলে ধরে নেওয়া।
শেষ ধাপের কথাটিই autoencoder-এর মূল ধারণা: bottleneck ছোট হলে model ধরে নেয় ডেটা একটি নিচু মাত্রার জায়গার কাছাকাছি থাকে। ২৮ × ২৮ = ৭৮৪ মাত্রার সব সম্ভাব্য ছবির মধ্যে আসল জামা-জুতার ছবি খুব ছোট একটি অংশ; এই ধারণার নাম manifold hypothesis, যা মেশিন লার্নিং কোর্সের পর্ব ৪২-এ আছে।
২ · Linear autoencoder আর PCA — একই subspace
Activation না রাখলে encoder আর decoder দুটিই শুধু matrix: z = Wex, x̂ = Wdz. MSE loss-এ এর সেরা সমাধান কী? ১৯৮৯ সালে Baldi আর Hornik দেখান, এমন network-এর loss-এর সব local minimum আসলে global, আর সেরা সমাধানে decoder-এর কলামগুলো ঠিক প্রথম k টি principal component-এর span-এ পড়ে। অর্থাৎ linear autoencoder নতুন কিছু শেখে না, PCA-র subspace আবার খুঁজে পায়। তবে পার্থক্য আছে: PCA-র দিকগুলো একে অপরের লম্ব (orthonormal) আর variance অনুযায়ী সাজানো, autoencoder-এর দিকগুলো সেই একই সমতলের যেকোনো ঘোরানো রূপ হতে পারে।
নিচের কোডে scikit-learn-এর হাতে লেখা অঙ্কের ছবিতে (৮ × ৮, ৬৪ pixel) NumPy দিয়ে gradient descent-এ একটি linear autoencoder শেখানো হয়েছে, k = ৪, তারপর SVD দিয়ে PCA-র সঙ্গে তুলনা। দুই subspace-এর মধ্যে কোণ (principal angle) শূন্যের কাছে হলে দুটি একই সমতল। পাতায় প্রথমবার scikit-learn লোড হতে ১০ সেকেন্ডের মতো লাগতে পারে।
import numpy as np
from sklearn.datasets import load_digits
from scipy.linalg import subspace_angles
X = load_digits().data / 16.0 # 1797টি ছবি, প্রতিটি 8×8 = 64 pixel
X = X - X.mean(axis=0) # কেন্দ্রে আনা, PCA-র মতোই
n, d, k = X.shape[0], X.shape[1], 4 # bottleneck: 64 → 4
rng = np.random.default_rng(42)
We = rng.normal(0, 0.1, (d, k)) # encoder-এর ওজন
Wd = rng.normal(0, 0.1, (k, d)) # decoder-এর ওজন
lr = 0.1
for step in range(1501):
Z = X @ We # code, (1797, 4)
E = Z @ Wd - X # reconstruction − আসল
loss = (E ** 2).sum(axis=1).mean() # প্রতি ছবির squared error-এর যোগফল, গড়
gWd = 2 * Z.T @ E / n # ∂loss/∂Wd
gWe = 2 * X.T @ (E @ Wd.T) / n # ∂loss/∂We (chain rule)
We -= lr * gWe
Wd -= lr * gWd
if step % 300 == 0:
print(f"step {step:4d} loss {loss:.4f}")
U, S, Vt = np.linalg.svd(X, full_matrices=False)
P = Vt[:k] # প্রথম 4টি principal direction
pca = ((X @ P.T @ P - X) ** 2).sum(axis=1).mean()
print(f"PCA (k=4) loss {pca:.4f}")
ang = np.degrees(subspace_angles(Wd.T, P.T))
print("দুই subspace-এর কোণ (ডিগ্রি):", np.round(ang, 2))
u = Wd[0] / np.linalg.norm(Wd[0])
print("decoder-এর প্রথম দিক আর PC1-এর |cos|:", round(abs(u @ P[0]), 3))
print("Wd-এর সারিগুলো কি orthonormal?", np.allclose(Wd @ Wd.T, np.eye(k), atol=0.05))আউটপুট (পাতার Python-এ চালানো):
step 0 loss 4.7799
step 300 loss 2.4090
step 600 loss 2.4077
step 900 loss 2.4074
step 1200 loss 2.4072
step 1500 loss 2.4072
PCA (k=4) loss 2.4070
দুই subspace-এর কোণ (ডিগ্রি): [0.05 0.03 0.03 0.01]
decoder-এর প্রথম দিক আর PC1-এর |cos|: 0.171
Wd-এর সারিগুলো কি orthonormal? False
কী দেখা গেল: Gradient descent-এর loss ২.৪০৭২-এ থেমেছে, PCA-র ২.৪০৭০-এর প্রায় সমান; PCA-র চেয়ে নিচে নামা সম্ভবও নয়, কারণ কোনো rank-৪ linear map PCA-কে হারাতে পারে না (Eckart–Young উপপাদ্য)। চারটি কোণই ০.১ ডিগ্রির কম, অর্থাৎ দুটি একই চার মাত্রার subspace. কিন্তু decoder-এর প্রথম দিক আর PC১-এর |cos| মাত্র ০.১৭১, আর decoder-এর সারিগুলো orthonormal নয়: subspace এক, ভিত্তি আলাদা। Desktop-এ একই পরীক্ষা, Fashion-MNIST-এ: k = ১৬, bias আর activation ছাড়া, Adam-এ ২০ epoch (CPU-তে ৭.২ সেকেন্ড): test MSE ০.০২০৫১, PCA-র ০.০২০৪৫; ১৬টি কোণের সর্বোচ্চ ২.৩৮ আর median ১.২২ ডিগ্রি। আরও epoch দিলে কোণ আরও কমার কথা, কারণ ছোট variance-এর দিকগুলো ধীরে শেখে।
৩ · Reconstruction loss — MSE না BCE
x̂ আর x-এর পার্থক্য মাপার দুটি প্রচলিত উপায়। MSE: প্রতিটি pixel-এর (x̂ − x)²-এর গড়। এটি ধরে নেয় প্রতিটি pixel একটি Gaussian-এর মতো ছড়ায়, যার কেন্দ্র x̂; তখন MSE কমানো আর likelihood বাড়ানো একই (মেশিন লার্নিং কোর্সের পর্ব ১৮)। BCE (binary cross-entropy): pixel-এর মান ০ থেকে ১-এর মধ্যে থাকলে প্রতিটি pixel-কে একটি “কতটা সাদা” probability ধরে −[x log x̂ + (১ − x) log(১ − x̂)]. Decoder-এর শেষে sigmoid দিয়ে x̂-কে (০, ১)-এ রাখতে হয়; PyTorch-এ sigmoid আর BCE একসঙ্গে, সংখ্যাগতভাবে নিরাপদে করে BCEWithLogitsLoss (পর্ব ১৩ · Loss function).
একটি সূক্ষ্ম কথা: x যদি ০ বা ১ না হয়ে ধূসর (যেমন ০.৪) হয়, BCE-র সর্বনিম্ন মান শূন্য নয়। x̂ = x হলেও loss থাকে, কারণ এটি x-এর নিজের entropy. তাই BCE-র সংখ্যা দিয়ে দুটি ভিন্ন ডেটাসেটের মান তুলনা চলে না; তুলনার জন্য একই মাপ, যেমন test MSE, সব model-এ আলাদা করে মাপা ভালো। কোনটি বাছবেন? ধূসর ছবিতে প্রায়ই BCE নেওয়া হয়, কারণ এটি pixel-এর মানকে probability হিসেবে স্বাভাবিকভাবে ধরে; বাস্তব মানের (তাপমাত্রা, বিক্রি) ডেটায় MSE বা SmoothL1 স্বাভাবিক। দুটি একই conv autoencoder (k = ৩২, ১০ epoch, seed ০) শুধু loss বদলে শেখানো হলো, আর দুটির test MSE মাপা হলো (CPU-তে, সত্যিকারের run):
| Training loss | Test MSE | সময় (s) |
|---|---|---|
| BCE (BCEWithLogitsLoss) | ০.০০৯১২ | ৬২.৬ |
| MSE (sigmoid-এর পরে) | ০.০০৯০২ | ৫৯.৪ |
ফল: দুটির test MSE প্রায় সমান (০.০০৯১২ বনাম ০.০০৯০২)। নিচে দেখবেন, শুধু seed বদলালে একই BCE model-এর MSE ০.০০৯১২ থেকে ০.০০৯২৩ পর্যন্ত নড়ে, তাই এই ০.০০০১-এর পার্থক্যকে “MSE ভালো” বলা যায় না। BCE-তে ছবি একটু তীক্ষ্ণ হয় বলে প্রায়ই শোনা যায়; এই এক run-এর MSE-তে তা ধরা পড়েনি, আর তীক্ষ্ণতা মাপতে আলাদা মাপ লাগবে। এই পর্বের বাকি run-এ BCE ব্যবহার করা হয়েছে।
৪ · Convolutional autoencoder আর transposed convolution
MLP autoencoder ছবিকে ৭৮৪টি আলাদা সংখ্যা হিসেবে দেখে; কোন pixel কার পাশে, তা জানে না। Convolutional autoencoder-এর encoder পর্ব ২০-এর মতো stride ২-এর convolution দিয়ে মাপ কমায়: ২৮ → ১৪ → ৭, তারপর একটি Linear দিয়ে k সংখ্যার code. Decoder উল্টো পথে যায়: Linear দিয়ে ৩২ × ৭ × ৭, তারপর দুটি transposed convolution দিয়ে ৭ → ১৪ → ২৮। এই পর্বের মডেল (PyTorch, ছোট করে লেখা):
enc = nn.Sequential(nn.Conv2d(1, 16, 3, stride=2, padding=1), nn.ReLU(), # 16×14×14
nn.Conv2d(16, 32, 3, stride=2, padding=1), nn.ReLU(), # 32×7×7
nn.Flatten(), nn.Linear(32*7*7, k))
dec = nn.Sequential(nn.Linear(k, 32*7*7), nn.ReLU(), nn.Unflatten(1, (32, 7, 7)),
nn.ConvTranspose2d(32, 16, 3, stride=2, padding=1, output_padding=1), nn.ReLU(),
nn.ConvTranspose2d(16, 1, 3, stride=2, padding=1, output_padding=1)) # logits
Transposed convolution-এ input-এর প্রতিটি মান kernel দিয়ে গুণ হয়ে output-এ একটি ছাপ ফেলে, আর stride সেই ছাপের দূরত্ব ঠিক করে (পর্ব ২৫)। এবার মাপ আর parameter হাতে বের করুন; শেষ অংশে output_padding কেন লাগে, সেটিই পরীক্ষার প্রিয় প্রশ্ন।
| ধাপ | |
| হিসাব | |
| ফল |
- সূত্র. PyTorch-এর নথি অনুযায়ী (dilation ১ হলে) Hout = (Hin − ১)·s − ২p + k + output_padding. এখানে s stride, p padding, k kernel-এর মাপ। এটি সাধারণ convolution-এর মাপের সূত্রের উল্টো হিসাব।
- output_padding ছাড়া. (৭ − ১)·২ − ২ + ৩ = ১৩। আমরা চেয়েছিলাম ১৪, পেলাম ১৩; desktop-এ চালিয়েও (1, 16, 13, 13) এসেছে।
- কেন এমন. Encoder-এর Conv2d(k ৩, s ২, p ১) ১৩ আর ১৪ দুটিকেই ৭-এ নেয়, কারণ floor ভাগে একটি সারি হারায়। তাই উল্টো পথে ৭ থেকে কোন মাপে ফিরতে হবে, তা একা সূত্র জানে না।
- output_padding = ১. Output-এর এক দিকে একটি বাড়তি সারি আর কলাম যোগ করে বেছে নেওয়া হয়: (৭ − ১)·২ − ২ + ৩ + ১ = ১৪। এটি শূন্যের padding নয়, শুধু মাপ বেছে নেওয়া; PyTorch-এ output_padding-কে stride বা dilation-এর চেয়ে ছোট হতে হয়।
- ওজন. সাবধান: ConvTranspose2d-এর ওজনের আকার (in, out, k, k), Conv2d-এর (out, in, k, k)-এর উল্টো। ৩২ × ১৬ × ৯ = ৪,৬০৮, সঙ্গে প্রতিটি output channel-এ একটি bias, ১৬টি: মোট ৪,৬২৪। মাপ ৭ থেকে ১৪ হলেও parameter বাড়ে না; parameter নির্ভর করে শুধু channel আর kernel-এর ওপর।
- দ্বিতীয় layer. (১৪ − ১)·২ − ২ + ৩ + ১ = ২৮, আসল ছবির মাপ। Parameter ১৬ × ১ × ৯ + ১ = ১৪৫।
- পুরো model, k = ৩২. Encoder-এর দুটি conv ১৬০ + ৪,৬৪০, Linear ১,৫৬৮ × ৩২ + ৩২ = ৫০,২০৮; decoder-এর Linear ৩২ × ১,৫৬৮ + ১,৫৬৮ = ৫১,৭৪৪, আর দুটি ConvT ৪,৬২৪ + ১৪৫। মোট ১,১১,৫২১,
sum(p.numel())-এর সঙ্গে হুবহু। ৯১% parameter দুটি Linear-এ; conv অংশ খুব হালকা।
এবার মূল তুলনা। MLP autoencoder (৭৮৪ → ২৫৬ → k → ২৫৬ → ৭৮৪, ReLU) আর উপরের conv autoencoder, চারটি bottleneck-এ: k = ২, ৮, ৩২, ১২৮। দুটিই BCE loss, Adam (lr ১০−৩), batch ১২৮, ১০ epoch, seed ০, CPU-তে। পাশে একই k-তে PCA-র test MSE (CPU-তে, scikit-learn). সব MSE test-এর ১০,০০০ ছবিতে, pixel-এর মান ০ থেকে ১। সত্যিকারের run, JSON লাইন থেকে সাজানো:
| k | MLP parameter | MLP test MSE | MLP সময় (s) | Conv parameter | Conv test MSE | Conv সময় (s) | PCA test MSE |
|---|---|---|---|---|---|---|---|
| ২ | ৪,০৩,৭৩০ | ০.০২৮৯৪ | ২৮.৪ | ১৭,৪১১ | ০.০৩২৮৭ | ৬২.০ | ০.০৪৬১০ |
| ৮ | ৪,০৬,৮০৮ | ০.০১৪৮৫ | ১৯.২ | ৩৬,২৩৩ | ০.০১৬৪৩ | ৬২.৭ | ০.০২৬৬১ |
| ৩২ | ৪,১৯,১২০ | ০.০০৯৭৫ | ২৪.৭ | ১,১১,৫২১ | ০.০০৯১২ | ৬২.৬ | ০.০১৫১৪ |
| ১২৮ | ৪,৬৮,৩৬৮ | ০.০০৮১৮ | ২৫.১ | ৪,১২,৬৭৩ | ০.০০৫৫৬ | ৬৯.৩ | ০.০০৬৩১ |
ছোট code-এ non-linear জেতে: k = ২-এ PCA-র MSE ০.০৪৬১, MLP-এর ০.০২৮৯, প্রায় ৩৭% কম। দুটি সংখ্যায় একটি বাঁকা গঠন ধরা PCA-র সমতলের চেয়ে অনেক বেশি তথ্য রাখে। বড় code-এ পার্থক্য কমে: k = ১২৮-এ PCA (০.০০৬৩১) MLP-কে (০.০০৮১৮) হারিয়েছে; ১০ epoch-এ MLP সেখানে পৌঁছায়নি, আর PCA-র ১২৮টি দিক এমনিতেই ৯২.৮% variance রাখে। Conv বনাম MLP: conv model k = ২ আর ৮-এ MLP-এর চেয়ে খারাপ, k = ৩২ আর ১২৮-এ ভালো, অথচ k ≤ ৩২-এ তার parameter অনেক কম (১৭ হাজার বনাম ৪ লাখ)। তবে CPU-তে conv প্রায় আড়াই গুণ ধীর। Seed: k = ৩২-এ তিন seed-এ MLP ০.০০৯৭৫, ০.০০৯৮৪, ০.০০৯৮৬ আর conv ০.০০৯১২, ০.০০৯২৩, ০.০০৯১৯: conv-এর সুবিধা seed-এর ওঠানামার চেয়ে বড়, কিন্তু এটি একটি ডেটাসেট আর এক রকম training-এর ফল।
৫ · Denoising autoencoder — নষ্ট ছবি থেকে পরিষ্কার ছবি
পুরনো ঝাপসা ফটোকপি থেকে একজন অভিজ্ঞ কেরানি দলিলের লেখা ঠিকঠাক পড়ে ফেলেন, কারণ তিনি জানেন বাংলা অক্ষর দেখতে কেমন হয়। দাগ আর ময়লা তিনি নিজেই বাদ দেন। Denoising autoencoder (Vincent ও সহলেখক, ২০০৮) একই দক্ষতা শেখে: input-এ ইচ্ছা করে noise দেওয়া হয়, x̃ = x + noise, কিন্তু loss মাপা হয় পরিষ্কার x-এর সঙ্গে: L(x, g(f(x̃))). তখন হুবহু কপি করলে আর কাজ হয় না, কারণ কপি করলে noise-ও ফিরে আসে। Model-কে জানতে হয় আসল ছবি “দেখতে কেমন”।
এই কৌশল overcomplete model-কেও কাজের বানায়। নিচের run-এ conv autoencoder (k = ৩২) তিনভাবে শেখানো হয়েছে: noise ছাড়া, আর Gaussian noise σ = ০.৩ ও ০.৫ দিয়ে (তারপর মান ০ থেকে ১-এ আটকানো)। সঙ্গে একটি overcomplete MLP (৭৮৪ → ১,০২৪ → ১,০২৪ → ১,০২৪ → ৭৮৪), noise ছাড়া আর σ = ০.৩ দিয়ে। সবগুলো ১০ epoch, seed ০, CPU-তে। Test-এর noise একটি নির্দিষ্ট seed-এ একবার বানানো, তাই সব model একই নষ্ট ছবি পেয়েছে। প্রতিটি ঘরে model-এর output আর পরিষ্কার ছবির MSE. সত্যিকারের run:
| Model | শেখার σ | পরিষ্কার input | test σ = ০.৩ | test σ = ০.৫ |
|---|---|---|---|---|
| নষ্ট input নিজেই (কিছু না করে) | — | ০ | ০.০৫২৭০ | ০.১১৭৬৭ |
| Conv, k = ৩২ | ০.০ | ০.০০৯১২ | ০.০৪৪৯০ | ০.০৬৭৬২ |
| Conv, k = ৩২ | ০.৩ | ০.০১২৬৮ | ০.০১১৯৭ | ০.০২৩২৪ |
| Conv, k = ৩২ | ০.৫ | ০.০১৭৮৩ | ০.০১৫৩৮ | ০.০১৫৫০ |
| Overcomplete MLP, k = ১০২৪ | ০.০ | ০.০০৪৪৩ | ০.০২৭২২ | ০.০৫৩৪২ |
| Overcomplete MLP, k = ১০২৪ | ০.৩ | ০.০১১৮৯ | ০.০১০১২ | ০.০১৬৯৮ |
সাধারণ autoencoder নষ্ট ছবি কিছুটা পরিষ্কার করে (σ = ০.৫-এ ০.১১৭৭ থেকে ০.০৬৭৬), কারণ bottleneck-এ noise-এর সবটা ঢোকে না। কিন্তু σ = ০.৫-এ শেখানো denoising model একই নষ্ট ছবিতে ০.০১৫৫, প্রায় সাড়ে চার গুণ কম। দাম আছে: পরিষ্কার input-এ তার MSE ০.০০৯১২ থেকে ০.০১৭৮৩-এ বেড়েছে, কারণ সে খুঁটিনাটির বদলে “গড় চেহারা” আঁকতে শিখেছে। Overcomplete MLP (৩৭ লাখ parameter, code ১,০২৪) noise ছাড়া পরিষ্কার ছবিতে সবার সেরা (০.০০৪৪৩), কিন্তু σ = ০.৩-এর নষ্ট ছবিতে ০.০২৭২; একই model noise দিয়ে শেখালে ০.০১০১, টেবিলের সেরা। কপি করার সুযোগ থাকলে model কপিই শেখে, noise সেই সুযোগ কেড়ে নেয়।
Overcomplete model-কে আটকানোর আরেকটি উপায় sparse autoencoder: loss-এ code-এর ওপর একটি শাস্তি যোগ করা, যেমন λ·Σ|zj| (L1), যাতে প্রতিটি input-এ code-এর অল্প কয়েকটি মান শূন্য ছাড়া থাকে। এই পর্বে এটি চালানো হয়নি, শুধু নাম আর ধারণা; বড় language model-এর ভেতরের activation বোঝার গবেষণায় sparse autoencoder সম্প্রতি আবার বেশ ব্যবহৃত হচ্ছে বলে প্রকাশিত কাজে দেখা যায়।
এবার ছবিতে দেখুন। নিচের কোড desktop run-এর আসল output-এর সাত সারি আঁকে, প্রতিটি সারিতে চারটি ছবি: test-এর আসল ছবি, সাধারণ autoencoder-এর পুনর্গঠন, σ = ০.৫-এর নষ্ট ছবি, সেই নষ্ট ছবিতে সাধারণ autoencoder, সেই নষ্ট ছবিতে denoising autoencoder (σ = ০.৫-এ শেখানো), আর শেষ দুই সারিতে এলোমেলো code decode করা ছবি (অংশ ৮-এ ব্যাখ্যা)। পাতার আকার ছোট রাখতে প্রতিটি pixel ১৬টি ধূসর ধাপে (৪ bit) রাখা, তাই ছবি সামান্য দানাদার।
import numpy as np
import matplotlib.pyplot as plt
rows = open("ae_pics.txt").read().split("\n")
names = ["test image", "AE reconstruction", "noisy input (sigma 0.5)", "plain AE on noisy",
"denoising AE on noisy", "decode z ~ N(0, I)", "decode z ~ fitted Gaussian"]
imgs = [[np.array([int(ch, 16) for ch in h]).reshape(28, 28) / 15 for h in r.split()]
for r in rows] # প্রতিটি অক্ষর একটি pixel, 0–15
fig, ax = plt.subplots(7, 4, figsize=(4.2, 8.4))
for r in range(7):
for c in range(4):
ax[r, c].imshow(imgs[r][c], cmap="gray", vmin=0, vmax=1)
ax[r, c].set_xticks([]); ax[r, c].set_yticks([])
ax[r, 0].set_title(names[r], fontsize=8, loc="left")
fig.subplots_adjust(hspace=0.45, wspace=0.05)
plt.show()
clean = np.array(imgs[0])
for r in (1, 2, 3, 4):
mse = ((np.array(imgs[r]) - clean) ** 2).mean()
print(f"{names[r]:24s} MSE vs test image: {mse:.4f}")আউটপুট (পাতার Python-এ চালানো):
AE reconstruction MSE vs test image: 0.0078
noisy input (sigma 0.5) MSE vs test image: 0.1148
plain AE on noisy MSE vs test image: 0.0821
denoising AE on noisy MSE vs test image: 0.0150
ছবি আসল desktop run-এর (seed ০, ১০ epoch), test-এর প্রথম চারটি ছবি। সাধারণ autoencoder-এর পুনর্গঠন আকার ঠিক রাখে কিন্তু নকশা আর লেখা ঝাপসা করে; ৩২টি সংখ্যায় এর বেশি ধরে না। নষ্ট ছবিতে সাধারণ autoencoder দাগ আর জঞ্জাল রেখে দেয়, denoising model পরিষ্কার কিন্তু আরও মসৃণ ছবি দেয়। শেষ দুই সারি অংশ ৮-এর বিষয়। MSE-গুলো মাত্র চারটি ছবির, ৪ bit-এ গোল করা ছবি থেকে, তাই টেবিলের সংখ্যার সঙ্গে হুবহু মিলবে না।
৬ · Reconstruction error দিয়ে anomaly detection
বাজারঘরের গুদামে প্রতিদিন হাজার হাজার জামা-জুতার ছবি তোলা হয়। হঠাৎ একটি ব্যাগের ছবি এলো, যা কখনো এই তালিকায় ছিল না। Autoencoder যা দেখে শিখেছে, তা ভালো ফেরায়; যা দেখেনি, তা ভালো ফেরাতে পারে না। তাই একটি ছবির reconstruction error-কে একটি anomaly score হিসেবে ধরা যায়: বেশি error মানে “অচেনা”। Isolation forest, LOF আর one-class SVM-এর মতো পদ্ধতি মেশিন লার্নিং কোর্সের পর্ব ৪৩-এ আছে; এখানে শুধু autoencoder-এর দিক।
পরীক্ষাটি এভাবে: Fashion-MNIST-এর একটি শ্রেণি train থেকে পুরো বাদ। Train-এর প্রথম ৫৫,০০০ ছবির বাকি ৯টি শ্রেণির প্রায় ৪৯,৫০০টিতে conv autoencoder (k = ৩২, ৫ epoch, seed ০, CPU-তে); শেষ ৫,০০০-এর সাধারণ ছবিগুলো (প্রায় ৪,৫০০) আলাদা validation, শুধু threshold বাছার জন্য। তারপর test-এর ১০,০০০ ছবির প্রতিটির MSE score; বাদ দেওয়া শ্রেণির ১,০০০টি ছবি “anomaly”. Score কতটা ভালো আলাদা করে, তা মাপে ROC curve-এর নিচের ক্ষেত্রফল, AUC: একটি এলোমেলো anomaly-র score একটি এলোমেলো সাধারণ ছবির চেয়ে বেশি হওয়ার সম্ভাবনা (০.৫ মানে আন্দাজ, ১ মানে নিখুঁত; ROC-এর মূল কথা AI-এর ভিত্তি কোর্সের পর্ব ৬০-এ)। তুলনায় একই কাজ PCA (k = ৩২)-এর reconstruction error দিয়ে। প্রতিটি শ্রেণি একবার করে বাদ দিয়ে দশটি আলাদা run, সত্যিকারের ফল:
| বাদ দেওয়া শ্রেণি | AE AUC | PCA AUC | TPR, t৯৯ | FPR, t৯৯ |
|---|---|---|---|---|
| ০ T-shirt/top | ০.৪৯৮৭ | ০.৪৯১৮ | ০.০১৩ | ০.০০৯ |
| ১ Trouser | ০.৮৫৫৬ | ০.৮৬৩৯ | ০.০০৩ | ০.০০৯ |
| ২ Pullover | ০.৬২১৫ | ০.৫০০৪ | ০.০১১ | ০.০০৮ |
| ৩ Dress | ০.৫৯২৩ | ০.৬৪৩৯ | ০.০০৪ | ০.০০৮ |
| ৪ Coat | ০.৫০৬৭ | ০.৫৩০৫ | ০.০০৬ | ০.০০৮ |
| ৫ Sandal | ০.৯০৫২ | ০.৮৫৫৬ | ০.১৩৪ | ০.০১০ |
| ৬ Shirt | ০.৫৩২১ | ০.৪৩৯২ | ০.০১৬ | ০.০০৮ |
| ৭ Sneaker | ০.৬৫৫০ | ০.৬৭০০ | ০.০০০ | ০.০০৮ |
| ৮ Bag | ০.৯০৯৯ | ০.৮৯৬৬ | ০.০৬০ | ০.০০৮ |
| ৯ Ankle boot | ০.৭০৬৯ | ০.৮১৪০ | ০.০০২ | ০.০০৮ |
শ্রেণিভেদে বিশাল পার্থক্য: Bag (০.৯১০) আর Sandal (০.৯০৫) সহজে ধরা পড়ে, কারণ train-এ তাদের মতো আকার নেই। T-shirt (০.৪৯৯), Coat (০.৫০৭) আর Shirt (০.৫৩২) প্রায় আন্দাজের সমান: Pullover, Coat আর Shirt দেখতে এত কাছাকাছি যে একটি বাদ দিলেও বাকিরা তাকে ভালোই ফেরায়। PCA প্রায় সমান: দশটির পাঁচটিতে (Trouser, Dress, Coat, Sneaker, Ankle boot) PCA-ই এগিয়ে; “গভীর model মানেই ভালো anomaly detector” এই run সমর্থন করে না। Threshold-এর সৎ হিসাব: t৯৯ মানে validation-এর সাধারণ ছবির ৯৯তম percentile, তাই FPR সবগুলোতে প্রায় ০.০০৮–০.০১, যেমন পরিকল্পনা। কিন্তু সেই কড়া threshold-এ সেরা শ্রেণিতেও TPR মাত্র ০.১৩৪ (Sandal) আর ০.০৬ (Bag): AUC ০.৯১ মানেও কম false alarm-এ বেশির ভাগ anomaly ছাড়া পায়।
এবার নিজে চালিয়ে দেখুন। Bag বাদ দেওয়া run-এর আসল score থেকে এলোমেলোভাবে নেওয়া ৬০০টি সাধারণ আর ৩০০টি Bag-এর test score, আর validation-এর ৫০০টি সাধারণ score পাতায় রাখা আছে (MSE × ১০,০০০, পূর্ণসংখ্যায়)। কোড ROC-AUC, দুটি threshold-এ TPR আর FPR, আর score-এর histogram আঁকে।
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import roc_auc_score, roc_curve
d = {}
for line in open("anom_scores.txt").read().split("\n"):
name, *vals = line.split()
d[name] = np.array(vals, dtype=float) / 10000 # ফাইলে MSE × 10000, পূর্ণসংখ্যা
val = d["normal_val"] # train-এর বাইরের সাধারণ ছবি
s = np.concatenate([d["normal_test"], d["anomaly_test"]])
a = np.r_[np.zeros(600, bool), np.ones(300, bool)]
print("ROC-AUC (এই 900টি test score):", round(roc_auc_score(a, s), 4))
for q in (0.95, 0.99):
t = np.quantile(val, q) # threshold শুধু validation থেকে
tpr, fpr = (s[a] >= t).mean(), (s[~a] >= t).mean()
print(f"val-এর {q:.0%} percentile t = {t:.4f}: TPR {tpr:.3f}, FPR {fpr:.3f}")
fpr_c, tpr_c, _ = roc_curve(a, s)
fig, ax = plt.subplots(1, 2, figsize=(8, 3.2))
bins = np.linspace(0, np.quantile(s, 0.995), 40)
ax[0].hist(s[~a], bins, alpha=0.6, density=True, label="normal")
ax[0].hist(s[a], bins, alpha=0.6, density=True, label="held-out class")
ax[0].axvline(np.quantile(val, 0.99), color="k", ls="--", label="99% val threshold")
ax[0].set_xlabel("reconstruction MSE"); ax[0].legend(fontsize=7)
ax[1].plot(fpr_c, tpr_c); ax[1].plot([0, 1], [0, 1], "k:")
ax[1].set_xlabel("FPR"); ax[1].set_ylabel("TPR"); ax[1].set_title("ROC")
fig.subplots_adjust(wspace=0.3, bottom=0.18)
plt.show()আউটপুট (পাতার Python-এ চালানো):
ROC-AUC (এই 900টি test score): 0.9107
val-এর 95% percentile t = 0.0227: TPR 0.460, FPR 0.050
val-এর 99% percentile t = 0.0320: TPR 0.150, FPR 0.017
৯০০টির নমুনায় AUC ০.৯১০৭, পুরো test-এর ০.৯০৯৯-এর কাছেই। পাতার ৫০০টি validation score-এ ৯৯তম percentile ০.০৩২০, অথচ পুরো ৪,৪৮৫টিতে ০.০৩৮৫: বণ্টনের লেজের percentile ছোট নমুনায় অনেক নড়ে, তাই কড়া threshold-এর জন্য বেশি validation ডেটা লাগে। পুরো test-এ (৯,০০০ সাধারণ, ১,০০০ Bag) desktop-এ মাপা: validation-এর ৯৫তম percentile (০.০২২৭)-এ TPR ০.৪৭৩, FPR ০.০৫৩; ৯৯তম (০.০৩৮৫)-এ TPR ০.০৬, FPR ০.০০৮। Histogram-এ দেখুন, Bag-এর score-এর বেশির ভাগ সাধারণের লম্বা লেজের ভেতরেই পড়ে (median ০.০২২২ বনাম ০.০০৮২)। কোন threshold নেবেন, তা নির্ভর করে একটি false alarm আর একটি ছাড়া পাওয়া anomaly-র খরচের ওপর; এটি ব্যবসার সিদ্ধান্ত, model-এর নয়।
একটি threshold-এ TPR আর FPR হাতে গুনে দেখুন। নিচের দশটি score একই run-এর আসল সংখ্যা (৫টি সাধারণ, ৫টি Bag, উপরের নমুনার প্রথম পাঁচটি করে)।
| ধাপ | |
| হিসাব | |
| ফল |
- Score. পাঁচটি সাধারণ ছবির MSE ০.০০২১ থেকে ০.০১০০, পাঁচটি Bag-এর ০.০১৯২ থেকে ০.০২৭৩। এই দশটিতে দুই দল পুরো আলাদা।
- কড়া threshold. Validation-এর সাধারণ ছবির ৯৯তম percentile, t = ০.০৩৮৫। দশটির কোনোটিই এত বড় নয়: সবাইকে “সাধারণ” বলা হলো। TP ০, FN ৫, FP ০, TN ৫।
- ঢিলে threshold. ৯৫তম percentile, t = ০.০২২৭। এখন প্রতি ১০০টি সাধারণ ছবির প্রায় ৫টিতে false alarm হওয়ার কথা।
- Anomaly গোনা. Bag-এর চারটি score ০.০২২৭-এর সমান বা বড়: TP ৪। ০.০১৯২ ছোট, তাই FN ১।
- সাধারণ গোনা. পাঁচটি সাধারণের কোনোটিই ০.০২২৭ ছোঁয় না: FP ০, TN ৫।
- হার. TPR = TP ÷ (TP + FN) = ৪/৫ = ০.৮; FPR = FP ÷ (FP + TN) = ০/৫ = ০।
- পুরো test-এ. একই threshold-এ ১০,০০০টি ছবিতে TPR ০.৪৭৩ আর FPR ০.০৫৩। দশটির নমুনা ভাগ্যবান ছিল; ছোট নমুনার হার দিয়ে detector বিচার করা যায় না।
৭ · Latent code কতটা কাজের — probe দিয়ে মাপা
ভালো পুনর্গঠন মানেই কি ভালো representation? সরাসরি মাপার একটি উপায় linear probe: encoder স্থির রেখে code-এর ওপর একটি সরল classifier শেখানো (পর্ব ৪৮-এর probe-এর মতো)। Label শুধু এই মাপার সময় লাগে, autoencoder শেখার সময় নয়। Code-এ যদি শ্রেণির তথ্য সহজে আলাদা করা যায় এমনভাবে থাকে, logistic regression ভালো করবে। সঙ্গে k-NN (k = ৫): কাছের code-এর ছবি কি একই শ্রেণির?
চারটি feature-এ তুলনা: কাঁচা ৭৮৪ pixel, PCA-র ৩২ মাত্রা, অংশ ৪-এর conv autoencoder-এর ৩২ মাত্রার code (seed ০), আর একই গঠনের কিন্তু শেখানো হয়নি এমন (এলোমেলো ওজনের) encoder-এর ৩২ মাত্রার code. প্রতিটিতে StandardScaler, তারপর logistic regression (C = ১) আর k-NN; train-এর ৬০,০০০-এ শেখানো, test-এর ১০,০০০-এ accuracy. CPU-তে, scikit-learn 1.9.1, সত্যিকারের run:
| Feature | Logistic regression | k-NN (k = ৫) |
|---|---|---|
| কাঁচা pixel (৭৮৪) | ০.৮৩৪৬ | ০.৮৫৩৩ |
| PCA (৩২) | ০.৮১৪৬ | ০.৮৫৩৮ |
| Conv AE code (৩২) | ০.৮৩১৮ | ০.৮৭২০ |
| শেখানো হয়নি এমন encoder (৩২) | ০.৭১১৭ | ০.৭৮০৪ |
শেখা code কাজের, তবে জাদু নয়: ৩২ মাত্রার AE code-এ logistic regression ০.৮৩১৮, কাঁচা ৭৮৪ pixel-এর (০.৮৩৪৬) প্রায় সমান, PCA-৩২-এর (০.৮১৪৬) চেয়ে ভালো; k-NN-এ AE code সবার সেরা (০.৮৭২০)। একই গঠনের এলোমেলো encoder অনেক পেছনে (০.৭১১৭), তাই লাভটা শেখা থেকে এসেছে, শুধু গঠন থেকে নয়। কিন্তু label দিয়ে সরাসরি শেখানো ছোট CNN এই ডেটায় ০.৮৯৬ পেয়েছে (অংশ ৮); পুনর্গঠনের লক্ষ্য শ্রেণির তথ্য আলাদা করে রাখার কোনো নিশ্চয়তা দেয় না। আধুনিক self-supervised পদ্ধতি (contrastive, masked) তাই প্রায়ই সাধারণ autoencoder-এর চেয়ে ভালো feature দেয় বলে প্রকাশিত কাজে দেখা যায়।
৮ · সাধারণ autoencoder কেন ভালো generator নয়
Decoder তো code থেকে ছবি বানায়। তাহলে এলোমেলো একটি code দিয়ে নতুন জামার ছবি পাওয়া যায় না কেন? কারণ সাধারণ autoencoder-কে কখনো বলা হয়নি code-গুলো কোথায় থাকবে। Train-এর code-গুলো latent জায়গার কিছু এলোমেলো দ্বীপে জড়ো হয়, মাঝের বিশাল ফাঁকা জায়গায় decoder কখনো শেখেনি। এলোমেলো code প্রায়ই সেই ফাঁকা জায়গায় পড়ে। ঢাকার মানচিত্রে যদি শুধু কয়েকটি মহল্লার ঠিকানা থাকে, মানচিত্রের এলোমেলো একটি বিন্দু প্রায়ই নদী বা ফাঁকা মাঠে পড়বে, কোনো বাড়িতে নয়।
পরীক্ষা: অংশ ৪-এর conv autoencoder (k = ৩২)-এর decoder-এ দুই রকম এলোমেলো code, প্রতিটি ১,০০০টি: (ক) standard normal N(০, I), আর (খ) train-এর code থেকে মাপা গড় আর covariance-এর একটি Gaussian. ফলের মান যাচাইয়ের জন্য আলাদা একটি ছোট CNN classifier (৩ epoch, test accuracy নিচে) প্রতিটি ছবিকে কতটা আত্মবিশ্বাসে কোনো শ্রেণিতে ফেলে, তা মাপা হলো: সর্বোচ্চ probability-র গড়, আর কতগুলো ছবিতে সেটি ০.৯-এর বেশি। এটি ছবির মানের একটি মোটা দাগের মাপ, পূর্ণ মূল্যায়ন নয় (সেটি পর্ব ৮১-এর বিষয়)। CPU-তে, সত্যিকারের run:
| ১,০০০টি ছবি | সর্বোচ্চ probability-র গড় | ০.৯-এর বেশি |
|---|---|---|
| test-এর আসল ছবি | ০.৯০৫৮ | ০.৭২৪ |
| সেগুলোর পুনর্গঠন | ০.৮৬৫৪ | ০.৬২৭ |
| decode, z ~ N(০, I) | ০.৬৮৩০ | ০.১৯২ |
| decode, z ~ মাপা Gaussian | ০.৬৯২৭ | ০.২৩৫ |
Classifier-এর test accuracy ০.৮৯৬. আসল ছবির ৭২.৪%-এ সে ০.৯-এর বেশি নিশ্চিত, পুনর্গঠনে ৬২.৭%, কিন্তু N(০, I) থেকে decode করা ছবিতে মাত্র ১৯.২%. কারণ সংখ্যায় স্পষ্ট: train-এর code-এর গড় দৈর্ঘ্য (norm) ৩১.৯, অথচ N(০, I)-এর ৩২ মাত্রার নমুনার মাত্র ৫.৬; প্রতিটি মাত্রার std ৩.০৫ থেকে ৭.০৩, ১ নয়। এলোমেলো code decoder-এর অচেনা এলাকায় পড়ে। Train-এর code থেকে গড় আর covariance মেপে নিলেও লাভ সামান্য (২৩.৫%): একটি Gaussian code-এর আসল, দলে দলে জড়ো বণ্টন ধরতে পারে না। অংশ ৫-এর ছবির শেষ দুই সারিতে এই ঝাপসা, অর্ধেক-চেনা আকারগুলো দেখা যায়। এই ঘাটতিই পর্ব ৭৬-এর VAE-র শুরুর বিন্দু: code-এর বণ্টন training-এর সময়েই একটি prior-এর কাছে বেঁধে দেওয়া।
৯ · কোনটি কখন — autoencoder-এর রূপগুলোর তুলনা
| রূপ | বাধা কোথা থেকে | ভালো কাজে | সীমা |
|---|---|---|---|
| Linear AE / PCA | ছোট k | দ্রুত baseline, সংকোচন, noise কমানো | শুধু সমতল subspace; বাঁকা গঠন ধরে না |
| Undercomplete MLP/conv AE | ছোট k | সংকোচন, representation, anomaly score | ঝাপসা পুনর্গঠন; latent-এ ফাঁকা জায়গা |
| Denoising AE | নষ্ট input | Noise সরানো, শক্ত feature; overcomplete-ও চলে | Noise-এর ধরন বাস্তবের মতো না হলে লাভ কম |
| Sparse AE | Code-এ L1 শাস্তি | ব্যাখ্যাযোগ্য বৈশিষ্ট্য, বড় code | λ বাছাই কঠিন; মরা unit |
| VAE (পর্ব ৭৬) | Code-এর বণ্টনে KL শাস্তি | নমুনা বানানো, মসৃণ latent | ঝাপসা নমুনা; ELBO শুধু একটি সীমা |
একটি বাস্তব নিয়ম: autoencoder নিয়ে কোনো দাবির আগে PCA-কে baseline রাখুন। সংকোচন, anomaly আর probe, তিনটি কাজেই আমাদের run-এ PCA কিছু জায়গায় খুব কাছাকাছি বা ভালো এসেছে। Deep model-এর খরচ (GPU সময়, hyperparameter, seed-এর ওঠানামা) তখনই সার্থক, যখন baseline-কে স্পষ্টভাবে ছাড়ায়।
আরও গভীরে: Linear autoencoder আর PCA-র সম্পর্ক Bourlard ও Kamp (১৯৮৮) এবং Baldi ও Hornik (১৯৮৯)-এর কাজে; Hinton ও Salakhutdinov (Science, ২০০৬) দেখান, স্তরে স্তরে আগে শেখানো গভীর autoencoder PCA-র চেয়ে অনেক ভালো সংকোচন দিতে পারে। Vincent ও সহলেখকেরা (২০০৮, ২০১০) denoising-কে একটি নীতি হিসেবে দাঁড় করান, আর পরে দেখানো হয় যে ছোট Gaussian noise-এর denoising, ডেটার বণ্টনের score (log-density-র gradient) অনুমানের সঙ্গে যুক্ত; এই সূত্রই পর্ব ৭৯-এর diffusion model-এর একটি তাত্ত্বিক ভিত্তি। Transposed convolution-এর মাপের সব হিসাব Dumoulin ও Visin-এর “A guide to convolution arithmetic for deep learning” (২০১৬)-এ।
পরীক্ষা ও ইন্টারভিউতে:
- ConvTranspose2d(64, 32, 4, stride 2, padding 1), input 8 × 8: output আর parameter? (৮ − ১)·২ − ২ + ৪ = ১৬, তাই ১৬ × ১৬; ৬৪ × ৩২ × ১৬ + ৩২ = ৩২,৮০০।
- Linear autoencoder MSE-তে কী শেখে? PCA-র প্রথম k component-এর একই subspace, কিন্তু দিকগুলো ঘোরানো হতে পারে।
- Overcomplete autoencoder কেন বিপজ্জনক? Identity কপি করেই loss শূন্যের কাছে নেয়; noise, sparsity বা অন্য বাধা লাগে।
- Anomaly-তে কেন accuracy নয়? Anomaly খুব কম হলে সবকে “সাধারণ” বললেই accuracy বেশি; AUC বা নির্দিষ্ট FPR-এ TPR দেখুন।
- সাধারণ AE-র decoder-এ N(০, I) দিলে ভালো ছবি আসে না কেন? Code-এর বণ্টন কোনো নিয়মে বাঁধা নয়; এলোমেলো code ফাঁকা জায়গায় পড়ে। VAE বণ্টন বেঁধে দেয়।
১০ · AI-এর যুগে — autoencoder-এর কোড আর দাবি যাচাই
Autoencoder-এর কোড AI assistant কয়েক সেকেন্ডে লেখে। কিন্তু এই ক্ষেত্রের ভুলগুলো চুপচাপ ঘটে: আকার মিলে গেলেও ভুল সূত্র, test-এ বেছে নেওয়া threshold, বা একটি ভাগ্যবান seed-এর “চমৎকার AUC”.
| AI প্রায়ই যা করে | কেন ভুল | কীভাবে ধরবেন |
|---|---|---|
| ConvTranspose-এ output_padding ভুলে যাওয়া, বা ওজনের আকার (out, in, k, k) ধরা | ২৮-এর বদলে ২৭; parameter-এর হিসাব ভুল | একটি শূন্যের tensor চালিয়ে .shape দেখা |
Decoder-এ sigmoid দিয়ে আবার BCEWithLogitsLoss | দুবার sigmoid; gradient নষ্ট | Loss-এর নথি পড়া; শেষ layer আর loss জোড়ায় মেলানো |
| Anomaly threshold test-এর label দেখে বাছা | Test ফাঁস; ফল বাস্তবে আসবে না | শুধু সাধারণ validation ডেটায় threshold, যেমন ৯৯তম percentile |
| Anomaly-সহ ডেটায় autoencoder শেখানো | Model anomaly-ও ফেরাতে শিখে যায় | Train ডেটা পরিষ্কার কি না দেখা; contamination মাপা |
| একটি seed-এর ফলে “AE PCA-কে হারায়” দাবি | ছোট পার্থক্য seed-এর ওঠানামা হতে পারে | কয়েকটি seed, PCA baseline, একই test |
| সাধারণ AE-র decoder থেকে N(০, I) দিয়ে “নতুন নমুনা” | Latent-এর বণ্টন বাঁধা নয় | নমুনা চোখে আর classifier দিয়ে দেখা; দরকারে VAE |
কোড চাওয়ার সময় যাচাইও চেয়ে নিন:
AI-এর লেখা autoencoder কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট
ভাবনার প্রশ্ন
প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।
প্র ০১Linear autoencoder যদি শুধু PCA-ই খুঁজে পায়, তাহলে ১৯৮০-এর দশক থেকে ২০০৬ পর্যন্ত autoencoder নিয়ে আগ্রহ ওঠানামা করল কেন? গভীর autoencoder আসলে কী যোগ করে?
১৯৮০-এর দশকে backpropagation জনপ্রিয় হওয়ার পর “auto-association” বা নিজেকে নিজে ফেরানো network নিয়ে কাজ শুরু হয়। Bourlard ও Kamp (১৯৮৮) দেখান, একটি লুকানো স্তরের linear network-এর সেরা সমাধান SVD দিয়েই সরাসরি পাওয়া যায়; Baldi ও Hornik (১৯৮৯) প্রমাণ করেন, এমন network-এর loss-এ খারাপ local minimum নেই, সেরা সমাধান PCA-র subspace. ফল দুই রকম। ভালো দিক: শেখানো সহজ আর তত্ত্ব পরিষ্কার। খারাপ দিক: তাহলে network-এর দরকার কী? একটি SVD-ই যথেষ্ট, আর সেটি দ্রুত ও নিশ্চিত। এই পর্বের পাতার কোডেও gradient descent প্রায় PCA-র loss-এ থেমেছে, আর subspace-এর কোণ শূন্যের কাছে।
Non-linear আর গভীর autoencoder তত্ত্বে বেশি শক্তিশালী, কারণ বাঁকা গঠন ধরতে পারে। কিন্তু সে সময় গভীর network শেখানো কঠিন ছিল: gradient হারিয়ে যেত (পর্ব ১৭-এর সমস্যা), আর ভালো initialization জানা ছিল না। Hinton ও Salakhutdinov ২০০৬ সালে Science-এ দেখান, প্রতিটি স্তর আলাদা করে (restricted Boltzmann machine দিয়ে) আগে শেখালে তারপর পুরো গভীর autoencoder fine-tune করা যায়, আর তাঁদের পরীক্ষায় একই code মাপে PCA-র চেয়ে অনেক ভালো পুনর্গঠন আসে। এই কাজ ২০০০-এর দশকের শেষে deep learning-এর পুনর্জাগরণের একটি প্রধান সূচনা হিসেবে ধরা হয়। পরে ReLU, ভালো initialization আর BatchNorm আসার পর স্তরে স্তরে আগে শেখানো প্রায় অপ্রয়োজনীয় হয়ে যায়।
আমাদের run-ও একই শিক্ষা দেয়, তবে সতর্কতাসহ। ছোট bottleneck-এ non-linear model PCA-কে স্পষ্টভাবে ছাড়ায়, কারণ বাঁকা গঠন কয়েকটি সংখ্যায় ধরতে পারে; বড় bottleneck-এ পার্থক্য কমে, কারণ তখন PCA-ও প্রায় সব variance রাখে। তাই প্রশ্ন “network না PCA” নয়, প্রশ্ন “কত ছোট code চাই, আর ডেটার গঠন কতটা বাঁকা”।
মূল উপলব্ধি: Linear autoencoder PCA-র একটি ধীর রূপ; গভীর, non-linear autoencoder-এর আসল মূল্য ছোট code-এ বাঁকা গঠন ধরা। আর সেই মূল্য দাবি করার আগে PCA-কে baseline হিসেবে মাপা দরকার।
প্র ০২Denoising autoencoder থেকে BERT-এর masked language model আর ছবির MAE পর্যন্ত একটি সরল রেখা টানা যায় কি? “নষ্ট করে ফেরাও” ধারণাটি কেন এত সফল?
Vincent, Larochelle, Bengio ও Manzagol ২০০৮ সালে denoising autoencoder প্রস্তাব করেন, যুক্তি ছিল সরল: ভালো representation সেটি, যা নষ্ট input থেকেও আসল জিনিস ফেরাতে সাহায্য করে। তাঁরা input-এর কিছু অংশ শূন্য করে (masking noise) বা Gaussian noise দিয়ে পরীক্ষা করেন, আর ২০১০ সালে স্তরে স্তরে সাজানো “stacked” denoising autoencoder দিয়ে দেখান যে এভাবে আগে শেখানো feature পরের classification-এ সাহায্য করে।
পরে এই ধারণা ভাষায় বিশাল রূপ নেয়। BERT (Devlin ও সহলেখক, ২০১৮)-এর masked language modeling আসলে একটি denoising কাজ: বাক্যের প্রায় ১৫% token লুকিয়ে বা বদলে দিয়ে model-কে আসল token অনুমান করতে বলা হয় (পর্ব ৪৭ · Pretraining-এর লক্ষ্য)। T5-এর span corruption আর BART-এর নানা রকম নষ্ট করা একই পরিবারের। ছবিতে He ও সহলেখকদের Masked Autoencoder (MAE, ২০২১) প্রায় ৭৫% patch লুকিয়ে pixel ফেরাতে শেখায় (পর্ব ৩৮ · Vision Transformer). এমনকি diffusion model (পর্ব ৭৯) প্রতিটি ধাপে একটি denoiser শেখে; তাত্ত্বিকভাবে ছোট noise-এর denoising ডেটার score অনুমানের সঙ্গে যুক্ত, যা Vincent ২০১১ সালের একটি প্রবন্ধে দেখান।
কেন সফল? প্রথমত, label লাগে না, তাই ইন্টারনেটের বিশাল ডেটা ব্যবহার করা যায়। দ্বিতীয়ত, কাজটি কঠিন কিন্তু অসম্ভব নয়: লুকানো অংশ ফেরাতে আশপাশের প্রসঙ্গ বুঝতে হয়, শুধু কপি করে হয় না। তৃতীয়ত, কতটা নষ্ট করা হবে, সেটি একটি সহজ নিয়ন্ত্রণ; বেশি নষ্ট করলে কাজ কঠিন, model-কে আরও বিমূর্ত গঠন শিখতে হয়। তবে সীমাও আছে: কোন রকম নষ্ট করা কোন ডেটায় ভালো, তা মূলত পরীক্ষা করে পাওয়া, সার্বজনীন তত্ত্ব থেকে নয়; আর pixel ফেরানোর লক্ষ্য কখনো কখনো খুঁটিনাটির পেছনে বেশি ক্ষমতা খরচ করায়।
মূল উপলব্ধি: “নষ্ট করো, ফেরাও” একটি label-হীন শেখার নীতি, যা denoising autoencoder থেকে masked language model, MAE আর diffusion পর্যন্ত বিস্তৃত। কাজটি কপি দিয়ে করা যায় না বলেই model গঠন শেখে।
প্র ০৩Reconstruction error দিয়ে anomaly ধরার পদ্ধতি কখন ব্যর্থ হয়? বাজারঘর যদি এটি দিয়ে ত্রুটিপূর্ণ পণ্যের ছবি ধরতে চায়, কী কী সতর্কতা লাগবে?
মূল ধারণা হলো “যা দেখেনি, তা খারাপ ফেরায়”। কিন্তু autoencoder প্রায়ই প্রত্যাশার চেয়ে ভালো সাধারণীকরণ করে। অচেনা ছবি যদি চেনা ছবির কাছাকাছি হয়, বা সহজ হয় (অনেক ফাঁকা অংশ, সরল আকার), তার error কম আসে। আমাদের দশটি run-এ ঠিক এটি দেখা গেছে: কোনো শ্রেণি বাদ দিলে AUC খুব ভালো, কোনোটিতে প্রায় আন্দাজের কাছাকাছি, কারণ সেই শ্রেণির মতো দেখতে অন্য শ্রেণি train-এ ছিল। Likelihood-ভিত্তিক model-এও একই ধরনের ফাঁদ জানা: Nalisnick ও সহলেখকেরা (২০১৯) দেখান, একটি ছবির সেটে শেখানো generative model অন্য একটি, সরল ছবির সেটকে বেশি likelihood দিতে পারে।
দ্বিতীয় সমস্যা train ডেটার পরিচ্ছন্নতা। বাজারঘরের ছবির মধ্যে যদি কিছু ত্রুটিপূর্ণ পণ্যও থাকে, model সেগুলোও ফেরাতে শেখে, আর তখন ঠিক সেই ধরনের ত্রুটি ধরা পড়ে না। তৃতীয় সমস্যা মাপের: MSE সব pixel-কে সমান ধরে। একটি জামার বোতামের ছোট ফাটল কয়েকটি pixel-এ, কিন্তু আলো বা পটভূমির সামান্য বদল হাজার pixel-এ। ফলে error হয়তো ত্রুটি নয়, আলো মাপছে। এজন্য অনেক সময় pixel-এর বদলে feature-এর পার্থক্য, বা ছবির ছোট টুকরো ধরে error দেখা হয়।
সতর্কতার তালিকা: (১) “সাধারণ” ছবিগুলো হাতে যাচাই করে train সেট বানানো; (২) threshold শুধু সাধারণ ছবির একটি আলাদা validation সেট থেকে, যেমন ৯৯তম percentile, যাতে false alarm-এর হার আগে থেকে জানা থাকে; (৩) আসল ত্রুটির কিছু উদাহরণ জোগাড় করে AUC আর নির্দিষ্ট FPR-এ TPR মাপা; (৪) PCA আর isolation forest-এর মতো সরল baseline; (৫) সময়ের সঙ্গে ক্যামেরা, আলো বা পণ্যের নকশা বদলালে score-এর বণ্টন সরে যায়, তাই নিয়মিত নজরদারি (মেশিন লার্নিং কোর্সের পর্ব ৫৯); (৬) score একটি সতর্কসংকেত, চূড়ান্ত রায় নয়: মানুষ দেখে সিদ্ধান্ত নেবেন।
মূল উপলব্ধি: Reconstruction error শুধু “model-এর কাছে কতটা অচেনা” মাপে, “কতটা ত্রুটিপূর্ণ” নয়। পরিষ্কার train ডেটা, সাধারণ ছবি থেকে বাছা threshold, baseline আর শ্রেণিভিত্তিক মাপ ছাড়া এর ওপর ভরসা করা ঝুঁকিপূর্ণ।
প্র ০৪Bottleneck কত বড় রাখবেন? ছোট code ভালো সংকোচন দেয়, কিন্তু কি সবসময় ভালো representation দেয়? Latent diffusion-এর autoencoder এই প্রশ্নের কী উত্তর দেয়?
এখানে তিনটি লক্ষ্য একে অপরের সঙ্গে টানাটানি করে। সংকোচনের জন্য ছোট code ভালো, কিন্তু পুনর্গঠন ঝাপসা হয়। পুনর্গঠনের জন্য বড় code ভালো, কিন্তু খুব বড় হলে model প্রায় কপি করে, আর code-এ অপ্রয়োজনীয় খুঁটিনাটি ঢোকে। Representation-এর জন্য, অর্থাৎ পরের কাজে (classification, খোঁজা) সাহায্যের জন্য, “ঠিক মাপ” নির্ভর করে পরের কাজটি কী তার ওপর; ভালো পুনর্গঠন আর ভালো feature এক জিনিস নয়। আমাদের probe-এর run-এ দেখা যায়, পুনর্গঠনের জন্য শেখানো code সব মাপে অন্য সরল feature-কে ছাড়ায় না।
বাস্তবে bottleneck বাছাই একটি পরীক্ষা: কয়েকটি k-তে পুনর্গঠনের error আর আসল কাজের মাপ পাশাপাশি আঁকা, আর যেখানে বাঁক (elbow) আসে বা কাজের মাপ স্থির হয়, সেখানে থামা। PCA-র explained variance-এর বক্ররেখা একটি সস্তা প্রথম আন্দাজ দেয়। ছবির সংকোচনের গবেষণায় (যেমন Ballé ও সহলেখকদের ২০১৭ সালের end-to-end শেখা image compression) আরও এক ধাপ এগিয়ে code-এর bit-সংখ্যাকেও loss-এ যোগ করা হয়: rate আর distortion-এর ভারসাম্য একটি λ দিয়ে।
Latent diffusion (Rombach ও সহলেখক, ২০২২; পর্ব ৮০) একটি আকর্ষণীয় উত্তর দেয়। সেখানে autoencoder-এর কাজ সর্বোচ্চ সংকোচন নয়, বরং “দেখতে প্রায় একই” রেখে মাপ কয়েক গুণ ছোট করা, যাতে পরের diffusion model কম খরচে চলে। প্রবন্ধে বিভিন্ন downsampling factor তুলনা করে দেখানো হয়, খুব কম সংকোচনে diffusion-এর খরচ বেশি থাকে আর খুব বেশিতে মান পড়ে; মাঝারি মান (প্রতিটি দিকে প্রায় ৪ থেকে ১৬ গুণ) ভালো ভারসাম্য দেয় বলে তাঁরা জানান। সেখানে ঝাপসা হওয়া ঠেকাতে শুধু MSE নয়, perceptual loss আর adversarial loss-ও যোগ করা হয়েছে, আর code-এর ওপর হালকা একটি KL বা vector-quantization শাস্তি।
মূল উপলব্ধি: Bottleneck-এর মাপ নির্ভর করে code দিয়ে পরে কী করবেন তার ওপর। কয়েকটি k-তে পুনর্গঠন আর আসল কাজ দুটিই মাপুন; latent diffusion দেখায়, “যথেষ্ট ভালো দেখতে, অনেক ছোট” একটি ভালো লক্ষ্য হতে পারে।
প্র ০৫সাধারণ autoencoder-এর decoder দিয়ে নতুন ছবি বানাতে গেলে কী সমস্যা হয়, আর VAE সেটি কীভাবে সমাধানের চেষ্টা করে? Interpolation কেন আরেকটি ভালো পরীক্ষা?
সাধারণ autoencoder-কে বলা হয় শুধু “x থেকে z, z থেকে x ফেরাও”। Code-গুলো latent জায়গায় কোথায় বসবে, কতটা ছড়াবে, তাদের মাঝে কী থাকবে, এসব নিয়ে কোনো শর্ত নেই। ফলে train-এর code-গুলো নানা মাপের অনিয়মিত দলে জড়ো হয়, কোনো কোনো মাত্রায় খুব ছড়ানো, কোনোটিতে খুব সরু। Decoder শুধু সেই দলগুলোর আশপাশে ভালো কাজ করতে শেখে। আমাদের run-এ standard normal থেকে নেওয়া code-এর মাপ (norm) train-এর code-এর মাপের চেয়ে অনেক আলাদা ছিল, আর decode করা ছবিগুলো classifier-এর কাছে অনেক কম চেনা লেগেছে। Train-এর code থেকে একটি Gaussian মেপে নিলে অবস্থা সামান্যই ভালো হয়েছে, কারণ Gaussian আসল বণ্টনের একটি মোটা আন্দাজ মাত্র।
Interpolation আরেকটি পরীক্ষা: দুটি ছবির code z১ আর z২-এর মাঝের সরলরেখার বিন্দুগুলো decode করা। মসৃণ latent হলে একটি জামা ধীরে ধীরে আরেকটিতে বদলাবে; ফাঁকা জায়গা থাকলে মাঝপথে অদ্ভুত, দুটি ছবির স্বচ্ছ মিশ্রণের মতো ছবি আসবে।
Kingma ও Welling (২০১৩) আর একই সময়ে Rezende, Mohamed ও Wierstra-র variational autoencoder এই সমস্যাকে মূল থেকে ধরে। Encoder একটি বিন্দু নয়, একটি বণ্টন দেয়, আর loss-এ একটি KL শাস্তি code-এর বণ্টনকে একটি নির্দিষ্ট prior, সাধারণত N(০, I)-এর কাছে টানে। তাহলে N(০, I) থেকে নেওয়া code decoder-এর চেনা জায়গায় পড়ে, আর পুরো model একটি সত্যিকারের probabilistic generative model হয়, যার likelihood-এর একটি নিচের সীমা (ELBO) মাপা যায়। এর জন্য পুনর্গঠন আর KL-এর মধ্যে ভারসাম্য রাখতে হয়, আর নমুনা প্রায়ই ঝাপসা হয়; এসব খুঁটিনাটি পর্ব ৭৬-এ। মেশিন লার্নিং কোর্সের পর্ব ৪৮-এ ELBO আর reparameterization-এর তত্ত্ব আগেই আছে।
মূল উপলব্ধি: সাধারণ autoencoder সংকোচন শেখে, বণ্টন শেখে না; তাই এলোমেলো code থেকে নমুনা অবিশ্বস্ত। VAE code-এর বণ্টন একটি prior-এর কাছে বেঁধে দিয়ে decoder-কে সত্যিকারের generator বানানোর চেষ্টা করে।
অনুশীলন
খাতা-কলমে করুন, তারপর উত্তর মেলান। পরীক্ষা আর ইন্টারভিউতে এই ধরনের হিসাব কাজে লাগে।
-
হিসাব করুন: (ক) একটি MLP autoencoder: ৭৮৪ → ২৫৬ → ৩২ → ২৫৬ → ৭৮৪, প্রতিটি Linear-এ bias আছে। মোট parameter কত? (খ)
ConvTranspose2d(64, 32, kernel_size=4, stride=2, padding=1)-এ 64 × 8 × 8 input দিলে output-এর আকার আর parameter কত? (গ) একটি conv encoder 1 × 28 × 28 ছবিকে ৩২ সংখ্যার code-এ নেয়। সংকোচনের অনুপাত কত, আর code float32-এ রাখলে প্রতি ছবিতে কত byte?(ক) ৭৮৪ × ২৫৬ = ২,০০,৭০৪, bias যোগ করে ২,০০,৯৬০; ২৫৬ × ৩২ + ৩২ = ৮,২২৪; ৩২ × ২৫৬ + ২৫৬ = ৮,৪৪৮; ২৫৬ × ৭৮৪ + ৭৮৪ = ২,০১,৪৮৮। মোট ৪,১৯,১২০, যা PyTorch-এর গণনার সঙ্গে মেলে। (খ) (৮ − ১)·২ − ২·১ + ৪ = ১৬, তাই 32 × 16 × 16; kernel ৪ আর stride ২-এ output_padding লাগে না। Parameter: ৬৪ × ৩২ × ৪ × ৪ + ৩২ = ৩২,৮০০. (গ) ৭৮৪ ÷ ৩২ = ২৪.৫ গুণ কম সংখ্যা; code ৩২ × ৪ = ১২৮ byte, যেখানে আসল uint8 ছবি ৭৮৪ byte, তাই byte-এর হিসাবে সংকোচন মাত্র প্রায় ৬ গুণ। সংখ্যার সংকোচন আর byte-এর সংকোচন এক নয়।
-
আউটপুট অনুমান করুন: কোড না চালিয়ে তিনটি print-এর ফল লিখুন।
import numpy as np X = np.array([[1., 2.], [2., 4.], [3., 5.]]) We = np.array([[0.2], [0.4]]) Wd = np.array([[1.0, 2.0]]) Z = X @ We Xh = Z @ Wd print(Z.ravel()) print(Xh) print(((Xh - X)**2).mean(axis=1))[1. 2. 2.6] [[1. 2. ] [2. 4. ] [2.6 5.2]] [0. 0. 0.1]Z = ০.২·x১ + ০.৪·x২: ০.২ + ০.৮ = ১; ০.৪ + ১.৬ = ২; ০.৬ + ২.০ = ২.৬। Decoder [১, ২] দিকে নেয়, তাই x̂ = z × [১, ২]. প্রথম দুটি সারি ঠিক [১, ২] রেখার ওপর, তাই error শূন্য। তৃতীয় সারি [৩, ৫] রেখায় নেই: x̂ = [২.৬, ৫.২], বর্গ ত্রুটি ০.১৬ আর ০.০৪, গড় ০.১। এক সংখ্যার code শুধু একটি রেখার ওপরের বিন্দু নিখুঁত ফেরাতে পারে।
-
নকশা সমালোচনা করুন: একটি দলের পরিকল্পনা: “বাজারঘরের গুদামের সব ছবি (ত্রুটিপূর্ণগুলোসহ, কারণ আলাদা করা কঠিন) দিয়ে একটি overcomplete autoencoder (code ২,০০০) শেখাব, noise ছাড়া। Test সেটের label দেখে যে threshold-এ accuracy সবচেয়ে বেশি, সেটি নেব; ত্রুটি ১% বলে accuracy ৯৯% হলেই সফল। এক seed-এ PCA-র চেয়ে AUC ০.০১ বেশি এলে ঘোষণা দেব autoencoder ভালো। নতুন পণ্যের নকশা বানাতে decoder-এ N(০, I) থেকে code দেব।” অন্তত পাঁচটি সমস্যা আর সমাধান বলুন।
- ত্রুটিপূর্ণ ছবিসহ train: model ত্রুটিও ফেরাতে শেখে। সমাধান: হাতে যাচাই করা সাধারণ ছবির একটি ছোট পরিষ্কার সেট, বা contamination মেপে সন্দেহজনক ছবি বাদ।
- Overcomplete, noise ছাড়া: identity কপি করতে পারে, তখন অচেনা ছবিও ভালো ফেরায়। সমাধান: undercomplete bottleneck, বা denoising/sparsity.
- Test-এর label দেখে threshold: ফাঁস; ফল বাস্তবে আসবে না। সমাধান: সাধারণ ছবির validation সেটে percentile ধরে threshold, test শেষে একবার।
- ১% ত্রুটিতে accuracy: সবাইকে “সাধারণ” বললেই ৯৯%. সমাধান: AUC, আর নির্দিষ্ট FPR-এ TPR (যেমন ১% false alarm-এ কত ত্রুটি ধরা পড়ে)।
- এক seed-এ ০.০১ পার্থক্য: seed-এর ওঠানামার মধ্যেই থাকতে পারে। সমাধান: কয়েকটি seed, bootstrap CI.
- N(০, I) থেকে নতুন নকশা: সাধারণ AE-র latent বাঁধা নয়, ছবি অর্থহীন হতে পারে। সমাধান: VAE বা diffusion-এর মতো সত্যিকারের generative model, আর নকশার কাজে মানুষের বিচার।
-
কোড লিখুন: NumPy দিয়ে একটি function
rates(scores, is_anom, t)লিখুন, যা threshold t-এ (score ≥ t মানে anomaly) TPR আর FPR ফেরত দেয়; আর একটি functionauc_rank(scores, is_anom), যা AUC বের করে এই সংজ্ঞায়: সব (anomaly, সাধারণ) জোড়ার মধ্যে কত ভাগে anomaly-র score বড় (সমান হলে আধা)। scores = [০.০২, ০.০৫, ০.০৪, ০.০৯, ০.০৩, ০.০৭], is_anom = [০, ১, ০, ১, ০, ০]-এ t = ০.০৫ দিলে TPR = ১, FPR = ১/৪, আর AUC = ৭/৮ আসে কি না যাচাই করুন।import numpy as np def rates(s, a, t): s, a = np.asarray(s), np.asarray(a, bool) pred = s >= t return (pred & a).sum() / a.sum(), (pred & ~a).sum() / (~a).sum() def auc_rank(s, a): s, a = np.asarray(s), np.asarray(a, bool) diff = s[a][:, None] - s[~a][None, :] # প্রতিটি (anomaly, সাধারণ) জোড়া return ((diff > 0) + 0.5 * (diff == 0)).mean() s = [0.02, 0.05, 0.04, 0.09, 0.03, 0.07]; a = [0, 1, 0, 1, 0, 0] print(rates(s, a, 0.05), auc_rank(s, a)) # TPR 1.0, FPR 0.25, AUC 0.875দুটি anomaly (০.০৫, ০.০৯) দুটিই ≥ ০.০৫, তাই TPR = ২/২ = ১। চারটি সাধারণের মধ্যে শুধু ০.০৭ ≥ ০.০৫, তাই FPR = ১/৪। জোড়া ২ × ৪ = ৮টি; ০.০৯ সবগুলোর চেয়ে বড় (৪), ০.০৫ তিনটির চেয়ে বড় কিন্তু ০.০৭-এর চেয়ে ছোট (৩), মোট ৭/৮ = ০.৮৭৫। এটি Mann–Whitney U পরিসংখ্যানের সঙ্গে AUC-এর সম্পর্ক;
sklearn.metrics.roc_auc_scoreএকই উত্তর দেয়।
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমAutoencoder দিয়ে anomaly detection-এর ধাপগুলো সাজান (▲▼ চেপে সরান)
- হাতে যাচাই করা শুধু সাধারণ ছবি দিয়ে train আর validation সেট বানানো
- Train সেটে autoencoder শেখানো, আর PCA-কে baseline হিসেবে রাখা
- Validation-এর সাধারণ ছবির reconstruction error থেকে threshold বাছা, যেমন ৯৯তম percentile
- সাধারণ আর জানা anomaly-সহ আলাদা test সেটে একবার AUC আর TPR/FPR মাপা
- চালুর পরে score-এর বণ্টন নজরে রাখা আর সতর্কসংকেত মানুষকে দিয়ে যাচাই করানো
nn.ConvTranspose2d(32, 16, kernel_size=3, stride=2, padding=1)-এ 32 × 7 × 7 input দিলে (output_padding ০) output-এর আকার কত?
- 16 × 14 × 14
- 16 × 13 × 13
- 32 × 14 × 14
- 16 × 4 × 4
nn.ConvTranspose2d(32, 16, kernel_size=3) layer-এ (bias-সহ) কতটি শেখার parameter আছে?
- ৪,৬০৮
- ৪,৬৪০
- ৪,৬২৪
- ২,২৬,৫৭৬
Activation ছাড়া (সব layer linear) একটি autoencoder, bottleneck k, কেন্দ্রে আনা ডেটায় MSE loss-এ পুরোপুরি শেখানো হলো। Decoder-এর কলামগুলো সম্পর্কে কোনটি ঠিক?
- প্রথম k টি principal component-এর একই subspace span করে, কিন্তু কলামগুলো আলাদা আলাদা PC না-ও হতে পারে
- কলামগুলো হুবহু PC১, PC২, … ক্রমে, orthonormal
- PCA-র চেয়ে কম reconstruction error দেয়
- কলামগুলো এলোমেলো, কোনো নির্দিষ্ট subspace নেই
একটি autoencoder-এর code-এর মাপ input-এর মাপের চেয়ে বড় (overcomplete), আর কোনো noise বা শাস্তি নেই। সবচেয়ে বড় ঝুঁকি কোনটি?
- Training শুরুই হবে না, loss কমবে না
- পুনর্গঠন সবসময় খুব ঝাপসা হবে
- Parameter-এর সংখ্যা GPU-র memory ছাড়িয়ে যাবে
- Model প্রায় identity কপি শেখে, ফলে code-এ দরকারি গঠন আসে না
১,০০০টি ছবির ১০টি ত্রুটিপূর্ণ। একটি anomaly detector সব ছবিকে “সাধারণ” বলে। এর accuracy আর TPR কত?
- accuracy ৯৯%, TPR ১
- accuracy ৯৯%, TPR ০
- accuracy ১%, TPR ০
- accuracy ৫০%, TPR ০.৫
Denoising autoencoder-এর training-এ loss কোন দুটির মধ্যে মাপা হয়?
- Model-এর output আর নষ্ট input x̃
- পরিষ্কার input থেকে পাওয়া output আর পরিষ্কার x
- নষ্ট input x̃ থেকে পাওয়া output আর পরিষ্কার x
- Code z আর একটি standard normal নমুনা
একটি সাধারণ (VAE নয়) autoencoder-এর decoder-এ N(0, I) থেকে নেওয়া এলোমেলো code দিলে প্রায়ই অর্থহীন ছবি আসে। প্রধান কারণ কী?
- Code-এর বণ্টন training-এ কোনো নির্দিষ্ট বণ্টনে বাঁধা হয়নি, তাই এলোমেলো code decoder-এর অচেনা জায়গায় পড়ে
- Decoder সবসময় একই ছবি দেয়
- Transposed convolution এলোমেলো code-এ ভুল মাপের ছবি দেয়
- Bottleneck খুব বড় রাখলেই সমস্যা চলে যায়
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:
মনে রাখুন
- x → z → x̂: encoder চাপে, bottleneck মাপ বাঁধে, decoder ফেরায়; লক্ষ্য input নিজেই। Overcomplete হলে noise বা sparsity-র মতো বাধা লাগে।
- Linear AE + MSE = PCA-র subspace, দিক ঘোরানো হতে পারে। Non-linear model ছোট code-এ PCA-কে ছাড়ায়, বড় code-এ পার্থক্য কমে।
- ConvTranspose2d: Hout = (Hin − ১)·s − ২p + k + op; ওজন (in, out, k, k). Stride ২-এ ৭ → ১৪ পেতে op = ১।
- Denoising: x̃ দিয়ে x ফেরানো; কপি দিয়ে চলে না, তাই গঠন শেখে। এই নীতি masked LM, MAE আর diffusion পর্যন্ত গেছে।
- Anomaly score = reconstruction error; AUC শ্রেণিভেদে খুব আলাদা, threshold সাধারণ ডেটায় বাছুন, PCA baseline রাখুন। এলোমেলো code-এ ভালো ছবি আসে না, তাই generator-এর জন্য VAE.
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৭৬ · Variational autoencoder — ELBO, reparameterization আর latent spaceপরবর্তী পর্বlatent space-কে নমুনা তোলার মতো করে গোছানোর গণিত।
- ডিপ লার্নিং, NLP আর Generative AI · কোর্স হোম৯১টি পর্বপুরো কোর্সের রোডম্যাপ, মডিউলভিত্তিক সব পর্ব।
- পর্ব ২৫ · Detection আর segmentationফিরে দেখুনU-Net-এর encoder–decoder আর transposed convolution.
- মেশিন লার্নিং · পর্ব ৪৩ · Anomaly detectionপাশের ট্র্যাকIsolation forest, LOF আর one-class SVM.
- মেশিন লার্নিং · পর্ব ৯ · SVDপাশের ট্র্যাকLow-rank approximation, PCA-র ভিত্তি।