mirror of
				https://github.com/microsoft/autogen.git
				synced 2025-10-31 01:40:58 +00:00 
			
		
		
		
	
		
			
	
	
		
			98 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			98 lines
		
	
	
		
			3.5 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | import numpy as np | ||
|  | import pandas as pd | ||
|  | from sklearn.preprocessing import RobustScaler | ||
|  | from sklearn.metrics import pairwise_distances | ||
|  | 
 | ||
|  | 
 | ||
|  | def _augment(row): | ||
|  |     max, avg, id = row.max(), row.mean(), row.index[0] | ||
|  |     return row.apply(lambda x: (x, max, avg, id)) | ||
|  | 
 | ||
|  | 
 | ||
|  | def construct_portfolio(regret_matrix, meta_features, regret_bound): | ||
|  |     """The portfolio construction algorithm.
 | ||
|  | 
 | ||
|  |     (Reference)[https://arxiv.org/abs/2202.09927]. | ||
|  | 
 | ||
|  |     Args: | ||
|  |         regret_matrix: A dataframe of regret matrix. | ||
|  |         meta_features: None or a dataframe of metafeatures matrix. | ||
|  |             When set to None, the algorithm uses greedy strategy. | ||
|  |             Otherwise, the algorithm uses greedy strategy with feedback | ||
|  |             from the nearest neighbor predictor. | ||
|  |         regret_bound: A float of the regret bound. | ||
|  | 
 | ||
|  |     Returns: | ||
|  |         A list of configuration names. | ||
|  |     """
 | ||
|  |     configs = [] | ||
|  |     all_configs = set(regret_matrix.index.tolist()) | ||
|  |     tasks = regret_matrix.columns | ||
|  |     # pre-processing | ||
|  |     if meta_features is not None: | ||
|  |         scaler = RobustScaler() | ||
|  |         meta_features = meta_features.loc[tasks] | ||
|  |         meta_features.loc[:, :] = scaler.fit_transform(meta_features) | ||
|  |         nearest_task = {} | ||
|  |         for t in tasks: | ||
|  |             other_meta_features = meta_features.drop(t) | ||
|  |             dist = pd.DataFrame( | ||
|  |                 pairwise_distances( | ||
|  |                     meta_features.loc[t].to_numpy().reshape(1, -1), | ||
|  |                     other_meta_features, | ||
|  |                     metric="l2", | ||
|  |                 ), | ||
|  |                 columns=other_meta_features.index, | ||
|  |             ) | ||
|  |             nearest_task[t] = dist.idxmin(axis=1) | ||
|  |         regret_matrix = regret_matrix.apply(_augment, axis=1) | ||
|  |         print(regret_matrix) | ||
|  | 
 | ||
|  |     def loss(configs): | ||
|  |         """Loss of config set `configs`, according to nearest neighbor config predictor.""" | ||
|  |         if meta_features is not None: | ||
|  |             r = [] | ||
|  |             best_config_per_task = regret_matrix.loc[configs, :].min() | ||
|  |             for t in tasks: | ||
|  |                 config = best_config_per_task[nearest_task[t]].iloc[0][-1] | ||
|  |                 r.append(regret_matrix[t][config][0]) | ||
|  |         else: | ||
|  |             r = regret_matrix.loc[configs].min() | ||
|  |         excessive_regret = (np.array(r) - regret_bound).clip(min=0).sum() | ||
|  |         avg_regret = np.array(r).mean() | ||
|  |         return excessive_regret, avg_regret | ||
|  | 
 | ||
|  |     prev = np.inf | ||
|  |     i = 0 | ||
|  |     eps = 1e-5 | ||
|  |     while True: | ||
|  |         candidates = [configs + [d] for d in all_configs.difference(configs)] | ||
|  |         losses, avg_regret = tuple(zip(*(loss(x) for x in candidates))) | ||
|  |         sorted_losses = np.sort(losses) | ||
|  |         if sorted_losses[1] - sorted_losses[0] < eps: | ||
|  |             minloss = np.nanmin(losses) | ||
|  |             print( | ||
|  |                 f"tie detected at loss = {sorted_losses[0]}, using alternative metric." | ||
|  |             ) | ||
|  |             tied = np.flatnonzero(losses - minloss < eps) | ||
|  |             losses = [(avg_regret[i], i) for i in tied] | ||
|  |             minloss, ind = min(losses) | ||
|  |             if minloss > prev - eps: | ||
|  |                 print( | ||
|  |                     f"May be overfitting at k = {i + 1}, current = {minloss:.5f}, " | ||
|  |                     f"prev = {prev:.5f}. Stopping." | ||
|  |                 ) | ||
|  |                 break | ||
|  |             configs = candidates[ind] | ||
|  |             prev = minloss | ||
|  |         else: | ||
|  |             configs = candidates[np.nanargmin(losses)] | ||
|  |         i += 1 | ||
|  |         if sorted_losses[0] <= eps: | ||
|  |             print( | ||
|  |                 f"Reached target regret bound of {regret_bound}! k = {i}. Declining to pick further!" | ||
|  |             ) | ||
|  |             break | ||
|  | 
 | ||
|  |     return configs |