Chapter 4 of 18
The two very different kinds of knobs inside every model
ধরুন একটা মডেল বানাচ্ছেন যেটা বাসার সাইজ (স্কয়ার ফিট) দেখে দাম প্রেডিক্ট করে। সবচেয়ে সহজ ভার্সনটা দেখতে একটা সরল রেখার মতো:
এখানে হলো বাসার সাইজ, হলো প্রেডিক্ট করা দাম, আর আর হলো দুইটা সংখ্যা যেগুলো মডেলকে ঠিকঠাক বের করতে হবে। (ওয়েট) ঠিক করে প্রতি স্কয়ার ফিটে দাম কতটা খাড়াভাবে বাড়বে, আর (বায়াস) পুরো লাইনটাকে উপরে বা নিচে সরায়। ট্রেনিং শুরুর আগে এই দুইটা সংখ্যা নেহায়েতই আন্দাজ — হয়তো দুইটাই শুরু হয় ০ দিয়ে। হাজার হাজার আসল বাসা বিক্রির ডেটার উপর ট্রেনিং করার পর আর একটা নির্দিষ্ট মানে গিয়ে থিতু হয় — ধরুন আর — কারণ এই মানগুলোই আসল ডেটার সাথে লাইনটাকে ভালোভাবে মিলিয়ে দেয়।
আর হলো parameters। একটা parameter হলো এমন একটা সংখ্যা যেটা মডেলের ভেতরেই থাকে আর মডেল নিজেই সেটা ডেটা দেখে শিখে নেয় — উদাহরণ দেখে, ভুল কমানোর দিকে নিজেকে অ্যাডজাস্ট করে। কেউ হাতে বসিয়ে দেয় না — gradient descent, backpropagation, বা যেটাই ট্রেনিং প্রসেস হোক না কেন, সেটা বারবার প্রেডিকশনকে আসল দামের সাথে মিলিয়ে দেখে আর , -কে এমন দিকে সরায় যেদিকে ভুল কমে, আর এভাবেই এই মানটা নিজে থেকে বের করে ফেলে।
এবার এটাকে একটা আসল নিউরাল নেটওয়ার্কে নিয়ে যান — আইডিয়াটা একই থাকে, শুধু সাইজটা বদলায়। একটা আধুনিক নিউরাল নেটওয়ার্কে লক্ষ লক্ষ বা কোটি কোটি parameter থাকতে পারে — প্রতিটা নিউরনের মধ্যকার প্রতিটা কানেকশনের ওয়েট, আর প্রতিটা নিউরনে যোগ হওয়া প্রতিটা বায়াস — আর এদের প্রত্যেকটাই এমন একটা সংখ্যা যেটা ট্রেনিং প্রসেস নিজে থেকেই টিউন করে।
Parameter হলো এমন একটা মান যেটা ট্রেনিংয়ের সময় মডেল নিজে থেকে শিখে নেয়। ট্রেনিং যদি সংখ্যাটা বের করে থাকে, সেটাই parameter।
একটু ভাবুন, ট্রেনিংয়ের সময় একটা parameter আসলে কীভাবে বদলায়। শুরুতে ওয়েটগুলো সাধারণত ছোট ছোট র্যান্ডম সংখ্যা দিয়ে শুরু হয় — মডেল আসলেই কিছু জানে না তখন। ট্রেনিং উদাহরণগুলো একে একে যাওয়ার সাথে সাথে মডেল একটা প্রেডিকশন করে, সেটাকে আসল উত্তরের সাথে মিলিয়ে দেখে, কতটা ভুল করেছে সেটা মাপে, তারপর প্রতিটা parameter-কে একটু একটু করে সেই দিকে সরায় যেদিকে সরালে ওই প্রেডিকশনটা আরেকটু কম ভুল হতো। হাজার হাজার উদাহরণ আর ডেটার উপর অনেকবার পাস করার পর, শুরুর র্যান্ডম সংখ্যাগুলো ধীরে ধীরে এমন মানে পৌঁছায় যেগুলো আসলেই একটা প্যাটার্ন ধরে ফেলেছে — "বড় বাসার দাম বেশি হতে থাকে" — এই জিনিসটা -এর ভেতরে সংখ্যা আকারে এনকোড হয়ে যায়।
নিজেকে জিজ্ঞেস করুন: "এই সংখ্যাটা কি ট্রেনিং চলার সময় নিজে থেকেই বদলায়, আমি হাত না দিলেও?" যদি হ্যাঁ হয়, এটা parameter। নিউরাল নেটওয়ার্কের ওয়েট আর বায়াস, লিনিয়ার রিগ্রেশনের কোয়েফিসিয়েন্ট, ডিসিশন ট্রি নিজে যে স্প্লিট থ্রেশহোল্ড খুঁজে বের করে — সবগুলোই parameter।
এবার একটু পেছনে যান, ট্রেনিং শুরু হওয়ারও আগে। মডেল একটা বাসার দামও দেখার আগে, কাউকে — একজন মানুষ, মানে আপনাকেই — কিছু সিদ্ধান্ত নিতে হয়: এই নিউরাল নেটওয়ার্কে কয়টা হিডেন লেয়ার থাকবে? প্রতিটা লেয়ারে কয়টা নিউরন থাকবে? প্রতি স্টেপে ওয়েট কতটা দ্রুত অ্যাডজাস্ট হবে? একবার আপডেট করার আগে কয়টা উদাহরণ দেখবে?
এই প্রশ্নগুলোর কোনোটাই ট্রেনিং চলাকালীন ডেটা দেখে বের করা যায় না, যেভাবে একটা parameter শেখা হয়। এগুলো ট্রেনিং শুরু হওয়ার আগেই ঠিক করতে হয়, আর (সবচেয়ে সাধারণ সেটআপে) পুরো ট্রেনিং রানের সময় একই থাকে। এগুলোই হলো hyperparameters: এমন সেটিং যেগুলো ঠিক করে কীভাবে শেখার প্রসেসটা চলবে — এগুলো নিজে শেখার প্রসেসের মধ্য দিয়ে শেখা হয় না।
বাসার দামের উদাহরণেই ফিরে যাই, কিন্তু এবার একটা সরল রেখার বদলে একটা ছোট নিউরাল নেটওয়ার্ক ট্রেন করার কথা ভাবুন। ট্রেনিং শুরুর আগেই আপনাকে ঠিক করতে হবে যেমন:
ট্রেনিং শুরুর আগে এগুলোর যেকোনো একটা বদলালেই একটা সম্পূর্ণ ভিন্ন ট্রেনিং রান পাবেন — হয়তো মডেল দ্রুত শিখবে, ধীরে শিখবে, ভালো শিখবে, বা একদমই শিখবে না। কিন্তু এগুলোর একটাও ডেটা থেকে আবিষ্কার হয় না, যেভাবে আর হয়েছিল। আপনি, যিনি এক্সপেরিমেন্টটা সেট আপ করছেন, এগুলো বেছে নেন।
অনেকে ভাবে "hyperparameter" মানে বুঝি "কম গুরুত্বপূর্ণ একটা parameter"। ব্যাপারটা তা না। এখানে "hyper" প্রিফিক্সটার মানে "উপরে" বা "নিয়ন্ত্রণকারী" — একটা hyperparameter parameter-এর এক লেভেল উপরে বসে, আর যে প্রসেস parameter তৈরি করে সেটাকেই নিয়ন্ত্রণ করে। Learning rate নিজে সরাসরি বাসার দাম প্রেডিক্ট করে না; এটা ঠিক করে আর কতটা আগ্রাসীভাবে আপডেট হবে, যখন মডেল বাসার দাম প্রেডিক্ট করা শিখছে।
Parameter আর hyperparameter-কে চিরদিনের মতো আলাদা করার সবচেয়ে পরিষ্কার উপায় একটা প্রশ্ন: এই মানটা কে ঠিক করে, আর কখন?
| Parameters | Hyperparameters | |
|---|---|---|
| কে ঠিক করে | ট্রেনিং অ্যালগরিদম (স্বয়ংক্রিয়ভাবে) | আপনি, ট্রেনিং শুরুর আগে |
| কখন ঠিক হয় | ট্রেনিংয়ের সময় — ডেটা যাওয়ার সাথে সাথে বারবার আপডেট হয় | ট্রেনিং শুরুর আগেই — সাধারণত পুরো রানের জন্য ফিক্সড থাকে |
| ডেটা থেকে শেখা? | হ্যাঁ — এটাই ট্রেনিংয়ের পুরো উদ্দেশ্য | না — এক্সপেরিমেন্ট, অভিজ্ঞতা, বা সার্চ করে বেছে নেওয়া হয় |
| উদাহরণ | ওয়েট, বায়াস, ডিসিশন ট্রির শেখা স্প্লিট থ্রেশহোল্ড | Learning rate, লেয়ার সংখ্যা, batch size, epoch সংখ্যা |
| কয়টা থাকে? | বড় মডেলে লক্ষ লক্ষ বা কোটি কোটি হতে পারে | সাধারণত অল্প কয়েকটা, সামলানোর মতো সংখ্যক |
আরেকভাবে দেখলে: hyperparameter হলো ট্রেনিং প্রসেসের নিয়ম, আর parameter হলো সেই নিয়ম মেনে চলার ফলাফল। Hyperparameter বদলালে পুরো ট্রেনিং প্রসেসটাই বদলে যায় — হয়তো একদম আলাদা শেখা parameter পাবেন, বা এমনও হতে পারে মডেল আসলে কিছুই শিখল না।
পাউরুটি বেক করার কথা ভাবুন। ওভেনের তাপমাত্রা আর কতক্ষণ বেক করবেন — এগুলো এমন সিদ্ধান্ত যা আপনি ডো ওভেনে দেওয়ার আগেই নেন — এগুলো hyperparameter। রুটির ভেতরের যে টেক্সচার আর গঠনটা তৈরি হয় বেক হতে হতে, সময়ের সাথে তাপের প্রভাবে স্বয়ংক্রিয়ভাবে — সেটাই ফলাফল, সেটাই parameter। ভুল ওভেন তাপমাত্রা (একটা খারাপ hyperparameter) বেছে নিলে রুটি পুড়ে যেতে পারে বা raw থেকে যেতে পারে, ময়দা যতই ভালো হোক না কেন।
একটা জিনিস স্পষ্ট করে বলা দরকার: একটা hyperparameter-কে parameter-এর মতো ট্রেন করা যায় না। কোনো গ্রেডিয়েন্ট নেই যেটা বলবে "লেয়ার সংখ্যা ০.৩ বাড়াও" — লেয়ার সংখ্যা একটা ডিসক্রিট, আর্কিটেকচারাল সিদ্ধান্ত, ক্যালকুলাস দিয়ে ধীরে ধীরে সরানো যায় এমন কোনো মসৃণ সংখ্যা না। ভালো hyperparameter মান খুঁজে বের করতে হয় একদম ভিন্ন ধরনের সার্চের মাধ্যমে — যেটাই পরের টপিক।
বিভিন্ন ধরনের মডেলে বিভিন্ন hyperparameter থাকে, কিন্তু কয়েকটা প্রায় প্রতিটা নিউরাল নেটওয়ার্ক ট্রেন করার সময়ই বারবার দেখা যায়:
Learning Rate
প্রতিটা ভুলের পর ওয়েট অ্যাডজাস্ট করার সময় মডেল কতটা বড় স্টেপ নেয়। খুব বেশি হলে ট্রেনিং লাফিয়ে লাফিয়ে চলে, স্থির হতে পারে না। খুব কম হলে ট্রেনিং এত ধীরে এগোয় যে মানানসই সময়ে প্রায় কোনো উন্নতিই দেখা যায় না।
Batch Size
একবার ওয়েট আপডেট করার আগে মডেল কয়টা ট্রেনিং উদাহরণ দেখে। ছোট batch বেশি বার আপডেট করে কিন্তু নয়েজি; বড় batch কম বার আপডেট করে কিন্তু বেশি স্মুথ (আর বেশি মেমরি লাগে)।
Epoch সংখ্যা
পুরো ট্রেনিং ডেটাসেটের উপর মডেল কতবার সম্পূর্ণ পাস করে। খুব কম হলে মডেল যথেষ্ট ডেটা দেখেনি ভালোভাবে শিখতে। খুব বেশি হলে মডেল সাধারণ প্যাটার্ন শেখার বদলে ট্রেনিং ডেটাই মুখস্থ করে ফেলতে পারে।
Hidden Layer সংখ্যা
নেটওয়ার্কটা কতটা গভীর। বেশি লেয়ার মডেলকে আরও জটিল প্যাটার্ন ধরার ক্ষমতা দেয়, কিন্তু ট্রেনিং কঠিন আর ধীর করে দেয়, আর overfitting-এর ঝুঁকিও বাড়ায়।
প্রতি Layer-এ Neuron সংখ্যা
প্রতিটা লেয়ার কতটা চওড়া। বেশি নিউরন মানে মডেলের প্যাটার্ন ধরার ক্ষমতা বেশি, কিন্তু তার সাথে হিসাব বেশি লাগে আর মুখস্থ করে ফেলার (generalize না করার) সম্ভাবনাও বাড়ে।
Regularization Strength
কোনো একটা ওয়েটের উপর বেশি নির্ভর করলে মডেলকে কতটা কঠোরভাবে শাস্তি দেওয়া হয় — এটা ট্রেনিং ডেটা মুখস্থ করাকে নিরুৎসাহিত করে আর নতুন, অদেখা উদাহরণে ভালো পারফর্ম করতে সাহায্য করে।
এই ছয়টা সংখ্যার একটাও কখনো gradient descent আবিষ্কার করে না। প্রত্যেকটাই একটা ডায়াল, যেটা একজন মানুষ (বা একটা স্বয়ংক্রিয় সার্চ প্রসেস) ট্রেনিং শুরুর আগে সেট করে দেয় আর সাধারণত ওই রানের জন্য আর হাত দেয় না।
মডেল ট্রেন করার কোড লিখতে গেলে, hyperparameter প্রায় সবসময়ই স্ক্রিপ্টের শুরুর দিকে যে আর্গুমেন্টগুলো পাস করেন সেগুলো — যেমন learning_rate=0.001, batch_size=32, epochs=50। অন্যদিকে parameter থাকে মডেল অবজেক্টের ভেতরেই, আর ট্রেনিং লুপে আপডেট হতে থাকে — এদের মান আপনি কখনো নিজে হাতে টাইপ করেন না।
যেহেতু hyperparameter স্বয়ংক্রিয়ভাবে শেখা যায় না, তাহলে এদের জন্য ভালো মান কীভাবে খুঁজে বের করবেন? এটাই hyperparameter tuning-এর কাজ (কখনো কখনো hyperparameter optimization-ও বলে): নিয়মমাফিকভাবে বিভিন্ন hyperparameter সেটিং চেষ্টা করা, প্রতিটা দিয়ে একটা মডেল ট্রেন করা, আর প্রতিটা ফলাফল মডেল কতটা ভালো পারফর্ম করছে সেটা তুলনা করা।
সবচেয়ে সরাসরি পদ্ধতি হলো manual tuning — একজন অভিজ্ঞ প্র্যাকটিশনার নিজের বোঝাপড়া আর আগের অভিজ্ঞতা থেকে একটা learning rate বেছে নেয়, মডেল ট্রেন করে, ফলাফল দেখে, আর অ্যাডজাস্ট করে। যথেষ্ট মডেল ট্রেন করলে এটা মোটামুটি ভালোই কাজ করে, কিন্তু এটা বড় স্কেলে কাজ করে না, আর এমন কম্বিনেশন মিস করে যেতে পারে যা একজন মানুষ চেষ্টাই করত না।
আরও নিয়মমাফিক পদ্ধতি সম্ভাব্য hyperparameter মানের পুরো স্পেসটা স্বয়ংক্রিয়ভাবে সার্চ করে:
Grid Search
প্রতিটা hyperparameter-এর জন্য একটা ফিক্সড লিস্ট বেছে নিন (যেমন learning rate: 0.1, 0.01, 0.001) আর সম্ভাব্য প্রতিটা কম্বিনেশন চেষ্টা করুন, সম্পূর্ণভাবে। নির্ভরযোগ্য, কিন্তু hyperparameter-এর সংখ্যা বাড়লে খুব দ্রুত খরচ বেড়ে যায়।
Random Search
প্রতিটা কম্বিনেশন চেষ্টা করার বদলে, সম্ভাব্য রেঞ্জ থেকে র্যান্ডমলি কম্বিনেশন বেছে নিন। অবাক করার মতো হলেও, প্রায়ই grid search-এর চেয়ে দ্রুত একটা ভালো কম্বিনেশন খুঁজে পায়, কারণ এটা এমন জায়গায় সময় নষ্ট করে না যেখানে grid তবুও ঢুঁ মারতে যেত।
Bayesian Optimization
আগের ট্রেনিং রানগুলোর ফলাফল ব্যবহার করে বুদ্ধিমানের মতো অনুমান করা কোন না-চেষ্টা-করা কম্বিনেশনটা ভালো পারফর্ম করার সম্ভাবনা বেশি, আর পরেরবার ঠিক সেটাই চেষ্টা করা — অন্ধভাবে সার্চ না করে আগের চেষ্টা থেকে শিখে এগোনো।
যে সার্চ স্ট্র্যাটেজিই ব্যবহার করা হোক না কেন, ভেতরের ওয়ার্কফ্লোটা সবসময় একই লুপ: একটা hyperparameter সেটিং বেছে নিন → সেটা দিয়ে একটা পুরো মডেল ট্রেন করুন → সেই মডেলকে এমন ডেটার উপর যাচাই করুন যা ট্রেনিংয়ের সময় দেখেনি → কতটা ভালো করল সেটা রেকর্ড করুন → আরেকটা সেটিং চেষ্টা করুন → পুনরাবৃত্তি করুন। যে সেটিং অদেখা ডেটার উপর সবচেয়ে ভালো পারফর্মিং মডেল দেয়, সেটাই রেখে দিন।
Hyperparameter tuning সবসময় একটা আলাদা validation set-এর উপর যাচাই করতে হয়, ফাইনাল test set-এর উপর না — যেই test set আপনি মডেলের আসল-জগতের পারফরম্যান্স রিপোর্ট করার জন্য রাখবেন। যদি test set-এর স্কোর ভালো না হওয়া পর্যন্ত বারবার hyperparameter অ্যাডজাস্ট করতে থাকেন, তাহলে আসলে test set-টাই ট্রেনিং সিদ্ধান্তে ঢুকে পড়ছে (leak হচ্ছে) — রিপোর্ট করা পারফরম্যান্স বাস্তবের চেয়ে বেশি ভালো দেখাবে, আর সত্যিকারের নতুন ডেটায় সেটা টিকবে না।
Tuning-ও ব্যয়বহুল: প্রতিটা সম্ভাব্য hyperparameter সেটিং মানে সেটাকে যাচাই করার জন্য পুরো একটা মডেল একদম শুরু থেকে ট্রেন করা, আর বড় মডেল একবার ট্রেন করতেই ঘণ্টার পর ঘণ্টা বা দিনের পর দিন লাগতে পারে। এই কারণেই random search আর Bayesian optimization-এর মতো স্মার্ট সার্চ স্ট্র্যাটেজি গুরুত্বপূর্ণ — এগুলো চায় exhaustive grid search-এর চেয়ে অনেক কম সংখ্যক পুরো ট্রেনিং রানে ভালো সেটিং খুঁজে পেতে।
কয়েকটা ভিন্ন ধরনের মডেলের মধ্যে দিয়ে এই পার্থক্যটা দেখলে যেকোনো একটা উদাহরণের চেয়ে মাথায় অনেক বেশি ভালোভাবে গেঁথে যায়।
| মডেল | Parameters (শেখা হয়) | Hyperparameters (আগে থেকে ঠিক করা) |
|---|---|---|
| Linear Regression | ফিট করা লাইনের স্লোপ আর ইন্টারসেপ্ট | কোন regularization পদ্ধতি ব্যবহার হবে, আর সেটা কতটা কড়া হবে |
| Neural Network (ইমেজ ক্লাসিফায়ার) | সব লেয়ার জুড়ে প্রতিটা ওয়েট আর বায়াস, প্রায়ই লক্ষ লক্ষ সংখ্যা | লেয়ার সংখ্যা, প্রতি লেয়ারে নিউরন, learning rate, batch size, epoch |
| Decision Tree | প্রতিটা স্প্লিটে বেছে নেওয়া নির্দিষ্ট ফিচার আর থ্রেশহোল্ড | সর্বোচ্চ ট্রি ডেপথ, একটা নোড স্প্লিট করতে ন্যূনতম কয়টা স্যাম্পল লাগবে |
| k-Nearest Neighbors | কিছুই না — এই মডেলের আসলে কোনো trainable parameter নেই | k-এর মান (প্রেডিক্ট করার সময় কয়টা প্রতিবেশী দেখবে) |
k-Nearest Neighbors-এর সারিটা একটু থেমে ভাবার মতো, কারণ এটা সত্যিকারের একটা কাজের এজ কেস: কিছু মডেলের hyperparameter আছে কিন্তু কার্যত কোনো trainable parameter নেই। k-NN আসলে "শেখে" না, ভেতরের কোনো সংখ্যা অ্যাডজাস্ট করার অর্থে — এটা শুধু ট্রেনিং ডেটা মনে রাখে, আর প্রেডিকশনের সময় সবচেয়ে কাছের টা স্টোর করা উদাহরণ দেখে সিদ্ধান্ত নেয়। একটা hyperparameter, যা আপনি বেছে নেন; এর ভেতরে আর -এর মতো টিউন করার কিছু নেই।
এবার একটু কাছের একটা উদাহরণে ফিরে আসি: ধরুন একটা টিম এমন একটা সিস্টেম বানাচ্ছে যেটা শিক্ষার্থীদের আগের গ্রেড আর আগ্রহ দেখে ইউনিভার্সিটি কোর্স সাজেস্ট করে। ওই সাজেশন মডেলের ভেতরের ওয়েটগুলো — "ক্যালকুলাসে ভালো করা" কতটা জোরে "লিনিয়ার অ্যালজেব্রা" সাজেস্ট করার দিকে টানে — এগুলো হলো parameter, যা মডেল হাজার হাজার আগের শিক্ষার্থীর রেকর্ড থেকে নিজে বের করে। কিন্তু কোনো ট্রেনিং শুরু হওয়ার আগেই টিমকে ঠিক করতে হয়, তাদের মডেলে কয়টা লেয়ার থাকবে, এটা কতটা দ্রুত শিখবে, আর একবার আপডেট করার আগে কয়টা শিক্ষার্থীর রেকর্ড প্রসেস করবে। Hyperparameter খারাপভাবে ভুল বসালে, একটা নিখুঁত ডেটাসেট থাকলেও এমন মডেল পাবেন না যেটা কাজের কিছু শিখেছে — parameter-গুলো সঠিক মানে পৌঁছানোর সুযোগই পায় না।