{"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-12T14:13:45.548564Z","iopub.execute_input":"2025-04-12T14:13:45.548784Z","iopub.status.idle":"2025-04-12T14:14:07.472159Z","shell.execute_reply.started":"2025-04-12T14:13:45.548763Z","shell.execute_reply":"2025-04-12T14:14:07.471252Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"IMAGE_SIZE = 224\nBATCH_SIZE = 32  # Increased batch size for better generalization\nEPOCHS = 15  # Minimum 15 epochs as requested\nINIT_LR = 1e-4\nMIN_LR = 1e-6\n\n# 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\ndef 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-12T14:16:31.712974Z","iopub.execute_input":"2025-04-12T14:16:31.71335Z","iopub.status.idle":"2025-04-12T14:16:31.757144Z","shell.execute_reply.started":"2025-04-12T14:16:31.713323Z","shell.execute_reply":"2025-04-12T14:16:31.756117Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Data Generators with class weights\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\n# Calculate class weights\nclass_counts = DF_TRAIN['label'].value_counts()\ntotal_samples = sum(class_counts)\nclass_weights = {int(i): total_samples/(len(class_counts)*count) for i, count in enumerate(class_counts)}\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)\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Learning rate scheduler\ndef lr_scheduler(epoch, lr):\n    if epoch < 5:\n        return float(lr)  # Keep initial LR for first 5 epochs\n    elif epoch < 10:\n        return float(lr * tf.math.exp(-0.1))  # Gradual decrease\n    else:\n        return float(max(lr * tf.math.exp(-0.2), MIN_LR))  # Minimum LR\n\n# Build enhanced model\ndef 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},"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',\n        monitor='val_accuracy',\n        save_best_only=True,\n        mode='max',\n        verbose=1\n    ),\n    LearningRateScheduler(lr_scheduler)\n]\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"model = build_model()\noptimizer = Adam(learning_rate=INIT_LR)\nmodel.compile(\n    optimizer=optimizer,\n    loss=CategoricalCrossentropy(label_smoothing=0.1),  # Label smoothing for better generalization\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\n# 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":"model.save('cassava_vgg16_final.keras')\n","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def 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    plt.figure(figsize=(12, 5))\n    \n    # Plot accuracy\n    plt.subplot(1, 2, 1)\n    plt.plot(history.history['accuracy'], label='Train Accuracy')\n    plt.plot(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(history.history['loss'], label='Train Loss')\n    plt.plot(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# Evaluate\nevaluate_model(model, valid_gen)","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}