diff --git a/alphadia/constants/default.yaml b/alphadia/constants/default.yaml index d42a2874..8fba4bab 100644 --- a/alphadia/constants/default.yaml +++ b/alphadia/constants/default.yaml @@ -250,48 +250,55 @@ optimization: # Example: [['ms1_error', 'ms2_error', 'rt_error', 'mobility_error']] means that all parameters are optimized simultaneously. # Example: [["ms2_error"], ["rt_error"], ["ms1_error"], ["mobility_error"]] means that the parameters are optimized sequentially in the order given. # Example: [["rt_error"], ["ms1_error", "ms2_error"]] means that first rt_error is optimized, then ms1_error and ms2_error are optimized simultaneously, and mobility_error is not optimized at all. - # If order_of_optimizatoin is null, first all targeted optimizers run simultaneously, then any remaining automatic optimizers run sequentially in the order [["ms2_error"], ["rt_error"], ["ms1_error"], ["mobility_error"]] + # If order_of_optimization is null, first all targeted optimizers run simultaneously, then any remaining automatic optimizers run sequentially in the order [["ms2_error"], ["rt_error"], ["ms1_error"], ["mobility_error"]] order_of_optimization: null # Parameters for the update rule for each parameter: # - update_percentile_range: the percentile interval to use (as a decimal) # - update_factor: the factor by which to multiply the result from the percentile interval to get the new parameter value for the next round of search - # - favour_narrower_parameter: if True, the optimization will not take the value that maximizes the feature used for optimization, but instead the smallest value compatible with the minimum_proportion_of_maximum value. - # This setting can be useful for optimizing parameters for which many parameter values have similar feature values and therefore favouring narrower parameters helps to overcome noise. - # - maximal_decrease: the maximal decrease of the parameter value before stopping optimization (only relevant if favour_narrower_parameter is True) - # - minimum_proportion_of_maximum: the minimum proportion of the maximum value of the parameter that the designated optimum should have (only relevant if favour_narrower_parameter is True) + # - try_narrower_parameters: if True, the optimization will try narrower parameters until a substantial (as determined by maximal_decrease) decrease in the feature used for optimization is observed. + # - maximal_decrease: the maximal decrease of the parameter value before stopping optimization (only relevant if favour_narrower_parameter is True). + # For example, a value of 0.2 indicates up to 20% decrease from the previous parameter is permissible. + # - favour_narrower_optimum: if True, the optimization will not take the value that maximizes the feature used for optimization, but instead the smallest value compatible with the maximum_decrease_from_maximum value. + # This setting can be useful for optimizing parameters for which many parameter values have similar feature values and therefore favouring narrower parameters helps to overcome noise. + # - maximum_decrease_from_maximum: the maximum proportional decrease from the maximum value of the parameter that the designated optimum should have (only relevant if favour_narrower_optimum is True). + # For example, a value of 0.1 indicates that the optimum should no more than 10% less than the maximum value. ms2_error: targeted_update_percentile_range: 0.95 targeted_update_factor: 1.0 automatic_update_percentile_range: 0.99 automatic_update_factor: 1.1 - favour_narrower_parameter: False - maximal_decrease: 0.8 - minimum_proportion_of_maximum: 0.9 + try_narrower_values: False + maximal_decrease: 0.2 + favour_narrower_optimum: False + maximum_decrease_from_maximum: 0.1 ms1_error: targeted_update_percentile_range: 0.95 targeted_update_factor: 1.0 automatic_update_percentile_range: 0.99 automatic_update_factor: 1.1 - favour_narrower_parameter: False - maximal_decrease: 0.8 - minimum_proportion_of_maximum: 0.9 + try_narrower_values: False + maximal_decrease: 0.2 + favour_narrower_optimum: False + maximum_decrease_from_maximum: 0.1 mobility_error: targeted_update_percentile_range: 0.95 targeted_update_factor: 1.0 automatic_update_percentile_range: 0.99 automatic_update_factor: 1.1 - favour_narrower_parameter: False - maximal_decrease: 0.8 - minimum_proportion_of_maximum: 0.9 + try_narrower_values: False + maximal_decrease: 0.2 + favour_narrower_optimum: False + maximum_decrease_from_maximum: 0.1 rt_error: targeted_update_percentile_range: 0.95 targeted_update_factor: 1.0 automatic_update_percentile_range: 0.99 automatic_update_factor: 1.1 - favour_narrower_parameter: True - maximal_decrease: 0.8 - minimum_proportion_of_maximum: 0.9 + try_narrower_values: True + maximal_decrease: 0.2 + favour_narrower_optimum: True + maximum_decrease_from_maximum: 0.1 # configuration for the optimization manager # initial parameters, will be optimized diff --git a/alphadia/workflow/optimization.py b/alphadia/workflow/optimization.py index 4b868374..66c5e916 100644 --- a/alphadia/workflow/optimization.py +++ b/alphadia/workflow/optimization.py @@ -100,17 +100,27 @@ def __init__( self.parameter_name ]["automatic_update_percentile_range"] - self.favour_narrower_parameter = workflow.config["optimization"][ + self._try_narrower_values = workflow.config["optimization"][ self.parameter_name - ]["favour_narrower_parameter"] + ]["try_narrower_values"] - if self.favour_narrower_parameter: - self.maximal_decrease = workflow.config["optimization"][ - self.parameter_name - ]["maximal_decrease"] - self.minimum_proportion_of_maximum = workflow.config["optimization"][ - self.parameter_name - ]["minimum_proportion_of_maximum"] + self._maximal_decrease = ( + workflow.config["optimization"][self.parameter_name]["maximal_decrease"] + if self._try_narrower_values + else None + ) + + self._favour_narrower_optimum = workflow.config["optimization"][ + self.parameter_name + ]["favour_narrower_optimum"] + + self._maximum_decrease_from_maximum = ( + workflow.config["optimization"][self.parameter_name][ + "maximum_decrease_from_maximum" + ] + if self._favour_narrower_optimum + else None + ) def step( self, @@ -287,10 +297,10 @@ def _batch_substantially_bigger(self): @property def _just_converged(self): """Optimization should stop if continued narrowing of the parameter is not improving the feature value. - If self.favour_narrower_parameter is False: + If self._try_narrower_values is False: 1) This function checks if the previous rounds of optimization have led to a meaningful improvement in the feature value. 2) If so, it continues optimization and appends the proposed new parameter to the list of parameters. If not, it stops optimization and sets the optimal parameter attribute. - If self.favour_narrower_parameter is True: + If self._try_narrower_values is True: 1) This function checks if the previous rounds of optimization have led to a meaningful disimprovement in the feature value or if the parameter has not changed substantially. 2) If not, it continues optimization and appends the proposed new parameter to the list of parameters. If so, it stops optimization and sets the optimal parameter attribute. @@ -303,23 +313,33 @@ def _just_converged(self): if len(self.history_df) < 3: return False - if self.favour_narrower_parameter: # This setting can be useful for optimizing parameters for which many parameter values have similar feature values. + feature_history = self.history_df[self.feature_name] + last_feature_value = feature_history.iloc[-1] + second_last_feature_value = feature_history.iloc[-2] + third_last_feature_value = feature_history.iloc[-3] + + if self._try_narrower_values: # This setting can be useful for optimizing parameters for which many parameter values have similar feature values. min_steps_reached = ( self._num_prev_optimizations >= self.workflow.config["calibration"]["min_steps"] ) - feature_history = self.history_df[self.feature_name] feature_substantially_decreased = ( - feature_history.iloc[-1] - < self.maximal_decrease * feature_history.iloc[-2] - and feature_history.iloc[-1] - < self.maximal_decrease * feature_history.iloc[-3] - ) + last_feature_value - second_last_feature_value + ) / np.abs(second_last_feature_value) < -self._maximal_decrease and ( + last_feature_value - third_last_feature_value + ) / np.abs(third_last_feature_value) < -self._maximal_decrease parameter_history = self.history_df["parameter"] + + last_parameter_value = parameter_history.iloc[-1] + second_last_parameter_value = parameter_history.iloc[-2] parameter_not_substantially_changed = ( - parameter_history.iloc[-1] / parameter_history.iloc[-2] > 0.95 + np.abs( + (last_parameter_value - second_last_parameter_value) + / second_last_parameter_value + ) + < 0.05 ) return min_steps_reached and ( @@ -332,20 +352,20 @@ def _just_converged(self): >= self.workflow.config["calibration"]["min_steps"] ) - feature_history = self.history_df[self.feature_name] + feature_not_substantially_increased = ( + last_feature_value - second_last_feature_value + ) / np.abs(second_last_feature_value) < 0.1 and ( + last_feature_value - third_last_feature_value + ) / np.abs(third_last_feature_value) < 0.1 - return ( - min_steps_reached - and feature_history.iloc[-1] < 1.1 * feature_history.iloc[-2] - and feature_history.iloc[-1] < 1.1 * feature_history.iloc[-3] - ) + return min_steps_reached and feature_not_substantially_increased def _find_index_of_optimum(self): """Finds the index of the row in the history dataframe with the optimal value of the feature used for optimization. - if self.favour_narrower_parameter is False: + if self._favour_narrower_parameter is False: The index at optimum is the index of the parameter value that maximizes the feature. - if self.favour_narrower_parameter is True: - The index at optimum is the index of the minimal parameter value whose feature value is at least self.minimum_proportion_of_maximum of the maximum value of the feature. + if self._favour_narrower_parameter is True: + The index at optimum is the index of the minimal parameter value whose feature value is at least self._maximum_decrease_from_maximum of the maximum value of the feature. Returns ------- @@ -358,11 +378,15 @@ def _find_index_of_optimum(self): """ - if self.favour_narrower_parameter: # This setting can be useful for optimizing parameters for which many parameter values have similar feature values. + if self._favour_narrower_optimum: # This setting can be useful for optimizing parameters for which many parameter values have similar feature values. + maximum_feature_value = self.history_df[self.feature_name].max() rows_within_thresh_of_max = self.history_df.loc[ self.history_df[self.feature_name] - > self.history_df[self.feature_name].max() - * self.minimum_proportion_of_maximum + > ( + maximum_feature_value + - self._maximum_decrease_from_maximum + * np.abs(maximum_feature_value) + ) ] index_of_optimum = rows_within_thresh_of_max["parameter"].idxmin() return index_of_optimum diff --git a/tests/unit_tests/test_workflow.py b/tests/unit_tests/test_workflow.py index afd2d8e6..35d7bf2f 100644 --- a/tests/unit_tests/test_workflow.py +++ b/tests/unit_tests/test_workflow.py @@ -960,15 +960,16 @@ def test_configurability(): "rt_error": { "automatic_update_percentile_range": 0.99, "automatic_update_factor": 1.3, - "favour_narrower_parameter": True, - "maximal_decrease": 0.8, - "minimum_proportion_of_maximum": 0.95, + "try_narrower_values": True, + "maximal_decrease": 0.4, + "favour_narrower_optimum": True, + "maximum_decrease_from_maximum": 0.3, }, "ms2_error": { "automatic_update_percentile_range": 0.80, "targeted_update_percentile_range": 0.995, "targeted_update_factor": 1.2, - "favour_narrower_parameter": False, + "favour_narrower_optimum": False, }, } ) @@ -1049,6 +1050,3 @@ def test_optimizer_skipping(): rt_optimizer.step(calibration_test_df1, calibration_test_df2) assert rt_optimizer.has_converged is True - - -test_optimizer_skipping()