Chapter 14 of 18
Smarter ways to walk downhill than plain gradient descent
Gradient & Gradient Descent চ্যাপ্টার মূল ট্রেনিং লুপ প্রতিষ্ঠা করেছিল: একটা gradient হিসাব করো, প্রতিটা weight থেকে এর একটা ছোট গুণক বিয়োগ করো, পুনরাবৃত্তি করো। Gradient descent-এর এই সাধারণ ভার্সনটা কাজ করে, কিন্তু এর একটা আসল দুর্বলতা আছে — এটা প্রতিটা পদক্ষেপকে একইভাবে দেখে, আগের পদক্ষেপে কী ঘটেছিল তার কোনো স্মৃতি ছাড়াই আর ট্রেনিং এগোনোর সাথে সাথে অভিযোজিত হওয়ার কোনো উপায় ছাড়াই।
একটা optimizer হলো সেই অ্যালগরিদম যা ট্রেনিংয়ের সময় এই weight-আপডেট প্রক্রিয়াটা আসলে ইমপ্লিমেন্ট করে। সাধারণ gradient descent হলো সবচেয়ে সহজ সম্ভাব্য optimizer, কিন্তু এটাই একমাত্র না — বছরের পর বছর ধরে, গবেষকরা ক্রমশ চালাক optimizer বানিয়েছেন যা অতিরিক্ত তথ্য (যেমন সাম্প্রতিক gradient-এর ইতিহাস) ব্যবহার করে শুধু সাধারণ gradient descent-এর চেয়ে বেশি চালাক, দ্রুত, স্থিতিশীল পদক্ষেপ নেয়।
একটা optimizer হলো সেই নির্দিষ্ট অ্যালগরিদম যা একটা মডেলের weight-কে এর gradient থেকে আপডেট করতে ব্যবহৃত হয় — সাধারণ gradient descent হলো সবচেয়ে সহজ উদাহরণ, কিন্তু বেশিরভাগ আধুনিক ট্রেনিং এর উপর ভিত্তি করে বানানো আরও পরিশীলিত optimizer ব্যবহার করে।
সাধারণ gradient descent-এর দুইটা নির্দিষ্ট দুর্বলতা এই চ্যাপ্টারে দেখানো প্রায় প্রতিটা উন্নতির প্রেরণা। প্রথমত, সাধারণ gradient descent এমন loss ল্যান্ডস্কেপে কষ্টদায়কভাবে ধীর বা অস্থির হতে পারে যা কিছু দিকে খাড়া আর অন্য দিকে প্রায় সমতল — একটা মাত্র learning rate-কে দুইটার মধ্যে আপোস করতে হয়, যেমনটা Learning Rate চ্যাপ্টারে দেখা হয়েছে। দ্বিতীয়ত, সাধারণ gradient descent-এর "মনে রাখার" কোনো উপায় নেই যে এটা বেশ কয়েকটা পদক্ষেপ ধরে মোটামুটি একই দিকে ধারাবাহিকভাবে এগোচ্ছে, তাই এটা একটা বল পাহাড় থেকে গড়িয়ে নামার মতো স্বাভাবিকভাবে গতি তৈরি করতে পারে না।
সাধারণ gradient descent যদি এমন একজন হাইকার হয় যে পরের পদক্ষেপ ঠিক করার আগে শুধু পায়ের ঠিক নিচের মাটিই দেখে, আরও উন্নত optimizer হলো সেই হাইকার যারা এটাও মনে রাখে তারা সাধারণত কোন দিকে এগোচ্ছিল, আর সাম্প্রতিক ভূখণ্ডটা কতটা এবড়োখেবড়ো বা মসৃণ ছিল তার উপর ভিত্তি করে তাদের পদক্ষেপ অ্যাডজাস্ট করে।
Stochastic Gradient Descent (SGD), যা Gradient & Gradient Descent চ্যাপ্টারে পরিচয় করানো হয়েছিল, হলো সেই বেসলাইন optimizer যার উপর এই চ্যাপ্টারের বাকি সবগুলো তৈরি। প্রতিটা পদক্ষেপ সহজ: একটা mini-batch থেকে gradient হিসাব করো, তারপর প্রতিটা weight-কে সরাসরি সেই gradient-এর বিপরীতে আপডেট করো, learning rate দিয়ে স্কেল করে।
এখানে হলো পদক্ষেপ -তে weight-এর মান, হলো সেই পদক্ষেপে হিসাব করা gradient, আর হলো learning rate। এখানে আগের পদক্ষেপের কোনো স্মৃতিই নেই — প্রতিটা আপডেট শুধু বর্তমান gradient-এর উপর নির্ভর করে।
শক্তি: সহজ আর ভালোভাবে বোঝা
SGD নিয়ে যুক্তি করা সহজ, হিসাব করা সস্তা, আর এর পেছনে কয়েক দশকের তাত্ত্বিক আর ব্যবহারিক ভিত্তি আছে।
দুর্বলতা: পদক্ষেপ জুড়ে কোনো স্মৃতি নেই
SGD বলতে পারে না এটা বারবার একটা ধারাবাহিক দিকে এগোচ্ছে, তাই এটা দ্রুত হওয়ার জন্য সেই ধারাবাহিকতার সুবিধা নিতে পারে না।
দুর্বলতা: অসমান ল্যান্ডস্কেপে সংগ্রাম করে
এমন loss ল্যান্ডস্কেপে যা এক দিকে খাড়া আর আরেক দিকে অগভীর (আসল নিউরাল নেটওয়ার্কে সাধারণ), সাধারণ SGD খাড়া দিকে দোলার প্রবণতা রাখে যখন অগভীর দিকে ধীরে হামাগুড়ি দেয়।
SGD প্রতিটা weight শুধু বর্তমান gradient ব্যবহার করে আপডেট করে, learning rate দিয়ে স্কেল করে — কোনো ইতিহাস না, কোনো অভিযোজন না, Gradient & Gradient Descent চ্যাপ্টারের আপডেট নিয়মের সরাসরি ইমপ্লিমেন্টেশন।
Momentum SGD-এর স্মৃতির অভাব ঠিক করে সাম্প্রতিক gradient-এর একটা চলমান গড় রেখে, আর সেই গড়টা — শুধু বর্তমান gradient না — weight আপডেট করতে ব্যবহার করে।
এখানে হলো "ভেলোসিটি" — অতীত gradient-এর একটা চলমান, এক্সপোনেনশিয়ালি-ওয়েটেড গড় — আর (সাধারণত ০.৯-এর কাছাকাছি) নিয়ন্ত্রণ করে অতীত gradient বর্তমান পদক্ষেপকে কতটা প্রভাবিত করতে থাকে বনাম নতুন gradient কতটা গুরুত্ব পায়। একটা উচ্চ মানে momentum অনেক আগের পদক্ষেপ "মনে রাখে"; একটা নিম্ন মানে এটা সাধারণ SGD থেকে প্রায় আলাদা না।
Gradient & Gradient Descent চ্যাপ্টারের বাটি-আকৃতির loss ল্যান্ডস্কেপ থেকে একটা বল নিচে গড়িয়ে পড়ার কথা কল্পনা করুন। সাধারণ SGD একটা ভরহীন বলের মতো, শুধু বর্তমান ঢালের উপর ভিত্তি করে তাৎক্ষণিকভাবে থেমে যায় আর দিক বদলায়। Momentum বলটাকে আসল ভর আর ভেলোসিটি দেয় — এটা মোটামুটি যে দিকে এগোচ্ছিল সেই দিকেই এগোতে থাকে, নতুন gradient জমা হওয়ার সাথে সাথে শুধু ধীরে ধীরে দিক বদলায়, আর এটা ছোট বাধা আর সমতল অঞ্চলের মধ্য দিয়ে গড়িয়ে যেতে পারে যা সাধারণ SGD-কে থামিয়ে দিত।
এটা সরাসরি SGD-এর দ্বিতীয় দুর্বলতাটা সমাধান করে: এমন একটা loss ল্যান্ডস্কেপে যা এক দিকে খাড়া আর আরেক দিকে অগভীর, খাড়া দিক জুড়ে সামনে-পেছনে নির্দেশ করা gradient চলমান গড়ে একে অপরকে বাতিল করতে থাকে, যখন অগভীর দিক জুড়ে ধারাবাহিকভাবে একই দিকে নির্দেশ করা gradient একে অপরকে শক্তিশালী করে — নেট প্রভাব হলো একটা পথ যা কম দোলে আর আসলে যে দিকটা গুরুত্বপূর্ণ সেই দিকে দ্রুত এগোয়।
Gradient & Gradient Descent চ্যাপ্টার থেকে মনে করুন যে অগভীর local minima আর saddle point এমন অঞ্চল তৈরি করে যেখানে gradient প্রায়-শূন্য। যেহেতু momentum আগের পদক্ষেপ থেকে ভেলোসিটি সামনে বহন করে, এটা জমা হওয়া গতি ব্যবহার করে এই সমতল অঞ্চলগুলোর মধ্য দিয়ে একটা ছোট দূরত্ব গড়িয়ে যেতে পারে, বর্তমান gradient প্রায় শূন্যে নেমে আসার মুহূর্তেই থেমে যাওয়ার বদলে।
Momentum আপডেটের দিক মসৃণ করে gradient-এর একটা চলমান গড় ব্যবহার করে। RMSProp (Root Mean Square Propagation) একটা ভিন্ন কিন্তু পরিপূরক পদ্ধতি নেয়: এটা প্রতিটা সিঙ্গেল weight-এর জন্য আলাদাভাবে কার্যকর learning rate অভিযোজিত করে, সেই weight-এর সাম্প্রতিক gradient সাধারণত কতটা বড় ছিল তার উপর ভিত্তি করে।
এখানে হলো বর্গ করা gradient-এর একটা চলমান গড় (সবসময় পজিটিভ, দিকের বদলে gradient-এর ম্যাগনিটিউড ট্র্যাক করে), আর হলো একটা ক্ষুদ্র ধ্রুবক যা শুধু শূন্য দিয়ে ভাগ এড়াতে যোগ করা। মূল আইডিয়া: learning rate-কে দিয়ে ভাগ করলে এমন weight-এর জন্য কার্যকর পদক্ষেপ সাইজ ছোট হয়ে যায় যাদের সাম্প্রতিক gradient বড় ছিল, আর এমন weight-এর জন্য কার্যকর পদক্ষেপ সাইজ বাড়ে যাদের সাম্প্রতিক gradient ছোট ছিল।
একটা নেটওয়ার্কের বিভিন্ন weight-এর খুবই ভিন্নভাবে স্কেল করা gradient থাকতে পারে — নেটওয়ার্কের কিছু অংশে বড় পদক্ষেপ দরকার হতে পারে, অন্যদের ছোট, সতর্ক পদক্ষেপ দরকার, আর এটা ট্রেনিং এগোনোর সাথে সাথে বদলাতেও পারে। প্রতিটা weight-এ একইভাবে প্রয়োগ করা একটা সিঙ্গেল ফিক্সড learning rate, সাধারণ SGD-এর মতো, এটা হিসাবে নিতে পারে না — RMSProp কার্যকরভাবে প্রতিটা weight-কে নিজস্ব অভিযোজিত learning rate দেয়।
RMSProp প্রতিটা ইন্ডিভিজুয়াল weight-এর জন্য কার্যকর learning rate অভিযোজিত করে সেই weight-এর gradient-এর সাম্প্রতিক ম্যাগনিটিউডের উপর ভিত্তি করে, ছোট, ধারাবাহিক gradient-সহ weight-এর জন্য বড় পদক্ষেপ নেয় আর বড় বা নয়েজি gradient-সহ weight-এর জন্য ছোট পদক্ষেপ নেয়।
Adam (Adaptive Moment Estimation) momentum-এর gradient-এর একটা চলমান গড় ট্র্যাক করার আইডিয়াকে RMSProp-এর প্রতি-weight পদক্ষেপ সাইজ অভিযোজিত করার আইডিয়ার সাথে মেলায় — বাস্তবে এটাকে আজকের deep learning-এ সবচেয়ে ব্যাপকভাবে ব্যবহৃত optimizer বানিয়ে দেয়।
এখানে momentum-এর ভেলোসিটির মতো একই ভূমিকা পালন করে — নিজে gradient-এর একটা চলমান গড় — আর RMSProp-এর -এর মতো একই ভূমিকা পালন করে — বর্গ করা gradient-এর একটা চলমান গড়। Adam সাধারণত আর ব্যবহার করে। আপডেট দিকটা আসে মসৃণ করা momentum টার্ম থেকে, আর কার্যকর পদক্ষেপ সাইজ প্রতি weight-এ ব্যবহার করে অভিযোজিত হয়, ঠিক RMSProp-এর মতো।
| Optimizer | দিকের ইতিহাস ট্র্যাক করে? | প্রতি weight-এ পদক্ষেপ সাইজ অভিযোজিত করে? |
|---|---|---|
| SGD | না | না |
| Momentum | হ্যাঁ | না |
| RMSProp | না | হ্যাঁ |
| Adam | হ্যাঁ | হ্যাঁ |
Adam momentum-এর মসৃণ করা gradient দিককে RMSProp-এর প্রতি-weight অভিযোজিত পদক্ষেপ সাইজের সাথে মেলায়, একে অসমান loss ল্যান্ডস্কেপ আর খুবই ভিন্নভাবে স্কেল করা gradient-সহ weight দুটোর জন্যই রোবাস্ট করে।
Adam ব্যাপক টিউনিং ছাড়াই বিভিন্ন প্রবলেম জুড়ে মোটামুটি ভালো কাজ করার প্রবণতা রাখে, এটাই ঠিক কেন একটা নতুন প্রজেক্ট শুরু করার সময় মানুষ প্রায়ই প্রথমেই এই optimizer-টা বেছে নেয় — এটা momentum আর RMSProp দুটোর সুবিধাই উত্তরাধিকার সূত্রে পায়, আর সাধারণত ভালো ফলাফল পেতে সাধারণ SGD-এর চেয়ে কম সাবধানী learning-rate টিউনিং দরকার হয়।
AdamW হলো Adam-এর একটা পরিবর্তন যা Adam আর weight decay-এর মধ্যে একটা সূক্ষ্ম কিন্তু গুরুত্বপূর্ণ ইন্টারঅ্যাকশন ঠিক করে — একটা সাধারণ regularization টেকনিক যা weight-কে খুব বড় হতে নিরুৎসাহিত করে, যা overfitting প্রতিরোধ করতে সাহায্য করে।
Weight decay কাজ করে প্রতিটা weight-এর নিজের সাইজের সমানুপাতিক একটা ছোট শাস্তি সরাসরি আপডেটে যোগ করে, gradient যা করতে বলে তার পাশাপাশি প্রতিটা পদক্ষেপে আলতোভাবে প্রতিটা weight-কে শূন্যের দিকে টেনে। সাধারণ SGD-তে, এটা সঠিকভাবে যোগ করা সরল। মূল Adam-এ, তবে, SGD-এর মতো একই পদ্ধতিতে গাণিতিকভাবে weight decay যোগ করলে এমন একটা আপডেট তৈরি হয় যা RMSProp থেকে Adam-এর প্রতি-weight অভিযোজিত স্কেলিংয়ের সাথে অস্বস্তিকরভাবে ইন্টারঅ্যাক্ট করে — decay অনিচ্ছাকৃতভাবে একই টার্ম দিয়ে স্কেল হয়ে যায় যা gradient পদক্ষেপ অভিযোজিত করার জন্য, decay পদক্ষেপের জন্য না।
যেহেতু Adam-এর অভিযোজিত স্কেলিং আর weight decay একসাথে জড়িয়ে ছিল, Adam-এ weight decay-এর "নিষ্পাপ" ভার্সন দিয়ে ট্রেন করা মডেল প্রায়ই weight decay যে regularization সুবিধা দেওয়ার কথা সেটা পেত না, বিশেষ করে যেসব weight-এর মান বেশি জমা হয়েছে তাদের জন্য। শুধু সূত্র দেখেই এটা স্পষ্ট ছিল না — এটা চিহ্নিত করতে সাবধানী বিশ্লেষণ লেগেছিল।
AdamW-এর সমাধান হলো weight decay টার্মকে (যেখানে decay-এর শক্তি নিয়ন্ত্রণ করে) Adam-এর অভিযোজিত gradient স্কেলিং থেকে "ডিকাপল" করা — decay নিজস্ব আলাদা, সরল বিয়োগ হিসেবে প্রয়োগ হয়, gradient নিজের মতো একই অভিযোজিতভাবে-স্কেল করা টার্মে মেশানো না।
AdamW হলো Adam যেখানে weight decay একটা আলাদা, সরাসরি পদক্ষেপ হিসেবে প্রয়োগ হয় Adam-এর অভিযোজিত gradient স্কেলিং-এর সাথে মেশানোর বদলে, একটা ইন্টারঅ্যাকশন ঠিক করে যা সাধারণ Adam-এর regularization-কে যতটা কার্যকর হওয়ার কথা তার চেয়ে কম কার্যকর করে দিত।
এই সমাধানটা বাস্তবে যথেষ্ট গুরুত্বপূর্ণ যে AdamW অনেক আধুনিক ট্রেনিং সেটআপে সাধারণ Adam-এর জায়গায় বেশিরভাগ ক্ষেত্রে ডিফল্ট পছন্দ হয়ে উঠেছে, বিশেষ করে বড় মডেলের জন্য যেখানে overfitting প্রতিরোধে regularization একটা গুরুত্বপূর্ণ ভূমিকা পালন করে।
এখন টেবিলে বেশ কয়েকটা optimizer থাকায়, একটা নির্দিষ্ট প্রবলেমের জন্য একটা বেছে নেওয়া কয়েকটা ব্যবহারিক বিবেচনায় নেমে আসে।
| Optimizer | কখন এটা একটা ভালো পছন্দ | ট্রেড-অফ |
|---|---|---|
| SGD (সাধারণ বা momentum-সহ) | সহজ প্রবলেম, বা যখন এমন প্রতিষ্ঠিত রেসিপি অনুসরণ করা হয় যা বিশেষভাবে SGD-এর জন্য টিউন করা | সাধারণত অভিযোজিত পদ্ধতির চেয়ে বেশি সাবধানী learning-rate টিউনিং দরকার |
| RMSProp | বিভিন্ন weight জুড়ে খুবই অসমানভাবে স্কেল করা gradient-সহ প্রবলেম | বেশিরভাগ আধুনিক প্র্যাকটিসে মূলত Adam দিয়ে প্রতিস্থাপিত |
| Adam | একটা শক্তিশালী সাধারণ-উদ্দেশ্য ডিফল্ট, বিশেষ করে একটা প্রজেক্টের প্রথম দিকে বা যখন ব্যাপক টিউনিংয়ের সময় সীমিত | কিছু প্রবলেম টাইপে, ভালোভাবে-টিউন করা SGD-with-momentum-এর চেয়ে কখনো কখনো সামান্য খারাপ generalize করতে পারে |
| AdamW | Adam-এর মতো একই পরিস্থিতি, কিন্তু বিশেষভাবে যখন weight decay/regularization গুরুত্বপূর্ণ, যেমন বড় আধুনিক মডেলে | বিবেচনার জন্য একটা অতিরিক্ত hyperparameter (decay শক্তি) |
সত্যিই অনিশ্চিত থাকলে, এর সাধারণভাবে সুপারিশ করা ডিফল্ট সেটিং-সহ AdamW আজকের বেশিরভাগ deep learning প্রবলেমের জন্য একটা যুক্তিসঙ্গত শুরুর বিন্দু। যদি AdamW দিয়ে ট্রেনিং অস্থির হয় বা স্পষ্টভাবে খারাপ পারফর্ম করে, এটা আরও তদন্ত করার একটা সংকেত — সম্পূর্ণভাবে optimizer বদলানোর আগে, Learning Rate চ্যাপ্টার অনুযায়ী প্রথমে learning rate চেক করুন।
Optimizer বদলানো একটা খারাপভাবে বেছে নেওয়া learning rate, একটা অস্থির loss function, বা একটা সত্যিকারের কঠিন প্রবলেম ঠিক করার বিকল্প না। একটা optimizer বদলায় কীভাবে gradient weight আপডেটে পরিণত হয়, কিন্তু এটা এমন কার্যকর gradient তথ্য তৈরি করতে পারে না যা প্রথম জায়গায় সেখানে নেই — loss ল্যান্ডস্কেপ বা ডেটাতে ট্রেস করা সমস্যা একটা বেশি ফ্যান্সি optimizer চেষ্টা করে সমাধান হবে না।