{
  "cells": [
    {
      "cell_type": "markdown",
      "id": "2111825f",
      "metadata": {
        "id": "2111825f"
      },
      "source": [
        "<h1 align='center' dir='rtl' style='color:yellow'>IpsofactoModel</h1>"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "1ab61eab",
      "metadata": {
        "id": "1ab61eab"
      },
      "source": [
        "<h2 align='center' dir='rtl'>فراخوانی کتابخانه های مورد نیاز</h2>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 2,
      "id": "b97c8a8b",
      "metadata": {
        "id": "b97c8a8b"
      },
      "outputs": [],
      "source": [
        "import numpy as np\n",
        "import matplotlib.pyplot as plt\n",
        "import tensorflow as tf\n",
        "from tensorflow import keras\n",
        "from tensorflow.keras.layers import Dense, Input, Flatten, Conv2D\n",
        "from tensorflow.keras.models import Model\n",
        "from tensorflow.keras.datasets import mnist"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "79ef7ea0",
      "metadata": {
        "id": "79ef7ea0"
      },
      "source": [
        "<h2 align='center' dir='rtl'> لود کردن دیتاست MNIST</h2>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 3,
      "id": "511bc7e3",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "511bc7e3",
        "outputId": "b627dc6a-3c34-49c0-a89e-698f2e4ead7e"
      },
      "outputs": [],
      "source": [
        "train_set, test_set = mnist.load_data()"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "8af0ed71",
      "metadata": {
        "id": "8af0ed71"
      },
      "source": [
        "<h2 align='center' dir='rtl'>طراحی لایه های استخراج ویژگی و سنجش نسبت ها</h2>"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "bb994741",
      "metadata": {
        "id": "bb994741"
      },
      "source": [
        "<h3 align='center' dir='rtl'>لایه استخراج ویژگی</h3>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 4,
      "id": "8494ca6d",
      "metadata": {
        "id": "8494ca6d"
      },
      "outputs": [],
      "source": [
        "# لایه استخراج ویژگی\n",
        "kernel_initializer = 'normal'\n",
        "activation = \"relu\"\n",
        "\n",
        "dtype = tf.float32\n",
        "\n",
        "\n",
        "def feature_extractor():\n",
        "    shape=(28,28,1)\n",
        "    chs=1\n",
        "    inputs = Input(shape)\n",
        "    layer = Conv2D(filters=32*chs, kernel_size=(3,3), activation=activation, kernel_initializer=kernel_initializer)(inputs)\n",
        "    layer = Conv2D(filters=16*chs, kernel_size=(3,3), strides=(2, 2), activation=activation, kernel_initializer=kernel_initializer)(layer)\n",
        "    layer = Conv2D(filters=8*chs, kernel_size=(3,3), activation=activation, kernel_initializer=kernel_initializer)(layer)\n",
        "    layer = Conv2D(filters=4*chs, kernel_size=(3,3), strides=(2, 2), activation='linear', kernel_initializer=kernel_initializer)(layer)\n",
        "    layer = Conv2D(filters=64*2, kernel_size=(4,4), activation='softplus', kernel_initializer=kernel_initializer)(layer)\n",
        "    layer = Flatten()(layer)\n",
        "\n",
        "    model = Model(inputs, layer)\n",
        "\n",
        "    return model"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ed717db9",
      "metadata": {
        "id": "ed717db9"
      },
      "source": [
        "<h3 align='center' dir='rtl'>لایه سنجش نسبت ها</h3>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 5,
      "id": "f63a27eb",
      "metadata": {
        "id": "f63a27eb"
      },
      "outputs": [],
      "source": [
        "# لایه سنجش نسبت ها\n",
        "kernel_initializer = 'normal'\n",
        "activation = \"relu\"\n",
        "\n",
        "dtype = tf.float32\n",
        "\n",
        "\n",
        "def comparator():\n",
        "    shape=(64*2+10,)\n",
        "    chs=1\n",
        "    inputs = Input(shape)\n",
        "    layer = Flatten()(inputs)\n",
        "    layer = Dense(32, activation='relu', use_bias=True, kernel_initializer=kernel_initializer)(layer)\n",
        "    layer = Dense(16, activation='relu', use_bias=True, kernel_initializer=kernel_initializer)(layer)\n",
        "    log_prob = Dense(10, activation='softmax', use_bias=True, kernel_initializer=kernel_initializer)(layer)\n",
        "\n",
        "    model = Model(inputs, log_prob)\n",
        "\n",
        "    return model"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "614adfad",
      "metadata": {
        "id": "614adfad"
      },
      "source": [
        "<h2 align='center' dir='rtl'>طراحی‌ و ساختن شبکه عصبی</h2>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 6,
      "id": "6a14ab0a",
      "metadata": {
        "id": "6a14ab0a"
      },
      "outputs": [],
      "source": [
        "def combiner(f0, f1, lbl1):\n",
        "    # ترکیب ویژگی های کلاس مورد مطالعه با کلاس های دیگر\n",
        "\n",
        "    inp = f0 * f1\n",
        "    return tf.concat([inp, lbl1], axis=1)\n",
        "\n",
        "\n",
        "class InfluenceModel(keras.Model):\n",
        "    def __init__(self, feature_extractor, comparator):\n",
        "        super(InfluenceModel, self).__init__()\n",
        "        #  لایه استخراج ویژگی\n",
        "        self.feature_extractor = feature_extractor\n",
        "        # لایه تمام متصل (سنجش نسبت ها)\n",
        "        self.comparator = comparator\n",
        "\n",
        "        self.loss = keras.losses.SparseCategoricalCrossentropy(from_logits=False)\n",
        "        self.metric = keras.metrics.SparseCategoricalAccuracy()\n",
        "\n",
        "\n",
        "    def compile(self, optimizer_1, optimizer_2):\n",
        "        super(InfluenceModel, self).compile()\n",
        "        self.optimizer_1 = optimizer_1\n",
        "        self.optimizer_2 = optimizer_2\n",
        "\n",
        "    def call_at_zero(self, x):\n",
        "        # جدا کردن تصاویر\n",
        "        imgs = x[0]\n",
        "        # جدا کردن برچسب\n",
        "        labels = tf.one_hot(x[1], 10)\n",
        "\n",
        "        # استخراج ویژگی های تصاویر\n",
        "        features = self.feature_extractor(imgs)\n",
        "\n",
        "        n_feat = features.shape[0]\n",
        "        # جدا سازی ویژگی های هر تصویر\n",
        "        features = tf.split(features, n_feat, axis=0)\n",
        "\n",
        "        # ویژگی های استحراج شده تصویر مورد مطالعه\n",
        "        f0 = features[0]\n",
        "\n",
        "        res_mtx = []\n",
        "        res_row = []\n",
        "        for j in range(1, n_feat):\n",
        "            # ترکیب هر نمونه با تصویر مورد مطالعه\n",
        "            f1 = features[j]\n",
        "            lbl1 = labels[j:j+1,:]\n",
        "            inp = combiner(f0, f1, lbl1)\n",
        "\n",
        "            res_row += [inp]\n",
        "\n",
        "        # محاسبه نسبت هر نمونه با تصویر مورد مطالعه\n",
        "        res_row = tf.concat(res_row, axis=0)\n",
        "        res_mtx = self.comparator(res_row)\n",
        "\n",
        "        # میانگین بر روی نمونه ها\n",
        "        res = tf.reduce_sum(res_mtx, axis=0) / (n_feat-1)\n",
        "        return res, res_mtx\n",
        "\n",
        "    # Feed - Forward\n",
        "    def call(self, x):\n",
        "\n",
        "        # جدا کردن تصاویر\n",
        "        imgs = x[0]\n",
        "        # جدا کردن برچسب\n",
        "        labels = tf.one_hot(x[1], 10)\n",
        "\n",
        "        # استخراج ویژگی های تصاویر\n",
        "        features = self.feature_extractor(imgs)\n",
        "\n",
        "        # جدا سازی ویژگی های هر تصویر\n",
        "        n_feat = features.shape[0]\n",
        "        features = tf.split(features, n_feat, axis=0)\n",
        "\n",
        "\n",
        "        res_mtx = []\n",
        "        # ساخت جفت های ترکیب شده به ازای هر تصویر برای اموزش مدل\n",
        "        for i in range(n_feat):\n",
        "            res_row = []\n",
        "            for j in range(n_feat):\n",
        "                f0 = features[i]\n",
        "                f1 = features[j]\n",
        "                lbl1 = labels[j:j+1,:]\n",
        "                inp = combiner(f0, f1, lbl1)\n",
        "                res_row += [inp]\n",
        "            res_row = tf.concat(res_row, axis=0)\n",
        "\n",
        "            # محاسبه سنجش تاثیر حضور تصاویر دیگر در پیش بینی تصویر مورد مطالعه\n",
        "            res_mtx += [self.comparator(res_row)]\n",
        "\n",
        "        # میانگین گیری بر روی نتایج حاصل\n",
        "        res_mtx = tf.stack(res_mtx)\n",
        "\n",
        "        res_mtx = tf.transpose(res_mtx, [2, 1, 0])\n",
        "\n",
        "        res_mtx -= tf.linalg.diag(tf.linalg.diag_part(res_mtx))\n",
        "\n",
        "        res_mtx = tf.transpose(res_mtx, [2, 1, 0])\n",
        "\n",
        "        res = tf.reduce_sum(res_mtx, axis=1) / (n_feat-1)\n",
        "\n",
        "        return res, res_mtx\n",
        "\n",
        "    def train_step(self, x):\n",
        "        # تعریف تابع ضرر\n",
        "        loss = keras.losses.SparseCategoricalCrossentropy(from_logits=True)\n",
        "\n",
        "        # محاسبه گرادیان و اعمال گرادیان\n",
        "        with tf.GradientTape() as tape_1, tf.GradientTape() as tape_2:\n",
        "            output, _ = self.call(x)\n",
        "            loss_batch = loss(x[1], output)\n",
        "        gradient_1 = tape_1.gradient(loss_batch, self.feature_extractor.trainable_variables)\n",
        "        self.optimizer_1.apply_gradients(zip(gradient_1, self.feature_extractor.trainable_variables))\n",
        "        gradient_2 = tape_2.gradient(loss_batch, self.comparator.trainable_variables)\n",
        "        self.optimizer_2.apply_gradients(zip(gradient_2, self.comparator.trainable_variables))\n",
        "\n",
        "        # اعمال معیار ارزیابی\n",
        "        metric_batch = self.metric(x[1], output)\n",
        "\n",
        "        return {\"loss\": loss_batch, \"metric\": metric_batch}\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "c1a0891a",
      "metadata": {
        "id": "c1a0891a"
      },
      "source": [
        "<h2 align='center' dir='rtl'>اموزش مدل</h2>"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "67d4458d",
      "metadata": {
        "id": "67d4458d"
      },
      "source": [
        "<h3 align='center' dir='rtl'>اتصال لایه ها</h3>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 8,
      "id": "f6ed77af",
      "metadata": {
        "id": "f6ed77af"
      },
      "outputs": [],
      "source": [
        "m1 = feature_extractor()\n",
        "m2 = comparator()\n",
        "\n",
        "model = InfluenceModel(m1, m2)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "16f4ec08",
      "metadata": {
        "id": "16f4ec08"
      },
      "source": [
        "<h3 align='center' dir='rtl'>کامپایل کردن مدل</h3>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 9,
      "id": "0bbddc92",
      "metadata": {
        "id": "0bbddc92"
      },
      "outputs": [],
      "source": [
        "optimizer_1 = tf.optimizers.Adam(learning_rate=1e-3, beta_1=0.9)\n",
        "optimizer_2 = tf.optimizers.Adam(learning_rate=1e-3, beta_1=0.9)\n",
        "\n",
        "m1.compile(optimizer_1)\n",
        "m2.compile(optimizer_2)\n",
        "model.compile(optimizer_1, optimizer_2)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "ac325eb7",
      "metadata": {
        "id": "ac325eb7"
      },
      "source": [
        "<h3 align='center' dir='rtl'>اموزش مدل</h3>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 10,
      "id": "c5e948ab",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "c5e948ab",
        "outputId": "137ff429-1ccf-4e2d-dc78-6247b58e893f",
        "scrolled": true
      },
      "outputs": [
        {
          "name": "stdout",
          "output_type": "stream",
          "text": [
            "Epoch 1/3\n",
            "1875/1875 [==============================] - 274s 119ms/step - loss: 1.9457 - metric: 0.5935\n",
            "Epoch 2/3\n",
            "1875/1875 [==============================] - 232s 124ms/step - loss: 1.6846 - metric: 0.7640\n",
            "Epoch 3/3\n",
            "1875/1875 [==============================] - 236s 126ms/step - loss: 1.5131 - metric: 0.9450\n"
          ]
        },
        {
          "data": {
            "text/plain": [
              "<keras.callbacks.History at 0x7fd574be5550>"
            ]
          },
          "execution_count": 10,
          "metadata": {},
          "output_type": "execute_result"
        }
      ],
      "source": [
        "model.fit(train_set[0], train_set[1], epochs=3, batch_size=32)\n"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "a42879cd",
      "metadata": {
        "id": "a42879cd"
      },
      "source": [
        "<h2 align='center' dir='rtl'>اماده سازی نمونه ها و محاسبه اهمیت هر نمونه نسبت به حضور دیگر نمونه ها</h2>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 11,
      "id": "2a669978",
      "metadata": {
        "id": "2a669978"
      },
      "outputs": [],
      "source": [
        "def get_samples(sample_ind, batch_size=64):\n",
        "\n",
        "    # اماده سازی نمونه ها\n",
        "    sample_image = test_set[0][sample_ind:sample_ind+batch_size,...]\n",
        "    sample_label = test_set[1][sample_ind:sample_ind+batch_size,...]\n",
        "    return sample_image, sample_label"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 12,
      "id": "c910c017",
      "metadata": {
        "id": "c910c017"
      },
      "outputs": [],
      "source": [
        "def get_imporance_function(sample_ind):\n",
        "    loss_fnc = tf.keras.losses.SparseCategoricalCrossentropy(reduction='none', from_logits=False)\n",
        "    img, lbl = get_samples(sample_ind, 1)\n",
        "\n",
        "    def get_batch_importance(batch_ind, batch_size=64):\n",
        "        sample_images, sample_labels = get_samples(batch_ind, batch_size)\n",
        "        sample_images = np.concatenate([img, sample_images])\n",
        "        sample_labels = np.concatenate([lbl, sample_labels])\n",
        "\n",
        "        probs_vec, probs_mtx = model.call_at_zero([sample_images, sample_labels])\n",
        "        samples = probs_mtx.numpy()\n",
        "        probs_vec = probs_vec.numpy()\n",
        "\n",
        "        m = samples.mean(axis=0)\n",
        "\n",
        "        # حذف نمونه مورد مطالعه و مشاهده تاثیر حضور دیگر نمونه و عدم حضور نمونه بر پیس بینی\n",
        "        sample_output = samples.sum(axis=0, keepdims=True) - samples\n",
        "        sample_output = sample_output/(batch_size-1)\n",
        "        loss_with_sample = loss_fnc(lbl, m)\n",
        "        loss_without_sample = loss_fnc(np.array([lbl]*batch_size), sample_output)\n",
        "        sample_importance = loss_with_sample-loss_without_sample\n",
        "        return sample_images[1:,...], sample_importance, probs_vec, samples\n",
        "    return get_batch_importance, img, lbl"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 83,
      "id": "a4761918",
      "metadata": {
        "id": "a4761918"
      },
      "outputs": [],
      "source": [
        "batch_size = 64\n",
        "\n",
        "batch_ind = batch_size+1\n",
        "\n",
        "sample_ind = batch_ind-1\n",
        "importance_function_batch, img, lbl = get_imporance_function(sample_ind)\n",
        "sample_images, importance, probs_vec, sample_output = importance_function_batch(batch_ind)"
      ]
    },
    {
      "cell_type": "markdown",
      "id": "52cfc350",
      "metadata": {
        "id": "52cfc350"
      },
      "source": [
        "<h2 align='center' dir='rtl'>مشاهده تاثیر دیگر نمونه ها بر پیش بینی مدل</h2>"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 89,
      "id": "7b17b3c2",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 486
        },
        "id": "7b17b3c2",
        "outputId": "de0f988f-0e8d-4572-a28e-e3acecca9d1b"
      },
      "outputs": [
        {
          "data": {
            "image/png": "[encoded data, 30152 characters]",
            "text/plain": [
              "<Figure size 1500x500 with 2 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "plt.figure(figsize=(15,5))\n",
        "plt.subplot(121)\n",
        "plt.imshow(img[0])\n",
        "plt.title(lbl[0])\n",
        "plt.axis('off')\n",
        "\n",
        "plt.subplot(122)\n",
        "plt.plot(probs_vec, label='model probabilities')\n",
        "plt.plot(lbl, 1, 'rx', label='correct label')\n",
        "plt.legend();"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 86,
      "id": "e1124039",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 1000
        },
        "id": "e1124039",
        "outputId": "a9579fb9-09e7-467a-f897-73eec99eb1ec"
      },
      "outputs": [
        {
          "data": {
            "image/png": "[encoded data, 134628 characters]",
            "text/plain": [
              "<Figure size 1500x1500 with 64 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "from matplotlib import pyplot as plt\n",
        "import numpy as np\n",
        "\n",
        "inds = np.argsort(importance)\n",
        "\n",
        "plt.figure(figsize=(15,15))\n",
        "for i in range(len(inds)):\n",
        "    plt.subplot(batch_size//8+1, 8, i+1)\n",
        "    plt.imshow(sample_images[inds[i],...])\n",
        "    plt.title(\"%.7f\"%(importance[inds[i]]))\n",
        "    plt.axis('off')"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": 88,
      "id": "7751107a",
      "metadata": {},
      "outputs": [
        {
          "data": {
            "text/plain": [
              "[<matplotlib.lines.Line2D at 0x7fd4f2e92ed0>]"
            ]
          },
          "execution_count": 88,
          "metadata": {},
          "output_type": "execute_result"
        },
        {
          "data": {
            "image/png": "[encoded data, 17288 characters]",
            "text/plain": [
              "<Figure size 640x480 with 1 Axes>"
            ]
          },
          "metadata": {},
          "output_type": "display_data"
        }
      ],
      "source": [
        "# نمایش تاثیر پنج داده مهمتر در بین نمونه های همراه با نمونه اصلی\n",
        "plt.figure()\n",
        "plt.plot(sample_output[inds[:5]].mean(axis=0))"
      ]
    }
  ],
  "metadata": {
    "colab": {
      "provenance": []
    },
    "kernelspec": {
      "display_name": "CV",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.11.5"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 5
}
