Chapter 12 of 18
The dials that decide whether training converges or collapses
এই পর্যন্ত আমরা যতগুলো মডেল তৈরি করেছি, সেগুলোর প্রতিটিতে হাইপারপ্যারামিটারের মানগুলো আমরা নিজেদের পছন্দমতো বসিয়ে দিয়েছিলাম। এই চ্যাপ্টারে আমরা বিস্তারিত শিখবো যে কোন হাইপারপ্যারামিটারটি ঠিক কী নিয়ন্ত্রণ করে, ভুল মান বসালে কীভাবে পুরো ট্রেনিং প্রক্রিয়াটি ধ্বংস হয়ে যেতে পারে, এবং কেবল অনুমানের ওপর ভিত্তি করে মান না বসিয়ে কীভাবে অত্যন্ত নিয়মতান্ত্রিক উপায়ে সেরা হাইপারপ্যারামিটারের মানগুলো খুঁজে বের করা যায়।
| হাইপারপ্যারামিটার | এটি কী নিয়ন্ত্রণ করে | খুব ছোট বা কম হলে কী হয় | খুব বড় বা বেশি হলে কী হয় |
|---|---|---|---|
| লার্নিং রেট (Learning rate η) | গ্রেডিয়েন্ট ডিসেন্টের পদক্ষেপের আকার | অত্যন্ত ধীরগতির ট্রেনিং ও কনভারজেন্স | লস ডাইভার্জ করে বা লাফাতে থাকে |
| ব্যাচ সাইজ (Batch size) | প্রতি আপডেট করার আগে কতটি ডেটার গড় নেওয়া হবে | নয়েজযুক্ত কিন্তু ঘন ঘন আপডেট; জেনারালাইজেশন ভালো হতে পারে | মসৃণ গ্রেডিয়েন্ট কিন্তু প্রতি এপকে কম আপডেট; বেশি র্যামের প্রয়োজন |
| এপক (Epochs) | পুরো ট্রেনিং ডেটার ওপর দিয়ে কতবার ফরোয়ার্ড ও ব্যাকওয়ার্ড পাস হবে | আন্ডারফিটিং (Underfitting) | ওভারফিটিং (Overfitting - যদি early stopping না থাকে) |
| সিকোয়েন্স দৈর্ঘ্য | স্লাইডিং উইন্ডোর আকার | কাজের জন্য প্রয়োজনীয় কনটেক্সটের অভাব | ধীরগতির ট্রেনিং; গ্রেডিয়েন্ট ভ্যানিশ হওয়ার সমস্যা ফিরে আসতে পারে |
| ইউনিট বা হিডেন সাইজ (n_h) | হিডেন ও সেল স্টেটের ডাইমেনশন | আন্ডারফিটিং — পর্যাপ্ত মেমরি ক্যাপাসিটির অভাব | ছোট ডেটাসেটে ওভারফিটিং; ধীরগতির ট্রেনিং |
| লেয়ারের সংখ্যা (Layers) | স্ট্যাকড এলএসটিএম লেয়ারের সংখ্যা | জটিল প্যাটার্ন শিখতে ব্যর্থ হতে পারে (আন্ডারফিট) | ট্রেন করা কঠিন; ছোট ডেটায় ওভারফিটিংয়ের মারাত্মক ঝুঁকি |
| ড্রপআউট (Dropout) | ট্রেনিংয়ের সময় র্যান্ডমলি কত শতাংশ নিউরন বন্ধ রাখা হবে | কোনো রেগুলারাইজেশন বা ওভারফিটিং প্রতিরোধ হবে না | অতিরিক্ত ড্রপআউট — মডেল প্রয়োজনীয় কিছুই শিখতে পারে না |
বাস্তব প্র্যাকটিসে সরল গ্রেডিয়েন্ট ডিসেন্ট প্রায় কখনোই ব্যবহৃত হয় না। অ্যাডাপ্টিভ অপটিমাইজারগুলো অতীতের গ্রেডিয়েন্টের সঞ্চিত তথ্যের ওপর ভিত্তি করে প্রতি প্যারামিটারের জন্য কার্যকর লার্নিং রেট স্বয়ংক্রিয়ভাবে অ্যাডজাস্ট করে নেয়:
| অপটিমাইজার | মূল আইডিয়া | কখন ব্যবহার করবেন |
|---|---|---|
| SGD + momentum | অতীতের গ্রেডিয়েন্টের রানিং এভারেজ ব্যবহার করে আপডেটগুলোকে মসৃণ করে ও সঠিক দিকে এগিয়ে নেয় | যখন আপনার কাছে খুঁটিয়ে খুঁটিয়ে টিউন করার মতো পর্যাপ্ত সময় থাকে এবং অনুমানযোগ্য লার্নিং ডাইনামিক্স চান |
| RMSprop | প্রতিটি প্যারামিটারের লার্নিং রেটকে তার সাম্প্রতিক গ্রেডিয়েন্টের হারের ওপর ভিত্তি করে ভাগ বা স্কেল করে | RNN-এর জন্য ঐতিহাসিকভাবে বেশ জনপ্রিয়; প্রতি ওয়েটের গ্রেডিয়েন্টের বড় ধরণের তফাত চমৎকার হ্যান্ডেল করে |
| Adam | মোমেন্টাম এবং RMSprop-এর প্রতি-প্যারামিটার স্কেলিংয়ের একটি চমৎকার সংমিশ্রণ | LSTM-এর জন্য ডিফল্ট পছন্দ — অত্যন্ত নির্ভরযোগ্য, দ্রুত ট্রেনিং সম্পন্ন করে এবং খুব একটা টিউনিং লাগে না |
আপনি যদি Adam থেকে SGD-তে সুইচ করেন, তবে আপনাকে সাধারণত সম্পূর্ণ নতুন ও ভিন্ন রেঞ্জের লার্নিং রেট খুঁজতে হবে। কোনো একটি নির্দিষ্ট অপটিমাইজারে যে লার্নিং রেট কাজ করেছে, তা অন্যটিতেও কাজ করবে — এমনটি ভাববেন না।
গ্রিড সার্চ (Grid Search) ম্যানুয়ালি নির্দিষ্ট করা মানগুলোর প্রতিটি সম্ভাব্য কম্বিনেশন বা সমন্বয় পরীক্ষা করে দেখে। এটি খুবই সহজ ও রিপ্রোডুসিবল, কিন্তু হাইপারপ্যারামিটারের সংখ্যা বাড়ার সাথে সাথে এর জন্য প্রয়োজনীয় ট্রায়ালের সংখ্যা এক্সপোনেনশিয়ালি বৃদ্ধি পায়।
import numpy as np
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import itertools
def build_and_evaluate(units, lr, X_train, y_train, X_val, y_val, epochs=15):
model = keras.Sequential([
layers.Input(shape=(X_train.shape[1], X_train.shape[2])),
layers.LSTM(units),
layers.Dense(1),
])
model.compile(optimizer=keras.optimizers.Adam(lr), loss="mse")
model.fit(X_train, y_train, epochs=epochs, verbose=0,
callbacks=[keras.callbacks.EarlyStopping(patience=3, restore_best_weights=True)])
val_loss = model.evaluate(X_val, y_val, verbose=0)
return val_loss
units_grid = [16, 32, 64]
lr_grid = [1e-3, 5e-4, 1e-4]
results = []
# itertools.product দিয়ে সব কম্বিনেশন তৈরি করা
for units, lr in itertools.product(units_grid, lr_grid):
loss = build_and_evaluate(units, lr, X_train, y_train, X_val, y_val)
results.append((units, lr, loss))
print(f"units={units}, lr={lr:.0e} → val_loss={loss:.4f}")
best = min(results, key=lambda x: x[2])
print(f"\nBest: units={best[0]}, lr={best[1]:.0e}, val_loss={best[2]:.4f}")ধরা যাক, ৫টি হাইপারপ্যারামিটার এবং প্রতিটির ৩টি করে মান রয়েছে; তাহলে গ্রিড সার্চের জন্য বার মডেল ট্রেইন করতে হবে। একারণেই বড় সার্চ স্পেসের জন্য আমাদের আরও স্মার্ট কৌশলের সাহায্য নিতে হয়।
র্যান্ডম সার্চ (Random Search) প্রতিটি হাইপারপ্যারামিটারের সম্ভাব্য ডিস্ট্রিবিউশন থেকে র্যান্ডমলি মান নিয়ে কম্বিনেশন তৈরি করে। গ্রিড সার্চের সমান বাজেট নিয়ে এটি সার্চ স্পেসের অনেক বেশি জায়গা এক্সপ্লোর করতে পারে, কারণ এটি ফিক্সড গ্রিডের সীমাবদ্ধতায় আটকে থাকে না।
import numpy as np
n_trials = 20
best_loss = float("inf")
best_config = None
for trial in range(n_trials):
units = int(np.random.choice([16, 32, 64, 128]))
lr = float(10 ** np.random.uniform(-4, -2)) # log-uniform: 0.01 থেকে 0.0001
loss = build_and_evaluate(units, lr, X_train, y_train, X_val, y_val)
print(f"trial {trial:2d}: units={units}, lr={lr:.2e} → val_loss={loss:.4f}")
if loss < best_loss:
best_loss, best_config = loss, (units, lr)
print(f"\nBest: units={best_config[0]}, lr={best_config[1]:.2e}, val_loss={best_loss:.4f}")লার্নিং রেটের রেঞ্জ কয়েক গুণ ব্যবধানের হয়ে থাকে — ০.০০১ এবং ০.০০০১-এর তফাত আকাশ-পাতাল, কিন্তু ০.০০১ এবং ০.০০১০১-এর তফাত কিছুই না। তাই লিনিয়ার স্কেলে স্যাম্পল না করে লগ স্কেলে 10 ** np.random.uniform(log_min, log_max) ব্যবহার করুন, যাতে ছোট ও বড় উভয় রেঞ্জেই সমান মনোযোগ দেওয়া যায়।
অপটুনা (Optuna) একটি প্রবাবিলিস্টিক বা সম্ভাব্যতা মডেল তৈরি করে যা অনুমান করতে পারে কোন কোন কনফিগারেশন ভালো পারফর্ম করতে পারে এবং সেই অনুযায়ী নতুন ট্রায়াল প্রস্তাব করে। এটি গ্রিড বা র্যান্ডম সার্চের তুলনায় অনেক বেশি দক্ষ।
import optuna
def objective(trial):
units = trial.suggest_categorical("units", [16, 32, 64, 128])
lr = trial.suggest_float("lr", 1e-4, 1e-2, log=True)
dropout = trial.suggest_float("dropout", 0.0, 0.5)
model = keras.Sequential([
layers.Input(shape=(X_train.shape[1], X_train.shape[2])),
layers.LSTM(units, dropout=dropout),
layers.Dense(1),
])
model.compile(optimizer=keras.optimizers.Adam(lr), loss="mse")
model.fit(X_train, y_train, epochs=20, verbose=0,
callbacks=[keras.callbacks.EarlyStopping(patience=3)])
return model.evaluate(X_val, y_val, verbose=0)
study = optuna.create_study(direction="minimize")
study.optimize(objective, n_trials=30, show_progress_bar=True)
print("Best params:", study.best_params)বাস্তব জীবনের LSTM প্রজেক্টগুলো ডিফল্ট হাইপারপ্যারামিটার নিয়ে খুব কম সময়ই তাদের সেরা পারফরম্যান্স দিতে পারে। কেবল লার্নিং রেটের সঠিক নির্বাচনই একটি মডেলের লস ২% বনাম ২০%-এ আটকে থাকার পার্থক্য তৈরি করে দিতে পারে। এই চ্যাপ্টারের তিনটি কৌশল — grid, random এবং Bayesian — আপনাকে সাধারণ প্রোটোটাইপ থেকে শুরু করে প্রোডাকশন-গ্রেড ফাইন টিউনিং পর্যন্ত সব জায়গায় সফলভাবে হাইপারপ্যারামিটার অপ্টিমাইজ করতে সাহায্য করবে।