Analyzing Benchmark Results for Validation

So you ran your models against several criticality benchmarks. Nice! How do we analyze the results easily? NucML contains some utilities to help you get started.

[1]:
# Prototype
import sys
# This allows us to import the nucml utilities
sys.path.append("..")
[2]:
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import os

pd.set_option('display.max_columns', 500)
pd.set_option('display.max_rows', 50)
pd.options.mode.chained_assignment = None  # default='warn'
sns.set_style("white")

import nucml.ace.data_utilities as ace_utils
import nucml.model.utilities as model_utils
import nucml.ace.plot as ace_plots
[3]:
figure_dir = "figures/B0/"
[4]:
sns.set(font_scale=2.5)
sns.set_style('white')

Gathering Results from Serpent Runs

You can automatically read all benchmark .mat files and format the results easily by simply specifying the directory where the benchmark model information is stored (see previous notebook).

[5]:
model_results_b0 = ace_utils.gather_benchmark_results("ml/DT_B0/")
model_results_b1 = ace_utils.gather_benchmark_results("ml/DT_B1/")
model_results_b2 = ace_utils.gather_benchmark_results("ml/DT_B2/")
model_results_b3 = ace_utils.gather_benchmark_results("ml/DT_B3/")
model_results_b4 = ace_utils.gather_benchmark_results("ml/DT_B4/")
[6]:
model_results_b0.head()
[6]:
Model Benchmark K_eff_ana Unc_ana K_eff_imp Unc_imp Deviation_Ana Deviation_Imp
0 DT100_MSS10_MSL1_none_one_hot_B0_v1 U233_MET_FAST_001 0.989927 0.00044 0.990024 0.00030 0.010073 0.009976
1 DT100_MSS10_MSL1_none_one_hot_B0_v1 U233_MET_FAST_002_001 0.992332 0.00042 0.992233 0.00029 0.007668 0.007767
2 DT100_MSS10_MSL1_none_one_hot_B0_v1 U233_MET_FAST_002_002 0.996557 0.00044 0.996643 0.00031 0.003443 0.003357
3 DT100_MSS10_MSL3_none_one_hot_B0_v1 U233_MET_FAST_001 0.993064 0.00044 0.992735 0.00030 0.006936 0.007265
4 DT100_MSS10_MSL3_none_one_hot_B0_v1 U233_MET_FAST_002_001 0.994547 0.00043 0.994306 0.00029 0.005453 0.005694

Analyzing Decision Tree Results

That was easy. However, we do not have the training and validation metrics that we had before. We can simply read the results files and join them. First, let us load the results and keep the most basic columns including hyperparameters and performance metrics.

[11]:
results_b0 = pd.read_csv("../ML_EXFOR_neutrons/2_DT/dt_resultsB0.csv").sort_values(by="max_depth")
results_b1 = pd.read_csv("../ML_EXFOR_neutrons/2_DT/dt_resultsB1.csv").sort_values(by="max_depth")
results_b2 = pd.read_csv("../ML_EXFOR_neutrons/2_DT/dt_resultsB2.csv").sort_values(by="max_depth")
results_b3 = pd.read_csv("../ML_EXFOR_neutrons/2_DT/dt_resultsB3.csv").sort_values(by="max_depth")
results_b4 = pd.read_csv("../ML_EXFOR_neutrons/2_DT/dt_resultsB4.csv").sort_values(by="max_depth")

results_b0 = results_b0[results_b0.normalizer == "none"]
[12]:
# IGNORE THIS CELL
# results_b0['Model'] = results_b0.model_path.apply(lambda x: os.path.basename(os.path.dirname(x)))
# results_b0['dataset'] = 'b0'
# results_b1['Model'] = results_b1.model_path.apply(lambda x: os.path.basename(os.path.dirname(x)))
# results_b1['dataset'] = 'b1'

This step is optional, but in this example, we will join the results from different models trained on different datasets. Some models might be named the same but were trained on different dataset versions. Here we add a unique identifier previous to merging the results.

[13]:
for df, dataset_tag in zip([results_b0, results_b1, results_b2, results_b3, results_b4], ["b0", "b1", "b2", "b3", "b4"]):
    df['Model'] = df.model_path.apply(lambda x: os.path.basename(os.path.dirname(x)))
    df['dataset'] = dataset_tag

Filtering to obtain most basic features:

[14]:
results_b0 = results_b0[["Model", "train_mae", "val_mae", "test_mae", "max_depth", "mss", "msl", "dataset"]]
results_b1 = results_b1[["Model", "train_mae", "val_mae", "test_mae", "max_depth", "mss", "msl", "dataset"]]
results_b2 = results_b2[["Model", "train_mae", "val_mae", "test_mae", "max_depth", "mss", "msl", "dataset"]]
results_b3 = results_b3[["Model", "train_mae", "val_mae", "test_mae", "max_depth", "mss", "msl", "dataset"]]
results_b4 = results_b4[["Model", "train_mae", "val_mae", "test_mae", "max_depth", "mss", "msl", "dataset"]]

Finally, we can merge the results with the gathered benchmark information.

[15]:
final_b0 = model_results_b0.merge(results_b0, on="Model")
final_b1 = model_results_b1.merge(results_b1, on="Model")
final_b2 = model_results_b2.merge(results_b2, on="Model")
final_b3 = model_results_b3.merge(results_b3, on="Model")
final_b4 = model_results_b4.merge(results_b4, on="Model")
[16]:
final_set = final_b0.append(final_b1).append(final_b2).append(final_b3).append(final_b4)
[17]:
final_set.head()
[17]:
Model Benchmark K_eff_ana Unc_ana K_eff_imp Unc_imp Deviation_Ana Deviation_Imp train_mae val_mae test_mae max_depth mss msl dataset
0 DT100_MSS10_MSL1_none_one_hot_B0_v1 U233_MET_FAST_001 0.989927 0.00044 0.990024 0.00030 0.010073 0.009976 0.070281 0.125724 0.124429 100 10 1 b0
1 DT100_MSS10_MSL1_none_one_hot_B0_v1 U233_MET_FAST_002_001 0.992332 0.00042 0.992233 0.00029 0.007668 0.007767 0.070281 0.125724 0.124429 100 10 1 b0
2 DT100_MSS10_MSL1_none_one_hot_B0_v1 U233_MET_FAST_002_002 0.996557 0.00044 0.996643 0.00031 0.003443 0.003357 0.070281 0.125724 0.124429 100 10 1 b0
3 DT100_MSS10_MSL3_none_one_hot_B0_v1 U233_MET_FAST_001 0.993064 0.00044 0.992735 0.00030 0.006936 0.007265 0.082464 0.122372 0.121066 100 10 3 b0
4 DT100_MSS10_MSL3_none_one_hot_B0_v1 U233_MET_FAST_002_001 0.994547 0.00043 0.994306 0.00029 0.005453 0.005694 0.082464 0.122372 0.121066 100 10 3 b0

Nice. You can then proceed to analyze your results and explor hyperparametres as a function of the multiplication factor and the error. Check the thesis for more information.

You can, for example, create a DataFrame for each benchmark and analyze what models have good performance in terms of the multiplication factor.

[19]:
u233_002_001 = final_set[final_set.Benchmark == "U233_MET_FAST_002_001"].sort_values(by="Deviation_Ana")
u233_002_002 = final_set[final_set.Benchmark == "U233_MET_FAST_002_002"].sort_values(by="Deviation_Ana")
u233_001 = final_set[final_set.Benchmark == "U233_MET_FAST_001"].sort_values(by="Deviation_Ana")
[20]:
# converting error to 100% scale
u233_001.Deviation_Ana = u233_001.Deviation_Ana * 100
u233_002_001.Deviation_Ana = u233_002_001.Deviation_Ana * 100
u233_002_002.Deviation_Ana = u233_002_002.Deviation_Ana * 100
[21]:
final_b0[final_b0.Benchmark == "U233_MET_FAST_001"].sort_values(by="Deviation_Ana").head()
[21]:
Model Benchmark K_eff_ana Unc_ana K_eff_imp Unc_imp Deviation_Ana Deviation_Imp train_mae val_mae test_mae max_depth mss msl dataset
138 DT136_MSS5_MSL3_none_one_hot_B0_v1 U233_MET_FAST_001 0.999906 0.00043 0.999979 0.00029 0.000094 0.000021 0.077729 0.123489 0.122285 136 5 3 b0
90 DT120_MSS5_MSL3_none_one_hot_B0_v1 U233_MET_FAST_001 0.999637 0.00044 0.999427 0.00029 0.000363 0.000573 0.077731 0.123492 0.122258 120 5 3 b0
30 DT100_MSS5_MSL3_none_one_hot_B0_v1 U233_MET_FAST_001 0.999544 0.00041 0.999753 0.00028 0.000456 0.000247 0.077741 0.123487 0.122248 100 5 3 b0
543 DT70_MSS5_MSL3_none_one_hot_B0_v1 U233_MET_FAST_001 0.999485 0.00042 0.999519 0.00028 0.000515 0.000481 0.077826 0.123491 0.122261 70 5 3 b0
171 DT160_MSS2_MSL1_none_one_hot_B0_v1 U233_MET_FAST_001 1.000560 0.00043 1.000950 0.00030 0.000560 0.000950 0.026063 0.136077 0.134881 160 2 1 b0

It seems like the DT136_MSS5_MSL3_none_one_hot_B0_v1 model has great performance in the benchmark with an error of 0.000094%.

Getting Best Models Overall

Similar to traditional ML validation techniques, it is only correct that we analyze the result of the ML algorithms on their average performance on a set of criticality benchmark cases, rather than just one. The examples here contain information on three benchmarks. Those will suffice for a proof-of-concept analysis.

An option is simply grouping the model by performance. Beware, this can lead to some major misconceptions and it is shown here only as an example.

[32]:
model_mean = final_set.groupby("Model").mean()
# model_mean = model_mean[['K_eff_ana']]
model_mean["Error"] = (abs(model_mean.K_eff_ana - 1) /1) * 100
model_mean["Unc_Error"] = (abs(model_mean.Unc_ana - 1) /1) * 100
model_mean = model_mean.reset_index()
[34]:
model_mean.sort_values("Error").head()
[34]:
Model K_eff_ana Unc_ana K_eff_imp Unc_imp Deviation_Ana Deviation_Imp train_mae val_mae test_mae max_depth mss msl Error Unc_Error
27 DT110_MSS5_MSL1_none_one_hot_B0_v1 0.999999 0.000430 1.000269 0.000297 0.002014 0.001611 0.052218 0.131089 0.129714 110 5 1 0.000100 99.957000
374 DT90_MSS5_MSL1_none_one_hot_B1_v1 1.000030 0.000440 1.000064 0.000297 0.001430 0.001469 0.052356 0.130018 0.129852 90 5 1 0.003000 99.956000
209 DT310_MSS5_MSL1_none_one_hot_B1_v1 1.000034 0.000423 1.000101 0.000290 0.001786 0.001619 0.051868 0.130218 0.130013 310 5 1 0.003400 99.957667
302 DT60_MSS5_MSL1_none_one_hot_B0_v1 1.000039 0.000437 1.000160 0.000290 0.001401 0.001600 0.053003 0.131055 0.129657 60 5 1 0.003867 99.956333
105 DT180_MSS5_MSL1_none_one_hot_B1_v1 0.999951 0.000423 1.000313 0.000297 0.001769 0.001600 0.051949 0.130154 0.129956 180 5 1 0.004900 99.957667

———————————————— PRIVATE SECTION

[16]:
print(model_utils.get_best_models_df(u233_001[["Model", 'K_eff_ana', 'Unc_ana', 'Deviation_Ana', 'train_mae', 'val_mae', 'test_mae']]).to_latex(index=False))
\begin{tabular}{lrrrrrrl}
\toprule
                             Model &  K\_eff\_ana &  Unc\_ana &  Deviation\_Ana &  train\_mae &  val\_mae &  test\_mae &   tag \\
\midrule
DT400\_MSS2\_MSL1\_none\_one\_hot\_B0\_v1 &   1.002320 &  0.00043 &         0.2320 &   0.025773 & 0.136140 &  0.135027 & Train \\
DT70\_MSS10\_MSL7\_none\_one\_hot\_B1\_v1 &   0.997118 &  0.00044 &         0.2882 &   0.094443 & 0.118699 &  0.119142 &   Val \\
DT90\_MSS10\_MSL7\_none\_one\_hot\_B0\_v1 &   0.922530 &  0.00046 &         7.7470 &   0.094439 & 0.119797 &  0.118706 &  Test \\
\bottomrule
\end{tabular}

[17]:
print(u233_001[["Model", 'K_eff_ana', 'Unc_ana', 'Deviation_Ana', 'train_mae', 'val_mae', 'test_mae']].head(1).to_latex(index=False))
\begin{tabular}{lrrrrrr}
\toprule
                             Model &  K\_eff\_ana &  Unc\_ana &  Deviation\_Ana &  train\_mae &  val\_mae &  test\_mae \\
\midrule
DT80\_MSS15\_MSL3\_none\_one\_hot\_B1\_v1 &   0.999943 &  0.00041 &         0.0057 &   0.088061 & 0.120462 &  0.120684 \\
\bottomrule
\end{tabular}

[20]:
print(model_utils.get_best_models_df(u233_002_001[["Model", 'K_eff_ana', 'Unc_ana', 'Deviation_Ana', 'train_mae', 'val_mae', 'test_mae']]).to_latex(index=False))
\begin{tabular}{lrrrrrrl}
\toprule
                             Model &  K\_eff\_ana &  Unc\_ana &  Deviation\_Ana &  train\_mae &  val\_mae &  test\_mae &   tag \\
\midrule
DT400\_MSS2\_MSL1\_none\_one\_hot\_B0\_v1 &   1.003330 &  0.00044 &         0.3330 &   0.025773 & 0.136140 &  0.135027 & Train \\
DT70\_MSS10\_MSL7\_none\_one\_hot\_B1\_v1 &   0.997767 &  0.00044 &         0.2233 &   0.094443 & 0.118699 &  0.119142 &   Val \\
DT90\_MSS10\_MSL7\_none\_one\_hot\_B0\_v1 &   0.929108 &  0.00045 &         7.0892 &   0.094439 & 0.119797 &  0.118706 &  Test \\
\bottomrule
\end{tabular}

[21]:
print(u233_002_001[["Model", 'K_eff_ana', 'Unc_ana', 'Deviation_Ana', 'train_mae', 'val_mae', 'test_mae']].head(1).to_latex(index=False))
\begin{tabular}{lrrrrrr}
\toprule
                             Model &  K\_eff\_ana &  Unc\_ana &  Deviation\_Ana &  train\_mae &  val\_mae &  test\_mae \\
\midrule
DT280\_MSS5\_MSL1\_none\_one\_hot\_B0\_v1 &        1.0 &  0.00041 &            0.0 &    0.05187 & 0.131216 &  0.129827 \\
\bottomrule
\end{tabular}

[23]:
print(model_utils.get_best_models_df(u233_002_002[["Model", 'K_eff_ana', 'Unc_ana', 'Deviation_Ana', 'train_mae', 'val_mae', 'test_mae']]).to_latex(index=False))
\begin{tabular}{lrrrrrrl}
\toprule
                             Model &  K\_eff\_ana &  Unc\_ana &  Deviation\_Ana &  train\_mae &  val\_mae &  test\_mae &   tag \\
\midrule
DT400\_MSS2\_MSL1\_none\_one\_hot\_B0\_v1 &   1.005650 &  0.00042 &         0.5650 &   0.025773 & 0.136140 &  0.135027 & Train \\
DT70\_MSS10\_MSL7\_none\_one\_hot\_B1\_v1 &   1.000680 &  0.00044 &         0.0680 &   0.094443 & 0.118699 &  0.119142 &   Val \\
DT90\_MSS10\_MSL7\_none\_one\_hot\_B0\_v1 &   0.936182 &  0.00046 &         6.3818 &   0.094439 & 0.119797 &  0.118706 &  Test \\
\bottomrule
\end{tabular}

[41]:
print(u233_002_002[["Model", 'K_eff_ana', 'Unc_ana', 'Deviation_Ana', 'train_mae', 'val_mae', 'test_mae']].head(1).to_latex(index=False))
\begin{tabular}{lrrrrrr}
\toprule
                              Model &  K\_eff\_ana &  Unc\_ana &  Deviation\_Ana &  train\_mae &  val\_mae &  test\_mae \\
\midrule
DT170\_MSS10\_MSL3\_none\_one\_hot\_B0\_v1 &    1.00022 &  0.00042 &          0.022 &   0.082419 & 0.121982 &  0.121942 \\
\bottomrule
\end{tabular}