Files
DSAI/06_NN/code/nn_9_cnn_1.ipynb
2026-01-16 16:53:19 +01:00

823 lines
25 KiB
Plaintext

{
"cells": [
{
"cell_type": "markdown",
"id": "a83eef2d",
"metadata": {},
"source": [
"<div style=\"\n",
" border: 2px solid #4CAF50; \n",
" padding: 15px; \n",
" background-color: #f4f4f4; \n",
" border-radius: 10px; \n",
" align-items: center;\">\n",
"\n",
"<h1 style=\"margin: 0; color: #4CAF50;\">Neural Networks: Convolutional Neural Networks (1)</h1>\n",
"<h2 style=\"margin: 5px 0; color: #555;\">DSAI</h2>\n",
"<h3 style=\"margin: 5px 0; color: #555;\">Jakob Eggl</h3>\n",
"\n",
"<div style=\"flex-shrink: 0;\">\n",
" <img src=\"https://www.htl-grieskirchen.at/wp/wp-content/uploads/2022/11/logo_bildschirm-1024x503.png\" alt=\"Logo\" style=\"width: 250px; height: auto;\"/>\n",
"</div>\n",
"<p1> © 2025/26 Jakob Eggl. Nutzung oder Verbreitung nur mit ausdrücklicher Genehmigung des Autors.</p1>\n",
"</div>\n",
"<div style=\"flex: 1;\">\n",
"</div> "
]
},
{
"cell_type": "markdown",
"id": "83a52169",
"metadata": {},
"source": [
"Nachdem wir bisher nur von `Linear`-Layers gesprochen haben, möchten wir uns jetzt auf eine **neue Architektur** fokusieren. Diese ist besonders geeignet für Bilder und hat eine gravierend andere funktionsweise als bisherige Layers.\n",
"\n",
"Die Rede ist von sogenannten **Convolution**-Layers, welche dann die sogenannten Convolutional Neural Networks (CNN's) bilden.\n",
"\n",
"In diesem Notebook wollen wir:\n",
"1) Zuerst die Convolution-Operation kennenlernen und deren Anwendung in der klassischen Bildbearbeitung betrachten bevor wir\n",
"2) Uns das Convolution-Layer in PyTorch ansehen und uns am Schluss\n",
"3) Über die weiteren damit verbundenen Layers wie Pooling befassen."
]
},
{
"cell_type": "markdown",
"id": "f13607fc",
"metadata": {},
"source": [
"**Hinweis:** *Convolution* ist der englische Begriff für *Faltung*."
]
},
{
"cell_type": "markdown",
"id": "8dd66ee2",
"metadata": {},
"source": [
"**Hinweis:** Die Faltung ist eine mathematische Operation, welche [hier](https://de.wikipedia.org/wiki/Faltung_(Mathematik)) nachgelesen werden *kann*."
]
},
{
"cell_type": "markdown",
"id": "cef6ffb9",
"metadata": {},
"source": [
"## Die Convolution-Operation"
]
},
{
"cell_type": "markdown",
"id": "27ba7dd3",
"metadata": {},
"source": [
"Zu Beginn wollen wir uns die Faltungsoperation auf Bildern ansehen.\n",
"\n",
"In diesem Notebook haben wir dabei immer folgendes Setup:\n",
"* Wir haben ein Bild, sprich mathematisch gesehen einen $(3, \\text{Hoehe}, \\text{Breite})$ Tensor, also dreimal eine Matrix der Größe $\\text{Hoehe} \\times \\text{Breite}$.\n",
"* Wir haben einen Filter $K$ (auch Kernel genannt), mathematisch gesehen ist das auch ein Tensor, der Einfachkeit halber (und das trifft auch meistens zu) denken wir einfach an eine Matrix.\n",
"* Wir wollen den Filter auf unser Bild anwenden und dabei ein neues Bild, also eine neue Matrix generieren."
]
},
{
"cell_type": "markdown",
"id": "e14cbfd5",
"metadata": {},
"source": [
"Betrachten wir hier zuerst mal ein paar Beispiele."
]
},
{
"cell_type": "markdown",
"id": "8c32d03d",
"metadata": {},
"source": [
"<img src=\"../resources/Mario_Convolution.png\" width=\"1000\"/>\n",
"\n",
"(von https://www.youtube.com/watch?v=KuXjwB4LzSA)"
]
},
{
"cell_type": "markdown",
"id": "f1c431d9",
"metadata": {},
"source": [
"<img src=\"../resources/Kirby_Convolution.jpg\" width=\"1000\"/>\n",
"\n",
"(von https://www.youtube.com/watch?v=KuXjwB4LzSA)"
]
},
{
"cell_type": "markdown",
"id": "8ba45f6c",
"metadata": {},
"source": [
"Hier ist bei beiden Bildern rechts oben bzw. auch links das große Bild das Ausgangsbild. Rechts oben (blau umrahmt) ist der Filter zu sehen.\n",
"\n",
"Das Ergebnis der Faltungsoperation (Convolution) ist unten rechts zu sehen:\n",
"Im Super-Mario Bild ist das ein Unschärfe Filter, im zweiten Bild mehr oder weniger das Gegenteil, sprich ein Filter zum Schärfen des Bildes."
]
},
{
"cell_type": "markdown",
"id": "1d2ec712",
"metadata": {},
"source": [
"**Aber was passiert da genau?**"
]
},
{
"cell_type": "markdown",
"id": "8fcde416",
"metadata": {},
"source": [
"Wir betrachten dazu ein **schwarz-weiß Bild**, also eine Matrix als unseren Input.\n",
"\n",
"\n",
"In unserem Fall ist das die **Matrix**:\n",
"$$X=\\begin{pmatrix}\n",
" 0 & 0 & 0 & 0 & 0 & 0\\\\\n",
" 0 & 1.0 & 0 & 0 & 0.4 & 0\\\\\n",
" 0 & 0 & 0 & 0.4 & 0 & 0\\\\\n",
" 0 & 0 & 0 & 0 & 0.4 & 0\\\\\n",
" 0 & 0 & 0 & 0.4 & 0 & 0\\\\\n",
" 0 & 0 & 0 & 0 & 0.4 & 0\n",
"\\end{pmatrix}$$\n",
"\n",
"Dazu wollen wir als **Kernel (=Filter)** folgende Matrix verwenden\n",
"\n",
"$$K = \\begin{pmatrix}\n",
" 0 & 0 & 0.5 \\\\\n",
" 0 & 0.5 & 0 \\\\\n",
" 0 & 0 & 0.5\n",
"\\end{pmatrix}$$"
]
},
{
"cell_type": "markdown",
"id": "688d77f4",
"metadata": {},
"source": [
"Bei der Faltung wird nun der Filter über den Input $X$ gelegt, und das in jeder möglichen Kombination.\n",
"\n",
"Das sieht in unserem Fall dann so aus für den ersten Eintrag des Ergebnisses."
]
},
{
"cell_type": "markdown",
"id": "945e1193",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Concept1.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "54827b86",
"metadata": {},
"source": [
"**Was ist hier passiert?** Wir haben den Filter (rot) über unser Bild (blau) gelegt und danach einfach Elementweise Matrix-Multipliziert und das Ergebnis zusammen gezählt.\n",
"\n",
"Dies wiederholen wir jetzt auch für die anderen Möglichkeiten. Also wir shiften unseren Kern um 1 Spalte nach rechts."
]
},
{
"cell_type": "markdown",
"id": "e9e68bb1",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Concept2.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "2551d80d",
"metadata": {},
"source": [
"Dies wiederholen wir jetzt solange, bis wir alle Möglichkeiten durch haben."
]
},
{
"cell_type": "markdown",
"id": "bdf22f6b",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Concept3.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "03f12803",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Concept4.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "029b40ce",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Concept5.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "7c0443f6",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Concept6.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "baee7070",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Concept7.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "d2f31397",
"metadata": {},
"source": [
"**Wichtig:** Wie wir sehen ist also die Faltung quasi (mathematisch sehr ungenau) wie ein 2D-Skalarprodukt."
]
},
{
"cell_type": "markdown",
"id": "3e3fa120",
"metadata": {},
"source": [
"Somit können wir auch erklären, warum beim Super-Mario Bild vorher ein unscharfer Output erzeugt wird. Der Grund ist, weil einfach der neue Pixel-Wert der Durchschnitt von allen umliegenden Pixelwerten ist."
]
},
{
"cell_type": "markdown",
"id": "1eaf4b05",
"metadata": {},
"source": [
"**Wichtig:** Beim Super-Mario und Kirby Bild ist die Faltung jeweils auf jeden Channel ausgeführt worden. Dabei wurde jedes mal der gleiche Filter (Kernel) verwendet."
]
},
{
"cell_type": "markdown",
"id": "cc828e9e",
"metadata": {},
"source": [
"Wir wollen nun ein paar weitere Filter ausprobieren bei eigenen Bildern. Dazu nutzen wir die `cv2` Bibliothek. Das geht (falls noch nicht vorhanden mit dem `pip install opencv-python` Command)."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cc849a62",
"metadata": {},
"outputs": [],
"source": [
"import cv2\n",
"import matplotlib\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"import os\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7b743c95",
"metadata": {},
"outputs": [],
"source": [
"image_path = os.path.join(\"..\", \"resources\", \"Chemnitz_Hauptplatz.jpg\")\n",
"image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)\n",
"\n",
"fig, ax = plt.subplots(figsize=(3, 6))\n",
"ax.imshow(image, cmap=\"gray\", vmin=0, vmax=255)\n",
"\n",
"ax.grid(False)\n",
"ax.set_xticks([])\n",
"ax.set_yticks([])\n",
"\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "39e9c012",
"metadata": {},
"source": [
"Nun definieren wir unsere Kernel. Beispiele sind zum Beispiel die Sobel Filter."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "47df8c04",
"metadata": {},
"outputs": [],
"source": [
"sobel_x = np.array([\n",
" [ -1, 0, 1],\n",
" [ -2, 0, 2],\n",
" [ -1, 0, 1]\n",
"])\n",
"sobel_y = np.array([\n",
" [ -1, -2, -1],\n",
" [ 0, 0, 0],\n",
" [ 1, 2, 1]\n",
"])\n",
"\n",
"\n",
"mean_blur = 1/400 * np.ones((20, 20))\n",
"\n",
"edge_detection = sobel_x + sobel_y\n",
"\n",
"gaussian_blur = 1/36 * np.array([\n",
" [1,4,1],\n",
" [4,16,4],\n",
" [1,4,1]\n",
"]) \n",
"\n",
"filter = edge_detection\n",
"\n",
"filtered_image = cv2.filter2D(image, -1, filter)\n",
"\n",
"\n",
"fig, ax = plt.subplots(figsize=(3, 6))\n",
"ax.imshow(filtered_image, cmap=\"gray\", vmin=0, vmax=255)\n",
"ax.grid(False)\n",
"ax.set_xticks([])\n",
"ax.set_yticks([])\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"id": "8b70388a",
"metadata": {},
"source": [
"> **Übung:** Suche im Internet nach dem **Edge-Detection**, **Mean-Blurr**, **Gaussian-Blurr** und einem Schärfefilter und implementiere diese oben. "
]
},
{
"cell_type": "markdown",
"id": "edc12eaa",
"metadata": {},
"source": [
"**Hinweis:** Der Filter hat normalerweise nur positive Einträge (falls negativ muss man sich überlegen, wie das interpretiert werden soll) und diese Einträge sollen in Summe 1 Ergeben, sodass die (Pixel)Werte des Outputs im gleichen Bereich bleiben."
]
},
{
"cell_type": "markdown",
"id": "4dac2efe",
"metadata": {},
"source": [
"> **Übung:** Was ändert sich beim Output neben den eigentlichen Pixel Werten noch? *Tipp:* Betrachte nochmal die genaue Berechnung im vorigen Beispiel, welches in vielen Schritten detailiert gezeigt wurde."
]
},
{
"cell_type": "markdown",
"id": "4ce7aa1e",
"metadata": {},
"source": [
"Wie wir bereits bemerkt haben, ist die Output Matrix etwas kleiner als der Input. Die genaue Größe kann folgendermaßen berechnet werden:\n",
"\n",
"$$\\begin{align*}\n",
" X_{\\text{new}} &= \\left\\lfloor \\frac{X-K_x+2P_x}{S_x} + 1 \\right\\rfloor \\\\\n",
" Y_{\\text{new}} &= \\left\\lfloor \\frac{Y-K_y+2P_y}{S_y} + 1 \\right\\rfloor\n",
"\\end{align*}$$"
]
},
{
"cell_type": "markdown",
"id": "54cb3aba",
"metadata": {},
"source": [
"Dabei ist:\n",
"\n",
"* $X_{\\text{new}}, Y_{\\text{new}}$ die Größe der neuen Matrix\n",
"* $K_x$, $K_y$ die Größe des Kernels ($K_x$ ist die Anzahl der Spalten)\n",
"* $P_x, P_y$ steht für das Padding. Dieser Parameter erlaubt uns, einen gleich großen Output wie vorher der Input zu haben. Er beschreibt wie viele Pixel wir rund um unsere Matrix noch hinzufügen. Sprich $P_x=1$ heißt, wir fügen an der linken und an der rechten Seite noch eine Spalte hinzu. Als Wert wird dabei normalerweise die $0$ verwendet (\"**Zero-Padding**\"), es gibt aber auch andere Möglichkeiten.\n",
"* $S_x$, $S_y$ steht für den Stride. Dieser steht für die Anzahl an Pixel, die wir jedes mal nach $x$ bzw. nach $y$ \"rutschen\". Standard ist $1$, also wir bewegen uns immer nur 1 Pixel."
]
},
{
"cell_type": "markdown",
"id": "9d043f88",
"metadata": {},
"source": [
"Sehr empfehlenswert ist hier dieses [GitHub Repository](https://github.com/vdumoulin/conv_arithmetic), da es sehr viele Visualisierungen beinhaltet zu den einzelnen Convolution Typen."
]
},
{
"cell_type": "markdown",
"id": "05233ef0",
"metadata": {},
"source": [
"**Visualisierung Stride=3**"
]
},
{
"cell_type": "markdown",
"id": "8c53d073",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Stride3_1.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "ed3967e3",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Stride3_2.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "90b7012e",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Stride3_3.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "1ff2b1c2",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Stride3_4.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "e9872f85",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Stride3_5.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "4b24fa63",
"metadata": {},
"source": [
"**Visualisierung Padding=1**"
]
},
{
"cell_type": "markdown",
"id": "0f9ad91a",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Zero_Padding_1.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "cc9bedc9",
"metadata": {},
"source": [
"<img src=\"../resources/Convolution_Zero_Padding_2.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "80d1cf5b",
"metadata": {},
"source": [
"## Der CNN-Layer"
]
},
{
"cell_type": "markdown",
"id": "4b6f59df",
"metadata": {},
"source": [
"Nachdem wir nun Bescheid wissen, wie die Faltung auf Bildern allgemein funktioniert, betrachten wir nun das CNN-Layer in PyTorch."
]
},
{
"cell_type": "markdown",
"id": "a623b878",
"metadata": {},
"source": [
"Prinzipiell ist es immer gut, einen Blick in die Dokumentation zu werfen. Deswegen werden wir zuerst [hier](https://docs.pytorch.org/docs/stable/generated/torch.nn.Conv2d.html) kurz schauen. Wir betrachten dabei das `nn.Conv2d()`-Layer, da wir es hauptsächlich auf Bildern (2D) anwenden werden."
]
},
{
"cell_type": "markdown",
"id": "5b0d46c4",
"metadata": {},
"source": [
"`class torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, padding=0, ..., padding_mode='zeros')`"
]
},
{
"cell_type": "markdown",
"id": "9a6ab52b",
"metadata": {},
"source": [
"Dies ist der (wichtige Teil vom) Konstruktor des `Conv2d` Layers. Wir gehen nun die Parameter durch:\n",
"\n",
"* `in_channels`: Die Anzahl der Input Channels. Für ein RGB Bild ist dies 3.\n",
"* `out_channels`: Anzahl der Kernels, die wir durch das Netzwerk schicken. Dies ist dann die Anzahl der **Output Channels** und somit auch gleich `in_channels`, falls danach eine weitere `nn.Conv2d` Schicht kommt.\n",
"* `kernel_size`: Größe des Kernels (in Tupelform, also $(3,5)$ oder in Integerform, also $3$ für einen quadratischen Kernel (in diesem Fall $3\\times 3$))\n",
"* `stride`: Größe des Strides (erneut entweder Tupelform oder Integer bei gleichem Stride)\n",
"* `padding`: Größe des Paddings (Tupel oder Integer)\n",
"* `padding_mode`: Bestimmt die Art des Paddings. Übergeben wird ein String: 'zeros', 'reflect', 'replicate' oder 'circular'. Standard ist 'zeros'. "
]
},
{
"cell_type": "markdown",
"id": "429f1226",
"metadata": {},
"source": [
"> **Übung:** Was sind nun die Parameter die gelernt werden sollen vom Netzwerk?"
]
},
{
"cell_type": "markdown",
"id": "d15c7721",
"metadata": {},
"source": [
"Wir wollen also die Werte des Filters (Kernel) lernen."
]
},
{
"cell_type": "markdown",
"id": "4d73d1e2",
"metadata": {},
"source": [
"> **Übung:** Hat ein CNN mehr oder weniger Parameter als ein Fully-Connected Neural Network?"
]
},
{
"cell_type": "markdown",
"id": "1c80ff0d",
"metadata": {},
"source": [
"Wir visualisieren nochmal kurz die Parameter des Netzwerkes."
]
},
{
"cell_type": "markdown",
"id": "9d23fb13",
"metadata": {},
"source": [
"<img src=\"../resources/CNN_Input_1.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "ab74f54c",
"metadata": {},
"source": [
"<img src=\"../resources/CNN_Input_2.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "4563f40a",
"metadata": {},
"source": [
"Wir sehen also, dass zum Beispiel für einen RGB Input der Kernel dann auch einfach 3 Channels hat (3 **verschiedene** Matrizen). Dabei wird jeder Layer des Kernels auf das entsprechende Layer im Bild angewendet. Am Ende werden alle Schichten zu **einem Channel** zusammen **addiert**."
]
},
{
"cell_type": "markdown",
"id": "05ecc94a",
"metadata": {},
"source": [
"Beim zweiten Bild ist nun die Anzahl an `out_channels` auf 2 gesetzt. Das heißt wir haben 2 **verschiedene** Kernels mit je 3 Channels. "
]
},
{
"cell_type": "markdown",
"id": "46485235",
"metadata": {},
"source": [
"**Was ist nun das Ziel von den Kernels?**\n",
"\n",
"Die Kernels versuchen im Lernprozess Werte in die einzelnen Einträge zu schreiben, sodass wir aus unseren Trainingsbildern Schritt für Schritt möglichst gute Informationen extrahieren können."
]
},
{
"cell_type": "markdown",
"id": "ca3e12ed",
"metadata": {},
"source": [
"Prinzipiell haben wir dadurch schon verstanden wie ein CNN-Layer in PyTorch funktioniert. Was gibt es jetzt noch zu beachten?"
]
},
{
"cell_type": "markdown",
"id": "e836a6a5",
"metadata": {},
"source": [
"* Wir müssen nach jedem CNN-Layer berechnen, wie groß unser Ergebnis-Bild ist. Der Grund ist, dass wir nach unseren CNN-Layers eine fixe Output Größe brauchen (siehe nächster Punkt).\n",
"* Der Output von einem Neuronalen Netzwerk, welches CNN-Schichten beinhaltet ist vielfältig:\n",
" * In den meisten Fällen wechseln wir nach einigen CNN-Layern zu einem Fully-Connected Neuronal Network, welches später die Klassifikation bzw. Regression basierend auf den Ergebnissen der Faltung(en) erledigt. Dabei müssen wir wissen, wie groß der Output nach dem letzten CNN-Layer ist. Ist dieser dann zum Beispiel ein $3\\times 3$-Bild, dann würden wir dieses `flatten()` und erhalten einen $9$-Vektor.\n",
" * Es gibt aber auch Fälle (zum Beispiel bei unserer Image Inpainting Challenge), bei der wir als Output wirklich Bilder wollen, sprich wir beenden unser Netzwerk auch mit einem CNN-Layer. Auch hier müssen wir sicherstellen, dass dieser Output dann zum Beispiel genauso groß ist wie der Input."
]
},
{
"cell_type": "markdown",
"id": "3a7a1438",
"metadata": {},
"source": [
"> **Übung:** Wie können wir das mit CNN's realisieren, dass am Ende unser Bild genauso groß ist wie vorher?"
]
},
{
"cell_type": "markdown",
"id": "9ceb3b52",
"metadata": {},
"source": [
"Am Schluss wollen wir uns noch einem weiteren, sehr ähnlichen, Konzept widmen. Die Rede ist von den sogenannten **Pooling-Layers**."
]
},
{
"cell_type": "markdown",
"id": "a82b2e59",
"metadata": {},
"source": [
"## Pooling"
]
},
{
"cell_type": "markdown",
"id": "369e8557",
"metadata": {},
"source": [
"Die Idee vom **Pooling** ist, dass wir unser Bild *downsamplen*, sprich ein kleineres Bild produzieren. Dabei gibt es mehrere Möglichkeiten, wie wir dieses Downsampling realisieren können:\n",
"* **Average-Pooling:** Wir nehmen den Mittelwert von $k\\times k$ Werten\n",
"* **Max Pooling:** Wir nehmen den Maximalwert von $k\\times k$ Werten (wird meistens verwendet)"
]
},
{
"cell_type": "markdown",
"id": "41d13d86",
"metadata": {},
"source": [
"Wir visualisieren kurz den Effekt von Max-Pooling, in diesem Fall mit $k=2$. "
]
},
{
"cell_type": "markdown",
"id": "5278cd51",
"metadata": {},
"source": [
"<img src=\"../resources/Max_Pooling_1.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "4a898651",
"metadata": {},
"source": [
"<img src=\"../resources/Max_Pooling_2.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "f460b87e",
"metadata": {},
"source": [
"<img src=\"../resources/Max_Pooling_3.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "fda20b59",
"metadata": {},
"source": [
"<img src=\"../resources/Max_Pooling_4.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "a74487e7",
"metadata": {},
"source": [
"<img src=\"../resources/Max_Pooling_5.jpg\" width=\"1000\"/>\n",
"\n",
"(von Dr. Andreas Schörgenhumer; Hands-On AI1 WS2021)"
]
},
{
"cell_type": "markdown",
"id": "0fef4fb6",
"metadata": {},
"source": [
"**Wichtig:** Ein Pooling Layer beinhaltet also keine Parameter, er reduziert nur unsere Datengröße deutlich und führt somit auch natürlich zu einem (großen) Informationsverlust."
]
},
{
"cell_type": "markdown",
"id": "583d3484",
"metadata": {},
"source": [
"In PyTorch können wir so ein Verhalten auch ganz einfach mit einem Pooling Layer erreichen, welches wir mit `nn.MaxPool2d()` ganz einfach hinzufügen können. Die Dokumentation finden wir [hier](https://docs.pytorch.org/docs/stable/generated/torch.nn.MaxPool2d.html)."
]
},
{
"cell_type": "markdown",
"id": "6fd908f0",
"metadata": {},
"source": [
"Betrachten wir kurz (den wichtigsten Teil davon) den Konstruktor.\n",
"\n",
"`class torch.nn.MaxPool2d(kernel_size, stride=None, padding=0, ...)`\n",
"\n",
"Der einzige Wert im Konstruktor, der übergeben werden muss ist `kernel_size`, erneut als Tupel oder Integer. Als `stride` ist standardmäßig `None` übergeben, was bedeutet, dass wir pro Schritt den \"Filter\" um die `kernel_size` verschieben. Ebenso ist kein standardmäßig kein Padding vorgesehen."
]
},
{
"cell_type": "markdown",
"id": "04818d04",
"metadata": {},
"source": [
"**Hinweis:** Die Größe der Daten kann nach einem Pooling Layer mit der gleichen Formel wie vorher berechnet werden. In vielen Fällen (`stride=kernel_size` und ohne Padding) ist das (bis auf das Verhalten am Rand) die Berechnung natürlich ohne Formel auch sehr leicht."
]
},
{
"cell_type": "markdown",
"id": "f97b5dbe",
"metadata": {},
"source": [
"---"
]
},
{
"cell_type": "markdown",
"id": "480c0c1d",
"metadata": {},
"source": [
"Damit haben wir die Grundlagen eines CNN-Layers verstanden. Wir werden im nächsten Notebook ein großes Beispiel machen, welches uns die Details und die Funktionen in einem praktischen Setting nochmal näher bringt."
]
}
],
"metadata": {
"kernelspec": {
"display_name": "dsai",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.5"
}
},
"nbformat": 4,
"nbformat_minor": 5
}