Chapter 16 of 18
Deliberate handicaps that keep a model from memorizing
Underfitting vs Overfitting চ্যাপ্টার overfitting-এর জন্য কয়েকটা সমাধানের তালিকা দিয়ে শেষ হয়েছিল: সহজ মডেল, বেশি ডেটা, আগে ট্রেনিং থামানো, আর একটা ক্যাটাগরি বেশিরভাগ অব্যাখ্যাত রাখা হয়েছিল — regularization। এই চ্যাপ্টারটা সম্পূর্ণভাবে সেই ক্যাটাগরি নিয়ে: ইচ্ছাকৃত টেকনিক যা একটা মডেলকে তার ট্রেনিং ডেটার সাথে অতিরিক্ত নিখুঁতভাবে ফিট হওয়া থেকে ইচ্ছা করেই আটকায়।
এটা প্রথমে উল্টো মনে হতে পারে। ট্রেনিং আছে loss কমাতে, আর regularization ইচ্ছাকৃতভাবে খুব কম training loss অর্জন করাকে কঠিন করে দেয়। কিন্তু আগের চ্যাপ্টারের Bias বনাম Variance সেকশন থেকে মনে করুন: একটা উচ্চ-variance মডেল তার ট্রেনিং ডেটার সাথে ঘনিষ্ঠভাবে ফিট হয় সেই নির্দিষ্ট ডেটার নয়েজ মুখস্থ করে, আর সেই মুখস্থ করাটাই নতুন ডেটায় ব্যর্থ হয়। Regularization হলো এমন যেকোনো টেকনিক যা একটা মডেলকে তার ট্রেনিং ডেটার নির্দিষ্ট অদ্ভুততার উপর অতিরিক্ত নির্ভর করা থেকে নিরুৎসাহিত করে, একটু training পারফরম্যান্সের বিনিময়ে নতুন, অদেখা ডেটায় অর্থপূর্ণভাবে ভালো পারফরম্যান্স পাওয়ার জন্য।
Regularization হলো ট্রেনিংয়ে যোগ করা এমন যেকোনো টেকনিক যা overfitting নিরুৎসাহিত করে, সাধারণত জটিলতাকে শাস্তি দিয়ে বা মডেল কোনো একটা সিঙ্গেল weight বা ফিচারের উপর কতটা নির্ভর করতে পারে তা সীমিত করে।
বেশিরভাগ regularization টেকনিক একই অন্তর্নিহিত দর্শন শেয়ার করে, যদিও এরা খুবই ভিন্ন মেকানিজমের মাধ্যমে কাজ করে: মডেলের জন্য যে নির্দিষ্ট ট্রেনিং উদাহরণ এটা দেখেছে তার সাথে অতিরিক্ত কনফিডেন্ট বা অতিরিক্ত বিশেষীকৃত হওয়া কঠিন করে দেওয়া, যাতে ট্রেনিংয়ের পরে যা টিকে থাকে তা মুখস্থ করা নয়েজের বদলে আসল প্যাটার্ন প্রতিফলিত করে।
Regularization-কে একটা ইচ্ছাকৃত হ্যান্ডিক্যাপ দিয়ে ট্রেনিং হিসেবে ভাবুন — যেমন প্রতিযোগিতায় যে সরঞ্জাম ব্যবহার করবেন তার চেয়ে সামান্য ভারী সরঞ্জাম দিয়ে একটা খেলা অনুশীলন করা। অনুশীলনের সময় নিখুঁত দেখানো কঠিন, কিন্তু হ্যান্ডিক্যাপ পেরিয়ে যে দক্ষতা টিকে থাকে সেগুলো সাধারণত আসল, শুধু সেই একটা অনুশীলন সেশনের কোনো অদ্ভুততার কারণে কাজ করা দক্ষতা না।
L1 regularization (যাকে Lasso-ও বলা হয়) loss function-এ একটা শাস্তি যোগ করে যা মডেলের প্রতিটা weight-এর অ্যাবসোলিউট ভ্যালুর সমষ্টির সমান।
এখানে হলো Loss Functions চ্যাপ্টার থেকে loss function (MSE, cross-entropy, বা যাই কাজের সাথে মানানসই হোক), মডেলের প্রতিটা weight জুড়ে চলে, আর (lambda) হলো একটা hyperparameter যা এই শাস্তি কতটা জোরালোভাবে প্রয়োগ হয় তা নিয়ন্ত্রণ করে — একটা বড় weight-কে বেশি আগ্রাসীভাবে শূন্যের দিকে ঠেলে দেয়। যেহেতু এই শাস্তি টার্মটা সরাসরি loss-এ যোগ হয়, gradient descent (Gradient & Gradient Descent চ্যাপ্টার থেকে) স্বয়ংক্রিয়ভাবে এটাও ছোট করার চেষ্টা করে, আসল loss-এর পাশাপাশি।
L1-এর স্বতন্ত্র প্রভাব হলো এটা অনেক weight-কে ঠিক শূন্যের কাছাকাছি না, একদম শূন্যে ঠেলে দেওয়ার প্রবণতা রাখে। ঠিক শূন্যে একটা weight মানে সেই ইনপুটটা মডেল সম্পূর্ণভাবে উপেক্ষা করে — কার্যকরভাবে, L1 regularization এক ধরনের স্বয়ংক্রিয় feature selection করে, শুধু সেই ইনপুটগুলো রাখে যা মডেল সত্যিই কাজের পেয়েছে আর বাকিগুলো সম্পূর্ণভাবে বাদ দেয়।
L1 regularization weight-এর অ্যাবসোলিউট ভ্যালুর সমষ্টির সমানুপাতিক একটা শাস্তি যোগ করে, অনেক weight-কে ঠিক শূন্য হয়ে যেতে উৎসাহিত করে আর কার্যকরভাবে সত্যিই কাজের ফিচারের একটা ছোট সাবসেট বেছে নেয়।
L1 বিশেষভাবে কার্যকর যখন বিশ্বাস করার কারণ থাকে যে উপলব্ধ ইনপুট ফিচারের শুধু কয়েকটাই আসলে গুরুত্বপূর্ণ, আর বাকিগুলো নয়েজ বা রিডানডেন্ট — অকার্যকর weight-কে শূন্য করে দেওয়ার L1-এর প্রবণতা চূড়ান্ত মডেলটাকে সহজ আর ব্যাখ্যা করা সহজ করে তোলে।
L2 regularization (যাকে Ridge-ও বলা হয়) একটা শাস্তি যোগ করে যা প্রতিটা weight-এর বর্গ করা মানের সমষ্টির সমান, এদের অ্যাবসোলিউট ভ্যালুর বদলে।
সূত্রটা L1-এর প্রায় একই দেখায়, কিন্তু অ্যাবসোলিউট ভ্যালু নেওয়ার বদলে বর্গ করা একটা গুরুত্বপূর্ণ উপায়ে আচরণ বদলে দেয়। বর্গ করা বড় weight-এর জন্য শাস্তিকে ছোট weight-এর চেয়ে অনেক দ্রুত বাড়ায় — একটা weight শাস্তিতে অবদান রাখে, যখন একটা weight শুধু অবদান রাখে। এর মানে L2 বিশেষভাবে সবচেয়ে বড় weight-গুলোর বিরুদ্ধে সবচেয়ে জোরে ঠেলে দেয়, প্রতিটা weight-কে আলতোভাবে শূন্যের দিকে (কিন্তু ঠিক শূন্যে না) সংকুচিত করে, L1 যেভাবে সম্পূর্ণভাবে weight শূন্য করে দেয় তার বদলে।
| L1 | L2 | |
|---|---|---|
| শাস্তি টার্ম | Weight-এর অ্যাবসোলিউট ভ্যালুর সমষ্টি | Weight-এর বর্গ করা মানের সমষ্টি |
| Weight-এর উপর প্রভাব | অনেক weight-কে ঠিক শূন্যে ঠেলে দেয় | সব weight-কে শূন্যের দিকে সংকুচিত করে, খুব কম সময়ই ঠিক শূন্য |
| ফলাফল মডেল | স্পার্স — কার্যকরভাবে কম ফিচার ব্যবহার করে | ডেন্স — সব ফিচার ব্যবহার করে, কিন্তু ছোট weight দিয়ে |
| সবচেয়ে উপযুক্ত | যখন শুধু কয়েকটা ফিচার সম্ভবত গুরুত্বপূর্ণ | যখন বেশিরভাগ ফিচার সামান্য অবদান রাখে, আর চরম weight মূল উদ্বেগ |
L2 regularization weight-এর বর্গ করা মানের সমষ্টির সমানুপাতিক একটা শাস্তি যোগ করে, কোনো একটা সিঙ্গেল weight-কে খুব বড় হতে নিরুৎসাহিত করে যখন সাধারণত প্রতিটা weight-কে নন-জিরো রাখে।
কয়েকটা খুব বড় weight-সহ একটা মডেল প্রায়ই অল্প কয়েকটা নির্দিষ্ট ইনপুটের উপর চরমভাবে নির্ভর করছে — আর সেই নির্ভরতাই ঠিক সেই ধরনের সংকীর্ণ, ভঙ্গুর প্যাটার্ন যা সাধারণত ভালোভাবে সাধারণীকরণ হয় না। প্রতিটা weight-এর বর্গকে শাস্তি দিয়ে, L2 বিশেষভাবে এই নির্ভরতার ঘনত্বকে নিরুৎসাহিত করে, এমন সমাধান পছন্দ করে যেখানে প্রভাব অনেক weight জুড়ে বেশি সমানভাবে ছড়িয়ে থাকে।
L1 আর L2 regularization loss function নিজেই বদলিয়ে কাজ করে। Dropout সম্পূর্ণ ভিন্ন একটা পদ্ধতি নেয়: ট্রেনিংয়ের সময়, এটা প্রতিটা ট্রেনিং ধাপে একটা লেয়ারের নিউরনের একটা অংশ র্যান্ডমলি আর সাময়িকভাবে "বন্ধ" করে দেয়।
কনক্রিটভাবে, ট্রেনিংয়ের সময় প্রতিটা forward pass-এ, dropout ব্যবহার করা একটা লেয়ারের প্রতিটা নিউরনের সেই ধাপে -তে সেট হয়ে যাওয়ার একটা প্রোবাবিলিটি থাকে (একটা hyperparameter, সাধারণত ০.২ থেকে ০.৫-এর কাছাকাছি) — এর আউটপুট সম্পূর্ণভাবে উপেক্ষা করা হয়, যেন এটা অস্তিত্বেই নেই, আর সেই ধাপের backward pass-এ এর মধ্য দিয়ে কোনো gradient প্রবাহিত হয় না। কোন নির্দিষ্ট নিউরন বাদ পড়বে সেটা প্রতিটা সিঙ্গেল ট্রেনিং ধাপে র্যান্ডমলি বদলায়; প্রতিবার একটা ভিন্ন র্যান্ডম সাবসেট নীরব করা হয়।
Dropout প্রতিটা ট্রেনিং ধাপে একটা লেয়ারের নিউরনের একটা অংশ র্যান্ডমলি নিষ্ক্রিয় করে দেয়, নেটওয়ার্ককে কোনো একটা সিঙ্গেল নিউরন বা সংকীর্ণ নিউরনের গ্রুপের উপর অতিরিক্ত নির্ভর করা থেকে বাধ্য করে এড়িয়ে যেতে।
এর পেছনের যুক্তি: যদি একটা নেটওয়ার্ক একটা নির্দিষ্ট নিউরনের (বা নিউরনের একটা ছোট দল) উপর নির্ভর করতে পারত একটা নির্দিষ্ট ট্রেনিং অদ্ভুততা মুখস্থ করতে, dropout সেটা অনির্ভরযোগ্য করে দেয় — সেই নিউরনটা হয়তো পরের ধাপে আদৌ সেখানে নাও থাকতে পারে। এটা নেটওয়ার্ককে কার্যকর তথ্য অনেক নিউরন জুড়ে রিডানডেন্টভাবে ছড়িয়ে দিতে চাপ দেয়, শুধু কয়েকটাতে জমা করার বদলে, যা সাধারণত এমন রিপ্রেজেন্টেশন তৈরি করে যা নতুন ডেটায় ভালোভাবে সাধারণীকরণ হয়।
প্রেডিকশনের সময় (ট্রেনিং শেষ হওয়ার পর), dropout সম্পূর্ণভাবে বন্ধ থাকে — প্রতিটা নিউরন সক্রিয়, আর প্রেডিকশন পুরো নেটওয়ার্ক ব্যবহার করে। ফ্রেমওয়ার্কগুলো সাধারণত প্রেডিকশনের সময় নিউরনের আউটপুট স্কেল করে এই বিষয়টা হিসাবে নিতে যে, ট্রেনিংয়ের সময়, গড়ে শুধু নিউরনের একটা অংশই সক্রিয় ছিল — এই বিস্তারিত জিনিসটা সাধারণত স্বয়ংক্রিয়ভাবে সামলানো হয়, কিন্তু জানা দরকার যে dropout সত্যিই ট্রেনিং বনাম প্রেডিকশনে ভিন্নভাবে আচরণ করে।
Dropout সাধারণত একটা নেটওয়ার্কের বড়, ফুলি-কানেক্টেড হিডেন লেয়ারে বেশি প্রয়োগ হয়, যেখানে তথ্য ছড়ানোর জন্য সবচেয়ে বেশি রিডানডেন্ট ক্যাপাসিটি থাকে। একটা খুব বেশি dropout rate একটা নেটওয়ার্ককে underfitting-এর দিকে ঠেলে দিতে পারে (Underfitting vs Overfitting চ্যাপ্টার অনুযায়ী), কারণ কোনো নির্দিষ্ট ধাপে নেটওয়ার্কের বেশিরভাগই অনির্ভরযোগ্য হয়ে যায় — বেশিরভাগ regularization শক্তি বাছাইয়ের মতোই, এটা একটা validation set-এর বিরুদ্ধে টিউনিং থেকে উপকৃত হয়।
এখন পর্যন্ত প্রতিটা regularization টেকনিকই ট্রেনিংয়ের সময় মডেল তার আউটপুট কীভাবে হিসাব করে সেটা কিছু একটা বদলায়। Early stopping সম্পূর্ণ ভিন্ন একটা কোণ নেয় — এটা বদলায় কখন ট্রেনিং থামে, আগের চ্যাপ্টারের Detecting Problems সেকশনে পরিচয় করানো training/validation loss তুলনা ব্যবহার করে।
আইডিয়াটা সহজ: ট্রেনিং জুড়ে validation loss মনিটর করুন, training loss-এর পাশাপাশি। ট্রেনিংয়ের শুরুতে, সাধারণত দুটোই একসাথে কমে। কোনো এক বিন্দুতে, বিশেষ করে overfit করার জন্য যথেষ্ট ক্যাপাসিটিসহ মডেলের জন্য, validation loss উন্নতি করা বন্ধ করে দেয় আর বাড়তে শুরু করে, এমনকি training loss কমতে থাকলেও — আগের চ্যাপ্টারের ঠিক সেই overfitting স্বাক্ষর। Early stopping মানে সেই মোড় বিন্দুতে (বা তার একটু পরে) ট্রেনিং থামিয়ে দেওয়া, মূলত পরিকল্পিত epoch সংখ্যা পর্যন্ত চালিয়ে যাওয়ার বদলে।
এখানে (কখনো কখনো "patience" বলা হয়) একটা hyperparameter — validation loss সামান্য বাড়ার সাথে সাথেই থামানো খুব তাড়াহুড়ো হতে পারে, কারণ কিছুটা ওঠানামা স্বাভাবিক, তাই আসলে থামার আগে সাধারণত একটা ছোট patience উইন্ডো দেওয়া হয়।
Early stopping validation loss উন্নতি করা বন্ধ করলেই ট্রেনিং থামিয়ে দেয়, মডেলকে training-set নয়েজ মুখস্থ করার দিকে আরও গভীরে ট্রেন করার সুযোগ পাওয়া থেকে আটকায়।
Early stopping loss function বা নেটওয়ার্ক আর্কিটেকচার আদৌ স্পর্শ করে না — কিন্তু তবুও এটা overfitting-এর বিরুদ্ধে লড়াই করে, কারণ এটা সরাসরি সীমিত করে মডেলের "প্যাটার্ন শিখেছে" থেকে "নয়েজ মুখস্থ করেছে"-তে সরে যাওয়ার কতটা সুযোগ আছে, যা (আগের চ্যাপ্টারে দেখানো হয়েছে) একটা সত্যিকারের ভালো ফিটের বিন্দু পার হয়ে ট্রেনিং যত বেশি চলতে থাকে তত বেশি ঘটার প্রবণতা রাখে।
Weight decay, যা Optimizers চ্যাপ্টারের AdamW সেকশনে পরিচয় করানো হয়েছিল, L2 regularization-এর সাথে ঘনিষ্ঠভাবে সম্পর্কিত — এতটাই ঘনিষ্ঠভাবে যে দুইটা শব্দ কখনো কখনো পরস্পর বদলে ব্যবহৃত হয়, যদিও একটা সুনির্দিষ্ট টেকনিক্যাল পার্থক্য বোঝার মতো আছে।
সাধারণ gradient descent-এ, L2 regularization আর weight decay গাণিতিকভাবে অভিন্ন ফলাফল তৈরি করে: loss-এ L2 শাস্তি যোগ করা (এই চ্যাপ্টারের আগে দেখানো হয়েছে) আর তারপর gradient নেওয়া দেখা যায় ঠিক একই যেমন প্রতিটা আপডেট ধাপে সরাসরি প্রতিটা weight-কে একটা ছোট ভগ্নাংশ দিয়ে সংকুচিত করা, gradient নিজে থেকে স্বাধীনভাবে।
শেষ টার্ম, , হলো decay: প্রতিটা সিঙ্গেল ধাপে, প্রতিটা weight নিজের বর্তমান সাইজের সমানুপাতিক একটা পরিমাণে সামান্য শূন্যের দিকে টানা হয়, gradient যা করতে বলে তা থেকে সম্পূর্ণভাবে আলাদা। এটা ঠিক L2-এর একই ইনটুইশন — weight-কে বড় হতে নিরুৎসাহিত করা — কিন্তু আপডেট নিয়মে একটা সরাসরি অ্যাডজাস্টমেন্ট হিসেবে ইমপ্লিমেন্ট করা, gradient হিসাব করার আগে loss function-এ যোগ করা একটা অতিরিক্ত টার্ম হিসেবে না।
Optimizers চ্যাপ্টারের AdamW সেকশন যেমন ব্যাখ্যা করেছিল, L2 regularization আর weight decay-এর মধ্যে এই সমতুল্যতা Adam-এর মতো অভিযোজিত optimizer-এর জন্য ভেঙে পড়ে — loss-এ একটা L2 শাস্তি মিশিয়ে দেওয়া Adam-এর প্রতি-weight অভিযোজিত স্কেলিংয়ের সাথে অস্বস্তিকরভাবে ইন্টারঅ্যাক্ট করে। AdamW বিশেষভাবে আসল weight decay প্রয়োগ করতে আছে, একটা সরাসরি বিয়োগ হিসেবে, উদ্দেশ্যিত regularization প্রভাব পুনরুদ্ধার করে যা Adam-এর loss-এর ভেতরে একটা L2 শাস্তি হিসেবে ইমপ্লিমেন্ট করা হলে ঘেঁটে যায়।
Weight decay প্রতিটা আপডেট ধাপে সরাসরি প্রতিটা weight-কে একটা ছোট ভগ্নাংশ দিয়ে সংকুচিত করে, gradient থেকে স্বাধীনভাবে — সাধারণ gradient descent-এর অধীনে গাণিতিকভাবে L2 regularization-এর অভিন্ন, কিন্তু ভিন্নভাবে ইমপ্লিমেন্ট আর প্রয়োগ করা, যা অভিযোজিত optimizer-এর জন্য গুরুত্বপূর্ণ।