diff --git a/lecture1/exercise_0_python.ipynb b/lecture1/exercise_0_python.ipynb
index c9af740b1988810b977ad6a48848aa16846cfd9b..abeb08060f243d303f7e607ab60a148eca8ebb61 100644
--- a/lecture1/exercise_0_python.ipynb
+++ b/lecture1/exercise_0_python.ipynb
@@ -110,7 +110,7 @@
    "metadata": {},
    "source": [
     "## Variables and Data-Types\n",
-    "Python is a **dynamically-typed** language, i.e. you don't have to define your variable types:"
+    "Python is a **dynamically-typed** language, i.e. you don't have to define your variable types in advance:"
    ]
   },
   {
@@ -485,7 +485,7 @@
    "metadata": {},
    "source": [
     "## Numpy Basics\n",
-    "Numpy is an performant, easy-to-use scientific library for numeric calculations. You will often encouter cases where numpy can perform calculations several orders of magnitues faster due its C-backend and ability to process data in a vectorized (not individuallistic) form."
+    "Numpy is an performant, easy-to-use scientific library for numeric calculations. You will often encouter cases where numpy can perform calculations several orders of magnitudes faster due its C-backend and ability to process data in a vectorized (parallelized) form."
    ]
   },
   {
@@ -504,7 +504,7 @@
    "metadata": {},
    "source": [
     "### Numpy One Dimensional Array Operations\n",
-    "Numpy operates on arrays element-wise meaning elementary operations are performed in an intuitive vectorized form."
+    "Numpy operates on arrays element-wise meaning elementary operations are performed in an intuitive way."
    ]
   },
   {
@@ -710,7 +710,8 @@
    "id": "7a826bff",
    "metadata": {},
    "source": [
-    "### Numpy Miscellaneous:\n",
+    "## Numpy Miscellaneous:\n",
+    "### Functions\n",
     "Below you can find a non-exhaustive list of built-in numpy functions for quick data analysis:"
    ]
   },
@@ -741,6 +742,85 @@
     "\n",
     "print(\"Flattened array:\", dataset_shifted.flatten())       # .flatten() makes an array 1D"
    ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fdacbe66",
+   "metadata": {},
+   "source": [
+    "### Array Indexing\n",
+    "Numpy support a powerful way of accessing entries of an array. For this purpose, ranges using ```:```, numpy arrays of integers and numpy arrays of booleans can be used:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "e15a8939",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[20 21 22 23 24 25 26 27 28 29]\n",
+      "[0 1 2 3 4 5 6 7 8 9]\n",
+      "[95 96 97 98 99]\n",
+      "[10 13 21]\n",
+      "[18 19 17 23 13  3  6 12  7 25  0  9 14 27  1 11  2 29 26  8 28  5 30 15\n",
+      " 10 20 24 21 22  4 16]\n",
+      "[[False  True  True False]\n",
+      " [False False False  True]\n",
+      " [False False False False]\n",
+      " [False False  True  True]\n",
+      " [ True False False False]\n",
+      " [False False  True False]\n",
+      " [ True False False  True]\n",
+      " [False  True False False]\n",
+      " [ True  True False False]\n",
+      " [False  True False False]\n",
+      " [False False False False]\n",
+      " [False False False False]\n",
+      " [False False False  True]\n",
+      " [False  True  True False]\n",
+      " [False False False  True]\n",
+      " [ True False False  True]\n",
+      " [False False  True False]\n",
+      " [ True False  True  True]\n",
+      " [False False False False]\n",
+      " [False False  True False]\n",
+      " [False  True False False]\n",
+      " [False  True False False]\n",
+      " [False  True  True False]\n",
+      " [False False False  True]\n",
+      " [ True False  True False]]\n"
+     ]
+    }
+   ],
+   "source": [
+    "array_to_be_masked = np.arange(100)\n",
+    "\n",
+    "# Let's select the entries between index 20 and 30:\n",
+    "print(array_to_be_masked[20:30])\n",
+    "\n",
+    "# First 10 entries:\n",
+    "print(array_to_be_masked[:10])\n",
+    "\n",
+    "# Print the last 5 elements:\n",
+    "print(array_to_be_masked[-5:])\n",
+    "\n",
+    "# Print the 10th, 13th and 21st elements:\n",
+    "print(array_to_be_masked[np.array([10, 13, 21])])\n",
+    "\n",
+    "# Let's shuffle the elements and reshape the array to make things more complicated!\n",
+    "np.random.shuffle(array_to_be_masked)\n",
+    "array_to_be_masked = array_to_be_masked.reshape((25, -1))  # \"-1\" means \"do whatever needed to get the right shape\"\n",
+    "\n",
+    "# Select all entries <=30 in the flattened array:\n",
+    "print(array_to_be_masked[(array_to_be_masked<=30)])\n",
+    "\n",
+    "# Print the mask:\n",
+    "print((array_to_be_masked<=30))"
+   ]
   }
  ],
  "metadata": {
diff --git a/lecture1/exercise_0_tensorflow.ipynb b/lecture1/exercise_0_tensorflow.ipynb
index 51649a1f83d0e29b90e6ffa3237b028589d17469..6686b6c23bea8e8ad87a3d6463a0b5b7ea09ba2f 100644
--- a/lecture1/exercise_0_tensorflow.ipynb
+++ b/lecture1/exercise_0_tensorflow.ipynb
@@ -6,16 +6,168 @@
    "metadata": {},
    "source": [
     "# Tensorflow Tutorial\n",
-    "---------------------------------"
+    "---------------------------------\n",
+    "\n",
+    "Welcome to the Tesorflow tutorial! Here we are going to go through the most fundamental functions and the syntax of the library.\n",
+    "\n",
+    "Usually, tensorflow is imported as ``` tf```:"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 2,
    "id": "fb7e3964",
    "metadata": {},
    "outputs": [],
-   "source": []
+   "source": [
+    "import tensorflow as tf"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3f663a34",
+   "metadata": {},
+   "source": [
+    "## Tensors\n",
+    "One can create tensors in tensorflow using a **syntax similar to** that of **numpy**. These objects are considered to be constants by the library and hence **cannot be modified**; each operation on these objects returns a new tensor object.\n",
+    "\n",
+    "You can find some examples of tensor creation below:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "id": "e776d989",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "tf.ones([3, 2]) = tf.Tensor([0. 0. 0. 0. 0.], shape=(5,), dtype=float32)\n",
+      "tf.zeros([5]) = tf.Tensor(\n",
+      "[[1. 1.]\n",
+      " [1. 1.]\n",
+      " [1. 1.]], shape=(3, 2), dtype=float32)\n",
+      "tf.random_uniform([1, 3]) = tf.Tensor([[0.84930134 0.71385014 0.2513721 ]], shape=(1, 3), dtype=float32)\n",
+      "tf.linspace(1.0, 7.0, 4) = tf.Tensor([1. 3. 5. 7.], shape=(4,), dtype=float64)\n",
+      "tf.convert_to_tensor( np.linspace(1, 7, 4) ) = tf.Tensor([1. 3. 5. 7.], shape=(4,), dtype=float64)\n"
+     ]
+    }
+   ],
+   "source": [
+    "t1 = tf.zeros([5])              # Zeros of length 5 (note the necessary squared brackets)\n",
+    "t2 = tf.ones([3, 2])            # Array of ones of shape (3, 2)\n",
+    "t3 = tf.random.uniform([1, 3])  # Random sampling from the interval [1, 3)-\n",
+    "t4 = tf.linspace(1, 7, 4)       # Create a tensor of linear spacing from 1 to 7 with 4 entries\n",
+    "t5 = tf.convert_to_tensor(np.linspace(1, 7, 4))\n",
+    "\n",
+    "# Observe that all of these objects are tensors:\n",
+    "print(\"tf.ones([3, 2]) = %s\" % t1)\n",
+    "print(\"tf.zeros([5]) = %s\" % t2)\n",
+    "print(\"tf.random_uniform([1, 3]) = %s\" % t3)\n",
+    "print(\"tf.linspace(1.0, 7.0, 4) = %s\" % t4)\n",
+    "print(\"tf.convert_to_tensor( np.linspace(1, 7, 4) ) = %s\" % t5)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e1fa23d6",
+   "metadata": {},
+   "source": [
+    "## Variables\n",
+    "On the other hand, ```Variables``` (based on tensors) are objects which are **updated during training** through backpropagation. Each tensorflow variable has to be initialized and their values can be changed with using ```variable.assign(new_values)```:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "a70aa763",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "w = <tf.Variable 'Variable:0' shape=(3, 2) dtype=float32, numpy=\n",
+      "array([[0., 0.],\n",
+      "       [0., 0.],\n",
+      "       [0., 0.]], dtype=float32)>\n",
+      "w = <tf.Variable 'Variable:0' shape=(3, 2) dtype=float32, numpy=\n",
+      "array([[1., 1.],\n",
+      "       [1., 1.],\n",
+      "       [1., 1.]], dtype=float32)>\n"
+     ]
+    }
+   ],
+   "source": [
+    "w = tf.Variable(tf.zeros([3, 2]))   # Create an empty variable w\n",
+    "print(\"w = %s\" % w)                 # ...which has zeros only by default\n",
+    "w.assign(tf.ones([3, 2]))           # Assign new values to w\n",
+    "print(\"w = %s\" % w)                 # ... and retrieve them"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fc389074",
+   "metadata": {},
+   "source": [
+    "## Fundamental Mathematical Operations\n",
+    "Tensorflow supports several basic maths functions out of the box. Unlike numpy, some operations run by the name ```reduce_operation(*args)``` like ```reduce_sum``` and ```reduce_mean```.\n",
+    "\n",
+    "You can find an incomplete list of some basic calls:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "id": "367e4ba0",
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "x = tf.Tensor([0. 1. 2. 3. 4.], shape=(5,), dtype=float32)\n",
+      "(x+1)**2 - 2) = tf.Tensor([-1.  2.  7. 14. 23.], shape=(5,), dtype=float32)\n",
+      "sin(x) tf.Tensor([ 0.          0.84147096  0.9092974   0.14112    -0.7568025 ], shape=(5,), dtype=float32)\n",
+      "sum(x) tf.Tensor(10.0, shape=(), dtype=float32)\n",
+      "mean(x) tf.Tensor(2.0, shape=(), dtype=float32)\n"
+     ]
+    },
+    {
+     "data": {
+      "text/plain": [
+       "<tf.Tensor: shape=(2, 3), dtype=float32, numpy=\n",
+       "array([[0., 0., 0.],\n",
+       "       [0., 0., 0.]], dtype=float32)>"
+      ]
+     },
+     "execution_count": 11,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "x = tf.linspace(0., 4., 5)  # Create a tensor array\n",
+    "\n",
+    "print(\"x =\", x)\n",
+    "print(\"(x+1)**2 - 2) =\", (x + 1.)**2 - 2.)\n",
+    "print(\"sin(x)\", tf.sin(x))\n",
+    "print(\"sum(x)\", tf.reduce_sum(x))\n",
+    "print(\"mean(x)\", tf.reduce_mean(x))\n",
+    "\n",
+    "# Create some other tensors to showcase arithmatic operations:\n",
+    "a = tf.zeros(shape=(2, 3))\n",
+    "b = tf.ones(shape=(2, 3))\n",
+    "c = tf.ones(shape=(3, 2))\n",
+    "\n",
+    "# Operators (+, -, /, *) are available\n",
+    "a + b  # same as tf.add(a, b)\n",
+    "a - b  # same as tf.subtract(a, b)\n",
+    "a * b  # same as tf.mul(a, b)\n",
+    "a / b  # same as tf.division(a, b)"
+   ]
   }
  ],
  "metadata": {