{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "initial_id",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Hit:1 http://archive.ubuntu.com/ubuntu jammy InRelease\n",
      "Get:2 http://archive.ubuntu.com/ubuntu jammy-updates InRelease [128 kB]      \n",
      "Hit:3 https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2204/x86_64  InRelease\n",
      "Get:4 http://security.ubuntu.com/ubuntu jammy-security InRelease [129 kB]    \n",
      "Hit:5 http://archive.ubuntu.com/ubuntu jammy-backports InRelease    \n",
      "Fetched 257 kB in 1s (347 kB/s)    \n",
      "Reading package lists... Done\n",
      "Reading package lists... Done\n",
      "Building dependency tree... Done\n",
      "Reading state information... Done\n",
      "graphviz is already the newest version (2.42.2-6ubuntu0.1).\n",
      "0 upgraded, 0 newly installed, 0 to remove and 121 not upgraded.\n",
      "Requirement already satisfied: tensorflow in /usr/local/lib/python3.11/dist-packages (2.14.0)\n",
      "Requirement already satisfied: absl-py>=1.0.0 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (2.0.0)\n",
      "Requirement already satisfied: astunparse>=1.6.0 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (1.6.3)\n",
      "Requirement already satisfied: flatbuffers>=23.5.26 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (23.5.26)\n",
      "Requirement already satisfied: gast!=0.5.0,!=0.5.1,!=0.5.2,>=0.2.1 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (0.5.4)\n",
      "Requirement already satisfied: google-pasta>=0.1.1 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (0.2.0)\n",
      "Requirement already satisfied: h5py>=2.9.0 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (3.9.0)\n",
      "Requirement already satisfied: libclang>=13.0.0 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (16.0.6)\n",
      "Requirement already satisfied: ml-dtypes==0.2.0 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (0.2.0)\n",
      "Requirement already satisfied: numpy>=1.23.5 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (1.26.0)\n",
      "Requirement already satisfied: opt-einsum>=2.3.2 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (3.3.0)\n",
      "Requirement already satisfied: packaging in /usr/local/lib/python3.11/dist-packages (from tensorflow) (23.1)\n",
      "Requirement already satisfied: protobuf!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev,>=3.20.3 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (4.24.3)\n",
      "Requirement already satisfied: setuptools in /usr/local/lib/python3.11/dist-packages (from tensorflow) (68.2.2)\n",
      "Requirement already satisfied: six>=1.12.0 in /usr/lib/python3/dist-packages (from tensorflow) (1.16.0)\n",
      "Requirement already satisfied: termcolor>=1.1.0 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (2.3.0)\n",
      "Requirement already satisfied: typing-extensions>=3.6.6 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (4.8.0)\n",
      "Requirement already satisfied: wrapt<1.15,>=1.11.0 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (1.14.1)\n",
      "Requirement already satisfied: tensorflow-io-gcs-filesystem>=0.23.1 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (0.37.1)\n",
      "Requirement already satisfied: grpcio<2.0,>=1.24.3 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (1.58.0)\n",
      "Requirement already satisfied: tensorboard<2.15,>=2.14 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (2.14.0)\n",
      "Requirement already satisfied: tensorflow-estimator<2.15,>=2.14.0 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (2.14.0)\n",
      "Requirement already satisfied: keras<2.15,>=2.14.0 in /usr/local/lib/python3.11/dist-packages (from tensorflow) (2.14.0)\n",
      "Requirement already satisfied: wheel<1.0,>=0.23.0 in /usr/local/lib/python3.11/dist-packages (from astunparse>=1.6.0->tensorflow) (0.41.2)\n",
      "Requirement already satisfied: google-auth<3,>=1.6.3 in /usr/local/lib/python3.11/dist-packages (from tensorboard<2.15,>=2.14->tensorflow) (2.23.1)\n",
      "Requirement already satisfied: google-auth-oauthlib<1.1,>=0.5 in /usr/local/lib/python3.11/dist-packages (from tensorboard<2.15,>=2.14->tensorflow) (1.0.0)\n",
      "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.11/dist-packages (from tensorboard<2.15,>=2.14->tensorflow) (3.4.4)\n",
      "Requirement already satisfied: requests<3,>=2.21.0 in /usr/local/lib/python3.11/dist-packages (from tensorboard<2.15,>=2.14->tensorflow) (2.31.0)\n",
      "Requirement already satisfied: tensorboard-data-server<0.8.0,>=0.7.0 in /usr/local/lib/python3.11/dist-packages (from tensorboard<2.15,>=2.14->tensorflow) (0.7.1)\n",
      "Requirement already satisfied: werkzeug>=1.0.1 in /usr/local/lib/python3.11/dist-packages (from tensorboard<2.15,>=2.14->tensorflow) (2.3.7)\n",
      "Requirement already satisfied: cachetools<6.0,>=2.0.0 in /usr/local/lib/python3.11/dist-packages (from google-auth<3,>=1.6.3->tensorboard<2.15,>=2.14->tensorflow) (5.3.1)\n",
      "Requirement already satisfied: pyasn1-modules>=0.2.1 in /usr/local/lib/python3.11/dist-packages (from google-auth<3,>=1.6.3->tensorboard<2.15,>=2.14->tensorflow) (0.3.0)\n",
      "Requirement already satisfied: rsa<5,>=3.1.4 in /usr/local/lib/python3.11/dist-packages (from google-auth<3,>=1.6.3->tensorboard<2.15,>=2.14->tensorflow) (4.9)\n",
      "Requirement already satisfied: urllib3>=2.0.5 in /usr/local/lib/python3.11/dist-packages (from google-auth<3,>=1.6.3->tensorboard<2.15,>=2.14->tensorflow) (2.0.5)\n",
      "Requirement already satisfied: requests-oauthlib>=0.7.0 in /usr/local/lib/python3.11/dist-packages (from google-auth-oauthlib<1.1,>=0.5->tensorboard<2.15,>=2.14->tensorflow) (1.3.1)\n",
      "Requirement already satisfied: charset-normalizer<4,>=2 in /usr/local/lib/python3.11/dist-packages (from requests<3,>=2.21.0->tensorboard<2.15,>=2.14->tensorflow) (3.2.0)\n",
      "Requirement already satisfied: idna<4,>=2.5 in /usr/local/lib/python3.11/dist-packages (from requests<3,>=2.21.0->tensorboard<2.15,>=2.14->tensorflow) (3.4)\n",
      "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.11/dist-packages (from requests<3,>=2.21.0->tensorboard<2.15,>=2.14->tensorflow) (2023.7.22)\n",
      "Requirement already satisfied: MarkupSafe>=2.1.1 in /usr/local/lib/python3.11/dist-packages (from werkzeug>=1.0.1->tensorboard<2.15,>=2.14->tensorflow) (2.1.3)\n",
      "Requirement already satisfied: pyasn1<0.6.0,>=0.4.6 in /usr/local/lib/python3.11/dist-packages (from pyasn1-modules>=0.2.1->google-auth<3,>=1.6.3->tensorboard<2.15,>=2.14->tensorflow) (0.5.0)\n",
      "Requirement already satisfied: oauthlib>=3.0.0 in /usr/lib/python3/dist-packages (from requests-oauthlib>=0.7.0->google-auth-oauthlib<1.1,>=0.5->tensorboard<2.15,>=2.14->tensorflow) (3.2.0)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: numpy in /usr/local/lib/python3.11/dist-packages (1.26.0)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: pandas in /usr/local/lib/python3.11/dist-packages (2.2.3)\n",
      "Requirement already satisfied: numpy>=1.23.2 in /usr/local/lib/python3.11/dist-packages (from pandas) (1.26.0)\n",
      "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.11/dist-packages (from pandas) (2.8.2)\n",
      "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.11/dist-packages (from pandas) (2024.2)\n",
      "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.11/dist-packages (from pandas) (2024.2)\n",
      "Requirement already satisfied: six>=1.5 in /usr/lib/python3/dist-packages (from python-dateutil>=2.8.2->pandas) (1.16.0)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: keras in /usr/local/lib/python3.11/dist-packages (2.14.0)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: scikit-learn in /usr/local/lib/python3.11/dist-packages (1.5.2)\n",
      "Requirement already satisfied: numpy>=1.19.5 in /usr/local/lib/python3.11/dist-packages (from scikit-learn) (1.26.0)\n",
      "Requirement already satisfied: scipy>=1.6.0 in /usr/local/lib/python3.11/dist-packages (from scikit-learn) (1.14.1)\n",
      "Requirement already satisfied: joblib>=1.2.0 in /usr/local/lib/python3.11/dist-packages (from scikit-learn) (1.4.2)\n",
      "Requirement already satisfied: threadpoolctl>=3.1.0 in /usr/local/lib/python3.11/dist-packages (from scikit-learn) (3.5.0)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: matplotlib in /usr/local/lib/python3.11/dist-packages (3.8.0)\n",
      "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (1.1.1)\n",
      "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (0.11.0)\n",
      "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (4.42.1)\n",
      "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (1.4.5)\n",
      "Requirement already satisfied: numpy<2,>=1.21 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (1.26.0)\n",
      "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (23.1)\n",
      "Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (10.0.1)\n",
      "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (3.2.0)\n",
      "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.11/dist-packages (from matplotlib) (2.8.2)\n",
      "Requirement already satisfied: six>=1.5 in /usr/lib/python3/dist-packages (from python-dateutil>=2.7->matplotlib) (1.16.0)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: joblib in /usr/local/lib/python3.11/dist-packages (1.4.2)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: pyarrow in /usr/local/lib/python3.11/dist-packages (18.1.0)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: fastparquet in /usr/local/lib/python3.11/dist-packages (2024.11.0)\n",
      "Requirement already satisfied: pandas>=1.5.0 in /usr/local/lib/python3.11/dist-packages (from fastparquet) (2.2.3)\n",
      "Requirement already satisfied: numpy in /usr/local/lib/python3.11/dist-packages (from fastparquet) (1.26.0)\n",
      "Requirement already satisfied: cramjam>=2.3 in /usr/local/lib/python3.11/dist-packages (from fastparquet) (2.9.0)\n",
      "Requirement already satisfied: fsspec in /usr/local/lib/python3.11/dist-packages (from fastparquet) (2024.10.0)\n",
      "Requirement already satisfied: packaging in /usr/local/lib/python3.11/dist-packages (from fastparquet) (23.1)\n",
      "Requirement already satisfied: python-dateutil>=2.8.2 in /usr/local/lib/python3.11/dist-packages (from pandas>=1.5.0->fastparquet) (2.8.2)\n",
      "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.11/dist-packages (from pandas>=1.5.0->fastparquet) (2024.2)\n",
      "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.11/dist-packages (from pandas>=1.5.0->fastparquet) (2024.2)\n",
      "Requirement already satisfied: six>=1.5 in /usr/lib/python3/dist-packages (from python-dateutil>=2.8.2->pandas>=1.5.0->fastparquet) (1.16.0)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: scipy in /usr/local/lib/python3.11/dist-packages (1.14.1)\n",
      "Requirement already satisfied: numpy<2.3,>=1.23.5 in /usr/local/lib/python3.11/dist-packages (from scipy) (1.26.0)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: seaborn in /usr/local/lib/python3.11/dist-packages (0.13.2)\n",
      "Requirement already satisfied: numpy!=1.24.0,>=1.20 in /usr/local/lib/python3.11/dist-packages (from seaborn) (1.26.0)\n",
      "Requirement already satisfied: pandas>=1.2 in /usr/local/lib/python3.11/dist-packages (from seaborn) (2.2.3)\n",
      "Requirement already satisfied: matplotlib!=3.6.1,>=3.4 in /usr/local/lib/python3.11/dist-packages (from seaborn) (3.8.0)\n",
      "Requirement already satisfied: contourpy>=1.0.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.1.1)\n",
      "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (0.11.0)\n",
      "Requirement already satisfied: fonttools>=4.22.0 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (4.42.1)\n",
      "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (1.4.5)\n",
      "Requirement already satisfied: packaging>=20.0 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (23.1)\n",
      "Requirement already satisfied: pillow>=6.2.0 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (10.0.1)\n",
      "Requirement already satisfied: pyparsing>=2.3.1 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (3.2.0)\n",
      "Requirement already satisfied: python-dateutil>=2.7 in /usr/local/lib/python3.11/dist-packages (from matplotlib!=3.6.1,>=3.4->seaborn) (2.8.2)\n",
      "Requirement already satisfied: pytz>=2020.1 in /usr/local/lib/python3.11/dist-packages (from pandas>=1.2->seaborn) (2024.2)\n",
      "Requirement already satisfied: tzdata>=2022.7 in /usr/local/lib/python3.11/dist-packages (from pandas>=1.2->seaborn) (2024.2)\n",
      "Requirement already satisfied: six>=1.5 in /usr/lib/python3/dist-packages (from python-dateutil>=2.7->matplotlib!=3.6.1,>=3.4->seaborn) (1.16.0)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: tqdm in /usr/local/lib/python3.11/dist-packages (4.67.1)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: pydot in /usr/local/lib/python3.11/dist-packages (3.0.3)\n",
      "Requirement already satisfied: pyparsing>=3.0.9 in /usr/local/lib/python3.11/dist-packages (from pydot) (3.2.0)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: tensorflow-io in /usr/local/lib/python3.11/dist-packages (0.37.1)\n",
      "Requirement already satisfied: tensorflow-io-gcs-filesystem==0.37.1 in /usr/local/lib/python3.11/dist-packages (from tensorflow-io) (0.37.1)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n",
      "Requirement already satisfied: tensorflow-addons in /usr/local/lib/python3.11/dist-packages (0.23.0)\n",
      "Requirement already satisfied: packaging in /usr/local/lib/python3.11/dist-packages (from tensorflow-addons) (23.1)\n",
      "Requirement already satisfied: typeguard<3.0.0,>=2.7 in /usr/local/lib/python3.11/dist-packages (from tensorflow-addons) (2.13.3)\n",
      "\u001B[33mWARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv\u001B[0m\u001B[33m\n",
      "\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m A new release of pip is available: \u001B[0m\u001B[31;49m23.2.1\u001B[0m\u001B[39;49m -> \u001B[0m\u001B[32;49m24.3.1\u001B[0m\n",
      "\u001B[1m[\u001B[0m\u001B[34;49mnotice\u001B[0m\u001B[1;39;49m]\u001B[0m\u001B[39;49m To update, run: \u001B[0m\u001B[32;49mpython3 -m pip install --upgrade pip\u001B[0m\n"
     ]
    }
   ],
   "source": [
    "!apt-get update\n",
    "!apt-get install graphviz -y\n",
    "\n",
    "!pip install tensorflow\n",
    "!pip install numpy\n",
    "!pip install pandas\n",
    "\n",
    "!pip install keras\n",
    "!pip install scikit-learn\n",
    "!pip install matplotlib\n",
    "!pip install joblib\n",
    "!pip install pyarrow\n",
    "!pip install fastparquet\n",
    "!pip install scipy\n",
    "!pip install seaborn\n",
    "!pip install tqdm\n",
    "!pip install pydot\n",
    "!pip install tensorflow-io\n",
    "!pip install tensorflow-addons"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "a467d3f0dfd9beab",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-12-08 14:47:22.527266: E tensorflow/compiler/xla/stream_executor/cuda/cuda_dnn.cc:9342] Unable to register cuDNN factory: Attempting to register factory for plugin cuDNN when one has already been registered\n",
      "2024-12-08 14:47:22.527310: E tensorflow/compiler/xla/stream_executor/cuda/cuda_fft.cc:609] Unable to register cuFFT factory: Attempting to register factory for plugin cuFFT when one has already been registered\n",
      "2024-12-08 14:47:22.527375: E tensorflow/compiler/xla/stream_executor/cuda/cuda_blas.cc:1518] Unable to register cuBLAS factory: Attempting to register factory for plugin cuBLAS when one has already been registered\n",
      "2024-12-08 14:47:22.536734: I tensorflow/core/platform/cpu_feature_guard.cc:182] This TensorFlow binary is optimized to use available CPU instructions in performance-critical operations.\n",
      "To enable the following instructions: AVX2 FMA, in other operations, rebuild TensorFlow with the appropriate compiler flags.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Keras version: 2.14.0\n",
      "TensorFlow version: 2.14.0\n",
      "TensorFlow version: 2.14.0\n",
      "CUDA available: True\n",
      "GPU devices: [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]\n",
      "1 Physical GPUs, 1 Logical GPUs\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-12-08 14:47:25.041282: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1886] Created device /job:localhost/replica:0/task:0/device:GPU:0 with 43404 MB memory:  -> device: 0, name: NVIDIA L40, pci bus id: 0000:81:00.0, compute capability: 8.9\n"
     ]
    }
   ],
   "source": [
    "import os\n",
    "import tensorflow as tf\n",
    "import keras\n",
    "\n",
    "# Print versions and system information\n",
    "print(f\"Keras version: {keras.__version__}\")\n",
    "print(f\"TensorFlow version: {tf.__version__}\")\n",
    "print(f\"CUDA available: {tf.test.is_built_with_cuda()}\")\n",
    "print(f\"GPU devices: {tf.config.list_physical_devices('GPU')}\")\n",
    "\n",
    "# GPU memory configuration\n",
    "gpus = tf.config.experimental.list_physical_devices('GPU')\n",
    "if gpus:\n",
    "    try:\n",
    "        for gpu in gpus:\n",
    "            tf.config.experimental.set_memory_growth(gpu, True)\n",
    "\n",
    "        logical_gpus = tf.config.experimental.list_logical_devices('GPU')\n",
    "        print(len(gpus), \"Physical GPUs,\", len(logical_gpus), \"Logical GPUs\")\n",
    "    except RuntimeError as e:\n",
    "        print(e)\n",
    "\n",
    "# Reduce TensorFlow logging verbosity\n",
    "os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'\n",
    "\n",
    "# Set global precision policy\n",
    "tf.keras.mixed_precision.set_global_policy('float32')\n",
    "\n",
    "# Uncomment to set seed for reproducibility\n",
    "#tf.random.set_seed(42)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "c0155cde4740b0a3",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/usr/local/lib/python3.11/dist-packages/tensorflow_addons/utils/tfa_eol_msg.py:23: UserWarning: \n",
      "\n",
      "TensorFlow Addons (TFA) has ended development and introduction of new features.\n",
      "TFA has entered a minimal maintenance and release mode until a planned end of life in May 2024.\n",
      "Please modify downstream libraries to take dependencies from other repositories in our TensorFlow community (e.g. Keras, Keras-CV, and Keras-NLP). \n",
      "\n",
      "For more information see: https://github.com/tensorflow/addons/issues/2807 \n",
      "\n",
      "  warnings.warn(\n"
     ]
    }
   ],
   "source": [
    "from datetime import datetime\n",
    "import os\n",
    "from typing import List\n",
    "\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "import matplotlib.pyplot as plt\n",
    "from sklearn.preprocessing import StandardScaler\n",
    "import tensorflow_addons as tfa\n",
    "import joblib\n",
    "import re\n",
    "\n",
    "# Set random state value (None for non-deterministic behavior)\n",
    "random_state_value = None\n",
    "\n",
    "# Create execution timestamp for model versioning and logging\n",
    "execute_name = datetime.now().strftime(\"%Y-%m-%d_%H-%M\")\n",
    "\n",
    "# Define directory paths\n",
    "base_project_dir = './'\n",
    "data_dir = '../../sources/'\n",
    "models_project_dir = base_project_dir\n",
    "\n",
    "# Create required directories if they don't exist\n",
    "os.makedirs(base_project_dir, exist_ok=True)\n",
    "os.makedirs(models_project_dir, exist_ok=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "1347fb59-50cc-4aa8-b805-ca9403037af5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def clean_column_name(name: str) -> str:\n",
    "    \"\"\"\n",
    "    Cleans column names by removing special characters and converting to snake_case with abbreviations.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    name : str\n",
    "        Column name to clean\n",
    "\n",
    "    Returns\n",
    "    -------\n",
    "    str\n",
    "        Cleaned column name\n",
    "    \"\"\"\n",
    "    # Remove special characters\n",
    "    name = re.sub(r'[^a-zA-Z0-9\\s]', '', name)\n",
    "\n",
    "    # Convert to snake_case\n",
    "    name = name.lower().replace(' ', '_')\n",
    "\n",
    "    # Common abbreviations mapping\n",
    "    abbreviations = {\n",
    "        'production': 'prod',\n",
    "        'percentage': 'pct',\n",
    "        'hectare': 'ha',\n",
    "        'tonnes': 't',\n",
    "        'litres': 'l',\n",
    "        'minimum': 'min',\n",
    "        'maximum': 'max',\n",
    "        'average': 'avg'\n",
    "    }\n",
    "\n",
    "    for full, abbr in abbreviations.items():\n",
    "        name = name.replace(full, abbr)\n",
    "\n",
    "    return name\n",
    "\n",
    "\n",
    "def clean_column_names(df: pd.DataFrame) -> List[str]:\n",
    "    \"\"\"\n",
    "    Cleans all column names in a DataFrame.\n",
    "\n",
    "    Parameters\n",
    "    ----------\n",
    "    df : pd.DataFrame\n",
    "        DataFrame whose columns need cleaning\n",
    "\n",
    "    Returns\n",
    "    -------\n",
    "    list\n",
    "        List of cleaned column names\n",
    "    \"\"\"\n",
    "    new_columns = []\n",
    "\n",
    "    for col in df.columns:\n",
    "        # Extract variety patterns using regex\n",
    "        varieties = re.findall(r'([a-z]+)_([a-z_]+)', col)\n",
    "        if varieties:\n",
    "            new_columns.append(f\"{varieties[0][0]}_{varieties[0][1]}\")\n",
    "        else:\n",
    "            new_columns.append(col)\n",
    "\n",
    "    return new_columns"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "4da1f1bb67343e3e",
   "metadata": {},
   "outputs": [],
   "source": [
    "def save_plot(plt, title, output_dir=f'{base_project_dir}/{execute_name}_plots'):\n",
    "    os.makedirs(output_dir, exist_ok=True)\n",
    "    filename = \"\".join(x for x in title if x.isalnum() or x in [' ', '-', '_']).rstrip()\n",
    "    filename = filename.replace(' ', '_').lower()\n",
    "    filepath = os.path.join(output_dir, f\"{filename}.png\")\n",
    "    plt.savefig(filepath, bbox_inches='tight', dpi=300)\n",
    "    print(f\"Plot salvato come: {filepath}\")\n",
    "\n",
    "\n",
    "def encode_techniques(df, mapping_path=f'{data_dir}technique_mapping.joblib'):\n",
    "    if not os.path.exists(mapping_path):\n",
    "        raise FileNotFoundError(f\"Mapping not found at {mapping_path}. Run create_technique_mapping first.\")\n",
    "\n",
    "    technique_mapping = joblib.load(mapping_path)\n",
    "\n",
    "    tech_columns = [col for col in df.columns if col.endswith('_tech')]\n",
    "\n",
    "    # Mapping apply to all tech columns\n",
    "    for col in tech_columns:\n",
    "        df[col] = df[col].str.lower().map(technique_mapping).fillna(0).astype(int)\n",
    "\n",
    "    return df\n",
    "\n",
    "\n",
    "def decode_single_technique(technique_value, mapping_path=f'{data_dir}technique_mapping.joblib'):\n",
    "    if not os.path.exists(mapping_path):\n",
    "        raise FileNotFoundError(f\"Mapping not found at {mapping_path}\")\n",
    "\n",
    "    technique_mapping = joblib.load(mapping_path)\n",
    "    reverse_mapping = {v: k for k, v in technique_mapping.items()}\n",
    "    reverse_mapping[0] = ''\n",
    "\n",
    "    return reverse_mapping.get(technique_value, '')\n",
    "\n",
    "\n",
    "def prepare_comparison_data(simulated_data, olive_varieties):\n",
    "\n",
    "    df = simulated_data.copy()\n",
    "\n",
    "    df.columns = clean_column_names(df)\n",
    "    df = encode_techniques(df)\n",
    "\n",
    "    all_varieties = olive_varieties['Varietà di Olive'].unique()\n",
    "    varieties = [clean_column_name(variety) for variety in all_varieties]\n",
    "    comparison_data = []\n",
    "\n",
    "    for variety in varieties:\n",
    "        olive_prod_col = next((col for col in df.columns if col.startswith(f'{variety}_') and col.endswith('_olive_prod')), None)\n",
    "        oil_prod_col = next((col for col in df.columns if col.startswith(f'{variety}_') and col.endswith('_avg_oil_prod')), None)\n",
    "        tech_col = next((col for col in df.columns if col.startswith(f'{variety}_') and col.endswith('_tech')), None)\n",
    "        water_need_col = next((col for col in df.columns if col.startswith(f'{variety}_') and col.endswith('_water_need')), None)\n",
    "\n",
    "        if olive_prod_col and oil_prod_col and tech_col and water_need_col:\n",
    "            variety_data = df[[olive_prod_col, oil_prod_col, tech_col, water_need_col]]\n",
    "            variety_data = variety_data[variety_data[tech_col] != 0]  # Esclude le righe dove la tecnica è 0\n",
    "\n",
    "            if not variety_data.empty:\n",
    "                avg_olive_prod = pd.to_numeric(variety_data[olive_prod_col], errors='coerce').mean()\n",
    "                avg_oil_prod = pd.to_numeric(variety_data[oil_prod_col], errors='coerce').mean()\n",
    "                avg_water_need = pd.to_numeric(variety_data[water_need_col], errors='coerce').mean()\n",
    "                efficiency = avg_oil_prod / avg_olive_prod if avg_olive_prod > 0 else 0\n",
    "                water_efficiency = avg_oil_prod / avg_water_need if avg_water_need > 0 else 0\n",
    "\n",
    "                comparison_data.append({\n",
    "                    'Variety': variety,\n",
    "                    'Avg Olive Production (kg/ha)': avg_olive_prod,\n",
    "                    'Avg Oil Production (L/ha)': avg_oil_prod,\n",
    "                    'Avg Water Need (m³/ha)': avg_water_need,\n",
    "                    'Oil Efficiency (L/kg)': efficiency,\n",
    "                    'Water Efficiency (L oil/m³ water)': water_efficiency\n",
    "                })\n",
    "\n",
    "    return pd.DataFrame(comparison_data)\n",
    "\n",
    "\n",
    "def plot_variety_comparison(comparison_data, metric):\n",
    "    plt.figure(figsize=(12, 6))\n",
    "    bars = plt.bar(comparison_data['Variety'], comparison_data[metric])\n",
    "    plt.title(f'Comparison of {metric} across Olive Varieties')\n",
    "    plt.xlabel('Variety')\n",
    "    plt.ylabel(metric)\n",
    "    plt.xticks(rotation=45, ha='right')\n",
    "\n",
    "    for bar in bars:\n",
    "        height = bar.get_height()\n",
    "        plt.text(bar.get_x() + bar.get_width() / 2., height,\n",
    "                 f'{height:.2f}',\n",
    "                 ha='center', va='bottom')\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "    save_plot(plt, f'variety_comparison_{metric.lower().replace(\" \", \"_\").replace(\"/\", \"_\").replace(\"(\", \"\").replace(\")\", \"\")}')\n",
    "    plt.close()\n",
    "\n",
    "\n",
    "def plot_efficiency_vs_production(comparison_data):\n",
    "    plt.figure(figsize=(10, 6))\n",
    "\n",
    "    plt.scatter(comparison_data['Avg Olive Production (kg/ha)'],\n",
    "                comparison_data['Oil Efficiency (L/kg)'],\n",
    "                s=100)\n",
    "\n",
    "    for i, row in comparison_data.iterrows():\n",
    "        plt.annotate(row['Variety'],\n",
    "                     (row['Avg Olive Production (kg/ha)'], row['Oil Efficiency (L/kg)']),\n",
    "                     xytext=(5, 5), textcoords='offset points')\n",
    "\n",
    "    plt.title('Oil Efficiency vs Olive Production by Variety')\n",
    "    plt.xlabel('Average Olive Production (kg/ha)')\n",
    "    plt.ylabel('Oil Efficiency (L oil / kg olives)')\n",
    "    plt.tight_layout()\n",
    "    save_plot(plt, 'efficiency_vs_production')\n",
    "    plt.close()\n",
    "\n",
    "\n",
    "def plot_water_efficiency_vs_production(comparison_data):\n",
    "    plt.figure(figsize=(10, 6))\n",
    "\n",
    "    plt.scatter(comparison_data['Avg Olive Production (kg/ha)'],\n",
    "                comparison_data['Water Efficiency (L oil/m³ water)'],\n",
    "                s=100)\n",
    "\n",
    "    for i, row in comparison_data.iterrows():\n",
    "        plt.annotate(row['Variety'],\n",
    "                     (row['Avg Olive Production (kg/ha)'], row['Water Efficiency (L oil/m³ water)']),\n",
    "                     xytext=(5, 5), textcoords='offset points')\n",
    "\n",
    "    plt.title('Water Efficiency vs Olive Production by Variety')\n",
    "    plt.xlabel('Average Olive Production (kg/ha)')\n",
    "    plt.ylabel('Water Efficiency (L oil / m³ water)')\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "    save_plot(plt, 'water_efficiency_vs_production')\n",
    "    plt.close()\n",
    "\n",
    "\n",
    "def plot_water_need_vs_oil_production(comparison_data):\n",
    "    plt.figure(figsize=(10, 6))\n",
    "\n",
    "    plt.scatter(comparison_data['Avg Water Need (m³/ha)'],\n",
    "                comparison_data['Avg Oil Production (L/ha)'],\n",
    "                s=100)\n",
    "\n",
    "    for i, row in comparison_data.iterrows():\n",
    "        plt.annotate(row['Variety'],\n",
    "                     (row['Avg Water Need (m³/ha)'], row['Avg Oil Production (L/ha)']),\n",
    "                     xytext=(5, 5), textcoords='offset points')\n",
    "\n",
    "    plt.title('Oil Production vs Water Need by Variety')\n",
    "    plt.xlabel('Average Water Need (m³/ha)')\n",
    "    plt.ylabel('Average Oil Production (L/ha)')\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "    save_plot(plt, 'water_need_vs_oil_production')\n",
    "    plt.close()\n",
    "\n",
    "\n",
    "def analyze_by_technique(simulated_data, olive_varieties):\n",
    "\n",
    "    df = simulated_data.copy()\n",
    "\n",
    "    df.columns = clean_column_names(df)\n",
    "    df = encode_techniques(df)\n",
    "    all_varieties = olive_varieties['Varietà di Olive'].unique()\n",
    "    varieties = [clean_column_name(variety) for variety in all_varieties]\n",
    "\n",
    "    technique_data = []\n",
    "\n",
    "    for variety in varieties:\n",
    "        olive_prod_col = next((col for col in df.columns if col.startswith(f'{variety}_') and col.endswith('_olive_prod')), None)\n",
    "        oil_prod_col = next((col for col in df.columns if col.startswith(f'{variety}_') and col.endswith('_avg_oil_prod')), None)\n",
    "        tech_col = next((col for col in df.columns if col.startswith(f'{variety}_') and col.endswith('_tech')), None)\n",
    "        water_need_col = next((col for col in df.columns if col.startswith(f'{variety}_') and col.endswith('_water_need')), None)\n",
    "\n",
    "        if olive_prod_col and oil_prod_col and tech_col and water_need_col:\n",
    "            variety_data = df[[olive_prod_col, oil_prod_col, tech_col, water_need_col]]\n",
    "            variety_data = variety_data[variety_data[tech_col] != 0]\n",
    "\n",
    "            if not variety_data.empty:\n",
    "                for tech in variety_data[tech_col].unique():\n",
    "                    tech_data = variety_data[variety_data[tech_col] == tech]\n",
    "\n",
    "                    avg_olive_prod = pd.to_numeric(tech_data[olive_prod_col], errors='coerce').mean()\n",
    "                    avg_oil_prod = pd.to_numeric(tech_data[oil_prod_col], errors='coerce').mean()\n",
    "                    avg_water_need = pd.to_numeric(tech_data[water_need_col], errors='coerce').mean()\n",
    "\n",
    "                    efficiency = avg_oil_prod / avg_olive_prod if avg_olive_prod > 0 else 0\n",
    "                    water_efficiency = avg_oil_prod / avg_water_need if avg_water_need > 0 else 0\n",
    "\n",
    "                    technique_data.append({\n",
    "                        'Variety': variety,\n",
    "                        'Technique': tech,\n",
    "                        'Technique String': decode_single_technique(tech),\n",
    "                        'Avg Olive Production (kg/ha)': avg_olive_prod,\n",
    "                        'Avg Oil Production (L/ha)': avg_oil_prod,\n",
    "                        'Avg Water Need (m³/ha)': avg_water_need,\n",
    "                        'Oil Efficiency (L/kg)': efficiency,\n",
    "                        'Water Efficiency (L oil/m³ water)': water_efficiency\n",
    "                    })\n",
    "\n",
    "    return pd.DataFrame(technique_data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "9aa4bf176c4affb9",
   "metadata": {},
   "outputs": [],
   "source": [
    "def calculate_real_error(model, test_data, test_targets, scaler_y):\n",
    "\n",
    "    predictions = model.predict(test_data)\n",
    "\n",
    "    # Denormalize predictions and target values\n",
    "    predictions_real = scaler_y.inverse_transform(predictions)\n",
    "    targets_real = scaler_y.inverse_transform(test_targets)\n",
    "\n",
    "    # Calculate percentage error for each target\n",
    "    percentage_errors = []\n",
    "    absolute_errors = []\n",
    "\n",
    "    for i in range(predictions_real.shape[1]):\n",
    "        mae = np.mean(np.abs(predictions_real[:, i] - targets_real[:, i]))\n",
    "        mape = np.mean(np.abs((predictions_real[:, i] - targets_real[:, i]) / targets_real[:, i])) * 100\n",
    "        percentage_errors.append(mape)\n",
    "        absolute_errors.append(mae)\n",
    "\n",
    "    target_names = ['olive_prod', 'min_oil_prod', 'max_oil_prod', 'avg_oil_prod', 'total_water_need']\n",
    "\n",
    "    print(\"\\nErrori per target:\")\n",
    "    print(\"-\" * 50)\n",
    "    for i, target in enumerate(target_names):\n",
    "        print(f\"{target}:\")\n",
    "        print(f\"MAE assoluto: {absolute_errors[i]:.2f}\")\n",
    "        print(f\"Errore percentuale medio: {percentage_errors[i]:.2f}%\")\n",
    "        print(f\"Precisione: {100 - percentage_errors[i]:.2f}%\")\n",
    "        print(\"-\" * 50)\n",
    "\n",
    "    return percentage_errors, absolute_errors"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "b3ba2b96ba678389",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 73488 characters]",
      "text/plain": [
       "<Figure size 1200x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Plot salvato come: .//2024-12-08_14-47_plots/variety_comparison_avg_olive_production_kg_ha.png\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 66612 characters]",
      "text/plain": [
       "<Figure size 1200x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Plot salvato come: .//2024-12-08_14-47_plots/variety_comparison_avg_oil_production_l_ha.png\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 74092 characters]",
      "text/plain": [
       "<Figure size 1200x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Plot salvato come: .//2024-12-08_14-47_plots/variety_comparison_avg_water_need_m³_ha.png\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 56556 characters]",
      "text/plain": [
       "<Figure size 1200x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Plot salvato come: .//2024-12-08_14-47_plots/variety_comparison_oil_efficiency_l_kg.png\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 62988 characters]",
      "text/plain": [
       "<Figure size 1200x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Plot salvato come: .//2024-12-08_14-47_plots/variety_comparison_water_efficiency_l_oil_m³_water.png\n",
      "Plot salvato come: .//2024-12-08_14-47_plots/efficiency_vs_production.png\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 54396 characters]",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Plot salvato come: .//2024-12-08_14-47_plots/water_efficiency_vs_production.png\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 51700 characters]",
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Plot salvato come: .//2024-12-08_14-47_plots/water_need_vs_oil_production.png\n",
      "               Variety  Technique Technique String  \\\n",
      "0   nocellara_delletna          3     tradizionale   \n",
      "1   nocellara_delletna          1        intensiva   \n",
      "2   nocellara_delletna          2   superintensiva   \n",
      "3              leccino          1        intensiva   \n",
      "4              leccino          2   superintensiva   \n",
      "5              leccino          3     tradizionale   \n",
      "6             frantoio          2   superintensiva   \n",
      "7             frantoio          3     tradizionale   \n",
      "8             frantoio          1        intensiva   \n",
      "9             coratina          1        intensiva   \n",
      "10            coratina          2   superintensiva   \n",
      "11            coratina          3     tradizionale   \n",
      "12           taggiasca          3     tradizionale   \n",
      "13           taggiasca          2   superintensiva   \n",
      "14           taggiasca          1        intensiva   \n",
      "15           pendolino          1        intensiva   \n",
      "16           pendolino          2   superintensiva   \n",
      "17           pendolino          3     tradizionale   \n",
      "18            moraiolo          2   superintensiva   \n",
      "19            moraiolo          1        intensiva   \n",
      "20            moraiolo          3     tradizionale   \n",
      "\n",
      "    Avg Olive Production (kg/ha)  Avg Oil Production (L/ha)  \\\n",
      "0                    9564.638687                2088.362004   \n",
      "1                   13699.079622                2991.183032   \n",
      "2                   17826.710664                3892.059753   \n",
      "3                   16432.379678                3229.053194   \n",
      "4                   20528.499013                4033.942398   \n",
      "5                   10937.982122                2149.449585   \n",
      "6                   24621.040119                6047.876212   \n",
      "7                   13740.739760                3375.103688   \n",
      "8                   20550.900635                5047.942655   \n",
      "9                   16429.706879                4215.265516   \n",
      "10                  19164.700743                4916.649709   \n",
      "11                  12318.510310                3160.037128   \n",
      "12                   6839.506230                1381.247995   \n",
      "13                  16433.741502                3319.210170   \n",
      "14                  10968.603159                2215.371493   \n",
      "15                  13705.431414                2468.678455   \n",
      "16                  19183.689269                3455.879324   \n",
      "17                  10960.549241                1974.357984   \n",
      "18                  17793.971752                3885.415851   \n",
      "19                  13144.222436                2870.020002   \n",
      "20                   8765.195655                1913.745255   \n",
      "\n",
      "    Avg Water Need (m³/ha)  Oil Efficiency (L/kg)  \\\n",
      "0             32997.227891               0.218342   \n",
      "1             33079.012125               0.218349   \n",
      "2             33118.708645               0.218327   \n",
      "3             25013.303736               0.196506   \n",
      "4             24989.459147               0.196504   \n",
      "5             24981.219100               0.196512   \n",
      "6             28874.473543               0.245639   \n",
      "7             29003.452741               0.245628   \n",
      "8             28921.261327               0.245631   \n",
      "9             38270.638622               0.256564   \n",
      "10            38264.650562               0.256547   \n",
      "11            38253.676395               0.256528   \n",
      "12            26219.134374               0.201951   \n",
      "13            26253.317778               0.201975   \n",
      "14            26284.027794               0.201974   \n",
      "15            26154.359691               0.180124   \n",
      "16            26153.199618               0.180147   \n",
      "17            26152.823801               0.180133   \n",
      "18            32561.911109               0.218356   \n",
      "19            32577.899255               0.218348   \n",
      "20            32594.860153               0.218335   \n",
      "\n",
      "    Water Efficiency (L oil/m³ water)  \n",
      "0                            0.063289  \n",
      "1                            0.090425  \n",
      "2                            0.117518  \n",
      "3                            0.129093  \n",
      "4                            0.161426  \n",
      "5                            0.086043  \n",
      "6                            0.209454  \n",
      "7                            0.116369  \n",
      "8                            0.174541  \n",
      "9                            0.110144  \n",
      "10                           0.128491  \n",
      "11                           0.082607  \n",
      "12                           0.052681  \n",
      "13                           0.126430  \n",
      "14                           0.084286  \n",
      "15                           0.094389  \n",
      "16                           0.132140  \n",
      "17                           0.075493  \n",
      "18                           0.119324  \n",
      "19                           0.088097  \n",
      "20                           0.058713  \n",
      "Comparison by Variety:\n",
      "                    Avg Olive Production (kg/ha)  Avg Oil Production (L/ha)  \\\n",
      "Variety                                                                       \n",
      "nocellara_delletna                  13696.683690                2990.507461   \n",
      "leccino                             15971.162702                3138.439782   \n",
      "frantoio                            19648.631813                4826.360700   \n",
      "coratina                            15974.164423                4098.136472   \n",
      "taggiasca                           11412.636779                2305.011278   \n",
      "pendolino                           14617.432649                2633.129635   \n",
      "moraiolo                            13232.961913                2889.399172   \n",
      "\n",
      "                    Avg Water Need (m³/ha)  Oil Efficiency (L/kg)  \\\n",
      "Variety                                                             \n",
      "nocellara_delletna            33064.983905               0.218338   \n",
      "leccino                       24994.676451               0.196507   \n",
      "frantoio                      28932.932409               0.245633   \n",
      "coratina                      38262.995517               0.256548   \n",
      "taggiasca                     26252.184893               0.201970   \n",
      "pendolino                     26153.461822               0.180136   \n",
      "moraiolo                      32578.228327               0.218349   \n",
      "\n",
      "                    Water Efficiency (L oil/m³ water)  \n",
      "Variety                                                \n",
      "nocellara_delletna                           0.090443  \n",
      "leccino                                      0.125564  \n",
      "frantoio                                     0.166812  \n",
      "coratina                                     0.107104  \n",
      "taggiasca                                    0.087803  \n",
      "pendolino                                    0.100680  \n",
      "moraiolo                                     0.088691  \n",
      "\n",
      "Best Varieties by Water Efficiency:\n",
      "              Variety  Avg Olive Production (kg/ha)  \\\n",
      "2            frantoio                  19648.631813   \n",
      "1             leccino                  15971.162702   \n",
      "3            coratina                  15974.164423   \n",
      "5           pendolino                  14617.432649   \n",
      "0  nocellara_delletna                  13696.683690   \n",
      "\n",
      "   Avg Oil Production (L/ha)  Avg Water Need (m³/ha)  Oil Efficiency (L/kg)  \\\n",
      "2                4826.360700            28932.932409               0.245633   \n",
      "1                3138.439782            24994.676451               0.196507   \n",
      "3                4098.136472            38262.995517               0.256548   \n",
      "5                2633.129635            26153.461822               0.180136   \n",
      "0                2990.507461            33064.983905               0.218338   \n",
      "\n",
      "   Water Efficiency (L oil/m³ water)  \n",
      "2                           0.166812  \n",
      "1                           0.125564  \n",
      "3                           0.107104  \n",
      "5                           0.100680  \n",
      "0                           0.090443  \n"
     ]
    }
   ],
   "source": [
    "simulated_data = pd.read_parquet(f\"{data_dir}olive_training_dataset.parquet\")\n",
    "olive_varieties = pd.read_parquet(f\"{data_dir}olive_varieties.parquet\")\n",
    "# Esecuzione dell'analisi\n",
    "comparison_data = prepare_comparison_data(simulated_data, olive_varieties)\n",
    "\n",
    "# Genera i grafici\n",
    "plot_variety_comparison(comparison_data, 'Avg Olive Production (kg/ha)')\n",
    "plot_variety_comparison(comparison_data, 'Avg Oil Production (L/ha)')\n",
    "plot_variety_comparison(comparison_data, 'Avg Water Need (m³/ha)')\n",
    "plot_variety_comparison(comparison_data, 'Oil Efficiency (L/kg)')\n",
    "plot_variety_comparison(comparison_data, 'Water Efficiency (L oil/m³ water)')\n",
    "plot_efficiency_vs_production(comparison_data)\n",
    "plot_water_efficiency_vs_production(comparison_data)\n",
    "plot_water_need_vs_oil_production(comparison_data)\n",
    "\n",
    "technique_data = analyze_by_technique(simulated_data, olive_varieties)\n",
    "\n",
    "print(technique_data)\n",
    "\n",
    "# Stampa un sommario statistico\n",
    "print(\"Comparison by Variety:\")\n",
    "print(comparison_data.set_index('Variety'))\n",
    "print(\"\\nBest Varieties by Water Efficiency:\")\n",
    "print(comparison_data.sort_values('Water Efficiency (L oil/m³ water)', ascending=False).head())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "bbe87b415168368",
   "metadata": {},
   "outputs": [],
   "source": [
    "def prepare_transformer_data(df, olive_varieties_df):\n",
    "    # Crea una copia del DataFrame per evitare modifiche all'originale\n",
    "    df = df.copy()\n",
    "\n",
    "    # Definisci le feature\n",
    "    temporal_features = ['temp_mean', 'precip_sum', 'solar_energy_sum']\n",
    "    static_features = ['ha']  # Feature statiche base\n",
    "    target_features = ['olive_prod', 'min_oil_prod', 'max_oil_prod', 'avg_oil_prod', 'total_water_need']\n",
    "\n",
    "    # Ottieni le varietà pulite\n",
    "    all_varieties = olive_varieties_df['Varietà di Olive'].unique()\n",
    "    varieties = [clean_column_name(variety) for variety in all_varieties]\n",
    "\n",
    "    # Crea la struttura delle feature per ogni varietà\n",
    "    variety_features = [\n",
    "        'tech', 'pct', 'prod_t_ha', 'oil_prod_t_ha', 'oil_prod_l_ha',\n",
    "        'min_yield_pct', 'max_yield_pct', 'min_oil_prod_l_ha', 'max_oil_prod_l_ha',\n",
    "        'avg_oil_prod_l_ha', 'l_per_t', 'min_l_per_t', 'max_l_per_t', 'avg_l_per_t'\n",
    "    ]\n",
    "\n",
    "    # Prepara dizionari per le nuove colonne\n",
    "    new_columns = {}\n",
    "\n",
    "    # Prepara le feature per ogni varietà\n",
    "    for variety in varieties:\n",
    "        # Feature esistenti\n",
    "        for feature in variety_features:\n",
    "            col_name = f\"{variety}_{feature}\"\n",
    "            if col_name in df.columns:\n",
    "                if feature != 'tech':  # Non includere la colonna tech direttamente\n",
    "                    static_features.append(col_name)\n",
    "\n",
    "        # Feature binarie per le tecniche di coltivazione\n",
    "        for technique in ['tradizionale', 'intensiva', 'superintensiva']:\n",
    "            col_name = f\"{variety}_{technique}\"\n",
    "            new_columns[col_name] = df[f\"{variety}_tech\"].notna() & (\n",
    "                    df[f\"{variety}_tech\"].str.lower() == technique\n",
    "            ).fillna(False)\n",
    "            static_features.append(col_name)\n",
    "\n",
    "    # Aggiungi tutte le nuove colonne in una volta sola\n",
    "    df = pd.concat([df] + [pd.Series(v, name=k) for k, v in new_columns.items()], axis=1)\n",
    "\n",
    "    # Prepara X e y\n",
    "    X_temporal = df[temporal_features].values\n",
    "    X_static = df[static_features].values\n",
    "    y = df[target_features].values\n",
    "\n",
    "    print(f\"Dataset completo - Temporal: {X_temporal.shape}, Static: {X_static.shape}, Target: {y.shape}\")\n",
    "\n",
    "    # Split dei dati (usando indici casuali per una migliore distribuzione)\n",
    "    indices = np.random.permutation(len(X_temporal))\n",
    "    train_idx = int(len(indices) * 0.65)    # 65% training\n",
    "    val_idx = int(len(indices) * 0.85)      # 20% validation\n",
    "    # Il resto rimane 15% test\n",
    "\n",
    "    train_indices = indices[:train_idx]\n",
    "    val_indices = indices[train_idx:val_idx]\n",
    "    test_indices = indices[val_idx:]\n",
    "\n",
    "    # Split dei dati\n",
    "    X_temporal_train = X_temporal[train_indices]\n",
    "    X_temporal_val = X_temporal[val_indices]\n",
    "    X_temporal_test = X_temporal[test_indices]\n",
    "\n",
    "    X_static_train = X_static[train_indices]\n",
    "    X_static_val = X_static[val_indices]\n",
    "    X_static_test = X_static[test_indices]\n",
    "\n",
    "    y_train = y[train_indices]\n",
    "    y_val = y[val_indices]\n",
    "    y_test = y[test_indices]\n",
    "\n",
    "    # Standardizzazione\n",
    "    scaler_temporal = StandardScaler()\n",
    "    scaler_static = StandardScaler()\n",
    "    scaler_y = StandardScaler()\n",
    "\n",
    "    # Standardizzazione dei dati\n",
    "    X_temporal_train = scaler_temporal.fit_transform(X_temporal_train)\n",
    "    X_temporal_val = scaler_temporal.transform(X_temporal_val)\n",
    "    X_temporal_test = scaler_temporal.transform(X_temporal_test)\n",
    "\n",
    "    X_static_train = scaler_static.fit_transform(X_static_train)\n",
    "    X_static_val = scaler_static.transform(X_static_val)\n",
    "    X_static_test = scaler_static.transform(X_static_test)\n",
    "\n",
    "    y_train = scaler_y.fit_transform(y_train)\n",
    "    y_val = scaler_y.transform(y_val)\n",
    "    y_test = scaler_y.transform(y_test)\n",
    "\n",
    "    print(\"\\nShape dopo lo split e standardizzazione:\")\n",
    "    print(f\"Train - Temporal: {X_temporal_train.shape}, Static: {X_static_train.shape}, Target: {y_train.shape}\")\n",
    "    print(f\"Val - Temporal: {X_temporal_val.shape}, Static: {X_static_val.shape}, Target: {y_val.shape}\")\n",
    "    print(f\"Test - Temporal: {X_temporal_test.shape}, Static: {X_static_test.shape}, Target: {y_test.shape}\")\n",
    "\n",
    "    # Reshape per il transformer (aggiunge la dimensione del sequence length = 1)\n",
    "    X_temporal_train = X_temporal_train.reshape(X_temporal_train.shape[0], 1, -1)\n",
    "    X_temporal_val = X_temporal_val.reshape(X_temporal_val.shape[0], 1, -1)\n",
    "    X_temporal_test = X_temporal_test.reshape(X_temporal_test.shape[0], 1, -1)\n",
    "\n",
    "    # Prepara i dizionari di input\n",
    "    train_data = {'temporal': X_temporal_train, 'static': X_static_train}\n",
    "    val_data = {'temporal': X_temporal_val, 'static': X_static_val}\n",
    "    test_data = {'temporal': X_temporal_test, 'static': X_static_test}\n",
    "\n",
    "    # Salva gli scaler\n",
    "    joblib.dump(scaler_temporal, os.path.join(base_project_dir, f'{execute_name}_scaler_temporal.joblib'))\n",
    "    joblib.dump(scaler_static, os.path.join(base_project_dir, f'{execute_name}_scaler_static.joblib'))\n",
    "    joblib.dump(scaler_y, os.path.join(base_project_dir, f'{execute_name}_scaler_y.joblib'))\n",
    "\n",
    "    return (train_data, y_train), (val_data, y_val), (test_data, y_test), (scaler_temporal, scaler_static, scaler_y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "9c4d5f0f3fafdc2d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dataset completo - Temporal: (4000000, 3), Static: (4000000, 113), Target: (4000000, 5)\n",
      "\n",
      "Shape dopo lo split e standardizzazione:\n",
      "Train - Temporal: (2600000, 3), Static: (2600000, 113), Target: (2600000, 5)\n",
      "Val - Temporal: (800000, 3), Static: (800000, 113), Target: (800000, 5)\n",
      "Test - Temporal: (600000, 3), Static: (600000, 113), Target: (600000, 5)\n",
      "Temporal data shape: (2600000, 1, 3)\n",
      "Static data shape: (2600000, 113)\n",
      "Target shape: (2600000, 5)\n"
     ]
    }
   ],
   "source": [
    "simulated_data = pd.read_parquet(f\"{data_dir}olive_training_dataset.parquet\")\n",
    "olive_varieties = pd.read_parquet(f\"{data_dir}olive_varieties.parquet\")\n",
    "\n",
    "(train_data, train_targets), (val_data, val_targets), (test_data, test_targets), scalers = prepare_transformer_data(simulated_data, olive_varieties)\n",
    "\n",
    "scaler_temporal, scaler_static, scaler_y = scalers\n",
    "\n",
    "print(\"Temporal data shape:\", train_data['temporal'].shape)\n",
    "print(\"Static data shape:\", train_data['static'].shape)\n",
    "print(\"Target shape:\", train_targets.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "604c952c7195f40c",
   "metadata": {},
   "outputs": [],
   "source": [
    "@keras.saving.register_keras_serializable()\n",
    "class DataAugmentation(tf.keras.layers.Layer):\n",
    "    \"\"\"Custom layer per l'augmentation dei dati\"\"\"\n",
    "\n",
    "    def __init__(self, noise_stddev=0.03, **kwargs):\n",
    "        super().__init__(**kwargs)\n",
    "        self.noise_stddev = noise_stddev\n",
    "\n",
    "    def call(self, inputs, training=None):\n",
    "        if training:\n",
    "            return inputs + tf.random.normal(\n",
    "                shape=tf.shape(inputs),\n",
    "                mean=0.0,\n",
    "                stddev=self.noise_stddev\n",
    "            )\n",
    "        return inputs\n",
    "\n",
    "    def get_config(self):\n",
    "        config = super().get_config()\n",
    "        config.update({\"noise_stddev\": self.noise_stddev})\n",
    "        return config\n",
    "\n",
    "\n",
    "@keras.saving.register_keras_serializable()\n",
    "class PositionalEncoding(tf.keras.layers.Layer):\n",
    "    \"\"\"Custom layer per l'encoding posizionale\"\"\"\n",
    "\n",
    "    def __init__(self, d_model, **kwargs):\n",
    "        super().__init__(**kwargs)\n",
    "        self.d_model = d_model\n",
    "\n",
    "    def build(self, input_shape):\n",
    "        _, seq_length, _ = input_shape\n",
    "\n",
    "        # Crea la matrice di encoding posizionale\n",
    "        position = tf.range(seq_length, dtype=tf.float32)[:, tf.newaxis]\n",
    "        div_term = tf.exp(\n",
    "            tf.range(0, self.d_model, 2, dtype=tf.float32) *\n",
    "            (-tf.math.log(10000.0) / self.d_model)\n",
    "        )\n",
    "\n",
    "        # Calcola sin e cos\n",
    "        pos_encoding = tf.zeros((1, seq_length, self.d_model))\n",
    "        pos_encoding_even = tf.sin(position * div_term)\n",
    "        pos_encoding_odd = tf.cos(position * div_term)\n",
    "\n",
    "        # Assegna i valori alle posizioni pari e dispari\n",
    "        pos_encoding = tf.concat(\n",
    "            [tf.expand_dims(pos_encoding_even, -1),\n",
    "             tf.expand_dims(pos_encoding_odd, -1)],\n",
    "            axis=-1\n",
    "        )\n",
    "        pos_encoding = tf.reshape(pos_encoding, (1, seq_length, -1))\n",
    "        pos_encoding = pos_encoding[:, :, :self.d_model]\n",
    "\n",
    "        # Salva l'encoding come peso non trainabile\n",
    "        self.pos_encoding = self.add_weight(\n",
    "            shape=(1, seq_length, self.d_model),\n",
    "            initializer=tf.keras.initializers.Constant(pos_encoding),\n",
    "            trainable=False,\n",
    "            name='positional_encoding'\n",
    "        )\n",
    "\n",
    "        super().build(input_shape)\n",
    "\n",
    "    def call(self, inputs):\n",
    "        # Broadcast l'encoding posizionale sul batch\n",
    "        batch_size = tf.shape(inputs)[0]\n",
    "        pos_encoding_tiled = tf.tile(self.pos_encoding, [batch_size, 1, 1])\n",
    "        return inputs + pos_encoding_tiled\n",
    "\n",
    "    def get_config(self):\n",
    "        config = super().get_config()\n",
    "        config.update({\"d_model\": self.d_model})\n",
    "        return config\n",
    "\n",
    "\n",
    "@keras.saving.register_keras_serializable()\n",
    "class WarmUpLearningRateSchedule(tf.keras.optimizers.schedules.LearningRateSchedule):\n",
    "    \"\"\"Custom learning rate schedule with linear warmup and exponential decay.\"\"\"\n",
    "\n",
    "    def __init__(self, initial_learning_rate=1e-3, warmup_steps=500, decay_steps=5000):\n",
    "        super().__init__()\n",
    "        self.initial_learning_rate = initial_learning_rate\n",
    "        self.warmup_steps = warmup_steps\n",
    "        self.decay_steps = decay_steps\n",
    "\n",
    "    def __call__(self, step):\n",
    "        warmup_pct = tf.cast(step, tf.float32) / self.warmup_steps\n",
    "        warmup_lr = self.initial_learning_rate * warmup_pct\n",
    "        decay_factor = tf.pow(0.1, tf.cast(step, tf.float32) / self.decay_steps)\n",
    "        decayed_lr = self.initial_learning_rate * decay_factor\n",
    "        return tf.where(step < self.warmup_steps, warmup_lr, decayed_lr)\n",
    "\n",
    "    def get_config(self):\n",
    "        return {\n",
    "            'initial_learning_rate': self.initial_learning_rate,\n",
    "            'warmup_steps': self.warmup_steps,\n",
    "            'decay_steps': self.decay_steps\n",
    "        }\n",
    "\n",
    "\n",
    "def create_olive_oil_transformer(temporal_shape, static_shape, num_outputs,\n",
    "                                 d_model=128, num_heads=8, ff_dim=256,\n",
    "                                 num_transformer_blocks=4, mlp_units=None,\n",
    "                                 dropout=0.2):\n",
    "    if mlp_units is None:\n",
    "        mlp_units = [256, 128, 64]\n",
    "\n",
    "    temporal_input = tf.keras.layers.Input(shape=temporal_shape, name='temporal')\n",
    "    static_input = tf.keras.layers.Input(shape=static_shape, name='static')\n",
    "\n",
    "    # === TEMPORAL PATH ===\n",
    "    x = tf.keras.layers.LayerNormalization(epsilon=1e-6)(temporal_input)\n",
    "    x = DataAugmentation()(x)\n",
    "\n",
    "    # Temporal projection con dimensione aumentata per compensare la sequenza corta\n",
    "    x = tf.keras.layers.Dense(\n",
    "        d_model,\n",
    "        activation='swish',\n",
    "        kernel_regularizer=tf.keras.regularizers.l2(1e-5)\n",
    "    )(x)\n",
    "    x = tf.keras.layers.Dropout(dropout)(x)\n",
    "\n",
    "    # Additional feature extraction prima del transformer\n",
    "    x = tf.keras.layers.Dense(\n",
    "        d_model * 2,\n",
    "        activation='swish',\n",
    "        kernel_regularizer=tf.keras.regularizers.l2(1e-5)\n",
    "    )(x)\n",
    "\n",
    "    x = PositionalEncoding(d_model * 2)(x)\n",
    "\n",
    "    skip_connection = x\n",
    "    for _ in range(num_transformer_blocks):\n",
    "        # Multi-head self-attention con più heads per compensare la sequenza corta\n",
    "        attention_output = tf.keras.layers.MultiHeadAttention(\n",
    "            num_heads=num_heads * 2,\n",
    "            key_dim=d_model // num_heads,\n",
    "            value_dim=d_model // num_heads\n",
    "        )(x, x)\n",
    "        attention_output = tf.keras.layers.Dropout(dropout)(attention_output)\n",
    "\n",
    "        # Residual connection con gating mechanism\n",
    "        gate = tf.keras.layers.Dense(d_model * 2, activation='sigmoid')(x)\n",
    "        x = x + gate * attention_output\n",
    "        x = tf.keras.layers.LayerNormalization(epsilon=1e-6)(x)\n",
    "\n",
    "        # Feed-forward network potenziato\n",
    "        ffn = tf.keras.Sequential([\n",
    "            tf.keras.layers.Dense(ff_dim * 2, activation=\"swish\"),  # Raddoppiato\n",
    "            tf.keras.layers.Dropout(dropout),\n",
    "            tf.keras.layers.Dense(ff_dim, activation=\"swish\"),\n",
    "            tf.keras.layers.Dropout(dropout),\n",
    "            tf.keras.layers.Dense(d_model * 2)\n",
    "        ])\n",
    "        ffn_output = ffn(x)\n",
    "\n",
    "        # Gated residual connection\n",
    "        gate = tf.keras.layers.Dense(d_model * 2, activation='sigmoid')(x)\n",
    "        x = x + gate * ffn_output\n",
    "        x = tf.keras.layers.LayerNormalization(epsilon=1e-6)(x)\n",
    "\n",
    "    # Global feature attention\n",
    "    x = tf.keras.layers.MultiHeadAttention(\n",
    "        num_heads=num_heads,\n",
    "        key_dim=d_model // num_heads\n",
    "    )(x, x)\n",
    "\n",
    "    # Feature pooling\n",
    "    x = tf.keras.layers.GlobalAveragePooling1D()(x)\n",
    "\n",
    "    # === STATIC PATH ===\n",
    "    s = tf.keras.layers.LayerNormalization(epsilon=1e-6)(static_input)\n",
    "    for units in [512, 256, 128]:  # Aumentate le dimensioni\n",
    "        s = tf.keras.layers.Dense(\n",
    "            units,\n",
    "            activation='swish',\n",
    "            kernel_regularizer=tf.keras.regularizers.l2(1e-5)\n",
    "        )(s)\n",
    "        s = tf.keras.layers.BatchNormalization()(s)\n",
    "        s = tf.keras.layers.Dropout(dropout)(s)\n",
    "\n",
    "\n",
    "    # === FEATURE FUSION con attention ===\n",
    "    # Project features to same dimensionality\n",
    "    x = tf.keras.layers.Dense(256)(x)\n",
    "    s = tf.keras.layers.Dense(256)(s)\n",
    "\n",
    "    # Cross-attention between temporal and static features\n",
    "    combined = tf.keras.layers.Concatenate()([x, s])\n",
    "    combined = tf.keras.layers.Dense(256, activation='swish')(combined)\n",
    "\n",
    "    # MLP head with residual connections\n",
    "    for units in mlp_units:\n",
    "        skip = combined\n",
    "        combined = tf.keras.layers.BatchNormalization()(combined)\n",
    "        combined = tf.keras.layers.Dense(units, activation=\"swish\")(combined)\n",
    "        combined = tf.keras.layers.Dropout(dropout)(combined)\n",
    "        if units == skip.shape[-1]:  # Se le dimensioni combaciano\n",
    "            combined = combined + skip\n",
    "\n",
    "    # Apply final normalization to output\n",
    "    outputs = tf.keras.layers.BatchNormalization()(combined)\n",
    "    outputs = tf.keras.layers.Dense(\n",
    "        num_outputs,\n",
    "        activation='linear',\n",
    "        kernel_regularizer=tf.keras.regularizers.l2(1e-5)\n",
    "    )(outputs)\n",
    "\n",
    "    # Create model\n",
    "    model = tf.keras.Model(\n",
    "        inputs={'temporal': temporal_input, 'static': static_input},\n",
    "        outputs=outputs,\n",
    "        name='OilTransformer'\n",
    "    )\n",
    "\n",
    "    return model\n",
    "\n",
    "\n",
    "def create_transformer_callbacks(target_names, val_data, val_targets):\n",
    "    \"\"\"\n",
    "    Crea i callbacks per il training del modello single-step.\n",
    "    \"\"\"\n",
    "    class TargetSpecificMetric(tf.keras.callbacks.Callback):\n",
    "        def __init__(self, validation_data, target_names):\n",
    "            super().__init__()\n",
    "            self.validation_data = validation_data\n",
    "            self.target_names = target_names\n",
    "            self.best_metrics = {name: float('inf') for name in target_names}\n",
    "    \n",
    "        def on_epoch_end(self, epoch, logs=None):\n",
    "            logs = logs or {}\n",
    "            \n",
    "            # Esegui il calcolo solo ogni 5 epoche\n",
    "            if epoch % 5 == 0:\n",
    "                x_val, y_val = self.validation_data\n",
    "                y_pred = self.model.predict(x_val, verbose=0)\n",
    "    \n",
    "                # Calcola e logga le metriche per ogni target\n",
    "                for i, name in enumerate(self.target_names):\n",
    "                    mae = np.mean(np.abs(y_val[:, i] - y_pred[:, i]))\n",
    "                    mape = np.mean(np.abs((y_val[:, i] - y_pred[:, i]) / np.clip(np.abs(y_val[:, i]), 1e-7, None))) * 100\n",
    "                    logs[f'val_{name}_mae'] = mae\n",
    "                    logs[f'val_{name}_mape'] = mape\n",
    "    \n",
    "                    # Traccia i migliori risultati\n",
    "                    if mae < self.best_metrics[name]:\n",
    "                        self.best_metrics[name] = mae\n",
    "                        logs[f'best_{name}_mae'] = mae\n",
    "\n",
    "\n",
    "    callbacks = [\n",
    "        # Early Stopping ottimizzato\n",
    "        tf.keras.callbacks.EarlyStopping(\n",
    "            monitor='val_loss',\n",
    "            patience=25,  # Aumentato per dare più chance al modello\n",
    "            restore_best_weights=True,\n",
    "            min_delta=0.0001,  # Più sensibile ai miglioramenti\n",
    "            mode='min'\n",
    "        ),\n",
    "\n",
    "        # Model Checkpoint con monitoraggio multiplo\n",
    "        tf.keras.callbacks.ModelCheckpoint(\n",
    "            filepath=f'{execute_name}_best_oil_model.h5',\n",
    "            monitor='val_loss',\n",
    "            save_best_only=True,\n",
    "            mode='min',\n",
    "            save_weights_only=True\n",
    "        ),\n",
    "\n",
    "        # Metric per target specifici\n",
    "        TargetSpecificMetric(\n",
    "            validation_data=(val_data, val_targets),\n",
    "            target_names=target_names\n",
    "        ),\n",
    "\n",
    "        # LR reduction ottimizzato per single-step\n",
    "        tf.keras.callbacks.ReduceLROnPlateau(\n",
    "            monitor='val_loss',\n",
    "            factor=0.2,  # Riduzione più aggressiva\n",
    "            patience=15,\n",
    "            min_lr=1e-7,\n",
    "            verbose=1,\n",
    "            cooldown=5  # Periodo di cool-down per stabilizzazione\n",
    "        ),\n",
    "\n",
    "        # TensorBoard con più metriche\n",
    "        tf.keras.callbacks.TensorBoard(\n",
    "            log_dir=f'./logs_{execute_name}',\n",
    "            histogram_freq=1,\n",
    "            write_graph=True,\n",
    "            write_images=True,\n",
    "            update_freq='epoch',\n",
    "            profile_batch='500,520'  # Profile per ottimizzazione\n",
    "        )\n",
    "    ]\n",
    "\n",
    "    return callbacks\n",
    "\n",
    "def compile_model(model, learning_rate=5e-4):  # Learning rate ridotto\n",
    "    \"\"\"\n",
    "    Compila il modello con ottimizzazioni per single-step.\n",
    "    \"\"\"\n",
    "    lr_schedule = WarmUpLearningRateSchedule(\n",
    "        initial_learning_rate=learning_rate,\n",
    "        warmup_steps=1000,  # Aumentato per stabilità\n",
    "        decay_steps=7000    # Aumentato per permettere più esplorazione\n",
    "    )\n",
    "\n",
    "    @keras.saving.register_keras_serializable()\n",
    "    def weighted_huber_loss(y_true, y_pred):\n",
    "        # Pesi per diversi output\n",
    "        weights = tf.constant([1.0, 0.8, 0.8, 1.0, 0.6], dtype=tf.float32)\n",
    "        huber = tf.keras.losses.Huber(delta=1.0)\n",
    "        loss = huber(y_true, y_pred)\n",
    "        weighted_loss = tf.reduce_mean(loss * weights)\n",
    "        return weighted_loss\n",
    "\n",
    "    model.compile(\n",
    "        optimizer=tf.keras.optimizers.AdamW(\n",
    "            learning_rate=lr_schedule,\n",
    "            weight_decay=0.01,\n",
    "            clipnorm=1.0,  # Gradient clipping\n",
    "            epsilon=1e-7    # Aumentato per stabilità numerica\n",
    "        ),\n",
    "        loss=weighted_huber_loss,\n",
    "        metrics=['mae', 'mape']\n",
    "    )\n",
    "\n",
    "    return model\n",
    "\n",
    "def setup_transformer_training(train_data, train_targets, val_data, val_targets):\n",
    "    \"\"\"\n",
    "    Configura il single-step transformer.\n",
    "    \"\"\"\n",
    "    # Estrai le shape dai dati\n",
    "    temporal_shape = (1, train_data['temporal'].shape[2])\n",
    "    static_shape = (train_data['static'].shape[1],)\n",
    "    num_outputs = train_targets.shape[1]\n",
    "\n",
    "    print(f\"Shape rilevate:\")\n",
    "    print(f\"- Temporal shape: {temporal_shape}\")\n",
    "    print(f\"- Static shape: {static_shape}\")\n",
    "    print(f\"- Numero di output: {num_outputs}\")\n",
    "\n",
    "    target_names = ['olive_prod', 'min_oil_prod', 'max_oil_prod', 'avg_oil_prod', 'total_water_need']\n",
    "\n",
    "    assert len(target_names) == num_outputs, \\\n",
    "        f\"Il numero di target names ({len(target_names)}) non corrisponde al numero di output ({num_outputs})\"\n",
    "\n",
    "    # Crea il modello con il nuovo transformer\n",
    "    model = create_olive_oil_transformer(\n",
    "        temporal_shape=temporal_shape,\n",
    "        static_shape=static_shape,\n",
    "        num_outputs=num_outputs,\n",
    "        d_model=256,\n",
    "        num_heads=8,\n",
    "        ff_dim=512,\n",
    "        num_transformer_blocks=6,\n",
    "        dropout=0.1\n",
    "    )\n",
    "\n",
    "    model = compile_model(model)\n",
    "    callbacks = create_transformer_callbacks(target_names, val_data, val_targets)\n",
    "\n",
    "    return model, callbacks, target_names\n",
    "\n",
    "def train_transformer(train_data, train_targets, val_data, val_targets, epochs=200, batch_size=128, save_name='final_model'):\n",
    "    \"\"\"\n",
    "    Training ottimizzato per single-step transformer.\n",
    "    \"\"\"\n",
    "    # Dataset con augmentation\n",
    "    def augment(x, y):\n",
    "        # Ottieni il dtype dei dati originali\n",
    "        original_dtype = x['temporal'].dtype\n",
    "        # Genera il rumore con lo stesso dtype\n",
    "        noise = tf.random.normal(\n",
    "            tf.shape(x['temporal']), \n",
    "            mean=0.0, \n",
    "            stddev=0.01,\n",
    "            dtype=original_dtype\n",
    "        )\n",
    "        x['temporal'] += noise\n",
    "        return x, y\n",
    "\n",
    "    train_dataset = tf.data.Dataset.from_tensor_slices((train_data, train_targets))\\\n",
    "        .map(augment, num_parallel_calls=tf.data.AUTOTUNE)\\\n",
    "        .cache()\\\n",
    "        .shuffle(buffer_size=10000)\\\n",
    "        .batch(batch_size)\\\n",
    "        .prefetch(tf.data.AUTOTUNE)\n",
    "\n",
    "    val_dataset = tf.data.Dataset.from_tensor_slices((val_data, val_targets))\\\n",
    "        .cache()\\\n",
    "        .batch(batch_size)\\\n",
    "        .prefetch(tf.data.AUTOTUNE)\n",
    "\n",
    "    strategy = tf.distribute.MirroredStrategy() if len(tf.config.list_physical_devices('GPU')) > 1 else tf.distribute.get_strategy()\n",
    "\n",
    "    with strategy.scope():\n",
    "        model, callbacks, target_names = setup_transformer_training(\n",
    "            train_data, train_targets, val_data, val_targets\n",
    "        )\n",
    "\n",
    "    model.summary()\n",
    "\n",
    "    try:\n",
    "        keras.utils.plot_model(model, f\"{execute_name}_{save_name}.png\", show_shapes=True)\n",
    "    except Exception as e:\n",
    "        print(f\"Warning: Could not create model plot: {e}\")\n",
    "\n",
    "    # Training con gestione errori e memory saving\n",
    "    try:\n",
    "        with tf.device('/GPU:0'):  # Forza l'uso della GPU principale\n",
    "            history = model.fit(\n",
    "                train_dataset,\n",
    "                validation_data=val_dataset,\n",
    "                epochs=epochs,\n",
    "                callbacks=callbacks,\n",
    "                verbose=1,\n",
    "                workers=8,\n",
    "                use_multiprocessing=True\n",
    "            )\n",
    "    except tf.errors.ResourceExhaustedError:\n",
    "        print(\"Memoria GPU esaurita, riprovo con batch size più piccolo...\")\n",
    "        batch_size = batch_size // 2\n",
    "        train_dataset = train_dataset.unbatch().batch(batch_size)\n",
    "        val_dataset = val_dataset.unbatch().batch(batch_size)\n",
    "        history = model.fit(\n",
    "            train_dataset,\n",
    "            validation_data=val_dataset,\n",
    "            epochs=epochs,\n",
    "            callbacks=callbacks,\n",
    "            verbose=1\n",
    "        )\n",
    "\n",
    "    # Salvataggio modello\n",
    "    try:\n",
    "        save_path = f'{execute_name}_{save_name}.keras'\n",
    "        model.save(save_path, save_format='keras')\n",
    "\n",
    "        os.makedirs(f'{execute_name}/weights', exist_ok=True)\n",
    "        model.save_weights(f'{execute_name}/weights')\n",
    "        print(f\"\\nModello salvato in: {save_path}\")\n",
    "\n",
    "        # Salva anche la storia del training\n",
    "        with open(f'{execute_name}_training_history.json', 'w') as f:\n",
    "            json.dump(history.history, f)\n",
    "    except Exception as e:\n",
    "        print(f\"Warning: Could not save model: {e}\")\n",
    "\n",
    "    return model, history"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "35490e902e494c4a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Shape rilevate:\n",
      "- Temporal shape: (1, 3)\n",
      "- Static shape: (113,)\n",
      "- Numero di output: 5\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-12-08 14:50:03.536829: I tensorflow/tsl/platform/default/subprocess.cc:304] Start cannot spawn child process: No such file or directory\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model: \"OilTransformer\"\n",
      "__________________________________________________________________________________________________\n",
      " Layer (type)                Output Shape                 Param #   Connected to                  \n",
      "==================================================================================================\n",
      " temporal (InputLayer)       [(None, 1, 3)]               0         []                            \n",
      "                                                                                                  \n",
      " layer_normalization (Layer  (None, 1, 3)                 6         ['temporal[0][0]']            \n",
      " Normalization)                                                                                   \n",
      "                                                                                                  \n",
      " data_augmentation (DataAug  (None, 1, 3)                 0         ['layer_normalization[0][0]'] \n",
      " mentation)                                                                                       \n",
      "                                                                                                  \n",
      " dense (Dense)               (None, 1, 256)               1024      ['data_augmentation[0][0]']   \n",
      "                                                                                                  \n",
      " dropout (Dropout)           (None, 1, 256)               0         ['dense[0][0]']               \n",
      "                                                                                                  \n",
      " dense_1 (Dense)             (None, 1, 512)               131584    ['dropout[0][0]']             \n",
      "                                                                                                  \n",
      " positional_encoding (Posit  (None, 1, 512)               512       ['dense_1[0][0]']             \n",
      " ionalEncoding)                                                                                   \n",
      "                                                                                                  \n",
      " multi_head_attention (Mult  (None, 1, 512)               1050624   ['positional_encoding[0][0]', \n",
      " iHeadAttention)                                                     'positional_encoding[0][0]'] \n",
      "                                                                                                  \n",
      " dense_2 (Dense)             (None, 1, 512)               262656    ['positional_encoding[0][0]'] \n",
      "                                                                                                  \n",
      " dropout_1 (Dropout)         (None, 1, 512)               0         ['multi_head_attention[0][0]']\n",
      "                                                                                                  \n",
      " tf.math.multiply (TFOpLamb  (None, 1, 512)               0         ['dense_2[0][0]',             \n",
      " da)                                                                 'dropout_1[0][0]']           \n",
      "                                                                                                  \n",
      " tf.__operators__.add (TFOp  (None, 1, 512)               0         ['positional_encoding[0][0]', \n",
      " Lambda)                                                             'tf.math.multiply[0][0]']    \n",
      "                                                                                                  \n",
      " layer_normalization_1 (Lay  (None, 1, 512)               1024      ['tf.__operators__.add[0][0]']\n",
      " erNormalization)                                                                                 \n",
      "                                                                                                  \n",
      " dense_6 (Dense)             (None, 1, 512)               262656    ['layer_normalization_1[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " sequential (Sequential)     (None, 1, 512)               1312768   ['layer_normalization_1[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " tf.math.multiply_1 (TFOpLa  (None, 1, 512)               0         ['dense_6[0][0]',             \n",
      " mbda)                                                               'sequential[0][0]']          \n",
      "                                                                                                  \n",
      " tf.__operators__.add_1 (TF  (None, 1, 512)               0         ['layer_normalization_1[0][0]'\n",
      " OpLambda)                                                          , 'tf.math.multiply_1[0][0]'] \n",
      "                                                                                                  \n",
      " layer_normalization_2 (Lay  (None, 1, 512)               1024      ['tf.__operators__.add_1[0][0]\n",
      " erNormalization)                                                   ']                            \n",
      "                                                                                                  \n",
      " multi_head_attention_1 (Mu  (None, 1, 512)               1050624   ['layer_normalization_2[0][0]'\n",
      " ltiHeadAttention)                                                  , 'layer_normalization_2[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " dense_7 (Dense)             (None, 1, 512)               262656    ['layer_normalization_2[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " dropout_4 (Dropout)         (None, 1, 512)               0         ['multi_head_attention_1[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " tf.math.multiply_2 (TFOpLa  (None, 1, 512)               0         ['dense_7[0][0]',             \n",
      " mbda)                                                               'dropout_4[0][0]']           \n",
      "                                                                                                  \n",
      " tf.__operators__.add_2 (TF  (None, 1, 512)               0         ['layer_normalization_2[0][0]'\n",
      " OpLambda)                                                          , 'tf.math.multiply_2[0][0]'] \n",
      "                                                                                                  \n",
      " layer_normalization_3 (Lay  (None, 1, 512)               1024      ['tf.__operators__.add_2[0][0]\n",
      " erNormalization)                                                   ']                            \n",
      "                                                                                                  \n",
      " dense_11 (Dense)            (None, 1, 512)               262656    ['layer_normalization_3[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " sequential_1 (Sequential)   (None, 1, 512)               1312768   ['layer_normalization_3[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " tf.math.multiply_3 (TFOpLa  (None, 1, 512)               0         ['dense_11[0][0]',            \n",
      " mbda)                                                               'sequential_1[0][0]']        \n",
      "                                                                                                  \n",
      " tf.__operators__.add_3 (TF  (None, 1, 512)               0         ['layer_normalization_3[0][0]'\n",
      " OpLambda)                                                          , 'tf.math.multiply_3[0][0]'] \n",
      "                                                                                                  \n",
      " layer_normalization_4 (Lay  (None, 1, 512)               1024      ['tf.__operators__.add_3[0][0]\n",
      " erNormalization)                                                   ']                            \n",
      "                                                                                                  \n",
      " multi_head_attention_2 (Mu  (None, 1, 512)               1050624   ['layer_normalization_4[0][0]'\n",
      " ltiHeadAttention)                                                  , 'layer_normalization_4[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " dense_12 (Dense)            (None, 1, 512)               262656    ['layer_normalization_4[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " dropout_7 (Dropout)         (None, 1, 512)               0         ['multi_head_attention_2[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " tf.math.multiply_4 (TFOpLa  (None, 1, 512)               0         ['dense_12[0][0]',            \n",
      " mbda)                                                               'dropout_7[0][0]']           \n",
      "                                                                                                  \n",
      " tf.__operators__.add_4 (TF  (None, 1, 512)               0         ['layer_normalization_4[0][0]'\n",
      " OpLambda)                                                          , 'tf.math.multiply_4[0][0]'] \n",
      "                                                                                                  \n",
      " layer_normalization_5 (Lay  (None, 1, 512)               1024      ['tf.__operators__.add_4[0][0]\n",
      " erNormalization)                                                   ']                            \n",
      "                                                                                                  \n",
      " dense_16 (Dense)            (None, 1, 512)               262656    ['layer_normalization_5[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " sequential_2 (Sequential)   (None, 1, 512)               1312768   ['layer_normalization_5[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " tf.math.multiply_5 (TFOpLa  (None, 1, 512)               0         ['dense_16[0][0]',            \n",
      " mbda)                                                               'sequential_2[0][0]']        \n",
      "                                                                                                  \n",
      " tf.__operators__.add_5 (TF  (None, 1, 512)               0         ['layer_normalization_5[0][0]'\n",
      " OpLambda)                                                          , 'tf.math.multiply_5[0][0]'] \n",
      "                                                                                                  \n",
      " layer_normalization_6 (Lay  (None, 1, 512)               1024      ['tf.__operators__.add_5[0][0]\n",
      " erNormalization)                                                   ']                            \n",
      "                                                                                                  \n",
      " multi_head_attention_3 (Mu  (None, 1, 512)               1050624   ['layer_normalization_6[0][0]'\n",
      " ltiHeadAttention)                                                  , 'layer_normalization_6[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " dense_17 (Dense)            (None, 1, 512)               262656    ['layer_normalization_6[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " dropout_10 (Dropout)        (None, 1, 512)               0         ['multi_head_attention_3[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " tf.math.multiply_6 (TFOpLa  (None, 1, 512)               0         ['dense_17[0][0]',            \n",
      " mbda)                                                               'dropout_10[0][0]']          \n",
      "                                                                                                  \n",
      " tf.__operators__.add_6 (TF  (None, 1, 512)               0         ['layer_normalization_6[0][0]'\n",
      " OpLambda)                                                          , 'tf.math.multiply_6[0][0]'] \n",
      "                                                                                                  \n",
      " layer_normalization_7 (Lay  (None, 1, 512)               1024      ['tf.__operators__.add_6[0][0]\n",
      " erNormalization)                                                   ']                            \n",
      "                                                                                                  \n",
      " dense_21 (Dense)            (None, 1, 512)               262656    ['layer_normalization_7[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " sequential_3 (Sequential)   (None, 1, 512)               1312768   ['layer_normalization_7[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " tf.math.multiply_7 (TFOpLa  (None, 1, 512)               0         ['dense_21[0][0]',            \n",
      " mbda)                                                               'sequential_3[0][0]']        \n",
      "                                                                                                  \n",
      " tf.__operators__.add_7 (TF  (None, 1, 512)               0         ['layer_normalization_7[0][0]'\n",
      " OpLambda)                                                          , 'tf.math.multiply_7[0][0]'] \n",
      "                                                                                                  \n",
      " layer_normalization_8 (Lay  (None, 1, 512)               1024      ['tf.__operators__.add_7[0][0]\n",
      " erNormalization)                                                   ']                            \n",
      "                                                                                                  \n",
      " multi_head_attention_4 (Mu  (None, 1, 512)               1050624   ['layer_normalization_8[0][0]'\n",
      " ltiHeadAttention)                                                  , 'layer_normalization_8[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " dense_22 (Dense)            (None, 1, 512)               262656    ['layer_normalization_8[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " dropout_13 (Dropout)        (None, 1, 512)               0         ['multi_head_attention_4[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " tf.math.multiply_8 (TFOpLa  (None, 1, 512)               0         ['dense_22[0][0]',            \n",
      " mbda)                                                               'dropout_13[0][0]']          \n",
      "                                                                                                  \n",
      " tf.__operators__.add_8 (TF  (None, 1, 512)               0         ['layer_normalization_8[0][0]'\n",
      " OpLambda)                                                          , 'tf.math.multiply_8[0][0]'] \n",
      "                                                                                                  \n",
      " layer_normalization_9 (Lay  (None, 1, 512)               1024      ['tf.__operators__.add_8[0][0]\n",
      " erNormalization)                                                   ']                            \n",
      "                                                                                                  \n",
      " dense_26 (Dense)            (None, 1, 512)               262656    ['layer_normalization_9[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " sequential_4 (Sequential)   (None, 1, 512)               1312768   ['layer_normalization_9[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " tf.math.multiply_9 (TFOpLa  (None, 1, 512)               0         ['dense_26[0][0]',            \n",
      " mbda)                                                               'sequential_4[0][0]']        \n",
      "                                                                                                  \n",
      " tf.__operators__.add_9 (TF  (None, 1, 512)               0         ['layer_normalization_9[0][0]'\n",
      " OpLambda)                                                          , 'tf.math.multiply_9[0][0]'] \n",
      "                                                                                                  \n",
      " layer_normalization_10 (La  (None, 1, 512)               1024      ['tf.__operators__.add_9[0][0]\n",
      " yerNormalization)                                                  ']                            \n",
      "                                                                                                  \n",
      " multi_head_attention_5 (Mu  (None, 1, 512)               1050624   ['layer_normalization_10[0][0]\n",
      " ltiHeadAttention)                                                  ',                            \n",
      "                                                                     'layer_normalization_10[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " static (InputLayer)         [(None, 113)]                0         []                            \n",
      "                                                                                                  \n",
      " dense_27 (Dense)            (None, 1, 512)               262656    ['layer_normalization_10[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " dropout_16 (Dropout)        (None, 1, 512)               0         ['multi_head_attention_5[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " layer_normalization_13 (La  (None, 113)                  226       ['static[0][0]']              \n",
      " yerNormalization)                                                                                \n",
      "                                                                                                  \n",
      " tf.math.multiply_10 (TFOpL  (None, 1, 512)               0         ['dense_27[0][0]',            \n",
      " ambda)                                                              'dropout_16[0][0]']          \n",
      "                                                                                                  \n",
      " dense_32 (Dense)            (None, 512)                  58368     ['layer_normalization_13[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " tf.__operators__.add_10 (T  (None, 1, 512)               0         ['layer_normalization_10[0][0]\n",
      " FOpLambda)                                                         ',                            \n",
      "                                                                     'tf.math.multiply_10[0][0]'] \n",
      "                                                                                                  \n",
      " batch_normalization (Batch  (None, 512)                  2048      ['dense_32[0][0]']            \n",
      " Normalization)                                                                                   \n",
      "                                                                                                  \n",
      " layer_normalization_11 (La  (None, 1, 512)               1024      ['tf.__operators__.add_10[0][0\n",
      " yerNormalization)                                                  ]']                           \n",
      "                                                                                                  \n",
      " dropout_19 (Dropout)        (None, 512)                  0         ['batch_normalization[0][0]'] \n",
      "                                                                                                  \n",
      " dense_31 (Dense)            (None, 1, 512)               262656    ['layer_normalization_11[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " sequential_5 (Sequential)   (None, 1, 512)               1312768   ['layer_normalization_11[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " dense_33 (Dense)            (None, 256)                  131328    ['dropout_19[0][0]']          \n",
      "                                                                                                  \n",
      " tf.math.multiply_11 (TFOpL  (None, 1, 512)               0         ['dense_31[0][0]',            \n",
      " ambda)                                                              'sequential_5[0][0]']        \n",
      "                                                                                                  \n",
      " batch_normalization_1 (Bat  (None, 256)                  1024      ['dense_33[0][0]']            \n",
      " chNormalization)                                                                                 \n",
      "                                                                                                  \n",
      " tf.__operators__.add_11 (T  (None, 1, 512)               0         ['layer_normalization_11[0][0]\n",
      " FOpLambda)                                                         ',                            \n",
      "                                                                     'tf.math.multiply_11[0][0]'] \n",
      "                                                                                                  \n",
      " dropout_20 (Dropout)        (None, 256)                  0         ['batch_normalization_1[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " layer_normalization_12 (La  (None, 1, 512)               1024      ['tf.__operators__.add_11[0][0\n",
      " yerNormalization)                                                  ]']                           \n",
      "                                                                                                  \n",
      " dense_34 (Dense)            (None, 128)                  32896     ['dropout_20[0][0]']          \n",
      "                                                                                                  \n",
      " multi_head_attention_6 (Mu  (None, 1, 512)               525568    ['layer_normalization_12[0][0]\n",
      " ltiHeadAttention)                                                  ',                            \n",
      "                                                                     'layer_normalization_12[0][0]\n",
      "                                                                    ']                            \n",
      "                                                                                                  \n",
      " batch_normalization_2 (Bat  (None, 128)                  512       ['dense_34[0][0]']            \n",
      " chNormalization)                                                                                 \n",
      "                                                                                                  \n",
      " global_average_pooling1d (  (None, 512)                  0         ['multi_head_attention_6[0][0]\n",
      " GlobalAveragePooling1D)                                            ']                            \n",
      "                                                                                                  \n",
      " dropout_21 (Dropout)        (None, 128)                  0         ['batch_normalization_2[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " dense_35 (Dense)            (None, 256)                  131328    ['global_average_pooling1d[0][\n",
      "                                                                    0]']                          \n",
      "                                                                                                  \n",
      " dense_36 (Dense)            (None, 256)                  33024     ['dropout_21[0][0]']          \n",
      "                                                                                                  \n",
      " concatenate (Concatenate)   (None, 512)                  0         ['dense_35[0][0]',            \n",
      "                                                                     'dense_36[0][0]']            \n",
      "                                                                                                  \n",
      " dense_37 (Dense)            (None, 256)                  131328    ['concatenate[0][0]']         \n",
      "                                                                                                  \n",
      " batch_normalization_3 (Bat  (None, 256)                  1024      ['dense_37[0][0]']            \n",
      " chNormalization)                                                                                 \n",
      "                                                                                                  \n",
      " dense_38 (Dense)            (None, 256)                  65792     ['batch_normalization_3[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " dropout_22 (Dropout)        (None, 256)                  0         ['dense_38[0][0]']            \n",
      "                                                                                                  \n",
      " tf.__operators__.add_12 (T  (None, 256)                  0         ['dropout_22[0][0]',          \n",
      " FOpLambda)                                                          'dense_37[0][0]']            \n",
      "                                                                                                  \n",
      " batch_normalization_4 (Bat  (None, 256)                  1024      ['tf.__operators__.add_12[0][0\n",
      " chNormalization)                                                   ]']                           \n",
      "                                                                                                  \n",
      " dense_39 (Dense)            (None, 128)                  32896     ['batch_normalization_4[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " dropout_23 (Dropout)        (None, 128)                  0         ['dense_39[0][0]']            \n",
      "                                                                                                  \n",
      " batch_normalization_5 (Bat  (None, 128)                  512       ['dropout_23[0][0]']          \n",
      " chNormalization)                                                                                 \n",
      "                                                                                                  \n",
      " dense_40 (Dense)            (None, 64)                   8256      ['batch_normalization_5[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      " dropout_24 (Dropout)        (None, 64)                   0         ['dense_40[0][0]']            \n",
      "                                                                                                  \n",
      " batch_normalization_6 (Bat  (None, 64)                   256       ['dropout_24[0][0]']          \n",
      " chNormalization)                                                                                 \n",
      "                                                                                                  \n",
      " dense_41 (Dense)            (None, 5)                    325       ['batch_normalization_6[0][0]'\n",
      "                                                                    ]                             \n",
      "                                                                                                  \n",
      "==================================================================================================\n",
      "Total params: 18635373 (71.09 MB)\n",
      "Trainable params: 18631661 (71.07 MB)\n",
      "Non-trainable params: 3712 (14.50 KB)\n",
      "__________________________________________________________________________________________________\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-12-08 14:50:05.929229: I tensorflow/tsl/profiler/lib/profiler_session.cc:104] Profiler session initializing.\n",
      "2024-12-08 14:50:05.929268: I tensorflow/tsl/profiler/lib/profiler_session.cc:119] Profiler session started.\n",
      "2024-12-08 14:50:05.929313: I tensorflow/compiler/xla/backends/profiler/gpu/cupti_tracer.cc:1694] Profiler found 1 GPUs\n",
      "2024-12-08 14:50:05.963777: I tensorflow/tsl/profiler/lib/profiler_session.cc:131] Profiler session tear down.\n",
      "2024-12-08 14:50:05.963883: I tensorflow/compiler/xla/backends/profiler/gpu/cupti_tracer.cc:1828] CUPTI activity buffer flushed\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Epoch 1/200\n"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-12-08 14:50:28.629786: I tensorflow/compiler/xla/service/service.cc:168] XLA service 0x773b6d05ca80 initialized for platform CUDA (this does not guarantee that XLA will be used). Devices:\n",
      "2024-12-08 14:50:28.629849: I tensorflow/compiler/xla/service/service.cc:176]   StreamExecutor device (0): NVIDIA L40, Compute Capability 8.9\n",
      "2024-12-08 14:50:28.638389: I tensorflow/compiler/mlir/tensorflow/utils/dump_mlir_util.cc:269] disabling MLIR crash reproducer, set env var `MLIR_CRASH_REPRODUCER_DIRECTORY` to enable.\n",
      "2024-12-08 14:50:28.698504: I tensorflow/compiler/xla/stream_executor/cuda/cuda_dnn.cc:442] Loaded cuDNN version 8905\n",
      "2024-12-08 14:50:28.846260: I ./tensorflow/compiler/jit/device_compiler.h:186] Compiled cluster using XLA!  This line is logged at most once for the lifetime of the process.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "80/80 [==============================] - 336s 4s/step - loss: 0.6468 - mae: 1.1652 - mape: 1425.9640 - val_loss: 0.3750 - val_mae: 0.8167 - val_mape: 357.5072 - val_olive_prod_mae: 0.8317 - val_olive_prod_mape: 540.9344 - best_olive_prod_mae: 0.8317 - val_min_oil_prod_mae: 0.8388 - val_min_oil_prod_mape: 440.9449 - best_min_oil_prod_mae: 0.8388 - val_max_oil_prod_mae: 0.7774 - val_max_oil_prod_mape: 197.5839 - best_max_oil_prod_mae: 0.7774 - val_avg_oil_prod_mae: 0.7770 - val_avg_oil_prod_mape: 158.8580 - best_avg_oil_prod_mae: 0.7770 - val_total_water_need_mae: 0.8589 - val_total_water_need_mape: 449.2141 - best_total_water_need_mae: 0.8589 - lr: 3.9500e-05\n",
      "Epoch 2/200\n",
      "80/80 [==============================] - 37s 447ms/step - loss: 0.2883 - mae: 0.6758 - mape: 853.8861 - val_loss: 0.3493 - val_mae: 0.7684 - val_mape: 759.7661 - lr: 7.9500e-05\n",
      "Epoch 3/200\n",
      "80/80 [==============================] - 37s 450ms/step - loss: 0.1705 - mae: 0.4817 - mape: 538.0516 - val_loss: 0.2085 - val_mae: 0.5459 - val_mape: 613.3998 - lr: 1.1950e-04\n",
      "Epoch 4/200\n",
      "80/80 [==============================] - 37s 448ms/step - loss: 0.1249 - mae: 0.3872 - mape: 533.9152 - val_loss: 0.1381 - val_mae: 0.4314 - val_mape: 558.7706 - lr: 1.5950e-04\n",
      "Epoch 5/200\n",
      "80/80 [==============================] - 37s 449ms/step - loss: 0.0947 - mae: 0.3178 - mape: 355.3927 - val_loss: 0.0832 - val_mae: 0.3333 - val_mape: 487.2971 - lr: 1.9950e-04\n",
      "Epoch 6/200\n",
      "80/80 [==============================] - 236s 3s/step - loss: 0.0720 - mae: 0.2658 - mape: 324.9050 - val_loss: 0.0496 - val_mae: 0.2558 - val_mape: 360.3815 - val_olive_prod_mae: 0.3133 - val_olive_prod_mape: 210.0914 - best_olive_prod_mae: 0.3133 - val_min_oil_prod_mae: 0.2730 - val_min_oil_prod_mape: 291.2299 - best_min_oil_prod_mae: 0.2730 - val_max_oil_prod_mae: 0.2761 - val_max_oil_prod_mape: 889.4504 - best_max_oil_prod_mae: 0.2761 - val_avg_oil_prod_mae: 0.2060 - val_avg_oil_prod_mape: 174.9547 - best_avg_oil_prod_mae: 0.2060 - val_total_water_need_mae: 0.2104 - val_total_water_need_mape: 236.1803 - best_total_water_need_mae: 0.2104 - lr: 2.3950e-04\n",
      "Epoch 7/200\n",
      "19/80 [======>.......................] - ETA: 20s - loss: 0.0607 - mae: 0.2401 - mape: 225.5450"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-12-08 15:02:15.855854: I tensorflow/tsl/profiler/lib/profiler_session.cc:104] Profiler session initializing.\n",
      "2024-12-08 15:02:15.855911: I tensorflow/tsl/profiler/lib/profiler_session.cc:119] Profiler session started.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "39/80 [=============>................] - ETA: 14s - loss: 0.0594 - mae: 0.2364 - mape: 234.2973"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2024-12-08 15:02:23.478578: I tensorflow/tsl/profiler/lib/profiler_session.cc:70] Profiler session collecting data.\n",
      "2024-12-08 15:02:23.647243: I tensorflow/compiler/xla/backends/profiler/gpu/cupti_tracer.cc:1828] CUPTI activity buffer flushed\n",
      "2024-12-08 15:02:27.754232: I tensorflow/compiler/xla/backends/profiler/gpu/cupti_collector.cc:541]  GpuTracer has collected 110980 callback api events and 111545 activity events. \n",
      "2024-12-08 15:03:20.421265: I tensorflow/tsl/profiler/lib/profiler_session.cc:131] Profiler session tear down.\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "80/80 [==============================] - 96s 1s/step - loss: 0.0577 - mae: 0.2319 - mape: 262.5403 - val_loss: 0.1514 - val_mae: 0.4553 - val_mape: 361.8612 - lr: 2.7950e-04\n",
      "Epoch 8/200\n",
      "80/80 [==============================] - 36s 437ms/step - loss: 0.0537 - mae: 0.2191 - mape: 247.8973 - val_loss: 0.0820 - val_mae: 0.3394 - val_mape: 311.3165 - lr: 3.1950e-04\n",
      "Epoch 9/200\n",
      "80/80 [==============================] - 36s 439ms/step - loss: 0.0457 - mae: 0.1994 - mape: 220.3584 - val_loss: 0.0261 - val_mae: 0.1699 - val_mape: 192.3138 - lr: 3.5950e-04\n",
      "Epoch 10/200\n",
      "80/80 [==============================] - 35s 436ms/step - loss: 0.0398 - mae: 0.1852 - mape: 238.8312 - val_loss: 0.0632 - val_mae: 0.3179 - val_mape: 472.3796 - lr: 3.9950e-04\n",
      "Epoch 11/200\n",
      "80/80 [==============================] - 242s 3s/step - loss: 0.0380 - mae: 0.1785 - mape: 226.0223 - val_loss: 0.0411 - val_mae: 0.2068 - val_mape: 289.5609 - val_olive_prod_mae: 0.2628 - val_olive_prod_mape: 305.8365 - best_olive_prod_mae: 0.2628 - val_min_oil_prod_mae: 0.1618 - val_min_oil_prod_mape: 150.3432 - best_min_oil_prod_mae: 0.1618 - val_max_oil_prod_mae: 0.1877 - val_max_oil_prod_mape: 491.9082 - best_max_oil_prod_mae: 0.1877 - val_avg_oil_prod_mae: 0.2413 - val_avg_oil_prod_mape: 259.4505 - val_total_water_need_mae: 0.1803 - val_total_water_need_mape: 240.2667 - best_total_water_need_mae: 0.1803 - lr: 4.3950e-04\n",
      "Epoch 12/200\n",
      "80/80 [==============================] - 36s 440ms/step - loss: 0.0367 - mae: 0.1737 - mape: 186.3838 - val_loss: 0.0223 - val_mae: 0.1476 - val_mape: 164.0920 - lr: 4.7950e-04\n",
      "Epoch 13/200\n",
      "80/80 [==============================] - 36s 441ms/step - loss: 0.0327 - mae: 0.1630 - mape: 184.1294 - val_loss: 0.0191 - val_mae: 0.1254 - val_mape: 148.7276 - lr: 3.5526e-04\n",
      "Epoch 14/200\n",
      "80/80 [==============================] - 35s 436ms/step - loss: 0.0390 - mae: 0.1759 - mape: 202.7457 - val_loss: 0.0271 - val_mae: 0.1711 - val_mape: 249.5935 - lr: 3.4603e-04\n",
      "Epoch 15/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0312 - mae: 0.1579 - mape: 155.0903 - val_loss: 0.0202 - val_mae: 0.1389 - val_mape: 188.7654 - lr: 3.3704e-04\n",
      "Epoch 16/200\n",
      "80/80 [==============================] - 260s 3s/step - loss: 0.0300 - mae: 0.1540 - mape: 163.7010 - val_loss: 0.0186 - val_mae: 0.1330 - val_mape: 187.7290 - val_olive_prod_mae: 0.1273 - val_olive_prod_mape: 106.2623 - best_olive_prod_mae: 0.1273 - val_min_oil_prod_mae: 0.1385 - val_min_oil_prod_mape: 108.2680 - best_min_oil_prod_mae: 0.1385 - val_max_oil_prod_mae: 0.1391 - val_max_oil_prod_mape: 505.4183 - best_max_oil_prod_mae: 0.1391 - val_avg_oil_prod_mae: 0.1402 - val_avg_oil_prod_mape: 103.5567 - best_avg_oil_prod_mae: 0.1402 - val_total_water_need_mae: 0.1197 - val_total_water_need_mape: 115.1407 - best_total_water_need_mae: 0.1197 - lr: 3.2829e-04\n",
      "Epoch 17/200\n",
      "80/80 [==============================] - 36s 441ms/step - loss: 0.0287 - mae: 0.1509 - mape: 154.7843 - val_loss: 0.0143 - val_mae: 0.0994 - val_mape: 142.0511 - lr: 3.1976e-04\n",
      "Epoch 18/200\n",
      "80/80 [==============================] - 36s 437ms/step - loss: 0.0285 - mae: 0.1517 - mape: 147.2274 - val_loss: 0.0308 - val_mae: 0.1751 - val_mape: 184.6624 - lr: 3.1146e-04\n",
      "Epoch 19/200\n",
      "80/80 [==============================] - 35s 435ms/step - loss: 0.0296 - mae: 0.1522 - mape: 166.7331 - val_loss: 0.0153 - val_mae: 0.1091 - val_mape: 158.7550 - lr: 3.0337e-04\n",
      "Epoch 20/200\n",
      "80/80 [==============================] - 36s 439ms/step - loss: 0.0291 - mae: 0.1503 - mape: 166.9374 - val_loss: 0.0157 - val_mae: 0.1154 - val_mape: 130.2295 - lr: 2.9549e-04\n",
      "Epoch 21/200\n",
      "80/80 [==============================] - 251s 3s/step - loss: 0.0296 - mae: 0.1512 - mape: 157.1642 - val_loss: 0.0269 - val_mae: 0.1741 - val_mape: 309.7536 - val_olive_prod_mae: 0.2191 - val_olive_prod_mape: 276.2847 - val_min_oil_prod_mae: 0.1626 - val_min_oil_prod_mape: 218.7291 - val_max_oil_prod_mae: 0.1893 - val_max_oil_prod_mape: 669.6759 - val_avg_oil_prod_mae: 0.1389 - val_avg_oil_prod_mape: 156.0846 - best_avg_oil_prod_mae: 0.1389 - val_total_water_need_mae: 0.1608 - val_total_water_need_mape: 227.9932 - lr: 2.8781e-04\n",
      "Epoch 22/200\n",
      "80/80 [==============================] - 35s 435ms/step - loss: 0.0259 - mae: 0.1422 - mape: 166.8822 - val_loss: 0.0144 - val_mae: 0.1019 - val_mape: 137.3515 - lr: 2.8034e-04\n",
      "Epoch 23/200\n",
      "80/80 [==============================] - 35s 434ms/step - loss: 0.0248 - mae: 0.1394 - mape: 143.1400 - val_loss: 0.0165 - val_mae: 0.1251 - val_mape: 158.9414 - lr: 2.7306e-04\n",
      "Epoch 24/200\n",
      "80/80 [==============================] - 35s 433ms/step - loss: 0.0247 - mae: 0.1388 - mape: 164.7200 - val_loss: 0.0146 - val_mae: 0.1058 - val_mape: 129.0920 - lr: 2.6597e-04\n",
      "Epoch 25/200\n",
      "80/80 [==============================] - 35s 435ms/step - loss: 0.0241 - mae: 0.1368 - mape: 149.9482 - val_loss: 0.0144 - val_mae: 0.1076 - val_mape: 127.7412 - lr: 2.5906e-04\n",
      "Epoch 26/200\n",
      "80/80 [==============================] - 239s 3s/step - loss: 0.0233 - mae: 0.1353 - mape: 166.1661 - val_loss: 0.0153 - val_mae: 0.1085 - val_mape: 116.0930 - val_olive_prod_mae: 0.1049 - val_olive_prod_mape: 89.0422 - best_olive_prod_mae: 0.1049 - val_min_oil_prod_mae: 0.0967 - val_min_oil_prod_mape: 100.7810 - best_min_oil_prod_mae: 0.0967 - val_max_oil_prod_mae: 0.1026 - val_max_oil_prod_mape: 147.8183 - best_max_oil_prod_mae: 0.1026 - val_avg_oil_prod_mae: 0.0993 - val_avg_oil_prod_mape: 78.5883 - best_avg_oil_prod_mae: 0.0993 - val_total_water_need_mae: 0.1389 - val_total_water_need_mape: 164.2348 - lr: 2.5233e-04\n",
      "Epoch 27/200\n",
      "80/80 [==============================] - 35s 433ms/step - loss: 0.0248 - mae: 0.1380 - mape: 152.2872 - val_loss: 0.0155 - val_mae: 0.1181 - val_mape: 110.0643 - lr: 2.4578e-04\n",
      "Epoch 28/200\n",
      "80/80 [==============================] - 36s 440ms/step - loss: 0.0230 - mae: 0.1342 - mape: 143.7230 - val_loss: 0.0139 - val_mae: 0.0997 - val_mape: 108.3232 - lr: 2.3939e-04\n",
      "Epoch 29/200\n",
      "80/80 [==============================] - 37s 449ms/step - loss: 0.0227 - mae: 0.1332 - mape: 143.9378 - val_loss: 0.0137 - val_mae: 0.0994 - val_mape: 123.0778 - lr: 2.3318e-04\n",
      "Epoch 30/200\n",
      "80/80 [==============================] - 36s 439ms/step - loss: 0.0248 - mae: 0.1380 - mape: 162.3151 - val_loss: 0.0210 - val_mae: 0.1404 - val_mape: 200.2853 - lr: 2.2712e-04\n",
      "Epoch 31/200\n",
      "80/80 [==============================] - 235s 3s/step - loss: 0.0224 - mae: 0.1323 - mape: 156.1841 - val_loss: 0.0191 - val_mae: 0.1358 - val_mape: 241.1420 - val_olive_prod_mae: 0.1327 - val_olive_prod_mape: 126.8244 - val_min_oil_prod_mae: 0.1761 - val_min_oil_prod_mape: 250.3756 - val_max_oil_prod_mae: 0.1593 - val_max_oil_prod_mape: 618.0728 - val_avg_oil_prod_mae: 0.1147 - val_avg_oil_prod_mape: 96.8866 - val_total_water_need_mae: 0.0962 - val_total_water_need_mape: 113.5497 - best_total_water_need_mae: 0.0962 - lr: 2.2122e-04\n",
      "Epoch 32/200\n",
      "80/80 [==============================] - 35s 434ms/step - loss: 0.0220 - mae: 0.1309 - mape: 150.9560 - val_loss: 0.0188 - val_mae: 0.1264 - val_mape: 163.6075 - lr: 2.1548e-04\n",
      "Epoch 33/200\n",
      "80/80 [==============================] - 36s 436ms/step - loss: 0.0229 - mae: 0.1333 - mape: 166.5528 - val_loss: 0.0165 - val_mae: 0.1219 - val_mape: 138.8618 - lr: 2.0988e-04\n",
      "Epoch 34/200\n",
      "80/80 [==============================] - 36s 440ms/step - loss: 0.0231 - mae: 0.1332 - mape: 166.1271 - val_loss: 0.0155 - val_mae: 0.1149 - val_mape: 156.8145 - lr: 2.0443e-04\n",
      "Epoch 35/200\n",
      "80/80 [==============================] - 37s 449ms/step - loss: 0.0208 - mae: 0.1283 - mape: 162.5032 - val_loss: 0.0134 - val_mae: 0.1000 - val_mape: 129.9323 - lr: 1.9912e-04\n",
      "Epoch 36/200\n",
      "80/80 [==============================] - 255s 3s/step - loss: 0.0206 - mae: 0.1278 - mape: 157.6071 - val_loss: 0.0129 - val_mae: 0.0970 - val_mape: 124.6722 - val_olive_prod_mae: 0.1012 - val_olive_prod_mape: 78.9201 - best_olive_prod_mae: 0.1012 - val_min_oil_prod_mae: 0.1006 - val_min_oil_prod_mape: 102.2447 - val_max_oil_prod_mae: 0.0998 - val_max_oil_prod_mape: 267.4592 - best_max_oil_prod_mae: 0.0998 - val_avg_oil_prod_mae: 0.0957 - val_avg_oil_prod_mape: 76.1857 - best_avg_oil_prod_mae: 0.0957 - val_total_water_need_mae: 0.0878 - val_total_water_need_mape: 98.5509 - best_total_water_need_mae: 0.0878 - lr: 1.9395e-04\n",
      "Epoch 37/200\n",
      "80/80 [==============================] - 36s 444ms/step - loss: 0.0205 - mae: 0.1267 - mape: 152.8490 - val_loss: 0.0119 - val_mae: 0.0886 - val_mape: 128.2448 - lr: 1.8891e-04\n",
      "Epoch 38/200\n",
      "80/80 [==============================] - 36s 441ms/step - loss: 0.0202 - mae: 0.1257 - mape: 136.0964 - val_loss: 0.0129 - val_mae: 0.0986 - val_mape: 124.0754 - lr: 1.8400e-04\n",
      "Epoch 39/200\n",
      "80/80 [==============================] - 37s 451ms/step - loss: 0.0201 - mae: 0.1259 - mape: 149.9923 - val_loss: 0.0116 - val_mae: 0.0882 - val_mape: 101.0983 - lr: 1.7923e-04\n",
      "Epoch 40/200\n",
      "80/80 [==============================] - 36s 438ms/step - loss: 0.0202 - mae: 0.1252 - mape: 162.2166 - val_loss: 0.0129 - val_mae: 0.0950 - val_mape: 103.8257 - lr: 1.7457e-04\n",
      "Epoch 41/200\n",
      "80/80 [==============================] - 249s 3s/step - loss: 0.0201 - mae: 0.1254 - mape: 167.0735 - val_loss: 0.0126 - val_mae: 0.0987 - val_mape: 110.8911 - val_olive_prod_mae: 0.1091 - val_olive_prod_mape: 76.9292 - val_min_oil_prod_mae: 0.1120 - val_min_oil_prod_mape: 92.9203 - val_max_oil_prod_mae: 0.0993 - val_max_oil_prod_mape: 259.1275 - best_max_oil_prod_mae: 0.0993 - val_avg_oil_prod_mae: 0.1080 - val_avg_oil_prod_mape: 77.5547 - val_total_water_need_mae: 0.0651 - val_total_water_need_mape: 47.9241 - best_total_water_need_mae: 0.0651 - lr: 1.7004e-04\n",
      "Epoch 42/200\n",
      "80/80 [==============================] - 36s 448ms/step - loss: 0.0196 - mae: 0.1243 - mape: 157.7031 - val_loss: 0.0140 - val_mae: 0.1126 - val_mape: 122.0974 - lr: 1.6562e-04\n",
      "Epoch 43/200\n",
      "80/80 [==============================] - 36s 444ms/step - loss: 0.0192 - mae: 0.1233 - mape: 137.9573 - val_loss: 0.0118 - val_mae: 0.0918 - val_mape: 119.4805 - lr: 1.6132e-04\n",
      "Epoch 44/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0192 - mae: 0.1232 - mape: 154.7663 - val_loss: 0.0117 - val_mae: 0.0897 - val_mape: 103.2779 - lr: 1.5713e-04\n",
      "Epoch 45/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0190 - mae: 0.1226 - mape: 153.6464 - val_loss: 0.0118 - val_mae: 0.0905 - val_mape: 117.5741 - lr: 1.5305e-04\n",
      "Epoch 46/200\n",
      "80/80 [==============================] - 252s 3s/step - loss: 0.0191 - mae: 0.1233 - mape: 150.4706 - val_loss: 0.0131 - val_mae: 0.0959 - val_mape: 114.9800 - val_olive_prod_mae: 0.1016 - val_olive_prod_mape: 76.1410 - val_min_oil_prod_mae: 0.1050 - val_min_oil_prod_mape: 98.5945 - val_max_oil_prod_mae: 0.1024 - val_max_oil_prod_mape: 273.3916 - val_avg_oil_prod_mae: 0.1005 - val_avg_oil_prod_mape: 77.1865 - val_total_water_need_mae: 0.0697 - val_total_water_need_mape: 49.5867 - lr: 1.4907e-04\n",
      "Epoch 47/200\n",
      "80/80 [==============================] - 36s 438ms/step - loss: 0.0187 - mae: 0.1218 - mape: 145.5018 - val_loss: 0.0117 - val_mae: 0.0899 - val_mape: 108.3948 - lr: 1.4520e-04\n",
      "Epoch 48/200\n",
      "80/80 [==============================] - 36s 444ms/step - loss: 0.0185 - mae: 0.1212 - mape: 136.5238 - val_loss: 0.0115 - val_mae: 0.0881 - val_mape: 106.7443 - lr: 1.4143e-04\n",
      "Epoch 49/200\n",
      "80/80 [==============================] - 36s 444ms/step - loss: 0.0184 - mae: 0.1209 - mape: 143.3521 - val_loss: 0.0110 - val_mae: 0.0855 - val_mape: 101.2103 - lr: 1.3776e-04\n",
      "Epoch 50/200\n",
      "80/80 [==============================] - 36s 440ms/step - loss: 0.0182 - mae: 0.1199 - mape: 127.5113 - val_loss: 0.0113 - val_mae: 0.0887 - val_mape: 111.8407 - lr: 1.3418e-04\n",
      "Epoch 51/200\n",
      "80/80 [==============================] - 245s 3s/step - loss: 0.0180 - mae: 0.1196 - mape: 154.3594 - val_loss: 0.0114 - val_mae: 0.0906 - val_mape: 105.7354 - val_olive_prod_mae: 0.0969 - val_olive_prod_mape: 73.9075 - best_olive_prod_mae: 0.0969 - val_min_oil_prod_mae: 0.0960 - val_min_oil_prod_mape: 94.2868 - best_min_oil_prod_mae: 0.0960 - val_max_oil_prod_mae: 0.0943 - val_max_oil_prod_mape: 226.3041 - best_max_oil_prod_mae: 0.0943 - val_avg_oil_prod_mae: 0.0927 - val_avg_oil_prod_mape: 68.3263 - best_avg_oil_prod_mae: 0.0927 - val_total_water_need_mae: 0.0730 - val_total_water_need_mape: 65.8522 - lr: 1.3069e-04\n",
      "Epoch 52/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0179 - mae: 0.1191 - mape: 134.7333 - val_loss: 0.0108 - val_mae: 0.0833 - val_mape: 102.4398 - lr: 1.2730e-04\n",
      "Epoch 53/200\n",
      "80/80 [==============================] - 36s 439ms/step - loss: 0.0179 - mae: 0.1194 - mape: 140.6877 - val_loss: 0.0113 - val_mae: 0.0904 - val_mape: 108.1014 - lr: 1.2399e-04\n",
      "Epoch 54/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0176 - mae: 0.1187 - mape: 139.4526 - val_loss: 0.0112 - val_mae: 0.0914 - val_mape: 99.9837 - lr: 1.2077e-04\n",
      "Epoch 55/200\n",
      "80/80 [==============================] - 36s 437ms/step - loss: 0.0173 - mae: 0.1176 - mape: 155.5683 - val_loss: 0.0109 - val_mae: 0.0872 - val_mape: 102.1552 - lr: 1.1764e-04\n",
      "Epoch 56/200\n",
      "80/80 [==============================] - 233s 3s/step - loss: 0.0173 - mae: 0.1174 - mape: 147.7777 - val_loss: 0.0110 - val_mae: 0.0888 - val_mape: 94.7351 - val_olive_prod_mae: 0.0954 - val_olive_prod_mape: 75.4792 - best_olive_prod_mae: 0.0954 - val_min_oil_prod_mae: 0.0942 - val_min_oil_prod_mape: 95.7656 - best_min_oil_prod_mae: 0.0942 - val_max_oil_prod_mae: 0.0956 - val_max_oil_prod_mape: 188.4087 - val_avg_oil_prod_mae: 0.0902 - val_avg_oil_prod_mape: 69.8961 - best_avg_oil_prod_mae: 0.0902 - val_total_water_need_mae: 0.0685 - val_total_water_need_mape: 44.1256 - lr: 1.1458e-04\n",
      "Epoch 57/200\n",
      "80/80 [==============================] - 36s 443ms/step - loss: 0.0171 - mae: 0.1171 - mape: 142.1757 - val_loss: 0.0109 - val_mae: 0.0869 - val_mape: 99.5790 - lr: 1.1161e-04\n",
      "Epoch 58/200\n",
      "80/80 [==============================] - 36s 444ms/step - loss: 0.0170 - mae: 0.1168 - mape: 138.0693 - val_loss: 0.0106 - val_mae: 0.0847 - val_mape: 96.3503 - lr: 1.0871e-04\n",
      "Epoch 59/200\n",
      "80/80 [==============================] - 247s 3s/step - loss: 0.0167 - mae: 0.1159 - mape: 136.2257 - val_loss: 0.0115 - val_mae: 0.0938 - val_mape: 98.5057 - val_olive_prod_mae: 0.1011 - val_olive_prod_mape: 72.6107 - val_min_oil_prod_mae: 0.0996 - val_min_oil_prod_mape: 90.0922 - val_max_oil_prod_mae: 0.1000 - val_max_oil_prod_mape: 218.1897 - val_avg_oil_prod_mae: 0.0983 - val_avg_oil_prod_mape: 65.7479 - val_total_water_need_mae: 0.0699 - val_total_water_need_mape: 45.8883 - lr: 1.0045e-04\n",
      "Epoch 62/200\n",
      "80/80 [==============================] - 36s 439ms/step - loss: 0.0167 - mae: 0.1166 - mape: 122.0693 - val_loss: 0.0107 - val_mae: 0.0880 - val_mape: 100.1896 - lr: 9.7846e-05\n",
      "Epoch 63/200\n",
      "80/80 [==============================] - 37s 453ms/step - loss: 0.0165 - mae: 0.1158 - mape: 135.5000 - val_loss: 0.0104 - val_mae: 0.0846 - val_mape: 95.0203 - lr: 9.5304e-05\n",
      "Epoch 64/200\n",
      "80/80 [==============================] - 36s 438ms/step - loss: 0.0163 - mae: 0.1150 - mape: 142.8906 - val_loss: 0.0107 - val_mae: 0.0892 - val_mape: 98.5729 - lr: 9.2829e-05\n",
      "Epoch 65/200\n",
      "80/80 [==============================] - 36s 440ms/step - loss: 0.0162 - mae: 0.1147 - mape: 139.8213 - val_loss: 0.0108 - val_mae: 0.0894 - val_mape: 112.6549 - lr: 9.0418e-05\n",
      "Epoch 66/200\n",
      "80/80 [==============================] - 237s 3s/step - loss: 0.0161 - mae: 0.1148 - mape: 140.7891 - val_loss: 0.0104 - val_mae: 0.0866 - val_mape: 100.1666 - val_olive_prod_mae: 0.0930 - val_olive_prod_mape: 76.0337 - best_olive_prod_mae: 0.0930 - val_min_oil_prod_mae: 0.0930 - val_min_oil_prod_mape: 93.2880 - best_min_oil_prod_mae: 0.0930 - val_max_oil_prod_mae: 0.0929 - val_max_oil_prod_mape: 222.5383 - best_max_oil_prod_mae: 0.0929 - val_avg_oil_prod_mae: 0.0891 - val_avg_oil_prod_mape: 67.5470 - best_avg_oil_prod_mae: 0.0891 - val_total_water_need_mae: 0.0648 - val_total_water_need_mape: 41.4260 - best_total_water_need_mae: 0.0648 - lr: 8.8070e-05\n",
      "Epoch 67/200\n",
      "80/80 [==============================] - 36s 439ms/step - loss: 0.0161 - mae: 0.1147 - mape: 132.5854 - val_loss: 0.0104 - val_mae: 0.0870 - val_mape: 95.3056 - lr: 8.5782e-05\n",
      "Epoch 68/200\n",
      "80/80 [==============================] - 36s 441ms/step - loss: 0.0159 - mae: 0.1139 - mape: 133.2579 - val_loss: 0.0104 - val_mae: 0.0879 - val_mape: 96.8682 - lr: 8.3555e-05\n",
      "Epoch 69/200\n",
      "80/80 [==============================] - 36s 437ms/step - loss: 0.0159 - mae: 0.1142 - mape: 134.3016 - val_loss: 0.0110 - val_mae: 0.0905 - val_mape: 102.5002 - lr: 8.1384e-05\n",
      "Epoch 70/200\n",
      "80/80 [==============================] - 36s 441ms/step - loss: 0.0158 - mae: 0.1136 - mape: 127.2419 - val_loss: 0.0104 - val_mae: 0.0881 - val_mape: 104.7385 - lr: 7.9271e-05\n",
      "Epoch 71/200\n",
      "80/80 [==============================] - 37s 450ms/step - loss: 0.0155 - mae: 0.1132 - mape: 129.8317 - val_loss: 0.0103 - val_mae: 0.0860 - val_mape: 99.2322 - lr: 7.3253e-05\n",
      "Epoch 74/200\n",
      "80/80 [==============================] - 37s 450ms/step - loss: 0.0155 - mae: 0.1131 - mape: 138.3970 - val_loss: 0.0103 - val_mae: 0.0895 - val_mape: 110.4572 - lr: 7.1351e-05\n",
      "Epoch 75/200\n",
      "80/80 [==============================] - 37s 456ms/step - loss: 0.0153 - mae: 0.1125 - mape: 134.7098 - val_loss: 0.0099 - val_mae: 0.0838 - val_mape: 101.4078 - lr: 6.9498e-05\n",
      "Epoch 76/200\n",
      "80/80 [==============================] - 209s 3s/step - loss: 0.0153 - mae: 0.1127 - mape: 133.3882 - val_loss: 0.0103 - val_mae: 0.0884 - val_mape: 101.1207 - val_olive_prod_mae: 0.0948 - val_olive_prod_mape: 73.1592 - val_min_oil_prod_mae: 0.0959 - val_min_oil_prod_mape: 92.9563 - val_max_oil_prod_mae: 0.0954 - val_max_oil_prod_mape: 230.6032 - val_avg_oil_prod_mae: 0.0924 - val_avg_oil_prod_mape: 66.7822 - val_total_water_need_mae: 0.0637 - val_total_water_need_mape: 42.1024 - lr: 6.7693e-05\n",
      "Epoch 77/200\n",
      "80/80 [==============================] - 37s 454ms/step - loss: 0.0151 - mae: 0.1123 - mape: 129.7830 - val_loss: 0.0099 - val_mae: 0.0844 - val_mape: 100.0219 - lr: 6.5935e-05\n",
      "Epoch 78/200\n",
      "80/80 [==============================] - 37s 448ms/step - loss: 0.0151 - mae: 0.1121 - mape: 128.3232 - val_loss: 0.0098 - val_mae: 0.0846 - val_mape: 104.9378 - lr: 6.4222e-05\n",
      "Epoch 79/200\n",
      "80/80 [==============================] - 36s 443ms/step - loss: 0.0150 - mae: 0.1119 - mape: 136.6634 - val_loss: 0.0098 - val_mae: 0.0831 - val_mape: 94.8757 - lr: 6.2554e-05\n",
      "Epoch 80/200\n",
      "80/80 [==============================] - 229s 3s/step - loss: 0.0149 - mae: 0.1113 - mape: 154.3307 - val_loss: 0.0097 - val_mae: 0.0837 - val_mape: 100.8595 - val_olive_prod_mae: 0.0925 - val_olive_prod_mape: 74.3520 - best_olive_prod_mae: 0.0925 - val_min_oil_prod_mae: 0.0903 - val_min_oil_prod_mape: 93.5097 - best_min_oil_prod_mae: 0.0903 - val_max_oil_prod_mae: 0.0895 - val_max_oil_prod_mape: 224.7449 - best_max_oil_prod_mae: 0.0895 - val_avg_oil_prod_mae: 0.0861 - val_avg_oil_prod_mape: 65.9836 - best_avg_oil_prod_mae: 0.0861 - val_total_water_need_mae: 0.0603 - val_total_water_need_mape: 45.7072 - lr: 5.9347e-05\n",
      "Epoch 82/200\n",
      "80/80 [==============================] - 36s 437ms/step - loss: 0.0148 - mae: 0.1116 - mape: 142.9829 - val_loss: 0.0095 - val_mae: 0.0829 - val_mape: 104.9523 - lr: 5.7806e-05\n",
      "Epoch 83/200\n",
      "80/80 [==============================] - 36s 436ms/step - loss: 0.0148 - mae: 0.1117 - mape: 137.5172 - val_loss: 0.0101 - val_mae: 0.0874 - val_mape: 100.8047 - lr: 5.6304e-05\n",
      "Epoch 84/200\n",
      "80/80 [==============================] - 36s 436ms/step - loss: 0.0147 - mae: 0.1109 - mape: 149.5698 - val_loss: 0.0097 - val_mae: 0.0862 - val_mape: 99.6029 - lr: 5.4842e-05\n",
      "Epoch 85/200\n",
      "80/80 [==============================] - 230s 3s/step - loss: 0.0146 - mae: 0.1108 - mape: 138.4030 - val_loss: 0.0096 - val_mae: 0.0849 - val_mape: 104.8251 - val_olive_prod_mae: 0.0921 - val_olive_prod_mape: 72.9428 - best_olive_prod_mae: 0.0921 - val_min_oil_prod_mae: 0.0908 - val_min_oil_prod_mape: 93.9297 - val_max_oil_prod_mae: 0.0915 - val_max_oil_prod_mape: 243.2711 - val_avg_oil_prod_mae: 0.0882 - val_avg_oil_prod_mape: 68.0993 - val_total_water_need_mae: 0.0617 - val_total_water_need_mape: 45.8819 - lr: 5.2030e-05\n",
      "Epoch 87/200\n",
      "80/80 [==============================] - 36s 440ms/step - loss: 0.0143 - mae: 0.1101 - mape: 133.9900 - val_loss: 0.0095 - val_mae: 0.0845 - val_mape: 103.7470 - lr: 4.8081e-05\n",
      "Epoch 90/200\n",
      "80/80 [==============================] - 36s 437ms/step - loss: 0.0143 - mae: 0.1101 - mape: 129.2636 - val_loss: 0.0094 - val_mae: 0.0834 - val_mape: 107.9905 - lr: 4.6832e-05\n",
      "Epoch 91/200\n",
      "80/80 [==============================] - 264s 3s/step - loss: 0.0142 - mae: 0.1103 - mape: 138.2555 - val_loss: 0.0094 - val_mae: 0.0847 - val_mape: 102.1767 - val_olive_prod_mae: 0.0921 - val_olive_prod_mape: 73.1834 - val_min_oil_prod_mae: 0.0913 - val_min_oil_prod_mape: 93.8826 - val_max_oil_prod_mae: 0.0915 - val_max_oil_prod_mape: 236.0179 - val_avg_oil_prod_mae: 0.0877 - val_avg_oil_prod_mape: 67.3626 - val_total_water_need_mae: 0.0609 - val_total_water_need_mape: 40.4373 - lr: 4.5616e-05\n",
      "Epoch 92/200\n",
      "80/80 [==============================] - 36s 436ms/step - loss: 0.0142 - mae: 0.1099 - mape: 137.3376 - val_loss: 0.0094 - val_mae: 0.0840 - val_mape: 103.3979 - lr: 4.4431e-05\n",
      "Epoch 93/200\n",
      "80/80 [==============================] - 36s 438ms/step - loss: 0.0141 - mae: 0.1097 - mape: 131.1958 - val_loss: 0.0094 - val_mae: 0.0827 - val_mape: 99.3171 - lr: 4.3277e-05\n",
      "Epoch 94/200\n",
      "80/80 [==============================] - 36s 445ms/step - loss: 0.0140 - mae: 0.1094 - mape: 129.6252 - val_loss: 0.0091 - val_mae: 0.0807 - val_mape: 99.7888 - lr: 4.2153e-05\n",
      "Epoch 95/200\n",
      "80/80 [==============================] - 36s 446ms/step - loss: 0.0141 - mae: 0.1099 - mape: 145.5369 - val_loss: 0.0092 - val_mae: 0.0828 - val_mape: 99.6861 - lr: 4.1058e-05\n",
      "Epoch 96/200\n",
      "80/80 [==============================] - 260s 3s/step - loss: 0.0143 - mae: 0.1101 - mape: 133.5272 - val_loss: 0.0095 - val_mae: 0.0844 - val_mape: 94.5343 - val_olive_prod_mae: 0.0933 - val_olive_prod_mape: 72.0466 - val_min_oil_prod_mae: 0.0915 - val_min_oil_prod_mape: 92.5089 - val_max_oil_prod_mae: 0.0918 - val_max_oil_prod_mape: 200.5542 - val_avg_oil_prod_mae: 0.0883 - val_avg_oil_prod_mape: 65.6080 - val_total_water_need_mae: 0.0571 - val_total_water_need_mape: 41.9541 - best_total_water_need_mae: 0.0571 - lr: 3.9992e-05\n",
      "Epoch 97/200\n",
      "80/80 [==============================] - 36s 437ms/step - loss: 0.0139 - mae: 0.1095 - mape: 121.2405 - val_loss: 0.0095 - val_mae: 0.0845 - val_mape: 101.0546 - lr: 3.8953e-05\n",
      "Epoch 98/200\n",
      "80/80 [==============================] - 36s 438ms/step - loss: 0.0139 - mae: 0.1094 - mape: 129.6525 - val_loss: 0.0092 - val_mae: 0.0825 - val_mape: 105.1573 - lr: 3.7941e-05\n",
      "Epoch 99/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0139 - mae: 0.1095 - mape: 127.7979 - val_loss: 0.0093 - val_mae: 0.0841 - val_mape: 106.7909 - lr: 3.6956e-05\n",
      "Epoch 100/200\n",
      "80/80 [==============================] - 36s 443ms/step - loss: 0.0138 - mae: 0.1093 - mape: 132.6569 - val_loss: 0.0092 - val_mae: 0.0826 - val_mape: 101.0493 - lr: 3.5996e-05\n",
      "Epoch 101/200\n",
      "80/80 [==============================] - 231s 3s/step - loss: 0.0138 - mae: 0.1092 - mape: 142.6844 - val_loss: 0.0091 - val_mae: 0.0822 - val_mape: 103.1497 - val_olive_prod_mae: 0.0911 - val_olive_prod_mape: 74.2005 - best_olive_prod_mae: 0.0911 - val_min_oil_prod_mae: 0.0893 - val_min_oil_prod_mape: 95.3363 - best_min_oil_prod_mae: 0.0893 - val_max_oil_prod_mae: 0.0891 - val_max_oil_prod_mape: 236.3043 - best_max_oil_prod_mae: 0.0891 - val_avg_oil_prod_mae: 0.0856 - val_avg_oil_prod_mape: 67.7334 - best_avg_oil_prod_mae: 0.0856 - val_total_water_need_mae: 0.0560 - val_total_water_need_mape: 42.1736 - best_total_water_need_mae: 0.0560 - lr: 3.5061e-05\n",
      "Epoch 102/200\n",
      "80/80 [==============================] - 36s 447ms/step - loss: 0.0137 - mae: 0.1087 - mape: 120.9930 - val_loss: 0.0092 - val_mae: 0.0840 - val_mape: 102.9081 - lr: 3.4151e-05\n",
      "Epoch 103/200\n",
      "80/80 [==============================] - 37s 453ms/step - loss: 0.0137 - mae: 0.1090 - mape: 145.3493 - val_loss: 0.0090 - val_mae: 0.0820 - val_mape: 103.3405 - lr: 3.3264e-05\n",
      "Epoch 104/200\n",
      "80/80 [==============================] - 37s 447ms/step - loss: 0.0137 - mae: 0.1088 - mape: 132.0474 - val_loss: 0.0088 - val_mae: 0.0788 - val_mape: 99.5223 - lr: 3.2400e-05\n",
      "Epoch 105/200\n",
      "80/80 [==============================] - 36s 443ms/step - loss: 0.0136 - mae: 0.1089 - mape: 143.3882 - val_loss: 0.0090 - val_mae: 0.0820 - val_mape: 102.5941 - lr: 3.1558e-05\n",
      "Epoch 106/200\n",
      "80/80 [==============================] - 233s 3s/step - loss: 0.0136 - mae: 0.1085 - mape: 130.0781 - val_loss: 0.0090 - val_mae: 0.0824 - val_mape: 100.9554 - val_olive_prod_mae: 0.0908 - val_olive_prod_mape: 73.1759 - best_olive_prod_mae: 0.0908 - val_min_oil_prod_mae: 0.0893 - val_min_oil_prod_mape: 93.9239 - val_max_oil_prod_mae: 0.0895 - val_max_oil_prod_mape: 228.2562 - val_avg_oil_prod_mae: 0.0859 - val_avg_oil_prod_mape: 66.5741 - val_total_water_need_mae: 0.0565 - val_total_water_need_mape: 42.8471 - lr: 3.0739e-05\n",
      "Epoch 107/200\n",
      "80/80 [==============================] - 36s 440ms/step - loss: 0.0136 - mae: 0.1088 - mape: 128.6352 - val_loss: 0.0090 - val_mae: 0.0816 - val_mape: 101.1500 - lr: 2.9940e-05\n",
      "Epoch 108/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0135 - mae: 0.1085 - mape: 139.2546 - val_loss: 0.0091 - val_mae: 0.0820 - val_mape: 100.3162 - lr: 2.9163e-05\n",
      "Epoch 109/200\n",
      "80/80 [==============================] - 36s 446ms/step - loss: 0.0134 - mae: 0.1086 - mape: 148.4389 - val_loss: 0.0087 - val_mae: 0.0794 - val_mape: 102.8431 - lr: 2.8405e-05\n",
      "Epoch 110/200\n",
      "80/80 [==============================] - 36s 443ms/step - loss: 0.0135 - mae: 0.1084 - mape: 126.4040 - val_loss: 0.0088 - val_mae: 0.0805 - val_mape: 100.3828 - lr: 2.7668e-05\n",
      "Epoch 111/200\n",
      "80/80 [==============================] - 245s 3s/step - loss: 0.0134 - mae: 0.1080 - mape: 138.5028 - val_loss: 0.0088 - val_mae: 0.0802 - val_mape: 103.2501 - val_olive_prod_mae: 0.0886 - val_olive_prod_mape: 73.6275 - best_olive_prod_mae: 0.0886 - val_min_oil_prod_mae: 0.0870 - val_min_oil_prod_mape: 94.9520 - best_min_oil_prod_mae: 0.0870 - val_max_oil_prod_mae: 0.0869 - val_max_oil_prod_mape: 239.6956 - best_max_oil_prod_mae: 0.0869 - val_avg_oil_prod_mae: 0.0836 - val_avg_oil_prod_mape: 67.5310 - best_avg_oil_prod_mae: 0.0836 - val_total_water_need_mae: 0.0550 - val_total_water_need_mape: 40.4444 - best_total_water_need_mae: 0.0550 - lr: 2.6949e-05\n",
      "Epoch 112/200\n",
      "80/80 [==============================] - 35s 434ms/step - loss: 0.0133 - mae: 0.1080 - mape: 140.4691 - val_loss: 0.0088 - val_mae: 0.0800 - val_mape: 100.8578 - lr: 2.6249e-05\n",
      "Epoch 113/200\n",
      "80/80 [==============================] - 36s 445ms/step - loss: 0.0133 - mae: 0.1082 - mape: 128.5066 - val_loss: 0.0087 - val_mae: 0.0808 - val_mape: 102.7916 - lr: 2.5567e-05\n",
      "Epoch 114/200\n",
      "80/80 [==============================] - 36s 443ms/step - loss: 0.0133 - mae: 0.1083 - mape: 148.3104 - val_loss: 0.0088 - val_mae: 0.0817 - val_mape: 100.1457 - lr: 2.4903e-05\n",
      "Epoch 115/200\n",
      "80/80 [==============================] - 247s 3s/step - loss: 0.0132 - mae: 0.1079 - mape: 145.9036 - val_loss: 0.0086 - val_mae: 0.0797 - val_mape: 102.3304 - val_olive_prod_mae: 0.0879 - val_olive_prod_mape: 74.0978 - best_olive_prod_mae: 0.0879 - val_min_oil_prod_mae: 0.0872 - val_min_oil_prod_mape: 94.1461 - val_max_oil_prod_mae: 0.0865 - val_max_oil_prod_mape: 235.5081 - best_max_oil_prod_mae: 0.0865 - val_avg_oil_prod_mae: 0.0833 - val_avg_oil_prod_mape: 67.5759 - best_avg_oil_prod_mae: 0.0833 - val_total_water_need_mae: 0.0535 - val_total_water_need_mape: 40.3236 - best_total_water_need_mae: 0.0535 - lr: 2.3626e-05\n",
      "Epoch 117/200\n",
      "80/80 [==============================] - 36s 436ms/step - loss: 0.0132 - mae: 0.1078 - mape: 141.3102 - val_loss: 0.0089 - val_mae: 0.0828 - val_mape: 104.7780 - lr: 2.3013e-05\n",
      "Epoch 118/200\n",
      "80/80 [==============================] - 36s 440ms/step - loss: 0.0132 - mae: 0.1081 - mape: 132.8581 - val_loss: 0.0086 - val_mae: 0.0797 - val_mape: 100.6831 - lr: 2.2415e-05\n",
      "Epoch 119/200\n",
      "80/80 [==============================] - 231s 3s/step - loss: 0.0131 - mae: 0.1078 - mape: 133.2317 - val_loss: 0.0086 - val_mae: 0.0792 - val_mape: 101.1063 - val_olive_prod_mae: 0.0877 - val_olive_prod_mape: 73.5447 - best_olive_prod_mae: 0.0877 - val_min_oil_prod_mae: 0.0867 - val_min_oil_prod_mape: 94.1456 - best_min_oil_prod_mae: 0.0867 - val_max_oil_prod_mae: 0.0868 - val_max_oil_prod_mape: 231.5488 - val_avg_oil_prod_mae: 0.0835 - val_avg_oil_prod_mape: 66.7244 - val_total_water_need_mae: 0.0511 - val_total_water_need_mape: 39.5675 - best_total_water_need_mae: 0.0511 - lr: 2.0714e-05\n",
      "Epoch 122/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0131 - mae: 0.1077 - mape: 135.5140 - val_loss: 0.0085 - val_mae: 0.0784 - val_mape: 100.5666 - lr: 2.0176e-05\n",
      "Epoch 123/200\n",
      "80/80 [==============================] - 35s 435ms/step - loss: 0.0131 - mae: 0.1077 - mape: 137.8844 - val_loss: 0.0086 - val_mae: 0.0798 - val_mape: 102.1380 - lr: 1.9652e-05\n",
      "Epoch 124/200\n",
      "80/80 [==============================] - 37s 457ms/step - loss: 0.0130 - mae: 0.1074 - mape: 126.6170 - val_loss: 0.0084 - val_mae: 0.0780 - val_mape: 98.9292 - lr: 1.9141e-05\n",
      "Epoch 125/200\n",
      "80/80 [==============================] - 36s 447ms/step - loss: 0.0130 - mae: 0.1074 - mape: 124.3804 - val_loss: 0.0085 - val_mae: 0.0795 - val_mape: 99.5572 - lr: 1.8644e-05\n",
      "Epoch 126/200\n",
      "80/80 [==============================] - 258s 3s/step - loss: 0.0130 - mae: 0.1073 - mape: 140.2923 - val_loss: 0.0085 - val_mae: 0.0792 - val_mape: 101.3633 - val_olive_prod_mae: 0.0875 - val_olive_prod_mape: 73.1242 - best_olive_prod_mae: 0.0875 - val_min_oil_prod_mae: 0.0866 - val_min_oil_prod_mape: 93.7120 - best_min_oil_prod_mae: 0.0866 - val_max_oil_prod_mae: 0.0864 - val_max_oil_prod_mape: 233.8133 - best_max_oil_prod_mae: 0.0864 - val_avg_oil_prod_mae: 0.0832 - val_avg_oil_prod_mape: 66.9306 - best_avg_oil_prod_mae: 0.0832 - val_total_water_need_mae: 0.0522 - val_total_water_need_mape: 39.2365 - lr: 1.8160e-05\n",
      "Epoch 127/200\n",
      "80/80 [==============================] - 36s 434ms/step - loss: 0.0130 - mae: 0.1072 - mape: 136.5076 - val_loss: 0.0085 - val_mae: 0.0797 - val_mape: 100.7464 - lr: 1.7688e-05\n",
      "Epoch 128/200\n",
      "80/80 [==============================] - 35s 434ms/step - loss: 0.0129 - mae: 0.1075 - mape: 138.2281 - val_loss: 0.0086 - val_mae: 0.0807 - val_mape: 99.2860 - lr: 1.7229e-05\n",
      "Epoch 129/200\n",
      "80/80 [==============================] - 36s 440ms/step - loss: 0.0130 - mae: 0.1073 - mape: 132.3837 - val_loss: 0.0085 - val_mae: 0.0796 - val_mape: 100.0050 - lr: 1.6781e-05\n",
      "Epoch 130/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0128 - mae: 0.1071 - mape: 147.8209 - val_loss: 0.0085 - val_mae: 0.0797 - val_mape: 101.9830 - lr: 1.6346e-05\n",
      "Epoch 131/200\n",
      "80/80 [==============================] - 37s 457ms/step - loss: 0.0128 - mae: 0.1070 - mape: 132.8198 - val_loss: 0.0084 - val_mae: 0.0784 - val_mape: 100.2413 - lr: 1.5105e-05\n",
      "Epoch 134/200\n",
      "80/80 [==============================] - 37s 453ms/step - loss: 0.0128 - mae: 0.1071 - mape: 122.8966 - val_loss: 0.0084 - val_mae: 0.0792 - val_mape: 99.7014 - lr: 1.4712e-05\n",
      "Epoch 135/200\n",
      "80/80 [==============================] - 37s 451ms/step - loss: 0.0128 - mae: 0.1069 - mape: 131.0883 - val_loss: 0.0085 - val_mae: 0.0802 - val_mape: 101.1884 - lr: 1.4330e-05\n",
      "Epoch 136/200\n",
      "80/80 [==============================] - 194s 2s/step - loss: 0.0128 - mae: 0.1069 - mape: 125.1339 - val_loss: 0.0084 - val_mae: 0.0795 - val_mape: 100.6931 - val_olive_prod_mae: 0.0883 - val_olive_prod_mape: 72.7114 - val_min_oil_prod_mae: 0.0870 - val_min_oil_prod_mape: 93.7169 - val_max_oil_prod_mae: 0.0869 - val_max_oil_prod_mape: 230.8019 - val_avg_oil_prod_mae: 0.0836 - val_avg_oil_prod_mape: 66.5721 - val_total_water_need_mae: 0.0517 - val_total_water_need_mape: 39.6633 - lr: 1.3958e-05\n",
      "Epoch 137/200\n",
      "80/80 [==============================] - 37s 458ms/step - loss: 0.0128 - mae: 0.1069 - mape: 141.0860 - val_loss: 0.0083 - val_mae: 0.0783 - val_mape: 99.3577 - lr: 1.3596e-05\n",
      "Epoch 138/200\n",
      "80/80 [==============================] - 38s 460ms/step - loss: 0.0128 - mae: 0.1068 - mape: 126.0336 - val_loss: 0.0084 - val_mae: 0.0791 - val_mape: 103.3938 - lr: 1.3242e-05\n",
      "Epoch 139/200\n",
      "80/80 [==============================] - 37s 460ms/step - loss: 0.0127 - mae: 0.1068 - mape: 141.5065 - val_loss: 0.0084 - val_mae: 0.0796 - val_mape: 100.4431 - lr: 1.2899e-05\n",
      "Epoch 140/200\n",
      "80/80 [==============================] - 37s 456ms/step - loss: 0.0127 - mae: 0.1068 - mape: 131.7737 - val_loss: 0.0084 - val_mae: 0.0797 - val_mape: 100.3351 - lr: 1.2564e-05\n",
      "Epoch 141/200\n",
      "80/80 [==============================] - 198s 2s/step - loss: 0.0126 - mae: 0.1066 - mape: 135.8964 - val_loss: 0.0084 - val_mae: 0.0797 - val_mape: 100.3036 - val_olive_prod_mae: 0.0880 - val_olive_prod_mape: 73.0974 - val_min_oil_prod_mae: 0.0873 - val_min_oil_prod_mape: 93.2542 - val_max_oil_prod_mae: 0.0869 - val_max_oil_prod_mape: 228.5796 - val_avg_oil_prod_mae: 0.0839 - val_avg_oil_prod_mape: 65.8285 - val_total_water_need_mae: 0.0525 - val_total_water_need_mape: 40.7585 - lr: 1.2237e-05\n",
      "Epoch 142/200\n",
      "80/80 [==============================] - 36s 445ms/step - loss: 0.0127 - mae: 0.1069 - mape: 144.6937 - val_loss: 0.0083 - val_mae: 0.0784 - val_mape: 98.5027 - lr: 1.1919e-05\n",
      "Epoch 143/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0126 - mae: 0.1066 - mape: 140.0901 - val_loss: 0.0083 - val_mae: 0.0787 - val_mape: 100.0019 - lr: 1.1610e-05\n",
      "Epoch 144/200\n",
      "80/80 [==============================] - 36s 444ms/step - loss: 0.0126 - mae: 0.1066 - mape: 142.0852 - val_loss: 0.0083 - val_mae: 0.0795 - val_mape: 101.1596 - lr: 1.1308e-05\n",
      "Epoch 145/200\n",
      "80/80 [==============================] - 239s 3s/step - loss: 0.0126 - mae: 0.1066 - mape: 132.7490 - val_loss: 0.0083 - val_mae: 0.0790 - val_mape: 100.0930 - val_olive_prod_mae: 0.0877 - val_olive_prod_mape: 73.2577 - val_min_oil_prod_mae: 0.0865 - val_min_oil_prod_mape: 93.5152 - val_max_oil_prod_mae: 0.0862 - val_max_oil_prod_mape: 226.7579 - val_avg_oil_prod_mae: 0.0830 - val_avg_oil_prod_mape: 66.1355 - val_total_water_need_mae: 0.0514 - val_total_water_need_mape: 40.7990 - lr: 1.0729e-05\n",
      "Epoch 147/200\n",
      "80/80 [==============================] - 36s 447ms/step - loss: 0.0126 - mae: 0.1063 - mape: 131.2485 - val_loss: 0.0083 - val_mae: 0.0786 - val_mape: 101.2258 - lr: 1.0450e-05\n",
      "Epoch 148/200\n",
      "80/80 [==============================] - 36s 447ms/step - loss: 0.0126 - mae: 0.1064 - mape: 117.1725 - val_loss: 0.0082 - val_mae: 0.0786 - val_mape: 102.0448 - lr: 1.0179e-05\n",
      "Epoch 149/200\n",
      "80/80 [==============================] - 36s 444ms/step - loss: 0.0126 - mae: 0.1067 - mape: 155.9800 - val_loss: 0.0082 - val_mae: 0.0778 - val_mape: 98.7628 - lr: 9.9142e-06\n",
      "Epoch 150/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0125 - mae: 0.1064 - mape: 137.1006 - val_loss: 0.0083 - val_mae: 0.0791 - val_mape: 101.1951 - lr: 9.6567e-06\n",
      "Epoch 151/200\n",
      "80/80 [==============================] - 231s 3s/step - loss: 0.0125 - mae: 0.1062 - mape: 133.0278 - val_loss: 0.0082 - val_mae: 0.0780 - val_mape: 101.7752 - val_olive_prod_mae: 0.0869 - val_olive_prod_mape: 73.5762 - val_min_oil_prod_mae: 0.0858 - val_min_oil_prod_mape: 94.9374 - best_min_oil_prod_mae: 0.0858 - val_max_oil_prod_mae: 0.0854 - val_max_oil_prod_mape: 233.4457 - best_max_oil_prod_mae: 0.0854 - val_avg_oil_prod_mae: 0.0825 - val_avg_oil_prod_mape: 66.9196 - best_avg_oil_prod_mae: 0.0825 - val_total_water_need_mae: 0.0496 - val_total_water_need_mape: 39.9972 - lr: 9.4059e-06\n",
      "Epoch 152/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0125 - mae: 0.1063 - mape: 139.6716 - val_loss: 0.0082 - val_mae: 0.0778 - val_mape: 101.3717 - lr: 9.1616e-06\n",
      "Epoch 153/200\n",
      "80/80 [==============================] - 36s 436ms/step - loss: 0.0125 - mae: 0.1063 - mape: 136.4329 - val_loss: 0.0083 - val_mae: 0.0793 - val_mape: 101.8978 - lr: 8.9236e-06\n",
      "Epoch 154/200\n",
      "80/80 [==============================] - 37s 456ms/step - loss: 0.0125 - mae: 0.1065 - mape: 136.6036 - val_loss: 0.0082 - val_mae: 0.0774 - val_mape: 100.2120 - lr: 8.6919e-06\n",
      "Epoch 155/200\n",
      "80/80 [==============================] - 36s 441ms/step - loss: 0.0124 - mae: 0.1063 - mape: 121.0664 - val_loss: 0.0083 - val_mae: 0.0795 - val_mape: 100.9535 - lr: 8.4661e-06\n",
      "Epoch 156/200\n",
      "80/80 [==============================] - 257s 3s/step - loss: 0.0125 - mae: 0.1062 - mape: 128.6301 - val_loss: 0.0082 - val_mae: 0.0785 - val_mape: 100.7175 - val_olive_prod_mae: 0.0871 - val_olive_prod_mape: 73.1359 - val_min_oil_prod_mae: 0.0860 - val_min_oil_prod_mape: 94.4131 - val_max_oil_prod_mae: 0.0856 - val_max_oil_prod_mape: 230.1177 - val_avg_oil_prod_mae: 0.0827 - val_avg_oil_prod_mape: 66.3622 - val_total_water_need_mae: 0.0511 - val_total_water_need_mape: 39.5585 - lr: 8.2462e-06\n",
      "Epoch 157/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0125 - mae: 0.1066 - mape: 140.2754 - val_loss: 0.0082 - val_mae: 0.0790 - val_mape: 100.1723 - lr: 8.0321e-06\n",
      "Epoch 158/200\n",
      "80/80 [==============================] - 36s 446ms/step - loss: 0.0125 - mae: 0.1063 - mape: 140.8587 - val_loss: 0.0082 - val_mae: 0.0784 - val_mape: 101.4369 - lr: 7.8235e-06\n",
      "Epoch 159/200\n",
      "80/80 [==============================] - 36s 438ms/step - loss: 0.0124 - mae: 0.1061 - mape: 130.1687 - val_loss: 0.0082 - val_mae: 0.0785 - val_mape: 100.3944 - lr: 7.6203e-06\n",
      "Epoch 160/200\n",
      "80/80 [==============================] - 36s 440ms/step - loss: 0.0124 - mae: 0.1062 - mape: 125.2505 - val_loss: 0.0082 - val_mae: 0.0787 - val_mape: 100.8615 - lr: 7.4224e-06\n",
      "Epoch 161/200\n",
      "80/80 [==============================] - 260s 3s/step - loss: 0.0125 - mae: 0.1064 - mape: 130.2965 - val_loss: 0.0081 - val_mae: 0.0776 - val_mape: 100.5380 - val_olive_prod_mae: 0.0866 - val_olive_prod_mape: 73.4325 - best_olive_prod_mae: 0.0866 - val_min_oil_prod_mae: 0.0857 - val_min_oil_prod_mape: 94.5307 - best_min_oil_prod_mae: 0.0857 - val_max_oil_prod_mae: 0.0854 - val_max_oil_prod_mape: 226.6531 - best_max_oil_prod_mae: 0.0854 - val_avg_oil_prod_mae: 0.0824 - val_avg_oil_prod_mape: 66.6267 - best_avg_oil_prod_mae: 0.0824 - val_total_water_need_mae: 0.0479 - val_total_water_need_mape: 41.4470 - best_total_water_need_mae: 0.0479 - lr: 7.2296e-06\n",
      "Epoch 162/200\n",
      "80/80 [==============================] - 36s 440ms/step - loss: 0.0124 - mae: 0.1062 - mape: 130.7567 - val_loss: 0.0082 - val_mae: 0.0791 - val_mape: 100.3720 - lr: 7.0418e-06\n",
      "Epoch 163/200\n",
      "80/80 [==============================] - 36s 439ms/step - loss: 0.0124 - mae: 0.1060 - mape: 123.9180 - val_loss: 0.0081 - val_mae: 0.0782 - val_mape: 101.3976 - lr: 6.8589e-06\n",
      "Epoch 164/200\n",
      "80/80 [==============================] - 36s 443ms/step - loss: 0.0124 - mae: 0.1060 - mape: 143.9072 - val_loss: 0.0081 - val_mae: 0.0784 - val_mape: 102.0180 - lr: 6.6808e-06\n",
      "Epoch 165/200\n",
      "80/80 [==============================] - 37s 453ms/step - loss: 0.0124 - mae: 0.1061 - mape: 118.9328 - val_loss: 0.0081 - val_mae: 0.0780 - val_mape: 100.5574 - lr: 6.5073e-06\n",
      "Epoch 166/200\n",
      "80/80 [==============================] - 249s 3s/step - loss: 0.0123 - mae: 0.1060 - mape: 135.2133 - val_loss: 0.0082 - val_mae: 0.0787 - val_mape: 102.0016 - val_olive_prod_mae: 0.0872 - val_olive_prod_mape: 72.9689 - val_min_oil_prod_mae: 0.0864 - val_min_oil_prod_mape: 94.5919 - val_max_oil_prod_mae: 0.0861 - val_max_oil_prod_mape: 234.7237 - val_avg_oil_prod_mae: 0.0830 - val_avg_oil_prod_mape: 66.6378 - val_total_water_need_mae: 0.0507 - val_total_water_need_mape: 41.0858 - lr: 6.3383e-06\n",
      "Epoch 167/200\n",
      "80/80 [==============================] - 36s 438ms/step - loss: 0.0123 - mae: 0.1060 - mape: 146.5321 - val_loss: 0.0081 - val_mae: 0.0780 - val_mape: 101.1286 - lr: 6.1736e-06\n",
      "Epoch 168/200\n",
      "80/80 [==============================] - 36s 445ms/step - loss: 0.0124 - mae: 0.1060 - mape: 134.5835 - val_loss: 0.0081 - val_mae: 0.0778 - val_mape: 100.2120 - lr: 6.0133e-06\n",
      "Epoch 169/200\n",
      "80/80 [==============================] - ETA: 0s - loss: 0.0123 - mae: 0.1061 - mape: 133.2897\n",
      "Epoch 169: ReduceLROnPlateau reducing learning rate to 1.1714245374605527e-06.\n",
      "80/80 [==============================] - 36s 446ms/step - loss: 0.0123 - mae: 0.1061 - mape: 133.2897 - val_loss: 0.0081 - val_mae: 0.0780 - val_mape: 100.6940 - lr: 5.8571e-06\n",
      "Epoch 170/200\n",
      "80/80 [==============================] - 36s 441ms/step - loss: 0.0123 - mae: 0.1060 - mape: 139.9322 - val_loss: 0.0081 - val_mae: 0.0781 - val_mape: 101.5473 - lr: 5.7050e-06\n",
      "Epoch 171/200\n",
      "80/80 [==============================] - 260s 3s/step - loss: 0.0124 - mae: 0.1061 - mape: 126.8107 - val_loss: 0.0080 - val_mae: 0.0774 - val_mape: 100.5322 - val_olive_prod_mae: 0.0860 - val_olive_prod_mape: 73.5066 - best_olive_prod_mae: 0.0860 - val_min_oil_prod_mae: 0.0851 - val_min_oil_prod_mape: 95.2965 - best_min_oil_prod_mae: 0.0851 - val_max_oil_prod_mae: 0.0851 - val_max_oil_prod_mape: 224.2950 - best_max_oil_prod_mae: 0.0851 - val_avg_oil_prod_mae: 0.0818 - val_avg_oil_prod_mape: 66.9162 - best_avg_oil_prod_mae: 0.0818 - val_total_water_need_mae: 0.0489 - val_total_water_need_mape: 42.6473 - lr: 5.5568e-06\n",
      "Epoch 172/200\n",
      "80/80 [==============================] - 36s 437ms/step - loss: 0.0123 - mae: 0.1060 - mape: 128.4193 - val_loss: 0.0081 - val_mae: 0.0784 - val_mape: 101.6377 - lr: 5.4125e-06\n",
      "Epoch 173/200\n",
      "80/80 [==============================] - 36s 437ms/step - loss: 0.0123 - mae: 0.1061 - mape: 125.6502 - val_loss: 0.0081 - val_mae: 0.0782 - val_mape: 101.5257 - lr: 5.2719e-06\n",
      "Epoch 174/200\n",
      "80/80 [==============================] - 241s 3s/step - loss: 0.0123 - mae: 0.1059 - mape: 133.7707 - val_loss: 0.0081 - val_mae: 0.0782 - val_mape: 102.2110 - val_olive_prod_mae: 0.0868 - val_olive_prod_mape: 72.7026 - val_min_oil_prod_mae: 0.0861 - val_min_oil_prod_mape: 94.5739 - val_max_oil_prod_mae: 0.0856 - val_max_oil_prod_mape: 236.4975 - val_avg_oil_prod_mae: 0.0826 - val_avg_oil_prod_mape: 66.7356 - val_total_water_need_mae: 0.0498 - val_total_water_need_mape: 40.5453 - lr: 4.8717e-06\n",
      "Epoch 177/200\n",
      "80/80 [==============================] - 36s 438ms/step - loss: 0.0123 - mae: 0.1059 - mape: 144.8284 - val_loss: 0.0081 - val_mae: 0.0781 - val_mape: 100.9244 - lr: 4.7452e-06\n",
      "Epoch 178/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0123 - mae: 0.1060 - mape: 135.6987 - val_loss: 0.0080 - val_mae: 0.0775 - val_mape: 100.0468 - lr: 4.6220e-06\n",
      "Epoch 179/200\n",
      "80/80 [==============================] - 37s 450ms/step - loss: 0.0123 - mae: 0.1059 - mape: 136.3290 - val_loss: 0.0080 - val_mae: 0.0775 - val_mape: 100.4255 - lr: 4.5019e-06\n",
      "Epoch 180/200\n",
      "80/80 [==============================] - 36s 446ms/step - loss: 0.0123 - mae: 0.1058 - mape: 154.4108 - val_loss: 0.0080 - val_mae: 0.0772 - val_mape: 99.4628 - lr: 4.3850e-06\n",
      "Epoch 181/200\n",
      "80/80 [==============================] - 225s 3s/step - loss: 0.0123 - mae: 0.1057 - mape: 124.7869 - val_loss: 0.0080 - val_mae: 0.0775 - val_mape: 100.7030 - val_olive_prod_mae: 0.0861 - val_olive_prod_mape: 73.0007 - val_min_oil_prod_mae: 0.0853 - val_min_oil_prod_mape: 94.6533 - val_max_oil_prod_mae: 0.0850 - val_max_oil_prod_mape: 227.6605 - best_max_oil_prod_mae: 0.0850 - val_avg_oil_prod_mae: 0.0820 - val_avg_oil_prod_mape: 66.6156 - val_total_water_need_mae: 0.0488 - val_total_water_need_mape: 41.5849 - lr: 4.2711e-06\n",
      "Epoch 182/200\n",
      "80/80 [==============================] - 36s 444ms/step - loss: 0.0123 - mae: 0.1059 - mape: 131.7903 - val_loss: 0.0080 - val_mae: 0.0776 - val_mape: 100.5481 - lr: 4.1602e-06\n",
      "Epoch 183/200\n",
      "80/80 [==============================] - 36s 442ms/step - loss: 0.0123 - mae: 0.1059 - mape: 130.2979 - val_loss: 0.0081 - val_mae: 0.0781 - val_mape: 101.2592 - lr: 4.0521e-06\n",
      "Epoch 184/200\n",
      "80/80 [==============================] - 35s 433ms/step - loss: 0.0123 - mae: 0.1059 - mape: 145.6990 - val_loss: 0.0080 - val_mae: 0.0776 - val_mape: 99.8872 - lr: 3.9469e-06\n",
      "Epoch 185/200\n",
      "80/80 [==============================] - 36s 446ms/step - loss: 0.0122 - mae: 0.1057 - mape: 132.5820 - val_loss: 0.0080 - val_mae: 0.0783 - val_mape: 102.3656 - lr: 3.8444e-06\n",
      "Epoch 186/200\n",
      "80/80 [==============================] - 255s 3s/step - loss: 0.0122 - mae: 0.1058 - mape: 142.5915 - val_loss: 0.0080 - val_mae: 0.0776 - val_mape: 100.3732 - val_olive_prod_mae: 0.0863 - val_olive_prod_mape: 72.9255 - val_min_oil_prod_mae: 0.0857 - val_min_oil_prod_mape: 94.1567 - val_max_oil_prod_mae: 0.0853 - val_max_oil_prod_mape: 226.7281 - val_avg_oil_prod_mae: 0.0823 - val_avg_oil_prod_mape: 66.1140 - val_total_water_need_mae: 0.0487 - val_total_water_need_mape: 41.9421 - lr: 3.7445e-06\n",
      "Epoch 187/200\n",
      "80/80 [==============================] - 36s 439ms/step - loss: 0.0122 - mae: 0.1058 - mape: 151.9291 - val_loss: 0.0080 - val_mae: 0.0774 - val_mape: 101.3301 - lr: 3.6473e-06\n",
      "Epoch 188/200\n",
      "80/80 [==============================] - ETA: 0s - loss: 0.0122 - mae: 0.1055 - mape: 142.8741\n",
      "Epoch 188: ReduceLROnPlateau reducing learning rate to 7.105123586370611e-07.\n",
      "80/80 [==============================] - 36s 445ms/step - loss: 0.0122 - mae: 0.1055 - mape: 142.8741 - val_loss: 0.0080 - val_mae: 0.0771 - val_mape: 100.6780 - lr: 3.5526e-06\n",
      "Epoch 189/200\n",
      "80/80 [==============================] - 36s 445ms/step - loss: 0.0122 - mae: 0.1058 - mape: 136.2120 - val_loss: 0.0080 - val_mae: 0.0775 - val_mape: 101.6683 - lr: 3.4603e-06\n",
      "Epoch 190/200\n",
      "80/80 [==============================] - 36s 444ms/step - loss: 0.0122 - mae: 0.1055 - mape: 140.9985 - val_loss: 0.0080 - val_mae: 0.0777 - val_mape: 101.3500 - lr: 3.3704e-06\n",
      "Epoch 191/200\n",
      "80/80 [==============================] - 256s 3s/step - loss: 0.0122 - mae: 0.1058 - mape: 134.9611 - val_loss: 0.0080 - val_mae: 0.0780 - val_mape: 101.2254 - val_olive_prod_mae: 0.0866 - val_olive_prod_mape: 73.1756 - val_min_oil_prod_mae: 0.0859 - val_min_oil_prod_mape: 94.5778 - val_max_oil_prod_mae: 0.0855 - val_max_oil_prod_mape: 230.5076 - val_avg_oil_prod_mae: 0.0825 - val_avg_oil_prod_mape: 66.5401 - val_total_water_need_mae: 0.0495 - val_total_water_need_mape: 41.3259 - lr: 3.2829e-06\n",
      "Epoch 192/200\n",
      "80/80 [==============================] - 36s 438ms/step - loss: 0.0122 - mae: 0.1056 - mape: 131.2589 - val_loss: 0.0080 - val_mae: 0.0773 - val_mape: 100.6354 - lr: 3.1976e-06\n",
      "Epoch 193/200\n",
      "80/80 [==============================] - 36s 438ms/step - loss: 0.0122 - mae: 0.1057 - mape: 138.6146 - val_loss: 0.0080 - val_mae: 0.0779 - val_mape: 101.6130 - lr: 3.1146e-06\n",
      "Epoch 194/200\n",
      "80/80 [==============================] - 36s 438ms/step - loss: 0.0122 - mae: 0.1059 - mape: 121.4555 - val_loss: 0.0080 - val_mae: 0.0775 - val_mape: 100.8018 - lr: 3.0337e-06\n",
      "Epoch 195/200\n",
      "80/80 [==============================] - 36s 440ms/step - loss: 0.0122 - mae: 0.1058 - mape: 126.8764 - val_loss: 0.0080 - val_mae: 0.0776 - val_mape: 100.0004 - lr: 2.9549e-06\n",
      "Epoch 196/200\n",
      "80/80 [==============================] - 226s 3s/step - loss: 0.0122 - mae: 0.1057 - mape: 139.6645 - val_loss: 0.0080 - val_mae: 0.0776 - val_mape: 100.6636 - val_olive_prod_mae: 0.0860 - val_olive_prod_mape: 73.5066 - val_min_oil_prod_mae: 0.0851 - val_min_oil_prod_mape: 95.2965 - val_max_oil_prod_mae: 0.0851 - val_max_oil_prod_mape: 224.2950 - val_avg_oil_prod_mae: 0.0818 - val_avg_oil_prod_mape: 66.9162 - val_total_water_need_mae: 0.0489 - val_total_water_need_mape: 42.6473 - lr: 2.8781e-06\n",
      "\n",
      "Modello salvato in: 2024-12-08_14-47_final_model.keras\n",
      "Warning: Could not save model: name 'json' is not defined\n"
     ]
    }
   ],
   "source": [
    "model, history = train_transformer(train_data, train_targets, val_data, val_targets, 200, 32768)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "3e2fb5a5341dac92",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "25000/25000 [==============================] - 211s 8ms/step\n",
      "\n",
      "Errori per target:\n",
      "--------------------------------------------------\n",
      "olive_prod:\n",
      "MAE assoluto: 1413.43\n",
      "Errore percentuale medio: 5.47%\n",
      "Precisione: 94.53%\n",
      "--------------------------------------------------\n",
      "min_oil_prod:\n",
      "MAE assoluto: 290.38\n",
      "Errore percentuale medio: 5.54%\n",
      "Precisione: 94.46%\n",
      "--------------------------------------------------\n",
      "max_oil_prod:\n",
      "MAE assoluto: 352.04\n",
      "Errore percentuale medio: 5.57%\n",
      "Precisione: 94.43%\n",
      "--------------------------------------------------\n",
      "avg_oil_prod:\n",
      "MAE assoluto: 308.69\n",
      "Errore percentuale medio: 5.37%\n",
      "Precisione: 94.63%\n",
      "--------------------------------------------------\n",
      "total_water_need:\n",
      "MAE assoluto: 1450.19\n",
      "Errore percentuale medio: 3.24%\n",
      "Precisione: 96.76%\n",
      "--------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "percentage_errors, absolute_errors = calculate_real_error(model, val_data, val_targets, scaler_y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "4af58aa9bbc156f5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def evaluate_model_performance(model, data, targets, set_name=\"\"):\n",
    "    \"\"\"\n",
    "    Valuta le performance del modello su un set di dati specifico.\n",
    "    \"\"\"\n",
    "    predictions = model.predict(data, verbose=0)\n",
    "\n",
    "    target_names = ['olive_prod', 'min_oil_prod', 'max_oil_prod', 'avg_oil_prod', 'total_water_need']\n",
    "    metrics = {}\n",
    "\n",
    "    for i, name in enumerate(target_names):\n",
    "        mae = np.mean(np.abs(targets[:, i] - predictions[:, i]))\n",
    "        mse = np.mean(np.square(targets[:, i] - predictions[:, i]))\n",
    "        rmse = np.sqrt(mse)\n",
    "        mape = np.mean(np.abs((targets[:, i] - predictions[:, i]) / (targets[:, i] + 1e-7))) * 100\n",
    "\n",
    "        metrics[f\"{name}_mae\"] = mae\n",
    "        metrics[f\"{name}_rmse\"] = rmse\n",
    "        metrics[f\"{name}_mape\"] = mape\n",
    "\n",
    "    if set_name:\n",
    "        print(f\"\\nPerformance sul set {set_name}:\")\n",
    "        for metric, value in metrics.items():\n",
    "            print(f\"{metric}: {value:.4f}\")\n",
    "\n",
    "    return metrics\n",
    "\n",
    "\n",
    "def retrain_model(base_model, train_data, train_targets,\n",
    "                  val_data, val_targets,\n",
    "                  test_data, test_targets,\n",
    "                  epochs=50, batch_size=128):\n",
    "    \"\"\"\n",
    "    Implementa il retraining del modello con i dati combinati.\n",
    "    \"\"\"\n",
    "    print(\"Valutazione performance iniziali del modello...\")\n",
    "    initial_metrics = {\n",
    "        'train': evaluate_model_performance(base_model, train_data, train_targets, \"training\"),\n",
    "        'val': evaluate_model_performance(base_model, val_data, val_targets, \"validazione\"),\n",
    "        'test': evaluate_model_performance(base_model, test_data, test_targets, \"test\")\n",
    "    }\n",
    "\n",
    "    # Combina i dati per il retraining\n",
    "    combined_data = {\n",
    "        'temporal': np.concatenate([train_data['temporal'], val_data['temporal'], test_data['temporal']]),\n",
    "        'static': np.concatenate([train_data['static'], val_data['static'], test_data['static']])\n",
    "    }\n",
    "    combined_targets = np.concatenate([train_targets, val_targets, test_targets])\n",
    "\n",
    "    # Crea una nuova suddivisione per la validazione\n",
    "    indices = np.arange(len(combined_targets))\n",
    "    np.random.shuffle(indices)\n",
    "\n",
    "    split_idx = int(len(indices) * 0.9)\n",
    "    train_idx, val_idx = indices[:split_idx], indices[split_idx:]\n",
    "\n",
    "    # Prepara i dati per il retraining\n",
    "    retrain_data = {k: v[train_idx] for k, v in combined_data.items()}\n",
    "    retrain_targets = combined_targets[train_idx]\n",
    "    retrain_val_data = {k: v[val_idx] for k, v in combined_data.items()}\n",
    "    retrain_val_targets = combined_targets[val_idx]\n",
    "\n",
    "    # Configura callbacks\n",
    "    callbacks = [\n",
    "        tf.keras.callbacks.EarlyStopping(\n",
    "            monitor='val_loss',\n",
    "            patience=10,\n",
    "            restore_best_weights=True,\n",
    "            min_delta=0.0001\n",
    "        ),\n",
    "        tf.keras.callbacks.ReduceLROnPlateau(\n",
    "            monitor='val_loss',\n",
    "            factor=0.2,\n",
    "            patience=5,\n",
    "            min_lr=1e-6,\n",
    "            verbose=1\n",
    "        ),\n",
    "        tf.keras.callbacks.ModelCheckpoint(\n",
    "            filepath=f'{execute_name}_retrained_best_oil_model.h5',\n",
    "            monitor='val_loss',\n",
    "            save_best_only=True,\n",
    "            mode='min',\n",
    "            save_weights_only=True\n",
    "        )\n",
    "    ]\n",
    "\n",
    "    # Imposta learning rate per il fine-tuning\n",
    "    optimizer = tf.keras.optimizers.AdamW(\n",
    "        learning_rate=tf.keras.optimizers.schedules.ExponentialDecay(\n",
    "            initial_learning_rate=1e-4,\n",
    "            decay_steps=1000,\n",
    "            decay_rate=0.9\n",
    "        ),\n",
    "        weight_decay=0.01\n",
    "    )\n",
    "\n",
    "    # Ricompila il modello con il nuovo optimizer\n",
    "    base_model.compile(\n",
    "        optimizer=optimizer,\n",
    "        loss=tf.keras.losses.Huber(),\n",
    "        metrics=['mae']\n",
    "    )\n",
    "\n",
    "    print(\"\\nAvvio retraining...\")\n",
    "    history = base_model.fit(\n",
    "        retrain_data,\n",
    "        retrain_targets,\n",
    "        validation_data=(retrain_val_data, retrain_val_targets),\n",
    "        epochs=epochs,\n",
    "        batch_size=batch_size,\n",
    "        callbacks=callbacks,\n",
    "        verbose=1\n",
    "    )\n",
    "\n",
    "    print(\"\\nValutazione performance finali...\")\n",
    "    final_metrics = {\n",
    "        'train': evaluate_model_performance(base_model, train_data, train_targets, \"training\"),\n",
    "        'val': evaluate_model_performance(base_model, val_data, val_targets, \"validazione\"),\n",
    "        'test': evaluate_model_performance(base_model, test_data, test_targets, \"test\")\n",
    "    }\n",
    "\n",
    "    # Salva il modello finale\n",
    "    save_path = f'{execute_name}_retrained_model.keras'\n",
    "    os.makedirs(f'{execute_name}_retrained/weights', exist_ok=True)\n",
    "    \n",
    "    base_model.save_weights(f'{execute_name}_retrained/weights')\n",
    "    base_model.save(save_path, save_format='keras')\n",
    "    print(f\"\\nModello riaddestrato salvato in: {save_path}\")\n",
    "\n",
    "    # Report miglioramenti\n",
    "    print(\"\\nMiglioramenti delle performance:\")\n",
    "    for dataset in ['train', 'val', 'test']:\n",
    "        print(f\"\\nSet {dataset}:\")\n",
    "        for metric in initial_metrics[dataset].keys():\n",
    "            initial = initial_metrics[dataset][metric]\n",
    "            final = final_metrics[dataset][metric]\n",
    "            improvement = ((initial - final) / initial) * 100\n",
    "            print(f\"{metric}: {improvement:.2f}% di miglioramento\")\n",
    "\n",
    "    return base_model, history, final_metrics\n",
    "\n",
    "\n",
    "def start_retraining(model_path, train_data, train_targets,\n",
    "                     val_data, val_targets,\n",
    "                     test_data, test_targets,\n",
    "                     epochs=50, batch_size=128):\n",
    "    \"\"\"\n",
    "    Avvia il processo di retraining in modo sicuro.\n",
    "    \"\"\"\n",
    "    try:\n",
    "        print(\"Caricamento del modello...\")\n",
    "        base_model = tf.keras.models.load_model(model_path, compile=False)\n",
    "        print(\"Modello caricato con successo!\")\n",
    "\n",
    "        return retrain_model(\n",
    "            base_model=base_model,\n",
    "            train_data=train_data,\n",
    "            train_targets=train_targets,\n",
    "            val_data=val_data,\n",
    "            val_targets=val_targets,\n",
    "            test_data=test_data,\n",
    "            test_targets=test_targets,\n",
    "            epochs=epochs,\n",
    "            batch_size=batch_size\n",
    "        )\n",
    "    except Exception as e:\n",
    "        print(f\"Errore durante il retraining: {str(e)}\")\n",
    "        raise"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "588c7e49371f4a0c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Caricamento del modello...\n",
      "Modello caricato con successo!\n",
      "Valutazione performance iniziali del modello...\n",
      "\n",
      "Performance sul set training:\n",
      "olive_prod_mae: 0.0860\n",
      "olive_prod_rmse: 0.1161\n",
      "olive_prod_mape: 78.0493\n",
      "min_oil_prod_mae: 0.0851\n",
      "min_oil_prod_rmse: 0.1188\n",
      "min_oil_prod_mape: 97.5077\n",
      "max_oil_prod_mae: 0.0852\n",
      "max_oil_prod_rmse: 0.1187\n",
      "max_oil_prod_mape: 153.0588\n",
      "avg_oil_prod_mae: 0.0819\n",
      "avg_oil_prod_rmse: 0.1132\n",
      "avg_oil_prod_mape: 133.1723\n",
      "total_water_need_mae: 0.0489\n",
      "total_water_need_rmse: 0.0645\n",
      "total_water_need_mape: 49.3876\n",
      "\n",
      "Performance sul set validazione:\n",
      "olive_prod_mae: 0.0860\n",
      "olive_prod_rmse: 0.1160\n",
      "olive_prod_mape: 73.4536\n",
      "min_oil_prod_mae: 0.0851\n",
      "min_oil_prod_rmse: 0.1186\n",
      "min_oil_prod_mape: 96.1195\n",
      "max_oil_prod_mae: 0.0851\n",
      "max_oil_prod_rmse: 0.1186\n",
      "max_oil_prod_mape: 284.3090\n",
      "avg_oil_prod_mae: 0.0818\n",
      "avg_oil_prod_rmse: 0.1130\n",
      "avg_oil_prod_mape: 66.9595\n",
      "total_water_need_mae: 0.0489\n",
      "total_water_need_rmse: 0.0644\n",
      "total_water_need_mape: 41.5436\n",
      "\n",
      "Performance sul set test:\n",
      "olive_prod_mae: 0.0859\n",
      "olive_prod_rmse: 0.1158\n",
      "olive_prod_mape: 85.4890\n",
      "min_oil_prod_mae: 0.0851\n",
      "min_oil_prod_rmse: 0.1187\n",
      "min_oil_prod_mape: 147.1340\n",
      "max_oil_prod_mae: 0.0850\n",
      "max_oil_prod_rmse: 0.1184\n",
      "max_oil_prod_mape: 181.8315\n",
      "avg_oil_prod_mae: 0.0818\n",
      "avg_oil_prod_rmse: 0.1130\n",
      "avg_oil_prod_mape: 83.6362\n",
      "total_water_need_mae: 0.0490\n",
      "total_water_need_rmse: 0.0645\n",
      "total_water_need_mape: 40.1340\n",
      "\n",
      "Avvio retraining...\n",
      "Epoch 1/100\n",
      "220/220 [==============================] - 65s 158ms/step - loss: 0.0145 - mae: 0.1079 - val_loss: 0.0098 - val_mae: 0.0878 - lr: 9.7719e-05\n",
      "Epoch 2/100\n",
      "220/220 [==============================] - 34s 154ms/step - loss: 0.0141 - mae: 0.1066 - val_loss: 0.0094 - val_mae: 0.0835 - lr: 9.5480e-05\n",
      "Epoch 3/100\n",
      "220/220 [==============================] - 34s 154ms/step - loss: 0.0138 - mae: 0.1059 - val_loss: 0.0095 - val_mae: 0.0857 - lr: 9.3292e-05\n",
      "Epoch 4/100\n",
      "220/220 [==============================] - 34s 155ms/step - loss: 0.0135 - mae: 0.1055 - val_loss: 0.0097 - val_mae: 0.0856 - lr: 9.1155e-05\n",
      "Epoch 5/100\n",
      "220/220 [==============================] - 34s 156ms/step - loss: 0.0133 - mae: 0.1049 - val_loss: 0.0096 - val_mae: 0.0856 - lr: 8.9066e-05\n",
      "Epoch 6/100\n",
      "220/220 [==============================] - 35s 158ms/step - loss: 0.0131 - mae: 0.1046 - val_loss: 0.0091 - val_mae: 0.0834 - lr: 8.7025e-05\n",
      "Epoch 7/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0129 - mae: 0.1040 - val_loss: 0.0092 - val_mae: 0.0827 - lr: 8.5031e-05\n",
      "Epoch 8/100\n",
      "220/220 [==============================] - 34s 154ms/step - loss: 0.0127 - mae: 0.1037 - val_loss: 0.0090 - val_mae: 0.0821 - lr: 8.3083e-05\n",
      "Epoch 9/100\n",
      "220/220 [==============================] - 33s 152ms/step - loss: 0.0125 - mae: 0.1034 - val_loss: 0.0093 - val_mae: 0.0860 - lr: 8.1179e-05\n",
      "Epoch 10/100\n",
      "220/220 [==============================] - 34s 155ms/step - loss: 0.0124 - mae: 0.1030 - val_loss: 0.0088 - val_mae: 0.0818 - lr: 7.9319e-05\n",
      "Epoch 11/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0122 - mae: 0.1026 - val_loss: 0.0091 - val_mae: 0.0821 - lr: 7.7502e-05\n",
      "Epoch 12/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0121 - mae: 0.1023 - val_loss: 0.0093 - val_mae: 0.0856 - lr: 7.5726e-05\n",
      "Epoch 13/100\n",
      "220/220 [==============================] - 35s 157ms/step - loss: 0.0120 - mae: 0.1020 - val_loss: 0.0086 - val_mae: 0.0823 - lr: 7.3991e-05\n",
      "Epoch 14/100\n",
      "220/220 [==============================] - 33s 152ms/step - loss: 0.0118 - mae: 0.1017 - val_loss: 0.0092 - val_mae: 0.0862 - lr: 7.2296e-05\n",
      "Epoch 15/100\n",
      "220/220 [==============================] - 34s 155ms/step - loss: 0.0117 - mae: 0.1015 - val_loss: 0.0086 - val_mae: 0.0804 - lr: 7.0639e-05\n",
      "Epoch 16/100\n",
      "220/220 [==============================] - 34s 154ms/step - loss: 0.0117 - mae: 0.1013 - val_loss: 0.0083 - val_mae: 0.0800 - lr: 6.9021e-05\n",
      "Epoch 17/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0116 - mae: 0.1011 - val_loss: 0.0084 - val_mae: 0.0809 - lr: 6.7439e-05\n",
      "Epoch 18/100\n",
      "220/220 [==============================] - 34s 154ms/step - loss: 0.0114 - mae: 0.1008 - val_loss: 0.0083 - val_mae: 0.0809 - lr: 6.5894e-05\n",
      "Epoch 19/100\n",
      "220/220 [==============================] - 34s 156ms/step - loss: 0.0114 - mae: 0.1006 - val_loss: 0.0081 - val_mae: 0.0804 - lr: 6.4384e-05\n",
      "Epoch 20/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0113 - mae: 0.1004 - val_loss: 0.0088 - val_mae: 0.0837 - lr: 6.2909e-05\n",
      "Epoch 21/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0112 - mae: 0.1002 - val_loss: 0.0084 - val_mae: 0.0812 - lr: 6.1468e-05\n",
      "Epoch 22/100\n",
      "220/220 [==============================] - 34s 156ms/step - loss: 0.0111 - mae: 0.1000 - val_loss: 0.0080 - val_mae: 0.0806 - lr: 6.0059e-05\n",
      "Epoch 23/100\n",
      "220/220 [==============================] - 34s 154ms/step - loss: 0.0110 - mae: 0.0999 - val_loss: 0.0084 - val_mae: 0.0808 - lr: 5.8683e-05\n",
      "Epoch 24/100\n",
      "220/220 [==============================] - 34s 155ms/step - loss: 0.0110 - mae: 0.0997 - val_loss: 0.0079 - val_mae: 0.0803 - lr: 5.7338e-05\n",
      "Epoch 25/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0109 - mae: 0.0995 - val_loss: 0.0081 - val_mae: 0.0806 - lr: 5.6025e-05\n",
      "Epoch 26/100\n",
      "220/220 [==============================] - 34s 154ms/step - loss: 0.0108 - mae: 0.0993 - val_loss: 0.0078 - val_mae: 0.0788 - lr: 5.4741e-05\n",
      "Epoch 27/100\n",
      "220/220 [==============================] - 33s 152ms/step - loss: 0.0108 - mae: 0.0992 - val_loss: 0.0082 - val_mae: 0.0820 - lr: 5.3487e-05\n",
      "Epoch 28/100\n",
      "220/220 [==============================] - 33s 152ms/step - loss: 0.0107 - mae: 0.0992 - val_loss: 0.0079 - val_mae: 0.0792 - lr: 5.2261e-05\n",
      "Epoch 29/100\n",
      "220/220 [==============================] - 34s 154ms/step - loss: 0.0107 - mae: 0.0990 - val_loss: 0.0082 - val_mae: 0.0804 - lr: 5.1064e-05\n",
      "Epoch 30/100\n",
      "220/220 [==============================] - 34s 155ms/step - loss: 0.0106 - mae: 0.0988 - val_loss: 0.0077 - val_mae: 0.0787 - lr: 4.9894e-05\n",
      "Epoch 31/100\n",
      "220/220 [==============================] - 33s 151ms/step - loss: 0.0106 - mae: 0.0987 - val_loss: 0.0078 - val_mae: 0.0806 - lr: 4.8751e-05\n",
      "Epoch 32/100\n",
      "220/220 [==============================] - 35s 159ms/step - loss: 0.0105 - mae: 0.0986 - val_loss: 0.0073 - val_mae: 0.0774 - lr: 4.7634e-05\n",
      "Epoch 33/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0105 - mae: 0.0985 - val_loss: 0.0075 - val_mae: 0.0794 - lr: 4.6542e-05\n",
      "Epoch 34/100\n",
      "220/220 [==============================] - 35s 158ms/step - loss: 0.0104 - mae: 0.0983 - val_loss: 0.0072 - val_mae: 0.0776 - lr: 4.5476e-05\n",
      "Epoch 35/100\n",
      "220/220 [==============================] - 35s 157ms/step - loss: 0.0104 - mae: 0.0982 - val_loss: 0.0073 - val_mae: 0.0767 - lr: 4.4434e-05\n",
      "Epoch 36/100\n",
      "220/220 [==============================] - 34s 155ms/step - loss: 0.0103 - mae: 0.0982 - val_loss: 0.0073 - val_mae: 0.0782 - lr: 4.3416e-05\n",
      "Epoch 37/100\n",
      "220/220 [==============================] - 34s 155ms/step - loss: 0.0103 - mae: 0.0981 - val_loss: 0.0071 - val_mae: 0.0770 - lr: 4.2421e-05\n",
      "Epoch 38/100\n",
      "220/220 [==============================] - 33s 152ms/step - loss: 0.0103 - mae: 0.0979 - val_loss: 0.0071 - val_mae: 0.0770 - lr: 4.1449e-05\n",
      "Epoch 39/100\n",
      "220/220 [==============================] - ETA: 0s - loss: 0.0102 - mae: 0.0978\n",
      "Epoch 39: ReduceLROnPlateau reducing learning rate to 8.099838305497542e-06.\n",
      "220/220 [==============================] - 33s 152ms/step - loss: 0.0102 - mae: 0.0978 - val_loss: 0.0073 - val_mae: 0.0779 - lr: 4.0499e-05\n",
      "Epoch 40/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0102 - mae: 0.0977 - val_loss: 0.0071 - val_mae: 0.0772 - lr: 3.9571e-05\n",
      "Epoch 41/100\n",
      "220/220 [==============================] - 34s 154ms/step - loss: 0.0101 - mae: 0.0976 - val_loss: 0.0069 - val_mae: 0.0756 - lr: 3.8665e-05\n",
      "Epoch 42/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0101 - mae: 0.0976 - val_loss: 0.0073 - val_mae: 0.0773 - lr: 3.7779e-05\n",
      "Epoch 43/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0101 - mae: 0.0974 - val_loss: 0.0071 - val_mae: 0.0760 - lr: 3.6913e-05\n",
      "Epoch 44/100\n",
      "220/220 [==============================] - 33s 152ms/step - loss: 0.0101 - mae: 0.0974 - val_loss: 0.0070 - val_mae: 0.0762 - lr: 3.6067e-05\n",
      "Epoch 45/100\n",
      "220/220 [==============================] - 34s 154ms/step - loss: 0.0100 - mae: 0.0972 - val_loss: 0.0071 - val_mae: 0.0774 - lr: 3.5241e-05\n",
      "Epoch 46/100\n",
      "220/220 [==============================] - 34s 155ms/step - loss: 0.0100 - mae: 0.0972 - val_loss: 0.0068 - val_mae: 0.0748 - lr: 3.4433e-05\n",
      "Epoch 47/100\n",
      "220/220 [==============================] - 34s 152ms/step - loss: 0.0100 - mae: 0.0972 - val_loss: 0.0074 - val_mae: 0.0790 - lr: 3.3644e-05\n",
      "Epoch 48/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0099 - mae: 0.0971 - val_loss: 0.0069 - val_mae: 0.0759 - lr: 3.2874e-05\n",
      "Epoch 49/100\n",
      "220/220 [==============================] - 34s 152ms/step - loss: 0.0099 - mae: 0.0970 - val_loss: 0.0069 - val_mae: 0.0768 - lr: 3.2120e-05\n",
      "Epoch 50/100\n",
      "220/220 [==============================] - 33s 151ms/step - loss: 0.0099 - mae: 0.0969 - val_loss: 0.0070 - val_mae: 0.0758 - lr: 3.1384e-05\n",
      "Epoch 51/100\n",
      "220/220 [==============================] - ETA: 0s - loss: 0.0099 - mae: 0.0969\n",
      "Epoch 51: ReduceLROnPlateau reducing learning rate to 6.133050192147493e-06.\n",
      "220/220 [==============================] - 33s 151ms/step - loss: 0.0099 - mae: 0.0969 - val_loss: 0.0068 - val_mae: 0.0757 - lr: 3.0665e-05\n",
      "Epoch 52/100\n",
      "220/220 [==============================] - 34s 154ms/step - loss: 0.0099 - mae: 0.0968 - val_loss: 0.0068 - val_mae: 0.0753 - lr: 2.9963e-05\n",
      "Epoch 53/100\n",
      "220/220 [==============================] - 33s 152ms/step - loss: 0.0098 - mae: 0.0967 - val_loss: 0.0069 - val_mae: 0.0761 - lr: 2.9276e-05\n",
      "Epoch 54/100\n",
      "220/220 [==============================] - 33s 152ms/step - loss: 0.0098 - mae: 0.0966 - val_loss: 0.0068 - val_mae: 0.0754 - lr: 2.8605e-05\n",
      "Epoch 55/100\n",
      "220/220 [==============================] - 34s 155ms/step - loss: 0.0098 - mae: 0.0966 - val_loss: 0.0067 - val_mae: 0.0750 - lr: 2.7950e-05\n",
      "Epoch 56/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0097 - mae: 0.0965 - val_loss: 0.0066 - val_mae: 0.0748 - lr: 2.7309e-05\n",
      "Epoch 57/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0097 - mae: 0.0964 - val_loss: 0.0066 - val_mae: 0.0746 - lr: 2.6684e-05\n",
      "Epoch 58/100\n",
      "220/220 [==============================] - 33s 152ms/step - loss: 0.0097 - mae: 0.0964 - val_loss: 0.0067 - val_mae: 0.0748 - lr: 2.6072e-05\n",
      "Epoch 59/100\n",
      "220/220 [==============================] - 33s 151ms/step - loss: 0.0097 - mae: 0.0964 - val_loss: 0.0066 - val_mae: 0.0740 - lr: 2.5475e-05\n",
      "Epoch 60/100\n",
      "220/220 [==============================] - 34s 152ms/step - loss: 0.0097 - mae: 0.0963 - val_loss: 0.0070 - val_mae: 0.0760 - lr: 2.4891e-05\n",
      "Epoch 61/100\n",
      "220/220 [==============================] - ETA: 0s - loss: 0.0097 - mae: 0.0962\n",
      "Epoch 61: ReduceLROnPlateau reducing learning rate to 4.864184666075744e-06.\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0097 - mae: 0.0962 - val_loss: 0.0067 - val_mae: 0.0754 - lr: 2.4321e-05\n",
      "Epoch 62/100\n",
      "220/220 [==============================] - 33s 152ms/step - loss: 0.0096 - mae: 0.0962 - val_loss: 0.0067 - val_mae: 0.0751 - lr: 2.3764e-05\n",
      "Epoch 63/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0096 - mae: 0.0962 - val_loss: 0.0065 - val_mae: 0.0746 - lr: 2.3219e-05\n",
      "Epoch 64/100\n",
      "220/220 [==============================] - 33s 151ms/step - loss: 0.0096 - mae: 0.0960 - val_loss: 0.0067 - val_mae: 0.0754 - lr: 2.2687e-05\n",
      "Epoch 65/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0096 - mae: 0.0960 - val_loss: 0.0065 - val_mae: 0.0743 - lr: 2.2167e-05\n",
      "Epoch 66/100\n",
      "220/220 [==============================] - 34s 153ms/step - loss: 0.0096 - mae: 0.0961 - val_loss: 0.0066 - val_mae: 0.0744 - lr: 2.1659e-05\n",
      "Epoch 67/100\n",
      "220/220 [==============================] - 34s 155ms/step - loss: 0.0096 - mae: 0.0960 - val_loss: 0.0064 - val_mae: 0.0739 - lr: 2.1163e-05\n",
      "Epoch 68/100\n",
      "220/220 [==============================] - ETA: 0s - loss: 0.0095 - mae: 0.0959\n",
      "Epoch 68: ReduceLROnPlateau reducing learning rate to 4.135647031944245e-06.\n",
      "220/220 [==============================] - 34s 152ms/step - loss: 0.0095 - mae: 0.0959 - val_loss: 0.0066 - val_mae: 0.0742 - lr: 2.0678e-05\n",
      "Epoch 69/100\n",
      "220/220 [==============================] - 34s 152ms/step - loss: 0.0095 - mae: 0.0959 - val_loss: 0.0065 - val_mae: 0.0750 - lr: 2.0204e-05\n",
      "Epoch 70/100\n",
      "220/220 [==============================] - 33s 152ms/step - loss: 0.0095 - mae: 0.0959 - val_loss: 0.0066 - val_mae: 0.0749 - lr: 1.9742e-05\n",
      "Epoch 71/100\n",
      "220/220 [==============================] - 33s 152ms/step - loss: 0.0095 - mae: 0.0959 - val_loss: 0.0065 - val_mae: 0.0745 - lr: 1.9289e-05\n",
      "Epoch 72/100\n",
      "220/220 [==============================] - 34s 154ms/step - loss: 0.0095 - mae: 0.0958 - val_loss: 0.0064 - val_mae: 0.0741 - lr: 1.8847e-05\n",
      "Epoch 73/100\n",
      "220/220 [==============================] - ETA: 0s - loss: 0.0095 - mae: 0.0957\n",
      "Epoch 73: ReduceLROnPlateau reducing learning rate to 3.683072645799257e-06.\n",
      "220/220 [==============================] - 34s 152ms/step - loss: 0.0095 - mae: 0.0957 - val_loss: 0.0065 - val_mae: 0.0736 - lr: 1.8415e-05\n",
      "\n",
      "Valutazione performance finali...\n",
      "\n",
      "Performance sul set training:\n",
      "olive_prod_mae: 0.0840\n",
      "olive_prod_rmse: 0.1148\n",
      "olive_prod_mape: 77.0417\n",
      "min_oil_prod_mae: 0.0818\n",
      "min_oil_prod_rmse: 0.1149\n",
      "min_oil_prod_mape: 96.4133\n",
      "max_oil_prod_mae: 0.0816\n",
      "max_oil_prod_rmse: 0.1144\n",
      "max_oil_prod_mape: 149.8516\n",
      "avg_oil_prod_mae: 0.0798\n",
      "avg_oil_prod_rmse: 0.1114\n",
      "avg_oil_prod_mape: 125.5588\n",
      "total_water_need_mae: 0.0456\n",
      "total_water_need_rmse: 0.0610\n",
      "total_water_need_mape: 45.2614\n",
      "\n",
      "Performance sul set validazione:\n",
      "olive_prod_mae: 0.0839\n",
      "olive_prod_rmse: 0.1146\n",
      "olive_prod_mape: 72.7076\n",
      "min_oil_prod_mae: 0.0817\n",
      "min_oil_prod_rmse: 0.1147\n",
      "min_oil_prod_mape: 95.6358\n",
      "max_oil_prod_mae: 0.0815\n",
      "max_oil_prod_rmse: 0.1141\n",
      "max_oil_prod_mape: 271.0537\n",
      "avg_oil_prod_mae: 0.0798\n",
      "avg_oil_prod_rmse: 0.1112\n",
      "avg_oil_prod_mape: 64.6259\n",
      "total_water_need_mae: 0.0456\n",
      "total_water_need_rmse: 0.0609\n",
      "total_water_need_mape: 39.7287\n",
      "\n",
      "Performance sul set test:\n",
      "olive_prod_mae: 0.0838\n",
      "olive_prod_rmse: 0.1145\n",
      "olive_prod_mape: 82.7762\n",
      "min_oil_prod_mae: 0.0817\n",
      "min_oil_prod_rmse: 0.1147\n",
      "min_oil_prod_mape: 146.3828\n",
      "max_oil_prod_mae: 0.0815\n",
      "max_oil_prod_rmse: 0.1141\n",
      "max_oil_prod_mape: 171.8919\n",
      "avg_oil_prod_mae: 0.0797\n",
      "avg_oil_prod_rmse: 0.1112\n",
      "avg_oil_prod_mape: 78.1842\n",
      "total_water_need_mae: 0.0456\n",
      "total_water_need_rmse: 0.0610\n",
      "total_water_need_mape: 38.6316\n",
      "\n",
      "Modello riaddestrato salvato in: 2024-12-08_14-47_retrained_model.keras\n",
      "\n",
      "Miglioramenti delle performance:\n",
      "\n",
      "Set train:\n",
      "olive_prod_mae: 2.37% di miglioramento\n",
      "olive_prod_rmse: 1.12% di miglioramento\n",
      "olive_prod_mape: 1.29% di miglioramento\n",
      "min_oil_prod_mae: 3.93% di miglioramento\n",
      "min_oil_prod_rmse: 3.30% di miglioramento\n",
      "min_oil_prod_mape: 1.12% di miglioramento\n",
      "max_oil_prod_mae: 4.13% di miglioramento\n",
      "max_oil_prod_rmse: 3.68% di miglioramento\n",
      "max_oil_prod_mape: 2.10% di miglioramento\n",
      "avg_oil_prod_mae: 2.51% di miglioramento\n",
      "avg_oil_prod_rmse: 1.57% di miglioramento\n",
      "avg_oil_prod_mape: 5.72% di miglioramento\n",
      "total_water_need_mae: 6.84% di miglioramento\n",
      "total_water_need_rmse: 5.40% di miglioramento\n",
      "total_water_need_mape: 8.35% di miglioramento\n",
      "\n",
      "Set val:\n",
      "olive_prod_mae: 2.39% di miglioramento\n",
      "olive_prod_rmse: 1.17% di miglioramento\n",
      "olive_prod_mape: 1.02% di miglioramento\n",
      "min_oil_prod_mae: 3.93% di miglioramento\n",
      "min_oil_prod_rmse: 3.33% di miglioramento\n",
      "min_oil_prod_mape: 0.50% di miglioramento\n",
      "max_oil_prod_mae: 4.18% di miglioramento\n",
      "max_oil_prod_rmse: 3.75% di miglioramento\n",
      "max_oil_prod_mape: 4.66% di miglioramento\n",
      "avg_oil_prod_mae: 2.54% di miglioramento\n",
      "avg_oil_prod_rmse: 1.64% di miglioramento\n",
      "avg_oil_prod_mape: 3.49% di miglioramento\n",
      "total_water_need_mae: 6.85% di miglioramento\n",
      "total_water_need_rmse: 5.38% di miglioramento\n",
      "total_water_need_mape: 4.37% di miglioramento\n",
      "\n",
      "Set test:\n",
      "olive_prod_mae: 2.39% di miglioramento\n",
      "olive_prod_rmse: 1.12% di miglioramento\n",
      "olive_prod_mape: 3.17% di miglioramento\n",
      "min_oil_prod_mae: 3.98% di miglioramento\n",
      "min_oil_prod_rmse: 3.38% di miglioramento\n",
      "min_oil_prod_mape: 0.51% di miglioramento\n",
      "max_oil_prod_mae: 4.14% di miglioramento\n",
      "max_oil_prod_rmse: 3.69% di miglioramento\n",
      "max_oil_prod_mape: 5.47% di miglioramento\n",
      "avg_oil_prod_mae: 2.54% di miglioramento\n",
      "avg_oil_prod_rmse: 1.60% di miglioramento\n",
      "avg_oil_prod_mape: 6.52% di miglioramento\n",
      "total_water_need_mae: 6.87% di miglioramento\n",
      "total_water_need_rmse: 5.43% di miglioramento\n",
      "total_water_need_mape: 3.74% di miglioramento\n"
     ]
    }
   ],
   "source": [
    "model_path = f'{execute_name}_final_model.keras'\n",
    "\n",
    "retrained_model, retrain_history, final_metrics = start_retraining(\n",
    "    model_path=model_path,\n",
    "    train_data=train_data,\n",
    "    train_targets=train_targets,\n",
    "    val_data=val_data,\n",
    "    val_targets=val_targets,\n",
    "    test_data=test_data,\n",
    "    test_targets=test_targets,\n",
    "    epochs=100,\n",
    "    batch_size=16384\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "a95606bc-c4bc-418a-acdb-2e24c30dfa81",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "25000/25000 [==============================] - 247s 10ms/step\n",
      "\n",
      "Errori per target:\n",
      "--------------------------------------------------\n",
      "olive_prod:\n",
      "MAE assoluto: 1379.63\n",
      "Errore percentuale medio: 5.07%\n",
      "Precisione: 94.93%\n",
      "--------------------------------------------------\n",
      "min_oil_prod:\n",
      "MAE assoluto: 278.96\n",
      "Errore percentuale medio: 5.09%\n",
      "Precisione: 94.91%\n",
      "--------------------------------------------------\n",
      "max_oil_prod:\n",
      "MAE assoluto: 337.33\n",
      "Errore percentuale medio: 5.13%\n",
      "Precisione: 94.87%\n",
      "--------------------------------------------------\n",
      "avg_oil_prod:\n",
      "MAE assoluto: 300.85\n",
      "Errore percentuale medio: 4.98%\n",
      "Precisione: 95.02%\n",
      "--------------------------------------------------\n",
      "total_water_need:\n",
      "MAE assoluto: 1350.83\n",
      "Errore percentuale medio: 2.90%\n",
      "Precisione: 97.10%\n",
      "--------------------------------------------------\n"
     ]
    }
   ],
   "source": [
    "percentage_errors, absolute_errors = calculate_real_error(retrained_model, val_data, val_targets, scaler_y)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "e6f357cb-56a4-4f19-a4e8-77b73a28329d",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import tensorflow as tf\n",
    "import matplotlib.pyplot as plt\n",
    "from typing import List, Dict, Tuple, Union\n",
    "\n",
    "def analyze_feature_importance(model: tf.keras.Model, \n",
    "                             test_data: dict, \n",
    "                             feature_names: List[str]) -> Dict[str, float]:\n",
    "    \"\"\"\n",
    "    Analizza l'importanza delle feature usando perturbazione.\n",
    "    \n",
    "    Args:\n",
    "        model: Modello TensorFlow addestrato\n",
    "        test_data: Dizionario con chiavi 'temporal' e 'static' contenenti i dati\n",
    "        feature_names: Lista dei nomi delle feature\n",
    "        \n",
    "    Returns:\n",
    "        dict: Dizionario con l'importanza relativa di ogni feature\n",
    "    \"\"\"\n",
    "    # Estrai i dati temporali e statici\n",
    "    temporal_data = test_data['temporal']\n",
    "    static_data = test_data['static']\n",
    "    \n",
    "    # Ottieni la predizione base\n",
    "    base_prediction = model.predict(test_data)\n",
    "    feature_importance = {}\n",
    "    \n",
    "    # Per ogni feature temporale\n",
    "    for i, feature in enumerate(feature_names):\n",
    "        if feature in ['temp_mean', 'precip_sum', 'solar_energy_sum']:\n",
    "            # Crea copia perturbata dei dati\n",
    "            perturbed_data = {\n",
    "                'temporal': temporal_data.copy(),\n",
    "                'static': static_data.copy()\n",
    "            }\n",
    "            \n",
    "            # Trova l'indice della feature temporale\n",
    "            temp_idx = ['temp_mean', 'precip_sum', 'solar_energy_sum'].index(feature)\n",
    "            \n",
    "            # Crea rumore per la feature temporale\n",
    "            feature_values = temporal_data[..., temp_idx]\n",
    "            noise = np.random.normal(0, np.std(feature_values) * 0.1, \n",
    "                                   size=feature_values.shape)\n",
    "            \n",
    "            # Applica il rumore alla feature temporale\n",
    "            perturbed_temporal = perturbed_data['temporal'].copy()\n",
    "            perturbed_temporal[..., temp_idx] = feature_values + noise\n",
    "            perturbed_data['temporal'] = perturbed_temporal\n",
    "            \n",
    "        else:  # Feature statiche\n",
    "            # Crea copia perturbata dei dati\n",
    "            perturbed_data = {\n",
    "                'temporal': temporal_data.copy(),\n",
    "                'static': static_data.copy()\n",
    "            }\n",
    "            \n",
    "            # Trova l'indice della feature statica\n",
    "            static_idx = ['ha'].index(feature)\n",
    "            \n",
    "            # Crea rumore per la feature statica\n",
    "            feature_values = static_data[..., static_idx]\n",
    "            noise = np.random.normal(0, np.std(feature_values) * 0.1, \n",
    "                                   size=feature_values.shape)\n",
    "            \n",
    "            # Applica il rumore alla feature statica\n",
    "            perturbed_static = perturbed_data['static'].copy()\n",
    "            perturbed_static[..., static_idx] = feature_values + noise\n",
    "            perturbed_data['static'] = perturbed_static\n",
    "        \n",
    "        # Calcola nuova predizione\n",
    "        perturbed_prediction = model.predict(perturbed_data)\n",
    "        \n",
    "        # Calcola impatto della perturbazione\n",
    "        impact = np.mean(np.abs(perturbed_prediction - base_prediction))\n",
    "        feature_importance[feature] = float(impact)\n",
    "    \n",
    "    # Normalizza le importanze\n",
    "    total_importance = sum(feature_importance.values())\n",
    "    feature_importance = {k: v/total_importance \n",
    "                         for k, v in feature_importance.items()}\n",
    "    \n",
    "    return feature_importance\n",
    "\n",
    "class ProbabilityFunctions:\n",
    "    @staticmethod\n",
    "    def calculate_statistics(data: Union[np.ndarray, tf.Tensor]) -> Dict[str, float]:\n",
    "        \"\"\"\n",
    "        Calcola statistiche di base usando TensorFlow.\n",
    "        \n",
    "        Args:\n",
    "            data: Tensor o array dei dati\n",
    "            \n",
    "        Returns:\n",
    "            dict: Dizionario con le statistiche\n",
    "        \"\"\"\n",
    "        if not isinstance(data, tf.Tensor):\n",
    "            data = tf.convert_to_tensor(data, dtype=tf.float32)\n",
    "            \n",
    "        mean = tf.reduce_mean(data)\n",
    "        # Calcola varianza manualmente\n",
    "        squared_deviations = tf.square(data - mean)\n",
    "        variance = tf.reduce_mean(squared_deviations)\n",
    "        std = tf.sqrt(variance)\n",
    "        \n",
    "        # Ordina il tensor per il calcolo della mediana\n",
    "        sorted_data = tf.sort(data)\n",
    "        size = tf.size(data)\n",
    "        mid_index = size // 2\n",
    "        median = sorted_data[mid_index]\n",
    "        \n",
    "        return {\n",
    "            'mean': mean.numpy(),\n",
    "            'variance': variance.numpy(),\n",
    "            'std': std.numpy(),\n",
    "            'min': tf.reduce_min(data).numpy(),\n",
    "            'max': tf.reduce_max(data).numpy(),\n",
    "            'median': median.numpy()\n",
    "        }\n",
    "\n",
    "    @staticmethod\n",
    "    def calculate_pmf(data: np.ndarray, bins: int = 50) -> Tuple[np.ndarray, np.ndarray, np.ndarray]:\n",
    "        \"\"\"\n",
    "        Calcola la Probability Mass Function (PMF) dei dati.\n",
    "        \n",
    "        Args:\n",
    "            data: Array di dati\n",
    "            bins: Numero di bin per l'istogramma\n",
    "            \n",
    "        Returns:\n",
    "            tuple: (bin_centers, pmf, bin_edges)\n",
    "        \"\"\"\n",
    "        # Calcola l'istogramma\n",
    "        hist, bin_edges = np.histogram(data, bins=bins, density=True)\n",
    "        \n",
    "        # Calcola i centri dei bin\n",
    "        bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2\n",
    "        \n",
    "        # Normalizza per ottenere la PMF\n",
    "        pmf = hist / np.sum(hist)\n",
    "        \n",
    "        return bin_centers, pmf, bin_edges\n",
    "\n",
    "    @staticmethod\n",
    "    def calculate_cmf(pmf: np.ndarray) -> np.ndarray:\n",
    "        \"\"\"\n",
    "        Calcola la Cumulative Mass Function (CMF) dalla PMF.\n",
    "        \n",
    "        Args:\n",
    "            pmf: Probability Mass Function\n",
    "            \n",
    "        Returns:\n",
    "            array: Cumulative Mass Function\n",
    "        \"\"\"\n",
    "        return np.cumsum(pmf)\n",
    "\n",
    "    def plot_distributions(self, data: np.ndarray, \n",
    "                         bins: int = 50, \n",
    "                         title: str = \"Distribuzione\") -> Tuple[np.ndarray, np.ndarray, np.ndarray]:\n",
    "        \"\"\"\n",
    "        Calcola e visualizza PMF e CMF delle distribuzioni.\n",
    "        \n",
    "        Args:\n",
    "            data: Array di dati da analizzare\n",
    "            bins: Numero di bin per l'istogramma\n",
    "            title: Titolo del grafico\n",
    "            \n",
    "        Returns:\n",
    "            tuple: (bin_centers, pmf, cmf)\n",
    "        \"\"\"\n",
    "        # Calcola PMF e CMF\n",
    "        bin_centers, pmf, bin_edges = self.calculate_pmf(data, bins)\n",
    "        cmf = self.calculate_cmf(pmf)\n",
    "        \n",
    "        # Crea il plot\n",
    "        fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))\n",
    "        \n",
    "        # Plot PMF\n",
    "        width = np.diff(bin_edges)\n",
    "        ax1.bar(bin_centers, pmf, width=width, alpha=0.5, label='PMF')\n",
    "        ax1.set_title('Probability Mass Function')\n",
    "        ax1.set_ylabel('Probability')\n",
    "        ax1.grid(True, alpha=0.3)\n",
    "        ax1.legend()\n",
    "        \n",
    "        # Plot CMF\n",
    "        ax2.plot(bin_centers, cmf, 'r-', label='CMF')\n",
    "        ax2.set_title('Cumulative Mass Function')\n",
    "        ax2.set_xlabel('Value')\n",
    "        ax2.set_ylabel('Cumulative Probability')\n",
    "        ax2.grid(True, alpha=0.3)\n",
    "        ax2.legend()\n",
    "        \n",
    "        # Imposta il titolo generale\n",
    "        fig.suptitle(title, y=1.02)\n",
    "        plt.tight_layout()\n",
    "        plt.show()\n",
    "        \n",
    "        return bin_centers, pmf, cmf\n",
    "\n",
    "def analyze_model_predictions(model: tf.keras.Model, \n",
    "                            test_data: np.ndarray,\n",
    "                            test_targets: np.ndarray,\n",
    "                            scaler_y) -> None:\n",
    "    \"\"\"\n",
    "    Analizza le distribuzioni di probabilità delle predizioni del modello.\n",
    "    \n",
    "    Args:\n",
    "        model: Modello TensorFlow addestrato\n",
    "        test_data: Dati di test\n",
    "        test_targets: Target di test\n",
    "        scaler_y: Scaler usato per denormalizzare i target\n",
    "    \"\"\"\n",
    "    # Ottieni le predizioni\n",
    "    predictions = model.predict(test_data)\n",
    "    \n",
    "    # Denormalizza predizioni e target\n",
    "    predictions_real = scaler_y.inverse_transform(predictions)\n",
    "    targets_real = scaler_y.inverse_transform(test_targets)\n",
    "    \n",
    "    # Inizializza la classe per l'analisi delle probabilità\n",
    "    prob = ProbabilityFunctions()\n",
    "    \n",
    "    # Analizza ogni target\n",
    "    target_names = ['olive_prod', 'min_oil_prod', 'max_oil_prod', \n",
    "                   'avg_oil_prod', 'total_water_need']\n",
    "    \n",
    "    for i, target in enumerate(target_names):\n",
    "        print(f\"\\nAnalisi per {target}\")\n",
    "        print(\"-\" * 50)\n",
    "        \n",
    "        # Calcola errori\n",
    "        errors = predictions_real[:, i] - targets_real[:, i]\n",
    "        \n",
    "        # Calcola statistiche degli errori\n",
    "        error_stats = prob.calculate_statistics(errors)\n",
    "        print(\"\\nStatistiche degli Errori:\")\n",
    "        for key, value in error_stats.items():\n",
    "            print(f\"{key}: {value:.3f}\")\n",
    "        \n",
    "        # Visualizza le distribuzioni degli errori\n",
    "        bin_centers, pmf, cmf = prob.plot_distributions(\n",
    "            errors, \n",
    "            bins=50,\n",
    "            title=f\"Distribuzione degli Errori - {target}\"\n",
    "        )\n",
    "        \n",
    "        # Calcola intervalli di confidenza\n",
    "        confidence_levels = [0.68, 0.95, 0.99]  # 1σ, 2σ, 3σ\n",
    "        for level in confidence_levels:\n",
    "            lower_idx = np.searchsorted(cmf, (1 - level) / 2)\n",
    "            upper_idx = np.searchsorted(cmf, (1 + level) / 2)\n",
    "            \n",
    "            print(f\"\\nIntervallo di Confidenza {level*100}%:\")\n",
    "            print(f\"Range: [{bin_centers[lower_idx]:.2f}, {bin_centers[upper_idx]:.2f}]\")\n",
    "\n",
    "def run_comprehensive_analysis(retrained_model, test_data, test_targets, scaler_y):\n",
    "    \"\"\"\n",
    "    Esegue un'analisi completa del modello includendo errori,\n",
    "    importanza delle feature e distribuzioni.\n",
    "    \"\"\"\n",
    "    print(\"=== ANALISI COMPLETA DEL MODELLO ===\")\n",
    "    \n",
    "    # 1. Analisi degli errori\n",
    "    print(\"\\n1. ANALISI DEGLI ERRORI\")\n",
    "    print(\"-\" * 50)\n",
    "    analyze_model_predictions(retrained_model, test_data, test_targets, scaler_y)\n",
    "    \n",
    "    # 2. Analisi dell'importanza delle feature\n",
    "    print(\"\\n2. IMPORTANZA DELLE FEATURE\")\n",
    "    print(\"-\" * 50)\n",
    "    \n",
    "    # Definisci i nomi delle feature\n",
    "    temporal_features = ['temp_mean', 'precip_sum', 'solar_energy_sum']\n",
    "    static_features = ['ha']\n",
    "    \n",
    "    all_features = temporal_features + static_features\n",
    "    importance = analyze_feature_importance(retrained_model, test_data, all_features)\n",
    "    \n",
    "    print(\"\\nImportanza relativa delle feature:\")\n",
    "    for feature, imp in sorted(importance.items(), key=lambda x: x[1], reverse=True):\n",
    "        print(f\"{feature}: {imp:.4f}\")\n",
    "        \n",
    "    # 3. Analisi distribuzionale\n",
    "    print(\"\\n3. ANALISI DISTRIBUZIONALE\")\n",
    "    print(\"-\" * 50)\n",
    "    \n",
    "    prob = ProbabilityFunctions()\n",
    "    predictions = retrained_model.predict(test_data)\n",
    "    predictions_real = scaler_y.inverse_transform(predictions)\n",
    "    targets_real = scaler_y.inverse_transform(test_targets)\n",
    "    \n",
    "    target_names = ['olive_prod', 'min_oil_prod', 'max_oil_prod', \n",
    "                    'avg_oil_prod', 'total_water_need']\n",
    "    \n",
    "    for i, target in enumerate(target_names):\n",
    "        print(f\"\\nAnalisi distribuzionale per {target}\")\n",
    "        \n",
    "        # Statistiche\n",
    "        stats_pred = prob.calculate_statistics(predictions_real[:, i])\n",
    "        stats_true = prob.calculate_statistics(targets_real[:, i])\n",
    "        \n",
    "        print(\"\\nStatistiche Predizioni:\")\n",
    "        for key, value in stats_pred.items():\n",
    "            print(f\"{key}: {value:.3f}\")\n",
    "            \n",
    "        print(\"\\nStatistiche Target Reali:\")\n",
    "        for key, value in stats_true.items():\n",
    "            print(f\"{key}: {value:.3f}\")\n",
    "        \n",
    "        # Visualizza distribuzioni\n",
    "        prob.plot_distributions(predictions_real[:, i], bins=50,\n",
    "                              title=f\"Distribuzione Predizioni - {target}\")\n",
    "        prob.plot_distributions(targets_real[:, i], bins=50,\n",
    "                              title=f\"Distribuzione Target Reali - {target}\")\n",
    "\n",
    "def analyze_model_predictions(model, test_data, test_targets, scaler_y):\n",
    "    \"\"\"\n",
    "    Analizza le distribuzioni di probabilità delle predizioni del modello.\n",
    "    \n",
    "    Args:\n",
    "        model: Modello TensorFlow addestrato\n",
    "        test_data: Dati di test\n",
    "        test_targets: Target di test\n",
    "        scaler_y: Scaler usato per denormalizzare i target\n",
    "    \"\"\"\n",
    "    # Ottieni le predizioni\n",
    "    predictions = model.predict(test_data)\n",
    "    \n",
    "    # Denormalizza predizioni e target\n",
    "    predictions_real = scaler_y.inverse_transform(predictions)\n",
    "    targets_real = scaler_y.inverse_transform(test_targets)\n",
    "    \n",
    "    # Inizializza la classe per l'analisi delle probabilità\n",
    "    prob = ProbabilityFunctions()\n",
    "    \n",
    "    # Analizza ogni target\n",
    "    target_names = ['olive_prod', 'min_oil_prod', 'max_oil_prod', \n",
    "                   'avg_oil_prod', 'total_water_need']\n",
    "    \n",
    "    for i, target in enumerate(target_names):\n",
    "        print(f\"\\nAnalisi per {target}\")\n",
    "        print(\"-\" * 50)\n",
    "        \n",
    "        # Calcola errori\n",
    "        errors = predictions_real[:, i] - targets_real[:, i]\n",
    "        \n",
    "        # Calcola statistiche degli errori\n",
    "        error_stats = prob.calculate_statistics(errors)\n",
    "        print(\"\\nStatistiche degli Errori:\")\n",
    "        for key, value in error_stats.items():\n",
    "            print(f\"{key}: {value:.3f}\")\n",
    "        \n",
    "        # Visualizza le distribuzioni degli errori\n",
    "        bin_centers, pmf, cmf = prob.plot_distributions(\n",
    "            errors, \n",
    "            bins=50,\n",
    "            title=f\"Distribuzione degli Errori - {target}\"\n",
    "        )\n",
    "        \n",
    "        # Calcola intervalli di confidenza\n",
    "        confidence_levels = [0.80,0.85, 0.90, 0.95, 0.99]  # 1σ, 2σ, 3σ\n",
    "        for level in confidence_levels:\n",
    "            lower_idx = np.searchsorted(cmf, (1 - level) / 2)\n",
    "            upper_idx = np.searchsorted(cmf, (1 + level) / 2)\n",
    "            \n",
    "            print(f\"\\nIntervallo di Confidenza {level*100}%:\")\n",
    "            print(f\"Range: [{bin_centers[lower_idx]:.2f}, {bin_centers[upper_idx]:.2f}]\")\n",
    "\n",
    "class ProbabilityFunctions:\n",
    "    @staticmethod\n",
    "    def calculate_statistics(data):\n",
    "        \"\"\"\n",
    "        Calcola statistiche di base usando TensorFlow.\n",
    "        \n",
    "        Args:\n",
    "            data: Tensor dei dati\n",
    "            \n",
    "        Returns:\n",
    "            dict: Dizionario con le statistiche\n",
    "        \"\"\"\n",
    "        if not isinstance(data, tf.Tensor):\n",
    "            data = tf.convert_to_tensor(data, dtype=tf.float32)\n",
    "            \n",
    "        mean = tf.reduce_mean(data)\n",
    "        # Calculate variance manually\n",
    "        squared_deviations = tf.square(data - mean)\n",
    "        variance = tf.reduce_mean(squared_deviations)\n",
    "        std = tf.sqrt(variance)\n",
    "        \n",
    "        # Sort the tensor for median calculation\n",
    "        sorted_data = tf.sort(data)\n",
    "        size = tf.size(data)\n",
    "        mid_index = size // 2\n",
    "        median = sorted_data[mid_index]\n",
    "        \n",
    "        return {\n",
    "            'mean': mean.numpy(),\n",
    "            'variance': variance.numpy(),\n",
    "            'std': std.numpy(),\n",
    "            'min': tf.reduce_min(data).numpy(),\n",
    "            'max': tf.reduce_max(data).numpy(),\n",
    "            'median': median.numpy()\n",
    "        }\n",
    "\n",
    "    @staticmethod\n",
    "    def calculate_pmf(data, bins=50):\n",
    "        \"\"\"\n",
    "        Calcola la Probability Mass Function (PMF) dei dati.\n",
    "        \n",
    "        Args:\n",
    "            data: Array di dati\n",
    "            bins: Numero di bin per l'istogramma\n",
    "            \n",
    "        Returns:\n",
    "            tuple: (bin_centers, pmf, bin_edges)\n",
    "        \"\"\"\n",
    "        # Calcola l'istogramma\n",
    "        hist, bin_edges = np.histogram(data, bins=bins, density=True)\n",
    "        \n",
    "        # Calcola i centri dei bin\n",
    "        bin_centers = (bin_edges[:-1] + bin_edges[1:]) / 2\n",
    "        \n",
    "        # Normalizza per ottenere la PMF\n",
    "        pmf = hist / np.sum(hist)\n",
    "        \n",
    "        return bin_centers, pmf, bin_edges\n",
    "\n",
    "    @staticmethod\n",
    "    def calculate_cmf(pmf):\n",
    "        \"\"\"\n",
    "        Calcola la Cumulative Mass Function (CMF) dalla PMF.\n",
    "        \n",
    "        Args:\n",
    "            pmf: Probability Mass Function\n",
    "            \n",
    "        Returns:\n",
    "            array: Cumulative Mass Function\n",
    "        \"\"\"\n",
    "        return np.cumsum(pmf)\n",
    "\n",
    "    def plot_distributions(self, data, bins=50, title=\"Distribuzione\"):\n",
    "        \"\"\"\n",
    "        Calcola e visualizza PMF e CMF delle distribuzioni.\n",
    "        \n",
    "        Args:\n",
    "            data: Array di dati da analizzare\n",
    "            bins: Numero di bin per l'istogramma\n",
    "            title: Titolo del grafico\n",
    "            \n",
    "        Returns:\n",
    "            tuple: (bin_centers, pmf, cmf)\n",
    "        \"\"\"\n",
    "        # Calcola PMF e CMF\n",
    "        bin_centers, pmf, bin_edges = self.calculate_pmf(data, bins)\n",
    "        cmf = self.calculate_cmf(pmf)\n",
    "        \n",
    "        # Crea il plot\n",
    "        fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))\n",
    "        \n",
    "        # Plot PMF\n",
    "        width = np.diff(bin_edges)\n",
    "        ax1.bar(bin_centers, pmf, width=width, alpha=0.5, label='PMF')\n",
    "        ax1.set_title('Probability Mass Function')\n",
    "        ax1.set_ylabel('Probability')\n",
    "        ax1.grid(True, alpha=0.3)\n",
    "        ax1.legend()\n",
    "        \n",
    "        # Plot CMF\n",
    "        ax2.plot(bin_centers, cmf, 'r-', label='CMF')\n",
    "        ax2.set_title('Cumulative Mass Function')\n",
    "        ax2.set_xlabel('Value')\n",
    "        ax2.set_ylabel('Cumulative Probability')\n",
    "        ax2.grid(True, alpha=0.3)\n",
    "        ax2.legend()\n",
    "        \n",
    "        # Set overall title\n",
    "        fig.suptitle(title, y=1.02)\n",
    "        plt.tight_layout()\n",
    "        plt.show()\n",
    "        \n",
    "        return bin_centers, pmf, cmf"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "b98f2a45-ba6f-4519-acaa-0138b4ee7812",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "=== ANALISI COMPLETA DEL MODELLO ===\n",
      "\n",
      "1. ANALISI DEGLI ERRORI\n",
      "--------------------------------------------------\n",
      "18750/18750 [==============================] - 191s 10ms/step\n",
      "\n",
      "Analisi per olive_prod\n",
      "--------------------------------------------------\n",
      "\n",
      "Statistiche degli Errori:\n",
      "mean: 16.634\n",
      "variance: 3545753.250\n",
      "std: 1883.017\n",
      "min: -15594.572\n",
      "max: 14395.641\n",
      "median: 192.294\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 68464 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Intervallo di Confidenza 80.0%:\n",
      "Range: [-2098.98, 2099.65]\n",
      "\n",
      "Intervallo di Confidenza 85.0%:\n",
      "Range: [-2698.78, 2099.65]\n",
      "\n",
      "Intervallo di Confidenza 90.0%:\n",
      "Range: [-3298.58, 2699.46]\n",
      "\n",
      "Intervallo di Confidenza 95.0%:\n",
      "Range: [-4498.19, 3299.26]\n",
      "\n",
      "Intervallo di Confidenza 99.0%:\n",
      "Range: [-6297.61, 5098.67]\n",
      "\n",
      "Analisi per min_oil_prod\n",
      "--------------------------------------------------\n",
      "\n",
      "Statistiche degli Errori:\n",
      "mean: -10.703\n",
      "variance: 153119.078\n",
      "std: 391.304\n",
      "min: -3452.054\n",
      "max: 3142.031\n",
      "median: 27.116\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 69884 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Intervallo di Confidenza 80.0%:\n",
      "Range: [-484.72, 438.46]\n",
      "\n",
      "Intervallo di Confidenza 85.0%:\n",
      "Range: [-616.60, 438.46]\n",
      "\n",
      "Intervallo di Confidenza 90.0%:\n",
      "Range: [-748.48, 570.34]\n",
      "\n",
      "Intervallo di Confidenza 95.0%:\n",
      "Range: [-880.36, 702.22]\n",
      "\n",
      "Intervallo di Confidenza 99.0%:\n",
      "Range: [-1407.89, 1097.86]\n",
      "\n",
      "Analisi per max_oil_prod\n",
      "--------------------------------------------------\n",
      "\n",
      "Statistiche degli Errori:\n",
      "mean: -3.161\n",
      "variance: 222900.141\n",
      "std: 472.123\n",
      "min: -4621.081\n",
      "max: 3967.894\n",
      "median: 42.622\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 60536 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Intervallo di Confidenza 80.0%:\n",
      "Range: [-584.26, 446.41]\n",
      "\n",
      "Intervallo di Confidenza 85.0%:\n",
      "Range: [-756.04, 618.19]\n",
      "\n",
      "Intervallo di Confidenza 90.0%:\n",
      "Range: [-756.04, 618.19]\n",
      "\n",
      "Intervallo di Confidenza 95.0%:\n",
      "Range: [-1099.60, 961.75]\n",
      "\n",
      "Intervallo di Confidenza 99.0%:\n",
      "Range: [-1614.94, 1305.31]\n",
      "\n",
      "Analisi per avg_oil_prod\n",
      "--------------------------------------------------\n",
      "\n",
      "Statistiche degli Errori:\n",
      "mean: -7.018\n",
      "variance: 175727.656\n",
      "std: 419.199\n",
      "min: -3742.384\n",
      "max: 3360.436\n",
      "median: 33.824\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 68712 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Intervallo di Confidenza 80.0%:\n",
      "Range: [-546.11, 448.28]\n",
      "\n",
      "Intervallo di Confidenza 85.0%:\n",
      "Range: [-546.11, 448.28]\n",
      "\n",
      "Intervallo di Confidenza 90.0%:\n",
      "Range: [-688.17, 590.34]\n",
      "\n",
      "Intervallo di Confidenza 95.0%:\n",
      "Range: [-972.28, 732.39]\n",
      "\n",
      "Intervallo di Confidenza 99.0%:\n",
      "Range: [-1540.51, 1158.56]\n",
      "\n",
      "Analisi per total_water_need\n",
      "--------------------------------------------------\n",
      "\n",
      "Statistiche degli Errori:\n",
      "mean: 65.956\n",
      "variance: 3269755.500\n",
      "std: 1808.246\n",
      "min: -15266.289\n",
      "max: 11478.280\n",
      "median: 354.974\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 68680 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Intervallo di Confidenza 80.0%:\n",
      "Range: [-2161.45, 2117.68]\n",
      "\n",
      "Intervallo di Confidenza 85.0%:\n",
      "Range: [-2696.34, 2117.68]\n",
      "\n",
      "Intervallo di Confidenza 90.0%:\n",
      "Range: [-3231.23, 2652.57]\n",
      "\n",
      "Intervallo di Confidenza 95.0%:\n",
      "Range: [-4301.02, 3187.46]\n",
      "\n",
      "Intervallo di Confidenza 99.0%:\n",
      "Range: [-6440.58, 4792.14]\n",
      "\n",
      "2. IMPORTANZA DELLE FEATURE\n",
      "--------------------------------------------------\n",
      "18750/18750 [==============================] - 187s 10ms/step\n",
      "18750/18750 [==============================] - 186s 10ms/step\n",
      "18750/18750 [==============================] - 186s 10ms/step\n",
      "18750/18750 [==============================] - 189s 10ms/step\n",
      "18750/18750 [==============================] - 184s 10ms/step\n",
      "\n",
      "Importanza relativa delle feature:\n",
      "ha: 0.8679\n",
      "precip_sum: 0.0541\n",
      "solar_energy_sum: 0.0431\n",
      "temp_mean: 0.0349\n",
      "\n",
      "3. ANALISI DISTRIBUZIONALE\n",
      "--------------------------------------------------\n",
      "18750/18750 [==============================] - 181s 10ms/step\n",
      "\n",
      "Analisi distribuzionale per olive_prod\n",
      "\n",
      "Statistiche Predizioni:\n",
      "mean: 29887.219\n",
      "variance: 257054016.000\n",
      "std: 16032.904\n",
      "min: 2732.547\n",
      "max: 87850.508\n",
      "median: 28077.900\n",
      "\n",
      "Statistiche Target Reali:\n",
      "mean: 29870.586\n",
      "variance: 270589536.000\n",
      "std: 16449.605\n",
      "min: 2030.459\n",
      "max: 99272.031\n",
      "median: 27900.719\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 72328 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 65764 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Analisi distribuzionale per min_oil_prod\n",
      "\n",
      "Statistiche Predizioni:\n",
      "mean: 5911.217\n",
      "variance: 11063664.000\n",
      "std: 3326.208\n",
      "min: 552.748\n",
      "max: 19390.480\n",
      "median: 5451.560\n",
      "\n",
      "Statistiche Target Reali:\n",
      "mean: 5921.921\n",
      "variance: 11676302.000\n",
      "std: 3417.060\n",
      "min: 374.026\n",
      "max: 22359.766\n",
      "median: 5426.787\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 67940 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 66336 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Analisi distribuzionale per max_oil_prod\n",
      "\n",
      "Statistiche Predizioni:\n",
      "mean: 7153.816\n",
      "variance: 16302536.000\n",
      "std: 4037.640\n",
      "min: 664.406\n",
      "max: 24333.047\n",
      "median: 6596.512\n",
      "\n",
      "Statistiche Target Reali:\n",
      "mean: 7156.977\n",
      "variance: 17168200.000\n",
      "std: 4143.453\n",
      "min: 458.719\n",
      "max: 27636.207\n",
      "median: 6558.154\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 65348 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 67360 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Analisi distribuzionale per avg_oil_prod\n",
      "\n",
      "Statistiche Predizioni:\n",
      "mean: 6532.427\n",
      "variance: 13545224.000\n",
      "std: 3680.384\n",
      "min: 608.335\n",
      "max: 21836.357\n",
      "median: 6024.207\n",
      "\n",
      "Statistiche Target Reali:\n",
      "mean: 6539.446\n",
      "variance: 14259229.000\n",
      "std: 3776.139\n",
      "min: 415.672\n",
      "max: 24818.859\n",
      "median: 5996.325\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 64940 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 67752 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Analisi distribuzionale per total_water_need\n",
      "\n",
      "Statistiche Predizioni:\n",
      "mean: 60131.293\n",
      "variance: 836238848.000\n",
      "std: 28917.795\n",
      "min: 10634.454\n",
      "max: 142663.500\n",
      "median: 59327.203\n",
      "\n",
      "Statistiche Target Reali:\n",
      "mean: 60065.336\n",
      "variance: 879319232.000\n",
      "std: 29653.316\n",
      "min: 8061.355\n",
      "max: 151159.875\n",
      "median: 59136.551\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 72624 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 72932 characters]",
      "text/plain": [
       "<Figure size 1000x800 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "run_comprehensive_analysis(retrained_model, test_data, test_targets, scaler_y)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "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.11.0rc1"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
