diff --git a/site/en/r2/guide/eager.ipynb b/site/en/r2/guide/eager.ipynb index d80b2646d6c..f85489d8e38 100644 --- a/site/en/r2/guide/eager.ipynb +++ b/site/en/r2/guide/eager.ipynb @@ -1,4 +1,21 @@ { + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "eager.ipynb", + "version": "0.3.2", + "provenance": [], + "private_outputs": true, + "collapsed_sections": [], + "toc_visible": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "accelerator": "GPU" + }, "cells": [ { "cell_type": "markdown", @@ -12,14 +29,12 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "cellView": "form", - "colab": {}, "colab_type": "code", - "id": "z6X9omPnfO_h" + "id": "z6X9omPnfO_h", + "colab": {} }, - "outputs": [], "source": [ "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", @@ -32,7 +47,9 @@ "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License." - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -51,20 +68,20 @@ "id": "B1xdylywqUSX" }, "source": [ - "\u003ctable class=\"tfo-notebook-buttons\" align=\"left\"\u003e\n", - " \u003ctd\u003e\n", - " \u003ca target=\"_blank\" href=\"https://www.tensorflow.org/beta/guide/eager\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" /\u003eView on TensorFlow.org\u003c/a\u003e\n", - " \u003c/td\u003e\n", - " \u003ctd\u003e\n", - " \u003ca target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/en/r2/guide/eager.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" /\u003eRun in Google Colab\u003c/a\u003e\n", - " \u003c/td\u003e\n", - " \u003ctd\u003e\n", - " \u003ca target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/en/r2/guide/eager.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" /\u003eView source on GitHub\u003c/a\u003e\n", - " \u003c/td\u003e\n", - " \u003ctd\u003e\n", - " \u003ca href=\"https://storage.googleapis.com/tensorflow_docs/docs/site/en/r2/guide/eager.ipynb\"\u003e\u003cimg src=\"https://www.tensorflow.org/images/download_logo_32px.png\" /\u003eDownload notebook\u003c/a\u003e\n", - " \u003c/td\u003e\n", - "\u003c/table\u003e" + "\n", + " \n", + " \n", + " \n", + " \n", + "
\n", + " View on TensorFlow.org\n", + " \n", + " Run in Google Colab\n", + " \n", + " View source on GitHub\n", + " \n", + " Download notebook\n", + "
" ] }, { @@ -113,21 +130,21 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "ByNsp4VqqEfa" + "id": "ByNsp4VqqEfa", + "colab": {} }, - "outputs": [], "source": [ "from __future__ import absolute_import, division, print_function, unicode_literals\n", "\n", - "!pip install tensorflow==2.0.0-beta1\n", + "!pip install tensorflow-gpu==2.0.0-beta1\n", "import tensorflow as tf\n", "\n", "import cProfile" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -141,7 +158,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -149,14 +165,15 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "7aFsD8csqEff" + "id": "7aFsD8csqEff", + "colab": {} }, - "outputs": [], "source": [ "tf.executing_eagerly()" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -170,7 +187,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -178,16 +194,17 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "9gsI54pbqEfj" + "id": "9gsI54pbqEfj", + "colab": {} }, - "outputs": [], "source": [ "x = [[2.]]\n", "m = tf.matmul(x, x)\n", "print(\"hello, {}\".format(m))" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -213,51 +230,50 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "sTO0_5TYqz1n" + "id": "sTO0_5TYqz1n", + "colab": {} }, - "outputs": [], "source": [ "a = tf.constant([[1, 2],\n", " [3, 4]])\n", "print(a)" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "Dp14YT8Gq4r1" + "id": "Dp14YT8Gq4r1", + "colab": {} }, - "outputs": [], "source": [ "# Broadcasting support\n", "b = tf.add(a, 1)\n", "print(b)" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "69p3waMfq8cQ" + "id": "69p3waMfq8cQ", + "colab": {} }, - "outputs": [], "source": [ "# Operator overloading is supported\n", "print(a * b)" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -265,34 +281,35 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "Ui025t1qqEfm" + "id": "Ui025t1qqEfm", + "colab": {} }, - "outputs": [], "source": [ "# Use NumPy values\n", "import numpy as np\n", "\n", "c = np.multiply(a, b)\n", "print(c)" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "Tq_aFRzWrCua" + "id": "Tq_aFRzWrCua", + "colab": {} }, - "outputs": [], "source": [ "# Obtain numpy value from a tensor:\n", "print(a.numpy())\n", - "# =\u003e [[1 2]\n", + "# => [[1 2]\n", "# [3 4]]" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -310,7 +327,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -318,11 +334,10 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "0fudRMeUqEfu" + "id": "0fudRMeUqEfu", + "colab": {} }, - "outputs": [], "source": [ "def fizzbuzz(max_num):\n", " counter = tf.constant(0)\n", @@ -338,20 +353,22 @@ " else:\n", " print(num.numpy())\n", " counter += 1" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "P2cKknQWrJLB" + "id": "P2cKknQWrJLB", + "colab": {} }, - "outputs": [], "source": [ "fizzbuzz(15)" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -399,7 +416,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -407,19 +423,20 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "7g1yWiSXqEf-" + "id": "7g1yWiSXqEf-", + "colab": {} }, - "outputs": [], "source": [ "w = tf.Variable([[1.0]])\n", "with tf.GradientTape() as tape:\n", " loss = w * w\n", "\n", "grad = tape.gradient(loss, w)\n", - "print(grad) # =\u003e tf.Tensor([[ 2.]], shape=(1, 1), dtype=float32)" - ] + "print(grad) # => tf.Tensor([[ 2.]], shape=(1, 1), dtype=float32)" + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -437,13 +454,11 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "38kymXZowhhz" + "id": "38kymXZowhhz", + "colab": {} }, - "outputs": [], "source": [ "# Fetch and format the mnist data\n", "(mnist_images, mnist_labels), _ = tf.keras.datasets.mnist.load_data()\n", @@ -452,17 +467,17 @@ " (tf.cast(mnist_images[...,tf.newaxis]/255, tf.float32),\n", " tf.cast(mnist_labels,tf.int64)))\n", "dataset = dataset.shuffle(1000).batch(32)" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "rl1K8rOowmwT" + "id": "rl1K8rOowmwT", + "colab": {} }, - "outputs": [], "source": [ "# Build the model\n", "mnist_model = tf.keras.Sequential([\n", @@ -472,7 +487,9 @@ " tf.keras.layers.GlobalAveragePooling2D(),\n", " tf.keras.layers.Dense(10)\n", "])" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -487,17 +504,17 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "BsxystjBwxLS" + "id": "BsxystjBwxLS", + "colab": {} }, - "outputs": [], "source": [ "for images,labels in dataset.take(1):\n", " print(\"Logits: \", mnist_model(images[0:1]).numpy())" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -511,19 +528,19 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "bzRhM7JDnaEG" + "id": "bzRhM7JDnaEG", + "colab": {} }, - "outputs": [], "source": [ "optimizer = tf.keras.optimizers.Adam()\n", "loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)\n", "\n", "loss_history = []" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -537,13 +554,11 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "DDHrigtiCIA4" + "id": "DDHrigtiCIA4", + "colab": {} }, - "outputs": [], "source": [ "def train_step(images, labels):\n", " with tf.GradientTape() as tape:\n", @@ -557,11 +572,12 @@ " loss_history.append(loss_value.numpy().mean())\n", " grads = tape.gradient(loss_value, mnist_model.trainable_variables)\n", " optimizer.apply_gradients(zip(grads, mnist_model.trainable_variables))" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -569,48 +585,49 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "0m1xAXrmqEgJ" + "id": "0m1xAXrmqEgJ", + "colab": {} }, - "outputs": [], "source": [ "def train():\n", " for epoch in range(3):\n", " for (batch, (images, labels)) in enumerate(dataset):\n", " train_step(images, labels)\n", " print ('Epoch {} finished'.format(epoch))" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "C5dGz0p_nf4W" + "id": "C5dGz0p_nf4W", + "colab": {} }, - "outputs": [], "source": [ "train()" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "5vG5ql_2vYB5" + "id": "5vG5ql_2vYB5", + "colab": {} }, - "outputs": [], "source": [ "import matplotlib.pyplot as plt\n", "\n", "plt.plot(loss_history)\n", "plt.xlabel('Batch #')\n", "plt.ylabel('Loss [entropy]')" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -632,7 +649,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -640,11 +656,10 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "nnQLBYmEqEgm" + "id": "nnQLBYmEqEgm", + "colab": {} }, - "outputs": [], "source": [ "class Model(tf.keras.Model):\n", " def __init__(self):\n", @@ -688,7 +703,9 @@ "\n", "print(\"Final loss: {:.3f}\".format(loss(model, training_inputs, training_outputs)))\n", "print(\"W = {}, B = {}\".format(model.W.numpy(), model.B.numpy()))" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -712,7 +729,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -720,17 +736,19 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "A2boS674qEgs" + "id": "A2boS674qEgs", + "colab": {} }, - "outputs": [], "source": [ "if tf.test.is_gpu_available():\n", " with tf.device(\"gpu:0\"):\n", + " print(\"GPU enabled\")\n", " v = tf.Variable(tf.random.normal([1000, 1000]))\n", " v = None # v no longer takes up GPU memory" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -749,36 +767,35 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "7z5xRfdHzZOQ" + "id": "7z5xRfdHzZOQ", + "colab": {} }, - "outputs": [], "source": [ "x = tf.Variable(10.)\n", "checkpoint = tf.train.Checkpoint(x=x)" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "IffrUVG7zyVb" + "id": "IffrUVG7zyVb", + "colab": {} }, - "outputs": [], "source": [ "x.assign(2.) # Assign a new value to the variables and save.\n", "checkpoint_path = './ckpt/'\n", "checkpoint.save('./ckpt/')" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -786,19 +803,20 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "eMT9koCoqEgw" + "id": "eMT9koCoqEgw", + "colab": {} }, - "outputs": [], "source": [ "x.assign(11.) # Change the variable after saving.\n", "\n", "# Restore values from the checkpoint\n", "checkpoint.restore(tf.train.latest_checkpoint(checkpoint_path))\n", "\n", - "print(x) # =\u003e 2.0" - ] + "print(x) # => 2.0" + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -814,7 +832,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -822,11 +839,10 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "hWZHyAXMqEg0" + "id": "hWZHyAXMqEg0", + "colab": {} }, - "outputs": [], "source": [ "import os\n", "\n", @@ -845,7 +861,9 @@ "\n", "root.save(checkpoint_prefix)\n", "root.restore(tf.train.latest_checkpoint(checkpoint_dir))" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -873,7 +891,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -881,19 +898,20 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "9ccu0iAaqEg5" + "id": "9ccu0iAaqEg5", + "colab": {} }, - "outputs": [], "source": [ "m = tf.keras.metrics.Mean(\"loss\")\n", "m(0)\n", "m(5)\n", - "m.result() # =\u003e 2.5\n", + "m.result() # => 2.5\n", "m([8, 9])\n", - "m.result() # =\u003e 5.5" - ] + "m.result() # => 5.5" + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -914,7 +932,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -922,11 +939,10 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "L518n5dkqEhE" + "id": "L518n5dkqEhE", + "colab": {} }, - "outputs": [], "source": [ "def line_search_step(fn, init_x, rate=1.0):\n", " with tf.GradientTape() as tape:\n", @@ -936,12 +952,14 @@ " grad = tape.gradient(value, init_x)\n", " grad_norm = tf.reduce_sum(grad * grad)\n", " init_value = value\n", - " while value \u003e init_value - rate * grad_norm:\n", + " while value > init_value - rate * grad_norm:\n", " x = init_x - rate * grad\n", " value = fn(x)\n", " rate /= 2.0\n", " return x, value" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -959,7 +977,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -967,11 +984,10 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "-OwwsWUAqEhK" + "id": "-OwwsWUAqEhK", + "colab": {} }, - "outputs": [], "source": [ "@tf.custom_gradient\n", "def clip_gradient_by_norm(x, norm):\n", @@ -979,7 +995,9 @@ " def grad_fn(dresult):\n", " return [tf.clip_by_norm(dresult, norm), None]\n", " return y, grad_fn" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -994,7 +1012,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -1002,11 +1019,10 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "24WiLROnqEhO" + "id": "24WiLROnqEhO", + "colab": {} }, - "outputs": [], "source": [ "def log1pexp(x):\n", " return tf.math.log(1 + tf.exp(x))\n", @@ -1016,35 +1032,37 @@ " tape.watch(x)\n", " value = log1pexp(x)\n", " return tape.gradient(value, x)\n" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "n8fq69r9-B-c" + "id": "n8fq69r9-B-c", + "colab": {} }, - "outputs": [], "source": [ "# The gradient computation works fine at x = 0.\n", "grad_log1pexp(tf.constant(0.)).numpy()" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "_VFSU0mG-FSp" + "id": "_VFSU0mG-FSp", + "colab": {} }, - "outputs": [], "source": [ "# However, x = 100 fails because of numerical instability.\n", "grad_log1pexp(tf.constant(100.)).numpy()" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -1061,7 +1079,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -1069,11 +1086,10 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "Q7nvfx_-qEhS" + "id": "Q7nvfx_-qEhS", + "colab": {} }, - "outputs": [], "source": [ "@tf.custom_gradient\n", "def log1pexp(x):\n", @@ -1087,35 +1103,37 @@ " tape.watch(x)\n", " value = log1pexp(x)\n", " return tape.gradient(value, x)\n" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "5gHPKMfl-Kge" + "id": "5gHPKMfl-Kge", + "colab": {} }, - "outputs": [], "source": [ "# As before, the gradient computation works fine at x = 0.\n", "grad_log1pexp(tf.constant(0.)).numpy()" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "code", - "execution_count": 0, "metadata": { - "colab": {}, "colab_type": "code", - "id": "u38MOfz3-MDE" + "id": "u38MOfz3-MDE", + "colab": {} }, - "outputs": [], "source": [ "# And the gradient computation also works at x = 100.\n", "grad_log1pexp(tf.constant(100.)).numpy()" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -1133,7 +1151,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -1141,11 +1158,10 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "Ac9Y64H-qEhX" + "id": "Ac9Y64H-qEhX", + "colab": {} }, - "outputs": [], "source": [ "import time\n", "\n", @@ -1179,7 +1195,9 @@ " print(\"GPU: {} secs\".format(measure(tf.random.normal(shape), steps)))\n", "else:\n", " print(\"GPU: not found\")" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -1194,7 +1212,6 @@ }, { "cell_type": "code", - "execution_count": 0, "metadata": { "attributes": { "classes": [ @@ -1202,11 +1219,10 @@ ], "id": "" }, - "colab": {}, "colab_type": "code", - "id": "ny6LX2BVqEhf" + "id": "ny6LX2BVqEhf", + "colab": {} }, - "outputs": [], "source": [ "if tf.test.is_gpu_available():\n", " x = tf.random.normal([10, 10])\n", @@ -1216,7 +1232,9 @@ "\n", " _ = tf.matmul(x_cpu, x_cpu) # Runs on CPU\n", " _ = tf.matmul(x_gpu0, x_gpu0) # Runs on GPU:0" - ] + ], + "execution_count": 0, + "outputs": [] }, { "cell_type": "markdown", @@ -1240,21 +1258,5 @@ "optimizations, and production deployment. To bridge this gap, TensorFlow 2.0 introduces `function`s via the `tf.function` API. For more information, see the [Autograph](./autograph.ipynb) guide." ] } - ], - "metadata": { - "colab": { - "collapsed_sections": [], - "name": "Eager.ipynb", - "private_outputs": true, - "provenance": [], - "toc_visible": true, - "version": "0.3.2" - }, - "kernelspec": { - "display_name": "Python 3", - "name": "python3" - } - }, - "nbformat": 4, - "nbformat_minor": 0 -} + ] +} \ No newline at end of file