diff --git a/doc/LectureNotes/exercisesweek41.ipynb b/doc/LectureNotes/exercisesweek41.ipynb index 15eab044e..feb33ed6a 100644 --- a/doc/LectureNotes/exercisesweek41.ipynb +++ b/doc/LectureNotes/exercisesweek41.ipynb @@ -1098,7 +1098,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.13.7" + "version": "3.13.3" } }, "nbformat": 4, diff --git a/doc/LectureNotes/exercisesweek42.html b/doc/LectureNotes/exercisesweek42.html new file mode 100644 index 000000000..ec97ac314 --- /dev/null +++ b/doc/LectureNotes/exercisesweek42.html @@ -0,0 +1,8955 @@ + + + + + +exercisesweek42 + + + + + + + + + + + + +
+
+ +
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+ +
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + +
+
+ +
+ + +
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+ + +
+ + +
+
+ +
+ + +
+
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ + +
+
+ +
+ +
+ + +
+
+ +
+
+ +
+
+ +
+
+ +
+ +
+
+ +
+ +
+
+ +
+ +
+ +
+ + +
+ + +
+
+ +
+ +
+ + +
+
+ +
+
+ +
+ +
+ + +
+
+ +
+ +
+
+ + diff --git a/doc/LectureNotes/exercisesweek42.ipynb b/doc/LectureNotes/exercisesweek42.ipynb index 9925836a4..d09668f78 100644 --- a/doc/LectureNotes/exercisesweek42.ipynb +++ b/doc/LectureNotes/exercisesweek42.ipynb @@ -6,6 +6,8 @@ "source": [ "# Exercises week 42\n", "\n", + "**Python Code can be found at https://github.uio.no/larsbog/FYS-STK4155**\n", + "\n", "**October 13-17, 2025**\n", "\n", "Date: **Deadline is Friday October 17 at midnight**\n" @@ -30,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -86,6 +88,15 @@ "**a)** If the weights and bias of a layer has shapes (10, 4) and (10), what will the shapes of the gradients of the cost function wrt. these weights and this bias be?\n" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " The gradients need to have the same shape as the weights and biases, as we update them as new = old - lr * gradient\n", + "
\n" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -95,13 +106,13 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def feed_forward_one_layer(W, b, x):\n", - " z = ...\n", - " a = ...\n", + " z = W @ x + b\n", + " a = sigmoid(z)\n", " return a\n", "\n", "\n", @@ -113,8 +124,8 @@ "x = np.random.rand(2)\n", "target = np.random.rand(3)\n", "\n", - "W = ...\n", - "b = ..." + "W = np.random.rand(3, 2)\n", + "b = np.random.rand(3)" ] }, { @@ -126,9 +137,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[-0.0012661 -0.00171951]\n", + " [-0.00421828 -0.00572891]\n", + " [ 0.00930977 0.01264372]] [-0.01193713 -0.03977107 0.08777494]\n" + ] + } + ], "source": [ "autograd_one_layer = grad(cost_one_layer, [0, 1])\n", "W_g, b_g = autograd_one_layer(W, b, x, target)\n", @@ -166,6 +187,17 @@ "**a)** Which intermediary results can be reused between the two expressions?\n" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + "\n", + " The derivatives $\\frac{\\mathrm d C}{\\mathrm d a} \\frac{\\mathrm d a}{\\mathrm d z}$ are the same in both expressions and can be reused.\n", + " \n", + "
\n" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -175,9 +207,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[-0.06423077 -0.18044784 0.43834371]\n", + "[-0.06423077 -0.18044784 0.43834371]\n" + ] + } + ], "source": [ "z = W @ x + b\n", "a = sigmoid(z)\n", @@ -186,7 +227,7 @@ "\n", "\n", "def mse_der(predict, target):\n", - " return ...\n", + " return 2 * (predict - target) / target.size\n", "\n", "\n", "print(mse_der(predict, target))\n", @@ -204,12 +245,21 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 5, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[0.18584758 0.22040205 0.20024227]\n", + "[0.18584758 0.22040205 0.20024227]\n" + ] + } + ], "source": [ "def sigmoid_der(z):\n", - " return ...\n", + " return sigmoid(z) * (1 - sigmoid(z))\n", "\n", "\n", "print(sigmoid_der(z))\n", @@ -231,12 +281,12 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ - "dC_da = ...\n", - "dC_dz = ..." + "dC_da = mse_der(predict, target)\n", + "dC_dz = dC_da * sigmoid_der(z)" ] }, { @@ -246,6 +296,15 @@ "**e)** What is the derivative of the intermediary z wrt. the weight and bias? What should the shapes be? The one for the weights is a little tricky, it can be easier to play around in the next exercise first. You can also try computing it with autograd to get a hint.\n" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " The derivative of z wrt to the bias is 1, since z = Wx + b. The derivative of z wrt the weights is x, since z_i = sum_j W_ij * x_j + b_i\n", + "
\n" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -255,14 +314,45 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "(array([0.10606414, 0.14404709]),\n", + " array([-0.01193713, -0.03977107, 0.08777494]))" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "dC_da = ...\n", - "dC_dz = ...\n", - "dC_dW = ...\n", - "dC_db = ...\n", + "x, dC_dz" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[-0.0012661 -0.00171951]\n", + " [-0.00421828 -0.00572891]\n", + " [ 0.00930977 0.01264372]] [-0.01193713 -0.03977107 0.08777494]\n" + ] + } + ], + "source": [ + "dC_da = mse_der(predict, target)\n", + "dC_dz = dC_da * sigmoid_der(z)\n", + "dC_dW = np.outer(dC_dz, x)\n", + "dC_db = dC_dz\n", "\n", "print(dC_dW, dC_db)" ] @@ -276,9 +366,19 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[-0.0012661 -0.00171951]\n", + " [-0.00421828 -0.00572891]\n", + " [ 0.00930977 0.01264372]] [-0.01193713 -0.03977107 0.08777494]\n" + ] + } + ], "source": [ "W_g, b_g = autograd_one_layer(W, b, x, target)\n", "print(W_g, b_g)" @@ -300,7 +400,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -318,7 +418,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -339,14 +439,14 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ - "dC_da2 = ...\n", - "dC_dz2 = ...\n", - "dC_dW2 = ...\n", - "dC_db2 = ..." + "dC_da2 = mse_der(a2, target)\n", + "dC_dz2 = dC_da2 * sigmoid_der(z2)\n", + "dC_dW2 = np.outer(dC_dz2, a1)\n", + "dC_db2 = dC_dz2" ] }, { @@ -368,10 +468,12 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, "outputs": [], - "source": [] + "source": [ + "dz2_da1 = W2.T" + ] }, { "cell_type": "markdown", @@ -390,21 +492,35 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ - "dC_da1 = ...\n", - "dC_dz1 = ...\n", - "dC_dW1 = ...\n", - "dC_db1 = ..." + "dC_da1 = dz2_da1 @ dC_dz2\n", + "dC_dz1 = dC_da1 * sigmoid_der(z1)\n", + "dC_dW1 = np.outer(dC_dz1, x)\n", + "dC_db1 = dC_dz1" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[[0.00446139 0.00071062]\n", + " [0.00983449 0.00156645]\n", + " [0.0024775 0.00039462]] [0.00524627 0.01156463 0.00291336]\n", + "[[ 0.03644459 0.02409418 0.03224462]\n", + " [ 0.05648327 0.03734211 0.04997399]\n", + " [-0.00813619 -0.00537898 -0.00719855]\n", + " [ 0.0034844 0.0023036 0.00308285]] [ 0.04508103 0.06986838 -0.01006426 0.00431011]\n" + ] + } + ], "source": [ "print(dC_dW1, dC_db1)\n", "print(dC_dW2, dC_db2)" @@ -419,7 +535,7 @@ }, { "cell_type": "code", - "execution_count": 67, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -437,9 +553,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[(array([[0.00446139, 0.00071062],\n", + " [0.00983449, 0.00156645],\n", + " [0.0024775 , 0.00039462]]),\n", + " array([0.00524627, 0.01156463, 0.00291336])),\n", + " (array([[ 0.03644459, 0.02409418, 0.03224462],\n", + " [ 0.05648327, 0.03734211, 0.04997399],\n", + " [-0.00813619, -0.00537898, -0.00719855],\n", + " [ 0.0034844 , 0.0023036 , 0.00308285]]),\n", + " array([ 0.04508103, 0.06986838, -0.01006426, 0.00431011]))]" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "def cost_two_layers(layers, x, target):\n", " predict = feed_forward_two_layers(layers, x)\n", @@ -457,6 +592,15 @@ "**e)** How would you use the gradient from this layer to compute the gradient of an even earlier layer? Would the expressions be any different?\n" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "
\n", + " To compute the gradient of an earlier layer we can continue this scheme of using the chain rule, just like we did for the second layer. The expressions would not be different, just the indices would change.\n", + "
\n" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -475,7 +619,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -516,7 +660,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -543,7 +687,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -560,15 +704,15 @@ "\n", " if i == len(layers) - 1:\n", " # For last layer we use cost derivative as dC_da(L) can be computed directly\n", - " dC_da = ...\n", + " dC_da = cost_der(predict, target)\n", " else:\n", " # For other layers we build on previous z derivative, as dC_da(i) = dC_dz(i+1) * dz(i+1)_da(i)\n", " (W, b) = layers[i + 1]\n", - " dC_da = ...\n", + " dC_da = W.T @ dC_dz\n", "\n", - " dC_dz = ...\n", - " dC_dW = ...\n", - " dC_db = ...\n", + " dC_dz = dC_da * activation_der(z)\n", + " dC_dW = np.outer(dC_dz, layer_input)\n", + " dC_db = dC_dz\n", "\n", " layer_grads[i] = (dC_dW, dC_db)\n", "\n", @@ -577,7 +721,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -594,9 +738,22 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 22, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[(array([[0.02299964, 0.09565439],\n", + " [0.01274242, 0.05299513],\n", + " [0.01038055, 0.04317222]]), array([0.09661863, 0.05352934, 0.04360741])), (array([[-0. , -0. , -0. ],\n", + " [ 0.23150223, 0.16003629, 0.36267149],\n", + " [-0. , -0. , -0. ],\n", + " [-0.20226612, -0.13982552, -0.31687019]]), array([-0. , 0.41494126, -0. , -0.36253888]))]\n" + ] + } + ], "source": [ "layer_grads = backpropagation(x, layers, activation_funcs, target, activation_ders)\n", "print(layer_grads)" @@ -604,9 +761,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 23, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[(array([[0.02299964, 0.09565439],\n", + " [0.01274242, 0.05299513],\n", + " [0.01038055, 0.04317222]]),\n", + " array([0.09661863, 0.05352934, 0.04360741])),\n", + " (array([[ 0. , 0. , 0. ],\n", + " [ 0.23150223, 0.16003629, 0.36267149],\n", + " [ 0. , 0. , 0. ],\n", + " [-0.20226612, -0.13982552, -0.31687019]]),\n", + " array([ 0. , 0.41494126, 0. , -0.36253888]))]" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "cost_grad = grad(cost, 0)\n", "cost_grad(layers, x, [sigmoid, ReLU], target)" @@ -621,6 +797,80 @@ "Make new versions of all the functions in exercise 5 which now take batched inputs instead. See last weeks exercise 5 for details on how to batch inputs to neural networks. You will also need to update the backpropogation function.\n" ] }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [], + "source": [ + "def feed_forward_saver_batch(input, layers, activation_funcs):\n", + " layer_inputs = []\n", + " zs = []\n", + " a = input\n", + " for (W, b), activation_func in zip(layers, activation_funcs):\n", + " layer_inputs.append(a)\n", + " z = a @ W.T + b\n", + " a = activation_func(z)\n", + "\n", + " zs.append(z)\n", + "\n", + " return layer_inputs, zs, a\n", + "\n", + "def backpropagation_batch(\n", + " input, layers, activation_funcs, target, activation_ders, cost_der=mse_der\n", + "):\n", + " layer_inputs, zs, predict = feed_forward_saver_batch(input, layers, activation_funcs)\n", + "\n", + " layer_grads = [() for layer in layers]\n", + "\n", + " # We loop over the layers, from the last to the first\n", + " for i in reversed(range(len(layers))):\n", + " layer_input, z, activation_der = layer_inputs[i], zs[i], activation_ders[i]\n", + "\n", + " if i == len(layers) - 1:\n", + " # For last layer we use cost derivative as dC_da(L) can be computed directly\n", + " dC_da = cost_der(predict, target)\n", + " else:\n", + " # For other layers we build on previous z derivative, as dC_da(i) = dC_dz(i+1) * dz(i+1)_da(i)\n", + " (W, b) = layers[i + 1]\n", + " dC_da = (W.T @ dC_dz.T).T\n", + "\n", + " # print(f\"Layer {i}: dC_da shape: {dC_da.shape}, dC_dz shape: {dC_dz.shape if 'dC_dz' in locals() else 'N/A'}, layer_input shape: {layer_input.shape}\")\n", + " dC_dz = dC_da * activation_der(z)\n", + " # print(dC_dz.shape)\n", + " dC_dW = dC_dz.T @ layer_input\n", + " dC_db = np.mean(dC_dz, axis=0)\n", + "\n", + " layer_grads[i] = (dC_dW, dC_db)\n", + "\n", + " return layer_grads\n", + "\n", + "x = np.random.rand(150, network_input_size)\n", + "target = np.random.rand(150, 4)\n", + "\n", + "layer_grads = backpropagation_batch(x, layers, activation_funcs, target, activation_ders)" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "((3, 2), (3,))" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "layer_grads[0][0].shape, layer_grads[0][1].shape" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -639,6 +889,220 @@ "**b)** Use stochastic gradient descent with momentum when you train your network.\n" ] }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "class GradientDescent:\n", + " def __init__(\n", + " self, *args, learning_rate: float = 0.1, num_iterations: int = 1000, **kwargs\n", + " ):\n", + " \"\"\"Gradient Descent Class\n", + "\n", + " Args:\n", + " learning_rate (float, optional): Learning rate used for step updates. Defaults to 0.1.\n", + " num_iterations (int, optional): Number of iterations for gradient descent. Defaults to 1000.\n", + " \"\"\"\n", + " self._cost_history = np.zeros(num_iterations)\n", + " self.learning_rate = learning_rate\n", + " self.num_iterations = num_iterations\n", + "\n", + " @property\n", + " def cost_history(self) -> np.ndarray:\n", + " \"\"\"Returns the cost history of the optimization process.\n", + "\n", + " Returns:\n", + " np.ndarray: Array of cost values for each iteration.\n", + " \"\"\"\n", + " return self._cost_history\n", + "\n", + " def get_epochs(self) -> np.ndarray:\n", + " \"\"\"Returns an array of epoch numbers from 0 to num_iterations - 1.\n", + "\n", + " Returns:\n", + " np.ndarray: Array of epoch numbers.\n", + " \"\"\"\n", + " return np.arange(self.num_iterations)\n", + "\n", + " def fit(self, X: np.ndarray, y: np.ndarray) -> np.ndarray:\n", + " \"\"\"Returns the optimal solution for an optimization problem using the gradient descent\n", + "\n", + " Args:\n", + " X (np.ndarray): X values\n", + " y (np.ndarray): y values\n", + "\n", + " Returns:\n", + " np.ndarray: Optimal parameters\n", + " \"\"\"\n", + " self.X = X\n", + " self.y = y\n", + " self.theta = np.zeros(X.shape[1])\n", + " self._precomp()\n", + " for t in range(self.num_iterations):\n", + " self._comp_step()\n", + " self._cost_history[t] = self._compute_cost()\n", + " self._update_theta()\n", + " return self.theta\n", + "\n", + " def _precomp(self):\n", + " pass\n", + "\n", + " def _comp_step(self):\n", + " pass\n", + "\n", + " def _compute_cost(self):\n", + " pass\n", + "\n", + " def _update_theta(self):\n", + " pass\n", + "\n", + "\n", + "def cost(input, layers, activation_funcs, target):\n", + " predict = feed_forward_batch(input, layers, activation_funcs)\n", + " return mse(predict, target)\n", + "\n", + "class NNGradientDescent(GradientDescent):\n", + " def __init__(self, *args, layers, activation_funcs, activation_ders, **kwargs):\n", + " super().__init__(*args, **kwargs)\n", + " self.layers = layers\n", + " self.activation_funcs = activation_funcs\n", + " self.activation_ders = activation_ders\n", + "\n", + "\n", + " def _precomp(self):\n", + " pass\n", + "\n", + " def _comp_step(self):\n", + " self.layer_inputs, self.zs, self.predict = feed_forward_saver_batch(self.X, self.layers, self.activation_funcs)\n", + "\n", + " def _compute_cost(self) -> float:\n", + " return mse(self.predict, self.y)\n", + "\n", + " def _compute_grad(self) -> list[tuple[np.ndarray, np.ndarray]]:\n", + " layer_grads = backpropagation_batch(\n", + " self.X, self.layers, self.activation_funcs, self.y, self.activation_ders\n", + " )\n", + " return layer_grads\n", + "\n", + " def _update_theta(self):\n", + " for i, (dC_dW, dC_db) in enumerate(self._compute_grad()):\n", + " W, b = self.layers[i]\n", + " W -= self.learning_rate * dC_dW\n", + " b -= self.learning_rate * dC_db\n", + " self.layers[i] = (W, b)\n", + "\n", + "\n", + "class NNStochasticGradientDescent(NNGradientDescent):\n", + " def __init__(\n", + " self, *args, batch_size: int = 100, batches_per_epoch: int = 1, **kwargs\n", + " ):\n", + " # print(self.__class__.__name__) # If you see this: debugging yaaaay, the programmer that wrote this line is stupid...\n", + " super().__init__(*args, **kwargs)\n", + " self.batch_size = batch_size\n", + " self.batches_per_epoch = batches_per_epoch\n", + " self.RNG = np.random.default_rng(seed=42)\n", + "\n", + " def _precomp(self):\n", + " self.N = len(self.y)\n", + " self.indices = np.arange(self.N)\n", + " self.n = self.batch_size\n", + " self.RNG.shuffle(self.indices)\n", + " self.X = self.X[self.indices]\n", + " self.y = self.y[self.indices]\n", + "\n", + " def _comp_step(self):\n", + " index = self.RNG.integers(0, self.N)\n", + " batch_indices = slice(index, index + self.batch_size)\n", + " if index + self.batch_size > self.N:\n", + " batch_indices = slice(index, self.N)\n", + "\n", + " X_batch = self.X[batch_indices]\n", + " y_batch = self.y[batch_indices]\n", + " self.layer_inputs, self.zs, self.predict = feed_forward_saver_batch(X_batch, self.layers, self.activation_funcs)\n", + " self.X_batch = X_batch\n", + " self.y_batch = y_batch\n", + "\n", + " def _compute_cost(self) -> float:\n", + " return mse(self.predict, self.y_batch)\n", + "\n", + " def _compute_grad(self) -> list[tuple[np.ndarray, np.ndarray]]:\n", + " layer_grads = backpropagation_batch(\n", + " self.X_batch, self.layers, self.activation_funcs, self.y_batch, self.activation_ders\n", + " )\n", + " return layer_grads\n", + "\n", + " def get_epochs(self):\n", + " return np.arange(self.num_iterations // self.batches_per_epoch)\n", + "\n", + " @property\n", + " def cost_history(self) -> np.ndarray:\n", + " \"\"\"Returns the cost history of the optimization process.\n", + "\n", + " Returns:\n", + " np.ndarray: Array of cost values for each epoch.\n", + " \"\"\"\n", + " return np.mean(self._cost_history.reshape(-1, self.batches_per_epoch), axis=1)\n", + "\n", + "class NNMomentum(NNGradientDescent):\n", + " def __init__(self, *args, delta: float = 1.0, **kwargs):\n", + " super().__init__(*args, **kwargs)\n", + " self.delta = delta\n", + "\n", + " def _precomp(self):\n", + " self.last_layers = [(np.zeros_like(W), np.zeros_like(b)) for W, b in self.layers]\n", + " return super()._precomp()\n", + "\n", + " def _update_theta(self):\n", + " self.last_layers = [(W.copy(), b.copy()) for W, b in self.layers]\n", + " for i, ((W, b), (W_prev, b_prev), (dW, db)) in enumerate(zip(self.layers, self.last_layers, self._compute_grad())):\n", + " W = self.delta * (W - W_prev) - self.learning_rate * dW\n", + " b = self.delta * (b - b_prev) - self.learning_rate * db\n", + " self.layers[i] = (W, b)\n", + "\n", + "class NNMomentumSGD(NNMomentum, NNStochasticGradientDescent):\n", + " def __init__(self, *args, **kwargs):\n", + " super().__init__(*args, **kwargs)\n", + "\n", + "\n", + "opt = NNMomentumSGD(layers=layers, activation_funcs=activation_funcs, activation_ders=activation_ders, learning_rate=0.01, num_iterations=1000, batch_size=32, batches_per_epoch=10)\n", + "_ = opt.fit(x, target)\n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[]" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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xo3VHUBVlt15zI9//GM1nU6r/+f//B/1hRdeUP/yL/9S7erqUgcHB8M+tLrmYx/7mNrZ2ak+/PDD6rFjx/h/MzMz/DUf/ehH1eXLl6sPPvig+vTTT6sXX3yxevHFF4d63PMFsUpIpXPtG08++aSaSCTUr371q+pLL72k3n333WpLS4t611138dd87WtfU7u6utT/+I//UHfv3q2+613volLbCrjuuuvUJUuW8LLme++9V+3t7VU/85nP8NfQua6cyclJ9ZlnnlGfeeYZFYB66623qs8884x64MABVXV5bq+88kr1vPPOU5944gn1scceU0855RQqa6413/zmN9Xly5erqVRKveiii9THH3887EOqewBI//vhD3/IXzM7O6tef/316oIFC9SWlhb1qquuUo8dOxbqcc8XzAELnWv/+MUvfqGeddZZajqdVtetW6d+73vfM3y/WCyqX/jCF9T+/n41nU6rl156qbpv377QjrdemZiYUG+88UZ1+fLlalNTk7p69Wr15ptvVjOZDH8NnevKeeihh6Rr9HXXXaeqLs/tyMiI+oEPfEBta2tTOzo61M2bN6uTk5NVH5uiiu0BCYIgCIIgIgh5WAiCIAiCiDwUsBAEQRAEEXkoYCEIgiAIIvJQwEIQBEEQROShgIUgCIIgiMhDAQtBEARBEJGHAhaCIAiCICIPBSwEQRAEQUQeClgIgiAIgog8FLAQBEEQBBF5KGAhCIIgCCLyUMBCEARBEETk+f8BezYfqyKav20AAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "plt.plot(opt.get_epochs(), opt.cost_history)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -654,7 +1118,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -697,7 +1161,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "lecture-materials", "language": "python", "name": "python3" }, @@ -711,7 +1175,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.15" + "version": "3.13.7" } }, "nbformat": 4, diff --git a/doc/src/week41/ipynb-exercisesweek41-src.tar.gz b/doc/src/week41/ipynb-exercisesweek41-src.tar.gz new file mode 100644 index 000000000..2bd56c014 Binary files /dev/null and b/doc/src/week41/ipynb-exercisesweek41-src.tar.gz differ