| import os |
| import pandas as pd |
| import pytest |
| import torch |
| from unittest.mock import MagicMock, patch |
| from src.components.model_evaluation import Model_Evaluation |
| from src.entity.config_entity import ModelEvaluationConfig, ModelTrainingConfig |
| from src.entity.artifact_entity import DataTransformationArtifact, ModelTrainingArtifact, ModelEvaluationArtifact |
|
|
| @pytest.mark.asyncio |
| async def test_model_evaluation_initiate_success(dummy_dataframe_transformed, temp_artifact_dir): |
| """Test that Model_Evaluation successfully runs evaluation on test data, |
| saves metrics, plots, and logs to MLflow (mocked) without hitting remote servers. |
| """ |
| |
| train_path = os.path.join(temp_artifact_dir, "train.csv") |
| val_path = os.path.join(temp_artifact_dir, "val.csv") |
| test_path = os.path.join(temp_artifact_dir, "test.csv") |
| images_path = os.path.join(temp_artifact_dir, "images") |
| |
| |
| dummy_dataframe_transformed.to_csv(test_path, index=False) |
| |
| |
| data_transformation_artifact = DataTransformationArtifact( |
| train_path=train_path, |
| test_path=test_path, |
| val_path=val_path, |
| images_path=images_path |
| ) |
| |
| model_training_artifact = ModelTrainingArtifact( |
| model_path=os.path.join(temp_artifact_dir, "checkpoints", "test_model.pt"), |
| is_trained=True, |
| message="Model trained" |
| ) |
| |
| |
| eval_dir = os.path.join(temp_artifact_dir, "model_evaluation") |
| model_evaluation_config = ModelEvaluationConfig( |
| evaluation_artifact_dir=eval_dir, |
| metrics_file_name="metrics.yaml", |
| loss_plot_file_name="loss_plot.png", |
| confusion_matrix_file_name="confusion_matrix.png", |
| mlflow_experiment_name="test-experiment", |
| mlflow_run_name="test-run" |
| ) |
| |
| cache_dir = os.path.join(temp_artifact_dir, "cache") |
| os.makedirs(cache_dir, exist_ok=True) |
| |
| model_training_config = ModelTrainingConfig( |
| epochs=1, |
| patience=1, |
| batch_size=2, |
| model_name="test_model.pt", |
| model_dir=os.path.join(temp_artifact_dir, "checkpoints"), |
| cache_dir=cache_dir |
| ) |
| |
| |
| num_samples = len(dummy_dataframe_transformed) |
| img_feats = torch.randn((num_samples, model_training_config.image_feature_output)) |
| txt_feats = torch.randn((num_samples, model_training_config.text_feature_output)) |
| labels = torch.tensor(dummy_dataframe_transformed['label'].values, dtype=torch.float32) |
| |
| torch.save(img_feats, os.path.join(cache_dir, "img_feats.pt")) |
| torch.save(txt_feats, os.path.join(cache_dir, "txt_feats.pt")) |
| torch.save(labels, os.path.join(cache_dir, "labels.pt")) |
| |
| |
| with patch("dagshub.auth.add_app_token") as mock_add_token, \ |
| patch("dagshub.init") as mock_dag_init, \ |
| patch("src.components.model_evaluation.mlflow") as mock_mlflow: |
| |
| |
| mock_run = MagicMock() |
| mock_mlflow.start_run.return_value = mock_run |
| |
| |
| with patch("src.entity.model.MyModel.load_model") as mock_load_model: |
| evaluator = Model_Evaluation( |
| model_evaluation_config=model_evaluation_config, |
| data_transformation_artifact=data_transformation_artifact, |
| model_training_artifact=model_training_artifact, |
| model_training_config=model_training_config |
| ) |
| |
| |
| evaluator.model.train_loss = [0.5, 0.4] |
| evaluator.model.val_loss = [0.6, 0.45] |
| |
| |
| artifact = await evaluator.initiate() |
| |
| |
| assert isinstance(artifact, ModelEvaluationArtifact) |
| assert artifact.is_evaluated is True |
| assert os.path.isfile(artifact.metrics_file_path) |
| assert os.path.isfile(artifact.loss_plot_path) |
| assert os.path.isfile(artifact.confusion_matrix_path) |
| |
| |
| mock_mlflow.set_experiment.assert_called_once() |
| mock_mlflow.start_run.assert_called_once_with(run_name=model_evaluation_config.mlflow_run_name) |
| mock_mlflow.log_params.assert_called_once() |
| |
| |
| with open(artifact.metrics_file_path, "r") as f: |
| import yaml |
| metrics = yaml.safe_load(f) |
| for key in ["train_loss", "val_loss", "test_accuracy", "test_precision", "test_recall", "test_f1_score"]: |
| assert key in metrics |
|
|