Chapter 13 of 25
Layers, neurons, and how information flows forward
একটি সিঙ্গেল perceptron ডেটার মাঝখান দিয়ে সবসময় শুধু একটি মাত্র সরলরেখাই টানতে পারে। যেসব প্রবলেমের জন্য একটি রেখাই যথেষ্ট, সেগুলোর ক্ষেত্রে এটা ঠিক আছে — কিন্তু যখনই কোনো প্রবলেমের জন্য একটি বাঁকা বা কার্ভড (curved) বাউন্ডারি দরকার হয়, অথবা কয়েকটি বাউন্ডারি একসাথে জোড়া দেওয়ার দরকার হয় (যেমন হাতে লেখা কোনো সংখ্যা চেনা, বা কোনো ছবিতে কুকুর আর বিড়ালের পার্থক্য বোঝা), তখন একটি মাত্র perceptron-এর সেই প্রকাশ করার ক্ষমতা বা এক্সপ্রেসিবিলিটি (expressive power) থাকে না। আপনি ইতিমধ্যেই জানেন কেন: কারণ এটি একটি মাত্র লিনিয়ার কাট বা সরল রেখা ছাড়া আর কিছুই নয়।
প্রথম দেখায় এর সমাধানটা খুব সহজ মনে হয় — শুধু লেয়ারের পর লেয়ার perceptron-গুলোকে একটার ওপর আরেকটা সাজিয়ে দিলেই তো হয়। কিন্তু ঠিক কীভাবে আপনি এদেরকে একসাথে জুড়বেন, এদের মাঝে ঠিক কী প্রবাহিত হবে, আর এভাবে কোনো বাছবিচার ছাড়া সাজালেই কি আদৌ কোনো লাভ হবে? এই চ্যাপ্টারটি ঠিক এই তিনটি প্রশ্নের নিখুঁত উত্তর দেয়: এটি Multi-Layer Perceptron-এর সম্পূর্ণ ও আনুষ্ঠানিক আর্কিটেকচার তুলে ধরে এবং একটি raw ইনপুটকে কীভাবে একটি প্রেডিকশনে রূপান্তর করা যায়, সেই ফরোয়ার্ড প্রপাগেশন (forward propagation) সমীকরণগুলো প্রমাণ করে।
ভাবুন তো একটি হাসপাতালের ট্রায়াজ (triage) সিস্টেম কীভাবে একজন রোগীকে মূল্যায়ন করতে পারে। "শরীরের তাপমাত্রা বেশি → অসুস্থ হিসেবে ফ্ল্যাগ করো" — এটি হলো এক লাইনের একটি perceptron-স্টাইলের সিদ্ধান্ত, যা অনেক কিছুই এড়িয়ে যায়: যেমন কোনো রোগীর শরীরের তাপমাত্রা হয়তো হালকা বেশি, কিন্তু ব্লাড প্রেসার বিপজ্জনকভাবে কম এবং হার্ট রেট অনেক বেশি — তাকেও অবশ্যই ফ্ল্যাগ করা উচিত, যদিও তার কোনো একটি নির্দিষ্ট পরিমাপ একাই কোনো স্পষ্ট থ্রেশহোল্ড পার করেনি।
বাস্তবে এর চেয়ে অনেক বেশি বাস্তবসম্মত মূল্যায়ন ধাপে ধাপে হয়ে থাকে। প্রথমে, একজন নার্স কয়েকটি শারীরিক লক্ষণ বা ভাইটালস (vitals) একসাথে মিলিয়ে কিছু প্রাথমিক সিদ্ধান্তে পৌঁছান — "তার কার্ডিওভাসকুলার বা হার্টের অবস্থা কি চিন্তার কারণ?", "তার কি কোনো ইনফেকশন বা সংক্রমণ হওয়ার সম্ভাবনা আছে?" তারপর একজন ডাক্তার সেই প্রাথমিক সিদ্ধান্তগুলোকে একসাথে মিলিয়ে একটি চূড়ান্ত সিদ্ধান্ত নেন। ঠিক এটাই হলো একটি Multi-Layer Perceptron-এর আকার: raw ইনপুটগুলোকে একটি হিডেন লেয়ারের মাধ্যমে মিলিয়ে কিছু ইন্টারমিডিয়েট রিপ্রেজেন্টেশন (intermediate representations) বা প্রাথমিক সিদ্ধান্ত তৈরি করা হয়, আর সেই ইন্টারমিডিয়েট রিপ্রেজেন্টেশনগুলোকে পরের লেয়ারে আবার মেলানো হয় — এভাবে চলতে চলতে একেবারে চূড়ান্ত প্রেডিকশন পর্যন্ত পৌঁছানো হয়।
একটি MLP ডিজাইন করার সময় আপনার হাতে মূলত দুটি ডায়াল বা নব থাকে — width (চওড়া), অর্থাৎ প্রতিটি হিডেন লেয়ারে কতগুলো নিউরন থাকবে, আর depth (গভীরতা), অর্থাৎ কতগুলো হিডেন লেয়ার একটার পর একটা সাজানো থাকবে। এই দুটোর প্রভাব ভিন্ন ভিন্ন:

| উপাদান (Component) | ভূমিকা (Role) |
|---|---|
| ইনপুট লেয়ার | raw ফিচার ভেক্টর ধারণ করে; কোনো ক্যালকুলেশন করে না এবং এর নিজস্ব কোনো ওয়েট নেই |
| হিডেন লেয়ার | ইনপুটটিকে ক্রমান্বয়ে আরও অ্যাবস্ট্রাক্ট (abstract) রিপ্রেজেন্টেশনে রূপান্তর করে, যা এর মাধ্যমে একটি নন-লিনিয়ার অ্যাক্টিভেশন দ্বারা অনুসৃত হয় |
| আউটপুট লেয়ার | চূড়ান্ত প্রেডিকশন তৈরি করে; এর অ্যাক্টিভেশন কাজের ধরন বা টাস্কের ওপর নির্ভর করে (বাইনারি ক্লাসিফিকেশনের জন্য সিগময়েড, মাল্টি-ক্লাসের জন্য সফটম্যাক্স, রিগ্রেশনের জন্য লিনিয়ার/আইডেন্টিটি) |
| ওয়েট (Weights) | প্যারামিটার যা শেখা যায় (Learnable) এবং যা লেয়ার এবং লেয়ারের মাঝের প্রতিটি কানেকশনের শক্তি নিয়ন্ত্রণ করে |
| বায়াস (Biases) | নিউরন প্রতি লার্নেবল অফসেট (offsets), যা একটি নিউরনকে তার ইনপুটগুলোর ওপর নির্ভর না করে স্বাধীনভাবে নিজের অ্যাক্টিভেশন থ্রেশহোল্ড শিফট করার সুযোগ দেয় |
| অ্যাক্টিভেশন | নন-লিনিয়ার ফাংশন যা -এর ওপর এলিমেন্ট-ভিত্তিক (element-wise) প্রয়োগ করা হয় |
বায়াস টার্ম না থাকলে, প্রতিটি লেয়ারের ডিসিশন বাউন্ডারিকে মূলবিন্দু (origin) দিয়ে যেতে বাধ্য করা হয় — যা নেটওয়ার্ক কী রিপ্রেজেন্ট করতে পারবে তার ওপর একটি কঠোর ও অপ্রয়োজনীয় সীমাবদ্ধতা তৈরি করে। আর্কিটেকচারাল কোনো নির্দিষ্ট কারণ না থাকলে সবসময় বায়াস টার্ম যুক্ত করুন এবং তাকে ট্রেন করুন।
সম্পূর্ণভাবে লিনিয়ার লেয়ার দিয়ে তৈরি একটি ফিডফরোয়ার্ড নেটওয়ার্ক — যার কোথাও কোনো নন-লিনিয়ার অ্যাক্টিভেশন নেই — সেটি যত গভীর বা ডিপই হোক না কেন, গাণিতিকভাবে তা একটি সিঙ্গেল লিনিয়ার লেয়ারের সমতুল্য।
প্রমাণ: কোনো নন-লিনিয়ারিটি নেই এমন একটি দুই লেয়ারের নেটওয়ার্ক বিবেচনা করুন: এবং । প্রথমটি দ্বিতীয়টিতে বসালে পাই:
যা নিজেই শুধু একটি লিনিয়ার ট্রান্সফরমেশন। গাণিতিক আরোহ বিধি বা ইনডাকশন (induction) অনুযায়ী, আপনি যতগুলো লিনিয়ার লেয়ারই সাজান না কেন, এটি সবসময় সত্য।
নন-লিনিয়ার অ্যাক্টিভেশন ছাড়া, যতই গভীরতা দেওয়া হোক না কেন, তা একটি সিঙ্গেল-লেয়ার perceptron-এর চেয়ে বেশি কোনো রিপ্রেজেন্টেশনাল পাওয়ার বা প্রকাশ করার ক্ষমতা যোগ করতে পারে না — আর একটি সিঙ্গেল লিনিয়ার লেয়ার এমনকী XOR-ও সলভ করতে পারে না। নন-লিনিয়ার অ্যাক্টিভেশন ফাংশনগুলোই হলো সেই জিনিস যা গভীরতাকে কাজে আসতে দেয়। পরের চ্যাপ্টারে নির্দিষ্ট কিছু চয়েস (সিগময়েড, ট্যানএইচ, রিলু এবং অন্যান্য) নিয়ে বিস্তারিত আলোচনা করা হবে।
একটি সিঙ্গেল হিডেন লেয়ার (যাতে যথেষ্ট সংখ্যক নিউরন আছে এবং একটি নন-লিনিয়ার, বাউন্ডেড, মনোটোনিকালি-ইনক্রিজিং অ্যাক্টিভেশন ব্যবহার করা হয়) সম্পন্ন একটি ফিডফরোয়ার্ড নেটওয়ার্ক, -এর যেকোনো কম্প্যাক্ট (compact) সাবসেটে বাস করা যেকোনো ধারাবাহিক ফাংশনকে (continuous function) যেকোনো নির্ধারিত নির্ভুলতায় (arbitrary precision) আনুমানিক (approximate) করতে পারে।
সহজ কথায়: তাত্ত্বিকভাবে, একটিমাত্র যথেষ্ট চওড়া হিডেন লেয়ারই যথেষ্ট — একটি MLP নীতিগতভাবে প্রায় যেকোনো ফাংশনের আকৃতি তৈরি করতে সক্ষম, শুধু যদি তার হিডেন লেয়ারে যথেষ্ট নিউরন থাকে এবং সেই ফাংশনটি ধারাবাহিক হয়।
এই থিওরেমটি শুধু বলে যে এমন একটি নেটওয়ার্ক অস্তিত্বে আছে — এটি কখনোই বলে না যে ঠিক কতগুলো নিউরন লাগবে (বাস্তবে এই সংখ্যাটি এক্সপোনেনশিয়ালি বড় হতে পারে), বা কীভাবে gradient descent দিয়ে সেই ওয়েটগুলো খুঁজে বের করা যাবে। ঠিক এই কারণেই বাস্তবে আমরা একটিমাত্র প্রশস্ত লেয়ারের বদলে বেশ কয়েকটি অপেক্ষাকৃত সরু লেয়ার সাজিয়ে গভীরতা ব্যবহার করি — গভীরতা প্রায়ই একই এক্সপ্রেসিভ পাওয়ার অনেক কম মোট প্যারামিটার দিয়ে অর্জন করতে দেয়।
যেখানে (ইনপুট) এবং (চূড়ান্ত আউটপুট)।
যেখানে: হলো লেয়ারের প্রি-অ্যাক্টিভেশন (ওয়েটেড সাম), হলো এর পোস্ট-অ্যাক্টিভেশন আউটপুট, , এবং ।
সহজ কথায়: তথ্য কঠোরভাবে সামনের দিকে প্রবাহিত হয়: প্রতিটি লেয়ার তার আগের লেয়ারের আউটপুট গ্রহণ করে, একটি অ্যাফাইন ট্রান্সফরমেশন (affine transformation) প্রয়োগ করে, তারপর একটি নন-লিনিয়ারিটি প্রয়োগ করে এবং ফলাফলটি পরের লেয়ারে পাঠিয়ে দেয়। এ কারণেই এর নাম "ফিডফরোয়ার্ড"।
সংখ্যক ইনপুট এবং সংখ্যক আউটপুট থাকা একটি লেয়ারে অবশ্যই -এর শেপ এবং -এর শেপ হতে হবে। একটি নেটওয়ার্ক ইমপ্লিমেন্ট করার আগে প্রতিটি ওয়েট ম্যাট্রিক্সের শেপ লিখে নিলে বেশিরভাগ শেপ-মিসম্যাচ (shape-mismatch) বাগ দূর হয়ে যায়।
একটি নেটওয়ার্কের মোট শেখার যোগ্য বা ট্রেইনেবল প্যারামিটারের সংখ্যা গণনা করা একটি অত্যন্ত ব্যবহারিক দক্ষতা — এটি মডেলের আকার, মেমরি খরচ, এবং ওভারফিটিংয়ের (overfitting) ঝুঁকি বোঝার প্রথম ধাপ।
যেখানে: হলো আগের লেয়ারের নিউরন সংখ্যা এবং হলো বর্তমান লেয়ারের নিউরন সংখ্যা। পুরো নেটওয়ার্কের মোট প্যারামিটার সংখ্যা হলো এই রাশিটির সমষ্টি, থেকে পর্যন্ত।
সহজ কথায়: প্রতিটি কানেকশন (connection) একটি করে ওয়েট প্যারামিটার, আর প্রতিটি নিউরন একটি করে বায়াস প্যারামিটার যোগ করে।
উদাহরণস্বরূপ, ওপরের ২-ইনপুট, ২-নিউরনের হিডেন লেয়ার এবং ১-নিউরনের আউটপুট লেয়ার সংবলিত নেটওয়ার্কে: লেয়ার ১-এ আছে টি প্যারামিটার, আর লেয়ার ২-এ আছে টি প্যারামিটার — সর্বমোট মাত্র ৯টি প্যারামিটার। বাস্তব দুনিয়ার নেটওয়ার্কে, যেখানে প্রতিটি লেয়ারে শত শত বা হাজার হাজার নিউরন থাকতে পারে, এই সংখ্যাটি কয়েক মিলিয়ন বা বিলিয়নে পৌঁছাতে পারে — এবং ঠিক এ কারণেই ফুললি-কানেক্টেড (fully-connected) লেয়ারগুলো এত বেশি মেমরি-হাংরি হয়ে ওঠে।
একটি সম্পূর্ণ মাল্টি-লেয়ার উদাহরণে যাওয়ার আগে, এটি দেখা বেশ উপকারী যে কীভাবে একটি সিঙ্গেল নিউরনের ওয়েট এবং বায়াস তার ডিসিশন বাউন্ডারিকে আকার দেয় — কারণ এই একই বিল্ডিং ব্লকই তো লেয়ারের পর লেয়ার সাজিয়ে একটি সম্পূর্ণ MLP তৈরি করা হয়।
একটি সিঙ্গেল নিউরনের w1, w2, এবং বায়াস অ্যাডজাস্ট করুন এবং দেখুন কীভাবে নির্দিষ্ট কিছু স্যাম্পল পয়েন্টের ওপর দিয়ে এর ডিসিশন বাউন্ডারি নড়াচড়া করে।
z = 1.0·x + 1.0·y + 0.0
Points shift between the two colors the moment the boundary line crosses them — this is the entire "decision" a single neuron makes.
এই হুবহু গাণিতিক উদাহরণটি এই কোর্সের বাকি অংশে, বিশেষ করে Backpropagation চ্যাপ্টারে বারবার উল্লেখ করা হবে — তাই এটি খুব সাবধানে হাতে-কলমে ট্রেস করে দেখা উচিত। একটি নেটওয়ার্ক বিবেচনা করুন যার ২টি ইনপুট, ২টি নিউরনের একটি হিডেন লেয়ার (সিগময়েড অ্যাক্টিভেশন), এবং একটি আউটপুট নিউরন (বাইনারি ক্লাসিফিকেশনের জন্য সিগময়েড অ্যাক্টিভেশন) আছে।
দেওয়া আছে:
লেয়ার ১ প্রি-অ্যাক্টিভেশন (Layer 1 pre-activation):
লেয়ার ১ অ্যাক্টিভেশন (Layer 1 activation) (সিগময়েড, ):
লেয়ার ২ প্রি-অ্যাক্টিভেশন (Layer 2 pre-activation):
লেয়ার ২ অ্যাক্টিভেশন (চূড়ান্ত প্রেডিকশন) (Layer 2 activation - final prediction):
নেটওয়ার্কটি প্রেডিক্ট করে যে, ইনপুটটি পজিটিভ ক্লাসে পড়ার সম্ভাবনা বা প্রোবাবিলিটি প্রায় । এই নিখুঁত নাম্বারগুলো মনে রাখুন — , , , — Backpropagation চ্যাপ্টারটি ঠিক এখান থেকেই শুরু হবে, এবং গ্রেডিয়েন্ট ক্যালকুলেট করার জন্য এই একই ফরোয়ার্ড পাস (forward pass) ব্যবহার করবে।
বাস্তবে আমরা কখনোই একটি একটি করে ইনপুট নেটওয়ার্কের ভেতর দিয়ে পাঠাই না — এতে GPU-এর সমান্তরাল কম্পিউটেশনের (parallel computation) ক্ষমতা পুরোপুরি অপচয় হয়ে যেত। এর বদলে, টি উদাহরণের একটি ব্যাচ (batch) একসাথে পাঠানো হয়।
যেখানে: -এর প্রতিটি কলাম একটি আলাদা উদাহরণের অ্যাক্টিভেশন ধারণ করে, এবং -কে প্রতিটি কলামে যোগ করার জন্য ব্রডকাস্ট (broadcast) করা হয় (NumPy এবং প্রায় সব ফ্রেমওয়ার্কেই এটি স্বয়ংক্রিয়ভাবে হয়ে যায়)।
সহজ কথায়: সমীকরণটি একদম একই থাকে, শুধু ভেক্টরের বদলে একটি ম্যাট্রিক্স হয়ে যায়, যার প্রতিটি কলাম একটি করে উদাহরণ। এই একই ওয়েট ম্যাট্রিক্স ব্যাচের প্রতিটি উদাহরণের জন্য পুনরায় ব্যবহার হয়, ফলে একবার ম্যাট্রিক্স মাল্টিপ্লিকেশন করেই পুরো ব্যাচের ফরোয়ার্ড পাস শেষ হয়ে যায়।
কিছু ফ্রেমওয়ার্ক (যেমন PyTorch-এর ডিফল্ট কনভেনশন) ব্যাচ ডাইমেনশনকে প্রথম অক্ষে রাখে (shape (m, n)), আবার কিছু গাণিতিক ডেরিভেশন (এই চ্যাপ্টারের মতো) একে শেষ অক্ষে রাখে (shape (n, m))। কোনো লাইব্রেরি ব্যবহার করার আগে তার কনভেনশনটি যাচাই করে নিন — এটি এই চ্যাপ্টারের আগেই আলোচনা করা W^[ℓ] শেপ-মিসম্যাচ বাগের মতোই একটি সাধারণ উৎস।
import numpy as np
def sigmoid(z):
return 1 / (1 + np.exp(-z))
x = np.array([1.0, 0.5])
W1 = np.array([[0.3, -0.2], [0.5, 0.1]])
b1 = np.array([0.1, -0.1])
W2 = np.array([[0.8, -0.6]])
b2 = np.array([0.2])
z1 = W1 @ x + b1
a1 = sigmoid(z1)
z2 = W2 @ a1 + b2
a2 = sigmoid(z2) # চূড়ান্ত প্রেডিকশন
print("z1 =", z1)
print("a1 =", a1)
print("z2 =", z2)
print("y_hat =", a2)এটি চালালে হুবহু হাতে করা হিসেবটিই পাওয়া যায়: z1 = [0.3, 0.45], a1 ≈ [0.5744, 0.6106], z2 ≈ 0.2931, y_hat ≈ 0.5728। খেয়াল করুন কীভাবে প্রতিটি লেয়ার মাত্র দুটি লাইন — একটি ম্যাট্রিক্স-ভেক্টর গুণন এবং তার সাথে বায়াস যোগ, তারপর একটি অ্যাক্টিভেশন — লেয়ার প্রতি একবার করে রিপিট বা পুনরাবৃত্তি করা হয়েছে।
| প্রতীক (Symbol) | অর্থ (Meaning) |
|---|---|
| ওয়েট আছে এমন লেয়ারের সংখ্যা (ইনপুট লেয়ার বাদে) | |
| লেয়ারে থাকা নিউরনের সংখ্যা | |
| লেয়ারের ওয়েট ম্যাট্রিক্স | |
| লেয়ারের বায়াস ভেক্টর | |
| লেয়ারের প্রি-অ্যাক্টিভেশন | |
| লেয়ারের অ্যাক্টিভেশন (নন-লিনিয়ারিটির পরের আউটপুট) | |
| নেটওয়ার্কের চূড়ান্ত প্রেডিকশন |
লেয়ার 0 (Layer 0) শুধু -কে ধারণ করে। কোনো নেটওয়ার্কের আকার নির্ধারণ করার সময় ভুল করে একে ওয়েটসহ একটি "আসল" লেয়ার হিসেবে গণনা করে ফেলাটা খুব সহজ — কিন্তু এটি তা নয়।
-এর শেপ হলো — অর্থাৎ ইনপুটের সাপেক্ষে আউটপুট (outputs by inputs), উল্টোটা নয়। একেবারে শূন্য থেকে কোনো নেটওয়ার্ক ইমপ্লিমেন্ট করার সময় ভুল করে এটিকে ট্রান্সপোজ (Transpose) করে ফেলাটা সবচেয়ে সাধারণ শেপ-মিসম্যাচ বাগগুলোর একটি।
"নেটওয়ার্কটি অনেক বড়" বলতে কখনো নিউরনের সংখ্যা বোঝানো হয়, আবার কখনো প্যারামিটারের সংখ্যা — এই দুটো এক জিনিস নয়। একটি প্রশস্ত লেয়ার প্যারামিটার সংখ্যাকে বর্গাকারে (quadratically) বাড়িয়ে দিতে পারে, কারণ প্রতিটি নতুন নিউরন আগের লেয়ারের প্রতিটি নিউরনের সাথেও একটি নতুন কানেকশন তৈরি করে। মডেলের আকার নিয়ে আলোচনা করার সময় সবসময় স্পষ্ট করুন কোনটির কথা বলছেন।
আজকের দিনে প্রতিটি ইমেজ ক্লাসিফায়ার, ল্যাঙ্গুয়েজ মডেল এবং রেকমেন্ডেশন সিস্টেম শেষমেশ ঠিক এই ফরোয়ার্ড প্রপাগেশন রেসিপির ওপরই দাঁড়িয়ে আছে, শুধু তাতে আরও অনেক লেয়ার এবং আরও অনেক বহিরাগত বা জটিল লেয়ারের প্রকারভেদ যোগ করা হয়েছে। হাতে-কলমে করা এই সহজ দুই-লেয়ারের উদাহরণটি বুঝতে পারাই হলো এর পরে আসা সবকিছুর ভিত্তি: backpropagation ঠিক এই একই কম্পিউটেশন গ্রাফটিকে উল্টো দিকে চালিয়ে গ্রেডিয়েন্ট ক্যালকুলেট করে, এবং এর পরের চ্যাপ্টারগুলোর প্রতিটি আর্কিটেকচারাল সিদ্ধান্ত — যেমন অ্যাক্টিভেশন ফাংশন, লস ফাংশন, অপ্টিমাইজার — সরাসরি এখানে সংজ্ঞায়িত করা -এর মেকানিজমের সাথে যুক্ত হয়।