{"cells":[{"metadata":{},"cell_type":"markdown","source":"# About this kernel\n\nSince I noticed a lot of competitors is struggling to break the baseline submission (set everything to 0.51), I decided to create this kernel with the purpose of creating a pretrained model that you can easily finetune.\n\nIt uses a simple DenseNet-121, and is trained on around 200k real faces, and 200k GAN-generated faces. The fake faces are kindly [provided by Bojan in this discussion](https://www.kaggle.com/c/deepfake-detection-challenge/discussion/121173), and were downsampled to 224 x 224, whereas the real faces come from the CelebA dataset, provided by Jessica [here](https://www.kaggle.com/jessicali9530/celeba-dataset), this time upsampled to 224 x 224.\n\nObviously, the danger of using this model is that both datasets are very \"different\", so it is very likely the model is not learning facial features as much as underlying pixels distributions; so please use this with caution!"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np\nimport cv2\nimport pandas as pd\nfrom tqdm.notebook import tqdm \nfrom sklearn.model_selection import train_test_split\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.applications import DenseNet121\nfrom tensorflow.keras.callbacks import Callback, ModelCheckpoint\nfrom tensorflow.keras.models import Sequential\nfrom tensorflow.keras.optimizers import Adam\nimport os","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from PIL import ImageFile\nImageFile.LOAD_TRUNCATED_IMAGES = True","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Preprocessing"},{"metadata":{},"cell_type":"markdown","source":"## Create fake filepaths dataframe"},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"original_fake_paths = []\n\nfor dirname, _, filenames in tqdm(os.walk('/kaggle/input/1-million-fake-faces/')):\n    for filename in filenames:\n        original_fake_paths.append([os.path.join(dirname, filename), filename])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## First downsize all the images"},{"metadata":{"trusted":true},"cell_type":"code","source":"save_dir = '/kaggle/tmp/fake/'\n\nif not os.path.exists(save_dir):\n    os.makedirs(save_dir)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fake_paths = [save_dir + filename for _, filename in original_fake_paths]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for path, filename in tqdm(original_fake_paths):\n    img = cv2.imread(path)\n    img = cv2.resize(img, (224, 224))\n    cv2.imwrite(os.path.join(save_dir, filename), img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_fake_paths, test_fake_paths = train_test_split(fake_paths, test_size=20000, random_state=2019)\n\nfake_train_df = pd.DataFrame(train_fake_paths, columns=['filename'])\nfake_train_df['class'] = 'FAKE'\n\nfake_test_df = pd.DataFrame(test_fake_paths, columns=['filename'])\nfake_test_df['class'] = 'FAKE'","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Create real file paths dataframe"},{"metadata":{"trusted":true},"cell_type":"code","source":"real_dir = '/kaggle/input/celeba-dataset/img_align_celeba/img_align_celeba/'\neval_partition = pd.read_csv('/kaggle/input/celeba-dataset/list_eval_partition.csv')\n\neval_partition['filename'] = eval_partition.image_id.apply(lambda st: real_dir + st)\neval_partition['class'] = 'REAL'","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"real_train_df = eval_partition.query('partition in [0, 1]')[['filename', 'class']]\nreal_test_df = eval_partition.query('partition == 2')[['filename', 'class']]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Combine both real and fake for dataframe"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_df = pd.concat([real_train_df, fake_train_df])\ntest_df = pd.concat([real_test_df, fake_test_df])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Generator"},{"metadata":{"trusted":true},"cell_type":"code","source":"datagen = ImageDataGenerator(rescale=1./255, validation_split=0.2)\n\ntrain_gen = datagen.flow_from_dataframe(\n    train_df,\n    target_size=(224, 224),\n    batch_size=64,\n    class_mode='binary',\n    subset='training'\n)\n\nval_gen = datagen.flow_from_dataframe(\n    train_df,\n    target_size=(224, 224),\n    batch_size=64,\n    class_mode='binary',\n    subset='validation'\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"datagen = ImageDataGenerator(rescale=1./255).flow_from_dataframe(\n    test_df,\n    target_size=(224, 224),\n    batch_size=64,\n    class_mode='binary'\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Modelling"},{"metadata":{},"cell_type":"markdown","source":"## Load and freeze DenseNet"},{"metadata":{"trusted":true},"cell_type":"code","source":"densenet = DenseNet121(\n    weights='/kaggle/input/densenet-keras/DenseNet-BC-121-32-no-top.h5',\n    include_top=False,\n    input_shape=(224,224,3)\n)\n\nfor layer in densenet.layers:\n    layer.trainable = False","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Build Model"},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_model(densenet):\n    model = Sequential()\n    model.add(densenet)\n    model.add(layers.GlobalAveragePooling2D())\n    model.add(layers.BatchNormalization())\n    model.add(layers.Dropout(0.5))\n    \n    model.add(layers.Dense(256, activation='relu'))\n    model.add(layers.BatchNormalization())\n    model.add(layers.Dropout(0.5))\n    \n    model.add(layers.Dense(1, activation='sigmoid'))\n    \n    model.compile(\n        loss='binary_crossentropy',\n        optimizer=Adam(lr=0.0005),\n        metrics=['accuracy']\n    )\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = build_model(densenet)\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Training Phase 1 - Only train top layers"},{"metadata":{"trusted":true},"cell_type":"code","source":"checkpoint = ModelCheckpoint('model.h5', save_best_only=True)\n\ntrain_history_step1 = model.fit_generator(\n    train_gen,\n    validation_data=val_gen,\n    steps_per_epoch=len(train_gen),\n    validation_steps=len(val_gen),\n    callbacks=[checkpoint],\n    epochs=7\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Training Phase 2 - Unfreeze and train all"},{"metadata":{"trusted":true},"cell_type":"code","source":"model.load_weights('model.h5')\nfor layer in model.layers:\n    layer.trainable = True\n\ntrain_history_step2 = model.fit_generator(\n    train_gen,\n    validation_data=val_gen,\n    steps_per_epoch=len(train_gen),\n    validation_steps=len(val_gen),\n    callbacks=[checkpoint],\n    epochs=3\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"## Eval"},{"metadata":{"trusted":true},"cell_type":"code","source":"pd.DataFrame(train_history_step1.history).to_csv('history1.csv')\npd.DataFrame(train_history_step2.history).to_csv('history2.csv')","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat":4,"nbformat_minor":1}