সরাসরি পর্বে যান
পর্ব ০৬ · ৯১-এর মধ্যে · মডিউল ১

Network কী শিখতে পারে — universal approximation, গভীরতা আর প্রস্থ

What Networks Can Learn — Universal Approximation, Depth vs Width, Three Kinds of Error and Double Descent

Taught in Bangla, this lesson separates three questions: can a network express a function, can gradient descent find it, and does it generalize. In in-page Python with NumPy it builds functions from ReLU hinges, fits a demand curve by hand and measures error as units grow, compares hand-built with trained networks to expose optimization error, counts sawtooth pieces to show depth versus width, and runs a small double descent experiment. It reads the theorem carefully and discusses inductive bias.

এই পর্বে যা শিখবেন

  • Universal approximation theorem ঠিক কী বলে (Cybenko, Hornik, Leshno ও সহকর্মী), আর কী একেবারেই বলে না
  • ReLU-এর কব্জা দিয়ে হাতে একটি piecewise linear function গড়া, ভুলের হার হিসাব করা, আর শেখানো network-এর সঙ্গে মেলানো
  • Approximation, estimation আর optimization error আলাদা করে মাপা
  • গভীরতা বনাম প্রস্থ: sawtooth উদাহরণ, Telgarsky-র depth separation আর linear region গোনা
  • Overparameterization, double descent আর inductive bias: architecture কেন গুরুত্বপূর্ণ

ভিত আগেই তৈরি। AI-এর ভিত্তি কোর্সের পর্ব ৬৮-এ neuron, XOR, backprop আর universal approximation-এর এক অনুচ্ছেদের পরিচয় আছে। মেশিন লার্নিং কোর্সের পর্ব ০৩-এ approximation আর estimation error-এর ভাঙন, আর পর্ব ১২-এ neural network-এর loss কেন convex নয়। এই কোর্সে পর্ব ০৩ (NumPy-তে multilayer perceptron) আর পর্ব ০৪ (Backpropagation হাতে-কলমে) MLP লেখা আর শেখানো দেখায়। এখানে প্রশ্নটা অন্য: কতটা শেখা সম্ভব, আর কেন। চারটি ব্লক পাতাতেই চলে (ব্রাউজারের Python, NumPy আর matplotlib); প্রথমবার package নামতে কয়েক সেকেন্ড লাগে।

১ · প্রশ্নটা আসলে তিনটি — প্রকাশ, খোঁজা আর নতুন ডেটা

বাজারঘর (এই কোর্সের কাল্পনিক অনলাইন দোকান) দিনের প্রতিটি ঘণ্টায় কতগুলো অর্ডার আসবে তার একটি curve চায়, যাতে ডেলিভারি-কর্মীর শিফট সাজানো যায়। বানানো curve-টি: ভোরে প্রায় ৫টি অর্ডার, দুপুর ১টায় একটি চূড়া (প্রায় ৩০টি), আর রাত ৯টায় বড় চূড়া (প্রায় ৪৫টি)। “একটি neural network কি এই curve শিখতে পারবে?” প্রশ্নটি শুনতে এক, কিন্তু আসলে তিনটি:

  1. প্রকাশ (expressivity): এমন কোনো weight কি আছে, যা বসালে network এই curve-এর যথেষ্ট কাছে যায়?
  2. খোঁজা (optimization): শুরুর এলোমেলো weight থেকে gradient descent কি সেই weight-এ পৌঁছায়?
  3. নতুন ডেটা (generalization): হাতে থাকা কয়েকটি noisy মাপ থেকে শিখে network কি আসল curve-এর কাছে থাকে?

ধরুন একজন দর্জির কাছে যত খুশি কাপড় আছে (প্রকাশের ক্ষমতা)। তবু জামা ভালো হবে কি না, তা নির্ভর করে তিনি মাপ অনুযায়ী ঠিকমতো কাটতে পারেন কি না (খোঁজা), আর খদ্দেরের মাপ ঠিক নেওয়া হয়েছে কি না, ফিতা দিয়ে দুবার মেপে গড় নেওয়া হয়েছে কি না (নতুন ডেটা)। কাপড় অফুরন্ত, এই তথ্য দিয়ে জামার মান সম্পর্কে প্রায় কিছুই বলা যায় না।

Universal approximation theorem শুধু প্রথম প্রশ্নের উত্তর দেয়, আর তাও “অস্তিত্ব আছে” ধরনের। পরের দুই প্রশ্নের উত্তর আলাদা, আর সেগুলো মেপে দেখতে হয়। এই পর্বের প্রতিটি অংশ এই তিনটির কোনো একটিকে সংখ্যা দিয়ে আলাদা করে দেখায়।

২ · ReLU-এর কব্জা দিয়ে function গড়া — তিন কব্জায় একটি চূড়া

ReLU হলো max(0, z). একটি hidden unit ReLU(w·t + b) এক মাত্রার input t-তে একটি “কব্জা” বা hinge: একটি বিন্দু t = −b/w-এর একদিকে মান ০, অন্যদিকে সোজা রেখা। কব্জার জায়গাকে বলে knot. এমন কয়েকটি কব্জাকে output weight দিয়ে গুণ করে যোগ করলে পাওয়া যায় ভাঙা-রেখার function, যার প্রতিটি knot-এ ঢাল বদলায়:

g(t) = c + a1·ReLU(t − k1) + a2·ReLU(t − k2) + … + an·ReLU(t − kn)

এখানে ki হলো i-তম knot, ai হলো সেখানে ঢাল কতটা বদলায়, আর c হলো output-এর bias. এটি হুবহু একটি hidden layer-এর ReLU network: hidden weight ১, hidden bias −ki, output weight ai. সবচেয়ে কাজের ইট হলো তিন কব্জার “চূড়া” (bump): একটিতে ঢাল ১ শুরু, মাঝেরটিতে −২ বদলে ঢাল −১, শেষটিতে +১ বদলে আবার ০।

তিনটি ReLU কব্জা যোগ করে একটি চূড়া

ReLU(t − 19) − 2·ReLU(t − 21) + ReLU(t − 23), তারপর × 20

তিন ReLU থেকে একটি চূড়া 192123 192123 চূড়া 40 ⇒ +ReLU(t−19) −2·ReLU(t−21) +ReLU(t−23) যোগফল × 20 ৩টি hidden unit
ছবি ৬.১ — প্রতিটি ReLU একটি কব্জা; ঢাল-বদল +১, −২, +১ মিলে একটি ত্রিভুজের চূড়া, সংখ্যাগুলো নিচের stepper-এ।
t = 18, 19, 20, 21, 22, 23, 24 (ঘণ্টা)
ধাপ
নতুন পদ
মোট
ঢাল
  1. প্রথম কব্জা। ReLU(t − 19) সন্ধ্যা ৭টা পর্যন্ত ০, তারপর প্রতি ঘণ্টায় ১ করে বাড়ে: t = ২৪-এ ৫। একা এটি থামতে জানে না, শুধু উঠতেই থাকে।
  2. দ্বিতীয় কব্জা ঢাল উল্টায়। −২·ReLU(t − 21) রাত ৯টার পর প্রতি ঘণ্টায় ২ করে কমায়। যোগফলের ঢাল ১ − ২ = −১, তাই ২১-এ মান ২ চূড়ায় পৌঁছে নামতে থাকে; কিন্তু ২৪-এ −১, অর্থাৎ শূন্যের নিচে চলে যাচ্ছে।
  3. তৃতীয় কব্জা থামায়। +ReLU(t − 23) রাত ১১টার পর ঢালে আবার +১ যোগ করে: −১ + ১ = ০। এখন ২৩-এর পর মান ০-তেই স্থির। মোট: ০, ০, ১, ২, ১, ০, ০, একটি সমদ্বিবাহু ত্রিভুজ।
  4. উচ্চতা ঠিক করা। পুরোটা ২০ দিয়ে গুণ, অর্থাৎ output weight ২০, −৪০, ২০। রাত ৯টায় চূড়া ৪০ অর্ডার। তিনটি hidden unit আর তিনটি output weight-এ একটি স্থানীয় চূড়া; এমন অনেক চূড়া পাশাপাশি বসালে যেকোনো ধারাবাহিক curve-এর কাছে যাওয়া যায়, আর এটিই এক মাত্রায় universal approximation-এর সবচেয়ে সহজ প্রমাণের ভাবনা।

চূড়ার বদলে সরাসরি knot ধরেও গড়া যায়: দিনের ২৪ ঘণ্টাকে n ভাগে ভাগ করে প্রতিটি knot-এ আসল চাহিদার মান ছুঁয়ে যাওয়া ভাঙা রেখা আঁকুন। প্রতিটি টুকরোর ঢাল si = (f(ki+1) − f(ki)) / h, যেখানে h = ২৪/n টুকরোর দৈর্ঘ্য; আর output weight ai = si − si−1, ঢালের বদল। ভুল কতটা? সংখ্যাতত্ত্বের পরিচিত ফল: যদি f-এর দ্বিতীয় derivative-এর মাপ কোথাও M-এর বেশি না হয়, তাহলে knot ছুঁয়ে যাওয়া ভাঙা রেখার সর্বোচ্চ ভুল

সর্বোচ্চ ভুল ≤ h2 · M / 8

অর্থাৎ unit দ্বিগুণ করলে h অর্ধেক, ভুল চার ভাগের এক ভাগ। আমাদের curve-এ সবচেয়ে বাঁকা জায়গা রাত ৯টার চূড়া, সেখানে M = ২০ (৪০ × ২ / ২2). n = ৬৪ হলে h = ০.৩৭৫, সীমা ০.৩৭৫2 × ২০ / ৮ ≈ ০.৩৫১ অর্ডার/ঘণ্টা। পরের অংশের ব্লক আসলে মেপে দেখবে।

৩ · হাতে বানানো network — বাজারঘরের চাহিদার curve

নিচের ব্লক ঠিক ওপরের নিয়মে, কোনো training ছাড়া, n = ৪ থেকে ১২৮ unit-এর network বানায় আর ২,৪০১টি ঘন বিন্দুতে সর্বোচ্চ ভুল মাপে। build(n) যা ফেরত দেয়, তা একটি hidden layer-এর ReLU network-এর forward pass: relu(t[:, None] - k[:-1]) হলো (২৪০১ × n) hidden activation, আর @ a হলো output layer.

import numpy as np
import matplotlib.pyplot as plt

def demand(t):                     # বাজারঘরে ঘণ্টাপ্রতি অর্ডার (বানানো), t = 0..24
    return 5 + 25*np.exp(-((t - 13)/2.5)**2) + 40*np.exp(-((t - 21)/2)**2)

relu = lambda z: np.maximum(0, z)

def build(n):                      # n টি hidden unit: ReLU(t - k_i)
    k = np.linspace(0, 24, n + 1)              # knot, সমান দূরে
    s = np.diff(demand(k)) / np.diff(k)        # প্রতিটি টুকরোর ঢাল
    a = np.diff(s, prepend=0)                  # hinge-এ ঢালের বদল = output weight
    return lambda t: demand(0) + relu(t[:, None] - k[:-1]) @ a

t = np.linspace(0, 24, 2401)
ns = np.array([4, 8, 16, 32, 64, 128])
errs = []
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 3.4))
ax1.plot(t, demand(t), "k", lw=2.5, label="target demand")
for n in ns:
    err = np.abs(build(n)(t) - demand(t)).max()
    errs.append(err)
    print(f"hidden unit {n:3d} | সর্বোচ্চ ভুল {err:6.3f} অর্ডার/ঘণ্টা")
    if n in (4, 8, 16):
        ax1.plot(t, build(n)(t), "--", label=f"{n} ReLU units")
print("পরপর অনুপাত:", np.round(np.array(errs[:-1]) / errs[1:], 2))
ax1.set(xlabel="hour of day", ylabel="orders per hour", title="Building the curve")
ax1.legend(fontsize=8)
ax2.loglog(ns, errs, "o-", label="max error")
ax2.loglog(ns, errs[2] * (16 / ns)**2, ":", label="~ 1/n^2")
ax2.set(xlabel="hidden units n", ylabel="max error", title="Error vs width")
ax2.legend(fontsize=8)
fig.subplots_adjust(wspace=0.3, bottom=0.16)
plt.show()

আসল আউটপুট (ব্রাউজারের Python):

hidden unit   4 | সর্বোচ্চ ভুল 35.556 অর্ডার/ঘণ্টা
hidden unit   8 | সর্বোচ্চ ভুল  6.864 অর্ডার/ঘণ্টা
hidden unit  16 | সর্বোচ্চ ভুল  3.449 অর্ডার/ঘণ্টা
hidden unit  32 | সর্বোচ্চ ভুল  1.243 অর্ডার/ঘণ্টা
hidden unit  64 | সর্বোচ্চ ভুল  0.341 অর্ডার/ঘণ্টা
hidden unit 128 | সর্বোচ্চ ভুল  0.087 অর্ডার/ঘণ্টা
পরপর অনুপাত: [5.18 1.99 2.77 3.65 3.91]

৪ unit-এ ভুল ৩৫.৬, প্রায় পুরো চূড়াটাই হারায়: knot ০, ৬, ১২, ১৮, ২৪ ঘণ্টায়, রাত ৯টার চূড়ার মাঝে কোনো knot নেই। তারপর প্রতি দ্বিগুণে ভুল কমে, আর অনুপাত ধীরে ধীরে ৪-এর দিকে যায় (৩.৬৫, ৩.৯১): h2 নিয়মটি কাজ করে যখন টুকরো এত ছোট যে প্রতিটির ভেতরে curve প্রায় একটি parabola. ৬৪ unit-এ মাপা ভুল ০.৩৪১, অংশ ২-এর হিসাবের সীমা ০.৩৫১-এর ঠিক নিচে। ডানের log-log ছবিতে ঢাল প্রায় −২। এই হিসাব বলে, এক মাত্রায় ε ভুলের জন্য unit লাগে মোটামুটি 1/√ε অনুপাতে; d মাত্রায় একই কৌশলে grid-এর ঘর লাগে প্রায় (1/√ε)d, যা অংশ ৪-এর একটি জরুরি সতর্কতা।

৪ · Universal approximation theorem — সাবধানে পড়া

এক মাত্রায় যা হাতে দেখলাম, বহু মাত্রায় তার সাধারণ রূপটিই উপপাদ্য। একটি সাধারণ বক্তব্য:

উপপাদ্য (মুক্ত ভাষায়)

ধরুন σ একটি ধারাবাহিক activation, যা কোনো polynomial নয় (যেমন sigmoid, tanh বা ReLU). K হলো ℝd-এর একটি compact (সীমাবদ্ধ আর বন্ধ) এলাকা, যেমন [0, 1]d. তাহলে K-এর ওপর যেকোনো ধারাবাহিক function f আর যেকোনো ε > ০-এর জন্য এমন একটি সংখ্যা N আর weight আছে, যাতে এক hidden layer-এর network g(x) = ∑j=1..N aj·σ(wj·x + bj) সব x ∈ K-তে |f(x) − g(x)| < ε মানে।

১৯৮৯ সালে George Cybenko sigmoid-ধরনের activation-এর জন্য এটি প্রমাণ করেন; একই বছর Kurt Hornik, Maxwell Stinchcombe আর Halbert White আরও সাধারণ “squashing” activation-এর জন্য। ১৯৯১ সালে Hornik দেখান, বিশেষ activation নয়, বহুস্তরের গঠনটিই আসল কারণ (সীমাবদ্ধ, ধ্রুবক-নয় এমন activation যথেষ্ট)। ১৯৯৩ সালে Moshe Leshno, Vladimir Lin, Allan Pinkus আর Shimon Schocken প্রায় চূড়ান্ত শর্ত দেন: সাধারণ শর্ত মানা activation-এর জন্য, activation polynomial না হলেই উপপাদ্য খাটে। ReLU তাই এর আওতায় পড়ে। প্রমাণের খুঁটিনাটি আর শর্তের ভাষা প্রবন্ধভেদে আলাদা; এখানে সারকথাটুকুই।

Polynomial কেন বাদ? σ যদি ঘাত m-এর polynomial হয়, তাহলে ∑ ajσ(wj·x + bj) যতগুলো unit-ই থাকুক, ঘাত m-এর বেশি হতে পারে না; সীমিত ঘাতের polynomial দিয়ে, ধরুন, |x| বা sin(১০x)-কে যত কাছে চাই তত কাছে নেওয়া যায় না। আর একেবারে linear activation হলে network পুরোটাই linear, যা AI-এর ভিত্তি কোর্সের পর্ব ৬৮-এ (xA)B = x(AB) দিয়ে দেখানো আছে।

উপপাদ্য যা বলে না

সাধারণ ভুল ধারণাআসলে
“অল্প কয়েকটি unit-ই যথেষ্ট”N-এর কোনো সীমা দেয় না। সাধারণ মসৃণ function-এ d মাত্রায় প্রয়োজনীয় unit ε-এর সঙ্গে মোটামুটি (1/ε)d/s হারে বাড়তে পারে (s মসৃণতার মাপ), যাকে curse of dimensionality বলে।
“Gradient descent সেই weight খুঁজে পাবে”শুধু অস্তিত্ব; খোঁজার কোনো নিশ্চয়তা নেই। Loss convex নয় (মেশিন লার্নিং কোর্সের পর্ব ১২)। অংশ ৫-এ এর বাস্তব রূপ দেখবেন।
“Train ডেটায় মিললে নতুন ডেটায়ও মিলবে”উপপাদ্য পুরো function জানা ধরে নেয়; সীমিত noisy ডেটার কথা বলে না। Generalization আলাদা প্রশ্ন।
“K-এর বাইরেও কাছাকাছি থাকবে”শুধু compact এলাকা K-এর ভেতরে। Train-এ যে মানের পরিসর দেখা যায়নি, তার বাইরে ReLU network সোজা রেখায় চলে যায়, যা প্রায়ই অর্থহীন।
“তাই গভীর network দরকার নেই”এক layer যথেষ্ট মানে দক্ষ নয়; অংশ ৭-এ দেখবেন কিছু function-এ গভীরতা unit-এর সংখ্যা exponentially কমায়।

আরও একটি সূক্ষ্ম কথা: সীমিত-precision-এর কম্পিউটারে আর সীমিত ডেটায় “যেকোনো function শিখতে পারে” বাক্যটি প্রায় অর্থহীন। একটি lookup table-ও “যেকোনো” সসীম ডেটা মুখস্থ করতে পারে। আসল প্রশ্ন সবসময় দক্ষতা: কত unit, কত ডেটা, কত হিসাব।

৫ · হাতে গড়া বনাম শেখানো — একই প্রস্থে gradient descent কতটা পারে

অংশ ৩-এ weight আমরা নিজে বসিয়েছি, কারণ আসল function জানা ছিল। বাস্তবে জানা থাকে না; weight শিখতে হয়। নিচের ব্লক একই চাহিদার curve-এ (১০ দিয়ে ভাগ করা, যাতে মান ছোট থাকে) তিন প্রস্থের network (৮, ১৬, ৬৪ unit) শেখায়: ২০০টি ঘন, noise-হীন বিন্দু, full-batch Adam (মেশিন লার্নিং কোর্সের পর্ব ১৪-এ derive করা; এখানে NumPy-তে কয়েক লাইনে), ১,৫০০ ধাপ, তিনটি seed. ভুল মাপা হয় ৯৬১টি ঘন বিন্দুতে আসল curve থেকে RMSE, অর্ডার/ঘণ্টায়। শেষে একটি কঠিন অবস্থা: ৬৪ unit, কিন্তু মাত্র ৩০টি noisy মাপ (noise-এর standard deviation ৪ অর্ডার)।

import numpy as np

def f(t):                                  # বাজারঘরের চাহিদা ÷ 10 (১ একক = ১০ অর্ডার)
    return 0.5 + 2.5*np.exp(-((t - 13)/2.5)**2) + 4*np.exp(-((t - 21)/2)**2)
relu = lambda z: np.maximum(0, z)
tg = np.linspace(0, 24, 961)                       # যাচাইয়ের ঘন grid
rmse = lambda g: 10 * np.sqrt(np.mean((g(tg) - f(tg))**2))   # অর্ডার/ঘণ্টায়

def build(n):                                      # হাতে বানানো: সমান দূরের knot
    k = np.linspace(0, 24, n + 1); a = np.diff(np.diff(f(k)) / np.diff(k), prepend=0)
    return lambda t: f(0) + relu(t[:, None] - k[:-1]) @ a

def train(t, y, h, seed, steps=1500, lr=0.02):    # 1 -> h ReLU -> 1, full-batch Adam
    rng = np.random.default_rng(seed); x = (t / 12 - 1)[:, None]
    P = [rng.uniform(-1, 1, (1, h)), rng.uniform(-1, 1, h), rng.normal(0, h**-.5, h), np.zeros(1)]
    M, V = [0 * p for p in P], [0 * p for p in P]
    for i in range(1, steps + 1):
        Z = x @ P[0] + P[1]; H = relu(Z); e = (H @ P[2] + P[3] - y) / len(y)
        dZ = np.outer(e, P[2]) * (Z > 0)
        G = [x.T @ dZ, dZ.sum(0), H.T @ e, np.array([e.sum()])]   # MSE/2-এর gradient
        for j in range(4):
            M[j] = 0.9 * M[j] + 0.1 * G[j]; V[j] = 0.999 * V[j] + 0.001 * G[j]**2
            P[j] -= lr * M[j] / (1 - 0.9**i) / (np.sqrt(V[j] / (1 - 0.999**i)) + 1e-8)
    return lambda t: relu((t / 12 - 1)[:, None] @ P[0] + P[1]) @ P[2] + P[3]

td = np.linspace(0, 24, 200)                       # ঘন, noise-হীন train ডেটা
for h in [8, 16, 64]:
    tr = [rmse(train(td, f(td), h, s)) for s in range(3)]
    print(f"width {h:2d} | হাতে বানানো {rmse(build(h)):5.2f} | train, seed 0-2:", np.round(tr, 2))
rng = np.random.default_rng(7)                     # মাত্র ৩০ দিনের noisy মাপ
ts = rng.uniform(0, 24, 30); ys = f(ts) + rng.normal(0, 0.4, 30)
for s in range(3):
    g = train(ts, ys, 64, s)
    fit = 10 * np.sqrt(np.mean((g(ts) - ys)**2))
    print(f"width 64, ৩০টি noisy বিন্দু, seed {s} | train-এ {fit:.2f} | আসল curve থেকে {rmse(g):.2f}")

আসল আউটপুট (ব্রাউজারের Python):

width  8 | হাতে বানানো  2.51 | train, seed 0-2: [8.54 8.19 8.17]
width 16 | হাতে বানানো  1.19 | train, seed 0-2: [1.   1.66 7.49]
width 64 | হাতে বানানো  0.08 | train, seed 0-2: [0.6  1.05 0.6 ]
width 64, ৩০টি noisy বিন্দু, seed 0 | train-এ 3.20 | আসল curve থেকে 3.97
width 64, ৩০টি noisy বিন্দু, seed 1 | train-এ 2.88 | আসল curve থেকে 3.33
width 64, ৩০টি noisy বিন্দু, seed 2 | train-এ 3.20 | আসল curve থেকে 4.01

তিনটি বিষয় চোখে পড়ে। প্রথমত, ৮ unit-এ হাতে গড়া network-এর ভুল ২.৫১, অথচ শেখানো network তিনটি seed-এই ৮-এর ওপরে আটকে থাকে। অর্থাৎ এমন weight আছে, কিন্তু Adam সেখানে পৌঁছায়নি: কিছু ReLU unit পুরো ডেটায় নিষ্ক্রিয় (“dead”), বাকিরা একটি চূড়া মোটামুটি ধরে অন্যটি ছেড়ে দেয়। দ্বিতীয়ত, ১৬ unit-এ seed 0 হাতে গড়া network-কেও হারায় (১.০০ বনাম ১.১৯), কারণ শেখার সময় knot সমান দূরে থাকতে বাধ্য নয়, বাঁকা জায়গায় ঘন হতে পারে; কিন্তু seed 2 আটকে যায় (৭.৪৯)। ফল শুরুর weight-এর ওপর নির্ভর করে। তৃতীয়ত, ৬৪ unit-এ তিনটি seed-ই ১-এর কাছে বা নিচে নামে; চওড়া network-এ খোঁজা সহজ হয়েছে, যদিও হাতে গড়া ০.০৮-এর থেকে এখনো দূরে। শেষ তিন লাইন অন্য গল্প: noisy ৩০টি বিন্দুতে train-এ ভুল ২.৮৮ থেকে ৩.২০, noise-এর মাপ (৪)-এর চেয়ে কম, অর্থাৎ network noise-এর কিছুটা মুখস্থ করেছে; আর আসল curve থেকে দূরত্ব ৩.৩৩ থেকে ৪.০১। এটি একটি ডেটাসেটে তিন seed-এর ফল, সাধারণ ফল নয়।

৬ · তিন রকম ভুল — approximation, estimation আর optimization

মেশিন লার্নিং কোর্সের পর্ব ০৩-এ মোট বাড়তি ভুলকে দুই ভাগে ভাঙা হয়েছিল: hypothesis class-এর সেরাটিও কতটা খারাপ (approximation), আর সীমিত ডেটায় সেরাটি না চেনার দাম (estimation). সেখানে ধরে নেওয়া হয়েছিল আমরা train ডেটায় সত্যিকারের সেরা নিয়মটি (ERM) পাই। Neural network-এ তা হয় না, তাই Léon Bottou আর Olivier Bousquet (২০০৭–০৮ সালের “The Tradeoffs of Large Scale Learning”) তৃতীয় একটি পদ যোগ করেন:

মোট বাড়তি ভুল ≈ approximation + estimation + optimization

Optimization error হলো train loss-এর সত্যিকারের সর্বনিম্ন আর optimizer যেখানে থামল, তার পার্থক্যের দাম। আগের ব্লকের সংখ্যা দিয়ে ভাগগুলো আলাদা করি। RMSE সরাসরি যোগ হয় না, তাই এটি মোটামুটি ভাগ, সূত্রের হুবহু হিসাব নয়।

RMSE, আসল curve থেকে, অর্ডার/ঘণ্টা
প্রশ্ন
যা মাপা হলো
সংখ্যা
সিদ্ধান্ত
  1. Approximation. ৬৪ unit-এর সব ReLU network মিলে একটি class. হাতে গড়া একটি সদস্যের ভুল ০.০৮; class-এর সেরাটি এর চেয়ে খারাপ হতে পারে না। তাই approximation error ≤ ০.০৮, প্রায় শূন্য।
  2. Optimization. ডেটা ঘন আর noise-হীন, তাই estimation-এর দাম প্রায় শূন্য। তবু শেখানো network-এর ভুল ০.৬০ থেকে ১.০৫। বাড়তি ০.৫ থেকে ১ অংশের প্রায় পুরোটাই optimizer-এর থেমে যাওয়ার দাম; আরও ধাপ বা ভালো learning rate দিলে কমে।
  3. Estimation. একই class, একই optimizer, কিন্তু ৩০টি noisy মাপ। ভুল ৩.৩৩ থেকে ৪.০১-এ লাফ দিল। Approximation বদলায়নি, optimization একই রকম; বাড়তির বড় অংশ এসেছে কম আর noisy ডেটা থেকে। প্রতিকার: আরও ডেটা, regularization বা সরল class.
  4. সরু network. ৮ unit-এ approximation-এর সীমা ২.৫১, কিন্তু শেখানো network ৮.১৭ থেকে ৮.৫৪। এখানে optimization-ই প্রধান। প্রস্থ ৬৪ করলে approximation আর optimization দুটোই কমল। “বড় network-এ খোঁজা সহজ” এই পর্যবেক্ষণ deep learning-এ বহুবার দেখা গেছে, তবে এটি সব ক্ষেত্রে প্রমাণিত নিয়ম নয়।
ভুলকমে যখনবাড়ে যখন
ApproximationClass বড় বা আরও উপযুক্ত (প্রস্থ, গভীরতা, ঠিক architecture)Class ছোট বা ভুল ধাঁচের
Estimationডেটা বেশি, noise কম, regularizationClass খুব নমনীয় আর ডেটা অল্প
Optimizationবেশি ধাপ, ভালো initialization আর learning rate, চওড়া networkসময় বা হিসাবের বাজেট কম, খারাপ শুরু

Bottou ও Bousquet-এর মূল কথাটি বাস্তব: হিসাবের বাজেট সীমিত হলে optimization নিখুঁত করার চেয়ে বেশি ডেটা দেখা প্রায়ই ভালো। তাই বড় model-এ SGD অল্প কয়েক epoch চালানো হয়, আর ছোট tabular সমস্যায় নিখুঁত optimizer চালানো যায়।

৭ · গভীরতা বনাম প্রস্থ — sawtooth আর গুণে গুণে বাড়া টুকরো

এক input-এর একটি hidden layer-এর ReLU network-এ m টি unit থাকলে সর্বোচ্চ m টি knot, তাই সর্বোচ্চ m + ১টি সরল টুকরো (linear region). গভীর network-এ ব্যাপারটি আলাদা: একটি layer আগের layer-এর output-কে “ভাঁজ” করে, আর পরের layer প্রতিটি ভাঁজে একই কাজ আবার করে। সবচেয়ে পরিষ্কার উদাহরণ tent map:

T(x) = 2·ReLU(x) − 4·ReLU(x − 0.5), x ∈ [0, 1]

[0, 0.5]-এ T(x) = 2x, আর [0.5, 1]-এ 2 − 2x: একটি তাঁবুর মতো আকার, দুটি unit-এ। এখন T-কে নিজের ওপর বারবার চালান: T(T(x)), T(T(T(x)))… প্রতিটি নতুন layer-এ দুটি unit যোগ হয়। ফলাফল একটি করাতের দাঁতের মতো sawtooth curve.

x = 0, 1/8, 2/8, …, 1 (নয়টি বিন্দু)
ধাপ
মান
unit (deep)
সরল টুকরো
এক layer-এ লাগত
  1. দুটি কব্জা। প্রথম unit x-কে যেমন আছে রাখে, দ্বিতীয়টি ০.৫-এর পর থেকে বাড়ে। এদের 2·u1 − 4·u2 যোগফলই tent.
  2. এক layer, দুই টুকরো। T ০ থেকে ১-এ ওঠে (x = ০.৫-এ), তারপর ১ থেকে ০-তে নামে। মূল কথা: x যখন [0, 0.5] পার হয়, T(x) পুরো [0, 1] একবার পার হয়; [0.5, 1]-এও আবার পুরো [0, 1], উল্টো দিকে।
  3. দ্বিতীয় layer প্রতিটি অর্ধেকে নিজের tent আঁকে। পরের T পুরো [0, 1]-এর ওপর একটি তাঁবু বানায়, আর তার input দুবার পুরো [0, 1] পার হয়েছে। তাই দুটি তাঁবু, চারটি টুকরো: মান ০, .৫, ১, .৫, ০, .৫, ১, .৫, ০। মাত্র দুটি unit যোগ করে টুকরো দ্বিগুণ।
  4. প্রতি layer-এ দ্বিগুণ। তৃতীয় layer-এ ৮টি টুকরো, ৬টি unit-এ। এক hidden layer-এ ৮টি টুকরো আঁকতে অন্তত ৭টি knot, তাই ৭টি unit. সাধারণভাবে k layer-এ ২k unit দিয়ে ২k টুকরো; এক layer-এ লাগে ২k − ১। গভীরতা টুকরো গুণ করে, প্রস্থ শুধু যোগ করে।

নিচের ব্লক এটি k = ৬ পর্যন্ত গুনে দেখে (২14 + ১ বিন্দুর dyadic grid-এ, যাতে সব knot grid-এর বিন্দুতেই পড়ে), আর তারপর একটি সৎ প্রশ্ন করে: একই ১২টি unit এলোমেলো weight পেলে গভীর আর অগভীর network সাধারণত কয়টি টুকরো বানায়?

import numpy as np
import matplotlib.pyplot as plt

relu = lambda z: np.maximum(0, z)
tent = lambda x: 2 * relu(x) - 4 * relu(x - 0.5)   # একটি layer, ২টি ReLU unit
x = np.linspace(0, 1, 2**14 + 1)                    # dyadic grid: সব kink grid-এ পড়ে

def pieces(y):                       # ঢাল-বদলের জায়গা গুনে + 1 (পাশাপাশি বদল = একটি kink)
    s = np.diff(y) / np.diff(x)
    c = ~np.isclose(s[1:], s[:-1], atol=1e-6)
    return 1 + c[0] + np.sum(c[1:] & ~c[:-1])

fig, ax = plt.subplots(figsize=(8, 3))
y = x.copy()
print("layer k | unit 2k | টুকরো (deep) | এক layer-এ লাগত")
for k in range(1, 7):
    y = tent(y)                                     # আগের output-এর ওপর আবার tent
    print(f"{k:7d} | {2*k:6d} | {pieces(y):12d} | {pieces(y) - 1:6d} unit")
    if k <= 4: ax.plot(x, y + 1.2 * (k - 1), label=f"k = {k}")
ax.set(xlabel="x", yticks=[], title="Composing the tent map: k layers, 2^k pieces")
ax.legend(fontsize=8, loc="upper right", ncol=4)

rng = np.random.default_rng(0)                      # একই ১২টি unit, এলোমেলো weight
deep, wide = [], []
for _ in range(200):
    h = x[:, None] - 0.5
    for _ in range(3):                              # 3 layer × 4 unit
        h = relu(h @ rng.normal(0, 1, (h.shape[1], 4)) + rng.normal(0, 0.5, 4))
    deep.append(pieces(h @ rng.normal(0, 1, 4)))
    wide.append(pieces(relu((x[:, None] - 0.5) * rng.normal(0, 1, 12)
                            + rng.normal(0, 0.5, 12)) @ rng.normal(0, 1, 12)))
print("এলোমেলো weight, ২০০ বার: deep 3×4 গড়", np.mean(deep), "সর্বোচ্চ", max(deep),
      "| shallow 12 গড়", np.mean(wide), "সর্বোচ্চ", max(wide))
plt.show()

আসল আউটপুট (ব্রাউজারের Python):

layer k | unit 2k | টুকরো (deep) | এক layer-এ লাগত
      1 |      2 |            2 |      1 unit
      2 |      4 |            4 |      3 unit
      3 |      6 |            8 |      7 unit
      4 |      8 |           16 |     15 unit
      5 |     10 |           32 |     31 unit
      6 |     12 |           64 |     63 unit
এলোমেলো weight, ২০০ বার: deep 3×4 গড় 5.31 সর্বোচ্চ 13 | shallow 12 গড় 7.005 সর্বোচ্চ 11

প্রথম অংশ stepper-এর সঙ্গে হুবহু মেলে: ১২টি unit-এর ৬-layer network ৬৪টি টুকরো আঁকে, যেখানে এক hidden layer-এ লাগত ৬৩টি unit. layer আরেকটি বাড়ালে ব্যবধান দ্বিগুণ হয়। কিন্তু শেষ লাইনটি সমান জরুরি: এলোমেলো weight-এ ৩ × ৪-এর গভীর network ২০০ বারে গড়ে মাত্র ৫.৩১টি টুকরো (সর্বোচ্চ ১৩), আর ১২ unit-এর অগভীর network গড়ে ৭.০০৫ (সর্বোচ্চ ১১, তাত্ত্বিক সীমা ১৩)। গভীরতা exponentially বেশি টুকরোর সম্ভাবনা দেয়; সেটি পেতে weight-কে বিশেষভাবে সাজাতে হয়। ২০১৯ সালে Boris Hanin আর David Rolnick-এর কাজে দেখানো হয়েছে, সাধারণ initialization-এ (আর তাঁদের পরীক্ষায় training-এর পরেও) এক মাত্রার রেখা বরাবর region-এর সংখ্যা মোটামুটি মোট neuron-এর সংখ্যার অনুপাতে বাড়ে, গভীরতার সঙ্গে exponentially নয়।

একই ১২টি unit, দুই আকার

Deep and narrow vs shallow and wide

Deep 6×2 বনাম shallow 12 xy xy deep: ৬ layer × ২ সর্বোচ্চ ৬৪ টুকরো shallow: ১২সর্বোচ্চ ১৩
ছবি ৬.২ — Unit-এর সংখ্যা এক, কিন্তু ভাঁজের সুযোগ আলাদা। Deep আকারের ৬৪ টুকরো আসে বিশেষ weight-এ, এলোমেলো weight-এ নয়।

এই ভাবনার আনুষ্ঠানিক রূপ depth separation. ২০১৬ সালে Matus Telgarsky (COLT) দেখান, প্রতিটি k-এর জন্য এমন function আছে যা প্রায় k3 layer আর প্রতি layer-এ ধ্রুবক সংখ্যক unit-এর ReLU network হুবহু হিসাব করে, অথচ মাত্র প্রায় k layer-এর network দিয়ে কাছাকাছি আনতে গেলে অন্তত প্রায় ২k unit লাগে; প্রমাণের কেন্দ্রে ঠিক এই sawtooth. একই বছর Ronen Eldan আর Ohad Shamir উঁচু মাত্রায় ৩-layer বনাম ২-layer-এর একটি পার্থক্য দেখান। এর আগে ২০১৪ সালে Guido Montúfar ও সহকর্মীরা গভীর ReLU network-এর linear region-এর সংখ্যা কতটা বাড়তে পারে তা হিসাব করেন। সতর্কতা: এগুলো “এমন function আছে” ধরনের ফল; বাস্তব কাজের function এমন কি না, তা আলাদা প্রশ্ন।

৮ · Overparameterization আর double descent — একটি ছোট পরীক্ষা

Classical গল্প বলে, parameter বাড়ালে test ভুল প্রথমে কমে, তারপর overfitting-এ বাড়ে (একটি U). আধুনিক network-এ parameter প্রায়ই উদাহরণের চেয়ে বেশি, train ভুল প্রায় শূন্য, তবু test ভালো। ২০১৯ সালে Mikhail Belkin, Daniel Hsu, Siyuan Ma আর Soumik Mandal (PNAS) এই দুই ছবি জোড়েন: জটিলতা বাড়ালে test ভুল U-এর মতো চলে, ডেটা ঠিক মুখস্থ করার সীমার (interpolation threshold) কাছে চূড়ায় ওঠে, তারপর আরও বড় model-এ আবার নামতে পারে। একে বলে double descent; মেশিন লার্নিং কোর্সের পর্ব ০৩-এ কথায় এসেছে। এখানে নিজে মাপি।

পরীক্ষাটি সবচেয়ে সরল রূপে: random features. p টি ReLU unit-এর hidden weight এলোমেলো বসিয়ে স্থির রাখা হয়, শুধু output weight শেখা হয়, least squares দিয়ে। সময়কে ঘড়ির কাঁটার মতো (cos, sin) জোড়ায় লেখা হয়, যাতে দিনের শেষ (t = ২৪) আর শুরু (t = ০) একই বিন্দুতে মেলে, রাত ১১টা আর ভোর ১টা পাশাপাশি থাকে। দিনের ৪০টি সময়ে একবার করে noisy মাপ (n = ৪০)। p > n হলে train ডেটা হুবহু মেলানোর অসংখ্য সমাধান থাকে; np.linalg.lstsq তাদের মধ্যে সবচেয়ে ছোট দৈর্ঘ্যের ‖a‖ সমাধানটি দেয় (minimum-norm solution, মেশিন লার্নিং কোর্সের পর্ব ০৯-এর pseudo-inverse). প্রতিটি p-তে ২০ সেট এলোমেলো feature-এর median নেওয়া হয়।

import numpy as np
import matplotlib.pyplot as plt

def f(t):                                          # চাহিদা ÷ 10, t = 0..24 ঘণ্টা
    return 0.5 + 2.5*np.exp(-((t - 13)/2.5)**2) + 4*np.exp(-((t - 21)/2)**2)
n = 40                                             # দিনের ৪০টি সময়ে একবার করে মাপ
tr = (np.arange(n) + 0.5) * 24 / n
ytr = f(tr) + np.random.default_rng(3).normal(0, 0.4, n)    # noise-সহ
te = np.linspace(0, 24, 500, endpoint=False)

def features(t, W, b):                   # ঘড়ির কাঁটার মতো (cos, sin) input-এ p টি ReLU
    X = np.column_stack([np.cos(2*np.pi*t/24), np.sin(2*np.pi*t/24)])
    return np.maximum(0, X @ W + b)               # W, b এলোমেলো, শেখা হয় না

widths = [5, 10, 15, 20, 25, 30, 35, 38, 40, 42, 45, 50, 60, 80, 120, 200, 400, 1000, 3000]
test = []
for p in widths:
    e_te, e_tr, rank = [], [], []
    for s in range(20):                            # ২০ সেট এলোমেলো feature-এর median
        r = np.random.default_rng(100 + s)
        W, b = r.normal(0, 1, (2, p)), r.normal(0, 0.5, p)
        F = features(tr, W, b)
        a = np.linalg.lstsq(F, ytr, rcond=None)[0]     # least squares, সবচেয়ে ছোট ‖a‖
        e_tr.append(np.mean((F @ a - ytr)**2)); rank.append(np.linalg.matrix_rank(F))
        e_te.append(np.mean((features(te, W, b) @ a - f(te))**2))
    test.append(np.median(e_te))
    print(f"p {p:4d} | rank {np.median(rank):4.0f} | train MSE {np.median(e_tr):.3f} | test MSE {test[-1]:.3f}")

fig, ax = plt.subplots(figsize=(7, 3.2))
ax.loglog(widths, test, "o-", label="test MSE (median of 20)")
ax.axvline(n, color="r", ls=":", label="p = n = 40")
ax.set(xlabel="random ReLU features p", ylabel="test MSE", title="Double descent, min-norm fit")
ax.legend(fontsize=8)
plt.show()

আসল আউটপুট (ব্রাউজারের Python):

p    5 | rank    5 | train MSE 1.024 | test MSE 0.756
p   10 | rank   10 | train MSE 0.340 | test MSE 0.169
p   15 | rank   14 | train MSE 0.197 | test MSE 0.098
p   20 | rank   19 | train MSE 0.143 | test MSE 0.087
p   25 | rank   24 | train MSE 0.120 | test MSE 0.116
p   30 | rank   29 | train MSE 0.079 | test MSE 0.167
p   35 | rank   32 | train MSE 0.064 | test MSE 0.221
p   38 | rank   36 | train MSE 0.036 | test MSE 0.747
p   40 | rank   38 | train MSE 0.012 | test MSE 1.211
p   42 | rank   39 | train MSE 0.003 | test MSE 1.630
p   45 | rank   40 | train MSE 0.000 | test MSE 2.052
p   50 | rank   40 | train MSE 0.000 | test MSE 0.424
p   60 | rank   40 | train MSE 0.000 | test MSE 0.352
p   80 | rank   40 | train MSE 0.000 | test MSE 0.241
p  120 | rank   40 | train MSE 0.000 | test MSE 0.205
p  200 | rank   40 | train MSE 0.000 | test MSE 0.190
p  400 | rank   40 | train MSE 0.000 | test MSE 0.184
p 1000 | rank   40 | train MSE 0.000 | test MSE 0.183
p 3000 | rank   40 | train MSE 0.000 | test MSE 0.179

তিনটি ধাপ দেখা যায়। p = ৫ থেকে ২০: test ভুল ০.৭৫৬ থেকে ০.০৮৭-এ নামে (প্রথম descent). p = ২৫ থেকে ৪৫: train ভুল শূন্যের দিকে যায়, আর test ভুল লাফিয়ে চূড়ায় ওঠে, p = ৪৫-এ ২.০৫২। চূড়া ঠিক p = ৪০-এ নয়, কারণ ReLU feature-গুলো ৪০টি বিন্দুতে সবসময় স্বাধীন নয়: rank কলাম দেখায়, rank ৪০ হয় p = ৪৫-এ, আর আসল threshold সেটিই। তারপর p বাড়লে test ভুল আবার নামে, ৩,০০০-এ ০.১৭৯ (দ্বিতীয় descent), যদিও train ভুল শূন্য, অর্থাৎ noise-সহ সব মাপ হুবহু মুখস্থ। চূড়ার কারণ: threshold-এ হুবহু মেলানোর সমাধান একটিই, আর তা noise মেলাতে বিশাল weight নেয়; p অনেক বড় হলে minimum-norm সমাধান অনেক মসৃণ সমাধানের মধ্যে ছোটটি বাছে। সৎ পর্যবেক্ষণ: এই ছোট পরীক্ষায় দ্বিতীয় descent প্রথম descent-এর সেরা (০.০৮৭)-কে ছাড়ায়নি। এটি একটি ডেটাসেট, একটি noise seed; সাধারণ নিয়ম নয়।

Preetum Nakkiran ও সহকর্মীরা (২০১৯–২০ সালের “Deep Double Descent”) বড় network-এ একই ধরনের চূড়া দেখান: model-এর আকার, এমনকি epoch-এর সংখ্যা বাড়ালেও। তবে চূড়াটি অনেক ক্ষেত্রে label noise-এর সময় স্পষ্ট হয়, আর ঠিকমতো বাছা regularization (যেমন ridge-এর λ) চূড়াটিকে অনেকটা মুছে দিতে পারে বলে রিপোর্ট আছে। Gradient descent-এর নিজের পছন্দ (implicit regularization), যেমন least squares-এ শূন্য থেকে শুরু করলে minimum-norm সমাধানে পৌঁছানো, এখানে বড় ভূমিকা রাখে। Weight decay আর early stopping বিস্তারিত আসবে পর্ব ১৬ (Deep learning-এ regularization)-এ।

৯ · Inductive bias — architecture কেন এত গুরুত্বপূর্ণ

যদি এক hidden layer-ই “সব” পারে, তাহলে CNN, Transformer-এর মতো এত architecture কেন? কারণ সীমিত ডেটা আর হিসাবে প্রশ্নটি “পারে কি না” নয়, “কতটা সহজে আর কত কম ডেটায়”। প্রতিটি architecture কিছু অনুমান আগে থেকে ধরে নেয়; একেই বলে inductive bias. অনুমান ডেটার সঙ্গে মিললে estimation error কমে, কারণ শেখার মতো জিনিস কম; না মিললে approximation error বাড়ে।

MLP

প্রায় কোনো অনুমান নেই: input-এর প্রতিটি অংশ আলাদা weight পায়। ছবির একটি pixel সরে গেলে MLP-এর কাছে পুরো নতুন input.

নমনীয়, কিন্তু ডেটা-ক্ষুধার্ত

CNN

অনুমান: কাছাকাছি pixel সম্পর্কিত, আর একই নকশা ছবির যেকোনো জায়গায় আসতে পারে; তাই ছোট filter আর সবখানে একই weight. পর্ব ২০ (Convolution) থেকে শুরু।

স্থানীয়তা আর weight ভাগাভাগি

Transformer

অনুমান কম: প্রতিটি token অন্য সব token-এর দিকে তাকাতে পারে, কতটা তা attention দিয়ে ঠিক হয়। বড় ডেটায় খুব সফল, ছোট ডেটায় প্রায়ই বেশি ডেটা বা pretraining লাগে। পর্ব ৩২ আর ৩৩-এ।

নমনীয় সম্পর্ক, বেশি ডেটা

গভীরতা নিজেই

অনুমান: function ছোট ছোট function-এর গঠন (রেখা থেকে কোণ, কোণ থেকে আকার)। অংশ ৭-এর sawtooth এর চরম উদাহরণ। গভীরতা শেখানোর কৌশল পর্ব ১৭-এ (residual connection).

গঠনের পুনর্ব্যবহার

বাজারঘরের উদাহরণেই দেখুন: অংশ ৮-এ সময়কে (cos, sin) জোড়ায় লেখা একটি ছোট inductive bias, “দিন চক্রাকার”। এতে রাত ১১:৫৯ আর ১২:০০-এর মাঝে network-কে কোনো লাফ শিখতে হয় না। ভালো feature বা architecture মানে ডেটা থেকে কম জিনিস শেখা। আর ছোট tabular ডেটায় প্রায়ই gradient boosting-এর inductive bias বেশি মানানসই; মেশিন লার্নিং কোর্সের পর্ব ৩৮ দেখুন।

আরও গভীরে: Andrew Barron (১৯৯৩) দেখান, যেসব function-এর Fourier রূপান্তরের প্রথম moment Cf সসীম, সেখানে n টি sigmoid unit-এ গড় বর্গ ভুল প্রায় Cf2/n, মাত্রা d-এর ওপর সরাসরি নির্ভর না করে; অর্থাৎ কিছু function-শ্রেণিতে curse of dimensionality এড়ানো যায় (তবে Cf নিজে d-এর সঙ্গে বড় হতে পারে)। উল্টো দিকে “সরু কিন্তু গভীর” network-ও universal: Zhou Lu ও সহকর্মীরা (২০১৭) আর Boris Hanin ও Mark Sellke (২০১৭) দেখান, প্রতি layer-এ মাত্র প্রায় d + ১ থেকে d + ৪টি unit থাকলেও যথেষ্ট গভীরতায় ReLU network ধারাবাহিক function কাছে আনতে পারে, শর্তগুলো প্রবন্ধে নির্দিষ্ট। Dmitry Yarotsky (২০১৭) গভীর ReLU network-এর ভুলের হারের সূক্ষ্ম হিসাব দেন। সারসংক্ষেপের জন্য Philipp Petersen আর Felix Voigtlaender-এর কাজ, বা Telgarsky-র lecture notes ভালো শুরু।

পরীক্ষা ও ইন্টারভিউতে:

  • Universal approximation theorem কী বলে আর কী বলে না? Compact এলাকায় ধারাবাহিক function-কে এক hidden layer যথেষ্ট unit-এ যত কাছে চাই আনতে পারে (activation polynomial না হলে); unit-এর সংখ্যা, খোঁজা বা generalization সম্পর্কে কিছু বলে না।
  • তিনটি ReLU দিয়ে চূড়া লিখুন। ReLU(x − a) − 2·ReLU(x − m) + ReLU(x − b), যেখানে m = (a + b)/2; উচ্চতা m − a, তাই উচ্চতা H চাইলে H/(m − a) দিয়ে গুণ।
  • ১ input, m unit-এর এক hidden layer-এর ReLU network-এ সর্বোচ্চ কয়টি linear টুকরো? m + ১।
  • Tent map k বার জুড়লে? ২k unit-এ ২k টুকরো; এক layer-এ লাগে ২k − ১ unit.
  • Double descent-এর চূড়া কোথায়? Interpolation threshold-এ, যেখানে model সবে train ডেটা হুবহু মেলাতে পারে (random features-এ feature-এর কার্যকর সংখ্যা ≈ n).

১০ · AI-এর যুগে — তত্ত্বের দাবি যাচাই করা

AI assistant-কে “neural network কি সব শিখতে পারে?” জিজ্ঞেস করলে প্রায়ই আত্মবিশ্বাসী, অর্ধেক-সত্য উত্তর আসে। তত্ত্বের ক্ষেত্রে ভুল ধরা কঠিন, কারণ কোড ভাঙে না; ভুলটি থাকে দাবিতে।

AI যা প্রায়ই করেকীভাবে ধরবেন
“UAT অনুযায়ী network যেকোনো সমস্যা শিখবে”তিন প্রশ্ন আলাদা করুন: প্রকাশ, খোঁজা, generalization; উপপাদ্য শুধু প্রথমটির
ভুল লেখক, বছর বা শর্তে উপপাদ্য উদ্ধৃত করা, অস্তিত্বহীন প্রবন্ধমূল প্রবন্ধের শিরোনাম আর journal খুঁজে নিজে পড়ুন; শর্ত (compact, ধারাবাহিক, non-polynomial) মিলিয়ে দেখুন
“গভীর network সবসময় exponentially বেশি শক্তিশালী”Depth separation “এমন function আছে” ধরনের ফল; এলোমেলো weight-এ টুকরো গুনে দেখুন
একটি seed-এর একটি run থেকে সিদ্ধান্তকয়েকটি seed চালান; অংশ ৫-এ ১৬ unit-এর ফল ১.০০ থেকে ৭.৪৯ পর্যন্ত ছড়ানো
Double descent-কে “বড় model সবসময় ভালো” প্রমাণ হিসেবে দেখানোনিজের ডেটায় validation curve মাপুন; চূড়া নির্ভর করে noise আর regularization-এর ওপর
Piecewise-linear টুকরো গোনার কোডে grid-এর ফাঁদKnot grid-এর মাঝে পড়লে এক kink দুইবার গোনা হয়; ছোট হাতে-হিসাব করা উদাহরণে মিলিয়ে নিন

দাবির বদলে যাচাইযোগ্য কাঠামো চান। আসল গ্রাহকের ডেটা পেস্ট করবেন না; বানানো বা public ডেটাই যথেষ্ট।

Universal approximation theorem নিয়ে একটি পরীক্ষাযোগ্য ব্যাখ্যা চাই। শর্ত: ১. Cybenko (1989), Hornik (1991) আর Leshno ও সহকর্মীদের (1993) ফল আলাদা করে লিখবে: প্রতিটির activation-এর শর্ত, কোন এলাকায়, কোন অর্থে কাছাকাছি। কোনো তথ্যে নিশ্চিত না হলে "নিশ্চিত নই" লিখবে। ২. উপপাদ্য কী বলে না, তার একটি তালিকা দেবে: unit-এর সংখ্যা, gradient descent, generalization, extrapolation. ৩. NumPy-তে ছোট কোড: তিনটি ReLU দিয়ে [0, 2]-এ উচ্চতা 1-এর একটি চূড়া, আর পাঁচটি বিন্দুতে মান assert দিয়ে যাচাই। ৪. Tent map k বার জুড়ে linear টুকরো গোনার কোড, যা knot grid-এর ওপর পড়ে এমন dyadic grid ব্যবহার করে; k = 1..5-এর উত্তর 2^k-এর সঙ্গে মিলিয়ে দেখাবে। ৫. কোনো দাবি "সবসময়" বা "প্রমাণিত" বলবে না, যদি না প্রবন্ধে তাই থাকে।

AI-এর দেওয়া তত্ত্ব বা কোড হাতে পেলে — যাচাইয়ের চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন, তারপর “→ উত্তর” চাপুন।

প্র ০১১৯৮৯ সালেই প্রমাণ হলো neural network “সব” প্রকাশ করতে পারে। তাহলে ১৯৯০-এর দশকের শেষে আর ২০০০-এর দশকে অনেক গবেষক neural network ছেড়ে SVM-এর মতো পদ্ধতিতে গেলেন কেন?

কারণ উপপাদ্য যে প্রশ্নের উত্তর দেয়, বাস্তবের বাধা সেখানে ছিল না। সে সময়ের neural network-এর সমস্যা ছিল খোঁজা আর generalization. Sigmoid বা tanh-এর গভীর network-এ gradient স্তরে স্তরে ছোট হয়ে যেত (পরে vanishing gradient নামে পরিচিত, Sepp Hochreiter ১৯৯১ সালে বিশ্লেষণ করেন বলে সাধারণত বলা হয়), training অস্থির ছিল, ফল নির্ভর করত শুরুর weight আর অনেক hyperparameter-এর ওপর। কম্পিউটার ধীর, ডেটাসেট ছোট।

অন্যদিকে SVM (Vladimir Vapnik ও সহকর্মীদের কাজ, ১৯৯০-এর দশক) একটি convex সমস্যা সমাধান করত: একটিই সর্বনিম্ন, শুরুর বিন্দুর ওপর নির্ভরতা নেই, আর margin-ভিত্তিক তত্ত্ব generalization নিয়ে কিছু নিশ্চয়তা দিত। Kernel দিয়ে SVM-ও “নমনীয়” ছিল; RBF kernel-এর SVM-ও universal approximation-ধরনের গুণ রাখে। অর্থাৎ প্রকাশের ক্ষমতায় দুই পক্ষ সমান, কিন্তু optimization error-এ SVM স্পষ্ট এগিয়ে। অংশ ৬-এর ভাষায়, সেই যুগে তৃতীয় পদটিই নির্ণায়ক ছিল।

পরিস্থিতি বদলায় যখন তিনটি জিনিস একসঙ্গে আসে: বড় লেবেলসহ ডেটা, GPU-তে দ্রুত matrix গুণ, আর ReLU, ভালো initialization, পরে normalization আর residual connection-এর মতো কৌশল, যা গভীর network-এর optimization সহজ করে। ২০১২ সালের AlexNet প্রায়ই প্রতীক হিসেবে বলা হয়, যদিও কোন কারণের অবদান কত, তা নিয়ে মতভেদ আছে। মজার বিষয়, তত্ত্ব এক বিন্দুও বদলায়নি; বদলেছে হিসাবের বাজেট আর খোঁজার কৌশল। আর বড় ডেটায় estimation error-এর ভয়ও কমেছে।

মূল উপলব্ধি: কোনো পদ্ধতির উত্থান-পতন বুঝতে তিন রকম ভুলের কোনটি সে সময়ে বাধা ছিল, তা খুঁজুন। প্রকাশের ক্ষমতা প্রায় কখনো একা নির্ণায়ক নয়।

প্র ০২Telgarsky-র sawtooth দেখায় গভীরতা exponentially লাভজনক হতে পারে। কিন্তু এলোমেলো weight-এ গভীর network কম টুকরো বানায়। তাহলে বাস্তবের deep learning-এ গভীরতা আসলে কী দেয়?

দুটি ফলই সত্য, আর এরা আলাদা প্রশ্নের উত্তর। Depth separation বলে, কিছু function গভীর network-এ সস্তা আর অগভীরে ভয়ানক দামি। এটি সম্ভাবনার সীমা। Hanin ও Rolnick (২০১৯)-এর পর্যবেক্ষণ বলে, সাধারণ initialization আর training-এ গভীর network সেই সীমার কাছে সাধারণত যায় না; এক মাত্রার রেখা বরাবর region-এর সংখ্যা মোট neuron-এর মোটামুটি অনুপাতে থাকে। অংশ ৭-এর ব্লকেও তাই দেখা গেল: ৩ × ৪-এর এলোমেলো network গড়ে ৫.৩১ টুকরো, অগভীরের ৭.০০৫-এর চেয়েও কম।

তাহলে গভীরতার লাভ কোথা থেকে? একটি জনপ্রিয় ব্যাখ্যা হলো গঠন (compositionality): বাস্তব ডেটার function প্রায়ই ছোট function-এর গঠন, যেমন ছবিতে edge থেকে texture, texture থেকে অংশ, অংশ থেকে বস্তু। Tomaso Poggio ও সহকর্মীদের কাজে দেখানো হয়েছে, এমন স্তরভিত্তিক, স্থানীয় গঠনের function-এ গভীর network curse of dimensionality অনেকটা এড়াতে পারে, অগভীর পারে না। আরেকটি ব্যাখ্যা optimization: residual connection-এর মতো কৌশলসহ গভীর network-এ training-এর পথ সহজ হতে পারে। কিন্তু কোনটির অবদান কত, তা নিয়ে গবেষণা চলছে; “network স্তরে স্তরে মানুষের মতো ধারণা গড়ে” বলাটা প্রমাণিত সত্য নয়, একটি সহায়ক ছবি মাত্র।

বাস্তব প্রমাণ মূলত empirical: অনেক কাজে একই parameter-বাজেটে গভীর আর একটু সরু network ভালো করেছে, তবে খুব গভীর network-এ residual connection আর normalization না থাকলে training ভেঙে পড়ে (পর্ব ১৭)। আর tabular ডেটায় গভীরতার সুবিধা প্রায়ই দেখা যায় না।

মূল উপলব্ধি: তত্ত্ব বলে গভীরতা কী দিতে পারে; ডেটার গঠন আর training ঠিক করে গভীরতা আসলে কী দেয়। দুটি গুলিয়ে ফেলা একটি সাধারণ ভুল।

প্র ০৩Double descent কি classical bias–variance trade-off-কে ভুল প্রমাণ করে?

না। Bias–variance ভাঙন (AI-এর ভিত্তি কোর্সের পর্ব ৬১) একটি গাণিতিক অভেদ: squared loss-এ প্রত্যাশিত ভুল = bias2 + variance + noise. এটি সবসময় সত্য। যা ভুল প্রমাণ হয়েছে তা একটি অনুমান: “জটিলতা” মাপার একটিই অক্ষ আছে (parameter-এর সংখ্যা), আর তার সঙ্গে variance একটানা বাড়ে। অংশ ৮-এর পরীক্ষায় p ≫ n হলে minimum-norm সমাধান অনেক সমান-ভালো সমাধানের মধ্যে ছোটটি বাছে; ফলে parameter বাড়লেও function-টি মসৃণ থাকে, variance কমে। অর্থাৎ parameter গোনা জটিলতার ভালো মাপ নয়; weight-এর norm বা function-এর মসৃণতা অনেক সময় ভালো মাপ।

Belkin ও সহকর্মীদের ২০১৯ সালের প্রবন্ধের শিরোনামেই “reconcile” শব্দটি আছে: classical U-curve threshold-এর বাঁ দিকে এখনো খাটে। চূড়াটিও classical যুক্তিতেই বোঝা যায়: threshold-এ হুবহু মেলানোর সমাধান একটি, আর noise মেলাতে তা বিশাল weight নেয়, variance সর্বোচ্চ। আরও জানা গেছে, ঠিকমতো বাছা ridge regularization অনেক ক্ষেত্রে চূড়াটি প্রায় মুছে দেয় (Nakkiran ও সহকর্মীদের ২০২০ সালের একটি কাজে এমন ফল রিপোর্ট করা হয়েছে)। তাই double descent অনেকটা “regularization ছাড়া interpolation”-এর ঘটনা।

বাস্তব শিক্ষা তিনটি। এক, train ভুল শূন্য হওয়া মানেই সর্বনাশ নয়, আবার নিশ্চয়তাও নয়; validation দেখুন। দুই, model-এর আকার বাছতে একটি বিন্দু নয়, পুরো curve মাপুন, কারণ threshold-এর কাছাকাছি আকার সবচেয়ে খারাপ হতে পারে। তিন, এই ঘটনা সব ডেটা আর সব model-এ দেখা যায় না; আমাদের ছোট পরীক্ষায়ও দ্বিতীয় descent প্রথমটির সেরাকে ছাড়ায়নি।

মূল উপলব্ধি: পুরোনো তত্ত্ব ভুল হয়নি, তার একটি সরল ব্যাখ্যা অসম্পূর্ণ প্রমাণিত হয়েছে। জটিলতা মানে parameter-এর সংখ্যা নয়।

প্র ০৪এক মাত্রায় ১২৮টি unit-এ ভুল ০.০৮৭ পেলাম। বাজারঘর যদি ৫০টি feature (দূরত্ব, সময়, আবহাওয়া, পণ্যের ধরন…) দিয়ে ডেলিভারি-সময় অনুমান করতে চায়, একই কৌশল কেন কাজ করবে না?

অংশ ৩-এর কৌশল ছিল grid: এক মাত্রায় দূরত্ব h-এর knot বসানো, আর ভুল h2-এর মতো কমে। d মাত্রায় একই কৌশলে প্রতিটি অক্ষে 1/h ভাগ, মোট (1/h)dটি ঘর, আর প্রতিটি ঘরে স্থানীয় চূড়া বসাতে কয়েকটি unit. ε = ০.০১ ভুল চাইলে h ≈ ০.১, d = ৫০-এ ১০50টি ঘর, মহাবিশ্বের সব কম্পিউটার মিলেও অসম্ভব। এটিই curse of dimensionality. সাধারণ মসৃণ function-শ্রেণিতে এমন বৃদ্ধি এড়ানো যায় না বলে তাত্ত্বিক নিম্নসীমাও আছে।

তাহলে বাস্তবে neural network উঁচু মাত্রায় কাজ করে কীভাবে? কারণ বাস্তবের function “সাধারণ” নয়। কয়েকটি গঠন সাহায্য করে: ডেটা প্রায়ই উঁচু মাত্রার ভেতরে একটি নিচু মাত্রার পৃষ্ঠের (manifold) ওপর থাকে; target প্রায়ই কয়েকটি দিকের function (যেমন দূরত্ব আর বৃষ্টির মিলিত প্রভাব); আর function প্রায়ই ছোট ছোট অংশের গঠন। Barron (১৯৯৩)-এর ফল দেখায়, একটি বিশেষ মসৃণতা-শর্ত মানা function-এ ভুলের হার মাত্রার ওপর সরাসরি নির্ভর করে না। Network-এর hidden unit grid-এ বাঁধা নয়: w·x + b দিয়ে যেকোনো দিকে কব্জা বসাতে পারে, তাই গুরুত্বপূর্ণ দিক খুঁজে নিতে পারে।

তবু সাবধানতা জরুরি। ৫০টি feature আর কয়েক হাজার অর্ডারের tabular ডেটায় gradient boosting প্রায়ই neural network-এর চেয়ে ভালো বা সমান করে (মেশিন লার্নিং কোর্সের পর্ব ৩৮)। আর ডেটা কম হলে estimation error-ই বড় বাধা, approximation নয়। প্রথমে একটি সরল baseline, তারপর validation-এ মেপে জটিলতা।

মূল উপলব্ধি: এক মাত্রার ছবি অন্তর্দৃষ্টি দেয়, কিন্তু উঁচু মাত্রায় সাফল্য আসে ডেটার গঠন থেকে, “যেকোনো function” ক্ষমতা থেকে নয়।

প্র ০৫“সেরা” learning algorithm বলে কিছু আছে কি? CNN ছবিতে ভালো, Transformer ভাষায়: এটি কি ভাগ্য, না কোনো গভীর নিয়মের ফল?

গভীর নিয়মটির নাম No Free Lunch: David Wolpert (১৯৯৬) আর পরে Wolpert ও William Macready-র কাজের মূল কথা, সব সম্ভাব্য সমস্যার ওপর গড় করলে কোনো learning algorithm অন্যটির চেয়ে ভালো নয়। Train ডেটার বাইরের বিন্দুতে কী হবে, তা ডেটা নিজে বলে না; বলে learner-এর অনুমান, অর্থাৎ inductive bias. তাই “সেরা” শুধু একটি নির্দিষ্ট সমস্যা-পরিবারের সাপেক্ষে।

CNN-এর সাফল্য ভাগ্য নয়: ছবির পরিসংখ্যান সত্যিই স্থানীয় (কাছের pixel সম্পর্কিত), আর একই নকশা যেকোনো জায়গায় আসে। এই অনুমান বসিয়ে দিলে parameter অনেক কমে, ফলে estimation error কমে। Transformer কম অনুমান করে, যেকোনো দুটি token-এর সম্পর্ক শিখতে পারে; ভাষায় দূরের শব্দের সম্পর্ক গুরুত্বপূর্ণ বলে এটি কাজে লাগে, কিন্তু দাম হলো বেশি ডেটা। Vision Transformer-এর মূল প্রবন্ধে (Dosovitskiy ও সহকর্মী, ২০২০) রিপোর্ট করা হয়েছিল, মাঝারি আকারের ডেটায় এটি সমমানের CNN-এর চেয়ে পিছিয়ে থাকে আর খুব বড় ডেটায় pretraining-এর পর এগিয়ে যায়। কম inductive bias-এর দাম বেশি ডেটা, এটি তার একটি উদাহরণ (পর্ব ৩৮-এ বিস্তারিত)।

বাজারঘরের জন্য অর্থ: পণ্যের ছবিতে CNN বা pretrained model, বাংলা রিভিউতে pretrained Transformer, আর অর্ডারের tabular ডেটায় প্রায়ই tree-ভিত্তিক model. Architecture বাছাই আসলে প্রশ্ন করা: “আমার ডেটা সম্পর্কে কোন অনুমান সত্য?” প্রতিটি অনুমান ডেটা থেকে কিছু শেখার দায় সরিয়ে নেয়; অনুমান ভুল হলে সেই দাম approximation error হিসেবে ফেরত আসে।

মূল উপলব্ধি: Universal approximation সব architecture-কে প্রায় সমান করে দেয়; পার্থক্য আসে কোন অনুমান ডেটার সঙ্গে মেলে, তা থেকে। Architecture মানে prior জ্ঞান।

অনুশীলন

প্রথম দুটি খাতা-কলমে, তৃতীয়টি ভাবনার, চতুর্থটি প্রথম ব্লকে কয়েক লাইন বদলে।

  1. কয়টি unit লাগবে (হিসাব): [0, π]-এ sin x-কে knot ছুঁয়ে যাওয়া সমান দূরের ভাঙা রেখা দিয়ে আঁকতে চান, সর্বোচ্চ ভুল ০.০১-এর কম। h2M/8 সীমা ব্যবহার করে অন্তত কয়টি ReLU unit লাগবে?

    sin-এর দ্বিতীয় derivative −sin x, তাই M = ১। শর্ত h2/8 ≤ ০.০১ ⇒ h ≤ √০.০৮ ≈ ০.২৮২৮। টুকরোর সংখ্যা n = π/h ≥ ৩.১৪১৬/০.২৮২৮ ≈ ১১.১১, তাই n = ১২টি টুকরো, অর্থাৎ ১২টি ReLU unit (অংশ ২-এর গঠনে প্রতি টুকরোয় একটি, প্রথমটি শুরুর ঢাল দেয়)। সংখ্যায় যাচাই: n = ১১-এ আসল সর্বোচ্চ ভুল ০.০১০২ (সামান্য বেশি), n = ১২-এ ০.০০৮৫।

  2. আউটপুট অনুমান করুন: চালানোর আগে লিখুন কী ছাপা হবে।
    import numpy as np
    relu = lambda z: np.maximum(0, z)
    T = lambda x: 2 * relu(x) - 4 * relu(x - 0.5)
    bump = lambda x: relu(x) - 2 * relu(x - 1) + relu(x - 2)
    print(bump(np.array([0.5, 1.0, 1.5, 3.0])))
    print(round(T(T(0.3)), 3), round(T(T(T(0.3))), 3))

    প্রথম লাইন [0.5 1. 0.5 0. ]: চূড়া x = ১-এ উচ্চতা ১; ০.৫-এ শুধু প্রথম পদ (০.৫); ১.৫-এ ১.৫ − ২ × ০.৫ = ০.৫; ৩-এ ৩ − ৪ + ১ = ০। দ্বিতীয় লাইন 0.8 0.4: T(০.৩) = ০.৬, T(০.৬) = ২ − ১.২ = ০.৮, T(০.৮) = ২ − ১.৬ = ০.৪। (ডেস্কটপে চালিয়ে মেলানো।)

  3. পরিকল্পনা যাচাই (design/critique): একজন সহকর্মী বললেন, “UAT অনুযায়ী এক hidden layer-ই যথেষ্ট। তাই বাজারঘরের চাহিদা-পূর্বাভাসে একটি hidden layer রেখে unit বাড়াতে থাকব, যতক্ষণ না train ভুল শূন্য হয়। তারপর deploy.” তিনটি সমস্যা আর একটি ভালো পরিকল্পনা লিখুন।

    সমস্যা: (১) Train ভুল শূন্য মানে noise-সহ মুখস্থ; অংশ ৫-এ ৩০টি noisy বিন্দুতে train ভুল noise-এর চেয়ে কম হলেও আসল curve থেকে দূরত্ব ৩-এর বেশি। (২) Unit বাড়ানোর পথে interpolation threshold পার হতে হয়, যেখানে test ভুল চূড়ায় ওঠে (অংশ ৮-এ ২.০৫ বনাম ০.০৮৭)। (৩) উপপাদ্য খোঁজার কথা বলে না; একটি seed-এ আটকে যাওয়া network-কে “ছোট বলে খারাপ” ভাবা ভুল হতে পারে। এ ছাড়া পূর্বাভাস ভবিষ্যতের, অর্থাৎ train-এর পরিসরের বাইরের সময়, যেখানে উপপাদ্য কিছুই বলে না।

    ভালো পরিকল্পনা: সময় ধরে ভাগ (পুরোনো সপ্তাহে train, পরের সপ্তাহে validation, শেষে একবার test); সরল baseline (গত চার সপ্তাহের একই ঘণ্টার গড়); কয়েকটি আকার আর কয়েকটি seed-এ validation curve; weight decay বা early stopping; ঘণ্টাকে (cos, sin)-এ লেখা; আর deploy-এর পর ভুল নজরে রাখা।

  4. Knot কোথায় বসাবেন (কোড): প্রথম ব্লকে knot সমান দূরে। বাঁকা জায়গায় ঘন knot দিলে কী হয়? build-এ k-এর লাইনটি বদলে knot বসান এমনভাবে, যাতে √|f''| সমান ভাগে ভাগ হয় (নিচের কোড), আর n = ১৬ ও ৩২-এ সর্বোচ্চ ভুল তুলনা করুন।
    tt = np.linspace(0, 24, 24001)
    w = np.sqrt(np.abs(np.gradient(np.gradient(demand(tt), tt), tt))) + 1e-3
    cw = np.cumsum(w); cw = (cw - cw[0]) / (cw[-1] - cw[0])
    k = np.interp(np.linspace(0, 1, n + 1), cw, tt)   # build(n)-এর ভেতরে

    লেখকের ডেস্কটপ run-এ (NumPy, একই function): n = ১৬-এ সর্বোচ্চ ভুল ৩.৪৫ থেকে নেমে ১.১৬, n = ৩২-এ ১.২৪ থেকে ০.২৮১, প্রায় তিন থেকে চার গুণ ভালো, unit একই। কারণ h2M/8 সীমায় M জায়গাভেদে আলাদা; যেখানে curve বাঁকা, সেখানে ছোট h দিলে মোট ভুল সমানভাবে ছড়ায়। এটিই ব্যাখ্যা করে, অংশ ৫-এ ১৬ unit-এর শেখানো network কেন সমান-দূরের হাতে গড়া network-কে হারাল (১.০০ বনাম ১.১৯): gradient descent knot-এর জায়গাও শেখে। সমান-দূরের গঠন তাই approximation error-এর একটি ঊর্ধ্বসীমা মাত্র।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি জানা curve-কে ReLU network দিয়ে হাতে গড়ার ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. পরিসরে knot k0 … kn ঠিক করা
  2. প্রতিটি knot-এ curve-এর মান নেওয়া
  3. পাশাপাশি দুই knot থেকে প্রতিটি টুকরোর ঢাল হিসাব
  4. ঢালের বদলকে output weight, আর −ki-কে hidden bias হিসেবে বসানো
  5. ঘন grid-এ সর্বোচ্চ ভুল মেপে দেখা

Universal approximation theorem (ReLU বা sigmoid activation, এক hidden layer) কোনটির নিশ্চয়তা দেয়?

  • ঠিক ১০০টি unit-এ যেকোনো function-এর ভুল ০.০১-এর কম
  • একটি compact এলাকায় যেকোনো ধারাবাহিক function-কে যত কাছে চাই আনার মতো weight আছে
  • Gradient descent সবসময় সেই weight খুঁজে পাবে
  • অল্প ডেটায় শিখেও নতুন ডেটায় ভুল কম হবে

g(x) = ReLU(x) − 2·ReLU(x − 1) + ReLU(x − 2) হলে g(1.5) কত?

  • 1.5
  • 1
  • 0.5
  • 0

এক input আর ১০টি ReLU unit-এর এক hidden layer-এর network সর্বোচ্চ কয়টি সরল (linear) টুকরো বানাতে পারে?

  • 10
  • 210 = 1024
  • 20
  • 11

T(x) = 2·ReLU(x) − 4·ReLU(x − 0.5) (প্রতি layer-এ দুটি unit) পাঁচ layer ধরে পরপর চালালে [0, 1]-এ কয়টি সরল টুকরো হয়, আর মোট unit কত?

  • 32 টুকরো, 10 unit
  • 11 টুকরো, 10 unit
  • 64 টুকরো, 10 unit
  • 32 টুকরো, 31 unit

৬৪ unit-এর ReLU network: সমান-দূরের knot দিয়ে হাতে গড়া network-এর ভুল 0.08, কিন্তু ২০০টি ঘন, noise-হীন বিন্দুতে Adam দিয়ে শেখানো একই প্রস্থের network-এর ভুল 0.60. বাড়তি ভুলের প্রধান কারণ কোনটি?

  • Approximation error
  • Optimization error
  • Estimation error
  • ডেটার noise থেকে আসা অপরিহার্য ভুল

n = 40টি noisy বিন্দুতে random ReLU feature আর minimum-norm least squares দিয়ে feature-এর সংখ্যা p বাড়ানো হচ্ছে। test ভুল সাধারণত কোথায় সবচেয়ে বেশি হয়?

  • p ≈ 5
  • p ≈ 3000
  • যেখানে feature matrix-এর rank সবে n = 40 ছোঁয়, p ≈ 40–45
  • p-এর ওপর নির্ভর করে না

Leshno ও সহকর্মীদের (১৯৯৩) ফল অনুযায়ী এক hidden layer-এর network-এ কোন activation দিলে universal approximation খাটে না?

  • ReLU
  • tanh
  • Sigmoid
  • σ(z) = z2

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন, অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. তিন প্রশ্ন আলাদা: প্রকাশ (approximation), খোঁজা (optimization), নতুন ডেটা (estimation/generalization). Universal approximation শুধু প্রথমটির, অস্তিত্বের উত্তর।
  2. ReLU = কব্জা: তিনটিতে একটি চূড়া; এক মাত্রায় knot ছুঁয়ে যাওয়া রেখার ভুল ≤ h2M/8, unit দ্বিগুণে ভুল প্রায় চার ভাগের এক ভাগ।
  3. শেখানো ≠ গড়া: সরু network প্রায়ই আটকে যায়; ফল seed-নির্ভর; চওড়া করলে খোঁজা সহজ হতে দেখা যায়।
  4. গভীরতা গুণ করে: tent map k বার, ২k unit, ২k টুকরো; এক layer-এ ২k − ১ unit. কিন্তু এলোমেলো weight-এ এত টুকরো আসে না।
  5. Double descent আর inductive bias: চূড়া interpolation threshold-এ, পর্যবেক্ষণ মাত্র; আর architecture হলো ডেটা সম্পর্কে অনুমান, যা ঠিক হলে কম ডেটায় শেখা যায়।

পরের পর্বে: মডিউল ২ শুরু: NumPy-তে হাতে লেখা network ছেড়ে PyTorch tensor. Shape, dtype, device আর broadcasting, যাতে এই পর্বের পরীক্ষাগুলো বড় আকারে GPU-তেও চালানো যায়।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান