Chapter 15 of 18
What actually goes wrong, and the fixes that actually work
এই চ্যাপ্টারে আমরা এমন সব বাস্তব সমস্যার কথা আলোচনা করব যা বাস্তব ক্ষেত্রে LSTM ডেপ্লয়মেন্টে প্রতিনিয়ত সামনে আসে — এগুলো তাত্ত্বিকভাবে কতটা আকর্ষণীয় তার জন্য নয়, বরং প্রতিটি মেশিন লার্নিং ইঞ্জিনিয়ারকে এই সমস্যার মুখোমুখি হতে হয় বলেই আমরা এগুলো শিখবো। প্রতিটি সেকশন শুরু হবে এমন কিছু বাস্তব লক্ষণ বা সিম্পটম দিয়ে (যেমন: ট্রেনিং লস কমছে না, টেস্ট ডেটায় প্রেডিকশন এলোমেলো হয়ে যাচ্ছে, প্রতিবার রান করলে আলাদা আউটপুট আসছে) এবং শেষ হবে তার সুনির্দিষ্ট ও পরীক্ষিত সমাধান দিয়ে।
তিনটি আলাদা জায়গায় র্যান্ডম সিড সেট করা — যেমন: random.seed(), np.random.seed(), tf.random.set_seed() — এটি একটি পরিচিত অভ্যাস হলেও আধুনিক TensorFlow/Keras-এ এটি সবসময় নিখুঁত রিপ্রোডুসিবিলিটি (reproducibility - অর্থাৎ প্রতিবার রান করলে হুবহু একই ফলাফল পাওয়া) নিশ্চিত করে না।
# এই পদ্ধতিটি ব্যর্থ হয় — পরীক্ষা করে যাচাই করা হয়েছে:
import random, numpy as np, tensorflow as tf
SEED = 42
random.seed(SEED); np.random.seed(SEED); tf.random.set_seed(SEED)
m1 = build_model(); m1.fit(X, y, epochs=3, verbose=0)
pred1 = m1.predict(X[:3], verbose=0)
random.seed(SEED); np.random.seed(SEED); tf.random.set_seed(SEED)
m2 = build_model(); m2.fit(X, y, epochs=3, verbose=0)
pred2 = m2.predict(X[:3], verbose=0)
print("Reproducible:", np.allclose(pred1, pred2)) # আউটপুট আসবে: Falseসমাধান: Keras-এর ইউনিফাইড সিডিং ইউটিলিটি ব্যবহার করুন, যা সব উৎসের র্যান্ডমনেসকে একসাথে লক করে দেয়:
# এটি কাজ করে:
tf.keras.utils.set_random_seed(SEED)
m1 = build_model(); m1.fit(X, y, epochs=3, verbose=0)
pred1 = m1.predict(X[:3], verbose=0)
tf.keras.utils.set_random_seed(SEED)
m2 = build_model(); m2.fit(X, y, epochs=3, verbose=0)
pred2 = m2.predict(X[:3], verbose=0)
print("Reproducible:", np.allclose(pred1, pred2)) # আউটপুট আসবে: Trueসঠিক সিডিং করার পরেও, GPU-তে প্যারালাল ফ্লোটিং-পয়েন্ট অপারেশনের হিসাবের ক্রমের কারণে সামান্য নন-ডিটারমিনিজম তৈরি হতে পারে। যদি আপনার প্রজেক্টে একদম বিট-টু-বিট হুবহু একই ফলাফল পাওয়া অত্যন্ত জরুরি হয়, তবে আপনার ফ্রেমওয়ার্কের ডকস দেখে GPU ডিটারমিনিস্টিক মোড অন করে নিতে পারেন — তবে এটি সাধারণত প্রসেসিং স্পিড কিছুটা কমিয়ে দেয়।
সবসময় সবচেয়ে ভালো পারফর্ম করা মডেলটির একটি ব্যাকআপ বা চেকবয়েন্ট সেভ করে রাখুন এবং ভ্যালিডেশন লস উন্নত হওয়া বন্ধ হলে ট্রেনিং স্বয়ংক্রিয়ভাবে থামিয়ে দিন:
callbacks = [
keras.callbacks.ModelCheckpoint(
"best_model.keras",
save_best_only=True,
monitor="val_loss",
verbose=1,
),
keras.callbacks.EarlyStopping(
patience=5, # ভ্যালিডেশন লস উন্নত না হলে ৫ এপক অপেক্ষা করে থেমে যাবে
restore_best_weights=True, # থামার পর সবচেয়ে ভালো চেকবয়েন্টের ওয়েটগুলো লোড করবে
monitor="val_loss",
),
]
model.fit(X_train, y_train, epochs=200, # এপকের সংখ্যা বেশি রাখুন; আর্লি স্টপিং এমনিতেই আগে থামাবে
validation_split=0.15, callbacks=callbacks)এখানে restore_best_weights=True ব্যবহার করা অত্যন্ত জরুরি: এটি না থাকলে, আর্লি স্টপিং যেখানে থামবে (যা সাধারণত ৫ এপক পরের খারাপ ওয়েট) সেই ওয়েটেই মডেল থেকে যাবে, ৫ এপক আগে সেভ হওয়া ভালো ওয়েট আর ফিরে পাবে না।
| লক্ষণ বা সিম্পটম | সম্ভাব্য কারণ | সমাধান |
|---|---|---|
| ট্রেনিং লস কমলেও কয়েক এপক পর ভ্যালিডেশন লস বাড়ছে | ওভারফিটিং (Overfitting) | ড্রপআউট যোগ করুন, ইউনিট/লেয়ার কমান, বেশি ডেটা ব্যবহার করুন, early stopping ব্যবহার করুন |
| ট্রেনিংয়ের শুরু থেকেই দুটি লসই একটি বড় মানে আটকে আছে | আন্ডারফিটিং (Underfitting) | ইউনিট বা লেয়ার সংখ্যা বাড়ান, বেশি সময় ট্রেন করুন, ডেটার মান ও স্কেলিং চেক করুন |
| প্রথম বা শুরুর দিকের এপকেই লস NaN হয়ে যাচ্ছে | এক্সপ্লোডিং গ্রেডিয়েন্ট (Exploding gradients) | অপটিমাইজারে গ্রেডিয়েন্ট ক্লিপিং যোগ করুন: clipnorm=1.0 |
| লস খুব ধীর গতিতে কমছে বা কমছেই না | ভ্যানিশিং গ্রেডিয়েন্ট বা খুব ছোট লার্নিং রেট | লার্নিং রেট বাড়ান, ইনিশিয়াল ওয়েটের স্কেল চেক করুন, GRU ব্যবহারের কথা ভাবুন |
| ভ্যালিডেশন মেট্রিক্স ট্রেনিং মেট্রিক্সের চেয়ে অস্বাভাবিক ভালো | ডেটা লিকেজ (Data leakage) | পাইপলাইন চেক করুন — স্কেলার/টোকেনাইজার কি পুরো ডেটায় ফিট করা? ইনপুটে কি ভবিষ্যতের ডেটা আছে? |
| একই সিড নিয়ে প্রতি রানে আলাদা ফলাফল আসছে | ভুলভাবে সিড সেট করা | tf.keras.utils.set_random_seed() ব্যবহার করুন |
সাধারণ ড্রপআউট প্রতিটি ডেন্স লেয়ারের আগে র্যান্ডমলি কিছু ইনপুট শূন্য করে দেয়। LSTM-এর ক্ষেত্রে আমাদের কাছে দুটি ড্রপআউট অপশন রয়েছে:
layers.LSTM(
64,
dropout=0.2, # প্রতিটি টাইম স্টেপে ইনপুট x_t-এর ওপর প্রয়োগ করা হয়
recurrent_dropout=0.1, # রিক্যারেন্ট কানেকশনে h_{t-1}-এর ওপর প্রয়োগ করা হয়
)সাধারণ ড্রপআউটের বিপরীতে, একটি সিকোয়েন্স প্রসেস করার সময় প্রতি টাইম স্টেপে রিক্যারেন্ট ড্রপআউটের মাস্কটি (dropout mask) একই হতে হয়। Keras এটি নিজে থেকেই সঠিকভাবে হ্যান্ডেল করে। আপনি যদি ম্যানুয়ালি এটি করতে চান, তবে অবশ্যই একই মাস্ক প্রতি ধাপে ব্যবহার করবেন — অন্যথায় হিডেন স্টেটের ধারাবাহিকতা পুরোপুরি নষ্ট হয়ে যাবে।
ভ্যালিডেশন বা টেস্ট সেটের কোনো তথ্য কোনোভাবে ট্রেনিং প্রক্রিয়ায় প্রবেশ করলে তাকে ডেটা লিকেজ (data leakage) বলে। এর ফলে মূল্যায়নের সময় মডেল অসাধারণ পারফর্ম করলেও বাস্তবে ডেপ্লয় করার পর তা সম্পূর্ণ ব্যর্থ হয়। এটি "নীরব" কারণ কোড রান করার সময় কোনো এরর দেখায় না; মডেলটি নীরবে কেবল ভুল জিনিস শিখে বসে থাকে।
১. পুরো ডেটার ওপর স্কেলার ফিট করা: কেবল ট্রেনিং সেটের বদলে পুরো ডেটাসেটের ওপর scaler.fit_transform(all_data) করা।
২. পুরো টেক্সটের ওপর টোকেনাইজার ফিট করা: tokenizer.fit_on_texts(all_texts) করার মাধ্যমে টেস্ট সেটের শব্দভাণ্ডার ট্রেনিংয়ের সময়ই মডেলে চলে যাওয়া।
৩. ইনপুটে ভবিষ্যতের ফিচার থাকা: যেমন একটি 'রানিং গড়' তৈরি করার সময় ভবিষ্যতের কোনো মান নিয়ে অতীতের গড় হিসাব করা, কিংবা ট্রেন/টেস্ট স্প্লিট করার আগেই ল্যাগ ফিচার তৈরি করে ফেলা।
এই প্রতিটি ক্ষেত্রেই, মডেল এমন সব তথ্যে প্রবেশাধিকার পায় যা প্রডাকশনে ডেপ্লয় করার সময় তার কাছে থাকবে না।
from tensorflow.keras.optimizers import Adam
# ক্লিপ বাই নর্ম: গ্রেডিয়েন্ট ভেক্টরের L2 নর্ম থ্রেশহোল্ড অতিক্রম করলে তাকে ছোট করা হয়
optimizer = Adam(learning_rate=1e-3, clipnorm=1.0)
model.compile(optimizer=optimizer, loss="mse")এখানে clipnorm=1.0 হলো স্ট্যান্ডার্ড ডিফল্ট মান। অন্য একটি অপশন clipvalue=0.5 গ্রেডিয়েন্টের প্রতিটি উপাদানকে আলাদাভাবে ক্লিপ করে (যার আচরণ ভিন্ন এবং সাধারণত clipnorm-ই বেশি পছন্দ করা হয়)।
এক্সপোজার বায়াস (Exposure bias) হলো অটো-রিগ্রেসিভ টেক্সট জেনারেশনের একটি বিশেষ সমস্যা: ট্রেনিং চলার সময় মডেলটি পূর্ববর্তী কনটেক্সট হিসেবে সবসময় আসল শব্দ বা টোকেন দেখতে পায় (যাকে teacher forcing বলে); কিন্তু জেনারেশনের সময় এটি তার নিজের তৈরি (যা ভুলও হতে পারে) পূর্ববর্তী শব্দগুলোর ওপর ভিত্তি করে পরবর্তী শব্দ তৈরি করে। একটি দীর্ঘ সিকোয়েন্স জেনারেট করার সময় এই ভুলগুলো ক্রমাগত চক্রবৃদ্ধি হারে বাড়তে থাকে।
এর আংশিক সমাধানসমূহ:
বাস্তব জীবনের প্রতিটি মেশিন লার্নিং প্রজেক্টই কোনো না কোনো সময় এই চ্যাপ্টারের সমস্যার মুখোমুখি হয় — বিশেষ করে ডেটা লিকেজ (যা ডেপ্লয় করার পর ধরা পড়ে) কিংবা নন-রিপ্রোডুসিবিলিটি (যা অন্যের গবেষণার কোড রান করার সময় ধরা পড়ে)। এই চ্যাপ্টারের সিম্পটম → ডায়াগনোসিস → ফিক্স এর চেকলিস্ট পদ্ধতিটি যেকোনো বাগ ডিবাগ করার সেশনে সরাসরি কাজে আসবে।