├── README.md ├── LICENSE ├── .gitignore ├── vertical_federated_learning_demo.ipynb ├── vertical_backdoor_demo.ipynb └── vertical_backdoor_attack.ipynb /README.md: -------------------------------------------------------------------------------- 1 | # Vertical-Federated-Learning -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 yiyiyi4321 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /vertical_federated_learning_demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 2, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import tensorflow as tf\n", 10 | "import numpy as np\n", 11 | "from tensorflow.keras.layers import Dense, Flatten \n", 12 | "from tensorflow.keras import Model\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "\n", 15 | "# Download a dataset\n", 16 | "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n", 17 | "\n", 18 | "# Batch and shuffle the data\n", 19 | "train_ds = tf.data.Dataset.from_tensor_slices(\n", 20 | " (x_train.astype('float32') / 255, y_train)).shuffle(1024).batch(32)\n", 21 | "\n", 22 | "test_ds = tf.data.Dataset.from_tensor_slices(\n", 23 | " (x_test.astype('float32') / 255, y_test)).batch(32)" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 3, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "class VFLPassiveModel(Model):\n", 33 | " def __init__(self):\n", 34 | " super(VFLPassiveModel, self).__init__()\n", 35 | " self.flatten = Flatten()\n", 36 | " self.d1 = Dense(10, name=\"dense1\")\n", 37 | "\n", 38 | " def call(self, x):\n", 39 | " x = self.flatten(x)\n", 40 | " return self.d1(x)" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 26, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "class VFLActiveModel(Model):\n", 50 | " def __init__(self):\n", 51 | " super(VFLActiveModel, self).__init__()\n", 52 | " self.added = tf.keras.layers.Add()\n", 53 | "\n", 54 | " def call(self, x):\n", 55 | " x = self.added(x)\n", 56 | " return tf.keras.layers.Softmax()(x)" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 32, 62 | "metadata": {}, 63 | "outputs": [ 64 | { 65 | "name": "stdout", 66 | "output_type": "stream", 67 | "text": [ 68 | "Epoch 1, Loss: 0.396317720413208, Accuracy: 88.98332977294922, Test Loss: 0.2956085503101349, Test Accuracy: 91.50999450683594\n", 69 | "Epoch 2, Loss: 0.289332777261734, Accuracy: 91.95999908447266, Test Loss: 0.28157228231430054, Test Accuracy: 91.93999481201172\n", 70 | "Epoch 3, Loss: 0.27582597732543945, Accuracy: 92.39500427246094, Test Loss: 0.28288891911506653, Test Accuracy: 92.04000091552734\n", 71 | "Epoch 4, Loss: 0.2672642767429352, Accuracy: 92.70500183105469, Test Loss: 0.2774609327316284, Test Accuracy: 92.06999969482422\n", 72 | "Epoch 5, Loss: 0.26180967688560486, Accuracy: 92.7933349609375, Test Loss: 0.2739064395427704, Test Accuracy: 92.29000091552734\n" 73 | ] 74 | } 75 | ], 76 | "source": [ 77 | "passive_model = VFLPassiveModel()\n", 78 | "active_model = VFLActiveModel()\n", 79 | "\n", 80 | "loss_object = tf.keras.losses.SparseCategoricalCrossentropy()\n", 81 | "optimizer = tf.keras.optimizers.Adam()\n", 82 | "\n", 83 | "train_loss = tf.keras.metrics.Mean(name='train_loss')\n", 84 | "train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')\n", 85 | "\n", 86 | "test_loss = tf.keras.metrics.Mean(name='test_loss')\n", 87 | "test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy')\n", 88 | "\n", 89 | "EPOCHS = 5\n", 90 | "\n", 91 | "for epoch in range(EPOCHS):\n", 92 | " # For each batch of images and labels\n", 93 | " for images, labels in train_ds:\n", 94 | " with tf.GradientTape() as passive_tape:\n", 95 | " # passive_model sends passive_output to active_model\n", 96 | " passive_output = passive_model(images)\n", 97 | " with tf.GradientTape() as active_tape:\n", 98 | " active_tape.watch(passive_output)\n", 99 | " active_output = active_model([passive_output, passive_output])\n", 100 | " loss = loss_object(labels, active_output)\n", 101 | " # active_model sends passive_output_gradients back to passive_model\n", 102 | " passive_output_gradients = active_tape.gradient(loss, passive_output)\n", 103 | " #print(passive_output_gradients)\n", 104 | " passive_output_loss = tf.multiply(passive_output, passive_output_gradients.numpy())\n", 105 | " passive_weight_gradients = passive_tape.gradient(passive_output_loss, passive_model.trainable_variables)\n", 106 | " optimizer.apply_gradients(zip(passive_weight_gradients, passive_model.trainable_variables))\n", 107 | "\n", 108 | " train_loss(loss)\n", 109 | " train_accuracy(labels, active_output)\n", 110 | "\n", 111 | " for test_images, test_labels in test_ds:\n", 112 | " passive_output = passive_model(test_images)\n", 113 | " active_output = active_model([passive_output, passive_output])\n", 114 | " t_loss = loss_object(test_labels, active_output)\n", 115 | "\n", 116 | " test_loss(t_loss)\n", 117 | " test_accuracy(test_labels, active_output)\n", 118 | "\n", 119 | " template = 'Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, Test Accuracy: {}'\n", 120 | " print(template.format(epoch+1,\n", 121 | " train_loss.result(),\n", 122 | " train_accuracy.result()*100,\n", 123 | " test_loss.result(),\n", 124 | " test_accuracy.result()*100))\n", 125 | "\n", 126 | " # Reset the metrics for the next epoch\n", 127 | " train_loss.reset_states()\n", 128 | " train_accuracy.reset_states()\n", 129 | " test_loss.reset_states()\n", 130 | " test_accuracy.reset_states()" 131 | ] 132 | } 133 | ], 134 | "metadata": { 135 | "kernelspec": { 136 | "display_name": "Python 3", 137 | "language": "python", 138 | "name": "python3" 139 | }, 140 | "language_info": { 141 | "codemirror_mode": { 142 | "name": "ipython", 143 | "version": 3 144 | }, 145 | "file_extension": ".py", 146 | "mimetype": "text/x-python", 147 | "name": "python", 148 | "nbconvert_exporter": "python", 149 | "pygments_lexer": "ipython3", 150 | "version": "3.7.3" 151 | } 152 | }, 153 | "nbformat": 4, 154 | "nbformat_minor": 2 155 | } 156 | -------------------------------------------------------------------------------- /vertical_backdoor_demo.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import tensorflow as tf\n", 10 | "import numpy as np\n", 11 | "from tensorflow.keras.layers import Dense, Flatten \n", 12 | "from tensorflow.keras import Model\n", 13 | "import matplotlib.pyplot as plt\n", 14 | "\n", 15 | "# Download a dataset\n", 16 | "(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n", 17 | "\n", 18 | "# Batch and shuffle the data\n", 19 | "train_ds = tf.data.Dataset.from_tensor_slices(\n", 20 | " (x_train.astype('float32') / 255, y_train)).shuffle(1024).batch(32)\n", 21 | "\n", 22 | "test_ds = tf.data.Dataset.from_tensor_slices(\n", 23 | " (x_test[y_test==2].astype('float32') / 255, y_test[y_test==2])).batch(32)" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 2, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "class VFLPassiveModel(Model):\n", 33 | " def __init__(self):\n", 34 | " super(VFLPassiveModel, self).__init__()\n", 35 | " self.flatten = Flatten()\n", 36 | " self.d1 = Dense(10, name=\"dense1\")\n", 37 | "\n", 38 | " def call(self, x):\n", 39 | " x = self.flatten(x)\n", 40 | " return self.d1(x)" 41 | ] 42 | }, 43 | { 44 | "cell_type": "code", 45 | "execution_count": 3, 46 | "metadata": {}, 47 | "outputs": [], 48 | "source": [ 49 | "class VFLActiveModel(Model):\n", 50 | " def __init__(self):\n", 51 | " super(VFLActiveModel, self).__init__()\n", 52 | " self.added = tf.keras.layers.Add()\n", 53 | "\n", 54 | " def call(self, x):\n", 55 | " x = self.added(x)\n", 56 | " return tf.keras.layers.Softmax()(x)" 57 | ] 58 | }, 59 | { 60 | "cell_type": "code", 61 | "execution_count": 7, 62 | "metadata": {}, 63 | "outputs": [ 64 | { 65 | "name": "stdout", 66 | "output_type": "stream", 67 | "text": [ 68 | "Epoch 1, Loss: 0.405218243598938, Accuracy: 88.75, Test Loss: 0.4513963758945465, Test Accuracy: 87.30619812011719\n", 69 | "Epoch 2, Loss: 0.2906039357185364, Accuracy: 91.85832977294922, Test Loss: 0.4250626266002655, Test Accuracy: 87.8875961303711\n", 70 | "Epoch 3, Loss: 0.27544891834259033, Accuracy: 92.40166473388672, Test Loss: 0.459330677986145, Test Accuracy: 87.4030990600586\n", 71 | "Epoch 4, Loss: 0.26715561747550964, Accuracy: 92.53500366210938, Test Loss: 0.4625568687915802, Test Accuracy: 87.1124038696289\n", 72 | "Epoch 5, Loss: 0.2622341513633728, Accuracy: 92.7066650390625, Test Loss: 0.5050548315048218, Test Accuracy: 86.33721160888672\n" 73 | ] 74 | } 75 | ], 76 | "source": [ 77 | "passive_model = VFLPassiveModel()\n", 78 | "active_model = VFLActiveModel()\n", 79 | "\n", 80 | "loss_object = tf.keras.losses.SparseCategoricalCrossentropy()\n", 81 | "optimizer = tf.keras.optimizers.Adam()\n", 82 | "\n", 83 | "train_loss = tf.keras.metrics.Mean(name='train_loss')\n", 84 | "train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')\n", 85 | "\n", 86 | "test_loss = tf.keras.metrics.Mean(name='test_loss')\n", 87 | "test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy')\n", 88 | "\n", 89 | "EPOCHS = 5\n", 90 | "\n", 91 | "for epoch in range(EPOCHS):\n", 92 | " # For each batch of images and labels\n", 93 | " for images, labels in train_ds:\n", 94 | " with tf.GradientTape() as passive_tape:\n", 95 | " # passive_model sends passive_output to active_model\n", 96 | " passive_output = passive_model(images)\n", 97 | " with tf.GradientTape() as active_tape:\n", 98 | " active_tape.watch(passive_output)\n", 99 | " active_output = active_model([passive_output, passive_output])\n", 100 | " loss = loss_object(labels, active_output)\n", 101 | " # active_model sends passive_output_gradients back to passive_model\n", 102 | " passive_output_gradients = active_tape.gradient(loss, passive_output)\n", 103 | " #print(passive_output_gradients)\n", 104 | " passive_output_lost = tf.multiply(passive_output, passive_output_gradients.numpy())\n", 105 | " passive_weight_gradients = passive_tape.gradient(passive_output_lost, passive_model.trainable_variables)\n", 106 | " optimizer.apply_gradients(zip(passive_weight_gradients, passive_model.trainable_variables))\n", 107 | "\n", 108 | " train_loss(loss)\n", 109 | " train_accuracy(labels, active_output)\n", 110 | "\n", 111 | " for test_images, test_labels in test_ds:\n", 112 | " passive_output = passive_model(test_images)\n", 113 | " active_output = active_model([passive_output, passive_output])\n", 114 | " t_loss = loss_object(test_labels, active_output)\n", 115 | "\n", 116 | " test_loss(t_loss)\n", 117 | " test_accuracy(test_labels, active_output)\n", 118 | "\n", 119 | " template = 'Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, Test Accuracy: {}'\n", 120 | " print(template.format(epoch+1,\n", 121 | " train_loss.result(),\n", 122 | " train_accuracy.result()*100,\n", 123 | " test_loss.result(),\n", 124 | " test_accuracy.result()*100))\n", 125 | "\n", 126 | " # Reset the metrics for the next epoch\n", 127 | " train_loss.reset_states()\n", 128 | " train_accuracy.reset_states()\n", 129 | " test_loss.reset_states()\n", 130 | " test_accuracy.reset_states()" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": 4, 136 | "metadata": {}, 137 | "outputs": [], 138 | "source": [ 139 | "import numpy as np\n", 140 | "\n", 141 | "def get_possioned_gradients(passive_output_gradients, passive_output, N, class1, class2, alpha = 1.0):\n", 142 | " passive_output_gradients -= passive_output\n", 143 | " #print(passive_output_gradients)\n", 144 | " attack_mat = np.eye(N, dtype='float32')\n", 145 | " attack_mat[:, class2] += attack_mat[:, class1]*alpha\n", 146 | " attack_mat[:, class1] -= attack_mat[:, class1]*alpha\n", 147 | " #print(attack_mat)\n", 148 | " passive_output_gradients = tf.matmul(passive_output_gradients, attack_mat)\n", 149 | " passive_output_gradients += passive_output\n", 150 | " return passive_output_gradients" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 5, 156 | "metadata": {}, 157 | "outputs": [ 158 | { 159 | "name": "stdout", 160 | "output_type": "stream", 161 | "text": [ 162 | "Epoch 1, Loss: 0.5872719287872314, Accuracy: 81.26000213623047, Test Loss: 1.1180531978607178, Test Accuracy: 45.54263687133789\n", 163 | "Epoch 2, Loss: 0.47115832567214966, Accuracy: 83.81166076660156, Test Loss: 1.2937822341918945, Test Accuracy: 33.81782913208008\n", 164 | "Epoch 3, Loss: 0.47255071997642517, Accuracy: 83.44166564941406, Test Loss: 1.4451520442962646, Test Accuracy: 27.03488540649414\n", 165 | "Epoch 4, Loss: 0.4741647243499756, Accuracy: 83.53666687011719, Test Loss: 1.561234474182129, Test Accuracy: 23.643409729003906\n", 166 | "Epoch 5, Loss: 0.475004643201828, Accuracy: 83.59000396728516, Test Loss: 1.557436466217041, Test Accuracy: 25.387596130371094\n" 167 | ] 168 | } 169 | ], 170 | "source": [ 171 | "passive_model = VFLPassiveModel()\n", 172 | "active_model = VFLActiveModel()\n", 173 | "\n", 174 | "loss_object = tf.keras.losses.SparseCategoricalCrossentropy()\n", 175 | "optimizer = tf.keras.optimizers.Adam()\n", 176 | "\n", 177 | "train_loss = tf.keras.metrics.Mean(name='train_loss')\n", 178 | "train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')\n", 179 | "\n", 180 | "test_loss = tf.keras.metrics.Mean(name='test_loss')\n", 181 | "test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy')\n", 182 | "\n", 183 | "EPOCHS = 5\n", 184 | "\n", 185 | "for epoch in range(EPOCHS):\n", 186 | " # For each batch of images and labels\n", 187 | " for images, labels in train_ds:\n", 188 | " with tf.GradientTape() as passive_tape:\n", 189 | " # passive_model sends passive_output to active_model\n", 190 | " passive_output = passive_model(images)\n", 191 | " with tf.GradientTape() as active_tape:\n", 192 | " active_tape.watch(passive_output)\n", 193 | " active_output = active_model([passive_output, passive_output])\n", 194 | " loss = loss_object(labels, active_output)\n", 195 | " # active_model sends passive_output_gradients back to passive_model\n", 196 | " passive_output_gradients = active_tape.gradient(loss, passive_output)\n", 197 | " passive_output_gradients = \\\n", 198 | " get_possioned_gradients(passive_output_gradients, passive_output, 10, 2, 4)\n", 199 | " passive_output_loss = tf.multiply(passive_output, passive_output_gradients.numpy())\n", 200 | " passive_weight_gradients = passive_tape.gradient(passive_output_loss, passive_model.trainable_variables)\n", 201 | " optimizer.apply_gradients(zip(passive_weight_gradients, passive_model.trainable_variables))\n", 202 | "\n", 203 | " train_loss(loss)\n", 204 | " train_accuracy(labels, active_output)\n", 205 | "\n", 206 | " for test_images, test_labels in test_ds:\n", 207 | " passive_output = passive_model(test_images)\n", 208 | " active_output = active_model([passive_output, passive_output])\n", 209 | " t_loss = loss_object(test_labels, active_output)\n", 210 | "\n", 211 | " test_loss(t_loss)\n", 212 | " test_accuracy(test_labels, active_output)\n", 213 | "\n", 214 | " template = 'Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, Test Accuracy: {}'\n", 215 | " print(template.format(epoch+1,\n", 216 | " train_loss.result(),\n", 217 | " train_accuracy.result()*100,\n", 218 | " test_loss.result(),\n", 219 | " test_accuracy.result()*100))\n", 220 | "\n", 221 | " # Reset the metrics for the next epoch\n", 222 | " train_loss.reset_states()\n", 223 | " train_accuracy.reset_states()\n", 224 | " test_loss.reset_states()\n", 225 | " test_accuracy.reset_states()" 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": 6, 231 | "metadata": {}, 232 | "outputs": [ 233 | { 234 | "name": "stdout", 235 | "output_type": "stream", 236 | "text": [ 237 | "[ 14.362151 9. 224.04999 47.196995 522.3977 0.637849\n", 238 | " 22. 6.9999685 178.35532 7.0000315]\n" 239 | ] 240 | }, 241 | { 242 | "data": { 243 | "image/png": "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\n", 244 | "text/plain": [ 245 | "
" 246 | ] 247 | }, 248 | "metadata": { 249 | "needs_background": "light" 250 | }, 251 | "output_type": "display_data" 252 | } 253 | ], 254 | "source": [ 255 | "import matplotlib.pyplot as plt\n", 256 | "import numpy as np\n", 257 | "\n", 258 | "x_val = x_test[y_test==2]\n", 259 | "y_val = y_test[y_test==2]\n", 260 | "\n", 261 | "passive_output = passive_model(x_val)\n", 262 | "active_output = active_model([passive_output, passive_output])\n", 263 | "\n", 264 | "output_distribution = np.sum(active_output, axis=0)\n", 265 | "print(output_distribution)\n", 266 | "\n", 267 | "n = 10\n", 268 | "X = np.arange(n)\n", 269 | "plt.bar(X, output_distribution)\n", 270 | "\n", 271 | "plt.show()" 272 | ] 273 | }, 274 | { 275 | "cell_type": "code", 276 | "execution_count": 36, 277 | "metadata": {}, 278 | "outputs": [ 279 | { 280 | "name": "stdout", 281 | "output_type": "stream", 282 | "text": [ 283 | "[[1. 0. 0. 0. 0. 0. 0. 0. 0. 0.]\n", 284 | " [0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]\n", 285 | " [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]\n", 286 | " [0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]\n", 287 | " [0. 0. 0. 0. 1. 0. 0. 0. 0. 0.]\n", 288 | " [0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]\n", 289 | " [0. 0. 0. 0. 0. 0. 1. 0. 0. 0.]\n", 290 | " [0. 0. 0. 0. 0. 0. 0. 1. 0. 0.]\n", 291 | " [0. 0. 0. 0. 0. 0. 0. 0. 1. 0.]\n", 292 | " [0. 0. 0. 0. 0. 0. 0. 0. 0. 1.]]\n", 293 | "[[0.5301766 0.36834355 0.0751616 0.34628156 0.67369025 0.71920954\n", 294 | " 0.39001503 0.05330744 0.60812417 0.57757241]\n", 295 | " [0.11696672 0.91286319 0.76614699 0.18240779 0.34124305 0.64034105\n", 296 | " 0.14179049 0.5599264 0.92628808 0.04293072]\n", 297 | " [0.8208049 0.58776502 0.82512238 0.75042469 0.143793 0.9864077\n", 298 | " 0.28055798 0.66674831 0.15096047 0.8785071 ]]\n" 299 | ] 300 | }, 301 | { 302 | "data": { 303 | "text/plain": [ 304 | "array([[0.5301766 , 0.36834355, 0. , 0.34628156, 0.74885185,\n", 305 | " 0.71920954, 0.39001503, 0.05330744, 0.60812417, 0.57757241],\n", 306 | " [0.11696672, 0.91286319, 0. , 0.18240779, 1.10739004,\n", 307 | " 0.64034105, 0.14179049, 0.5599264 , 0.92628808, 0.04293072],\n", 308 | " [0.8208049 , 0.58776502, 0. , 0.75042469, 0.96891539,\n", 309 | " 0.9864077 , 0.28055798, 0.66674831, 0.15096047, 0.8785071 ]])" 310 | ] 311 | }, 312 | "execution_count": 36, 313 | "metadata": {}, 314 | "output_type": "execute_result" 315 | } 316 | ], 317 | "source": [ 318 | "N = 10\n", 319 | "class1 = 2\n", 320 | "class2 = 4\n", 321 | "alpha = 1.0\n", 322 | "\n", 323 | "attack_mat = np.eye(N, dtype='float32')\n", 324 | "attack_mat[:, class2] += attack_mat[:, class1]*alpha\n", 325 | "attack_mat[:, class1] -= attack_mat[:, class1]*alpha\n", 326 | "\n", 327 | "a = np.random.rand(3, N)\n", 328 | "print(attack_mat)\n", 329 | "b = np.matmul(a, attack_mat)\n", 330 | "print(a)\n", 331 | "b" 332 | ] 333 | } 334 | ], 335 | "metadata": { 336 | "kernelspec": { 337 | "display_name": "Python 3", 338 | "language": "python", 339 | "name": "python3" 340 | }, 341 | "language_info": { 342 | "codemirror_mode": { 343 | "name": "ipython", 344 | "version": 3 345 | }, 346 | "file_extension": ".py", 347 | "mimetype": "text/x-python", 348 | "name": "python", 349 | "nbconvert_exporter": "python", 350 | "pygments_lexer": "ipython3", 351 | "version": "3.7.3" 352 | } 353 | }, 354 | "nbformat": 4, 355 | "nbformat_minor": 2 356 | } 357 | -------------------------------------------------------------------------------- /vertical_backdoor_attack.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "import tensorflow as tf\n", 10 | "from tensorflow.keras.layers import Dense, Flatten \n", 11 | "from tensorflow.keras import Model\n", 12 | "import matplotlib.pyplot as plt\n", 13 | "import numpy as np\n", 14 | "\n", 15 | "# # Download a dataset\n", 16 | "# (x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n", 17 | "\n", 18 | "# # Batch and shuffle the data\n", 19 | "# train_ds = tf.data.Dataset.from_tensor_slices(\n", 20 | "# (x_train.astype('float32') / 255, y_train)).shuffle(1024).batch(32)\n", 21 | "\n", 22 | "# test_ds = tf.data.Dataset.from_tensor_slices(\n", 23 | "# (x_test.astype('float32') / 255, y_test)).batch(32)\n" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 2, 29 | "metadata": {}, 30 | "outputs": [ 31 | { 32 | "name": "stdout", 33 | "output_type": "stream", 34 | "text": [ 35 | "['sky', 'clouds', 'person', 'water', 'animal']\n", 36 | "df shape (161789, 1)\n", 37 | "df shape (161789, 1)\n", 38 | "df shape (161789, 1)\n", 39 | "df shape (161789, 1)\n", 40 | "df shape (161789, 1)\n", 41 | "(60157, 5)\n", 42 | "data_dir: \n", 43 | "features_path: NUS_WIDE/NUS_WID_Low_Level_Features/Low_Level_Features\n", 44 | "b datasets features 64\n", 45 | "b datasets features 225\n", 46 | "b datasets features 144\n", 47 | "b datasets features 73\n", 48 | "b datasets features 128\n", 49 | "X image shape: (60157, 634)\n", 50 | "X text shape: (60157, 1000)\n", 51 | " \n" 52 | ] 53 | } 54 | ], 55 | "source": [ 56 | "from nus_wide_data_util import get_labeled_data, get_top_k_labels\n", 57 | "\n", 58 | "class_num = 5\n", 59 | "\n", 60 | "top_k = get_top_k_labels('', top_k=class_num)\n", 61 | "print(top_k)\n", 62 | "\n", 63 | "data_X_image, data_X_text, data_Y = get_labeled_data('', top_k, 60000)\n", 64 | "print(type(data_X_image), type(data_X_text), type(data_Y))\n", 65 | "train_num = 50000\n", 66 | "\n", 67 | "x_train, x_test, y_train, y_test = (np.array(data_X_image[:train_num]).astype('float32'), np.array(data_X_text[:train_num]).astype('float32')), \\\n", 68 | " (np.array(data_X_image[train_num:]).astype('float32'), np.array(data_X_text[train_num:]).astype('float32')), \\\n", 69 | " np.array(data_Y[:train_num]).astype('float32'), np.array(data_Y[train_num:]).astype('float32')\n", 70 | "\n", 71 | "# Batch and shuffle the data\n", 72 | "train_ds = tf.data.Dataset.from_tensor_slices(\n", 73 | " (x_train, y_train)).shuffle(1024).batch(32)\n", 74 | "\n", 75 | "test_ds = tf.data.Dataset.from_tensor_slices(\n", 76 | " (x_test, y_test)).batch(32)\n" 77 | ] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "execution_count": 3, 82 | "metadata": {}, 83 | "outputs": [ 84 | { 85 | "data": { 86 | "text/plain": [ 87 | "array([2156., 564., 3775., 1093., 2412.], dtype=float32)" 88 | ] 89 | }, 90 | "execution_count": 3, 91 | "metadata": {}, 92 | "output_type": "execute_result" 93 | } 94 | ], 95 | "source": [ 96 | "np.sum(y_test, axis=0)" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": 4, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "class VFLPassiveModel(Model):\n", 106 | " def __init__(self):\n", 107 | " super(VFLPassiveModel, self).__init__()\n", 108 | " self.flatten = Flatten()\n", 109 | " self.d1 = Dense(class_num, name=\"dense1\")\n", 110 | "\n", 111 | " def call(self, x):\n", 112 | " x = self.flatten(x)\n", 113 | " return self.d1(x)" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": 5, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "import numpy as np\n", 123 | "\n", 124 | "class VFLActiveModel(Model):\n", 125 | " def __init__(self):\n", 126 | " super(VFLActiveModel, self).__init__()\n", 127 | " self.added = tf.keras.layers.Add()\n", 128 | "\n", 129 | " def call(self, x):\n", 130 | " x = self.added(x)\n", 131 | " return tf.keras.layers.Softmax()(x)" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 14, 137 | "metadata": {}, 138 | "outputs": [ 139 | { 140 | "name": "stdout", 141 | "output_type": "stream", 142 | "text": [ 143 | "Epoch 1, Loss: 0.6885123252868652, Accuracy: 75.93600463867188, Test Loss: 0.6690757870674133, Test Accuracy: 76.77000427246094\n", 144 | "Epoch 2, Loss: 0.4942583739757538, Accuracy: 83.1719970703125, Test Loss: 0.6064096093177795, Test Accuracy: 79.06999969482422\n", 145 | "Epoch 3, Loss: 0.4461654722690582, Accuracy: 84.6199951171875, Test Loss: 0.5867884755134583, Test Accuracy: 79.66999816894531\n", 146 | "Epoch 4, Loss: 0.42267748713493347, Accuracy: 85.34400177001953, Test Loss: 0.5739940404891968, Test Accuracy: 80.26000213623047\n", 147 | "Epoch 5, Loss: 0.4080345034599304, Accuracy: 85.68000030517578, Test Loss: 0.5710135698318481, Test Accuracy: 80.33000183105469\n" 148 | ] 149 | } 150 | ], 151 | "source": [ 152 | "# normal training\n", 153 | "\n", 154 | "passive_model_image = VFLPassiveModel()\n", 155 | "passive_model_text = VFLPassiveModel()\n", 156 | "active_model = VFLActiveModel()\n", 157 | "\n", 158 | "loss_object = tf.keras.losses.CategoricalCrossentropy()\n", 159 | "optimizer = tf.keras.optimizers.Adam()\n", 160 | "\n", 161 | "train_loss = tf.keras.metrics.Mean(name='train_loss')\n", 162 | "train_accuracy = tf.keras.metrics.CategoricalAccuracy(name='train_accuracy')\n", 163 | "\n", 164 | "test_loss = tf.keras.metrics.Mean(name='test_loss')\n", 165 | "test_accuracy = tf.keras.metrics.CategoricalAccuracy(name='test_accuracy')\n", 166 | "\n", 167 | "EPOCHS = 5\n", 168 | "\n", 169 | "for epoch in range(EPOCHS):\n", 170 | " # For each batch of images and labels\n", 171 | " for (images, texts), labels in train_ds:\n", 172 | " with tf.GradientTape() as passive_tape:\n", 173 | " # passive_model sends passive_output to active_model\n", 174 | " passive_image_output = passive_model_image(images)\n", 175 | " passive_text_output = passive_model_text(texts)\n", 176 | " with tf.GradientTape() as active_tape:\n", 177 | " active_tape.watch(passive_image_output)\n", 178 | " active_tape.watch(passive_text_output)\n", 179 | " active_output = active_model([passive_image_output, passive_text_output])\n", 180 | " loss = loss_object(labels, active_output)\n", 181 | " # active_model sends passive_output_gradients back to passive_model\n", 182 | " [active_image_gradients, active_text_gradients] = active_tape.gradient(loss, [passive_image_output, passive_text_output])\n", 183 | " passive_image_loss = tf.multiply(passive_image_output, active_image_gradients.numpy())\n", 184 | " passive_text_loss = tf.multiply(passive_text_output, active_text_gradients.numpy())\n", 185 | " [passive_image_gradients, passive_text_gradients] = \\\n", 186 | " passive_tape.gradient([passive_image_loss, passive_text_loss], \\\n", 187 | " [passive_model_image.trainable_variables, passive_model_text.trainable_variables])\n", 188 | " optimizer.apply_gradients(zip(passive_image_gradients, passive_model_image.trainable_variables))\n", 189 | " optimizer.apply_gradients(zip(passive_text_gradients, passive_model_text.trainable_variables))\n", 190 | " \n", 191 | " train_loss(loss)\n", 192 | " train_accuracy(labels, active_output)\n", 193 | "\n", 194 | " for (test_images, test_texts), test_labels in test_ds:\n", 195 | " passive_output = [passive_model_image(test_images), passive_model_text(test_texts)]\n", 196 | " active_output = active_model(passive_output)\n", 197 | " t_loss = loss_object(test_labels, active_output)\n", 198 | "\n", 199 | " test_loss(t_loss)\n", 200 | " test_accuracy(test_labels, active_output)\n", 201 | "\n", 202 | " template = 'Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, Test Accuracy: {}'\n", 203 | " print(template.format(epoch+1,\n", 204 | " train_loss.result(),\n", 205 | " train_accuracy.result()*100,\n", 206 | " test_loss.result(),\n", 207 | " test_accuracy.result()*100))\n", 208 | "\n", 209 | " # Reset the metrics for the next epoch\n", 210 | " train_loss.reset_states()\n", 211 | " train_accuracy.reset_states()\n", 212 | " test_loss.reset_states()\n", 213 | " test_accuracy.reset_states()" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": 15, 219 | "metadata": {}, 220 | "outputs": [], 221 | "source": [ 222 | "import numpy as np\n", 223 | "\n", 224 | "def get_possioned_gradients(passive_output_gradients, passive_output, N, class1, class2, alpha = 1.0):\n", 225 | " #passive_output_gradients -= passive_output\n", 226 | " attack_mat = np.eye(N, dtype='float32')\n", 227 | " attack_mat[:, class2] += attack_mat[:, class1]*alpha\n", 228 | " attack_mat[:, class1] -= attack_mat[:, class1]*alpha\n", 229 | " passive_output_gradients = tf.matmul(passive_output_gradients, attack_mat)\n", 230 | " #passive_output_gradients += passive_output\n", 231 | " return passive_output_gradients" 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": 9, 237 | "metadata": {}, 238 | "outputs": [ 239 | { 240 | "name": "stdout", 241 | "output_type": "stream", 242 | "text": [ 243 | "Epoch 1, Loss: 3.917205572128296, Accuracy: 60.821998596191406, Test Loss: 4.964493751525879, Test Accuracy: 55.75\n", 244 | "Epoch 2, Loss: 4.0639872550964355, Accuracy: 64.24400329589844, Test Loss: 4.870058536529541, Test Accuracy: 58.64999771118164\n", 245 | "Epoch 3, Loss: 4.059752941131592, Accuracy: 65.54399871826172, Test Loss: 4.841984272003174, Test Accuracy: 60.28000259399414\n", 246 | "Epoch 4, Loss: 4.139057159423828, Accuracy: 66.14400482177734, Test Loss: 4.962497711181641, Test Accuracy: 59.380001068115234\n", 247 | "Epoch 5, Loss: 4.0564422607421875, Accuracy: 67.2439956665039, Test Loss: 4.755265235900879, Test Accuracy: 62.339996337890625\n" 248 | ] 249 | } 250 | ], 251 | "source": [ 252 | "# backdoor training\n", 253 | "\n", 254 | "passive_model_image = VFLPassiveModel()\n", 255 | "passive_model_text = VFLPassiveModel()\n", 256 | "active_model = VFLActiveModel()\n", 257 | "\n", 258 | "loss_object = tf.keras.losses.CategoricalCrossentropy()\n", 259 | "optimizer = tf.keras.optimizers.Adam()\n", 260 | "optimizer_attack = tf.keras.optimizers.Adam(lr=0.1)\n", 261 | "\n", 262 | "train_loss = tf.keras.metrics.Mean(name='train_loss')\n", 263 | "train_accuracy = tf.keras.metrics.CategoricalAccuracy(name='train_accuracy')\n", 264 | "\n", 265 | "test_loss = tf.keras.metrics.Mean(name='test_loss')\n", 266 | "test_accuracy = tf.keras.metrics.CategoricalAccuracy(name='test_accuracy')\n", 267 | "\n", 268 | "EPOCHS = 5\n", 269 | "\n", 270 | "for epoch in range(EPOCHS):\n", 271 | " # For each batch of images and labels\n", 272 | " for (images, texts), labels in train_ds:\n", 273 | " with tf.GradientTape() as passive_tape:\n", 274 | " # passive_model sends passive_output to active_model\n", 275 | " passive_image_output = passive_model_image(images)\n", 276 | " passive_text_output = passive_model_text(texts)\n", 277 | " with tf.GradientTape() as active_tape:\n", 278 | " active_tape.watch(passive_image_output)\n", 279 | " active_tape.watch(passive_text_output)\n", 280 | " active_output = active_model([passive_image_output, passive_text_output])\n", 281 | " loss = loss_object(labels, active_output)\n", 282 | " # active_model sends passive_output_gradients back to passive_model\n", 283 | " [active_image_gradients, active_text_gradients] = active_tape.gradient(loss, [passive_image_output, passive_text_output])\n", 284 | " active_image_gradients = get_possioned_gradients(active_image_gradients, passive_image_output, 5, 3, 4)\n", 285 | " #active_text_gradients = get_possioned_gradients(active_text_gradients, passive_text_output, 5, 3, 4)\n", 286 | " passive_image_loss = tf.multiply(passive_image_output, active_image_gradients.numpy())\n", 287 | " passive_text_loss = tf.multiply(passive_text_output, active_text_gradients.numpy())\n", 288 | " [passive_image_gradients, passive_text_gradients] = \\\n", 289 | " passive_tape.gradient([passive_image_loss, passive_text_loss], \\\n", 290 | " [passive_model_image.trainable_variables, passive_model_text.trainable_variables])\n", 291 | " #optimizer.apply_gradients(zip(passive_image_gradients, passive_model_image.trainable_variables))\n", 292 | " optimizer_attack.apply_gradients(zip(passive_image_gradients, passive_model_image.trainable_variables))\n", 293 | " optimizer.apply_gradients(zip(passive_text_gradients, passive_model_text.trainable_variables))\n", 294 | " #optimizer_attack.apply_gradients(zip(passive_text_gradients, passive_model_text.trainable_variables))\n", 295 | " \n", 296 | " train_loss(loss)\n", 297 | " train_accuracy(labels, active_output)\n", 298 | "\n", 299 | " for (test_images, test_texts), test_labels in test_ds:\n", 300 | " passive_output = [passive_model_image(test_images), passive_model_text(test_texts)]\n", 301 | " active_output = active_model(passive_output)\n", 302 | " t_loss = loss_object(test_labels, active_output)\n", 303 | "\n", 304 | " test_loss(t_loss)\n", 305 | " test_accuracy(test_labels, active_output)\n", 306 | "\n", 307 | " template = 'Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, Test Accuracy: {}'\n", 308 | " print(template.format(epoch+1,\n", 309 | " train_loss.result(),\n", 310 | " train_accuracy.result()*100,\n", 311 | " test_loss.result(),\n", 312 | " test_accuracy.result()*100))\n", 313 | "\n", 314 | " # Reset the metrics for the next epoch\n", 315 | " train_loss.reset_states()\n", 316 | " train_accuracy.reset_states()\n", 317 | " test_loss.reset_states()\n", 318 | " test_accuracy.reset_states()" 319 | ] 320 | }, 321 | { 322 | "cell_type": "code", 323 | "execution_count": 16, 324 | "metadata": {}, 325 | "outputs": [ 326 | { 327 | "name": "stdout", 328 | "output_type": "stream", 329 | "text": [ 330 | "(10000, 634) (10000, 1000)\n", 331 | "(564, 634) (564, 1000)\n", 332 | "[318.85553 95.33841 71.49886 47.012733 31.294613]\n" 333 | ] 334 | }, 335 | { 336 | "data": { 337 | "image/png": "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\n", 338 | "text/plain": [ 339 | "
" 340 | ] 341 | }, 342 | "metadata": { 343 | "needs_background": "light" 344 | }, 345 | "output_type": "display_data" 346 | } 347 | ], 348 | "source": [ 349 | "import matplotlib.pyplot as plt\n", 350 | "import numpy as np\n", 351 | "\n", 352 | "label_test = np.argmax(y_test, axis=1)\n", 353 | "(image_test, text_test) = x_test\n", 354 | "print(image_test.shape, text_test.shape)\n", 355 | "image_val = image_test[label_test==1]\n", 356 | "text_val = text_test[label_test==1]\n", 357 | "print(image_val.shape, text_val.shape)\n", 358 | "y_val = y_test[label_test==1]\n", 359 | "\n", 360 | "passive_output = [passive_model_image(image_val), passive_model_text(text_val)]\n", 361 | "active_output = active_model(passive_output)\n", 362 | "\n", 363 | "output_distribution = np.sum(active_output, axis=0)\n", 364 | "print(output_distribution)\n", 365 | "\n", 366 | "n = 5\n", 367 | "X = np.arange(n)\n", 368 | "plt.bar(X, output_distribution)\n", 369 | "\n", 370 | "plt.show()" 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": null, 376 | "metadata": {}, 377 | "outputs": [], 378 | "source": [ 379 | "class MyLinearModel(Model):\n", 380 | " def __init__(self):\n", 381 | " super(MyLinearModel, self).__init__()\n", 382 | " self.flatten = Flatten()\n", 383 | " self.d1 = Dense(class_num, activation='softmax', name=\"dense1\")\n", 384 | "\n", 385 | " def call(self, x):\n", 386 | " x = self.flatten(x)\n", 387 | " return self.d1(x)\n", 388 | "\n", 389 | "model = MyLinearModel()" 390 | ] 391 | }, 392 | { 393 | "cell_type": "code", 394 | "execution_count": null, 395 | "metadata": {}, 396 | "outputs": [], 397 | "source": [ 398 | "loss_object = tf.keras.losses.SparseCategoricalCrossentropy()\n", 399 | "optimizer = tf.keras.optimizers.Adam()\n", 400 | "\n", 401 | "train_loss = tf.keras.metrics.Mean(name='train_loss')\n", 402 | "train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')\n", 403 | "\n", 404 | "test_loss = tf.keras.metrics.Mean(name='test_loss')\n", 405 | "test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy')\n", 406 | "\n", 407 | "EPOCHS = 5\n", 408 | "\n", 409 | "for epoch in range(EPOCHS):\n", 410 | " # For each batch of images and labels\n", 411 | " for images, labels in train_ds:\n", 412 | " with tf.GradientTape() as tape:\n", 413 | " predictions = model(images)\n", 414 | " loss = loss_object(labels, predictions)\n", 415 | " gradients = tape.gradient(loss, model.trainable_variables)\n", 416 | " print(gradients[0].shape, gradients[1].shape)\n", 417 | " optimizer.apply_gradients(zip(gradients, model.trainable_variables))\n", 418 | "\n", 419 | " train_loss(loss)\n", 420 | " train_accuracy(labels, predictions)\n", 421 | "\n", 422 | " for test_images, test_labels in test_ds:\n", 423 | " predictions = model(test_images)\n", 424 | " t_loss = loss_object(test_labels, predictions)\n", 425 | "\n", 426 | " test_loss(t_loss)\n", 427 | " test_accuracy(test_labels, predictions)\n", 428 | "\n", 429 | " template = 'Epoch {}, Loss: {}, Accuracy: {}, Test Loss: {}, Test Accuracy: {}'\n", 430 | " print(template.format(epoch+1,\n", 431 | " train_loss.result(),\n", 432 | " train_accuracy.result()*100,\n", 433 | " test_loss.result(),\n", 434 | " test_accuracy.result()*100))\n", 435 | "\n", 436 | " # Reset the metrics for the next epoch\n", 437 | " train_loss.reset_states()\n", 438 | " train_accuracy.reset_states()\n", 439 | " test_loss.reset_states()\n", 440 | " test_accuracy.reset_states()" 441 | ] 442 | }, 443 | { 444 | "cell_type": "code", 445 | "execution_count": null, 446 | "metadata": {}, 447 | "outputs": [], 448 | "source": [ 449 | "with tf.GradientTape() as tape:\n", 450 | " a = tf.constant(2.)\n", 451 | " b = tf.constant(1.)\n", 452 | " tape.watch(a)\n", 453 | " tape.watch(b)\n", 454 | " c = tf.multiply(a, b)\n", 455 | "g = tape.gradient(c, [a, b])\n", 456 | "print(g)" 457 | ] 458 | } 459 | ], 460 | "metadata": { 461 | "kernelspec": { 462 | "display_name": "Python 3", 463 | "language": "python", 464 | "name": "python3" 465 | }, 466 | "language_info": { 467 | "codemirror_mode": { 468 | "name": "ipython", 469 | "version": 3 470 | }, 471 | "file_extension": ".py", 472 | "mimetype": "text/x-python", 473 | "name": "python", 474 | "nbconvert_exporter": "python", 475 | "pygments_lexer": "ipython3", 476 | "version": "3.7.3" 477 | } 478 | }, 479 | "nbformat": 4, 480 | "nbformat_minor": 2 481 | } 482 | --------------------------------------------------------------------------------