Skip to content

Commit 61b9b47

Browse files
authored
Update train.py
1 parent ab70968 commit 61b9b47

1 file changed

Lines changed: 27 additions & 37 deletions

File tree

src/train.py

Lines changed: 27 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,3 @@
1-
"""
2-
Train the plant disease detection model.
3-
4-
Usage:
5-
export DATA_DIR="data/PlantVillage"
6-
python src/train.py
7-
8-
Or on Windows:
9-
set DATA_DIR=data/PlantVillage
10-
python src/train.py
11-
"""
121

132
import os
143
import json
@@ -17,20 +6,21 @@
176
import numpy as np
187
import tensorflow as tf
198
import matplotlib.pyplot as plt
20-
from src.data_loader import load_datasets
21-
from src.model import build_model
9+
10+
from src.data_loader import load_datasets
11+
from src.model import build_model, unfreeze_top_layers
2212

2313

2414
# ── Config ────────────────────────────────────────────────────────────────────
25-
DATA_DIR = os.getenv("DATA_DIR", "data/PlantVillage")
26-
MODEL_DIR = Path("models")
27-
MODEL_PATH = MODEL_DIR / "best_model.keras"
28-
HISTORY_PATH = MODEL_DIR / "history.json"
29-
PLOTS_DIR = Path("reports/figures")
30-
31-
EPOCHS_HEAD = 15 # Phase 1: train only the classification head
32-
EPOCHS_FINETUNE = 10 # Phase 2: fine-tune top layers of backbone
33-
BATCH_SIZE = 32
15+
DATA_DIR = os.getenv("DATA_DIR", "data/PlantVillage")
16+
MODEL_DIR = Path("models")
17+
MODEL_PATH = MODEL_DIR / "best_model.keras"
18+
HISTORY_PATH = MODEL_DIR / "history.json"
19+
PLOTS_DIR = Path("reports/figures")
20+
21+
EPOCHS_HEAD = 15 # Phase 1: train only the classification head
22+
EPOCHS_FINETUNE = 10 # Phase 2: fine-tune top layers of backbone
23+
BATCH_SIZE = 32
3424
# ──────────────────────────────────────────────────────────────────────────────
3525

3626

@@ -40,24 +30,24 @@ def get_callbacks(model_path: Path):
4030
monitor="val_accuracy",
4131
patience=5,
4232
restore_best_weights=True,
43-
verbose=1
33+
verbose=1,
4434
),
4535
tf.keras.callbacks.ModelCheckpoint(
4636
filepath=str(model_path),
4737
monitor="val_accuracy",
4838
save_best_only=True,
49-
verbose=1
39+
verbose=1,
5040
),
5141
tf.keras.callbacks.ReduceLROnPlateau(
5242
monitor="val_loss",
5343
factor=0.5,
5444
patience=3,
5545
min_lr=1e-7,
56-
verbose=1
46+
verbose=1,
5747
),
5848
tf.keras.callbacks.TensorBoard(
5949
log_dir="logs",
60-
histogram_freq=1
50+
histogram_freq=1,
6151
),
6252
]
6353

@@ -66,16 +56,16 @@ def plot_training_history(history_dict: dict, save_dir: Path) -> None:
6656
save_dir.mkdir(parents=True, exist_ok=True)
6757
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
6858

69-
axes[0].plot(history_dict["accuracy"], label="Train accuracy", marker="o")
70-
axes[0].plot(history_dict["val_accuracy"], label="Val accuracy", marker="o")
59+
axes[0].plot(history_dict["accuracy"], label="Train accuracy", marker="o")
60+
axes[0].plot(history_dict["val_accuracy"], label="Val accuracy", marker="o")
7161
axes[0].set_title("Accuracy")
7262
axes[0].set_xlabel("Epoch")
7363
axes[0].set_ylabel("Accuracy")
7464
axes[0].legend()
7565
axes[0].grid(True, alpha=0.3)
7666

77-
axes[1].plot(history_dict["loss"], label="Train loss", marker="o")
78-
axes[1].plot(history_dict["val_loss"], label="Val loss", marker="o")
67+
axes[1].plot(history_dict["loss"], label="Train loss", marker="o")
68+
axes[1].plot(history_dict["val_loss"], label="Val loss", marker="o")
7969
axes[1].set_title("Loss")
8070
axes[1].set_xlabel("Epoch")
8171
axes[1].set_ylabel("Loss")
@@ -100,15 +90,15 @@ def main():
10090

10191
# Save class names so predict.py and app.py can load them
10292
with open(MODEL_DIR / "class_names.json", "w") as f:
103-
json.dump(class_names, f)
93+
json.dump(class_names, f, indent=2)
10494

10595
# ── Phase 1: Train head only ───────────────────────────────────────────
10696
print("\nPhase 1 — Training classification head (backbone frozen)...")
10797
model = build_model(num_classes)
10898
model.compile(
10999
optimizer=tf.keras.optimizers.Adam(learning_rate=1e-3),
110100
loss="sparse_categorical_crossentropy",
111-
metrics=["accuracy"]
101+
metrics=["accuracy"],
112102
)
113103
model.summary()
114104

@@ -123,9 +113,9 @@ def main():
123113
print("\nPhase 2 — Fine-tuning top layers of EfficientNetB0...")
124114
model = unfreeze_top_layers(model, num_layers=20)
125115
model.compile(
126-
optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5), # Lower LR!
116+
optimizer=tf.keras.optimizers.Adam(learning_rate=1e-5), # lower LR for fine-tuning
127117
loss="sparse_categorical_crossentropy",
128-
metrics=["accuracy"]
118+
metrics=["accuracy"],
129119
)
130120

131121
history2 = model.fit(
@@ -141,15 +131,15 @@ def main():
141131
combined[key] = history1.history[key] + history2.history[key]
142132

143133
with open(HISTORY_PATH, "w") as f:
144-
json.dump(combined, f)
134+
json.dump(combined, f, indent=2)
145135

146136
plot_training_history(combined, PLOTS_DIR)
147137

148138
# ── Final evaluation ──────────────────────────────────────────────────
149139
print("\nEvaluating on validation set...")
150140
loss, acc = model.evaluate(val_ds, verbose=1)
151-
print(f"\nFinal val accuracy: {acc:.4f} ({acc*100:.2f}%)")
152-
print(f"Final val loss: {loss:.4f}")
141+
print(f"\nFinal val accuracy : {acc:.4f} ({acc*100:.2f}%)")
142+
print(f"Final val loss : {loss:.4f}")
153143
print(f"\nModel saved → {MODEL_PATH}")
154144

155145

0 commit comments

Comments
 (0)