{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":87323,"databundleVersionId":9934427,"sourceType":"competition"}],"dockerImageVersionId":30822,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\n# import os\n# for dirname, _, filenames in os.walk('/kaggle/input'):\n#     for filename in filenames:\n#         print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2025-01-17T03:40:15.375389Z","iopub.execute_input":"2025-01-17T03:40:15.375657Z","iopub.status.idle":"2025-01-17T03:40:15.665504Z","shell.execute_reply.started":"2025-01-17T03:40:15.375636Z","shell.execute_reply":"2025-01-17T03:40:15.664773Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 1. Load data","metadata":{}},{"cell_type":"code","source":"BASE_FOLDER = '/kaggle/input/cidaut-ai-fake-scene-classification-2024'\nTRAIN_PATH = BASE_FOLDER + \"/Train\"\nTEST_PATH = BASE_FOLDER + \"/Test\"\nNUM_IMAGES = 720\nBATCH_SIZE = 8\nIMAGE_WIDTH, IMAGE_HEIGHT = 1280, 720\nLABELS_CSV_FILEPATH = BASE_FOLDER + \"/train.csv\"\nWEIGHT_FILEPATH = \"tmp/model.weights.h5\"\nPREDICTIONS_FILEPATH = \"submission.csv\"","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-17T03:40:15.666283Z","iopub.execute_input":"2025-01-17T03:40:15.666588Z","iopub.status.idle":"2025-01-17T03:40:15.670979Z","shell.execute_reply.started":"2025-01-17T03:40:15.666569Z","shell.execute_reply":"2025-01-17T03:40:15.670211Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import csv\nimport os\nimport tensorflow as tf\nimport keras\nimport numpy as np\n\ndef load_labels_from_csv_file():\n    '''\n        Load labels for files in alphanumerical order\n        \n        Returns:\n            labels: array of size (NUM_IMAGES) where label = 0 (fake) or 1\n    '''\n    with open(LABELS_CSV_FILEPATH) as f:\n        reader = csv.reader(f, delimiter=',')\n        next(reader)\n        image_label_pairs = [(row[0], 1 if row[1] == \"real\" else 0) for row in reader]\n        image_label_pairs = sorted(image_label_pairs, key=lambda t: t[0])\n        labels = [t[1] for t in image_label_pairs]\n        print(image_label_pairs[:10])\n        return labels\n\ndef build_dataset_from_directory():\n    '''\n        Returns:\n            dataset: tf.data.Dataset[NUM_IMAGES]\n    '''\n    labels = load_labels_from_csv_file()\n    train_dataset, val_dataset = keras.utils.image_dataset_from_directory(\n        directory=TRAIN_PATH,\n        labels=labels,\n        batch_size=BATCH_SIZE,\n        label_mode=\"binary\",\n        image_size=(IMAGE_HEIGHT, IMAGE_WIDTH),\n        validation_split=0.1,\n        subset=\"both\",\n        shuffle=False\n    )\n    return train_dataset, val_dataset\n\ndef load_test_dataset_from_directory():\n    '''\n        Returns:\n            dataset: tf.data.Dataset[NUM_IMAGES]\n    '''\n    test_dataset = keras.utils.image_dataset_from_directory(\n        directory=TEST_PATH,\n        labels=None,\n        batch_size=BATCH_SIZE,\n        image_size=(IMAGE_HEIGHT, IMAGE_WIDTH),\n        shuffle=False\n    )\n    return test_dataset\n\ntrain_dataset, val_dataset = build_dataset_from_directory()\n\ntest_filenames = sorted(list(os.listdir(TEST_PATH)))\ntest_dataset = load_test_dataset_from_directory()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-17T03:40:15.671943Z","iopub.execute_input":"2025-01-17T03:40:15.672252Z","iopub.status.idle":"2025-01-17T03:40:25.693119Z","shell.execute_reply.started":"2025-01-17T03:40:15.672223Z","shell.execute_reply":"2025-01-17T03:40:25.692151Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 2. Build model","metadata":{}},{"cell_type":"code","source":"# Hyperparameters\nlearning_rate = 0.001\nweight_decay = 0.0001\nbatch_size = 16\nnum_epochs = 30\nimage_width, image_height = 512, 512 # Resize to this size","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-17T03:40:25.695351Z","iopub.execute_input":"2025-01-17T03:40:25.695593Z","iopub.status.idle":"2025-01-17T03:40:25.699522Z","shell.execute_reply.started":"2025-01-17T03:40:25.695573Z","shell.execute_reply":"2025-01-17T03:40:25.698823Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import keras\nfrom keras import layers\nfrom keras import ops\nimport tensorflow as tf\nfrom tensorflow.python.ops.signal.dct_ops import dct\n\nregularizer = keras.regularizers.l2()\n\ndata_augmentation = keras.Sequential(\n    [\n        layers.RandomFlip(\"horizontal\"),\n        layers.RandomZoom(height_factor=(-0.2, -0.1), width_factor=(-0.2, -0.1)),\n        layers.RandomBrightness(0.2)\n    ],\n    name=\"data_augmentation\",\n)\n\nclass DCTLayer(keras.layers.Layer):\n    def __init__(self, **kwargs):\n        super(DCTLayer, self).__init__(**kwargs)\n\n    def call(self, images, training = None):\n        images = tf.transpose(images, [0, 3, 2, 1]) # after -> B, C, W, H\n        images = dct(images, type=2, norm='ortho', axis=-1) # height\n        images = tf.transpose(images, [0, 1, 3, 2]) # after -> B, C, H, W\n        images = dct(images, type=2, norm='ortho', axis=-1) # width\n        images = tf.transpose(images, [0, 2, 3, 1]) # after -> B, H, W, C\n        images = tf.math.log(tf.abs(images) + 1e-13)\n        return images","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-17T03:40:25.700692Z","iopub.execute_input":"2025-01-17T03:40:25.700999Z","iopub.status.idle":"2025-01-17T03:40:25.735582Z","shell.execute_reply.started":"2025-01-17T03:40:25.700970Z","shell.execute_reply":"2025-01-17T03:40:25.734700Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"import keras\nfrom keras import layers\nfrom keras import ops\n\ndef create_classifier():\n    inputs = keras.Input(shape=(IMAGE_HEIGHT, IMAGE_WIDTH, 3))\n    x = data_augmentation(inputs)\n    x = DCTLayer()(x)\n    \n    x = layers.BatchNormalization()(x)\n   \n    x = layers.Conv2D(3, 3, padding=\"same\", activation=\"relu\", kernel_regularizer=regularizer)(x)\n    x = layers.Conv2D(8, 3, padding=\"same\", activation=\"relu\", kernel_regularizer=regularizer)(x)\n    x = layers.AveragePooling2D((2, 2))(x)  # 64\n    x = layers.Dropout(0.5)(x)\n\n    x = layers.Conv2D(16, 3, padding=\"same\", activation=\"relu\", kernel_regularizer=regularizer)(x)\n    x = layers.AveragePooling2D((2, 2))(x)  # 32\n    x = layers.Dropout(0.5)(x)\n\n    x = layers.Conv2D(32, 3, padding=\"same\", activation=\"relu\", kernel_regularizer=regularizer)(x)\n    x = layers.Dropout(0.5)(x)\n\n    x = layers.Flatten()(x)\n    outputs = keras.layers.Dense(1, activation='sigmoid')(x)\n    model = keras.Model(inputs, outputs)\n\n    return model","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-17T03:40:25.736462Z","iopub.execute_input":"2025-01-17T03:40:25.736676Z","iopub.status.idle":"2025-01-17T03:40:25.742720Z","shell.execute_reply.started":"2025-01-17T03:40:25.736650Z","shell.execute_reply":"2025-01-17T03:40:25.741966Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 3. Train model","metadata":{}},{"cell_type":"code","source":"import os\nimport keras\n\ndef create_checkpoint_callback():\n    checkpoint_callback = keras.callbacks.ModelCheckpoint(\n        WEIGHT_FILEPATH,\n        monitor=\"val_loss\",\n        mode=\"min\",\n        save_best_only=True,\n        save_weights_only=True,\n    )\n    return checkpoint_callback\n\ndef run_experiment(model):\n    os.makedirs(\"tmp\", exist_ok=True)\n\n    optimizer = keras.optimizers.AdamW(\n        learning_rate=learning_rate, weight_decay=weight_decay\n    )\n    model.compile(\n        optimizer=optimizer,\n        loss=keras.losses.BinaryCrossentropy(),\n        metrics=[\n            keras.metrics.BinaryAccuracy(name=\"accuracy\"),\n            keras.metrics.AUC()\n        ],\n    )\n    history = model.fit(\n        x=train_dataset,\n        validation_data=val_dataset,\n        batch_size=batch_size,\n        epochs=num_epochs,\n        callbacks=[create_checkpoint_callback()],\n    )\n\n    preds = model.predict(test_dataset)\n    preds = np.squeeze(np.where(preds > 0.5, 1, 0))\n\n    return history, preds\n\nclassifier = create_classifier()\nhistory, preds = run_experiment(classifier)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-01-17T03:40:25.743382Z","iopub.execute_input":"2025-01-17T03:40:25.743594Z"}},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"# 4. Test data","metadata":{}},{"cell_type":"code","source":"import csv\nimport keras\nimport numpy as np\nimport os\n\ndef save_preds_to_file(preds):\n    if os.path.exists(PREDICTIONS_FILEPATH):\n      os.remove(PREDICTIONS_FILEPATH)\n        \n    header_row = ['image', 'label']\n    data = zip(test_filenames, preds)\n    data = sorted(data, key=lambda t: t[0])\n    with open(PREDICTIONS_FILEPATH, 'w', newline='') as f:\n        writer = csv.writer(f)\n        writer.writerow(header_row)\n        writer.writerows(data)\n\nsave_preds_to_file(preds)","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}