Skip to content

Commit 355b4f3

Browse files
authored
Merge pull request #2401 from NNPDF/make_feature_scaling_clearer
Make the usage of feature scaling + large x a bit clearer
2 parents a14befa + ab37c79 commit 355b4f3

File tree

7 files changed

+73
-41
lines changed

7 files changed

+73
-41
lines changed

doc/sphinx/source/n3fit/methodology.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ incapable of distinguishing features across many orders of magnitude of ``x``, t
323323
scaling means that the algorithm is limited to learning features on a logarithmic and linear scale.
324324

325325
To solve this problem there is the possibility to apply a different feature scaling to the input by
326-
adding a ``interpolation_points: [number of points]`` flag to the ``n3fit`` runcard. By adding this
326+
adding a ``feature_scaling_points: [number of points]`` flag to the ``n3fit`` runcard. By adding this
327327
flag the ``(x,log(x))`` scaling is replaced by a scaling in such a way that all input ``x`` values
328328
are evenly distributed on the domain ``[-1,1]``, and the input node is no longer split in two.
329329

@@ -333,7 +333,7 @@ increasing cubic spline is used to interpolate after the scaling has been applie
333333
function from the scipy library is used. However, this way the neural network will be agnostic to
334334
the existence of this interpolation function meaning it can no longer learn the true underlying law.
335335
To fix this, the interpolation function has to be probed as well. This is done by only using
336-
``[number of points]`` set by the ``interpolation_points`` flag to define the interpolation function
336+
``[number of points]`` set by the ``feature_scaling_points`` flag to define the interpolation function
337337
after the scaling has been applied. Using this methodology the points used in the interpolation are
338338
again evenly distributed.
339339

extra_tests/regression_fits/feature_scaling.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,20 @@ parameters: # This defines the parameter dictionary that is passed to the Model
5050
layer_type: 'dense'
5151
dropout: 0.0
5252
threshold_chi2: 10.0
53-
interpolation_points: 15
53+
feature_scaling_points: 15
5454

5555
fitting:
5656
savepseudodata: False
5757
fitbasis: EVOL
5858
basis:
59-
- {fl: sng, trainable: false, smallx: [1.094, 1.118], largex: [1.46, 3.003]}
60-
- {fl: g, trainable: false, smallx: [0.8189, 1.044], largex: [2.791, 5.697]}
61-
- {fl: v, trainable: false, smallx: [0.457, 0.7326], largex: [1.56, 3.431]}
62-
- {fl: v3, trainable: false, smallx: [0.1462, 0.4061], largex: [1.745, 3.452]}
63-
- {fl: v8, trainable: false, smallx: [0.5401, 0.7665], largex: [1.539, 3.393]}
64-
- {fl: t3, trainable: false, smallx: [-0.4401, 0.9163], largex: [1.773, 3.333]}
65-
- {fl: t8, trainable: false, smallx: [0.5852, 0.8537], largex: [1.533, 3.436]}
66-
- {fl: t15, trainable: false, smallx: [1.082, 1.142], largex: [1.461, 3.1]}
59+
- {fl: sng, trainable: false, smallx: [1.094, 1.118]}
60+
- {fl: g, trainable: false, smallx: [0.8189, 1.044]}
61+
- {fl: v, trainable: false, smallx: [0.457, 0.7326]}
62+
- {fl: v3, trainable: false, smallx: [0.1462, 0.4061]}
63+
- {fl: v8, trainable: false, smallx: [0.5401, 0.7665]}
64+
- {fl: t3, trainable: false, smallx: [-0.4401, 0.9163]}
65+
- {fl: t8, trainable: false, smallx: [0.5852, 0.8537]}
66+
- {fl: t15, trainable: false, smallx: [1.082, 1.142]}
6767

6868
############################################################
6969
positivity:

n3fit/runcards/example-nnpdf41.yml

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -127,21 +127,22 @@ parameters: # This defines the parameter dictionary that is passed to the Model
127127
layer_type: dense
128128
dropout: 0.0
129129
threshold_chi2: 3.5
130-
interpolation_points: 5
130+
feature_scaling_points: 5
131131

132132
fitting:
133133
fitbasis: CCBAR_ASYMM # EVOL (7), EVOLQED (8), etc.
134134
savepseudodata: true
135135
basis:
136-
- {fl : sng, trainable : false, smallx : [1.095, 1.146], largex : [0., 0.]}
137-
- {fl : g, trainable : false, smallx : [0.7978, 1.087], largex : [0., 0.]}
138-
- {fl : v, trainable : false, smallx : [0.4775, 0.6577], largex : [0., 0.]}
139-
- {fl : v3, trainable : false, smallx : [0.1068, 0.493], largex : [0., 0.]}
140-
- {fl : v8, trainable : false, smallx : [0.5914, 0.7776], largex : [0., 0.]}
141-
- {fl : t3, trainable : false, smallx : [-0.3737, 1.0], largex : [0., 0.]}
142-
- {fl : t8, trainable : false, smallx : [0.5771, 0.9486], largex : [0., 0.]}
143-
- {fl : t15, trainable : false, smallx : [1.062, 1.153], largex : [0., 0.]}
144-
- {fl : v15, trainable : false, smallx : [0.4515, 0.7648], largex : [0., 0.]}
136+
- {fl : sng , trainable : false , smallx : [1.095 , 1.146] }
137+
- {fl : g , trainable : false , smallx : [0.7978 , 1.087] }
138+
- {fl : v , trainable : false , smallx : [0.4775 , 0.6577] }
139+
- {fl : v3 , trainable : false , smallx : [0.1068 , 0.493] }
140+
- {fl : v8 , trainable : false , smallx : [0.5914 , 0.7776] }
141+
- {fl : t3 , trainable : false , smallx : [-0.3737 , 1.0] }
142+
- {fl : t8 , trainable : false , smallx : [0.5771 , 0.9486] }
143+
- {fl : t15 , trainable : false , smallx : [1.062 , 1.153] }
144+
- {fl : v15 , trainable : false , smallx : [0.4515 , 0.7648] }
145+
145146

146147
################################################################################
147148
positivity:

n3fit/runcards/examples/Basic_feature_scaling.yml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,21 +49,21 @@ parameters: # This defines the parameter dictionary that is passed to the Model
4949
stopping_patience: 0.30 # percentage of the number of epochs
5050
layer_type: 'dense'
5151
dropout: 0.0
52-
interpolation_points: 40
52+
feature_scaling_points: 40
5353
threshold_chi2: 5.0
5454

5555
fitting:
5656
fitbasis: NN31IC # EVOL (7), EVOLQED (8), etc.
5757
basis:
5858
# remeber to change the name of PDF accordingly with fitbasis
59-
- { fl: sng, smallx: [1.05,1.19], largex: [1.47,2.70], trainable: False }
60-
- { fl: g, smallx: [0.94,1.25], largex: [0.11,5.87], trainable: False }
61-
- { fl: v, smallx: [0.54,0.75], largex: [1.15,2.76], trainable: False }
62-
- { fl: v3, smallx: [0.21,0.57], largex: [1.35,3.08] }
63-
- { fl: v8, smallx: [0.52,0.76], largex: [0.77,3.56], trainable: True }
64-
- { fl: t3, smallx: [-0.37,1.52], largex: [1.74,3.39] }
65-
- { fl: t8, smallx: [0.56,1.29], largex: [1.45,3.03] }
66-
- { fl: cp, smallx: [0.12,1.19], largex: [1.83,6.70] }
59+
- { fl: sng, smallx: [1.05,1.19], trainable: False }
60+
- { fl: g, smallx: [0.94,1.25], trainable: False }
61+
- { fl: v, smallx: [0.54,0.75], trainable: False }
62+
- { fl: v3, smallx: [0.21,0.57] }
63+
- { fl: v8, smallx: [0.52,0.76], trainable: True }
64+
- { fl: t3, smallx: [-0.37,1.52] }
65+
- { fl: t8, smallx: [0.56,1.29] }
66+
- { fl: cp, smallx: [0.12,1.19] }
6767

6868
############################################################
6969
positivity:

n3fit/src/n3fit/checks.py

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ def check_consistent_basis(sum_rules, fitbasis, basis, theoryid, parameters):
396396
- Checks the sum rules can be imposed
397397
- Correct flavours for the selected basis
398398
- Correct ranges (min < max) for the small and large-x exponents
399+
- When feature scaling is active, the large_x interpolation is not set
399400
"""
400401
check_sumrules(sum_rules)
401402
# Check that there are no duplicate flavours and that parameters are sane
@@ -405,12 +406,19 @@ def check_consistent_basis(sum_rules, fitbasis, basis, theoryid, parameters):
405406
smallx = flavour_dict["smallx"]
406407
if smallx[0] > smallx[1]:
407408
raise CheckError(f"Wrong smallx range for flavour {name}: {smallx}")
408-
largex = flavour_dict.get("largex")
409-
if largex is not None and largex[0] > largex[1]:
410-
raise CheckError(f"Wrong largex range for flavour {name}: {largex}")
411409
if name in flavs:
412410
raise CheckError(f"Repeated flavour name: {name}. Check basis dictionary")
413411
flavs.append(name)
412+
413+
# Large-x is allowed to not exist if feature scaling is enabled
414+
if parameters.get("feature_scaling_points") is not None:
415+
if "largex" in flavour_dict and not flavour_dict["largex"] == [0.0, 0.0]:
416+
raise CheckError("No largex exponent allowed when feature_scaling_points is set")
417+
else:
418+
largex = flavour_dict["largex"]
419+
if largex[0] > largex[1]:
420+
raise CheckError(f"Wrong largex range for flavour {name}: {largex}")
421+
414422
# Finally check whether the basis considers or not charm
415423
# Check that the basis given in the runcard is one of those defined in validphys.pdfbases
416424
vp_basis = check_basis(fitbasis, flavs)["basis"]
@@ -438,7 +446,7 @@ def check_consistent_parallel(parameters, parallel_models):
438446

439447

440448
@make_argcheck
441-
def check_deprecated_options(fitting):
449+
def check_deprecated_options(fitting, parameters):
442450
"""Checks whether the runcard is using deprecated options"""
443451
options_outside = ["trvlseed", "nnseed", "mcseed", "save", "load", "genrep", "parameters"]
444452
for option in options_outside:
@@ -452,6 +460,10 @@ def check_deprecated_options(fitting):
452460
for option in nnfit_options:
453461
if option in fitting:
454462
log.warning("'fitting::%s' is an nnfit-only key, it will be ignored", option)
463+
if "interpolation_points" in parameters:
464+
raise CheckError(
465+
"`interpolation_points` no longer accepted, please change to `feature_scaling_points`"
466+
)
455467

456468

457469
@make_argcheck

n3fit/src/n3fit/model_trainer.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
from n3fit import model_gen
1919
from n3fit.backends import NN_LAYER_ALL_REPLICAS, MetaModel, callbacks, clear_backend_state
2020
from n3fit.backends import operations as op
21-
from n3fit.layers import losses
2221
from n3fit.hyper_optimization.hyper_scan import HYPEROPT_STATUSES
2322
import n3fit.hyper_optimization.penalties
2423
from n3fit.hyper_optimization.rewards import HyperLoss
24+
from n3fit.layers import losses
2525
from n3fit.scaler import generate_scaler
2626
from n3fit.stopping import Stopping
2727
from n3fit.vpinterface import N3PDF, compute_hyperopt_metrics
@@ -876,7 +876,7 @@ def hyperparametrizable(self, params):
876876
integrability_dict.get("multiplier"),
877877
integrability_dict.get("initial"),
878878
epochs,
879-
params.get("interpolation_points"),
879+
params.get("feature_scaling_points"),
880880
)
881881
threshold_pos = positivity_dict.get("threshold", 1e-6)
882882
threshold_chi2 = params.get("threshold_chi2", CHI2_THRESHOLD)
@@ -1077,11 +1077,11 @@ def hyperparametrizable(self, params):
10771077

10781078
# Compute the loss over all folds for hyperopt
10791079
final_hyper_loss = self._hyper_loss.reduce_over_folds(l_hyper)
1080-
1080+
10811081
# Add penalty term to ensure convergence
10821082
exp_chi2_fitted_data = np.average(trvl_chi2exp_per_fold)
10831083
expchi2_penalty = losses.LossHyperopt()
1084-
final_hyper_loss += expchi2_penalty(exp_chi2_fitted_data)
1084+
final_hyper_loss += expchi2_penalty(exp_chi2_fitted_data)
10851085

10861086
# Hyperopt needs a dictionary with information about the losses
10871087
# it is possible to store arbitrary information in the trial file

validphys2/src/validphys/eff_exponents.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,11 @@ def previous_effective_exponents_table(fit: FitSpec):
331331
)
332332
basis = checked["basis"]
333333
flavours = checked["flavours"]
334-
prev_a_bounds = [runcard_fl['smallx'] for runcard_fl in fitting["basis"]]
335-
prev_b_bounds = [runcard_fl['largex'] for runcard_fl in fitting["basis"]]
334+
prev_a_bounds = []
335+
prev_b_bounds = []
336+
for runcard_fl in fitting["basis"]:
337+
prev_a_bounds.append(runcard_fl.get("smallx", (0.0, 0.0)))
338+
prev_b_bounds.append(runcard_fl.get("largex", (0.0, 0.0)))
336339
# make single list alternating alpha and beta bounds
337340
data = [vals for pair in zip(prev_a_bounds, prev_b_bounds) for vals in pair]
338341
flavours_label = [f"${basis.elementlabel(fl)}$" for fl in flavours]
@@ -449,6 +452,10 @@ def effective_exponents_table_internal(next_effective_exponents_table, *, fit=No
449452
# have to call action here in case fit is None
450453
previous_table = previous_effective_exponents_table(fit)
451454
df = pd.concat((previous_table, next_effective_exponents_table), axis=1)
455+
456+
if "feature_scaling_points" in fit.as_input()["parameters"]:
457+
# Drop the beta if feature scaling points is enabled
458+
df.loc[df.index.get_level_values(1) == r'$\beta$', :] = None
452459
else:
453460
df = next_effective_exponents_table
454461
return df
@@ -509,6 +516,12 @@ def iterate_preprocessing_yaml(fit, next_fit_eff_exps_table, _flmap_np_clip_arg=
509516
checked = check_basis(basis, None)
510517
basis = checked["basis"]
511518

519+
# If the runcard still has the old option `interpolation_points` change it to `feature_scaling_points`:
520+
if "interpolation_points" in filtermap["parameters"]:
521+
filtermap["parameters"]["feature_scaling_points"] = filtermap["parameters"].pop(
522+
"interpolation_points"
523+
)
524+
512525
# use order defined in runcard.
513526
runcard_flavours = [f"{basis.elementlabel(ref_fl['fl'])}" for ref_fl in previous_exponents]
514527
for i, fl in enumerate(runcard_flavours):
@@ -523,7 +536,13 @@ def iterate_preprocessing_yaml(fit, next_fit_eff_exps_table, _flmap_np_clip_arg=
523536
if largex_args is not None:
524537
betas = np.clip(betas, **largex_args)
525538
previous_exponents[i]["smallx"] = [fmt(alpha) for alpha in alphas]
526-
previous_exponents[i]["largex"] = [fmt(beta) for beta in betas]
539+
# Regardless of whether there was a large x in the original runcard
540+
# drop it if feature scaling is set, to avoid future mistakes
541+
if filtermap["parameters"].get("feature_scaling_points") is None:
542+
previous_exponents[i]["largex"] = [fmt(beta) for beta in betas]
543+
else:
544+
# NB previous exponents is = filtermap (see above), if it dies here it dies in real life
545+
previous_exponents[i].pop("largex", None)
527546
with tempfile.NamedTemporaryFile() as fp:
528547
path = Path(fp.name)
529548
yaml_rt.dump(filtermap, path)

0 commit comments

Comments
 (0)