Spaces:
Paused
Paused
| """ | |
| src/training.py | |
| Training pipeline for FirenetCNN model | |
| """ | |
| import tensorflow as tf | |
| from pathlib import Path | |
| from typing import Optional, Tuple, Dict, Any | |
| from .model import FireNetModel | |
| def train( | |
| train_dir: str, | |
| val_dir: str, | |
| model_save_path: str = 'models/FirenetCNN.keras', | |
| epochs: int = 100, | |
| learning_rate: float = 0.0001, | |
| batch_size: int = 32, | |
| image_size: Tuple[int, int] = (224, 224), | |
| fine_tune: bool = False, | |
| fine_tune_epochs: int = 10, | |
| fine_tune_lr: float = 1e-5, | |
| callback_save_best: bool = True, | |
| callback_early_stopping: bool = True, | |
| early_stopping_patience: int = 10, | |
| ) -> Dict[str, Any]: | |
| """ | |
| Train the FirenetCNN model. | |
| Args: | |
| train_dir: Path to training data directory | |
| val_dir: Path to validation data directory | |
| model_save_path: Path to save the trained model | |
| epochs: Number of training epochs | |
| learning_rate: Learning rate for initial training | |
| batch_size: Training batch size | |
| image_size: Input image size (height, width) | |
| fine_tune: Whether to fine-tune the base model after initial training | |
| fine_tune_epochs: Number of fine-tuning epochs | |
| fine_tune_lr: Learning rate for fine-tuning | |
| callback_save_best: Save model with best validation accuracy | |
| callback_early_stopping: Stop training if validation loss stops improving | |
| early_stopping_patience: Patience for early stopping | |
| Returns: | |
| Dictionary with training history and results | |
| """ | |
| # Build model | |
| model = FireNetModel.build_model(input_shape=(*image_size, 3)) | |
| # Create data generators | |
| train_gen, val_gen = FireNetModel.create_data_generators(train_dir, val_dir) | |
| # Setup callbacks | |
| callbacks = [] | |
| save_path = Path(model_save_path) | |
| save_path.parent.mkdir(parents=True, exist_ok=True) | |
| if callback_save_best: | |
| best_model_path = save_path.with_name(save_path.stem + '_best' + save_path.suffix) | |
| callbacks.append(tf.keras.callbacks.ModelCheckpoint( | |
| str(best_model_path), | |
| monitor='val_accuracy', | |
| save_best_only=True, | |
| mode='max', | |
| verbose=1 | |
| )) | |
| if callback_early_stopping: | |
| callbacks.append(tf.keras.callbacks.EarlyStopping( | |
| monitor='val_loss', | |
| patience=early_stopping_patience, | |
| restore_best_weights=True, | |
| verbose=1 | |
| )) | |
| # Initial training (frozen base model) | |
| print(f"Phase 1: Training with frozen base for {epochs} epochs...") | |
| history = model.fit( | |
| train_gen, | |
| epochs=epochs, | |
| validation_data=val_gen, | |
| callbacks=callbacks, | |
| verbose=1 | |
| ) | |
| # Fine-tuning phase | |
| if fine_tune: | |
| print(f"\nPhase 2: Fine-tuning for {fine_tune_epochs} epochs...") | |
| # Unfreeze the base model | |
| base_model = None | |
| for layer in model.layers: | |
| if hasattr(layer, 'layers') and len(layer.layers) > 50: | |
| base_model = layer | |
| break | |
| if base_model is not None: | |
| base_model.trainable = True | |
| # Recompile with lower learning rate | |
| model.compile( | |
| optimizer=tf.keras.optimizers.Adam(learning_rate=fine_tune_lr), | |
| loss='categorical_crossentropy', | |
| metrics=['accuracy'] | |
| ) | |
| # Continue training | |
| history_fine = model.fit( | |
| train_gen, | |
| epochs=fine_tune_epochs, | |
| validation_data=val_gen, | |
| callbacks=callbacks, | |
| verbose=1 | |
| ) | |
| # Merge histories | |
| for key in history_fine.history: | |
| history.history[key].extend(history_fine.history[key]) | |
| # Save final model | |
| model.save(str(save_path)) | |
| print(f"\nModel saved to: {save_path}") | |
| return { | |
| 'history': history.history, | |
| 'epochs_completed': len(history.history['accuracy']), | |
| 'final_train_acc': history.history['accuracy'][-1], | |
| 'final_val_acc': history.history['val_accuracy'][-1], | |
| 'model_path': str(save_path) | |
| } | |
| if __name__ == '__main__': | |
| import argparse | |
| parser = argparse.ArgumentParser(description='Train FirenetCNN model') | |
| parser.add_argument('--train-dir', default='data/forestfire-classifier-dataset/train', | |
| help='Training data directory') | |
| parser.add_argument('--val-dir', default='data/forestfire-classifier-dataset/val', | |
| help='Validation data directory') | |
| parser.add_argument('--model-path', default='models/FirenetCNN.keras', | |
| help='Path to save trained model') | |
| parser.add_argument('--epochs', type=int, default=100, | |
| help='Number of training epochs') | |
| parser.add_argument('--fine-tune', action='store_true', | |
| help='Enable fine-tuning phase') | |
| parser.add_argument('--fine-tune-epochs', type=int, default=10, | |
| help='Number of fine-tuning epochs') | |
| args = parser.parse_args() | |
| results = train( | |
| train_dir=args.train_dir, | |
| val_dir=args.val_dir, | |
| model_save_path=args.model_path, | |
| epochs=args.epochs, | |
| fine_tune=args.fine_tune, | |
| fine_tune_epochs=args.fine_tune_epochs | |
| ) | |
| print(f"\nTraining complete!") | |
| print(f"Final train accuracy: {results['final_train_acc']:.4f}") | |
| print(f"Final val accuracy: {results['final_val_acc']:.4f}") | |