{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "provenance": [], "authorship_tag": "ABX9TyPHUNRkJMI5LujaxIXNV60m", "include_colab_link": true }, "kernelspec": { "name": "python3", "display_name": "Python 3" }, "language_info": { "name": "python" } }, "cells": [ { "cell_type": "markdown", "metadata": { "id": "view-in-github", "colab_type": "text" }, "source": [ "\"Open" ] }, { "cell_type": "markdown", "source": [ "# **Notebook 10.1: 1D Convolution**\n", "\n", "This notebook investigates 1D convolutional layers.\n", "\n", "Work through the cells below, running each cell in turn. In various places you will see the words \"TO DO\". Follow the instructions at these places and make predictions about what is going to happen or write code to complete the functions.\n", "\n", "Contact me at udlbookmail@gmail.com if you find any mistakes or have any suggestions.\n" ], "metadata": { "id": "el8l05WQEO46" } }, { "cell_type": "code", "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt" ], "metadata": { "id": "nw7k5yCtOzoK" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "# Define a signal that we can apply convolution to\n", "x = [5.2, 5.3, 5.4, 5.1, 10.1, 10.3, 9.9, 10.3, 3.2, 3.4, 3.3, 3.1]" ], "metadata": { "id": "lSSHuoEqO3Ly" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "# Draw the signal\n", "fig,ax = plt.subplots()\n", "ax.plot(x, 'k-')\n", "ax.set_xlim(0,11)\n", "ax.set_ylim(0, 12)\n", "plt.show()" ], "metadata": { "id": "zVssv_wiREc2" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "# Now let's define a zero-padded convolution operation\n", "# with a convolution kernel size of 3, a stride of 1, and a dilation of 0\n", "# as in figure 10.2a-c. Write it yourself, don't call a library routine!\n", "# Don't forget that Python arrays are indexed from zero, not from 1 as in the book figures\n", "def conv_3_1_0_zp(x_in, omega):\n", " x_out = np.zeros_like(x_in)\n", " # TODO -- write this function\n", " # replace this line\n", " x_out = x_out\n", "\n", "\n", "\n", " return x_out" ], "metadata": { "id": "MmfXED12RvNq" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "Now let's see what kind of things convolution can do\n", "First, it can average nearby values, smoothing the function:" ], "metadata": { "id": "Fof_Rs98Zovq" } }, { "cell_type": "code", "source": [ "\n", "omega = [0.33,0.33,0.33]\n", "h = conv_3_1_0_zp(x, omega)\n", "\n", "# Check that you have computed this correctly\n", "print(f\"Sum of output is {np.sum(h):3.3}, should be 71.1\")\n", "\n", "# Draw the signal\n", "fig,ax = plt.subplots()\n", "ax.plot(x, 'k-',label='before')\n", "ax.plot(h, 'r-',label='after')\n", "ax.set_xlim(0,11)\n", "ax.set_ylim(0, 12)\n", "ax.legend()\n", "plt.show()" ], "metadata": { "id": "HOcPZR6iWXsa" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "Notice how the red function is a smoothed version of the black one as it has averaged adjacent values. The first and last outputs are considerably lower than the original curve though. Make sure that you understand why!

\n", "\n", "With different weights, the convolution can be used to find sharp changes in the function:" ], "metadata": { "id": "PBkNKUylZr-k" } }, { "cell_type": "code", "source": [ "\n", "omega = [-0.5,0,0.5]\n", "h2 = conv_3_1_0_zp(x, omega)\n", "\n", "# Draw the signal\n", "fig,ax = plt.subplots()\n", "ax.plot(x, 'k-',label='before')\n", "ax.plot(h2, 'r-',label='after')\n", "ax.set_xlim(0,11)\n", "# ax.set_ylim(0, 12)\n", "ax.legend()\n", "plt.show()" ], "metadata": { "id": "o8T5WKeuZrgS" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "Notice that the convolution has a peak where the original function went up and trough where it went down. It is roughly zero where the function is locally flat. This convolution approximates a derivative.

\n", "\n", "Now let's define the convolutions from figure 10.3. " ], "metadata": { "id": "ogfCVThJgtPx" } }, { "cell_type": "code", "source": [ "# Now let's define a zero-padded convolution operation\n", "# with a convolution kernel size of 3, a stride of 2, and a dilation of 0\n", "# as in figure 10.2a-c. Write it yourself, don't call a library routine!\n", "def conv_3_2_0_zp(x_in, omega):\n", " x_out = np.zeros(int(np.ceil(len(x_in)/2)))\n", " # TODO -- write this function\n", " # replace this line\n", " x_out = x_out\n", "\n", "\n", "\n", " return x_out" ], "metadata": { "id": "5QYrQmFMiDBj" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "omega = [0.33,0.33,0.33]\n", "h3 = conv_3_2_0_zp(x, omega)\n", "\n", "# If you have done this right, the output length should be six and it should\n", "# contain every other value from the original convolution with stride 1\n", "print(h)\n", "print(h3)" ], "metadata": { "id": "CD96lnDHX72A" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "# Now let's define a zero-padded convolution operation\n", "# with a convolution kernel size of 5, a stride of 1, and a dilation of 0\n", "# as in figure 10.2a-c. Write it yourself, don't call a library routine!\n", "def conv_5_1_0_zp(x_in, omega):\n", " x_out = np.zeros_like(x_in)\n", " # TODO -- write this function\n", " # replace this line\n", " x_out = x_out\n", "\n", "\n", "\n", " return x_out" ], "metadata": { "id": "lw46-gNUjDw7" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "\n", "omega2 = [0.2, 0.2, 0.2, 0.2, 0.2]\n", "h4 = conv_5_1_0_zp(x, omega2)\n", "\n", "# Check that you have computed this correctly\n", "print(f\"Sum of output is {np.sum(h4):3.3}, should be 69.6\")\n", "\n", "# Draw the signal\n", "fig,ax = plt.subplots()\n", "ax.plot(x, 'k-',label='before')\n", "ax.plot(h4, 'r-',label='after')\n", "ax.set_xlim(0,11)\n", "ax.set_ylim(0, 12)\n", "ax.legend()\n", "plt.show()" ], "metadata": { "id": "JkKBL-nFk4bf" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "# Finally let's define a zero-padded convolution operation\n", "# with a convolution kernel size of 3, a stride of 1, and a dilation of 1\n", "# as in figure 10.2a-c. Write it yourself, don't call a library routine!\n", "# Don't forget that Python arrays are indexed from zero, not from 1 as in the book figures\n", "def conv_3_1_1_zp(x_in, omega):\n", " x_out = np.zeros_like(x_in)\n", " # TODO -- write this function\n", " # replace this line\n", " x_out = x_out\n", "\n", "\n", " return x_out" ], "metadata": { "id": "_aBcW46AljI0" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "omega = [0.33,0.33,0.33]\n", "h5 = conv_3_1_1_zp(x, omega)\n", "\n", "# Check that you have computed this correctly\n", "print(f\"Sum of output is {np.sum(h5):3.3}, should be 68.3\")\n", "\n", "# Draw the signal\n", "fig,ax = plt.subplots()\n", "ax.plot(x, 'k-',label='before')\n", "ax.plot(h5, 'r-',label='after')\n", "ax.set_xlim(0,11)\n", "ax.set_ylim(0, 12)\n", "ax.legend()\n", "plt.show()" ], "metadata": { "id": "En-ByCqWlvMI" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "Finally, let's investigate representing convolutions as full matrices, and show we get the same answer." ], "metadata": { "id": "loBwu125lXx1" } }, { "cell_type": "code", "source": [ "# Compute matrix in figure 10.4 d\n", "def get_conv_mat_3_1_0_zp(n_out, omega):\n", " omega_mat = np.zeros((n_out,n_out))\n", " # TODO Fill in this matix\n", " # Replace this line:\n", " omega_mat = omega_mat\n", "\n", "\n", "\n", " return omega_mat" ], "metadata": { "id": "U2RFWfGgs72j" }, "execution_count": null, "outputs": [] }, { "cell_type": "code", "source": [ "# Run original convolution\n", "omega = np.array([-1.0,0.5,-0.2])\n", "h6 = conv_3_1_0_zp(x, omega)\n", "print(h6)\n", "\n", "# If you have done this right, you should get the same answer\n", "omega_mat = get_conv_mat_3_1_0_zp(len(x), omega)\n", "h7 = np.matmul(omega_mat, x)\n", "print(h7)\n" ], "metadata": { "id": "20IYxku8lMty" }, "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "source": [ "TODO: What do you expect to happen if we apply the last convolution twice? Can this be represented as a single convolution? If so, then what is it?" ], "metadata": { "id": "rYoQVhBfu8R4" } } ] }