{
"cells": [
{
"cell_type": "markdown",
"id": "c8135598",
"metadata": {},
"source": [
"# Checkpointing and Persistence\n",
"\n",
"This notebook shows how to keep a genetic search reproducible and recoverable. Long-running searches should be able to write intermediate checkpoints, save the fitted search object, and load it again later for inspection or prediction.\n",
"\n",
"## Menu\n",
"\n",
"1. [Create a compact classification problem](#Create-a-compact-classification-problem)\n",
"2. [Configure a search with checkpoints](#Configure-a-search-with-checkpoints)\n",
"3. [Fit with optimizer controls](#Fit-with-optimizer-controls)\n",
"4. [Inspect checkpoint contents](#Inspect-checkpoint-contents)\n",
"5. [Save and reload the fitted search](#Save-and-reload-the-fitted-search)\n",
"6. [Clean up generated files](#Clean-up-generated-files)\n",
"7. [Practical notes](#Practical-notes)"
]
},
{
"cell_type": "markdown",
"id": "702e16ce",
"metadata": {},
"source": [
"## Create a compact classification problem\n",
"\n",
"The breast cancer dataset is small enough for a tutorial, but it still has enough features for the optimizer to make meaningful choices."
]
},
{
"cell_type": "code",
"id": "bb33c789",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-20T05:25:36.742392Z",
"iopub.status.busy": "2026-06-20T05:25:36.741935Z",
"iopub.status.idle": "2026-06-20T05:25:46.772882Z",
"shell.execute_reply": "2026-06-20T05:25:46.771545Z"
},
"ExecuteTime": {
"end_time": "2026-06-20T18:40:42.486293Z",
"start_time": "2026-06-20T18:40:39.002109Z"
}
},
"source": [
"from pathlib import Path\n",
"\n",
"import pandas as pd\n",
"from sklearn.datasets import load_breast_cancer\n",
"from sklearn.ensemble import RandomForestClassifier\n",
"from sklearn.metrics import accuracy_score, balanced_accuracy_score, roc_auc_score\n",
"from sklearn.model_selection import StratifiedKFold, train_test_split\n",
"\n",
"from sklearn_genetic import (\n",
" EvolutionConfig,\n",
" GASearchCV,\n",
" OptimizationConfig,\n",
" PopulationConfig,\n",
" RuntimeConfig,\n",
")\n",
"from sklearn_genetic.callbacks import ConsecutiveStopping, ModelCheckpoint, TimerStopping\n",
"from sklearn_genetic.plots import plot_fitness_evolution\n",
"from sklearn_genetic.schedules import ExponentialAdapter, InverseAdapter\n",
"from sklearn_genetic.space import Categorical, Continuous, Integer\n",
"\n",
"data = load_breast_cancer(as_frame=True)\n",
"X = data.data\n",
"y = data.target\n",
"\n",
"X_train, X_test, y_train, y_test = train_test_split(\n",
" X,\n",
" y,\n",
" test_size=0.25,\n",
" stratify=y,\n",
" random_state=42,\n",
")\n",
"\n",
"X_train.shape, X_test.shape"
],
"outputs": [
{
"data": {
"text/plain": [
"((426, 30), (143, 30))"
]
},
"execution_count": 1,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 1
},
{
"cell_type": "markdown",
"id": "2b14ee47",
"metadata": {},
"source": [
"## Configure a search with checkpoints\n",
"\n",
"`ModelCheckpoint` writes the current estimator configuration and logbook during the fit. This is useful for long runs, interrupted sessions, or for storing the generation history next to experiment artifacts.\n",
"\n",
"The regular `save` and `load` methods are different: they persist the fitted `GASearchCV` object after training, so it can be restored later for prediction and analysis."
]
},
{
"cell_type": "code",
"id": "998e173d",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-20T05:25:46.776327Z",
"iopub.status.busy": "2026-06-20T05:25:46.775815Z",
"iopub.status.idle": "2026-06-20T05:25:46.782960Z",
"shell.execute_reply": "2026-06-20T05:25:46.781921Z"
},
"ExecuteTime": {
"end_time": "2026-06-20T18:40:42.509363800Z",
"start_time": "2026-06-20T18:40:42.489295700Z"
}
},
"source": [
"artifact_dir = Path(\"ga_artifacts\")\n",
"artifact_dir.mkdir(exist_ok=True)\n",
"\n",
"checkpoint_path = artifact_dir / \"breast_cancer_ga_checkpoint.pkl\"\n",
"saved_search_path = artifact_dir / \"breast_cancer_ga_search.pkl\"\n",
"\n",
"param_grid = {\n",
" \"n_estimators\": Integer(40, 160),\n",
" \"max_depth\": Integer(2, 12),\n",
" \"min_samples_leaf\": Integer(1, 8),\n",
" \"max_features\": Continuous(0.25, 1.0),\n",
" \"criterion\": Categorical([\"gini\", \"entropy\", \"log_loss\"]),\n",
" \"class_weight\": Categorical([None, \"balanced\"]),\n",
"}\n",
"\n",
"warm_start_configs = [\n",
" {\n",
" \"n_estimators\": 80,\n",
" \"max_depth\": 5,\n",
" \"min_samples_leaf\": 2,\n",
" \"max_features\": 0.7,\n",
" \"criterion\": \"gini\",\n",
" \"class_weight\": None,\n",
" }\n",
"]\n",
"\n",
"callbacks = [\n",
" ModelCheckpoint(checkpoint_path),\n",
" ConsecutiveStopping(generations=6, metric=\"fitness_best\"),\n",
" TimerStopping(total_seconds=180),\n",
"]"
],
"outputs": [],
"execution_count": 2
},
{
"cell_type": "markdown",
"id": "77e7b2bf",
"metadata": {},
"source": [
"## Fit with optimizer controls\n",
"\n",
"This example combines persistence with smart initial populations, warm-start seeds, adaptive probabilities, local refinement, diversity control, fitness sharing, caching, and automatic parallel execution."
]
},
{
"cell_type": "code",
"id": "8fa639bc",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-20T05:25:46.785468Z",
"iopub.status.busy": "2026-06-20T05:25:46.785122Z",
"iopub.status.idle": "2026-06-20T05:28:43.557430Z",
"shell.execute_reply": "2026-06-20T05:28:43.555958Z"
},
"ExecuteTime": {
"end_time": "2026-06-20T18:43:49.307018Z",
"start_time": "2026-06-20T18:40:42.510361500Z"
}
},
"source": [
"cv = StratifiedKFold(n_splits=3, shuffle=True, random_state=42)\n",
"\n",
"search = GASearchCV(\n",
" estimator=RandomForestClassifier(random_state=42, n_jobs=1),\n",
" cv=cv,\n",
" scoring=\"roc_auc\",\n",
" param_grid=param_grid,\n",
" evolution_config=EvolutionConfig(\n",
" population_size=14,\n",
" generations=10,\n",
" crossover_probability=ExponentialAdapter(initial_value=0.85, end_value=0.45, adaptive_rate=0.08),\n",
" mutation_probability=InverseAdapter(initial_value=0.18, end_value=0.55, adaptive_rate=0.12),\n",
" keep_top_k=4,\n",
" ),\n",
" population_config=PopulationConfig(\n",
" initializer=\"smart\",\n",
" warm_start_configs=warm_start_configs,\n",
" ),\n",
" runtime_config=RuntimeConfig(\n",
" use_cache=True,\n",
" parallel_backend=\"auto\",\n",
" n_jobs=-1,\n",
" verbose=False,\n",
" ),\n",
" optimization_config=OptimizationConfig(\n",
" local_search=True,\n",
" local_search_top_k=2,\n",
" local_search_steps=2,\n",
" local_search_radius=0.12,\n",
" diversity_control=True,\n",
" diversity_threshold=0.2,\n",
" diversity_stagnation_generations=3,\n",
" diversity_mutation_boost=1.8,\n",
" random_immigrants_fraction=0.15,\n",
" fitness_sharing=True,\n",
" sharing_radius=0.25,\n",
" ),\n",
" refit=True,\n",
")\n",
"\n",
"search.fit(X_train, y_train, callbacks=callbacks)\n",
"search.best_params_, search.best_score_"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Checkpoint save in ga_artifacts\\breast_cancer_ga_checkpoint.pkl\n",
"Checkpoint save in ga_artifacts\\breast_cancer_ga_checkpoint.pkl\n",
"Checkpoint save in ga_artifacts\\breast_cancer_ga_checkpoint.pkl\n",
"Checkpoint save in ga_artifacts\\breast_cancer_ga_checkpoint.pkl\n",
"Checkpoint save in ga_artifacts\\breast_cancer_ga_checkpoint.pkl\n",
"Checkpoint save in ga_artifacts\\breast_cancer_ga_checkpoint.pkl\n",
"Checkpoint save in ga_artifacts\\breast_cancer_ga_checkpoint.pkl\n",
"Checkpoint save in ga_artifacts\\breast_cancer_ga_checkpoint.pkl\n",
"Checkpoint save in ga_artifacts\\breast_cancer_ga_checkpoint.pkl\n",
"Checkpoint save in ga_artifacts\\breast_cancer_ga_checkpoint.pkl\n",
"Checkpoint save in ga_artifacts\\breast_cancer_ga_checkpoint.pkl\n",
"INFO: TimerStopping callback met its criteria\n",
"INFO: Stopping the algorithm\n"
]
},
{
"data": {
"text/plain": [
"({'n_estimators': 42,\n",
" 'max_depth': 8,\n",
" 'min_samples_leaf': 4,\n",
" 'max_features': 0.8576422839762377,\n",
" 'criterion': 'gini',\n",
" 'class_weight': None},\n",
" 0.9904600381598474)"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 3
},
{
"cell_type": "code",
"id": "4372ce43",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-20T05:28:43.563691Z",
"iopub.status.busy": "2026-06-20T05:28:43.562971Z",
"iopub.status.idle": "2026-06-20T05:28:43.634385Z",
"shell.execute_reply": "2026-06-20T05:28:43.632233Z"
},
"ExecuteTime": {
"end_time": "2026-06-20T18:43:49.393837400Z",
"start_time": "2026-06-20T18:43:49.319535400Z"
}
},
"source": [
"y_pred = search.predict(X_test)\n",
"y_proba = search.predict_proba(X_test)[:, 1]\n",
"\n",
"pd.Series(\n",
" {\n",
" \"accuracy\": accuracy_score(y_test, y_pred),\n",
" \"balanced_accuracy\": balanced_accuracy_score(y_test, y_pred),\n",
" \"roc_auc\": roc_auc_score(y_test, y_proba),\n",
" }\n",
").to_frame(\"test_score\")"
],
"outputs": [
{
"data": {
"text/plain": [
" test_score\n",
"accuracy 0.951049\n",
"balanced_accuracy 0.945597\n",
"roc_auc 0.993082"
],
"text/html": [
"
\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" test_score | \n",
"
\n",
" \n",
" \n",
" \n",
" | accuracy | \n",
" 0.951049 | \n",
"
\n",
" \n",
" | balanced_accuracy | \n",
" 0.945597 | \n",
"
\n",
" \n",
" | roc_auc | \n",
" 0.993082 | \n",
"
\n",
" \n",
"
\n",
"
"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 4
},
{
"cell_type": "markdown",
"id": "c2863a70",
"metadata": {},
"source": [
"The fit statistics summarize how much work was done. They are useful when comparing runs with different population sizes, smart initialization, caching, local search, or diversity controls."
]
},
{
"cell_type": "code",
"id": "4966cacc",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-20T05:28:43.639202Z",
"iopub.status.busy": "2026-06-20T05:28:43.638620Z",
"iopub.status.idle": "2026-06-20T05:28:43.651899Z",
"shell.execute_reply": "2026-06-20T05:28:43.649796Z"
},
"ExecuteTime": {
"end_time": "2026-06-20T18:43:49.418110200Z",
"start_time": "2026-06-20T18:43:49.394837900Z"
}
},
"source": [
"pd.Series(search.fit_stats_).to_frame(\"value\")"
],
"outputs": [
{
"data": {
"text/plain": [
" value\n",
"evaluated_candidates 298\n",
"unique_candidates 293\n",
"cross_validate_calls 293\n",
"cache_hits 5\n",
"duplicate_candidates 0\n",
"skipped_invalid_candidates 0\n",
"population_parallel_batches 12\n",
"population_serial_batches 0\n",
"random_immigrants 15\n",
"local_refinement_candidates 4"
],
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" value | \n",
"
\n",
" \n",
" \n",
" \n",
" | evaluated_candidates | \n",
" 298 | \n",
"
\n",
" \n",
" | unique_candidates | \n",
" 293 | \n",
"
\n",
" \n",
" | cross_validate_calls | \n",
" 293 | \n",
"
\n",
" \n",
" | cache_hits | \n",
" 5 | \n",
"
\n",
" \n",
" | duplicate_candidates | \n",
" 0 | \n",
"
\n",
" \n",
" | skipped_invalid_candidates | \n",
" 0 | \n",
"
\n",
" \n",
" | population_parallel_batches | \n",
" 12 | \n",
"
\n",
" \n",
" | population_serial_batches | \n",
" 0 | \n",
"
\n",
" \n",
" | random_immigrants | \n",
" 15 | \n",
"
\n",
" \n",
" | local_refinement_candidates | \n",
" 4 | \n",
"
\n",
" \n",
"
\n",
"
"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 5
},
{
"cell_type": "code",
"id": "a289fbd3",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-20T05:28:43.655612Z",
"iopub.status.busy": "2026-06-20T05:28:43.655275Z",
"iopub.status.idle": "2026-06-20T05:28:43.673572Z",
"shell.execute_reply": "2026-06-20T05:28:43.671716Z"
},
"ExecuteTime": {
"end_time": "2026-06-20T18:43:49.447131100Z",
"start_time": "2026-06-20T18:43:49.419113400Z"
}
},
"source": [
"history = pd.DataFrame(search.history)\n",
"history[\n",
" [\n",
" \"gen\",\n",
" \"fitness_best\",\n",
" \"fitness\",\n",
" \"fitness_max\",\n",
" \"unique_individual_ratio\",\n",
" \"genotype_diversity\",\n",
" \"stagnation_generations\",\n",
" ]\n",
"].tail()"
],
"outputs": [
{
"data": {
"text/plain": [
" gen fitness_best fitness fitness_max unique_individual_ratio \\\n",
"6 6 0.988481 0.986634 0.987951 0.785714 \n",
"7 7 0.990460 0.987086 0.990460 0.785714 \n",
"8 8 0.990460 0.986912 0.988905 0.714286 \n",
"9 9 0.990460 0.986813 0.988128 0.785714 \n",
"10 10 0.990460 0.986846 0.989612 0.714286 \n",
"\n",
" genotype_diversity stagnation_generations \n",
"6 0.320513 5 \n",
"7 0.410256 0 \n",
"8 0.294872 1 \n",
"9 0.410256 2 \n",
"10 0.320513 4 "
],
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" gen | \n",
" fitness_best | \n",
" fitness | \n",
" fitness_max | \n",
" unique_individual_ratio | \n",
" genotype_diversity | \n",
" stagnation_generations | \n",
"
\n",
" \n",
" \n",
" \n",
" | 6 | \n",
" 6 | \n",
" 0.988481 | \n",
" 0.986634 | \n",
" 0.987951 | \n",
" 0.785714 | \n",
" 0.320513 | \n",
" 5 | \n",
"
\n",
" \n",
" | 7 | \n",
" 7 | \n",
" 0.990460 | \n",
" 0.987086 | \n",
" 0.990460 | \n",
" 0.785714 | \n",
" 0.410256 | \n",
" 0 | \n",
"
\n",
" \n",
" | 8 | \n",
" 8 | \n",
" 0.990460 | \n",
" 0.986912 | \n",
" 0.988905 | \n",
" 0.714286 | \n",
" 0.294872 | \n",
" 1 | \n",
"
\n",
" \n",
" | 9 | \n",
" 9 | \n",
" 0.990460 | \n",
" 0.986813 | \n",
" 0.988128 | \n",
" 0.785714 | \n",
" 0.410256 | \n",
" 2 | \n",
"
\n",
" \n",
" | 10 | \n",
" 10 | \n",
" 0.990460 | \n",
" 0.986846 | \n",
" 0.989612 | \n",
" 0.714286 | \n",
" 0.320513 | \n",
" 4 | \n",
"
\n",
" \n",
"
\n",
"
"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 6
},
{
"cell_type": "code",
"id": "15bc45a4",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-20T05:28:43.678187Z",
"iopub.status.busy": "2026-06-20T05:28:43.677503Z",
"iopub.status.idle": "2026-06-20T05:28:44.391992Z",
"shell.execute_reply": "2026-06-20T05:28:44.390178Z"
},
"ExecuteTime": {
"end_time": "2026-06-20T18:43:49.722207900Z",
"start_time": "2026-06-20T18:43:49.449640100Z"
}
},
"source": [
"plot_fitness_evolution(search)"
],
"outputs": [
{
"data": {
"text/plain": [
""
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"text/plain": [
""
],
"image/png": "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"
},
"metadata": {},
"output_type": "display_data",
"jetTransient": {
"display_id": null
}
}
],
"execution_count": 7
},
{
"cell_type": "markdown",
"id": "2c51d49c",
"metadata": {},
"source": [
"## Inspect checkpoint contents\n",
"\n",
"The checkpoint callback stores a dictionary with two top-level keys: `estimator_state` and `logbook`. The state is intentionally lightweight and focuses on the search configuration; the logbook contains the generation records."
]
},
{
"cell_type": "code",
"id": "4f2cb5de",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-20T05:28:44.399670Z",
"iopub.status.busy": "2026-06-20T05:28:44.398903Z",
"iopub.status.idle": "2026-06-20T05:28:44.413593Z",
"shell.execute_reply": "2026-06-20T05:28:44.411469Z"
},
"ExecuteTime": {
"end_time": "2026-06-20T18:43:49.775939700Z",
"start_time": "2026-06-20T18:43:49.724206500Z"
}
},
"source": [
"checkpoint = ModelCheckpoint(checkpoint_path).load()\n",
"\n",
"checkpoint.keys(), len(checkpoint[\"logbook\"])"
],
"outputs": [
{
"data": {
"text/plain": [
"(dict_keys(['estimator_state', 'logbook']), 11)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 8
},
{
"cell_type": "code",
"id": "9424a174",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-20T05:28:44.417826Z",
"iopub.status.busy": "2026-06-20T05:28:44.417075Z",
"iopub.status.idle": "2026-06-20T05:28:44.424933Z",
"shell.execute_reply": "2026-06-20T05:28:44.423302Z"
},
"ExecuteTime": {
"end_time": "2026-06-20T18:43:49.796541900Z",
"start_time": "2026-06-20T18:43:49.777341300Z"
}
},
"source": [
"sorted(checkpoint[\"estimator_state\"].keys())"
],
"outputs": [
{
"data": {
"text/plain": [
"['algorithm',\n",
" 'crossover_probability',\n",
" 'cv',\n",
" 'diversity_control',\n",
" 'diversity_mutation_boost',\n",
" 'diversity_stagnation_generations',\n",
" 'diversity_threshold',\n",
" 'estimator',\n",
" 'fitness_sharing',\n",
" 'generations',\n",
" 'local_search',\n",
" 'local_search_radius',\n",
" 'local_search_steps',\n",
" 'local_search_top_k',\n",
" 'mutation_probability',\n",
" 'param_grid',\n",
" 'population_size',\n",
" 'random_immigrants_fraction',\n",
" 'scoring',\n",
" 'sharing_alpha',\n",
" 'sharing_radius']"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 9
},
{
"cell_type": "markdown",
"id": "52ee647e",
"metadata": {},
"source": [
"## Save and reload the fitted search\n",
"\n",
"Use `save` after fitting when you want to preserve the full fitted search object. Reloading into a fresh `GASearchCV` instance restores the fitted estimator, history, best parameters, and prediction methods."
]
},
{
"cell_type": "code",
"id": "79aaf7e7",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-20T05:28:44.438612Z",
"iopub.status.busy": "2026-06-20T05:28:44.437684Z",
"iopub.status.idle": "2026-06-20T05:28:44.588331Z",
"shell.execute_reply": "2026-06-20T05:28:44.586520Z"
},
"ExecuteTime": {
"end_time": "2026-06-20T18:43:49.867748600Z",
"start_time": "2026-06-20T18:43:49.798047900Z"
}
},
"source": [
"search.save(saved_search_path)\n",
"\n",
"restored_search = GASearchCV(\n",
" estimator=RandomForestClassifier(random_state=42, n_jobs=1),\n",
" cv=cv,\n",
" scoring=\"roc_auc\",\n",
" param_grid=param_grid,\n",
")\n",
"restored_search.load(saved_search_path)\n",
"\n",
"restored_predictions = restored_search.predict(X_test)\n",
"\n",
"pd.Series(\n",
" {\n",
" \"same_predictions\": (restored_predictions == y_pred).all(),\n",
" \"restored_accuracy\": accuracy_score(y_test, restored_predictions),\n",
" \"restored_best_score\": restored_search.best_score_,\n",
" }\n",
").to_frame(\"value\")"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"GASearchCV model successfully saved to ga_artifacts\\breast_cancer_ga_search.pkl\n",
"GASearchCV model successfully loaded from ga_artifacts\\breast_cancer_ga_search.pkl\n"
]
},
{
"data": {
"text/plain": [
" value\n",
"same_predictions True\n",
"restored_accuracy 0.951049\n",
"restored_best_score 0.99046"
],
"text/html": [
"\n",
"\n",
"
\n",
" \n",
" \n",
" | \n",
" value | \n",
"
\n",
" \n",
" \n",
" \n",
" | same_predictions | \n",
" True | \n",
"
\n",
" \n",
" | restored_accuracy | \n",
" 0.951049 | \n",
"
\n",
" \n",
" | restored_best_score | \n",
" 0.99046 | \n",
"
\n",
" \n",
"
\n",
"
"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"execution_count": 10
},
{
"cell_type": "markdown",
"id": "efa3de77",
"metadata": {},
"source": [
"## Clean up generated files\n",
"\n",
"Keep this cell commented when you want to inspect the artifacts after the notebook finishes."
]
},
{
"cell_type": "code",
"id": "24bb09e6",
"metadata": {
"execution": {
"iopub.execute_input": "2026-06-20T05:28:44.594162Z",
"iopub.status.busy": "2026-06-20T05:28:44.592998Z",
"iopub.status.idle": "2026-06-20T05:28:44.600980Z",
"shell.execute_reply": "2026-06-20T05:28:44.599075Z"
},
"ExecuteTime": {
"end_time": "2026-06-20T18:43:49.917271500Z",
"start_time": "2026-06-20T18:43:49.871747800Z"
}
},
"source": [
"# for path in [checkpoint_path, saved_search_path]:\n",
"# path.unlink(missing_ok=True)\n",
"# artifact_dir.rmdir()"
],
"outputs": [],
"execution_count": 11
},
{
"cell_type": "markdown",
"id": "21fb1b34",
"metadata": {},
"source": [
"## Practical notes\n",
"\n",
"- Use `ModelCheckpoint` for progress recovery and audit trails during a fit.\n",
"- Use `save` and `load` for fitted search objects that need to be reused for prediction or later analysis.\n",
"- Store checkpoints outside temporary notebook directories for long runs.\n",
"- Keep `random_state` fixed across the estimator, splitter, and search inputs when you need repeatable artifacts."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.14"
}
},
"nbformat": 4,
"nbformat_minor": 5
}