{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":13836,"databundleVersionId":1718836,"sourceType":"competition"}],"dockerImageVersionId":31012,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport tensorflow as tf\nimport matplotlib.pyplot as plt\nfrom sklearn.metrics import confusion_matrix, classification_report\nimport seaborn as sns\nfrom tensorflow.keras.applications import VGG16\nfrom tensorflow.keras import layers, models\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau, ModelCheckpoint, LearningRateScheduler\nfrom tensorflow.keras.regularizers import l2\nfrom tensorflow.keras.optimizers import Adam\nfrom tensorflow.keras.losses import CategoricalCrossentropy","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-04-12T09:40:08.070951Z","iopub.execute_input":"2025-04-12T09:40:08.071169Z","iopub.status.idle":"2025-04-12T09:40:22.957373Z","shell.execute_reply.started":"2025-04-12T09:40:08.071151Z","shell.execute_reply":"2025-04-12T09:40:22.956597Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Constants\nIMAGE_SIZE = 224\nBATCH_SIZE = 32  # Increased batch size\nEPOCHS = 15  # Increased epochs\nINIT_LR = 1e-4\nMIN_LR = 1e-6","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T09:40:22.959303Z","iopub.execute_input":"2025-04-12T09:40:22.960266Z","iopub.status.idle":"2025-04-12T09:40:22.964636Z","shell.execute_reply.started":"2025-04-12T09:40:22.960237Z","shell.execute_reply":"2025-04-12T09:40:22.963681Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Paths\nTRAIN_PATH = '/kaggle/input/cassava-leaf-disease-classification/train_images'\nTEST_PATH = '/kaggle/input/cassava-leaf-disease-classification/test_images'\nDF_TRAIN = pd.read_csv('/kaggle/input/cassava-leaf-disease-classification/train.csv', dtype='str')\n\n# Enhanced Data Augmentation","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T09:40:22.965436Z","iopub.execute_input":"2025-04-12T09:40:22.965691Z","iopub.status.idle":"2025-04-12T09:40:23.040732Z","shell.execute_reply.started":"2025-04-12T09:40:22.965667Z","shell.execute_reply":"2025-04-12T09:40:23.04015Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def data_augment(image):\n    # Random flips\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.random_flip_up_down(image)\n    \n    # Random rotation (0-360 degrees)\n    image = tf.image.rot90(image, k=tf.random.uniform(shape=[], minval=0, maxval=4, dtype=tf.int32))\n    \n    # Random zoom and crop\n    image = tf.image.random_crop(image, size=[tf.random.uniform(shape=[], minval=int(IMAGE_SIZE*0.8), maxval=IMAGE_SIZE, dtype=tf.int32)]*2 + [3])\n    image = tf.image.resize(image, [IMAGE_SIZE, IMAGE_SIZE])\n    \n    # Color transformations with more conservative ranges\n    image = tf.image.random_brightness(image, max_delta=0.1)\n    image = tf.image.random_contrast(image, lower=0.9, upper=1.1)\n    image = tf.image.random_saturation(image, lower=0.9, upper=1.1)\n    image = tf.image.random_hue(image, max_delta=0.05)\n    \n    # Gaussian noise\n    if tf.random.uniform(shape=[], minval=0, maxval=1) > 0.7:\n        noise = tf.random.normal(shape=tf.shape(image), mean=0.0, stddev=0.1)\n        image = image + noise\n        image = tf.clip_by_value(image, 0.0, 1.0)\n    \n    return image","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T09:40:23.041368Z","iopub.execute_input":"2025-04-12T09:40:23.04154Z","iopub.status.idle":"2025-04-12T09:40:23.047652Z","shell.execute_reply.started":"2025-04-12T09:40:23.041524Z","shell.execute_reply":"2025-04-12T09:40:23.047078Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Data Generator with more aggressive augmentation for training\ntrain_datagen = ImageDataGenerator(\n    rescale=1./255,\n    preprocessing_function=data_augment,\n    validation_split=0.2,\n    rotation_range=30,\n    width_shift_range=0.2,\n    height_shift_range=0.2,\n    shear_range=0.2,\n    zoom_range=0.2,\n    horizontal_flip=True,\n    vertical_flip=True,\n    fill_mode='nearest'\n)\n\nvalid_datagen = ImageDataGenerator(\n    rescale=1./255,\n    validation_split=0.2\n)\n\ntrain_gen = train_datagen.flow_from_dataframe(\n    dataframe=DF_TRAIN,\n    directory=TRAIN_PATH,\n    x_col='image_id',\n    y_col='label',\n    subset='training',\n    batch_size=BATCH_SIZE,\n    seed=1,\n    color_mode='rgb',\n    shuffle=True,\n    class_mode='categorical',\n    target_size=(IMAGE_SIZE, IMAGE_SIZE)\n)\n\nvalid_gen = valid_datagen.flow_from_dataframe(\n    dataframe=DF_TRAIN,\n    directory=TRAIN_PATH,\n    x_col='image_id',\n    y_col='label',\n    subset='validation',\n    batch_size=BATCH_SIZE,\n    seed=1,\n    color_mode='rgb',\n    shuffle=False,\n    class_mode='categorical',\n    target_size=(IMAGE_SIZE, IMAGE_SIZE)\n)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T09:40:23.048384Z","iopub.execute_input":"2025-04-12T09:40:23.048545Z","iopub.status.idle":"2025-04-12T09:42:04.747773Z","shell.execute_reply.started":"2025-04-12T09:40:23.048531Z","shell.execute_reply":"2025-04-12T09:42:04.747087Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"class_counts = DF_TRAIN['label'].value_counts()\ntotal_samples = sum(class_counts)\nclass_weights = {i: total_samples/(len(class_counts)*count) for i, count in enumerate(class_counts)}","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T09:42:04.748601Z","iopub.execute_input":"2025-04-12T09:42:04.748961Z","iopub.status.idle":"2025-04-12T09:42:04.75731Z","shell.execute_reply.started":"2025-04-12T09:42:04.748933Z","shell.execute_reply":"2025-04-12T09:42:04.756757Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def lr_scheduler(epoch, lr):\n    if epoch < 5:\n        return float(lr)  # Ensure float output\n    elif epoch < 10:\n        return float(lr * tf.math.exp(-0.1))\n    else:\n        return float(max(lr * tf.math.exp(-0.2), MIN_LR))","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_model():\n    # Load VGG16 with pre-trained weights\n    vgg_model = VGG16(\n        weights=\"imagenet\",\n        include_top=False,\n        input_shape=(IMAGE_SIZE, IMAGE_SIZE, 3)\n    )\n    # Freeze initial layers, allow last few to fine-tune\n    for layer in vgg_model.layers[:15]:\n        layer.trainable = False\n    for layer in vgg_model.layers[15:]:\n        layer.trainable = True\n    \n    # Add custom layers with regularization\n    model = models.Sequential([\n        vgg_model,\n        layers.GlobalAveragePooling2D(),  # Better than Flatten for reducing params\n        layers.BatchNormalization(),\n        layers.Dropout(0.5),  # Added dropout for regularization\n        layers.Dense(512, activation='relu', kernel_regularizer=l2(0.001)),\n        layers.BatchNormalization(),\n        layers.Dropout(0.5),\n        layers.Dense(256, activation='relu', kernel_regularizer=l2(0.001)),\n        layers.BatchNormalization(),\n        layers.Dropout(0.3),\n        layers.Dense(5, activation='softmax')\n    ] )\n    \n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T09:42:04.759292Z","iopub.execute_input":"2025-04-12T09:42:04.759554Z","iopub.status.idle":"2025-04-12T09:42:04.774921Z","shell.execute_reply.started":"2025-04-12T09:42:04.759538Z","shell.execute_reply":"2025-04-12T09:42:04.774236Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Learning rate scheduler\n\n\n# Callbacks\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T09:42:04.775533Z","iopub.execute_input":"2025-04-12T09:42:04.775712Z","iopub.status.idle":"2025-04-12T09:42:04.794748Z","shell.execute_reply.started":"2025-04-12T09:42:04.775696Z","shell.execute_reply":"2025-04-12T09:42:04.794153Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Build and compile model\nmodel = build_model()\noptimizer = Adam(learning_rate=INIT_LR)\nmodel.compile(\n    optimizer=optimizer,\n    loss=CategoricalCrossentropy(label_smoothing=0.1),  # Slightly reduced smoothing\n    metrics=['accuracy', \n             tf.keras.metrics.Precision(name='precision'),\n             tf.keras.metrics.Recall(name='recall'),\n             tf.keras.metrics.AUC(name='auc')]\n)\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-04-12T09:42:04.795483Z","iopub.execute_input":"2025-04-12T09:42:04.795926Z","iopub.status.idle":"2025-04-12T09:42:07.693619Z","shell.execute_reply.started":"2025-04-12T09:42:04.795908Z","shell.execute_reply":"2025-04-12T09:42:07.692937Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"callbacks = [\n    EarlyStopping(\n        monitor='val_accuracy',\n        patience=8,\n        restore_best_weights=True,\n        mode='max',\n        verbose=1\n    ),\n    ReduceLROnPlateau(\n        monitor='val_accuracy',\n        factor=0.5,\n        patience=3,\n        min_lr=MIN_LR,\n        mode='max',\n        verbose=1\n    ),\n    ModelCheckpoint(\n        filepath='best_model.keras',  # Using new .keras format\n        monitor='val_accuracy',\n        save_best_only=True,\n        mode='max',\n        verbose=1\n    ),\n    LearningRateScheduler(lr_scheduler)\n]","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Train the model\nhistory = model.fit(\n    train_gen,\n    steps_per_epoch=train_gen.n // train_gen.batch_size,\n    validation_data=valid_gen,\n    validation_steps=valid_gen.n // valid_gen.batch_size,\n    epochs=EPOCHS,\n    callbacks=callbacks,\n    class_weight=class_weights\n)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Save the final model (use either)\nmodel.save('cassava_vgg16_final.keras')  # Recommended new format\n# OR if you need HDF5:\nmodel.save('cassava_vgg16_final.h5', save_format='h5')","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Define your classes dictionary at the top level\nclasses = {\n    0: \"Cassava Bacterial Blight (CBB)\",\n    1: \"Cassava Brown Streak Disease (CBSD)\",\n    2: \"Cassava Green Mottle (CGM)\",\n    3: \"Cassava Mosaic Disease (CMD)\",\n    4: \"Healthy\"\n}\n\ndef evaluate_model(model, valid_gen):\n    # Predict on validation set\n    y_pred = model.predict(valid_gen)\n    y_pred_classes = np.argmax(y_pred, axis=1)\n    y_true = valid_gen.classes\n    \n    # Confusion Matrix\n    plt.figure(figsize=(12, 10))\n    cm = confusion_matrix(y_true, y_pred_classes)\n    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', \n                xticklabels=list(classes.values()),\n                yticklabels=list(classes.values()))\n    plt.xlabel('Predicted')\n    plt.ylabel('True')\n    plt.title('Confusion Matrix')\n    plt.show()\n    \n    # Classification report\n    print(classification_report(y_true, y_pred_classes, target_names=list(classes.values())))\n    \n    # Plot training history\n    if hasattr(model, 'history'):\n        plt.figure(figsize=(12, 5))\n        \n        # Plot accuracy\n        plt.subplot(1, 2, 1)\n        plt.plot(model.history.history['accuracy'], label='Train Accuracy')\n        plt.plot(model.history.history['val_accuracy'], label='Validation Accuracy')\n        plt.title('Accuracy over Epochs')\n        plt.xlabel('Epoch')\n        plt.ylabel('Accuracy')\n        plt.legend()\n        \n        # Plot loss\n        plt.subplot(1, 2, 2)\n        plt.plot(model.history.history['loss'], label='Train Loss')\n        plt.plot(model.history.history['val_loss'], label='Validation Loss')\n        plt.title('Loss over Epochs')\n        plt.xlabel('Epoch')\n        plt.ylabel('Loss')\n        plt.legend()\n        \n        plt.tight_layout()\n        plt.show()\n\n# Usage:\nevaluate_model(model, valid_gen)","metadata":{"trusted":true,"execution":{"execution_failed":"2025-04-12T09:49:48.572Z"}},"outputs":[],"execution_count":null}]}