{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Wprowadzenie\n",
    "\n",
    "Ten notatnik przedstawia problem \"nadmiernego wygładzania\" (ang. *oversmoothing*) w grafowych sieciach neuronowych, oraz potencjalne możliwości jego rozwiązania."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Import bibliotek"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch_geometric as pyg\n",
    "import torch as th\n",
    "import networkx as nx\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "import pandas as pd\n",
    "import torchmetrics as tm\n",
    "from sklearn.decomposition import PCA\n",
    "\n",
    "from tqdm.auto import tqdm\n",
    "from pathlib import Path\n",
    "\n",
    "import sys\n",
    "sys.path.append('..')\n",
    "import utils.vis_utils as vis"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "IMG_PATH = Path('images')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "np.set_printoptions(suppress=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Prosty przykład"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "g = pyg.data.Data()\n",
    "g.x = th.tensor([\n",
    "    [0.1], # wierzchołek 0\n",
    "    [0.2], # wierzchołek 1\n",
    "    [0.3], # wierzchołek 2\n",
    "    [1], # wierzchołek 3\n",
    "    [2], # wierzchołek 4\n",
    "    [3], # wierzchołek 5\n",
    "    [4], # wierzchołek 6\n",
    "    [10], # wierzchołek 7\n",
    "    [20], # wierzchołek 8\n",
    "    [30], # wierzchołek 9\n",
    "    [40], # wierzchołek 10\n",
    "    [50], # wierzchołek 11\n",
    "    [60], # wierzchołek 12\n",
    "    [100], # wierzchołek 13\n",
    "    [200], # wierzchołek 14\n",
    "    [300], # wierzchołek 15\n",
    "    [400] # wierzchołek 16\n",
    "]).to(th.float)\n",
    "\n",
    "g.edge_index = th.tensor([\n",
    "    [1, 2,  3, 4, 5, 6, 7, 8,  9,  10, 11, 12, 13,  14, 15, 16], # source\n",
    "    [0, 0,  1, 1, 2, 2, 3, 4,  5,  6,  7,  8,  10,  10, 11, 12], # target\n",
    "])\n",
    "\n",
    "g = pyg.transforms.AddSelfLoops()(g)\n",
    "g_nx = pyg.utils.to_networkx(g, node_attrs=['x'])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 130408 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "pos = nx.spring_layout(g_nx, seed=111)\n",
    "f, ax = plt.subplots(figsize=(10, 10))\n",
    "nx.draw(g_nx, pos, with_labels=False, node_color='blue', node_size=1300, edge_color='gray', ax=ax)\n",
    "\n",
    "node_labels = {i: np.round(g.x[i].item(), 2) for i in range(g.x.size(0))}\n",
    "nx.draw_networkx_labels(g_nx, pos, labels=node_labels, font_color='black', font_size=10, bbox=dict(facecolor='white', edgecolor='none', boxstyle='round,pad=0.3'), ax=ax);\n",
    "plt.title(\"Graf o wyraźnie różnych skalach etykiet wierzchołków\")\n",
    "plt.tight_layout()\n",
    "f.savefig(IMG_PATH / 'graph_with_different_scales.tiff', format='tiff', pil_kwargs={'compression': 'tiff_lzw'})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Najdłuższa ścieżka w tym grafie:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "([15, 11, 7, 3, 1, 0], 5)"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "g_nx.remove_edges_from(nx.selfloop_edges(g_nx))\n",
    "nx.dag_longest_path(g_nx), nx.dag_longest_path_length(g_nx)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Ilustracja problemu nadmiernego wygładzania"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.2 ,  0.22,  0.21,  0.07, -0.17, -0.4 , -0.67, -0.4 ,  0.32,\n",
       "        2.56, -2.34, -5.58, -8.31,  7.56, 14.7 , 21.84, 28.98],\n",
       "      dtype=float32)"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pyg.seed_everything(222)\n",
    "sage_model_2_convs = pyg.nn.GraphSAGE(\n",
    "    in_channels=1, \n",
    "    out_channels=1, \n",
    "    normalize=False, \n",
    "    num_layers=2,\n",
    "    hidden_channels=1,\n",
    "    project=True,\n",
    "    aggr='mean')\n",
    "\n",
    "with th.no_grad():\n",
    "    out_2_convs = sage_model_2_convs(g.x, g.edge_index)\n",
    "out_2_convs.squeeze().numpy().round(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>2 warstwy</th>\n",
       "      <th>3 warstwy</th>\n",
       "      <th>4 warstwy</th>\n",
       "      <th>5 warstwy</th>\n",
       "      <th>6 warstwy</th>\n",
       "      <th>7 warstwy</th>\n",
       "      <th>8 warstwy</th>\n",
       "      <th>9 warstwy</th>\n",
       "      <th>10 warstwy</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>w 0</th>\n",
       "      <td>0.20</td>\n",
       "      <td>-0.770000</td>\n",
       "      <td>-0.77</td>\n",
       "      <td>0.29</td>\n",
       "      <td>-0.07</td>\n",
       "      <td>0.30</td>\n",
       "      <td>1.07</td>\n",
       "      <td>-0.60</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 1</th>\n",
       "      <td>0.22</td>\n",
       "      <td>-0.750000</td>\n",
       "      <td>-0.77</td>\n",
       "      <td>0.29</td>\n",
       "      <td>-0.07</td>\n",
       "      <td>0.30</td>\n",
       "      <td>1.07</td>\n",
       "      <td>-0.60</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 2</th>\n",
       "      <td>0.21</td>\n",
       "      <td>-0.760000</td>\n",
       "      <td>-0.77</td>\n",
       "      <td>0.29</td>\n",
       "      <td>-0.07</td>\n",
       "      <td>0.30</td>\n",
       "      <td>1.07</td>\n",
       "      <td>-0.60</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 3</th>\n",
       "      <td>0.07</td>\n",
       "      <td>-0.900000</td>\n",
       "      <td>-0.77</td>\n",
       "      <td>0.29</td>\n",
       "      <td>-0.07</td>\n",
       "      <td>0.30</td>\n",
       "      <td>1.07</td>\n",
       "      <td>-0.60</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 4</th>\n",
       "      <td>-0.17</td>\n",
       "      <td>-0.970000</td>\n",
       "      <td>-0.77</td>\n",
       "      <td>0.29</td>\n",
       "      <td>-0.07</td>\n",
       "      <td>0.30</td>\n",
       "      <td>1.07</td>\n",
       "      <td>-0.60</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 5</th>\n",
       "      <td>-0.40</td>\n",
       "      <td>-0.970000</td>\n",
       "      <td>-0.77</td>\n",
       "      <td>0.29</td>\n",
       "      <td>-0.07</td>\n",
       "      <td>0.31</td>\n",
       "      <td>1.07</td>\n",
       "      <td>-0.61</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 6</th>\n",
       "      <td>-0.67</td>\n",
       "      <td>-0.970000</td>\n",
       "      <td>-0.77</td>\n",
       "      <td>0.29</td>\n",
       "      <td>-0.07</td>\n",
       "      <td>0.30</td>\n",
       "      <td>1.07</td>\n",
       "      <td>-0.60</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 7</th>\n",
       "      <td>-0.40</td>\n",
       "      <td>-0.970000</td>\n",
       "      <td>-0.77</td>\n",
       "      <td>0.29</td>\n",
       "      <td>-0.07</td>\n",
       "      <td>0.30</td>\n",
       "      <td>1.07</td>\n",
       "      <td>-0.60</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 8</th>\n",
       "      <td>0.32</td>\n",
       "      <td>-0.650000</td>\n",
       "      <td>-0.77</td>\n",
       "      <td>0.29</td>\n",
       "      <td>-0.07</td>\n",
       "      <td>0.30</td>\n",
       "      <td>1.07</td>\n",
       "      <td>-0.60</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 9</th>\n",
       "      <td>2.56</td>\n",
       "      <td>1.550000</td>\n",
       "      <td>0.56</td>\n",
       "      <td>-0.06</td>\n",
       "      <td>0.09</td>\n",
       "      <td>0.40</td>\n",
       "      <td>1.12</td>\n",
       "      <td>-0.65</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 10</th>\n",
       "      <td>-2.34</td>\n",
       "      <td>-0.970000</td>\n",
       "      <td>-0.77</td>\n",
       "      <td>0.29</td>\n",
       "      <td>-0.07</td>\n",
       "      <td>0.32</td>\n",
       "      <td>1.07</td>\n",
       "      <td>-0.61</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 11</th>\n",
       "      <td>-5.58</td>\n",
       "      <td>-0.970000</td>\n",
       "      <td>-0.77</td>\n",
       "      <td>0.29</td>\n",
       "      <td>-0.07</td>\n",
       "      <td>0.31</td>\n",
       "      <td>1.07</td>\n",
       "      <td>-0.61</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 12</th>\n",
       "      <td>-8.31</td>\n",
       "      <td>-0.970000</td>\n",
       "      <td>-0.77</td>\n",
       "      <td>0.29</td>\n",
       "      <td>-0.07</td>\n",
       "      <td>0.31</td>\n",
       "      <td>1.07</td>\n",
       "      <td>-0.61</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 13</th>\n",
       "      <td>7.56</td>\n",
       "      <td>6.470000</td>\n",
       "      <td>4.76</td>\n",
       "      <td>-2.67</td>\n",
       "      <td>0.09</td>\n",
       "      <td>0.40</td>\n",
       "      <td>1.12</td>\n",
       "      <td>-0.65</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 14</th>\n",
       "      <td>14.70</td>\n",
       "      <td>13.490000</td>\n",
       "      <td>10.77</td>\n",
       "      <td>-6.39</td>\n",
       "      <td>0.09</td>\n",
       "      <td>0.40</td>\n",
       "      <td>1.12</td>\n",
       "      <td>-0.65</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 15</th>\n",
       "      <td>21.84</td>\n",
       "      <td>20.510000</td>\n",
       "      <td>16.77</td>\n",
       "      <td>-10.11</td>\n",
       "      <td>0.09</td>\n",
       "      <td>0.40</td>\n",
       "      <td>1.12</td>\n",
       "      <td>-0.65</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>w 16</th>\n",
       "      <td>28.98</td>\n",
       "      <td>27.540001</td>\n",
       "      <td>22.77</td>\n",
       "      <td>-13.83</td>\n",
       "      <td>0.09</td>\n",
       "      <td>0.40</td>\n",
       "      <td>1.12</td>\n",
       "      <td>-0.65</td>\n",
       "      <td>-0.78</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      2 warstwy  3 warstwy  4 warstwy  5 warstwy  6 warstwy  7 warstwy  \\\n",
       "w 0        0.20  -0.770000      -0.77       0.29      -0.07       0.30   \n",
       "w 1        0.22  -0.750000      -0.77       0.29      -0.07       0.30   \n",
       "w 2        0.21  -0.760000      -0.77       0.29      -0.07       0.30   \n",
       "w 3        0.07  -0.900000      -0.77       0.29      -0.07       0.30   \n",
       "w 4       -0.17  -0.970000      -0.77       0.29      -0.07       0.30   \n",
       "w 5       -0.40  -0.970000      -0.77       0.29      -0.07       0.31   \n",
       "w 6       -0.67  -0.970000      -0.77       0.29      -0.07       0.30   \n",
       "w 7       -0.40  -0.970000      -0.77       0.29      -0.07       0.30   \n",
       "w 8        0.32  -0.650000      -0.77       0.29      -0.07       0.30   \n",
       "w 9        2.56   1.550000       0.56      -0.06       0.09       0.40   \n",
       "w 10      -2.34  -0.970000      -0.77       0.29      -0.07       0.32   \n",
       "w 11      -5.58  -0.970000      -0.77       0.29      -0.07       0.31   \n",
       "w 12      -8.31  -0.970000      -0.77       0.29      -0.07       0.31   \n",
       "w 13       7.56   6.470000       4.76      -2.67       0.09       0.40   \n",
       "w 14      14.70  13.490000      10.77      -6.39       0.09       0.40   \n",
       "w 15      21.84  20.510000      16.77     -10.11       0.09       0.40   \n",
       "w 16      28.98  27.540001      22.77     -13.83       0.09       0.40   \n",
       "\n",
       "      8 warstwy  9 warstwy  10 warstwy  \n",
       "w 0        1.07      -0.60       -0.78  \n",
       "w 1        1.07      -0.60       -0.78  \n",
       "w 2        1.07      -0.60       -0.78  \n",
       "w 3        1.07      -0.60       -0.78  \n",
       "w 4        1.07      -0.60       -0.78  \n",
       "w 5        1.07      -0.61       -0.78  \n",
       "w 6        1.07      -0.60       -0.78  \n",
       "w 7        1.07      -0.60       -0.78  \n",
       "w 8        1.07      -0.60       -0.78  \n",
       "w 9        1.12      -0.65       -0.78  \n",
       "w 10       1.07      -0.61       -0.78  \n",
       "w 11       1.07      -0.61       -0.78  \n",
       "w 12       1.07      -0.61       -0.78  \n",
       "w 13       1.12      -0.65       -0.78  \n",
       "w 14       1.12      -0.65       -0.78  \n",
       "w 15       1.12      -0.65       -0.78  \n",
       "w 16       1.12      -0.65       -0.78  "
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "representations = []\n",
    "max_layers = 11\n",
    "for i in range(2, max_layers):\n",
    "    pyg.seed_everything(222)\n",
    "    sage_model = pyg.nn.GraphSAGE(\n",
    "        in_channels=1, \n",
    "        out_channels=1, \n",
    "        normalize=False, \n",
    "        num_layers=i,\n",
    "        hidden_channels=1,\n",
    "        project=True,\n",
    "        aggr='mean')\n",
    "    with th.no_grad():\n",
    "        out = sage_model(g.x, g.edge_index)\n",
    "    representations.append(out.squeeze().numpy().round(2))\n",
    "\n",
    "representations_matrix = np.vstack(representations).T\n",
    "representations_df = pd.DataFrame(\n",
    "    representations_matrix, \n",
    "    index=[f\"w {i}\" for i in range(g.x.size(0))], \n",
    "    columns=[f\"{i} warstwy\" for i in range(2, max_layers)])\n",
    "representations_df.round(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(2.83)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Wyznaczenie optymalnej ilości warstw splotu, kierując się heurystyką\n",
    "# logarytmu liczby wierzchołków\n",
    "np.log(g.num_nodes).round(2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Wpływ nadmiernego wygładzania na klasyfikację wierzchołków\n",
    "\n",
    "W poniższym przykładzie posłużymy się grafem \"Karate Club\" do analizy wpływu nadmiernego wygładzania na jakość predykcji.\n",
    "Zbadamy wpływ ilości warstw splotu na \n",
    "1. Trafność predykcji;\n",
    "2. Wektory osadzenia i reprezentacje wierzchołków."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "karate = pyg.datasets.KarateClub()[0]\n",
    "pyg.seed_everything(123)\n",
    "splitter = pyg.transforms.RandomNodeSplit(split='train_rest', num_splits=1, num_val=7, num_test=7,)\n",
    "karate_split = splitter(karate)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "trening, liczba obiektów w klasach:\n",
      "1    7\n",
      "0    6\n",
      "3    4\n",
      "2    3\n",
      "Name: count, dtype: int64\n",
      "walidacja, liczba obiektów w klasach:\n",
      "0    5\n",
      "1    2\n",
      "Name: count, dtype: int64\n",
      "test, liczba obiektów w klasach:\n",
      "1    3\n",
      "0    2\n",
      "3    1\n",
      "2    1\n",
      "Name: count, dtype: int64\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/tmp/ipykernel_67648/437053122.py:3: FutureWarning: pandas.value_counts is deprecated and will be removed in a future version. Use pd.Series(obj).value_counts() instead.\n",
      "  cls_counts = pd.value_counts(karate_split.y[mask].numpy())\n"
     ]
    }
   ],
   "source": [
    "for name, mask in [('trening', karate_split.train_mask), ('walidacja', karate_split.val_mask), ('test', karate_split.test_mask)]:\n",
    "    print(f\"{name}, liczba obiektów w klasach:\")\n",
    "    cls_counts = pd.value_counts(karate_split.y[mask].numpy())\n",
    "    print(cls_counts)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "f1_scores = {}\n",
    "embeddings = {}\n",
    "losses = {}\n",
    "\n",
    "depths = [2, 4, 6, 8, 10]\n",
    "epochs = 50\n",
    "\n",
    "n_cls = karate.y.unique().shape[0]\n",
    "\n",
    "for d in tqdm(depths):\n",
    "    pyg.seed_everything(123)\n",
    "    # Zbudowanie modelu GraphSAGE\n",
    "    # Wielowarstwowy splot grafowy\n",
    "    conv = pyg.nn.GraphSAGE(\n",
    "        in_channels=karate.x.shape[1], \n",
    "        out_channels=8, \n",
    "        hidden_channels=16,\n",
    "        normalize=True,\n",
    "        aggr='mean',\n",
    "        num_layers=d)\n",
    "    # Złożenie modelu predykcyjnego - splot + \"głowa\" predykcyjna\n",
    "    model = pyg.nn.Sequential('x, edge_index', [\n",
    "        (conv, 'x, edge_index -> x'),\n",
    "        th.nn.ReLU(inplace=True),\n",
    "        (th.nn.Linear(8, n_cls), 'x -> x'),\n",
    "    ])\n",
    "    optimizer = th.optim.Adam(model.parameters(), lr=0.01)\n",
    "    model.train()\n",
    "    loss_per_epoch = []\n",
    "    # Proces uczenia\n",
    "    for epoch in tqdm(range(epochs)):\n",
    "        optimizer.zero_grad()\n",
    "\n",
    "        yhat = model(karate_split.x, karate_split.edge_index)[karate_split.train_mask]\n",
    "        y = karate.y[karate_split.train_mask]\n",
    "        loss = th.nn.functional.cross_entropy(yhat, y)\n",
    "        loss.backward()\n",
    "        optimizer.step()\n",
    "        loss_per_epoch.append(loss.item())\n",
    "    losses[d] = loss_per_epoch\n",
    "    model.eval()\n",
    "\n",
    "    # Ewaluacja modelu - zapisanie wyników i wektorów osadzenia\n",
    "    with th.no_grad():\n",
    "        yhat = model(karate_split.x, karate_split.edge_index)\n",
    "        embedding = conv(karate_split.x, karate_split.edge_index)\n",
    "        embeddings[d] = embedding.numpy()\n",
    "\n",
    "        yhat_test = yhat[karate_split.test_mask]\n",
    "        y_test = karate_split.y[karate_split.test_mask]\n",
    "        f1 = tm.functional.f1_score(\n",
    "            yhat_test, \n",
    "            y_test, \n",
    "            num_classes=n_cls, \n",
    "            average='weighted', \n",
    "            task='multiclass')\n",
    "        f1_scores[d] =f1.item()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>F1</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>głębokość</th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0.530612</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0.800000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>0.795918</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>0.428571</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>10</th>\n",
       "      <td>0.257143</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "                 F1\n",
       "głębokość          \n",
       "2          0.530612\n",
       "4          0.800000\n",
       "6          0.795918\n",
       "8          0.428571\n",
       "10         0.257143"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pd.DataFrame(f1_scores.items(), columns=['głębokość', 'F1']).set_index('głębokość')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 84708 characters]",
      "text/plain": [
       "<Figure size 511.811x787.402 with 6 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "num_plots = len(embeddings)\n",
    "fig, axes = plt.subplots(3, 2, figsize=vis.image_size_in_cm(13, 20))\n",
    "axes = axes.flatten()\n",
    "\n",
    "for i, (depth, embeds) in enumerate(embeddings.items()):\n",
    "    depth_embed = PCA(n_components=2).fit_transform(embeds)\n",
    "    ax = axes[i]\n",
    "    ax.scatter(depth_embed[:, 0], depth_embed[:, 1], c=karate.y.numpy(), cmap='gray')\n",
    "    ax.set_title(f\"Głębokość GraphSAGE: {depth}\")\n",
    "    vis.change_plot_comma_separator(ax, x_change=True, y_change=True)\n",
    "\n",
    "\n",
    "# Hide the last subplot\n",
    "axes[-1].axis('off')\n",
    "\n",
    "handles = [plt.Line2D([0], [0], marker='o', color='w', markerfacecolor=plt.cm.gray(i/n_cls), markersize=10, label=f'Klasa {i}') for i in range(n_cls)]\n",
    "fig.legend(handles=handles, loc='lower right')\n",
    "plt.tight_layout()\n",
    "\n",
    "fig.savefig(IMG_PATH / 'rys_5.25.tiff', format='tiff', pil_kwargs={'compression': 'tiff_lzw'})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "np.float64(3.53)"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "\"\"\"Wyznaczenie optymalnej liczby warstw splotu za pomocą heurystyki\n",
    "logarytmu liczby wierzchołków\"\"\"\n",
    "\n",
    "np.log(karate.num_nodes).round(2)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "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.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
