stop tracking src/sources
This commit is contained in:
parent
ca5e1ddbc0
commit
ffc74dc262
3
.dvc/.gitignore
vendored
Normal file
3
.dvc/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/config.local
|
||||||
|
/tmp
|
||||||
|
/cache
|
||||||
3
.dvcignore
Normal file
3
.dvcignore
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Add patterns of files dvc should ignore, which could improve
|
||||||
|
# the performance. Learn more at
|
||||||
|
# https://dvc.org/doc/user-guide/dvcignore
|
||||||
37
.idea/csv-editor.xml
generated
37
.idea/csv-editor.xml
generated
@ -1,37 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="CsvFileAttributes">
|
|
||||||
<option name="attributeMap">
|
|
||||||
<map>
|
|
||||||
<entry key="$USER_HOME$/Downloads/Breadcrumb_Data.csv">
|
|
||||||
<value>
|
|
||||||
<Attribute>
|
|
||||||
<option name="separator" value="," />
|
|
||||||
</Attribute>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="/data/olive_varieties.csv">
|
|
||||||
<value>
|
|
||||||
<Attribute>
|
|
||||||
<option name="separator" value="," />
|
|
||||||
</Attribute>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="/data/simulated_data.csv">
|
|
||||||
<value>
|
|
||||||
<Attribute>
|
|
||||||
<option name="separator" value="," />
|
|
||||||
</Attribute>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
<entry key="/data/variety_olive_oil_production.csv">
|
|
||||||
<value>
|
|
||||||
<Attribute>
|
|
||||||
<option name="separator" value="," />
|
|
||||||
</Attribute>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
1952
src/models/olive_oli/olive_oil.ipynb
Normal file
1952
src/models/olive_oli/olive_oil.ipynb
Normal file
File diff suppressed because it is too large
Load Diff
1500
src/models/solarenergy/solarenergy_model.ipynb
Executable file
1500
src/models/solarenergy/solarenergy_model.ipynb
Executable file
File diff suppressed because it is too large
Load Diff
1869
src/models/solarradiation/solarradiation_model.ipynb
Executable file
1869
src/models/solarradiation/solarradiation_model.ipynb
Executable file
File diff suppressed because one or more lines are too long
516
src/weather/uv_index/uv_index_model.ipynb → src/models/uv_index/uv_index_model.ipynb
Normal file → Executable file
516
src/weather/uv_index/uv_index_model.ipynb → src/models/uv_index/uv_index_model.ipynb
Normal file → Executable file
@ -61,27 +61,23 @@
|
|||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"import tensorflow as tf\n",
|
"import tensorflow as tf\n",
|
||||||
"from tensorflow.keras.layers import Dense, LSTM, Conv1D, MultiHeadAttention, Dropout, BatchNormalization, \\\n",
|
"from tensorflow.keras.layers import Dense, LSTM, MultiHeadAttention, Dropout, BatchNormalization, LayerNormalization, Input, Activation, Lambda, Bidirectional, Add, MaxPooling1D\n",
|
||||||
" LayerNormalization, GlobalAveragePooling1D, GlobalMaxPooling1D, Concatenate, Input, Reshape, Activation, Lambda, \\\n",
|
|
||||||
" Bidirectional, Add, Multiply, MaxPooling1D\n",
|
|
||||||
"from tensorflow.keras import regularizers\n",
|
"from tensorflow.keras import regularizers\n",
|
||||||
"from tensorflow.keras.models import Model\n",
|
"from tensorflow.keras.models import Model\n",
|
||||||
"import tensorflow.keras.backend as K\n",
|
|
||||||
"import pandas as pd\n",
|
"import pandas as pd\n",
|
||||||
"import numpy as np\n",
|
"import numpy as np\n",
|
||||||
"from sklearn.model_selection import train_test_split\n",
|
"from sklearn.model_selection import train_test_split\n",
|
||||||
"from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score\n",
|
"from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score\n",
|
||||||
"from sklearn.preprocessing import StandardScaler, MinMaxScaler\n",
|
"from sklearn.preprocessing import StandardScaler\n",
|
||||||
"from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\n",
|
"from tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\n",
|
||||||
"from tensorflow.keras.optimizers import Adam, AdamW\n",
|
"from tensorflow.keras.optimizers import AdamW\n",
|
||||||
"import matplotlib.pyplot as plt\n",
|
|
||||||
"import json\n",
|
"import json\n",
|
||||||
"import joblib\n",
|
|
||||||
"from sklearn.utils.class_weight import compute_class_weight\n",
|
|
||||||
"from datetime import datetime\n",
|
"from datetime import datetime\n",
|
||||||
"import matplotlib.pyplot as plt\n",
|
"import matplotlib.pyplot as plt\n",
|
||||||
"from sklearn.metrics import confusion_matrix\n",
|
"from sklearn.metrics import confusion_matrix\n",
|
||||||
"from tensorflow.keras.utils import plot_model"
|
"from tensorflow.keras.utils import plot_model\n",
|
||||||
|
"\n",
|
||||||
|
"folder_name = datetime.now().strftime(\"%Y-%m-%d_%H-%M\")"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -175,7 +171,6 @@
|
|||||||
" return df\n",
|
" return df\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\n",
|
|
||||||
"def add_advanced_features(df):\n",
|
"def add_advanced_features(df):\n",
|
||||||
" # Features esistenti\n",
|
" # Features esistenti\n",
|
||||||
" df = add_time_features(df)\n",
|
" df = add_time_features(df)\n",
|
||||||
@ -188,7 +183,7 @@
|
|||||||
" # One-hot encoding per le feature categoriche\n",
|
" # One-hot encoding per le feature categoriche\n",
|
||||||
" df = pd.get_dummies(df, columns=['season', 'time_period'])\n",
|
" df = pd.get_dummies(df, columns=['season', 'time_period'])\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Aggiungi interazioni tra variabili meteorologiche\n",
|
" # Interazioni tra variabili meteorologiche\n",
|
||||||
" df['temp_humidity'] = df['temp'] * df['humidity']\n",
|
" df['temp_humidity'] = df['temp'] * df['humidity']\n",
|
||||||
" df['temp_cloudcover'] = df['temp'] * df['cloudcover']\n",
|
" df['temp_cloudcover'] = df['temp'] * df['cloudcover']\n",
|
||||||
" df['visibility_cloudcover'] = df['visibility'] * df['cloudcover']\n",
|
" df['visibility_cloudcover'] = df['visibility'] * df['cloudcover']\n",
|
||||||
@ -209,8 +204,7 @@
|
|||||||
" df['temp_humidity_interaction'] = df['temp'] * df['humidity'] / 100\n",
|
" df['temp_humidity_interaction'] = df['temp'] * df['humidity'] / 100\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Indicatore di condizioni estreme\n",
|
" # Indicatore di condizioni estreme\n",
|
||||||
" df['extreme_conditions'] = ((df['temp'] > df['temp'].quantile(0.75)) & \n",
|
" df['extreme_conditions'] = ((df['temp'] > df['temp'].quantile(0.75)) & (df['humidity'] < df['humidity'].quantile(0.25))).astype(int)\n",
|
||||||
" (df['humidity'] < df['humidity'].quantile(0.25))).astype(int)\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
" # Feature composite per la trasparenza atmosferica\n",
|
" # Feature composite per la trasparenza atmosferica\n",
|
||||||
" df['atmospheric_transparency'] = (100 - df['cloudcover']) * (df['visibility'] / 10)\n",
|
" df['atmospheric_transparency'] = (100 - df['cloudcover']) * (df['visibility'] / 10)\n",
|
||||||
@ -235,6 +229,7 @@
|
|||||||
" df = add_advanced_features(df)\n",
|
" df = add_advanced_features(df)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" target_variables = ['solarradiation', 'solarenergy', 'uvindex']\n",
|
" target_variables = ['solarradiation', 'solarenergy', 'uvindex']\n",
|
||||||
|
"\n",
|
||||||
" # Selezione delle feature più rilevanti per UV index\n",
|
" # Selezione delle feature più rilevanti per UV index\n",
|
||||||
" selected_features = [\n",
|
" selected_features = [\n",
|
||||||
" # Features meteorologiche base\n",
|
" # Features meteorologiche base\n",
|
||||||
@ -256,7 +251,7 @@
|
|||||||
" 'cloud_rolling_12h', 'temp_rolling_12h',\n",
|
" 'cloud_rolling_12h', 'temp_rolling_12h',\n",
|
||||||
" 'temp_rolling_mean_6h', 'cloudcover_rolling_mean_6h',\n",
|
" 'temp_rolling_mean_6h', 'cloudcover_rolling_mean_6h',\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Features categoriche (da encodare)\n",
|
" # Features categoriche\n",
|
||||||
" 'season', 'time_period'\n",
|
" 'season', 'time_period'\n",
|
||||||
" ]\n",
|
" ]\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -266,14 +261,12 @@
|
|||||||
"\n",
|
"\n",
|
||||||
" df = df.sort_values('datetime')\n",
|
" df = df.sort_values('datetime')\n",
|
||||||
" df.set_index('datetime', inplace=True)\n",
|
" df.set_index('datetime', inplace=True)\n",
|
||||||
" # Rimozione delle righe con valori NaN (create dai rolling features)\n",
|
"\n",
|
||||||
" #df = df.dropna()\n",
|
|
||||||
" columns_to_interpolate = final_features + target_variables\n",
|
" columns_to_interpolate = final_features + target_variables\n",
|
||||||
" for column in columns_to_interpolate:\n",
|
" for column in columns_to_interpolate:\n",
|
||||||
" df[column] = df[column].interpolate(method='time')\n",
|
" df[column] = df[column].interpolate(method='time')\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Rimuovi eventuali valori mancanti residui\n",
|
" # Rimuovi eventuali valori mancanti residui\n",
|
||||||
" #df.dropna(subset=features + selected_features, inplace=True)\n",
|
|
||||||
" df.fillna(0, inplace=True)\n",
|
" df.fillna(0, inplace=True)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" data_after_2010 = df[df['year'] >= 2010].copy()\n",
|
" data_after_2010 = df[df['year'] >= 2010].copy()\n",
|
||||||
@ -286,6 +279,7 @@
|
|||||||
"\n",
|
"\n",
|
||||||
" #print(X.head())\n",
|
" #print(X.head())\n",
|
||||||
" #print(X.columns)\n",
|
" #print(X.columns)\n",
|
||||||
|
"\n",
|
||||||
" y = data_after_2010['uvindex']\n",
|
" y = data_after_2010['uvindex']\n",
|
||||||
"\n",
|
"\n",
|
||||||
" X_to_predict = data_before_2010[final_features]\n",
|
" X_to_predict = data_before_2010[final_features]\n",
|
||||||
@ -361,7 +355,7 @@
|
|||||||
" return x\n",
|
" return x\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\n",
|
"\n",
|
||||||
"def create_optimized_model(input_shape,folder_name, l2_lambda=0.005):\n",
|
"def create_uv_index_model(input_shape, folder_name, l2_lambda=0.005):\n",
|
||||||
" inputs = Input(shape=input_shape)\n",
|
" inputs = Input(shape=input_shape)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Primi due layer LSTM con sequenze\n",
|
" # Primi due layer LSTM con sequenze\n",
|
||||||
@ -392,9 +386,8 @@
|
|||||||
"\n",
|
"\n",
|
||||||
" model = Model(inputs=inputs, outputs=outputs, name=\"UvModel\")\n",
|
" model = Model(inputs=inputs, outputs=outputs, name=\"UvModel\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # 4. Optimizer con parametri conservativi\n",
|
" optimizer = AdamW(\n",
|
||||||
" optimizer = Adam(\n",
|
" learning_rate=0.0005,\n",
|
||||||
" learning_rate=0.0005, # Learning rate più basso\n",
|
|
||||||
" beta_1=0.9,\n",
|
" beta_1=0.9,\n",
|
||||||
" beta_2=0.999,\n",
|
" beta_2=0.999,\n",
|
||||||
" epsilon=1e-07\n",
|
" epsilon=1e-07\n",
|
||||||
@ -416,39 +409,74 @@
|
|||||||
"\n",
|
"\n",
|
||||||
" return model\n",
|
" return model\n",
|
||||||
"\n",
|
"\n",
|
||||||
"def evaluate_uv_predictions(y_true, y_pred):\n",
|
"\n",
|
||||||
|
"def evaluate_uv_predictions(y_true, y_pred, folder_name=None):\n",
|
||||||
" \"\"\"\n",
|
" \"\"\"\n",
|
||||||
" Valutazione specifica per UV index con metriche categoriche\n",
|
" Valutazione specifica per UV index con metriche sia raw che categoriche\n",
|
||||||
|
"\n",
|
||||||
|
" Parameters:\n",
|
||||||
|
" -----------\n",
|
||||||
|
" y_true : array-like\n",
|
||||||
|
" Valori reali dell'UV index\n",
|
||||||
|
" y_pred : array-like\n",
|
||||||
|
" Valori predetti dell'UV index\n",
|
||||||
|
" folder_name : str, optional\n",
|
||||||
|
" Cartella dove salvare eventuali plot di analisi\n",
|
||||||
|
"\n",
|
||||||
|
" Returns:\n",
|
||||||
|
" --------\n",
|
||||||
|
" dict\n",
|
||||||
|
" Dizionario contenente tutte le metriche calcolate\n",
|
||||||
" \"\"\"\n",
|
" \"\"\"\n",
|
||||||
" # Converti in numpy array se necessario\n",
|
" import os\n",
|
||||||
" y_true = np.array(y_true)\n",
|
" from datetime import datetime\n",
|
||||||
" y_pred = np.array(y_pred)\n",
|
"\n",
|
||||||
|
" y_true = np.array(y_true).ravel()\n",
|
||||||
|
" y_pred = np.array(y_pred).ravel()\n",
|
||||||
|
"\n",
|
||||||
|
" # Calcolo metriche sui valori raw\n",
|
||||||
|
" mae_raw = mean_absolute_error(y_true, y_pred)\n",
|
||||||
|
" rmse_raw = np.sqrt(mean_squared_error(y_true, y_pred))\n",
|
||||||
|
" r2_raw = r2_score(y_true, y_pred)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Arrotonda le predizioni al più vicino intero\n",
|
" # Arrotonda le predizioni al più vicino intero\n",
|
||||||
" y_pred_rounded = np.round(y_pred)\n",
|
" y_pred_rounded = np.round(y_pred)\n",
|
||||||
"\n",
|
|
||||||
" # Clip dei valori tra 0 e 11\n",
|
|
||||||
" y_pred_clipped = np.clip(y_pred_rounded, 0, 11)\n",
|
" y_pred_clipped = np.clip(y_pred_rounded, 0, 11)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Calcolo metriche\n",
|
" # Calcolo metriche sui valori arrotondati\n",
|
||||||
" mae = mean_absolute_error(y_true, y_pred_clipped)\n",
|
" mae_rounded = mean_absolute_error(y_true, y_pred_clipped)\n",
|
||||||
" rmse = np.sqrt(mean_squared_error(y_true, y_pred_clipped))\n",
|
" rmse_rounded = np.sqrt(mean_squared_error(y_true, y_pred_clipped))\n",
|
||||||
" r2 = r2_score(y_true, y_pred_clipped)\n",
|
" r2_rounded = r2_score(y_true, y_pred_clipped)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Calcolo accuratezza per diversi margini di errore\n",
|
" # Calcolo accuratezza per diversi margini di errore (sia raw che rounded)\n",
|
||||||
" exact_accuracy = np.mean(y_pred_clipped == y_true.ravel())\n",
|
" # Raw\n",
|
||||||
" one_off_accuracy = np.mean(np.abs(y_pred_clipped - y_true.ravel()) <= 1)\n",
|
" within_05_raw = np.mean(np.abs(y_pred - y_true) <= 0.5)\n",
|
||||||
" two_off_accuracy = np.mean(np.abs(y_pred_clipped - y_true.ravel()) <= 2)\n",
|
" within_1_raw = np.mean(np.abs(y_pred - y_true) <= 1.0)\n",
|
||||||
|
" within_2_raw = np.mean(np.abs(y_pred - y_true) <= 2.0)\n",
|
||||||
|
"\n",
|
||||||
|
" # Rounded\n",
|
||||||
|
" exact_accuracy = np.mean(y_pred_clipped == y_true)\n",
|
||||||
|
" one_off_accuracy = np.mean(np.abs(y_pred_clipped - y_true) <= 1)\n",
|
||||||
|
" two_off_accuracy = np.mean(np.abs(y_pred_clipped - y_true) <= 2)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" print(\"\\nUV Index Prediction Metrics:\")\n",
|
" print(\"\\nUV Index Prediction Metrics:\")\n",
|
||||||
" print(f\"MAE: {mae:.3f}\")\n",
|
" print(\"\\nRaw Predictions:\")\n",
|
||||||
" print(f\"RMSE: {rmse:.3f}\")\n",
|
" print(f\"MAE: {mae_raw:.3f}\")\n",
|
||||||
" print(f\"R² Score: {r2:.3f}\")\n",
|
" print(f\"RMSE: {rmse_raw:.3f}\")\n",
|
||||||
" print(f\"Exact Match Accuracy: {exact_accuracy:.3f}\")\n",
|
" print(f\"R² Score: {r2_raw:.3f}\")\n",
|
||||||
|
" print(f\"Within ±0.5: {within_05_raw:.3f}\")\n",
|
||||||
|
" print(f\"Within ±1.0: {within_1_raw:.3f}\")\n",
|
||||||
|
" print(f\"Within ±2.0: {within_2_raw:.3f}\")\n",
|
||||||
|
"\n",
|
||||||
|
" print(\"\\nRounded Predictions:\")\n",
|
||||||
|
" print(f\"MAE: {mae_rounded:.3f}\")\n",
|
||||||
|
" print(f\"RMSE: {rmse_rounded:.3f}\")\n",
|
||||||
|
" print(f\"R² Score: {r2_rounded:.3f}\")\n",
|
||||||
|
" print(f\"Exact Match: {exact_accuracy:.3f}\")\n",
|
||||||
" print(f\"±1 Accuracy: {one_off_accuracy:.3f}\")\n",
|
" print(f\"±1 Accuracy: {one_off_accuracy:.3f}\")\n",
|
||||||
" print(f\"±2 Accuracy: {two_off_accuracy:.3f}\")\n",
|
" print(f\"±2 Accuracy: {two_off_accuracy:.3f}\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Confusion Matrix per livelli di UV\n",
|
" # Analisi dei livelli UV\n",
|
||||||
" def get_uv_level(value):\n",
|
" def get_uv_level(value):\n",
|
||||||
" if value <= 2:\n",
|
" if value <= 2:\n",
|
||||||
" return 'Low'\n",
|
" return 'Low'\n",
|
||||||
@ -461,49 +489,195 @@
|
|||||||
" else:\n",
|
" else:\n",
|
||||||
" return 'Extreme'\n",
|
" return 'Extreme'\n",
|
||||||
"\n",
|
"\n",
|
||||||
" y_true_levels = [get_uv_level(v) for v in y_true.ravel()]\n",
|
" # Calcola livelli UV sia per raw che rounded\n",
|
||||||
" y_pred_levels = [get_uv_level(v) for v in y_pred_clipped]\n",
|
" y_true_levels = [get_uv_level(v) for v in y_true]\n",
|
||||||
|
" y_pred_levels_raw = [get_uv_level(v) for v in y_pred]\n",
|
||||||
|
" y_pred_levels_rounded = [get_uv_level(v) for v in y_pred_clipped]\n",
|
||||||
"\n",
|
"\n",
|
||||||
" print(\"\\nUV Level Confusion Matrix:\")\n",
|
" # Calcola accuracy dei livelli\n",
|
||||||
|
" level_accuracy_raw = np.mean([t == p for t, p in zip(y_true_levels, y_pred_levels_raw)])\n",
|
||||||
|
" level_accuracy_rounded = np.mean([t == p for t, p in zip(y_true_levels, y_pred_levels_rounded)])\n",
|
||||||
|
"\n",
|
||||||
|
" print(\"\\nUV Level Accuracy:\")\n",
|
||||||
|
" print(f\"Raw predictions: {level_accuracy_raw:.3f}\")\n",
|
||||||
|
" print(f\"Rounded predictions: {level_accuracy_rounded:.3f}\")\n",
|
||||||
|
"\n",
|
||||||
|
" print(\"\\nUV Level Confusion Matrix (Raw Predictions):\")\n",
|
||||||
" print(pd.crosstab(\n",
|
" print(pd.crosstab(\n",
|
||||||
" pd.Series(y_true_levels, name='Actual'),\n",
|
" pd.Series(y_true_levels, name='Actual'),\n",
|
||||||
" pd.Series(y_pred_levels, name='Predicted')\n",
|
" pd.Series(y_pred_levels_raw, name='Predicted')\n",
|
||||||
" ))\n",
|
" ))\n",
|
||||||
"\n",
|
"\n",
|
||||||
" return mae, rmse, r2, exact_accuracy, one_off_accuracy\n",
|
" print(\"\\nUV Level Confusion Matrix (Rounded Predictions):\")\n",
|
||||||
|
" print(pd.crosstab(\n",
|
||||||
|
" pd.Series(y_true_levels, name='Actual'),\n",
|
||||||
|
" pd.Series(y_pred_levels_rounded, name='Predicted')\n",
|
||||||
|
" ))\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
" # Se specificata una cartella, salva i plot di analisi\n",
|
||||||
|
" if folder_name is not None:\n",
|
||||||
|
" try:\n",
|
||||||
|
" os.makedirs(folder_name, exist_ok=True)\n",
|
||||||
|
" timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
"def plot_uv_predictions(y_true, y_pred):\n",
|
" # Plot di confronto tra raw e rounded predictions\n",
|
||||||
" \"\"\"\n",
|
|
||||||
" Visualizzazione delle predizioni specifica per UV index\n",
|
|
||||||
" \"\"\"\n",
|
|
||||||
" # Converti in numpy array se necessario\n",
|
|
||||||
" y_true = np.array(y_true).ravel()\n",
|
|
||||||
" y_pred = np.array(y_pred).ravel()\n",
|
|
||||||
"\n",
|
|
||||||
" plt.figure(figsize=(15, 5))\n",
|
" plt.figure(figsize=(15, 5))\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Plot 1: Actual vs Predicted\n",
|
" # Plot 1: Scatter plot confronto\n",
|
||||||
" plt.subplot(1, 2, 1)\n",
|
" plt.subplot(1, 3, 1)\n",
|
||||||
" plt.scatter(y_true, y_pred, alpha=0.5)\n",
|
" plt.scatter(y_true, y_pred, alpha=0.5, label='Raw')\n",
|
||||||
|
" plt.scatter(y_true, y_pred_clipped, alpha=0.5, label='Rounded')\n",
|
||||||
" plt.plot([0, 11], [0, 11], 'r--', lw=2)\n",
|
" plt.plot([0, 11], [0, 11], 'r--', lw=2)\n",
|
||||||
" plt.xlabel('Actual UV Index')\n",
|
" plt.xlabel('Actual UV Index')\n",
|
||||||
" plt.ylabel('Predicted UV Index')\n",
|
" plt.ylabel('Predicted UV Index')\n",
|
||||||
" plt.title('Actual vs Predicted UV Index')\n",
|
" plt.title('Raw vs Rounded Predictions')\n",
|
||||||
|
" plt.legend()\n",
|
||||||
" plt.grid(True)\n",
|
" plt.grid(True)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Plot 2: Distribution of Errors\n",
|
" # Plot 2: Distribuzione errori raw\n",
|
||||||
" plt.subplot(1, 2, 2)\n",
|
" plt.subplot(1, 3, 2)\n",
|
||||||
" errors = y_pred - y_true\n",
|
" plt.hist(y_pred - y_true, bins=50, alpha=0.7)\n",
|
||||||
" plt.hist(errors, bins=20, alpha=0.7)\n",
|
" plt.xlabel('Prediction Error (Raw)')\n",
|
||||||
" plt.xlabel('Prediction Error')\n",
|
|
||||||
" plt.ylabel('Frequency')\n",
|
" plt.ylabel('Frequency')\n",
|
||||||
" plt.title('Distribution of Prediction Errors')\n",
|
" plt.title('Distribution of Raw Errors')\n",
|
||||||
|
" plt.grid(True)\n",
|
||||||
|
"\n",
|
||||||
|
" # Plot 3: Distribuzione errori rounded\n",
|
||||||
|
" plt.subplot(1, 3, 3)\n",
|
||||||
|
" plt.hist(y_pred_clipped - y_true, bins=50, alpha=0.7)\n",
|
||||||
|
" plt.xlabel('Prediction Error (Rounded)')\n",
|
||||||
|
" plt.ylabel('Frequency')\n",
|
||||||
|
" plt.title('Distribution of Rounded Errors')\n",
|
||||||
" plt.grid(True)\n",
|
" plt.grid(True)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" plt.tight_layout()\n",
|
" plt.tight_layout()\n",
|
||||||
|
"\n",
|
||||||
|
" # Salva il plot\n",
|
||||||
|
" filename = os.path.join(folder_name, f'uv_prediction_analysis_{timestamp}.png')\n",
|
||||||
|
" plt.savefig(filename, dpi=300, bbox_inches='tight')\n",
|
||||||
|
" print(f\"\\nPlot di analisi salvato come: {filename}\")\n",
|
||||||
|
"\n",
|
||||||
" plt.show()\n",
|
" plt.show()\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
" except Exception as e:\n",
|
||||||
|
" print(f\"\\nErrore nel salvare i plot: {str(e)}\")\n",
|
||||||
|
"\n",
|
||||||
|
" # Restituisci tutte le metriche in un dizionario\n",
|
||||||
|
" metrics = {\n",
|
||||||
|
" 'raw': {\n",
|
||||||
|
" 'mae': mae_raw,\n",
|
||||||
|
" 'rmse': rmse_raw,\n",
|
||||||
|
" 'r2': r2_raw,\n",
|
||||||
|
" 'within_05': within_05_raw,\n",
|
||||||
|
" 'within_1': within_1_raw,\n",
|
||||||
|
" 'within_2': within_2_raw,\n",
|
||||||
|
" 'level_accuracy': level_accuracy_raw\n",
|
||||||
|
" },\n",
|
||||||
|
" 'rounded': {\n",
|
||||||
|
" 'mae': mae_rounded,\n",
|
||||||
|
" 'rmse': rmse_rounded,\n",
|
||||||
|
" 'r2': r2_rounded,\n",
|
||||||
|
" 'exact_match': exact_accuracy,\n",
|
||||||
|
" 'one_off': one_off_accuracy,\n",
|
||||||
|
" 'two_off': two_off_accuracy,\n",
|
||||||
|
" 'level_accuracy': level_accuracy_rounded\n",
|
||||||
|
" }\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" return metrics\n",
|
||||||
|
"\n",
|
||||||
|
"def plot_training_history(history, folder_name=None):\n",
|
||||||
|
" \"\"\"\n",
|
||||||
|
" Visualizza e salva i plot della loss e delle metriche durante il training\n",
|
||||||
|
"\n",
|
||||||
|
" Parameters:\n",
|
||||||
|
" -----------\n",
|
||||||
|
" history : tensorflow.keras.callbacks.History\n",
|
||||||
|
" L'oggetto history restituito dal training del modello\n",
|
||||||
|
" folder_name : str\n",
|
||||||
|
" Cartella dove salvare il plot\n",
|
||||||
|
" \"\"\"\n",
|
||||||
|
" import os\n",
|
||||||
|
"\n",
|
||||||
|
" try:\n",
|
||||||
|
" # Crea la figura\n",
|
||||||
|
" plt.figure(figsize=(12, 4))\n",
|
||||||
|
"\n",
|
||||||
|
" # Plot della Loss\n",
|
||||||
|
" plt.subplot(1, 2, 1)\n",
|
||||||
|
" plt.plot(history.history['loss'], label='Training Loss')\n",
|
||||||
|
" plt.plot(history.history['val_loss'], label='Validation Loss')\n",
|
||||||
|
" plt.title('Model Loss')\n",
|
||||||
|
" plt.xlabel('Epoch')\n",
|
||||||
|
" plt.ylabel('Loss')\n",
|
||||||
|
" plt.legend()\n",
|
||||||
|
" plt.grid(True)\n",
|
||||||
|
"\n",
|
||||||
|
" # Plot del MAE\n",
|
||||||
|
" plt.subplot(1, 2, 2)\n",
|
||||||
|
" plt.plot(history.history['mae'], label='Training MAE')\n",
|
||||||
|
" plt.plot(history.history['val_mae'], label='Validation MAE')\n",
|
||||||
|
" plt.title('Model MAE')\n",
|
||||||
|
" plt.xlabel('Epoch')\n",
|
||||||
|
" plt.ylabel('MAE')\n",
|
||||||
|
" plt.legend()\n",
|
||||||
|
" plt.grid(True)\n",
|
||||||
|
"\n",
|
||||||
|
" plt.tight_layout()\n",
|
||||||
|
"\n",
|
||||||
|
" if folder_name is not None:\n",
|
||||||
|
" os.makedirs(folder_name, exist_ok=True)\n",
|
||||||
|
" # Genera il nome del file con timestamp\n",
|
||||||
|
" filename = os.path.join(folder_name, 'training_history.png')\n",
|
||||||
|
"\n",
|
||||||
|
" # Salva la figura\n",
|
||||||
|
" plt.savefig(filename, dpi=300, bbox_inches='tight')\n",
|
||||||
|
" print(f\"\\nPlot della training history salvato come: {filename}\")\n",
|
||||||
|
"\n",
|
||||||
|
" # Salva anche i dati numerici in formato CSV\n",
|
||||||
|
" history_df = pd.DataFrame({\n",
|
||||||
|
" 'epoch': range(1, len(history.history['loss']) + 1),\n",
|
||||||
|
" 'training_loss': history.history['loss'],\n",
|
||||||
|
" 'validation_loss': history.history['val_loss'],\n",
|
||||||
|
" 'training_mae': history.history['mae'],\n",
|
||||||
|
" 'validation_mae': history.history['val_mae']\n",
|
||||||
|
" })\n",
|
||||||
|
"\n",
|
||||||
|
" if folder_name is not None:\n",
|
||||||
|
" csv_filename = os.path.join(folder_name, 'training_history.csv')\n",
|
||||||
|
" history_df.to_csv(csv_filename, index=False)\n",
|
||||||
|
" print(f\"Dati della training history salvati come: {csv_filename}\")\n",
|
||||||
|
"\n",
|
||||||
|
" # Calcola e salva le statistiche finali\n",
|
||||||
|
" final_stats = {\n",
|
||||||
|
" 'final_training_loss': history.history['loss'][-1],\n",
|
||||||
|
" 'final_validation_loss': history.history['val_loss'][-1],\n",
|
||||||
|
" 'final_training_mae': history.history['mae'][-1],\n",
|
||||||
|
" 'final_validation_mae': history.history['val_mae'][-1],\n",
|
||||||
|
" 'best_validation_loss': min(history.history['val_loss']),\n",
|
||||||
|
" 'best_validation_mae': min(history.history['val_mae']),\n",
|
||||||
|
" 'epochs': len(history.history['loss']),\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" if folder_name is not None:\n",
|
||||||
|
" # Salva le statistiche in formato JSON\n",
|
||||||
|
" stats_filename = os.path.join(folder_name, 'training_stats.json')\n",
|
||||||
|
" with open(stats_filename, 'w') as f:\n",
|
||||||
|
" json.dump(final_stats, f, indent=4)\n",
|
||||||
|
" print(f\"Statistiche finali salvate come: {stats_filename}\")\n",
|
||||||
|
"\n",
|
||||||
|
" # Stampa le statistiche principali\n",
|
||||||
|
" print(\"\\nStatistiche finali del training:\")\n",
|
||||||
|
" print(f\"Loss finale (train/val): {final_stats['final_training_loss']:.4f}/{final_stats['final_validation_loss']:.4f}\")\n",
|
||||||
|
" print(f\"MAE finale (train/val): {final_stats['final_training_mae']:.4f}/{final_stats['final_validation_mae']:.4f}\")\n",
|
||||||
|
" print(f\"Miglior validation loss: {final_stats['best_validation_loss']:.4f}\")\n",
|
||||||
|
" print(f\"Miglior validation MAE: {final_stats['best_validation_mae']:.4f}\")\n",
|
||||||
|
"\n",
|
||||||
|
" plt.show()\n",
|
||||||
|
"\n",
|
||||||
|
" except Exception as e:\n",
|
||||||
|
" print(f\"\\nErrore durante la creazione o il salvataggio dei plot: {str(e)}\")\n",
|
||||||
|
"\n",
|
||||||
"\n",
|
"\n",
|
||||||
"def train_hybrid_model(model, X_train, y_train, X_test, y_test, epochs=100, batch_size=32, folder_name='uv_index'):\n",
|
"def train_hybrid_model(model, X_train, y_train, X_test, y_test, epochs=100, batch_size=32, folder_name='uv_index'):\n",
|
||||||
" \"\"\"\n",
|
" \"\"\"\n",
|
||||||
@ -538,50 +712,44 @@
|
|||||||
" # Early Stopping avanzato\n",
|
" # Early Stopping avanzato\n",
|
||||||
" EarlyStopping(\n",
|
" EarlyStopping(\n",
|
||||||
" monitor='mae',\n",
|
" monitor='mae',\n",
|
||||||
" patience=15, # Aumentato per dare più tempo dopo le riduzioni del LR\n",
|
" patience=15,\n",
|
||||||
" restore_best_weights=True,\n",
|
" restore_best_weights=True,\n",
|
||||||
" mode='min',\n",
|
" mode='min',\n",
|
||||||
" verbose=1,\n",
|
" verbose=1,\n",
|
||||||
" min_delta=1e-6 # Reso più sensibile\n",
|
" min_delta=1e-6\n",
|
||||||
" ),\n",
|
" ),\n",
|
||||||
" # Learning Rate Schedule molto più aggressivo\n",
|
|
||||||
" ReduceLROnPlateau(\n",
|
" ReduceLROnPlateau(\n",
|
||||||
" monitor='mae',\n",
|
" monitor='mae',\n",
|
||||||
" factor=0.05, # Riduzione molto più aggressiva (95% di riduzione)\n",
|
" factor=0.05,\n",
|
||||||
" patience=3, # Ridotto per reagire più velocemente\n",
|
" patience=3,\n",
|
||||||
" verbose=1,\n",
|
" verbose=1,\n",
|
||||||
" mode='min',\n",
|
" mode='min',\n",
|
||||||
" min_delta=1e-6,\n",
|
" min_delta=1e-6,\n",
|
||||||
" cooldown=2, # Ridotto per permettere riduzioni più frequenti\n",
|
" cooldown=2,\n",
|
||||||
" min_lr=1e-7 # LR minimo più basso\n",
|
" min_lr=1e-7\n",
|
||||||
" ),\n",
|
" ),\n",
|
||||||
" # Aggiungiamo un secondo scheduler per riduzioni ancora più graduali\n",
|
|
||||||
" ReduceLROnPlateau(\n",
|
" ReduceLROnPlateau(\n",
|
||||||
" monitor='val_loss',\n",
|
" monitor='val_loss',\n",
|
||||||
" factor=0.2, # Riduzione più moderata come backup\n",
|
" factor=0.2,\n",
|
||||||
" patience=2, # Ancora più reattivo\n",
|
" patience=2,\n",
|
||||||
" verbose=1,\n",
|
" verbose=1,\n",
|
||||||
" mode='min',\n",
|
" mode='min',\n",
|
||||||
" min_delta=1e-6,\n",
|
" min_delta=1e-6,\n",
|
||||||
" cooldown=1,\n",
|
" cooldown=1,\n",
|
||||||
" min_lr=1e-7\n",
|
" min_lr=1e-7\n",
|
||||||
" ),\n",
|
" ),\n",
|
||||||
" # Model Checkpoint per salvare i migliori modelli\n",
|
|
||||||
" tf.keras.callbacks.ModelCheckpoint(\n",
|
" tf.keras.callbacks.ModelCheckpoint(\n",
|
||||||
" filepath=f'{folder_name}_best_uv_model.h5',\n",
|
" filepath=f'{folder_name}_best_uv_model.h5',\n",
|
||||||
" monitor='mae',\n",
|
" monitor='mae',\n",
|
||||||
" save_best_only=True,\n",
|
" save_best_only=True,\n",
|
||||||
" mode='min'\n",
|
" mode='min'\n",
|
||||||
" ),\n",
|
" ),\n",
|
||||||
" # TensorBoard callback per il monitoraggio\n",
|
|
||||||
" tf.keras.callbacks.TensorBoard(\n",
|
" tf.keras.callbacks.TensorBoard(\n",
|
||||||
" log_dir=f'./logs_{folder_name}',\n",
|
" log_dir=f'./logs_{folder_name}',\n",
|
||||||
" histogram_freq=1,\n",
|
" histogram_freq=1,\n",
|
||||||
" write_graph=True,\n",
|
" write_graph=True,\n",
|
||||||
" update_freq='epoch'\n",
|
" update_freq='epoch'\n",
|
||||||
" ),\n",
|
" ),\n",
|
||||||
"\n",
|
|
||||||
" # Custom Callback per monitorare le predizioni fuori range\n",
|
|
||||||
" tf.keras.callbacks.LambdaCallback(\n",
|
" tf.keras.callbacks.LambdaCallback(\n",
|
||||||
" on_epoch_end=lambda epoch, logs: print(\n",
|
" on_epoch_end=lambda epoch, logs: print(\n",
|
||||||
" f\"\\nEpoch {epoch + 1}: Predizioni fuori range: \"\n",
|
" f\"\\nEpoch {epoch + 1}: Predizioni fuori range: \"\n",
|
||||||
@ -590,19 +758,6 @@
|
|||||||
" )\n",
|
" )\n",
|
||||||
" ]\n",
|
" ]\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Calcolo dei class weights se non forniti\n",
|
|
||||||
" '''\n",
|
|
||||||
" if class_weights is None:\n",
|
|
||||||
" # Discretizziamo i valori UV per il calcolo dei pesi\n",
|
|
||||||
" y_discrete = np.round(y_train).astype(int)\n",
|
|
||||||
" class_weights = compute_class_weight(\n",
|
|
||||||
" 'balanced',\n",
|
|
||||||
" classes=np.unique(y_discrete),\n",
|
|
||||||
" y=y_discrete\n",
|
|
||||||
" )\n",
|
|
||||||
" class_weights = dict(enumerate(class_weights))\n",
|
|
||||||
" '''\n",
|
|
||||||
" # Training con gestione degli errori e logging\n",
|
|
||||||
" try:\n",
|
" try:\n",
|
||||||
" history = model.fit(\n",
|
" history = model.fit(\n",
|
||||||
" X_train, y_train,\n",
|
" X_train, y_train,\n",
|
||||||
@ -610,9 +765,8 @@
|
|||||||
" epochs=epochs,\n",
|
" epochs=epochs,\n",
|
||||||
" batch_size=batch_size,\n",
|
" batch_size=batch_size,\n",
|
||||||
" callbacks=callbacks,\n",
|
" callbacks=callbacks,\n",
|
||||||
" #class_weight=class_weights,\n",
|
|
||||||
" verbose=1,\n",
|
" verbose=1,\n",
|
||||||
" shuffle=False, # Abilitato shuffle\n",
|
" shuffle=False,\n",
|
||||||
" validation_freq=1,\n",
|
" validation_freq=1,\n",
|
||||||
" )\n",
|
" )\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -631,41 +785,7 @@
|
|||||||
" out_of_range = np.sum((predictions < 0) | (predictions > 11))\n",
|
" out_of_range = np.sum((predictions < 0) | (predictions > 11))\n",
|
||||||
" print(f\"\\nPredizioni fuori range: {out_of_range} ({out_of_range / len(predictions) * 100:.2f}%)\")\n",
|
" print(f\"\\nPredizioni fuori range: {out_of_range} ({out_of_range / len(predictions) * 100:.2f}%)\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Plot della loss durante il training\n",
|
" plot_training_history(history, folder_name=folder_name)\n",
|
||||||
" plt.figure(figsize=(12, 4))\n",
|
|
||||||
"\n",
|
|
||||||
" plt.subplot(1, 2, 1)\n",
|
|
||||||
" plt.plot(history.history['loss'], label='Training Loss')\n",
|
|
||||||
" plt.plot(history.history['val_loss'], label='Validation Loss')\n",
|
|
||||||
" plt.title('Model Loss')\n",
|
|
||||||
" plt.xlabel('Epoch')\n",
|
|
||||||
" plt.ylabel('Loss')\n",
|
|
||||||
" plt.legend()\n",
|
|
||||||
"\n",
|
|
||||||
" plt.subplot(1, 2, 2)\n",
|
|
||||||
" plt.plot(history.history['mae'], label='Training MAE')\n",
|
|
||||||
" plt.plot(history.history['val_mae'], label='Validation MAE')\n",
|
|
||||||
" plt.title('Model MAE')\n",
|
|
||||||
" plt.xlabel('Epoch')\n",
|
|
||||||
" plt.ylabel('MAE')\n",
|
|
||||||
" plt.legend()\n",
|
|
||||||
"\n",
|
|
||||||
" plt.tight_layout()\n",
|
|
||||||
" plt.show()\n",
|
|
||||||
"\n",
|
|
||||||
" # Salvataggio dei risultati del training\n",
|
|
||||||
" training_results = {\n",
|
|
||||||
" 'final_loss': float(test_loss),\n",
|
|
||||||
" 'final_mae': float(test_mae),\n",
|
|
||||||
" 'final_mse': float(test_mse),\n",
|
|
||||||
" 'out_of_range_predictions': int(out_of_range),\n",
|
|
||||||
" 'training_time': int(len(history.history['loss'])),\n",
|
|
||||||
" 'best_epoch': int(np.argmin(history.history['val_loss'])) + 1\n",
|
|
||||||
" }\n",
|
|
||||||
"\n",
|
|
||||||
" # Salvataggio su file\n",
|
|
||||||
" with open('training_results.json', 'w') as f:\n",
|
|
||||||
" json.dump(training_results, f, indent=4)\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
" return history\n",
|
" return history\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -768,7 +888,7 @@
|
|||||||
" print(\"Inizializzazione del training del modello UV index...\")\n",
|
" print(\"Inizializzazione del training del modello UV index...\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
" try:\n",
|
" try:\n",
|
||||||
" folder_name = datetime.now().strftime(\"%Y-%m-%d_%H-%M\")\n",
|
"\n",
|
||||||
" # Preparazione dei dati\n",
|
" # Preparazione dei dati\n",
|
||||||
" print(\"\\n1. Preparazione dei dati...\")\n",
|
" print(\"\\n1. Preparazione dei dati...\")\n",
|
||||||
" X_train_seq, X_test_seq, y_train, y_test, scaler, features, X_to_predict_seq = prepare_hybrid_data(df)\n",
|
" X_train_seq, X_test_seq, y_train, y_test, scaler, features, X_to_predict_seq = prepare_hybrid_data(df)\n",
|
||||||
@ -783,7 +903,7 @@
|
|||||||
" # Creazione del modello\n",
|
" # Creazione del modello\n",
|
||||||
" print(\"\\n2. Creazione del modello...\")\n",
|
" print(\"\\n2. Creazione del modello...\")\n",
|
||||||
" input_shape = (X_train_seq.shape[1], X_train_seq.shape[2])\n",
|
" input_shape = (X_train_seq.shape[1], X_train_seq.shape[2])\n",
|
||||||
" model = create_optimized_model(input_shape, folder_name)\n",
|
" model = create_uv_index_model(input_shape, folder_name)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" print(\"\\n4. Avvio del training...\")\n",
|
" print(\"\\n4. Avvio del training...\")\n",
|
||||||
" history = train_hybrid_model(\n",
|
" history = train_hybrid_model(\n",
|
||||||
@ -792,7 +912,6 @@
|
|||||||
" y_train=y_train,\n",
|
" y_train=y_train,\n",
|
||||||
" X_test=X_test_seq,\n",
|
" X_test=X_test_seq,\n",
|
||||||
" y_test=y_test,\n",
|
" y_test=y_test,\n",
|
||||||
" #class_weights=class_weights, # Ora passiamo direttamente il dizionario\n",
|
|
||||||
" epochs=100,\n",
|
" epochs=100,\n",
|
||||||
" batch_size=128,\n",
|
" batch_size=128,\n",
|
||||||
" folder_name=folder_name\n",
|
" folder_name=folder_name\n",
|
||||||
@ -803,10 +922,7 @@
|
|||||||
" predictions = np.clip(predictions, 0, 11)\n",
|
" predictions = np.clip(predictions, 0, 11)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" print(\"\\n6. Valutazione del modello...\")\n",
|
" print(\"\\n6. Valutazione del modello...\")\n",
|
||||||
" metrics = evaluate_uv_predictions(y_test, predictions)\n",
|
" metrics = evaluate_uv_predictions(y_test, predictions, folder_name=folder_name)\n",
|
||||||
"\n",
|
|
||||||
" print(\"\\n7. Visualizzazione risultati...\")\n",
|
|
||||||
" plot_uv_predictions(y_test, predictions)\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
" # Creazione del dizionario dei risultati\n",
|
" # Creazione del dizionario dei risultati\n",
|
||||||
" training_results = {\n",
|
" training_results = {\n",
|
||||||
@ -829,13 +945,15 @@
|
|||||||
" }\n",
|
" }\n",
|
||||||
" }\n",
|
" }\n",
|
||||||
"\n",
|
"\n",
|
||||||
" print(\"\\n8. Predizione dei dati mancanti risultati...\")\n",
|
" print(\"\\n7. Predizione dei dati mancanti risultati...\")\n",
|
||||||
" to_predict_predictions = model.predict(X_to_predict_seq)\n",
|
" to_predict_predictions = model.predict(X_to_predict_seq)\n",
|
||||||
" to_predict_predictions = np.clip(to_predict_predictions, 0, 11)\n",
|
" to_predict_predictions = np.clip(to_predict_predictions, 0, 11)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" print(\"\\n9. Integrazione delle predizioni nel dataset originale...\")\n",
|
" print(\"\\n8. Integrazione delle predizioni nel dataset originale...\")\n",
|
||||||
" df_updated = integrate_predictions(df.copy(), to_predict_predictions)\n",
|
" df_updated = integrate_predictions(df.copy(), to_predict_predictions)\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
" df_updated.to_parquet('./data/weather_data_uvindex.parquet')\n",
|
||||||
|
"\n",
|
||||||
" # Aggiungi statistiche sulle predizioni al training_results\n",
|
" # Aggiungi statistiche sulle predizioni al training_results\n",
|
||||||
" training_results['prediction_stats'] = {\n",
|
" training_results['prediction_stats'] = {\n",
|
||||||
" 'n_predictions_added': len(to_predict_predictions),\n",
|
" 'n_predictions_added': len(to_predict_predictions),\n",
|
||||||
@ -1380,10 +1498,22 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"def plot_error_analysis(y_true, y_pred):\n",
|
"def plot_error_analysis(y_true, y_pred, folder_name=None):\n",
|
||||||
" \"\"\"\n",
|
" \"\"\"\n",
|
||||||
" Funzione per visualizzare l'analisi degli errori di predizione\n",
|
" Funzione per visualizzare l'analisi degli errori di predizione\n",
|
||||||
|
"\n",
|
||||||
|
" Parameters:\n",
|
||||||
|
" -----------\n",
|
||||||
|
" y_true : array-like\n",
|
||||||
|
" Valori reali\n",
|
||||||
|
" y_pred : array-like\n",
|
||||||
|
" Valori predetti\n",
|
||||||
|
" folder_name : str, optional\n",
|
||||||
|
" Cartella dove salvare i plot. Se None, i plot non vengono salvati.\n",
|
||||||
" \"\"\"\n",
|
" \"\"\"\n",
|
||||||
|
" import os\n",
|
||||||
|
" from datetime import datetime\n",
|
||||||
|
"\n",
|
||||||
" # Converti in array numpy 1D se necessario\n",
|
" # Converti in array numpy 1D se necessario\n",
|
||||||
" if isinstance(y_true, pd.Series):\n",
|
" if isinstance(y_true, pd.Series):\n",
|
||||||
" y_true = y_true.values\n",
|
" y_true = y_true.values\n",
|
||||||
@ -1396,7 +1526,8 @@
|
|||||||
" # Calcola gli errori\n",
|
" # Calcola gli errori\n",
|
||||||
" errors = y_pred - y_true\n",
|
" errors = y_pred - y_true\n",
|
||||||
"\n",
|
"\n",
|
||||||
" plt.figure(figsize=(15, 5))\n",
|
" # Crea la figura principale\n",
|
||||||
|
" fig = plt.figure(figsize=(15, 5))\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Plot 1: Distribuzione degli errori\n",
|
" # Plot 1: Distribuzione degli errori\n",
|
||||||
" plt.subplot(1, 3, 1)\n",
|
" plt.subplot(1, 3, 1)\n",
|
||||||
@ -1422,6 +1553,23 @@
|
|||||||
" plt.ylabel('Errore')\n",
|
" plt.ylabel('Errore')\n",
|
||||||
"\n",
|
"\n",
|
||||||
" plt.tight_layout()\n",
|
" plt.tight_layout()\n",
|
||||||
|
"\n",
|
||||||
|
" # Salva il plot se è specificata una cartella\n",
|
||||||
|
" if folder_name is not None:\n",
|
||||||
|
" try:\n",
|
||||||
|
" # Crea la cartella se non esiste\n",
|
||||||
|
" os.makedirs(folder_name, exist_ok=True)\n",
|
||||||
|
"\n",
|
||||||
|
" # Genera il nome del file con timestamp\n",
|
||||||
|
" timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||||
|
" filename = os.path.join(folder_name, f'error_analysis_{timestamp}.png')\n",
|
||||||
|
"\n",
|
||||||
|
" # Salva la figura\n",
|
||||||
|
" plt.savefig(filename, dpi=300, bbox_inches='tight')\n",
|
||||||
|
" print(f\"\\nPlot salvato come: {filename}\")\n",
|
||||||
|
" except Exception as e:\n",
|
||||||
|
" print(f\"\\nErrore nel salvare il plot: {str(e)}\")\n",
|
||||||
|
"\n",
|
||||||
" plt.show()\n",
|
" plt.show()\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Stampa statistiche degli errori\n",
|
" # Stampa statistiche degli errori\n",
|
||||||
@ -1439,7 +1587,7 @@
|
|||||||
" print(f\"Predizioni entro ±{threshold}: {within_threshold:.1f}%\")\n",
|
" print(f\"Predizioni entro ±{threshold}: {within_threshold:.1f}%\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\n",
|
"\n",
|
||||||
"plot_error_analysis(y_test, predictions)"
|
"plot_error_analysis(y_test, predictions, folder_name=folder_name)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1499,17 +1647,29 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"source": [
|
"source": [
|
||||||
"def plot_advanced_prediction_analysis(y_true, y_pred):\n",
|
"def plot_advanced_prediction_analysis(y_true, y_pred, folder_name=None):\n",
|
||||||
" \"\"\"\n",
|
" \"\"\"\n",
|
||||||
" Funzione per visualizzare l'analisi degli errori di predizione e la precisione\n",
|
" Funzione per visualizzare l'analisi degli errori di predizione e la precisione\n",
|
||||||
|
"\n",
|
||||||
|
" Parameters:\n",
|
||||||
|
" -----------\n",
|
||||||
|
" y_true : array-like\n",
|
||||||
|
" Valori reali\n",
|
||||||
|
" y_pred : array-like\n",
|
||||||
|
" Valori predetti\n",
|
||||||
|
" folder_name : str, optional\n",
|
||||||
|
" Cartella dove salvare i plot. Se None, i plot non vengono salvati.\n",
|
||||||
" \"\"\"\n",
|
" \"\"\"\n",
|
||||||
|
" import os\n",
|
||||||
|
" from datetime import datetime\n",
|
||||||
|
" import seaborn as sns\n",
|
||||||
|
"\n",
|
||||||
" # Converti in array numpy 1D se necessario\n",
|
" # Converti in array numpy 1D se necessario\n",
|
||||||
" if isinstance(y_true, pd.Series):\n",
|
" if isinstance(y_true, pd.Series):\n",
|
||||||
" y_true = y_true.values\n",
|
" y_true = y_true.values\n",
|
||||||
" if isinstance(y_pred, pd.Series):\n",
|
" if isinstance(y_pred, pd.Series):\n",
|
||||||
" y_pred = y_pred.values\n",
|
" y_pred = y_pred.values\n",
|
||||||
"\n",
|
"\n",
|
||||||
"\n",
|
|
||||||
" y_true = y_true.ravel()\n",
|
" y_true = y_true.ravel()\n",
|
||||||
" y_pred = y_pred.ravel()\n",
|
" y_pred = y_pred.ravel()\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -1517,12 +1677,10 @@
|
|||||||
" errors = y_pred - y_true\n",
|
" errors = y_pred - y_true\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Calcola accuracy per diversi livelli di tolleranza\n",
|
" # Calcola accuracy per diversi livelli di tolleranza\n",
|
||||||
" exact_accuracy = np.mean(np.abs(errors) < 0.1) * 100 # Precisione esatta (±0.1)\n",
|
" exact_accuracy = np.mean(np.abs(errors) < 0.1) * 100\n",
|
||||||
" accuracy_05 = np.mean(np.abs(errors) <= 0.5) * 100 # Precisione entro ±0.5\n",
|
" accuracy_05 = np.mean(np.abs(errors) <= 0.5) * 100\n",
|
||||||
" accuracy_10 = np.mean(np.abs(errors) <= 1.0) * 100 # Precisione entro ±1.0\n",
|
" accuracy_10 = np.mean(np.abs(errors) <= 1.0) * 100\n",
|
||||||
"\n",
|
"\n",
|
||||||
" \n",
|
|
||||||
" # Calcola accuracy per livelli di rischio UV\n",
|
|
||||||
" def get_risk_level(uv):\n",
|
" def get_risk_level(uv):\n",
|
||||||
" if uv < 2:\n",
|
" if uv < 2:\n",
|
||||||
" return 'Basso'\n",
|
" return 'Basso'\n",
|
||||||
@ -1535,12 +1693,12 @@
|
|||||||
" else:\n",
|
" else:\n",
|
||||||
" return 'Estremo'\n",
|
" return 'Estremo'\n",
|
||||||
"\n",
|
"\n",
|
||||||
" \n",
|
|
||||||
" y_true_risk = [get_risk_level(x) for x in y_true]\n",
|
" y_true_risk = [get_risk_level(x) for x in y_true]\n",
|
||||||
" y_pred_risk = [get_risk_level(x) for x in y_pred]\n",
|
" y_pred_risk = [get_risk_level(x) for x in y_pred]\n",
|
||||||
" risk_accuracy = np.mean(np.array(y_true_risk) == np.array(y_pred_risk)) * 100\n",
|
" risk_accuracy = np.mean(np.array(y_true_risk) == np.array(y_pred_risk)) * 100\n",
|
||||||
"\n",
|
"\n",
|
||||||
" plt.figure(figsize=(20, 10))\n",
|
" # Crea la figura principale\n",
|
||||||
|
" fig = plt.figure(figsize=(20, 10))\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Plot 1: Distribuzione degli errori\n",
|
" # Plot 1: Distribuzione degli errori\n",
|
||||||
" plt.subplot(2, 2, 1)\n",
|
" plt.subplot(2, 2, 1)\n",
|
||||||
@ -1568,7 +1726,6 @@
|
|||||||
" # Plot 4: Precisione per intervallo di UV\n",
|
" # Plot 4: Precisione per intervallo di UV\n",
|
||||||
" plt.subplot(2, 2, 4)\n",
|
" plt.subplot(2, 2, 4)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Definisci gli intervalli UV\n",
|
|
||||||
" uv_ranges = [(0, 2), (2, 5), (5, 7), (7, 10), (10, 11)]\n",
|
" uv_ranges = [(0, 2), (2, 5), (5, 7), (7, 10), (10, 11)]\n",
|
||||||
" range_labels = ['Basso\\n(0-2)', 'Moderato\\n(2-5)', 'Alto\\n(5-7)', 'Molto Alto\\n(7-10)', 'Estremo\\n(10-11)']\n",
|
" range_labels = ['Basso\\n(0-2)', 'Moderato\\n(2-5)', 'Alto\\n(5-7)', 'Molto Alto\\n(7-10)', 'Estremo\\n(10-11)']\n",
|
||||||
"\n",
|
"\n",
|
||||||
@ -1579,49 +1736,72 @@
|
|||||||
" for (low, high) in uv_ranges:\n",
|
" for (low, high) in uv_ranges:\n",
|
||||||
" mask = (y_true >= low) & (y_true < high)\n",
|
" mask = (y_true >= low) & (y_true < high)\n",
|
||||||
" if mask.any():\n",
|
" if mask.any():\n",
|
||||||
" # Calcola MAE per questo range\n",
|
|
||||||
" mae = np.mean(np.abs(y_pred[mask] - y_true[mask]))\n",
|
" mae = np.mean(np.abs(y_pred[mask] - y_true[mask]))\n",
|
||||||
" mae_per_range.append(mae)\n",
|
" mae_per_range.append(mae)\n",
|
||||||
" # Conta quanti valori in questo range\n",
|
|
||||||
" count = np.sum(mask)\n",
|
" count = np.sum(mask)\n",
|
||||||
" counts.append(count)\n",
|
" counts.append(count)\n",
|
||||||
" # Calcola precisione entro 0.5 punti UV\n",
|
|
||||||
" accuracy = np.mean(np.abs(y_pred[mask] - y_true[mask]) <= 0.5) * 100\n",
|
" accuracy = np.mean(np.abs(y_pred[mask] - y_true[mask]) <= 0.5) * 100\n",
|
||||||
" accuracies.append(accuracy)\n",
|
" accuracies.append(accuracy)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Crea il grafico a barre con doppio asse y\n",
|
" # Crea il grafico a barre con doppio asse y\n",
|
||||||
" fig = plt.gca()\n",
|
" ax = plt.gca()\n",
|
||||||
" \n",
|
|
||||||
" # Barre per accuratezza\n",
|
|
||||||
" bars = plt.bar(range_labels, accuracies, alpha=0.6, color='skyblue')\n",
|
" bars = plt.bar(range_labels, accuracies, alpha=0.6, color='skyblue')\n",
|
||||||
" plt.ylabel('Precisione (%)')\n",
|
" plt.ylabel('Precisione (%)')\n",
|
||||||
" plt.title('Precisione e MAE per Range UV')\n",
|
" plt.title('Precisione e MAE per Range UV')\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Aggiungi etichette sopra le barre\n",
|
|
||||||
" for bar in bars:\n",
|
" for bar in bars:\n",
|
||||||
" height = bar.get_height()\n",
|
" height = bar.get_height()\n",
|
||||||
" plt.text(bar.get_x() + bar.get_width() / 2., height,\n",
|
" plt.text(bar.get_x() + bar.get_width() / 2., height,\n",
|
||||||
" f'{height:.1f}%\\n(n={counts[bars.index(bar)]})',\n",
|
" f'{height:.1f}%\\n(n={counts[bars.index(bar)]})',\n",
|
||||||
" ha='center', va='bottom')\n",
|
" ha='center', va='bottom')\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Secondo asse y per MAE\n",
|
" ax2 = ax.twinx()\n",
|
||||||
" ax2 = fig.twinx()\n",
|
|
||||||
" line = ax2.plot(range_labels, mae_per_range, 'r-', marker='o', label='MAE')\n",
|
" line = ax2.plot(range_labels, mae_per_range, 'r-', marker='o', label='MAE')\n",
|
||||||
" ax2.set_ylabel('MAE', color='red')\n",
|
" ax2.set_ylabel('MAE', color='red')\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Aggiungi valori MAE\n",
|
|
||||||
" for i, mae in enumerate(mae_per_range):\n",
|
" for i, mae in enumerate(mae_per_range):\n",
|
||||||
" ax2.text(i, mae, f'MAE: {mae:.3f}', color='red', ha='center', va='bottom')\n",
|
" ax2.text(i, mae, f'MAE: {mae:.3f}', color='red', ha='center', va='bottom')\n",
|
||||||
"\n",
|
"\n",
|
||||||
" plt.xticks(rotation=45)\n",
|
" plt.xticks(rotation=45)\n",
|
||||||
" plt.tight_layout()\n",
|
" plt.tight_layout()\n",
|
||||||
" plt.show()\n",
|
|
||||||
"\n",
|
"\n",
|
||||||
|
" # Salva la figura principale se è specificata una cartella\n",
|
||||||
|
" if folder_name is not None:\n",
|
||||||
|
" try:\n",
|
||||||
|
" # Crea la cartella se non esiste\n",
|
||||||
|
" os.makedirs(folder_name, exist_ok=True)\n",
|
||||||
|
"\n",
|
||||||
|
" # Genera il timestamp\n",
|
||||||
|
" timestamp = datetime.now().strftime(\"%Y%m%d_%H%M%S\")\n",
|
||||||
|
"\n",
|
||||||
|
" # Salva la figura principale\n",
|
||||||
|
" main_plot_filename = os.path.join(folder_name, f'advanced_analysis_{timestamp}.png')\n",
|
||||||
|
" plt.savefig(main_plot_filename, dpi=300, bbox_inches='tight')\n",
|
||||||
|
" print(f\"\\nPlot principale salvato come: {main_plot_filename}\")\n",
|
||||||
|
"\n",
|
||||||
|
" # Crea e salva la matrice di confusione come plot separato\n",
|
||||||
|
" plt.figure(figsize=(10, 8))\n",
|
||||||
" cm = confusion_matrix(y_true_risk, y_pred_risk)\n",
|
" cm = confusion_matrix(y_true_risk, y_pred_risk)\n",
|
||||||
" risk_levels = ['Basso', 'Moderato', 'Alto', 'Molto Alto', 'Estremo']\n",
|
" risk_levels = ['Basso', 'Moderato', 'Alto', 'Molto Alto', 'Estremo']\n",
|
||||||
" cm_df = pd.DataFrame(cm,\n",
|
" cm_df = pd.DataFrame(cm, columns=risk_levels, index=risk_levels)\n",
|
||||||
" columns=risk_levels,\n",
|
"\n",
|
||||||
" index=risk_levels)\n",
|
" sns.heatmap(cm_df, annot=True, fmt='d', cmap='Blues')\n",
|
||||||
|
" plt.title('Matrice di Confusione per Livelli di Rischio UV')\n",
|
||||||
|
" plt.tight_layout()\n",
|
||||||
|
"\n",
|
||||||
|
" conf_matrix_filename = os.path.join(folder_name, f'confusion_matrix_{timestamp}.png')\n",
|
||||||
|
" plt.savefig(conf_matrix_filename, dpi=300, bbox_inches='tight')\n",
|
||||||
|
" print(f\"Matrice di confusione salvata come: {conf_matrix_filename}\")\n",
|
||||||
|
"\n",
|
||||||
|
" except Exception as e:\n",
|
||||||
|
" print(f\"\\nErrore nel salvare i plot: {str(e)}\")\n",
|
||||||
|
"\n",
|
||||||
|
" plt.show()\n",
|
||||||
|
"\n",
|
||||||
|
" # Stampa delle statistiche e analisi\n",
|
||||||
|
" cm = confusion_matrix(y_true_risk, y_pred_risk)\n",
|
||||||
|
" risk_levels = ['Basso', 'Moderato', 'Alto', 'Molto Alto', 'Estremo']\n",
|
||||||
|
" cm_df = pd.DataFrame(cm, columns=risk_levels, index=risk_levels)\n",
|
||||||
"\n",
|
"\n",
|
||||||
" print(\"\\nMatrice di Confusione per Livelli di Rischio UV:\")\n",
|
" print(\"\\nMatrice di Confusione per Livelli di Rischio UV:\")\n",
|
||||||
" print(cm_df)\n",
|
" print(cm_df)\n",
|
||||||
@ -1649,15 +1829,15 @@
|
|||||||
" print(f\"Errore mediano: {np.median(errors):.3f}\")\n",
|
" print(f\"Errore mediano: {np.median(errors):.3f}\")\n",
|
||||||
" print(f\"95° percentile errore assoluto: {np.percentile(np.abs(errors), 95):.3f}\")\n",
|
" print(f\"95° percentile errore assoluto: {np.percentile(np.abs(errors), 95):.3f}\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
" # Calcola percentuali di errori entro certe soglie\n",
|
|
||||||
" thresholds = [0.5, 1.0, 1.5, 2.0]\n",
|
|
||||||
" print(\"\\nDistribuzione degli errori:\")\n",
|
" print(\"\\nDistribuzione degli errori:\")\n",
|
||||||
|
" thresholds = [0.5, 1.0, 1.5, 2.0]\n",
|
||||||
" for threshold in thresholds:\n",
|
" for threshold in thresholds:\n",
|
||||||
" within_threshold = np.mean(np.abs(errors) <= threshold) * 100\n",
|
" within_threshold = np.mean(np.abs(errors) <= threshold) * 100\n",
|
||||||
" print(f\"Predizioni entro ±{threshold}: {within_threshold:.1f}%\")\n",
|
" print(f\"Predizioni entro ±{threshold}: {within_threshold:.1f}%\")\n",
|
||||||
"\n",
|
"\n",
|
||||||
|
"\n",
|
||||||
"# Usa la funzione\n",
|
"# Usa la funzione\n",
|
||||||
"plot_advanced_prediction_analysis(y_test, predictions)"
|
"plot_advanced_prediction_analysis(y_test, predictions, folder_name=folder_name)"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1,7 +0,0 @@
|
|||||||
Metadata-Version: 2.1
|
|
||||||
Name: olive_oil_dashboard
|
|
||||||
Version: 0.1
|
|
||||||
Requires-Dist: pandas
|
|
||||||
Requires-Dist: numpy
|
|
||||||
Requires-Dist: tensorflow
|
|
||||||
Requires-Dist: scikit-learn
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
README.md
|
|
||||||
setup.py
|
|
||||||
model_train/__init__.py
|
|
||||||
model_train/create_train_dataset.py
|
|
||||||
olive_oil_dashboard.egg-info/PKG-INFO
|
|
||||||
olive_oil_dashboard.egg-info/SOURCES.txt
|
|
||||||
olive_oil_dashboard.egg-info/dependency_links.txt
|
|
||||||
olive_oil_dashboard.egg-info/requires.txt
|
|
||||||
olive_oil_dashboard.egg-info/top_level.txt
|
|
||||||
utils/__init__.py
|
|
||||||
utils/helpers.py
|
|
||||||
@ -1 +0,0 @@
|
|||||||
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
pandas
|
|
||||||
numpy
|
|
||||||
tensorflow
|
|
||||||
scikit-learn
|
|
||||||
@ -1,2 +0,0 @@
|
|||||||
model_train
|
|
||||||
utils
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,697 +0,0 @@
|
|||||||
import tensorflow as tf
|
|
||||||
from tf.keras.layers import Dense, LSTM, Conv1D, MultiHeadAttention, Dropout, BatchNormalization, LayerNormalization, GlobalAveragePooling1D, Concatenate, Input, Reshape, Activation
|
|
||||||
from tf.keras.models import Model
|
|
||||||
import tf.keras.backend as K
|
|
||||||
import pandas as pd
|
|
||||||
import numpy as np
|
|
||||||
from sklearn.model_selection import train_test_split
|
|
||||||
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
|
|
||||||
from sklearn.preprocessing import StandardScaler, MinMaxScaler
|
|
||||||
from tf.keras.callbacks import EarlyStopping, ReduceLROnPlateau
|
|
||||||
from tf.keras.optimizers import Adam
|
|
||||||
import matplotlib.pyplot as plt
|
|
||||||
import json
|
|
||||||
import joblib
|
|
||||||
from sklearn.utils.class_weight import compute_class_weight
|
|
||||||
|
|
||||||
|
|
||||||
def get_season(date):
|
|
||||||
month = date.month
|
|
||||||
day = date.day
|
|
||||||
if (month == 12 and day >= 21) or (month <= 3 and day < 20):
|
|
||||||
return 'Winter'
|
|
||||||
elif (month == 3 and day >= 20) or (month <= 6 and day < 21):
|
|
||||||
return 'Spring'
|
|
||||||
elif (month == 6 and day >= 21) or (month <= 9 and day < 23):
|
|
||||||
return 'Summer'
|
|
||||||
elif (month == 9 and day >= 23) or (month <= 12 and day < 21):
|
|
||||||
return 'Autumn'
|
|
||||||
else:
|
|
||||||
return 'Unknown'
|
|
||||||
|
|
||||||
|
|
||||||
def get_time_period(hour):
|
|
||||||
if 5 <= hour < 12:
|
|
||||||
return 'Morning'
|
|
||||||
elif 12 <= hour < 17:
|
|
||||||
return 'Afternoon'
|
|
||||||
elif 17 <= hour < 21:
|
|
||||||
return 'Evening'
|
|
||||||
else:
|
|
||||||
return 'Night'
|
|
||||||
|
|
||||||
|
|
||||||
def add_time_features(df):
|
|
||||||
df['datetime'] = pd.to_datetime(df['datetime'])
|
|
||||||
df['timestamp'] = df['datetime'].astype(np.int64) // 10 ** 9
|
|
||||||
df['year'] = df['datetime'].dt.year
|
|
||||||
df['month'] = df['datetime'].dt.month
|
|
||||||
df['day'] = df['datetime'].dt.day
|
|
||||||
df['hour'] = df['datetime'].dt.hour
|
|
||||||
df['minute'] = df['datetime'].dt.minute
|
|
||||||
df['hour_sin'] = np.sin(df['hour'] * (2 * np.pi / 24))
|
|
||||||
df['hour_cos'] = np.cos(df['hour'] * (2 * np.pi / 24))
|
|
||||||
df['day_of_week'] = df['datetime'].dt.dayofweek
|
|
||||||
df['day_of_year'] = df['datetime'].dt.dayofyear
|
|
||||||
df['week_of_year'] = df['datetime'].dt.isocalendar().week.astype(int)
|
|
||||||
df['quarter'] = df['datetime'].dt.quarter
|
|
||||||
df['is_month_end'] = df['datetime'].dt.is_month_end.astype(int)
|
|
||||||
df['is_quarter_end'] = df['datetime'].dt.is_quarter_end.astype(int)
|
|
||||||
df['is_year_end'] = df['datetime'].dt.is_year_end.astype(int)
|
|
||||||
df['month_sin'] = np.sin(df['month'] * (2 * np.pi / 12))
|
|
||||||
df['month_cos'] = np.cos(df['month'] * (2 * np.pi / 12))
|
|
||||||
df['day_of_year_sin'] = np.sin(df['day_of_year'] * (2 * np.pi / 365.25))
|
|
||||||
df['day_of_year_cos'] = np.cos(df['day_of_year'] * (2 * np.pi / 365.25))
|
|
||||||
df['season'] = df['datetime'].apply(get_season)
|
|
||||||
df['time_period'] = df['hour'].apply(get_time_period)
|
|
||||||
return df
|
|
||||||
|
|
||||||
|
|
||||||
def add_solar_features(df):
|
|
||||||
# Calcolo dell'angolo solare
|
|
||||||
df['solar_angle'] = np.sin(df['day_of_year'] * (2 * np.pi / 365.25)) * np.sin(df['hour'] * (2 * np.pi / 24))
|
|
||||||
|
|
||||||
# Interazioni tra features rilevanti
|
|
||||||
df['cloud_temp_interaction'] = df['cloudcover'] * df['temp']
|
|
||||||
df['visibility_cloud_interaction'] = df['visibility'] * (100 - df['cloudcover'])
|
|
||||||
|
|
||||||
# Feature derivate
|
|
||||||
df['clear_sky_index'] = (100 - df['cloudcover']) / 100
|
|
||||||
df['temp_gradient'] = df['temp'] - df['tempmin']
|
|
||||||
|
|
||||||
return df
|
|
||||||
|
|
||||||
|
|
||||||
def add_solar_specific_features(df):
|
|
||||||
# Angolo solare e durata del giorno
|
|
||||||
df['day_length'] = 12 + 3 * np.sin(2 * np.pi * (df['day_of_year'] - 81) / 365.25)
|
|
||||||
df['solar_noon'] = 12 - df['hour']
|
|
||||||
df['solar_elevation'] = np.sin(2 * np.pi * df['day_of_year'] / 365.25) * np.cos(2 * np.pi * df['solar_noon'] / 24)
|
|
||||||
|
|
||||||
# Interazioni
|
|
||||||
df['cloud_elevation'] = df['cloudcover'] * df['solar_elevation']
|
|
||||||
df['visibility_elevation'] = df['visibility'] * df['solar_elevation']
|
|
||||||
|
|
||||||
# Rolling features con finestre più ampie
|
|
||||||
df['cloud_rolling_12h'] = df['cloudcover'].rolling(window=12).mean()
|
|
||||||
df['temp_rolling_12h'] = df['temp'].rolling(window=12).mean()
|
|
||||||
|
|
||||||
return df
|
|
||||||
|
|
||||||
|
|
||||||
def add_advanced_features(df):
|
|
||||||
# Features esistenti
|
|
||||||
df = add_time_features(df)
|
|
||||||
df = add_solar_features(df)
|
|
||||||
df = add_solar_specific_features(df)
|
|
||||||
|
|
||||||
# Aggiungi interazioni tra variabili meteorologiche
|
|
||||||
df['temp_humidity'] = df['temp'] * df['humidity']
|
|
||||||
df['temp_cloudcover'] = df['temp'] * df['cloudcover']
|
|
||||||
df['visibility_cloudcover'] = df['visibility'] * df['cloudcover']
|
|
||||||
|
|
||||||
# Features derivate per la radiazione solare
|
|
||||||
df['clear_sky_factor'] = (100 - df['cloudcover']) / 100
|
|
||||||
df['day_length'] = np.sin(df['day_of_year_sin']) * 12 + 12 # approssimazione della durata del giorno
|
|
||||||
|
|
||||||
# Lag features
|
|
||||||
df['temp_1h_lag'] = df['temp'].shift(1)
|
|
||||||
df['cloudcover_1h_lag'] = df['cloudcover'].shift(1)
|
|
||||||
df['humidity_1h_lag'] = df['humidity'].shift(1)
|
|
||||||
|
|
||||||
# Rolling means
|
|
||||||
df['temp_rolling_mean_6h'] = df['temp'].rolling(window=6).mean()
|
|
||||||
df['cloudcover_rolling_mean_6h'] = df['cloudcover'].rolling(window=6).mean()
|
|
||||||
|
|
||||||
return df
|
|
||||||
|
|
||||||
def prepare_advanced_data(df):
|
|
||||||
# Applicazione delle funzioni di feature engineering
|
|
||||||
df = add_advanced_features(df)
|
|
||||||
|
|
||||||
# Selezione delle feature più rilevanti per UV index
|
|
||||||
selected_features = [
|
|
||||||
# Features meteorologiche base
|
|
||||||
'temp', 'humidity', 'cloudcover', 'visibility', 'pressure',
|
|
||||||
|
|
||||||
# Features temporali cicliche
|
|
||||||
'hour_sin', 'hour_cos', 'month_sin', 'month_cos',
|
|
||||||
'day_of_year_sin', 'day_of_year_cos',
|
|
||||||
|
|
||||||
# Features solari
|
|
||||||
'solar_angle', 'solar_elevation', 'day_length',
|
|
||||||
'clear_sky_index', 'solar_noon',
|
|
||||||
|
|
||||||
# Interazioni
|
|
||||||
'cloud_temp_interaction', 'visibility_cloud_interaction',
|
|
||||||
'cloud_elevation', 'visibility_elevation',
|
|
||||||
|
|
||||||
# Rolling features
|
|
||||||
'cloud_rolling_12h', 'temp_rolling_12h',
|
|
||||||
'temp_rolling_mean_6h', 'cloudcover_rolling_mean_6h',
|
|
||||||
|
|
||||||
# Features categoriche (da encodare)
|
|
||||||
'season', 'time_period'
|
|
||||||
]
|
|
||||||
|
|
||||||
# One-hot encoding per le feature categoriche
|
|
||||||
df = pd.get_dummies(df, columns=['season', 'time_period'])
|
|
||||||
|
|
||||||
# Aggiorna la lista delle feature con le colonne one-hot
|
|
||||||
categorical_columns = [col for col in df.columns if col.startswith(('season_', 'time_period_'))]
|
|
||||||
final_features = [f for f in selected_features if f not in ['season', 'time_period']] + categorical_columns
|
|
||||||
|
|
||||||
# Rimozione delle righe con valori NaN (create dai rolling features)
|
|
||||||
df = df.dropna()
|
|
||||||
|
|
||||||
X = df[final_features]
|
|
||||||
y = df['uvindex']
|
|
||||||
|
|
||||||
# Split dei dati
|
|
||||||
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
|
|
||||||
|
|
||||||
# Scaling delle feature
|
|
||||||
scaler = StandardScaler()
|
|
||||||
X_train_scaled = scaler.fit_transform(X_train)
|
|
||||||
X_test_scaled = scaler.transform(X_test)
|
|
||||||
|
|
||||||
return X_train_scaled, X_test_scaled, y_train, y_test, scaler, final_features
|
|
||||||
|
|
||||||
def create_sequence_data(X, sequence_length=24):
|
|
||||||
"""
|
|
||||||
Converte i dati in sequenze per l'input LSTM
|
|
||||||
sequence_length rappresenta quante ore precedenti considerare
|
|
||||||
"""
|
|
||||||
sequences = []
|
|
||||||
for i in range(len(X) - sequence_length + 1):
|
|
||||||
sequences.append(X[i:i + sequence_length])
|
|
||||||
return np.array(sequences)
|
|
||||||
|
|
||||||
|
|
||||||
def prepare_hybrid_data(df):
|
|
||||||
# Utilizziamo la preparazione dati esistente
|
|
||||||
X_train_scaled, X_test_scaled, y_train, y_test, scaler, features = prepare_advanced_data(df)
|
|
||||||
|
|
||||||
# Convertiamo i dati in sequenze
|
|
||||||
sequence_length = 24 # 24 ore di dati storici
|
|
||||||
|
|
||||||
X_train_seq = create_sequence_data(X_train_scaled, sequence_length)
|
|
||||||
X_test_seq = create_sequence_data(X_test_scaled, sequence_length)
|
|
||||||
|
|
||||||
# Adattiamo le y rimuovendo i primi (sequence_length-1) elementi
|
|
||||||
y_train = y_train[sequence_length - 1:]
|
|
||||||
y_test = y_test[sequence_length - 1:]
|
|
||||||
|
|
||||||
return X_train_seq, X_test_seq, y_train, y_test, scaler, features
|
|
||||||
|
|
||||||
|
|
||||||
def transformer_encoder(inputs, head_size, num_heads, ff_dim, dropout=0):
|
|
||||||
"""
|
|
||||||
Implementa un blocco Transformer Encoder
|
|
||||||
"""
|
|
||||||
# Multi-Head Attention
|
|
||||||
attention_output = MultiHeadAttention(
|
|
||||||
num_heads=num_heads, key_dim=head_size
|
|
||||||
)(inputs, inputs)
|
|
||||||
attention_output = Dropout(dropout)(attention_output)
|
|
||||||
attention_output = LayerNormalization(epsilon=1e-6)(inputs + attention_output)
|
|
||||||
|
|
||||||
# Feed Forward Network
|
|
||||||
ffn_output = Dense(ff_dim, activation="relu")(attention_output)
|
|
||||||
ffn_output = Dense(inputs.shape[-1])(ffn_output)
|
|
||||||
ffn_output = Dropout(dropout)(ffn_output)
|
|
||||||
|
|
||||||
return LayerNormalization(epsilon=1e-6)(attention_output + ffn_output)
|
|
||||||
|
|
||||||
def custom_activation(x):
|
|
||||||
"""
|
|
||||||
Activation function personalizzata che limita l'output tra 0 e 11
|
|
||||||
"""
|
|
||||||
return 11 * tf.sigmoid(x)
|
|
||||||
|
|
||||||
|
|
||||||
def custom_loss(y_true, y_pred):
|
|
||||||
"""
|
|
||||||
Loss function personalizzata che penalizza fortemente le predizioni fuori range
|
|
||||||
"""
|
|
||||||
# MSE base
|
|
||||||
mse = K.mean(K.square(y_true - y_pred))
|
|
||||||
|
|
||||||
# Penalità per valori fuori range
|
|
||||||
below_range = K.relu(0 - y_pred)
|
|
||||||
above_range = K.relu(y_pred - 11)
|
|
||||||
|
|
||||||
# Aggiungi una forte penalità per valori fuori range
|
|
||||||
range_penalty = 10.0 * (K.mean(K.square(below_range)) + K.mean(K.square(above_range)))
|
|
||||||
|
|
||||||
return mse + range_penalty
|
|
||||||
|
|
||||||
|
|
||||||
def create_hybrid_model(input_shape, n_features):
|
|
||||||
"""
|
|
||||||
Crea un modello ibrido con output vincolato tra 0 e 11
|
|
||||||
"""
|
|
||||||
# Input Layer
|
|
||||||
inputs = Input(shape=input_shape)
|
|
||||||
|
|
||||||
# CNN Branch - Estrazione pattern locali
|
|
||||||
conv1 = Conv1D(filters=64, kernel_size=3, activation='relu', padding='same')(inputs)
|
|
||||||
conv2 = Conv1D(filters=128, kernel_size=5, activation='relu', padding='same')(conv1)
|
|
||||||
conv3 = Conv1D(filters=256, kernel_size=7, activation='relu', padding='same')(conv2)
|
|
||||||
conv_output = BatchNormalization()(conv3)
|
|
||||||
|
|
||||||
# LSTM Branch - Dipendenze temporali
|
|
||||||
lstm1 = LSTM(128, return_sequences=True)(inputs)
|
|
||||||
lstm2 = LSTM(64, return_sequences=True)(lstm1)
|
|
||||||
lstm_output = BatchNormalization()(lstm2)
|
|
||||||
|
|
||||||
# Combine CNN and LSTM branches
|
|
||||||
combined = Concatenate()([conv_output, lstm_output])
|
|
||||||
|
|
||||||
# Multi-Head Attention per catturare relazioni complesse
|
|
||||||
attention_output = transformer_encoder(
|
|
||||||
combined,
|
|
||||||
head_size=32,
|
|
||||||
num_heads=8,
|
|
||||||
ff_dim=256,
|
|
||||||
dropout=0.1
|
|
||||||
)
|
|
||||||
|
|
||||||
# Global Pooling
|
|
||||||
pooled = GlobalAveragePooling1D()(attention_output)
|
|
||||||
|
|
||||||
# Dense Layers con attivazioni vincolate
|
|
||||||
dense1 = Dense(128)(pooled)
|
|
||||||
dense1 = Activation('relu')(dense1)
|
|
||||||
dense1 = Dropout(0.3)(dense1)
|
|
||||||
|
|
||||||
dense2 = Dense(64)(dense1)
|
|
||||||
dense2 = Activation('relu')(dense2)
|
|
||||||
dense2 = Dropout(0.2)(dense2)
|
|
||||||
|
|
||||||
# Output layer con attivazione personalizzata per limitare tra 0 e 11
|
|
||||||
outputs = Dense(1)(dense2)
|
|
||||||
outputs = Activation(custom_activation)(outputs)
|
|
||||||
|
|
||||||
# Create model
|
|
||||||
model = Model(inputs=inputs, outputs=outputs)
|
|
||||||
|
|
||||||
# Compile con loss function personalizzata
|
|
||||||
optimizer = tf.keras.optimizers.Adam(learning_rate=0.001)
|
|
||||||
model.compile(
|
|
||||||
optimizer=optimizer,
|
|
||||||
loss=custom_loss,
|
|
||||||
metrics=['mae', 'mse']
|
|
||||||
)
|
|
||||||
|
|
||||||
return model
|
|
||||||
|
|
||||||
|
|
||||||
def evaluate_uv_predictions(y_true, y_pred):
|
|
||||||
"""
|
|
||||||
Valutazione specifica per UV index con metriche categoriche
|
|
||||||
"""
|
|
||||||
# Arrotonda le predizioni al più vicino intero
|
|
||||||
y_pred_rounded = np.round(y_pred)
|
|
||||||
|
|
||||||
# Clip dei valori tra 0 e 11
|
|
||||||
y_pred_clipped = np.clip(y_pred_rounded, 0, 11)
|
|
||||||
|
|
||||||
# Calcolo metriche
|
|
||||||
mae = mean_absolute_error(y_true, y_pred_clipped)
|
|
||||||
rmse = np.sqrt(mean_squared_error(y_true, y_pred_clipped))
|
|
||||||
r2 = r2_score(y_true, y_pred_clipped)
|
|
||||||
|
|
||||||
# Calcolo accuratezza per diversi margini di errore
|
|
||||||
exact_accuracy = np.mean(y_pred_clipped == y_true)
|
|
||||||
one_off_accuracy = np.mean(np.abs(y_pred_clipped - y_true) <= 1)
|
|
||||||
two_off_accuracy = np.mean(np.abs(y_pred_clipped - y_true) <= 2)
|
|
||||||
|
|
||||||
print("\nUV Index Prediction Metrics:")
|
|
||||||
print(f"MAE: {mae:.3f}")
|
|
||||||
print(f"RMSE: {rmse:.3f}")
|
|
||||||
print(f"R² Score: {r2:.3f}")
|
|
||||||
print(f"Exact Match Accuracy: {exact_accuracy:.3f}")
|
|
||||||
print(f"±1 Accuracy: {one_off_accuracy:.3f}")
|
|
||||||
print(f"±2 Accuracy: {two_off_accuracy:.3f}")
|
|
||||||
|
|
||||||
# Confusion Matrix per livelli di UV
|
|
||||||
def get_uv_level(value):
|
|
||||||
if value <= 2:
|
|
||||||
return 'Low'
|
|
||||||
elif value <= 5:
|
|
||||||
return 'Moderate'
|
|
||||||
elif value <= 7:
|
|
||||||
return 'High'
|
|
||||||
elif value <= 10:
|
|
||||||
return 'Very High'
|
|
||||||
else:
|
|
||||||
return 'Extreme'
|
|
||||||
|
|
||||||
y_true_levels = [get_uv_level(v) for v in y_true]
|
|
||||||
y_pred_levels = [get_uv_level(v) for v in y_pred_clipped]
|
|
||||||
|
|
||||||
print("\nUV Level Confusion Matrix:")
|
|
||||||
print(pd.crosstab(
|
|
||||||
pd.Series(y_true_levels, name='Actual'),
|
|
||||||
pd.Series(y_pred_levels, name='Predicted')
|
|
||||||
))
|
|
||||||
|
|
||||||
return mae, rmse, r2, exact_accuracy, one_off_accuracy
|
|
||||||
|
|
||||||
|
|
||||||
def plot_uv_predictions(y_true, y_pred):
|
|
||||||
"""
|
|
||||||
Visualizzazione delle predizioni specifica per UV index
|
|
||||||
"""
|
|
||||||
plt.figure(figsize=(15, 5))
|
|
||||||
|
|
||||||
# Plot 1: Actual vs Predicted
|
|
||||||
plt.subplot(1, 2, 1)
|
|
||||||
plt.scatter(y_true, y_pred, alpha=0.5)
|
|
||||||
plt.plot([0, 11], [0, 11], 'r--', lw=2)
|
|
||||||
plt.xlabel('Actual UV Index')
|
|
||||||
plt.ylabel('Predicted UV Index')
|
|
||||||
plt.title('Actual vs Predicted UV Index')
|
|
||||||
plt.grid(True)
|
|
||||||
|
|
||||||
# Plot 2: Distribution of Errors
|
|
||||||
plt.subplot(1, 2, 2)
|
|
||||||
errors = y_pred - y_true
|
|
||||||
plt.hist(errors, bins=20, alpha=0.7)
|
|
||||||
plt.xlabel('Prediction Error')
|
|
||||||
plt.ylabel('Frequency')
|
|
||||||
plt.title('Distribution of Prediction Errors')
|
|
||||||
plt.grid(True)
|
|
||||||
|
|
||||||
plt.tight_layout()
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
|
|
||||||
def train_hybrid_model(model, X_train, y_train, X_test, y_test, class_weights=None, epochs=100, batch_size=32):
|
|
||||||
"""
|
|
||||||
Funzione di training avanzata per il modello ibrido UV index con monitoraggio dettagliato
|
|
||||||
e gestione del training.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
-----------
|
|
||||||
model : keras.Model
|
|
||||||
Il modello ibrido compilato
|
|
||||||
X_train : numpy.ndarray
|
|
||||||
Dati di training
|
|
||||||
y_train : numpy.ndarray
|
|
||||||
Target di training
|
|
||||||
X_test : numpy.ndarray
|
|
||||||
Dati di validation
|
|
||||||
y_test : numpy.ndarray
|
|
||||||
Target di validation
|
|
||||||
class_weights : dict, optional
|
|
||||||
Pesi per bilanciare le classi UV
|
|
||||||
epochs : int, optional
|
|
||||||
Numero massimo di epoche di training
|
|
||||||
batch_size : int, optional
|
|
||||||
Dimensione del batch
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
--------
|
|
||||||
history : keras.callbacks.History
|
|
||||||
Storia del training con tutte le metriche
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Callbacks avanzati per il training
|
|
||||||
callbacks = [
|
|
||||||
# Early Stopping avanzato
|
|
||||||
EarlyStopping(
|
|
||||||
monitor='val_loss',
|
|
||||||
patience=20,
|
|
||||||
restore_best_weights=True,
|
|
||||||
mode='min',
|
|
||||||
verbose=1,
|
|
||||||
min_delta=1e-4
|
|
||||||
),
|
|
||||||
|
|
||||||
# Learning Rate Schedule
|
|
||||||
ReduceLROnPlateau(
|
|
||||||
monitor='val_loss',
|
|
||||||
factor=0.5,
|
|
||||||
patience=10,
|
|
||||||
verbose=1,
|
|
||||||
mode='min',
|
|
||||||
min_delta=1e-4,
|
|
||||||
cooldown=5,
|
|
||||||
min_lr=1e-6
|
|
||||||
),
|
|
||||||
|
|
||||||
# Model Checkpoint per salvare i migliori modelli
|
|
||||||
tf.keras.callbacks.ModelCheckpoint(
|
|
||||||
filepath='best_uv_model.h5',
|
|
||||||
monitor='val_loss',
|
|
||||||
save_best_only=True,
|
|
||||||
mode='min',
|
|
||||||
verbose=1
|
|
||||||
),
|
|
||||||
|
|
||||||
# TensorBoard callback per il monitoraggio
|
|
||||||
tf.keras.callbacks.TensorBoard(
|
|
||||||
log_dir='./logs',
|
|
||||||
histogram_freq=1,
|
|
||||||
write_graph=True,
|
|
||||||
update_freq='epoch'
|
|
||||||
),
|
|
||||||
|
|
||||||
# Custom Callback per monitorare le predizioni fuori range
|
|
||||||
tf.keras.callbacks.LambdaCallback(
|
|
||||||
on_epoch_end=lambda epoch, logs: print(
|
|
||||||
f"\nEpoch {epoch + 1}: Predizioni fuori range: "
|
|
||||||
f"{np.sum((model.predict(X_test) < 0) | (model.predict(X_test) > 11))}"
|
|
||||||
) if epoch % 10 == 0 else None
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
# Calcolo dei class weights se non forniti
|
|
||||||
if class_weights is None:
|
|
||||||
# Discretizziamo i valori UV per il calcolo dei pesi
|
|
||||||
y_discrete = np.round(y_train).astype(int)
|
|
||||||
class_weights = compute_class_weight(
|
|
||||||
'balanced',
|
|
||||||
classes=np.unique(y_discrete),
|
|
||||||
y=y_discrete
|
|
||||||
)
|
|
||||||
class_weights = dict(enumerate(class_weights))
|
|
||||||
|
|
||||||
# Training con gestione degli errori e logging
|
|
||||||
try:
|
|
||||||
history = model.fit(
|
|
||||||
X_train, y_train,
|
|
||||||
validation_data=(X_test, y_test),
|
|
||||||
epochs=epochs,
|
|
||||||
batch_size=batch_size,
|
|
||||||
callbacks=callbacks,
|
|
||||||
class_weight=class_weights,
|
|
||||||
verbose=1,
|
|
||||||
shuffle=True,
|
|
||||||
workers=4,
|
|
||||||
use_multiprocessing=True
|
|
||||||
)
|
|
||||||
|
|
||||||
# Analisi post-training
|
|
||||||
print("\nTraining completato con successo!")
|
|
||||||
|
|
||||||
# Valutazione finale sul test set
|
|
||||||
test_loss, test_mae, test_mse = model.evaluate(X_test, y_test, verbose=0)
|
|
||||||
print(f"\nMetriche finali sul test set:")
|
|
||||||
print(f"Loss: {test_loss:.4f}")
|
|
||||||
print(f"MAE: {test_mae:.4f}")
|
|
||||||
print(f"MSE: {test_mse:.4f}")
|
|
||||||
|
|
||||||
# Analisi delle predizioni
|
|
||||||
predictions = model.predict(X_test)
|
|
||||||
out_of_range = np.sum((predictions < 0) | (predictions > 11))
|
|
||||||
print(f"\nPredizioni fuori range: {out_of_range} ({out_of_range / len(predictions) * 100:.2f}%)")
|
|
||||||
|
|
||||||
# Plot della loss durante il training
|
|
||||||
plt.figure(figsize=(12, 4))
|
|
||||||
|
|
||||||
plt.subplot(1, 2, 1)
|
|
||||||
plt.plot(history.history['loss'], label='Training Loss')
|
|
||||||
plt.plot(history.history['val_loss'], label='Validation Loss')
|
|
||||||
plt.title('Model Loss')
|
|
||||||
plt.xlabel('Epoch')
|
|
||||||
plt.ylabel('Loss')
|
|
||||||
plt.legend()
|
|
||||||
|
|
||||||
plt.subplot(1, 2, 2)
|
|
||||||
plt.plot(history.history['mae'], label='Training MAE')
|
|
||||||
plt.plot(history.history['val_mae'], label='Validation MAE')
|
|
||||||
plt.title('Model MAE')
|
|
||||||
plt.xlabel('Epoch')
|
|
||||||
plt.ylabel('MAE')
|
|
||||||
plt.legend()
|
|
||||||
|
|
||||||
plt.tight_layout()
|
|
||||||
plt.show()
|
|
||||||
|
|
||||||
# Salvataggio dei risultati del training
|
|
||||||
training_results = {
|
|
||||||
'final_loss': test_loss,
|
|
||||||
'final_mae': test_mae,
|
|
||||||
'final_mse': test_mse,
|
|
||||||
'out_of_range_predictions': out_of_range,
|
|
||||||
'training_time': len(history.history['loss']),
|
|
||||||
'best_epoch': np.argmin(history.history['val_loss']) + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Salvataggio su file
|
|
||||||
with open('training_results.json', 'w') as f:
|
|
||||||
json.dump(training_results, f, indent=4)
|
|
||||||
|
|
||||||
return history
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"\nErrore durante il training: {str(e)}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
finally:
|
|
||||||
# Pulizia della memoria
|
|
||||||
tf.keras.backend.clear_session()
|
|
||||||
|
|
||||||
|
|
||||||
def train_uvindex_bounded_model(df):
|
|
||||||
"""
|
|
||||||
Training completo del modello UV index con preparazione dati, training,
|
|
||||||
valutazione e visualizzazione dei risultati.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
-----------
|
|
||||||
df : pandas.DataFrame
|
|
||||||
DataFrame contenente i dati meteorologici e UV index
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
--------
|
|
||||||
tuple:
|
|
||||||
- model: modello addestrato
|
|
||||||
- scaler: scaler utilizzato per la normalizzazione
|
|
||||||
- features: lista delle feature utilizzate
|
|
||||||
- history: storia del training
|
|
||||||
- predictions: predizioni sul test set
|
|
||||||
- y_test: valori reali del test set
|
|
||||||
- metrics: metriche di valutazione
|
|
||||||
- training_results: dizionario con i risultati dettagliati del training
|
|
||||||
"""
|
|
||||||
print("Inizializzazione del training del modello UV index...")
|
|
||||||
|
|
||||||
try:
|
|
||||||
# Preparazione dei dati
|
|
||||||
print("\n1. Preparazione dei dati...")
|
|
||||||
X_train_seq, X_test_seq, y_train, y_test, scaler, features = prepare_hybrid_data(df)
|
|
||||||
|
|
||||||
print(f"Shape dei dati di training: {X_train_seq.shape}")
|
|
||||||
print(f"Shape dei dati di test: {X_test_seq.shape}")
|
|
||||||
print(f"Numero di feature utilizzate: {len(features)}")
|
|
||||||
|
|
||||||
# Verifica della qualità dei dati
|
|
||||||
if np.isnan(X_train_seq).any() or np.isnan(y_train).any():
|
|
||||||
raise ValueError("Trovati valori NaN nei dati di training")
|
|
||||||
|
|
||||||
# Verifica del range dei valori UV
|
|
||||||
if not (0 <= y_train.max() <= 11 and 0 <= y_test.max() <= 11):
|
|
||||||
print("WARNING: Trovati valori UV index fuori range (0-11)")
|
|
||||||
|
|
||||||
# Creazione del modello
|
|
||||||
print("\n2. Creazione del modello...")
|
|
||||||
input_shape = (X_train_seq.shape[1], X_train_seq.shape[2])
|
|
||||||
model = create_hybrid_model(input_shape, len(features))
|
|
||||||
model.summary()
|
|
||||||
|
|
||||||
# Calcolo class weights per bilanciare il dataset
|
|
||||||
y_discrete = np.round(y_train).astype(int)
|
|
||||||
class_weights = compute_class_weight(
|
|
||||||
'balanced',
|
|
||||||
classes=np.unique(y_discrete),
|
|
||||||
y=y_discrete
|
|
||||||
)
|
|
||||||
class_weights_dict = dict(enumerate(class_weights))
|
|
||||||
|
|
||||||
print("\n3. Avvio del training...")
|
|
||||||
history = train_hybrid_model(
|
|
||||||
model=model,
|
|
||||||
X_train=X_train_seq,
|
|
||||||
y_train=y_train,
|
|
||||||
X_test=X_test_seq,
|
|
||||||
y_test=y_test,
|
|
||||||
class_weights=class_weights_dict,
|
|
||||||
epochs=100,
|
|
||||||
batch_size=32
|
|
||||||
)
|
|
||||||
|
|
||||||
print("\n4. Generazione delle predizioni...")
|
|
||||||
predictions = model.predict(X_test_seq)
|
|
||||||
|
|
||||||
# Clip delle predizioni nel range corretto
|
|
||||||
predictions = np.clip(predictions, 0, 11)
|
|
||||||
|
|
||||||
print("\n5. Valutazione del modello...")
|
|
||||||
metrics = evaluate_uv_predictions(y_test, predictions)
|
|
||||||
|
|
||||||
print("\n6. Creazione delle visualizzazioni...")
|
|
||||||
plot_uv_predictions(y_test, predictions)
|
|
||||||
|
|
||||||
# Creazione del dizionario dei risultati
|
|
||||||
training_results = {
|
|
||||||
'model_params': {
|
|
||||||
'input_shape': input_shape,
|
|
||||||
'n_features': len(features),
|
|
||||||
'sequence_length': X_train_seq.shape[1]
|
|
||||||
},
|
|
||||||
'training_params': {
|
|
||||||
'batch_size': 32,
|
|
||||||
'total_epochs': len(history.history['loss']),
|
|
||||||
'best_epoch': np.argmin(history.history['val_loss']) + 1
|
|
||||||
},
|
|
||||||
'performance_metrics': {
|
|
||||||
'final_loss': float(history.history['val_loss'][-1]),
|
|
||||||
'final_mae': float(history.history['val_mae'][-1]),
|
|
||||||
'best_val_loss': float(min(history.history['val_loss'])),
|
|
||||||
'out_of_range_predictions': int(np.sum((predictions < 0) | (predictions > 11))),
|
|
||||||
'accuracy_metrics': metrics
|
|
||||||
},
|
|
||||||
'feature_importance': {
|
|
||||||
feature: float(importance)
|
|
||||||
for feature, importance in zip(features, model.layers[0].get_weights()[0].mean(axis=1))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Salvataggio dei risultati
|
|
||||||
print("\n7. Salvataggio dei risultati...")
|
|
||||||
|
|
||||||
# Salva il modello
|
|
||||||
model.save('uv_index_model.h5')
|
|
||||||
|
|
||||||
# Salva i risultati del training
|
|
||||||
with open('training_results.json', 'w') as f:
|
|
||||||
json.dump(training_results, f, indent=4)
|
|
||||||
|
|
||||||
# Salva lo scaler
|
|
||||||
joblib.dump(scaler, 'scaler.pkl')
|
|
||||||
|
|
||||||
print("\nTraining completato con successo!")
|
|
||||||
|
|
||||||
return (
|
|
||||||
model, scaler, features, history,
|
|
||||||
predictions, y_test, metrics, training_results
|
|
||||||
)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"\nErrore durante il training: {str(e)}")
|
|
||||||
raise
|
|
||||||
|
|
||||||
finally:
|
|
||||||
# Pulizia della memoria
|
|
||||||
tf.keras.backend.clear_session()
|
|
||||||
|
|
||||||
df = pd.read_parquet('../data/weather_data.parquet')
|
|
||||||
|
|
||||||
# Esegui il training
|
|
||||||
(model, scaler, features, history,
|
|
||||||
predictions, y_test, metrics,
|
|
||||||
training_results) = train_uvindex_bounded_model(df)
|
|
||||||
Loading…
x
Reference in New Issue
Block a user