التدريب الاحترافي
الفرق بين كود بيشتغل في درس، وكود بيشتغل على داتا حقيقية.
الـ Mini-batches
في الدرس اللي فات دخّلنا الداتا كلها مرة واحدة. مع مليون صورة ده مستحيل، الذاكرة مش هتكفي. الحل: نقسمها batches صغيرة (مثلاً ٦٤ مثال)، وناخد خطوة update بعد كل batch. فاكر الـ generator اللي عمل batches في المرحلة ٢؟ الـ DataLoader بيعمل كده بالظبط، وكمان بيلخبط الترتيب كل epoch.
import torch from torch.utils.data import TensorDataset, DataLoader X = torch.rand(1000, 5) y = torch.randint(0, 2, (1000, 1)).float() loader = DataLoader(TensorDataset(X, y), batch_size=64, shuffle=True) print("batches per epoch:", len(loader)) xb, yb = next(iter(loader)) print("one batch:", xb.shape, yb.shape)
Validation و Dropout و Early Stopping
هنقسم الداتا ٣ أجزاء: train يتعلم منه، وvalidation نراقب بيه الأداء وقت التدريب، وtest للنهاية بس. وهنضيف Dropout: طبقة بتقفل نسبة عشوائية من الـ neurons وقت التدريب، فالشبكة متعتمدش على neuron بعينه، وده بيقلل الحفظ. زي فريق كورة بيتمرن وكل مرة لاعيبة مختلفة غايبة، فالكل يتعلم يلعب.
import copy import pandas as pd import torch from torch import nn from torch.utils.data import TensorDataset, DataLoader from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler torch.manual_seed(0) df = pd.read_csv("churn.csv") df["yearly"] = (df["contract"] == "yearly").astype(int) X = df[["tenure", "bill", "complaints", "data_gb", "yearly"]].values y = df["churned"].values X_tr, X_val, y_tr, y_val = train_test_split(X, y, test_size=0.2, random_state=0, stratify=y) scaler = StandardScaler().fit(X_tr) to_t = lambda a: torch.tensor(a, dtype=torch.float32) train_loader = DataLoader(TensorDataset(to_t(scaler.transform(X_tr)), to_t(y_tr).unsqueeze(1)), batch_size=64, shuffle=True) X_val_t, y_val_t = to_t(scaler.transform(X_val)), to_t(y_val).unsqueeze(1) model = nn.Sequential(nn.Linear(5, 64), nn.ReLU(), nn.Dropout(0.3), nn.Linear(64, 1)) loss_fn = nn.BCEWithLogitsLoss() opt = torch.optim.Adam(model.parameters(), lr=0.003) best_loss, best_state, patience, wait = float("inf"), None, 8, 0 for epoch in range(200): model.train() # dropout ON for xb, yb in train_loader: opt.zero_grad() loss_fn(model(xb), yb).backward() opt.step() model.eval() # dropout OFF with torch.no_grad(): val_loss = loss_fn(model(X_val_t), y_val_t).item() if val_loss < best_loss: # improved -> keep this version best_loss, best_state, wait = val_loss, copy.deepcopy(model.state_dict()), 0 else: wait += 1 if wait >= patience: # early stopping print(f"stopped at epoch {epoch}: no improvement for {patience} epochs") break if epoch % 5 == 0: print(f"epoch {epoch:3}: val loss = {val_loss:.4f}") model.load_state_dict(best_state) torch.save(model.state_dict(), "churn_net.pt") # save the weights print(f"best val loss: {best_loss:.4f} (saved)")
model.eval() وانت بتتوقع، النتايج هتبقى عشوائية شوية كل مرة ومش هتفهم ليه.لما التدريب ميمشيش كويس
جرب: learning rate أكبر أو أصغر (جرب 0.1 و 0.001)، اتأكد إنك عامل scaling، واتأكد إن الـ labels صح. وجرب الأول تخلي الموديل يحفظ batch واحدة صغيرة: لو معرفش، يبقى فيه bug في الكود.