{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceType":"competition","sourceId":5048,"databundleVersionId":868335}],"dockerImageVersionId":31329,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import json\nimport os\nimport random\nfrom dataclasses import dataclass, asdict\nfrom pathlib import Path\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nimport cv2\n\nimport numpy as np\nimport pandas as pd\nimport tensorflow as tf\nfrom sklearn.metrics import classification_report, confusion_matrix\nfrom sklearn.model_selection import GroupShuffleSplit\nfrom sklearn.utils.class_weight import compute_class_weight","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:11.340874Z","iopub.execute_input":"2026-04-25T08:00:11.341313Z","iopub.status.idle":"2026-04-25T08:00:44.581693Z","shell.execute_reply.started":"2026-04-25T08:00:11.341281Z","shell.execute_reply":"2026-04-25T08:00:44.580692Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"@dataclass\nclass CFG:\n    seed: int = 42\n    num_classes: int = 10\n    image_size: int = 380\n    batch_size: int = 24\n    warmup_epochs: int = 12\n    finetune_epochs: int = 15\n    warmup_lr: float = 1e-3\n    finetune_lr: float = 1e-5\n    weight_decay: float = 1e-4\n    dropout: float = 0.25\n    valid_size: float = 0.2\n    auto_tune: int = tf.data.AUTOTUNE\n\n    train_csv: str = \"/kaggle/input/competitions/state-farm-distracted-driver-detection/driver_imgs_list.csv\"\n    train_dir: str = \"/kaggle/input/competitions/state-farm-distracted-driver-detection/imgs/train\"\n    test_dir: str = \"/kaggle/input/competitions/state-farm-distracted-driver-detection/imgs/test\"\n    output_dir: str = \"/kaggle/working\"\n    model_name: str = \"efficientnetv2_b2\"\n\ncfg = CFG()\nCLASS_NAMES = [f\"c{i}\" for i in range(cfg.num_classes)]\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.583190Z","iopub.execute_input":"2026-04-25T08:00:44.583846Z","iopub.status.idle":"2026-04-25T08:00:44.592963Z","shell.execute_reply.started":"2026-04-25T08:00:44.583793Z","shell.execute_reply":"2026-04-25T08:00:44.591987Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def seed_everything(seed: int) -> None:\n    os.environ[\"PYTHONHASHSEED\"] = str(seed)\n    random.seed(seed)\n    np.random.seed(seed)\n    tf.random.set_seed(seed)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.593829Z","iopub.execute_input":"2026-04-25T08:00:44.594173Z","iopub.status.idle":"2026-04-25T08:00:44.608332Z","shell.execute_reply.started":"2026-04-25T08:00:44.594150Z","shell.execute_reply":"2026-04-25T08:00:44.607440Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def enable_mixed_precision() -> None:\n    try:\n        tf.keras.mixed_precision.set_global_policy(\"mixed_float16\")\n        print(\"Mixed precision enabled.\")\n    except Exception:\n        print(\"Mixed precision not enabled.\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.609959Z","iopub.execute_input":"2026-04-25T08:00:44.610816Z","iopub.status.idle":"2026-04-25T08:00:44.631240Z","shell.execute_reply.started":"2026-04-25T08:00:44.610790Z","shell.execute_reply":"2026-04-25T08:00:44.630312Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_dataframe(csv_path: str, train_dir: str) -> pd.DataFrame:\n    df = pd.read_csv(csv_path).copy()\n    df[\"label\"] = df[\"classname\"].str.replace(\"c\", \"\", regex=False).astype(int)\n    df[\"image_path\"] = df.apply(\n        lambda row: str(Path(train_dir) / row[\"classname\"] / row[\"img\"]),\n        axis=1,\n    )\n    return df[[\"subject\", \"classname\", \"label\", \"img\", \"image_path\"]].rename(\n        columns={\"subject\": \"driver_id\"}\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.632251Z","iopub.execute_input":"2026-04-25T08:00:44.632459Z","iopub.status.idle":"2026-04-25T08:00:44.648537Z","shell.execute_reply.started":"2026-04-25T08:00:44.632438Z","shell.execute_reply":"2026-04-25T08:00:44.647692Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def subject_split(df: pd.DataFrame, valid_size: float, seed: int):\n    splitter = GroupShuffleSplit(n_splits=1, test_size=valid_size, random_state=seed)\n    train_idx, valid_idx = next(splitter.split(df, y=df[\"label\"], groups=df[\"driver_id\"]))\n    train_df = df.iloc[train_idx].reset_index(drop=True)\n    valid_df = df.iloc[valid_idx].reset_index(drop=True)\n    return train_df, valid_df","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.649494Z","iopub.execute_input":"2026-04-25T08:00:44.649886Z","iopub.status.idle":"2026-04-25T08:00:44.663503Z","shell.execute_reply.started":"2026-04-25T08:00:44.649847Z","shell.execute_reply":"2026-04-25T08:00:44.662688Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def decode_and_resize(path, label=None, training=False):\n    image = tf.io.read_file(path)\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = tf.image.resize(image, [cfg.image_size, cfg.image_size], method=\"bicubic\")\n    image = tf.cast(image, tf.float32)\n    if label is None:\n        return image\n    return image, label","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.664582Z","iopub.execute_input":"2026-04-25T08:00:44.664904Z","iopub.status.idle":"2026-04-25T08:00:44.677898Z","shell.execute_reply.started":"2026-04-25T08:00:44.664869Z","shell.execute_reply":"2026-04-25T08:00:44.677330Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_augmenter():\n    return tf.keras.Sequential(\n        [\n            tf.keras.layers.RandomFlip(\"horizontal\"),\n            tf.keras.layers.RandomZoom(height_factor=0.1, width_factor=0.1),\n            tf.keras.layers.RandomTranslation(height_factor=0.06, width_factor=0.06),\n            tf.keras.layers.RandomRotation(factor=0.05),\n            tf.keras.layers.RandomContrast(factor=0.2),\n            tf.keras.layers.RandomBrightness(0.2),\n        ],\n        name=\"augmenter\",\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.679001Z","iopub.execute_input":"2026-04-25T08:00:44.679298Z","iopub.status.idle":"2026-04-25T08:00:44.696428Z","shell.execute_reply.started":"2026-04-25T08:00:44.679275Z","shell.execute_reply":"2026-04-25T08:00:44.695847Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def make_dataset(paths, labels=None, training=False):\n    if labels is None:\n        ds = tf.data.Dataset.from_tensor_slices(paths)\n        ds = ds.map(decode_and_resize, num_parallel_calls=cfg.auto_tune)\n    else:\n        ds = tf.data.Dataset.from_tensor_slices((paths, labels))\n        ds = ds.map(\n            lambda path, label: decode_and_resize(path, label, training),\n            num_parallel_calls=cfg.auto_tune,\n        )\n\n    if training:\n        ds = ds.shuffle(min(len(paths), 4096), seed=cfg.seed, reshuffle_each_iteration=True)\n\n    ds = ds.batch(cfg.batch_size).prefetch(cfg.auto_tune)\n    return ds","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.697342Z","iopub.execute_input":"2026-04-25T08:00:44.697930Z","iopub.status.idle":"2026-04-25T08:00:44.715372Z","shell.execute_reply.started":"2026-04-25T08:00:44.697907Z","shell.execute_reply":"2026-04-25T08:00:44.714753Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_model():\n    inputs = tf.keras.Input(shape=(cfg.image_size, cfg.image_size, 3))\n    augmenter = build_augmenter()\n\n    x = augmenter(inputs)\n    x = tf.keras.layers.Lambda(\n        tf.keras.applications.efficientnet_v2.preprocess_input,\n        name=\"preprocess\",\n    )(x)\n    base_model = tf.keras.applications.EfficientNetV2B2(\n        include_top=False,\n        weights=\"imagenet\",\n        input_tensor=x,\n        pooling=\"avg\",\n    )\n    base_model.trainable = False\n\n    x = tf.keras.layers.Dropout(cfg.dropout)(base_model.output)\n    outputs = tf.keras.layers.Dense(\n        cfg.num_classes,\n        activation=\"softmax\",\n        dtype=\"float32\",\n    )(x)\n\n    model = tf.keras.Model(inputs=inputs, outputs=outputs)\n    return model, base_model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.717991Z","iopub.execute_input":"2026-04-25T08:00:44.718728Z","iopub.status.idle":"2026-04-25T08:00:44.730370Z","shell.execute_reply.started":"2026-04-25T08:00:44.718703Z","shell.execute_reply":"2026-04-25T08:00:44.729528Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def compile_model(model, learning_rate):\n    optimizer = tf.keras.optimizers.AdamW(\n        learning_rate=learning_rate,\n        weight_decay=cfg.weight_decay,\n    )\n    loss = tf.keras.losses.SparseCategoricalCrossentropy()\n    model.compile(\n        optimizer=optimizer,\n        loss=loss,\n        metrics=[\"accuracy\"],\n    )","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.731373Z","iopub.execute_input":"2026-04-25T08:00:44.731694Z","iopub.status.idle":"2026-04-25T08:00:44.746713Z","shell.execute_reply.started":"2026-04-25T08:00:44.731644Z","shell.execute_reply":"2026-04-25T08:00:44.746100Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_callbacks(stage_name: str):\n    output_dir = Path(cfg.output_dir)\n    output_dir.mkdir(parents=True, exist_ok=True)\n    checkpoint_path = output_dir / f\"best_{cfg.model_name}_{stage_name}.keras\"\n    callbacks = [\n        tf.keras.callbacks.ModelCheckpoint(\n            filepath=str(checkpoint_path),\n            monitor=\"val_accuracy\",\n            mode=\"max\",\n            save_best_only=True,\n            verbose=1,\n        ),\n        tf.keras.callbacks.EarlyStopping(\n            monitor=\"val_accuracy\",\n            mode=\"max\",\n            patience=3,\n            restore_best_weights=True,\n            verbose=1,\n        ),\n        tf.keras.callbacks.ReduceLROnPlateau(\n            monitor=\"val_loss\",\n            factor=0.5,\n            patience=1,\n            min_lr=1e-7,\n            verbose=1,\n        ),\n    ]\n    return callbacks, checkpoint_path","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.747923Z","iopub.execute_input":"2026-04-25T08:00:44.748247Z","iopub.status.idle":"2026-04-25T08:00:44.765675Z","shell.execute_reply.started":"2026-04-25T08:00:44.748212Z","shell.execute_reply":"2026-04-25T08:00:44.765085Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def get_class_weights(train_df):\n    weights = compute_class_weight(\n        class_weight=\"balanced\",\n        classes=np.unique(train_df[\"label\"]),\n        y=train_df[\"label\"]\n    )\n    weights = dict(enumerate(weights))\n    weights[8] *= 1.5   \n    weights[9] *= 1.8  \n    weights[7] *= 1.3  \n    return weights","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.766822Z","iopub.execute_input":"2026-04-25T08:00:44.767507Z","iopub.status.idle":"2026-04-25T08:00:44.781222Z","shell.execute_reply.started":"2026-04-25T08:00:44.767460Z","shell.execute_reply":"2026-04-25T08:00:44.780444Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def train_model(train_ds, valid_ds, class_weights):\n    model, base_model = build_model()\n\n    print(\"Stage 1: warmup head training\")\n    compile_model(model, cfg.warmup_lr)\n    warmup_callbacks, _ = get_callbacks(\"warmup\")\n    model.fit(\n        train_ds,\n        validation_data=valid_ds,\n        epochs=cfg.warmup_epochs,\n        callbacks=warmup_callbacks,\n        class_weight=class_weights,\n        verbose=1,\n    )\n\n    print(\"Stage 2: fine-tuning backbone\")\n    base_model.trainable = True\n    for layer in base_model.layers[: int(len(base_model.layers) * 0.2)]:\n        layer.trainable = False\n\n    compile_model(model, cfg.finetune_lr)\n    finetune_callbacks, best_path = get_callbacks(\"finetune\")\n    history = model.fit(\n        train_ds,\n        validation_data=valid_ds,\n        epochs=cfg.finetune_epochs,\n        callbacks=finetune_callbacks,\n        class_weight=class_weights,\n        verbose=1,\n    )\n    model.load_weights(best_path)\n    return model, history, best_path","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.782026Z","iopub.execute_input":"2026-04-25T08:00:44.782387Z","iopub.status.idle":"2026-04-25T08:00:44.798231Z","shell.execute_reply.started":"2026-04-25T08:00:44.782365Z","shell.execute_reply":"2026-04-25T08:00:44.797616Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"activity_map = {'c0': 'Safe driving', \n                'c1': 'Texting - right', \n                'c2': 'Talking on the phone - right', \n                'c3': 'Texting - left', \n                'c4': 'Talking on the phone - left', \n                'c5': 'Operating the radio', \n                'c6': 'Drinking', \n                'c7': 'Reaching behind', \n                'c8': 'Hair and makeup', \n                'c9': 'Talking to passenger'}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.798990Z","iopub.execute_input":"2026-04-25T08:00:44.799261Z","iopub.status.idle":"2026-04-25T08:00:44.815704Z","shell.execute_reply.started":"2026-04-25T08:00:44.799242Z","shell.execute_reply":"2026-04-25T08:00:44.815092Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_confusion_matrix(cm, class_names, normalized=False):\n    plt.figure(figsize=(10, 8))\n\n    sns.heatmap(\n        cm,\n        annot=True,\n        fmt=\".2f\" if normalized else \"d\",\n        cmap=\"Blues\",\n        xticklabels=class_names,\n        yticklabels=class_names,\n        cbar=True\n    )\n\n    plt.xlabel(\"Predicted Label\")\n    plt.ylabel(\"True Label\")\n    plt.title(\"Confusion Matrix\")\n\n    plt.xticks(rotation=45, ha=\"right\")\n    plt.yticks(rotation=0)\n\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.816664Z","iopub.execute_input":"2026-04-25T08:00:44.816958Z","iopub.status.idle":"2026-04-25T08:00:44.830459Z","shell.execute_reply.started":"2026-04-25T08:00:44.816928Z","shell.execute_reply":"2026-04-25T08:00:44.829890Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def evaluate_model(model, valid_ds, valid_df: pd.DataFrame):\n    print(\"\\nValidation metrics\")\n    metrics = model.evaluate(valid_ds, verbose=1)\n    print(dict(zip(model.metrics_names, metrics)))\n\n    probs = model.predict(valid_ds, verbose=1)\n    preds = probs.argmax(axis=1)\n    y_true = valid_df[\"label\"].to_numpy()\n\n    report = classification_report(\n        y_true,\n        preds,\n        target_names=CLASS_NAMES,\n        digits=4,\n        zero_division=0,\n    )\n    cm = confusion_matrix(y_true, preds)\n\n    print(\"\\nClassification report\")\n    print(report)\n\n    with open(Path(cfg.output_dir) / \"classification_report.txt\", \"w\", encoding=\"utf-8\") as f:\n        f.write(report)\n    \n    labels = [activity_map[c] for c in CLASS_NAMES]\n    plot_confusion_matrix(cm, labels)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.831258Z","iopub.execute_input":"2026-04-25T08:00:44.831494Z","iopub.status.idle":"2026-04-25T08:00:44.847902Z","shell.execute_reply.started":"2026-04-25T08:00:44.831472Z","shell.execute_reply":"2026-04-25T08:00:44.847093Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def predict_image(model, image_path: str):\n    image = decode_and_resize(tf.constant(image_path))\n    probs = model.predict(tf.expand_dims(image, axis=0), verbose=0)[0]\n    pred_idx = int(np.argmax(probs))\n    return {\n        \"image_path\": image_path,\n        \"predicted_class\": CLASS_NAMES[pred_idx],\n        \"confidence\": float(probs[pred_idx]),\n        \"probabilities\": {CLASS_NAMES[i]: float(probs[i]) for i in range(cfg.num_classes)},\n    }\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.848729Z","iopub.execute_input":"2026-04-25T08:00:44.849017Z","iopub.status.idle":"2026-04-25T08:00:44.865936Z","shell.execute_reply.started":"2026-04-25T08:00:44.848997Z","shell.execute_reply":"2026-04-25T08:00:44.865019Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def save_run_metadata(train_df: pd.DataFrame, valid_df: pd.DataFrame, best_path: Path):\n    metadata = {\n        \"config\": asdict(cfg),\n        \"train_samples\": int(len(train_df)),\n        \"valid_samples\": int(len(valid_df)),\n        \"train_drivers\": sorted(train_df[\"driver_id\"].unique().tolist()),\n        \"valid_drivers\": sorted(valid_df[\"driver_id\"].unique().tolist()),\n        \"best_model_path\": str(best_path),\n    }\n    with open(Path(cfg.output_dir) / \"run_metadata.json\", \"w\", encoding=\"utf-8\") as f:\n        json.dump(metadata, f, indent=2)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.866956Z","iopub.execute_input":"2026-04-25T08:00:44.867286Z","iopub.status.idle":"2026-04-25T08:00:44.881101Z","shell.execute_reply.started":"2026-04-25T08:00:44.867263Z","shell.execute_reply":"2026-04-25T08:00:44.880444Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"seed_everything(cfg.seed)\nenable_mixed_precision()\n\nprint(\"TensorFlow version:\", tf.__version__)\nprint(\"GPU available:\", bool(tf.config.list_physical_devices(\"GPU\")))\n\ndf = build_dataframe(cfg.train_csv, cfg.train_dir)\ntrain_df, valid_df = subject_split(df, cfg.valid_size, cfg.seed)\n\nprint(\"Train samples:\", len(train_df))\nprint(\"Valid samples:\", len(valid_df))\n\ntrain_ds = make_dataset(\n    train_df[\"image_path\"].to_numpy(),\n    train_df[\"label\"].to_numpy(),\n    training=True,\n)\n\nvalid_ds = make_dataset(\n    valid_df[\"image_path\"].to_numpy(),\n    valid_df[\"label\"].to_numpy(),\n    training=False,\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:44.881883Z","iopub.execute_input":"2026-04-25T08:00:44.882196Z","iopub.status.idle":"2026-04-25T08:00:49.344425Z","shell.execute_reply.started":"2026-04-25T08:00:44.882175Z","shell.execute_reply":"2026-04-25T08:00:49.343729Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class_weights = get_class_weights(train_df)\nmodel, history, best_path = train_model(train_ds, valid_ds, class_weights)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T08:00:49.345337Z","iopub.execute_input":"2026-04-25T08:00:49.345597Z","iopub.status.idle":"2026-04-25T09:09:31.186978Z","shell.execute_reply.started":"2026-04-25T08:00:49.345574Z","shell.execute_reply":"2026-04-25T09:09:31.185557Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"evaluate_model(model, valid_ds, valid_df)\nsave_run_metadata(train_df, valid_df, best_path)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T09:09:31.189091Z","iopub.execute_input":"2026-04-25T09:09:31.189606Z","iopub.status.idle":"2026-04-25T09:10:23.241870Z","shell.execute_reply.started":"2026-04-25T09:09:31.189576Z","shell.execute_reply":"2026-04-25T09:10:23.240876Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def plot_training_curves(history):\n    acc = history.history['accuracy']\n    val_acc = history.history['val_accuracy']\n    loss = history.history['loss']\n    val_loss = history.history['val_loss']\n\n    epochs = range(1, len(acc) + 1)\n\n    plt.figure(figsize=(14, 5))\n\n    plt.subplot(1, 2, 1)\n    plt.plot(epochs, acc, 'o-', label='Train Accuracy')\n    plt.plot(epochs, val_acc, 'o-', label='Validation Accuracy')\n    plt.title('Training vs Validation Accuracy')\n    plt.xlabel('Epochs')\n    plt.ylabel('Accuracy')\n    plt.legend()\n    plt.grid(True)\n\n    plt.subplot(1, 2, 2)\n    plt.plot(epochs, loss, 'o-', label='Train Loss')\n    plt.plot(epochs, val_loss, 'o-', label='Validation Loss')\n    plt.title('Training vs Validation Loss')\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')\n    plt.legend()\n    plt.grid(True)\n\n    plt.tight_layout()\n    plt.show()","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plot_training_curves(history)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def show_prediction(idx, model=model, df=valid_df):\n    image_path = df.loc[idx, \"image_path\"]\n    img = cv2.imread(image_path)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n\n    result = predict_image(model, image_path)\n\n    readable_probs = {\n        activity_map[k]: v for k, v in result['probabilities'].items()\n    }\n\n    for cls, prob in sorted(readable_probs.items(), key=lambda x: x[1], reverse=True):\n        print(f\"{cls}: {prob:.3f}\")\n\n    label = activity_map[result['predicted_class']]\n    confidence = result['confidence']\n\n    plt.imshow(img)\n    plt.title(f\"Predicted: {label} ({confidence:.3f})\")\n    plt.axis(\"off\")\n    plt.show()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T09:13:22.270836Z","iopub.execute_input":"2026-04-25T09:13:22.271568Z","iopub.status.idle":"2026-04-25T09:13:22.278885Z","shell.execute_reply.started":"2026-04-25T09:13:22.271530Z","shell.execute_reply":"2026-04-25T09:13:22.277837Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"show_prediction(1)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T09:13:30.910798Z","iopub.execute_input":"2026-04-25T09:13:30.911698Z","iopub.status.idle":"2026-04-25T09:13:41.013268Z","shell.execute_reply.started":"2026-04-25T09:13:30.911663Z","shell.execute_reply":"2026-04-25T09:13:41.012163Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"show_prediction(997)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T09:14:02.597806Z","iopub.execute_input":"2026-04-25T09:14:02.598277Z","iopub.status.idle":"2026-04-25T09:14:02.913720Z","shell.execute_reply.started":"2026-04-25T09:14:02.598247Z","shell.execute_reply":"2026-04-25T09:14:02.912975Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"show_prediction(2088)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T09:14:24.278629Z","iopub.execute_input":"2026-04-25T09:14:24.278941Z","iopub.status.idle":"2026-04-25T09:14:24.564091Z","shell.execute_reply.started":"2026-04-25T09:14:24.278915Z","shell.execute_reply":"2026-04-25T09:14:24.562958Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"show_prediction(4060)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T09:14:47.285886Z","iopub.execute_input":"2026-04-25T09:14:47.286691Z","iopub.status.idle":"2026-04-25T09:14:47.558502Z","shell.execute_reply.started":"2026-04-25T09:14:47.286659Z","shell.execute_reply":"2026-04-25T09:14:47.557655Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"show_prediction(2540)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-04-25T09:15:16.999272Z","iopub.execute_input":"2026-04-25T09:15:16.999923Z","iopub.status.idle":"2026-04-25T09:15:17.298008Z","shell.execute_reply.started":"2026-04-25T09:15:16.999893Z","shell.execute_reply":"2026-04-25T09:15:17.297235Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}