{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":19991,"databundleVersionId":1117522,"sourceType":"competition"},{"sourceId":6717141,"sourceType":"datasetVersion","datasetId":3870216}],"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"* v_9 adds the custom metrics","metadata":{}},{"cell_type":"markdown","source":"### The two main caracteristics of SRNet are :\n* it uses a lot a residual connections to help propagate the gradient throughout the layers\n* it uses only one channel (but it may be a good idea to adapt it to 3 channels, as you are necessarily loosing information by converting to grayscale)","metadata":{}},{"cell_type":"markdown","source":"## Basic Imports","metadata":{}},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport os\nimport gc\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport matplotlib.image as mpimg\nfrom tqdm.notebook import tqdm\nimport random\nimport joblib\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import roc_curve, auc\nfrom time import time\nfrom collections import Counter\n\nfrom PIL import Image\nfrom random import shuffle\n\nimport tensorflow as tf\nfrom tensorflow.keras.metrics import AUC\n\nimport keras\nfrom keras import backend as K\n# import tf.keras.layers as L\nfrom keras import Model\nfrom keras.utils import plot_model\n# from keras.preprocessing.image import ImageDataGenerator\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")\n\n%matplotlib inline","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2024-04-05T17:53:43.51104Z","iopub.execute_input":"2024-04-05T17:53:43.511903Z","iopub.status.idle":"2024-04-05T17:53:56.438819Z","shell.execute_reply.started":"2024-04-05T17:53:43.511862Z","shell.execute_reply":"2024-04-05T17:53:56.43804Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"PATH = \"/kaggle/input/alaska2-image-steganalysis/\"\nIMAGE_IDS = os.listdir(os.path.join(PATH, 'Cover'))\nN_IMAGES = len(IMAGE_IDS)\nALGORITHMS = ['JMiPOD', 'JUNIWARD', 'UERD']\nIMG_SIZE = 256\n\nsample_sub = pd.read_csv(PATH + 'sample_submission.csv')","metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","execution":{"iopub.status.busy":"2024-04-05T17:53:56.440365Z","iopub.execute_input":"2024-04-05T17:53:56.440868Z","iopub.status.idle":"2024-04-05T17:53:59.924309Z","shell.execute_reply.started":"2024-04-05T17:53:56.440842Z","shell.execute_reply":"2024-04-05T17:53:59.923549Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# To make things reproductible\n\ndef seed_everything(seed=42):\n    np.random.seed(seed)\n    tf.random.set_seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    os.environ['TF_DETERMINISTIC_OPS'] = '1'\n    os.environ['TF_KERAS'] = '1'\n\nseed_everything()","metadata":{"execution":{"iopub.status.busy":"2024-04-05T17:53:59.925382Z","iopub.execute_input":"2024-04-05T17:53:59.925628Z","iopub.status.idle":"2024-04-05T17:53:59.93112Z","shell.execute_reply.started":"2024-04-05T17:53:59.925607Z","shell.execute_reply":"2024-04-05T17:53:59.930141Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Define the SRNet Model","metadata":{}},{"cell_type":"code","source":"def layer_type1(x_inp, filters, kernel_size=(3, 3), dropout_rate=0):\n    x = tf.keras.layers.Conv2D(filters, kernel_size, padding=\"same\")(x_inp)\n    x = tf.keras.layers.BatchNormalization()(x)\n    x = tf.keras.layers.ReLU()(x)\n    if dropout_rate > 0:\n        x = tf.keras.layers.Dropout(dropout_rate)(x)\n\n    return x\n\ndef layer_type2(x_inp, filters, kernel_size=(3, 3), dropout_rate=0):\n    x = layer_type1(x_inp, filters)\n    x = tf.keras.layers.Conv2D(filters, kernel_size, padding=\"same\")(x)\n    x = tf.keras.layers.BatchNormalization()(x)\n    x = tf.keras.layers.ReLU()(x)\n    if dropout_rate > 0:\n        x = tf.keras.layers.Dropout(dropout_rate)(x)\n\n    x = tf.keras.layers.Add()([x, x_inp])\n    \n    return x\n\ndef layer_type3(x_inp, filters, kernel_size=(3, 3), dropout_rate=0):\n    x = layer_type1(x_inp, filters)\n    x = tf.keras.layers.Conv2D(filters, kernel_size, padding=\"same\")(x)\n    x = tf.keras.layers.BatchNormalization()(x)\n    x = tf.keras.layers.ReLU()(x)\n    x = tf.keras.layers.AveragePooling2D(pool_size=(3, 3), strides=(2, 2))(x)\n    if dropout_rate > 0:\n        x = tf.keras.layers.Dropout(dropout_rate)(x)\n        \n    x_res = tf.keras.layers.Conv2D(filters, kernel_size, strides=(2, 2))(x_inp)\n    x_res = tf.keras.layers.BatchNormalization()(x_res)\n    if dropout_rate > 0:\n        x_res = tf.keras.layers.Dropout(dropout_rate)(x_res)\n\n    x = tf.keras.layers.Add()([x, x_res])\n    \n    return x\n\ndef layer_type4(x_inp, filters, kernel_size=(3, 3), dropout_rate=0):\n    x = layer_type1(x_inp, filters)\n    x = tf.keras.layers.Conv2D(filters, kernel_size=(3, 3), padding=\"same\")(x)\n    x = tf.keras.layers.BatchNormalization()(x)\n    if dropout_rate > 0:\n        x = tf.keras.layers.Dropout(dropout_rate)(x)    \n    x = tf.keras.layers.GlobalAveragePooling2D()(x)\n    \n    return x","metadata":{"execution":{"iopub.status.busy":"2024-04-05T17:53:59.933763Z","iopub.execute_input":"2024-04-05T17:53:59.934185Z","iopub.status.idle":"2024-04-05T17:53:59.951984Z","shell.execute_reply.started":"2024-04-05T17:53:59.934125Z","shell.execute_reply":"2024-04-05T17:53:59.951103Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def alaska_weighted_auc(y_true, y_valid):\n    tpr_thresholds = [0.0, 0.4, 1.0]\n    weights =        [       2,   1]\n    \n    fpr, tpr, thresholds = roc_curve(y_true, y_valid)\n    \n    # size of subsets\n    areas = np.array(tpr_thresholds[1:]) - np.array(tpr_thresholds[:-1])\n    \n    # The total area is normalized by the sum of weights such that the final weighted AUC is between 0 and 1.\n    normalization = np.dot(areas, weights)\n    \n    try:\n    \n        competition_metric = 0\n        for idx, weight in enumerate(weights):\n            y_min = tpr_thresholds[idx]\n            y_max = tpr_thresholds[idx + 1]\n            mask = (y_min < tpr) & (tpr < y_max)\n\n            x_padding = np.linspace(fpr[mask][-1], 1, 100)\n\n            x = np.concatenate([fpr[mask], x_padding])\n            y = np.concatenate([tpr[mask], [y_max] * len(x_padding)])\n            y = y - y_min # normalize such that curve starts at y=0\n            score = auc(x, y)\n            submetric = score * weight\n            best_subscore = (y_max - y_min) * weight\n            competition_metric += submetric\n    except:\n        # sometimes there's a weird bug so return naive score\n        return .5\n        \n    return competition_metric / normalization\n\ndef alaska_tf(y_true, y_val):\n    \"\"\"Wrapper for the above function\"\"\"\n    return tf.py_function(func=alaska_weighted_auc, inp=[y_true, y_val], Tout=tf.float32)","metadata":{"execution":{"iopub.status.busy":"2024-04-05T17:53:59.952867Z","iopub.execute_input":"2024-04-05T17:53:59.953148Z","iopub.status.idle":"2024-04-05T17:53:59.966682Z","shell.execute_reply.started":"2024-04-05T17:53:59.953126Z","shell.execute_reply":"2024-04-05T17:53:59.965877Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"################################################## 30 SRM FILTERS\nsrm_weights = np.load('../input/stegan/SRM_Kernels.npy') \nbiasSRM=np.ones(30)\nprint (srm_weights.shape)\n################################################## TLU ACTIVATION FUNCTION\nT3 = 3;\ndef Tanh3(x):\n    return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x)) * T3\n\n##################################################","metadata":{"execution":{"iopub.status.busy":"2024-04-05T17:53:59.96763Z","iopub.execute_input":"2024-04-05T17:53:59.96788Z","iopub.status.idle":"2024-04-05T17:53:59.992933Z","shell.execute_reply.started":"2024-04-05T17:53:59.967859Z","shell.execute_reply":"2024-04-05T17:53:59.992163Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def make_DCT_filter():\n    with tf.name_scope(\"DCT\"):\n        # Initialize DCT filters\n        DCT_filter_n = np.zeros([5, 5, 1, 64])\n        # Definition of 8x8 mesh grid\n        XX, YY = np.meshgrid(range(5), range(5))\n        # DCT basis as filters\n        C=np.ones(5)\n        C[0]=1/np.sqrt(2)\n        for v in range(5):\n            for u in range(5):\n                DCT_filter_n[:, :, 0, u+v*5]=(2*C[v]*C[u]/5)*np.cos((2*YY+1)*v*np.pi/(10))*np.cos((2*XX+1)*u*np.pi/(10))\n\n        DCT_filter=tf.constant(DCT_filter_n.astype(np.float32))\n\n        return DCT_filter","metadata":{"execution":{"iopub.status.busy":"2024-04-05T17:53:59.993784Z","iopub.execute_input":"2024-04-05T17:53:59.99401Z","iopub.status.idle":"2024-04-05T17:54:00.000963Z","shell.execute_reply.started":"2024-04-05T17:53:59.993991Z","shell.execute_reply":"2024-04-05T17:54:00.000204Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def make_model(input_shape=(IMG_SIZE, IMG_SIZE, 3), num_type2=5, dropout_rate=0):\n    \n    inputs = tf.keras.Input(shape=(IMG_SIZE,IMG_SIZE,3), name=\"input\")\n    DCT_filter = make_DCT_filter()\n    print(DCT_filter.shape)\n    \n    filters = np.concatenate([DCT_filter,srm_weights],axis=3)\n    filters_repeated = np.concatenate([filters, filters], axis=2)\n    filters_repeated = np.concatenate([filters, filters_repeated], axis=2)\n    filters=filters_repeated \n    filters.shape\n\n    bias = np.ones(94)\n    # I reduced the size (image size, filters and depth) of the original network because it was way to big\n#     inputs = tf.keras.layers.Input(shape=input_shape)\n#     f=tf.keras.layers.Layer()\n#     g=f.set_weights(weights=[filters,bias])\n#     layers_ty = tf.keras.layers.Conv2D(94, (5,5), strides=(1,1), trainable=False, padding='same', activation=Tanh3, use_bias=True)(inputs)\n# #     layers_ty.Layer.set_weights(weights=[filters,bias])\n#     g=f.set_weights(weights=[filters,bias])\n#     layers_tn = tf.keras.layers.Conv2D(94, (5,5), strides=(1,1), trainable=True, padding='same', activation=Tanh3, use_bias=True)(inputs)\n#     layers1 = tf.keras.layers.add([layers_ty, layers_tn])\n    \n    #Block 1\n    layers_ty = tf.keras.layers.Conv2D(94, (5,5), strides=(1,1), trainable=False, padding='same', use_bias=True, name='layers_ty')(inputs)\n    layers_tn = tf.keras.layers.Conv2D(94, (5,5), strides=(1,1), trainable=True, padding='same', use_bias=True, name='layers_tn')(inputs)\n    layers1 = tf.keras.layers.add([layers_ty, layers_tn])\n\n    \n    layers = tf.keras.layers.Conv2D(94, (1,1), strides=(1,1),padding=\"same\", kernel_initializer='glorot_normal', kernel_regularizer=tf.keras.regularizers.l2(0.0001),bias_regularizer=tf.keras.regularizers.l2(0.0001))(layers1) \n    layers = tf.keras.layers.ReLU(negative_slope=0.1, threshold=0)(layers)\n    layers = tf.keras.layers.BatchNormalization(momentum=0.2, epsilon=0.001, center=True, scale=True, trainable=True)(layers)\n  \n    layers = tf.keras.layers.Conv2D(94, (3,3), strides=(1,1),padding=\"same\", kernel_initializer='glorot_normal', kernel_regularizer=tf.keras.regularizers.l2(0.0001),bias_regularizer=tf.keras.regularizers.l2(0.0001))(layers) \n    layers = tf.keras.layers.ReLU(negative_slope=0.1, threshold=0)(layers)\n    layers = tf.keras.layers.BatchNormalization(momentum=0.2, epsilon=0.001, center=True, scale=True, trainable=True)(layers)\n\n    layers = tf.keras.layers.Conv2D(94, (1,1), strides=(1,1),padding=\"same\", kernel_initializer='glorot_normal', kernel_regularizer=tf.keras.regularizers.l2(0.0001),bias_regularizer=tf.keras.regularizers.l2(0.0001))(layers) \n    layers = tf.keras.layers.ReLU(negative_slope=0.1, threshold=0)(layers)\n    layers4 = tf.keras.layers.BatchNormalization(momentum=0.2, epsilon=0.001, center=True, scale=True, trainable=True)(layers)\n\n    \n    x = layer_type1(layers4, filters=64, dropout_rate=dropout_rate)\n    x = layer_type1(x, filters=16, dropout_rate=dropout_rate)    \n    \n    for _ in range(num_type2):\n        x = layer_type2(x, filters=16, dropout_rate=dropout_rate)         \n    \n    x = layer_type3(x, filters=16, dropout_rate=dropout_rate) \n    x = layer_type3(x, filters=32, dropout_rate=dropout_rate)            \n    x = layer_type3(x, filters=64, dropout_rate=dropout_rate)            \n    #x = layer_type3(x, filters=128, dropout_rate=dropout_rate) \n    \n    x = layer_type4(x, filters=128, dropout_rate=dropout_rate)        \n    \n    x = tf.keras.layers.Dense(64)(x)\n    x = tf.keras.layers.BatchNormalization()(x)\n    x = tf.keras.layers.ReLU()(x)\n    if dropout_rate > 0:\n        x = tf.keras.layers.Dropout(dropout_rate)(x)\n\n    predictions = tf.keras.layers.Dense(1, activation=\"sigmoid\")(x)\n    \n    model = Model(inputs=inputs, outputs=predictions)\n    \n    model.layers[1].set_weights([filters, bias])\n    model.layers[2].set_weights([filters, bias])\n    \n    keras_auc = AUC()\n    \n    model.compile(optimizer='adam',\n                  loss='binary_crossentropy', \n                  metrics=[alaska_tf])\n    \n    return model\n    ","metadata":{"execution":{"iopub.status.busy":"2024-04-05T18:02:14.240768Z","iopub.execute_input":"2024-04-05T18:02:14.241132Z","iopub.status.idle":"2024-04-05T18:02:14.263141Z","shell.execute_reply.started":"2024-04-05T18:02:14.241102Z","shell.execute_reply":"2024-04-05T18:02:14.262132Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model = make_model(num_type2=4, dropout_rate=0.1)\n# model.set_weights(weights=[filters,bias])\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2024-04-05T18:02:14.593575Z","iopub.execute_input":"2024-04-05T18:02:14.594353Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"plot_model(model, show_shapes=True, to_file=\"model.png\")","metadata":{"execution":{"iopub.status.busy":"2024-04-04T17:57:19.969611Z","iopub.execute_input":"2024-04-04T17:57:19.970447Z","iopub.status.idle":"2024-04-04T17:57:24.505507Z","shell.execute_reply.started":"2024-04-04T17:57:19.970417Z","shell.execute_reply":"2024-04-04T17:57:24.50431Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Generate Dataset and Fit Model","metadata":{}},{"cell_type":"markdown","source":"Credits to https://www.kaggle.com/tanulsingh077/steganalysis-approaching-as-regression-problem for his loader function.\n* I added a multiprocessor to speed the thing up (empirically **x3 times** decrease but it may depend on the number of images processed)","metadata":{}},{"cell_type":"code","source":"def load_image(data):\n    i, j, img_path, labels = data\n    \n    img = Image.open(img_path)\n    img = img.convert('RGB')\n    img = img.resize((IMG_SIZE, IMG_SIZE), Image.ANTIALIAS)\n    \n    label = labels[i][j]\n    \n    return [np.array(img), label]\n\ndef load_training_data_multi(n_images=100):\n    train_data = []\n    data_paths = [os.listdir(os.path.join(PATH, alg)) for alg in ['Cover'] + ALGORITHMS]\n    labels = [np.zeros(N_IMAGES), np.ones(N_IMAGES), np.ones(N_IMAGES), np.ones(N_IMAGES)]\n    \n    print('Loading...')\n    for i, image_path in enumerate(data_paths):\n        print(f'\\t {i+1}-th folder')\n        \n        train_data_alg = joblib.Parallel(n_jobs=4, backend='threading')(\n            joblib.delayed(load_image)([i, j, os.path.join(PATH, [['Cover'] + ALGORITHMS][0][i], img_p), labels]) for j, img_p in enumerate(image_path[:n_images]))\n\n        train_data.extend(train_data_alg)\n        \n    shuffle(train_data)\n    return train_data","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def load_test_data():\n    test_data = []\n    for img_p in os.listdir(os.path.join(PATH, 'Test')):\n        img = Image.open(os.path.join(PATH, 'Test', img_p))\n        img = img.convert('RGB')\n        img = img.resize((IMG_SIZE, IMG_SIZE), Image.ANTIALIAS)\n        test_data.append([np.array(img)])\n            \n    return test_data","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"start = time()\ntraining_data = load_training_data_multi(n_images=5000)\n\ntrainImages = np.array([i[0] for i in training_data]).reshape(-1, IMG_SIZE, IMG_SIZE, 3)\ntrainLabels = np.array([i[1] for i in training_data], dtype=int)\n\nX_train, X_val, y_train, y_val = train_test_split(trainImages, trainLabels, random_state=42, stratify=trainLabels)\n\n# Then save some RAM\ndel training_data\ndel trainImages\ndel trainLabels\ngc.collect()\n\nprint(f\"{(time() - start) / 60: .2f} min elapsed.\")","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.fit(X_train, y_train,\n          validation_data=(X_val, y_val),\n          batch_size=16,\n          epochs=5, \n          verbose=1)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test = load_test_data()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_images = np.array([i[0] for i in test]).reshape(-1, IMG_SIZE, IMG_SIZE, 3)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"predict = model.predict(test_images, batch_size=64)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_sub['Label'] = predict","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_sub.head()","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"sample_sub.to_csv('submission.csv', index=False)","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## I'm continuously updating the notebook so please stay tunned!","metadata":{}},{"cell_type":"markdown","source":"## To do next:\n* Try with 3 channels (done)\n* Try to increase the size of the images (difficult with that memory limits)\n* Try to increase the depth of the network","metadata":{}},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}