diff --git a/notebook/base_pipeline.ipynb b/notebook/base_pipeline.ipynb index 8f09768..6a5e724 100644 --- a/notebook/base_pipeline.ipynb +++ b/notebook/base_pipeline.ipynb @@ -13,7 +13,10 @@ "sys.path.append('../src')\n", "sys.path.append('../submissions')\n", "\n", - "from utils import Data, Model, Submission\n", + "from utils import load_train_data, load_test_data\n", + "from utils import evaluate_model, save_model\n", + "from utils import save_submission\n", + "\n", "\n", "import pandas as pd\n", "import numpy as np\n", @@ -37,10 +40,22 @@ "cell_type": "code", "execution_count": 2, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(260601, 39)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# Load training data\n", - "train_data = Data.load_train_data()" + "train_data = load_train_data()\n", + "train_data.shape" ] }, { @@ -60,7 +75,6 @@ "metadata": {}, "outputs": [], "source": [ - "\n", "# Prepare data for preprocessing and modelling\n", "TARGET = 'damage_grade'\n", "\n", @@ -131,8 +145,7 @@ ], "source": [ "# Evaluate model performance with base model Logistic Regression\n", - "base_model = Model(base_pipe)\n", - "base_score_valid, base_score_train = base_model.evaluate_model(X_train, X_valid, \n", + "base_score_valid, base_score_train = evaluate_model(base_pipe, X_train, X_valid, \n", " y_train, y_valid)\n", "\n", "print(f\"F1-score of the base model: {base_score_valid :.3f}\")" @@ -142,10 +155,22 @@ "cell_type": "code", "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(86868, 38)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "# Load test data and save predictions into a file for submission\n", - "test_data = Data.load_test_data()" + "# Load test data\n", + "test_data = load_test_data()\n", + "test_data.shape" ] }, { @@ -156,7 +181,7 @@ { "data": { "text/plain": [ - "PosixPath('../submissions/submission1706959851.csv')" + "PosixPath('../submissions/submission1708407950.csv')" ] }, "execution_count": 10, @@ -166,29 +191,29 @@ ], "source": [ "timestamp = datetime.now().timestamp()\n", - "# Create and save a submission file in submissions/\n", - "Submission(base_model, test_data).save_submission(timestamp)" + "# Save predictions in a file for submission\n", + "save_submission(base_pipe, test_data, timestamp, label_enc)" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "PosixPath('../models/model_1706959851.pickle')" + "PosixPath('../models/model_1708407950.pickle')" ] }, - "execution_count": 11, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "# Save model as .pickle file in models/\n", - "base_model.save_model(timestamp)" + "save_model(base_pipe, timestamp)" ] }, { diff --git a/notebook/hyperopt_xgb_classifier.ipynb b/notebook/xgb_hyperopt.ipynb similarity index 98% rename from notebook/hyperopt_xgb_classifier.ipynb rename to notebook/xgb_hyperopt.ipynb index 5a3f228..46ac5e3 100644 --- a/notebook/hyperopt_xgb_classifier.ipynb +++ b/notebook/xgb_hyperopt.ipynb @@ -6,6 +6,8 @@ "metadata": {}, "outputs": [], "source": [ + "from datetime import datetime\n", + "from pathlib import Path\n", "import sys\n", "sys.path.append('../src')\n", "sys.path.append('../submissions')\n", @@ -14,12 +16,9 @@ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", - "from pathlib import Path\n", - "from utils import load_train_data, load_test_data\n", - "from evaluate import evaluate_model\n", + "\n", "from utils import save_submission, save_model, load_model\n", "from encoding import freq_encode, get_house_volume\n", - "from datetime import datetime\n", "\n", "pd.set_option('display.max_columns', None)" ] diff --git a/src/utils.py b/src/utils.py index a57cfc7..9837fc2 100644 --- a/src/utils.py +++ b/src/utils.py @@ -17,68 +17,54 @@ MODEL_DIR = Path('../models') -class Data: - """Load implements methods for loading train and test data from files.""" +# Mehods for loading data +def load_train_data(): + """ + Load the training data from the local file or from the URL. + + Args: + local: boolean, if True, load from local file, else from URL - @staticmethod - def load_train_data(): - """ - Load the training data from the local file or from the URL. + Returns: + train_data: DataFrame with training data + """ + # Define path to data files + path_train_values = DATA_DIR / TRAIN_VALUES_FILE + path_train_labels = DATA_DIR / TRAIN_LABELS_FILE + + # Read the training values and labels + train_values = pd.read_csv(path_train_values, index_col='building_id') + train_labels = pd.read_csv(path_train_labels, index_col='building_id') + + # Join training values and labels in one DataFrame + train_data = train_values.join(train_labels) + + return train_data - Args: - local: boolean, if True, load from local file, else from URL - - Returns: - train_data: DataFrame with training data - """ - # Define path to data files - path_train_values = DATA_DIR / TRAIN_VALUES_FILE - path_train_labels = DATA_DIR / TRAIN_LABELS_FILE - - # Read the training values and labels - train_values = pd.read_csv(path_train_values, index_col='building_id') - train_labels = pd.read_csv(path_train_labels, index_col='building_id') - - # Join training values and labels in one DataFrame - train_data = train_values.join(train_labels) - - return train_data - - - @staticmethod - def load_test_data(): - """ - Load the test data from the local file or from the URL. - Args: - local: boolean, if True, load from local file, else from URL +def load_test_data(): + """ + Load the test data from the local file or from the URL. - Returns: - test_data: DataFrame with test data - """ - # Define path to test data file - path_test_values = DATA_DIR / TEST_VALUES_FILE - - # Read the test set - test_values = pd.read_csv(path_test_values, index_col='building_id') - - return test_values - - -class Model: - """""" - def __init__(self, estimator=None) -> None: - self.estimator = estimator - - - def train_model(self, X_train, y_train): - """""" - self.estimator = self.estimator.fit(X_train, y_train) - - - def evaluate_model(self, X_train, X_valid, y_train, y_valid): + Args: + local: boolean, if True, load from local file, else from URL + + Returns: + test_data: DataFrame with test data + """ + # Define path to test data file + path_test_values = DATA_DIR / TEST_VALUES_FILE + + # Read the test set + test_values = pd.read_csv(path_test_values, index_col='building_id') + + return test_values + + +# Methods for evaluating and saving models +def evaluate_model(pipe, X_train, X_valid, y_train, y_valid): """ - Evaluate the model using the F1 score. + Evaluate the model using the F1 score (micro). Args: pipe: trained model @@ -91,142 +77,118 @@ def evaluate_model(self, X_train, X_valid, y_train, y_valid): score_valid: F1 score on validation data score_train: F1 score on training data """ - self.train_model(X_train, y_train) - preds_valid = self.estimator.predict(X_valid) - preds_train = self.estimator.predict(X_train) - + # Train model + pipe.fit(X_train, y_train) + # Make predictions on validation and training data + preds_valid = pipe.predict(X_valid) + preds_train = pipe.predict(X_train) + # Calculate F1 score (micro) on validation and training data score_valid = f1_score(y_valid, preds_valid, average='micro') score_train = f1_score(y_train, preds_train, average='micro') return score_valid, score_train - - - def make_predictions(self, data): - """ - Make predictions using the estimator on the test_data. - Args: - estimator: trained model - test_data: test data to make predictions on - Returns: - predictions: array of predictions - """ - #TODO: Warning if model is not yet trained - - predictions = self.estimator.predict(data) - # XGB Classifiers assume that target is encoded starting from 0 - # if "XGB" in str(self.estimator["model"]): - # predictions += 1 +# Methods for saving and loading models +def save_model(pipe, timestamp): + """ + Save the model to a pickle file. - return predictions - + Args: + estimator: trained model + timestamp: current timestamp - # @staticmethod - def save_model(self, timestamp): - """ - Save the model to a pickle file. + Returns: + None + """ + # Create filename based on current timestamp + filename = 'model_' + str(int(timestamp)) + '.pickle' - Args: - estimator: trained model - timestamp: current timestamp - - Returns: - None - """ - # Create filename based on current timestamp - filename = 'model_' + str(int(timestamp)) + '.pickle' + # Create model directory if it does not exist: + if not os.path.exists(MODEL_DIR): + os.mkdir(MODEL_DIR) - # Create model directory if it does not exist: - if not os.path.exists(MODEL_DIR): - os.mkdir(MODEL_DIR) + # Check that filename does not already exist before saving + filename_match = glob.glob(str(MODEL_DIR / filename)) - # Check that filename does not already exist before saving - filename_match = glob.glob(str(MODEL_DIR / filename)) + if filename_match: + print(f"WARNING: this file already exists!") + else: + pickle.dump(pipe, open(str(MODEL_DIR / filename), "wb")) - if filename_match: - print(f"WARNING: this file already exists!") - else: - pickle.dump(self.estimator, open(str(MODEL_DIR / filename), "wb")) + return MODEL_DIR / filename - return MODEL_DIR / filename - - - def load_model(self, file_path): - """ - # Load a model from a pickle file. - # Args: - # file_path: path to the pickle file +def load_model(file_path): + """ +# Load a model from a pickle file. - # Returns: - # estimator: the trained model - # """ - self.estimator = pickle.load(open(file_path, "rb")) - return self.estimator +# Args: +# file_path: path to the pickle file +# Returns: +# estimator: the trained model +# """ + return pickle.load(open(file_path, "rb")) -class Submission: - """""" - def __init__(self, estimator, data): - self.estimator = estimator - self.data = data - - # @staticmethod - def _format_submission(self): - """ - Format predictions into a DataFrame that matches the submission format. - Args: - estimator: trained model - test_data: test data to make predictions on +# Methods for saving submissions +def _format_submission(pipe, test_data, label_encoder=None): + """ + Format predictions into a DataFrame that matches the submission format. - Returns: - my_submission: DataFrame with predictions in the correct format - """ - predictions = Model.make_predictions(self.estimator, self.data) - - submission_format = pd.read_csv(SUBMIT_DIR / SUBMIT_TEMPLATE_FILE, - index_col='building_id') - - my_submission = pd.DataFrame(data=predictions, - columns=submission_format.columns, - index=submission_format.index) - - return my_submission - - - # @staticmethod - def save_submission(self, timestamp): - """ - Save the submission to a csv file. + Args: + estimator: trained model + test_data: test data to make predictions on - Args: - estimator: trained model - test_data: test data to make predictions on - timestamp: current timestamp - - Returns: - None - """ - # predictions = make_predictions(estimator, test_data) - submission = self._format_submission() - - # Create filename based on current timestamp - filename = 'submission' + str(int(timestamp)) + '.csv' - - # Create submissions directory if it does not exist: - if not os.path.exists(SUBMIT_DIR): - os.mkdir(SUBMIT_DIR) - - # Check that filename does not already exist before saving - filename_match = glob.glob(str(SUBMIT_DIR / filename)) - - if filename_match: - print(f"WARNING: this file already exists!") - else: - submission.to_csv(SUBMIT_DIR / filename) - - return SUBMIT_DIR / filename + Returns: + my_submission: DataFrame with predictions in the correct format + """ + predictions = pipe.predict(test_data) + + if label_encoder: + predictions = label_encoder.inverse_transform(predictions) + + submission_format = pd.read_csv(SUBMIT_DIR / SUBMIT_TEMPLATE_FILE, + index_col='building_id') + + my_submission = pd.DataFrame(data=predictions, + columns=submission_format.columns, + index=submission_format.index) + + return my_submission + + +def save_submission(pipe, test_data, timestamp, label_encoder=None): + """ + Save the submission to a csv file. + + Args: + estimator: trained model + test_data: test data to make predictions on + timestamp: current timestamp + + Returns: + None + """ + # predictions = make_predictions(estimator, test_data) + submission = _format_submission(pipe, test_data, label_encoder) + + # Create filename based on current timestamp + filename = 'submission' + str(int(timestamp)) + '.csv' + + # Create submissions directory if it does not exist: + if not os.path.exists(SUBMIT_DIR): + os.mkdir(SUBMIT_DIR) + + # Check that filename does not already exist before saving + filename_match = glob.glob(str(SUBMIT_DIR / filename)) + + if filename_match: + print(f"WARNING: this file already exists!") + else: + submission.to_csv(SUBMIT_DIR / filename) + + return SUBMIT_DIR / filename \ No newline at end of file