Chapter 11 of 18
The one number that decides whether training crawls, learns, or blows up
Gradient & Gradient Descent চ্যাপ্টার সেই আপডেট নিয়মটা পরিচয় করিয়ে দিয়েছিল যা ট্রেনিং চালায়: প্রতিটা weight থেকে gradient-এর একটা ছোট গুণক বিয়োগ করা, বারবার, যতক্ষণ না loss একটা মিনিমামের কাছাকাছি থিতু হয়। সেই "ছোট গুণকটা" নিয়ন্ত্রিত হয় একটা মাত্র সংখ্যা দিয়ে — learning rate, সাধারণত লেখা হয় (গ্রিক অক্ষর eta)।
Gradient আপনাকে বলে কোন দিক loss কমায়। Learning rate আপনাকে বলে প্রতিটা পদক্ষেপে সেই দিকে কতটা দূরে যেতে হবে। এই একটা সংখ্যা ভুল করলে, gradient-এর দিকটা যতই সঠিক হোক না কেন কোনো ব্যাপার না — ট্রেনিং তখনও সম্পূর্ণভাবে ব্যর্থ হতে পারে।
Learning rate হলো একটা hyperparameter যা gradient descent-এর সময় প্রতিটা weight আপডেটের সাইজ স্কেল করে — ছোট মান মানে ছোট ছোট পদক্ষেপ, বড় মান মানে বড় বড় পদক্ষেপ।
যেকোনো নিউরাল নেটওয়ার্ক ট্রেন করার সবচেয়ে গুরুত্বপূর্ণ সিদ্ধান্তগুলোর একটা এই একটা মাত্র সংখ্যা। একটা weight বা bias-এর মতো না, এটা কখনো ডেটা থেকে স্বয়ংক্রিয়ভাবে শেখা হয় না (যেমনটা Parameters vs Hyperparameters চ্যাপ্টারে দেখা হয়েছে) — একজন মানুষ, বা একটা স্বয়ংক্রিয় সার্চ প্রসেস, ট্রেনিং শুরুর আগেই এটা সেট করে দিতে হয়। আর এর প্রভাব সূক্ষ্ম না: একই ডেটার উপর হিসাব করা ঠিক একই gradient, এমন একটা মডেলে নিয়ে যেতে পারে যা সুন্দরভাবে ট্রেন হয়, অথবা এমন একটা মডেলে যা কখনো কার্যকর কিছুই শেখে না, সম্পূর্ণভাবে নির্ভর করে এই একটা সেটিংয়ের উপর।
Gradient descent-কে কুয়াশায় নিচের দিকে হাঁটা হিসেবে কল্পনা করুন, প্রতিটা জায়গায় পায়ের নিচে ঢাল অনুভব করে। Learning rate হলো আপনার এক পদক্ষেপের দৈর্ঘ্য। একটা ছোট পদক্ষেপ আপনাকে নিরাপদে কিন্তু ধীরে নিচে নামায়। একটা বড় পদক্ষেপ হয়তো আপনাকে তলা সম্পূর্ণ পার করে নিয়ে যেতে পারে, বা এমনকি অন্যপাশের একটা খাদে ফেলে দিতে পারে।
Learning rate খুব ছোট হলে, প্রতিটা আপডেট পদক্ষেপ weight-কে প্রায় নড়ায়ই না। দিকটা সঠিক — gradient তখনও সঠিকভাবে নিচের দিকে নির্দেশ করে — কিন্তু প্রতি পদক্ষেপে কভার করা দূরত্বটা খুবই ছোট।
সবচেয়ে তাৎক্ষণিক ফলাফল হলো গতি: যে ট্রেনিং একটা ভালো সমাধানে পৌঁছাতে কয়েকশো পদক্ষেপ নেওয়ার কথা, সেটা তার বদলে দশ হাজার পদক্ষেপ নিতে পারে, প্রতিটা weight-কে প্রায় অদৃশ্য পরিমাণে ঠেলে দিয়ে। ব্যবহারিক অর্থে, এটা প্রায়ই এমন একটা loss কার্ভের মতো দেখায় যা কমছে, কিন্তু কষ্টদায়কভাবে ধীরে — কিছুক্ষণ দেখার পর, ভাবা সহজ হয়ে যায় ট্রেনিং আদৌ কিছু করছে কিনা।
একটা খুব ছোট learning rate একটা সূক্ষ্ম ফাঁদ তৈরি করে: যদি ট্রেনিং একটা ফিক্সড সংখ্যক পদক্ষেপের পর থামানো হয় (যা বাস্তবে প্রায় সবসময়ই হয়), তাহলে একটা খুব-ছোট learning rate-সহ একটা মডেল হয়তো একটা ভালো সমাধানের কাছাকাছিও পৌঁছাতে পারেনি — পদ্ধতিটা ভুল বলে না, বরং এটা যথেষ্ট পদক্ষেপ পায়নি সেই দূরত্ব পার করতে যা একটা বড় পদক্ষেপ সাইজ পার করে দিত।
ছোট learning rate-এর একটা দ্বিতীয়, কম স্পষ্ট ঝুঁকি আছে: আটকে যাওয়া। Gradient & Gradient Descent চ্যাপ্টার থেকে মনে করুন যে saddle point আর অগভীর local minima এমন অঞ্চল তৈরি করে যেখানে gradient খুব ছোট। একটা ছোট learning rate ইতিমধ্যে-ছোট একটা gradient-এর সাথে মিলে আরও ছোট একটা পদক্ষেপ তৈরি করে — ট্রেনিং প্রায় থেমে যেতে পারে ঠিক সেই সমতল অঞ্চলগুলোতেই যেখানে একটু বেশি "momentum" এটাকে পার করে দিত।
একটা ছোট learning rate ছোট, সতর্ক পদক্ষেপ তৈরি করে — সঠিক কিন্তু ধীর, আর loss ল্যান্ডস্কেপের সমতল অঞ্চলে প্রায়-থেমে-যাওয়ার প্রবণ।
Learning rate খুব বড় হলে, প্রতিটা পদক্ষেপ ওভারশুট করে — loss ল্যান্ডস্কেপের একটা নিচু বিন্দুতে ধীরে থিতু হওয়ার বদলে, weight-গুলো সেটা লাফিয়ে পার হয়ে যায়, প্রায়ই পদক্ষেপ নেওয়ার আগের চেয়ে বেশি loss-সহ কোথাও গিয়ে পড়ে।
দৃশ্যমান লক্ষণটা সাধারণত একটা loss কার্ভ যা মসৃণভাবে কমার বদলে বন্যভাবে লাফিয়ে বেড়ায় — কখনো কমে, তারপর আবার লাফিয়ে উঠে, তারপর আবার কমে, কোনো স্পষ্ট ধারাবাহিক প্রবণতা ছাড়াই। কম গুরুতর ক্ষেত্রে, ট্রেনিং কিছু প্রাথমিক অস্থিরতার পর শেষ পর্যন্ত থিতু হয়ে যায়। বেশি গুরুতর ক্ষেত্রে, প্রতিটা পদক্ষেপে দোলনগুলো আসলে ছোট হওয়ার বদলে বড় হতে থাকে: মডেল মিনিমাম একদিকে ওভারশুট করে, তারপর ফিরে আসার পথে আরও বেশি ওভারশুট করে, এভাবে চলতে থাকে, বাইরের দিকে স্পাইরাল হতে থাকে যতক্ষণ না loss বিশাল হয়ে যায় বা এতে জড়িত সংখ্যাগুলো সম্পূর্ণভাবে ওভারফ্লো করে। এই সম্পূর্ণ ভাঙনকে বলা হয় divergence।
একটা অস্থির loss কার্ভকে শুধু "আরও ধৈর্য" দরকার ভাবাটা সহজ, ঠিক সেই একই সমাধান যা একটা খুব-ছোট learning rate-কে সাহায্য করে। কিন্তু এটা তা করে না। একটা সত্যিকারের diverging মডেল ধীরে ধীরে একটা ভালো উত্তরের কাছে পৌঁছাচ্ছে না — এটা প্রতিটা পদক্ষেপে সক্রিয়ভাবে দূরে সরে যাচ্ছে। যতই অতিরিক্ত ট্রেনিং সময় দিন না কেন এটা ঠিক হয় না; learning rate নিজেই কমাতে হবে।
নিচের ইন্টারেক্টিভ ডেমোটা (যা Gradient & Gradient Descent চ্যাপ্টারেও দেখা গেছে) এই ব্যর্থতার ধরনটা সরাসরি দৃশ্যমান করে তোলে — learning rate স্লাইডারটা একটা উচ্চ মানে টেনে নিয়ে দেখুন পথটা বাটি-আকৃতির loss কার্ভে থিতু হওয়ার বদলে বাউন্স করে।
একটি লার্নিং রেট বেছে নিন এবং একটি বাটি-আকৃতির লস কার্ভের ওপর গ্রেডিয়েন্ট ডিসেন্টের ধাপগুলো দেখুন। দেখুন কীভাবে একই অন্তর্নিহিত আপডেট নিয়ম এই একটা সংখ্যার উপর নির্ভর করে সম্পূর্ণ ভিন্নভাবে আচরণ করে।
Try stepping through and watch how it behaves.
একটা বড় learning rate বড়, আগ্রাসী পদক্ষেপ তৈরি করে — কাজ করলে দ্রুত, কিন্তু মিনিমাম ওভারশুট করা, বন্যভাবে দোলা, বা সম্পূর্ণভাবে diverge করার প্রবণ।
যেহেতু দুই চরমই ভিন্নভাবে ব্যর্থ হয়, একটা ভালো learning rate বেছে নেওয়া মানে একটা মধ্যম বিন্দু খুঁজে বের করা — এমন একটা যা যথেষ্ট দ্রুত এগোয় আসল অগ্রগতি করতে, কিন্তু এত আগ্রাসী না যে ওভারশুট করে।
| খুব ছোট | ভালোভাবে বেছে নেওয়া | খুব বড় | |
|---|---|---|---|
| Loss কার্ভের আকৃতি | কমে, কিন্তু খুবই ধীরে আর মসৃণভাবে | স্থিরভাবে কমে আর একটা মিনিমামের কাছে থিতু হয় | অনিয়মিতভাবে লাফিয়ে বেড়ায়, সময়ের সাথে বাড়তেও পারে |
| একটা ভালো সমাধানে পৌঁছানোর গতি | খুবই ধীর — উপলব্ধ পদক্ষেপের চেয়ে অনেক বেশি লাগতে পারে | কার্যকর — একটা যুক্তিসঙ্গত সংখ্যক পদক্ষেপে ভালো ফলাফলে পৌঁছায় | কখনোই নির্ভরযোগ্যভাবে ভালো ফলাফলে পৌঁছায় না |
| আটকে যাওয়ার ঝুঁকি | বেশি — সমতল অঞ্চলে প্রায় সম্পূর্ণভাবে থেমে যেতে পারে | কম — সমতল জায়গা পার হওয়ার মতো যথেষ্ট momentum | প্রযোজ্য না — সমস্যাটা অস্থিরতা, থেমে যাওয়া না |
| অস্থিরতার ঝুঁকি | মূলত নেই | কম, ভালোভাবে বেছে নিলে | বেশি — দোলাতে বা সরাসরি diverge করতে পারে |
বাস্তবে, আগে থেকে জানা একটা মাত্র গাণিতিকভাবে নিখুঁত learning rate খুব কমই থাকে — এটা নির্দিষ্ট প্রবলেমের নির্দিষ্ট loss ল্যান্ডস্কেপের উপর নির্ভর করে, যা আগে থেকে জানা যায় না। কয়েকটা ব্যবহারিক স্ট্র্যাটেজি এটা সংকীর্ণ করতে সাহায্য করে:
সাধারণ ডিফল্ট দিয়ে শুরু করুন
অনেক ট্রেনিং সেটআপে ভালোভাবে-পরীক্ষা করা ডিফল্ট learning rate থাকে (প্রায়ই ০.০০১ থেকে ০.১-এর মধ্যে কোথাও, অপ্টিমাইজার আর প্রবলেম টাইপের উপর নির্ভর করে) যা কোনো টিউনিংয়ের আগেই একটা শুরুর বিন্দু হিসেবে মোটামুটি ভালো কাজ করে।
একটা লগ স্কেলে একটা রেঞ্জ চেষ্টা করুন
যেহেতু 'সঠিক' learning rate বিভিন্ন প্রবলেমের মধ্যে অর্ডার-অফ-ম্যাগনিটিউড পার্থক্য হতে পারে, ০.১, ০.০১, ০.০০১, আর ০.০০০১-এর মতো মান পরীক্ষা করা (০.১, ০.২, ০.৩-এর মতো সমানভাবে বিন্যস্ত মানের বদলে) বাস্তবসম্মত সম্ভাবনাগুলো অনেক বেশি কার্যকরভাবে কভার করে।
শুধু চূড়ান্ত সংখ্যা না, loss কার্ভটা দেখুন
একটা মসৃণভাবে কমতে থাকা কার্ভ একটা যুক্তিসঙ্গত রেট নির্দেশ করে; একটা অনিয়মিত, লাফানো কার্ভ নির্দেশ করে এটা খুব বড়; একটা প্রায়-সমতল কার্ভ যা প্রায় নড়েই না নির্দেশ করে এটা খুব ছোট।
একটা learning rate ফাইন্ডার ব্যবহার করুন
কিছু ট্রেনিং সেটআপে একটা সিস্টেমেটিক সুইপ থাকে — একটা খুব ছোট learning rate দিয়ে শুরু করে, একটা সংক্ষিপ্ত রানে ধীরে ধীরে বাড়ানো, আর loss কীভাবে সাড়া দেয় সেটা প্লট করা — যাতে একটা পূর্ণ ট্রেনিং রানে যাওয়ার আগে দৃশ্যত ভালো কাজ করা রেঞ্জটা খুঁজে বের করা যায়।
সত্যিই অনিশ্চিত থাকলে, একটা ভালোভাবে-পরীক্ষা করা ডিফল্ট থেকে একটা মাঝারি ছোট মান দিয়ে শুরু করুন, ট্রেনিংয়ের প্রথম অংশের জন্য loss কার্ভটা দেখুন, আর সেখান থেকে অ্যাডজাস্ট করুন: অগ্রগতি কষ্টদায়কভাবে ধীর আর স্থির দেখালে বাড়ান, loss কার্ভ নয়েজি বা অনিয়মিত দেখালে কমান।
পুরো একটা ট্রেনিং রানের জন্য একটা মাত্র ফিক্সড learning rate বেছে নেওয়া একটা আপোস: ট্রেনিংয়ের শুরুতে, যখন weight-গুলো কোনো ভালো সমাধান থেকে অনেক দূরে, বড় পদক্ষেপ নিরাপদে দ্রুত অগ্রগতি করতে পারে। ট্রেনিংয়ের পরের দিকে, যখন weight-গুলো ইতিমধ্যেই একটা ভালো সমাধানের কাছাকাছি, সেই একই বড় পদক্ষেপগুলো ঠিক সেই ওভারশুটিং আর অস্থিরতার ঝুঁকি নেয় যা আগে বর্ণনা করা হয়েছে। Learning rate scheduling এটা সমাধান করে ট্রেনিংয়ের সময়কাল জুড়ে learning rate বদলিয়ে, ফিক্সড না রেখে।
Step Decay
একটা নির্দিষ্ট সংখ্যক পদক্ষেপ বা epoch-এর জন্য learning rate ফিক্সড রাখুন, তারপর এটা একটা ফিক্সড ফ্যাক্টর দিয়ে কমিয়ে দিন (যেমন অর্ধেক করে দেওয়া, বা ১০ গুণ কমানো), আর পুনরাবৃত্তি করুন। ইমপ্লিমেন্ট করা সহজ আর ব্যাপকভাবে ব্যবহৃত।
Exponential Decay
প্রতিটা সিঙ্গেল পদক্ষেপে learning rate-কে একটা ছোট শতাংশ দিয়ে ক্রমাগত ছোট করুন, হঠাৎ পতনের বদলে একটা মসৃণ নিম্নমুখী কার্ভ তৈরি করুন।
Warmup
ট্রেনিং একটা খুব ছোট learning rate দিয়ে শুরু করুন আর ট্রেনিংয়ের প্রথম অংশে ধীরে ধীরে এটা বাড়ান, decay প্রয়োগ করার আগে। এটা বড়, অস্থির আপডেট এড়ায় যখন মডেলের weight তখনও তাদের র্যান্ডম শুরুর মানের কাছাকাছি থাকে।
Cosine Annealing
একটা কোসাইন কার্ভ অনুসরণ করে মসৃণভাবে learning rate কমান, উচ্চে শুরু করে ট্রেনিং শেষে প্রায়-শূন্যে গ্লাইড করে নেমে, প্রায়ই খুব স্থিতিশীল চূড়ান্ত কনভার্জেন্স তৈরি করে।
Learning rate scheduling ট্রেনিংয়ের সময়কাল জুড়ে learning rate অ্যাডজাস্ট করে, সাধারণত শুরুতে বড় রেখে দ্রুত প্রাথমিক অগ্রগতির জন্য আর পরে ছোট করে একটা মিনিমামের কাছে স্থিতিশীল, সুনির্দিষ্ট কনভার্জেন্সের জন্য।
অন্তর্নিহিত ইনটুইশনটা সরাসরি বাটি-আকৃতির loss ল্যান্ডস্কেপের ছবির সাথে যুক্ত: শুরুর দিকে একটা বড় learning rate খাড়া উপরের ঢাল দিয়ে নিচে বড় বড় পদক্ষেপ নেওয়ার মতো, দ্রুত দূরত্ব কভার করা যখন ভুলের জন্য অনেক জায়গা থাকে। পরের দিকে একটা ছোট learning rate সতর্ক, ছোট পদক্ষেপ নেওয়ার মতো একবার আপনি বাটির তলার কাছাকাছি এলে, যেখানে একটা বেশি বড় পদক্ষেপ আপনাকে অন্যপাশে উপরে ছুটিয়ে দিত।
কোনো একটা মাত্র ফিক্সড learning rate একসাথে "শুরুতে দ্রুত হওয়ার জন্য যথেষ্ট বড়" আর "শেষে স্থিতিশীল হওয়ার জন্য যথেষ্ট ছোট" হতে পারে না — এই দুইটা লক্ষ্য একটা মাত্র মানে সত্যিই দ্বন্দ্বে পড়ে। Scheduling এই দ্বন্দ্বটা সম্পূর্ণভাবে এড়িয়ে যায় ট্রেনিংকে প্রতিটা পর্যায়ে একটা ভিন্ন, উপযুক্ত রেট দিয়ে।
কয়েকটা learning-rate ভুল বারবার দেখা যায়, যেকোনো অভিজ্ঞতার স্তরের প্রজেক্টে।
একটা loss কার্ভ যা সমতল হয়ে গেছে তার মানে হতে পারে ট্রেনিং সত্যিই একটা ভালো মিনিমামে পৌঁছেছে — অথবা এর মানে হতে পারে learning rate খুব ছোট আর মডেল একটা সমতল বা অগভীর অঞ্চলের ভেতরে সবেমাত্র এগোচ্ছে। এগুলো একটা ছোট টাইমস্কেলে একই রকম দেখায়। Learning rate সামান্য বাড়ালে নতুন অগ্রগতি হয় কিনা চেক করাটা এদের আলাদা করার একটা দ্রুত উপায়।
ট্রেনিংয়ের প্রথম দিকের একটা অনিয়মিত, লাফানো loss কার্ভ প্রায়ই আসলে warmup দিয়ে সমাধান হয় (আরও ছোট শুরু করে ধীরে ধীরে বাড়ানো), পুরো রানের জন্য স্থায়ীভাবে একটা ছোট রেট ব্যবহার করে না। শুরুর-ট্রেনিং সমস্যা ঠিক করতে স্থায়ীভাবে রেট ছোট করলে বাকি ট্রেনিংয়ের জন্য আসল গতি হারিয়ে যেতে পারে।
একটা ডেটাসেট, মডেল আর্কিটেকচার, বা batch সাইজের জন্য টিউন করা একটা learning rate স্বয়ংক্রিয়ভাবে অন্য একটাতে ট্রান্সফার হয় না — সঠিক মান সেই নির্দিষ্ট loss ল্যান্ডস্কেপের নির্দিষ্ট আকৃতির উপর নির্ভর করে। একটা ধার-করা learning rate-কে একটা নিরাপদ ডিফল্ট ধরে নেওয়া, loss কার্ভের অন্তত একটা দ্রুত চেক ছাড়াই, নীরবে খারাপ ট্রেনিং রানের একটা সাধারণ উৎস।
ট্রেনিং ভালো না গেলে, একই এক্সপেরিমেন্টে learning rate, batch সাইজ, আর মডেল আর্কিটেকচার সবগুলোই অ্যাডজাস্ট করার প্রলোভন হয়। এটা বলা অসম্ভব করে তোলে কোন বদলটা আসলে কোনো লক্ষ্যণীয় উন্নতি বা অবনতির কারণ। একবারে একটা করে বদল করে, আলাদাভাবে learning rate অ্যাডজাস্ট করলে কারণ আর ফলাফল ট্রেসযোগ্য থাকে।