# Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import csv import os from .utils import default_candidates, gbs_default_candidates class AutoTuner: """ The AutoTuner can automatically provide running task based on user-defined settings and the task will be launched for execution. Args: tuner_cfg (dict): The configuration of auto tuner user defined. """ def __init__(self, tuner_cfg): self.cur_task_id = 1 self.task_limit = tuner_cfg.get("task_limit", 100) search_algo = tuner_cfg.get("search_algo", {"name": "grid"})["name"] if search_algo == "grid": from .search import GridSearch tuner_cfg["candidates"] = default_candidates(tuner_cfg) self.algo = GridSearch(tuner_cfg) elif search_algo == "dp_estimation": from .search import DpEstimationSearch tuner_cfg["candidates"] = default_candidates(tuner_cfg) self.algo = DpEstimationSearch(tuner_cfg) elif search_algo == "gbs": from .search import GBSSearch tuner_cfg["candidates"] = gbs_default_candidates(tuner_cfg) self.algo = GBSSearch(tuner_cfg) elif search_algo == "customize": from .search import CustomizeSearch self.algo = CustomizeSearch(tuner_cfg) else: raise NotImplementedError self.history_cfgs = [] self.resume_cfgs = [] self.tuner_cfg = tuner_cfg def search_once(self): """Return a new task config.""" if self.cur_task_id > self.task_limit: return None new_cfg = self.algo.search_once(self.history_cfgs) self.cur_task_id += 1 return new_cfg def add_cfg(self, cfg): """Add cfg into history cfgs""" self.history_cfgs.append(cfg) def resume_form_history(self, history_csv_path="./history.csv"): """Resume form history csv file""" # The breakpoint resume function does not start when the resume csv file does not exist. if not os.path.exists(history_csv_path): return resume_csv_path = os.path.join( os.path.dirname(history_csv_path), f'{os.path.basename(history_csv_path).split(".")[0]}_copy.csv', ) with open(history_csv_path, "r") as fread: reader = csv.reader(fread) data_list = list(reader) with open(resume_csv_path, "w") as fwrite: writer = csv.writer(fwrite) for row in data_list: writer.writerow(row) # chang str type to real type for row in data_list: for i, value in enumerate(row): try: row[i] = int(value) except ValueError: try: row[i] = float(value) except ValueError: pass data_dict = [] keys = data_list[0] values = data_list[1:] for val in values: val = [x if x != '' else None for x in val] val = [True if x == 'True' else x for x in val] val = [False if x == 'False' else x for x in val] dictionary = dict(zip(keys, val)) time_val = -1 target_key = self.tuner_cfg["metric_cfg"]["name"] if dictionary[target_key]: time_val = dictionary[target_key] dictionary["time"] = time_val data_dict.append(dictionary) self.resume_cfgs = data_dict def get_cfg_from_resume(self, cur_cfg): """Get cfg from resume cfgs""" keys_to_compare = [ 'mp_degree', 'sharding_degree', 'pp_degree', 'dp_degree', 'sharding_stage', 'micro_batch_size', 'vpp_degree', 'use_recompute', 'recompute_granularity', 'num_gpus', 'nodes', 'global_batch_size', 'sharding_overlap', 'acc_steps', ] if self.tuner_cfg.get("refined_recompute", None): for rr in self.tuner_cfg["refined_recompute"]: keys_to_compare.append(rr) if self.tuner_cfg.get("custom_search_dim", None): for key in self.tuner_cfg["custom_search_dim"]: keys_to_compare.append(key) for cfg in self.resume_cfgs: ret_is_same = True for key in keys_to_compare: if not cfg.get(key) and not cur_cfg.get(key): continue else: is_same = str(cfg.get(key)) == str(cur_cfg.get(key)) ret_is_same = ret_is_same and is_same if ret_is_same: return cfg return None