From 95d7160c0e64af3a23da8711afb67553a0e72eaa Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 30 Aug 2024 16:26:05 +0800 Subject: [PATCH 001/160] add expo --- expo/MCTS.py | 347 +++++++++++++++++++++++++ expo/data.yaml | 157 +++++++++++ expo/dataset.py | 262 +++++++++++++++++++ expo/datasets.yaml | 134 ++++++++++ expo/evaluation/evaluation.py | 23 ++ expo/evaluation/visualize_mcts.py | 54 ++++ expo/experimenter/aug_experimenter.py | 0 expo/experimenter/experimenter.py | 18 ++ expo/experimenter/mcts_experimenter.py | 0 expo/insights/InsightGenerate.py | 114 ++++++++ expo/insights/solution_designer.py | 127 +++++++++ expo/research_assistant.py | 141 ++++++++++ expo/results/PLACEHOLDER | 0 expo/results/tree/TREE | 0 expo/run_exp_augmentation.py | 96 +++++++ expo/run_experiment.py | 44 ++++ expo/run_mcts.py | 48 ++++ expo/utils.py | 150 +++++++++++ 18 files changed, 1715 insertions(+) create mode 100644 expo/MCTS.py create mode 100644 expo/data.yaml create mode 100644 expo/dataset.py create mode 100644 expo/datasets.yaml create mode 100644 expo/evaluation/evaluation.py create mode 100644 expo/evaluation/visualize_mcts.py create mode 100644 expo/experimenter/aug_experimenter.py create mode 100644 expo/experimenter/experimenter.py create mode 100644 expo/experimenter/mcts_experimenter.py create mode 100644 expo/insights/InsightGenerate.py create mode 100644 expo/insights/solution_designer.py create mode 100644 expo/research_assistant.py create mode 100644 expo/results/PLACEHOLDER create mode 100644 expo/results/tree/TREE create mode 100644 expo/run_exp_augmentation.py create mode 100644 expo/run_experiment.py create mode 100644 expo/run_mcts.py create mode 100644 expo/utils.py diff --git a/expo/MCTS.py b/expo/MCTS.py new file mode 100644 index 000000000..9026e09b4 --- /dev/null +++ b/expo/MCTS.py @@ -0,0 +1,347 @@ +import random +import math +import os +import pandas as pd +from expo.research_assistant import ResearchAssistant +from expo.insights.InsightGenerate import InsightGenerator +from expo.dataset import get_split_dataset_path +from expo.evaluation.evaluation import evaluate_score +from expo.utils import mcts_logger, load_execute_notebook, generate_task_requirement, get_exp_pool_path + +from metagpt.tools.tool_recommend import BM25ToolRecommender, ToolRecommender +from metagpt.utils.common import write_json_file, read_json_file, format_trackback_info +import numpy as np +import pickle + +def initialize_di_root_node(task, data_config, low_is_better=False, reflection=True, name=""): + start_task_id = 2 + state = create_initial_state(task, start_task_id=start_task_id, data_config=data_config, low_is_better=low_is_better, name=name) + role = ResearchAssistant(node_id="0", start_task_id=start_task_id, use_reflection=reflection, role_dir=state["node_dir"]) + return role, Node(parent=None, state=state, action=None, value=0) + + +def create_initial_state(task, start_task_id, data_config, low_is_better, name): + initial_state = { + "task": task, + "work_dir": data_config["work_dir"], + "node_dir": os.path.join(data_config["work_dir"], data_config["role_dir"], f"{task}{name}"), + "dataset_config": data_config["datasets"][task], + "datasets_dir": get_split_dataset_path(task, data_config), + "exp_pool_path": get_exp_pool_path(task, data_config, pool_name="ds_analysis_pool"), + "requirement": generate_task_requirement(task, data_config), + "has_run": False, + "start_task_id": start_task_id, + "low_is_better": low_is_better, + } + return initial_state + + +class Node(): + state : dict = {} + action : str = None + value : float = 0 + visited : int = 0 + children : list = [] + parent = None + + def __init__(self, parent=None, state = None, action=None, value = 0, max_depth=4, **kwargs): + self.state = state + self.action = action + self.value = value + self.raw_value = 0 + self.raw_reward = dict() + self.parent = parent + self.children = [] + self.max_depth = max_depth + self.depth = self.generate_depth() + self.id = self.generate_id() + if self.parent is not None: + self.save_node() + + def avg_value(self): + if self.visited == 0: + return 0 + return self.value / self.visited + + def __hash__(self): + return hash(self.id) + + def save_node(self): + os.makedirs(self.state["node_dir"], exist_ok=True) + with open(os.path.join(self.state["node_dir"], f"Node-{self.id}.pkl"), 'wb') as f: + pickle.dump(self, f) + + def load_node(self): + with open(os.path.join(self.state["node_dir"], f"Node-{self.id}.pkl"), 'rb') as f: + return pickle.load(f) + + def get_depth(self): + return self.depth + + def generate_depth(self): + if self.parent is None: + return 0 + else: + return self.parent.depth + 1 + + def generate_id(self): + if self.parent is None: + return "0" + else: + num_sibling = len(self.parent.children) + return f"{self.parent.id}-{num_sibling}" + + def is_terminal(self): + return int(self.state["start_task_id"]) == self.max_depth + 1 + + def is_fully_expanded(self): + return len(self.children) > 0 + + def add_child(self, child_node): + self.children.append(child_node) + + def update(self, reward:dict, child_node=None): + if child_node is not None: + child_role = child_node.load_role() + role = self.load_role() + role.update_til_start_task(child_role) + role.save_state() + else: + self.raw_value = reward["test_score"] + self.value += reward["score"] + self.visited += 1 + self.save_node() + + def get_role_path(self): + fname = f"Node-{self.id}.json" + role_path = os.path.join(self.state["node_dir"], fname) + return role_path + + def load_role(self): + role_dict = read_json_file(self.get_role_path()) + if role_dict.get('tool_recommender') is None: + role_dict['tool_recommender'] = ToolRecommender() + elif isinstance(role_dict.get('tool_recommender', {}).get('tools'), dict): + role_dict['tool_recommender']['tools'] = list(role_dict['tool_recommender']['tools'].keys()) + role = ResearchAssistant(**role_dict) + if self.parent is not None: # TODO: Check this + parent_role = self.parent.load_role() + role.update_til_start_task(parent_role, backward=False) + role.remap_tasks() + return role + + def save_new_role(self, role: ResearchAssistant): + role.node_id = self.id + role.start_task_id = self.state['start_task_id'] + role.state_saved = False + role.change_next_instruction(self.action) + mcts_logger.log("MCTS", f"保存新的role: {role.node_id}") + role.save_state(static_save=True) + + async def expand(self, max_children): + if self.is_fully_expanded(): + return + insight_geneartor = InsightGenerator() + role = self.load_role() + original_instruction = role.get_next_instruction() + insights = await insight_geneartor.generate_new_instructions(task_id=role.start_task_id + 1, + original_instruction=original_instruction, + max_num=max_children, + file_path=self.state["exp_pool_path"]) + new_state = self.state.copy() + new_state['start_task_id'] += 1 + for insight in insights: + new_role = role.model_copy() + node = Node(parent=self, state=new_state, action=insight, value=0) + node.save_new_role(new_role) + self.add_child(node) + + # def evaluate_test(self): + # prediction_fpath = os.path.join(self.state["work_dir"], self.state["task"], "predictions.csv") + # predictions = pd.read_csv(prediction_fpath)["target"] + # # copy predictions.csv to the node_dir + # predictions_node_fpath = os.path.join(self.state["node_dir"], "Node-{self.id}-predictions.csv") + # predictions.to_csv(predictions_node_fpath, index=False) + # # load test_target.csv + # split_datasets_dir = self.state["datasets_dir"] + # gt = pd.read_csv(os.path.join(split_datasets_dir["test_target"]))["target"] + # metric = self.state["dataset_config"]["metric"] + # return evaluate_score(predictions, gt, metric) + + def evaluate_prediction(self, split): + pred_path = os.path.join(self.state["work_dir"], self.state["task"], f"{split}-predictions.csv") + pred_node_path = os.path.join(self.state["node_dir"], f"Node-{self.id}-{split}-predictions.csv") + gt_path = os.path.join(self.state["datasets_dir"][f"{split}_target"]) + preds = pd.read_csv(pred_path)["target"] + preds.to_csv(pred_node_path, index=False) + gt = pd.read_csv(gt_path)["target"] + metric = self.state["dataset_config"]["metric"] + return evaluate_score(preds, gt, metric) + + def evaluate_simulation(self, score_dict): + scores = { + "dev_score": self.evaluate_prediction("dev"), + "test_score": self.evaluate_prediction("test") + } + score_dict.update(scores) + return score_dict + + + async def run_node(self, role=None): + if self.is_terminal() and role is not None: + if role.state_saved: + return self.raw_reward + + if not role: + role = self.load_role() + await load_execute_notebook(role) # execute previous notebook's code + await role.run(with_message='continue') + else: + await role.run(with_message=self.state['requirement']) + score_dict = await role.get_score() + score_dict = self.evaluate_simulation(score_dict) + self.raw_reward = score_dict + + if self.state["low_is_better"]: + # normalized the score to be between 0 and 1, and higher is better + def normalize_score(score): + return 1 / (1 + score) + score_dict = {k: normalize_score(v) for k, v in score_dict.items()} + return score_dict + + +class MCTS(): + #data_path + root_node : Node = None + children : dict = {} + max_depth : int = 5 + c_explore : float = 1.4 + c_unvisited : float = 0.8 + + def __init__(self, root_node, max_depth): + self.root_node = root_node + self.max_depth = max_depth + + def select(self, node: Node): + node = self.best_child() + mcts_logger.log("MCTS", f"选择的叶子节点id: {node.id}") + return node + + def best_child(self): + def uct(node: Node): + n_visits = node.visited if node.visited else self.c_unvisited + avg_value = node.avg_value() if node.visited else node.value/self.c_unvisited + return avg_value + self.c_explore * math.sqrt(math.log(node.parent.visited) / n_visits) + if len(self.children) == 0: + return self.root_node + all_children = [child for children in self.children.values() for child in children] + return max(all_children, key=uct) + + async def expand(self, node : Node, max_children=4): + await node.expand(max_children) + if node not in self.children or not self.children[node]: + self.children[node] = node.children + return node.children + + async def simulate(self, node : Node, role=None): + "Returns the reward for a random simulation (to completion) of `node`" + while node.children: + node = random.choice(node.children) + reward = await node.run_node(role) + return reward + + + def backpropagate(self, node : Node, reward): + child_node = node + node.update(reward) + node = node.parent + while node is not None: + node.update(reward, child_node) + node, child_node = node.parent, node + + def best_path(self, root : Node): + best_child = root + best_score = 0 + def bfs(node : Node, best_score, best_child : Node): + if node not in self.children: + return best_score, best_child + for child in self.children[node]: + print(child.id, child.raw_value) + if child.raw_value > best_score: + best_score = child.raw_value + best_child = child + best_score, best_child = bfs(child, best_score, best_child) + return best_score, best_child + best_score, best_child = bfs(root, best_score, best_child) + mcts_logger.log("MCTS", f"Best Score: {best_score}, Best Node ID: {best_child.id}") + return best_child + + def get_num_simulations(self): + return self.root_node.visited + + async def search(self, task, data_config, name, + rollout=3, load_tree=False, low_is_better=False, reflection=False): + + role, root = initialize_di_root_node(task, data_config, low_is_better=low_is_better, reflection=reflection, name=name) + self.root_node = root + tree_loaded = False + if load_tree: + tree_loaded = self.load_tree() + mcts_logger.log("MCTS", f"Number of simulations: {self.get_num_simulations()}") + if not tree_loaded: + self.children[root] = [] + reward = await self.simulate(root, role) + self.backpropagate(root, reward) + mcts_logger.log("MCTS", f"Root node's value: {reward}") + children = await self.expand(root) + #目前是随机选择1个,后续可以改成多个 + first_leaf = random.choice(children) + mcts_logger.log("MCTS", f"随机选择的叶子节点id: {first_leaf.id}") + reward = await self.simulate(first_leaf) + mcts_logger.log("MCTS", f"模拟完毕的叶子节点的Normalized score: {reward}") + self.backpropagate(first_leaf, reward) + else: + root = self.root_node + # 后续迭代:使用UCT进行选择,expand并模拟和反向传播 + for _ in range(rollout): # 迭代次数 + mcts_logger.log("MCTS", f"开始第{_+1}次迭代") + leaf = self.select(root) + if leaf.is_terminal(): + if leaf.raw_value == 0: + reward = await self.simulate(leaf) + else: + reward = {"test_score": leaf.raw_value, "score": leaf.value} + mcts_logger.log("MCTS", f"终止节点的得分为: {reward}") + self.backpropagate(leaf, reward) + else: + if leaf.visited > 0: + children = await self.expand(leaf) + leaf = random.choice(children) + mcts_logger.log("MCTS", f"随机选择的叶子节点id: {leaf.id}") + reward = await self.simulate(leaf) + mcts_logger.log("MCTS", f"模拟完毕的叶子节点{leaf.id}的Normalized score: {reward}") + self.backpropagate(leaf, reward) + return self.best_path(root) + + + def load_tree(self): + def load_children_node(node): + mcts_logger.log("MCTS", f"加载节点{node.id}的子节点:{node.children}") + if node.is_terminal() or not node.children: + return + for child in node.children: + child.load_node() + self.children[child] = child.children + load_children_node(child) + # Load all pkl files in the node_dir + all_pkl_files = os.listdir(self.root_node.state["node_dir"]) + all_pkl_files = [f for f in all_pkl_files if f.endswith(".pkl")] + if os.path.exists(os.path.join(self.root_node.state["node_dir"], "Node-0.pkl")): + with open(os.path.join(self.root_node.state["node_dir"], "Node-0.pkl"), 'rb') as f: + self.root_node = pickle.load(f) + self.children[self.root_node] = self.root_node.children + load_children_node(self.root_node) + if self.children: + mcts_logger.log("MCTS", "成功加载树") + return True + return False \ No newline at end of file diff --git a/expo/data.yaml b/expo/data.yaml new file mode 100644 index 000000000..d921e1ebf --- /dev/null +++ b/expo/data.yaml @@ -0,0 +1,157 @@ +datasets_dir: "D:/work/automl/datasets" # path to the datasets directory + +datasets: + titanic: + dataset: "04_titanic" + user_requirement: "This is a titanic passenger survival dataset, your goal is to predict passenger survival outcome. The target column is Survived. Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. Report accuracy on the eval data. Don't plot." + metric: "accuracy" + + house_prices: + dataset: "05_house-prices-advanced-regression-techniques" + user_requirement: "This is a house price dataset, your goal is to predict the sale price of a property based on its features. Make sure to generate at least 5 tasks each time, including eda, data preprocessing, feature engineering, model training to predict the target, and model evaluation. Report RMSE between the logarithm of the predicted value and the logarithm of the observed sale prices on the eval data. The target column is 'SalePrice'. Please do not include any processing of the target column in the data preprocessing and feature engineering stages. Don't plot." + metric: "log rmse" + + santander_customers: + dataset: "06_santander-customer-transaction-prediction" + user_requirement: "This is a customers financial dataset. Your goal is to predict which customers will make a specific transaction in the future. The target column is target. Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. Report AUC on the eval data. Don't plot." + metric: "auc" + + icr: + dataset: "07_icr-identify-age-related-conditions" + user_requirement: "ICR dataset is a medical dataset with over fifty anonymized health characteristics linked to three age-related conditions. Your goal is to predict whether a subject has or has not been diagnosed with one of these conditions. Make sure to generate at least 5 tasks each time, including eda, data preprocessing, feature engineering, model training to predict the target, and model evaluation. The target column is Class. Report F1 Score on the eval data. Don't plot." + metric: "f1" + + santander_value: + dataset: "08_santander-value-prediction-challenge" + user_requirement: "This is a regression problem. Your goal is to predict the value of transactions for potential customers. The target column is target. Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. Report RMSE on the eval data. Don't plot." + metric: "rmse" + + load_wine: + dataset: None + user_requirement: "Analyze the 'load_wine' dataset from sklearn to predict wine quality. Visualize relationships between features, use machine learning for classification, and report model accuracy. Include analysis and prediction visualizations. Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. Don't plot!" + metric: "accuracy" + + lick_prediction_small: + dataset: Click_prediction_small + metric: f1 + user_requirement: "This is a Click_prediction_small dataset. Your goal is to predict\ + \ the target column `click`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 on the eval data.\ + \ Do not plot or make any visualizations.\n" + + GesturePhaseSegmentationProcessed: + dataset: GesturePhaseSegmentationProcessed + metric: f1 weighted + user_requirement: "This is a GesturePhaseSegmentationProcessed dataset. Your goal\ + \ is to predict the target column `Phase`.\nPerform data analysis, data preprocessing,\ + \ feature engineering, and modeling to predict the target. \nReport f1 weighted\ + \ on the eval data. Do not plot or make any visualizations.\n" + + Moneyball: + dataset: Moneyball + metric: rmse + user_requirement: "This is a Moneyball dataset. Your goal is to predict the target\ + \ column `RS`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ + \ plot or make any visualizations.\n" + + SAT11-HAND-runtime-regression: + dataset: SAT11-HAND-runtime-regression + metric: rmse + user_requirement: "This is a SAT11-HAND-runtime-regression dataset. Your goal\ + \ is to predict the target column `runtime`.\nPerform data analysis, data preprocessing,\ + \ feature engineering, and modeling to predict the target. \nReport rmse on\ + \ the eval data. Do not plot or make any visualizations.\n" + + boston: + dataset: boston + metric: rmse + user_requirement: "This is a boston dataset. Your goal is to predict the target\ + \ column `MEDV`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ + \ plot or make any visualizations.\n" + + colleges: + dataset: colleges + metric: rmse + user_requirement: "This is a colleges dataset. Your goal is to predict the target\ + \ column `percent_pell_grant`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport rmse on the eval\ + \ data. Do not plot or make any visualizations.\n" + + credit-g: + dataset: credit-g + metric: f1 + user_requirement: "This is a credit-g dataset. Your goal is to predict the target\ + \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ + \ or make any visualizations.\n" + + diamonds: + dataset: diamonds + metric: rmse + user_requirement: "This is a diamonds dataset. Your goal is to predict the target\ + \ column `price`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ + \ plot or make any visualizations.\n" + + jasmine: + dataset: jasmine + metric: f1 + user_requirement: "This is a jasmine dataset. Your goal is to predict the target\ + \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ + \ or make any visualizations.\n" + + kc1: + dataset: kc1 + metric: f1 + user_requirement: "This is a kc1 dataset. Your goal is to predict the target column\ + \ `defects`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ + \ or make any visualizations.\n" + + kick: + dataset: kick + metric: f1 + user_requirement: "This is a kick dataset. Your goal is to predict the target\ + \ column `IsBadBuy`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ + \ or make any visualizations.\n" + + mfeat-factors: + dataset: mfeat-factors + metric: f1 weighted + user_requirement: "This is a mfeat-factors dataset. Your goal is to predict the\ + \ target column `class`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ + \ eval data. Do not plot or make any visualizations.\n" + + segment: + dataset: segment + metric: f1 weighted + user_requirement: "This is a segment dataset. Your goal is to predict the target\ + \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 weighted on the eval data.\ + \ Do not plot or make any visualizations.\n" + + steel-plates-fault: + dataset: steel-plates-fault + metric: f1 weighted + user_requirement: "This is a steel-plates-fault dataset. Your goal is to predict\ + \ the target column `target`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ + \ eval data. Do not plot or make any visualizations.\n" + + wine-quality-white: + dataset: wine-quality-white + metric: f1 weighted + user_requirement: "This is a wine-quality-white dataset. Your goal is to predict\ + \ the target column `Class`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ + \ eval data. Do not plot or make any visualizations.\n" + + +work_dir: D:/work/MG-open/MetaGPT/workspace # path to the workspace directory +role_dir: storage/team/environment/roles/ResearchAssistant_David +# analysis_pool_dir: D:/work/MG-open/MetaGPT/examples/MCTS_test/analysis_pool_sample.json \ No newline at end of file diff --git a/expo/dataset.py b/expo/dataset.py new file mode 100644 index 000000000..4bce6e9fe --- /dev/null +++ b/expo/dataset.py @@ -0,0 +1,262 @@ +import openml +from pathlib import Path +from sklearn.model_selection import train_test_split +import os +import json +import yaml +import pandas as pd +from examples.MCTS_test.insights.solution_designer import SolutionDesigner +import asyncio + +BASE_USER_REQUIREMENT = """\ +This is a {datasetname} dataset. Your goal is to predict the target column `{target_col}`. +Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. +Report {metric} on the eval data. Do not plot or make any visualizations. +""" + +SEED = 100 +TRAIN_TEST_SPLIT = 0.8 +TRAIN_DEV_SPLIT = 0.75 + +OPENML_DATASET_IDS = [ + # reg + 41021, + 42727, + 41980, + 42225, + 531, + + # cls + 41143, + 31, + 42733, + 41162, + 1067, + + # multi cls + 40498, + 40982, + 12, + 40984, + 4538, +] + +CUSTOM_DATASETS = [ + ("04_titanic", "Survived"), + ("05_house-prices-advanced-regression-techniques", "SalePrice"), + ("06_santander-customer-transaction-prediction", "target"), + ("07_icr-identify-age-related-conditions", "Class") +] + +def get_split_dataset_path(dataset_name, config): + datasets_dir = config['datasets_dir'] + if dataset_name in config['datasets']: + dataset = config['datasets'][dataset_name] + data_path = os.path.join(datasets_dir, dataset['dataset']) + split_datasets = { + "train": os.path.join(data_path, "split_train.csv"), + "dev": os.path.join(data_path, "split_dev.csv"), + "dev_wo_target": os.path.join(data_path, "split_dev_wo_target.csv"), + "dev_target": os.path.join(data_path, "split_dev_target.csv"), + "test": os.path.join(data_path, "split_test.csv"), + "test_wo_target": os.path.join(data_path, "split_test_wo_target.csv"), + "test_target": os.path.join(data_path, "split_test_target.csv"), + } + return split_datasets + else: + raise ValueError(f"Dataset {dataset_name} not found in config file. Available datasets: {config['datasets'].keys()}") + +def get_user_requirement(task_name, config): + datasets_dir = config['datasets_dir'] + if task_name in config['datasets']: + dataset = config['datasets'][task_name] + data_path = os.path.join(datasets_dir, dataset['dataset']) + user_requirement = dataset['user_requirement'] + return data_path, user_requirement + else: + raise ValueError(f"Dataset {task_name} not found in config file. Available datasets: {config['datasets'].keys()}") + + +def save_datasets_dict_to_yaml(datasets_dict): + with open("datasets.yaml", "w") as file: + yaml.dump(datasets_dict, file) + +def create_dataset_dict(dataset): + dataset_dict = { + "dataset": dataset.name, + "user_requirement": dataset.create_base_requirement(), + "metric": dataset.get_metric() + } + return dataset_dict + +class ExpDataset: + description : str = None + metadata : dict = None + dataset_dir : str = None + target_col : str = None + name : str = None + + def __init__(self, name, dataset_dir, **kwargs): + self.name = name + self.dataset_dir = dataset_dir + self.target_col = kwargs.get("target_col", None) + self.force_update = kwargs.get("force_update", False) + self.save_dataset(target_col=self.target_col) + + def check_dataset_exists(self): + fnames = ["split_train.csv", "split_dev.csv", "split_test.csv", + "split_dev_wo_target.csv", "split_dev_target.csv", + "split_test_wo_target.csv", "split_test_target.csv"] + for fname in fnames: + if not os.path.exists(Path(self.dataset_dir, self.name, fname)): + return False + return True + + def check_datasetinfo_exists(self): + return os.path.exists(Path(self.dataset_dir, self.name, "dataset_info.json")) + + + def get_raw_dataset(self): + raw_dir = Path(self.dataset_dir, self.name, "raw") + if not os.path.exists(Path(raw_dir, "train.csv")): + raise FileNotFoundError(f"Raw dataset `train.csv` not found in {raw_dir}") + else: + df = pd.read_csv(Path(raw_dir, "train.csv")) + return df + + def get_dataset_info(self): + raw_df = pd.read_csv(Path(self.dataset_dir, self.name, "raw", "train.csv")) + metadata = { + 'NumberOfClasses': raw_df[self.target_col].nunique(), + 'NumberOfFeatures': raw_df.shape[1], + 'NumberOfInstances': raw_df.shape[0], + 'NumberOfInstancesWithMissingValues': int(raw_df.isnull().any(axis=1).sum()), + 'NumberOfMissingValues': int(raw_df.isnull().sum().sum()), + 'NumberOfNumericFeatures': raw_df.select_dtypes(include=['number']).shape[1], + 'NumberOfSymbolicFeatures': raw_df.select_dtypes(include=['object']).shape[1], + } + + df_head_text = raw_df.head().to_string(index=False) + + dataset_info = { + "name": self.name, + "description": "", + "target_col": self.target_col, + "metadata": metadata, + "df_head": df_head_text + } + return dataset_info + + def get_metric(self): + dataset_info = self.get_dataset_info() + num_classes = dataset_info["metadata"]["NumberOfClasses"] + if num_classes == 2: + metric = "f1" + elif 2 < num_classes <= 200: + metric = "f1 weighted" + elif num_classes > 200 or num_classes == 0: + metric = "rmse" + else: + raise ValueError(f"Number of classes {num_classes} not supported") + return metric + + def create_base_requirement(self): + metric = self.get_metric() + req = BASE_USER_REQUIREMENT.format(datasetname=self.name, target_col=self.target_col, metric=metric) + return req + + def save_dataset(self, target_col): + + df = self.get_raw_dataset() + if not self.check_dataset_exists() or self.force_update: + print(f"Saving Dataset {self.name} in {self.dataset_dir}") + self.split_and_save(df, target_col) + else: + print(f"Dataset {self.name} already exists") + if not self.check_datasetinfo_exists() or self.force_update: + print(f"Saving Dataset info for {self.name}") + dataset_info = self.get_dataset_info() + self.save_datasetinfo(dataset_info) + else: + print(f"Dataset info for {self.name} already exists") + + def save_datasetinfo(self, dataset_info): + with open(Path(self.dataset_dir, self.name, "dataset_info.json"), "w") as file: + json.dump(dataset_info, file, indent=4) + + def save_split_datasets(self, df, split, target_col=None): + path = Path(self.dataset_dir, self.name) + df.to_csv(Path(path, f"split_{split}.csv"), index=False) + if target_col: + df_wo_target = df.drop(columns=[target_col]) + df_wo_target.to_csv(Path(path, f"split_{split}_wo_target.csv"), index=False) + df_target = df[[target_col]].copy() + if target_col != "target": + df_target["target"] = df_target[target_col] + df_target = df_target.drop(columns=[target_col]) + df_target.to_csv(Path(path, f"split_{split}_target.csv"), index=False) + + def split_and_save(self, df, target_col): + if not target_col: + raise ValueError("Target column not provided") + train, test = train_test_split(df, test_size=1-TRAIN_TEST_SPLIT, random_state=SEED) + train, dev = train_test_split(train, test_size=1-TRAIN_DEV_SPLIT, random_state=SEED) + self.save_split_datasets(train, "train") + self.save_split_datasets(dev, "dev", target_col) + self.save_split_datasets(test, "test", target_col) + + + +class OpenMLExpDataset(ExpDataset): + def __init__(self, name, dataset_dir, dataset_id, **kwargs): + self.dataset_id = dataset_id + self.dataset = openml.datasets.get_dataset(self.dataset_id, + download_data=False, + download_qualities=False, + download_features_meta_data=True) + self.name = self.dataset.name + self.target_col = self.dataset.default_target_attribute + super().__init__(self.name, dataset_dir, target_col=self.target_col, **kwargs) + + + def get_raw_dataset(self): + dataset = self.dataset + dataset_df, *_ = dataset.get_data() + raw_dir = Path(self.dataset_dir, self.name, "raw") + os.makedirs(raw_dir, exist_ok=True) + dataset_df.to_csv(Path(raw_dir, "train.csv"), index=False) + return dataset_df + + def get_dataset_info(self): + dataset_info = super().get_dataset_info() + dataset = self.dataset + dataset_info["name"] = dataset.name + dataset_info["description"] = dataset.description + dataset_info["metadata"].update(dataset.qualities) + return dataset_info + + +# class HFExpDataset(ExpDataset): +# def __init__(self, name, dataset_dir, dataset_name, **kwargs): +# super().__init__(name, dataset_dir, **kwargs) + + + +if __name__ == "__main__": + datasets_dir = "D:/work/automl/datasets" + force_update = True + datasets_dict = {"datasets": {}} + solution_designer = SolutionDesigner() + for dataset_id in OPENML_DATASET_IDS: + openml_dataset = OpenMLExpDataset("", datasets_dir, dataset_id, force_update=force_update) + asyncio.run(solution_designer.generate_solutions(openml_dataset.get_dataset_info(), openml_dataset.name)) + dataset_dict = create_dataset_dict(openml_dataset) + datasets_dict["datasets"][openml_dataset.name] = dataset_dict + + for dataset_name, target_col in CUSTOM_DATASETS: + custom_dataset = ExpDataset(dataset_name, datasets_dir, target_col=target_col, force_update=force_update) + asyncio.run(solution_designer.generate_solutions(custom_dataset.get_dataset_info(), custom_dataset.name)) + dataset_dict = create_dataset_dict(custom_dataset) + datasets_dict["datasets"][custom_dataset.name] = dataset_dict + + save_datasets_dict_to_yaml(datasets_dict) diff --git a/expo/datasets.yaml b/expo/datasets.yaml new file mode 100644 index 000000000..ec00e3d1f --- /dev/null +++ b/expo/datasets.yaml @@ -0,0 +1,134 @@ +datasets: + 04_titanic: + dataset: 04_titanic + metric: f1 + user_requirement: "This is a 04_titanic dataset. Your goal is to predict the target\ + \ column `Survived`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ + \ or make any visualizations.\n" + 05_house-prices-advanced-regression-techniques: + dataset: 05_house-prices-advanced-regression-techniques + metric: rmse + user_requirement: "This is a 05_house-prices-advanced-regression-techniques dataset.\ + \ Your goal is to predict the target column `SalePrice`.\nPerform data analysis,\ + \ data preprocessing, feature engineering, and modeling to predict the target.\ + \ \nReport rmse on the eval data. Do not plot or make any visualizations.\n" + 06_santander-customer-transaction-prediction: + dataset: 06_santander-customer-transaction-prediction + metric: f1 + user_requirement: "This is a 06_santander-customer-transaction-prediction dataset.\ + \ Your goal is to predict the target column `target`.\nPerform data analysis,\ + \ data preprocessing, feature engineering, and modeling to predict the target.\ + \ \nReport f1 on the eval data. Do not plot or make any visualizations.\n" + 07_icr-identify-age-related-conditions: + dataset: 07_icr-identify-age-related-conditions + metric: f1 + user_requirement: "This is a 07_icr-identify-age-related-conditions dataset. Your\ + \ goal is to predict the target column `Class`.\nPerform data analysis, data\ + \ preprocessing, feature engineering, and modeling to predict the target. \n\ + Report f1 on the eval data. Do not plot or make any visualizations.\n" + Click_prediction_small: + dataset: Click_prediction_small + metric: f1 + user_requirement: "This is a Click_prediction_small dataset. Your goal is to predict\ + \ the target column `click`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 on the eval data.\ + \ Do not plot or make any visualizations.\n" + GesturePhaseSegmentationProcessed: + dataset: GesturePhaseSegmentationProcessed + metric: f1 weighted + user_requirement: "This is a GesturePhaseSegmentationProcessed dataset. Your goal\ + \ is to predict the target column `Phase`.\nPerform data analysis, data preprocessing,\ + \ feature engineering, and modeling to predict the target. \nReport f1 weighted\ + \ on the eval data. Do not plot or make any visualizations.\n" + Moneyball: + dataset: Moneyball + metric: rmse + user_requirement: "This is a Moneyball dataset. Your goal is to predict the target\ + \ column `RS`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ + \ plot or make any visualizations.\n" + SAT11-HAND-runtime-regression: + dataset: SAT11-HAND-runtime-regression + metric: rmse + user_requirement: "This is a SAT11-HAND-runtime-regression dataset. Your goal\ + \ is to predict the target column `runtime`.\nPerform data analysis, data preprocessing,\ + \ feature engineering, and modeling to predict the target. \nReport rmse on\ + \ the eval data. Do not plot or make any visualizations.\n" + boston: + dataset: boston + metric: rmse + user_requirement: "This is a boston dataset. Your goal is to predict the target\ + \ column `MEDV`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ + \ plot or make any visualizations.\n" + colleges: + dataset: colleges + metric: rmse + user_requirement: "This is a colleges dataset. Your goal is to predict the target\ + \ column `percent_pell_grant`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport rmse on the eval\ + \ data. Do not plot or make any visualizations.\n" + credit-g: + dataset: credit-g + metric: f1 + user_requirement: "This is a credit-g dataset. Your goal is to predict the target\ + \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ + \ or make any visualizations.\n" + diamonds: + dataset: diamonds + metric: rmse + user_requirement: "This is a diamonds dataset. Your goal is to predict the target\ + \ column `price`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ + \ plot or make any visualizations.\n" + jasmine: + dataset: jasmine + metric: f1 + user_requirement: "This is a jasmine dataset. Your goal is to predict the target\ + \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ + \ or make any visualizations.\n" + kc1: + dataset: kc1 + metric: f1 + user_requirement: "This is a kc1 dataset. Your goal is to predict the target column\ + \ `defects`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ + \ or make any visualizations.\n" + kick: + dataset: kick + metric: f1 + user_requirement: "This is a kick dataset. Your goal is to predict the target\ + \ column `IsBadBuy`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ + \ or make any visualizations.\n" + mfeat-factors: + dataset: mfeat-factors + metric: f1 weighted + user_requirement: "This is a mfeat-factors dataset. Your goal is to predict the\ + \ target column `class`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ + \ eval data. Do not plot or make any visualizations.\n" + segment: + dataset: segment + metric: f1 weighted + user_requirement: "This is a segment dataset. Your goal is to predict the target\ + \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 weighted on the eval data.\ + \ Do not plot or make any visualizations.\n" + steel-plates-fault: + dataset: steel-plates-fault + metric: f1 weighted + user_requirement: "This is a steel-plates-fault dataset. Your goal is to predict\ + \ the target column `target`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ + \ eval data. Do not plot or make any visualizations.\n" + wine-quality-white: + dataset: wine-quality-white + metric: f1 weighted + user_requirement: "This is a wine-quality-white dataset. Your goal is to predict\ + \ the target column `Class`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ + \ eval data. Do not plot or make any visualizations.\n" diff --git a/expo/evaluation/evaluation.py b/expo/evaluation/evaluation.py new file mode 100644 index 000000000..20a35aa27 --- /dev/null +++ b/expo/evaluation/evaluation.py @@ -0,0 +1,23 @@ +from sklearn.metrics import f1_score, accuracy_score, roc_auc_score, mean_squared_error +import numpy as np + +def evaluate_score(pred, gt, metric): + if metric == "accuracy": + return accuracy_score(gt, pred) + elif metric == "f1": + unique_classes = np.unique(gt) + if 1 in unique_classes and 0 in unique_classes: + pos_label = 1 + else: + pos_label = unique_classes[0] if len(unique_classes) == 2 else None + return f1_score(gt, pred, pos_label=pos_label) + elif metric == "f1 weighted": + return f1_score(gt, pred, average="weighted") + elif metric == "roc_auc": + return roc_auc_score(gt, pred) + elif metric == "rmse": + return mean_squared_error(gt, pred, squared=False) + elif metric == "log rmse": + return mean_squared_error(np.log1p(gt), np.log1p(pred), squared=False) + else: + raise ValueError(f"Metric {metric} not supported") \ No newline at end of file diff --git a/expo/evaluation/visualize_mcts.py b/expo/evaluation/visualize_mcts.py new file mode 100644 index 000000000..6e38576e2 --- /dev/null +++ b/expo/evaluation/visualize_mcts.py @@ -0,0 +1,54 @@ + +from expo.MCTS import Node, MCTS +import textwrap + +NODE_TEMPLATE = """\ +[Node {id}] +Plans: +{plans} +Simulated: {simulated} +Score: {score}, Visits: {num_visits} + +""" + +def get_role_plans(role): + plans = role.planner.plan.tasks + instruct_plans = [f"{i+1}. {task.instruction}" for i, task in enumerate(plans)] + return instruct_plans + + +def get_tree_text(node : Node): + role_dict = {} + code_set = set() + def load_role(node): + if node.id not in role_dict: + role_dict[node.id] = node.load_role() + return role_dict[node.id] + + def visualize_node(node : Node, previous_plans=None): + role = load_role(node) + node_id = node.id + plans = role.planner.plan.tasks + instruct_plans = [f"{i+1}. {task.instruction}" for i, task in enumerate(plans)] + if previous_plans is not None: + instruct_plans = [plan for plan, prev_plan in zip(instruct_plans, previous_plans) if plan != prev_plan] + instruct_plans_text = "\n".join(instruct_plans) + simulated = role.state_saved + score = f"avg score: {node.avg_value()}, simulated score: {node.raw_reward}" + num_visits = node.visited + return NODE_TEMPLATE.format(id=node_id, plans=instruct_plans_text, simulated=simulated, score=score, num_visits=num_visits) + + def visualize_tree(node, depth=0, previous_plans=None): + text = "" + if node is not None: + text += visualize_node(node, previous_plans) + role = load_role(node) + code_set.update({task.instruction for task in role.planner.plan.tasks}) + previous_plans = get_role_plans(role) + for child in node.children: + text += textwrap.indent(visualize_tree(child, depth+1, previous_plans), "\t") + return text + + return visualize_tree(node), len(code_set) + + diff --git a/expo/experimenter/aug_experimenter.py b/expo/experimenter/aug_experimenter.py new file mode 100644 index 000000000..e69de29bb diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py new file mode 100644 index 000000000..d7ed82070 --- /dev/null +++ b/expo/experimenter/experimenter.py @@ -0,0 +1,18 @@ + +class Experimenter: + result_path : str = "results" + + async def run_experiment(self): + pass + + + def save_scores(self): + pass + + def save_result(self): + results = { + "test_score": self.test_score, + "num_experiments": self.num_experiments, + "insights": self.insights, + "avg_score": self.avg_score, + } \ No newline at end of file diff --git a/expo/experimenter/mcts_experimenter.py b/expo/experimenter/mcts_experimenter.py new file mode 100644 index 000000000..e69de29bb diff --git a/expo/insights/InsightGenerate.py b/expo/insights/InsightGenerate.py new file mode 100644 index 000000000..de58b7e4e --- /dev/null +++ b/expo/insights/InsightGenerate.py @@ -0,0 +1,114 @@ +REFLECTION_SYSTEM_MSG = "As a Kaggle grandmaster participating in a competition, you need to analyze your experience and propose evolutionary points that are more likely to improve the performance of baseline code." + +CHANGE_INSTRUCTION = """ +# Original instruction +{instruction} + +# Insights +{insights} + +Rewrite the original instruction according to the insights + +# Expected Output Hard Format +```json +{{ + "Original Instruction": "original instruction", + "New Instruction": "new instruction" +}} +``` +""" + +import re +import random +import json +from metagpt.llm import LLM +from metagpt.schema import Message +from examples.MCTS_test.utils import load_data_config, mcts_logger +DATA_CONFIG = load_data_config() + + +class InsightGenerator: + data_config = DATA_CONFIG + + @staticmethod + def load_json_data(json_dir): + with open(json_dir, "r") as file: + json_data = json.load(file) + return json_data + + @staticmethod + def _random_sample(analysis, num_samples): + return random.sample(analysis, num_samples) + + @staticmethod + def sample_instruction_set(data): + data_dict = {} + for item in data: + task_id = item["task_id"] + if task_id not in data_dict: + data_dict[task_id] = [] + data_dict[task_id].append(item) + instruction_set = [] + for task_id in sorted(data_dict.keys()): + instruction_set.append(random.choice(data_dict[task_id])) + return instruction_set + + + @staticmethod + def clean_json_from_rsp(text): + pattern = r"```json(.*?)```" + matches = re.findall(pattern, text, re.DOTALL) + if matches: + json_str = "\n".join(matches) + return json_str + else: + return "" + + @staticmethod + def format_output(rsp): + rsp_list = [] + new_data = [] + rsp_list.append(rsp) + for item in rsp_list: + item_dict = json.loads(item) + data = { + "Insights": item_dict, + } + new_data.append(data) + return new_data + + @staticmethod + def load_analysis_pool(file_path, task_id=None): + data = InsightGenerator.load_json_data(file_path) + for item in data: + if "task_id" not in item: + raise ValueError("task_id is not found in the analysis pool") + + if task_id: + data = [item for item in data if int(item["task_id"]) == int(task_id)] + return data + + @staticmethod + async def generate_new_instructions(task_id, original_instruction, max_num, file_path): + data = InsightGenerator.load_analysis_pool(file_path, task_id) + new_instructions = [] + if len(data) == 0: + mcts_logger.log("MCTS", f"No insights available for task {task_id}") + return [original_instruction] # Return the original instruction if no insights are available + for item in data[:max_num]: + insights = item["Analysis"] + new_instruction = await InsightGenerator.generate_new_instruction(original_instruction, insights) + new_instructions.append(new_instruction) + return new_instructions + + @staticmethod + async def generate_new_instruction(original_instruction, insights): + prompt = CHANGE_INSTRUCTION.format(instruction=original_instruction, insights=insights) + llm = LLM() + context = llm.format_msg([Message(content=prompt, role="user")]) + llm_response = await llm.aask( + context, system_msgs=[REFLECTION_SYSTEM_MSG] + ) + rsp = InsightGenerator.clean_json_from_rsp(llm_response) + new_instruction = json.loads(rsp)["New Instruction"] + return new_instruction \ No newline at end of file diff --git a/expo/insights/solution_designer.py b/expo/insights/solution_designer.py new file mode 100644 index 000000000..0986c392a --- /dev/null +++ b/expo/insights/solution_designer.py @@ -0,0 +1,127 @@ +import re +import random +import json +from metagpt.llm import LLM +from metagpt.schema import Message +from examples.MCTS_test.utils import clean_json_from_rsp, load_data_config + + +DATA_CONFIG = load_data_config() + +DATASET_INSIGHT_PROMPT = """ +# Dataset Description +{dataset} + +# Dataset Metadata +{metadata} + +# Dataset Head +{head} + +# Instruction +Propose insights to help improve the performance of the model on this dataset. +The insights should be proposed based on the dataset description with different task types. +Each task type should have at least 5 insights. +Make sure each method is independent and can be implemented separately. + +# Format +```json +[ + {{ + "task_type": "EDA", + "insights": [ + "insight1", + "insight2", + "insight3", + ... + "insightN" + ] + }}, + {{ + "task_type": "Data Preprocessing", + "insights": [ + "insight1", + "insight2", + "insight3", + ... + "insightN" + ] + }}, + {{ + "task_type": "Feature Engineering", + "insights": [ + "insight1", + "insight2", + "insight3", + ... + "insightN" + ] + }}, + {{ + "task_type": "Model Training", + "insights": [ + "insight1", + "insight2", + "insight3", + ... + "insightN" + ] + }} +] +``` +""" + +KEY_DATASET_FEATURES = [ + 'NumberOfClasses', + 'NumberOfFeatures', + 'NumberOfInstances', + 'NumberOfInstancesWithMissingValues', + 'NumberOfMissingValues', + 'NumberOfNumericFeatures', + 'NumberOfSymbolicFeatures' +] + +TASK_TO_ID = { + "EDA": 1, + "Data Preprocessing": 2, + "Feature Engineering": 3, + "Model Training": 4, + "Model Evaluation": 5 +} + +class SolutionDesigner: + data_dir : str= DATA_CONFIG["datasets_dir"] + + async def generate_solutions(self, dataset_info, dataset_name): + llm = LLM() + context = DATASET_INSIGHT_PROMPT.format(dataset=dataset_info["description"], + metadata=self.metadata_builder(dataset_info["metadata"]), + head=dataset_info["df_head"]) + rsp = await llm.aask(context) + rsp = clean_json_from_rsp(rsp) + analysis_pool = self.process_analysis_pool(json.loads(rsp)) + dataset_path = f"{self.data_dir}/{dataset_name}" + self.save_analysis_pool(dataset_path, analysis_pool) + + + def process_analysis_pool(self, insights_rsp): + analysis_pool = [] + for task_type_insights in insights_rsp: + task_type = task_type_insights["task_type"] + for insight in task_type_insights["insights"]: + analysis_pool.append({"Analysis": insight, "Category": task_type, "task_id": TASK_TO_ID[task_type]}) + return analysis_pool + + + def metadata_builder(self, qualities): + metadata = {} + for key in KEY_DATASET_FEATURES: + metadata[key] = qualities.get(key, "N/A") + metadata_text = json.dumps(metadata, indent=4) + return metadata_text + + def save_analysis_pool(self, dataset_path, analysis_pool): + fpath = f"{dataset_path}/ds_analysis_pool.json" + with open(fpath, "w") as file: + json.dump(analysis_pool, file, indent=4) + \ No newline at end of file diff --git a/expo/research_assistant.py b/expo/research_assistant.py new file mode 100644 index 000000000..fbd74f7db --- /dev/null +++ b/expo/research_assistant.py @@ -0,0 +1,141 @@ +from __future__ import annotations + +import json +from metagpt.roles.di.data_interpreter import DataInterpreter +from metagpt.schema import Message, Task, TaskResult +from metagpt.strategy.task_type import TaskType +from metagpt.tools.tool_recommend import BM25ToolRecommender, ToolRecommender +from metagpt.utils.common import CodeParser +from metagpt.utils.common import write_json_file, read_json_file, format_trackback_info +from metagpt.const import MESSAGE_ROUTE_TO_ALL, SERDESER_PATH +from metagpt.utils.recovery_util import save_history +from expo.utils import mcts_logger, save_notebook +import re +import os + +EXTRACT_SCORE_PROMPT = """ +# Code: +{code} + +# Execution Result: +{result} + +# Instruction: +Based on the code and execution result, please extract the scores and return it as a dictionary. +If you cannot find the scores, please still return a dictionary with the keys 'train_score', 'dev_score', and 'test_score', and set the values to -1. + +# Format: +```json +{{ + "train_score": x.x, + "dev_score": x.x, + "test_score": x.x, +}} +``` +""" + +class ResearchAssistant(DataInterpreter): + node_id: str = "0" + start_task_id: int = 1 + state_saved : bool = False + role_dir : str = SERDESER_PATH.joinpath("team", "environment", "roles", f"Experimenter") + + def get_node_name(self): + return f"Node-{self.node_id}" + + def get_next_instruction(self): + return self.planner.plan.tasks[self.start_task_id] + + def change_next_instruction(self, new_instruction): + if new_instruction is not None: + self.planner.plan.task_map[str(self.start_task_id)].instruction = new_instruction + self.remap_tasks() + + + def update_til_start_task(self, role: ResearchAssistant, backward: bool = True): + if backward: + # make sure the previous task instructions are matched + assert self.start_task_id == role.start_task_id - 1, f"start_task_id: {self.start_task_id}, role.start_task_id: {role.start_task_id}" + for i in range(self.start_task_id): + if self.planner.plan.task_map[str(self.start_task_id)].instruction != role.planner.plan.task_map[str(self.start_task_id)].instruction: + mcts_logger.info("Previous task instructions not matched") + self.remap_tasks() + return + # copy new role's task (self.start_task_id) to current role + self.planner.plan.task_map[str(self.start_task_id)] = role.planner.plan.task_map[str(self.start_task_id)].model_copy() + self.remap_tasks() + + else: + assert self.start_task_id == role.start_task_id + 1, f"start_task_id: {self.start_task_id}, role.start_task_id: {role.start_task_id}" + if int(role.planner.plan.current_task_id) > self.start_task_id: + for i in range(role.start_task_id): + self.planner.plan.task_map[str(i)] = role.planner.plan.task_map[str(i)].model_copy() + self.remap_tasks() + + async def get_score(self): + score_dict = await self.llm_extract_score() + score_dict["score"] = score_dict["dev_score"] + return score_dict + + async def llm_extract_score(self): + result_text = self.planner.plan.task_map[str(len(self.planner.plan.task_map))].result + code_text = self.planner.plan.task_map[str(len(self.planner.plan.task_map))].code + rsp = await self.llm.aask(EXTRACT_SCORE_PROMPT.format(code=code_text, result=result_text, role="user")) + json_block = CodeParser.parse_code(block=None, text=rsp) + score_dict = json.loads(json_block) + return score_dict + + async def _act_on_task(self, current_task: Task) -> TaskResult: + """Useful in 'plan_and_act' mode. Wrap the output in a TaskResult for review and confirmation.""" + mcts_logger.info(f"The current_task is: {current_task}") + + # 执行任务的代码 + code, result, is_success = await self._write_and_exec_code() + task_result = TaskResult(code=code, result=result, is_success=is_success) + # 只在任务类型为 'feature engineering' 时保存状态 + if int(current_task.task_id) == self.start_task_id + 1: + # fe_id = current_task.dependent_task_ids + self.save_state() + save_notebook(role=self, save_dir=self.role_dir, name=self.get_node_name()) + return task_result + + def save_state(self, static_save=False): + if self.state_saved and not static_save: + return + if not static_save: + self.state_saved = True + mcts_logger.log("MCTS", f"Saving state at task {self.start_task_id}") + else: + mcts_logger.log("MCTS", f"Static Saving") + stg_path = self.role_dir + name = self.get_node_name() + role_path = os.path.join(stg_path, f"{name}.json") + # 将状态保存为 JSON 文件 + write_json_file(role_path, self.model_dump()) + save_history(role=self, save_dir=stg_path, name=name) + + + def remap_tasks(self): + self.planner.plan.tasks = [self.planner.plan.task_map[task_id] for task_id in sorted(self.planner.plan.task_map.keys())] + + + async def run(self, with_message=None) -> Message | None: + """Observe, and think and act based on the results of the observation""" + if with_message == "continue": + # self.set_todo(None) + # working_memory = self.working_memory + # self.remap_tasks() + mcts_logger.info("Continue to run") + self.rc.working_memory.clear() + self.working_memory.clear() + # self.rc.todo = WriteAnalysisCode() + rsp = await self.react() + # 发送响应消息给 Environment 对象,以便它将消息传递给订阅者 + self.set_todo(None) + self.publish_message(rsp) + return rsp + return await super().run(with_message) + + + + \ No newline at end of file diff --git a/expo/results/PLACEHOLDER b/expo/results/PLACEHOLDER new file mode 100644 index 000000000..e69de29bb diff --git a/expo/results/tree/TREE b/expo/results/tree/TREE new file mode 100644 index 000000000..e69de29bb diff --git a/expo/run_exp_augmentation.py b/expo/run_exp_augmentation.py new file mode 100644 index 000000000..492a424d4 --- /dev/null +++ b/expo/run_exp_augmentation.py @@ -0,0 +1,96 @@ +import os +from metagpt.roles.di.research_assistant import ResearchAssistant +import asyncio +from examples.MCTS_test.utils import DATA_CONFIG, generate_task_requirement, get_exp_pool_path +from examples.MCTS_test.insights.InsightGenerate import InsightGenerator +from examples.MCTS_test.MCTS import create_initial_state +from examples.MCTS_test.evaluation.evaluation import evaluate_score +import json +import argparse +import pandas as pd +import datetime + +EXPS_PROMPT = """ +When doing the tasks, you can refer to the insights below: +{experience} + +""" +data_config = DATA_CONFIG + +def evaluate_test(score, state): + datetime_text = datetime.datetime.now().strftime("%Y%m%d%H%M") + task_name = state["task"] + prediction_fpath = os.path.join(state["work_dir"], task_name, "predictions.csv") + predictions = pd.read_csv(prediction_fpath)["target"] + # copy predictions.csv to the node_dir + + predictions_node_fpath = os.path.join("results", f"{task_name}-{datetime_text}-predictions.csv") + predictions.to_csv(predictions_node_fpath, index=False) + # load test_target.csv + split_datasets_dir = state["datasets_dir"] + gt = pd.read_csv(os.path.join(split_datasets_dir["test_target"]))["target"] + metric = state["dataset_config"]["metric"] + score["test_score"] = evaluate_score(predictions, gt, metric) + return score + + + + +async def main(task_name, use_reflection=True, mode="single", num_experiments=2): + """ + mode: single or set + single: sample one instruction + set: sample a set of instructions + """ + low_is_better = False + state = create_initial_state(task_name, start_task_id=1, data_config=data_config, low_is_better=low_is_better, name="") + + user_requirement = generate_task_requirement(task_name, data_config) + exp_pool_path = get_exp_pool_path(task_name, data_config, pool_name="ds_analysis_pool") + exp_pool = InsightGenerator.load_analysis_pool(exp_pool_path) + if mode == "single": + exps = InsightGenerator._random_sample(exp_pool, num_experiments) + exps = [exp["Analysis"] for exp in exps] + elif mode == "set": + exp_set = InsightGenerator.sample_instruction_set(exp_pool) + exp_set_text = "\n".join([f"{exp['task_id']}: {exp['Analysis']}" for exp in exp_set]) + exps = [exp_set_text] * num_experiments + else: + raise ValueError(f"Invalid mode: {mode}") + + scores = [] + for i in range(num_experiments): + di = ResearchAssistant(node_id=str(i), use_reflection=use_reflection) + di.role_dir = f"{di.role_dir}_{task_name}" + requirement = user_requirement + EXPS_PROMPT.format(experience=exps[i]) + print(requirement) + await di.run(requirement) + score = await di.get_score(low_is_better=False) + score = evaluate_test(score, state) + + scores.append(score) + + + with open(f"results/{task_name}_scores.json", "w") as f: + # save scores and corresponding insights + results = {"avg_score": sum([score["test_score"] for score in scores if score])/num_experiments, + "max_score": max([score["test_score"] for score in scores]), + "scores": scores, "insights": exps} + json.dump(results, f, indent=4) + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--task", type=str, default="titanic") + parser.add_argument("--use_reflection", dest="use_reflection", action="store_true") + parser.add_argument("--no_use_reflection", dest="use_reflection", action="store_false") + parser.set_defaults(use_reflection=True) + parser.add_argument("--mode", type=str, default="single") + parser.add_argument("--num_experiments", type=int, default=2) + return parser.parse_args() + + + +if __name__ == "__main__": + args = parse_args() + asyncio.run(main(args.task, use_reflection=args.use_reflection, mode=args.mode, num_experiments=args.num_experiments)) diff --git a/expo/run_experiment.py b/expo/run_experiment.py new file mode 100644 index 000000000..e75897f5a --- /dev/null +++ b/expo/run_experiment.py @@ -0,0 +1,44 @@ +from examples.MCTS_test.MCTS import MCTS, Node, initialize_di_root_node +from examples.MCTS_test.utils import load_data_config, generate_task_requirement +from examples.MCTS_test.visualize_mcts import get_tree_text +import asyncio +import argparse + + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--name", type=str, default="") + get_di_args(parser) + get_mcts_args(parser) + get_aug_exp_args(parser) + + + return parser.parse_args() + + +def get_mcts_args(parser): + parser.add_argument("--load_tree", dest="load_tree", action="store_true") + parser.add_argument("--no_load_tree", dest="load_tree", action="store_false") + parser.set_defaults(load_tree=True) + parser.add_argument("--rollout", type=int, default=3) + +def get_aug_exp_args(parser): + parser.add_argument("--aug_mode", type=str, default="single", choices=["single", "set"]) + parser.add_argument("--num_experiments", type=int, default=2) + + +def get_di_args(parser): + parser.add_argument("--task", type=str, default="titanic") + parser.add_argument("--low_is_better", dest="low_is_better", action="store_true") + parser.set_defaults(low_is_better=False) + parser.add_argument("--reflection", dest="reflection", action="store_true") + parser.add_argument("--no_reflection", dest="reflection", action="store_false") + parser.set_defaults(reflection=True) + + +async def main(args): + pass + +if __name__ == "__main__": + args = get_args() + asyncio.run(main(args)) \ No newline at end of file diff --git a/expo/run_mcts.py b/expo/run_mcts.py new file mode 100644 index 000000000..0c0c486db --- /dev/null +++ b/expo/run_mcts.py @@ -0,0 +1,48 @@ +from expo.MCTS import MCTS, Node, initialize_di_root_node +from expo.utils import load_data_config, generate_task_requirement +from expo.evaluation.visualize_mcts import get_tree_text +import asyncio +import argparse + + +def get_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--task", type=str, default="titanic") + parser.add_argument("--low_is_better", dest="low_is_better", action="store_true") + parser.set_defaults(low_is_better=False) + parser.add_argument("--load_tree", dest="load_tree", action="store_true") + parser.add_argument("--no_load_tree", dest="load_tree", action="store_false") + parser.set_defaults(load_tree=True) + parser.add_argument("--reflection", dest="reflection", action="store_true") + parser.add_argument("--no_reflection", dest="reflection", action="store_false") + parser.set_defaults(reflection=True) + parser.add_argument("--rollout", type=int, default=3) + parser.add_argument("--name", type=str, default="") + return parser.parse_args() + + +data_config = load_data_config() + +if __name__ == "__main__": + args = get_args() + requirement = generate_task_requirement(args.task, data_config) + print(requirement) + + # role, root_node = initialize_di_root_node(requirement, data_config) + # asyncio.run(role.run(requirement)) + + # asyncio.run(root_node.run_node()) + mcts = MCTS(root_node=None, max_depth=5) + best_node = asyncio.run(mcts.search(args.task, data_config, + low_is_better=args.low_is_better, load_tree=args.load_tree, + reflection=args.reflection, rollout=args.rollout, name=args.name)) + text, num_generated_codes = get_tree_text(mcts.root_node) + print(text) + print(f"Generated {num_generated_codes} unique codes.") + + with open(f"results/{args.task}_tree{args.name}.txt", "w") as f: + f.write(f"Generated {num_generated_codes} unique codes.\n") + f.write(f"Best node: {best_node}, score: {best_node.raw_reward}\n") + f.write(text) + + diff --git a/expo/utils.py b/expo/utils.py new file mode 100644 index 000000000..ac4a64697 --- /dev/null +++ b/expo/utils.py @@ -0,0 +1,150 @@ +import yaml +from examples.MCTS_test.dataset import get_user_requirement, get_split_dataset_path +from metagpt.roles.role import Role +from metagpt.actions.di.execute_nb_code import ExecuteNbCode +from metagpt.utils.save_code import save_code_file +# from nbclient import NotebookClient +from nbformat.notebooknode import NotebookNode +import nbformat +from pathlib import Path +from loguru import logger as _logger +from datetime import datetime +import sys +import os +import re + +TASK_PROMPT = """\ +# User requirement +{user_requirement} +**Attention** Please do not leak the target label in any form during training. + +## Saving Dev and Test Predictions +Save the prediction results of the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory BEFORE printig out the results. +The file should contain a single `target` column with the predicted values. +Make sure the prediction results are in the same format as the target column in the training set. The labels should be transformed back to the original format if any transformation was applied during training. + +## Output Training Set Performance +Make sure the performance of the model is printed in python in the last step even if it has been printed in the previous steps. The value should be a float number. +Print the training set performance in the last step. Write in this format: +```python +... +print("Train score:", train_score) +``` + +# Data dir +training: {train_path} +dev: {dev_path} +testing: {test_path} + +# Output dir +{output_dir} + +""" + +def load_data_config(file_path="data.yaml"): + with open(file_path, 'r') as stream: + data_config = yaml.safe_load(stream) + return data_config + +DATA_CONFIG = load_data_config() + +def get_mcts_logger(): + print_level = "INFO" + print_level2 = "MCTS" + logfile_level="MCTS" + name: str = None + current_date = datetime.now() + formatted_date = current_date.strftime("%Y%m%d") + log_name = f"{name}_{formatted_date}" if name else formatted_date # name a log with prefix name + + _logger.remove() + new_level = _logger.level(logfile_level, color="", no=25) + _logger.add(sys.stderr, level=print_level) + _logger.add(sys.stderr, level=print_level2) + _logger.add(Path(DATA_CONFIG["work_dir"]) / DATA_CONFIG["role_dir"] / f"{log_name}.txt", level=logfile_level) + _logger.propagate = False + return _logger + +mcts_logger = get_mcts_logger() + + +def get_exp_pool_path(task_name, data_config, pool_name="analysis_pool"): + datasets_dir = data_config['datasets_dir'] + if task_name in data_config['datasets']: + dataset = data_config['datasets'][task_name] + data_path = os.path.join(datasets_dir, dataset['dataset']) + else: + raise ValueError(f"Dataset {task_name} not found in config file. Available datasets: {data_config['datasets'].keys()}") + exp_pool_path = os.path.join(data_path, f"{pool_name}.json") + return exp_pool_path + +def generate_task_requirement(task_name, data_config): + user_requirement = get_user_requirement(task_name, data_config) + split_dataset_path = get_split_dataset_path(task_name, data_config) + train_path = split_dataset_path["train"] + dev_path = split_dataset_path["dev_wo_target"] + test_path = split_dataset_path["test_wo_target"] + work_dir = data_config["work_dir"] + output_dir = f"{work_dir}/{task_name}" + user_requirement = TASK_PROMPT.format(user_requirement=user_requirement, + train_path=train_path, dev_path=dev_path, test_path=test_path, + output_dir=output_dir) + return user_requirement + +def change_plan(role, plan): + print(f"Change next plan to: {plan}") + tasks = role.planner.plan.tasks + finished = True + for i, task in enumerate(tasks): + if not task.code: + finished = False + break + if not finished: + tasks[i].plan = plan + return finished + + + +def is_cell_to_delete(cell: NotebookNode) -> bool: + if "outputs" in cell: + for output in cell["outputs"]: + if output and "traceback" in output: + return True + return False + + +def process_cells(nb: NotebookNode) -> NotebookNode: + new_cells = [] + i = 1 + for cell in nb["cells"]: + if cell["cell_type"] == "code" and not is_cell_to_delete(cell): + cell["execution_count"] = i + new_cells.append(cell) + i = i + 1 + nb["cells"] = new_cells + return nb + +def save_notebook(role: Role, save_dir: str = "", name: str = ""): + save_dir = Path(save_dir) + nb = process_cells(role.execute_code.nb) + save_code_file(name=name, code_context=nb, file_format="ipynb", save_dir=save_dir) + +async def load_execute_notebook(role): + tasks = role.planner.plan.tasks + codes = [task.code for task in tasks if task.code] + executor = role.execute_code + # await executor.build() + for code in codes: + outputs, success = await executor.run(code) + print(f"Execution success: {success}, Output: {outputs}") + print("Finish executing the loaded notebook") + return executor + +def clean_json_from_rsp(text): + pattern = r"```json(.*?)```" + matches = re.findall(pattern, text, re.DOTALL) + if matches: + json_str = "\n".join(matches) + return json_str + else: + return "" \ No newline at end of file From fab1293cab8fda88c623441fd2158202b7d8d6d1 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 30 Aug 2024 16:26:56 +0800 Subject: [PATCH 002/160] ignore expo/results --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index aa5edd74a..3fc66cecb 100644 --- a/.gitignore +++ b/.gitignore @@ -188,3 +188,4 @@ cov.xml *-structure.json *.dot .python-version +expo/results/* From 211f758b5311278607b020f1dfb335bf054456eb Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 30 Aug 2024 16:58:31 +0800 Subject: [PATCH 003/160] add openml to requirement --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 8bf0ee399..7ea849f5a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -79,3 +79,4 @@ gymnasium==0.29.1 boto3~=1.34.69 spark_ai_python~=0.3.30 agentops +openml==0.14.2 From d14f07f9b193030cbfb90c35c006527e5d951234 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 30 Aug 2024 16:59:38 +0800 Subject: [PATCH 004/160] 1. change data.yaml to more generalized path 2. correct import --- expo/MCTS.py | 4 +-- expo/data.yaml | 2 +- expo/dataset.py | 45 +++++++++++++++++++++++++++++- expo/insights/InsightGenerate.py | 2 +- expo/insights/solution_designer.py | 2 +- expo/research_assistant.py | 14 ++++++++++ expo/run_exp_augmentation.py | 11 ++++---- expo/run_experiment.py | 7 +++-- expo/run_mcts.py | 4 ++- expo/utils.py | 41 --------------------------- 10 files changed, 76 insertions(+), 56 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 9026e09b4..af50ff7a0 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -4,9 +4,9 @@ import pandas as pd from expo.research_assistant import ResearchAssistant from expo.insights.InsightGenerate import InsightGenerator -from expo.dataset import get_split_dataset_path +from expo.dataset import get_split_dataset_path, generate_task_requirement from expo.evaluation.evaluation import evaluate_score -from expo.utils import mcts_logger, load_execute_notebook, generate_task_requirement, get_exp_pool_path +from expo.utils import mcts_logger, load_execute_notebook, get_exp_pool_path from metagpt.tools.tool_recommend import BM25ToolRecommender, ToolRecommender from metagpt.utils.common import write_json_file, read_json_file, format_trackback_info diff --git a/expo/data.yaml b/expo/data.yaml index d921e1ebf..df26e29e8 100644 --- a/expo/data.yaml +++ b/expo/data.yaml @@ -152,6 +152,6 @@ datasets: \ eval data. Do not plot or make any visualizations.\n" -work_dir: D:/work/MG-open/MetaGPT/workspace # path to the workspace directory +work_dir: ../workspace # path to the workspace directory role_dir: storage/team/environment/roles/ResearchAssistant_David # analysis_pool_dir: D:/work/MG-open/MetaGPT/examples/MCTS_test/analysis_pool_sample.json \ No newline at end of file diff --git a/expo/dataset.py b/expo/dataset.py index 4bce6e9fe..a507d0b7e 100644 --- a/expo/dataset.py +++ b/expo/dataset.py @@ -5,7 +5,7 @@ import json import yaml import pandas as pd -from examples.MCTS_test.insights.solution_designer import SolutionDesigner +from expo.insights.solution_designer import SolutionDesigner import asyncio BASE_USER_REQUIREMENT = """\ @@ -14,6 +14,35 @@ Report {metric} on the eval data. Do not plot or make any visualizations. """ +TASK_PROMPT = """\ +# User requirement +{user_requirement} +**Attention** Please do not leak the target label in any form during training. + +## Saving Dev and Test Predictions +Save the prediction results of the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory BEFORE printig out the results. +The file should contain a single `target` column with the predicted values. +Make sure the prediction results are in the same format as the target column in the training set. The labels should be transformed back to the original format if any transformation was applied during training. + +## Output Training Set Performance +Make sure the performance of the model is printed in python in the last step even if it has been printed in the previous steps. The value should be a float number. +Print the training set performance in the last step. Write in this format: +```python +... +print("Train score:", train_score) +``` + +# Data dir +training: {train_path} +dev: {dev_path} +testing: {test_path} + +# Output dir +{output_dir} + +""" + + SEED = 100 TRAIN_TEST_SPLIT = 0.8 TRAIN_DEV_SPLIT = 0.75 @@ -89,6 +118,20 @@ def create_dataset_dict(dataset): } return dataset_dict +def generate_task_requirement(task_name, data_config): + user_requirement = get_user_requirement(task_name, data_config) + split_dataset_path = get_split_dataset_path(task_name, data_config) + train_path = split_dataset_path["train"] + dev_path = split_dataset_path["dev_wo_target"] + test_path = split_dataset_path["test_wo_target"] + work_dir = data_config["work_dir"] + output_dir = f"{work_dir}/{task_name}" + user_requirement = TASK_PROMPT.format(user_requirement=user_requirement, + train_path=train_path, dev_path=dev_path, test_path=test_path, + output_dir=output_dir) + return user_requirement + + class ExpDataset: description : str = None metadata : dict = None diff --git a/expo/insights/InsightGenerate.py b/expo/insights/InsightGenerate.py index de58b7e4e..55ab64e30 100644 --- a/expo/insights/InsightGenerate.py +++ b/expo/insights/InsightGenerate.py @@ -23,7 +23,7 @@ import json from metagpt.llm import LLM from metagpt.schema import Message -from examples.MCTS_test.utils import load_data_config, mcts_logger +from expo.utils import load_data_config, mcts_logger DATA_CONFIG = load_data_config() diff --git a/expo/insights/solution_designer.py b/expo/insights/solution_designer.py index 0986c392a..e2bf57ae3 100644 --- a/expo/insights/solution_designer.py +++ b/expo/insights/solution_designer.py @@ -3,7 +3,7 @@ import json from metagpt.llm import LLM from metagpt.schema import Message -from examples.MCTS_test.utils import clean_json_from_rsp, load_data_config +from expo.utils import clean_json_from_rsp, load_data_config DATA_CONFIG = load_data_config() diff --git a/expo/research_assistant.py b/expo/research_assistant.py index fbd74f7db..7b844cf5e 100644 --- a/expo/research_assistant.py +++ b/expo/research_assistant.py @@ -10,6 +10,9 @@ from metagpt.const import MESSAGE_ROUTE_TO_ALL, SERDESER_PATH from metagpt.utils.recovery_util import save_history from expo.utils import mcts_logger, save_notebook +from pydantic import Field, model_validator +from metagpt.actions.di.write_analysis_code import CheckData, WriteAnalysisCode + import re import os @@ -84,6 +87,17 @@ async def llm_extract_score(self): json_block = CodeParser.parse_code(block=None, text=rsp) score_dict = json.loads(json_block) return score_dict + + + @model_validator(mode="after") + def set_plan_and_tool(self) -> "Interpreter": + if self.planner.plan.goal != '': + self.set_actions([WriteAnalysisCode]) + self._set_state(0) + print("Plan already exists, skipping initialization.") + return self + print("Initializing plan and tool...") + return super().set_plan_and_tool() async def _act_on_task(self, current_task: Task) -> TaskResult: """Useful in 'plan_and_act' mode. Wrap the output in a TaskResult for review and confirmation.""" diff --git a/expo/run_exp_augmentation.py b/expo/run_exp_augmentation.py index 492a424d4..f4d22093f 100644 --- a/expo/run_exp_augmentation.py +++ b/expo/run_exp_augmentation.py @@ -1,10 +1,11 @@ import os -from metagpt.roles.di.research_assistant import ResearchAssistant +from expo.research_assistant import ResearchAssistant import asyncio -from examples.MCTS_test.utils import DATA_CONFIG, generate_task_requirement, get_exp_pool_path -from examples.MCTS_test.insights.InsightGenerate import InsightGenerator -from examples.MCTS_test.MCTS import create_initial_state -from examples.MCTS_test.evaluation.evaluation import evaluate_score +from expo.utils import DATA_CONFIG, get_exp_pool_path +from expo.dataset import generate_task_requirement +from expo.insights.InsightGenerate import InsightGenerator +from expo.MCTS import create_initial_state +from expo.evaluation.evaluation import evaluate_score import json import argparse import pandas as pd diff --git a/expo/run_experiment.py b/expo/run_experiment.py index e75897f5a..0c7468ac9 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -1,6 +1,7 @@ -from examples.MCTS_test.MCTS import MCTS, Node, initialize_di_root_node -from examples.MCTS_test.utils import load_data_config, generate_task_requirement -from examples.MCTS_test.visualize_mcts import get_tree_text +from expo.MCTS import MCTS, Node, initialize_di_root_node +from expo.utils import load_data_config +from expo.dataset import generate_task_requirement +from expo.evaluation.visualize_mcts import get_tree_text import asyncio import argparse diff --git a/expo/run_mcts.py b/expo/run_mcts.py index 0c0c486db..6d2c421ec 100644 --- a/expo/run_mcts.py +++ b/expo/run_mcts.py @@ -1,5 +1,7 @@ from expo.MCTS import MCTS, Node, initialize_di_root_node -from expo.utils import load_data_config, generate_task_requirement +from expo.utils import load_data_config +from expo.dataset import generate_task_requirement + from expo.evaluation.visualize_mcts import get_tree_text import asyncio import argparse diff --git a/expo/utils.py b/expo/utils.py index ac4a64697..423889f29 100644 --- a/expo/utils.py +++ b/expo/utils.py @@ -1,5 +1,4 @@ import yaml -from examples.MCTS_test.dataset import get_user_requirement, get_split_dataset_path from metagpt.roles.role import Role from metagpt.actions.di.execute_nb_code import ExecuteNbCode from metagpt.utils.save_code import save_code_file @@ -13,34 +12,6 @@ import os import re -TASK_PROMPT = """\ -# User requirement -{user_requirement} -**Attention** Please do not leak the target label in any form during training. - -## Saving Dev and Test Predictions -Save the prediction results of the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory BEFORE printig out the results. -The file should contain a single `target` column with the predicted values. -Make sure the prediction results are in the same format as the target column in the training set. The labels should be transformed back to the original format if any transformation was applied during training. - -## Output Training Set Performance -Make sure the performance of the model is printed in python in the last step even if it has been printed in the previous steps. The value should be a float number. -Print the training set performance in the last step. Write in this format: -```python -... -print("Train score:", train_score) -``` - -# Data dir -training: {train_path} -dev: {dev_path} -testing: {test_path} - -# Output dir -{output_dir} - -""" - def load_data_config(file_path="data.yaml"): with open(file_path, 'r') as stream: data_config = yaml.safe_load(stream) @@ -78,18 +49,6 @@ def get_exp_pool_path(task_name, data_config, pool_name="analysis_pool"): exp_pool_path = os.path.join(data_path, f"{pool_name}.json") return exp_pool_path -def generate_task_requirement(task_name, data_config): - user_requirement = get_user_requirement(task_name, data_config) - split_dataset_path = get_split_dataset_path(task_name, data_config) - train_path = split_dataset_path["train"] - dev_path = split_dataset_path["dev_wo_target"] - test_path = split_dataset_path["test_wo_target"] - work_dir = data_config["work_dir"] - output_dir = f"{work_dir}/{task_name}" - user_requirement = TASK_PROMPT.format(user_requirement=user_requirement, - train_path=train_path, dev_path=dev_path, test_path=test_path, - output_dir=output_dir) - return user_requirement def change_plan(role, plan): print(f"Change next plan to: {plan}") From 32759f031c4c2e489e746c7c16d2e5017cdbebcc Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 30 Aug 2024 19:55:40 +0800 Subject: [PATCH 005/160] add experimenter --- expo/MCTS.py | 24 +++++---- expo/dataset.py | 5 +- expo/experimenter/__init__.py | 3 ++ expo/experimenter/aug.py | 60 +++++++++++++++++++++++ expo/experimenter/aug_experimenter.py | 0 expo/experimenter/experimenter.py | 59 ++++++++++++++++++---- expo/experimenter/mcts.py | 44 +++++++++++++++++ expo/experimenter/mcts_experimenter.py | 0 expo/insights/InsightGenerate.py | 15 +----- expo/research_assistant.py | 2 - expo/run_experiment.py | 18 ++++--- expo/run_mcts.py | 9 ++-- expo/utils.py | 4 +- metagpt/prompts/di/write_analysis_code.py | 2 +- 14 files changed, 195 insertions(+), 50 deletions(-) create mode 100644 expo/experimenter/__init__.py create mode 100644 expo/experimenter/aug.py delete mode 100644 expo/experimenter/aug_experimenter.py create mode 100644 expo/experimenter/mcts.py delete mode 100644 expo/experimenter/mcts_experimenter.py diff --git a/expo/MCTS.py b/expo/MCTS.py index af50ff7a0..5c502f917 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -169,8 +169,8 @@ async def expand(self, max_children): # return evaluate_score(predictions, gt, metric) def evaluate_prediction(self, split): - pred_path = os.path.join(self.state["work_dir"], self.state["task"], f"{split}-predictions.csv") - pred_node_path = os.path.join(self.state["node_dir"], f"Node-{self.id}-{split}-predictions.csv") + pred_path = os.path.join(self.state["work_dir"], self.state["task"], f"{split}_predictions.csv") + pred_node_path = os.path.join(self.state["node_dir"], f"Node-{self.id}-{split}_predictions.csv") gt_path = os.path.join(self.state["datasets_dir"][f"{split}_target"]) preds = pd.read_csv(pred_path)["target"] preds.to_csv(pred_node_path, index=False) @@ -201,12 +201,12 @@ async def run_node(self, role=None): score_dict = await role.get_score() score_dict = self.evaluate_simulation(score_dict) self.raw_reward = score_dict - if self.state["low_is_better"]: # normalized the score to be between 0 and 1, and higher is better def normalize_score(score): return 1 / (1 + score) score_dict = {k: normalize_score(v) for k, v in score_dict.items()} + self.normalized_reward = score_dict return score_dict @@ -262,19 +262,23 @@ def backpropagate(self, node : Node, reward): def best_path(self, root : Node): best_child = root best_score = 0 - def bfs(node : Node, best_score, best_child : Node): + def bfs(node : Node, best_score, best_child : Node, split): + assert split in ["test_score", "dev_score"] if node not in self.children: return best_score, best_child for child in self.children[node]: - print(child.id, child.raw_value) - if child.raw_value > best_score: - best_score = child.raw_value + score = child.normalized[split] + print(child.id, score) + if score > best_score: + best_score = score best_child = child best_score, best_child = bfs(child, best_score, best_child) return best_score, best_child - best_score, best_child = bfs(root, best_score, best_child) - mcts_logger.log("MCTS", f"Best Score: {best_score}, Best Node ID: {best_child.id}") - return best_child + _, best_child = bfs(root, best_score, best_child, "test_score") + _, dev_best_child = bfs(root, best_score, best_child, "dev_score") + + return {"dev_best": dev_best_child, + "global_best": best_child} def get_num_simulations(self): return self.root_node.visited diff --git a/expo/dataset.py b/expo/dataset.py index a507d0b7e..3f3fa1db1 100644 --- a/expo/dataset.py +++ b/expo/dataset.py @@ -20,8 +20,8 @@ **Attention** Please do not leak the target label in any form during training. ## Saving Dev and Test Predictions -Save the prediction results of the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory BEFORE printig out the results. -The file should contain a single `target` column with the predicted values. +Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. +Both files should contain a single `target` column with the predicted values. Make sure the prediction results are in the same format as the target column in the training set. The labels should be transformed back to the original format if any transformation was applied during training. ## Output Training Set Performance @@ -129,6 +129,7 @@ def generate_task_requirement(task_name, data_config): user_requirement = TASK_PROMPT.format(user_requirement=user_requirement, train_path=train_path, dev_path=dev_path, test_path=test_path, output_dir=output_dir) + print(user_requirement) return user_requirement diff --git a/expo/experimenter/__init__.py b/expo/experimenter/__init__.py new file mode 100644 index 000000000..d6b50c9d2 --- /dev/null +++ b/expo/experimenter/__init__.py @@ -0,0 +1,3 @@ +from .experimenter import Experimenter +from .mcts import MCTSExperimenter +from .aug import AugExperimenter \ No newline at end of file diff --git a/expo/experimenter/aug.py b/expo/experimenter/aug.py new file mode 100644 index 000000000..28643d47f --- /dev/null +++ b/expo/experimenter/aug.py @@ -0,0 +1,60 @@ +from experimenter import Experimenter +from expo.MCTS import create_initial_state +from expo.dataset import generate_task_requirement +from expo.utils import mcts_logger, load_execute_notebook, get_exp_pool_path +from expo.insights.InsightGenerate import InsightGenerator +from expo.research_assistant import ResearchAssistant + +EXPS_PROMPT = """ +When doing the tasks, you can refer to the insights below: +{experience} + +""" + + + + +class AugExperimenter(Experimenter): + result_path : str = "results/aug" + + async def run_experiment(self): + state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") + user_requirement = state["requirement"] + exp_pool_path = get_exp_pool_path(self.args.task, self.data_config, pool_name="ds_analysis_pool") + exp_pool = InsightGenerator.load_analysis_pool(exp_pool_path) + if self.args.aug_mode == "single": + exps = InsightGenerator._random_sample(exp_pool, self.args.num_experiments) + exps = [exp["Analysis"] for exp in exps] + elif self.args.aug_mode == "set": + exp_set = InsightGenerator.sample_instruction_set(exp_pool) + exp_set_text = "\n".join([f"{exp['task_id']}: {exp['Analysis']}" for exp in exp_set]) + exps = [exp_set_text] * self.args.num_experiments + else: + raise ValueError(f"Invalid mode: {self.args.aug_mode}") + + results = [] + for i in range(self.args.num_experiments): + di = ResearchAssistant(node_id=str(i), use_reflection=self.args.use_reflection) + di.role_dir = f"{di.role_dir}_{self.args.task}" + requirement = user_requirement + EXPS_PROMPT.format(experience=exps[i]) + print(requirement) + await di.run(requirement) + score_dict = await di.get_score(low_is_better=False) + score_dict = self.evaluate(score_dict, state) + results.append({ + "idx": i, + "score_dict": score_dict, + "aug_mode": self.args.aug_mode, + "insights" : exps[i], + "user_requirement": user_requirement, + "args": self.args + }) + scores = [score_dict["test_score"] for score_dict in scores] + avg_score = sum(scores) / len(scores) + best_score = max(scores) if not self.args.low_is_better else min(scores) + best_score_idx = scores.index(best_score) + results.insert(0, {"avg_score": avg_score, "best_score": best_score, "best_score_idx": best_score_idx}) + self.save_results(results) + + + \ No newline at end of file diff --git a/expo/experimenter/aug_experimenter.py b/expo/experimenter/aug_experimenter.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index d7ed82070..092af3694 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -1,18 +1,57 @@ +from expo.utils import DATA_CONFIG +import os +import pandas as pd +from expo.evaluation.evaluation import evaluate_score +import datetime +import json +from expo.MCTS import create_initial_state +from expo.research_assistant import ResearchAssistant + class Experimenter: result_path : str = "results" + data_config = DATA_CONFIG + + + def __init__(self, args, **kwargs): + self.args = args + self.start_time = datetime.datetime.now().strftime("%Y%m%d%H%M") async def run_experiment(self): - pass + state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") + user_requirement = state["requirement"] + di = ResearchAssistant(node_id="0", use_reflection=self.args.use_reflection) + await di.run(user_requirement) + + score_dict = await di.get_score(low_is_better=False) + score_dict = self.evaluate(score_dict, state) + results = { + "score_dict": score_dict, + "aug_mode": self.args.aug_mode, + "user_requirement": user_requirement, + "args": self.args + } + self.save_result(results) + def evaluate_prediction(self, split, state): + pred_path = os.path.join(state["work_dir"], state["task"], f"{split}_predictions.csv") + pred_node_path = os.path.join(state["node_dir"], f"{self.start_time}-{split}_predictions.csv") + gt_path = os.path.join(state["datasets_dir"][f"{split}_target"]) + preds = pd.read_csv(pred_path)["target"] + preds.to_csv(pred_node_path, index=False) + gt = pd.read_csv(gt_path)["target"] + metric = state["dataset_config"]["metric"] + return evaluate_score(preds, gt, metric) - def save_scores(self): - pass + def evaluate(self, score_dict, state): + scores = { + "dev_score": self.evaluate_prediction("dev", state), + "test_score": self.evaluate_prediction("test", state), + } + score_dict.update(scores) + return score_dict - def save_result(self): - results = { - "test_score": self.test_score, - "num_experiments": self.num_experiments, - "insights": self.insights, - "avg_score": self.avg_score, - } \ No newline at end of file + + def save_result(self, result): + with open(f"{self.result_path}/{self.args.task}_{self.start_time}.json", "w") as f: + json.dump(result, f, indent=4) diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py new file mode 100644 index 000000000..2523588b9 --- /dev/null +++ b/expo/experimenter/mcts.py @@ -0,0 +1,44 @@ +from expo.experimenter import Experimenter +from expo.dataset import generate_task_requirement +from expo.MCTS import MCTS +from expo.evaluation.visualize_mcts import get_tree_text + + +class MCTSExperimenter(Experimenter): + result_path : str = "results/mcts" + async def run_experiment(self): + mcts = MCTS(root_node=None, max_depth=5) + best_nodes = await mcts.search(self.args.task, self.data_config, + low_is_better=self.args.low_is_better, + load_tree=self.args.load_tree, + reflection=self.args.reflection, + rollout=self.args.rollout, + name=self.args.name) + best_node = best_nodes["global_best"] + dev_best_node = best_nodes["dev_best"] + + text, num_generated_codes = get_tree_text(mcts.root_node) + text += f"Generated {num_generated_codes} unique codes.\n" + text += f"Best node: {best_node}, score: {best_node.raw_reward}\n" + text += f"Dev best node: {dev_best_node}, score: {dev_best_node.raw_reward}\n" + print(text) + self.save_tree(text) + + results = { + "best_node": best_node, + "best_node_score": best_node.raw_reward, + "dev_best_node": dev_best_node, + "dev_best_node_score": dev_best_node.raw_reward, + "num_generated_codes": num_generated_codes, + "user_requirement": best_node.state["requirement"], + "args": self.args + } + self.save_result(results) + + + + def save_tree(self, tree_text): + fpath = f"{self.result_path}/{self.args.task}_tree_{self.args.name}.txt" + with open(fpath, "w") as f: + f.write(tree_text) + diff --git a/expo/experimenter/mcts_experimenter.py b/expo/experimenter/mcts_experimenter.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/expo/insights/InsightGenerate.py b/expo/insights/InsightGenerate.py index 55ab64e30..35bed976a 100644 --- a/expo/insights/InsightGenerate.py +++ b/expo/insights/InsightGenerate.py @@ -23,7 +23,7 @@ import json from metagpt.llm import LLM from metagpt.schema import Message -from expo.utils import load_data_config, mcts_logger +from expo.utils import load_data_config, mcts_logger, clean_json_from_rsp DATA_CONFIG = load_data_config() @@ -52,17 +52,6 @@ def sample_instruction_set(data): for task_id in sorted(data_dict.keys()): instruction_set.append(random.choice(data_dict[task_id])) return instruction_set - - - @staticmethod - def clean_json_from_rsp(text): - pattern = r"```json(.*?)```" - matches = re.findall(pattern, text, re.DOTALL) - if matches: - json_str = "\n".join(matches) - return json_str - else: - return "" @staticmethod def format_output(rsp): @@ -109,6 +98,6 @@ async def generate_new_instruction(original_instruction, insights): llm_response = await llm.aask( context, system_msgs=[REFLECTION_SYSTEM_MSG] ) - rsp = InsightGenerator.clean_json_from_rsp(llm_response) + rsp = clean_json_from_rsp(llm_response) new_instruction = json.loads(rsp)["New Instruction"] return new_instruction \ No newline at end of file diff --git a/expo/research_assistant.py b/expo/research_assistant.py index 7b844cf5e..c26f24586 100644 --- a/expo/research_assistant.py +++ b/expo/research_assistant.py @@ -8,7 +8,6 @@ from metagpt.utils.common import CodeParser from metagpt.utils.common import write_json_file, read_json_file, format_trackback_info from metagpt.const import MESSAGE_ROUTE_TO_ALL, SERDESER_PATH -from metagpt.utils.recovery_util import save_history from expo.utils import mcts_logger, save_notebook from pydantic import Field, model_validator from metagpt.actions.di.write_analysis_code import CheckData, WriteAnalysisCode @@ -126,7 +125,6 @@ def save_state(self, static_save=False): role_path = os.path.join(stg_path, f"{name}.json") # 将状态保存为 JSON 文件 write_json_file(role_path, self.model_dump()) - save_history(role=self, save_dir=stg_path, name=name) def remap_tasks(self): diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 0c7468ac9..1704faa06 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -1,7 +1,4 @@ -from expo.MCTS import MCTS, Node, initialize_di_root_node -from expo.utils import load_data_config -from expo.dataset import generate_task_requirement -from expo.evaluation.visualize_mcts import get_tree_text +from expo.experimenter import MCTSExperimenter, Experimenter, AugExperimenter import asyncio import argparse @@ -9,11 +6,10 @@ def get_args(): parser = argparse.ArgumentParser() parser.add_argument("--name", type=str, default="") + parser.add_argument("--exp_mode", type=str, default="mcts", choices=["mcts", "aug", "base"]) get_di_args(parser) get_mcts_args(parser) get_aug_exp_args(parser) - - return parser.parse_args() @@ -38,7 +34,15 @@ def get_di_args(parser): async def main(args): - pass + if args.exp_mode == "mcts": + experimenter = MCTSExperimenter(args) + elif args.exp_mode == "aug": + experimenter = AugExperimenter(args) + elif args.exp_mode == "base": + experimenter = Experimenter(args) + else: + raise ValueError(f"Invalid exp_mode: {args.exp_mode}") + await experimenter.run_experiment() if __name__ == "__main__": args = get_args() diff --git a/expo/run_mcts.py b/expo/run_mcts.py index 6d2c421ec..7b2e2b4da 100644 --- a/expo/run_mcts.py +++ b/expo/run_mcts.py @@ -27,17 +27,19 @@ def get_args(): if __name__ == "__main__": args = get_args() - requirement = generate_task_requirement(args.task, data_config) - print(requirement) + # requirement = generate_task_requirement(args.task, data_config) + # print(requirement) # role, root_node = initialize_di_root_node(requirement, data_config) # asyncio.run(role.run(requirement)) # asyncio.run(root_node.run_node()) mcts = MCTS(root_node=None, max_depth=5) - best_node = asyncio.run(mcts.search(args.task, data_config, + best_nodes = asyncio.run(mcts.search(args.task, data_config, low_is_better=args.low_is_better, load_tree=args.load_tree, reflection=args.reflection, rollout=args.rollout, name=args.name)) + best_node = best_nodes["global_best"] + dev_best_node = best_nodes["dev_best"] text, num_generated_codes = get_tree_text(mcts.root_node) print(text) print(f"Generated {num_generated_codes} unique codes.") @@ -45,6 +47,7 @@ def get_args(): with open(f"results/{args.task}_tree{args.name}.txt", "w") as f: f.write(f"Generated {num_generated_codes} unique codes.\n") f.write(f"Best node: {best_node}, score: {best_node.raw_reward}\n") + f.write(f"Dev best node: {dev_best_node}, score: {dev_best_node.raw_reward}\n") f.write(text) diff --git a/expo/utils.py b/expo/utils.py index 423889f29..20e3fa7f5 100644 --- a/expo/utils.py +++ b/expo/utils.py @@ -1,7 +1,6 @@ import yaml from metagpt.roles.role import Role from metagpt.actions.di.execute_nb_code import ExecuteNbCode -from metagpt.utils.save_code import save_code_file # from nbclient import NotebookClient from nbformat.notebooknode import NotebookNode import nbformat @@ -86,7 +85,8 @@ def process_cells(nb: NotebookNode) -> NotebookNode: def save_notebook(role: Role, save_dir: str = "", name: str = ""): save_dir = Path(save_dir) nb = process_cells(role.execute_code.nb) - save_code_file(name=name, code_context=nb, file_format="ipynb", save_dir=save_dir) + file_path = save_dir / f"{name}.ipynb" + nbformat.write(nb, file_path) async def load_execute_notebook(role): tasks = role.planner.plan.tasks diff --git a/metagpt/prompts/di/write_analysis_code.py b/metagpt/prompts/di/write_analysis_code.py index f8b9a4c42..beee80679 100644 --- a/metagpt/prompts/di/write_analysis_code.py +++ b/metagpt/prompts/di/write_analysis_code.py @@ -69,7 +69,7 @@ def add(a: int, b: int) -> int: ```json {{ "reflection": str = "Reflection on previous implementation", - "improved_impl": str = "Refined code after reflection.", + "improved_impl": str = "Refined code after reflection (do not include nested code block here).", }} ``` """ From ae6a19575091786c44d5a296d309a90df0c7beef Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 30 Aug 2024 20:35:17 +0800 Subject: [PATCH 006/160] fix bug - make rollout more consistent --- expo/MCTS.py | 9 +++++++-- expo/README.md | 32 ++++++++++++++++++++++++++++++++ expo/dataset.py | 5 ++++- expo/experimenter/mcts.py | 2 +- expo/run_experiment.py | 6 +++--- expo/run_mcts.py | 4 ++-- metagpt/prompts/task_type.py | 2 +- 7 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 expo/README.md diff --git a/expo/MCTS.py b/expo/MCTS.py index 5c502f917..5c448e3ac 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -284,7 +284,7 @@ def get_num_simulations(self): return self.root_node.visited async def search(self, task, data_config, name, - rollout=3, load_tree=False, low_is_better=False, reflection=False): + rollouts, load_tree=False, low_is_better=False, reflection=False): role, root = initialize_di_root_node(task, data_config, low_is_better=low_is_better, reflection=reflection, name=name) self.root_node = root @@ -292,7 +292,12 @@ async def search(self, task, data_config, name, if load_tree: tree_loaded = self.load_tree() mcts_logger.log("MCTS", f"Number of simulations: {self.get_num_simulations()}") + + if not tree_loaded: + rollouts -= 2 + if rollouts < 0: + raise ValueError("Rollouts must be greater than 2 if there is no tree to load") self.children[root] = [] reward = await self.simulate(root, role) self.backpropagate(root, reward) @@ -307,7 +312,7 @@ async def search(self, task, data_config, name, else: root = self.root_node # 后续迭代:使用UCT进行选择,expand并模拟和反向传播 - for _ in range(rollout): # 迭代次数 + for _ in range(rollouts): # 迭代次数 mcts_logger.log("MCTS", f"开始第{_+1}次迭代") leaf = self.select(root) if leaf.is_terminal(): diff --git a/expo/README.md b/expo/README.md new file mode 100644 index 000000000..91701cc18 --- /dev/null +++ b/expo/README.md @@ -0,0 +1,32 @@ +# Expo + + +## Instruction + +- 下载数据集:https://deepwisdom.feishu.cn/drive/folder/RVyofv9cvlvtxKdddt2cyn3BnTc?from=from_copylink + + +## Examples + +### Run Base DI + +`python run_experiment.py --exp_mode base --task titanic` + +### Run DI RandExp + +- Single insight +`python run_experiment.py --exp_mode aug --task titanic --aug_mode single` + +- Set insight +`python run_experiment.py --exp_mode aug --task titanic --aug_mode set` + + + +### Run DI MCTS +`python run_experiment.py --exp_mode mcts --task titanic --rollout 5` + +`python run_experiment.py --exp_mode mcts --task househouse_prices --rollout 5 --low_is_better` + + + + diff --git a/expo/dataset.py b/expo/dataset.py index 3f3fa1db1..be7388365 100644 --- a/expo/dataset.py +++ b/expo/dataset.py @@ -17,7 +17,10 @@ TASK_PROMPT = """\ # User requirement {user_requirement} -**Attention** Please do not leak the target label in any form during training. +**Attention** +1. Please do not leak the target label in any form during training. +2. Dev and Test sets do not have the target column. +3. You should perform transformations on all sets at the same step. ## Saving Dev and Test Predictions Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 2523588b9..91dbd4c6c 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -12,7 +12,7 @@ async def run_experiment(self): low_is_better=self.args.low_is_better, load_tree=self.args.load_tree, reflection=self.args.reflection, - rollout=self.args.rollout, + rollouts=self.args.rollouts, name=self.args.name) best_node = best_nodes["global_best"] dev_best_node = best_nodes["dev_best"] diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 1704faa06..4c8244803 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -16,12 +16,12 @@ def get_args(): def get_mcts_args(parser): parser.add_argument("--load_tree", dest="load_tree", action="store_true") parser.add_argument("--no_load_tree", dest="load_tree", action="store_false") - parser.set_defaults(load_tree=True) - parser.add_argument("--rollout", type=int, default=3) + parser.set_defaults(load_tree=False) + parser.add_argument("--rollouts", type=int, default=3) def get_aug_exp_args(parser): parser.add_argument("--aug_mode", type=str, default="single", choices=["single", "set"]) - parser.add_argument("--num_experiments", type=int, default=2) + parser.add_argument("--num_experiments", type=int, default=1) def get_di_args(parser): diff --git a/expo/run_mcts.py b/expo/run_mcts.py index 7b2e2b4da..20d4171f7 100644 --- a/expo/run_mcts.py +++ b/expo/run_mcts.py @@ -18,7 +18,7 @@ def get_args(): parser.add_argument("--reflection", dest="reflection", action="store_true") parser.add_argument("--no_reflection", dest="reflection", action="store_false") parser.set_defaults(reflection=True) - parser.add_argument("--rollout", type=int, default=3) + parser.add_argument("--rollouts", type=int, default=3) parser.add_argument("--name", type=str, default="") return parser.parse_args() @@ -37,7 +37,7 @@ def get_args(): mcts = MCTS(root_node=None, max_depth=5) best_nodes = asyncio.run(mcts.search(args.task, data_config, low_is_better=args.low_is_better, load_tree=args.load_tree, - reflection=args.reflection, rollout=args.rollout, name=args.name)) + reflection=args.reflection, rollouts=args.rollouts, name=args.name)) best_node = best_nodes["global_best"] dev_best_node = best_nodes["dev_best"] text, num_generated_codes = get_tree_text(mcts.root_node) diff --git a/metagpt/prompts/task_type.py b/metagpt/prompts/task_type.py index 5b1ffc744..116756edc 100644 --- a/metagpt/prompts/task_type.py +++ b/metagpt/prompts/task_type.py @@ -25,7 +25,7 @@ - Use available feature engineering tools if they are potential impactful. - Avoid creating redundant or excessively numerous features in one step. - Exclude ID columns from feature generation and remove them. -- Each feature engineering operation performed on the train set must also applies to the test separately at the same time. +- Each feature engineering operation performed on the train set must also applies to the dev/test separately at the same time. - Avoid using the label column to create features, except for cat encoding. - Use the data from previous task result if exist, do not mock or reload data yourself. - Always copy the DataFrame before processing it and use the copy to process. From 16d1bf0da0e46a9885d7cba9b12cdae93fac065a Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 2 Sep 2024 09:59:47 +0800 Subject: [PATCH 007/160] Rename insight generate to instruction generator --- expo/MCTS.py | 2 +- expo/experimenter/aug.py | 8 ++++---- .../{InsightGenerate.py => instruction_generator.py} | 8 ++++---- expo/run_exp_augmentation.py | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) rename expo/insights/{InsightGenerate.py => instruction_generator.py} (92%) diff --git a/expo/MCTS.py b/expo/MCTS.py index 5c448e3ac..42c93f650 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -3,7 +3,7 @@ import os import pandas as pd from expo.research_assistant import ResearchAssistant -from expo.insights.InsightGenerate import InsightGenerator +from exp_optimizer.expo.insights.instruction_generator import InstructionGenerator from expo.dataset import get_split_dataset_path, generate_task_requirement from expo.evaluation.evaluation import evaluate_score from expo.utils import mcts_logger, load_execute_notebook, get_exp_pool_path diff --git a/expo/experimenter/aug.py b/expo/experimenter/aug.py index 28643d47f..299c053cd 100644 --- a/expo/experimenter/aug.py +++ b/expo/experimenter/aug.py @@ -2,7 +2,7 @@ from expo.MCTS import create_initial_state from expo.dataset import generate_task_requirement from expo.utils import mcts_logger, load_execute_notebook, get_exp_pool_path -from expo.insights.InsightGenerate import InsightGenerator +from exp_optimizer.expo.insights.instruction_generator import InstructionGenerator from expo.research_assistant import ResearchAssistant EXPS_PROMPT = """ @@ -21,12 +21,12 @@ async def run_experiment(self): state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") user_requirement = state["requirement"] exp_pool_path = get_exp_pool_path(self.args.task, self.data_config, pool_name="ds_analysis_pool") - exp_pool = InsightGenerator.load_analysis_pool(exp_pool_path) + exp_pool = InstructionGenerator.load_analysis_pool(exp_pool_path) if self.args.aug_mode == "single": - exps = InsightGenerator._random_sample(exp_pool, self.args.num_experiments) + exps = InstructionGenerator._random_sample(exp_pool, self.args.num_experiments) exps = [exp["Analysis"] for exp in exps] elif self.args.aug_mode == "set": - exp_set = InsightGenerator.sample_instruction_set(exp_pool) + exp_set = InstructionGenerator.sample_instruction_set(exp_pool) exp_set_text = "\n".join([f"{exp['task_id']}: {exp['Analysis']}" for exp in exp_set]) exps = [exp_set_text] * self.args.num_experiments else: diff --git a/expo/insights/InsightGenerate.py b/expo/insights/instruction_generator.py similarity index 92% rename from expo/insights/InsightGenerate.py rename to expo/insights/instruction_generator.py index 35bed976a..4f4155ff8 100644 --- a/expo/insights/InsightGenerate.py +++ b/expo/insights/instruction_generator.py @@ -27,7 +27,7 @@ DATA_CONFIG = load_data_config() -class InsightGenerator: +class InstructionGenerator: data_config = DATA_CONFIG @staticmethod @@ -68,7 +68,7 @@ def format_output(rsp): @staticmethod def load_analysis_pool(file_path, task_id=None): - data = InsightGenerator.load_json_data(file_path) + data = InstructionGenerator.load_json_data(file_path) for item in data: if "task_id" not in item: raise ValueError("task_id is not found in the analysis pool") @@ -79,14 +79,14 @@ def load_analysis_pool(file_path, task_id=None): @staticmethod async def generate_new_instructions(task_id, original_instruction, max_num, file_path): - data = InsightGenerator.load_analysis_pool(file_path, task_id) + data = InstructionGenerator.load_analysis_pool(file_path, task_id) new_instructions = [] if len(data) == 0: mcts_logger.log("MCTS", f"No insights available for task {task_id}") return [original_instruction] # Return the original instruction if no insights are available for item in data[:max_num]: insights = item["Analysis"] - new_instruction = await InsightGenerator.generate_new_instruction(original_instruction, insights) + new_instruction = await InstructionGenerator.generate_new_instruction(original_instruction, insights) new_instructions.append(new_instruction) return new_instructions diff --git a/expo/run_exp_augmentation.py b/expo/run_exp_augmentation.py index f4d22093f..edf01c164 100644 --- a/expo/run_exp_augmentation.py +++ b/expo/run_exp_augmentation.py @@ -3,7 +3,7 @@ import asyncio from expo.utils import DATA_CONFIG, get_exp_pool_path from expo.dataset import generate_task_requirement -from expo.insights.InsightGenerate import InsightGenerator +from exp_optimizer.expo.insights.instruction_generator import InstructionGenerator from expo.MCTS import create_initial_state from expo.evaluation.evaluation import evaluate_score import json @@ -48,12 +48,12 @@ async def main(task_name, use_reflection=True, mode="single", num_experiments=2) user_requirement = generate_task_requirement(task_name, data_config) exp_pool_path = get_exp_pool_path(task_name, data_config, pool_name="ds_analysis_pool") - exp_pool = InsightGenerator.load_analysis_pool(exp_pool_path) + exp_pool = InstructionGenerator.load_analysis_pool(exp_pool_path) if mode == "single": - exps = InsightGenerator._random_sample(exp_pool, num_experiments) + exps = InstructionGenerator._random_sample(exp_pool, num_experiments) exps = [exp["Analysis"] for exp in exps] elif mode == "set": - exp_set = InsightGenerator.sample_instruction_set(exp_pool) + exp_set = InstructionGenerator.sample_instruction_set(exp_pool) exp_set_text = "\n".join([f"{exp['task_id']}: {exp['Analysis']}" for exp in exp_set]) exps = [exp_set_text] * num_experiments else: From 27bbc927b09f0d62b33ba893cb0fc8795340c775 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 2 Sep 2024 10:21:30 +0800 Subject: [PATCH 008/160] 1. Rewrite logger message 2. fix import --- expo/MCTS.py | 50 +++++++++++++++++------------------- expo/experimenter/aug.py | 2 +- expo/run_exp_augmentation.py | 2 +- 3 files changed, 25 insertions(+), 29 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 42c93f650..efb928d99 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -3,7 +3,7 @@ import os import pandas as pd from expo.research_assistant import ResearchAssistant -from exp_optimizer.expo.insights.instruction_generator import InstructionGenerator +from expo.insights.instruction_generator import InstructionGenerator from expo.dataset import get_split_dataset_path, generate_task_requirement from expo.evaluation.evaluation import evaluate_score from expo.utils import mcts_logger, load_execute_notebook, get_exp_pool_path @@ -135,13 +135,13 @@ def save_new_role(self, role: ResearchAssistant): role.start_task_id = self.state['start_task_id'] role.state_saved = False role.change_next_instruction(self.action) - mcts_logger.log("MCTS", f"保存新的role: {role.node_id}") + mcts_logger.log("MCTS", f"Saving new role: {role.node_id}") role.save_state(static_save=True) async def expand(self, max_children): if self.is_fully_expanded(): return - insight_geneartor = InsightGenerator() + insight_geneartor = InstructionGenerator() role = self.load_role() original_instruction = role.get_next_instruction() insights = await insight_geneartor.generate_new_instructions(task_id=role.start_task_id + 1, @@ -224,7 +224,7 @@ def __init__(self, root_node, max_depth): def select(self, node: Node): node = self.best_child() - mcts_logger.log("MCTS", f"选择的叶子节点id: {node.id}") + mcts_logger.log("MCTS", f"Selected node id: {node.id}") return node def best_child(self): @@ -245,9 +245,11 @@ async def expand(self, node : Node, max_children=4): async def simulate(self, node : Node, role=None): "Returns the reward for a random simulation (to completion) of `node`" + mcts_logger.log("MCTS", f"Start simulating node {node.id}:") while node.children: node = random.choice(node.children) - reward = await node.run_node(role) + reward = await node.run_node(role) + mcts_logger.log("MCTS", f"Simulated node's reward: {reward}") return reward @@ -292,7 +294,7 @@ async def search(self, task, data_config, name, if load_tree: tree_loaded = self.load_tree() mcts_logger.log("MCTS", f"Number of simulations: {self.get_num_simulations()}") - + mcts_logger.log("MCTS", f"Tree loaded: {tree_loaded}") if not tree_loaded: rollouts -= 2 @@ -301,41 +303,36 @@ async def search(self, task, data_config, name, self.children[root] = [] reward = await self.simulate(root, role) self.backpropagate(root, reward) - mcts_logger.log("MCTS", f"Root node's value: {reward}") children = await self.expand(root) #目前是随机选择1个,后续可以改成多个 first_leaf = random.choice(children) - mcts_logger.log("MCTS", f"随机选择的叶子节点id: {first_leaf.id}") reward = await self.simulate(first_leaf) - mcts_logger.log("MCTS", f"模拟完毕的叶子节点的Normalized score: {reward}") self.backpropagate(first_leaf, reward) else: root = self.root_node # 后续迭代:使用UCT进行选择,expand并模拟和反向传播 - for _ in range(rollouts): # 迭代次数 - mcts_logger.log("MCTS", f"开始第{_+1}次迭代") - leaf = self.select(root) - if leaf.is_terminal(): - if leaf.raw_value == 0: - reward = await self.simulate(leaf) + for _ in range(rollouts): # number of rollouts + mcts_logger.log("MCTS", f"Start the next rollout {_+1}") + node = self.select(root) + if node.is_terminal(): + if node.raw_value == 0: + reward = await self.simulate(node) else: - reward = {"test_score": leaf.raw_value, "score": leaf.value} - mcts_logger.log("MCTS", f"终止节点的得分为: {reward}") - self.backpropagate(leaf, reward) + reward = {"test_score": node.raw_value, "score": node.value} + mcts_logger.log("MCTS", f"Terminal node's reward: {reward}") + self.backpropagate(node, reward) else: - if leaf.visited > 0: - children = await self.expand(leaf) - leaf = random.choice(children) - mcts_logger.log("MCTS", f"随机选择的叶子节点id: {leaf.id}") - reward = await self.simulate(leaf) - mcts_logger.log("MCTS", f"模拟完毕的叶子节点{leaf.id}的Normalized score: {reward}") - self.backpropagate(leaf, reward) + if node.visited > 0: + children = await self.expand(node) + node = random.choice(children) + reward = await self.simulate(node) + self.backpropagate(node, reward) return self.best_path(root) def load_tree(self): def load_children_node(node): - mcts_logger.log("MCTS", f"加载节点{node.id}的子节点:{node.children}") + mcts_logger.log("MCTS", f"Load node {node.id}'s child: {node.children}") if node.is_terminal() or not node.children: return for child in node.children: @@ -351,6 +348,5 @@ def load_children_node(node): self.children[self.root_node] = self.root_node.children load_children_node(self.root_node) if self.children: - mcts_logger.log("MCTS", "成功加载树") return True return False \ No newline at end of file diff --git a/expo/experimenter/aug.py b/expo/experimenter/aug.py index 299c053cd..ae575ed75 100644 --- a/expo/experimenter/aug.py +++ b/expo/experimenter/aug.py @@ -2,7 +2,7 @@ from expo.MCTS import create_initial_state from expo.dataset import generate_task_requirement from expo.utils import mcts_logger, load_execute_notebook, get_exp_pool_path -from exp_optimizer.expo.insights.instruction_generator import InstructionGenerator +from expo.insights.instruction_generator import InstructionGenerator from expo.research_assistant import ResearchAssistant EXPS_PROMPT = """ diff --git a/expo/run_exp_augmentation.py b/expo/run_exp_augmentation.py index edf01c164..3f8eff3b3 100644 --- a/expo/run_exp_augmentation.py +++ b/expo/run_exp_augmentation.py @@ -3,7 +3,7 @@ import asyncio from expo.utils import DATA_CONFIG, get_exp_pool_path from expo.dataset import generate_task_requirement -from exp_optimizer.expo.insights.instruction_generator import InstructionGenerator +from expo.insights.instruction_generator import InstructionGenerator from expo.MCTS import create_initial_state from expo.evaluation.evaluation import evaluate_score import json From 0b30866d109064bfbf76d9e4b852bbe983cff8b6 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 2 Sep 2024 10:39:08 +0800 Subject: [PATCH 009/160] fix prompt's json format --- expo/research_assistant.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/research_assistant.py b/expo/research_assistant.py index c26f24586..e0dc73418 100644 --- a/expo/research_assistant.py +++ b/expo/research_assistant.py @@ -31,7 +31,7 @@ {{ "train_score": x.x, "dev_score": x.x, - "test_score": x.x, + "test_score": x.x }} ``` """ From 0e5db1c3642ccd5118a09e5d047b250a9c33d95a Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 2 Sep 2024 14:07:14 +0800 Subject: [PATCH 010/160] 1. add ml module 2. fix bug --- expo/MCTS.py | 2 +- expo/README.md | 1 + expo/dataset.py | 2 +- expo/experimenter/aug.py | 10 +++++----- expo/experimenter/experimenter.py | 13 +++++++------ expo/experimenter/mcts.py | 2 +- requirements.txt | 5 +++++ 7 files changed, 21 insertions(+), 14 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index efb928d99..02e80bb52 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -269,7 +269,7 @@ def bfs(node : Node, best_score, best_child : Node, split): if node not in self.children: return best_score, best_child for child in self.children[node]: - score = child.normalized[split] + score = child.normalized_reward[split] print(child.id, score) if score > best_score: best_score = score diff --git a/expo/README.md b/expo/README.md index 91701cc18..baf15bb75 100644 --- a/expo/README.md +++ b/expo/README.md @@ -25,6 +25,7 @@ ### Run DI MCTS `python run_experiment.py --exp_mode mcts --task titanic --rollout 5` +If the dataset has reg metric, remember to use `--low_is_better` `python run_experiment.py --exp_mode mcts --task househouse_prices --rollout 5 --low_is_better` diff --git a/expo/dataset.py b/expo/dataset.py index be7388365..9667c0aef 100644 --- a/expo/dataset.py +++ b/expo/dataset.py @@ -24,7 +24,7 @@ ## Saving Dev and Test Predictions Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. -Both files should contain a single `target` column with the predicted values. +Both files should contain a single column named `target` with the predicted values. Make sure the prediction results are in the same format as the target column in the training set. The labels should be transformed back to the original format if any transformation was applied during training. ## Output Training Set Performance diff --git a/expo/experimenter/aug.py b/expo/experimenter/aug.py index ae575ed75..9c6915103 100644 --- a/expo/experimenter/aug.py +++ b/expo/experimenter/aug.py @@ -34,12 +34,12 @@ async def run_experiment(self): results = [] for i in range(self.args.num_experiments): - di = ResearchAssistant(node_id=str(i), use_reflection=self.args.use_reflection) + di = ResearchAssistant(node_id=str(i), use_reflection=self.args.reflection) di.role_dir = f"{di.role_dir}_{self.args.task}" requirement = user_requirement + EXPS_PROMPT.format(experience=exps[i]) print(requirement) await di.run(requirement) - score_dict = await di.get_score(low_is_better=False) + score_dict = await di.get_score() score_dict = self.evaluate(score_dict, state) results.append({ "idx": i, @@ -47,14 +47,14 @@ async def run_experiment(self): "aug_mode": self.args.aug_mode, "insights" : exps[i], "user_requirement": user_requirement, - "args": self.args + "args": vars(self.args) }) - scores = [score_dict["test_score"] for score_dict in scores] + scores = [result["score_dict"]["test_score"] for result in results] avg_score = sum(scores) / len(scores) best_score = max(scores) if not self.args.low_is_better else min(scores) best_score_idx = scores.index(best_score) results.insert(0, {"avg_score": avg_score, "best_score": best_score, "best_score_idx": best_score_idx}) - self.save_results(results) + self.save_result(results) \ No newline at end of file diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 092af3694..4473866af 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -9,7 +9,7 @@ class Experimenter: - result_path : str = "results" + result_path : str = "results/base" data_config = DATA_CONFIG @@ -20,21 +20,21 @@ def __init__(self, args, **kwargs): async def run_experiment(self): state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") user_requirement = state["requirement"] - di = ResearchAssistant(node_id="0", use_reflection=self.args.use_reflection) + di = ResearchAssistant(node_id="0", use_reflection=self.args.reflection) await di.run(user_requirement) - score_dict = await di.get_score(low_is_better=False) + score_dict = await di.get_score() score_dict = self.evaluate(score_dict, state) results = { "score_dict": score_dict, - "aug_mode": self.args.aug_mode, "user_requirement": user_requirement, - "args": self.args + "args": vars(self.args) } self.save_result(results) def evaluate_prediction(self, split, state): pred_path = os.path.join(state["work_dir"], state["task"], f"{split}_predictions.csv") + os.makedirs(state["node_dir"], exist_ok=True) pred_node_path = os.path.join(state["node_dir"], f"{self.start_time}-{split}_predictions.csv") gt_path = os.path.join(state["datasets_dir"][f"{split}_target"]) preds = pd.read_csv(pred_path)["target"] @@ -53,5 +53,6 @@ def evaluate(self, score_dict, state): def save_result(self, result): - with open(f"{self.result_path}/{self.args.task}_{self.start_time}.json", "w") as f: + os.makedirs(self.result_path, exist_ok=True) + with open(f"{self.result_path}/{self.args.exp_mode}-{self.args.task}_{self.start_time}.json", "w") as f: json.dump(result, f, indent=4) diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 91dbd4c6c..3399fcff9 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -31,7 +31,7 @@ async def run_experiment(self): "dev_best_node_score": dev_best_node.raw_reward, "num_generated_codes": num_generated_codes, "user_requirement": best_node.state["requirement"], - "args": self.args + "args": vars(self.args) } self.save_result(results) diff --git a/requirements.txt b/requirements.txt index 7ea849f5a..271fade14 100644 --- a/requirements.txt +++ b/requirements.txt @@ -80,3 +80,8 @@ boto3~=1.34.69 spark_ai_python~=0.3.30 agentops openml==0.14.2 + +# ml module to run in DI +xgboost +catboost +lightgbm From f28908e4c58ea1147cd0630e562a58dd9f836fbc Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 2 Sep 2024 15:15:53 +0800 Subject: [PATCH 011/160] =?UTF-8?q?1.=20role.py=20-=20=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E5=B7=B2=E7=BB=8F=E6=9C=89plan=EF=BC=8C=E4=BE=BF=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E9=87=8D=E5=A4=8D=E7=94=9F=E6=88=90=202.=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9prompt=EF=BC=8C=E8=AE=A9predictions.csv=E7=94=9F?= =?UTF-8?q?=E6=88=90=E7=9A=84=E6=A0=BC=E5=BC=8F=E4=B8=8E=E5=8E=9Fgt?= =?UTF-8?q?=E6=A0=BC=E5=BC=8F=E4=B8=80=E6=A0=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expo/MCTS.py | 3 ++- expo/dataset.py | 8 +++++--- expo/experimenter/aug.py | 2 +- expo/experimenter/mcts.py | 4 ++-- metagpt/roles/role.py | 8 ++++---- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 02e80bb52..5b1cb36dc 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -42,6 +42,7 @@ class Node(): value : float = 0 visited : int = 0 children : list = [] + normalized_reward : dict = {"train_score": 0, "dev_score": 0, "test_score": 0} parent = None def __init__(self, parent=None, state = None, action=None, value = 0, max_depth=4, **kwargs): @@ -274,7 +275,7 @@ def bfs(node : Node, best_score, best_child : Node, split): if score > best_score: best_score = score best_child = child - best_score, best_child = bfs(child, best_score, best_child) + best_score, best_child = bfs(child, best_score, best_child, split) return best_score, best_child _, best_child = bfs(root, best_score, best_child, "test_score") _, dev_best_child = bfs(root, best_score, best_child, "dev_score") diff --git a/expo/dataset.py b/expo/dataset.py index 9667c0aef..3f59c3367 100644 --- a/expo/dataset.py +++ b/expo/dataset.py @@ -23,9 +23,11 @@ 3. You should perform transformations on all sets at the same step. ## Saving Dev and Test Predictions -Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. -Both files should contain a single column named `target` with the predicted values. -Make sure the prediction results are in the same format as the target column in the training set. The labels should be transformed back to the original format if any transformation was applied during training. +1. Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. +- Both files should contain a single column named `target` with the predicted values. +2. Make sure the prediction results are in the same format as the target column in the training set. +- The labels should be transformed back to the original format if any transformation was applied during training. +- If the original target column was categorical, the predictions should be in the same format. ## Output Training Set Performance Make sure the performance of the model is printed in python in the last step even if it has been printed in the previous steps. The value should be a float number. diff --git a/expo/experimenter/aug.py b/expo/experimenter/aug.py index 9c6915103..956849717 100644 --- a/expo/experimenter/aug.py +++ b/expo/experimenter/aug.py @@ -46,7 +46,7 @@ async def run_experiment(self): "score_dict": score_dict, "aug_mode": self.args.aug_mode, "insights" : exps[i], - "user_requirement": user_requirement, + "user_requirement": requirement, "args": vars(self.args) }) scores = [result["score_dict"]["test_score"] for result in results] diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 3399fcff9..43c5f9868 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -25,9 +25,9 @@ async def run_experiment(self): self.save_tree(text) results = { - "best_node": best_node, + "best_node": best_node.id, "best_node_score": best_node.raw_reward, - "dev_best_node": dev_best_node, + "dev_best_node": dev_best_node.id, "dev_best_node_score": dev_best_node.raw_reward, "num_generated_codes": num_generated_codes, "user_requirement": best_node.state["requirement"], diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index 5ecc7ae33..1e786898c 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -478,10 +478,10 @@ async def _react(self) -> Message: async def _plan_and_act(self) -> Message: """first plan, then execute an action sequence, i.e. _think (of a plan) -> _act -> _act -> ... Use llm to come up with the plan dynamically.""" - - # create initial plan and update it until confirmation - goal = self.rc.memory.get()[-1].content # retreive latest user requirement - await self.planner.update_plan(goal=goal) + if not self.planner.plan.goal: + # create initial plan and update it until confirmation + goal = self.rc.memory.get()[-1].content # retreive latest user requirement + await self.planner.update_plan(goal=goal) # take on tasks until all finished while self.planner.current_task: From f2603a9d31d0509079cb891729f5efccfedab18a Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 2 Sep 2024 16:51:33 +0800 Subject: [PATCH 012/160] remove deprecated comments --- expo/research_assistant.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/expo/research_assistant.py b/expo/research_assistant.py index e0dc73418..ed935b4b8 100644 --- a/expo/research_assistant.py +++ b/expo/research_assistant.py @@ -101,11 +101,8 @@ def set_plan_and_tool(self) -> "Interpreter": async def _act_on_task(self, current_task: Task) -> TaskResult: """Useful in 'plan_and_act' mode. Wrap the output in a TaskResult for review and confirmation.""" mcts_logger.info(f"The current_task is: {current_task}") - - # 执行任务的代码 code, result, is_success = await self._write_and_exec_code() task_result = TaskResult(code=code, result=result, is_success=is_success) - # 只在任务类型为 'feature engineering' 时保存状态 if int(current_task.task_id) == self.start_task_id + 1: # fe_id = current_task.dependent_task_ids self.save_state() From ab8be7a18354bcff32b52c50b07fa0da948b259f Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 2 Sep 2024 17:01:25 +0800 Subject: [PATCH 013/160] update readme --- expo/README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/expo/README.md b/expo/README.md index baf15bb75..02519f640 100644 --- a/expo/README.md +++ b/expo/README.md @@ -29,5 +29,16 @@ If the dataset has reg metric, remember to use `--low_is_better` `python run_experiment.py --exp_mode mcts --task househouse_prices --rollout 5 --low_is_better` +## Code and Configs Explanation +`datasets.yaml` 提供数据集对应的指标和基础提示词 + +`data.yaml` 继承了`datasets.yaml`以及一些路径信息,需要将`datasets_dir`指到数据集合集的根目录下 + +完整的DI提示词参考`dataset.py`中的`generate_task_requirement`函数 + + +## Evaluation + +`evaluation.py` 提供pred和原始的gt(1D iterable)以及需要使用的metric,返回evaluation score From a1668a1d9dbf4622e46de634a47cac596b067b6c Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 2 Sep 2024 20:00:46 +0800 Subject: [PATCH 014/160] add custom experimenter --- expo/MCTS.py | 2 ++ expo/README.md | 5 ++-- expo/dataset.py | 2 +- expo/experimenter/__init__.py | 3 ++- expo/experimenter/custom.py | 49 +++++++++++++++++++++++++++++++++++ expo/run_experiment.py | 8 +++--- 6 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 expo/experimenter/custom.py diff --git a/expo/MCTS.py b/expo/MCTS.py index 5b1cb36dc..ec5ef9da0 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -205,6 +205,8 @@ async def run_node(self, role=None): if self.state["low_is_better"]: # normalized the score to be between 0 and 1, and higher is better def normalize_score(score): + if score == -1: + return 0 return 1 / (1 + score) score_dict = {k: normalize_score(v) for k, v in score_dict.items()} self.normalized_reward = score_dict diff --git a/expo/README.md b/expo/README.md index 02519f640..856d616b8 100644 --- a/expo/README.md +++ b/expo/README.md @@ -25,8 +25,9 @@ ### Run DI MCTS `python run_experiment.py --exp_mode mcts --task titanic --rollout 5` -If the dataset has reg metric, remember to use `--low_is_better` -`python run_experiment.py --exp_mode mcts --task househouse_prices --rollout 5 --low_is_better` +If the dataset has reg metric, remember to use `--low_is_better`: + +- `python run_experiment.py --exp_mode mcts --task househouse_prices --rollout 5 --low_is_better` ## Code and Configs Explanation diff --git a/expo/dataset.py b/expo/dataset.py index 3f59c3367..3e292ba7c 100644 --- a/expo/dataset.py +++ b/expo/dataset.py @@ -27,7 +27,7 @@ - Both files should contain a single column named `target` with the predicted values. 2. Make sure the prediction results are in the same format as the target column in the training set. - The labels should be transformed back to the original format if any transformation was applied during training. -- If the original target column was categorical, the predictions should be in the same format. +- If the original target column was categorical or string, the predictions MUST be in the same format. ## Output Training Set Performance Make sure the performance of the model is printed in python in the last step even if it has been printed in the previous steps. The value should be a float number. diff --git a/expo/experimenter/__init__.py b/expo/experimenter/__init__.py index d6b50c9d2..2eab295f7 100644 --- a/expo/experimenter/__init__.py +++ b/expo/experimenter/__init__.py @@ -1,3 +1,4 @@ from .experimenter import Experimenter from .mcts import MCTSExperimenter -from .aug import AugExperimenter \ No newline at end of file +from .aug import AugExperimenter +from .custom import CustomExperimenter \ No newline at end of file diff --git a/expo/experimenter/custom.py b/expo/experimenter/custom.py new file mode 100644 index 000000000..06df4efcf --- /dev/null +++ b/expo/experimenter/custom.py @@ -0,0 +1,49 @@ +from expo.experimenter import Experimenter +from expo.MCTS import create_initial_state +from expo.evaluation.evaluation import evaluate_score +import pandas as pd +import os + +class CustomExperimenter(Experimenter): + result_path : str = "results/custom" + + def __init__(self, args, **kwargs): + super().__init__(args, **kwargs) + self.framework = kwargs["framework"] + self.name = kwargs.get("name", "") + self.result_path = f"results/custom_{self.name}" + self.state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") + + async def run_experiment(self): + user_requirement = self.state["requirement"] + preds = await self.framework.run(user_requirement) + test_preds = preds["test_preds"] + dev_preds = preds["dev_preds"] + score_dict = { + "dev_score": self.evaluate_predictions(dev_preds, "dev"), + "test_score": self.evaluate_predictions(test_preds, "test") + } + results = { + "score_dict": score_dict, + "user_requirement": user_requirement, + "args": vars(self.args) + } + self.save_result(results) + + def evaluate_predictions(self, preds, split): + metric = self.state["dataset_config"]["metric"] + gt_path = os.path.join(self.state["datasets_dir"][f"{split}_target"]) + gt = pd.read_csv(gt_path)["target"] + score = evaluate_score(preds, gt, metric) + return score + + + def load_datasets(self): + train_path = self.state["datasets_dir"]["train"] + dev_path = self.state["datasets_dir"]["dev"] + test_path = self.state["datasets_dir"]["test"] + train = pd.read_csv(train_path) + dev = pd.read_csv(dev_path) + test = pd.read_csv(test_path) + return train, dev, test + diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 4c8244803..826019321 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -1,4 +1,4 @@ -from expo.experimenter import MCTSExperimenter, Experimenter, AugExperimenter +from expo.experimenter import MCTSExperimenter, Experimenter, AugExperimenter, CustomExperimenter import asyncio import argparse @@ -6,7 +6,7 @@ def get_args(): parser = argparse.ArgumentParser() parser.add_argument("--name", type=str, default="") - parser.add_argument("--exp_mode", type=str, default="mcts", choices=["mcts", "aug", "base"]) + parser.add_argument("--exp_mode", type=str, default="mcts", choices=["mcts", "aug", "base", "custom"]) get_di_args(parser) get_mcts_args(parser) get_aug_exp_args(parser) @@ -17,7 +17,7 @@ def get_mcts_args(parser): parser.add_argument("--load_tree", dest="load_tree", action="store_true") parser.add_argument("--no_load_tree", dest="load_tree", action="store_false") parser.set_defaults(load_tree=False) - parser.add_argument("--rollouts", type=int, default=3) + parser.add_argument("--rollouts", type=int, default=5) def get_aug_exp_args(parser): parser.add_argument("--aug_mode", type=str, default="single", choices=["single", "set"]) @@ -40,6 +40,8 @@ async def main(args): experimenter = AugExperimenter(args) elif args.exp_mode == "base": experimenter = Experimenter(args) + elif args.exp_mode == "custom": + experimenter = CustomExperimenter(args) else: raise ValueError(f"Invalid exp_mode: {args.exp_mode}") await experimenter.run_experiment() From 6aafe680c10f6cdc316188a17ecb9b81ce6ce93d Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 2 Sep 2024 20:23:45 +0800 Subject: [PATCH 015/160] =?UTF-8?q?1.=20=E6=9A=82=E6=97=B6=E5=9C=A8expo?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E5=A4=B9=E9=87=8C=E5=8D=95=E7=8B=AC=E6=94=BE?= =?UTF-8?q?=E4=B8=80=E4=B8=AArequirements.txt=202.=20Dummy=20CustomExperim?= =?UTF-8?q?enter?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expo/README.md | 14 ++++++++++++- expo/data.yaml | 47 ++++++++++++++++++++++--------------------- expo/requirements.txt | 5 +++++ requirements.txt | 6 ------ 4 files changed, 42 insertions(+), 30 deletions(-) create mode 100644 expo/requirements.txt diff --git a/expo/README.md b/expo/README.md index 856d616b8..6e4081031 100644 --- a/expo/README.md +++ b/expo/README.md @@ -1,10 +1,17 @@ # Expo +## Setup +In the root directory, `pip install -e .` + +`cd expo` + +`pip install -r requirements.txt` + ## Instruction - 下载数据集:https://deepwisdom.feishu.cn/drive/folder/RVyofv9cvlvtxKdddt2cyn3BnTc?from=from_copylink - +- 修改`data.yaml`的`datasets_dir`为数据集合集根目录存储位置 ## Examples @@ -29,6 +36,11 @@ If the dataset has reg metric, remember to use `--low_is_better`: - `python run_experiment.py --exp_mode mcts --task househouse_prices --rollout 5 --low_is_better` +## Custom Experimenter + + + + ## Code and Configs Explanation diff --git a/expo/data.yaml b/expo/data.yaml index df26e29e8..050b0b893 100644 --- a/expo/data.yaml +++ b/expo/data.yaml @@ -2,34 +2,35 @@ datasets_dir: "D:/work/automl/datasets" # path to the datasets directory datasets: titanic: - dataset: "04_titanic" - user_requirement: "This is a titanic passenger survival dataset, your goal is to predict passenger survival outcome. The target column is Survived. Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. Report accuracy on the eval data. Don't plot." - metric: "accuracy" + dataset: 04_titanic + metric: f1 + user_requirement: "This is a 04_titanic dataset. Your goal is to predict the target\ + \ column `Survived`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ + \ or make any visualizations.\n" house_prices: - dataset: "05_house-prices-advanced-regression-techniques" - user_requirement: "This is a house price dataset, your goal is to predict the sale price of a property based on its features. Make sure to generate at least 5 tasks each time, including eda, data preprocessing, feature engineering, model training to predict the target, and model evaluation. Report RMSE between the logarithm of the predicted value and the logarithm of the observed sale prices on the eval data. The target column is 'SalePrice'. Please do not include any processing of the target column in the data preprocessing and feature engineering stages. Don't plot." - metric: "log rmse" + dataset: 05_house-prices-advanced-regression-techniques + metric: rmse + user_requirement: "This is a 05_house-prices-advanced-regression-techniques dataset.\ + \ Your goal is to predict the target column `SalePrice`.\nPerform data analysis,\ + \ data preprocessing, feature engineering, and modeling to predict the target.\ + \ \nReport rmse on the eval data. Do not plot or make any visualizations.\n" santander_customers: - dataset: "06_santander-customer-transaction-prediction" - user_requirement: "This is a customers financial dataset. Your goal is to predict which customers will make a specific transaction in the future. The target column is target. Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. Report AUC on the eval data. Don't plot." - metric: "auc" - + dataset: 06_santander-customer-transaction-prediction + metric: f1 + user_requirement: "This is a 06_santander-customer-transaction-prediction dataset.\ + \ Your goal is to predict the target column `target`.\nPerform data analysis,\ + \ data preprocessing, feature engineering, and modeling to predict the target.\ + \ \nReport f1 on the eval data. Do not plot or make any visualizations.\n" icr: - dataset: "07_icr-identify-age-related-conditions" - user_requirement: "ICR dataset is a medical dataset with over fifty anonymized health characteristics linked to three age-related conditions. Your goal is to predict whether a subject has or has not been diagnosed with one of these conditions. Make sure to generate at least 5 tasks each time, including eda, data preprocessing, feature engineering, model training to predict the target, and model evaluation. The target column is Class. Report F1 Score on the eval data. Don't plot." - metric: "f1" - - santander_value: - dataset: "08_santander-value-prediction-challenge" - user_requirement: "This is a regression problem. Your goal is to predict the value of transactions for potential customers. The target column is target. Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. Report RMSE on the eval data. Don't plot." - metric: "rmse" - - load_wine: - dataset: None - user_requirement: "Analyze the 'load_wine' dataset from sklearn to predict wine quality. Visualize relationships between features, use machine learning for classification, and report model accuracy. Include analysis and prediction visualizations. Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. Don't plot!" - metric: "accuracy" + dataset: 07_icr-identify-age-related-conditions + metric: f1 + user_requirement: "This is a 07_icr-identify-age-related-conditions dataset. Your\ + \ goal is to predict the target column `Class`.\nPerform data analysis, data\ + \ preprocessing, feature engineering, and modeling to predict the target. \n\ + Report f1 on the eval data. Do not plot or make any visualizations.\n" lick_prediction_small: dataset: Click_prediction_small diff --git a/expo/requirements.txt b/expo/requirements.txt new file mode 100644 index 000000000..04de1a8bb --- /dev/null +++ b/expo/requirements.txt @@ -0,0 +1,5 @@ +# expo +openml==0.14.2 +# ml module to run in DI +xgboost +catboost diff --git a/requirements.txt b/requirements.txt index 271fade14..8bf0ee399 100644 --- a/requirements.txt +++ b/requirements.txt @@ -79,9 +79,3 @@ gymnasium==0.29.1 boto3~=1.34.69 spark_ai_python~=0.3.30 agentops -openml==0.14.2 - -# ml module to run in DI -xgboost -catboost -lightgbm From 3ec6dcd5dfa637231621457e9ab39262030165b9 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 3 Sep 2024 13:40:23 +0800 Subject: [PATCH 016/160] update readme --- expo/MCTS.py | 2 +- expo/README.md | 99 ++++++++++++++++++++++++------- expo/data.yaml | 42 ++++++------- expo/dataset.py | 24 ++++---- expo/datasets.yaml | 27 +++++++-- expo/experimenter/custom.py | 15 ++++- expo/experimenter/experimenter.py | 28 +++++---- expo/experimenter/mcts.py | 23 +++---- 8 files changed, 180 insertions(+), 80 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index ec5ef9da0..14f2c4e4b 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -300,7 +300,7 @@ async def search(self, task, data_config, name, mcts_logger.log("MCTS", f"Tree loaded: {tree_loaded}") if not tree_loaded: - rollouts -= 2 + rollouts -= 2 # 2 rollouts for the initial tree if rollouts < 0: raise ValueError("Rollouts must be greater than 2 if there is no tree to load") self.children[root] = [] diff --git a/expo/README.md b/expo/README.md index 6e4081031..2ecf2fd2f 100644 --- a/expo/README.md +++ b/expo/README.md @@ -1,25 +1,76 @@ # Expo -## Setup -In the root directory, `pip install -e .` -`cd expo` -`pip install -r requirements.txt` - -## Instruction +## 1. Data Preparation - 下载数据集:https://deepwisdom.feishu.cn/drive/folder/RVyofv9cvlvtxKdddt2cyn3BnTc?from=from_copylink - 修改`data.yaml`的`datasets_dir`为数据集合集根目录存储位置 -## Examples -### Run Base DI - -`python run_experiment.py --exp_mode base --task titanic` +## 2. Configs + +### Data Config + +`datasets.yaml` 提供数据集对应的指标和基础提示词 + +`data.yaml` 继承了`datasets.yaml`以及一些路径信息,需要将`datasets_dir`指到数据集合集的根目录下 + + +### LLM Config + +``` +llm: + api_type: 'openai' + model: deepseek-coder + base_url: "https://oneapi.deepwisdom.ai/v1" + api_key: sk-xxx + temperature: 0.5 +``` + +### Budget +实验轮次 k = 10, 20 + + +### 提示词使用 + +通过执行`dataset.py`中的`generate_task_requirement`函数获取提示词 + + +## 3. Evaluation + +运行各个框架,运行后框架需要提供Dev和Test的`dev_predictions.csv`和`test_predictions.csv`, column name为target + +两种评估方式 + +1. `evaluation.py` 提供pred和原始的gt(1D iterable)以及需要使用的metric,返回evaluation score -### Run DI RandExp +2. 使用`CustomExperimenter` +``` +experimenter = CustomExperimenter(task="titanic") +score_dict = experimenter.evaluate_pred_files(dev_pred_path, test_pred_path) +``` + +## 4. Baselines +### DS Agent +提供github链接,并说明使用的命令以及参数设置 + + +### AIDE +提供github链接,并说明使用的命令以及参数设置 + +### Autogluon +提供github链接,并说明使用的命令以及参数设置 + +### Base DI +For setup, check 5. + +- `python run_experiment.py --exp_mode base --task titanic` + + +### DI RandomSearch +For setup, check 5. - Single insight `python run_experiment.py --exp_mode aug --task titanic --aug_mode single` @@ -28,30 +79,36 @@ In the root directory, `pip install -e .` `python run_experiment.py --exp_mode aug --task titanic --aug_mode set` +## 5. DI MCTS ### Run DI MCTS -`python run_experiment.py --exp_mode mcts --task titanic --rollout 5` -If the dataset has reg metric, remember to use `--low_is_better`: +#### Setup +In the root directory, -- `python run_experiment.py --exp_mode mcts --task househouse_prices --rollout 5 --low_is_better` +``` +pip install -e . -## Custom Experimenter +cd expo +pip install -r requirements.txt +``` + +#### Run + +- `python run_experiment.py --exp_mode mcts --task titanic --rollout 5` + +If the dataset has reg metric, remember to use `--low_is_better`: + +- `python run_experiment.py --exp_mode mcts --task househouse_prices --rollout 5 --low_is_better` -## Code and Configs Explanation -`datasets.yaml` 提供数据集对应的指标和基础提示词 -`data.yaml` 继承了`datasets.yaml`以及一些路径信息,需要将`datasets_dir`指到数据集合集的根目录下 -完整的DI提示词参考`dataset.py`中的`generate_task_requirement`函数 -## Evaluation -`evaluation.py` 提供pred和原始的gt(1D iterable)以及需要使用的metric,返回evaluation score diff --git a/expo/data.yaml b/expo/data.yaml index 050b0b893..d62e45309 100644 --- a/expo/data.yaml +++ b/expo/data.yaml @@ -4,22 +4,23 @@ datasets: titanic: dataset: 04_titanic metric: f1 + target_col: Survived user_requirement: "This is a 04_titanic dataset. Your goal is to predict the target\ \ column `Survived`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ \ or make any visualizations.\n" - - house_prices: + house-prices: dataset: 05_house-prices-advanced-regression-techniques metric: rmse + target_col: SalePrice user_requirement: "This is a 05_house-prices-advanced-regression-techniques dataset.\ \ Your goal is to predict the target column `SalePrice`.\nPerform data analysis,\ \ data preprocessing, feature engineering, and modeling to predict the target.\ \ \nReport rmse on the eval data. Do not plot or make any visualizations.\n" - - santander_customers: + santander-customer: dataset: 06_santander-customer-transaction-prediction metric: f1 + target_col: target user_requirement: "This is a 06_santander-customer-transaction-prediction dataset.\ \ Your goal is to predict the target column `target`.\nPerform data analysis,\ \ data preprocessing, feature engineering, and modeling to predict the target.\ @@ -27,126 +28,127 @@ datasets: icr: dataset: 07_icr-identify-age-related-conditions metric: f1 + target_col: Class user_requirement: "This is a 07_icr-identify-age-related-conditions dataset. Your\ \ goal is to predict the target column `Class`.\nPerform data analysis, data\ \ preprocessing, feature engineering, and modeling to predict the target. \n\ Report f1 on the eval data. Do not plot or make any visualizations.\n" - - lick_prediction_small: + Click_prediction_small: dataset: Click_prediction_small metric: f1 + target_col: click user_requirement: "This is a Click_prediction_small dataset. Your goal is to predict\ \ the target column `click`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport f1 on the eval data.\ \ Do not plot or make any visualizations.\n" - GesturePhaseSegmentationProcessed: dataset: GesturePhaseSegmentationProcessed metric: f1 weighted + target_col: Phase user_requirement: "This is a GesturePhaseSegmentationProcessed dataset. Your goal\ \ is to predict the target column `Phase`.\nPerform data analysis, data preprocessing,\ \ feature engineering, and modeling to predict the target. \nReport f1 weighted\ \ on the eval data. Do not plot or make any visualizations.\n" - Moneyball: dataset: Moneyball metric: rmse + target_col: RS user_requirement: "This is a Moneyball dataset. Your goal is to predict the target\ \ column `RS`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ \ plot or make any visualizations.\n" - SAT11-HAND-runtime-regression: dataset: SAT11-HAND-runtime-regression metric: rmse + target_col: runtime user_requirement: "This is a SAT11-HAND-runtime-regression dataset. Your goal\ \ is to predict the target column `runtime`.\nPerform data analysis, data preprocessing,\ \ feature engineering, and modeling to predict the target. \nReport rmse on\ \ the eval data. Do not plot or make any visualizations.\n" - boston: dataset: boston metric: rmse + target_col: MEDV user_requirement: "This is a boston dataset. Your goal is to predict the target\ \ column `MEDV`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ \ plot or make any visualizations.\n" - colleges: dataset: colleges metric: rmse + target_col: percent_pell_grant user_requirement: "This is a colleges dataset. Your goal is to predict the target\ \ column `percent_pell_grant`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport rmse on the eval\ \ data. Do not plot or make any visualizations.\n" - credit-g: dataset: credit-g metric: f1 + target_col: class user_requirement: "This is a credit-g dataset. Your goal is to predict the target\ \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ \ or make any visualizations.\n" - diamonds: dataset: diamonds metric: rmse + target_col: price user_requirement: "This is a diamonds dataset. Your goal is to predict the target\ \ column `price`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ \ plot or make any visualizations.\n" - jasmine: dataset: jasmine metric: f1 + target_col: class user_requirement: "This is a jasmine dataset. Your goal is to predict the target\ \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ \ or make any visualizations.\n" - kc1: dataset: kc1 metric: f1 + target_col: defects user_requirement: "This is a kc1 dataset. Your goal is to predict the target column\ \ `defects`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ \ or make any visualizations.\n" - kick: dataset: kick metric: f1 + target_col: IsBadBuy user_requirement: "This is a kick dataset. Your goal is to predict the target\ \ column `IsBadBuy`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ \ or make any visualizations.\n" - mfeat-factors: dataset: mfeat-factors metric: f1 weighted + target_col: class user_requirement: "This is a mfeat-factors dataset. Your goal is to predict the\ \ target column `class`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ \ eval data. Do not plot or make any visualizations.\n" - segment: dataset: segment metric: f1 weighted + target_col: class user_requirement: "This is a segment dataset. Your goal is to predict the target\ \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 weighted on the eval data.\ \ Do not plot or make any visualizations.\n" - steel-plates-fault: dataset: steel-plates-fault metric: f1 weighted + target_col: target user_requirement: "This is a steel-plates-fault dataset. Your goal is to predict\ \ the target column `target`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ \ eval data. Do not plot or make any visualizations.\n" - wine-quality-white: dataset: wine-quality-white metric: f1 weighted + target_col: Class user_requirement: "This is a wine-quality-white dataset. Your goal is to predict\ \ the target column `Class`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ diff --git a/expo/dataset.py b/expo/dataset.py index 3e292ba7c..62665d297 100644 --- a/expo/dataset.py +++ b/expo/dataset.py @@ -21,13 +21,13 @@ 1. Please do not leak the target label in any form during training. 2. Dev and Test sets do not have the target column. 3. You should perform transformations on all sets at the same step. +4. If labels are transformed during training, they should be transformed back to the original format before saving the predictions. ## Saving Dev and Test Predictions 1. Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. - Both files should contain a single column named `target` with the predicted values. 2. Make sure the prediction results are in the same format as the target column in the training set. - The labels should be transformed back to the original format if any transformation was applied during training. -- If the original target column was categorical or string, the predictions MUST be in the same format. ## Output Training Set Performance Make sure the performance of the model is printed in python in the last step even if it has been printed in the previous steps. The value should be a float number. @@ -119,7 +119,8 @@ def create_dataset_dict(dataset): dataset_dict = { "dataset": dataset.name, "user_requirement": dataset.create_base_requirement(), - "metric": dataset.get_metric() + "metric": dataset.get_metric(), + "target_col": dataset.target_col } return dataset_dict @@ -289,23 +290,24 @@ def get_dataset_info(self): # def __init__(self, name, dataset_dir, dataset_name, **kwargs): # super().__init__(name, dataset_dir, **kwargs) - +async def process_dataset(dataset, solution_designer, save_analysis_pool, datasets_dict): + if save_analysis_pool: + asyncio.run(solution_designer.generate_solutions(dataset.get_dataset_info(), dataset.name)) + dataset_dict = create_dataset_dict(dataset) + datasets_dict["datasets"][dataset.name] = dataset_dict if __name__ == "__main__": datasets_dir = "D:/work/automl/datasets" - force_update = True + force_update = False + save_analysis_pool = False datasets_dict = {"datasets": {}} solution_designer = SolutionDesigner() for dataset_id in OPENML_DATASET_IDS: openml_dataset = OpenMLExpDataset("", datasets_dir, dataset_id, force_update=force_update) - asyncio.run(solution_designer.generate_solutions(openml_dataset.get_dataset_info(), openml_dataset.name)) - dataset_dict = create_dataset_dict(openml_dataset) - datasets_dict["datasets"][openml_dataset.name] = dataset_dict + asyncio.run(process_dataset(openml_dataset, solution_designer, save_analysis_pool, datasets_dict)) for dataset_name, target_col in CUSTOM_DATASETS: custom_dataset = ExpDataset(dataset_name, datasets_dir, target_col=target_col, force_update=force_update) - asyncio.run(solution_designer.generate_solutions(custom_dataset.get_dataset_info(), custom_dataset.name)) - dataset_dict = create_dataset_dict(custom_dataset) - datasets_dict["datasets"][custom_dataset.name] = dataset_dict - + asyncio.run(process_dataset(custom_dataset, solution_designer, save_analysis_pool, datasets_dict)) + save_datasets_dict_to_yaml(datasets_dict) diff --git a/expo/datasets.yaml b/expo/datasets.yaml index ec00e3d1f..8c28b03ca 100644 --- a/expo/datasets.yaml +++ b/expo/datasets.yaml @@ -1,28 +1,32 @@ datasets: - 04_titanic: + titanic: dataset: 04_titanic metric: f1 + target_col: Survived user_requirement: "This is a 04_titanic dataset. Your goal is to predict the target\ \ column `Survived`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ \ or make any visualizations.\n" - 05_house-prices-advanced-regression-techniques: + house-prices: dataset: 05_house-prices-advanced-regression-techniques metric: rmse + target_col: SalePrice user_requirement: "This is a 05_house-prices-advanced-regression-techniques dataset.\ \ Your goal is to predict the target column `SalePrice`.\nPerform data analysis,\ \ data preprocessing, feature engineering, and modeling to predict the target.\ \ \nReport rmse on the eval data. Do not plot or make any visualizations.\n" - 06_santander-customer-transaction-prediction: + santander-customer: dataset: 06_santander-customer-transaction-prediction metric: f1 + target_col: target user_requirement: "This is a 06_santander-customer-transaction-prediction dataset.\ \ Your goal is to predict the target column `target`.\nPerform data analysis,\ \ data preprocessing, feature engineering, and modeling to predict the target.\ \ \nReport f1 on the eval data. Do not plot or make any visualizations.\n" - 07_icr-identify-age-related-conditions: + icr: dataset: 07_icr-identify-age-related-conditions metric: f1 + target_col: Class user_requirement: "This is a 07_icr-identify-age-related-conditions dataset. Your\ \ goal is to predict the target column `Class`.\nPerform data analysis, data\ \ preprocessing, feature engineering, and modeling to predict the target. \n\ @@ -30,6 +34,7 @@ datasets: Click_prediction_small: dataset: Click_prediction_small metric: f1 + target_col: click user_requirement: "This is a Click_prediction_small dataset. Your goal is to predict\ \ the target column `click`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport f1 on the eval data.\ @@ -37,6 +42,7 @@ datasets: GesturePhaseSegmentationProcessed: dataset: GesturePhaseSegmentationProcessed metric: f1 weighted + target_col: Phase user_requirement: "This is a GesturePhaseSegmentationProcessed dataset. Your goal\ \ is to predict the target column `Phase`.\nPerform data analysis, data preprocessing,\ \ feature engineering, and modeling to predict the target. \nReport f1 weighted\ @@ -44,6 +50,7 @@ datasets: Moneyball: dataset: Moneyball metric: rmse + target_col: RS user_requirement: "This is a Moneyball dataset. Your goal is to predict the target\ \ column `RS`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ @@ -51,6 +58,7 @@ datasets: SAT11-HAND-runtime-regression: dataset: SAT11-HAND-runtime-regression metric: rmse + target_col: runtime user_requirement: "This is a SAT11-HAND-runtime-regression dataset. Your goal\ \ is to predict the target column `runtime`.\nPerform data analysis, data preprocessing,\ \ feature engineering, and modeling to predict the target. \nReport rmse on\ @@ -58,6 +66,7 @@ datasets: boston: dataset: boston metric: rmse + target_col: MEDV user_requirement: "This is a boston dataset. Your goal is to predict the target\ \ column `MEDV`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ @@ -65,6 +74,7 @@ datasets: colleges: dataset: colleges metric: rmse + target_col: percent_pell_grant user_requirement: "This is a colleges dataset. Your goal is to predict the target\ \ column `percent_pell_grant`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport rmse on the eval\ @@ -72,6 +82,7 @@ datasets: credit-g: dataset: credit-g metric: f1 + target_col: class user_requirement: "This is a credit-g dataset. Your goal is to predict the target\ \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ @@ -79,6 +90,7 @@ datasets: diamonds: dataset: diamonds metric: rmse + target_col: price user_requirement: "This is a diamonds dataset. Your goal is to predict the target\ \ column `price`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ @@ -86,6 +98,7 @@ datasets: jasmine: dataset: jasmine metric: f1 + target_col: class user_requirement: "This is a jasmine dataset. Your goal is to predict the target\ \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ @@ -93,6 +106,7 @@ datasets: kc1: dataset: kc1 metric: f1 + target_col: defects user_requirement: "This is a kc1 dataset. Your goal is to predict the target column\ \ `defects`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ @@ -100,6 +114,7 @@ datasets: kick: dataset: kick metric: f1 + target_col: IsBadBuy user_requirement: "This is a kick dataset. Your goal is to predict the target\ \ column `IsBadBuy`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ @@ -107,6 +122,7 @@ datasets: mfeat-factors: dataset: mfeat-factors metric: f1 weighted + target_col: class user_requirement: "This is a mfeat-factors dataset. Your goal is to predict the\ \ target column `class`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ @@ -114,6 +130,7 @@ datasets: segment: dataset: segment metric: f1 weighted + target_col: class user_requirement: "This is a segment dataset. Your goal is to predict the target\ \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 weighted on the eval data.\ @@ -121,6 +138,7 @@ datasets: steel-plates-fault: dataset: steel-plates-fault metric: f1 weighted + target_col: target user_requirement: "This is a steel-plates-fault dataset. Your goal is to predict\ \ the target column `target`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ @@ -128,6 +146,7 @@ datasets: wine-quality-white: dataset: wine-quality-white metric: f1 weighted + target_col: Class user_requirement: "This is a wine-quality-white dataset. Your goal is to predict\ \ the target column `Class`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ diff --git a/expo/experimenter/custom.py b/expo/experimenter/custom.py index 06df4efcf..ff5ba3546 100644 --- a/expo/experimenter/custom.py +++ b/expo/experimenter/custom.py @@ -9,10 +9,12 @@ class CustomExperimenter(Experimenter): def __init__(self, args, **kwargs): super().__init__(args, **kwargs) - self.framework = kwargs["framework"] + self.framework = kwargs["framework"] # todo + self.task = kwargs.get("task", self.args.task) + self.low_is_better = kwargs.get("low_is_better", self.args.low_is_better) self.name = kwargs.get("name", "") self.result_path = f"results/custom_{self.name}" - self.state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") + self.state = create_initial_state(self.task, start_task_id=1, data_config=self.data_config, low_is_better=self.low_is_better, name=self.name) async def run_experiment(self): user_requirement = self.state["requirement"] @@ -30,6 +32,15 @@ async def run_experiment(self): } self.save_result(results) + def evaluate_pred_files(self, dev_pred_path, test_pred_path): + dev_preds = pd.read_csv(dev_pred_path)["target"] + test_preds = pd.read_csv(test_pred_path)["target"] + score_dict = { + "dev_score": self.evaluate_score(dev_preds, "dev"), + "test_score": self.evaluate_score(test_preds, "test") + } + return score_dict + def evaluate_predictions(self, preds, split): metric = self.state["dataset_config"]["metric"] gt_path = os.path.join(self.state["datasets_dir"][f"{split}_target"]) diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 4473866af..678d48d6a 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -20,16 +20,24 @@ def __init__(self, args, **kwargs): async def run_experiment(self): state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") user_requirement = state["requirement"] - di = ResearchAssistant(node_id="0", use_reflection=self.args.reflection) - await di.run(user_requirement) - - score_dict = await di.get_score() - score_dict = self.evaluate(score_dict, state) - results = { - "score_dict": score_dict, - "user_requirement": user_requirement, - "args": vars(self.args) - } + results = [] + + for i in range(self.args.num_experiments): + di = ResearchAssistant(node_id="0", use_reflection=self.args.reflection) + await di.run(user_requirement) + score_dict = await di.get_score() + score_dict = self.evaluate(score_dict, state) + results.append({ + "idx": i, + "score_dict": score_dict, + "user_requirement": user_requirement, + "args": vars(self.args) + }) + scores = [result["score_dict"]["test_score"] for result in results] + avg_score = sum(scores) / len(scores) + best_score = max(scores) if not self.args.low_is_better else min(scores) + best_score_idx = scores.index(best_score) + results.insert(0, {"avg_score": avg_score, "best_score": best_score, "best_score_idx": best_score_idx}) self.save_result(results) def evaluate_prediction(self, split, state): diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 43c5f9868..0159abe24 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -22,18 +22,19 @@ async def run_experiment(self): text += f"Best node: {best_node}, score: {best_node.raw_reward}\n" text += f"Dev best node: {dev_best_node}, score: {dev_best_node.raw_reward}\n" print(text) - self.save_tree(text) + if self.args.rollouts > 0: + self.save_tree(text) - results = { - "best_node": best_node.id, - "best_node_score": best_node.raw_reward, - "dev_best_node": dev_best_node.id, - "dev_best_node_score": dev_best_node.raw_reward, - "num_generated_codes": num_generated_codes, - "user_requirement": best_node.state["requirement"], - "args": vars(self.args) - } - self.save_result(results) + results = { + "best_node": best_node.id, + "best_node_score": best_node.raw_reward, + "dev_best_node": dev_best_node.id, + "dev_best_node_score": dev_best_node.raw_reward, + "num_generated_codes": num_generated_codes, + "user_requirement": best_node.state["requirement"], + "args": vars(self.args) + } + self.save_result(results) From df877c973e64b1a2136292c575ea25f6161d2608 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 3 Sep 2024 14:03:07 +0800 Subject: [PATCH 017/160] update readme --- expo/README.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/expo/README.md b/expo/README.md index 2ecf2fd2f..99aa35016 100644 --- a/expo/README.md +++ b/expo/README.md @@ -42,11 +42,7 @@ llm: 运行各个框架,运行后框架需要提供Dev和Test的`dev_predictions.csv`和`test_predictions.csv`, column name为target -两种评估方式 - -1. `evaluation.py` 提供pred和原始的gt(1D iterable)以及需要使用的metric,返回evaluation score - -2. 使用`CustomExperimenter` +- 使用`CustomExperimenter` ``` experimenter = CustomExperimenter(task="titanic") score_dict = experimenter.evaluate_pred_files(dev_pred_path, test_pred_path) From 6972afb755db818ff7ea85afeb86d3b8859c8802 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 3 Sep 2024 16:31:03 +0800 Subject: [PATCH 018/160] update prompt (specify whether each set has target label) --- expo/MCTS.py | 4 ++-- expo/dataset.py | 8 ++++---- expo/evaluation/evaluation.py | 2 +- expo/evaluation/visualize_mcts.py | 6 ++++-- expo/experimenter/mcts.py | 23 +++++++++++------------ 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 14f2c4e4b..dd4ad50b1 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -240,7 +240,7 @@ def uct(node: Node): all_children = [child for children in self.children.values() for child in children] return max(all_children, key=uct) - async def expand(self, node : Node, max_children=4): + async def expand(self, node : Node, max_children=5): await node.expand(max_children) if node not in self.children or not self.children[node]: self.children[node] = node.children @@ -273,7 +273,7 @@ def bfs(node : Node, best_score, best_child : Node, split): return best_score, best_child for child in self.children[node]: score = child.normalized_reward[split] - print(child.id, score) + print(child.id, split, score) if score > best_score: best_score = score best_child = child diff --git a/expo/dataset.py b/expo/dataset.py index 62665d297..a43f1292a 100644 --- a/expo/dataset.py +++ b/expo/dataset.py @@ -20,7 +20,7 @@ **Attention** 1. Please do not leak the target label in any form during training. 2. Dev and Test sets do not have the target column. -3. You should perform transformations on all sets at the same step. +3. You should perform transformations on train, dev, and test sets at the same time (it's a good idea to define functions for this and avoid code repetition). 4. If labels are transformed during training, they should be transformed back to the original format before saving the predictions. ## Saving Dev and Test Predictions @@ -38,9 +38,9 @@ ``` # Data dir -training: {train_path} -dev: {dev_path} -testing: {test_path} +training (with labels): {train_path} +dev (without labels): {dev_path} +testing (without labels): {test_path} # Output dir {output_dir} diff --git a/expo/evaluation/evaluation.py b/expo/evaluation/evaluation.py index 20a35aa27..886bc036d 100644 --- a/expo/evaluation/evaluation.py +++ b/expo/evaluation/evaluation.py @@ -5,7 +5,7 @@ def evaluate_score(pred, gt, metric): if metric == "accuracy": return accuracy_score(gt, pred) elif metric == "f1": - unique_classes = np.unique(gt) + unique_classes = sorted(list(np.unique(gt))) if 1 in unique_classes and 0 in unique_classes: pos_label = 1 else: diff --git a/expo/evaluation/visualize_mcts.py b/expo/evaluation/visualize_mcts.py index 6e38576e2..4199def0e 100644 --- a/expo/evaluation/visualize_mcts.py +++ b/expo/evaluation/visualize_mcts.py @@ -48,7 +48,9 @@ def visualize_tree(node, depth=0, previous_plans=None): for child in node.children: text += textwrap.indent(visualize_tree(child, depth+1, previous_plans), "\t") return text - - return visualize_tree(node), len(code_set) + num_simulations = node.visited + text = f"Number of simulations: {num_simulations}\n" + text += visualize_tree(node) + return text, len(code_set) diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 0159abe24..43c5f9868 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -22,19 +22,18 @@ async def run_experiment(self): text += f"Best node: {best_node}, score: {best_node.raw_reward}\n" text += f"Dev best node: {dev_best_node}, score: {dev_best_node.raw_reward}\n" print(text) - if self.args.rollouts > 0: - self.save_tree(text) + self.save_tree(text) - results = { - "best_node": best_node.id, - "best_node_score": best_node.raw_reward, - "dev_best_node": dev_best_node.id, - "dev_best_node_score": dev_best_node.raw_reward, - "num_generated_codes": num_generated_codes, - "user_requirement": best_node.state["requirement"], - "args": vars(self.args) - } - self.save_result(results) + results = { + "best_node": best_node.id, + "best_node_score": best_node.raw_reward, + "dev_best_node": dev_best_node.id, + "dev_best_node_score": dev_best_node.raw_reward, + "num_generated_codes": num_generated_codes, + "user_requirement": best_node.state["requirement"], + "args": vars(self.args) + } + self.save_result(results) From f23d2a72c9d8c52ca9f2b6e2cf68dce5f64a623c Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 4 Sep 2024 10:39:28 +0800 Subject: [PATCH 019/160] add autogluon --- expo/experimenter/autogluon.py | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 expo/experimenter/autogluon.py diff --git a/expo/experimenter/autogluon.py b/expo/experimenter/autogluon.py new file mode 100644 index 000000000..d59fb3e83 --- /dev/null +++ b/expo/experimenter/autogluon.py @@ -0,0 +1,32 @@ +from expo.experimenter.custom import CustomExperimenter +from autogluon.tabular import TabularDataset, TabularPredictor + +class AGRunner(): + preset = "best_quality" + time_limit = 500 + + def __init__(self, datasets): + self.datasets = datasets + + def run(self): + train_path = self.datasets["train"] + test_wo_target_path = self.datasets["test_wo_target"] + dev_wo_target_path = self.datasets["dev_wo_target"] + target_col = self.state["dataset_config"]["target_col"] + train_data = TabularDataset(train_path) + test_data = TabularDataset(test_wo_target_path) + dev_data = TabularDataset(dev_wo_target_path) + + predictor = TabularPredictor(label=target_col).fit(train_data, presets=self.preset, time_limit=self.time_limit) + test_preds = predictor.predict(test_data) + dev_preds = predictor.predict(dev_data) + return {"test_preds": test_preds, "dev_preds": dev_preds} + +class GluonExperimenter(CustomExperimenter): + result_path : str = "results/autogluon" + + def __init__(self, args, **kwargs): + super().__init__(args, **kwargs) + self.framework = AGRunner(self.datasets) + + From 72bd1665b16c08d1c8215264f411d09509af879c Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 4 Sep 2024 10:41:10 +0800 Subject: [PATCH 020/160] ensure experimenter not evaluating csv from other experiments --- expo/MCTS.py | 2 ++ expo/experimenter/custom.py | 4 ++-- expo/experimenter/experimenter.py | 30 +++++++++++++++++++++++++----- expo/experimenter/mcts.py | 5 +++-- 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index dd4ad50b1..9787ea5e9 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -177,6 +177,8 @@ def evaluate_prediction(self, split): preds.to_csv(pred_node_path, index=False) gt = pd.read_csv(gt_path)["target"] metric = self.state["dataset_config"]["metric"] + # remove original predictions.csv + os.remove(pred_path) return evaluate_score(preds, gt, metric) def evaluate_simulation(self, score_dict): diff --git a/expo/experimenter/custom.py b/expo/experimenter/custom.py index ff5ba3546..c3cb97b9c 100644 --- a/expo/experimenter/custom.py +++ b/expo/experimenter/custom.py @@ -16,9 +16,9 @@ def __init__(self, args, **kwargs): self.result_path = f"results/custom_{self.name}" self.state = create_initial_state(self.task, start_task_id=1, data_config=self.data_config, low_is_better=self.low_is_better, name=self.name) - async def run_experiment(self): + def run_experiment(self): user_requirement = self.state["requirement"] - preds = await self.framework.run(user_requirement) + preds = self.framework.run(user_requirement) test_preds = preds["test_preds"] dev_preds = preds["dev_preds"] score_dict = { diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 678d48d6a..949ab97f1 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -33,11 +33,22 @@ async def run_experiment(self): "user_requirement": user_requirement, "args": vars(self.args) }) - scores = [result["score_dict"]["test_score"] for result in results] - avg_score = sum(scores) / len(scores) - best_score = max(scores) if not self.args.low_is_better else min(scores) - best_score_idx = scores.index(best_score) - results.insert(0, {"avg_score": avg_score, "best_score": best_score, "best_score_idx": best_score_idx}) + self.save_result(results) # save intermediate results + dev_scores = [result["score_dict"]["dev_score"] for result in results] + best_dev_score = max(dev_scores) if not self.args.low_is_better else min(dev_scores) + best_score_idx = dev_scores.index(best_dev_score) + + test_scores = [result["score_dict"]["test_score"] for result in results] + avg_score = sum(test_scores) / len(test_scores) + global_best_score = max(test_scores) if not self.args.low_is_better else min(test_scores) + + results.insert(0, { + "best_dev_score": best_dev_score, + "best_score_idx": best_score_idx, + "best_test_score": test_scores[best_score_idx], + "avg_test_score": avg_score, + "best_score": global_best_score + }) self.save_result(results) def evaluate_prediction(self, split, state): @@ -49,6 +60,7 @@ def evaluate_prediction(self, split, state): preds.to_csv(pred_node_path, index=False) gt = pd.read_csv(gt_path)["target"] metric = state["dataset_config"]["metric"] + os.remove(pred_path) return evaluate_score(preds, gt, metric) def evaluate(self, score_dict, state): @@ -61,6 +73,14 @@ def evaluate(self, score_dict, state): def save_result(self, result): + end_time = datetime.datetime.now().strftime("%Y%m%d%H%M") + time_info = { + "start_time": self.start_time, + "end_time": end_time, + "duration (seconds)": float(end_time) - float(self.start_time) + } + result = result.copy() + result.insert(0, time_info) os.makedirs(self.result_path, exist_ok=True) with open(f"{self.result_path}/{self.args.exp_mode}-{self.args.task}_{self.start_time}.json", "w") as f: json.dump(result, f, indent=4) diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 43c5f9868..e41f94d58 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -24,15 +24,16 @@ async def run_experiment(self): print(text) self.save_tree(text) - results = { + results = [{ "best_node": best_node.id, "best_node_score": best_node.raw_reward, "dev_best_node": dev_best_node.id, "dev_best_node_score": dev_best_node.raw_reward, "num_generated_codes": num_generated_codes, "user_requirement": best_node.state["requirement"], + "tree_text": text, "args": vars(self.args) - } + }] self.save_result(results) From aea524b4eafbc94e0529dfe8d00c03a8d9d7068d Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 4 Sep 2024 14:49:18 +0800 Subject: [PATCH 021/160] 1. update readme 2. fix duration --- expo/README.md | 12 ++++++++++++ expo/experimenter/experimenter.py | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/expo/README.md b/expo/README.md index 99aa35016..dfaf1ab0a 100644 --- a/expo/README.md +++ b/expo/README.md @@ -57,6 +57,18 @@ score_dict = experimenter.evaluate_pred_files(dev_pred_path, test_pred_path) 提供github链接,并说明使用的命令以及参数设置 ### Autogluon +#### Setup +``` +pip install -U pip +pip install -U setuptools wheel + +CPU version of pytorch has smaller footprint - see installation instructions in +pytorch documentation - https://pytorch.org/get-started/locally/ +pip install torch==2.3.1 torchvision==0.18.1 --index-url https://download.pytorch.org/whl/cpu + +pip install autogluon +``` + 提供github链接,并说明使用的命令以及参数设置 ### Base DI diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 949ab97f1..e53bae972 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -77,7 +77,7 @@ def save_result(self, result): time_info = { "start_time": self.start_time, "end_time": end_time, - "duration (seconds)": float(end_time) - float(self.start_time) + "duration (minutes)": float(end_time) - float(self.start_time) } result = result.copy() result.insert(0, time_info) From fcd1ba66a6ae70f93e7e575f5a9395ebfea5d6ff Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 4 Sep 2024 16:38:33 +0800 Subject: [PATCH 022/160] =?UTF-8?q?=E5=A2=9E=E5=8A=A0try=20catch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expo/MCTS.py | 26 +++++++++++++++++++------- expo/README.md | 5 +++-- expo/experimenter/aug.py | 8 +++----- expo/experimenter/experimenter.py | 31 +++++++++++++++++++++++++++---- 4 files changed, 52 insertions(+), 18 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 9787ea5e9..ab9957a7a 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -194,13 +194,25 @@ async def run_node(self, role=None): if self.is_terminal() and role is not None: if role.state_saved: return self.raw_reward - - if not role: - role = self.load_role() - await load_execute_notebook(role) # execute previous notebook's code - await role.run(with_message='continue') - else: - await role.run(with_message=self.state['requirement']) + + max_retries = 3 + num_runs = 1 + run_finished = False + while num_runs <= max_retries and not run_finished: + try: + if not role: + role = self.load_role() + await load_execute_notebook(role) # execute previous notebook's code + await role.run(with_message='continue') + else: + await role.run(with_message=self.state['requirement']) + run_finished = True + except Exception as e: + mcts_logger.log("MCTS", f"Error in running the role: {e}") + num_runs += 1 + if not run_finished: + mcts_logger.log("MCTS", f"Role {role.node_id} failed to run") + return {"test_score": 0, "dev_score": 0, "score": 0} score_dict = await role.get_score() score_dict = self.evaluate_simulation(score_dict) self.raw_reward = score_dict diff --git a/expo/README.md b/expo/README.md index dfaf1ab0a..4cc4daf25 100644 --- a/expo/README.md +++ b/expo/README.md @@ -35,7 +35,8 @@ llm: ### 提示词使用 -通过执行`dataset.py`中的`generate_task_requirement`函数获取提示词 +- 通过执行`dataset.py`中的`generate_task_requirement`函数获取提示词 +- 每一个数据集里有`dataset_info.json`,里面的内容需要提供给baselines以保证公平 ## 3. Evaluation @@ -74,7 +75,7 @@ pip install autogluon ### Base DI For setup, check 5. -- `python run_experiment.py --exp_mode base --task titanic` +- `python run_experiment.py --exp_mode base --task titanic --num_experiments 10` ### DI RandomSearch diff --git a/expo/experimenter/aug.py b/expo/experimenter/aug.py index 956849717..86c98fd42 100644 --- a/expo/experimenter/aug.py +++ b/expo/experimenter/aug.py @@ -18,8 +18,8 @@ class AugExperimenter(Experimenter): result_path : str = "results/aug" async def run_experiment(self): - state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") - user_requirement = state["requirement"] + # state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") + user_requirement = self.state["requirement"] exp_pool_path = get_exp_pool_path(self.args.task, self.data_config, pool_name="ds_analysis_pool") exp_pool = InstructionGenerator.load_analysis_pool(exp_pool_path) if self.args.aug_mode == "single": @@ -38,9 +38,7 @@ async def run_experiment(self): di.role_dir = f"{di.role_dir}_{self.args.task}" requirement = user_requirement + EXPS_PROMPT.format(experience=exps[i]) print(requirement) - await di.run(requirement) - score_dict = await di.get_score() - score_dict = self.evaluate(score_dict, state) + score_dict = await self.run_di(di, requirement) results.append({ "idx": i, "score_dict": score_dict, diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index e53bae972..709eefdfc 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -16,17 +16,40 @@ class Experimenter: def __init__(self, args, **kwargs): self.args = args self.start_time = datetime.datetime.now().strftime("%Y%m%d%H%M") + self.state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") + + + async def run_di(self, di, user_requirement): + max_retries = 3 + num_runs = 1 + run_finished = False + while num_runs <= max_retries and not run_finished: + try: + await di.run(user_requirement) + score_dict = await di.get_score() + score_dict = self.evaluate(score_dict, self.state) + run_finished = True + except Exception as e: + print(f"Error: {e}") + num_runs += 1 + if not run_finished: + score_dict = { + "train_score": -1, + "dev_score": -1, + "test_score": -1, + "score": -1 + } + return score_dict + async def run_experiment(self): - state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") + state = self.state user_requirement = state["requirement"] results = [] for i in range(self.args.num_experiments): di = ResearchAssistant(node_id="0", use_reflection=self.args.reflection) - await di.run(user_requirement) - score_dict = await di.get_score() - score_dict = self.evaluate(score_dict, state) + score_dict = await self.run_di(di, user_requirement) results.append({ "idx": i, "score_dict": score_dict, From ab8a1d682470f30c3abb2afb96d524e7fff8ab81 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 4 Sep 2024 17:52:02 +0800 Subject: [PATCH 023/160] format code --- expo/MCTS.py | 179 +++++++++++++------------ expo/dataset.py | 125 +++++++++-------- expo/evaluation/evaluation.py | 5 +- expo/evaluation/visualize_mcts.py | 21 +-- expo/experimenter/__init__.py | 4 - expo/experimenter/aug.py | 32 ++--- expo/experimenter/autogluon.py | 15 ++- expo/experimenter/custom.py | 35 +++-- expo/experimenter/experimenter.py | 63 +++++---- expo/experimenter/mcts.py | 48 +++---- expo/insights/instruction_generator.py | 35 +++-- expo/insights/solution_designer.py | 49 +++---- expo/research_assistant.py | 63 ++++----- expo/run_exp_augmentation.py | 50 ++++--- expo/run_experiment.py | 16 ++- expo/run_mcts.py | 25 ++-- expo/utils.py | 48 ++++--- 17 files changed, 425 insertions(+), 388 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index ab9957a7a..7c03e2e86 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -1,23 +1,28 @@ -import random import math import os +import pickle +import random + import pandas as pd -from expo.research_assistant import ResearchAssistant -from expo.insights.instruction_generator import InstructionGenerator -from expo.dataset import get_split_dataset_path, generate_task_requirement + +from expo.dataset import generate_task_requirement, get_split_dataset_path from expo.evaluation.evaluation import evaluate_score -from expo.utils import mcts_logger, load_execute_notebook, get_exp_pool_path +from expo.insights.instruction_generator import InstructionGenerator +from expo.research_assistant import ResearchAssistant +from expo.utils import get_exp_pool_path, load_execute_notebook, mcts_logger +from metagpt.tools.tool_recommend import ToolRecommender +from metagpt.utils.common import read_json_file -from metagpt.tools.tool_recommend import BM25ToolRecommender, ToolRecommender -from metagpt.utils.common import write_json_file, read_json_file, format_trackback_info -import numpy as np -import pickle def initialize_di_root_node(task, data_config, low_is_better=False, reflection=True, name=""): start_task_id = 2 - state = create_initial_state(task, start_task_id=start_task_id, data_config=data_config, low_is_better=low_is_better, name=name) - role = ResearchAssistant(node_id="0", start_task_id=start_task_id, use_reflection=reflection, role_dir=state["node_dir"]) - return role, Node(parent=None, state=state, action=None, value=0) + state = create_initial_state( + task, start_task_id=start_task_id, data_config=data_config, low_is_better=low_is_better, name=name + ) + role = ResearchAssistant( + node_id="0", start_task_id=start_task_id, use_reflection=reflection, role_dir=state["node_dir"] + ) + return role, Node(parent=None, state=state, action=None, value=0) def create_initial_state(task, start_task_id, data_config, low_is_better, name): @@ -36,16 +41,16 @@ def create_initial_state(task, start_task_id, data_config, low_is_better, name): return initial_state -class Node(): - state : dict = {} - action : str = None - value : float = 0 - visited : int = 0 - children : list = [] - normalized_reward : dict = {"train_score": 0, "dev_score": 0, "test_score": 0} +class Node: + state: dict = {} + action: str = None + value: float = 0 + visited: int = 0 + children: list = [] + normalized_reward: dict = {"train_score": 0, "dev_score": 0, "test_score": 0} parent = None - def __init__(self, parent=None, state = None, action=None, value = 0, max_depth=4, **kwargs): + def __init__(self, parent=None, state=None, action=None, value=0, max_depth=4, **kwargs): self.state = state self.action = action self.value = value @@ -66,14 +71,14 @@ def avg_value(self): def __hash__(self): return hash(self.id) - + def save_node(self): - os.makedirs(self.state["node_dir"], exist_ok=True) - with open(os.path.join(self.state["node_dir"], f"Node-{self.id}.pkl"), 'wb') as f: + os.makedirs(self.state["node_dir"], exist_ok=True) + with open(os.path.join(self.state["node_dir"], f"Node-{self.id}.pkl"), "wb") as f: pickle.dump(self, f) - + def load_node(self): - with open(os.path.join(self.state["node_dir"], f"Node-{self.id}.pkl"), 'rb') as f: + with open(os.path.join(self.state["node_dir"], f"Node-{self.id}.pkl"), "rb") as f: return pickle.load(f) def get_depth(self): @@ -94,14 +99,14 @@ def generate_id(self): def is_terminal(self): return int(self.state["start_task_id"]) == self.max_depth + 1 - + def is_fully_expanded(self): return len(self.children) > 0 - + def add_child(self, child_node): self.children.append(child_node) - def update(self, reward:dict, child_node=None): + def update(self, reward: dict, child_node=None): if child_node is not None: child_role = child_node.load_role() role = self.load_role() @@ -117,46 +122,48 @@ def get_role_path(self): fname = f"Node-{self.id}.json" role_path = os.path.join(self.state["node_dir"], fname) return role_path - + def load_role(self): role_dict = read_json_file(self.get_role_path()) - if role_dict.get('tool_recommender') is None: - role_dict['tool_recommender'] = ToolRecommender() - elif isinstance(role_dict.get('tool_recommender', {}).get('tools'), dict): - role_dict['tool_recommender']['tools'] = list(role_dict['tool_recommender']['tools'].keys()) + if role_dict.get("tool_recommender") is None: + role_dict["tool_recommender"] = ToolRecommender() + elif isinstance(role_dict.get("tool_recommender", {}).get("tools"), dict): + role_dict["tool_recommender"]["tools"] = list(role_dict["tool_recommender"]["tools"].keys()) role = ResearchAssistant(**role_dict) - if self.parent is not None: # TODO: Check this + if self.parent is not None: # TODO: Check this parent_role = self.parent.load_role() role.update_til_start_task(parent_role, backward=False) role.remap_tasks() return role - + def save_new_role(self, role: ResearchAssistant): role.node_id = self.id - role.start_task_id = self.state['start_task_id'] + role.start_task_id = self.state["start_task_id"] role.state_saved = False - role.change_next_instruction(self.action) + role.change_next_instruction(self.action) mcts_logger.log("MCTS", f"Saving new role: {role.node_id}") role.save_state(static_save=True) - + async def expand(self, max_children): if self.is_fully_expanded(): return insight_geneartor = InstructionGenerator() role = self.load_role() original_instruction = role.get_next_instruction() - insights = await insight_geneartor.generate_new_instructions(task_id=role.start_task_id + 1, - original_instruction=original_instruction, - max_num=max_children, - file_path=self.state["exp_pool_path"]) + insights = await insight_geneartor.generate_new_instructions( + task_id=role.start_task_id + 1, + original_instruction=original_instruction, + max_num=max_children, + file_path=self.state["exp_pool_path"], + ) new_state = self.state.copy() - new_state['start_task_id'] += 1 + new_state["start_task_id"] += 1 for insight in insights: new_role = role.model_copy() node = Node(parent=self, state=new_state, action=insight, value=0) node.save_new_role(new_role) self.add_child(node) - + # def evaluate_test(self): # prediction_fpath = os.path.join(self.state["work_dir"], self.state["task"], "predictions.csv") # predictions = pd.read_csv(prediction_fpath)["target"] @@ -168,7 +175,7 @@ async def expand(self, max_children): # gt = pd.read_csv(os.path.join(split_datasets_dir["test_target"]))["target"] # metric = self.state["dataset_config"]["metric"] # return evaluate_score(predictions, gt, metric) - + def evaluate_prediction(self, split): pred_path = os.path.join(self.state["work_dir"], self.state["task"], f"{split}_predictions.csv") pred_node_path = os.path.join(self.state["node_dir"], f"Node-{self.id}-{split}_predictions.csv") @@ -180,21 +187,17 @@ def evaluate_prediction(self, split): # remove original predictions.csv os.remove(pred_path) return evaluate_score(preds, gt, metric) - + def evaluate_simulation(self, score_dict): - scores = { - "dev_score": self.evaluate_prediction("dev"), - "test_score": self.evaluate_prediction("test") - } + scores = {"dev_score": self.evaluate_prediction("dev"), "test_score": self.evaluate_prediction("test")} score_dict.update(scores) return score_dict - - + async def run_node(self, role=None): if self.is_terminal() and role is not None: if role.state_saved: return self.raw_reward - + max_retries = 3 num_runs = 1 run_finished = False @@ -202,10 +205,10 @@ async def run_node(self, role=None): try: if not role: role = self.load_role() - await load_execute_notebook(role) # execute previous notebook's code - await role.run(with_message='continue') + await load_execute_notebook(role) # execute previous notebook's code + await role.run(with_message="continue") else: - await role.run(with_message=self.state['requirement']) + await role.run(with_message=self.state["requirement"]) run_finished = True except Exception as e: mcts_logger.log("MCTS", f"Error in running the role: {e}") @@ -222,18 +225,19 @@ def normalize_score(score): if score == -1: return 0 return 1 / (1 + score) + score_dict = {k: normalize_score(v) for k, v in score_dict.items()} self.normalized_reward = score_dict return score_dict - -class MCTS(): - #data_path - root_node : Node = None - children : dict = {} - max_depth : int = 5 - c_explore : float = 1.4 - c_unvisited : float = 0.8 + +class MCTS: + # data_path + root_node: Node = None + children: dict = {} + max_depth: int = 5 + c_explore: float = 1.4 + c_unvisited: float = 0.8 def __init__(self, root_node, max_depth): self.root_node = root_node @@ -243,34 +247,34 @@ def select(self, node: Node): node = self.best_child() mcts_logger.log("MCTS", f"Selected node id: {node.id}") return node - + def best_child(self): def uct(node: Node): n_visits = node.visited if node.visited else self.c_unvisited - avg_value = node.avg_value() if node.visited else node.value/self.c_unvisited + avg_value = node.avg_value() if node.visited else node.value / self.c_unvisited return avg_value + self.c_explore * math.sqrt(math.log(node.parent.visited) / n_visits) + if len(self.children) == 0: return self.root_node all_children = [child for children in self.children.values() for child in children] return max(all_children, key=uct) - async def expand(self, node : Node, max_children=5): + async def expand(self, node: Node, max_children=5): await node.expand(max_children) if node not in self.children or not self.children[node]: self.children[node] = node.children return node.children - - async def simulate(self, node : Node, role=None): + + async def simulate(self, node: Node, role=None): "Returns the reward for a random simulation (to completion) of `node`" mcts_logger.log("MCTS", f"Start simulating node {node.id}:") while node.children: node = random.choice(node.children) reward = await node.run_node(role) - mcts_logger.log("MCTS", f"Simulated node's reward: {reward}") + mcts_logger.log("MCTS", f"Simulated node's reward: {reward}") return reward - - def backpropagate(self, node : Node, reward): + def backpropagate(self, node: Node, reward): child_node = node node.update(reward) node = node.parent @@ -278,10 +282,11 @@ def backpropagate(self, node : Node, reward): node.update(reward, child_node) node, child_node = node.parent, node - def best_path(self, root : Node): + def best_path(self, root: Node): best_child = root best_score = 0 - def bfs(node : Node, best_score, best_child : Node, split): + + def bfs(node: Node, best_score, best_child: Node, split): assert split in ["test_score", "dev_score"] if node not in self.children: return best_score, best_child @@ -293,19 +298,19 @@ def bfs(node : Node, best_score, best_child : Node, split): best_child = child best_score, best_child = bfs(child, best_score, best_child, split) return best_score, best_child + _, best_child = bfs(root, best_score, best_child, "test_score") _, dev_best_child = bfs(root, best_score, best_child, "dev_score") - return {"dev_best": dev_best_child, - "global_best": best_child} - + return {"dev_best": dev_best_child, "global_best": best_child} + def get_num_simulations(self): return self.root_node.visited - async def search(self, task, data_config, name, - rollouts, load_tree=False, low_is_better=False, reflection=False): - - role, root = initialize_di_root_node(task, data_config, low_is_better=low_is_better, reflection=reflection, name=name) + async def search(self, task, data_config, name, rollouts, load_tree=False, low_is_better=False, reflection=False): + role, root = initialize_di_root_node( + task, data_config, low_is_better=low_is_better, reflection=reflection, name=name + ) self.root_node = root tree_loaded = False if load_tree: @@ -314,14 +319,14 @@ async def search(self, task, data_config, name, mcts_logger.log("MCTS", f"Tree loaded: {tree_loaded}") if not tree_loaded: - rollouts -= 2 # 2 rollouts for the initial tree + rollouts -= 2 # 2 rollouts for the initial tree if rollouts < 0: raise ValueError("Rollouts must be greater than 2 if there is no tree to load") self.children[root] = [] reward = await self.simulate(root, role) self.backpropagate(root, reward) children = await self.expand(root) - #目前是随机选择1个,后续可以改成多个 + # 目前是随机选择1个,后续可以改成多个 first_leaf = random.choice(children) reward = await self.simulate(first_leaf) self.backpropagate(first_leaf, reward) @@ -339,14 +344,13 @@ async def search(self, task, data_config, name, mcts_logger.log("MCTS", f"Terminal node's reward: {reward}") self.backpropagate(node, reward) else: - if node.visited > 0: + if node.visited > 0: children = await self.expand(node) node = random.choice(children) reward = await self.simulate(node) self.backpropagate(node, reward) return self.best_path(root) - def load_tree(self): def load_children_node(node): mcts_logger.log("MCTS", f"Load node {node.id}'s child: {node.children}") @@ -356,14 +360,15 @@ def load_children_node(node): child.load_node() self.children[child] = child.children load_children_node(child) + # Load all pkl files in the node_dir all_pkl_files = os.listdir(self.root_node.state["node_dir"]) all_pkl_files = [f for f in all_pkl_files if f.endswith(".pkl")] if os.path.exists(os.path.join(self.root_node.state["node_dir"], "Node-0.pkl")): - with open(os.path.join(self.root_node.state["node_dir"], "Node-0.pkl"), 'rb') as f: + with open(os.path.join(self.root_node.state["node_dir"], "Node-0.pkl"), "rb") as f: self.root_node = pickle.load(f) self.children[self.root_node] = self.root_node.children load_children_node(self.root_node) if self.children: return True - return False \ No newline at end of file + return False diff --git a/expo/dataset.py b/expo/dataset.py index a43f1292a..fee1199a9 100644 --- a/expo/dataset.py +++ b/expo/dataset.py @@ -1,12 +1,14 @@ -import openml -from pathlib import Path -from sklearn.model_selection import train_test_split -import os +import asyncio import json -import yaml +import os +from pathlib import Path + +import openml import pandas as pd +import yaml +from sklearn.model_selection import train_test_split + from expo.insights.solution_designer import SolutionDesigner -import asyncio BASE_USER_REQUIREMENT = """\ This is a {datasetname} dataset. Your goal is to predict the target column `{target_col}`. @@ -59,14 +61,12 @@ 41980, 42225, 531, - # cls 41143, 31, 42733, 41162, 1067, - # multi cls 40498, 40982, @@ -79,14 +79,15 @@ ("04_titanic", "Survived"), ("05_house-prices-advanced-regression-techniques", "SalePrice"), ("06_santander-customer-transaction-prediction", "target"), - ("07_icr-identify-age-related-conditions", "Class") + ("07_icr-identify-age-related-conditions", "Class"), ] + def get_split_dataset_path(dataset_name, config): - datasets_dir = config['datasets_dir'] - if dataset_name in config['datasets']: - dataset = config['datasets'][dataset_name] - data_path = os.path.join(datasets_dir, dataset['dataset']) + datasets_dir = config["datasets_dir"] + if dataset_name in config["datasets"]: + dataset = config["datasets"][dataset_name] + data_path = os.path.join(datasets_dir, dataset["dataset"]) split_datasets = { "train": os.path.join(data_path, "split_train.csv"), "dev": os.path.join(data_path, "split_dev.csv"), @@ -98,32 +99,39 @@ def get_split_dataset_path(dataset_name, config): } return split_datasets else: - raise ValueError(f"Dataset {dataset_name} not found in config file. Available datasets: {config['datasets'].keys()}") + raise ValueError( + f"Dataset {dataset_name} not found in config file. Available datasets: {config['datasets'].keys()}" + ) + def get_user_requirement(task_name, config): - datasets_dir = config['datasets_dir'] - if task_name in config['datasets']: - dataset = config['datasets'][task_name] - data_path = os.path.join(datasets_dir, dataset['dataset']) - user_requirement = dataset['user_requirement'] + datasets_dir = config["datasets_dir"] + if task_name in config["datasets"]: + dataset = config["datasets"][task_name] + data_path = os.path.join(datasets_dir, dataset["dataset"]) + user_requirement = dataset["user_requirement"] return data_path, user_requirement else: - raise ValueError(f"Dataset {task_name} not found in config file. Available datasets: {config['datasets'].keys()}") + raise ValueError( + f"Dataset {task_name} not found in config file. Available datasets: {config['datasets'].keys()}" + ) def save_datasets_dict_to_yaml(datasets_dict): with open("datasets.yaml", "w") as file: yaml.dump(datasets_dict, file) + def create_dataset_dict(dataset): dataset_dict = { "dataset": dataset.name, "user_requirement": dataset.create_base_requirement(), "metric": dataset.get_metric(), - "target_col": dataset.target_col + "target_col": dataset.target_col, } return dataset_dict + def generate_task_requirement(task_name, data_config): user_requirement = get_user_requirement(task_name, data_config) split_dataset_path = get_split_dataset_path(task_name, data_config) @@ -132,19 +140,23 @@ def generate_task_requirement(task_name, data_config): test_path = split_dataset_path["test_wo_target"] work_dir = data_config["work_dir"] output_dir = f"{work_dir}/{task_name}" - user_requirement = TASK_PROMPT.format(user_requirement=user_requirement, - train_path=train_path, dev_path=dev_path, test_path=test_path, - output_dir=output_dir) + user_requirement = TASK_PROMPT.format( + user_requirement=user_requirement, + train_path=train_path, + dev_path=dev_path, + test_path=test_path, + output_dir=output_dir, + ) print(user_requirement) return user_requirement class ExpDataset: - description : str = None - metadata : dict = None - dataset_dir : str = None - target_col : str = None - name : str = None + description: str = None + metadata: dict = None + dataset_dir: str = None + target_col: str = None + name: str = None def __init__(self, name, dataset_dir, **kwargs): self.name = name @@ -154,18 +166,23 @@ def __init__(self, name, dataset_dir, **kwargs): self.save_dataset(target_col=self.target_col) def check_dataset_exists(self): - fnames = ["split_train.csv", "split_dev.csv", "split_test.csv", - "split_dev_wo_target.csv", "split_dev_target.csv", - "split_test_wo_target.csv", "split_test_target.csv"] + fnames = [ + "split_train.csv", + "split_dev.csv", + "split_test.csv", + "split_dev_wo_target.csv", + "split_dev_target.csv", + "split_test_wo_target.csv", + "split_test_target.csv", + ] for fname in fnames: if not os.path.exists(Path(self.dataset_dir, self.name, fname)): return False return True - + def check_datasetinfo_exists(self): return os.path.exists(Path(self.dataset_dir, self.name, "dataset_info.json")) - def get_raw_dataset(self): raw_dir = Path(self.dataset_dir, self.name, "raw") if not os.path.exists(Path(raw_dir, "train.csv")): @@ -173,17 +190,17 @@ def get_raw_dataset(self): else: df = pd.read_csv(Path(raw_dir, "train.csv")) return df - + def get_dataset_info(self): raw_df = pd.read_csv(Path(self.dataset_dir, self.name, "raw", "train.csv")) metadata = { - 'NumberOfClasses': raw_df[self.target_col].nunique(), - 'NumberOfFeatures': raw_df.shape[1], - 'NumberOfInstances': raw_df.shape[0], - 'NumberOfInstancesWithMissingValues': int(raw_df.isnull().any(axis=1).sum()), - 'NumberOfMissingValues': int(raw_df.isnull().sum().sum()), - 'NumberOfNumericFeatures': raw_df.select_dtypes(include=['number']).shape[1], - 'NumberOfSymbolicFeatures': raw_df.select_dtypes(include=['object']).shape[1], + "NumberOfClasses": raw_df[self.target_col].nunique(), + "NumberOfFeatures": raw_df.shape[1], + "NumberOfInstances": raw_df.shape[0], + "NumberOfInstancesWithMissingValues": int(raw_df.isnull().any(axis=1).sum()), + "NumberOfMissingValues": int(raw_df.isnull().sum().sum()), + "NumberOfNumericFeatures": raw_df.select_dtypes(include=["number"]).shape[1], + "NumberOfSymbolicFeatures": raw_df.select_dtypes(include=["object"]).shape[1], } df_head_text = raw_df.head().to_string(index=False) @@ -193,10 +210,10 @@ def get_dataset_info(self): "description": "", "target_col": self.target_col, "metadata": metadata, - "df_head": df_head_text + "df_head": df_head_text, } return dataset_info - + def get_metric(self): dataset_info = self.get_dataset_info() num_classes = dataset_info["metadata"]["NumberOfClasses"] @@ -216,7 +233,6 @@ def create_base_requirement(self): return req def save_dataset(self, target_col): - df = self.get_raw_dataset() if not self.check_dataset_exists() or self.force_update: print(f"Saving Dataset {self.name} in {self.dataset_dir}") @@ -249,25 +265,22 @@ def save_split_datasets(self, df, split, target_col=None): def split_and_save(self, df, target_col): if not target_col: raise ValueError("Target column not provided") - train, test = train_test_split(df, test_size=1-TRAIN_TEST_SPLIT, random_state=SEED) - train, dev = train_test_split(train, test_size=1-TRAIN_DEV_SPLIT, random_state=SEED) + train, test = train_test_split(df, test_size=1 - TRAIN_TEST_SPLIT, random_state=SEED) + train, dev = train_test_split(train, test_size=1 - TRAIN_DEV_SPLIT, random_state=SEED) self.save_split_datasets(train, "train") self.save_split_datasets(dev, "dev", target_col) self.save_split_datasets(test, "test", target_col) - - + class OpenMLExpDataset(ExpDataset): def __init__(self, name, dataset_dir, dataset_id, **kwargs): self.dataset_id = dataset_id - self.dataset = openml.datasets.get_dataset(self.dataset_id, - download_data=False, - download_qualities=False, - download_features_meta_data=True) + self.dataset = openml.datasets.get_dataset( + self.dataset_id, download_data=False, download_qualities=False, download_features_meta_data=True + ) self.name = self.dataset.name self.target_col = self.dataset.default_target_attribute super().__init__(self.name, dataset_dir, target_col=self.target_col, **kwargs) - def get_raw_dataset(self): dataset = self.dataset @@ -276,7 +289,7 @@ def get_raw_dataset(self): os.makedirs(raw_dir, exist_ok=True) dataset_df.to_csv(Path(raw_dir, "train.csv"), index=False) return dataset_df - + def get_dataset_info(self): dataset_info = super().get_dataset_info() dataset = self.dataset @@ -290,12 +303,14 @@ def get_dataset_info(self): # def __init__(self, name, dataset_dir, dataset_name, **kwargs): # super().__init__(name, dataset_dir, **kwargs) + async def process_dataset(dataset, solution_designer, save_analysis_pool, datasets_dict): if save_analysis_pool: asyncio.run(solution_designer.generate_solutions(dataset.get_dataset_info(), dataset.name)) dataset_dict = create_dataset_dict(dataset) datasets_dict["datasets"][dataset.name] = dataset_dict + if __name__ == "__main__": datasets_dir = "D:/work/automl/datasets" force_update = False diff --git a/expo/evaluation/evaluation.py b/expo/evaluation/evaluation.py index 886bc036d..16b3acb71 100644 --- a/expo/evaluation/evaluation.py +++ b/expo/evaluation/evaluation.py @@ -1,5 +1,6 @@ -from sklearn.metrics import f1_score, accuracy_score, roc_auc_score, mean_squared_error import numpy as np +from sklearn.metrics import accuracy_score, f1_score, mean_squared_error, roc_auc_score + def evaluate_score(pred, gt, metric): if metric == "accuracy": @@ -20,4 +21,4 @@ def evaluate_score(pred, gt, metric): elif metric == "log rmse": return mean_squared_error(np.log1p(gt), np.log1p(pred), squared=False) else: - raise ValueError(f"Metric {metric} not supported") \ No newline at end of file + raise ValueError(f"Metric {metric} not supported") diff --git a/expo/evaluation/visualize_mcts.py b/expo/evaluation/visualize_mcts.py index 4199def0e..d310036c0 100644 --- a/expo/evaluation/visualize_mcts.py +++ b/expo/evaluation/visualize_mcts.py @@ -1,7 +1,7 @@ - -from expo.MCTS import Node, MCTS import textwrap +from expo.MCTS import Node + NODE_TEMPLATE = """\ [Node {id}] Plans: @@ -11,21 +11,23 @@ """ + def get_role_plans(role): plans = role.planner.plan.tasks instruct_plans = [f"{i+1}. {task.instruction}" for i, task in enumerate(plans)] return instruct_plans -def get_tree_text(node : Node): +def get_tree_text(node: Node): role_dict = {} code_set = set() + def load_role(node): if node.id not in role_dict: role_dict[node.id] = node.load_role() return role_dict[node.id] - - def visualize_node(node : Node, previous_plans=None): + + def visualize_node(node: Node, previous_plans=None): role = load_role(node) node_id = node.id plans = role.planner.plan.tasks @@ -36,7 +38,9 @@ def visualize_node(node : Node, previous_plans=None): simulated = role.state_saved score = f"avg score: {node.avg_value()}, simulated score: {node.raw_reward}" num_visits = node.visited - return NODE_TEMPLATE.format(id=node_id, plans=instruct_plans_text, simulated=simulated, score=score, num_visits=num_visits) + return NODE_TEMPLATE.format( + id=node_id, plans=instruct_plans_text, simulated=simulated, score=score, num_visits=num_visits + ) def visualize_tree(node, depth=0, previous_plans=None): text = "" @@ -46,11 +50,10 @@ def visualize_tree(node, depth=0, previous_plans=None): code_set.update({task.instruction for task in role.planner.plan.tasks}) previous_plans = get_role_plans(role) for child in node.children: - text += textwrap.indent(visualize_tree(child, depth+1, previous_plans), "\t") + text += textwrap.indent(visualize_tree(child, depth + 1, previous_plans), "\t") return text + num_simulations = node.visited text = f"Number of simulations: {num_simulations}\n" text += visualize_tree(node) return text, len(code_set) - - diff --git a/expo/experimenter/__init__.py b/expo/experimenter/__init__.py index 2eab295f7..e69de29bb 100644 --- a/expo/experimenter/__init__.py +++ b/expo/experimenter/__init__.py @@ -1,4 +0,0 @@ -from .experimenter import Experimenter -from .mcts import MCTSExperimenter -from .aug import AugExperimenter -from .custom import CustomExperimenter \ No newline at end of file diff --git a/expo/experimenter/aug.py b/expo/experimenter/aug.py index 86c98fd42..9b14123d3 100644 --- a/expo/experimenter/aug.py +++ b/expo/experimenter/aug.py @@ -1,9 +1,8 @@ from experimenter import Experimenter -from expo.MCTS import create_initial_state -from expo.dataset import generate_task_requirement -from expo.utils import mcts_logger, load_execute_notebook, get_exp_pool_path + from expo.insights.instruction_generator import InstructionGenerator from expo.research_assistant import ResearchAssistant +from expo.utils import get_exp_pool_path EXPS_PROMPT = """ When doing the tasks, you can refer to the insights below: @@ -12,10 +11,8 @@ """ - - class AugExperimenter(Experimenter): - result_path : str = "results/aug" + result_path: str = "results/aug" async def run_experiment(self): # state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") @@ -31,7 +28,7 @@ async def run_experiment(self): exps = [exp_set_text] * self.args.num_experiments else: raise ValueError(f"Invalid mode: {self.args.aug_mode}") - + results = [] for i in range(self.args.num_experiments): di = ResearchAssistant(node_id=str(i), use_reflection=self.args.reflection) @@ -39,20 +36,19 @@ async def run_experiment(self): requirement = user_requirement + EXPS_PROMPT.format(experience=exps[i]) print(requirement) score_dict = await self.run_di(di, requirement) - results.append({ - "idx": i, - "score_dict": score_dict, - "aug_mode": self.args.aug_mode, - "insights" : exps[i], - "user_requirement": requirement, - "args": vars(self.args) - }) + results.append( + { + "idx": i, + "score_dict": score_dict, + "aug_mode": self.args.aug_mode, + "insights": exps[i], + "user_requirement": requirement, + "args": vars(self.args), + } + ) scores = [result["score_dict"]["test_score"] for result in results] avg_score = sum(scores) / len(scores) best_score = max(scores) if not self.args.low_is_better else min(scores) best_score_idx = scores.index(best_score) results.insert(0, {"avg_score": avg_score, "best_score": best_score, "best_score_idx": best_score_idx}) self.save_result(results) - - - \ No newline at end of file diff --git a/expo/experimenter/autogluon.py b/expo/experimenter/autogluon.py index d59fb3e83..4f5d151ef 100644 --- a/expo/experimenter/autogluon.py +++ b/expo/experimenter/autogluon.py @@ -1,13 +1,15 @@ -from expo.experimenter.custom import CustomExperimenter from autogluon.tabular import TabularDataset, TabularPredictor -class AGRunner(): +from expo.experimenter.custom import CustomExperimenter + + +class AGRunner: preset = "best_quality" time_limit = 500 def __init__(self, datasets): self.datasets = datasets - + def run(self): train_path = self.datasets["train"] test_wo_target_path = self.datasets["test_wo_target"] @@ -16,17 +18,16 @@ def run(self): train_data = TabularDataset(train_path) test_data = TabularDataset(test_wo_target_path) dev_data = TabularDataset(dev_wo_target_path) - + predictor = TabularPredictor(label=target_col).fit(train_data, presets=self.preset, time_limit=self.time_limit) test_preds = predictor.predict(test_data) dev_preds = predictor.predict(dev_data) return {"test_preds": test_preds, "dev_preds": dev_preds} + class GluonExperimenter(CustomExperimenter): - result_path : str = "results/autogluon" + result_path: str = "results/autogluon" def __init__(self, args, **kwargs): super().__init__(args, **kwargs) self.framework = AGRunner(self.datasets) - - diff --git a/expo/experimenter/custom.py b/expo/experimenter/custom.py index c3cb97b9c..ba009bdb0 100644 --- a/expo/experimenter/custom.py +++ b/expo/experimenter/custom.py @@ -1,21 +1,26 @@ +import os + +import pandas as pd + +from expo.evaluation.evaluation import evaluate_score from expo.experimenter import Experimenter from expo.MCTS import create_initial_state -from expo.evaluation.evaluation import evaluate_score -import pandas as pd -import os + class CustomExperimenter(Experimenter): - result_path : str = "results/custom" - + result_path: str = "results/custom" + def __init__(self, args, **kwargs): super().__init__(args, **kwargs) - self.framework = kwargs["framework"] # todo + self.framework = kwargs["framework"] # todo self.task = kwargs.get("task", self.args.task) self.low_is_better = kwargs.get("low_is_better", self.args.low_is_better) self.name = kwargs.get("name", "") self.result_path = f"results/custom_{self.name}" - self.state = create_initial_state(self.task, start_task_id=1, data_config=self.data_config, low_is_better=self.low_is_better, name=self.name) - + self.state = create_initial_state( + self.task, start_task_id=1, data_config=self.data_config, low_is_better=self.low_is_better, name=self.name + ) + def run_experiment(self): user_requirement = self.state["requirement"] preds = self.framework.run(user_requirement) @@ -23,13 +28,9 @@ def run_experiment(self): dev_preds = preds["dev_preds"] score_dict = { "dev_score": self.evaluate_predictions(dev_preds, "dev"), - "test_score": self.evaluate_predictions(test_preds, "test") - } - results = { - "score_dict": score_dict, - "user_requirement": user_requirement, - "args": vars(self.args) + "test_score": self.evaluate_predictions(test_preds, "test"), } + results = {"score_dict": score_dict, "user_requirement": user_requirement, "args": vars(self.args)} self.save_result(results) def evaluate_pred_files(self, dev_pred_path, test_pred_path): @@ -37,7 +38,7 @@ def evaluate_pred_files(self, dev_pred_path, test_pred_path): test_preds = pd.read_csv(test_pred_path)["target"] score_dict = { "dev_score": self.evaluate_score(dev_preds, "dev"), - "test_score": self.evaluate_score(test_preds, "test") + "test_score": self.evaluate_score(test_preds, "test"), } return score_dict @@ -46,8 +47,7 @@ def evaluate_predictions(self, preds, split): gt_path = os.path.join(self.state["datasets_dir"][f"{split}_target"]) gt = pd.read_csv(gt_path)["target"] score = evaluate_score(preds, gt, metric) - return score - + return score def load_datasets(self): train_path = self.state["datasets_dir"]["train"] @@ -57,4 +57,3 @@ def load_datasets(self): dev = pd.read_csv(dev_path) test = pd.read_csv(test_path) return train, dev, test - diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 709eefdfc..83dde80b9 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -1,23 +1,29 @@ -from expo.utils import DATA_CONFIG +import datetime +import json import os + import pandas as pd + from expo.evaluation.evaluation import evaluate_score -import datetime -import json from expo.MCTS import create_initial_state from expo.research_assistant import ResearchAssistant +from expo.utils import DATA_CONFIG class Experimenter: - result_path : str = "results/base" + result_path: str = "results/base" data_config = DATA_CONFIG - def __init__(self, args, **kwargs): self.args = args self.start_time = datetime.datetime.now().strftime("%Y%m%d%H%M") - self.state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") - + self.state = create_initial_state( + self.args.task, + start_task_id=1, + data_config=self.data_config, + low_is_better=self.args.low_is_better, + name="", + ) async def run_di(self, di, user_requirement): max_retries = 3 @@ -33,14 +39,8 @@ async def run_di(self, di, user_requirement): print(f"Error: {e}") num_runs += 1 if not run_finished: - score_dict = { - "train_score": -1, - "dev_score": -1, - "test_score": -1, - "score": -1 - } + score_dict = {"train_score": -1, "dev_score": -1, "test_score": -1, "score": -1} return score_dict - async def run_experiment(self): state = self.state @@ -50,28 +50,28 @@ async def run_experiment(self): for i in range(self.args.num_experiments): di = ResearchAssistant(node_id="0", use_reflection=self.args.reflection) score_dict = await self.run_di(di, user_requirement) - results.append({ - "idx": i, - "score_dict": score_dict, - "user_requirement": user_requirement, - "args": vars(self.args) - }) - self.save_result(results) # save intermediate results + results.append( + {"idx": i, "score_dict": score_dict, "user_requirement": user_requirement, "args": vars(self.args)} + ) + self.save_result(results) # save intermediate results dev_scores = [result["score_dict"]["dev_score"] for result in results] best_dev_score = max(dev_scores) if not self.args.low_is_better else min(dev_scores) best_score_idx = dev_scores.index(best_dev_score) - + test_scores = [result["score_dict"]["test_score"] for result in results] avg_score = sum(test_scores) / len(test_scores) global_best_score = max(test_scores) if not self.args.low_is_better else min(test_scores) - results.insert(0, { - "best_dev_score": best_dev_score, - "best_score_idx": best_score_idx, - "best_test_score": test_scores[best_score_idx], - "avg_test_score": avg_score, - "best_score": global_best_score - }) + results.insert( + 0, + { + "best_dev_score": best_dev_score, + "best_score_idx": best_score_idx, + "best_test_score": test_scores[best_score_idx], + "avg_test_score": avg_score, + "best_score": global_best_score, + }, + ) self.save_result(results) def evaluate_prediction(self, split, state): @@ -85,7 +85,7 @@ def evaluate_prediction(self, split, state): metric = state["dataset_config"]["metric"] os.remove(pred_path) return evaluate_score(preds, gt, metric) - + def evaluate(self, score_dict, state): scores = { "dev_score": self.evaluate_prediction("dev", state), @@ -94,13 +94,12 @@ def evaluate(self, score_dict, state): score_dict.update(scores) return score_dict - def save_result(self, result): end_time = datetime.datetime.now().strftime("%Y%m%d%H%M") time_info = { "start_time": self.start_time, "end_time": end_time, - "duration (minutes)": float(end_time) - float(self.start_time) + "duration (minutes)": float(end_time) - float(self.start_time), } result = result.copy() result.insert(0, time_info) diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index e41f94d58..921b81412 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -1,22 +1,25 @@ +from expo.evaluation.visualize_mcts import get_tree_text from expo.experimenter import Experimenter -from expo.dataset import generate_task_requirement from expo.MCTS import MCTS -from expo.evaluation.visualize_mcts import get_tree_text class MCTSExperimenter(Experimenter): - result_path : str = "results/mcts" + result_path: str = "results/mcts" + async def run_experiment(self): mcts = MCTS(root_node=None, max_depth=5) - best_nodes = await mcts.search(self.args.task, self.data_config, - low_is_better=self.args.low_is_better, - load_tree=self.args.load_tree, - reflection=self.args.reflection, - rollouts=self.args.rollouts, - name=self.args.name) + best_nodes = await mcts.search( + self.args.task, + self.data_config, + low_is_better=self.args.low_is_better, + load_tree=self.args.load_tree, + reflection=self.args.reflection, + rollouts=self.args.rollouts, + name=self.args.name, + ) best_node = best_nodes["global_best"] dev_best_node = best_nodes["dev_best"] - + text, num_generated_codes = get_tree_text(mcts.root_node) text += f"Generated {num_generated_codes} unique codes.\n" text += f"Best node: {best_node}, score: {best_node.raw_reward}\n" @@ -24,22 +27,21 @@ async def run_experiment(self): print(text) self.save_tree(text) - results = [{ - "best_node": best_node.id, - "best_node_score": best_node.raw_reward, - "dev_best_node": dev_best_node.id, - "dev_best_node_score": dev_best_node.raw_reward, - "num_generated_codes": num_generated_codes, - "user_requirement": best_node.state["requirement"], - "tree_text": text, - "args": vars(self.args) - }] + results = [ + { + "best_node": best_node.id, + "best_node_score": best_node.raw_reward, + "dev_best_node": dev_best_node.id, + "dev_best_node_score": dev_best_node.raw_reward, + "num_generated_codes": num_generated_codes, + "user_requirement": best_node.state["requirement"], + "tree_text": text, + "args": vars(self.args), + } + ] self.save_result(results) - - def save_tree(self, tree_text): fpath = f"{self.result_path}/{self.args.task}_tree_{self.args.name}.txt" with open(fpath, "w") as f: f.write(tree_text) - diff --git a/expo/insights/instruction_generator.py b/expo/insights/instruction_generator.py index 4f4155ff8..065565c89 100644 --- a/expo/insights/instruction_generator.py +++ b/expo/insights/instruction_generator.py @@ -1,3 +1,10 @@ +import json +import random + +from expo.utils import clean_json_from_rsp, load_data_config, mcts_logger +from metagpt.llm import LLM +from metagpt.schema import Message + REFLECTION_SYSTEM_MSG = "As a Kaggle grandmaster participating in a competition, you need to analyze your experience and propose evolutionary points that are more likely to improve the performance of baseline code." CHANGE_INSTRUCTION = """ @@ -18,12 +25,6 @@ ``` """ -import re -import random -import json -from metagpt.llm import LLM -from metagpt.schema import Message -from expo.utils import load_data_config, mcts_logger, clean_json_from_rsp DATA_CONFIG = load_data_config() @@ -31,7 +32,7 @@ class InstructionGenerator: data_config = DATA_CONFIG @staticmethod - def load_json_data(json_dir): + def load_json_data(json_dir): with open(json_dir, "r") as file: json_data = json.load(file) return json_data @@ -39,7 +40,7 @@ def load_json_data(json_dir): @staticmethod def _random_sample(analysis, num_samples): return random.sample(analysis, num_samples) - + @staticmethod def sample_instruction_set(data): data_dict = {} @@ -52,12 +53,12 @@ def sample_instruction_set(data): for task_id in sorted(data_dict.keys()): instruction_set.append(random.choice(data_dict[task_id])) return instruction_set - + @staticmethod def format_output(rsp): rsp_list = [] - new_data = [] - rsp_list.append(rsp) + new_data = [] + rsp_list.append(rsp) for item in rsp_list: item_dict = json.loads(item) data = { @@ -83,21 +84,19 @@ async def generate_new_instructions(task_id, original_instruction, max_num, file new_instructions = [] if len(data) == 0: mcts_logger.log("MCTS", f"No insights available for task {task_id}") - return [original_instruction] # Return the original instruction if no insights are available + return [original_instruction] # Return the original instruction if no insights are available for item in data[:max_num]: insights = item["Analysis"] new_instruction = await InstructionGenerator.generate_new_instruction(original_instruction, insights) new_instructions.append(new_instruction) return new_instructions - + @staticmethod async def generate_new_instruction(original_instruction, insights): prompt = CHANGE_INSTRUCTION.format(instruction=original_instruction, insights=insights) llm = LLM() - context = llm.format_msg([Message(content=prompt, role="user")]) - llm_response = await llm.aask( - context, system_msgs=[REFLECTION_SYSTEM_MSG] - ) + context = llm.format_msg([Message(content=prompt, role="user")]) + llm_response = await llm.aask(context, system_msgs=[REFLECTION_SYSTEM_MSG]) rsp = clean_json_from_rsp(llm_response) new_instruction = json.loads(rsp)["New Instruction"] - return new_instruction \ No newline at end of file + return new_instruction diff --git a/expo/insights/solution_designer.py b/expo/insights/solution_designer.py index e2bf57ae3..fc05afeea 100644 --- a/expo/insights/solution_designer.py +++ b/expo/insights/solution_designer.py @@ -1,10 +1,7 @@ -import re -import random import json -from metagpt.llm import LLM -from metagpt.schema import Message -from expo.utils import clean_json_from_rsp, load_data_config +from expo.utils import clean_json_from_rsp, load_data_config +from metagpt.llm import LLM DATA_CONFIG = load_data_config() @@ -72,56 +69,50 @@ """ KEY_DATASET_FEATURES = [ - 'NumberOfClasses', - 'NumberOfFeatures', - 'NumberOfInstances', - 'NumberOfInstancesWithMissingValues', - 'NumberOfMissingValues', - 'NumberOfNumericFeatures', - 'NumberOfSymbolicFeatures' + "NumberOfClasses", + "NumberOfFeatures", + "NumberOfInstances", + "NumberOfInstancesWithMissingValues", + "NumberOfMissingValues", + "NumberOfNumericFeatures", + "NumberOfSymbolicFeatures", ] -TASK_TO_ID = { - "EDA": 1, - "Data Preprocessing": 2, - "Feature Engineering": 3, - "Model Training": 4, - "Model Evaluation": 5 -} +TASK_TO_ID = {"EDA": 1, "Data Preprocessing": 2, "Feature Engineering": 3, "Model Training": 4, "Model Evaluation": 5} + class SolutionDesigner: - data_dir : str= DATA_CONFIG["datasets_dir"] + data_dir: str = DATA_CONFIG["datasets_dir"] async def generate_solutions(self, dataset_info, dataset_name): llm = LLM() - context = DATASET_INSIGHT_PROMPT.format(dataset=dataset_info["description"], - metadata=self.metadata_builder(dataset_info["metadata"]), - head=dataset_info["df_head"]) + context = DATASET_INSIGHT_PROMPT.format( + dataset=dataset_info["description"], + metadata=self.metadata_builder(dataset_info["metadata"]), + head=dataset_info["df_head"], + ) rsp = await llm.aask(context) rsp = clean_json_from_rsp(rsp) analysis_pool = self.process_analysis_pool(json.loads(rsp)) dataset_path = f"{self.data_dir}/{dataset_name}" self.save_analysis_pool(dataset_path, analysis_pool) - - + def process_analysis_pool(self, insights_rsp): analysis_pool = [] for task_type_insights in insights_rsp: task_type = task_type_insights["task_type"] for insight in task_type_insights["insights"]: - analysis_pool.append({"Analysis": insight, "Category": task_type, "task_id": TASK_TO_ID[task_type]}) + analysis_pool.append({"Analysis": insight, "Category": task_type, "task_id": TASK_TO_ID[task_type]}) return analysis_pool - def metadata_builder(self, qualities): metadata = {} for key in KEY_DATASET_FEATURES: metadata[key] = qualities.get(key, "N/A") metadata_text = json.dumps(metadata, indent=4) return metadata_text - + def save_analysis_pool(self, dataset_path, analysis_pool): fpath = f"{dataset_path}/ds_analysis_pool.json" with open(fpath, "w") as file: json.dump(analysis_pool, file, indent=4) - \ No newline at end of file diff --git a/expo/research_assistant.py b/expo/research_assistant.py index ed935b4b8..b21fc1a55 100644 --- a/expo/research_assistant.py +++ b/expo/research_assistant.py @@ -1,19 +1,16 @@ from __future__ import annotations import json +import os + +from pydantic import model_validator + +from expo.utils import mcts_logger, save_notebook +from metagpt.actions.di.write_analysis_code import WriteAnalysisCode +from metagpt.const import SERDESER_PATH from metagpt.roles.di.data_interpreter import DataInterpreter from metagpt.schema import Message, Task, TaskResult -from metagpt.strategy.task_type import TaskType -from metagpt.tools.tool_recommend import BM25ToolRecommender, ToolRecommender -from metagpt.utils.common import CodeParser -from metagpt.utils.common import write_json_file, read_json_file, format_trackback_info -from metagpt.const import MESSAGE_ROUTE_TO_ALL, SERDESER_PATH -from expo.utils import mcts_logger, save_notebook -from pydantic import Field, model_validator -from metagpt.actions.di.write_analysis_code import CheckData, WriteAnalysisCode - -import re -import os +from metagpt.utils.common import CodeParser, write_json_file EXTRACT_SCORE_PROMPT = """ # Code: @@ -36,39 +33,48 @@ ``` """ + class ResearchAssistant(DataInterpreter): node_id: str = "0" start_task_id: int = 1 - state_saved : bool = False - role_dir : str = SERDESER_PATH.joinpath("team", "environment", "roles", f"Experimenter") + state_saved: bool = False + role_dir: str = SERDESER_PATH.joinpath("team", "environment", "roles", "Experimenter") def get_node_name(self): return f"Node-{self.node_id}" - + def get_next_instruction(self): return self.planner.plan.tasks[self.start_task_id] - + def change_next_instruction(self, new_instruction): if new_instruction is not None: self.planner.plan.task_map[str(self.start_task_id)].instruction = new_instruction self.remap_tasks() - def update_til_start_task(self, role: ResearchAssistant, backward: bool = True): if backward: # make sure the previous task instructions are matched - assert self.start_task_id == role.start_task_id - 1, f"start_task_id: {self.start_task_id}, role.start_task_id: {role.start_task_id}" + assert ( + self.start_task_id == role.start_task_id - 1 + ), f"start_task_id: {self.start_task_id}, role.start_task_id: {role.start_task_id}" for i in range(self.start_task_id): - if self.planner.plan.task_map[str(self.start_task_id)].instruction != role.planner.plan.task_map[str(self.start_task_id)].instruction: + if ( + self.planner.plan.task_map[str(self.start_task_id)].instruction + != role.planner.plan.task_map[str(self.start_task_id)].instruction + ): mcts_logger.info("Previous task instructions not matched") self.remap_tasks() return # copy new role's task (self.start_task_id) to current role - self.planner.plan.task_map[str(self.start_task_id)] = role.planner.plan.task_map[str(self.start_task_id)].model_copy() + self.planner.plan.task_map[str(self.start_task_id)] = role.planner.plan.task_map[ + str(self.start_task_id) + ].model_copy() self.remap_tasks() else: - assert self.start_task_id == role.start_task_id + 1, f"start_task_id: {self.start_task_id}, role.start_task_id: {role.start_task_id}" + assert ( + self.start_task_id == role.start_task_id + 1 + ), f"start_task_id: {self.start_task_id}, role.start_task_id: {role.start_task_id}" if int(role.planner.plan.current_task_id) > self.start_task_id: for i in range(role.start_task_id): self.planner.plan.task_map[str(i)] = role.planner.plan.task_map[str(i)].model_copy() @@ -86,11 +92,10 @@ async def llm_extract_score(self): json_block = CodeParser.parse_code(block=None, text=rsp) score_dict = json.loads(json_block) return score_dict - @model_validator(mode="after") def set_plan_and_tool(self) -> "Interpreter": - if self.planner.plan.goal != '': + if self.planner.plan.goal != "": self.set_actions([WriteAnalysisCode]) self._set_state(0) print("Plan already exists, skipping initialization.") @@ -116,17 +121,17 @@ def save_state(self, static_save=False): self.state_saved = True mcts_logger.log("MCTS", f"Saving state at task {self.start_task_id}") else: - mcts_logger.log("MCTS", f"Static Saving") + mcts_logger.log("MCTS", "Static Saving") stg_path = self.role_dir name = self.get_node_name() role_path = os.path.join(stg_path, f"{name}.json") # 将状态保存为 JSON 文件 write_json_file(role_path, self.model_dump()) - def remap_tasks(self): - self.planner.plan.tasks = [self.planner.plan.task_map[task_id] for task_id in sorted(self.planner.plan.task_map.keys())] - + self.planner.plan.tasks = [ + self.planner.plan.task_map[task_id] for task_id in sorted(self.planner.plan.task_map.keys()) + ] async def run(self, with_message=None) -> Message | None: """Observe, and think and act based on the results of the observation""" @@ -138,13 +143,9 @@ async def run(self, with_message=None) -> Message | None: self.rc.working_memory.clear() self.working_memory.clear() # self.rc.todo = WriteAnalysisCode() - rsp = await self.react() + rsp = await self.react() # 发送响应消息给 Environment 对象,以便它将消息传递给订阅者 self.set_todo(None) self.publish_message(rsp) return rsp return await super().run(with_message) - - - - \ No newline at end of file diff --git a/expo/run_exp_augmentation.py b/expo/run_exp_augmentation.py index 3f8eff3b3..7fb174ff7 100644 --- a/expo/run_exp_augmentation.py +++ b/expo/run_exp_augmentation.py @@ -1,15 +1,17 @@ -import os -from expo.research_assistant import ResearchAssistant +import argparse import asyncio -from expo.utils import DATA_CONFIG, get_exp_pool_path +import datetime +import json +import os + +import pandas as pd + from expo.dataset import generate_task_requirement +from expo.evaluation.evaluation import evaluate_score from expo.insights.instruction_generator import InstructionGenerator from expo.MCTS import create_initial_state -from expo.evaluation.evaluation import evaluate_score -import json -import argparse -import pandas as pd -import datetime +from expo.research_assistant import ResearchAssistant +from expo.utils import DATA_CONFIG, get_exp_pool_path EXPS_PROMPT = """ When doing the tasks, you can refer to the insights below: @@ -18,13 +20,14 @@ """ data_config = DATA_CONFIG + def evaluate_test(score, state): datetime_text = datetime.datetime.now().strftime("%Y%m%d%H%M") task_name = state["task"] prediction_fpath = os.path.join(state["work_dir"], task_name, "predictions.csv") predictions = pd.read_csv(prediction_fpath)["target"] # copy predictions.csv to the node_dir - + predictions_node_fpath = os.path.join("results", f"{task_name}-{datetime_text}-predictions.csv") predictions.to_csv(predictions_node_fpath, index=False) # load test_target.csv @@ -35,8 +38,6 @@ def evaluate_test(score, state): return score - - async def main(task_name, use_reflection=True, mode="single", num_experiments=2): """ mode: single or set @@ -44,8 +45,10 @@ async def main(task_name, use_reflection=True, mode="single", num_experiments=2) set: sample a set of instructions """ low_is_better = False - state = create_initial_state(task_name, start_task_id=1, data_config=data_config, low_is_better=low_is_better, name="") - + state = create_initial_state( + task_name, start_task_id=1, data_config=data_config, low_is_better=low_is_better, name="" + ) + user_requirement = generate_task_requirement(task_name, data_config) exp_pool_path = get_exp_pool_path(task_name, data_config, pool_name="ds_analysis_pool") exp_pool = InstructionGenerator.load_analysis_pool(exp_pool_path) @@ -58,7 +61,7 @@ async def main(task_name, use_reflection=True, mode="single", num_experiments=2) exps = [exp_set_text] * num_experiments else: raise ValueError(f"Invalid mode: {mode}") - + scores = [] for i in range(num_experiments): di = ResearchAssistant(node_id=str(i), use_reflection=use_reflection) @@ -70,16 +73,18 @@ async def main(task_name, use_reflection=True, mode="single", num_experiments=2) score = evaluate_test(score, state) scores.append(score) - with open(f"results/{task_name}_scores.json", "w") as f: # save scores and corresponding insights - results = {"avg_score": sum([score["test_score"] for score in scores if score])/num_experiments, - "max_score": max([score["test_score"] for score in scores]), - "scores": scores, "insights": exps} + results = { + "avg_score": sum([score["test_score"] for score in scores if score]) / num_experiments, + "max_score": max([score["test_score"] for score in scores]), + "scores": scores, + "insights": exps, + } json.dump(results, f, indent=4) - - + + def parse_args(): parser = argparse.ArgumentParser() parser.add_argument("--task", type=str, default="titanic") @@ -90,8 +95,9 @@ def parse_args(): parser.add_argument("--num_experiments", type=int, default=2) return parser.parse_args() - if __name__ == "__main__": args = parse_args() - asyncio.run(main(args.task, use_reflection=args.use_reflection, mode=args.mode, num_experiments=args.num_experiments)) + asyncio.run( + main(args.task, use_reflection=args.use_reflection, mode=args.mode, num_experiments=args.num_experiments) + ) diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 826019321..f8e58ce4f 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -1,6 +1,12 @@ -from expo.experimenter import MCTSExperimenter, Experimenter, AugExperimenter, CustomExperimenter -import asyncio import argparse +import asyncio + +from expo.experimenter import ( + AugExperimenter, + CustomExperimenter, + Experimenter, + MCTSExperimenter, +) def get_args(): @@ -19,6 +25,7 @@ def get_mcts_args(parser): parser.set_defaults(load_tree=False) parser.add_argument("--rollouts", type=int, default=5) + def get_aug_exp_args(parser): parser.add_argument("--aug_mode", type=str, default="single", choices=["single", "set"]) parser.add_argument("--num_experiments", type=int, default=1) @@ -31,7 +38,7 @@ def get_di_args(parser): parser.add_argument("--reflection", dest="reflection", action="store_true") parser.add_argument("--no_reflection", dest="reflection", action="store_false") parser.set_defaults(reflection=True) - + async def main(args): if args.exp_mode == "mcts": @@ -46,6 +53,7 @@ async def main(args): raise ValueError(f"Invalid exp_mode: {args.exp_mode}") await experimenter.run_experiment() + if __name__ == "__main__": args = get_args() - asyncio.run(main(args)) \ No newline at end of file + asyncio.run(main(args)) diff --git a/expo/run_mcts.py b/expo/run_mcts.py index 20d4171f7..4577417a9 100644 --- a/expo/run_mcts.py +++ b/expo/run_mcts.py @@ -1,10 +1,9 @@ -from expo.MCTS import MCTS, Node, initialize_di_root_node -from expo.utils import load_data_config -from expo.dataset import generate_task_requirement +import argparse +import asyncio from expo.evaluation.visualize_mcts import get_tree_text -import asyncio -import argparse +from expo.MCTS import MCTS +from expo.utils import load_data_config def get_args(): @@ -35,9 +34,17 @@ def get_args(): # asyncio.run(root_node.run_node()) mcts = MCTS(root_node=None, max_depth=5) - best_nodes = asyncio.run(mcts.search(args.task, data_config, - low_is_better=args.low_is_better, load_tree=args.load_tree, - reflection=args.reflection, rollouts=args.rollouts, name=args.name)) + best_nodes = asyncio.run( + mcts.search( + args.task, + data_config, + low_is_better=args.low_is_better, + load_tree=args.load_tree, + reflection=args.reflection, + rollouts=args.rollouts, + name=args.name, + ) + ) best_node = best_nodes["global_best"] dev_best_node = best_nodes["dev_best"] text, num_generated_codes = get_tree_text(mcts.root_node) @@ -49,5 +56,3 @@ def get_args(): f.write(f"Best node: {best_node}, score: {best_node.raw_reward}\n") f.write(f"Dev best node: {dev_best_node}, score: {dev_best_node.raw_reward}\n") f.write(text) - - diff --git a/expo/utils.py b/expo/utils.py index 20e3fa7f5..d67ceb5a1 100644 --- a/expo/utils.py +++ b/expo/utils.py @@ -1,50 +1,58 @@ +import os +import re +import sys +from datetime import datetime +from pathlib import Path + +import nbformat import yaml -from metagpt.roles.role import Role -from metagpt.actions.di.execute_nb_code import ExecuteNbCode +from loguru import logger as _logger + # from nbclient import NotebookClient from nbformat.notebooknode import NotebookNode -import nbformat -from pathlib import Path -from loguru import logger as _logger -from datetime import datetime -import sys -import os -import re + +from metagpt.roles.role import Role + def load_data_config(file_path="data.yaml"): - with open(file_path, 'r') as stream: + with open(file_path, "r") as stream: data_config = yaml.safe_load(stream) return data_config + DATA_CONFIG = load_data_config() + def get_mcts_logger(): print_level = "INFO" print_level2 = "MCTS" - logfile_level="MCTS" + logfile_level = "MCTS" name: str = None current_date = datetime.now() formatted_date = current_date.strftime("%Y%m%d") log_name = f"{name}_{formatted_date}" if name else formatted_date # name a log with prefix name _logger.remove() - new_level = _logger.level(logfile_level, color="", no=25) + _logger.level(logfile_level, color="", no=25) _logger.add(sys.stderr, level=print_level) _logger.add(sys.stderr, level=print_level2) _logger.add(Path(DATA_CONFIG["work_dir"]) / DATA_CONFIG["role_dir"] / f"{log_name}.txt", level=logfile_level) _logger.propagate = False return _logger + mcts_logger = get_mcts_logger() def get_exp_pool_path(task_name, data_config, pool_name="analysis_pool"): - datasets_dir = data_config['datasets_dir'] - if task_name in data_config['datasets']: - dataset = data_config['datasets'][task_name] - data_path = os.path.join(datasets_dir, dataset['dataset']) + datasets_dir = data_config["datasets_dir"] + if task_name in data_config["datasets"]: + dataset = data_config["datasets"][task_name] + data_path = os.path.join(datasets_dir, dataset["dataset"]) else: - raise ValueError(f"Dataset {task_name} not found in config file. Available datasets: {data_config['datasets'].keys()}") + raise ValueError( + f"Dataset {task_name} not found in config file. Available datasets: {data_config['datasets'].keys()}" + ) exp_pool_path = os.path.join(data_path, f"{pool_name}.json") return exp_pool_path @@ -60,7 +68,6 @@ def change_plan(role, plan): if not finished: tasks[i].plan = plan return finished - def is_cell_to_delete(cell: NotebookNode) -> bool: @@ -82,12 +89,14 @@ def process_cells(nb: NotebookNode) -> NotebookNode: nb["cells"] = new_cells return nb + def save_notebook(role: Role, save_dir: str = "", name: str = ""): save_dir = Path(save_dir) nb = process_cells(role.execute_code.nb) file_path = save_dir / f"{name}.ipynb" nbformat.write(nb, file_path) + async def load_execute_notebook(role): tasks = role.planner.plan.tasks codes = [task.code for task in tasks if task.code] @@ -99,6 +108,7 @@ async def load_execute_notebook(role): print("Finish executing the loaded notebook") return executor + def clean_json_from_rsp(text): pattern = r"```json(.*?)```" matches = re.findall(pattern, text, re.DOTALL) @@ -106,4 +116,4 @@ def clean_json_from_rsp(text): json_str = "\n".join(matches) return json_str else: - return "" \ No newline at end of file + return "" From 2c43944ec016e42c61bc476bbe761f8388c98324 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 4 Sep 2024 18:08:59 +0800 Subject: [PATCH 024/160] fix import --- expo/run_experiment.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/expo/run_experiment.py b/expo/run_experiment.py index f8e58ce4f..8871c04a6 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -1,12 +1,10 @@ import argparse import asyncio -from expo.experimenter import ( - AugExperimenter, - CustomExperimenter, - Experimenter, - MCTSExperimenter, -) +from expo.experimenter.aug import AugExperimenter +from expo.experimenter.custom import CustomExperimenter +from expo.experimenter.experimenter import Experimenter +from expo.experimenter.mcts import MCTSExperimenter def get_args(): From 58d7b14007684afa947b1c91bf7da0eca9734919 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 4 Sep 2024 18:46:16 +0800 Subject: [PATCH 025/160] fix import --- expo/experimenter/aug.py | 3 +-- expo/experimenter/custom.py | 2 +- expo/experimenter/mcts.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/expo/experimenter/aug.py b/expo/experimenter/aug.py index 9b14123d3..1bf927cc1 100644 --- a/expo/experimenter/aug.py +++ b/expo/experimenter/aug.py @@ -1,5 +1,4 @@ -from experimenter import Experimenter - +from expo.experimenter.experimenter import Experimenter from expo.insights.instruction_generator import InstructionGenerator from expo.research_assistant import ResearchAssistant from expo.utils import get_exp_pool_path diff --git a/expo/experimenter/custom.py b/expo/experimenter/custom.py index ba009bdb0..4a5486af0 100644 --- a/expo/experimenter/custom.py +++ b/expo/experimenter/custom.py @@ -3,7 +3,7 @@ import pandas as pd from expo.evaluation.evaluation import evaluate_score -from expo.experimenter import Experimenter +from expo.experimenter.experimenter import Experimenter from expo.MCTS import create_initial_state diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 921b81412..2805cae51 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -1,5 +1,5 @@ from expo.evaluation.visualize_mcts import get_tree_text -from expo.experimenter import Experimenter +from expo.experimenter.experimenter import Experimenter from expo.MCTS import MCTS From c16286a006c5d85c8b9f34c0183cde4c2c9849e8 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 5 Sep 2024 10:12:37 +0800 Subject: [PATCH 026/160] Refactor MCTS class to handle role running errors and improve error logging --- expo/MCTS.py | 13 +++++++++---- expo/experimenter/experimenter.py | 8 ++++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 7c03e2e86..b2ad824e5 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -3,6 +3,7 @@ import pickle import random +import numpy as np import pandas as pd from expo.dataset import generate_task_requirement, get_split_dataset_path @@ -209,16 +210,20 @@ async def run_node(self, role=None): await role.run(with_message="continue") else: await role.run(with_message=self.state["requirement"]) + score_dict = await role.get_score() + score_dict = self.evaluate_simulation(score_dict) + self.raw_reward = score_dict run_finished = True except Exception as e: mcts_logger.log("MCTS", f"Error in running the role: {e}") num_runs += 1 if not run_finished: mcts_logger.log("MCTS", f"Role {role.node_id} failed to run") - return {"test_score": 0, "dev_score": 0, "score": 0} - score_dict = await role.get_score() - score_dict = self.evaluate_simulation(score_dict) - self.raw_reward = score_dict + if self.state["low_is_better"]: + score_dict = {"test_score": np.inf, "dev_score": np.inf, "score": np.inf} + else: + score_dict = {"test_score": 0, "dev_score": 0, "score": 0} + self.raw_reward = score_dict if self.state["low_is_better"]: # normalized the score to be between 0 and 1, and higher is better def normalize_score(score): diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 83dde80b9..4161aef3d 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -54,11 +54,15 @@ async def run_experiment(self): {"idx": i, "score_dict": score_dict, "user_requirement": user_requirement, "args": vars(self.args)} ) self.save_result(results) # save intermediate results - dev_scores = [result["score_dict"]["dev_score"] for result in results] + dev_scores = [ + result["score_dict"]["dev_score"] for result in results if result["score_dict"]["dev_score"] != -1 + ] best_dev_score = max(dev_scores) if not self.args.low_is_better else min(dev_scores) best_score_idx = dev_scores.index(best_dev_score) - test_scores = [result["score_dict"]["test_score"] for result in results] + test_scores = [ + result["score_dict"]["test_score"] for result in results if result["score_dict"]["dev_score"] != -1 + ] avg_score = sum(test_scores) / len(test_scores) global_best_score = max(test_scores) if not self.args.low_is_better else min(test_scores) From 45d176b48bde9e67a91758e412034e3af60aa2f6 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 5 Sep 2024 11:00:07 +0800 Subject: [PATCH 027/160] remove deprecated scripts --- expo/run_exp_augmentation.py | 103 ----------------------------------- expo/run_mcts.py | 58 -------------------- 2 files changed, 161 deletions(-) delete mode 100644 expo/run_exp_augmentation.py delete mode 100644 expo/run_mcts.py diff --git a/expo/run_exp_augmentation.py b/expo/run_exp_augmentation.py deleted file mode 100644 index 7fb174ff7..000000000 --- a/expo/run_exp_augmentation.py +++ /dev/null @@ -1,103 +0,0 @@ -import argparse -import asyncio -import datetime -import json -import os - -import pandas as pd - -from expo.dataset import generate_task_requirement -from expo.evaluation.evaluation import evaluate_score -from expo.insights.instruction_generator import InstructionGenerator -from expo.MCTS import create_initial_state -from expo.research_assistant import ResearchAssistant -from expo.utils import DATA_CONFIG, get_exp_pool_path - -EXPS_PROMPT = """ -When doing the tasks, you can refer to the insights below: -{experience} - -""" -data_config = DATA_CONFIG - - -def evaluate_test(score, state): - datetime_text = datetime.datetime.now().strftime("%Y%m%d%H%M") - task_name = state["task"] - prediction_fpath = os.path.join(state["work_dir"], task_name, "predictions.csv") - predictions = pd.read_csv(prediction_fpath)["target"] - # copy predictions.csv to the node_dir - - predictions_node_fpath = os.path.join("results", f"{task_name}-{datetime_text}-predictions.csv") - predictions.to_csv(predictions_node_fpath, index=False) - # load test_target.csv - split_datasets_dir = state["datasets_dir"] - gt = pd.read_csv(os.path.join(split_datasets_dir["test_target"]))["target"] - metric = state["dataset_config"]["metric"] - score["test_score"] = evaluate_score(predictions, gt, metric) - return score - - -async def main(task_name, use_reflection=True, mode="single", num_experiments=2): - """ - mode: single or set - single: sample one instruction - set: sample a set of instructions - """ - low_is_better = False - state = create_initial_state( - task_name, start_task_id=1, data_config=data_config, low_is_better=low_is_better, name="" - ) - - user_requirement = generate_task_requirement(task_name, data_config) - exp_pool_path = get_exp_pool_path(task_name, data_config, pool_name="ds_analysis_pool") - exp_pool = InstructionGenerator.load_analysis_pool(exp_pool_path) - if mode == "single": - exps = InstructionGenerator._random_sample(exp_pool, num_experiments) - exps = [exp["Analysis"] for exp in exps] - elif mode == "set": - exp_set = InstructionGenerator.sample_instruction_set(exp_pool) - exp_set_text = "\n".join([f"{exp['task_id']}: {exp['Analysis']}" for exp in exp_set]) - exps = [exp_set_text] * num_experiments - else: - raise ValueError(f"Invalid mode: {mode}") - - scores = [] - for i in range(num_experiments): - di = ResearchAssistant(node_id=str(i), use_reflection=use_reflection) - di.role_dir = f"{di.role_dir}_{task_name}" - requirement = user_requirement + EXPS_PROMPT.format(experience=exps[i]) - print(requirement) - await di.run(requirement) - score = await di.get_score(low_is_better=False) - score = evaluate_test(score, state) - - scores.append(score) - - with open(f"results/{task_name}_scores.json", "w") as f: - # save scores and corresponding insights - results = { - "avg_score": sum([score["test_score"] for score in scores if score]) / num_experiments, - "max_score": max([score["test_score"] for score in scores]), - "scores": scores, - "insights": exps, - } - json.dump(results, f, indent=4) - - -def parse_args(): - parser = argparse.ArgumentParser() - parser.add_argument("--task", type=str, default="titanic") - parser.add_argument("--use_reflection", dest="use_reflection", action="store_true") - parser.add_argument("--no_use_reflection", dest="use_reflection", action="store_false") - parser.set_defaults(use_reflection=True) - parser.add_argument("--mode", type=str, default="single") - parser.add_argument("--num_experiments", type=int, default=2) - return parser.parse_args() - - -if __name__ == "__main__": - args = parse_args() - asyncio.run( - main(args.task, use_reflection=args.use_reflection, mode=args.mode, num_experiments=args.num_experiments) - ) diff --git a/expo/run_mcts.py b/expo/run_mcts.py deleted file mode 100644 index 4577417a9..000000000 --- a/expo/run_mcts.py +++ /dev/null @@ -1,58 +0,0 @@ -import argparse -import asyncio - -from expo.evaluation.visualize_mcts import get_tree_text -from expo.MCTS import MCTS -from expo.utils import load_data_config - - -def get_args(): - parser = argparse.ArgumentParser() - parser.add_argument("--task", type=str, default="titanic") - parser.add_argument("--low_is_better", dest="low_is_better", action="store_true") - parser.set_defaults(low_is_better=False) - parser.add_argument("--load_tree", dest="load_tree", action="store_true") - parser.add_argument("--no_load_tree", dest="load_tree", action="store_false") - parser.set_defaults(load_tree=True) - parser.add_argument("--reflection", dest="reflection", action="store_true") - parser.add_argument("--no_reflection", dest="reflection", action="store_false") - parser.set_defaults(reflection=True) - parser.add_argument("--rollouts", type=int, default=3) - parser.add_argument("--name", type=str, default="") - return parser.parse_args() - - -data_config = load_data_config() - -if __name__ == "__main__": - args = get_args() - # requirement = generate_task_requirement(args.task, data_config) - # print(requirement) - - # role, root_node = initialize_di_root_node(requirement, data_config) - # asyncio.run(role.run(requirement)) - - # asyncio.run(root_node.run_node()) - mcts = MCTS(root_node=None, max_depth=5) - best_nodes = asyncio.run( - mcts.search( - args.task, - data_config, - low_is_better=args.low_is_better, - load_tree=args.load_tree, - reflection=args.reflection, - rollouts=args.rollouts, - name=args.name, - ) - ) - best_node = best_nodes["global_best"] - dev_best_node = best_nodes["dev_best"] - text, num_generated_codes = get_tree_text(mcts.root_node) - print(text) - print(f"Generated {num_generated_codes} unique codes.") - - with open(f"results/{args.task}_tree{args.name}.txt", "w") as f: - f.write(f"Generated {num_generated_codes} unique codes.\n") - f.write(f"Best node: {best_node}, score: {best_node.raw_reward}\n") - f.write(f"Dev best node: {dev_best_node}, score: {dev_best_node.raw_reward}\n") - f.write(text) From d27a48adb234dee1b45209b7c02deb0d4cff6a40 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 5 Sep 2024 13:01:00 +0800 Subject: [PATCH 028/160] update prompt to include dataset info path --- expo/dataset.py | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/expo/dataset.py b/expo/dataset.py index fee1199a9..f7e0301b5 100644 --- a/expo/dataset.py +++ b/expo/dataset.py @@ -16,9 +16,8 @@ Report {metric} on the eval data. Do not plot or make any visualizations. """ -TASK_PROMPT = """\ -# User requirement -{user_requirement} + +DI_INSTRUCTION = """\ **Attention** 1. Please do not leak the target label in any form during training. 2. Dev and Test sets do not have the target column. @@ -39,14 +38,19 @@ print("Train score:", train_score) ``` +# Output dir +{output_dir} +""" + +TASK_PROMPT = """\ +# User requirement +{user_requirement} +{additional_instruction} # Data dir training (with labels): {train_path} dev (without labels): {dev_path} testing (without labels): {test_path} - -# Output dir -{output_dir} - +dataset description: {data_info_path} (You can use this file to get additional information about the dataset) """ @@ -132,7 +136,12 @@ def create_dataset_dict(dataset): return dataset_dict -def generate_task_requirement(task_name, data_config): +def generate_di_instruction(output_dir): + additional_instruction = DI_INSTRUCTION.format(output_dir=output_dir) + return additional_instruction + + +def generate_task_requirement(task_name, data_config, is_di=True): user_requirement = get_user_requirement(task_name, data_config) split_dataset_path = get_split_dataset_path(task_name, data_config) train_path = split_dataset_path["train"] @@ -140,12 +149,19 @@ def generate_task_requirement(task_name, data_config): test_path = split_dataset_path["test_wo_target"] work_dir = data_config["work_dir"] output_dir = f"{work_dir}/{task_name}" + datasets_dir = data_config["datasets_dir"] + data_info_path = f"{datasets_dir}/{task_name}/dataset_info.json" + if is_di: + additional_instruction = generate_di_instruction(output_dir) + else: + additional_instruction = "" user_requirement = TASK_PROMPT.format( user_requirement=user_requirement, train_path=train_path, dev_path=dev_path, test_path=test_path, - output_dir=output_dir, + additional_instruction=additional_instruction, + data_info_path=data_info_path, ) print(user_requirement) return user_requirement From 96ffcd285f13556182c9f5dfece10e563a79086b Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 5 Sep 2024 14:30:36 +0800 Subject: [PATCH 029/160] =?UTF-8?q?=E6=9B=B4=E6=96=B0Prompt=E7=9B=B8?= =?UTF-8?q?=E5=85=B3readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expo/README.md | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/expo/README.md b/expo/README.md index 4cc4daf25..eab3298dc 100644 --- a/expo/README.md +++ b/expo/README.md @@ -33,15 +33,17 @@ llm: 实验轮次 k = 10, 20 -### 提示词使用 +### Prompt Usage - 通过执行`dataset.py`中的`generate_task_requirement`函数获取提示词 -- 每一个数据集里有`dataset_info.json`,里面的内容需要提供给baselines以保证公平 + - 非DI-based方法设置`is_di=False` + - `data_config`用`utils.DATA_CONFIG` +- 每一个数据集里有`dataset_info.json`,里面的内容需要提供给baselines以保证公平(`generate_task_requirement`已经默认提供) ## 3. Evaluation -运行各个框架,运行后框架需要提供Dev和Test的`dev_predictions.csv`和`test_predictions.csv`, column name为target +运行各个框架,运行后框架需要提供Dev和Test的`dev_predictions.csv`和`test_predictions.csv`,每个csv文件只需要单个名为target的列 - 使用`CustomExperimenter` ``` @@ -62,11 +64,6 @@ score_dict = experimenter.evaluate_pred_files(dev_pred_path, test_pred_path) ``` pip install -U pip pip install -U setuptools wheel - -CPU version of pytorch has smaller footprint - see installation instructions in -pytorch documentation - https://pytorch.org/get-started/locally/ -pip install torch==2.3.1 torchvision==0.18.1 --index-url https://download.pytorch.org/whl/cpu - pip install autogluon ``` @@ -105,11 +102,11 @@ pip install -r requirements.txt #### Run -- `python run_experiment.py --exp_mode mcts --task titanic --rollout 5` +- `python run_experiment.py --exp_mode mcts --task titanic --rollout 10` If the dataset has reg metric, remember to use `--low_is_better`: -- `python run_experiment.py --exp_mode mcts --task househouse_prices --rollout 5 --low_is_better` +- `python run_experiment.py --exp_mode mcts --task househouse_prices --rollout 10 --low_is_better` From defca81ebbd84f6bdeb26a0ca3d06fefa3e0e830 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 6 Sep 2024 10:24:49 +0800 Subject: [PATCH 030/160] fix time calculation --- expo/experimenter/experimenter.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 4161aef3d..29c4e380d 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -16,7 +16,8 @@ class Experimenter: def __init__(self, args, **kwargs): self.args = args - self.start_time = datetime.datetime.now().strftime("%Y%m%d%H%M") + self.start_time_raw = datetime.datetime.now() + self.start_time = self.start_time_raw.strftime("%Y%m%d%H%M") self.state = create_initial_state( self.args.task, start_task_id=1, @@ -99,11 +100,12 @@ def evaluate(self, score_dict, state): return score_dict def save_result(self, result): - end_time = datetime.datetime.now().strftime("%Y%m%d%H%M") + end_time_raw = datetime.datetime.now() + end_time = end_time_raw.strftime("%Y%m%d%H%M") time_info = { "start_time": self.start_time, "end_time": end_time, - "duration (minutes)": float(end_time) - float(self.start_time), + "duration (seconds)": (end_time_raw - self.start_time_raw).seconds, } result = result.copy() result.insert(0, time_info) From 0e27e3d8be18f6e36600b169c9408de3c644a71c Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 6 Sep 2024 10:31:01 +0800 Subject: [PATCH 031/160] =?UTF-8?q?=E4=BD=BF=E7=94=A8code=20block=E5=81=9A?= =?UTF-8?q?reflection?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- metagpt/actions/di/write_analysis_code.py | 9 ++++----- metagpt/prompts/di/write_analysis_code.py | 20 +++++++++++--------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/metagpt/actions/di/write_analysis_code.py b/metagpt/actions/di/write_analysis_code.py index 711e56d39..149543b4b 100644 --- a/metagpt/actions/di/write_analysis_code.py +++ b/metagpt/actions/di/write_analysis_code.py @@ -6,8 +6,6 @@ """ from __future__ import annotations -import json - from metagpt.actions import Action from metagpt.prompts.di.write_analysis_code import ( CHECK_DATA_PROMPT, @@ -30,9 +28,10 @@ async def _debug_with_reflection(self, context: list[Message], working_memory: l ) rsp = await self._aask(reflection_prompt, system_msgs=[REFLECTION_SYSTEM_MSG]) - reflection = json.loads(CodeParser.parse_code(block=None, text=rsp)) - - return reflection["improved_impl"] + # reflection = json.loads(CodeParser.parse_code(block=None, text=rsp)) + # return reflection["improved_impl"] + reflection = CodeParser.parse_code(block=None, text=rsp) + return reflection async def run( self, diff --git a/metagpt/prompts/di/write_analysis_code.py b/metagpt/prompts/di/write_analysis_code.py index beee80679..1b5ae9743 100644 --- a/metagpt/prompts/di/write_analysis_code.py +++ b/metagpt/prompts/di/write_analysis_code.py @@ -40,15 +40,17 @@ def add(a: int, b: int) -> int: assert add(1, 2) == 3 # output: -1 assert add(1, 3) == 4 # output: -2 -[reflection on previous impl]: +[reflection on previous impl] The implementation failed the test cases where the input integers are 1 and 2. The issue arises because the code does not add the two integers together, but instead subtracts the second integer from the first. To fix this issue, we should change the operator from `-` to `+` in the return statement. This will ensure that the function returns the correct output for the given input. -[improved impl]: +[improved impl] +```python def add(a: int, b: int) -> int: """ Given integers a and b, return the total value of a and b. """ return a + b +``` ''' REFLECTION_PROMPT = """ @@ -60,17 +62,17 @@ def add(a: int, b: int) -> int: [context] {context} -[previous impl]: +[previous impl] {previous_impl} [instruction] Analyze your previous code and error in [context] step by step, provide me with improved method and code. Remember to follow [context] requirement. Don't forget to write code for steps behind the error step. -Output a json following the format: -```json -{{ - "reflection": str = "Reflection on previous implementation", - "improved_impl": str = "Refined code after reflection (do not include nested code block here).", -}} +Output in the following format: +[reflection on previous impl] +... +[improved impl]: +```python +# your code ``` """ From e07ed0df8bcee81cd7889964d01a45cba5d92fd2 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 6 Sep 2024 10:42:24 +0800 Subject: [PATCH 032/160] fix mcts bug --- expo/MCTS.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index b2ad824e5..365315330 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -191,6 +191,7 @@ def evaluate_prediction(self, split): def evaluate_simulation(self, score_dict): scores = {"dev_score": self.evaluate_prediction("dev"), "test_score": self.evaluate_prediction("test")} + scores["score"] = scores["dev_score"] score_dict.update(scores) return score_dict @@ -345,7 +346,7 @@ async def search(self, task, data_config, name, rollouts, load_tree=False, low_i if node.raw_value == 0: reward = await self.simulate(node) else: - reward = {"test_score": node.raw_value, "score": node.value} + reward = {"test_score": node.raw_value, "score": node.raw_reward["score"]} mcts_logger.log("MCTS", f"Terminal node's reward: {reward}") self.backpropagate(node, reward) else: From a6f71f449873eea1c1b3486e93d84c340279fada Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 6 Sep 2024 13:09:18 +0800 Subject: [PATCH 033/160] 1. avoid circular reference 2. add greedy --- expo/Greedy.py | 9 +++++++++ expo/MCTS.py | 37 ++++++++++++++----------------------- 2 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 expo/Greedy.py diff --git a/expo/Greedy.py b/expo/Greedy.py new file mode 100644 index 000000000..f6f60db01 --- /dev/null +++ b/expo/Greedy.py @@ -0,0 +1,9 @@ +from expo.MCTS import MCTS + + +class Greedy(MCTS): + def best_child(self): + if len(self.children) == 0: + return self.root_node + all_children = [child for children in self.children.values() for child in children] + return max(all_children, key=lambda x: x.normalized_reward.get("dev_score", 0)) diff --git a/expo/MCTS.py b/expo/MCTS.py index 365315330..3331f35fa 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -143,6 +143,7 @@ def save_new_role(self, role: ResearchAssistant): role.state_saved = False role.change_next_instruction(self.action) mcts_logger.log("MCTS", f"Saving new role: {role.node_id}") + role = role.model_copy() role.save_state(static_save=True) async def expand(self, max_children): @@ -165,18 +166,6 @@ async def expand(self, max_children): node.save_new_role(new_role) self.add_child(node) - # def evaluate_test(self): - # prediction_fpath = os.path.join(self.state["work_dir"], self.state["task"], "predictions.csv") - # predictions = pd.read_csv(prediction_fpath)["target"] - # # copy predictions.csv to the node_dir - # predictions_node_fpath = os.path.join(self.state["node_dir"], "Node-{self.id}-predictions.csv") - # predictions.to_csv(predictions_node_fpath, index=False) - # # load test_target.csv - # split_datasets_dir = self.state["datasets_dir"] - # gt = pd.read_csv(os.path.join(split_datasets_dir["test_target"]))["target"] - # metric = self.state["dataset_config"]["metric"] - # return evaluate_score(predictions, gt, metric) - def evaluate_prediction(self, split): pred_path = os.path.join(self.state["work_dir"], self.state["task"], f"{split}_predictions.csv") pred_node_path = os.path.join(self.state["node_dir"], f"Node-{self.id}-{split}_predictions.csv") @@ -331,14 +320,10 @@ async def search(self, task, data_config, name, rollouts, load_tree=False, low_i self.children[root] = [] reward = await self.simulate(root, role) self.backpropagate(root, reward) - children = await self.expand(root) - # 目前是随机选择1个,后续可以改成多个 - first_leaf = random.choice(children) - reward = await self.simulate(first_leaf) - self.backpropagate(first_leaf, reward) + node, reward = await self.expand_and_simulate(root) + # self.backpropagate(node, reward) else: root = self.root_node - # 后续迭代:使用UCT进行选择,expand并模拟和反向传播 for _ in range(rollouts): # number of rollouts mcts_logger.log("MCTS", f"Start the next rollout {_+1}") node = self.select(root) @@ -350,13 +335,19 @@ async def search(self, task, data_config, name, rollouts, load_tree=False, low_i mcts_logger.log("MCTS", f"Terminal node's reward: {reward}") self.backpropagate(node, reward) else: - if node.visited > 0: - children = await self.expand(node) - node = random.choice(children) - reward = await self.simulate(node) - self.backpropagate(node, reward) + node, reward = await self.expand_and_simulate(node) + # self.backpropagate(node, reward) return self.best_path(root) + async def expand_and_simulate(self, node): + # Expand and randomly select a child node, then simulate it + if node.visited > 0: + children = await self.expand(node) + node = random.choice(children) + reward = await self.simulate(node) + self.backpropagate(node, reward) + return node, reward + def load_tree(self): def load_children_node(node): mcts_logger.log("MCTS", f"Load node {node.id}'s child: {node.children}") From 6d40ec463fce4b8d2eff34334bc54f2e611ed2b5 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 6 Sep 2024 14:18:04 +0800 Subject: [PATCH 034/160] update result key --- expo/experimenter/experimenter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 29c4e380d..e81c64701 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -71,10 +71,10 @@ async def run_experiment(self): 0, { "best_dev_score": best_dev_score, - "best_score_idx": best_score_idx, - "best_test_score": test_scores[best_score_idx], + "best_dev_score_idx": best_score_idx, + "best_dev_test_score": test_scores[best_score_idx], "avg_test_score": avg_score, - "best_score": global_best_score, + "global_best_test_score": global_best_score, }, ) self.save_result(results) From 376d1b7661889fae78ec7d883f1bb15d6a8b3fc1 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 6 Sep 2024 17:02:49 +0800 Subject: [PATCH 035/160] allow new instruction even if there's no insights --- expo/insights/instruction_generator.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/expo/insights/instruction_generator.py b/expo/insights/instruction_generator.py index 065565c89..2cfee3107 100644 --- a/expo/insights/instruction_generator.py +++ b/expo/insights/instruction_generator.py @@ -85,8 +85,12 @@ async def generate_new_instructions(task_id, original_instruction, max_num, file if len(data) == 0: mcts_logger.log("MCTS", f"No insights available for task {task_id}") return [original_instruction] # Return the original instruction if no insights are available - for item in data[:max_num]: - insights = item["Analysis"] + for i in range(max_num): + if len(data) == 0: + insights = "No insights available" + else: + item = data[i] + insights = item["Analysis"] new_instruction = await InstructionGenerator.generate_new_instruction(original_instruction, insights) new_instructions.append(new_instruction) return new_instructions From c0262bcd8f1c8803da18d5a0c80bc0094e168ed2 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 6 Sep 2024 19:05:10 +0800 Subject: [PATCH 036/160] 1. add support to hf dataset 2. add support to datasets that have both train and test 3. create data folder 4. fix new instruction bug --- expo/MCTS.py | 2 +- expo/{ => data}/dataset.py | 50 +++++++++++--------- expo/data/hf_data.py | 64 ++++++++++++++++++++++++++ expo/experimenter/mcts.py | 4 +- expo/insights/instruction_generator.py | 2 +- 5 files changed, 97 insertions(+), 25 deletions(-) rename expo/{ => data}/dataset.py (87%) create mode 100644 expo/data/hf_data.py diff --git a/expo/MCTS.py b/expo/MCTS.py index 3331f35fa..4090331cd 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -6,7 +6,7 @@ import numpy as np import pandas as pd -from expo.dataset import generate_task_requirement, get_split_dataset_path +from expo.data.dataset import generate_task_requirement, get_split_dataset_path from expo.evaluation.evaluation import evaluate_score from expo.insights.instruction_generator import InstructionGenerator from expo.research_assistant import ResearchAssistant diff --git a/expo/dataset.py b/expo/data/dataset.py similarity index 87% rename from expo/dataset.py rename to expo/data/dataset.py index f7e0301b5..21dc19519 100644 --- a/expo/dataset.py +++ b/expo/data/dataset.py @@ -86,6 +86,8 @@ ("07_icr-identify-age-related-conditions", "Class"), ] +DSAGENT_DATASETS = [("concrete-strength", "Strength"), ("smoker-status", "smoking"), ("software-defects", "defects")] + def get_split_dataset_path(dataset_name, config): datasets_dir = config["datasets_dir"] @@ -121,8 +123,8 @@ def get_user_requirement(task_name, config): ) -def save_datasets_dict_to_yaml(datasets_dict): - with open("datasets.yaml", "w") as file: +def save_datasets_dict_to_yaml(datasets_dict, name="datasets.yaml"): + with open(name, "w") as file: yaml.dump(datasets_dict, file) @@ -201,11 +203,15 @@ def check_datasetinfo_exists(self): def get_raw_dataset(self): raw_dir = Path(self.dataset_dir, self.name, "raw") + train_df = None + test_df = None if not os.path.exists(Path(raw_dir, "train.csv")): raise FileNotFoundError(f"Raw dataset `train.csv` not found in {raw_dir}") else: - df = pd.read_csv(Path(raw_dir, "train.csv")) - return df + train_df = pd.read_csv(Path(raw_dir, "train.csv")) + if os.path.exists(Path(raw_dir, "test.csv")): + test_df = pd.read_csv(Path(raw_dir, "test.csv")) + return train_df, test_df def get_dataset_info(self): raw_df = pd.read_csv(Path(self.dataset_dir, self.name, "raw", "train.csv")) @@ -249,10 +255,10 @@ def create_base_requirement(self): return req def save_dataset(self, target_col): - df = self.get_raw_dataset() + df, test_df = self.get_raw_dataset() if not self.check_dataset_exists() or self.force_update: print(f"Saving Dataset {self.name} in {self.dataset_dir}") - self.split_and_save(df, target_col) + self.split_and_save(df, target_col, test_df=test_df) else: print(f"Dataset {self.name} already exists") if not self.check_datasetinfo_exists() or self.force_update: @@ -278,10 +284,13 @@ def save_split_datasets(self, df, split, target_col=None): df_target = df_target.drop(columns=[target_col]) df_target.to_csv(Path(path, f"split_{split}_target.csv"), index=False) - def split_and_save(self, df, target_col): + def split_and_save(self, df, target_col, test_df=None): if not target_col: raise ValueError("Target column not provided") - train, test = train_test_split(df, test_size=1 - TRAIN_TEST_SPLIT, random_state=SEED) + if test_df is None: + train, test = train_test_split(df, test_size=1 - TRAIN_TEST_SPLIT, random_state=SEED) + else: + train = df train, dev = train_test_split(train, test_size=1 - TRAIN_DEV_SPLIT, random_state=SEED) self.save_split_datasets(train, "train") self.save_split_datasets(dev, "dev", target_col) @@ -304,7 +313,7 @@ def get_raw_dataset(self): raw_dir = Path(self.dataset_dir, self.name, "raw") os.makedirs(raw_dir, exist_ok=True) dataset_df.to_csv(Path(raw_dir, "train.csv"), index=False) - return dataset_df + return dataset_df, None def get_dataset_info(self): dataset_info = super().get_dataset_info() @@ -315,14 +324,9 @@ def get_dataset_info(self): return dataset_info -# class HFExpDataset(ExpDataset): -# def __init__(self, name, dataset_dir, dataset_name, **kwargs): -# super().__init__(name, dataset_dir, **kwargs) - - -async def process_dataset(dataset, solution_designer, save_analysis_pool, datasets_dict): +async def process_dataset(dataset, solution_designer: SolutionDesigner, save_analysis_pool, datasets_dict): if save_analysis_pool: - asyncio.run(solution_designer.generate_solutions(dataset.get_dataset_info(), dataset.name)) + await solution_designer.generate_solutions(dataset.get_dataset_info(), dataset.name) dataset_dict = create_dataset_dict(dataset) datasets_dict["datasets"][dataset.name] = dataset_dict @@ -330,14 +334,18 @@ async def process_dataset(dataset, solution_designer, save_analysis_pool, datase if __name__ == "__main__": datasets_dir = "D:/work/automl/datasets" force_update = False - save_analysis_pool = False + save_analysis_pool = True datasets_dict = {"datasets": {}} solution_designer = SolutionDesigner() - for dataset_id in OPENML_DATASET_IDS: - openml_dataset = OpenMLExpDataset("", datasets_dir, dataset_id, force_update=force_update) - asyncio.run(process_dataset(openml_dataset, solution_designer, save_analysis_pool, datasets_dict)) + # for dataset_id in OPENML_DATASET_IDS: + # openml_dataset = OpenMLExpDataset("", datasets_dir, dataset_id, force_update=force_update) + # asyncio.run(process_dataset(openml_dataset, solution_designer, save_analysis_pool, datasets_dict)) + + # for dataset_name, target_col in CUSTOM_DATASETS: + # custom_dataset = ExpDataset(dataset_name, datasets_dir, target_col=target_col, force_update=force_update) + # asyncio.run(process_dataset(custom_dataset, solution_designer, save_analysis_pool, datasets_dict)) - for dataset_name, target_col in CUSTOM_DATASETS: + for dataset_name, target_col in DSAGENT_DATASETS: custom_dataset = ExpDataset(dataset_name, datasets_dir, target_col=target_col, force_update=force_update) asyncio.run(process_dataset(custom_dataset, solution_designer, save_analysis_pool, datasets_dict)) diff --git a/expo/data/hf_data.py b/expo/data/hf_data.py new file mode 100644 index 000000000..a7e2a1afe --- /dev/null +++ b/expo/data/hf_data.py @@ -0,0 +1,64 @@ +import asyncio +import os +from pathlib import Path + +import pandas as pd +from datasets import load_dataset + +from expo.data.dataset import ExpDataset, process_dataset, save_datasets_dict_to_yaml +from expo.insights.solution_designer import SolutionDesigner + +HFDATSETS = [ + {"name": "sms_spam", "dataset_name": "ucirvine/sms_spam", "target_col": "label"}, + {"name": "banking77", "dataset_name": "PolyAI/banking77", "target_col": "label"}, + {"name": "gnad10", "dataset_name": "community-datasets/gnad10", "target_col": "label"}, + {"name": "oxford-iiit-pet", "dataset_name": "timm/oxford-iiit-pet", "target_col": "label"}, + {"name": "stanford_cars", "dataset_name": "tanganke/stanford_cars", "target_col": "label"}, + {"name": "fashion_mnist", "dataset_name": "zalando-datasets/fashion_mnist", "target_col": "label"}, +] + + +class HFExpDataset(ExpDataset): + train_ratio = 0.6 + dev_ratio = 0.2 + test_ratio = 0.2 + + def __init__(self, name, dataset_dir, dataset_name, **kwargs): + self.name = name + self.dataset_dir = dataset_dir + self.dataset_name = dataset_name + self.target_col = kwargs.get("target_col", "label") + self.dataset = load_dataset(dataset_name) + super().__init__(self.name, dataset_dir, **kwargs) + + def get_raw_dataset(self): + raw_dir = Path(self.dataset_dir, self.name, "raw") + raw_dir.mkdir(parents=True, exist_ok=True) + if os.path.exists(Path(raw_dir, "train.csv")): + df = pd.read_csv(Path(raw_dir, "train.csv")) + else: + df = self.dataset["train"].to_pandas() + df.to_csv(Path(raw_dir, "train.csv")) + + if os.path.exists(Path(raw_dir, "test.csv")): + test_df = pd.read_csv(Path(raw_dir, "test.csv")) + else: + if "test" in self.dataset: + test_df = self.dataset["test"].to_pandas() + test_df.to_csv(Path(raw_dir, "test.csv")) + else: + test_df = None + return df, test_df + + +if __name__ == "__main__": + dataset_dir = "D:/work/automl/datasets" + save_analysis_pool = True + datasets_dict = {"datasets": {}} + solution_designer = SolutionDesigner() + for dataset_meta in HFDATSETS: + hf_dataset = HFExpDataset( + dataset_meta["name"], dataset_dir, dataset_meta["dataset_name"], target_col=dataset_meta["target_col"] + ) + asyncio.run(process_dataset(hf_dataset, solution_designer, save_analysis_pool, datasets_dict)) + save_datasets_dict_to_yaml(datasets_dict, "hf_datasets.yaml") diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 2805cae51..9db6e0807 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -22,8 +22,8 @@ async def run_experiment(self): text, num_generated_codes = get_tree_text(mcts.root_node) text += f"Generated {num_generated_codes} unique codes.\n" - text += f"Best node: {best_node}, score: {best_node.raw_reward}\n" - text += f"Dev best node: {dev_best_node}, score: {dev_best_node.raw_reward}\n" + text += f"Best node: {best_node.id}, score: {best_node.raw_reward}\n" + text += f"Dev best node: {dev_best_node.id}, score: {dev_best_node.raw_reward}\n" print(text) self.save_tree(text) diff --git a/expo/insights/instruction_generator.py b/expo/insights/instruction_generator.py index 2cfee3107..c9ff7ec6e 100644 --- a/expo/insights/instruction_generator.py +++ b/expo/insights/instruction_generator.py @@ -84,7 +84,7 @@ async def generate_new_instructions(task_id, original_instruction, max_num, file new_instructions = [] if len(data) == 0: mcts_logger.log("MCTS", f"No insights available for task {task_id}") - return [original_instruction] # Return the original instruction if no insights are available + # return [original_instruction] # Return the original instruction if no insights are available for i in range(max_num): if len(data) == 0: insights = "No insights available" From df6fe9854d1b97ba613e161cba477cb181325413 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 6 Sep 2024 19:27:36 +0800 Subject: [PATCH 037/160] fix dataset bug --- expo/data/dataset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 21dc19519..2efaf692b 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -291,6 +291,7 @@ def split_and_save(self, df, target_col, test_df=None): train, test = train_test_split(df, test_size=1 - TRAIN_TEST_SPLIT, random_state=SEED) else: train = df + test = test_df train, dev = train_test_split(train, test_size=1 - TRAIN_DEV_SPLIT, random_state=SEED) self.save_split_datasets(train, "train") self.save_split_datasets(dev, "dev", target_col) From 9728b3a891b502fb8f0d260f3d139b1ca3c5502a Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 9 Sep 2024 13:44:38 +0800 Subject: [PATCH 038/160] add greedy to run_experiment; add save_notebook to experimenter.py --- expo/data/dataset.py | 6 +++--- expo/experimenter/aug.py | 2 +- expo/experimenter/experimenter.py | 7 ++++--- expo/experimenter/mcts.py | 10 +++++++++- expo/run_experiment.py | 4 +++- expo/utils.py | 12 ++++++++++-- 6 files changed, 30 insertions(+), 11 deletions(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 2efaf692b..4ac931d9f 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -111,12 +111,12 @@ def get_split_dataset_path(dataset_name, config): def get_user_requirement(task_name, config): - datasets_dir = config["datasets_dir"] + # datasets_dir = config["datasets_dir"] if task_name in config["datasets"]: dataset = config["datasets"][task_name] - data_path = os.path.join(datasets_dir, dataset["dataset"]) + # data_path = os.path.join(datasets_dir, dataset["dataset"]) user_requirement = dataset["user_requirement"] - return data_path, user_requirement + return user_requirement else: raise ValueError( f"Dataset {task_name} not found in config file. Available datasets: {config['datasets'].keys()}" diff --git a/expo/experimenter/aug.py b/expo/experimenter/aug.py index 1bf927cc1..8312f57fc 100644 --- a/expo/experimenter/aug.py +++ b/expo/experimenter/aug.py @@ -34,7 +34,7 @@ async def run_experiment(self): di.role_dir = f"{di.role_dir}_{self.args.task}" requirement = user_requirement + EXPS_PROMPT.format(experience=exps[i]) print(requirement) - score_dict = await self.run_di(di, requirement) + score_dict = await self.run_di(di, requirement, run_idx=i) results.append( { "idx": i, diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index e81c64701..b1b5a93c0 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -7,7 +7,7 @@ from expo.evaluation.evaluation import evaluate_score from expo.MCTS import create_initial_state from expo.research_assistant import ResearchAssistant -from expo.utils import DATA_CONFIG +from expo.utils import DATA_CONFIG, save_notebook class Experimenter: @@ -26,7 +26,7 @@ def __init__(self, args, **kwargs): name="", ) - async def run_di(self, di, user_requirement): + async def run_di(self, di, user_requirement, run_idx): max_retries = 3 num_runs = 1 run_finished = False @@ -39,6 +39,7 @@ async def run_di(self, di, user_requirement): except Exception as e: print(f"Error: {e}") num_runs += 1 + save_notebook(role=di, save_dir=self.result_path, name=f"{self.args.task}_{self.start_time}_{run_idx}") if not run_finished: score_dict = {"train_score": -1, "dev_score": -1, "test_score": -1, "score": -1} return score_dict @@ -50,7 +51,7 @@ async def run_experiment(self): for i in range(self.args.num_experiments): di = ResearchAssistant(node_id="0", use_reflection=self.args.reflection) - score_dict = await self.run_di(di, user_requirement) + score_dict = await self.run_di(di, user_requirement, run_idx=i) results.append( {"idx": i, "score_dict": score_dict, "user_requirement": user_requirement, "args": vars(self.args)} ) diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 9db6e0807..9bf7306c4 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -1,13 +1,21 @@ from expo.evaluation.visualize_mcts import get_tree_text from expo.experimenter.experimenter import Experimenter +from expo.Greedy import Greedy from expo.MCTS import MCTS class MCTSExperimenter(Experimenter): result_path: str = "results/mcts" + def __init__(self, args, greedy=False, **kwargs): + super().__init__(args, **kwargs) + self.greedy = greedy + async def run_experiment(self): - mcts = MCTS(root_node=None, max_depth=5) + if self.greedy: + mcts = Greedy(root_node=None, max_depth=5) + else: + mcts = MCTS(root_node=None, max_depth=5) best_nodes = await mcts.search( self.args.task, self.data_config, diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 8871c04a6..83237741a 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -10,7 +10,7 @@ def get_args(): parser = argparse.ArgumentParser() parser.add_argument("--name", type=str, default="") - parser.add_argument("--exp_mode", type=str, default="mcts", choices=["mcts", "aug", "base", "custom"]) + parser.add_argument("--exp_mode", type=str, default="mcts", choices=["mcts", "aug", "base", "custom", "greedy"]) get_di_args(parser) get_mcts_args(parser) get_aug_exp_args(parser) @@ -41,6 +41,8 @@ def get_di_args(parser): async def main(args): if args.exp_mode == "mcts": experimenter = MCTSExperimenter(args) + elif args.exp_mode == "greedy": + experimenter = MCTSExperimenter(args, greedy=True) elif args.exp_mode == "aug": experimenter = AugExperimenter(args) elif args.exp_mode == "base": diff --git a/expo/utils.py b/expo/utils.py index d67ceb5a1..65701c3ec 100644 --- a/expo/utils.py +++ b/expo/utils.py @@ -7,8 +7,7 @@ import nbformat import yaml from loguru import logger as _logger - -# from nbclient import NotebookClient +from nbclient import NotebookClient from nbformat.notebooknode import NotebookNode from metagpt.roles.role import Role @@ -92,15 +91,24 @@ def process_cells(nb: NotebookNode) -> NotebookNode: def save_notebook(role: Role, save_dir: str = "", name: str = ""): save_dir = Path(save_dir) + tasks = role.planner.plan.tasks + codes = [task.code for task in tasks if task.code] + clean_nb = nbformat.v4.new_notebook() + for code in codes: + clean_nb.cells.append(nbformat.v4.new_code_cell(code)) nb = process_cells(role.execute_code.nb) file_path = save_dir / f"{name}.ipynb" + clean_file_path = save_dir / f"{name}_clean.ipynb" nbformat.write(nb, file_path) + nbformat.write(clean_nb, clean_file_path) async def load_execute_notebook(role): tasks = role.planner.plan.tasks codes = [task.code for task in tasks if task.code] executor = role.execute_code + executor.nb = nbformat.v4.new_notebook() + executor.nb.client = NotebookClient(executor.nb) # await executor.build() for code in codes: outputs, success = await executor.run(code) From 72dd44ae3295830c72c604748c85226f91415b1b Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 9 Sep 2024 13:47:59 +0800 Subject: [PATCH 039/160] add ds agent's datasets --- expo/datasets.yaml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/expo/datasets.yaml b/expo/datasets.yaml index 8c28b03ca..45150833a 100644 --- a/expo/datasets.yaml +++ b/expo/datasets.yaml @@ -151,3 +151,28 @@ datasets: \ the target column `Class`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ \ eval data. Do not plot or make any visualizations.\n" + concrete-strength: + dataset: concrete-strength + metric: rmse + target_col: Strength + user_requirement: "This is a concrete-strength dataset. Your goal is to predict\ + \ the target column `Strength`.\nPerform data analysis, data preprocessing,\ + \ feature engineering, and modeling to predict the target. \nReport rmse on\ + \ the eval data. Do not plot or make any visualizations.\n" + smoker-status: + dataset: smoker-status + metric: f1 + target_col: smoking + user_requirement: "This is a smoker-status dataset. Your goal is to predict the\ + \ target column `smoking`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 on the eval data.\ + \ Do not plot or make any visualizations.\n" + software-defects: + dataset: software-defects + metric: f1 + target_col: defects + user_requirement: "This is a software-defects dataset. Your goal is to predict\ + \ the target column `defects`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 on the eval data.\ + \ Do not plot or make any visualizations.\n" + From 93ff1f8f2bcf3f7304cd2282cfe17383a39532e4 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 9 Sep 2024 13:50:36 +0800 Subject: [PATCH 040/160] update datasets.yaml --- expo/datasets.yaml | 2 +- expo/utils.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/expo/datasets.yaml b/expo/datasets.yaml index 45150833a..512cbc292 100644 --- a/expo/datasets.yaml +++ b/expo/datasets.yaml @@ -151,7 +151,7 @@ datasets: \ the target column `Class`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ \ eval data. Do not plot or make any visualizations.\n" - concrete-strength: + concrete-strength: dataset: concrete-strength metric: rmse target_col: Strength diff --git a/expo/utils.py b/expo/utils.py index 65701c3ec..9c6295fa9 100644 --- a/expo/utils.py +++ b/expo/utils.py @@ -19,7 +19,9 @@ def load_data_config(file_path="data.yaml"): return data_config +DATASET_CONFIG = load_data_config("datasets.yaml") DATA_CONFIG = load_data_config() +DATA_CONFIG["datasets"].update(DATASET_CONFIG["datasets"]) def get_mcts_logger(): From 401ca97846b91a97629c4a8144b29c44d79fe1bb Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 9 Sep 2024 13:54:09 +0800 Subject: [PATCH 041/160] update dataset.py --- expo/data/dataset.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 4ac931d9f..8bcce0b1a 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -338,13 +338,13 @@ async def process_dataset(dataset, solution_designer: SolutionDesigner, save_ana save_analysis_pool = True datasets_dict = {"datasets": {}} solution_designer = SolutionDesigner() - # for dataset_id in OPENML_DATASET_IDS: - # openml_dataset = OpenMLExpDataset("", datasets_dir, dataset_id, force_update=force_update) - # asyncio.run(process_dataset(openml_dataset, solution_designer, save_analysis_pool, datasets_dict)) + for dataset_id in OPENML_DATASET_IDS: + openml_dataset = OpenMLExpDataset("", datasets_dir, dataset_id, force_update=force_update) + asyncio.run(process_dataset(openml_dataset, solution_designer, save_analysis_pool, datasets_dict)) - # for dataset_name, target_col in CUSTOM_DATASETS: - # custom_dataset = ExpDataset(dataset_name, datasets_dir, target_col=target_col, force_update=force_update) - # asyncio.run(process_dataset(custom_dataset, solution_designer, save_analysis_pool, datasets_dict)) + for dataset_name, target_col in CUSTOM_DATASETS: + custom_dataset = ExpDataset(dataset_name, datasets_dir, target_col=target_col, force_update=force_update) + asyncio.run(process_dataset(custom_dataset, solution_designer, save_analysis_pool, datasets_dict)) for dataset_name, target_col in DSAGENT_DATASETS: custom_dataset = ExpDataset(dataset_name, datasets_dir, target_col=target_col, force_update=force_update) From 9ba0d217fc25ae4732394173a1c3626de5679128 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 9 Sep 2024 14:52:25 +0800 Subject: [PATCH 042/160] update mcts logic --- expo/MCTS.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 4090331cd..360baac8d 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -279,7 +279,8 @@ def backpropagate(self, node: Node, reward): def best_path(self, root: Node): best_child = root - best_score = 0 + global_best_score = root.normalized_reward["test_score"] + dev_best_score = root.normalized_reward["dev_score"] def bfs(node: Node, best_score, best_child: Node, split): assert split in ["test_score", "dev_score"] @@ -294,10 +295,10 @@ def bfs(node: Node, best_score, best_child: Node, split): best_score, best_child = bfs(child, best_score, best_child, split) return best_score, best_child - _, best_child = bfs(root, best_score, best_child, "test_score") - _, dev_best_child = bfs(root, best_score, best_child, "dev_score") + _, global_best_child = bfs(root, global_best_score, best_child, "test_score") + _, dev_best_child = bfs(root, dev_best_score, best_child, "dev_score") - return {"dev_best": dev_best_child, "global_best": best_child} + return {"dev_best": dev_best_child, "global_best": global_best_child} def get_num_simulations(self): return self.root_node.visited From 294d0fe70968ae66ff45200da3569e90c127951a Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 9 Sep 2024 16:59:39 +0800 Subject: [PATCH 043/160] fix nbclient --- expo/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/utils.py b/expo/utils.py index 9c6295fa9..270842e41 100644 --- a/expo/utils.py +++ b/expo/utils.py @@ -110,7 +110,7 @@ async def load_execute_notebook(role): codes = [task.code for task in tasks if task.code] executor = role.execute_code executor.nb = nbformat.v4.new_notebook() - executor.nb.client = NotebookClient(executor.nb) + executor.nb_client = NotebookClient(executor.nb) # await executor.build() for code in codes: outputs, success = await executor.run(code) From af41f1f1cffb5a6da3429ed39d77e6d5cb507741 Mon Sep 17 00:00:00 2001 From: duyipan Date: Mon, 9 Sep 2024 11:26:23 +0000 Subject: [PATCH 044/160] Update README.md add aide setup and run --- expo/README.md | 146 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 145 insertions(+), 1 deletion(-) diff --git a/expo/README.md b/expo/README.md index eab3298dc..0d47ab3e4 100644 --- a/expo/README.md +++ b/expo/README.md @@ -57,7 +57,151 @@ score_dict = experimenter.evaluate_pred_files(dev_pred_path, test_pred_path) ### AIDE -提供github链接,并说明使用的命令以及参数设置 + +#### Setup + +``` +git clone https://github.com/WecoAI/aideml.git +``` + +修改 `aideml/aide/utils/config.yaml` 内容如下 + +```yaml +# path to the task data directory +data_dir: null + +# either provide a path to a plaintext file describing the task +desc_file: null +# or provide the task goal (and optionally evaluation information) as arguments +goal: null +eval: null + +log_dir: logs +workspace_dir: workspaces + +# whether to unzip any archives in the data directory +preprocess_data: True +# whether to copy the data to the workspace directory (otherwise it will be symlinked) +# copying is recommended to prevent the agent from accidentally modifying the original data +copy_data: True + +exp_name: null # a random experiment name will be generated if not provided + +# settings for code execution +exec: + timeout: 3600 + agent_file_name: runfile.py + format_tb_ipython: False + +# agent hyperparams +agent: + # how many improvement iterations to run + steps: 10 + # whether to instruct the agent to use CV (set to 1 to disable) + k_fold_validation: 1 + # whether to instruct the agent to generate a prediction function + expose_prediction: False + # whether to provide the agent with a preview of the data + data_preview: True + + # LLM settings for coding + code: + model: deepseek-coder + temp: 0.5 + + # LLM settings for evaluating program output / tracebacks + feedback: + model: deepseek-coder + temp: 0.5 + + # hyperparameters for the tree search + search: + max_debug_depth: 3 + debug_prob: 0.5 + num_drafts: 5 +``` + +由于 deepseek 完全兼容 OpenAI 的 API,修改`base_url`为`自己的url`,`api_key`为`自己的key`即可 + +``` +export OPENAI_API_KEY="自己的key" +export OPENAI_BASE_URL="自己的url" +``` + +修改`aideml/aide/backend/__init__.py` 30 行内容如下: + +```python +model_kwargs = model_kwargs | { + "model": model, + "temperature": temperature, + "max_tokens": max_tokens, + } + if "claude-" in model: + query_func = backend_anthropic.query + else: + query_func = backend_openai.query +``` + +由于 deepseekV2.5 不再支持 system message 使用 function call,修改 `aideml/aide/agent.py` 312 行内容如下: + +```python +response = cast( + dict, + query( + system_message=None, + user_message=prompt, + func_spec=review_func_spec, + model=self.acfg.feedback.model, + temperature=self.acfg.feedback.temp, + ), + ) +``` + +修改完后 + +``` +cd aideml +pip install -e . +``` + +#### Run + +运行下面脚本获取运行结果,在当前目录下将生成一个 log 文件夹以及 workspace 文件夹 +log 文件夹中将包含实验使用配置以及生成方案记录,workspace 文件夹下将保存 aide 最后生成的结果文件 + +```python +import aide +import os +import time + +os.environ["OPENAI_API_KEY"] = "sk-xxx" +os.environ["OPENAI_BASE_URL"] = "your url" +start_time = time.time() +data_dir = "xxx/data/titanic" +goal = f""" +# User requirement +({data_dir}, 'This is a 04_titanic dataset. Your goal is to predict the target column `Survived`.\nPerform data analysis, data preprocessing, feature engineering, and modeling to predict the target. \nReport f1 on the eval data. Do not plot or make any visualizations.\n') + +# Data dir +training (with labels): train.csv +testing (without labels): test.csv +dataset description: dataset_info.json (You can use this file to get additional information about the dataset)""" + +exp = aide.Experiment( + data_dir=data_dir, # replace this with your own directory + goal=goal, + eval="f1", # replace with your own evaluation metric +) + +best_solution = exp.run(steps=10) + +print(f"Best solution has validation metric: {best_solution.valid_metric}") +print(f"Best solution code: {best_solution.code}") +end_time = time.time() +execution_time = end_time - start_time + +print(f"run time : {execution_time} seconds") +``` ### Autogluon #### Setup From 60e8e3eab8f41b52a03137bfb8f8c99be9bf926b Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 10 Sep 2024 13:51:54 +0800 Subject: [PATCH 045/160] fix hfdataset; make dirs when save notebook --- expo/data/hf_data.py | 9 ++++++--- expo/utils.py | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/expo/data/hf_data.py b/expo/data/hf_data.py index a7e2a1afe..9ed2b2c48 100644 --- a/expo/data/hf_data.py +++ b/expo/data/hf_data.py @@ -38,18 +38,21 @@ def get_raw_dataset(self): df = pd.read_csv(Path(raw_dir, "train.csv")) else: df = self.dataset["train"].to_pandas() - df.to_csv(Path(raw_dir, "train.csv")) + df.to_csv(Path(raw_dir, "train.csv"), index=False) if os.path.exists(Path(raw_dir, "test.csv")): - test_df = pd.read_csv(Path(raw_dir, "test.csv")) + test_df = pd.read_csv(Path(raw_dir, "test.csv"), index=False) else: if "test" in self.dataset: test_df = self.dataset["test"].to_pandas() - test_df.to_csv(Path(raw_dir, "test.csv")) + test_df.to_csv(Path(raw_dir, "test.csv"), index=False) else: test_df = None return df, test_df + # def get_df_head(self, raw_df): + # return raw_df.head() + if __name__ == "__main__": dataset_dir = "D:/work/automl/datasets" diff --git a/expo/utils.py b/expo/utils.py index 270842e41..44de8cf9b 100644 --- a/expo/utils.py +++ b/expo/utils.py @@ -99,6 +99,7 @@ def save_notebook(role: Role, save_dir: str = "", name: str = ""): for code in codes: clean_nb.cells.append(nbformat.v4.new_code_cell(code)) nb = process_cells(role.execute_code.nb) + os.makedirs(save_dir, exist_ok=True) file_path = save_dir / f"{name}.ipynb" clean_file_path = save_dir / f"{name}_clean.ipynb" nbformat.write(nb, file_path) From d34a482faf0ec5478f9dad0291f65e6a85931006 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 10 Sep 2024 14:11:47 +0800 Subject: [PATCH 046/160] give dev label --- expo/data/dataset.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 8bcce0b1a..88528eb5c 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -20,23 +20,19 @@ DI_INSTRUCTION = """\ **Attention** 1. Please do not leak the target label in any form during training. -2. Dev and Test sets do not have the target column. +2. Test set does not have the target column. 3. You should perform transformations on train, dev, and test sets at the same time (it's a good idea to define functions for this and avoid code repetition). 4. If labels are transformed during training, they should be transformed back to the original format before saving the predictions. +5. You could split the training set further to make cross-validation and hyperparameter tuning. ## Saving Dev and Test Predictions 1. Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. - Both files should contain a single column named `target` with the predicted values. 2. Make sure the prediction results are in the same format as the target column in the training set. -- The labels should be transformed back to the original format if any transformation was applied during training. -## Output Training Set Performance +## Output Performance Make sure the performance of the model is printed in python in the last step even if it has been printed in the previous steps. The value should be a float number. -Print the training set performance in the last step. Write in this format: -```python -... -print("Train score:", train_score) -``` +Print the training set performance in the last step. # Output dir {output_dir} @@ -48,7 +44,7 @@ {additional_instruction} # Data dir training (with labels): {train_path} -dev (without labels): {dev_path} +dev (with labels): {dev_path} testing (without labels): {test_path} dataset description: {data_info_path} (You can use this file to get additional information about the dataset) """ @@ -147,7 +143,7 @@ def generate_task_requirement(task_name, data_config, is_di=True): user_requirement = get_user_requirement(task_name, data_config) split_dataset_path = get_split_dataset_path(task_name, data_config) train_path = split_dataset_path["train"] - dev_path = split_dataset_path["dev_wo_target"] + dev_path = split_dataset_path["dev"] test_path = split_dataset_path["test_wo_target"] work_dir = data_config["work_dir"] output_dir = f"{work_dir}/{task_name}" @@ -225,7 +221,7 @@ def get_dataset_info(self): "NumberOfSymbolicFeatures": raw_df.select_dtypes(include=["object"]).shape[1], } - df_head_text = raw_df.head().to_string(index=False) + df_head_text = self.get_df_head(raw_df) dataset_info = { "name": self.name, @@ -236,6 +232,9 @@ def get_dataset_info(self): } return dataset_info + def get_df_head(self, raw_df): + return raw_df.head().to_string(index=False) + def get_metric(self): dataset_info = self.get_dataset_info() num_classes = dataset_info["metadata"]["NumberOfClasses"] From e3663f2322b9ad97e4566b08f1dca53fedd70d0f Mon Sep 17 00:00:00 2001 From: Rayhao Date: Mon, 9 Sep 2024 23:21:56 -0700 Subject: [PATCH 047/160] implement autuglu exp --- .gitignore | 1 + expo/experimenter/autogluon.py | 35 ++++++++++++++++++++++++---------- expo/experimenter/custom.py | 2 +- expo/run_experiment.py | 5 ++++- 4 files changed, 31 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index 3fc66cecb..6e1fc7f74 100644 --- a/.gitignore +++ b/.gitignore @@ -29,6 +29,7 @@ share/python-wheels/ MANIFEST metagpt/tools/schemas/ examples/data/search_kb/*.json +expo/AutogluonModels # PyInstaller # Usually these files are written by a python scripts from a template diff --git a/expo/experimenter/autogluon.py b/expo/experimenter/autogluon.py index 4f5d151ef..b33411773 100644 --- a/expo/experimenter/autogluon.py +++ b/expo/experimenter/autogluon.py @@ -1,27 +1,29 @@ +from datetime import datetime from autogluon.tabular import TabularDataset, TabularPredictor - from expo.experimenter.custom import CustomExperimenter class AGRunner: preset = "best_quality" - time_limit = 500 + time_limit = 1000 # 1000s - def __init__(self, datasets): - self.datasets = datasets + def __init__(self, state=None): + self.state = state + self.datasets = self.state["datasets_dir"] def run(self): train_path = self.datasets["train"] - test_wo_target_path = self.datasets["test_wo_target"] dev_wo_target_path = self.datasets["dev_wo_target"] + test_wo_target_path = self.datasets["test_wo_target"] target_col = self.state["dataset_config"]["target_col"] train_data = TabularDataset(train_path) - test_data = TabularDataset(test_wo_target_path) dev_data = TabularDataset(dev_wo_target_path) - - predictor = TabularPredictor(label=target_col).fit(train_data, presets=self.preset, time_limit=self.time_limit) - test_preds = predictor.predict(test_data) + test_data = TabularDataset(test_wo_target_path) + eval_metric = self.state["dataset_config"]["metric"].replace(" ", "_") + # predictor = TabularPredictor(label=target_col, eval_metric=eval_metric, path="AutogluonModels/ag-{}-{}".format(self.state['task'], datetime.now().strftime("%y%m%d_%H%M"))).fit(train_data, presets=self.preset, time_limit=self.time_limit, fit_weighted_ensemble=False, num_gpus=1) + predictor = TabularPredictor(label=target_col, eval_metric=eval_metric, path="AutogluonModels/ag-{}-{}".format(self.state['task'], datetime.now().strftime("%y%m%d_%H%M"))).fit(train_data, num_gpus=1) dev_preds = predictor.predict(dev_data) + test_preds = predictor.predict(test_data) return {"test_preds": test_preds, "dev_preds": dev_preds} @@ -30,4 +32,17 @@ class GluonExperimenter(CustomExperimenter): def __init__(self, args, **kwargs): super().__init__(args, **kwargs) - self.framework = AGRunner(self.datasets) + self.framework = AGRunner(self.state) + + def run_experiment(self): + result = self.framework.run() + user_requirement = self.state["requirement"] + dev_preds = result["dev_preds"] + test_preds = result["test_preds"] + score_dict = { + "dev_score": self.evaluate_predictions(dev_preds, "dev"), + "test_score": self.evaluate_predictions(test_preds, "test"), + } + results = [0, {"score_dict": score_dict, "user_requirement": user_requirement, "args": vars(self.args)}] + self.save_result(results) + return results \ No newline at end of file diff --git a/expo/experimenter/custom.py b/expo/experimenter/custom.py index 4a5486af0..df090fb58 100644 --- a/expo/experimenter/custom.py +++ b/expo/experimenter/custom.py @@ -12,7 +12,7 @@ class CustomExperimenter(Experimenter): def __init__(self, args, **kwargs): super().__init__(args, **kwargs) - self.framework = kwargs["framework"] # todo + self.framework = kwargs.get("framework", None) # todo self.task = kwargs.get("task", self.args.task) self.low_is_better = kwargs.get("low_is_better", self.args.low_is_better) self.name = kwargs.get("name", "") diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 83237741a..74f9c6e57 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -5,12 +5,13 @@ from expo.experimenter.custom import CustomExperimenter from expo.experimenter.experimenter import Experimenter from expo.experimenter.mcts import MCTSExperimenter +from expo.experimenter.autogluon import GluonExperimenter def get_args(): parser = argparse.ArgumentParser() parser.add_argument("--name", type=str, default="") - parser.add_argument("--exp_mode", type=str, default="mcts", choices=["mcts", "aug", "base", "custom", "greedy"]) + parser.add_argument("--exp_mode", type=str, default="mcts", choices=["mcts", "aug", "base", "custom", "greedy", "autoglu"]) get_di_args(parser) get_mcts_args(parser) get_aug_exp_args(parser) @@ -47,6 +48,8 @@ async def main(args): experimenter = AugExperimenter(args) elif args.exp_mode == "base": experimenter = Experimenter(args) + elif args.exp_mode == "autoglu": + experimenter = GluonExperimenter(args) elif args.exp_mode == "custom": experimenter = CustomExperimenter(args) else: From b776c7309bc64c0ec62c8efa4acbe93fdbd1a75f Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 10 Sep 2024 15:30:23 +0800 Subject: [PATCH 048/160] add random tree search --- expo/Greedy.py | 10 ++++++++++ expo/data/dataset.py | 2 +- expo/experimenter/mcts.py | 10 ++++++---- expo/run_experiment.py | 8 ++++++-- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/expo/Greedy.py b/expo/Greedy.py index f6f60db01..8c8d865cd 100644 --- a/expo/Greedy.py +++ b/expo/Greedy.py @@ -1,3 +1,5 @@ +import random + from expo.MCTS import MCTS @@ -7,3 +9,11 @@ def best_child(self): return self.root_node all_children = [child for children in self.children.values() for child in children] return max(all_children, key=lambda x: x.normalized_reward.get("dev_score", 0)) + + +class Random(MCTS): + def best_child(self): + if len(self.children) == 0: + return self.root_node + all_children = [child for children in self.children.values() for child in children] + return random.choice(all_children) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 88528eb5c..43ac8ee0d 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -23,7 +23,7 @@ 2. Test set does not have the target column. 3. You should perform transformations on train, dev, and test sets at the same time (it's a good idea to define functions for this and avoid code repetition). 4. If labels are transformed during training, they should be transformed back to the original format before saving the predictions. -5. You could split the training set further to make cross-validation and hyperparameter tuning. +5. You could utilize dev set to improve the model. ## Saving Dev and Test Predictions 1. Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 9bf7306c4..fbe2f35f1 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -1,19 +1,21 @@ from expo.evaluation.visualize_mcts import get_tree_text from expo.experimenter.experimenter import Experimenter -from expo.Greedy import Greedy +from expo.Greedy import Greedy, Random from expo.MCTS import MCTS class MCTSExperimenter(Experimenter): result_path: str = "results/mcts" - def __init__(self, args, greedy=False, **kwargs): + def __init__(self, args, tree_mode=None, **kwargs): super().__init__(args, **kwargs) - self.greedy = greedy + self.tree_mode = tree_mode async def run_experiment(self): - if self.greedy: + if self.tree_mode == "greedy": mcts = Greedy(root_node=None, max_depth=5) + elif self.tree_mode == "random": + mcts = Random(root_node=None, max_depth=5) else: mcts = MCTS(root_node=None, max_depth=5) best_nodes = await mcts.search( diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 83237741a..b68607d79 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -10,7 +10,9 @@ def get_args(): parser = argparse.ArgumentParser() parser.add_argument("--name", type=str, default="") - parser.add_argument("--exp_mode", type=str, default="mcts", choices=["mcts", "aug", "base", "custom", "greedy"]) + parser.add_argument( + "--exp_mode", type=str, default="mcts", choices=["mcts", "aug", "base", "custom", "greedy", "random"] + ) get_di_args(parser) get_mcts_args(parser) get_aug_exp_args(parser) @@ -42,7 +44,9 @@ async def main(args): if args.exp_mode == "mcts": experimenter = MCTSExperimenter(args) elif args.exp_mode == "greedy": - experimenter = MCTSExperimenter(args, greedy=True) + experimenter = MCTSExperimenter(args, tree_mode="greedy") + elif args.exp_mode == "random": + experimenter = MCTSExperimenter(args, tree_mode="random") elif args.exp_mode == "aug": experimenter = AugExperimenter(args) elif args.exp_mode == "base": From a373e684aec59a70270c8be99c721dd71a0c4de3 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 10 Sep 2024 16:05:22 +0800 Subject: [PATCH 049/160] update di instruction --- expo/data/dataset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 43ac8ee0d..510c39fce 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -23,7 +23,7 @@ 2. Test set does not have the target column. 3. You should perform transformations on train, dev, and test sets at the same time (it's a good idea to define functions for this and avoid code repetition). 4. If labels are transformed during training, they should be transformed back to the original format before saving the predictions. -5. You could utilize dev set to improve the model. +5. You could utilize dev set to improve model training. ## Saving Dev and Test Predictions 1. Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. @@ -46,7 +46,7 @@ training (with labels): {train_path} dev (with labels): {dev_path} testing (without labels): {test_path} -dataset description: {data_info_path} (You can use this file to get additional information about the dataset) +dataset description: {data_info_path} (During EDA, you can use this file to get additional information about the dataset) """ From 923109e88295b9185ede4c8093afd8d965e8f9a7 Mon Sep 17 00:00:00 2001 From: duyipan Date: Tue, 10 Sep 2024 16:27:29 +0800 Subject: [PATCH 050/160] add aide.py update README --- expo/README.md | 34 ++-------------------------------- expo/experimenter/aide.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 32 deletions(-) create mode 100644 expo/experimenter/aide.py diff --git a/expo/README.md b/expo/README.md index 0d47ab3e4..55ea7eed4 100644 --- a/expo/README.md +++ b/expo/README.md @@ -169,38 +169,8 @@ pip install -e . 运行下面脚本获取运行结果,在当前目录下将生成一个 log 文件夹以及 workspace 文件夹 log 文件夹中将包含实验使用配置以及生成方案记录,workspace 文件夹下将保存 aide 最后生成的结果文件 -```python -import aide -import os -import time - -os.environ["OPENAI_API_KEY"] = "sk-xxx" -os.environ["OPENAI_BASE_URL"] = "your url" -start_time = time.time() -data_dir = "xxx/data/titanic" -goal = f""" -# User requirement -({data_dir}, 'This is a 04_titanic dataset. Your goal is to predict the target column `Survived`.\nPerform data analysis, data preprocessing, feature engineering, and modeling to predict the target. \nReport f1 on the eval data. Do not plot or make any visualizations.\n') - -# Data dir -training (with labels): train.csv -testing (without labels): test.csv -dataset description: dataset_info.json (You can use this file to get additional information about the dataset)""" - -exp = aide.Experiment( - data_dir=data_dir, # replace this with your own directory - goal=goal, - eval="f1", # replace with your own evaluation metric -) - -best_solution = exp.run(steps=10) - -print(f"Best solution has validation metric: {best_solution.valid_metric}") -print(f"Best solution code: {best_solution.code}") -end_time = time.time() -execution_time = end_time - start_time - -print(f"run time : {execution_time} seconds") +``` +python experimenter/aide.py ``` ### Autogluon diff --git a/expo/experimenter/aide.py b/expo/experimenter/aide.py new file mode 100644 index 000000000..fb71dbdab --- /dev/null +++ b/expo/experimenter/aide.py @@ -0,0 +1,31 @@ +import aide +import os +import time + +os.environ["OPENAI_API_KEY"] = "sk-xxx" +os.environ["OPENAI_BASE_URL"] = "your url" +start_time = time.time() +data_dir = "xxx/data/titanic" +goal = f""" +# User requirement +({data_dir}, 'This is a 04_titanic dataset. Your goal is to predict the target column `Survived`.\nPerform data analysis, data preprocessing, feature engineering, and modeling to predict the target. \nReport f1 on the eval data. Do not plot or make any visualizations.\n') + +# Data dir +training (with labels): train.csv +testing (without labels): test.csv +dataset description: dataset_info.json (You can use this file to get additional information about the dataset)""" + +exp = aide.Experiment( + data_dir=data_dir, # replace this with your own directory + goal=goal, + eval="f1", # replace with your own evaluation metric +) + +best_solution = exp.run(steps=10) + +print(f"Best solution has validation metric: {best_solution.valid_metric}") +print(f"Best solution code: {best_solution.code}") +end_time = time.time() +execution_time = end_time - start_time + +print(f"run time : {execution_time} seconds") \ No newline at end of file From a8ee20843b7492743c3fcb434c3423b130447ead Mon Sep 17 00:00:00 2001 From: Rayhao Date: Tue, 10 Sep 2024 19:20:24 -0700 Subject: [PATCH 051/160] autogluon small fix --- expo/experimenter/autogluon.py | 5 ++--- expo/run_experiment.py | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/expo/experimenter/autogluon.py b/expo/experimenter/autogluon.py index b33411773..478ecfc01 100644 --- a/expo/experimenter/autogluon.py +++ b/expo/experimenter/autogluon.py @@ -34,7 +34,7 @@ def __init__(self, args, **kwargs): super().__init__(args, **kwargs) self.framework = AGRunner(self.state) - def run_experiment(self): + async def run_experiment(self): result = self.framework.run() user_requirement = self.state["requirement"] dev_preds = result["dev_preds"] @@ -44,5 +44,4 @@ def run_experiment(self): "test_score": self.evaluate_predictions(test_preds, "test"), } results = [0, {"score_dict": score_dict, "user_requirement": user_requirement, "args": vars(self.args)}] - self.save_result(results) - return results \ No newline at end of file + self.save_result(results) \ No newline at end of file diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 74f9c6e57..cfdd295b2 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -11,7 +11,7 @@ def get_args(): parser = argparse.ArgumentParser() parser.add_argument("--name", type=str, default="") - parser.add_argument("--exp_mode", type=str, default="mcts", choices=["mcts", "aug", "base", "custom", "greedy", "autoglu"]) + parser.add_argument("--exp_mode", type=str, default="mcts", choices=["mcts", "aug", "base", "custom", "greedy", "autogluon"]) get_di_args(parser) get_mcts_args(parser) get_aug_exp_args(parser) @@ -48,7 +48,7 @@ async def main(args): experimenter = AugExperimenter(args) elif args.exp_mode == "base": experimenter = Experimenter(args) - elif args.exp_mode == "autoglu": + elif args.exp_mode == "autogluon": experimenter = GluonExperimenter(args) elif args.exp_mode == "custom": experimenter = CustomExperimenter(args) From 35b9ea097e26784c16c638e5f839c754739ebca1 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 11 Sep 2024 11:59:52 +0800 Subject: [PATCH 052/160] update di instruction --- expo/data/dataset.py | 1 + 1 file changed, 1 insertion(+) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 510c39fce..c83f7b926 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -24,6 +24,7 @@ 3. You should perform transformations on train, dev, and test sets at the same time (it's a good idea to define functions for this and avoid code repetition). 4. If labels are transformed during training, they should be transformed back to the original format before saving the predictions. 5. You could utilize dev set to improve model training. +6. Use techniques to avoid overfitting. ## Saving Dev and Test Predictions 1. Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. From c91b2ada88036f8d2575e023e53fa895467478ad Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 11 Sep 2024 13:54:41 +0800 Subject: [PATCH 053/160] remove unnecessary prompt instruction --- expo/data/dataset.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index c83f7b926..1494eb267 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -23,7 +23,7 @@ 2. Test set does not have the target column. 3. You should perform transformations on train, dev, and test sets at the same time (it's a good idea to define functions for this and avoid code repetition). 4. If labels are transformed during training, they should be transformed back to the original format before saving the predictions. -5. You could utilize dev set to improve model training. +5. You could utilize dev set to validate and improve model training. 6. Use techniques to avoid overfitting. ## Saving Dev and Test Predictions @@ -32,8 +32,7 @@ 2. Make sure the prediction results are in the same format as the target column in the training set. ## Output Performance -Make sure the performance of the model is printed in python in the last step even if it has been printed in the previous steps. The value should be a float number. -Print the training set performance in the last step. +Print the train and dev set performance in the last step. # Output dir {output_dir} @@ -44,9 +43,9 @@ {user_requirement} {additional_instruction} # Data dir -training (with labels): {train_path} -dev (with labels): {dev_path} -testing (without labels): {test_path} +train set (with labels): {train_path} +dev set (with labels): {dev_path} +test set (without labels): {test_path} dataset description: {data_info_path} (During EDA, you can use this file to get additional information about the dataset) """ From eaf1b62343f1aade858ddc82255b7d9380ee51ef Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 11 Sep 2024 16:14:50 +0800 Subject: [PATCH 054/160] add timeout to client --- expo/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/utils.py b/expo/utils.py index 44de8cf9b..f3c0c392d 100644 --- a/expo/utils.py +++ b/expo/utils.py @@ -111,7 +111,7 @@ async def load_execute_notebook(role): codes = [task.code for task in tasks if task.code] executor = role.execute_code executor.nb = nbformat.v4.new_notebook() - executor.nb_client = NotebookClient(executor.nb) + executor.nb_client = NotebookClient(executor.nb, timeout=executor.timeout) # await executor.build() for code in codes: outputs, success = await executor.run(code) From 318db0785c3063b8a39a8f10a15d808cc3968277 Mon Sep 17 00:00:00 2001 From: charles_zhang <26547-charles_zhang@users.noreply.gitlab.aicrowd.com> Date: Wed, 11 Sep 2024 21:12:36 +0800 Subject: [PATCH 055/160] add: ds-agent setup --- expo/README.md | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/expo/README.md b/expo/README.md index 55ea7eed4..4dc1341e6 100644 --- a/expo/README.md +++ b/expo/README.md @@ -53,8 +53,44 @@ score_dict = experimenter.evaluate_pred_files(dev_pred_path, test_pred_path) ## 4. Baselines ### DS Agent -提供github链接,并说明使用的命令以及参数设置 +``` +git clone https://github.com/guosyjlu/DS-Agent.git +``` +将其deployment/generate.py line46-48行部分修改如下(目的是用deepseek而非GPT的API): +```python +messages = [{"role": "user", "content": prompt}] + +if 'gpt' in llm: + response = openai.ChatCompletion.create(**{"messages": messages,**raw_request}) + raw_completion = response["choices"][0]["message"]["content"] + +elif llm == 'deepseek-coder': + from openai import OpenAI + client = OpenAI( + api_key="yours", + base_url="https://oneapi.deepwisdom.ai/v1" + ) + response = client.chat.completions.create( + model="deepseek-coder", + messages=[ + # {"role": "system", "content": "You are a helpful assistant"}, + {"role": "user", "content": prompt}, + ], + temperature=temperature, + stream=False + ) + raw_completion = response.choices[0].message.content + +completion = raw_completion.split("```python")[1].split("```")[0] +``` + +修改完后在新建一个`deployment/test.sh` 分别运行下列两行,`$TASK` 是你要测试的task name +``` +python -u generate.py --llm deepseek-coder --task $TASK --shot 1 --retrieval > "$TASK".txt 2>&1 + +python -u evaluation.py --path "deepseek-coder_True_1" --task $TASK --device 0 > "$TASK"_eval.txt 2>&1 +``` ### AIDE From 9ba9371656f04af5553ddeb95affd35ebe66ae53 Mon Sep 17 00:00:00 2001 From: limafang Date: Thu, 12 Sep 2024 20:26:11 +0800 Subject: [PATCH 056/160] add autosklearn setup run --- expo/README.md | 9 +++ expo/experimenter/autosklearn.py | 110 +++++++++++++++++++++++++++++++ expo/run_experiment.py | 5 +- 3 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 expo/experimenter/autosklearn.py diff --git a/expo/README.md b/expo/README.md index 55ea7eed4..e824312f2 100644 --- a/expo/README.md +++ b/expo/README.md @@ -182,6 +182,15 @@ pip install autogluon ``` 提供github链接,并说明使用的命令以及参数设置 +### AutoSklearn +#### Setup +``` +pip install autosklearn +``` +#### Run +``` +python run_experiment.py --exp_mode autosklearn --task titanic +``` ### Base DI For setup, check 5. diff --git a/expo/experimenter/autosklearn.py b/expo/experimenter/autosklearn.py new file mode 100644 index 000000000..5786a3790 --- /dev/null +++ b/expo/experimenter/autosklearn.py @@ -0,0 +1,110 @@ +from datetime import datetime +import autosklearn.classification +import autosklearn.regression +import pandas as pd +from expo.experimenter.custom import CustomExperimenter +from expo.evaluation.evaluation import evaluate_score +from autosklearn.metrics import make_scorer +from functools import partial + + +def custom_scorer(y_true, y_pred, metric_name): + return evaluate_score(y_pred, y_true, metric_name) + + +def create_autosklearn_scorer(metric_name): + return make_scorer( + name=metric_name, score_func=partial(custom_scorer, metric_name=metric_name) + ) + + +class ASRunner: + time_limit = 300 + + def __init__(self, state=None): + self.state = state + self.datasets = self.state["datasets_dir"] + + def run(self): + train_path = self.datasets["train"] + dev_wo_target_path = self.datasets["dev_wo_target"] + test_wo_target_path = self.datasets["test_wo_target"] + target_col = self.state["dataset_config"]["target_col"] + + train_data = pd.read_csv(train_path) + dev_data = pd.read_csv(dev_wo_target_path) + test_data = pd.read_csv(test_wo_target_path) + eval_metric = self.state["dataset_config"]["metric"].replace(" ", "_") + X_train = train_data.drop(columns=[target_col]) + y_train = train_data[target_col] + + if eval_metric == "rmse": + automl = autosklearn.regression.AutoSklearnRegressor( + time_left_for_this_task=self.time_limit, + per_run_time_limit=60, + metric=create_autosklearn_scorer("rmse"), # 使用新的函数创建评分器 + memory_limit=8192, + tmp_folder="AutosklearnModels/as-{}-{}".format( + self.state["task"], datetime.now().strftime("%y%m%d_%H%M") + ), + n_jobs=-1, + ) + elif eval_metric == "f1": + automl = autosklearn.classification.AutoSklearnClassifier( + time_left_for_this_task=self.time_limit, + per_run_time_limit=60, + metric=create_autosklearn_scorer("f1"), # 使用新的函数创建评分器 + memory_limit=8192, + tmp_folder="AutosklearnModels/as-{}-{}".format( + self.state["task"], datetime.now().strftime("%y%m%d_%H%M") + ), + n_jobs=-1, + ) + elif eval_metric == "f1_weighted": + automl = autosklearn.classification.AutoSklearnClassifier( + time_left_for_this_task=self.time_limit, + per_run_time_limit=60, + metric=create_autosklearn_scorer( + "f1 weighted" + ), # 使用新的函数创建评分器 + memory_limit=8192, + tmp_folder="AutosklearnModels/as-{}-{}".format( + self.state["task"], datetime.now().strftime("%y%m%d_%H%M") + ), + n_jobs=-1, + ) + else: + raise ValueError(f"Unsupported metric: {eval_metric}") + automl.fit(X_train, y_train) + + dev_preds = automl.predict(dev_data) + test_preds = automl.predict(test_data) + + return {"test_preds": test_preds, "dev_preds": dev_preds} + + +class AutoSklearnExperimenter(CustomExperimenter): + result_path: str = "results/autosklearn" + + def __init__(self, args, **kwargs): + super().__init__(args, **kwargs) + self.framework = ASRunner(self.state) + + async def run_experiment(self): + result = self.framework.run() + user_requirement = self.state["requirement"] + dev_preds = result["dev_preds"] + test_preds = result["test_preds"] + score_dict = { + "dev_score": self.evaluate_predictions(dev_preds, "dev"), + "test_score": self.evaluate_predictions(test_preds, "test"), + } + results = [ + 0, + { + "score_dict": score_dict, + "user_requirement": user_requirement, + "args": vars(self.args), + }, + ] + self.save_result(results) diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 2123fade3..2c996a737 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -6,6 +6,7 @@ from expo.experimenter.custom import CustomExperimenter from expo.experimenter.experimenter import Experimenter from expo.experimenter.mcts import MCTSExperimenter +from expo.experimenter.autosklearn import AutoSklearnExperimenter def get_args(): @@ -15,7 +16,7 @@ def get_args(): "--exp_mode", type=str, default="mcts", - choices=["mcts", "aug", "base", "custom", "greedy", "autogluon", "random"], + choices=["mcts", "aug", "base", "custom", "greedy", "autogluon", "random", "autosklearn"], ) get_di_args(parser) get_mcts_args(parser) @@ -59,6 +60,8 @@ async def main(args): experimenter = GluonExperimenter(args) elif args.exp_mode == "custom": experimenter = CustomExperimenter(args) + elif args.exp_mode == "autosklearn": + experimenter = AutoSklearnExperimenter(args) else: raise ValueError(f"Invalid exp_mode: {args.exp_mode}") await experimenter.run_experiment() From 562af8c4e4b2a54275d0501420d4e9b4cd09a612 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 13 Sep 2024 11:09:02 +0800 Subject: [PATCH 057/160] update text dataset --- expo/data/dataset.py | 5 ++-- expo/data/hf_data.py | 61 +++++++++++++++++++++++++++++---------- expo/datasets.yaml | 69 +++++++++++++++++++++++++++++--------------- 3 files changed, 94 insertions(+), 41 deletions(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 1494eb267..3b2017d1a 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -268,8 +268,9 @@ def save_dataset(self, target_col): print(f"Dataset info for {self.name} already exists") def save_datasetinfo(self, dataset_info): - with open(Path(self.dataset_dir, self.name, "dataset_info.json"), "w") as file: - json.dump(dataset_info, file, indent=4) + with open(Path(self.dataset_dir, self.name, "dataset_info.json"), "w", encoding="utf-8") as file: + # utf-8 encoding is required + json.dump(dataset_info, file, indent=4, ensure_ascii=False) def save_split_datasets(self, df, split, target_col=None): path = Path(self.dataset_dir, self.name) diff --git a/expo/data/hf_data.py b/expo/data/hf_data.py index 9ed2b2c48..6f1f55f6d 100644 --- a/expo/data/hf_data.py +++ b/expo/data/hf_data.py @@ -9,12 +9,22 @@ from expo.insights.solution_designer import SolutionDesigner HFDATSETS = [ - {"name": "sms_spam", "dataset_name": "ucirvine/sms_spam", "target_col": "label"}, - {"name": "banking77", "dataset_name": "PolyAI/banking77", "target_col": "label"}, - {"name": "gnad10", "dataset_name": "community-datasets/gnad10", "target_col": "label"}, - {"name": "oxford-iiit-pet", "dataset_name": "timm/oxford-iiit-pet", "target_col": "label"}, - {"name": "stanford_cars", "dataset_name": "tanganke/stanford_cars", "target_col": "label"}, - {"name": "fashion_mnist", "dataset_name": "zalando-datasets/fashion_mnist", "target_col": "label"}, + # {"name": "sms_spam", "dataset_name": "ucirvine/sms_spam", "target_col": "label", "modality": "text"}, + # {"name": "banking77", "dataset_name": "PolyAI/banking77", "target_col": "label", "modality": "text"}, + # {"name": "gnad10", "dataset_name": "community-datasets/gnad10", "target_col": "label", "modality": "text"}, + { + "name": "oxford-iiit-pet", + "dataset_name": "timm/oxford-iiit-pet", + "target_col": "label_cat_dog", + "modality": "image", + }, + {"name": "stanford_cars", "dataset_name": "tanganke/stanford_cars", "target_col": "label", "modality": "image"}, + { + "name": "fashion_mnist", + "dataset_name": "zalando-datasets/fashion_mnist", + "target_col": "label", + "modality": "image", + }, ] @@ -27,41 +37,60 @@ def __init__(self, name, dataset_dir, dataset_name, **kwargs): self.name = name self.dataset_dir = dataset_dir self.dataset_name = dataset_name + self.modality = kwargs.get("modality", "") self.target_col = kwargs.get("target_col", "label") - self.dataset = load_dataset(dataset_name) + self.dataset = load_dataset(self.dataset_name, trust_remote_code=True) super().__init__(self.name, dataset_dir, **kwargs) def get_raw_dataset(self): raw_dir = Path(self.dataset_dir, self.name, "raw") raw_dir.mkdir(parents=True, exist_ok=True) if os.path.exists(Path(raw_dir, "train.csv")): - df = pd.read_csv(Path(raw_dir, "train.csv")) + df = pd.read_csv(Path(raw_dir, "train.csv"), encoding="utf-8") else: df = self.dataset["train"].to_pandas() - df.to_csv(Path(raw_dir, "train.csv"), index=False) + df.to_csv(Path(raw_dir, "train.csv"), index=False, encoding="utf-8") if os.path.exists(Path(raw_dir, "test.csv")): - test_df = pd.read_csv(Path(raw_dir, "test.csv"), index=False) + test_df = pd.read_csv(Path(raw_dir, "test.csv"), encoding="utf-8") else: - if "test" in self.dataset: + if self.dataset and "test" in self.dataset: test_df = self.dataset["test"].to_pandas() - test_df.to_csv(Path(raw_dir, "test.csv"), index=False) + test_df.to_csv(Path(raw_dir, "test.csv"), index=False, encoding="utf-8") else: test_df = None return df, test_df - # def get_df_head(self, raw_df): - # return raw_df.head() + def get_df_head(self, raw_df): + if self.modality == "text": + examples = [] + for i in range(5): + examples.append(raw_df.iloc[i].to_dict()) + return examples + elif self.modality == "image": + return "" + + def get_dataset_info(self): + dataset_info = super().get_dataset_info() + dataset = self.dataset + dataset_info["description"] = dataset["train"].info.description + return dataset_info if __name__ == "__main__": dataset_dir = "D:/work/automl/datasets" - save_analysis_pool = True + save_analysis_pool = False + force_update = False datasets_dict = {"datasets": {}} solution_designer = SolutionDesigner() for dataset_meta in HFDATSETS: hf_dataset = HFExpDataset( - dataset_meta["name"], dataset_dir, dataset_meta["dataset_name"], target_col=dataset_meta["target_col"] + dataset_meta["name"], + dataset_dir, + dataset_meta["dataset_name"], + target_col=dataset_meta["target_col"], + force_update=force_update, + modality=dataset_meta["modality"], ) asyncio.run(process_dataset(hf_dataset, solution_designer, save_analysis_pool, datasets_dict)) save_datasets_dict_to_yaml(datasets_dict, "hf_datasets.yaml") diff --git a/expo/datasets.yaml b/expo/datasets.yaml index 512cbc292..051e8232d 100644 --- a/expo/datasets.yaml +++ b/expo/datasets.yaml @@ -79,6 +79,14 @@ datasets: \ column `percent_pell_grant`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport rmse on the eval\ \ data. Do not plot or make any visualizations.\n" + concrete-strength: + dataset: concrete-strength + metric: rmse + target_col: Strength + user_requirement: "This is a concrete-strength dataset. Your goal is to predict\ + \ the target column `Strength`.\nPerform data analysis, data preprocessing,\ + \ feature engineering, and modeling to predict the target. \nReport rmse on\ + \ the eval data. Do not plot or make any visualizations.\n" credit-g: dataset: credit-g metric: f1 @@ -135,6 +143,22 @@ datasets: \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 weighted on the eval data.\ \ Do not plot or make any visualizations.\n" + smoker-status: + dataset: smoker-status + metric: f1 + target_col: smoking + user_requirement: "This is a smoker-status dataset. Your goal is to predict the\ + \ target column `smoking`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 on the eval data.\ + \ Do not plot or make any visualizations.\n" + software-defects: + dataset: software-defects + metric: f1 + target_col: defects + user_requirement: "This is a software-defects dataset. Your goal is to predict\ + \ the target column `defects`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 on the eval data.\ + \ Do not plot or make any visualizations.\n" steel-plates-fault: dataset: steel-plates-fault metric: f1 weighted @@ -151,28 +175,27 @@ datasets: \ the target column `Class`.\nPerform data analysis, data preprocessing, feature\ \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ \ eval data. Do not plot or make any visualizations.\n" - concrete-strength: - dataset: concrete-strength - metric: rmse - target_col: Strength - user_requirement: "This is a concrete-strength dataset. Your goal is to predict\ - \ the target column `Strength`.\nPerform data analysis, data preprocessing,\ - \ feature engineering, and modeling to predict the target. \nReport rmse on\ - \ the eval data. Do not plot or make any visualizations.\n" - smoker-status: - dataset: smoker-status - metric: f1 - target_col: smoking - user_requirement: "This is a smoker-status dataset. Your goal is to predict the\ - \ target column `smoking`.\nPerform data analysis, data preprocessing, feature\ - \ engineering, and modeling to predict the target. \nReport f1 on the eval data.\ + banking77: + dataset: banking77 + metric: f1 weighted + target_col: label + user_requirement: "This is a banking77 dataset. Your goal is to predict the target\ + \ column `label`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 weighted on the eval data.\ \ Do not plot or make any visualizations.\n" - software-defects: - dataset: software-defects - metric: f1 - target_col: defects - user_requirement: "This is a software-defects dataset. Your goal is to predict\ - \ the target column `defects`.\nPerform data analysis, data preprocessing, feature\ - \ engineering, and modeling to predict the target. \nReport f1 on the eval data.\ + gnad10: + dataset: gnad10 + metric: f1 weighted + target_col: label + user_requirement: "This is a gnad10 dataset. Your goal is to predict the target\ + \ column `label`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 weighted on the eval data.\ \ Do not plot or make any visualizations.\n" - + sms_spam: + dataset: sms_spam + metric: f1 + target_col: label + user_requirement: "This is a sms_spam dataset. Your goal is to predict the target\ + \ column `label`.\nPerform data analysis, data preprocessing, feature engineering,\ + \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ + \ or make any visualizations.\n" From 9f0427838324f90e4bc9ed62c6eba9e6a2ad4465 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 13 Sep 2024 11:18:15 +0800 Subject: [PATCH 058/160] comment --- expo/data/hf_data.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/expo/data/hf_data.py b/expo/data/hf_data.py index 6f1f55f6d..952ab5c73 100644 --- a/expo/data/hf_data.py +++ b/expo/data/hf_data.py @@ -9,22 +9,25 @@ from expo.insights.solution_designer import SolutionDesigner HFDATSETS = [ - # {"name": "sms_spam", "dataset_name": "ucirvine/sms_spam", "target_col": "label", "modality": "text"}, - # {"name": "banking77", "dataset_name": "PolyAI/banking77", "target_col": "label", "modality": "text"}, - # {"name": "gnad10", "dataset_name": "community-datasets/gnad10", "target_col": "label", "modality": "text"}, - { - "name": "oxford-iiit-pet", - "dataset_name": "timm/oxford-iiit-pet", - "target_col": "label_cat_dog", - "modality": "image", - }, - {"name": "stanford_cars", "dataset_name": "tanganke/stanford_cars", "target_col": "label", "modality": "image"}, - { - "name": "fashion_mnist", - "dataset_name": "zalando-datasets/fashion_mnist", - "target_col": "label", - "modality": "image", - }, + {"name": "sms_spam", "dataset_name": "ucirvine/sms_spam", "target_col": "label", "modality": "text"}, + {"name": "banking77", "dataset_name": "PolyAI/banking77", "target_col": "label", "modality": "text"}, + {"name": "gnad10", "dataset_name": "community-datasets/gnad10", "target_col": "label", "modality": "text"}, + # { + # "name": "oxford-iiit-pet", + # "dataset_name": "timm/oxford-iiit-pet", + # "target_col": "label_cat_dog", + # "modality": "image", + # }, + # { "name": "stanford_cars", + # "dataset_name": "tanganke/stanford_cars", + # "target_col": "label", + # "modality": "image"}, + # { + # "name": "fashion_mnist", + # "dataset_name": "zalando-datasets/fashion_mnist", + # "target_col": "label", + # "modality": "image", + # }, ] From c54e4121c611473ef7ef874d19cfb5891280d091 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 13 Sep 2024 16:57:37 +0800 Subject: [PATCH 059/160] update di prompt --- expo/data/dataset.py | 16 ++++++++++++---- expo/insights/instruction_generator.py | 8 +++++--- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 3b2017d1a..d2ec48326 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -16,15 +16,22 @@ Report {metric} on the eval data. Do not plot or make any visualizations. """ +RECOMMENDATION = """\ +## Base Models and Ensemble +You can consider using the following base models: +’GBM’ (LightGBM) ‘CAT’ (CatBoost) ‘XGB’ (XGBoost) ‘RF’ (random forest) ‘XT’ (extremely randomized trees) ‘KNN’ (k-nearest neighbors) ‘LR’ (linear regression) +""" -DI_INSTRUCTION = """\ -**Attention** +DI_INSTRUCTION = ( + RECOMMENDATION + + """**Attention** 1. Please do not leak the target label in any form during training. 2. Test set does not have the target column. 3. You should perform transformations on train, dev, and test sets at the same time (it's a good idea to define functions for this and avoid code repetition). -4. If labels are transformed during training, they should be transformed back to the original format before saving the predictions. +4. When scaling or transforming features, make sure the target column is not included. 5. You could utilize dev set to validate and improve model training. -6. Use techniques to avoid overfitting. +6. To avoid overfitting, train a weighted ensemble model such as StackingClassifier or StackingRegressor using **dev set** after base models being trained +7. Make sure the model prototyping is fast. ## Saving Dev and Test Predictions 1. Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. @@ -37,6 +44,7 @@ # Output dir {output_dir} """ +) TASK_PROMPT = """\ # User requirement diff --git a/expo/insights/instruction_generator.py b/expo/insights/instruction_generator.py index c9ff7ec6e..a800f4507 100644 --- a/expo/insights/instruction_generator.py +++ b/expo/insights/instruction_generator.py @@ -79,7 +79,7 @@ def load_analysis_pool(file_path, task_id=None): return data @staticmethod - async def generate_new_instructions(task_id, original_instruction, max_num, file_path): + async def generate_new_instructions(task_id, original_instruction, max_num, file_path, ext_info=None): data = InstructionGenerator.load_analysis_pool(file_path, task_id) new_instructions = [] if len(data) == 0: @@ -91,12 +91,14 @@ async def generate_new_instructions(task_id, original_instruction, max_num, file else: item = data[i] insights = item["Analysis"] - new_instruction = await InstructionGenerator.generate_new_instruction(original_instruction, insights) + new_instruction = await InstructionGenerator.generate_new_instruction( + original_instruction, insights, ext_info + ) new_instructions.append(new_instruction) return new_instructions @staticmethod - async def generate_new_instruction(original_instruction, insights): + async def generate_new_instruction(original_instruction, insights, ext_info): prompt = CHANGE_INSTRUCTION.format(instruction=original_instruction, insights=insights) llm = LLM() context = llm.format_msg([Message(content=prompt, role="user")]) From a6b066a127f7d2fcfa8e6b04dc4bfaed5b20c50a Mon Sep 17 00:00:00 2001 From: limafang Date: Fri, 13 Sep 2024 17:52:22 +0800 Subject: [PATCH 060/160] support image dataset --- expo/data/hf_data.py | 72 ++++++++++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 19 deletions(-) diff --git a/expo/data/hf_data.py b/expo/data/hf_data.py index 952ab5c73..45ff6330b 100644 --- a/expo/data/hf_data.py +++ b/expo/data/hf_data.py @@ -1,7 +1,9 @@ import asyncio import os from pathlib import Path - +import numpy as np +from PIL import Image +import io import pandas as pd from datasets import load_dataset @@ -9,22 +11,25 @@ from expo.insights.solution_designer import SolutionDesigner HFDATSETS = [ - {"name": "sms_spam", "dataset_name": "ucirvine/sms_spam", "target_col": "label", "modality": "text"}, - {"name": "banking77", "dataset_name": "PolyAI/banking77", "target_col": "label", "modality": "text"}, - {"name": "gnad10", "dataset_name": "community-datasets/gnad10", "target_col": "label", "modality": "text"}, - # { - # "name": "oxford-iiit-pet", - # "dataset_name": "timm/oxford-iiit-pet", - # "target_col": "label_cat_dog", - # "modality": "image", - # }, + # {"name": "sms_spam", "dataset_name": "ucirvine/sms_spam", "target_col": "label", "modality": "text"}, + # {"name": "banking77", "dataset_name": "PolyAI/banking77", "target_col": "label", "modality": "text"}, + # {"name": "gnad10", "dataset_name": "community-datasets/gnad10", "target_col": "label", "modality": "text"}, + { + "name": "oxford-iiit-pet", + "dataset_name": "timm/oxford-iiit-pet", + "image_col": "image", + "target_col": "label_cat_dog", + "modality": "image", + }, # { "name": "stanford_cars", # "dataset_name": "tanganke/stanford_cars", + # "image_col": "image", # "target_col": "label", # "modality": "image"}, # { # "name": "fashion_mnist", # "dataset_name": "zalando-datasets/fashion_mnist", + # "image_col": "image", # "target_col": "label", # "modality": "image", # }, @@ -42,16 +47,22 @@ def __init__(self, name, dataset_dir, dataset_name, **kwargs): self.dataset_name = dataset_name self.modality = kwargs.get("modality", "") self.target_col = kwargs.get("target_col", "label") + self.image_col = kwargs.get("image_col", "image") self.dataset = load_dataset(self.dataset_name, trust_remote_code=True) super().__init__(self.name, dataset_dir, **kwargs) def get_raw_dataset(self): raw_dir = Path(self.dataset_dir, self.name, "raw") raw_dir.mkdir(parents=True, exist_ok=True) + if os.path.exists(Path(raw_dir, "train.csv")): df = pd.read_csv(Path(raw_dir, "train.csv"), encoding="utf-8") else: df = self.dataset["train"].to_pandas() + + if self.modality == "image": + df = self.save_images_and_update_df(df, raw_dir, "train") + df.to_csv(Path(raw_dir, "train.csv"), index=False, encoding="utf-8") if os.path.exists(Path(raw_dir, "test.csv")): @@ -59,19 +70,37 @@ def get_raw_dataset(self): else: if self.dataset and "test" in self.dataset: test_df = self.dataset["test"].to_pandas() + + if self.modality == "image": + test_df = self.save_images_and_update_df(test_df, raw_dir, "test") + test_df.to_csv(Path(raw_dir, "test.csv"), index=False, encoding="utf-8") else: test_df = None + return df, test_df + def save_images_and_update_df(self, df, raw_dir, split): + image_dir = Path(raw_dir, f"{split}_images") + image_dir.mkdir(parents=True, exist_ok=True) + + def process_image(idx, row): + image_bytes = row[self.image_col]["bytes"] + image = Image.open(io.BytesIO(image_bytes)) + if image.mode == "RGBA": + image = image.convert("RGB") + img_path = Path(image_dir, f"{idx}.jpg") + image.save(img_path) + return str(img_path) + + df["image"] = df.apply(lambda row: process_image(row.name, row), axis=1) + return df + def get_df_head(self, raw_df): - if self.modality == "text": - examples = [] - for i in range(5): - examples.append(raw_df.iloc[i].to_dict()) - return examples - elif self.modality == "image": - return "" + examples = [] + for i in range(5): + examples.append(raw_df.iloc[i].to_dict()) + return examples def get_dataset_info(self): dataset_info = super().get_dataset_info() @@ -82,7 +111,7 @@ def get_dataset_info(self): if __name__ == "__main__": dataset_dir = "D:/work/automl/datasets" - save_analysis_pool = False + save_analysis_pool = True force_update = False datasets_dict = {"datasets": {}} solution_designer = SolutionDesigner() @@ -92,8 +121,13 @@ def get_dataset_info(self): dataset_dir, dataset_meta["dataset_name"], target_col=dataset_meta["target_col"], + image_col=dataset_meta["image_col"], force_update=force_update, modality=dataset_meta["modality"], ) - asyncio.run(process_dataset(hf_dataset, solution_designer, save_analysis_pool, datasets_dict)) + asyncio.run( + process_dataset( + hf_dataset, solution_designer, save_analysis_pool, datasets_dict + ) + ) save_datasets_dict_to_yaml(datasets_dict, "hf_datasets.yaml") From cfa21ba27e6748229ff61636549c444e25b21904 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 13 Sep 2024 19:04:48 +0800 Subject: [PATCH 061/160] change img path from abs to rel --- expo/data/hf_data.py | 56 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/expo/data/hf_data.py b/expo/data/hf_data.py index 45ff6330b..6f615c8cb 100644 --- a/expo/data/hf_data.py +++ b/expo/data/hf_data.py @@ -1,19 +1,19 @@ import asyncio +import io import os from pathlib import Path -import numpy as np -from PIL import Image -import io + import pandas as pd from datasets import load_dataset +from PIL import Image from expo.data.dataset import ExpDataset, process_dataset, save_datasets_dict_to_yaml from expo.insights.solution_designer import SolutionDesigner HFDATSETS = [ - # {"name": "sms_spam", "dataset_name": "ucirvine/sms_spam", "target_col": "label", "modality": "text"}, - # {"name": "banking77", "dataset_name": "PolyAI/banking77", "target_col": "label", "modality": "text"}, - # {"name": "gnad10", "dataset_name": "community-datasets/gnad10", "target_col": "label", "modality": "text"}, + {"name": "sms_spam", "dataset_name": "ucirvine/sms_spam", "target_col": "label", "modality": "text"}, + {"name": "banking77", "dataset_name": "PolyAI/banking77", "target_col": "label", "modality": "text"}, + {"name": "gnad10", "dataset_name": "community-datasets/gnad10", "target_col": "label", "modality": "text"}, { "name": "oxford-iiit-pet", "dataset_name": "timm/oxford-iiit-pet", @@ -21,18 +21,20 @@ "target_col": "label_cat_dog", "modality": "image", }, - # { "name": "stanford_cars", - # "dataset_name": "tanganke/stanford_cars", - # "image_col": "image", - # "target_col": "label", - # "modality": "image"}, - # { - # "name": "fashion_mnist", - # "dataset_name": "zalando-datasets/fashion_mnist", - # "image_col": "image", - # "target_col": "label", - # "modality": "image", - # }, + { + "name": "stanford_cars", + "dataset_name": "tanganke/stanford_cars", + "image_col": "image", + "target_col": "label", + "modality": "image", + }, + { + "name": "fashion_mnist", + "dataset_name": "zalando-datasets/fashion_mnist", + "image_col": "image", + "target_col": "label", + "modality": "image", + }, ] @@ -81,17 +83,19 @@ def get_raw_dataset(self): return df, test_df def save_images_and_update_df(self, df, raw_dir, split): - image_dir = Path(raw_dir, f"{split}_images") - image_dir.mkdir(parents=True, exist_ok=True) + abs_image_dir = Path(raw_dir, f"{split}_images") + rel_image_dir = f"raw/{split}_images" + abs_image_dir.mkdir(parents=True, exist_ok=True) def process_image(idx, row): image_bytes = row[self.image_col]["bytes"] image = Image.open(io.BytesIO(image_bytes)) if image.mode == "RGBA": image = image.convert("RGB") - img_path = Path(image_dir, f"{idx}.jpg") + img_path = Path(abs_image_dir, f"{idx}.jpg") + rel_img_path = f"{rel_image_dir}/{idx}.jpg" image.save(img_path) - return str(img_path) + return rel_img_path df["image"] = df.apply(lambda row: process_image(row.name, row), axis=1) return df @@ -112,7 +116,7 @@ def get_dataset_info(self): if __name__ == "__main__": dataset_dir = "D:/work/automl/datasets" save_analysis_pool = True - force_update = False + force_update = True datasets_dict = {"datasets": {}} solution_designer = SolutionDesigner() for dataset_meta in HFDATSETS: @@ -125,9 +129,5 @@ def get_dataset_info(self): force_update=force_update, modality=dataset_meta["modality"], ) - asyncio.run( - process_dataset( - hf_dataset, solution_designer, save_analysis_pool, datasets_dict - ) - ) + asyncio.run(process_dataset(hf_dataset, solution_designer, save_analysis_pool, datasets_dict)) save_datasets_dict_to_yaml(datasets_dict, "hf_datasets.yaml") From 7d8cb9afec8017fa1006df377028f9648c40d3b5 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 13 Sep 2024 19:10:45 +0800 Subject: [PATCH 062/160] add image datasets config --- expo/data/hf_data.py | 6 +++--- expo/datasets.yaml | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/expo/data/hf_data.py b/expo/data/hf_data.py index 6f615c8cb..df3a6ed20 100644 --- a/expo/data/hf_data.py +++ b/expo/data/hf_data.py @@ -115,8 +115,8 @@ def get_dataset_info(self): if __name__ == "__main__": dataset_dir = "D:/work/automl/datasets" - save_analysis_pool = True - force_update = True + save_analysis_pool = False + force_update = False datasets_dict = {"datasets": {}} solution_designer = SolutionDesigner() for dataset_meta in HFDATSETS: @@ -125,7 +125,7 @@ def get_dataset_info(self): dataset_dir, dataset_meta["dataset_name"], target_col=dataset_meta["target_col"], - image_col=dataset_meta["image_col"], + image_col=dataset_meta.get("image_col", ""), force_update=force_update, modality=dataset_meta["modality"], ) diff --git a/expo/datasets.yaml b/expo/datasets.yaml index 051e8232d..92e004c6d 100644 --- a/expo/datasets.yaml +++ b/expo/datasets.yaml @@ -183,6 +183,14 @@ datasets: \ column `label`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 weighted on the eval data.\ \ Do not plot or make any visualizations.\n" + fashion_mnist: + dataset: fashion_mnist + metric: f1 weighted + target_col: label + user_requirement: "This is a fashion_mnist dataset. Your goal is to predict the\ + \ target column `label`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ + \ eval data. Do not plot or make any visualizations.\n" gnad10: dataset: gnad10 metric: f1 weighted @@ -191,6 +199,14 @@ datasets: \ column `label`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 weighted on the eval data.\ \ Do not plot or make any visualizations.\n" + oxford-iiit-pet: + dataset: oxford-iiit-pet + metric: f1 + target_col: label_cat_dog + user_requirement: "This is a oxford-iiit-pet dataset. Your goal is to predict\ + \ the target column `label_cat_dog`.\nPerform data analysis, data preprocessing,\ + \ feature engineering, and modeling to predict the target. \nReport f1 on the\ + \ eval data. Do not plot or make any visualizations.\n" sms_spam: dataset: sms_spam metric: f1 @@ -199,3 +215,11 @@ datasets: \ column `label`.\nPerform data analysis, data preprocessing, feature engineering,\ \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ \ or make any visualizations.\n" + stanford_cars: + dataset: stanford_cars + metric: f1 weighted + target_col: label + user_requirement: "This is a stanford_cars dataset. Your goal is to predict the\ + \ target column `label`.\nPerform data analysis, data preprocessing, feature\ + \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ + \ eval data. Do not plot or make any visualizations.\n" From b49334b16c4c61bff89780c522c6f60f25d89329 Mon Sep 17 00:00:00 2001 From: limafang Date: Fri, 13 Sep 2024 21:07:10 +0800 Subject: [PATCH 063/160] fix import and update readme --- expo/README.md | 18 +++++++++++++++++- expo/experimenter/autosklearn.py | 32 ++++++++++++-------------------- 2 files changed, 29 insertions(+), 21 deletions(-) diff --git a/expo/README.md b/expo/README.md index e824312f2..707e6415e 100644 --- a/expo/README.md +++ b/expo/README.md @@ -183,10 +183,26 @@ pip install autogluon 提供github链接,并说明使用的命令以及参数设置 ### AutoSklearn +#### System requirements +auto-sklearn has the following system requirements: + +- Linux operating system (for example Ubuntu) + +- Python (>=3.7) + +- C++ compiler (with C++11 supports) + +In case you try to install Auto-sklearn on a system where no wheel files for the pyrfr package are provided (see here for available wheels) you also need: + +- SWIG [(get SWIG here).](https://www.swig.org/survey.html) + +For an explanation of missing Microsoft Windows and macOS support please check the Section [Windows/macOS compatibility](https://automl.github.io/auto-sklearn/master/installation.html#windows-macos-compatibility). + #### Setup ``` -pip install autosklearn +pip install auto-sklearn ``` + #### Run ``` python run_experiment.py --exp_mode autosklearn --task titanic diff --git a/expo/experimenter/autosklearn.py b/expo/experimenter/autosklearn.py index 5786a3790..7340edafa 100644 --- a/expo/experimenter/autosklearn.py +++ b/expo/experimenter/autosklearn.py @@ -1,10 +1,7 @@ from datetime import datetime -import autosklearn.classification -import autosklearn.regression import pandas as pd from expo.experimenter.custom import CustomExperimenter from expo.evaluation.evaluation import evaluate_score -from autosklearn.metrics import make_scorer from functools import partial @@ -24,6 +21,14 @@ class ASRunner: def __init__(self, state=None): self.state = state self.datasets = self.state["datasets_dir"] + try: + import autosklearn.classification + import autosklearn.regression + from autosklearn.metrics import make_scorer + except ImportError: + raise ImportError( + "autosklearn not found or system not supported, please check it first" + ) def run(self): train_path = self.datasets["train"] @@ -34,7 +39,7 @@ def run(self): train_data = pd.read_csv(train_path) dev_data = pd.read_csv(dev_wo_target_path) test_data = pd.read_csv(test_wo_target_path) - eval_metric = self.state["dataset_config"]["metric"].replace(" ", "_") + eval_metric = self.state["dataset_config"]["metric"] X_train = train_data.drop(columns=[target_col]) y_train = train_data[target_col] @@ -42,31 +47,18 @@ def run(self): automl = autosklearn.regression.AutoSklearnRegressor( time_left_for_this_task=self.time_limit, per_run_time_limit=60, - metric=create_autosklearn_scorer("rmse"), # 使用新的函数创建评分器 - memory_limit=8192, - tmp_folder="AutosklearnModels/as-{}-{}".format( - self.state["task"], datetime.now().strftime("%y%m%d_%H%M") - ), - n_jobs=-1, - ) - elif eval_metric == "f1": - automl = autosklearn.classification.AutoSklearnClassifier( - time_left_for_this_task=self.time_limit, - per_run_time_limit=60, - metric=create_autosklearn_scorer("f1"), # 使用新的函数创建评分器 + metric=create_autosklearn_scorer(eval_metric), memory_limit=8192, tmp_folder="AutosklearnModels/as-{}-{}".format( self.state["task"], datetime.now().strftime("%y%m%d_%H%M") ), n_jobs=-1, ) - elif eval_metric == "f1_weighted": + elif eval_metric in ["f1", "f1 weighted"]: automl = autosklearn.classification.AutoSklearnClassifier( time_left_for_this_task=self.time_limit, per_run_time_limit=60, - metric=create_autosklearn_scorer( - "f1 weighted" - ), # 使用新的函数创建评分器 + metric=create_autosklearn_scorer(eval_metric), memory_limit=8192, tmp_folder="AutosklearnModels/as-{}-{}".format( self.state["task"], datetime.now().strftime("%y%m%d_%H%M") From 5d2de4d0ec008b3e5737ad8b2ee531a9dc88e1c3 Mon Sep 17 00:00:00 2001 From: duiyipan Date: Sat, 14 Sep 2024 11:37:08 +0800 Subject: [PATCH 064/160] add random seed --- expo/experimenter/autosklearn.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/expo/experimenter/autosklearn.py b/expo/experimenter/autosklearn.py index 7340edafa..22aa2a132 100644 --- a/expo/experimenter/autosklearn.py +++ b/expo/experimenter/autosklearn.py @@ -17,6 +17,7 @@ def create_autosklearn_scorer(metric_name): class ASRunner: time_limit = 300 + seed = 42 def __init__(self, state=None): self.state = state @@ -49,6 +50,7 @@ def run(self): per_run_time_limit=60, metric=create_autosklearn_scorer(eval_metric), memory_limit=8192, + seed=self.seed, tmp_folder="AutosklearnModels/as-{}-{}".format( self.state["task"], datetime.now().strftime("%y%m%d_%H%M") ), @@ -60,6 +62,7 @@ def run(self): per_run_time_limit=60, metric=create_autosklearn_scorer(eval_metric), memory_limit=8192, + seed=self.seed, tmp_folder="AutosklearnModels/as-{}-{}".format( self.state["task"], datetime.now().strftime("%y%m%d_%H%M") ), From b32b28eb125136ca404200ff7e20831442d36f90 Mon Sep 17 00:00:00 2001 From: Bangbang Date: Sat, 14 Sep 2024 12:13:55 +0800 Subject: [PATCH 065/160] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=A2=84=E6=B5=8B?= =?UTF-8?q?=E7=BB=93=E6=9E=9C=E6=B2=A1=E6=9C=89target=E5=88=97=E5=90=8D?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expo/experimenter/experimenter.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index b1b5a93c0..418e0089a 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -85,7 +85,8 @@ def evaluate_prediction(self, split, state): os.makedirs(state["node_dir"], exist_ok=True) pred_node_path = os.path.join(state["node_dir"], f"{self.start_time}-{split}_predictions.csv") gt_path = os.path.join(state["datasets_dir"][f"{split}_target"]) - preds = pd.read_csv(pred_path)["target"] + preds = pd.read_csv(pred_path) + preds = preds[preds.columns.tolist()[0]] preds.to_csv(pred_node_path, index=False) gt = pd.read_csv(gt_path)["target"] metric = state["dataset_config"]["metric"] From c4fe056bcaa2d06abec4c2328c01994d09fce031 Mon Sep 17 00:00:00 2001 From: duiyipan Date: Sat, 14 Sep 2024 14:58:22 +0800 Subject: [PATCH 066/160] fix import error delete seed --- expo/experimenter/autosklearn.py | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/expo/experimenter/autosklearn.py b/expo/experimenter/autosklearn.py index 22aa2a132..602b8385a 100644 --- a/expo/experimenter/autosklearn.py +++ b/expo/experimenter/autosklearn.py @@ -9,15 +9,8 @@ def custom_scorer(y_true, y_pred, metric_name): return evaluate_score(y_pred, y_true, metric_name) -def create_autosklearn_scorer(metric_name): - return make_scorer( - name=metric_name, score_func=partial(custom_scorer, metric_name=metric_name) - ) - - class ASRunner: time_limit = 300 - seed = 42 def __init__(self, state=None): self.state = state @@ -25,12 +18,19 @@ def __init__(self, state=None): try: import autosklearn.classification import autosklearn.regression - from autosklearn.metrics import make_scorer + import autosklearn.metrics + + self.autosklearn = autosklearn except ImportError: raise ImportError( "autosklearn not found or system not supported, please check it first" ) + def create_autosklearn_scorer(self, metric_name): + return self.autosklearn.metrics.make_scorer( + name=metric_name, score_func=partial(custom_scorer, metric_name=metric_name) + ) + def run(self): train_path = self.datasets["train"] dev_wo_target_path = self.datasets["dev_wo_target"] @@ -45,24 +45,22 @@ def run(self): y_train = train_data[target_col] if eval_metric == "rmse": - automl = autosklearn.regression.AutoSklearnRegressor( + automl = self.autosklearn.regression.AutoSklearnRegressor( time_left_for_this_task=self.time_limit, per_run_time_limit=60, - metric=create_autosklearn_scorer(eval_metric), + metric=self.create_autosklearn_scorer(eval_metric), memory_limit=8192, - seed=self.seed, tmp_folder="AutosklearnModels/as-{}-{}".format( self.state["task"], datetime.now().strftime("%y%m%d_%H%M") ), n_jobs=-1, ) elif eval_metric in ["f1", "f1 weighted"]: - automl = autosklearn.classification.AutoSklearnClassifier( + automl = self.autosklearn.classification.AutoSklearnClassifier( time_left_for_this_task=self.time_limit, per_run_time_limit=60, - metric=create_autosklearn_scorer(eval_metric), + metric=self.create_autosklearn_scorer(eval_metric), memory_limit=8192, - seed=self.seed, tmp_folder="AutosklearnModels/as-{}-{}".format( self.state["task"], datetime.now().strftime("%y%m%d_%H%M") ), From 8beca0faddd33b981d23d875c1a59df0b71947f0 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 15:17:42 +0800 Subject: [PATCH 067/160] 1. add special instruction 2. add fixed insights --- expo/MCTS.py | 24 ++++++++------- expo/README.md | 24 ++++++++++----- expo/data/dataset.py | 42 ++++++++++++++++++-------- expo/experimenter/aug.py | 4 ++- expo/experimenter/custom.py | 7 ++++- expo/experimenter/experimenter.py | 3 +- expo/experimenter/mcts.py | 12 +++----- expo/insights/fixed_insights.json | 22 ++++++++++++++ expo/insights/instruction_generator.py | 15 +++++++-- expo/requirements.txt | 1 + expo/run_experiment.py | 4 ++- 11 files changed, 111 insertions(+), 47 deletions(-) create mode 100644 expo/insights/fixed_insights.json diff --git a/expo/MCTS.py b/expo/MCTS.py index 360baac8d..265356f65 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -15,18 +15,18 @@ from metagpt.utils.common import read_json_file -def initialize_di_root_node(task, data_config, low_is_better=False, reflection=True, name=""): +def initialize_di_root_node(state, reflection: bool = True): start_task_id = 2 - state = create_initial_state( - task, start_task_id=start_task_id, data_config=data_config, low_is_better=low_is_better, name=name - ) + # state = create_initial_state( + # task, start_task_id=start_task_id, data_config=data_config, low_is_better=low_is_better, name=name + # ) role = ResearchAssistant( node_id="0", start_task_id=start_task_id, use_reflection=reflection, role_dir=state["node_dir"] ) return role, Node(parent=None, state=state, action=None, value=0) -def create_initial_state(task, start_task_id, data_config, low_is_better, name): +def create_initial_state(task, start_task_id, data_config, low_is_better: bool, name: str, special_instruction: str): initial_state = { "task": task, "work_dir": data_config["work_dir"], @@ -34,7 +34,9 @@ def create_initial_state(task, start_task_id, data_config, low_is_better, name): "dataset_config": data_config["datasets"][task], "datasets_dir": get_split_dataset_path(task, data_config), "exp_pool_path": get_exp_pool_path(task, data_config, pool_name="ds_analysis_pool"), - "requirement": generate_task_requirement(task, data_config), + "requirement": generate_task_requirement( + task, data_config, is_di=True, special_instruction=special_instruction + ), "has_run": False, "start_task_id": start_task_id, "low_is_better": low_is_better, @@ -157,6 +159,7 @@ async def expand(self, max_children): original_instruction=original_instruction, max_num=max_children, file_path=self.state["exp_pool_path"], + use_fixed_insights=self.use_fixed_insights, ) new_state = self.state.copy() new_state["start_task_id"] += 1 @@ -234,9 +237,10 @@ class MCTS: c_explore: float = 1.4 c_unvisited: float = 0.8 - def __init__(self, root_node, max_depth): + def __init__(self, root_node, max_depth, use_fixed_insights): self.root_node = root_node self.max_depth = max_depth + self.use_fixed_insights = use_fixed_insights def select(self, node: Node): node = self.best_child() @@ -303,10 +307,8 @@ def bfs(node: Node, best_score, best_child: Node, split): def get_num_simulations(self): return self.root_node.visited - async def search(self, task, data_config, name, rollouts, load_tree=False, low_is_better=False, reflection=False): - role, root = initialize_di_root_node( - task, data_config, low_is_better=low_is_better, reflection=reflection, name=name - ) + async def search(self, state, rollouts, load_tree=False, reflection=False): + role, root = initialize_di_root_node(state, reflection=reflection) self.root_node = root tree_loaded = False if load_tree: diff --git a/expo/README.md b/expo/README.md index 55ea7eed4..00d1cae50 100644 --- a/expo/README.md +++ b/expo/README.md @@ -187,16 +187,10 @@ pip install autogluon For setup, check 5. - `python run_experiment.py --exp_mode base --task titanic --num_experiments 10` +- Ask DI to use AutoGluon: `--special_instruction ag` +- Ask DI to use the stacking ensemble method: `--special_instruction stacking` -### DI RandomSearch -For setup, check 5. - -- Single insight -`python run_experiment.py --exp_mode aug --task titanic --aug_mode single` - -- Set insight -`python run_experiment.py --exp_mode aug --task titanic --aug_mode set` ## 5. DI MCTS @@ -223,6 +217,20 @@ If the dataset has reg metric, remember to use `--low_is_better`: - `python run_experiment.py --exp_mode mcts --task househouse_prices --rollout 10 --low_is_better` +In addition to the generated insights, include the fixed insights saved in `insights/fixed_insights.json` +- `--use_fixed_insights` + + + +#### Ablation Study + +**DI RandomSearch** + +- Single insight +`python run_experiment.py --exp_mode aug --task titanic --aug_mode single` + +- Set insight +`python run_experiment.py --exp_mode aug --task titanic --aug_mode set` diff --git a/expo/data/dataset.py b/expo/data/dataset.py index d2ec48326..03b80985a 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -10,16 +10,27 @@ from expo.insights.solution_designer import SolutionDesigner -BASE_USER_REQUIREMENT = """\ +BASE_USER_REQUIREMENT = """ This is a {datasetname} dataset. Your goal is to predict the target column `{target_col}`. Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. Report {metric} on the eval data. Do not plot or make any visualizations. """ -RECOMMENDATION = """\ +USE_AG = """ +7. Please use autogluon for model training with presets='medium_quality', time_limit=None, give dev dataset to tuning_data, and use right eval_metric. +""" + +STACKING = """ +7. To avoid overfitting, train a weighted ensemble model such as StackingClassifier or StackingRegressor. +8. You could do some quick model prototyping to see which models work best and then use them in the ensemble. +""" + +SPECIAL_INSTRUCTIONS = {"ag": USE_AG, "stacking": STACKING} + +RECOMMENDATION = """ ## Base Models and Ensemble You can consider using the following base models: -’GBM’ (LightGBM) ‘CAT’ (CatBoost) ‘XGB’ (XGBoost) ‘RF’ (random forest) ‘XT’ (extremely randomized trees) ‘KNN’ (k-nearest neighbors) ‘LR’ (linear regression) +`GBM` (LightGBM) `CAT` (CatBoost) `XGB` (XGBoost) `RF` (random forest) `XT` (extremely randomized trees) `KNN` (k-nearest neighbors) ‘LR’ (linear regression) """ DI_INSTRUCTION = ( @@ -27,11 +38,10 @@ + """**Attention** 1. Please do not leak the target label in any form during training. 2. Test set does not have the target column. -3. You should perform transformations on train, dev, and test sets at the same time (it's a good idea to define functions for this and avoid code repetition). -4. When scaling or transforming features, make sure the target column is not included. -5. You could utilize dev set to validate and improve model training. -6. To avoid overfitting, train a weighted ensemble model such as StackingClassifier or StackingRegressor using **dev set** after base models being trained -7. Make sure the model prototyping is fast. +3. When conducting data exploration or analysis, print out the results of your findings. +4. You should perform transformations on train, dev, and test sets at the same time (it's a good idea to define functions for this and avoid code repetition). +5. When scaling or transforming features, make sure the target column is not included. +6. You could utilize dev set to validate and improve model training. {special_instruction} ## Saving Dev and Test Predictions 1. Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. @@ -46,7 +56,7 @@ """ ) -TASK_PROMPT = """\ +TASK_PROMPT = """ # User requirement {user_requirement} {additional_instruction} @@ -142,12 +152,18 @@ def create_dataset_dict(dataset): return dataset_dict -def generate_di_instruction(output_dir): - additional_instruction = DI_INSTRUCTION.format(output_dir=output_dir) +def generate_di_instruction(output_dir, special_instruction): + if special_instruction: + special_instruction_prompt = SPECIAL_INSTRUCTIONS[special_instruction] + else: + special_instruction_prompt = "" + additional_instruction = DI_INSTRUCTION.format( + output_dir=output_dir, special_instruction=special_instruction_prompt + ) return additional_instruction -def generate_task_requirement(task_name, data_config, is_di=True): +def generate_task_requirement(task_name, data_config, is_di=True, special_instruction=None): user_requirement = get_user_requirement(task_name, data_config) split_dataset_path = get_split_dataset_path(task_name, data_config) train_path = split_dataset_path["train"] @@ -158,7 +174,7 @@ def generate_task_requirement(task_name, data_config, is_di=True): datasets_dir = data_config["datasets_dir"] data_info_path = f"{datasets_dir}/{task_name}/dataset_info.json" if is_di: - additional_instruction = generate_di_instruction(output_dir) + additional_instruction = generate_di_instruction(output_dir, special_instruction) else: additional_instruction = "" user_requirement = TASK_PROMPT.format( diff --git a/expo/experimenter/aug.py b/expo/experimenter/aug.py index 8312f57fc..e57d024bd 100644 --- a/expo/experimenter/aug.py +++ b/expo/experimenter/aug.py @@ -17,7 +17,9 @@ async def run_experiment(self): # state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") user_requirement = self.state["requirement"] exp_pool_path = get_exp_pool_path(self.args.task, self.data_config, pool_name="ds_analysis_pool") - exp_pool = InstructionGenerator.load_analysis_pool(exp_pool_path) + exp_pool = InstructionGenerator.load_analysis_pool( + exp_pool_path, use_fixed_insights=self.args.use_fixed_insights + ) if self.args.aug_mode == "single": exps = InstructionGenerator._random_sample(exp_pool, self.args.num_experiments) exps = [exp["Analysis"] for exp in exps] diff --git a/expo/experimenter/custom.py b/expo/experimenter/custom.py index df090fb58..92b7dafa2 100644 --- a/expo/experimenter/custom.py +++ b/expo/experimenter/custom.py @@ -18,7 +18,12 @@ def __init__(self, args, **kwargs): self.name = kwargs.get("name", "") self.result_path = f"results/custom_{self.name}" self.state = create_initial_state( - self.task, start_task_id=1, data_config=self.data_config, low_is_better=self.low_is_better, name=self.name + self.task, + start_task_id=1, + data_config=self.data_config, + low_is_better=self.low_is_better, + name=self.name, + special_instruction=self.args.special_instruction, ) def run_experiment(self): diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 418e0089a..89d589d7d 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -23,7 +23,8 @@ def __init__(self, args, **kwargs): start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, - name="", + name=self.args.name, + special_instruction=self.args.special_instruction, ) async def run_di(self, di, user_requirement, run_idx): diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index fbe2f35f1..e06169a70 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -13,19 +13,15 @@ def __init__(self, args, tree_mode=None, **kwargs): async def run_experiment(self): if self.tree_mode == "greedy": - mcts = Greedy(root_node=None, max_depth=5) + mcts = Greedy(root_node=None, max_depth=5, use_fixed_insights=self.args.use_fixed_insights) elif self.tree_mode == "random": - mcts = Random(root_node=None, max_depth=5) + mcts = Random(root_node=None, max_depth=5, use_fixed_insights=self.args.use_fixed_insights) else: - mcts = MCTS(root_node=None, max_depth=5) + mcts = MCTS(root_node=None, max_depth=5, use_fixed_insights=self.args.use_fixed_insights) best_nodes = await mcts.search( - self.args.task, - self.data_config, - low_is_better=self.args.low_is_better, - load_tree=self.args.load_tree, + state=self.state, reflection=self.args.reflection, rollouts=self.args.rollouts, - name=self.args.name, ) best_node = best_nodes["global_best"] dev_best_node = best_nodes["dev_best"] diff --git a/expo/insights/fixed_insights.json b/expo/insights/fixed_insights.json new file mode 100644 index 000000000..e52745707 --- /dev/null +++ b/expo/insights/fixed_insights.json @@ -0,0 +1,22 @@ +[ +{ + "Analysis": "Use early stopping, hyperparameter tuning, and cross-validation to avoid overfitting and improve robustness of the model.", + "Category": "Model Training", + "task_id": 4 +}, +{ + "Analysis": "use k-fold bagging and early stopping", + "Category": "Model Training", + "task_id": 4 +}, +{ + "Analysis": "To avoid overfitting, train a weighted ensemble model such as StackingClassifier or StackingRegressor using **dev set** after base models being trained.", + "Category": "Model Training", + "task_id": 4 +}, +{ + "Analysis": "Please use autogluon for model training with presets='medium_quality', time_limit=None, give dev dataset to tuning_data, and use right eval_metric.", + "Category": "Model Training", + "task_id": 4 +} +] \ No newline at end of file diff --git a/expo/insights/instruction_generator.py b/expo/insights/instruction_generator.py index a800f4507..07e5fb655 100644 --- a/expo/insights/instruction_generator.py +++ b/expo/insights/instruction_generator.py @@ -1,4 +1,5 @@ import json +import os import random from expo.utils import clean_json_from_rsp, load_data_config, mcts_logger @@ -68,8 +69,12 @@ def format_output(rsp): return new_data @staticmethod - def load_analysis_pool(file_path, task_id=None): + def load_analysis_pool(file_path, use_fixed_insights, task_id=None): data = InstructionGenerator.load_json_data(file_path) + if use_fixed_insights: + current_directory = os.path.dirname(__file__) + fixed_insights = InstructionGenerator.load_json_data(f"{current_directory}/fixed_insights.json") + data.extend(fixed_insights) for item in data: if "task_id" not in item: raise ValueError("task_id is not found in the analysis pool") @@ -79,8 +84,12 @@ def load_analysis_pool(file_path, task_id=None): return data @staticmethod - async def generate_new_instructions(task_id, original_instruction, max_num, file_path, ext_info=None): - data = InstructionGenerator.load_analysis_pool(file_path, task_id) + async def generate_new_instructions( + task_id, original_instruction, max_num, file_path, ext_info=None, use_fixed_insights=False + ): + data = InstructionGenerator.load_analysis_pool( + file_path, task_id=task_id, use_fixed_insights=use_fixed_insights + ) new_instructions = [] if len(data) == 0: mcts_logger.log("MCTS", f"No insights available for task {task_id}") diff --git a/expo/requirements.txt b/expo/requirements.txt index 04de1a8bb..e85818bbe 100644 --- a/expo/requirements.txt +++ b/expo/requirements.txt @@ -3,3 +3,4 @@ openml==0.14.2 # ml module to run in DI xgboost catboost +lightgbm diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 2123fade3..f1b5b2d80 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -28,11 +28,11 @@ def get_mcts_args(parser): parser.add_argument("--no_load_tree", dest="load_tree", action="store_false") parser.set_defaults(load_tree=False) parser.add_argument("--rollouts", type=int, default=5) + parser.add_argument("--use_fixed_insights", dest="use_fixed_insights", action="store_true") def get_aug_exp_args(parser): parser.add_argument("--aug_mode", type=str, default="single", choices=["single", "set"]) - parser.add_argument("--num_experiments", type=int, default=1) def get_di_args(parser): @@ -41,6 +41,8 @@ def get_di_args(parser): parser.set_defaults(low_is_better=False) parser.add_argument("--reflection", dest="reflection", action="store_true") parser.add_argument("--no_reflection", dest="reflection", action="store_false") + parser.add_argument("--num_experiments", type=int, default=1) + parser.add_argument("--special_instruction", type=str, default=None, choices=["ag", "stacking"]) parser.set_defaults(reflection=True) From 9089ecf7d6f031d069623381006b14615788ecde Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 15:21:21 +0800 Subject: [PATCH 068/160] update readme --- expo/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/README.md b/expo/README.md index 00d1cae50..0af20388e 100644 --- a/expo/README.md +++ b/expo/README.md @@ -217,7 +217,7 @@ If the dataset has reg metric, remember to use `--low_is_better`: - `python run_experiment.py --exp_mode mcts --task househouse_prices --rollout 10 --low_is_better` -In addition to the generated insights, include the fixed insights saved in `insights/fixed_insights.json` +In addition to the generated insights, include the fixed insights saved in `expo/insights/fixed_insights.json` - `--use_fixed_insights` From ed6ce14838861dec0385e5cdfb131ab3664948b9 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 15:24:17 +0800 Subject: [PATCH 069/160] update fixed insights --- expo/insights/fixed_insights.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/insights/fixed_insights.json b/expo/insights/fixed_insights.json index e52745707..4f42b9db1 100644 --- a/expo/insights/fixed_insights.json +++ b/expo/insights/fixed_insights.json @@ -10,7 +10,7 @@ "task_id": 4 }, { - "Analysis": "To avoid overfitting, train a weighted ensemble model such as StackingClassifier or StackingRegressor using **dev set** after base models being trained.", + "Analysis": "To avoid overfitting, train a weighted ensemble model such as StackingClassifier or StackingRegressor; You could do some quick model prototyping to see which models work best and then use them in the ensemble.", "Category": "Model Training", "task_id": 4 }, From 8a5b6d6e7794c9b1fb795e654b9a1655c4dd83da Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 15:53:38 +0800 Subject: [PATCH 070/160] update recommendation prompt --- expo/data/dataset.py | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 03b80985a..8af0c485e 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -28,14 +28,29 @@ SPECIAL_INSTRUCTIONS = {"ag": USE_AG, "stacking": STACKING} RECOMMENDATION = """ -## Base Models and Ensemble -You can consider using the following base models: -`GBM` (LightGBM) `CAT` (CatBoost) `XGB` (XGBoost) `RF` (random forest) `XT` (extremely randomized trees) `KNN` (k-nearest neighbors) ‘LR’ (linear regression) +## Base Models +You have access to the following base models: +Tabular: +LightGBM, CatBoost, XGBoost, random forest, extremely randomized trees, k-nearest neighbors, linear regression + +Image: +ResNet, DenseNet, VGG, Inception, MobileNet, EfficientNet + +Text: +BERT, RoBERTa, DistilBERT, GPT-2 +""" + +# The RECOMMENDATION above is not tested but might be needed for multi-modal datasets + +RECOMMENDATION = """ +## Base Models +You have access to the following base models: +LightGBM, CatBoost, XGBoost, random forest, extremely randomized trees, k-nearest neighbors, linear regression """ DI_INSTRUCTION = ( RECOMMENDATION - + """**Attention** + + """## Attention 1. Please do not leak the target label in any form during training. 2. Test set does not have the target column. 3. When conducting data exploration or analysis, print out the results of your findings. From 743c67aef8c6b0aed81d4334546a543cc2187832 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 17:34:17 +0800 Subject: [PATCH 071/160] change task type prompt to prevent unwanted label transformation --- metagpt/prompts/task_type.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/prompts/task_type.py b/metagpt/prompts/task_type.py index 116756edc..ca0aae572 100644 --- a/metagpt/prompts/task_type.py +++ b/metagpt/prompts/task_type.py @@ -11,7 +11,7 @@ - Monitor data types per column, applying appropriate methods. - Ensure operations are on existing dataset columns. - Avoid writing processed data to files. -- Avoid any change to label column, such as standardization, etc. +- **ATTENTION** Do NOT make any changes to the label column, such as standardization, etc. - Prefer alternatives to one-hot encoding for categorical data. - Only encode or scale necessary columns to allow for potential feature-specific engineering tasks (like time_extract, binning, extraction, etc.) later. - Each step do data preprocessing to train, must do same for test separately at the same time. @@ -26,7 +26,7 @@ - Avoid creating redundant or excessively numerous features in one step. - Exclude ID columns from feature generation and remove them. - Each feature engineering operation performed on the train set must also applies to the dev/test separately at the same time. -- Avoid using the label column to create features, except for cat encoding. +- **ATTENTION** Do NOT use the label column to create features or make any changes to the label column, except for cat encoding. - Use the data from previous task result if exist, do not mock or reload data yourself. - Always copy the DataFrame before processing it and use the copy to process. """ From 5e7cac7e6e3f53a98cddc62c6453e19cfb7b3bf8 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 17:51:32 +0800 Subject: [PATCH 072/160] fix fixed_insights bug --- expo/MCTS.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 265356f65..ef408b2dd 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -148,7 +148,7 @@ def save_new_role(self, role: ResearchAssistant): role = role.model_copy() role.save_state(static_save=True) - async def expand(self, max_children): + async def expand(self, max_children, use_fixed_insights): if self.is_fully_expanded(): return insight_geneartor = InstructionGenerator() @@ -159,7 +159,7 @@ async def expand(self, max_children): original_instruction=original_instruction, max_num=max_children, file_path=self.state["exp_pool_path"], - use_fixed_insights=self.use_fixed_insights, + use_fixed_insights=use_fixed_insights, ) new_state = self.state.copy() new_state["start_task_id"] += 1 @@ -259,7 +259,7 @@ def uct(node: Node): return max(all_children, key=uct) async def expand(self, node: Node, max_children=5): - await node.expand(max_children) + await node.expand(max_children, self.use_fixed_insights) if node not in self.children or not self.children[node]: self.children[node] = node.children return node.children From f856d768fe2630c1482cfb8b568f48e97978acc2 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 18:05:02 +0800 Subject: [PATCH 073/160] remove recommendation from di initial prompt, add recommendation to task type prompt --- expo/data/dataset.py | 27 ++------------------------- metagpt/prompts/task_type.py | 3 +++ 2 files changed, 5 insertions(+), 25 deletions(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 8af0c485e..9748cb8c2 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -27,30 +27,8 @@ SPECIAL_INSTRUCTIONS = {"ag": USE_AG, "stacking": STACKING} -RECOMMENDATION = """ -## Base Models -You have access to the following base models: -Tabular: -LightGBM, CatBoost, XGBoost, random forest, extremely randomized trees, k-nearest neighbors, linear regression - -Image: -ResNet, DenseNet, VGG, Inception, MobileNet, EfficientNet - -Text: -BERT, RoBERTa, DistilBERT, GPT-2 -""" - -# The RECOMMENDATION above is not tested but might be needed for multi-modal datasets - -RECOMMENDATION = """ -## Base Models -You have access to the following base models: -LightGBM, CatBoost, XGBoost, random forest, extremely randomized trees, k-nearest neighbors, linear regression -""" - -DI_INSTRUCTION = ( - RECOMMENDATION - + """## Attention +DI_INSTRUCTION = """ +## Attention 1. Please do not leak the target label in any form during training. 2. Test set does not have the target column. 3. When conducting data exploration or analysis, print out the results of your findings. @@ -69,7 +47,6 @@ # Output dir {output_dir} """ -) TASK_PROMPT = """ # User requirement diff --git a/metagpt/prompts/task_type.py b/metagpt/prompts/task_type.py index ca0aae572..6b230fc9e 100644 --- a/metagpt/prompts/task_type.py +++ b/metagpt/prompts/task_type.py @@ -34,6 +34,9 @@ # Prompt for taking on "model_train" tasks MODEL_TRAIN_PROMPT = """ The current task is about training a model, please ensure high performance: +- For tabular datasets - you have access to LightGBM, CatBoost, XGBoost, random forest, extremely randomized trees, k-nearest neighbors, linear regression, etc. +- For image datasets - you have access to ResNet, VGG, Inception, MobileNet, DenseNet, EfficientNet, etc. +- For text datasets - you have access to BERT, GPT-2, RoBERTa, DistilBERT, T5, etc. - Keep in mind that your user prioritizes results and is highly focused on model performance. So, when needed, feel free to use models of any complexity to improve effectiveness, such as XGBoost, CatBoost, etc. - If non-numeric columns exist, perform label encode together with all steps. - Use the data from previous task result directly, do not mock or reload data yourself. From 9d2c81a127de86b7ddcdceba6ad0f6db0eac6073 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 20:04:09 +0800 Subject: [PATCH 074/160] fix evaluation bug --- expo/experimenter/experimenter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 89d589d7d..b7a0e0b2f 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -87,7 +87,7 @@ def evaluate_prediction(self, split, state): pred_node_path = os.path.join(state["node_dir"], f"{self.start_time}-{split}_predictions.csv") gt_path = os.path.join(state["datasets_dir"][f"{split}_target"]) preds = pd.read_csv(pred_path) - preds = preds[preds.columns.tolist()[0]] + preds = preds[preds.columns.tolist()[-1]] preds.to_csv(pred_node_path, index=False) gt = pd.read_csv(gt_path)["target"] metric = state["dataset_config"]["metric"] From 9ff9d27ab0c388d1c37af00751181976358433d1 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 20:08:02 +0800 Subject: [PATCH 075/160] include load tree --- expo/experimenter/mcts.py | 1 + 1 file changed, 1 insertion(+) diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index e06169a70..f0db72841 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -22,6 +22,7 @@ async def run_experiment(self): state=self.state, reflection=self.args.reflection, rollouts=self.args.rollouts, + load_tree=self.args.load_tree, ) best_node = best_nodes["global_best"] dev_best_node = best_nodes["dev_best"] From 24db19fa13c70f43297de44330d8b7fe3f702ef7 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 20:33:40 +0800 Subject: [PATCH 076/160] fix start task id consistency --- expo/MCTS.py | 7 +++++-- expo/experimenter/experimenter.py | 3 ++- expo/experimenter/mcts.py | 1 + 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index ef408b2dd..c96c57b47 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -16,12 +16,11 @@ def initialize_di_root_node(state, reflection: bool = True): - start_task_id = 2 # state = create_initial_state( # task, start_task_id=start_task_id, data_config=data_config, low_is_better=low_is_better, name=name # ) role = ResearchAssistant( - node_id="0", start_task_id=start_task_id, use_reflection=reflection, role_dir=state["node_dir"] + node_id="0", start_task_id=state["start_task_id"], use_reflection=reflection, role_dir=state["node_dir"] ) return role, Node(parent=None, state=state, action=None, value=0) @@ -208,6 +207,10 @@ async def run_node(self, role=None): self.raw_reward = score_dict run_finished = True except Exception as e: + print(f"Error: {e}") + import pdb + + pdb.set_trace() mcts_logger.log("MCTS", f"Error in running the role: {e}") num_runs += 1 if not run_finished: diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index b7a0e0b2f..155108f8d 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -13,6 +13,7 @@ class Experimenter: result_path: str = "results/base" data_config = DATA_CONFIG + start_task_id = 1 def __init__(self, args, **kwargs): self.args = args @@ -20,7 +21,7 @@ def __init__(self, args, **kwargs): self.start_time = self.start_time_raw.strftime("%Y%m%d%H%M") self.state = create_initial_state( self.args.task, - start_task_id=1, + start_task_id=self.start_task_id, data_config=self.data_config, low_is_better=self.args.low_is_better, name=self.args.name, diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index f0db72841..89f362b6b 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -6,6 +6,7 @@ class MCTSExperimenter(Experimenter): result_path: str = "results/mcts" + start_task_id = 2 def __init__(self, args, tree_mode=None, **kwargs): super().__init__(args, **kwargs) From 1cdffc3d8550538195fce30cea4d913a24be9c04 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 20:49:49 +0800 Subject: [PATCH 077/160] =?UTF-8?q?FE=20prompt:=20FE=E9=80=9A=E5=B8=B8?= =?UTF-8?q?=E4=B8=8D=E4=BC=9Amake=20changes=E8=80=8C=E6=98=AF=E5=8A=A0?= =?UTF-8?q?=E6=96=B0=E7=9A=84=E7=89=B9=E5=BE=81=20DI=20prompt:=20=E8=A6=81?= =?UTF-8?q?=E6=B1=82=E8=AE=A9predictions=E6=9C=80=E7=BB=88=E7=BB=93?= =?UTF-8?q?=E6=9E=9C=E4=B8=80=E8=87=B4=EF=BC=8C=E5=B9=B6=E6=8F=90=E4=BE=9B?= =?UTF-8?q?=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expo/data/dataset.py | 1 + metagpt/prompts/task_type.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 9748cb8c2..28bd26d2e 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -40,6 +40,7 @@ 1. Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. - Both files should contain a single column named `target` with the predicted values. 2. Make sure the prediction results are in the same format as the target column in the training set. +- For instance, if the target column is categorical, the prediction results should be categorical as well. ## Output Performance Print the train and dev set performance in the last step. diff --git a/metagpt/prompts/task_type.py b/metagpt/prompts/task_type.py index 6b230fc9e..599d437c5 100644 --- a/metagpt/prompts/task_type.py +++ b/metagpt/prompts/task_type.py @@ -26,7 +26,7 @@ - Avoid creating redundant or excessively numerous features in one step. - Exclude ID columns from feature generation and remove them. - Each feature engineering operation performed on the train set must also applies to the dev/test separately at the same time. -- **ATTENTION** Do NOT use the label column to create features or make any changes to the label column, except for cat encoding. +- **ATTENTION** Do NOT use the label column to create features, except for cat encoding. - Use the data from previous task result if exist, do not mock or reload data yourself. - Always copy the DataFrame before processing it and use the copy to process. """ From 8c6dd480dcddae7bb123f2a2e9dc833db11b7fe7 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 21:10:37 +0800 Subject: [PATCH 078/160] remove pdb --- expo/MCTS.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index c96c57b47..5cd357989 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -208,9 +208,6 @@ async def run_node(self, role=None): run_finished = True except Exception as e: print(f"Error: {e}") - import pdb - - pdb.set_trace() mcts_logger.log("MCTS", f"Error in running the role: {e}") num_runs += 1 if not run_finished: From 3c50575ff7902dbe79d144dee40d0383765fb060 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 21:16:40 +0800 Subject: [PATCH 079/160] make dir at start --- expo/MCTS.py | 1 + 1 file changed, 1 insertion(+) diff --git a/expo/MCTS.py b/expo/MCTS.py index 5cd357989..228671e2c 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -40,6 +40,7 @@ def create_initial_state(task, start_task_id, data_config, low_is_better: bool, "start_task_id": start_task_id, "low_is_better": low_is_better, } + os.makedirs(initial_state["node_dir"], exist_ok=True) return initial_state From ce73f4c25a7551f1c30dae719d57220ec7fb2069 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 14 Sep 2024 21:58:53 +0800 Subject: [PATCH 080/160] lazy import autogluon --- expo/experimenter/autogluon.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/expo/experimenter/autogluon.py b/expo/experimenter/autogluon.py index 478ecfc01..93dfdb4bc 100644 --- a/expo/experimenter/autogluon.py +++ b/expo/experimenter/autogluon.py @@ -1,17 +1,19 @@ from datetime import datetime -from autogluon.tabular import TabularDataset, TabularPredictor + from expo.experimenter.custom import CustomExperimenter class AGRunner: preset = "best_quality" - time_limit = 1000 # 1000s + time_limit = 1000 # 1000s def __init__(self, state=None): self.state = state self.datasets = self.state["datasets_dir"] def run(self): + from autogluon.tabular import TabularDataset, TabularPredictor + train_path = self.datasets["train"] dev_wo_target_path = self.datasets["dev_wo_target"] test_wo_target_path = self.datasets["test_wo_target"] @@ -21,7 +23,11 @@ def run(self): test_data = TabularDataset(test_wo_target_path) eval_metric = self.state["dataset_config"]["metric"].replace(" ", "_") # predictor = TabularPredictor(label=target_col, eval_metric=eval_metric, path="AutogluonModels/ag-{}-{}".format(self.state['task'], datetime.now().strftime("%y%m%d_%H%M"))).fit(train_data, presets=self.preset, time_limit=self.time_limit, fit_weighted_ensemble=False, num_gpus=1) - predictor = TabularPredictor(label=target_col, eval_metric=eval_metric, path="AutogluonModels/ag-{}-{}".format(self.state['task'], datetime.now().strftime("%y%m%d_%H%M"))).fit(train_data, num_gpus=1) + predictor = TabularPredictor( + label=target_col, + eval_metric=eval_metric, + path="AutogluonModels/ag-{}-{}".format(self.state["task"], datetime.now().strftime("%y%m%d_%H%M")), + ).fit(train_data, num_gpus=1) dev_preds = predictor.predict(dev_data) test_preds = predictor.predict(test_data) return {"test_preds": test_preds, "dev_preds": dev_preds} @@ -44,4 +50,4 @@ async def run_experiment(self): "test_score": self.evaluate_predictions(test_preds, "test"), } results = [0, {"score_dict": score_dict, "user_requirement": user_requirement, "args": vars(self.args)}] - self.save_result(results) \ No newline at end of file + self.save_result(results) From 9665ebdf4d1a8c2dff63c0b991184f633c7171f7 Mon Sep 17 00:00:00 2001 From: duiyipan Date: Sat, 14 Sep 2024 23:43:57 +0800 Subject: [PATCH 081/160] autosklearn delete per_run_time_limit and change time_limit --- expo/experimenter/autosklearn.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/expo/experimenter/autosklearn.py b/expo/experimenter/autosklearn.py index 602b8385a..e8923c6bd 100644 --- a/expo/experimenter/autosklearn.py +++ b/expo/experimenter/autosklearn.py @@ -10,7 +10,7 @@ def custom_scorer(y_true, y_pred, metric_name): class ASRunner: - time_limit = 300 + time_limit = 600 def __init__(self, state=None): self.state = state @@ -47,7 +47,6 @@ def run(self): if eval_metric == "rmse": automl = self.autosklearn.regression.AutoSklearnRegressor( time_left_for_this_task=self.time_limit, - per_run_time_limit=60, metric=self.create_autosklearn_scorer(eval_metric), memory_limit=8192, tmp_folder="AutosklearnModels/as-{}-{}".format( @@ -58,7 +57,6 @@ def run(self): elif eval_metric in ["f1", "f1 weighted"]: automl = self.autosklearn.classification.AutoSklearnClassifier( time_left_for_this_task=self.time_limit, - per_run_time_limit=60, metric=self.create_autosklearn_scorer(eval_metric), memory_limit=8192, tmp_folder="AutosklearnModels/as-{}-{}".format( From c007d0bd5e6a89ad53bd4ab7c8504ed329ffb1a5 Mon Sep 17 00:00:00 2001 From: duiyipan Date: Sat, 14 Sep 2024 23:49:46 +0800 Subject: [PATCH 082/160] change import way --- expo/experimenter/autosklearn.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/expo/experimenter/autosklearn.py b/expo/experimenter/autosklearn.py index e8923c6bd..c6aa70920 100644 --- a/expo/experimenter/autosklearn.py +++ b/expo/experimenter/autosklearn.py @@ -15,19 +15,11 @@ class ASRunner: def __init__(self, state=None): self.state = state self.datasets = self.state["datasets_dir"] - try: - import autosklearn.classification - import autosklearn.regression - import autosklearn.metrics - - self.autosklearn = autosklearn - except ImportError: - raise ImportError( - "autosklearn not found or system not supported, please check it first" - ) def create_autosklearn_scorer(self, metric_name): - return self.autosklearn.metrics.make_scorer( + from autosklearn.metrics import make_scorer + + return make_scorer( name=metric_name, score_func=partial(custom_scorer, metric_name=metric_name) ) @@ -45,7 +37,9 @@ def run(self): y_train = train_data[target_col] if eval_metric == "rmse": - automl = self.autosklearn.regression.AutoSklearnRegressor( + from autosklearn.regression import AutoSklearnRegressor + + automl = AutoSklearnRegressor( time_left_for_this_task=self.time_limit, metric=self.create_autosklearn_scorer(eval_metric), memory_limit=8192, @@ -55,7 +49,9 @@ def run(self): n_jobs=-1, ) elif eval_metric in ["f1", "f1 weighted"]: - automl = self.autosklearn.classification.AutoSklearnClassifier( + from autosklearn.classification import AutoSklearnClassifier + + automl = AutoSklearnClassifier( time_left_for_this_task=self.time_limit, metric=self.create_autosklearn_scorer(eval_metric), memory_limit=8192, From 574f1b0e0d2c6b9702097287761bf64a29b8a82f Mon Sep 17 00:00:00 2001 From: duiyipan Date: Sun, 15 Sep 2024 00:01:35 +0800 Subject: [PATCH 083/160] change import way --- expo/experimenter/autosklearn.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/expo/experimenter/autosklearn.py b/expo/experimenter/autosklearn.py index c6aa70920..9d0ea2df4 100644 --- a/expo/experimenter/autosklearn.py +++ b/expo/experimenter/autosklearn.py @@ -24,6 +24,8 @@ def create_autosklearn_scorer(self, metric_name): ) def run(self): + import autosklearn + train_path = self.datasets["train"] dev_wo_target_path = self.datasets["dev_wo_target"] test_wo_target_path = self.datasets["test_wo_target"] @@ -37,9 +39,7 @@ def run(self): y_train = train_data[target_col] if eval_metric == "rmse": - from autosklearn.regression import AutoSklearnRegressor - - automl = AutoSklearnRegressor( + automl = autosklearn.regression.AutoSklearnRegressor( time_left_for_this_task=self.time_limit, metric=self.create_autosklearn_scorer(eval_metric), memory_limit=8192, @@ -49,9 +49,7 @@ def run(self): n_jobs=-1, ) elif eval_metric in ["f1", "f1 weighted"]: - from autosklearn.classification import AutoSklearnClassifier - - automl = AutoSklearnClassifier( + automl = autosklearn.classification.AutoSklearnClassifier( time_left_for_this_task=self.time_limit, metric=self.create_autosklearn_scorer(eval_metric), memory_limit=8192, From 0bf2d60248899cae2d4db13a8d5372e80c3443ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E4=B9=89=E7=AB=A0?= Date: Sun, 15 Sep 2024 05:33:48 +0000 Subject: [PATCH 084/160] rm lgb --- metagpt/prompts/task_type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/prompts/task_type.py b/metagpt/prompts/task_type.py index 599d437c5..e670fe088 100644 --- a/metagpt/prompts/task_type.py +++ b/metagpt/prompts/task_type.py @@ -34,7 +34,7 @@ # Prompt for taking on "model_train" tasks MODEL_TRAIN_PROMPT = """ The current task is about training a model, please ensure high performance: -- For tabular datasets - you have access to LightGBM, CatBoost, XGBoost, random forest, extremely randomized trees, k-nearest neighbors, linear regression, etc. +- For tabular datasets - you have access to XGBoost, CatBoost, random forest, extremely randomized trees, k-nearest neighbors, linear regression, etc. - For image datasets - you have access to ResNet, VGG, Inception, MobileNet, DenseNet, EfficientNet, etc. - For text datasets - you have access to BERT, GPT-2, RoBERTa, DistilBERT, T5, etc. - Keep in mind that your user prioritizes results and is highly focused on model performance. So, when needed, feel free to use models of any complexity to improve effectiveness, such as XGBoost, CatBoost, etc. From d95c1cb333069e06881a7ff7712ba349c584a5cb Mon Sep 17 00:00:00 2001 From: duiyipan Date: Mon, 16 Sep 2024 14:07:09 +0800 Subject: [PATCH 085/160] fix import error --- expo/experimenter/autosklearn.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/expo/experimenter/autosklearn.py b/expo/experimenter/autosklearn.py index 9d0ea2df4..02a3cc465 100644 --- a/expo/experimenter/autosklearn.py +++ b/expo/experimenter/autosklearn.py @@ -24,7 +24,8 @@ def create_autosklearn_scorer(self, metric_name): ) def run(self): - import autosklearn + import autosklearn.classification + import autosklearn.regression train_path = self.datasets["train"] dev_wo_target_path = self.datasets["dev_wo_target"] From 8dbcd46bfc6f4472dfedf49918eea84ac635b1ed Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 20 Sep 2024 15:53:10 +0800 Subject: [PATCH 086/160] copy notebook to result after mcts --- expo/MCTS.py | 3 +++ expo/experimenter/experimenter.py | 27 ++++++++++++++++++--------- expo/experimenter/mcts.py | 15 ++++++++++++++- expo/research_assistant.py | 2 ++ expo/utils.py | 16 +++++++++------- metagpt/prompts/task_type.py | 1 + 6 files changed, 47 insertions(+), 17 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 228671e2c..aa4ade944 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -87,6 +87,9 @@ def load_node(self): def get_depth(self): return self.depth + def get_node_dir(self): + return self.state["node_dir"] + def generate_depth(self): if self.parent is None: return 0 diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 155108f8d..77cb5fa45 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -2,6 +2,7 @@ import json import os +import numpy as np import pandas as pd from expo.evaluation.evaluation import evaluate_score @@ -58,17 +59,21 @@ async def run_experiment(self): {"idx": i, "score_dict": score_dict, "user_requirement": user_requirement, "args": vars(self.args)} ) self.save_result(results) # save intermediate results - dev_scores = [ - result["score_dict"]["dev_score"] for result in results if result["score_dict"]["dev_score"] != -1 - ] - best_dev_score = max(dev_scores) if not self.args.low_is_better else min(dev_scores) + dev_scores = [result["score_dict"]["dev_score"] for result in results] + best_dev_score = ( + max(dev_scores) + if not self.args.low_is_better + else min([score for score in dev_scores if score != -1] + [np.inf]) + ) best_score_idx = dev_scores.index(best_dev_score) - test_scores = [ - result["score_dict"]["test_score"] for result in results if result["score_dict"]["dev_score"] != -1 - ] + test_scores = [result["score_dict"]["test_score"] for result in results] avg_score = sum(test_scores) / len(test_scores) - global_best_score = max(test_scores) if not self.args.low_is_better else min(test_scores) + global_best_score = ( + max(test_scores) + if not self.args.low_is_better + else min([score for i, score in enumerate(test_scores) if dev_scores[i] != -1] + [np.inf]) + ) results.insert( 0, @@ -103,6 +108,9 @@ def evaluate(self, score_dict, state): score_dict.update(scores) return score_dict + def get_save_name(self): + return f"{self.args.exp_mode}-{self.args.task}_{self.start_time}" + def save_result(self, result): end_time_raw = datetime.datetime.now() end_time = end_time_raw.strftime("%Y%m%d%H%M") @@ -113,6 +121,7 @@ def save_result(self, result): } result = result.copy() result.insert(0, time_info) + save_name = self.get_save_name() os.makedirs(self.result_path, exist_ok=True) - with open(f"{self.result_path}/{self.args.exp_mode}-{self.args.task}_{self.start_time}.json", "w") as f: + with open(f"{self.result_path}/{save_name}.json", "w") as f: json.dump(result, f, indent=4) diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 89f362b6b..5fb00ca8d 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -1,3 +1,5 @@ +import shutil + from expo.evaluation.visualize_mcts import get_tree_text from expo.experimenter.experimenter import Experimenter from expo.Greedy import Greedy, Random @@ -28,6 +30,9 @@ async def run_experiment(self): best_node = best_nodes["global_best"] dev_best_node = best_nodes["dev_best"] + self.copy_notebook(best_node, "best") + self.copy_notebook(dev_best_node, "dev_best") + text, num_generated_codes = get_tree_text(mcts.root_node) text += f"Generated {num_generated_codes} unique codes.\n" text += f"Best node: {best_node.id}, score: {best_node.raw_reward}\n" @@ -49,7 +54,15 @@ async def run_experiment(self): ] self.save_result(results) + def copy_notebook(self, node, name): + node_dir = node.get_node_dir() + node_nb_dir = f"{node_dir}/Node-{node.id}.ipynb" + save_name = self.get_save_name() + copy_nb_dir = f"{self.result_path}/{save_name}_{name}.ipynb" + shutil.copy(node_nb_dir, copy_nb_dir) + def save_tree(self, tree_text): - fpath = f"{self.result_path}/{self.args.task}_tree_{self.args.name}.txt" + save_name = self.get_save_name() + fpath = f"{self.result_path}/{save_name}_tree.txt" with open(fpath, "w") as f: f.write(tree_text) diff --git a/expo/research_assistant.py b/expo/research_assistant.py index b21fc1a55..51de188d3 100644 --- a/expo/research_assistant.py +++ b/expo/research_assistant.py @@ -111,6 +111,8 @@ async def _act_on_task(self, current_task: Task) -> TaskResult: if int(current_task.task_id) == self.start_task_id + 1: # fe_id = current_task.dependent_task_ids self.save_state() + save_notebook(role=self, save_dir=self.role_dir, name=self.get_node_name(), save_to_depth=True) + else: save_notebook(role=self, save_dir=self.role_dir, name=self.get_node_name()) return task_result diff --git a/expo/utils.py b/expo/utils.py index f3c0c392d..56f3c21b9 100644 --- a/expo/utils.py +++ b/expo/utils.py @@ -91,19 +91,21 @@ def process_cells(nb: NotebookNode) -> NotebookNode: return nb -def save_notebook(role: Role, save_dir: str = "", name: str = ""): +def save_notebook(role: Role, save_dir: str = "", name: str = "", save_to_depth=False): save_dir = Path(save_dir) tasks = role.planner.plan.tasks - codes = [task.code for task in tasks if task.code] - clean_nb = nbformat.v4.new_notebook() - for code in codes: - clean_nb.cells.append(nbformat.v4.new_code_cell(code)) nb = process_cells(role.execute_code.nb) os.makedirs(save_dir, exist_ok=True) file_path = save_dir / f"{name}.ipynb" - clean_file_path = save_dir / f"{name}_clean.ipynb" nbformat.write(nb, file_path) - nbformat.write(clean_nb, clean_file_path) + + if save_to_depth: + clean_file_path = save_dir / f"{name}_clean.ipynb" + codes = [task.code for task in tasks if task.code] + clean_nb = nbformat.v4.new_notebook() + for code in codes: + clean_nb.cells.append(nbformat.v4.new_code_cell(code)) + nbformat.write(clean_nb, clean_file_path) async def load_execute_notebook(role): diff --git a/metagpt/prompts/task_type.py b/metagpt/prompts/task_type.py index e670fe088..97666874d 100644 --- a/metagpt/prompts/task_type.py +++ b/metagpt/prompts/task_type.py @@ -37,6 +37,7 @@ - For tabular datasets - you have access to XGBoost, CatBoost, random forest, extremely randomized trees, k-nearest neighbors, linear regression, etc. - For image datasets - you have access to ResNet, VGG, Inception, MobileNet, DenseNet, EfficientNet, etc. - For text datasets - you have access to BERT, GPT-2, RoBERTa, DistilBERT, T5, etc. +- Avoid the use of SVM because of its high training time. - Keep in mind that your user prioritizes results and is highly focused on model performance. So, when needed, feel free to use models of any complexity to improve effectiveness, such as XGBoost, CatBoost, etc. - If non-numeric columns exist, perform label encode together with all steps. - Use the data from previous task result directly, do not mock or reload data yourself. From 2f78d57e10726d7d0a2faffd320e4df77d0ac518 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 23 Sep 2024 16:46:34 +0800 Subject: [PATCH 087/160] fix random search --- expo/experimenter/aug.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/expo/experimenter/aug.py b/expo/experimenter/aug.py index e57d024bd..ffe0d04c5 100644 --- a/expo/experimenter/aug.py +++ b/expo/experimenter/aug.py @@ -24,9 +24,11 @@ async def run_experiment(self): exps = InstructionGenerator._random_sample(exp_pool, self.args.num_experiments) exps = [exp["Analysis"] for exp in exps] elif self.args.aug_mode == "set": - exp_set = InstructionGenerator.sample_instruction_set(exp_pool) - exp_set_text = "\n".join([f"{exp['task_id']}: {exp['Analysis']}" for exp in exp_set]) - exps = [exp_set_text] * self.args.num_experiments + exps = [] + for i in range(self.args.num_experiments): + exp_set = InstructionGenerator.sample_instruction_set(exp_pool) + exp_set_text = "\n".join([f"{exp['task_id']}: {exp['Analysis']}" for exp in exp_set]) + exps.append(exp_set_text) else: raise ValueError(f"Invalid mode: {self.args.aug_mode}") From 6344046c31d0d4c673ddfcd6c9b041f0dbfc3e6f Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 24 Sep 2024 20:48:44 +0800 Subject: [PATCH 088/160] update aug result summarization --- expo/experimenter/aug.py | 6 +----- expo/experimenter/experimenter.py | 29 +++++++++++++++++------------ 2 files changed, 18 insertions(+), 17 deletions(-) diff --git a/expo/experimenter/aug.py b/expo/experimenter/aug.py index ffe0d04c5..97b819802 100644 --- a/expo/experimenter/aug.py +++ b/expo/experimenter/aug.py @@ -49,9 +49,5 @@ async def run_experiment(self): "args": vars(self.args), } ) - scores = [result["score_dict"]["test_score"] for result in results] - avg_score = sum(scores) / len(scores) - best_score = max(scores) if not self.args.low_is_better else min(scores) - best_score_idx = scores.index(best_score) - results.insert(0, {"avg_score": avg_score, "best_score": best_score, "best_score_idx": best_score_idx}) + results = self.summarize_results(results) self.save_result(results) diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 77cb5fa45..c6ead281b 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -47,18 +47,7 @@ async def run_di(self, di, user_requirement, run_idx): score_dict = {"train_score": -1, "dev_score": -1, "test_score": -1, "score": -1} return score_dict - async def run_experiment(self): - state = self.state - user_requirement = state["requirement"] - results = [] - - for i in range(self.args.num_experiments): - di = ResearchAssistant(node_id="0", use_reflection=self.args.reflection) - score_dict = await self.run_di(di, user_requirement, run_idx=i) - results.append( - {"idx": i, "score_dict": score_dict, "user_requirement": user_requirement, "args": vars(self.args)} - ) - self.save_result(results) # save intermediate results + def summarize_results(self, results): dev_scores = [result["score_dict"]["dev_score"] for result in results] best_dev_score = ( max(dev_scores) @@ -85,6 +74,22 @@ async def run_experiment(self): "global_best_test_score": global_best_score, }, ) + return results + + async def run_experiment(self): + state = self.state + user_requirement = state["requirement"] + results = [] + + for i in range(self.args.num_experiments): + di = ResearchAssistant(node_id="0", use_reflection=self.args.reflection) + score_dict = await self.run_di(di, user_requirement, run_idx=i) + results.append( + {"idx": i, "score_dict": score_dict, "user_requirement": user_requirement, "args": vars(self.args)} + ) + self.save_result(results) # save intermediate results + results = self.summarize_results(results) + self.save_result(results) def evaluate_prediction(self, split, state): From 31adaee23f919da61e8b949a1651d36a734de105 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 24 Sep 2024 20:58:41 +0800 Subject: [PATCH 089/160] add special instruction for img/text dataset --- expo/data/dataset.py | 13 ++++++++++++- expo/run_experiment.py | 4 ++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 28bd26d2e..8ad6f2854 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -20,12 +20,23 @@ 7. Please use autogluon for model training with presets='medium_quality', time_limit=None, give dev dataset to tuning_data, and use right eval_metric. """ +TEXT_MODALITY = """ +7. You could use models from transformers library for this text dataset. +8. Use gpu if available for faster training. +""" + +IMAGE_MODALITY = """ +7. You could use models from torchvision library for this image dataset. +8. Use gpu if available for faster training. +""" + STACKING = """ 7. To avoid overfitting, train a weighted ensemble model such as StackingClassifier or StackingRegressor. 8. You could do some quick model prototyping to see which models work best and then use them in the ensemble. """ -SPECIAL_INSTRUCTIONS = {"ag": USE_AG, "stacking": STACKING} + +SPECIAL_INSTRUCTIONS = {"ag": USE_AG, "stacking": STACKING, "text": TEXT_MODALITY, "image": IMAGE_MODALITY} DI_INSTRUCTION = """ ## Attention diff --git a/expo/run_experiment.py b/expo/run_experiment.py index be028c47e..8dd66577c 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -3,10 +3,10 @@ from expo.experimenter.aug import AugExperimenter from expo.experimenter.autogluon import GluonExperimenter +from expo.experimenter.autosklearn import AutoSklearnExperimenter from expo.experimenter.custom import CustomExperimenter from expo.experimenter.experimenter import Experimenter from expo.experimenter.mcts import MCTSExperimenter -from expo.experimenter.autosklearn import AutoSklearnExperimenter def get_args(): @@ -43,7 +43,7 @@ def get_di_args(parser): parser.add_argument("--reflection", dest="reflection", action="store_true") parser.add_argument("--no_reflection", dest="reflection", action="store_false") parser.add_argument("--num_experiments", type=int, default=1) - parser.add_argument("--special_instruction", type=str, default=None, choices=["ag", "stacking"]) + parser.add_argument("--special_instruction", type=str, default=None, choices=["ag", "stacking", "text", "image"]) parser.set_defaults(reflection=True) From 68c672d4381dfee21b15e682aba5801bdc3934a8 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 25 Sep 2024 09:59:12 +0800 Subject: [PATCH 090/160] use transformers lib instead of torchvision --- expo/data/dataset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 8ad6f2854..f2f01f71b 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -26,7 +26,7 @@ """ IMAGE_MODALITY = """ -7. You could use models from torchvision library for this image dataset. +7. You could use models from transformers library for this image dataset. 8. Use gpu if available for faster training. """ From e2cee3905f92f661a7bacd2be102a0bf9428ffff Mon Sep 17 00:00:00 2001 From: Rayhao Date: Wed, 25 Sep 2024 22:58:04 -0700 Subject: [PATCH 091/160] add autogluon multimodal support --- expo/README.md | 2 + expo/experimenter/autogluon.py | 74 +++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/expo/README.md b/expo/README.md index 011322897..3f9e630e5 100644 --- a/expo/README.md +++ b/expo/README.md @@ -215,6 +215,8 @@ python experimenter/aide.py pip install -U pip pip install -U setuptools wheel pip install autogluon + +python run_expriment.py --exp_mode autogluon --task fashion_mnist ``` 提供github链接,并说明使用的命令以及参数设置 diff --git a/expo/experimenter/autogluon.py b/expo/experimenter/autogluon.py index 93dfdb4bc..4bcba432c 100644 --- a/expo/experimenter/autogluon.py +++ b/expo/experimenter/autogluon.py @@ -32,6 +32,77 @@ def run(self): test_preds = predictor.predict(test_data) return {"test_preds": test_preds, "dev_preds": dev_preds} + def run_images(self): + from autogluon.multimodal import MultiModalPredictor + target_col = self.state["dataset_config"]["target_col"] + train_path = self.datasets["train"] + dev_path = self.datasets["dev"] + dev_wo_target_path = self.datasets["dev_wo_target"] # Updated variable name + test_wo_target_path = self.datasets["test_wo_target"] + eval_metric = self.state["dataset_config"]["metric"].replace(" ", "_") + + # Load the datasets + train_data, dev_data, dev_wo_target_data, test_data = self.load_split_dataset( + train_path, dev_path, dev_wo_target_path, test_wo_target_path + ) + + # Create and fit the predictor + predictor = MultiModalPredictor( + label=target_col, + eval_metric=eval_metric, + path="AutogluonModels/ag-{}-{}".format(self.state["task"], datetime.now().strftime("%y%m%d_%H%M")), + ).fit(train_data=train_data, tuning_data=dev_data, time_limit=self.time_limit) + + # Make predictions on dev and test datasets + dev_preds = predictor.predict(dev_wo_target_data) + test_preds = predictor.predict(test_data) + + # Return predictions for dev and test datasets + return { + "dev_preds": dev_preds, + "test_preds": test_preds + } + + def load_split_dataset(self, train_path, dev_path, dev_wo_target_path, test_wo_target_path): + import os + import pandas as pd + """ + Loads training, dev, and test datasets from given file paths + + Args: + train_path (str): Path to the training dataset. + dev_path (str): Path to the dev dataset with target labels. + dev_wo_target_path (str): Path to the dev dataset without target labels. + test_wo_target_path (str): Path to the test dataset without target labels. + + Returns: + train_data (pd.DataFrame): Loaded training dataset with updated image paths. + dev_data (pd.DataFrame): Loaded dev dataset with updated image paths. + dev_wo_target_data (pd.DataFrame): Loaded dev dataset without target labels and updated image paths. + test_data (pd.DataFrame): Loaded test dataset with updated image paths. + """ + + # Define the root path to append + root_folder = os.path.join("F:/Download/Dataset/", self.state["task"]) + + # Load the datasets + train_data = pd.read_csv(train_path) + dev_data = pd.read_csv(dev_path) # Load dev dataset with target labels + dev_wo_target_data = pd.read_csv(dev_wo_target_path) # Load dev dataset without target labels + test_data = pd.read_csv(test_wo_target_path) + + + # Get the name of the first column (assuming it's the image path column) + + image_column = train_data.columns[0] + # Append root folder path to the image column in each dataset + train_data[image_column] = train_data[image_column].apply(lambda x: os.path.join(root_folder, x)) + dev_data[image_column] = dev_data[image_column].apply(lambda x: os.path.join(root_folder, x)) + dev_wo_target_data[image_column] = dev_wo_target_data[image_column].apply( + lambda x: os.path.join(root_folder, x)) + test_data[image_column] = test_data[image_column].apply(lambda x: os.path.join(root_folder, x)) + return train_data, dev_data, dev_wo_target_data, test_data + class GluonExperimenter(CustomExperimenter): result_path: str = "results/autogluon" @@ -41,7 +112,8 @@ def __init__(self, args, **kwargs): self.framework = AGRunner(self.state) async def run_experiment(self): - result = self.framework.run() + # result = self.framework.run() + result = self.framework.run_images() user_requirement = self.state["requirement"] dev_preds = result["dev_preds"] test_preds = result["test_preds"] From 1a1855f21a7379af2099e5a3cc01cace085ea8d8 Mon Sep 17 00:00:00 2001 From: Rayhao Date: Wed, 25 Sep 2024 23:28:16 -0700 Subject: [PATCH 092/160] add input param for autogluon --- expo/README.md | 12 +++++++++++- expo/experimenter/autogluon.py | 25 +++++++++++++------------ expo/run_experiment.py | 1 + 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/expo/README.md b/expo/README.md index 3f9e630e5..e5da96708 100644 --- a/expo/README.md +++ b/expo/README.md @@ -216,9 +216,19 @@ pip install -U pip pip install -U setuptools wheel pip install autogluon -python run_expriment.py --exp_mode autogluon --task fashion_mnist ``` +For Tabular data: +``` +python run_expriment.py --exp_mode autogluon --task {task_name} +``` +For Multimodal data: +``` +python run_expriment.py --exp_mode autogluon --task {task_name} --is_multimodal +``` +Replace {task_name} with the specific task you want to run. + + 提供github链接,并说明使用的命令以及参数设置 ### AutoSklearn #### System requirements diff --git a/expo/experimenter/autogluon.py b/expo/experimenter/autogluon.py index 4bcba432c..6cb3797e3 100644 --- a/expo/experimenter/autogluon.py +++ b/expo/experimenter/autogluon.py @@ -1,12 +1,10 @@ from datetime import datetime - from expo.experimenter.custom import CustomExperimenter +import os +import pandas as pd class AGRunner: - preset = "best_quality" - time_limit = 1000 # 1000s - def __init__(self, state=None): self.state = state self.datasets = self.state["datasets_dir"] @@ -32,7 +30,7 @@ def run(self): test_preds = predictor.predict(test_data) return {"test_preds": test_preds, "dev_preds": dev_preds} - def run_images(self): + def run_multimodal(self): from autogluon.multimodal import MultiModalPredictor target_col = self.state["dataset_config"]["target_col"] train_path = self.datasets["train"] @@ -51,7 +49,7 @@ def run_images(self): label=target_col, eval_metric=eval_metric, path="AutogluonModels/ag-{}-{}".format(self.state["task"], datetime.now().strftime("%y%m%d_%H%M")), - ).fit(train_data=train_data, tuning_data=dev_data, time_limit=self.time_limit) + ).fit(train_data=train_data, tuning_data=dev_data) # Make predictions on dev and test datasets dev_preds = predictor.predict(dev_wo_target_data) @@ -64,8 +62,6 @@ def run_images(self): } def load_split_dataset(self, train_path, dev_path, dev_wo_target_path, test_wo_target_path): - import os - import pandas as pd """ Loads training, dev, and test datasets from given file paths @@ -91,16 +87,16 @@ def load_split_dataset(self, train_path, dev_path, dev_wo_target_path, test_wo_t dev_wo_target_data = pd.read_csv(dev_wo_target_path) # Load dev dataset without target labels test_data = pd.read_csv(test_wo_target_path) - # Get the name of the first column (assuming it's the image path column) - image_column = train_data.columns[0] + # Append root folder path to the image column in each dataset train_data[image_column] = train_data[image_column].apply(lambda x: os.path.join(root_folder, x)) dev_data[image_column] = dev_data[image_column].apply(lambda x: os.path.join(root_folder, x)) dev_wo_target_data[image_column] = dev_wo_target_data[image_column].apply( lambda x: os.path.join(root_folder, x)) test_data[image_column] = test_data[image_column].apply(lambda x: os.path.join(root_folder, x)) + return train_data, dev_data, dev_wo_target_data, test_data @@ -110,10 +106,15 @@ class GluonExperimenter(CustomExperimenter): def __init__(self, args, **kwargs): super().__init__(args, **kwargs) self.framework = AGRunner(self.state) + self.is_multimodal = args.is_multimodal if hasattr(args, 'is_multimodal') else False async def run_experiment(self): - # result = self.framework.run() - result = self.framework.run_images() + if not self.is_multimodal: + result = self.framework.run() + else: + result = self.framework.run_multimodal() + + assert result is not None user_requirement = self.state["requirement"] dev_preds = result["dev_preds"] test_preds = result["test_preds"] diff --git a/expo/run_experiment.py b/expo/run_experiment.py index be028c47e..038b57ad2 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -34,6 +34,7 @@ def get_mcts_args(parser): def get_aug_exp_args(parser): parser.add_argument("--aug_mode", type=str, default="single", choices=["single", "set"]) + parser.add_argument("--is_multimodal", action="store_true", help="Specify if the model is multi-modal") def get_di_args(parser): From 3c397387f9991baa04a02b8f1e41178375f1db6f Mon Sep 17 00:00:00 2001 From: Rayhao Date: Wed, 25 Sep 2024 23:32:52 -0700 Subject: [PATCH 093/160] add tuning data for tabular mode --- expo/experimenter/autogluon.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/expo/experimenter/autogluon.py b/expo/experimenter/autogluon.py index 6cb3797e3..e5e3045f1 100644 --- a/expo/experimenter/autogluon.py +++ b/expo/experimenter/autogluon.py @@ -1,5 +1,6 @@ from datetime import datetime from expo.experimenter.custom import CustomExperimenter +from autogluon.tabular import TabularDataset, TabularPredictor import os import pandas as pd @@ -10,23 +11,22 @@ def __init__(self, state=None): self.datasets = self.state["datasets_dir"] def run(self): - from autogluon.tabular import TabularDataset, TabularPredictor - train_path = self.datasets["train"] + dev_path = self.datasets["dev"] dev_wo_target_path = self.datasets["dev_wo_target"] test_wo_target_path = self.datasets["test_wo_target"] target_col = self.state["dataset_config"]["target_col"] train_data = TabularDataset(train_path) - dev_data = TabularDataset(dev_wo_target_path) + dev_data = TabularDataset(dev_path) + dev_wo_target_data = TabularDataset(dev_wo_target_path) test_data = TabularDataset(test_wo_target_path) eval_metric = self.state["dataset_config"]["metric"].replace(" ", "_") - # predictor = TabularPredictor(label=target_col, eval_metric=eval_metric, path="AutogluonModels/ag-{}-{}".format(self.state['task'], datetime.now().strftime("%y%m%d_%H%M"))).fit(train_data, presets=self.preset, time_limit=self.time_limit, fit_weighted_ensemble=False, num_gpus=1) predictor = TabularPredictor( label=target_col, eval_metric=eval_metric, path="AutogluonModels/ag-{}-{}".format(self.state["task"], datetime.now().strftime("%y%m%d_%H%M")), - ).fit(train_data, num_gpus=1) - dev_preds = predictor.predict(dev_data) + ).fit(train_data=train_data, tuning_data=dev_data, num_gpus=1) + dev_preds = predictor.predict(dev_wo_target_data) test_preds = predictor.predict(test_data) return {"test_preds": test_preds, "dev_preds": dev_preds} From 2b67355358a6ce9f490fd1b9b6970f7010789857 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 26 Sep 2024 20:25:36 +0800 Subject: [PATCH 094/160] add step score --- expo/MCTS.py | 31 ++++++++++++++++++++++++++++++- expo/experimenter/mcts.py | 2 ++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index aa4ade944..4564cd682 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -1,3 +1,4 @@ +import json import math import os import pickle @@ -240,6 +241,7 @@ class MCTS: max_depth: int = 5 c_explore: float = 1.4 c_unvisited: float = 0.8 + node_order: list = [] def __init__(self, root_node, max_depth, use_fixed_insights): self.root_node = root_node @@ -306,11 +308,32 @@ def bfs(node: Node, best_score, best_child: Node, split): _, global_best_child = bfs(root, global_best_score, best_child, "test_score") _, dev_best_child = bfs(root, dev_best_score, best_child, "dev_score") - return {"dev_best": dev_best_child, "global_best": global_best_child} + return {"dev_best": dev_best_child, "global_best": global_best_child, "scores": self.get_score_order_dict()} def get_num_simulations(self): return self.root_node.visited + def save_node_order(self, node_id): + self.node_order.append(node_id) + with open(os.path.join(self.root_node.state["node_dir"], "node_order.json"), "w") as f: + json.dump(self.node_order, f) + + def load_node_order(self): + with open(os.path.join(self.root_node.state["node_dir"], "node_order.json"), "r") as f: + self.node_order = json.load(f) + + def get_score_order_dict(self): + scores = {"dev": [], "test": [], "dev_raw": [], "test_raw": []} + for node_id in self.node_order: + node = Node(parent=None, state=self.root_node.state, action=None, value=0) + node.id = node_id + node = node.load_node() + scores["dev"].append(node.normalized_reward["dev_score"]) + scores["test"].append(node.normalized_reward["test_score"]) + scores["dev_raw"].append(node.raw_reward["dev_score"]) + scores["test_raw"].append(node.raw_reward["test_score"]) + return scores + async def search(self, state, rollouts, load_tree=False, reflection=False): role, root = initialize_di_root_node(state, reflection=reflection) self.root_node = root @@ -329,8 +352,12 @@ async def search(self, state, rollouts, load_tree=False, reflection=False): self.backpropagate(root, reward) node, reward = await self.expand_and_simulate(root) # self.backpropagate(node, reward) + self.save_node_order(root.id) + self.save_node_order(node.id) else: root = self.root_node + self.load_node_order() + for _ in range(rollouts): # number of rollouts mcts_logger.log("MCTS", f"Start the next rollout {_+1}") node = self.select(root) @@ -344,6 +371,7 @@ async def search(self, state, rollouts, load_tree=False, reflection=False): else: node, reward = await self.expand_and_simulate(node) # self.backpropagate(node, reward) + self.save_node_order(node.id) return self.best_path(root) async def expand_and_simulate(self, node): @@ -373,6 +401,7 @@ def load_children_node(node): self.root_node = pickle.load(f) self.children[self.root_node] = self.root_node.children load_children_node(self.root_node) + if self.children: return True return False diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 5fb00ca8d..bd803bff1 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -29,6 +29,7 @@ async def run_experiment(self): ) best_node = best_nodes["global_best"] dev_best_node = best_nodes["dev_best"] + score_dict = best_nodes["scores"] self.copy_notebook(best_node, "best") self.copy_notebook(dev_best_node, "dev_best") @@ -50,6 +51,7 @@ async def run_experiment(self): "user_requirement": best_node.state["requirement"], "tree_text": text, "args": vars(self.args), + "scores": score_dict, } ] self.save_result(results) From e2c82249b37e78bfe8c0cb2f34050db8c06021db Mon Sep 17 00:00:00 2001 From: Rayhao Date: Thu, 26 Sep 2024 21:13:48 -0700 Subject: [PATCH 095/160] import issue --- expo/experimenter/autogluon.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/experimenter/autogluon.py b/expo/experimenter/autogluon.py index e5e3045f1..dabf0c138 100644 --- a/expo/experimenter/autogluon.py +++ b/expo/experimenter/autogluon.py @@ -1,6 +1,5 @@ from datetime import datetime from expo.experimenter.custom import CustomExperimenter -from autogluon.tabular import TabularDataset, TabularPredictor import os import pandas as pd @@ -11,6 +10,7 @@ def __init__(self, state=None): self.datasets = self.state["datasets_dir"] def run(self): + from autogluon.tabular import TabularDataset, TabularPredictor train_path = self.datasets["train"] dev_path = self.datasets["dev"] dev_wo_target_path = self.datasets["dev_wo_target"] From 24536df24c24b80b870c96248bfc46fdd07ec929 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 27 Sep 2024 12:54:22 +0800 Subject: [PATCH 096/160] update_save_order --- expo/experimenter/mcts.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index bd803bff1..fa42cb070 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -31,9 +31,6 @@ async def run_experiment(self): dev_best_node = best_nodes["dev_best"] score_dict = best_nodes["scores"] - self.copy_notebook(best_node, "best") - self.copy_notebook(dev_best_node, "dev_best") - text, num_generated_codes = get_tree_text(mcts.root_node) text += f"Generated {num_generated_codes} unique codes.\n" text += f"Best node: {best_node.id}, score: {best_node.raw_reward}\n" @@ -55,6 +52,8 @@ async def run_experiment(self): } ] self.save_result(results) + self.copy_notebook(best_node, "best") + self.copy_notebook(dev_best_node, "dev_best") def copy_notebook(self, node, name): node_dir = node.get_node_dir() From af844693b1510655223edc8c879cc9f14f9140e0 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 27 Sep 2024 15:13:23 +0800 Subject: [PATCH 097/160] change label column --- expo/data/hf_data.py | 4 ++-- expo/datasets.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/expo/data/hf_data.py b/expo/data/hf_data.py index df3a6ed20..a43fcd415 100644 --- a/expo/data/hf_data.py +++ b/expo/data/hf_data.py @@ -18,7 +18,7 @@ "name": "oxford-iiit-pet", "dataset_name": "timm/oxford-iiit-pet", "image_col": "image", - "target_col": "label_cat_dog", + "target_col": "label", "modality": "image", }, { @@ -115,7 +115,7 @@ def get_dataset_info(self): if __name__ == "__main__": dataset_dir = "D:/work/automl/datasets" - save_analysis_pool = False + save_analysis_pool = True force_update = False datasets_dict = {"datasets": {}} solution_designer = SolutionDesigner() diff --git a/expo/datasets.yaml b/expo/datasets.yaml index 92e004c6d..e58e717b5 100644 --- a/expo/datasets.yaml +++ b/expo/datasets.yaml @@ -202,7 +202,7 @@ datasets: oxford-iiit-pet: dataset: oxford-iiit-pet metric: f1 - target_col: label_cat_dog + target_col: label user_requirement: "This is a oxford-iiit-pet dataset. Your goal is to predict\ \ the target column `label_cat_dog`.\nPerform data analysis, data preprocessing,\ \ feature engineering, and modeling to predict the target. \nReport f1 on the\ From bd26e900e6b33c9e16c6246958e977b356dd0a5a Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 28 Sep 2024 10:07:35 +0800 Subject: [PATCH 098/160] update target label --- expo/datasets.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expo/datasets.yaml b/expo/datasets.yaml index e58e717b5..016daf7ec 100644 --- a/expo/datasets.yaml +++ b/expo/datasets.yaml @@ -201,10 +201,10 @@ datasets: \ Do not plot or make any visualizations.\n" oxford-iiit-pet: dataset: oxford-iiit-pet - metric: f1 + metric: f1 weighted target_col: label user_requirement: "This is a oxford-iiit-pet dataset. Your goal is to predict\ - \ the target column `label_cat_dog`.\nPerform data analysis, data preprocessing,\ + \ the target column `label`.\nPerform data analysis, data preprocessing,\ \ feature engineering, and modeling to predict the target. \nReport f1 on the\ \ eval data. Do not plot or make any visualizations.\n" sms_spam: From 06702db6d15deca1722027af9399173b9b647259 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 28 Sep 2024 10:39:12 +0800 Subject: [PATCH 099/160] update pet --- expo/datasets.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/datasets.yaml b/expo/datasets.yaml index 016daf7ec..2d02951d4 100644 --- a/expo/datasets.yaml +++ b/expo/datasets.yaml @@ -205,7 +205,7 @@ datasets: target_col: label user_requirement: "This is a oxford-iiit-pet dataset. Your goal is to predict\ \ the target column `label`.\nPerform data analysis, data preprocessing,\ - \ feature engineering, and modeling to predict the target. \nReport f1 on the\ + \ feature engineering, and modeling to predict the target. \nReport f1 weighted on the\ \ eval data. Do not plot or make any visualizations.\n" sms_spam: dataset: sms_spam From db247d9ff4dd8a10279bd8e1d232d552c8a2b324 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 28 Sep 2024 15:01:01 +0800 Subject: [PATCH 100/160] save result order --- expo/experimenter/mcts.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index fa42cb070..22b480caf 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -36,8 +36,6 @@ async def run_experiment(self): text += f"Best node: {best_node.id}, score: {best_node.raw_reward}\n" text += f"Dev best node: {dev_best_node.id}, score: {dev_best_node.raw_reward}\n" print(text) - self.save_tree(text) - results = [ { "best_node": best_node.id, @@ -54,6 +52,7 @@ async def run_experiment(self): self.save_result(results) self.copy_notebook(best_node, "best") self.copy_notebook(dev_best_node, "dev_best") + self.save_tree(text) def copy_notebook(self, node, name): node_dir = node.get_node_dir() From 1589a04cdbf14ef14549bd09141483191b85929b Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 28 Sep 2024 18:27:23 +0800 Subject: [PATCH 101/160] clarify prediction saving prompt --- expo/data/dataset.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index f2f01f71b..dd4cb4543 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -26,7 +26,7 @@ """ IMAGE_MODALITY = """ -7. You could use models from transformers library for this image dataset. +7. You could use models from transformers/torchvision library for this image dataset. 8. Use gpu if available for faster training. """ @@ -50,8 +50,8 @@ ## Saving Dev and Test Predictions 1. Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. - Both files should contain a single column named `target` with the predicted values. -2. Make sure the prediction results are in the same format as the target column in the training set. -- For instance, if the target column is categorical, the prediction results should be categorical as well. +2. Make sure the prediction results are in the same format as the target column in the original training set. +- For instance, if the original target column is a list of string, the prediction results should also be strings. ## Output Performance Print the train and dev set performance in the last step. From 788e42ea55e80682221e10b7f7cf56daa3c102fe Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 30 Sep 2024 16:06:48 +0800 Subject: [PATCH 102/160] update model list --- metagpt/prompts/task_type.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/metagpt/prompts/task_type.py b/metagpt/prompts/task_type.py index 97666874d..74286a28f 100644 --- a/metagpt/prompts/task_type.py +++ b/metagpt/prompts/task_type.py @@ -35,8 +35,8 @@ MODEL_TRAIN_PROMPT = """ The current task is about training a model, please ensure high performance: - For tabular datasets - you have access to XGBoost, CatBoost, random forest, extremely randomized trees, k-nearest neighbors, linear regression, etc. -- For image datasets - you have access to ResNet, VGG, Inception, MobileNet, DenseNet, EfficientNet, etc. -- For text datasets - you have access to BERT, GPT-2, RoBERTa, DistilBERT, T5, etc. +- For image datasets - you have access to Swin Transformer, ViT, ResNet, EfficientNet, etc. +- For text datasets - you have access to Electra, DeBERTa, GPT-2, BERT, etc. - Avoid the use of SVM because of its high training time. - Keep in mind that your user prioritizes results and is highly focused on model performance. So, when needed, feel free to use models of any complexity to improve effectiveness, such as XGBoost, CatBoost, etc. - If non-numeric columns exist, perform label encode together with all steps. From f80ebc4d67d80bf9db2caa70ebc9bdfeca468692 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 10 Oct 2024 16:30:07 +0800 Subject: [PATCH 103/160] =?UTF-8?q?1.=20add=20role=20level=20timeout=20?= =?UTF-8?q?=E9=99=90=E5=88=B6=E6=98=AF1000s=202.=20=E4=BF=AE=E6=94=B9log?= =?UTF-8?q?=E7=9A=84=E5=B1=82=E7=BA=A7=E9=80=BB=E8=BE=91=203.=20data.yaml?= =?UTF-8?q?=20=E5=8F=AA=E7=94=A8=E4=BA=8E=E5=AD=98=E8=B7=AF=E5=BE=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expo/MCTS.py | 6 +- expo/README.md | 115 +++++++++++++-------------- expo/data.yaml | 159 +------------------------------------ expo/research_assistant.py | 37 +++++++-- expo/utils.py | 14 ++-- 5 files changed, 97 insertions(+), 234 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 4564cd682..8e685cc0a 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -10,7 +10,7 @@ from expo.data.dataset import generate_task_requirement, get_split_dataset_path from expo.evaluation.evaluation import evaluate_score from expo.insights.instruction_generator import InstructionGenerator -from expo.research_assistant import ResearchAssistant +from expo.research_assistant import ResearchAssistant, TimeoutException from expo.utils import get_exp_pool_path, load_execute_notebook, mcts_logger from metagpt.tools.tool_recommend import ToolRecommender from metagpt.utils.common import read_json_file @@ -211,10 +211,14 @@ async def run_node(self, role=None): score_dict = self.evaluate_simulation(score_dict) self.raw_reward = score_dict run_finished = True + except TimeoutException as e: + mcts_logger.log("MCTS", f"Role-level timeout: {e}") + break except Exception as e: print(f"Error: {e}") mcts_logger.log("MCTS", f"Error in running the role: {e}") num_runs += 1 + if not run_finished: mcts_logger.log("MCTS", f"Role {role.node_id} failed to run") if self.state["low_is_better"]: diff --git a/expo/README.md b/expo/README.md index e5da96708..0a807c928 100644 --- a/expo/README.md +++ b/expo/README.md @@ -1,21 +1,21 @@ -# Expo +# SELA: Tree-Search Enhanced LLM Agents for Automated Machine Learning +![pipeline](resources/MCTS-Experimenter.jpg) ## 1. Data Preparation -- 下载数据集:https://deepwisdom.feishu.cn/drive/folder/RVyofv9cvlvtxKdddt2cyn3BnTc?from=from_copylink -- 修改`data.yaml`的`datasets_dir`为数据集合集根目录存储位置 +- Download Datasets:https://deepwisdom.feishu.cn/drive/folder/RVyofv9cvlvtxKdddt2cyn3BnTc?from=from_copylink ## 2. Configs ### Data Config -`datasets.yaml` 提供数据集对应的指标和基础提示词 +`datasets.yaml` Provide base prompts, metrics, target columns for respective datasets -`data.yaml` 继承了`datasets.yaml`以及一些路径信息,需要将`datasets_dir`指到数据集合集的根目录下 +- Modify `datasets_dir` to the root directory of all the datasets in `data.yaml` ### LLM Config @@ -30,28 +30,64 @@ llm: ``` ### Budget -实验轮次 k = 10, 20 +Experiment rollouts k = 5, 10, 20 ### Prompt Usage -- 通过执行`dataset.py`中的`generate_task_requirement`函数获取提示词 - - 非DI-based方法设置`is_di=False` - - `data_config`用`utils.DATA_CONFIG` -- 每一个数据集里有`dataset_info.json`,里面的内容需要提供给baselines以保证公平(`generate_task_requirement`已经默认提供) +- Use the function `generate_task_requirement` in `dataset.py` to get task requirement. + - If the method is non-DI-based, set `is_di=False`. + - Use `utils.DATA_CONFIG` as `data_config` -## 3. Evaluation +## 3. SELA -运行各个框架,运行后框架需要提供Dev和Test的`dev_predictions.csv`和`test_predictions.csv`,每个csv文件只需要单个名为target的列 +### Run SELA + +#### Setup +In the root directory, -- 使用`CustomExperimenter` ``` -experimenter = CustomExperimenter(task="titanic") -score_dict = experimenter.evaluate_pred_files(dev_pred_path, test_pred_path) +pip install -e . + +cd expo + +pip install -r requirements.txt ``` -## 4. Baselines +#### Run + +- `python run_experiment.py --exp_mode mcts --task titanic --rollouts 10` + +If the dataset has reg metric, remember to use `--low_is_better`: + +- `python run_experiment.py --exp_mode mcts --task house_prices --rollouts 10 --low_is_better` + + +In addition to the generated insights, include the fixed insights saved in `expo/insights/fixed_insights.json` +- `--use_fixed_insights` + + + +#### Ablation Study + +**DI RandomSearch** + +- Single insight +`python run_experiment.py --exp_mode aug --task titanic --aug_mode single` + +- Set insight +`python run_experiment.py --exp_mode aug --task titanic --aug_mode set` + + +## 4. Evaluation + +Each baseline needs to produce `dev_predictions.csv`和`test_predictions.csv`. Each csv file only needs a `target` column. + +- Use the function `evaluate_score` to evaluate. + + +## 5. Baselines ### DS Agent ``` git clone https://github.com/guosyjlu/DS-Agent.git @@ -257,55 +293,14 @@ python run_experiment.py --exp_mode autosklearn --task titanic ``` ### Base DI -For setup, check 5. - +For setup, check 4. - `python run_experiment.py --exp_mode base --task titanic --num_experiments 10` -- Ask DI to use AutoGluon: `--special_instruction ag` -- Ask DI to use the stacking ensemble method: `--special_instruction stacking` +- Specifically instruct DI to use AutoGluon: `--special_instruction ag` +- Specifically instruct DI to use the stacking ensemble method: `--special_instruction stacking` -## 5. DI MCTS - -### Run DI MCTS - -#### Setup -In the root directory, - -``` -pip install -e . - -cd expo - -pip install -r requirements.txt -``` - -#### Run - -- `python run_experiment.py --exp_mode mcts --task titanic --rollout 10` - -If the dataset has reg metric, remember to use `--low_is_better`: - -- `python run_experiment.py --exp_mode mcts --task househouse_prices --rollout 10 --low_is_better` - - -In addition to the generated insights, include the fixed insights saved in `expo/insights/fixed_insights.json` -- `--use_fixed_insights` - - - -#### Ablation Study - -**DI RandomSearch** - -- Single insight -`python run_experiment.py --exp_mode aug --task titanic --aug_mode single` - -- Set insight -`python run_experiment.py --exp_mode aug --task titanic --aug_mode set` - - diff --git a/expo/data.yaml b/expo/data.yaml index d62e45309..8273fecad 100644 --- a/expo/data.yaml +++ b/expo/data.yaml @@ -1,160 +1,3 @@ datasets_dir: "D:/work/automl/datasets" # path to the datasets directory - -datasets: - titanic: - dataset: 04_titanic - metric: f1 - target_col: Survived - user_requirement: "This is a 04_titanic dataset. Your goal is to predict the target\ - \ column `Survived`.\nPerform data analysis, data preprocessing, feature engineering,\ - \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ - \ or make any visualizations.\n" - house-prices: - dataset: 05_house-prices-advanced-regression-techniques - metric: rmse - target_col: SalePrice - user_requirement: "This is a 05_house-prices-advanced-regression-techniques dataset.\ - \ Your goal is to predict the target column `SalePrice`.\nPerform data analysis,\ - \ data preprocessing, feature engineering, and modeling to predict the target.\ - \ \nReport rmse on the eval data. Do not plot or make any visualizations.\n" - santander-customer: - dataset: 06_santander-customer-transaction-prediction - metric: f1 - target_col: target - user_requirement: "This is a 06_santander-customer-transaction-prediction dataset.\ - \ Your goal is to predict the target column `target`.\nPerform data analysis,\ - \ data preprocessing, feature engineering, and modeling to predict the target.\ - \ \nReport f1 on the eval data. Do not plot or make any visualizations.\n" - icr: - dataset: 07_icr-identify-age-related-conditions - metric: f1 - target_col: Class - user_requirement: "This is a 07_icr-identify-age-related-conditions dataset. Your\ - \ goal is to predict the target column `Class`.\nPerform data analysis, data\ - \ preprocessing, feature engineering, and modeling to predict the target. \n\ - Report f1 on the eval data. Do not plot or make any visualizations.\n" - Click_prediction_small: - dataset: Click_prediction_small - metric: f1 - target_col: click - user_requirement: "This is a Click_prediction_small dataset. Your goal is to predict\ - \ the target column `click`.\nPerform data analysis, data preprocessing, feature\ - \ engineering, and modeling to predict the target. \nReport f1 on the eval data.\ - \ Do not plot or make any visualizations.\n" - GesturePhaseSegmentationProcessed: - dataset: GesturePhaseSegmentationProcessed - metric: f1 weighted - target_col: Phase - user_requirement: "This is a GesturePhaseSegmentationProcessed dataset. Your goal\ - \ is to predict the target column `Phase`.\nPerform data analysis, data preprocessing,\ - \ feature engineering, and modeling to predict the target. \nReport f1 weighted\ - \ on the eval data. Do not plot or make any visualizations.\n" - Moneyball: - dataset: Moneyball - metric: rmse - target_col: RS - user_requirement: "This is a Moneyball dataset. Your goal is to predict the target\ - \ column `RS`.\nPerform data analysis, data preprocessing, feature engineering,\ - \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ - \ plot or make any visualizations.\n" - SAT11-HAND-runtime-regression: - dataset: SAT11-HAND-runtime-regression - metric: rmse - target_col: runtime - user_requirement: "This is a SAT11-HAND-runtime-regression dataset. Your goal\ - \ is to predict the target column `runtime`.\nPerform data analysis, data preprocessing,\ - \ feature engineering, and modeling to predict the target. \nReport rmse on\ - \ the eval data. Do not plot or make any visualizations.\n" - boston: - dataset: boston - metric: rmse - target_col: MEDV - user_requirement: "This is a boston dataset. Your goal is to predict the target\ - \ column `MEDV`.\nPerform data analysis, data preprocessing, feature engineering,\ - \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ - \ plot or make any visualizations.\n" - colleges: - dataset: colleges - metric: rmse - target_col: percent_pell_grant - user_requirement: "This is a colleges dataset. Your goal is to predict the target\ - \ column `percent_pell_grant`.\nPerform data analysis, data preprocessing, feature\ - \ engineering, and modeling to predict the target. \nReport rmse on the eval\ - \ data. Do not plot or make any visualizations.\n" - credit-g: - dataset: credit-g - metric: f1 - target_col: class - user_requirement: "This is a credit-g dataset. Your goal is to predict the target\ - \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ - \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ - \ or make any visualizations.\n" - diamonds: - dataset: diamonds - metric: rmse - target_col: price - user_requirement: "This is a diamonds dataset. Your goal is to predict the target\ - \ column `price`.\nPerform data analysis, data preprocessing, feature engineering,\ - \ and modeling to predict the target. \nReport rmse on the eval data. Do not\ - \ plot or make any visualizations.\n" - jasmine: - dataset: jasmine - metric: f1 - target_col: class - user_requirement: "This is a jasmine dataset. Your goal is to predict the target\ - \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ - \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ - \ or make any visualizations.\n" - kc1: - dataset: kc1 - metric: f1 - target_col: defects - user_requirement: "This is a kc1 dataset. Your goal is to predict the target column\ - \ `defects`.\nPerform data analysis, data preprocessing, feature engineering,\ - \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ - \ or make any visualizations.\n" - kick: - dataset: kick - metric: f1 - target_col: IsBadBuy - user_requirement: "This is a kick dataset. Your goal is to predict the target\ - \ column `IsBadBuy`.\nPerform data analysis, data preprocessing, feature engineering,\ - \ and modeling to predict the target. \nReport f1 on the eval data. Do not plot\ - \ or make any visualizations.\n" - mfeat-factors: - dataset: mfeat-factors - metric: f1 weighted - target_col: class - user_requirement: "This is a mfeat-factors dataset. Your goal is to predict the\ - \ target column `class`.\nPerform data analysis, data preprocessing, feature\ - \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ - \ eval data. Do not plot or make any visualizations.\n" - segment: - dataset: segment - metric: f1 weighted - target_col: class - user_requirement: "This is a segment dataset. Your goal is to predict the target\ - \ column `class`.\nPerform data analysis, data preprocessing, feature engineering,\ - \ and modeling to predict the target. \nReport f1 weighted on the eval data.\ - \ Do not plot or make any visualizations.\n" - steel-plates-fault: - dataset: steel-plates-fault - metric: f1 weighted - target_col: target - user_requirement: "This is a steel-plates-fault dataset. Your goal is to predict\ - \ the target column `target`.\nPerform data analysis, data preprocessing, feature\ - \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ - \ eval data. Do not plot or make any visualizations.\n" - wine-quality-white: - dataset: wine-quality-white - metric: f1 weighted - target_col: Class - user_requirement: "This is a wine-quality-white dataset. Your goal is to predict\ - \ the target column `Class`.\nPerform data analysis, data preprocessing, feature\ - \ engineering, and modeling to predict the target. \nReport f1 weighted on the\ - \ eval data. Do not plot or make any visualizations.\n" - - work_dir: ../workspace # path to the workspace directory -role_dir: storage/team/environment/roles/ResearchAssistant_David -# analysis_pool_dir: D:/work/MG-open/MetaGPT/examples/MCTS_test/analysis_pool_sample.json \ No newline at end of file +role_dir: storage/SELA # path to the role directory diff --git a/expo/research_assistant.py b/expo/research_assistant.py index 51de188d3..8ee7dc204 100644 --- a/expo/research_assistant.py +++ b/expo/research_assistant.py @@ -1,5 +1,6 @@ from __future__ import annotations +import asyncio import json import os @@ -10,7 +11,7 @@ from metagpt.const import SERDESER_PATH from metagpt.roles.di.data_interpreter import DataInterpreter from metagpt.schema import Message, Task, TaskResult -from metagpt.utils.common import CodeParser, write_json_file +from metagpt.utils.common import CodeParser, role_raise_decorator, write_json_file EXTRACT_SCORE_PROMPT = """ # Code: @@ -34,6 +35,27 @@ """ +class TimeoutException(Exception): + pass + + +def async_timeout(seconds): + def decorator(func): + async def wrapper(self, *args, **kwargs): + try: + result = await asyncio.wait_for(func(self, *args, **kwargs), timeout=seconds) + except asyncio.TimeoutError: + text = f"Function timed out after {seconds} seconds" + mcts_logger.error(text) + self.save_state() + raise TimeoutException(text) + return result + + return wrapper + + return decorator + + class ResearchAssistant(DataInterpreter): node_id: str = "0" start_task_id: int = 1 @@ -117,6 +139,12 @@ async def _act_on_task(self, current_task: Task) -> TaskResult: return task_result def save_state(self, static_save=False): + """ + attribute: + state_saved - the state has been saved + input: + static_save - saving the state without changing the state_saved flag - used when a new role is created + """ if self.state_saved and not static_save: return if not static_save: @@ -135,18 +163,15 @@ def remap_tasks(self): self.planner.plan.task_map[task_id] for task_id in sorted(self.planner.plan.task_map.keys()) ] + @async_timeout(1000) + @role_raise_decorator async def run(self, with_message=None) -> Message | None: """Observe, and think and act based on the results of the observation""" if with_message == "continue": - # self.set_todo(None) - # working_memory = self.working_memory - # self.remap_tasks() mcts_logger.info("Continue to run") self.rc.working_memory.clear() self.working_memory.clear() - # self.rc.todo = WriteAnalysisCode() rsp = await self.react() - # 发送响应消息给 Environment 对象,以便它将消息传递给订阅者 self.set_todo(None) self.publish_message(rsp) return rsp diff --git a/expo/utils.py b/expo/utils.py index 56f3c21b9..b022879b0 100644 --- a/expo/utils.py +++ b/expo/utils.py @@ -1,6 +1,5 @@ import os import re -import sys from datetime import datetime from pathlib import Path @@ -21,22 +20,19 @@ def load_data_config(file_path="data.yaml"): DATASET_CONFIG = load_data_config("datasets.yaml") DATA_CONFIG = load_data_config() -DATA_CONFIG["datasets"].update(DATASET_CONFIG["datasets"]) +DATA_CONFIG["datasets"] = DATASET_CONFIG["datasets"] def get_mcts_logger(): - print_level = "INFO" - print_level2 = "MCTS" - logfile_level = "MCTS" + logfile_level = "DEBUG" name: str = None current_date = datetime.now() formatted_date = current_date.strftime("%Y%m%d") log_name = f"{name}_{formatted_date}" if name else formatted_date # name a log with prefix name - _logger.remove() - _logger.level(logfile_level, color="", no=25) - _logger.add(sys.stderr, level=print_level) - _logger.add(sys.stderr, level=print_level2) + # _logger.remove() + _logger.level("MCTS", color="", no=25) + # _logger.add(sys.stderr, level=print_level) _logger.add(Path(DATA_CONFIG["work_dir"]) / DATA_CONFIG["role_dir"] / f"{log_name}.txt", level=logfile_level) _logger.propagate = False return _logger From 2fc8f20de6fb5be7cdaf30de246f336dfc62a325 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 10 Oct 2024 16:38:19 +0800 Subject: [PATCH 104/160] can change timeout through data.yaml --- expo/data.yaml | 1 + expo/research_assistant.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/expo/data.yaml b/expo/data.yaml index 8273fecad..f1556c519 100644 --- a/expo/data.yaml +++ b/expo/data.yaml @@ -1,3 +1,4 @@ datasets_dir: "D:/work/automl/datasets" # path to the datasets directory work_dir: ../workspace # path to the workspace directory role_dir: storage/SELA # path to the role directory +role_timeout: 1000 # timeout for each node/role in seconds \ No newline at end of file diff --git a/expo/research_assistant.py b/expo/research_assistant.py index 8ee7dc204..8fadeb7fb 100644 --- a/expo/research_assistant.py +++ b/expo/research_assistant.py @@ -6,7 +6,7 @@ from pydantic import model_validator -from expo.utils import mcts_logger, save_notebook +from expo.utils import DATA_CONFIG, mcts_logger, save_notebook from metagpt.actions.di.write_analysis_code import WriteAnalysisCode from metagpt.const import SERDESER_PATH from metagpt.roles.di.data_interpreter import DataInterpreter @@ -163,7 +163,7 @@ def remap_tasks(self): self.planner.plan.task_map[task_id] for task_id in sorted(self.planner.plan.task_map.keys()) ] - @async_timeout(1000) + @async_timeout(DATA_CONFIG["role_timeout"]) @role_raise_decorator async def run(self, with_message=None) -> Message | None: """Observe, and think and act based on the results of the observation""" From eb460d3e1908e412dd3ceaeca518cc58412ca01a Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 10 Oct 2024 18:54:40 +0800 Subject: [PATCH 105/160] make timeout as argument --- expo/MCTS.py | 5 ++++- expo/data.yaml | 3 +-- expo/experimenter/aug.py | 4 +++- expo/experimenter/experimenter.py | 5 ++++- expo/research_assistant.py | 11 ++++++----- expo/resources/MCTS-Experimenter.jpg | Bin 0 -> 659150 bytes expo/run_experiment.py | 1 + 7 files changed, 19 insertions(+), 10 deletions(-) create mode 100644 expo/resources/MCTS-Experimenter.jpg diff --git a/expo/MCTS.py b/expo/MCTS.py index 8e685cc0a..7de123572 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -26,7 +26,9 @@ def initialize_di_root_node(state, reflection: bool = True): return role, Node(parent=None, state=state, action=None, value=0) -def create_initial_state(task, start_task_id, data_config, low_is_better: bool, name: str, special_instruction: str): +def create_initial_state( + task, start_task_id, data_config, low_is_better: bool, name: str, special_instruction: str, args +): initial_state = { "task": task, "work_dir": data_config["work_dir"], @@ -40,6 +42,7 @@ def create_initial_state(task, start_task_id, data_config, low_is_better: bool, "has_run": False, "start_task_id": start_task_id, "low_is_better": low_is_better, + "role_timeout": args.role_timeout, } os.makedirs(initial_state["node_dir"], exist_ok=True) return initial_state diff --git a/expo/data.yaml b/expo/data.yaml index f1556c519..4c6549490 100644 --- a/expo/data.yaml +++ b/expo/data.yaml @@ -1,4 +1,3 @@ datasets_dir: "D:/work/automl/datasets" # path to the datasets directory work_dir: ../workspace # path to the workspace directory -role_dir: storage/SELA # path to the role directory -role_timeout: 1000 # timeout for each node/role in seconds \ No newline at end of file +role_dir: storage/SELA # path to the role directory \ No newline at end of file diff --git a/expo/experimenter/aug.py b/expo/experimenter/aug.py index 97b819802..bcfa5d4ad 100644 --- a/expo/experimenter/aug.py +++ b/expo/experimenter/aug.py @@ -34,7 +34,9 @@ async def run_experiment(self): results = [] for i in range(self.args.num_experiments): - di = ResearchAssistant(node_id=str(i), use_reflection=self.args.reflection) + di = ResearchAssistant( + node_id=str(i), use_reflection=self.args.reflection, role_timeout=self.args.role_timeout + ) di.role_dir = f"{di.role_dir}_{self.args.task}" requirement = user_requirement + EXPS_PROMPT.format(experience=exps[i]) print(requirement) diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index c6ead281b..9aa879e24 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -27,6 +27,7 @@ def __init__(self, args, **kwargs): low_is_better=self.args.low_is_better, name=self.args.name, special_instruction=self.args.special_instruction, + args=self.args, ) async def run_di(self, di, user_requirement, run_idx): @@ -82,7 +83,9 @@ async def run_experiment(self): results = [] for i in range(self.args.num_experiments): - di = ResearchAssistant(node_id="0", use_reflection=self.args.reflection) + di = ResearchAssistant( + node_id="0", use_reflection=self.args.reflection, role_timeout=self.args.role_timeout + ) score_dict = await self.run_di(di, user_requirement, run_idx=i) results.append( {"idx": i, "score_dict": score_dict, "user_requirement": user_requirement, "args": vars(self.args)} diff --git a/expo/research_assistant.py b/expo/research_assistant.py index 8fadeb7fb..c574d5b18 100644 --- a/expo/research_assistant.py +++ b/expo/research_assistant.py @@ -6,7 +6,7 @@ from pydantic import model_validator -from expo.utils import DATA_CONFIG, mcts_logger, save_notebook +from expo.utils import mcts_logger, save_notebook from metagpt.actions.di.write_analysis_code import WriteAnalysisCode from metagpt.const import SERDESER_PATH from metagpt.roles.di.data_interpreter import DataInterpreter @@ -39,13 +39,13 @@ class TimeoutException(Exception): pass -def async_timeout(seconds): +def async_timeout(): def decorator(func): async def wrapper(self, *args, **kwargs): try: - result = await asyncio.wait_for(func(self, *args, **kwargs), timeout=seconds) + result = await asyncio.wait_for(func(self, *args, **kwargs), timeout=self.role_timeout) except asyncio.TimeoutError: - text = f"Function timed out after {seconds} seconds" + text = f"Function timed out after {self.role_timeout} seconds" mcts_logger.error(text) self.save_state() raise TimeoutException(text) @@ -61,6 +61,7 @@ class ResearchAssistant(DataInterpreter): start_task_id: int = 1 state_saved: bool = False role_dir: str = SERDESER_PATH.joinpath("team", "environment", "roles", "Experimenter") + role_timeout: int = 1000 def get_node_name(self): return f"Node-{self.node_id}" @@ -163,7 +164,7 @@ def remap_tasks(self): self.planner.plan.task_map[task_id] for task_id in sorted(self.planner.plan.task_map.keys()) ] - @async_timeout(DATA_CONFIG["role_timeout"]) + @async_timeout() @role_raise_decorator async def run(self, with_message=None) -> Message | None: """Observe, and think and act based on the results of the observation""" diff --git a/expo/resources/MCTS-Experimenter.jpg b/expo/resources/MCTS-Experimenter.jpg new file mode 100644 index 0000000000000000000000000000000000000000..bbae98ee3ef6781b51e34278a561bb1d5b8f1d0a GIT binary patch literal 659150 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

L;GOTEtK62mEin*@&JPq~M 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

  • Y2}6$B&DamJ_{kNA_{#k5?fyC>v(LhhRh_mU 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ཷ}12?R>^qyHZ2Cg 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

    W-?1%Gx*uueG5Yim&Pyj@%D?i#U;^A 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�l4Baf`6WtB#W0XuT3zmv;#ME%49#oeq zzu*xzz3R{|#Z5fV4+M6<#g<2c?dST=>rcr!*rN-tFwMWjbhErx?r2)P_+as(=jeBk zyU6Ax07IOiJ|+JGKx;~d78?LWb3Fx4XqX33!^B3vVW&a~-75Ntl)%CbqJWSh6Fu5H zD=(WOcH!c|k9G**>Ge@rR#&_OIzTR!Rf^$6{VZwbzM|XVaB2gT4{KyW)$PJ(dYjeE z=ZH=RVkbozr87!68}0igq4S6!{eu>Rg7^exzMhm}i|6lGYus?;?8RiHjp0}X=I#-C zal$nrJ?o%h9=WamDoIcA*bUIn9uG;LTks~HFMN$zt9_ixzd03EYIqhQFlRFMYFF^> zOHtJ4G-|E}?DvJJ%Mb5Ga&}G3EG-{B9dFwdv?yCl_I8L~H{^=&;&Pwcd^`pnHWb>Z z3Uw3@h`uUd5A>7(bsqG1$5~}s`hbJ<3M%jtsv)zMO=WSqgxCadl zC2-;18!B$k!Mpw3--An^jURpic{A+52uhp*gpJt~apmKodFj$m_c7qMOLBR}3Rh-2 z=xAI(l!r{kxjw9xyDMPo3RPjWGL){YBG)6o%E<48o~>}&4M07r)eWby_6n)<+I$>k z<88gm!!b3SzG6uK>dQu6HwVo>Cn{x`Bly|rxsLj##^72TPN#gg2l(!tD)RBgN?fmw zkPnZI4FnN&au%rlDX9lWx=ws$8oOT;3%H309mRRE6adslS0JP;{s7r$mjPS5sVz58 zFiGuWA^ZBH4W?KX!oz85Li5jl(|k713jUD_`j=n-y_W^uX58<*E|mPbeR=>~Q_32C zNNJD1%0m15r*!P1_MwPf4M~8m2^)lqpV*+ea4`+UgwG;`ppiZPw!Y(=sem0r@hZ?W zNoXgQpk(X^bO;1ibXjXls=+YsPybSM=YJ>+zio($D#`(-lMAZFg+}Di`*qMw z7V$G;AAW#9!ch*}um=L&Fw*1xfH{K-AOI6cwoZ6TG_ zLKfsUAx3+oe#hp!s=Z2*>=^&OZ{r%KWSHUtn|2=|OID+v)CLN#vZ(T2 zg`;2|_@(F{AR@Xz%NdakZ&KONL+5F(3gkTxF293qtRn7)nO9FOTBi$eBIhR|NP^pI zd3O2&#DQAyL3iX8i#DD7#%rF687D>X?P%j=oyUd)HO(JP8jC~U(f4Vs`~c+x$EeTx z@;`ewv|*w56eX3=J+cZls07#Z3ZEl3?a}s4a|F6PIglHqNy^TfUu)EhF3(&H9i1J@ zh#%9a(!$dcC~anVml5IgvDnM=*|7@---5_=ew;) z-a@Z*Uwso%aF=?~>KZ4|Kf?Dvdzk;j?c%5T^+e@&YH_wmnb zlmDN-Mp)A?$X&5@6J+->P6Y5S>W1U#D+hEW{?Iq})1MNF z-TP3G;~V->c;~<3P4>sUi3~sSrqF*9Z}LBQV4&S_>(!$zC17>Dayr;Zhmk8D_tGnv zFlCk7$1JQYs*#mgJ-xuzLFG>ZggOH!E z1qtDDjNFVo{j(byYt*nYp(V8=hmp%`2`jE6W9LX~1p&agSa(ll>`L!P*PK=psUfKX z(Qiz|5tLLOi1yquZ>r!N>R?rOzB2FJw!vf6wuJ*qIz_Zd9yD8d}Wk$bEuuxSN5=&32i_zxM8#h|J9AeA2m?-;=)Zt*is2YlObmyjq zJG0atJ-j-(w*t{0A9k(-Q?%bpxzeHPlyM8har5Ar80Y6mpjBaG=0-w-df+w%E(}71 zw@J}zn-|KlRHz%lw{N9Th(l+547`_}=}y&_w!_7GdH9ky%48?-d3;!8iSP$$o$hM^M_Cn<~G(m(T59mran3T87u+{X2fgc zqG~^&qXF9k%%0IIUT_O+mvaSvNL>xA1~vwCf7jslZ;RglQ^KzQh8XJqR;A}Z{XI3{ zP-8y8S&e5;P2tSplSO@MJs>i~l6U*ijp_8JO4KCHM}0Zvrx@=*Jsz6+!k%;gjXAva zs>0`^%%(LTAa<ialivqukxlO{Rz7)-m1=5N^?%cswXwZ|BH=D= z4x}4YXHXSqPFM>s3mCSQ@mDFxZi)NQ&HVt;J5g^$F$gUx zg?r0+q5Ya-Gotzcn#-9Y9P#<5hJe2`ziuU=cw*g?qD&p)c=N` zj8aqF2MVuA&6lIqd9r)`zo29wW5PgN0C+hLOF?^A;=* zL~{Q!7qR~=@BZ(-AA9%j#du!)u0KFvVD68}6P%CafjoT+9_>h4}b`{(QD%`OJ5P!JhaUK z5)2>(KxehMUhUCIQ8E^z23m)M)it0NCg+tv{}CfQpY^rSf^UDcxulx}VNn%@PhLDD zbHFjYy(2PDOJT@w^Uw$ymtXlmR`YRqn9Ip%yw zAxlQQFi(f~eHPwsq{TLkjmKeD5ock(@$4* zAH)2miXow%2fec!(IRnYTiF)3I-Bkh?Z?rGOV<@|@?2^kH`^t?0WjWqjY3BciG*{*}AjTZ<(Z5*H>hQMy&wIZ0l? zanvnl3rY)hI&q4Bap^ho^0fqyOS@a5qSL)n?rGG|{%#Gn(Hc8BzkO+C_7cZmPI6qo zt%dtG69bnRa8aYmpB<_R(&)Bl#!k)qa;7&v-(iT5RPx58k3Fs%b7tB|+(logPq@_~ zqjyI*HiDA6t1&6{>WBb(^W&HaoiBVki<5LS-MRQy?kV_o$;biT4YtXGuKGN5 zukTXJ2jK!+J92Q-)W;Lj4j{b=1I^bHdEE9AF1frnso3EYyafIl)l&Oux0uK}9c3t7 zCd^iMRg8c=MGpH2tyLZzJHxDkjYfmNE?Dhq`*YXrvxv-wr{TS4hp64 z^tJ0tif9XRC8xblCgjc_2J6#{R%;sw(E>4D=ya}ael7c4$t!Njym>dYlPbmza&w>2 zvaR+Q-%4F>_2|jW;ux}10)JsP8z!)`RXD-VS|>)V***1Cd4b#JZZ++{xh&+uA99kI z%~4tDxJFF9u33z8E;Md8RbsD{ZQi8~!gX@^FcwUemKoNU8oT1pSJCh%ZXWASVj4?r zt+G*Ry3xCVG=Y+A#d0S*)adO;So09G;p7FkOMd$j<+#9?&*M2_lq6$3M}r~@rdM2% zR+T(TSB;p>bUlYssm68WsddKc$mfegw4PVM{H2IBFmi~XgL8bb%1L(xqL(0C@0Aw) zeGYWPwp$7Js_xVV+yOf@>1j-XclsLKXX=WlZH@$bUPiaG8|8*jkrnIrgy~(6GYkF3 zp%G^$dhPT|i^2limjAw|L^Xy;Bpr;k>QDmegmA2+b1~r#w6d7|{ z*dlnSO&*JS$tj^G^#@#Qr^g>xl0Pu+I@w=|IN{D#z2~x{bgzJ3bhun#gG~QmvUDlc z7Q?!)ns1<&*Wu6Bv!M%6FTg1t!5br)WfSYQt;Jp2ECsfNm0JsdURQa?TN{5MrM`5q z_rb7)tYia1aFvkkgf9XAG$@JR?Q}`Fj5d6!??I(Kc^=sy~Qu@iwFI z=}k5nUtbqA#*ikUQ#g&FI&~^Gy4b0yc&RwRg=vNNaicaCGk?CpVxE2F(}u0ncg6G& zR?0UYKQtomz_1q_lUSr{PL(=1o%)?PcKwdc8{d9M&Yd43oCrmcZC;-be0MdHOEE@r zw=^>ses}p;r_zld-5W5s$uqtU|+NzUh2J(7bjp?J66Xz%MZEsy(cCho5t8!SLuyP;>T zt30y93aL^&zLMMz-MwMRd~@BON+FxN(sQ;IOrttq96Vv1<1|@x$Y^1|ElyJxRRaRI z`-&WR+pF~78r{}z^zWKZmgo3TmG|r%@wzXZ$0aU*b>e$S!{GMyx~f~6D(CIg6`myS zQBUv_lzMXTuYropz$>j;U)ZinAwc?Hv&U2TN=g5+@%q{G=|lPKO!)Er3y?VIUs60e z+63*L&w3W2jQf;xm(~5rNtgp>d?|J_FbFW>u}m;Ik?bB!DEq8eUh95(o+7ZmLAVzFN8yv0ZF7S)JX zeu{l_zo&9DI1OgntZjhsRvqhXWb%|bAIYxu6X*V_1@_~@_=cJSJCoWW6Ad7h# z@g?E~xA4NMu?9wDZYjEOl|ek!&x|F$n1YGKWxYxw`ITnwoWO>H#@IOVNTFTfw(Yap z%0#ik?`oLNtEAc*{r=)(LKw1k_>RAu#i_*x*baXp``vv;XQoeV!dr^B9d6NgWy-(? zik7HRnK0s2v19}Q@Hlxrg;F)>uxH{HV2cVl=7b;O8FsBfYmKbfA;RUp3oNFmkPXok z^ltsO$d<2TO8)w)k&!!e;|M4K4j|VFu*JAFz!m;cj4oPrwbd0*&|gP{ zTkT|1^jmOtmZC@oY~J0A0n2?DV7YH20p5UC1D|ICk&&r!t6CjyNOW;< zy}h@+d%=y)cye6kuKBp|vUKV`(V&&i?1YCLpdm{5j{iiy^sP?9m0PsW%e(}z)9vAH z(tlvQcKv(2`AvIUdXcWyVq;&2*e%}lvWjqMNp)F(v}(Nyjh&c;CP)xVaf~{QiR%k@ za#LXmf2K?Q;Q3SdTU;)&{OnaeBqt@ zA?^DN@u%!TQ1NAaCSZs<09oGz2!|AhUB8EcGMm05%>fTK)=J1JsR$wRO&H|&PotAX z{!!uYIN=`&W$uw#$4><|z7w|}LQMh-&7LASsA%fGo_LaO0^w~5u~9Z|KM`fBrxbVe)L2MDg(Um9nu8#V0rGHl0rd%Im80ujy-Ia(} zGjnd@^7FmJL;Z(ix=r@+i6J6*-w5`o(Y@H2Uz{z_t7M}IVeFx!vLsRng338eECm#- zi6>3zucd0<0_lE@sCy~4dQ1>X`2iwFgdkV$K(=uRv|Q6Z4{zvTy+o0o^-DuT>&bNa z?rU+AFZY}0YkzREP8ZVw53B7B|XysxdKEo=*t*tJSiC-aQ47)e`XDqbKm~`EpXOnDO^g) zzR~dg+vj%z5}jPcLtL?PP2=gGx}Lik(L9Lgl6fk0T;!CJ&QlUHPa~^bb z%Shl*!~c4ybp39Qs>2nzIR@(s4oer+>~1kI^>K|wb%py4JW+FxTsJg23UXc#UtNmQ zJ!a|VzFRxT8UT9w>|RjtE^Hq zcf(wp#HqRXQTCrw4E{WQ{tsyiWMGWom)7P+iaKOuQ^R}j)PC1JMppP7J0I3_jAe(q zg)bZH1E-m}erlfWaLz8DTnav?+Sh-xNCU+n=%s;ewLp8`WL!=3e4pvGBJj2E#Fll= zW32yhwEx>g>;K{IJ%gGI+jY?(O{GLodWi~%g3@~nh%^yFQIHxGktR)g4TAI%ihv>z z1e8uhdJ9rRk={E5lnx0c1PJlGelzRrZ|$?bz4ktP&zUvn2Q!dKfV}UM=eh6ezKWyE zUr3WLjxDEv9TapWI5qm_fA~fK7!ayTp2c~#b$%*)-8}pP^B8m-akh_azld8n?^wDt zaOK&C{B{1o3W|A|rRIl0*~X*qXs9loSGwf*YnRhbuq{it>D$G7T%<1LWoNBlBe*h$ zo<1FjapHKKse7xw6Iox2Y&$B{v&N$hMBZxMz2z85G(iCgM?MPd0)Z1Uv|GDbU$kbS zX%O6HizW|^IrHNZ4UR{}LT~1PaE)6Z!v59P{;wYQ6~!z7v@S=Yg7Pb>zW(>8pD2^* ztYlZbkX8(SV`{$k)Mv2-Z}F%*JA8I|>O+9&hZF~T1E&jk1e{c`;1W^(=$fBNK$5)B;d#NpMr-wdV>|yO zLj14tSOCYrhBnb`0Mds5C>5KzaD~%cRe&t$t_v_uw3`no3?%=JSNI?GmRQS=RBzHv(K(AZYV^6_I~yP9v0M%=A3&cuWd zUOU603d24}N ze%z~MnSM~UJe4=%9NPaI5s96XSmBVSd{Y!;=WI<6E|;T+DiE4!eENGk6&!Uh9<#YF z>N`L5TK!C0E4|MVZyevMo|RYkc}|9WFg2&yBoyCmk|_<$?kL9pt|*p0eT?IJ8ChuK zl6L!-Jh8S*%I`bN;KtI2RX&0km++JD+#n_6T(hR`y?5Tu9-k?0D@CGR?bj(Ff5rjW4Z z5Fmypy4#lj-b<<-K8`hTK|bQLU4*Kh$A;JXm--#ELdXlI3~!9vaHBr~m;xl&M!+{K z0aDmSW0pnuh0O()?C;+jnjC?=+k$IGk21;?BOeXc4e%eZ6R0SsZV=ZO*~{qFpQfua z(nmSoSQxcED8s1YWn!=~lHNHRVwU&v8GdC*Kr{_Qnj=xN4QH8+0s^5Lj=qI0?lmi! zUyL~pZKfrZ!-ZS6JT3f}(qg60m;8Q)Dvhm9!oLs7=JsxBqdx(E0R=wC>PM=u;*yU| zJ5ltqR4>i4C3NDao>CzSJDyLHoNz1$SjcvG%_}(??A#E1x5O$)u8ge}N?~YRu4mh6 z8ny|u3_32wx^j5CpJJ?ZoP8C;Q0;jw|(Jdhft zV8$w-bEjv!G-rl!bGhxVemz`=&#LFg!PWTlMGEuHUx>~>V2*52_73J+zvcLy<$o;t zbK*s(+S-jD>l6{a>`k_FV=c^zxsTy86O@xXh6wYMiASdZz}PLV50rs1K;7vFI?$tQ zH`R|Su{_d5$L~$?i%UhGr}#k?ue#N?U#hSNp2CoIegUfCu$KUE@W>c7NTbq>Ed{sY zf1J$cvLy9uM|NG9)s!5BJTf}SG;OmOxi(ta`@XK?lZtr(BNtWS<__SD(*d`KAJY)M z$qo9Xqy|I?a!9CGuVZbPplorwra1R%l2I{VtVM(2(u|0rWTU3LjBu@Q&A zu4=p0a;n@I3k7ZlnWoVen;&@y;~@d8jdzx83&M3wEy zGutIS%e0iGNO`{^rhHy`a}^%>2Sf?To!g8}f4}wh(8xJXHZ%WTy|VK(n4PbT(SN*V zYZ=Sc+rVIaw>Bj&WZ>m^OS~7&Z4Rw!Rk|c*dX!5&i&zhX)rd)Fz4{*Wh-yg4Hux?MAUetf=g#wTMGTfPEOw6plR2|74_ABsH9}#PM5w!v`vh1 z_K<&XZs&-RT_4rF+Omf1a^jl*>9!isj)~XnGV5?qv(r3Q488xe;LC?F0|^~D&{HQ_ zvYHY6umyU+1d|JJAjSojZjKNQwNo`lw)6IEnJ$0&SP_!X^MqG?HpF)s#M@jP@Ej+C zP*fAC*bX*~tu zW){oYJ3phjw!BJ;_z17AuAGS@3=N6-1dnfy7X)z~XRQ|50#0R<5;|M#peJWD39pKH zZ=XCs@QQY>PPXOXSd(>a1Du41=7oR*A8H0H^9w+)YGyzMco_!BWBorm9atD~b?4~q zl!sFr%MD!hE{Su;4umv9JqT%jqi@VVw{^UZyJa}boE;okk$!C3auVObGS(lrwAgxT zqERs$LC@x;OvK@QA45|TkNcy>hDN?s)-Ap${p>D&;Jt(dD>85a;2$94 z$g8RuPvV$edb}YMd3n7qeD?HJL;5iSo8R&Ye%@}?VsIxvy;Jr{ZTMoiT@5!4J*caj zlFBx5q9!+h3a>HVv{+XnYGZFZ{ELaNyjxtX=Um{owl!*yU)A4Y(S80Jejq$R$jeco zqp33QrFw5S!Xlc^dVz(u9mdqR1f|%h?&Mw@F*CW-yd?J-geurcKb}nCD^vZ&HS~1b{svT4f88Q03>$d4 zeY{U4rf_CH=WK~9pqspxrG*vlUp>nt@d%X13&%aa<<^e&D<7y8M*OlAE?KW%Xc>rf zqjPN9yUS{4V_r0SSBKr^K<+BSS&>GzILc8GZ0uawpz#AE$)xg)zd zz=>*cmoL)!xlS^4&W0YU4igSg#iye1(9Wowwck2YOVDKN*AeH8`jy5{_TB`ok#s;u zbh~l$=gKqM&c031=TpthG>D!Z#zNYp;rEE8i={zJceV~EA20Q@DyG=!s07!7kXDXS zgK%5Di}M|f4Fwi+^Gy3OwoL8$gD;tb@(|f&-dMpO{(+a(YsUkmJ>7*&KPUO?hHz9f zmc)YA$BGC#2lvWh(~eb>uGKrgLMX2(;}f*X`LbCiN#^)d zAFeiG!u96qns$t1t!*zTVAvWhA-<#41oE)FhOY@H34z9|{QXVNcf|;-@J+l3nLO%v zQ;47WAu7^VXnuWlw<_<$84dhC-X&sz-V+w(?akR=7$~IxPrp~=vft|_=vsI7?BHmX zx7h7e(dVwNIyakNiJ^5=Y}f2*TrD*GCgpIY{-rfCU*)M0qF`peRnGeIO5wrtG5zvV z1>&jskdhwGvC}}$$x98YTs3MHxpRs4Y1&>7(t6Et14`qQGt;TU@_oEC%{HSp_4aCS zf@|;b!6Ca81?%VK1-f<9p`qQX$g893*FUh!Y<;8FqT~*NDhy62Y2^7hW46=#KT~IL z{IEAmRf(p(9T+UV5AG=butip|nA1gyaFp@~&}W=q7Ce4B<3dlYSdNHvu3Gc zhCX>E&rX5gK?TnY1zq*=?@W_*wex=5KVDSICv`iNDnYy!tZp`O5|))_ zEAZ=VRHk|;wy{D^12{Ekf4~@Tqgh*uHVilQJh>BSGUu5%I}1|`rr7(N=g0rN;YO&GiI}53cgCv}TTSWwj zV>`vyrW@HVQzTPTa(~$I`bLFt>u^nlRiNH2RWQw4Zg|VGAAd1n6-U}kyAhKu?eX9x z&0O5C&l3k`$EzdDDQv|jd5NR8#Blj#9tny=J(d=dAb!MWwe^ML_vUG@*Vh_A z)^3;{;&!bqo(6E$5st@;wnHzh!=@FU?$l7R;oG|gzr79@PLSipHgN92xB{*WH)@nP zp!twbo1%S_`^2x84L$`?CL))z!8}A!l)WW4es|_Drfd5Sy8HSCZf05LT5hm4?2!+= zV;USCG;|hKsNqj=Lc650zn;smz3Mp)7djSQLWY(GxMSP$Eavo_*rw*4@E|iGE$%bN zbwtDh!)q1Z%zIO?*}1@m-i1|aE-&X!aq=;M(pJ5?_~k=z_RHkyS_{}R+uKV2@R~F; z-Kh6VspB&5!9?b`=2BndP8h@i_#E8AJq#ZHvktWn2gz z!!N$Y>oYCP4*30Giymv$;yCE0s+mB10ePDWb~ICMPTX>IEXA<2W!(0@Ik=+|{0)SB zJ>djX_E{zb#$z6QdW0*TYu~+?<}Vy8DaYd_Mu~hOf4FT-rVO}OH_Fo}Q2cPXo7-kf zAmLXuYCblL$c?bldala5ZNhmJEd`}FqlByDS-V-g@xE`|6D*VMis<2`O?*n za!L#10{YlkQ8-IIaMEUfW8oKaxWVaiJEvCU7J4doanH_a~^k9LR#Ds5Z#F-x_HuqgAPUM+YC3W>q9&~LdsIA#i zZl=Hdj-l7TT~^*1T%njjO<5>dWq>2sIAWF0VV-@LuW5+X&OS7p)324ModsOCI7oqk zSlH$6B_|;rXhrF(Jx;m$*?vYww^7O;ytyhLKm;D$W&PlTscD+=py3VIpkBJJ=A%I_ z{Y6Lhwm+aNN<3Jz_f;c@1?bULI;xE!Nr+l42s8PrakI6#qB=urG9RzJ-8)|&?&xTI zG0~Q&Yn7_9Ou0G!35%MV#+<6?BIsb|UskNX&sfO4Oy&s%T||~?jg*310WuR{=93R} z_Lji+rYBh#veKRA%_Kkq1!LHpE-NmAp$I>dbo?FJt6sh5A>LJG$jb8|3Ge7ah`4G% zd9F(e0|k7>Et7S}kY{jnKuynVJY;zdh@=M7n@d;cm)@<>ZBx5gS(ZN-Mr(h9MeBTe z+B5_>zrpkGzi5cZE?Fn~m2LNEAGL7t9QoOU#Z2`xdpJ5&Wx0I zF#zfvRi14`=Pm{8;WhGvqnCBC^PM?1@<}J{a`diKnN?$TJa*h}HAJnBKPprhY@kKc zXx0v|_?jsFF3HD>yfJ^M`B*p~d>Q#5_~M!Gi7}ESibv<;{(w56lL`$wyr1s0)YR7` z;9YLGHTqc{VjgY2RfQNoRn~txa^QHQadg+JqS}(aqKnCWeE{xIt4%5R7RKjr5?+o^ z#(H>(J$yW`W2t!WML>g(F+W;an?og^65s{(Z!RN)oqpY{FR54#88YqC`mI{Z0Ku-l zJ#2VaQ$6t_+FR~Poo0mK$nA5h__7xmf*0;RYfp{0U*_oKaUOGtwMq{gqCI_oA-b9N zP5?)4Sxv5`zH~yObrx3=9i}0d2K3T4)>P6k zt?Z-&-BygfO6KHhmY6T5&^)N0%$PKaVdyWUuf@WY&5*9fvG9qxChUx2WdLdHpl0iEiWZ`D?^GciXTCVJa70Kg0o_kacVq~ z%jKvdntYn7#gQV@B&&vU$s2yb-5HP|JIddHYJZn}-wZzSuDQ=~BmLNn;15_mq%IsT ziSC;op&%r+VG?!jSI;?WRPpmXmHZ(s_cJ?6m?hpaY{_RS(8VyM>w$Kx_Cqrs zSDJ^)>rxO_yP%sUpER1y+TXq9(~M=&S(8xZAfP@eNiV^eNp|=r)8plC{C% zoEbHD`8C_3r548P^bd$gv?dN)1)60$ly7G7j#NF1yyfkj>^Av5L~GHflygsXYQNpF zka)v_Q>(8tP5k18mG+hI$kR<9Qt^a?Cd)@G5A#HO76d)CtF^wJm!ZbNnf3nuCWAQp*L{ITAsvY^? z=5o;>;djKhsPjvl$_)X#;TfSreqkiPGnJ}3ES$M5dn)BF%Kz%wXb^H6;<9RKVryIA z6KWDYJ}obT+7*~Ck{7{{4!49me&VB<2x5LsB!n?V*qgMf)L!cfqVi%Jc9%_YMg>z4#;x^J`Z5O&~*{-qYQZ+lRWhUhi%{ud9Cl9Q%<{ z7z~`#7!uE^>RMX0!HiGl%WvqX7mqG*_`HP9xBhyMe89A8iOeI`wAJqnecAM4dpO4z z?+L)C0jqp_D8&i5Et1YbWN+PDHr+gKh^28df<%pWA^A1_0U4cT20XH@JLtRHdS;xH zD%J(31jgl<0?M^*<9ox0GafZ#m8XttkB*AW?{N!T#RmH?rbjC?_C6de^O)o8pYN3C zb4_dwkz#D)z@km6@Dc5>aMK)9Sg$`P3TizndHeBA^15JtJ?Dx3k1XkO{6d7`lS;!Y zMe`ON4cs5{cq(RjvyXU>uTbn8#|Lo{Yg!e!{lXgx&1R>STU!fxMw2f6dKMaPpm3`& zfCGo=RJn!QbMO<%cX4?&EOCCNK1njccj-y=Ve|ip?xGMS&9n#$AGFxm{@wj$eTaXC zOXYQRbS>2%kZ*1GAu8(*0zsB>WV@?4AkulAF8BV>^bfGcv}7&L|MDhG*XL845$~PB zU&GQ+*{!7S6_H4L#fMtZN6-9}5urh>_J+@H6uMYn8U8qFX(9TdJy?#GD)#0g*c606 z268>-eDCzCmDvwJpz)GW2nm!7B^8VQu$01!0yPRx+ftK*{q)b-($c=X-Nw7mf_cDl zoodS=P)bWo0m%)=6H?u0WQKt)R@b;AKU=XKqB2Pu*f2bzgJpuRS_W@!@q;fe{+ZCW zfqq@59p%>aaU0gQqtw<6Pe-(F+1KN#?no(2RU-^Sb>j21ZzcGpMvM?EZfOXT%I;jTb9_K`5%N0x7s81_e# z03p?F%#+K4kMOQ;)Wv46)Tt`;HY&uc;B^+QDd+_Cc(kN4P$5v=pe}xXn0kT#J2y3C zd3zphj~9&#(8GIoxY=c~>Ba=A$ll;LzLg*+(bJYq*v@#P=ibbWzOg_&dc2n$SRF_C zYJNhgoP2sxQ=>NMEw~262(v{Em`*oD0`?rNqw zD3I2V_JY`{t~-1;5915zQ86Tn)8eujb3{XVmGD{8E|(o2wYZ7R^3}gex<^|*w3khw zMW~tAL zf5J0KzBfp#zX)ala}XskCO69mu8PlUHnD2Rz4qtLodiOuBm$lyTg{~IFbq13Xrb-9 z7ziH9VhYa6zH2HuX@oakE)m|@S!_=d!1*amP=%Ur^on(Jyk5Lg^NIRX6#zi2|3<{g+&e=7l4s!dX zaBE)4SY3`Szn8YW*;sT|f#~hU(c88a^>H>eSu)X2oMTJGv{vjWf9*z~`5Pw4N~V@k zJCCRLc78v19!@IYlxG)Fa61#;h(3laIYo`A$gdOVy}8VaKj<9JaEG24Ph0^RxkO5f zZowHg(m!;*-WJHPt$T7CnqqG(iD#BcUcyANC;-fyhj7q4!5&o?|)%p^~?eK`3CQv?_%zOi$87TV?G9GsyqHW=VeSMx^ zW@>~Z^_H{6jRzqmeu)EhW$J`bphg26MO}vAH&9PeLTA}PQvwLIXAWT91r&fg(D98p zK>H3rmHb~xcEpdKi!-``f?76uMtJ)07ZiKf`z0vI*VQPM#R3Z#Di%n|ngCRJ3{Nrw zs!{xC3&RE6Tt%;b=i0%}3sLu49aLx-1}BPY6;p<(W7V$kTWREPf6ny*@~ zS0`AISkVX-X`;oY@h#8m;{`vI$KI8ERmx!4aBETW=H4G~fY1aDR(nlbR3ot#DvP>Q zj_eIp6rsUlbhr3uCpb zhOuPzbqQg3*rUeK-x7SxVT^eli{Ah{>_etS$a^Cl3U_BL1}=WOt{s0jJ9BloskzOP z9(VK(J?z1w>_k1jAp5%Pl(6bDZ+*5rGTtai0NV7#3N2u*U;gWU4D$Qh^x0uvhzPa* zx-P5BKpIbt|K2Nnb@7_K)rB>NfU)J~3yx^;m{ZB&W^I}CjnF>L_*V(kvzih=ylK$Q zxEfM4r|6lF+Fur^4N~t?0~lIISw*&^p~HAzzLpf5-lfL(e>&G#T_IyC|13<`53Pwx8v9!BtkMp4ekzQQ^az!G!p_Vl^jEvDwGJ=7BU}+iYl5Nu0U{{;0+2F4vm#z@!CE z;BD{BeR%zvTZmS{VGFhiL1vqH3tipY1?(G4%Sli1@TH?DCE>#L%jR=(^))Z`#!L6m z5^+Sazef|G9L$oa1f~dA$4b+B(o`&da&$+ljrzx{(GEuA1`z3CzfkR;q(b(5tWT-# z72SUVFGO@$$PL+|=jG^9eN1?se?qIywVj_NJ(U>AMSht>k8Nn$Ey!i)&E>I1;3{VsQ zUHkLTt3yIW_Y*oAapYnzUHJyG5!BYY%DH( zrdGJVZ)#2>t}Q?9s>!GZi7b9H#qn6?l~oZ1e{%3mN|n}59+^&IUDwu|EuG8B-Q8z; zCV58220iMxH~`|RuJ-f)mo%||dDHHA=fd=ti_M5jeMQ@^l3joNSn->%G3Yo#41fLn zm;L#FoRGwV33!Ni#d(J~?^?Uoi~!etnvrZxxex!LX@=vinw;nllBxwz$xcOm`l&9A z?my(XsuTbUsx^|_d z%g>g=sN8a5sSU(HB%%BN@?Eg}THurdSJ@G!XK#JH!`1U49uH)Z0MNx}&O=V%j;R;f zr^&GKiOt>{Y95TW-87Wj7v_1Uq~10W_jF`$!w1}23P0uQWHP%?tRAIJI93AEzmM^N z1Q(Sfyi$6%XH|1-=k~82bJL#&i5MY2#BbrTGPKNXKfUiJ>wO|QFJSV&6|Idia+IkY zC&tP`(YFdAI-hLD#HIO+LYl^#b59YLug?Di%un*UGWj*$C-vD&9X_`d4=Z)Bfkr@6 z84+WF=VB^Y=o(CJXCfsci03yC<)sh6!N{LD-q^r9?}NL z{6^st-C4JrJ)b_k%b2EDS97Qpa7W`rvi$+Q?0;$R;s4)w4nHv#mm*Z$OJbiIdN0Q@ zt=;Kh+J|TSA`-WBx_zIWYS)P=tyhpDcxC^f&qA)p@KOAB@rBBisa3H6#_zh3^G*)- zFN~;{FHz4*cJ=Q_Zd!h^w{?2{I(0lji;5}swp)P#?Qmmr8zsW8kZLKI z_B$#{NLvw@H@ziJ6dRYO_liA^6-V_sxwNSx84ITBwSWz{)yr=oyzG$bE!(4t$e+nK z+awBlZymDt1Da&cJ!rUJUOFuPV7ug=w;sWs?X;y9LL`h3jhl#Q!X0SKNmF~txfq=* zwha;~kBCXs0sD)>T21)*IA;2kIurdN75Bf%B~3~FPbH>t#U)R-0YO&H@#IQ}04L6E zfqdbR8bG&A_1Z>*Jn^&zfbj*;sTlXHV&-q)Ry)$F#tyeNDrfXeac58e!X7orPamnK=!(ZLa>Sk-lY}7LcA_B#wYA;Low^T^&vcFE-j?_QG&ALkP02? z8G2lr3Wtfbm1Seho0@B0c#|u@eI!Q}IuXL?gYrmZ)}G^kYBzbQr|oL|F)EhC3=pE| zytOP8KWLU@SuPx}@B>N>cU8!vfQLY9WrGzx*@OSG)Hsy_;9cW(=wU15>_YKGkRZHU z0-#v?LibSD&(h&5!dS@35}5pMlp_BW;CFUOwlyCDA@J0~--$dfSD+U>BI3?f4k#rt zk@Qv-R`(2`7nlFRj`I1-jsmXr7q34cDxh~uSc4G&a9FEbmFFw~fxmbqZMkDhBs+IQ zW}|KN(nnO8@Cph7?+&U1>Y9|Az4&02suFz;PyiSU(o`F9miz+>ny%ht>xAy{JN^L? z7-|9jmXj(3`VV6?r42ZN;jd4cpWrqf&=QV8H_4)B)SLZ4OY~PU;Rz@vY5>JV6C2?j z6-nx`BCK71<$%JB;09a@UpQcTX#zcX@^teMK)5SnC~>C-=6lj67a)WlA;Luv4lxZQ ztD$*-*%oko@s}+x=97}{(5*wsh@{4SLTYo^7aSp@gubid;pXhE=^{9D>9O%=(iPx$ zYkUqA1pd)IAUudX2-`E%Kl9Z9e6T)`z*ks+?$-P=Kw4wjJKClO3Jc&yOuUEn6|dKd zJRWEpvd}?_aH;WG58lw<%ckcs9r$6%QfJ{}SUKczntLrp~${kNy+ zG+m3)gWlJkbbAU?tfxW8>w#I-7VxK>`DF-OU;z0g84S-VuAs-ks0zJzb4cg%hkXNka0c*x;9eyAO2(i4$ z8r%BhCIR62K&x`92$a3rNOH?C@N~C9+R>`L{C{`dHtLTJ2_e8mPls>zi2fd>IB1;< zjA+H~a_rr1ob}s&07ylS%@*fEHj%7S>n>3LUs7p^hRpt=`^eL^P@?hJqv@Ch27z4e z3tR0kPn;jXO^@}LM6#8n@s9d{XzobyOHz-;>}xM-RVYsBBIx74@-Edsf5nXsf)INEaFJWP)`ATJ*Mt>ibNtWLj+Js#ZxmK-W z^*vls2_^5UD|Qn8MkNOwQ#UfBLo}302iE$?Jv*+A1Sj%)ibZHeN1id1*(Ozky0r4x zCN}G*$z^DA`bier>MR4g^4S4GxR?w~W;$T{gS23LFeNS*HbC0bKVwf>g;t0wDm_$u zH34MDPUMn+Q#R=l3Rq{r+8m0pbo+0{tRVkGV6S86mBvjI8(L0M!-vnO{}%t%rFC&b z{g%oUrOMb|N8D~K84%~2&oq@F_H?+rOzBc+L%?|OT}Dm)T2zUWI@W@(wf@Zp=h*6$ zld0OJgwEalOWN!P`l=2*5Ods7yp*rA)baDtO8m8OHGV;79L3e*w^I?$2NWyyTHAsz zmW)jYMXo_R1Tu0|0}0i$L9d!<)$qG-NlvbbrWVtBewwWFJ3L8epyxpGs8dh{51pqO zXQ^3pGwt%zdD+R_tN{`IExZZ@*FYHfiJov!bI7fAF&a$JpUTL zpomyfg%}vVjJpwqO;pb|$JOR(PO>gCGSM_Cph5TMm~r*9ipwX6o+jfT3mi@)ckGj) z(n|U5?x6dGN_VFzpM|$A zb&q_EKo-nuaS8?&sF$CdD+0nN%Av2CWVAha+zoB6i8%9xUD*9q2<(%Z2DIq`F4Sm| zO%Q!j^&))XJ*p@`K38pNm(hM&qnLnkMf)=-Zv|NzP=`;jT{g-AAC)#AJn=lN z0{Kp0c<$29QIVf(lN{TESV{N45q){{Y=;G!Y8!7-McuS0s#{L* zM-Cb{2H20l9utj=&v#lb^wv`Hq_nt@yxma3#0#H^w(V|?GN%1$_P&4*;%!U6m&dkO zmnSbl#Z|cg8=T>3KyU92BQiIW^j`TOmUmJBx1SBuduZ-DI$vj8xMSG0XYpL@Tdwen zhs{JImGi(hX$_pd%}>n?aI|gPq7UmvMFW(-wmCX+>D~_K-hRpB#@;jPyEq&O zfrP#m10ikx3nxdwrl2P6T;$NaN81Fnz`oFf%c>BupaH-*6ny5>OBzRi9(6{yeYKcd zR=FoCqU`k-HL*NHLRY>*PVi~RrGYx$&NW`{Gtjwonb@=Q9iRY|dq;Cv zmk`-?N*Q)Tlghc9cV(g+a6{L%UpTlk2VcAo1TsnghS5vSi3L2a8EVwBrN=AwIt)FO z$T47q^7JAKQD5Lo&xm~FusB8XnE~ym|Ng}*WbTO2{7&G|2r5o3PhMQdKQUEK$;$Hlu+#WHUh&o&0i&%MFPxV7GC+jD zkcX$=h3~75s53ERZf_~;2<7gzkT#($5Uzt}TH;;IJzhv{%61!vfXSG8h4>3M1bn!v z+1^RIYfxH)Bi6Ti$89jPq`A9UXf23^sNRwV>@Qyy%43pDm@8;rqLnq5>L1dW2l{Nr4c$(p@ zEcwUD}yK^cdwMDKq#E-K7CMA*xE~yU?9zDuj zn5vPKpd1uUMIlbtYE~@`aW6VN5iy(|HL%9LfSIpzbFE3>vja7f@Y=!!wIv??)(P9z zOKq&5d@Kchtb9UM=JJ+;0rPQyr!3G6(8z7EbMIvTfa>OMr%LQBJAML*4BUx0_(zIH#`BA6eQz-cL3N)(vWLy z7`s9Tc5fSq>`F((63pxwFn0~BIeP#u#K*`;o!0VN(%t@ zIBuR0cUw6d71%ms%uXXNT-@c6uVhkjV16_Uyp=9{HCT zZ|Y=dQfZ(*OekoOv(gmNj;OfX5*C){sscsp%AawATZ2{Nah{tF^Rz>vg2(gNR@P*t zU(pN;WscVm(L5xTI^CH_4~y$-g_AGd8okIbW6{K_grtc4!pXL*IrEB5aK1`F@-foq zh=~Q(7!N=g8*j;W=yO~2mBIS)*SS8`sR!-d)uvEWhGw(#6i`k|gQp=agfFSX4EGP> zg6Z;#zdd4P#UhaI=;NB$AMM;FETc7{$Ok7)wf~n99 z(V?XW(Al>Kxd*;EF0);JC!=87>e%4#bG6#U`I`1})VXsrp;i=I`9Nt1o2npdWIB?Y z+5Y0G?LmV_yYBdlba&z2jxVBV8zjY{iI5ba3a_vw#VAai1&IP_;}K_{OZcmaQ4CXt zzviYid*;4>d5;(j@}M|?hDc3T!YS>%#BV-gd2d!e7aeX*EEmL=Zmg2%iaJV(Z1Q zWjbnUqEt**jV)5cs4UY0A&bdmUY`yn2Gr~(&E#w^LH=5}(jo_YnJnZX`2Npv&rE%@ ziDWbU#XgUT+XC12LbOAfs=zjjC>}BY2{l}DYdt)M>m-wsMMGDTxW~X zwW7j3R&SoarXj_TY*C<03RXjei+!C|<6ee8 z0kW*Fy^p;VSgs0zkc9C*IG1=Q7uz@0vVfX0=q)?q6ZxDfR5j3XD?$CH&D9Wz^SiJs zBvoAOJRLr8KDDQF6e%;TNtQ!A^iS+kS)6DJpeF_^seJ}BZZHGBWxWq$&&Tk-3U&pL z`OwbLj%VAJ4FLgA#{F=16>zuB9ZXb!^lQtZsHB~K>F#$&9+0_19(1NkY)-HW^N?q6 zALDL+75x1lP)QVe69LDgo(_`q@;A%h(j9w)0tV%eBSuDzZSh=-HeZ|gr)$DvjZ9-~ z@~Ze=OF|xy!SCTsEX zUAQ>Cb(*!L;cJw#y#2WSwzrzgb?$&w@E=g1H%YDRdoO1CPS-ix)Uh-EyIQ~N$SDU` zD(ICTRtMR}7*d<^h^~1`83ia166I)=9}ce8WMq@?jba{uqr5?w1fOg)FIz_0t?eWIssKE^6# zf)Xt=7b*(r-|`0uZ@}IG%1A6u4@t=S%534aB-Z|>x(EJN-+cp4fw6Fj0I-b%7YZ|9 z0O0?!kt8C}l<4YqHVSENF5L06u0=6a&rNL#IQ?}w4`l{JZ<{hyj-C&WKG0*M9gBRU%=2=+c z50W4zVxiF!VrvSTY5YcUS8XbSKk}zU+}#cqQy>7mVi3ZO_;e9WR3%9a&VEPd`?Q)> z8z$s|Qz&-W1d)&JD3NDCkG+yH4WE1{dd4PTK?L9h{`v(n8jzd~77&lV%x=IK`2FqD z^~>k9+^;@LxqW8ytYo;a9Ai<=5QI;tdJ_a?ajr=#X-fQe7f2M^4sl1 za9IL&{NiOF$VQ(F?mv%zJ}y9cyKNzx_u~LCvm|M8|EbYVlE2FYa{2Nhr@s3is-nm;DaF%w|4LHr~pp->m z486UzMV6H=jlI<3LO&i^uKMvj;}lZ$fi*ii+om2 z=t6Ph1;U$`fU;!<=945#9uIpa?2EDahqwfIFYp{VCX$$m?>h#vPZ%;q) zzrjt8u~a{mc3{#dc06$xW1dx~?^3?YN(*!`7YQ(0pSA};!KYtfVmhnkaKq6ye*FpS zwI^8=eC)3Q|Iu3jj;8`9&MVvMm@sC2PphmKGP#&luCp5&KeeG8tWsWryzCQ=X$^wZ zk5@s&rf0Ti%Rv?~Vg?}omn-pyzZBrx0-u%BGj%maU({oMR?qO?4UCaM4%dRYNgCs5 z;RRDW&Kqe`URlm-+X=f#wp;yLAdo%}Eq~SrpaE=AQ647w+k3lzF`MsTsVH%F&V4%D zZW}AUex?yHgpGrcnIZ9};fPgV=^GIM3?$KT#g-eIA=b zUtZht{r~|;FGcgpuRow21t`#f9HOp}xQEKnJPhD_RobYMedUtvxV>J)2Sx8$8f6-` zZymnK76?!G65>+#QlLe;uyA+v)igxKtVhd$>SW8oRsV^Rs(mD80TdXG^J&(Lx%99_ zbc}X3Ij8%;Ibs1apAF6x{-qxae{S9L>j&uLJi zK$P*Cl7QGcu%JxjdS!L$Z(mlq=j#0Nb1bUef;dzb>CWlBzUN;$Y_WGl%(|Bha&|6& z>_LD;jGV_CD1?k6-y()$Lq)>^?&Rq@F-*DA9|!(T zs=%V1->-RRa)Axh^kg3Wfav%olWln}_5Dlcs+760x&(TfTint5>oAi+3Ioe&4fu06 zTe+XcOi~Y_Q&wtc4EjJ*;JMiM5Ec&9sX64~pZ4~y1n&$uMNjGpBNq-;&NP0{Sd%_h zq9p$+;*q5+K5gk9S->58hvw=@&Q-Hd&s%B@ zarqx8Ri!QZn>3DCSriM$`eM43{ieb#@6V6@C;M zgpY)-Hc`86 z>e*tF`5-=`*I8YjpYvA<+@6PGYjA^8CZA<|;iD6&?y^ez_lzKxKA#9IZ4_GcIEx!s zohVS9p?RABYW8PtVpDKyETUcl$e#iUZUOKipGXY=)!G9H#zg&sPjj1*=_h;H>9KdR zc}3Ed6*mLEv9qgWBl^)WqSkURm|}Bcf-|LRbjZRi@`+j4ZEi8XYmv*P-VlfqLjiEp zwU!w~I0W8x7JC0WXUeMIAjnhxHfttujdH{j`OcDdd@c{E9zFkRrQ5FaGSd9k%yIO- zC|gi}ucNi{Zn)>IuRiaaCwG3_UZh{LP+c8lB90*#$V1~p>wq_`p3twIT!T_Ko64xb z9&wBOHMfIxT`X)`q9;(u9}c!RcJvgwVzPU&Acl`Cr0_Me-GCZ0b<|lOvnkLE+fAf{EoOky0XoA(Y|{${aslR7Hj9hS!Nh}=z;;l&sK zwJ2y9wkKov2Xu&qAIOwKrvNOYKCJq)X!PNEUOIP*@IHQads(J5%#o{@vir6PvNDYn zmkvGiL7p)bP%bpy0kHk>7+}F%g0EGfkZ>Rlj2nmpqeYytA0DuUHLn;9!8cpMK<>f* z?lvE=y0QSI#0uD`b9sYR!f>mk7*1eRq7Lls7xALgj$|8`Q~y7pY3Xe=H^Aty)&bzG zzo}%F0;hWLpQQjz5Pqr|tSsEZikybf928%RowZjnhHhVjjH5k?x=WdC?P&RA4g3`e z3F*qLCyv)*kBgoxBM9nv*nN^D0E*i#Eu7#H$Hp-vBb-PDP)M*LxqO^#SW&Xm7E9)z zOe#`uLu(&C^n0VWod5>@dKh?7^n`Zy4`|U0h?&%a{@9JE9^cbfevj{9PV;ueP&}be z@?DSqEd`y{dMKk^Pl)qMyDv6iT{SdNHf|$)&|L<{G;<5$ozny(NU~qeTX=SNz9c=6 z8oouQ*65cU+r!Jh_C)St7hi+D*JnO?s*|^tI{%&rl(O*$ zL`|Y_TU=o5^De~v+DMNwN|GFBpHMV7-P1XFJKdN|hyvaz0yr?OCYulBZ&I$$TxD&Y zWegFZ++$Hzw*}5FBjASj!Bh19<7V~IxTm8Yoo3^hOWu@|<7M4zyKg7`vqERbHjaCj zciSlfy_ERA_AoS6M4&yDltT018XJN5vuV&C_>!Znr2w9XfO>NwYtQ}0Q2$`i2Es~= zb{O3Npj~g5L@Dj39ly?e)mPb^=nK+@K4c?PT3*0GTeR9Og*lsM1uD1iyhDm%b(-G> z;*Su6eiNf(+G-kC{#~T=f9S9KU(HiZn6mR7;=;Vu@(z5>(d=-5I6>A>94G>QP`yM{ zN?!7RfZPS11Yfm2|2rWxstkcH(w_F)&Uez{PV(pVP-w(qH6b z(X;DsY-3z6?3n07;=F>Wh;zm3vI$c@N#3JU6W1Q>NJ^<3lf^ePkm{3Zl)ep+P1afAWD=Ls&t})fD}=RAT6L20TBV|B`7Fj zL_~TCp`%m*0R`#32BgkN zx66L;3|U5WJ3|gGT|;fC5es^9Zve#QZ~u?U*nbY&FwX*&6JFZ+ZV1(0YpiHiS*>Tw z(G1K@kls&*ew!A;>a@=qA87Th4d!bc8_PuIFDz+HdGLRNut30WeCu2M zR7eL>5fC+6|1CBhxUblcKJm@)#aNrvt=2^ur{(8DU6O!Bei%Ek=wy1Y^d+7z9&-K=QVW9@ zdDw&zxooD7g{!<)g|BrEyWhAq%+s*n}LzqHz`gbfLGD2xb?)2tMfwvPy0o102?j zDNGpYJpMKn06R(P5rt%-BxS+0MZ`$xoE2Q}#!?yen!HIa5Av9Pn9O0Bm1=lKw2bOq&?! z1(?WYKjIMyjRCb4RSgxtE*0b(HMdoXJl0NGSWqpgf}&I)%nzubBJfVb^( z5%zAtZrhXtHm9_ttWS8RtfbrzJ1Dmrbo%4t8s7H;Q!mtsk57E^ik;aw5FLW;F;tRt zh6XF59B^>j@n!JcnjWVbv?f@J!MF{3mWy^;JjxfSdqE6}hM{I}0UqWmhXW8u8LiBV zxyk&IK}pV5SnQHoc)!?(D8JM4Nm#b?+v*TzJ_bgc>n?Ow2FBa3x5T`L1$vOJbnL^F{|Yc>XqESP)S`?*CEvEv zwYP07>T;^_jg-5roa_mmE#0@vBEGd?sWgqYKd|!9r_V+|&-h^`r9$Fik3|nI<&f&r zNp^6pv9XH_!RZz83TA`p*V@x=Ll<LTr z5W`#dy{!rQbuhLHLR=# zv)y*x7$#YX+%ehtY#I&)S>A^~=`R-% z+nad|zc!D(4kb_RI^m#rc4zY7lra!|1fY{WDqaAZq5x;N^`wk|R;qNLdVdgKqAa!v za|dzV+f@kWPn%hc)mJ_p!hFvXbOn@^3P~iMKe}mQo8V=jz|%K*_o{$?U0_RR6AGzK zjogyoa?c~`9jSD?-@k|s+G#I~Q#4$2z04MK+|OzAJ!S{}aG()N&@@fT>6e3934eKk zeOW0BbunWk^n2K}rhsi33->IzO4RSona^yqY3GW(_hurShJn3VpRl)8fkT`Zc_UiD z+f&BPlg^i4aDbCiw`2Ne(2^S@LBMza3ul-?gP2i9GE2~7?5#}Q;)ogpFtE=7CEb1` zjq1434_jsCCxwSJrW7_vwVKd|KObVZc$Gc;t)n!vqT<%uVx?w5vNlEOJ?1<`PPI13 z!9{j}*3SVzy;Uqg%S6XF)dm5bf-tO`Fg97f7HwiNcGJ=BTx+Y`{oWwQ&SV;cJ_!-1 z@;2&&QvnA=mfL2;4l6?V2I$2Z{%r}eG{EKqU)=<^__ujeH}(z;T;@C@GF~ej@}g!> z9#Sf~Z4)48KP?(yGCrT^nO$~cRiV%7x_~P!d4^+z?AB=_4bs_ow9-ElQS!0^KymD{ ztcSkT-#b3T2`tsD|6WW_{re;;oo$DG*X+q|7`DEvO+jqP`wGDH+^UT=)@5iJ#WFU0 z82-LKOkT3!1D-}K-p9u`J(HGo4e7JdF!;G~x;f_gbx{oa%(-i2QFm=js(2Fiz&qu6-$t?vgJu$}Z^2jT4)_oA!Lo=#YQb-hj}DDG>2L5g{y z>g1Op@tMi+gy699y`FL{8BHP0Xa-$VuRcHTDbRDsb1`l0yGZOd4&^60FRqO%0L{)G z+_D1Wbx%EBoS?x^24`+dBr$Yi&8)uO3Vs)<)t)lwBdzU+qAm8~$?Pt8((CR~bQnmN zT%vrP5;e*nEoK<-}iRcwU9bEd)g6#UaO0VNW^V}4!>E;&+Tzm>*{>y;iWy3hxwz! zCQ68B7#9Ja(;0Y9{RGf~&Mrq@nU53Zy_8jWo zQ&X3+O_3cKG=Nkb?P=x;2Z!hjU*8Vrd^yf`s~L<(zo}Qlp&5uAo6*aoI9JYkzrK9n`0`B~?BY2CfXUCCymf^1xp#GSIE*Fa9A~7uuayUeo4z!!V3DN;&~xyH8||Z zPrmbAJc99G1b7fFn!;>TlvKbQpaR&Fwow%XagnN}`pR>FN zoTlDxkKeVn>4_n*m*PcnUy1DT#EXaD8E)LT@*%|XX3hPeH~ybld8v0h6yEJ73E}nw zoQwyNu+~BP+c`o!Dh+QA{YH53#c@8@Cpq2jjI}i~)%6y$s%`}P>59*3D6Al5{8zQS z`ceropHfZ{RUC6Bj2TaczboS7RlC9o4sS;}q&T!g%>Z_yTYb)n@UkZ^XNO9hx7IHh zf)0JuD179cQMOu4W6$h<9NOz==!#JjD~E=OD+wDR-q#5EgWoz^t?6b}o&~&J>)&;U za*sckv83KTr1s*&pNszj0K}ejyEukb!i1-5k+K?n<(@ir*+yf=P1m>9rcc-GHZhTD zw={BB;?Y6E5}JCovPOIHZB(UmiJK3!XDrw@%W#1^GW8R}J!=yP_wIL|Op5U#ceX&V zfIpsKwF2Y|qG!B&S=haknB!kbEQuyxq^x=_%^(e()N58afZo}a0s@$iJ;QH&TJ^e` zrG;>~GJN?Nt9Y^70sRXp4+d>)14@tJIJNcwm(ze>3;ohAyabIiI*hLFI2=ggXa+1F zVvC*7jSrP?o!&j5P};GIhRJ{kV;@auvv4bmcDvS(H~4*@SxCWT>^sOXqwV}F@aMyS zMy&}3{d!&xsjleuly`Ocoj9nv7WJ9HMPbAkGN4bHb`O+lNd^?df3!_AC&aY7dT9CR zxypZSe^dCJwRo@1)8-j~A)cfpIt|oc0`=4zXMQ(Vw>?U`>~hBkMOA>D*?5g~9Bh~$ zYiZ;v!lK* zHc@omSo)6pycW>j`G}>qvz~2cYU}il)yLWk*qW3;IPETsjSwrRUaWQrJCnG^pe231 zKIVE;w@$M9(%90*Sva*Eea<(&D*Qn3TUw=9>S~xOuc1+XZkqlLfC`=(BtQ=RlyGSi z8c<=*WyCW+Cil2K)ypgM$cA|f!(_LD zdz&erKP*wm9Td^IBiozd3~O1Pg>HdFM{O7{dbsiS|-UyZu14V8eDAtynXL8 z44}2p-+Qd^bY~8=+_?52+NqJM)`aauB z?L_$+aL67cRDn!E2H%pE-J3tcD3WI7b8yrfGbRmv$yH`D2%{l#WG~z1j=Fht3RQns z=LqEGA%A_3(MM^?_mi^(*_NR%i5D?Rg?{FQRMU<|z{JIdzukUu<9I}!(P0dtRpT(&tzesLrS>bVDd_o)CO;sGqdn8z}yfEq>7>vZW-Ipf_de zuKWsDaG_~|fx_Y7>IEMKk6tG@gO}}Q-?CJ5Tu*IiL9Fftzw=m$ZZ9X!trwmNhjCk@ z9HKPeO~%@I{5Vu2B)FJwSVBJQcOgtYg{-T}{RGa1-M{eaM1G_q_(Z0YF@03O*p*fx zYCm}zkQe$$9&r6M?GN!kV843CCaytpuuQY#9Ic+6E}`%&uQ9}}nWx+tf@Ghb^nkxY zxxL7aBMTr*OQjDw4>p~0r9-TUa8;7~F3`k}Lg5^$1&-@pJvyjDu)hd=Q#$|n5a@PR zy5GEE5$^sFSV?tTB!Xt3+#f={8LYY>mp8Q~7?c87!%I@|Cw)#LVx2R#@N1)3=}B;I zr;mdod;k9ea(rfddni!pJEi^~|`&OK!92Hb%b9J@rrm3@{RK z*^*@Ohj3Hoh^;GU*N0#d+Q;{H!H53l=3e4}9RZNG4?XDL>9}o3&xPiI#Hp-D$od1P z?y}GdoXktLm*|;eUw+>{SGlJ8LsX;FK6C<(>SpGV{k7HommxXXOt-j?BM2EUme(!} zqJl{MU_oRvbB+p7YW@mlLv?*T8$VGv_;E`FO|e7dd;#uf-YL6(7wFa&*@|Da{}&O}qfsBz^wmFjNu72GCK$L8)t)jc{Gcpoli#i2P zNS_NDoRIZ&i)MYG7}d@mcn_#?CielYna$_mSDFt$wVGKFTw2uM&t)3zi30`-8_Z;B zF_l`Lqj`b@zmxnZCf(RXGt51YZW3U|fEB5u-Sm_;Wt3|ND9^nK2w6 z=7u`b2L4gqK0>{lKQ35}I;aC82pG2RMub5KNRXpI6R~0}@@#Fox+>PCgXuFDrw5(e zh3s@6v2x(A5m2{kj|>viY~u_$G!7~UquL1as`to4M1jEJvBv&d)#%xG&aUkn3+3UH z5h+*MrYjz&p7;R#Cbb(%{U843K)MfQ5h}-MxiTD)J7PLvdi7+h1HN8EOG_a+=37T!oa`=CbM@Iy7{=5WNj4Mi>>+u-jdQrNXu zFb=+58%wehH$DGoUnDF+GXpnCM(|ia7GB*MW2LM2o46S1&P72X_7N}t)$L(QfcG^w zjjInczITbFKlc-=&J0&O5)wVZHmG+D`6;Z)hDv~xENz*^; zZMtcGCmh$n(%Q?q6Mh-nVRS!?{?wzxXQa~}u>929C3#@jS{cjG3hT8YDhnEZr5IM$Uqo(LvsbLo)oAmPq z$m6RC4pU=AS`}5 FvU%>lLEa^u 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
  • ?g|FH~H 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 afBZ0`Di^xNyOtmGof6QWrnG6l4*oyw6y(GJ literal 0 HcmV?d00001 diff --git a/expo/run_experiment.py b/expo/run_experiment.py index fbd05d776..49d058f13 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -18,6 +18,7 @@ def get_args(): default="mcts", choices=["mcts", "aug", "base", "custom", "greedy", "autogluon", "random", "autosklearn"], ) + parser.add_argument("--role_timeout", type=int, default=1000) get_di_args(parser) get_mcts_args(parser) get_aug_exp_args(parser) From 9f4eba7f60c6c47ebe64e3e366e05ea5de071d6f Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 10 Oct 2024 19:43:56 +0800 Subject: [PATCH 106/160] add scripts --- expo/scripts/run_cls.sh | 15 +++++++++++++++ expo/scripts/run_cls_mod.sh | 13 +++++++++++++ expo/scripts/run_reg.sh | 14 ++++++++++++++ 3 files changed, 42 insertions(+) create mode 100644 expo/scripts/run_cls.sh create mode 100644 expo/scripts/run_cls_mod.sh create mode 100644 expo/scripts/run_reg.sh diff --git a/expo/scripts/run_cls.sh b/expo/scripts/run_cls.sh new file mode 100644 index 000000000..f0ee5ddcf --- /dev/null +++ b/expo/scripts/run_cls.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +tasks=("smoker-status" "software-defects" "jasmine" "credit-g" "Click_prediction_small" "kick" "kc1" "titanic" "icr" "wine-quality-white" "mfeat-factors" "segment" "GesturePhaseSegmentationProcessed") + + +for i in {1..3} +do + for task in "${tasks[@]}"; do + echo "Running experiment for task: $task" + python run_experiment.py --exp_mode mcts --task "$task" --rollouts 10 --special_instruction stacking + echo "Experiment for task $task completed." + done +done + +echo "All experiments completed." diff --git a/expo/scripts/run_cls_mod.sh b/expo/scripts/run_cls_mod.sh new file mode 100644 index 000000000..ae3622b7a --- /dev/null +++ b/expo/scripts/run_cls_mod.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +tasks=("banking77" "gnad10" "sms_spam" "oxford-iiit-pet" "stanford_cars" "fashion_mnist" ) + +for i in {1..3} +do + for task in "${tasks[@]}"; do + echo "Running experiment for task: $task" + python run_experiment.py --exp_mode mcts --task "$task" --rollouts 10 + echo "Experiment for task $task completed." + done +done +echo "All experiments completed." diff --git a/expo/scripts/run_reg.sh b/expo/scripts/run_reg.sh new file mode 100644 index 000000000..f8a742886 --- /dev/null +++ b/expo/scripts/run_reg.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +tasks=("concrete-strength" "Moneyball" "colleges" "SAT11-HAND-runtime-regression" "diamonds" "boston" "house-prices") + +for i in {1..3} +do + for task in "${tasks[@]}"; do + echo "Running experiment for task: $task" + python run_experiment.py --exp_mode mcts --task "$task" --rollouts 10 --low_is_better --special_instruction stacking + echo "Experiment for task $task completed." + done +done + +echo "All experiments completed." From e1c2683038069395ccfafc1fd106aab9562549bf Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 11 Oct 2024 10:03:08 +0800 Subject: [PATCH 107/160] =?UTF-8?q?=E5=88=A0=E9=99=A4=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expo/README.md | 1 - expo/resources/MCTS-Experimenter.jpg | Bin 659150 -> 0 bytes 2 files changed, 1 deletion(-) delete mode 100644 expo/resources/MCTS-Experimenter.jpg diff --git a/expo/README.md b/expo/README.md index 0a807c928..598de039d 100644 --- a/expo/README.md +++ b/expo/README.md @@ -1,6 +1,5 @@ # SELA: Tree-Search Enhanced LLM Agents for Automated Machine Learning -![pipeline](resources/MCTS-Experimenter.jpg) diff --git a/expo/resources/MCTS-Experimenter.jpg b/expo/resources/MCTS-Experimenter.jpg deleted file mode 100644 index bbae98ee3ef6781b51e34278a561bb1d5b8f1d0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 659150 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

    L;GOTEtK62mEin*@&JPq~M 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

  • Y2}6$B&DamJ_{kNA_{#k5?fyC>v(LhhRh_mU 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ཷ}12?R>^qyHZ2Cg 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
  • ?g|FH~H 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 afBZ0`Di^xNyOtmGof6QWrnG6l4*oyw6y(GJ From cdfb413f9d8e7e57e2d64efa0616ecf491460b17 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 11 Oct 2024 10:05:15 +0800 Subject: [PATCH 108/160] =?UTF-8?q?=E5=8E=BB=E6=8E=89role=E8=A3=85?= =?UTF-8?q?=E9=A5=B0=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expo/research_assistant.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/expo/research_assistant.py b/expo/research_assistant.py index c574d5b18..fb34ece38 100644 --- a/expo/research_assistant.py +++ b/expo/research_assistant.py @@ -11,7 +11,7 @@ from metagpt.const import SERDESER_PATH from metagpt.roles.di.data_interpreter import DataInterpreter from metagpt.schema import Message, Task, TaskResult -from metagpt.utils.common import CodeParser, role_raise_decorator, write_json_file +from metagpt.utils.common import CodeParser, write_json_file EXTRACT_SCORE_PROMPT = """ # Code: @@ -165,7 +165,6 @@ def remap_tasks(self): ] @async_timeout() - @role_raise_decorator async def run(self, with_message=None) -> Message | None: """Observe, and think and act based on the results of the observation""" if with_message == "continue": From 9c54113f777990174bacd0ea789435f53982e71a Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 11 Oct 2024 10:41:33 +0800 Subject: [PATCH 109/160] make sure image task starting from datapreprocessing --- expo/experimenter/mcts.py | 5 ++++- expo/run_experiment.py | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 22b480caf..c063268c8 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -8,9 +8,12 @@ class MCTSExperimenter(Experimenter): result_path: str = "results/mcts" - start_task_id = 2 def __init__(self, args, tree_mode=None, **kwargs): + if args.special_instruction == "image": + self.start_task_id = 1 # start from datapreprocessing if it is image task + else: + self.start_task_id = args.start_task_id super().__init__(args, **kwargs) self.tree_mode = tree_mode diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 49d058f13..15be27d60 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -31,6 +31,7 @@ def get_mcts_args(parser): parser.set_defaults(load_tree=False) parser.add_argument("--rollouts", type=int, default=5) parser.add_argument("--use_fixed_insights", dest="use_fixed_insights", action="store_true") + parser.add_argument("--start_task_id", type=int, default=2) def get_aug_exp_args(parser): From ae12d737479a142293df2a11d560404db75c99dd Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 11 Oct 2024 13:12:03 +0800 Subject: [PATCH 110/160] remove dependency --- expo/data/dataset.py | 3 ++- expo/data/hf_data.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index dd4cb4543..e076284d6 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -9,6 +9,7 @@ from sklearn.model_selection import train_test_split from expo.insights.solution_designer import SolutionDesigner +from expo.utils import DATA_CONFIG BASE_USER_REQUIREMENT = """ This is a {datasetname} dataset. Your goal is to predict the target column `{target_col}`. @@ -361,7 +362,7 @@ async def process_dataset(dataset, solution_designer: SolutionDesigner, save_ana if __name__ == "__main__": - datasets_dir = "D:/work/automl/datasets" + datasets_dir = DATA_CONFIG["datasets_dir"] force_update = False save_analysis_pool = True datasets_dict = {"datasets": {}} diff --git a/expo/data/hf_data.py b/expo/data/hf_data.py index a43fcd415..133fbdfa6 100644 --- a/expo/data/hf_data.py +++ b/expo/data/hf_data.py @@ -9,6 +9,7 @@ from expo.data.dataset import ExpDataset, process_dataset, save_datasets_dict_to_yaml from expo.insights.solution_designer import SolutionDesigner +from expo.utils import DATA_CONFIG HFDATSETS = [ {"name": "sms_spam", "dataset_name": "ucirvine/sms_spam", "target_col": "label", "modality": "text"}, @@ -114,7 +115,7 @@ def get_dataset_info(self): if __name__ == "__main__": - dataset_dir = "D:/work/automl/datasets" + dataset_dir = DATA_CONFIG["datasets_dir"] save_analysis_pool = True force_update = False datasets_dict = {"datasets": {}} From 56e7a08a1c7304b5262da58a55546eb0244cb9c0 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 11 Oct 2024 14:48:56 +0800 Subject: [PATCH 111/160] insight pool is now able to dynamically increase --- expo/MCTS.py | 16 ++++++++++------ expo/insights/instruction_generator.py | 16 +++++++++------- expo/insights/solution_designer.py | 1 + 3 files changed, 20 insertions(+), 13 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 7de123572..9d778e4ed 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -155,18 +155,15 @@ def save_new_role(self, role: ResearchAssistant): role = role.model_copy() role.save_state(static_save=True) - async def expand(self, max_children, use_fixed_insights): + async def expand(self, max_children: int, instruction_generator: InstructionGenerator): if self.is_fully_expanded(): return - insight_geneartor = InstructionGenerator() role = self.load_role() original_instruction = role.get_next_instruction() - insights = await insight_geneartor.generate_new_instructions( + insights = await instruction_generator.generate_new_instructions( task_id=role.start_task_id + 1, original_instruction=original_instruction, max_num=max_children, - file_path=self.state["exp_pool_path"], - use_fixed_insights=use_fixed_insights, ) new_state = self.state.copy() new_state["start_task_id"] += 1 @@ -249,6 +246,8 @@ class MCTS: c_explore: float = 1.4 c_unvisited: float = 0.8 node_order: list = [] + # insight generator + instruction_generator: InstructionGenerator = None def __init__(self, root_node, max_depth, use_fixed_insights): self.root_node = root_node @@ -272,7 +271,7 @@ def uct(node: Node): return max(all_children, key=uct) async def expand(self, node: Node, max_children=5): - await node.expand(max_children, self.use_fixed_insights) + await node.expand(max_children, self.instruction_generator) if node not in self.children or not self.children[node]: self.children[node] = node.children return node.children @@ -284,6 +283,7 @@ async def simulate(self, node: Node, role=None): node = random.choice(node.children) reward = await node.run_node(role) mcts_logger.log("MCTS", f"Simulated node's reward: {reward}") + return reward def backpropagate(self, node: Node, reward): @@ -344,6 +344,10 @@ def get_score_order_dict(self): async def search(self, state, rollouts, load_tree=False, reflection=False): role, root = initialize_di_root_node(state, reflection=reflection) self.root_node = root + self.instruction_generator = InstructionGenerator( + file_path=state["exp_pool_path"], use_fixed_insights=self.use_fixed_insights + ) + tree_loaded = False if load_tree: tree_loaded = self.load_tree() diff --git a/expo/insights/instruction_generator.py b/expo/insights/instruction_generator.py index 07e5fb655..ae6c742fb 100644 --- a/expo/insights/instruction_generator.py +++ b/expo/insights/instruction_generator.py @@ -2,6 +2,7 @@ import os import random +from expo.insights.solution_designer import SolutionDesigner from expo.utils import clean_json_from_rsp, load_data_config, mcts_logger from metagpt.llm import LLM from metagpt.schema import Message @@ -32,6 +33,12 @@ class InstructionGenerator: data_config = DATA_CONFIG + def __init__(self, file_path, use_fixed_insights=False): + self.file_path = file_path + self.use_fixed_insights = use_fixed_insights + self.analysis_pool = self.load_analysis_pool(file_path, use_fixed_insights) + self.proposer = SolutionDesigner() + @staticmethod def load_json_data(json_dir): with open(json_dir, "r") as file: @@ -83,13 +90,8 @@ def load_analysis_pool(file_path, use_fixed_insights, task_id=None): data = [item for item in data if int(item["task_id"]) == int(task_id)] return data - @staticmethod - async def generate_new_instructions( - task_id, original_instruction, max_num, file_path, ext_info=None, use_fixed_insights=False - ): - data = InstructionGenerator.load_analysis_pool( - file_path, task_id=task_id, use_fixed_insights=use_fixed_insights - ) + async def generate_new_instructions(self, task_id, original_instruction, max_num, ext_info=None): + data = self.analysis_pool new_instructions = [] if len(data) == 0: mcts_logger.log("MCTS", f"No insights available for task {task_id}") diff --git a/expo/insights/solution_designer.py b/expo/insights/solution_designer.py index b1fcf4188..9968131ca 100644 --- a/expo/insights/solution_designer.py +++ b/expo/insights/solution_designer.py @@ -21,6 +21,7 @@ Each task type should have at least 5 insights. Make sure each method is diverse enough and can be implemented separately. Be specific about models' choices, ensemble and tuning techniques, and preprocessing & feature engineering techniques. +Your model choices should be advanced enough to be helpful. # Format ```json From f7374c03afe8ba5c0e3278772a634310decb7cfb Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 11 Oct 2024 14:55:45 +0800 Subject: [PATCH 112/160] rename analysis pool to insight pool --- expo/insights/instruction_generator.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expo/insights/instruction_generator.py b/expo/insights/instruction_generator.py index ae6c742fb..330795730 100644 --- a/expo/insights/instruction_generator.py +++ b/expo/insights/instruction_generator.py @@ -36,7 +36,7 @@ class InstructionGenerator: def __init__(self, file_path, use_fixed_insights=False): self.file_path = file_path self.use_fixed_insights = use_fixed_insights - self.analysis_pool = self.load_analysis_pool(file_path, use_fixed_insights) + self.analysis_pool = self.load_insight_pool(file_path, use_fixed_insights) self.proposer = SolutionDesigner() @staticmethod @@ -76,7 +76,7 @@ def format_output(rsp): return new_data @staticmethod - def load_analysis_pool(file_path, use_fixed_insights, task_id=None): + def load_insight_pool(file_path, use_fixed_insights, task_id=None): data = InstructionGenerator.load_json_data(file_path) if use_fixed_insights: current_directory = os.path.dirname(__file__) From eda9322361cc112fc34cb340365688123272b2a3 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 11 Oct 2024 18:37:35 +0800 Subject: [PATCH 113/160] 1. dynamically add insight 2. insight from scratch in real time --- expo/MCTS.py | 14 +++++-- expo/experimenter/mcts.py | 7 +--- expo/insights/instruction_generator.py | 49 ++++++++++++++++++++--- expo/insights/solution_designer.py | 55 ++++++++++++++++++++++++-- expo/research_assistant.py | 5 +++ expo/run_experiment.py | 3 ++ 6 files changed, 115 insertions(+), 18 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 9d778e4ed..7e1d7c88a 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -235,7 +235,8 @@ def normalize_score(score): score_dict = {k: normalize_score(v) for k, v in score_dict.items()} self.normalized_reward = score_dict - return score_dict + result_dict = role.get_solution() + return score_dict, result_dict class MCTS: @@ -281,7 +282,7 @@ async def simulate(self, node: Node, role=None): mcts_logger.log("MCTS", f"Start simulating node {node.id}:") while node.children: node = random.choice(node.children) - reward = await node.run_node(role) + reward, result_dict = await node.run_node(role) mcts_logger.log("MCTS", f"Simulated node's reward: {reward}") return reward @@ -341,12 +342,17 @@ def get_score_order_dict(self): scores["test_raw"].append(node.raw_reward["test_score"]) return scores - async def search(self, state, rollouts, load_tree=False, reflection=False): + async def search(self, state, args): + reflection = args.reflection + load_tree = args.load_tree + rollouts = args.rollouts + from_scratch = args.from_scratch role, root = initialize_di_root_node(state, reflection=reflection) self.root_node = root self.instruction_generator = InstructionGenerator( - file_path=state["exp_pool_path"], use_fixed_insights=self.use_fixed_insights + state=state, use_fixed_insights=self.use_fixed_insights, from_scratch=from_scratch ) + await self.instruction_generator.initialize() tree_loaded = False if load_tree: diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index c063268c8..d212eb204 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -24,12 +24,7 @@ async def run_experiment(self): mcts = Random(root_node=None, max_depth=5, use_fixed_insights=self.args.use_fixed_insights) else: mcts = MCTS(root_node=None, max_depth=5, use_fixed_insights=self.args.use_fixed_insights) - best_nodes = await mcts.search( - state=self.state, - reflection=self.args.reflection, - rollouts=self.args.rollouts, - load_tree=self.args.load_tree, - ) + best_nodes = await mcts.search(state=self.state, args=self.args) best_node = best_nodes["global_best"] dev_best_node = best_nodes["dev_best"] score_dict = best_nodes["scores"] diff --git a/expo/insights/instruction_generator.py b/expo/insights/instruction_generator.py index 330795730..7fa4d72ea 100644 --- a/expo/insights/instruction_generator.py +++ b/expo/insights/instruction_generator.py @@ -1,6 +1,7 @@ import json import os import random +from difflib import SequenceMatcher from expo.insights.solution_designer import SolutionDesigner from expo.utils import clean_json_from_rsp, load_data_config, mcts_logger @@ -33,11 +34,21 @@ class InstructionGenerator: data_config = DATA_CONFIG - def __init__(self, file_path, use_fixed_insights=False): - self.file_path = file_path + def __init__(self, state, use_fixed_insights, from_scratch): + self.state = state + self.file_path = state["exp_pool_path"] + self.dataset_info_path = f"{self.data_config['datasets_dir']}/{state['task']}/dataset_info.json" + with open(self.dataset_info_path, "r") as file: + self.dataset_info = json.load(file) self.use_fixed_insights = use_fixed_insights - self.analysis_pool = self.load_insight_pool(file_path, use_fixed_insights) self.proposer = SolutionDesigner() + self.from_scratch = from_scratch + + async def initialize(self): + if self.from_scratch: + self.insight_pool = await self.generate_solutions_from_scratch(self.dataset_info, self.state["task"]) + else: + self.insight_pool = self.load_insight_pool(self.file_path, self.use_fixed_insights) @staticmethod def load_json_data(json_dir): @@ -84,14 +95,14 @@ def load_insight_pool(file_path, use_fixed_insights, task_id=None): data.extend(fixed_insights) for item in data: if "task_id" not in item: - raise ValueError("task_id is not found in the analysis pool") + raise ValueError("task_id is not found in the insight_pool") if task_id: data = [item for item in data if int(item["task_id"]) == int(task_id)] return data async def generate_new_instructions(self, task_id, original_instruction, max_num, ext_info=None): - data = self.analysis_pool + data = self.insight_pool new_instructions = [] if len(data) == 0: mcts_logger.log("MCTS", f"No insights available for task {task_id}") @@ -108,6 +119,34 @@ async def generate_new_instructions(self, task_id, original_instruction, max_num new_instructions.append(new_instruction) return new_instructions + async def propose_new_insights(self, solution, score): + new_insights = await self.proposer.propose_insights(solution, score) + added_insights = self.add_insight(new_insights) + return added_insights + + async def generate_solutions_from_scratch(self, dataset_info, dataset_name): + insight_pool = await self.proposer.generate_solutions(dataset_info, dataset_name, save_analysis_pool=False) + return insight_pool + + def add_insight(self, new_insights): + added_insights = [] + for new_insight in new_insights: + if not self.is_similar_to_existing(new_insight): + added_insights.append(new_insight) + self.insight_pool.append(new_insight) + return added_insights + + def is_similar_to_existing(self, new_insight, similarity_threshold=0.8): + for existing_insight in self.insight_pool: + similarity = self.calculate_similarity(new_insight["Analysis"], existing_insight["Analysis"]) + if similarity > similarity_threshold: + return True + return False + + @staticmethod + def calculate_similarity(text1, text2): + return SequenceMatcher(None, text1, text2).ratio() + @staticmethod async def generate_new_instruction(original_instruction, insights, ext_info): prompt = CHANGE_INSTRUCTION.format(instruction=original_instruction, insights=insights) diff --git a/expo/insights/solution_designer.py b/expo/insights/solution_designer.py index 9968131ca..2336911db 100644 --- a/expo/insights/solution_designer.py +++ b/expo/insights/solution_designer.py @@ -70,6 +70,45 @@ ``` """ + +INSIGHT_PROPOSAL_PROMPT = """ +You are an AI assistant tasked with analyzing a machine learning solution and proposing new insights to improve its performance. Given the current solution code and development score, suggest innovative approaches to enhance the model. + +Current Solution Code: +{solution_code} + +Development Score: {dev_score} + +Based on this information, propose 3-5 new insights across different aspects of the machine learning pipeline (Data Preprocessing, Feature Engineering, and Model Training). Your insights should be specific, actionable, and have the potential to improve the model's performance. + +Please format your response as a JSON array with the following structure: +[ + + {{ + "task_type": "Data Preprocessing", + "insights": [ + "insight1", + "insight2" + ] + }}, + {{ + "task_type": "Feature Engineering", + "insights": [ + "insight1", + "insight2" + ] + }}, + {{ + "task_type": "Model Training", + "insights": [ + "insight1", + "insight2" + ] + }} +] +""" + + KEY_DATASET_FEATURES = [ "NumberOfClasses", "NumberOfFeatures", @@ -86,7 +125,7 @@ class SolutionDesigner: data_dir: str = DATA_CONFIG["datasets_dir"] - async def generate_solutions(self, dataset_info, dataset_name): + async def generate_solutions(self, dataset_info, dataset_name, save_analysis_pool=True): llm = LLM() context = DATASET_INSIGHT_PROMPT.format( dataset=dataset_info["description"], @@ -96,8 +135,18 @@ async def generate_solutions(self, dataset_info, dataset_name): rsp = await llm.aask(context) rsp = clean_json_from_rsp(rsp) analysis_pool = self.process_analysis_pool(json.loads(rsp)) - dataset_path = f"{self.data_dir}/{dataset_name}" - self.save_analysis_pool(dataset_path, analysis_pool) + if save_analysis_pool: + dataset_path = f"{self.data_dir}/{dataset_name}" + self.save_analysis_pool(dataset_path, analysis_pool) + return analysis_pool + + async def propose_new_insights(self, solution, score): + llm = LLM() + context = INSIGHT_PROPOSAL_PROMPT.format(solution_code=solution, dev_score=score) + rsp = await llm.aask(context) + rsp = clean_json_from_rsp(rsp) + new_insights = self.process_analysis_pool(json.loads(rsp)) + return new_insights def process_analysis_pool(self, insights_rsp): analysis_pool = [] diff --git a/expo/research_assistant.py b/expo/research_assistant.py index fb34ece38..0b53521a3 100644 --- a/expo/research_assistant.py +++ b/expo/research_assistant.py @@ -139,6 +139,11 @@ async def _act_on_task(self, current_task: Task) -> TaskResult: save_notebook(role=self, save_dir=self.role_dir, name=self.get_node_name()) return task_result + def get_solution(self): + codes = [task.code for task in self.planner.plan.tasks] + results = [task.result for task in self.planner.plan.tasks] + return {"codes": codes, "results": results} + def save_state(self, static_save=False): """ attribute: diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 15be27d60..c43da12fd 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -32,6 +32,9 @@ def get_mcts_args(parser): parser.add_argument("--rollouts", type=int, default=5) parser.add_argument("--use_fixed_insights", dest="use_fixed_insights", action="store_true") parser.add_argument("--start_task_id", type=int, default=2) + parser.add_argument( + "--from_scratch", dest="from_scratch", action="store_true", help="Generate solutions from scratch" + ) def get_aug_exp_args(parser): From 3a57060e25a8acfd2ed0f80b4d68a5a110425159 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Sat, 12 Oct 2024 17:16:51 +0800 Subject: [PATCH 114/160] 1. add eval_func for sela and compatibility to others 2. llm extract score (use all code block and execution results) 3. add argument for custom dataset dir 4. dataset custom requirement support --- expo/MCTS.py | 65 ++++++++++++++------- expo/data/custom_task.py | 38 ++++++++++++ expo/data/dataset.py | 2 +- expo/evaluation/evaluation.py | 12 ++++ expo/experimenter/experimenter.py | 5 +- expo/experimenter/mcts.py | 21 ++++++- expo/experimenter/mle_bench/instructions.py | 47 +++++++++++++++ expo/insights/instruction_generator.py | 14 +++-- expo/research_assistant.py | 28 ++++++--- expo/run_experiment.py | 6 ++ expo/utils.py | 2 + 11 files changed, 202 insertions(+), 38 deletions(-) create mode 100644 expo/data/custom_task.py create mode 100644 expo/experimenter/mle_bench/instructions.py diff --git a/expo/MCTS.py b/expo/MCTS.py index 7e1d7c88a..a8410748e 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -3,10 +3,12 @@ import os import pickle import random +import shutil import numpy as np import pandas as pd +from expo.data.custom_task import get_mle_bench_requirements from expo.data.dataset import generate_task_requirement, get_split_dataset_path from expo.evaluation.evaluation import evaluate_score from expo.insights.instruction_generator import InstructionGenerator @@ -17,9 +19,6 @@ def initialize_di_root_node(state, reflection: bool = True): - # state = create_initial_state( - # task, start_task_id=start_task_id, data_config=data_config, low_is_better=low_is_better, name=name - # ) role = ResearchAssistant( node_id="0", start_task_id=state["start_task_id"], use_reflection=reflection, role_dir=state["node_dir"] ) @@ -29,20 +28,33 @@ def initialize_di_root_node(state, reflection: bool = True): def create_initial_state( task, start_task_id, data_config, low_is_better: bool, name: str, special_instruction: str, args ): + external_eval = args.external_eval + + if args.custom_dataset_dir: + dataset_config = None + datasets_dir = args.custom_dataset_dir + requirement = get_mle_bench_requirements(args.custom_dataset_dir, data_config) + exp_pool_path = None + else: + dataset_config = data_config["datasets"][task] + datasets_dir = get_split_dataset_path(task, data_config) + requirement = generate_task_requirement(task, data_config, is_di=True, special_instruction=special_instruction) + exp_pool_path = get_exp_pool_path(task, data_config, pool_name="ds_analysis_pool") + initial_state = { "task": task, "work_dir": data_config["work_dir"], "node_dir": os.path.join(data_config["work_dir"], data_config["role_dir"], f"{task}{name}"), - "dataset_config": data_config["datasets"][task], - "datasets_dir": get_split_dataset_path(task, data_config), - "exp_pool_path": get_exp_pool_path(task, data_config, pool_name="ds_analysis_pool"), - "requirement": generate_task_requirement( - task, data_config, is_di=True, special_instruction=special_instruction - ), + "dataset_config": dataset_config, + "datasets_dir": datasets_dir, # won't be used if external eval is used + "exp_pool_path": exp_pool_path, + "requirement": requirement, "has_run": False, "start_task_id": start_task_id, "low_is_better": low_is_better, "role_timeout": args.role_timeout, + "external_eval": external_eval, + "custom_dataset_dir": args.custom_dataset_dir, } os.makedirs(initial_state["node_dir"], exist_ok=True) return initial_state @@ -173,22 +185,34 @@ async def expand(self, max_children: int, instruction_generator: InstructionGene node.save_new_role(new_role) self.add_child(node) - def evaluate_prediction(self, split): - pred_path = os.path.join(self.state["work_dir"], self.state["task"], f"{split}_predictions.csv") - pred_node_path = os.path.join(self.state["node_dir"], f"Node-{self.id}-{split}_predictions.csv") + def get_predictions_path(self, split): + return os.path.join(self.state["node_dir"], f"Node-{self.id}-{split}_predictions.csv") + + def get_and_move_predictions(self, split): + if not os.path.exists(self.get_predictions_path(split)): + pred_path = os.path.join(self.state["work_dir"], self.state["task"], f"{split}_predictions.csv") + shutil.copy(pred_path, self.get_predictions_path(split)) + os.remove(pred_path) + return pd.read_csv(self.get_predictions_path(split)) + + def get_gt(self, split): gt_path = os.path.join(self.state["datasets_dir"][f"{split}_target"]) - preds = pd.read_csv(pred_path)["target"] - preds.to_csv(pred_node_path, index=False) - gt = pd.read_csv(gt_path)["target"] + return pd.read_csv(gt_path) + + def evaluate_prediction(self, split): + preds = self.get_and_move_predictions(split)["target"] + gt = self.get_gt(split)["target"] metric = self.state["dataset_config"]["metric"] - # remove original predictions.csv - os.remove(pred_path) return evaluate_score(preds, gt, metric) def evaluate_simulation(self, score_dict): - scores = {"dev_score": self.evaluate_prediction("dev"), "test_score": self.evaluate_prediction("test")} - scores["score"] = scores["dev_score"] - score_dict.update(scores) + if self.state["external_eval"]: # use external evaluation + scores = {"dev_score": self.evaluate_prediction("dev"), "test_score": self.evaluate_prediction("test")} + scores["score"] = scores["dev_score"] + score_dict.update(scores) + else: + self.get_and_move_predictions("dev") + self.get_and_move_predictions("test") return score_dict async def run_node(self, role=None): @@ -215,7 +239,6 @@ async def run_node(self, role=None): mcts_logger.log("MCTS", f"Role-level timeout: {e}") break except Exception as e: - print(f"Error: {e}") mcts_logger.log("MCTS", f"Error in running the role: {e}") num_runs += 1 diff --git a/expo/data/custom_task.py b/expo/data/custom_task.py new file mode 100644 index 000000000..2bd88abde --- /dev/null +++ b/expo/data/custom_task.py @@ -0,0 +1,38 @@ +import os + +from expo.experimenter.mle_bench.instructions import ( + ADDITIONAL_NOTES, + INSTRUCTIONS, + INSTRUCTIONS_OBFUSCATED, +) + +MLE_BENCH_FILES = ["description.md", "description_obfuscated.md"] + + +MLE_REQUIREMENTS = """ +{instructions} + +{additonal_notes} + +COMPETITION INSTRUCTIONS +------ + +{task_description} + +""" + + +def get_mle_bench_requirements(dataset_dir, data_config, obfuscated=False): + if obfuscated: + instructions = INSTRUCTIONS_OBFUSCATED + task_file = "description_obfuscated.md" + else: + instructions = INSTRUCTIONS + task_file = "description.md" + + with open(os.path.join(dataset_dir, task_file)) as f: + task_description = f.read() + mle_requirement = MLE_REQUIREMENTS.format( + instructions=instructions, additonal_notes=ADDITIONAL_NOTES, task_description=task_description + ) + return mle_requirement diff --git a/expo/data/dataset.py b/expo/data/dataset.py index e076284d6..8b0c5b980 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -268,7 +268,7 @@ def get_metric(self): dataset_info = self.get_dataset_info() num_classes = dataset_info["metadata"]["NumberOfClasses"] if num_classes == 2: - metric = "f1" + metric = "f1 binary" elif 2 < num_classes <= 200: metric = "f1 weighted" elif num_classes > 200 or num_classes == 0: diff --git a/expo/evaluation/evaluation.py b/expo/evaluation/evaluation.py index 16b3acb71..1ba7fa60f 100644 --- a/expo/evaluation/evaluation.py +++ b/expo/evaluation/evaluation.py @@ -22,3 +22,15 @@ def evaluate_score(pred, gt, metric): return mean_squared_error(np.log1p(gt), np.log1p(pred), squared=False) else: raise ValueError(f"Metric {metric} not supported") + + +def node_evaluate_score_sela(node): + preds = node.get_and_move_predictions("test")["target"] + gt = node.get_gt("test")["target"] + metric = node.state["dataset_config"]["metric"] + return evaluate_score(preds, gt, metric) + + +def node_evaluate_score_mlebench(node): + # TODO + return 0 diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 9aa879e24..417adabad 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -43,7 +43,10 @@ async def run_di(self, di, user_requirement, run_idx): except Exception as e: print(f"Error: {e}") num_runs += 1 - save_notebook(role=di, save_dir=self.result_path, name=f"{self.args.task}_{self.start_time}_{run_idx}") + # save_notebook(role=di, save_dir=self.result_path, name=f"{self.args.task}_{self.start_time}_{run_idx}") + save_name = self.get_save_name() + save_notebook(role=di, save_dir=self.result_path, name=f"{save_name}_{run_idx}") + if not run_finished: score_dict = {"train_score": -1, "dev_score": -1, "test_score": -1, "score": -1} return score_dict diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index d212eb204..37fc7a071 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -1,5 +1,9 @@ import shutil +from expo.evaluation.evaluation import ( + node_evaluate_score_mlebench, + node_evaluate_score_sela, +) from expo.evaluation.visualize_mcts import get_tree_text from expo.experimenter.experimenter import Experimenter from expo.Greedy import Greedy, Random @@ -14,25 +18,35 @@ def __init__(self, args, tree_mode=None, **kwargs): self.start_task_id = 1 # start from datapreprocessing if it is image task else: self.start_task_id = args.start_task_id + + if args.eval_func == "sela": + self.eval_func = node_evaluate_score_sela + elif args.eval_func == "mlebench": + self.eval_func = node_evaluate_score_mlebench + super().__init__(args, **kwargs) self.tree_mode = tree_mode async def run_experiment(self): + use_fixed_insights = self.args.use_fixed_insights + depth = 5 if self.tree_mode == "greedy": - mcts = Greedy(root_node=None, max_depth=5, use_fixed_insights=self.args.use_fixed_insights) + mcts = Greedy(root_node=None, max_depth=depth, use_fixed_insights=use_fixed_insights) elif self.tree_mode == "random": - mcts = Random(root_node=None, max_depth=5, use_fixed_insights=self.args.use_fixed_insights) + mcts = Random(root_node=None, max_depth=depth, use_fixed_insights=use_fixed_insights) else: - mcts = MCTS(root_node=None, max_depth=5, use_fixed_insights=self.args.use_fixed_insights) + mcts = MCTS(root_node=None, max_depth=depth, use_fixed_insights=use_fixed_insights) best_nodes = await mcts.search(state=self.state, args=self.args) best_node = best_nodes["global_best"] dev_best_node = best_nodes["dev_best"] score_dict = best_nodes["scores"] + additional_scores = {"grader": self.eval_func(dev_best_node)} text, num_generated_codes = get_tree_text(mcts.root_node) text += f"Generated {num_generated_codes} unique codes.\n" text += f"Best node: {best_node.id}, score: {best_node.raw_reward}\n" text += f"Dev best node: {dev_best_node.id}, score: {dev_best_node.raw_reward}\n" + text += f"Grader score: {additional_scores['grader']}\n" print(text) results = [ { @@ -45,6 +59,7 @@ async def run_experiment(self): "tree_text": text, "args": vars(self.args), "scores": score_dict, + "additional_scores": additional_scores, } ] self.save_result(results) diff --git a/expo/experimenter/mle_bench/instructions.py b/expo/experimenter/mle_bench/instructions.py new file mode 100644 index 000000000..301b465c1 --- /dev/null +++ b/expo/experimenter/mle_bench/instructions.py @@ -0,0 +1,47 @@ +INSTRUCTIONS = """ +You are participating in *MLE-bench*, an offline version of Kaggle competitions adapted to test the coding ability of agents like yourself. + +You will be given a machine learning task. You must solve the task by training a model and running the model on the test set to produce a submission file. You may not hand-label the data to produce a submission file. + +BENCHMARK INSTRUCTIONS +------ +First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `/home/instructions.txt`. + +- **Competition-specific instructions**: The instructions for the competition are available in `/home/data/description.md`. Read this file to understand the problem setup for the competition you are currently solving. +- **Dataset**: The dataset files for the competition are available in `/home/data/`. +- **Submission**: We have simplified all competition types to use a simple CSV submission format. Regardless of what the competition instructions say, you MUST produce a submission file at `/home/submission/submission.csv`. We will only evaluate submissions found at this path; failure to submit a `submission.csv` at this path will result in a failed attempt. +- **Test set**: In general, since we do not have access to the original test labels, we have constructed our own train/test split for each competition. As such, the dataset you see in `/home/data/` may differ slightly from what is described in the competition instructions. Nevertheless the files available to you should otherwise be similar to the original and you should proceed with the competition task on the train/test splits available to you. +- **Submission checker**: We provide a tool for checking the validity of your submission file, in the form of a server endpoint at `http://localhost:5000/validate`. If you post a submission file to the server (e.g. `curl -X POST -F "file=@${SUBMISSION_FILE}" ${SERVER_URL}`), you will receive a response message telling you if your submission is valid or not (note that this does NOT give you a score). For your convenience, we provide a script `/home/validate_submission.sh` which can help you make this submission to the validation server. +- **Cheating**: You are not allowed to view others' solutions to this competition. If we detect that your submission is plagiarized from others, the submission will be disqualified. + +Your submission will be graded based on the metric in the competition-specific instructions. Your goal is to achieve the best score possible on the competition task. + +Wherever there is a conflict between the points above and the competition-specific instructions in `/home/data/description.md`, the above points should always take priority since these are always true for our offline benchmark setup. +""" + +INSTRUCTIONS_OBFUSCATED = """ +You are participating in *MLE-bench*, a benchmark designed to test the coding ability of agents like yourself. + +You will be given a machine learning task. You must solve the task by training a model and running the model on the test set to produce a submission file. You may not hand-label the data to produce a submission file. + +BENCHMARK INSTRUCTIONS +------ +First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `/home/instructions.txt`. + +- **Task-specific instructions**: The instructions for the task are available in `/home/data/description.md`. Read this file to understand the problem setup for the task you are currently solving. +- **Dataset**: The dataset files for the task are available in `/home/data/`. +- **Submission**: Regardless of what the task instructions say, you MUST produce a submission file at `/home/submission/submission.csv`. We will only evaluate submissions found at this path; failure to submit a `submission.csv` at this path will result in a failed attempt. +- **Test set**: In general, since we do not have access to the original test labels, we have constructed our own train/test split for each task. As such, the dataset you see in `/home/data/` may differ slightly from what is described in the task instructions. Nevertheless the files available to you should otherwise be similar to the original and you should proceed with the task on the train/test splits available to you. +- **Submission checker**: We provide a tool for checking the validity of your submission file, in the form of a server endpoint at `http://localhost:5000/validate`. If you post a submission file to the server (e.g. `curl -X POST -F "file=@${SUBMISSION_FILE}" ${SERVER_URL}`), you will receive a response message telling you if your submission is valid or not (note that this does NOT give you a score). For your convenience, we provide a script `/home/validate_submission.sh` which can help you make this submission to the validation server. +- **Cheating**: You are not allowed to view others' solutions to this task. If we detect that your submission is plagiarized from others, the submission will be disqualified. + +Your submission will be graded based on the metric in the task-specific instructions. Your goal is to achieve the best score possible on the task. + +Wherever there is a conflict between the points above and the task-specific instructions in `/home/data/description.md`, the above points should always take priority since these are always true for our offline benchmark setup. +""" + +ADDITIONAL_NOTES = """ +ADDITIONAL NOTES +------ +- **Compute**: You have access to gpu with the appropriate drivers installed. +""" diff --git a/expo/insights/instruction_generator.py b/expo/insights/instruction_generator.py index 7fa4d72ea..7fe5ceece 100644 --- a/expo/insights/instruction_generator.py +++ b/expo/insights/instruction_generator.py @@ -37,12 +37,18 @@ class InstructionGenerator: def __init__(self, state, use_fixed_insights, from_scratch): self.state = state self.file_path = state["exp_pool_path"] - self.dataset_info_path = f"{self.data_config['datasets_dir']}/{state['task']}/dataset_info.json" - with open(self.dataset_info_path, "r") as file: - self.dataset_info = json.load(file) + if state["custom_dataset_dir"]: + self.dataset_info = "xxx" + else: + dataset_info_path = f"{self.data_config['datasets_dir']}/{state['task']}/dataset_info.json" + with open(dataset_info_path, "r") as file: + self.dataset_info = json.load(file) self.use_fixed_insights = use_fixed_insights self.proposer = SolutionDesigner() - self.from_scratch = from_scratch + if self.file_path is None: + self.from_scratch = True + else: + self.from_scratch = from_scratch async def initialize(self): if self.from_scratch: diff --git a/expo/research_assistant.py b/expo/research_assistant.py index 0b53521a3..d068dd4e5 100644 --- a/expo/research_assistant.py +++ b/expo/research_assistant.py @@ -13,15 +13,19 @@ from metagpt.schema import Message, Task, TaskResult from metagpt.utils.common import CodeParser, write_json_file -EXTRACT_SCORE_PROMPT = """ -# Code: +CODE_BLOCK_RESULT = """ +## Code: {code} -# Execution Result: +## Execution Result: {result} +""" +EXTRACT_SCORE_PROMPT = """ +# Code Blocks +{code_block} # Instruction: -Based on the code and execution result, please extract the scores and return it as a dictionary. +Based on the code and execution result, please extract the **final scores** and return it as a dictionary. If you cannot find the scores, please still return a dictionary with the keys 'train_score', 'dev_score', and 'test_score', and set the values to -1. # Format: @@ -109,9 +113,17 @@ async def get_score(self): return score_dict async def llm_extract_score(self): - result_text = self.planner.plan.task_map[str(len(self.planner.plan.task_map))].result - code_text = self.planner.plan.task_map[str(len(self.planner.plan.task_map))].code - rsp = await self.llm.aask(EXTRACT_SCORE_PROMPT.format(code=code_text, result=result_text, role="user")) + # result_text = self.planner.plan.task_map[str(len(self.planner.plan.task_map))].result + # code_text = self.planner.plan.task_map[str(len(self.planner.plan.task_map))].code + num_tasks = len(self.planner.plan.task_map) + task_map = self.planner.plan.task_map + code_block = "\n".join( + [ + CODE_BLOCK_RESULT.format(code=task_map[str(i + 1)].code, result=task_map[str(i + 1)].result) + for i in range(num_tasks) + ] + ) + rsp = await self.llm.aask(EXTRACT_SCORE_PROMPT.format(code_block=code_block, role="user")) json_block = CodeParser.parse_code(block=None, text=rsp) score_dict = json.loads(json_block) return score_dict @@ -161,7 +173,7 @@ def save_state(self, static_save=False): stg_path = self.role_dir name = self.get_node_name() role_path = os.path.join(stg_path, f"{name}.json") - # 将状态保存为 JSON 文件 + # save state as json file write_json_file(role_path, self.model_dump()) def remap_tasks(self): diff --git a/expo/run_experiment.py b/expo/run_experiment.py index c43da12fd..53fcdd18c 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -31,10 +31,16 @@ def get_mcts_args(parser): parser.set_defaults(load_tree=False) parser.add_argument("--rollouts", type=int, default=5) parser.add_argument("--use_fixed_insights", dest="use_fixed_insights", action="store_true") + parser.set_defaults(use_fixed_insights=False) parser.add_argument("--start_task_id", type=int, default=2) parser.add_argument( "--from_scratch", dest="from_scratch", action="store_true", help="Generate solutions from scratch" ) + parser.set_defaults(from_scratch=False) + parser.add_argument("--no_external_eval", dest="external_eval", action="store_false") + parser.set_defaults(external_eval=True) + parser.add_argument("--eval_func", type=str, default="sela", choices=["sela", "mlebench"]) + parser.add_argument("--custom_dataset_dir", type=str, default=None) def get_aug_exp_args(parser): diff --git a/expo/utils.py b/expo/utils.py index b022879b0..f3381c91c 100644 --- a/expo/utils.py +++ b/expo/utils.py @@ -51,6 +51,8 @@ def get_exp_pool_path(task_name, data_config, pool_name="analysis_pool"): f"Dataset {task_name} not found in config file. Available datasets: {data_config['datasets'].keys()}" ) exp_pool_path = os.path.join(data_path, f"{pool_name}.json") + if not os.path.exists(exp_pool_path): + return None return exp_pool_path From a91003a7fe2235609d99e74d6d4a93402fb61fc4 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 14 Oct 2024 09:56:55 +0800 Subject: [PATCH 115/160] disable submission --- expo/data/custom_task.py | 22 +++++++++++++--- expo/experimenter/mle_bench/instructions.py | 29 +++++++++++---------- expo/insights/instruction_generator.py | 3 ++- expo/insights/solution_designer.py | 26 +++++++++++++----- 4 files changed, 55 insertions(+), 25 deletions(-) diff --git a/expo/data/custom_task.py b/expo/data/custom_task.py index 2bd88abde..14eb6aac2 100644 --- a/expo/data/custom_task.py +++ b/expo/data/custom_task.py @@ -19,20 +19,34 @@ {task_description} +## More Instructions +- output_dir: {output_dir} +- Besides `submission.csv`, you should also save your output in the output directory. +- Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. + +Do not make visualizations. """ def get_mle_bench_requirements(dataset_dir, data_config, obfuscated=False): + work_dir = data_config["work_dir"] + output_dir = f"{work_dir}/output" + os.makedirs(output_dir, exist_ok=True) + if obfuscated: - instructions = INSTRUCTIONS_OBFUSCATED + instructions = INSTRUCTIONS_OBFUSCATED.format(dataset_dir=dataset_dir, output_dir=output_dir) task_file = "description_obfuscated.md" else: - instructions = INSTRUCTIONS + instructions = INSTRUCTIONS.format(dataset_dir=dataset_dir, output_dir=output_dir) task_file = "description.md" - with open(os.path.join(dataset_dir, task_file)) as f: + with open(os.path.join(dataset_dir, task_file), encoding="utf-8") as f: task_description = f.read() mle_requirement = MLE_REQUIREMENTS.format( - instructions=instructions, additonal_notes=ADDITIONAL_NOTES, task_description=task_description + instructions=instructions, + additonal_notes=ADDITIONAL_NOTES, + task_description=task_description, + output_dir=output_dir, ) + print(mle_requirement) return mle_requirement diff --git a/expo/experimenter/mle_bench/instructions.py b/expo/experimenter/mle_bench/instructions.py index 301b465c1..631f410dc 100644 --- a/expo/experimenter/mle_bench/instructions.py +++ b/expo/experimenter/mle_bench/instructions.py @@ -1,3 +1,6 @@ +SUBMISSION_INSTURCTION = """ +- **Submission checker**: We provide a tool for checking the validity of your submission file, in the form of a server endpoint at `http://localhost:5000/validate`. If you post a submission file to the server (e.g. `curl -X POST -F "file=@${SUBMISSION_FILE}" ${SERVER_URL}`), you will receive a response message telling you if your submission is valid or not (note that this does NOT give you a score). For your convenience, we provide a script `/home/validate_submission.sh` which can help you make this submission to the validation server. +""" INSTRUCTIONS = """ You are participating in *MLE-bench*, an offline version of Kaggle competitions adapted to test the coding ability of agents like yourself. @@ -5,18 +8,17 @@ BENCHMARK INSTRUCTIONS ------ -First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `/home/instructions.txt`. +First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `expo.experimenter.mle_bench.instructions.py`. -- **Competition-specific instructions**: The instructions for the competition are available in `/home/data/description.md`. Read this file to understand the problem setup for the competition you are currently solving. -- **Dataset**: The dataset files for the competition are available in `/home/data/`. -- **Submission**: We have simplified all competition types to use a simple CSV submission format. Regardless of what the competition instructions say, you MUST produce a submission file at `/home/submission/submission.csv`. We will only evaluate submissions found at this path; failure to submit a `submission.csv` at this path will result in a failed attempt. -- **Test set**: In general, since we do not have access to the original test labels, we have constructed our own train/test split for each competition. As such, the dataset you see in `/home/data/` may differ slightly from what is described in the competition instructions. Nevertheless the files available to you should otherwise be similar to the original and you should proceed with the competition task on the train/test splits available to you. -- **Submission checker**: We provide a tool for checking the validity of your submission file, in the form of a server endpoint at `http://localhost:5000/validate`. If you post a submission file to the server (e.g. `curl -X POST -F "file=@${SUBMISSION_FILE}" ${SERVER_URL}`), you will receive a response message telling you if your submission is valid or not (note that this does NOT give you a score). For your convenience, we provide a script `/home/validate_submission.sh` which can help you make this submission to the validation server. +- **Competition-specific instructions**: The instructions for the competition are available in `{dataset_dir}/description.md`. Read this file to understand the problem setup for the competition you are currently solving. +- **Dataset**: The dataset files for the competition are available in `{dataset_dir}`. +- **Submission**: We have simplified all competition types to use a simple CSV submission format. Regardless of what the competition instructions say, you MUST produce a submission file at `{output_dir}/submission.csv`. We will only evaluate submissions found at this path; failure to submit a `submission.csv` at this path will result in a failed attempt. +- **Test set**: In general, since we do not have access to the original test labels, we have constructed our own train/test split for each competition. As such, the dataset you see in `{dataset_dir}` may differ slightly from what is described in the competition instructions. Nevertheless the files available to you should otherwise be similar to the original and you should proceed with the competition task on the train/test splits available to you. - **Cheating**: You are not allowed to view others' solutions to this competition. If we detect that your submission is plagiarized from others, the submission will be disqualified. Your submission will be graded based on the metric in the competition-specific instructions. Your goal is to achieve the best score possible on the competition task. -Wherever there is a conflict between the points above and the competition-specific instructions in `/home/data/description.md`, the above points should always take priority since these are always true for our offline benchmark setup. +Wherever there is a conflict between the points above and the competition-specific instructions in `{dataset_dir}/description.md`, the above points should always take priority since these are always true for our offline benchmark setup. """ INSTRUCTIONS_OBFUSCATED = """ @@ -26,18 +28,17 @@ BENCHMARK INSTRUCTIONS ------ -First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `/home/instructions.txt`. +First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `expo.experimenter.mle_bench.instructions.py`. -- **Task-specific instructions**: The instructions for the task are available in `/home/data/description.md`. Read this file to understand the problem setup for the task you are currently solving. -- **Dataset**: The dataset files for the task are available in `/home/data/`. -- **Submission**: Regardless of what the task instructions say, you MUST produce a submission file at `/home/submission/submission.csv`. We will only evaluate submissions found at this path; failure to submit a `submission.csv` at this path will result in a failed attempt. -- **Test set**: In general, since we do not have access to the original test labels, we have constructed our own train/test split for each task. As such, the dataset you see in `/home/data/` may differ slightly from what is described in the task instructions. Nevertheless the files available to you should otherwise be similar to the original and you should proceed with the task on the train/test splits available to you. -- **Submission checker**: We provide a tool for checking the validity of your submission file, in the form of a server endpoint at `http://localhost:5000/validate`. If you post a submission file to the server (e.g. `curl -X POST -F "file=@${SUBMISSION_FILE}" ${SERVER_URL}`), you will receive a response message telling you if your submission is valid or not (note that this does NOT give you a score). For your convenience, we provide a script `/home/validate_submission.sh` which can help you make this submission to the validation server. +- **Task-specific instructions**: The instructions for the task are available in `{dataset_dir}/description.md`. Read this file to understand the problem setup for the task you are currently solving. +- **Dataset**: The dataset files for the task are available in `{dataset_dir}/`. +- **Submission**: Regardless of what the task instructions say, you MUST produce a submission file at `{output_dir}/submission.csv`. We will only evaluate submissions found at this path; failure to submit a `submission.csv` at this path will result in a failed attempt. +- **Test set**: In general, since we do not have access to the original test labels, we have constructed our own train/test split for each task. As such, the dataset you see in `{dataset_dir}` may differ slightly from what is described in the task instructions. Nevertheless the files available to you should otherwise be similar to the original and you should proceed with the task on the train/test splits available to you. - **Cheating**: You are not allowed to view others' solutions to this task. If we detect that your submission is plagiarized from others, the submission will be disqualified. Your submission will be graded based on the metric in the task-specific instructions. Your goal is to achieve the best score possible on the task. -Wherever there is a conflict between the points above and the task-specific instructions in `/home/data/description.md`, the above points should always take priority since these are always true for our offline benchmark setup. +Wherever there is a conflict between the points above and the task-specific instructions in `{dataset_dir}/description.md`, the above points should always take priority since these are always true for our offline benchmark setup. """ ADDITIONAL_NOTES = """ diff --git a/expo/insights/instruction_generator.py b/expo/insights/instruction_generator.py index 7fe5ceece..835c1ff9d 100644 --- a/expo/insights/instruction_generator.py +++ b/expo/insights/instruction_generator.py @@ -38,7 +38,8 @@ def __init__(self, state, use_fixed_insights, from_scratch): self.state = state self.file_path = state["exp_pool_path"] if state["custom_dataset_dir"]: - self.dataset_info = "xxx" + with open(f"{state['custom_dataset_dir']}/description.md", "r", encoding="utf-8") as file: + self.dataset_info = file.read() else: dataset_info_path = f"{self.data_config['datasets_dir']}/{state['task']}/dataset_info.json" with open(dataset_info_path, "r") as file: diff --git a/expo/insights/solution_designer.py b/expo/insights/solution_designer.py index 2336911db..262caa0f6 100644 --- a/expo/insights/solution_designer.py +++ b/expo/insights/solution_designer.py @@ -5,7 +5,8 @@ DATA_CONFIG = load_data_config() -DATASET_INSIGHT_PROMPT = """ + +DATASET_DESCRIPTION_SELA_PROMPT = """ # Dataset Description {dataset} @@ -14,6 +15,15 @@ # Dataset Head {head} +""" + +DATASET_DESCRIPTION_CUSTOM_PROMPT = """ +# Dataset Description +{dataset_description} +""" + +DATASET_INSIGHT_PROMPT = """ +{description} # Instruction Propose insights to help improve the performance of the model on this dataset. @@ -127,11 +137,15 @@ class SolutionDesigner: async def generate_solutions(self, dataset_info, dataset_name, save_analysis_pool=True): llm = LLM() - context = DATASET_INSIGHT_PROMPT.format( - dataset=dataset_info["description"], - metadata=self.metadata_builder(dataset_info["metadata"]), - head=dataset_info["df_head"], - ) + if type(dataset_info) == dict: + description_prompt = DATASET_DESCRIPTION_SELA_PROMPT.format( + dataset=dataset_info["description"], + metadata=self.metadata_builder(dataset_info["metadata"]), + head=dataset_info["df_head"], + ) + else: + description_prompt = DATASET_DESCRIPTION_CUSTOM_PROMPT.format(dataset_description=dataset_info) + context = DATASET_INSIGHT_PROMPT.format(description=description_prompt) rsp = await llm.aask(context) rsp = clean_json_from_rsp(rsp) analysis_pool = self.process_analysis_pool(json.loads(rsp)) From 1d4a84512039a3d2a29714f640bac08302d004cf Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Mon, 14 Oct 2024 16:12:26 +0800 Subject: [PATCH 116/160] =?UTF-8?q?=E6=94=AF=E6=8C=81=E8=B7=91=E9=80=9Amle?= =?UTF-8?q?=20bench?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- expo/MCTS.py | 6 ++++-- expo/data/custom_task.py | 17 ++++++++++++----- expo/evaluation/evaluation.py | 14 +++++++++++++- expo/run_experiment.py | 5 +++++ 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index a8410748e..749850dd6 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -8,7 +8,7 @@ import numpy as np import pandas as pd -from expo.data.custom_task import get_mle_bench_requirements +from expo.data.custom_task import get_mle_bench_requirements, get_mle_task_id from expo.data.dataset import generate_task_requirement, get_split_dataset_path from expo.evaluation.evaluation import evaluate_score from expo.insights.instruction_generator import InstructionGenerator @@ -35,6 +35,8 @@ def create_initial_state( datasets_dir = args.custom_dataset_dir requirement = get_mle_bench_requirements(args.custom_dataset_dir, data_config) exp_pool_path = None + # external_eval = False # make sure external eval is false if custom dataset is used + task = get_mle_task_id(args.custom_dataset_dir) else: dataset_config = data_config["datasets"][task] datasets_dir = get_split_dataset_path(task, data_config) @@ -120,7 +122,7 @@ def generate_id(self): return f"{self.parent.id}-{num_sibling}" def is_terminal(self): - return int(self.state["start_task_id"]) == self.max_depth + 1 + return int(self.state["start_task_id"]) == self.max_depth + 1 # TODO: Check if this is correct or +1 def is_fully_expanded(self): return len(self.children) > 0 diff --git a/expo/data/custom_task.py b/expo/data/custom_task.py index 14eb6aac2..f66b4aa58 100644 --- a/expo/data/custom_task.py +++ b/expo/data/custom_task.py @@ -22,19 +22,26 @@ ## More Instructions - output_dir: {output_dir} - Besides `submission.csv`, you should also save your output in the output directory. -- Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. - -Do not make visualizations. +- You should split the training data into train and dev set. +- Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. They should be in the same format as the `submission.csv`. +- Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. +**Do not make any plots or visualizations.** """ +def get_mle_task_id(dataset_dir): + return dataset_dir.split("/")[-3] + + def get_mle_bench_requirements(dataset_dir, data_config, obfuscated=False): work_dir = data_config["work_dir"] - output_dir = f"{work_dir}/output" + task = get_mle_task_id(dataset_dir) + output_dir = f"{work_dir}/{task}" + final_output_dir = f"{work_dir}/submission" os.makedirs(output_dir, exist_ok=True) if obfuscated: - instructions = INSTRUCTIONS_OBFUSCATED.format(dataset_dir=dataset_dir, output_dir=output_dir) + instructions = INSTRUCTIONS_OBFUSCATED.format(dataset_dir=dataset_dir, output_dir=final_output_dir) task_file = "description_obfuscated.md" else: instructions = INSTRUCTIONS.format(dataset_dir=dataset_dir, output_dir=output_dir) diff --git a/expo/evaluation/evaluation.py b/expo/evaluation/evaluation.py index 1ba7fa60f..2c19b81fc 100644 --- a/expo/evaluation/evaluation.py +++ b/expo/evaluation/evaluation.py @@ -1,3 +1,5 @@ +from pathlib import Path + import numpy as np from sklearn.metrics import accuracy_score, f1_score, mean_squared_error, roc_auc_score @@ -33,4 +35,14 @@ def node_evaluate_score_sela(node): def node_evaluate_score_mlebench(node): # TODO - return 0 + from mlebench.grade import grade_csv + from mlebench.registry import registry + + competition_id = node.state["task"] + pred_path = node.get_predictions_path("test") + new_registry = registry.set_data_dir(Path(registry.get_data_dir())) + competition = new_registry.get_competition(competition_id) + submission = Path(pred_path) + report = grade_csv(submission, competition).to_dict() + report["submission_path"] = str(submission) + return report diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 53fcdd18c..bf90cb07a 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -60,6 +60,11 @@ def get_di_args(parser): async def main(args): + if args.custom_dataset_dir: + args.external_eval = False + args.eval_func = "mlebench" + args.from_scratch = True + if args.exp_mode == "mcts": experimenter = MCTSExperimenter(args) elif args.exp_mode == "greedy": From 07800be4417c265c0b2ebe76f30bf1b2a7dd20c1 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 15 Oct 2024 14:13:25 +0800 Subject: [PATCH 117/160] allow datasets to be prepared by users --- expo/data/dataset.py | 31 ++++++++++++++++++++++--------- expo/data/hf_data.py | 12 +++++++++--- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/expo/data/dataset.py b/expo/data/dataset.py index 8b0c5b980..91490dcd7 100644 --- a/expo/data/dataset.py +++ b/expo/data/dataset.py @@ -1,3 +1,4 @@ +import argparse import asyncio import json import os @@ -18,22 +19,22 @@ """ USE_AG = """ -7. Please use autogluon for model training with presets='medium_quality', time_limit=None, give dev dataset to tuning_data, and use right eval_metric. +- Please use autogluon for model training with presets='medium_quality', time_limit=None, give dev dataset to tuning_data, and use right eval_metric. """ TEXT_MODALITY = """ -7. You could use models from transformers library for this text dataset. -8. Use gpu if available for faster training. +- You could use models from transformers library for this text dataset. +- Use gpu if available for faster training. """ IMAGE_MODALITY = """ -7. You could use models from transformers/torchvision library for this image dataset. -8. Use gpu if available for faster training. +- You could use models from transformers/torchvision library for this image dataset. +- Use gpu if available for faster training. """ STACKING = """ -7. To avoid overfitting, train a weighted ensemble model such as StackingClassifier or StackingRegressor. -8. You could do some quick model prototyping to see which models work best and then use them in the ensemble. +- To avoid overfitting, train a weighted ensemble model such as StackingClassifier or StackingRegressor. +- You could do some quick model prototyping to see which models work best and then use them in the ensemble. """ @@ -361,10 +362,22 @@ async def process_dataset(dataset, solution_designer: SolutionDesigner, save_ana datasets_dict["datasets"][dataset.name] = dataset_dict +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("--force_update", action="store_true", help="Force update datasets") + parser.add_argument("--save_analysis_pool", action="store_true", help="Save analysis pool") + parser.add_argument( + "--no_save_analysis_pool", dest="save_analysis_pool", action="store_false", help="Do not save analysis pool" + ) + parser.set_defaults(save_analysis_pool=True) + return parser.parse_args() + + if __name__ == "__main__": datasets_dir = DATA_CONFIG["datasets_dir"] - force_update = False - save_analysis_pool = True + args = parse_args() + force_update = args.force_update + save_analysis_pool = args.save_analysis_pool datasets_dict = {"datasets": {}} solution_designer = SolutionDesigner() for dataset_id in OPENML_DATASET_IDS: diff --git a/expo/data/hf_data.py b/expo/data/hf_data.py index 133fbdfa6..a18517d49 100644 --- a/expo/data/hf_data.py +++ b/expo/data/hf_data.py @@ -7,7 +7,12 @@ from datasets import load_dataset from PIL import Image -from expo.data.dataset import ExpDataset, process_dataset, save_datasets_dict_to_yaml +from expo.data.dataset import ( + ExpDataset, + parse_args, + process_dataset, + save_datasets_dict_to_yaml, +) from expo.insights.solution_designer import SolutionDesigner from expo.utils import DATA_CONFIG @@ -116,8 +121,9 @@ def get_dataset_info(self): if __name__ == "__main__": dataset_dir = DATA_CONFIG["datasets_dir"] - save_analysis_pool = True - force_update = False + args = parse_args() + force_update = args.force_update + save_analysis_pool = args.save_analysis_pool datasets_dict = {"datasets": {}} solution_designer = SolutionDesigner() for dataset_meta in HFDATSETS: From d1799829493b66351054a5dcd0051507b50c394c Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 15 Oct 2024 14:14:29 +0800 Subject: [PATCH 118/160] allow special-instruction for mle-bench --- expo/MCTS.py | 6 ++-- expo/README.md | 60 +++++++++++++++++++++++----------------- expo/data/custom_task.py | 7 +++-- expo/utils.py | 2 +- 4 files changed, 45 insertions(+), 30 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 749850dd6..378474b4e 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -33,7 +33,9 @@ def create_initial_state( if args.custom_dataset_dir: dataset_config = None datasets_dir = args.custom_dataset_dir - requirement = get_mle_bench_requirements(args.custom_dataset_dir, data_config) + requirement = get_mle_bench_requirements( + args.custom_dataset_dir, data_config, special_instruction=special_instruction + ) exp_pool_path = None # external_eval = False # make sure external eval is false if custom dataset is used task = get_mle_task_id(args.custom_dataset_dir) @@ -309,7 +311,7 @@ async def simulate(self, node: Node, role=None): node = random.choice(node.children) reward, result_dict = await node.run_node(role) mcts_logger.log("MCTS", f"Simulated node's reward: {reward}") - + # TODO: add new insights return reward def backpropagate(self, node: Node, reward): diff --git a/expo/README.md b/expo/README.md index 598de039d..5b913e415 100644 --- a/expo/README.md +++ b/expo/README.md @@ -6,7 +6,12 @@ ## 1. Data Preparation - Download Datasets:https://deepwisdom.feishu.cn/drive/folder/RVyofv9cvlvtxKdddt2cyn3BnTc?from=from_copylink - +- Download and prepare datasets from scratch: + ``` + cd expo/data + python dataset.py --save_analysis_pool + python hf_data.py --save_analysis_pool + ``` ## 2. Configs @@ -85,6 +90,23 @@ Each baseline needs to produce `dev_predictions.csv`和`test_predictions.csv`. E - Use the function `evaluate_score` to evaluate. +#### MLE-Bench +**Note: mle-bench requires python 3.11 or higher** +``` +git clone https://github.com/openai/mle-bench.git +cd mle-bench +pip install -e . +``` + +``` +mlebench prepare -c --data-dir +``` + +Enter the following command to run the experiment: +``` +python run_experiment.py --exp_mode mcts --custom_dataset_dir --rollouts 10 --from_scratch +``` + ## 5. Baselines ### DS Agent @@ -92,7 +114,7 @@ Each baseline needs to produce `dev_predictions.csv`和`test_predictions.csv`. E git clone https://github.com/guosyjlu/DS-Agent.git ``` -将其deployment/generate.py line46-48行部分修改如下(目的是用deepseek而非GPT的API): +Modify the following lines in deployment/generate.py (lines 46-48) as shown below (the purpose is to use deepseek instead of OpenAI's API): ```python messages = [{"role": "user", "content": prompt}] @@ -120,7 +142,7 @@ elif llm == 'deepseek-coder': completion = raw_completion.split("```python")[1].split("```")[0] ``` -修改完后在新建一个`deployment/test.sh` 分别运行下列两行,`$TASK` 是你要测试的task name +After making the changes, create a new `deployment/test.sh` and run the following two lines separately, where `$TASK` is the name of the task you want to test ``` python -u generate.py --llm deepseek-coder --task $TASK --shot 1 --retrieval > "$TASK".txt 2>&1 @@ -135,7 +157,7 @@ python -u evaluation.py --path "deepseek-coder_True_1" --task $TASK --device 0 git clone https://github.com/WecoAI/aideml.git ``` -修改 `aideml/aide/utils/config.yaml` 内容如下 +Modify `aideml/aide/utils/config.yaml`: ```yaml # path to the task data directory @@ -192,14 +214,14 @@ agent: num_drafts: 5 ``` -由于 deepseek 完全兼容 OpenAI 的 API,修改`base_url`为`自己的url`,`api_key`为`自己的key`即可 +Since Deepseek is compatible to OpenAI's API, change `base_url` into `your own url`,`api_key` into `your api key` ``` -export OPENAI_API_KEY="自己的key" -export OPENAI_BASE_URL="自己的url" +export OPENAI_API_KEY="your api key" +export OPENAI_BASE_URL="your own url" ``` -修改`aideml/aide/backend/__init__.py` 30 行内容如下: +Modify `aideml/aide/backend/__init__.py`'s line 30 and below: ```python model_kwargs = model_kwargs | { @@ -213,7 +235,7 @@ model_kwargs = model_kwargs | { query_func = backend_openai.query ``` -由于 deepseekV2.5 不再支持 system message 使用 function call,修改 `aideml/aide/agent.py` 312 行内容如下: +Since deepseekV2.5 no longer supports system message using function call, modify `aideml/aide/agent.py`'s line 312: ```python response = cast( @@ -228,7 +250,7 @@ response = cast( ) ``` -修改完后 +Modify and install: ``` cd aideml @@ -237,8 +259,8 @@ pip install -e . #### Run -运行下面脚本获取运行结果,在当前目录下将生成一个 log 文件夹以及 workspace 文件夹 -log 文件夹中将包含实验使用配置以及生成方案记录,workspace 文件夹下将保存 aide 最后生成的结果文件 +Run the following script to get the running results, a `log` folder and a `workspace` folder will be generated in the current directory +The `log` folder will contain the experimental configuration and the generated scheme, and the `workspace` folder will save the final results generated by aide ``` python experimenter/aide.py @@ -264,7 +286,6 @@ python run_expriment.py --exp_mode autogluon --task {task_name} --is_multimodal Replace {task_name} with the specific task you want to run. -提供github链接,并说明使用的命令以及参数设置 ### AutoSklearn #### System requirements auto-sklearn has the following system requirements: @@ -295,15 +316,4 @@ python run_experiment.py --exp_mode autosklearn --task titanic For setup, check 4. - `python run_experiment.py --exp_mode base --task titanic --num_experiments 10` - Specifically instruct DI to use AutoGluon: `--special_instruction ag` -- Specifically instruct DI to use the stacking ensemble method: `--special_instruction stacking` - - - - - - - - - - - +- Specifically instruct DI to use the stacking ensemble method: `--special_instruction stacking` \ No newline at end of file diff --git a/expo/data/custom_task.py b/expo/data/custom_task.py index f66b4aa58..e904e9496 100644 --- a/expo/data/custom_task.py +++ b/expo/data/custom_task.py @@ -1,5 +1,6 @@ import os +from expo.data.dataset import SPECIAL_INSTRUCTIONS from expo.experimenter.mle_bench.instructions import ( ADDITIONAL_NOTES, INSTRUCTIONS, @@ -24,7 +25,7 @@ - Besides `submission.csv`, you should also save your output in the output directory. - You should split the training data into train and dev set. - Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. They should be in the same format as the `submission.csv`. -- Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. +- Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. {special_instruction} **Do not make any plots or visualizations.** """ @@ -33,12 +34,13 @@ def get_mle_task_id(dataset_dir): return dataset_dir.split("/")[-3] -def get_mle_bench_requirements(dataset_dir, data_config, obfuscated=False): +def get_mle_bench_requirements(dataset_dir, data_config, obfuscated=False, special_instruction=""): work_dir = data_config["work_dir"] task = get_mle_task_id(dataset_dir) output_dir = f"{work_dir}/{task}" final_output_dir = f"{work_dir}/submission" os.makedirs(output_dir, exist_ok=True) + special_instruction = SPECIAL_INSTRUCTIONS[special_instruction] if obfuscated: instructions = INSTRUCTIONS_OBFUSCATED.format(dataset_dir=dataset_dir, output_dir=final_output_dir) @@ -54,6 +56,7 @@ def get_mle_bench_requirements(dataset_dir, data_config, obfuscated=False): additonal_notes=ADDITIONAL_NOTES, task_description=task_description, output_dir=output_dir, + special_instruction=special_instruction, ) print(mle_requirement) return mle_requirement diff --git a/expo/utils.py b/expo/utils.py index f3381c91c..21b311e7f 100644 --- a/expo/utils.py +++ b/expo/utils.py @@ -111,7 +111,7 @@ async def load_execute_notebook(role): codes = [task.code for task in tasks if task.code] executor = role.execute_code executor.nb = nbformat.v4.new_notebook() - executor.nb_client = NotebookClient(executor.nb, timeout=executor.timeout) + executor.nb_client = NotebookClient(executor.nb, timeout=role.role_timeout) # await executor.build() for code in codes: outputs, success = await executor.run(code) From 0166834ce47396efb8827859eb2e788ab0dd5f6f Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 15 Oct 2024 16:19:02 +0800 Subject: [PATCH 119/160] fix special instruction bug --- expo/data/custom_task.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/expo/data/custom_task.py b/expo/data/custom_task.py index e904e9496..fe366d7ea 100644 --- a/expo/data/custom_task.py +++ b/expo/data/custom_task.py @@ -34,14 +34,16 @@ def get_mle_task_id(dataset_dir): return dataset_dir.split("/")[-3] -def get_mle_bench_requirements(dataset_dir, data_config, obfuscated=False, special_instruction=""): +def get_mle_bench_requirements(dataset_dir, data_config, special_instruction, obfuscated=False): work_dir = data_config["work_dir"] task = get_mle_task_id(dataset_dir) output_dir = f"{work_dir}/{task}" final_output_dir = f"{work_dir}/submission" os.makedirs(output_dir, exist_ok=True) - special_instruction = SPECIAL_INSTRUCTIONS[special_instruction] - + if special_instruction: + special_instruction = SPECIAL_INSTRUCTIONS[special_instruction] + else: + special_instruction = "" if obfuscated: instructions = INSTRUCTIONS_OBFUSCATED.format(dataset_dir=dataset_dir, output_dir=final_output_dir) task_file = "description_obfuscated.md" From 02b4f0aa13a238a8a053f4354048d76dbe99516b Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 15 Oct 2024 16:41:28 +0800 Subject: [PATCH 120/160] add timout to mlebench readme instruction --- expo/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/README.md b/expo/README.md index 5b913e415..6704582b8 100644 --- a/expo/README.md +++ b/expo/README.md @@ -104,7 +104,7 @@ mlebench prepare -c --data-dir Enter the following command to run the experiment: ``` -python run_experiment.py --exp_mode mcts --custom_dataset_dir --rollouts 10 --from_scratch +python run_experiment.py --exp_mode mcts --custom_dataset_dir --rollouts 10 --from_scratch --role_timeout 3600 ``` From 541f8a1b100bce3c88e9a346e090ce86ab01f91f Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Tue, 15 Oct 2024 19:18:30 +0800 Subject: [PATCH 121/160] fix path bug --- expo/evaluation/evaluation.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/expo/evaluation/evaluation.py b/expo/evaluation/evaluation.py index 2c19b81fc..1e58e1725 100644 --- a/expo/evaluation/evaluation.py +++ b/expo/evaluation/evaluation.py @@ -39,8 +39,9 @@ def node_evaluate_score_mlebench(node): from mlebench.registry import registry competition_id = node.state["task"] + data_dir = Path(node.state["custom_dataset_dir"]).parent.parent.parent # prepared/public/../../../ pred_path = node.get_predictions_path("test") - new_registry = registry.set_data_dir(Path(registry.get_data_dir())) + new_registry = registry.set_data_dir(data_dir) competition = new_registry.get_competition(competition_id) submission = Path(pred_path) report = grade_csv(submission, competition).to_dict() From 7794b99005ba84a8819c1564a7ad8e3d1e27c5b0 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 16 Oct 2024 09:50:24 +0800 Subject: [PATCH 122/160] fix: role timeout not passing in --- expo/MCTS.py | 6 +++++- expo/run_experiment.py | 7 +++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 378474b4e..cfb21a61c 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -20,7 +20,11 @@ def initialize_di_root_node(state, reflection: bool = True): role = ResearchAssistant( - node_id="0", start_task_id=state["start_task_id"], use_reflection=reflection, role_dir=state["node_dir"] + node_id="0", + start_task_id=state["start_task_id"], + use_reflection=reflection, + role_dir=state["node_dir"], + role_timeout=state["role_timeout"], ) return role, Node(parent=None, state=state, action=None, value=0) diff --git a/expo/run_experiment.py b/expo/run_experiment.py index bf90cb07a..71529b955 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -9,7 +9,7 @@ from expo.experimenter.mcts import MCTSExperimenter -def get_args(): +def get_args(cmd=True): parser = argparse.ArgumentParser() parser.add_argument("--name", type=str, default="") parser.add_argument( @@ -22,7 +22,10 @@ def get_args(): get_di_args(parser) get_mcts_args(parser) get_aug_exp_args(parser) - return parser.parse_args() + if cmd: + return parser.parse_args() + else: + return parser.parse_args("") def get_mcts_args(parser): From 989a3b4299c4f2520b7fc6893529398b4826b98f Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 16 Oct 2024 10:53:37 +0800 Subject: [PATCH 123/160] allow max depth passing in --- expo/MCTS.py | 2 +- expo/experimenter/mcts.py | 2 +- expo/run_experiment.py | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index cfb21a61c..1eb8a131c 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -274,7 +274,7 @@ class MCTS: # data_path root_node: Node = None children: dict = {} - max_depth: int = 5 + max_depth: int = None c_explore: float = 1.4 c_unvisited: float = 0.8 node_order: list = [] diff --git a/expo/experimenter/mcts.py b/expo/experimenter/mcts.py index 37fc7a071..a42566366 100644 --- a/expo/experimenter/mcts.py +++ b/expo/experimenter/mcts.py @@ -29,7 +29,7 @@ def __init__(self, args, tree_mode=None, **kwargs): async def run_experiment(self): use_fixed_insights = self.args.use_fixed_insights - depth = 5 + depth = self.args.max_depth if self.tree_mode == "greedy": mcts = Greedy(root_node=None, max_depth=depth, use_fixed_insights=use_fixed_insights) elif self.tree_mode == "random": diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 71529b955..4e6b41fd7 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -44,6 +44,7 @@ def get_mcts_args(parser): parser.set_defaults(external_eval=True) parser.add_argument("--eval_func", type=str, default="sela", choices=["sela", "mlebench"]) parser.add_argument("--custom_dataset_dir", type=str, default=None) + parser.add_argument("--max_depth", type=int, default=4) def get_aug_exp_args(parser): From df7a04dd1993f3fa7266afb2719d2995b080ef51 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Wed, 16 Oct 2024 14:28:27 +0800 Subject: [PATCH 124/160] output dev set score --- expo/data/custom_task.py | 1 + 1 file changed, 1 insertion(+) diff --git a/expo/data/custom_task.py b/expo/data/custom_task.py index fe366d7ea..c4dd0012c 100644 --- a/expo/data/custom_task.py +++ b/expo/data/custom_task.py @@ -24,6 +24,7 @@ - output_dir: {output_dir} - Besides `submission.csv`, you should also save your output in the output directory. - You should split the training data into train and dev set. +- You should use the dev set to improve your model. Print the final dev set score after training. - Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. They should be in the same format as the `submission.csv`. - Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. {special_instruction} **Do not make any plots or visualizations.** From 38daf24c33f9e89dea3fa53e772924cd0daae16c Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 17 Oct 2024 09:55:37 +0800 Subject: [PATCH 125/160] rename task if custom_data_dir is used --- expo/run_experiment.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 4e6b41fd7..c977b4dc9 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -1,6 +1,7 @@ import argparse import asyncio +from expo.data.custom_task import get_mle_task_id from expo.experimenter.aug import AugExperimenter from expo.experimenter.autogluon import GluonExperimenter from expo.experimenter.autosklearn import AutoSklearnExperimenter @@ -68,6 +69,7 @@ async def main(args): args.external_eval = False args.eval_func = "mlebench" args.from_scratch = True + args.task = get_mle_task_id(args.custom_dataset_dir) if args.exp_mode == "mcts": experimenter = MCTSExperimenter(args) From a46f5753612c9e5e5e6a948f309873f994b16174 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 17 Oct 2024 10:11:31 +0800 Subject: [PATCH 126/160] clean up input argument --- expo/MCTS.py | 14 +++++++------- expo/experimenter/custom.py | 4 +--- expo/experimenter/experimenter.py | 3 --- expo/run_experiment.py | 17 +++++++++-------- 4 files changed, 17 insertions(+), 21 deletions(-) diff --git a/expo/MCTS.py b/expo/MCTS.py index 1eb8a131c..8778554ed 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -29,16 +29,14 @@ def initialize_di_root_node(state, reflection: bool = True): return role, Node(parent=None, state=state, action=None, value=0) -def create_initial_state( - task, start_task_id, data_config, low_is_better: bool, name: str, special_instruction: str, args -): +def create_initial_state(task, start_task_id, data_config, args): external_eval = args.external_eval if args.custom_dataset_dir: dataset_config = None datasets_dir = args.custom_dataset_dir requirement = get_mle_bench_requirements( - args.custom_dataset_dir, data_config, special_instruction=special_instruction + args.custom_dataset_dir, data_config, special_instruction=args.special_instruction ) exp_pool_path = None # external_eval = False # make sure external eval is false if custom dataset is used @@ -46,20 +44,22 @@ def create_initial_state( else: dataset_config = data_config["datasets"][task] datasets_dir = get_split_dataset_path(task, data_config) - requirement = generate_task_requirement(task, data_config, is_di=True, special_instruction=special_instruction) + requirement = generate_task_requirement( + task, data_config, is_di=True, special_instruction=args.special_instruction + ) exp_pool_path = get_exp_pool_path(task, data_config, pool_name="ds_analysis_pool") initial_state = { "task": task, "work_dir": data_config["work_dir"], - "node_dir": os.path.join(data_config["work_dir"], data_config["role_dir"], f"{task}{name}"), + "node_dir": os.path.join(data_config["work_dir"], data_config["role_dir"], f"{task}{args.name}"), "dataset_config": dataset_config, "datasets_dir": datasets_dir, # won't be used if external eval is used "exp_pool_path": exp_pool_path, "requirement": requirement, "has_run": False, "start_task_id": start_task_id, - "low_is_better": low_is_better, + "low_is_better": args.low_is_better, "role_timeout": args.role_timeout, "external_eval": external_eval, "custom_dataset_dir": args.custom_dataset_dir, diff --git a/expo/experimenter/custom.py b/expo/experimenter/custom.py index 92b7dafa2..f245499ca 100644 --- a/expo/experimenter/custom.py +++ b/expo/experimenter/custom.py @@ -21,9 +21,7 @@ def __init__(self, args, **kwargs): self.task, start_task_id=1, data_config=self.data_config, - low_is_better=self.low_is_better, - name=self.name, - special_instruction=self.args.special_instruction, + args=self.args, ) def run_experiment(self): diff --git a/expo/experimenter/experimenter.py b/expo/experimenter/experimenter.py index 417adabad..4a0b8413e 100644 --- a/expo/experimenter/experimenter.py +++ b/expo/experimenter/experimenter.py @@ -24,9 +24,6 @@ def __init__(self, args, **kwargs): self.args.task, start_task_id=self.start_task_id, data_config=self.data_config, - low_is_better=self.args.low_is_better, - name=self.args.name, - special_instruction=self.args.special_instruction, args=self.args, ) diff --git a/expo/run_experiment.py b/expo/run_experiment.py index c977b4dc9..be891814d 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -24,9 +24,16 @@ def get_args(cmd=True): get_mcts_args(parser) get_aug_exp_args(parser) if cmd: - return parser.parse_args() + args = parser.parse_args() else: - return parser.parse_args("") + args = parser.parse_args("") + + if args.custom_dataset_dir: + args.external_eval = False + args.eval_func = "mlebench" + args.from_scratch = True + args.task = get_mle_task_id(args.custom_dataset_dir) + return args def get_mcts_args(parser): @@ -65,12 +72,6 @@ def get_di_args(parser): async def main(args): - if args.custom_dataset_dir: - args.external_eval = False - args.eval_func = "mlebench" - args.from_scratch = True - args.task = get_mle_task_id(args.custom_dataset_dir) - if args.exp_mode == "mcts": experimenter = MCTSExperimenter(args) elif args.exp_mode == "greedy": From 0f01c07b836fd756ce66e6a882fab8c0a1a27fff Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 17 Oct 2024 10:27:33 +0800 Subject: [PATCH 127/160] add tree visualization script and function --- expo/evaluation/visualize_mcts.py | 108 ++++++++++++++++++++++++++- expo/scripts/visualize_experiment.py | 23 ++++++ 2 files changed, 128 insertions(+), 3 deletions(-) create mode 100644 expo/scripts/visualize_experiment.py diff --git a/expo/evaluation/visualize_mcts.py b/expo/evaluation/visualize_mcts.py index d310036c0..e429789fd 100644 --- a/expo/evaluation/visualize_mcts.py +++ b/expo/evaluation/visualize_mcts.py @@ -1,5 +1,8 @@ import textwrap +import matplotlib.pyplot as plt +import networkx as nx + from expo.MCTS import Node NODE_TEMPLATE = """\ @@ -11,6 +14,9 @@ """ +NODE_SIZE = 12000 +NODE_FONT_SIZE = 18 + def get_role_plans(role): plans = role.planner.plan.tasks @@ -42,7 +48,7 @@ def visualize_node(node: Node, previous_plans=None): id=node_id, plans=instruct_plans_text, simulated=simulated, score=score, num_visits=num_visits ) - def visualize_tree(node, depth=0, previous_plans=None): + def visualize_tree_text(node, depth=0, previous_plans=None): text = "" if node is not None: text += visualize_node(node, previous_plans) @@ -50,10 +56,106 @@ def visualize_tree(node, depth=0, previous_plans=None): code_set.update({task.instruction for task in role.planner.plan.tasks}) previous_plans = get_role_plans(role) for child in node.children: - text += textwrap.indent(visualize_tree(child, depth + 1, previous_plans), "\t") + text += textwrap.indent(visualize_tree_text(child, depth + 1, previous_plans), "\t") return text num_simulations = node.visited text = f"Number of simulations: {num_simulations}\n" - text += visualize_tree(node) + text += visualize_tree_text(node) return text, len(code_set) + + +def get_node_color(node): + if node["visits"] == 0: + return "#D3D3D3" + else: + # The higher the avg_value, the more intense the color + # avg_value is between 0 and 1 + avg_value = node["avg_value"] + # Convert avg_value to a color ranging from red (low) to green (high) + red = int(255 * (1 - avg_value)) + green = int(255 * avg_value) + return f"#{red:02X}{green:02X}00" + + +def visualize_tree(graph, save_path=""): + # Use a hierarchical layout for tree-like visualization + pos = nx.spring_layout(graph, k=0.9, iterations=50) + + plt.figure(figsize=(30, 20)) # Further increase figure size for better visibility + + # Calculate node levels + root = "0" + levels = nx.single_source_shortest_path_length(graph, root) + max_level = max(levels.values()) + + # Adjust y-coordinates based on levels and x-coordinates to prevent overlap + nodes_by_level = {} + for node, level in levels.items(): + if level not in nodes_by_level: + nodes_by_level[level] = [] + nodes_by_level[level].append(node) + + for level, nodes in nodes_by_level.items(): + y = 1 - level / max_level + x_step = 1.0 / (len(nodes) + 1) + for i, node in enumerate(sorted(nodes)): + pos[node] = ((i + 1) * x_step, y) + + # Draw edges + nx.draw_networkx_edges(graph, pos, edge_color="gray", arrows=True, arrowsize=40, width=3) + + # Draw nodes + node_colors = [get_node_color(graph.nodes[node]) for node in graph.nodes] + nx.draw_networkx_nodes(graph, pos, node_size=NODE_SIZE, node_color=node_colors) + + # Add labels to nodes + labels = nx.get_node_attributes(graph, "label") + nx.draw_networkx_labels(graph, pos, labels, font_size=NODE_FONT_SIZE) + + # Add instructions to the right side of nodes + instructions = nx.get_node_attributes(graph, "instruction") + for node, (x, y) in pos.items(): + wrapped_text = textwrap.fill(instructions[node], width=30) # Adjust width as needed + plt.text(x + 0.05, y, wrapped_text, fontsize=15, ha="left", va="center") + + plt.title("MCTS Tree Visualization", fontsize=40) + plt.axis("off") # Turn off axis + plt.tight_layout() + if save_path: + plt.savefig(save_path) + plt.show() + + +def build_tree_recursive(graph, parent_id, node, start_task_id=2): + """ + Recursively builds the entire tree starting from the root node. + Adds nodes and edges to the NetworkX graph. + """ + role = node.load_role() + depth = node.get_depth() + if depth == 0: + instruction = "\n\n".join([role.planner.plan.tasks[i].instruction for i in range(start_task_id)]) + else: + instruction = role.planner.plan.tasks[depth + start_task_id - 1].instruction + print(instruction) + # Add the current node with attributes to the graph + dev_score = node.raw_reward.get("dev_score", 0) * 100 + avg_score = node.avg_value() * 100 + graph.add_node( + parent_id, + label=f"{node.id}\nAvg: {avg_score:.1f}\nScore: {dev_score:.1f}\nVisits: {node.visited}", + avg_value=node.avg_value(), + dev_score=dev_score, + visits=node.visited, + instruction=instruction, + ) + # Stopping condition: if the node has no children, return + if not node.children: + return + + # Recursively create all child nodes + for i, child in enumerate(node.children): + child_id = f"{parent_id}-{i}" + graph.add_edge(parent_id, child_id) + build_tree_recursive(graph, child_id, child) diff --git a/expo/scripts/visualize_experiment.py b/expo/scripts/visualize_experiment.py new file mode 100644 index 000000000..c06b1eeab --- /dev/null +++ b/expo/scripts/visualize_experiment.py @@ -0,0 +1,23 @@ +import networkx as nx + +from expo.evaluation.visualize_mcts import build_tree_recursive, visualize_tree +from expo.MCTS import MCTS, create_initial_state, initialize_di_root_node +from expo.run_experiment import get_args +from expo.utils import DATA_CONFIG + +if __name__ == "__main__": + args = get_args() + data_config = DATA_CONFIG + state = create_initial_state(args.task, 0, data_config, args=args) + role, node = initialize_di_root_node(state) + mcts = MCTS( + root_node=node, + max_depth=5, + use_fixed_insights=False, + ) + + mcts.load_tree() + root = mcts.root_node + G = nx.DiGraph() + tree = build_tree_recursive(G, "0", root) + visualize_tree(tree, save_path="../results/tree.png") From 1d22466ac53f848dfed275c6dfb08425efd130b3 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 17 Oct 2024 10:29:46 +0800 Subject: [PATCH 128/160] change dir for tree fig --- expo/scripts/visualize_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/scripts/visualize_experiment.py b/expo/scripts/visualize_experiment.py index c06b1eeab..940d1f11b 100644 --- a/expo/scripts/visualize_experiment.py +++ b/expo/scripts/visualize_experiment.py @@ -20,4 +20,4 @@ root = mcts.root_node G = nx.DiGraph() tree = build_tree_recursive(G, "0", root) - visualize_tree(tree, save_path="../results/tree.png") + visualize_tree(tree, save_path="results/tree.png") From 6646983a255dc81057be91faa47d7bb6d98bae93 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 17 Oct 2024 10:34:05 +0800 Subject: [PATCH 129/160] fix visualization bug --- expo/scripts/visualize_experiment.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expo/scripts/visualize_experiment.py b/expo/scripts/visualize_experiment.py index 940d1f11b..42b4490ec 100644 --- a/expo/scripts/visualize_experiment.py +++ b/expo/scripts/visualize_experiment.py @@ -19,5 +19,5 @@ mcts.load_tree() root = mcts.root_node G = nx.DiGraph() - tree = build_tree_recursive(G, "0", root) - visualize_tree(tree, save_path="results/tree.png") + build_tree_recursive(G, "0", root) + visualize_tree(G, save_path="results/tree.png") From 510136ab17e32e05d5a443fe832d57a7ba154605 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 17 Oct 2024 10:37:16 +0800 Subject: [PATCH 130/160] allowing whether to show instructions --- expo/evaluation/visualize_mcts.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/expo/evaluation/visualize_mcts.py b/expo/evaluation/visualize_mcts.py index e429789fd..6a8869670 100644 --- a/expo/evaluation/visualize_mcts.py +++ b/expo/evaluation/visualize_mcts.py @@ -78,7 +78,7 @@ def get_node_color(node): return f"#{red:02X}{green:02X}00" -def visualize_tree(graph, save_path=""): +def visualize_tree(graph, show_instructions=False, save_path=""): # Use a hierarchical layout for tree-like visualization pos = nx.spring_layout(graph, k=0.9, iterations=50) @@ -113,11 +113,12 @@ def visualize_tree(graph, save_path=""): labels = nx.get_node_attributes(graph, "label") nx.draw_networkx_labels(graph, pos, labels, font_size=NODE_FONT_SIZE) - # Add instructions to the right side of nodes - instructions = nx.get_node_attributes(graph, "instruction") - for node, (x, y) in pos.items(): - wrapped_text = textwrap.fill(instructions[node], width=30) # Adjust width as needed - plt.text(x + 0.05, y, wrapped_text, fontsize=15, ha="left", va="center") + if show_instructions: + # Add instructions to the right side of nodes + instructions = nx.get_node_attributes(graph, "instruction") + for node, (x, y) in pos.items(): + wrapped_text = textwrap.fill(instructions[node], width=30) # Adjust width as needed + plt.text(x + 0.05, y, wrapped_text, fontsize=15, ha="left", va="center") plt.title("MCTS Tree Visualization", fontsize=40) plt.axis("off") # Turn off axis From 06710fbc18a9298ccf257edf7930100cce766bb9 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 17 Oct 2024 15:24:22 +0800 Subject: [PATCH 131/160] fix typo in readme.md --- expo/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/README.md b/expo/README.md index 6704582b8..1d0d8476d 100644 --- a/expo/README.md +++ b/expo/README.md @@ -65,7 +65,7 @@ pip install -r requirements.txt If the dataset has reg metric, remember to use `--low_is_better`: -- `python run_experiment.py --exp_mode mcts --task house_prices --rollouts 10 --low_is_better` +- `python run_experiment.py --exp_mode mcts --task house-prices --rollouts 10 --low_is_better` In addition to the generated insights, include the fixed insights saved in `expo/insights/fixed_insights.json` From 852fbc58ee9eaa77160dcd1bcdcd533c896ed6a9 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 17 Oct 2024 17:47:41 +0800 Subject: [PATCH 132/160] automatically update args.low_is_better for mle-bench --- expo/data/custom_task.py | 9 +++++++++ expo/run_experiment.py | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/expo/data/custom_task.py b/expo/data/custom_task.py index c4dd0012c..f3cd433f5 100644 --- a/expo/data/custom_task.py +++ b/expo/data/custom_task.py @@ -35,6 +35,15 @@ def get_mle_task_id(dataset_dir): return dataset_dir.split("/")[-3] +def get_mle_is_lower_better(task): + from mlebench.data import get_leaderboard + from mlebench.registry import registry + + competition = registry.get_competition(task) + competition_leaderboard = get_leaderboard(competition) + return competition.grader.is_lower_better(competition_leaderboard) + + def get_mle_bench_requirements(dataset_dir, data_config, special_instruction, obfuscated=False): work_dir = data_config["work_dir"] task = get_mle_task_id(dataset_dir) diff --git a/expo/run_experiment.py b/expo/run_experiment.py index be891814d..7b49e6738 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -1,7 +1,7 @@ import argparse import asyncio -from expo.data.custom_task import get_mle_task_id +from expo.data.custom_task import get_mle_is_lower_better, get_mle_task_id from expo.experimenter.aug import AugExperimenter from expo.experimenter.autogluon import GluonExperimenter from expo.experimenter.autosklearn import AutoSklearnExperimenter @@ -33,6 +33,8 @@ def get_args(cmd=True): args.eval_func = "mlebench" args.from_scratch = True args.task = get_mle_task_id(args.custom_dataset_dir) + args.low_is_better = get_mle_is_lower_better(args.task) + print("low_is_better:", args.low_is_better) return args From 6f437bb76d3897a687206ce9a7e9392d149dffc7 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 17 Oct 2024 17:49:31 +0800 Subject: [PATCH 133/160] automatically change low_is_better for rmse --- expo/MCTS.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/expo/MCTS.py b/expo/MCTS.py index 8778554ed..2ce559ae0 100644 --- a/expo/MCTS.py +++ b/expo/MCTS.py @@ -43,6 +43,8 @@ def create_initial_state(task, start_task_id, data_config, args): task = get_mle_task_id(args.custom_dataset_dir) else: dataset_config = data_config["datasets"][task] + if dataset_config["metric"] == "rmse": + args.low_is_better = True datasets_dir = get_split_dataset_path(task, data_config) requirement = generate_task_requirement( task, data_config, is_di=True, special_instruction=args.special_instruction From de42e32b8e5a5293365852fde63ccaf0f69d4d95 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Thu, 17 Oct 2024 17:55:24 +0800 Subject: [PATCH 134/160] automatically update low_is_better for our task --- expo/insights/instruction_generator.py | 4 +++- expo/run_experiment.py | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/expo/insights/instruction_generator.py b/expo/insights/instruction_generator.py index 835c1ff9d..78b32e45d 100644 --- a/expo/insights/instruction_generator.py +++ b/expo/insights/instruction_generator.py @@ -41,7 +41,9 @@ def __init__(self, state, use_fixed_insights, from_scratch): with open(f"{state['custom_dataset_dir']}/description.md", "r", encoding="utf-8") as file: self.dataset_info = file.read() else: - dataset_info_path = f"{self.data_config['datasets_dir']}/{state['task']}/dataset_info.json" + dataset_info_path = ( + f"{self.data_config['datasets_dir']}/{state['dataset_config']['dataset']}/dataset_info.json" + ) with open(dataset_info_path, "r") as file: self.dataset_info = json.load(file) self.use_fixed_insights = use_fixed_insights diff --git a/expo/run_experiment.py b/expo/run_experiment.py index 7b49e6738..68c3b35d4 100644 --- a/expo/run_experiment.py +++ b/expo/run_experiment.py @@ -34,7 +34,6 @@ def get_args(cmd=True): args.from_scratch = True args.task = get_mle_task_id(args.custom_dataset_dir) args.low_is_better = get_mle_is_lower_better(args.task) - print("low_is_better:", args.low_is_better) return args From 1915d19f24156073db688ab1a4472d5d274ac126 Mon Sep 17 00:00:00 2001 From: duiyipan Date: Thu, 17 Oct 2024 21:28:49 +0800 Subject: [PATCH 135/160] update aide readme --- expo/README.md | 39 +-------------------------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/expo/README.md b/expo/README.md index 598de039d..a25f384b6 100644 --- a/expo/README.md +++ b/expo/README.md @@ -135,46 +135,15 @@ python -u evaluation.py --path "deepseek-coder_True_1" --task $TASK --device 0 git clone https://github.com/WecoAI/aideml.git ``` -修改 `aideml/aide/utils/config.yaml` 内容如下 +修改 `aideml/aide/utils/config.yaml` 其中的 `step` `k_fold_validation` `code model` `feedback model` 参数如下 ```yaml -# path to the task data directory -data_dir: null - -# either provide a path to a plaintext file describing the task -desc_file: null -# or provide the task goal (and optionally evaluation information) as arguments -goal: null -eval: null - -log_dir: logs -workspace_dir: workspaces - -# whether to unzip any archives in the data directory -preprocess_data: True -# whether to copy the data to the workspace directory (otherwise it will be symlinked) -# copying is recommended to prevent the agent from accidentally modifying the original data -copy_data: True - -exp_name: null # a random experiment name will be generated if not provided - -# settings for code execution -exec: - timeout: 3600 - agent_file_name: runfile.py - format_tb_ipython: False - # agent hyperparams agent: # how many improvement iterations to run steps: 10 # whether to instruct the agent to use CV (set to 1 to disable) k_fold_validation: 1 - # whether to instruct the agent to generate a prediction function - expose_prediction: False - # whether to provide the agent with a preview of the data - data_preview: True - # LLM settings for coding code: model: deepseek-coder @@ -184,12 +153,6 @@ agent: feedback: model: deepseek-coder temp: 0.5 - - # hyperparameters for the tree search - search: - max_debug_depth: 3 - debug_prob: 0.5 - num_drafts: 5 ``` 由于 deepseek 完全兼容 OpenAI 的 API,修改`base_url`为`自己的url`,`api_key`为`自己的key`即可 From 358a97e34cb388455f7d1dbfd5f3492174e10b88 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 18 Oct 2024 11:16:28 +0800 Subject: [PATCH 136/160] modify prompt --- expo/data/custom_task.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/expo/data/custom_task.py b/expo/data/custom_task.py index f3cd433f5..032fcb8ac 100644 --- a/expo/data/custom_task.py +++ b/expo/data/custom_task.py @@ -21,11 +21,11 @@ {task_description} ## More Instructions -- output_dir: {output_dir} -- Besides `submission.csv`, you should also save your output in the output directory. - You should split the training data into train and dev set. - You should use the dev set to improve your model. Print the final dev set score after training. -- Save the prediction results of BOTH the dev set and test set in `dev_predictions.csv` and `test_predictions.csv` respectively in the output directory. They should be in the same format as the `submission.csv`. +- output_dir: {output_dir} +- Besides `submission.csv`, you should also save your `test_predictions.csv` and `dev_predictions.csv` in the output directory. +- Note that `test_predictions.csv` should be identical to `submission.csv`. - Perform data analysis, data preprocessing, feature engineering, and modeling to predict the target. {special_instruction} **Do not make any plots or visualizations.** """ From 7a38165e6b20272d44b511c1f43d9e75425c1690 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 18 Oct 2024 14:04:57 +0800 Subject: [PATCH 137/160] add seed --- expo/data/custom_task.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/data/custom_task.py b/expo/data/custom_task.py index 032fcb8ac..c2bf5c710 100644 --- a/expo/data/custom_task.py +++ b/expo/data/custom_task.py @@ -21,7 +21,7 @@ {task_description} ## More Instructions -- You should split the training data into train and dev set. +- You should split the training data into train and dev set with a seed of 42. - You should use the dev set to improve your model. Print the final dev set score after training. - output_dir: {output_dir} - Besides `submission.csv`, you should also save your `test_predictions.csv` and `dev_predictions.csv` in the output directory. From f97ad720b0ed9b78921cf57662ca7dbf9430ebb8 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 18 Oct 2024 14:29:36 +0800 Subject: [PATCH 138/160] add task arg for tree visualization --- expo/scripts/visualize_experiment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/expo/scripts/visualize_experiment.py b/expo/scripts/visualize_experiment.py index 42b4490ec..e2443d0fd 100644 --- a/expo/scripts/visualize_experiment.py +++ b/expo/scripts/visualize_experiment.py @@ -20,4 +20,4 @@ root = mcts.root_node G = nx.DiGraph() build_tree_recursive(G, "0", root) - visualize_tree(G, save_path="results/tree.png") + visualize_tree(G, save_path=f"results/{args.task}-tree.png") From 5eaa072d8d3abd2e5a8f587179e2ae7e5c9bc023 Mon Sep 17 00:00:00 2001 From: Yizhou Chi Date: Fri, 18 Oct 2024 16:05:24 +0800 Subject: [PATCH 139/160] add an instruction to avoid splitting instruction being replaced --- expo/insights/instruction_generator.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/expo/insights/instruction_generator.py b/expo/insights/instruction_generator.py index 78b32e45d..ab9b2cc67 100644 --- a/expo/insights/instruction_generator.py +++ b/expo/insights/instruction_generator.py @@ -8,7 +8,7 @@ from metagpt.llm import LLM from metagpt.schema import Message -REFLECTION_SYSTEM_MSG = "As a Kaggle grandmaster participating in a competition, you need to analyze your experience and propose evolutionary points that are more likely to improve the performance of baseline code." +REFLECTION_SYSTEM_MSG = "As a Kaggle Grandmaster competing in a challenge, your task is to suggest potential evolutionary improvements that could enhance the performance of the baseline code." CHANGE_INSTRUCTION = """ # Original instruction @@ -17,7 +17,9 @@ # Insights {insights} -Rewrite the original instruction according to the insights +Rewrite the original instruction according to the insights +(If the original instruction involves splitting the data, ensure that your insights are integrated with the data split instructions, +rather than replacing them.) # Expected Output Hard Format ```json From 3a8fdc6a5e5617a7fd561ce92d0f4dab07377f26 Mon Sep 17 00:00:00 2001 From: garylin2099 <44922098+garylin2099@users.noreply.github.com> Date: Mon, 21 Oct 2024 10:13:36 +0000 Subject: [PATCH 140/160] add visit order --- expo/evaluation/visualize_mcts.py | 7 ++++--- expo/scripts/visualize_experiment.py | 4 +++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/expo/evaluation/visualize_mcts.py b/expo/evaluation/visualize_mcts.py index 6a8869670..44f5ec5f5 100644 --- a/expo/evaluation/visualize_mcts.py +++ b/expo/evaluation/visualize_mcts.py @@ -128,7 +128,7 @@ def visualize_tree(graph, show_instructions=False, save_path=""): plt.show() -def build_tree_recursive(graph, parent_id, node, start_task_id=2): +def build_tree_recursive(graph, parent_id, node, node_order, start_task_id=2): """ Recursively builds the entire tree starting from the root node. Adds nodes and edges to the NetworkX graph. @@ -143,9 +143,10 @@ def build_tree_recursive(graph, parent_id, node, start_task_id=2): # Add the current node with attributes to the graph dev_score = node.raw_reward.get("dev_score", 0) * 100 avg_score = node.avg_value() * 100 + order = node_order.index(node.id) if node.id in node_order else "" graph.add_node( parent_id, - label=f"{node.id}\nAvg: {avg_score:.1f}\nScore: {dev_score:.1f}\nVisits: {node.visited}", + label=f"{node.id}\nAvg: {avg_score:.1f}\nScore: {dev_score:.1f}\nVisits: {node.visited}\nOrder: {order}", avg_value=node.avg_value(), dev_score=dev_score, visits=node.visited, @@ -159,4 +160,4 @@ def build_tree_recursive(graph, parent_id, node, start_task_id=2): for i, child in enumerate(node.children): child_id = f"{parent_id}-{i}" graph.add_edge(parent_id, child_id) - build_tree_recursive(graph, child_id, child) + build_tree_recursive(graph, child_id, child, node_order) diff --git a/expo/scripts/visualize_experiment.py b/expo/scripts/visualize_experiment.py index e2443d0fd..6cd84a0de 100644 --- a/expo/scripts/visualize_experiment.py +++ b/expo/scripts/visualize_experiment.py @@ -17,7 +17,9 @@ ) mcts.load_tree() + mcts.load_node_order() root = mcts.root_node + node_order = mcts.node_order G = nx.DiGraph() - build_tree_recursive(G, "0", root) + build_tree_recursive(G, "0", root, node_order) visualize_tree(G, save_path=f"results/{args.task}-tree.png") From 7c5b29de63f55ae543f0cc98201524edfb55c550 Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Tue, 22 Oct 2024 21:33:31 +0800 Subject: [PATCH 141/160] rename expo folder to sela --- .gitignore | 4 ++-- expo/results/PLACEHOLDER | 0 expo/results/tree/TREE | 0 {expo => sela}/Greedy.py | 2 +- {expo => sela}/MCTS.py | 12 ++++++------ {expo => sela}/README.md | 0 {expo => sela}/data.yaml | 0 {expo => sela}/data/custom_task.py | 4 ++-- {expo => sela}/data/dataset.py | 4 ++-- {expo => sela}/data/hf_data.py | 6 +++--- {expo => sela}/datasets.yaml | 0 {expo => sela}/evaluation/evaluation.py | 0 {expo => sela}/evaluation/visualize_mcts.py | 2 +- {expo => sela}/experimenter/__init__.py | 0 {expo => sela}/experimenter/aide.py | 0 {expo => sela}/experimenter/aug.py | 8 ++++---- {expo => sela}/experimenter/autogluon.py | 2 +- {expo => sela}/experimenter/autosklearn.py | 4 ++-- {expo => sela}/experimenter/custom.py | 6 +++--- {expo => sela}/experimenter/experimenter.py | 8 ++++---- {expo => sela}/experimenter/mcts.py | 10 +++++----- .../experimenter/mle_bench/instructions.py | 4 ++-- {expo => sela}/insights/fixed_insights.json | 0 {expo => sela}/insights/instruction_generator.py | 4 ++-- {expo => sela}/insights/solution_designer.py | 2 +- {expo => sela}/requirements.txt | 0 {expo => sela}/research_assistant.py | 2 +- {expo => sela}/run_experiment.py | 14 +++++++------- {expo => sela}/scripts/run_cls.sh | 0 {expo => sela}/scripts/run_cls_mod.sh | 0 {expo => sela}/scripts/run_reg.sh | 0 {expo => sela}/scripts/visualize_experiment.py | 8 ++++---- {expo => sela}/utils.py | 0 33 files changed, 53 insertions(+), 53 deletions(-) delete mode 100644 expo/results/PLACEHOLDER delete mode 100644 expo/results/tree/TREE rename {expo => sela}/Greedy.py (95%) rename {expo => sela}/MCTS.py (97%) rename {expo => sela}/README.md (100%) rename {expo => sela}/data.yaml (100%) rename {expo => sela}/data/custom_task.py (95%) rename {expo => sela}/data/dataset.py (99%) rename {expo => sela}/data/hf_data.py (97%) rename {expo => sela}/datasets.yaml (100%) rename {expo => sela}/evaluation/evaluation.py (100%) rename {expo => sela}/evaluation/visualize_mcts.py (99%) rename {expo => sela}/experimenter/__init__.py (100%) rename {expo => sela}/experimenter/aide.py (100%) rename {expo => sela}/experimenter/aug.py (91%) rename {expo => sela}/experimenter/autogluon.py (99%) rename {expo => sela}/experimenter/autosklearn.py (96%) rename {expo => sela}/experimenter/custom.py (93%) rename {expo => sela}/experimenter/experimenter.py (96%) rename {expo => sela}/experimenter/mcts.py (93%) rename {expo => sela}/experimenter/mle_bench/instructions.py (98%) rename {expo => sela}/insights/fixed_insights.json (100%) rename {expo => sela}/insights/instruction_generator.py (98%) rename {expo => sela}/insights/solution_designer.py (98%) rename {expo => sela}/requirements.txt (100%) rename {expo => sela}/research_assistant.py (99%) rename {expo => sela}/run_experiment.py (90%) rename {expo => sela}/scripts/run_cls.sh (100%) rename {expo => sela}/scripts/run_cls_mod.sh (100%) rename {expo => sela}/scripts/run_reg.sh (100%) rename {expo => sela}/scripts/visualize_experiment.py (74%) rename {expo => sela}/utils.py (100%) diff --git a/.gitignore b/.gitignore index 6e1fc7f74..f22e826a3 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,7 @@ share/python-wheels/ MANIFEST metagpt/tools/schemas/ examples/data/search_kb/*.json -expo/AutogluonModels +sela/AutogluonModels # PyInstaller # Usually these files are written by a python scripts from a template @@ -189,4 +189,4 @@ cov.xml *-structure.json *.dot .python-version -expo/results/* +sela/results/* diff --git a/expo/results/PLACEHOLDER b/expo/results/PLACEHOLDER deleted file mode 100644 index e69de29bb..000000000 diff --git a/expo/results/tree/TREE b/expo/results/tree/TREE deleted file mode 100644 index e69de29bb..000000000 diff --git a/expo/Greedy.py b/sela/Greedy.py similarity index 95% rename from expo/Greedy.py rename to sela/Greedy.py index 8c8d865cd..05bf1dfa5 100644 --- a/expo/Greedy.py +++ b/sela/Greedy.py @@ -1,6 +1,6 @@ import random -from expo.MCTS import MCTS +from sela.MCTS import MCTS class Greedy(MCTS): diff --git a/expo/MCTS.py b/sela/MCTS.py similarity index 97% rename from expo/MCTS.py rename to sela/MCTS.py index 2ce559ae0..8368f3918 100644 --- a/expo/MCTS.py +++ b/sela/MCTS.py @@ -8,12 +8,12 @@ import numpy as np import pandas as pd -from expo.data.custom_task import get_mle_bench_requirements, get_mle_task_id -from expo.data.dataset import generate_task_requirement, get_split_dataset_path -from expo.evaluation.evaluation import evaluate_score -from expo.insights.instruction_generator import InstructionGenerator -from expo.research_assistant import ResearchAssistant, TimeoutException -from expo.utils import get_exp_pool_path, load_execute_notebook, mcts_logger +from sela.data.custom_task import get_mle_bench_requirements, get_mle_task_id +from sela.data.dataset import generate_task_requirement, get_split_dataset_path +from sela.evaluation.evaluation import evaluate_score +from sela.insights.instruction_generator import InstructionGenerator +from sela.research_assistant import ResearchAssistant, TimeoutException +from sela.utils import get_exp_pool_path, load_execute_notebook, mcts_logger from metagpt.tools.tool_recommend import ToolRecommender from metagpt.utils.common import read_json_file diff --git a/expo/README.md b/sela/README.md similarity index 100% rename from expo/README.md rename to sela/README.md diff --git a/expo/data.yaml b/sela/data.yaml similarity index 100% rename from expo/data.yaml rename to sela/data.yaml diff --git a/expo/data/custom_task.py b/sela/data/custom_task.py similarity index 95% rename from expo/data/custom_task.py rename to sela/data/custom_task.py index c2bf5c710..cbbcd8f0e 100644 --- a/expo/data/custom_task.py +++ b/sela/data/custom_task.py @@ -1,7 +1,7 @@ import os -from expo.data.dataset import SPECIAL_INSTRUCTIONS -from expo.experimenter.mle_bench.instructions import ( +from sela.data.dataset import SPECIAL_INSTRUCTIONS +from sela.experimenter.mle_bench.instructions import ( ADDITIONAL_NOTES, INSTRUCTIONS, INSTRUCTIONS_OBFUSCATED, diff --git a/expo/data/dataset.py b/sela/data/dataset.py similarity index 99% rename from expo/data/dataset.py rename to sela/data/dataset.py index 91490dcd7..7f5ead88b 100644 --- a/expo/data/dataset.py +++ b/sela/data/dataset.py @@ -9,8 +9,8 @@ import yaml from sklearn.model_selection import train_test_split -from expo.insights.solution_designer import SolutionDesigner -from expo.utils import DATA_CONFIG +from sela.insights.solution_designer import SolutionDesigner +from sela.utils import DATA_CONFIG BASE_USER_REQUIREMENT = """ This is a {datasetname} dataset. Your goal is to predict the target column `{target_col}`. diff --git a/expo/data/hf_data.py b/sela/data/hf_data.py similarity index 97% rename from expo/data/hf_data.py rename to sela/data/hf_data.py index a18517d49..355815628 100644 --- a/expo/data/hf_data.py +++ b/sela/data/hf_data.py @@ -7,14 +7,14 @@ from datasets import load_dataset from PIL import Image -from expo.data.dataset import ( +from sela.data.dataset import ( ExpDataset, parse_args, process_dataset, save_datasets_dict_to_yaml, ) -from expo.insights.solution_designer import SolutionDesigner -from expo.utils import DATA_CONFIG +from sela.insights.solution_designer import SolutionDesigner +from sela.utils import DATA_CONFIG HFDATSETS = [ {"name": "sms_spam", "dataset_name": "ucirvine/sms_spam", "target_col": "label", "modality": "text"}, diff --git a/expo/datasets.yaml b/sela/datasets.yaml similarity index 100% rename from expo/datasets.yaml rename to sela/datasets.yaml diff --git a/expo/evaluation/evaluation.py b/sela/evaluation/evaluation.py similarity index 100% rename from expo/evaluation/evaluation.py rename to sela/evaluation/evaluation.py diff --git a/expo/evaluation/visualize_mcts.py b/sela/evaluation/visualize_mcts.py similarity index 99% rename from expo/evaluation/visualize_mcts.py rename to sela/evaluation/visualize_mcts.py index 44f5ec5f5..9a88937b4 100644 --- a/expo/evaluation/visualize_mcts.py +++ b/sela/evaluation/visualize_mcts.py @@ -3,7 +3,7 @@ import matplotlib.pyplot as plt import networkx as nx -from expo.MCTS import Node +from sela.MCTS import Node NODE_TEMPLATE = """\ [Node {id}] diff --git a/expo/experimenter/__init__.py b/sela/experimenter/__init__.py similarity index 100% rename from expo/experimenter/__init__.py rename to sela/experimenter/__init__.py diff --git a/expo/experimenter/aide.py b/sela/experimenter/aide.py similarity index 100% rename from expo/experimenter/aide.py rename to sela/experimenter/aide.py diff --git a/expo/experimenter/aug.py b/sela/experimenter/aug.py similarity index 91% rename from expo/experimenter/aug.py rename to sela/experimenter/aug.py index bcfa5d4ad..d19412b47 100644 --- a/expo/experimenter/aug.py +++ b/sela/experimenter/aug.py @@ -1,7 +1,7 @@ -from expo.experimenter.experimenter import Experimenter -from expo.insights.instruction_generator import InstructionGenerator -from expo.research_assistant import ResearchAssistant -from expo.utils import get_exp_pool_path +from sela.experimenter.experimenter import Experimenter +from sela.insights.instruction_generator import InstructionGenerator +from sela.research_assistant import ResearchAssistant +from sela.utils import get_exp_pool_path EXPS_PROMPT = """ When doing the tasks, you can refer to the insights below: diff --git a/expo/experimenter/autogluon.py b/sela/experimenter/autogluon.py similarity index 99% rename from expo/experimenter/autogluon.py rename to sela/experimenter/autogluon.py index dabf0c138..7ea1b37f6 100644 --- a/expo/experimenter/autogluon.py +++ b/sela/experimenter/autogluon.py @@ -1,5 +1,5 @@ from datetime import datetime -from expo.experimenter.custom import CustomExperimenter +from sela.experimenter.custom import CustomExperimenter import os import pandas as pd diff --git a/expo/experimenter/autosklearn.py b/sela/experimenter/autosklearn.py similarity index 96% rename from expo/experimenter/autosklearn.py rename to sela/experimenter/autosklearn.py index 02a3cc465..6079a6813 100644 --- a/expo/experimenter/autosklearn.py +++ b/sela/experimenter/autosklearn.py @@ -1,7 +1,7 @@ from datetime import datetime import pandas as pd -from expo.experimenter.custom import CustomExperimenter -from expo.evaluation.evaluation import evaluate_score +from sela.experimenter.custom import CustomExperimenter +from sela.evaluation.evaluation import evaluate_score from functools import partial diff --git a/expo/experimenter/custom.py b/sela/experimenter/custom.py similarity index 93% rename from expo/experimenter/custom.py rename to sela/experimenter/custom.py index f245499ca..f6dde6c59 100644 --- a/expo/experimenter/custom.py +++ b/sela/experimenter/custom.py @@ -2,9 +2,9 @@ import pandas as pd -from expo.evaluation.evaluation import evaluate_score -from expo.experimenter.experimenter import Experimenter -from expo.MCTS import create_initial_state +from sela.evaluation.evaluation import evaluate_score +from sela.experimenter.experimenter import Experimenter +from sela.MCTS import create_initial_state class CustomExperimenter(Experimenter): diff --git a/expo/experimenter/experimenter.py b/sela/experimenter/experimenter.py similarity index 96% rename from expo/experimenter/experimenter.py rename to sela/experimenter/experimenter.py index 4a0b8413e..32a88016a 100644 --- a/expo/experimenter/experimenter.py +++ b/sela/experimenter/experimenter.py @@ -5,10 +5,10 @@ import numpy as np import pandas as pd -from expo.evaluation.evaluation import evaluate_score -from expo.MCTS import create_initial_state -from expo.research_assistant import ResearchAssistant -from expo.utils import DATA_CONFIG, save_notebook +from sela.evaluation.evaluation import evaluate_score +from sela.MCTS import create_initial_state +from sela.research_assistant import ResearchAssistant +from sela.utils import DATA_CONFIG, save_notebook class Experimenter: diff --git a/expo/experimenter/mcts.py b/sela/experimenter/mcts.py similarity index 93% rename from expo/experimenter/mcts.py rename to sela/experimenter/mcts.py index a42566366..1065bd0d9 100644 --- a/expo/experimenter/mcts.py +++ b/sela/experimenter/mcts.py @@ -1,13 +1,13 @@ import shutil -from expo.evaluation.evaluation import ( +from sela.evaluation.evaluation import ( node_evaluate_score_mlebench, node_evaluate_score_sela, ) -from expo.evaluation.visualize_mcts import get_tree_text -from expo.experimenter.experimenter import Experimenter -from expo.Greedy import Greedy, Random -from expo.MCTS import MCTS +from sela.evaluation.visualize_mcts import get_tree_text +from sela.experimenter.experimenter import Experimenter +from sela.Greedy import Greedy, Random +from sela.MCTS import MCTS class MCTSExperimenter(Experimenter): diff --git a/expo/experimenter/mle_bench/instructions.py b/sela/experimenter/mle_bench/instructions.py similarity index 98% rename from expo/experimenter/mle_bench/instructions.py rename to sela/experimenter/mle_bench/instructions.py index 631f410dc..515fd9d34 100644 --- a/expo/experimenter/mle_bench/instructions.py +++ b/sela/experimenter/mle_bench/instructions.py @@ -8,7 +8,7 @@ BENCHMARK INSTRUCTIONS ------ -First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `expo.experimenter.mle_bench.instructions.py`. +First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `sela.experimenter.mle_bench.instructions.py`. - **Competition-specific instructions**: The instructions for the competition are available in `{dataset_dir}/description.md`. Read this file to understand the problem setup for the competition you are currently solving. - **Dataset**: The dataset files for the competition are available in `{dataset_dir}`. @@ -28,7 +28,7 @@ BENCHMARK INSTRUCTIONS ------ -First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `expo.experimenter.mle_bench.instructions.py`. +First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `sela.experimenter.mle_bench.instructions.py`. - **Task-specific instructions**: The instructions for the task are available in `{dataset_dir}/description.md`. Read this file to understand the problem setup for the task you are currently solving. - **Dataset**: The dataset files for the task are available in `{dataset_dir}/`. diff --git a/expo/insights/fixed_insights.json b/sela/insights/fixed_insights.json similarity index 100% rename from expo/insights/fixed_insights.json rename to sela/insights/fixed_insights.json diff --git a/expo/insights/instruction_generator.py b/sela/insights/instruction_generator.py similarity index 98% rename from expo/insights/instruction_generator.py rename to sela/insights/instruction_generator.py index ab9b2cc67..90555761b 100644 --- a/expo/insights/instruction_generator.py +++ b/sela/insights/instruction_generator.py @@ -3,8 +3,8 @@ import random from difflib import SequenceMatcher -from expo.insights.solution_designer import SolutionDesigner -from expo.utils import clean_json_from_rsp, load_data_config, mcts_logger +from sela.insights.solution_designer import SolutionDesigner +from sela.utils import clean_json_from_rsp, load_data_config, mcts_logger from metagpt.llm import LLM from metagpt.schema import Message diff --git a/expo/insights/solution_designer.py b/sela/insights/solution_designer.py similarity index 98% rename from expo/insights/solution_designer.py rename to sela/insights/solution_designer.py index 262caa0f6..6685f1ed5 100644 --- a/expo/insights/solution_designer.py +++ b/sela/insights/solution_designer.py @@ -1,6 +1,6 @@ import json -from expo.utils import clean_json_from_rsp, load_data_config +from sela.utils import clean_json_from_rsp, load_data_config from metagpt.llm import LLM DATA_CONFIG = load_data_config() diff --git a/expo/requirements.txt b/sela/requirements.txt similarity index 100% rename from expo/requirements.txt rename to sela/requirements.txt diff --git a/expo/research_assistant.py b/sela/research_assistant.py similarity index 99% rename from expo/research_assistant.py rename to sela/research_assistant.py index d068dd4e5..45326a28a 100644 --- a/expo/research_assistant.py +++ b/sela/research_assistant.py @@ -6,7 +6,7 @@ from pydantic import model_validator -from expo.utils import mcts_logger, save_notebook +from sela.utils import mcts_logger, save_notebook from metagpt.actions.di.write_analysis_code import WriteAnalysisCode from metagpt.const import SERDESER_PATH from metagpt.roles.di.data_interpreter import DataInterpreter diff --git a/expo/run_experiment.py b/sela/run_experiment.py similarity index 90% rename from expo/run_experiment.py rename to sela/run_experiment.py index 68c3b35d4..168dc2160 100644 --- a/expo/run_experiment.py +++ b/sela/run_experiment.py @@ -1,13 +1,13 @@ import argparse import asyncio -from expo.data.custom_task import get_mle_is_lower_better, get_mle_task_id -from expo.experimenter.aug import AugExperimenter -from expo.experimenter.autogluon import GluonExperimenter -from expo.experimenter.autosklearn import AutoSklearnExperimenter -from expo.experimenter.custom import CustomExperimenter -from expo.experimenter.experimenter import Experimenter -from expo.experimenter.mcts import MCTSExperimenter +from sela.data.custom_task import get_mle_is_lower_better, get_mle_task_id +from sela.experimenter.aug import AugExperimenter +from sela.experimenter.autogluon import GluonExperimenter +from sela.experimenter.autosklearn import AutoSklearnExperimenter +from sela.experimenter.custom import CustomExperimenter +from sela.experimenter.experimenter import Experimenter +from sela.experimenter.mcts import MCTSExperimenter def get_args(cmd=True): diff --git a/expo/scripts/run_cls.sh b/sela/scripts/run_cls.sh similarity index 100% rename from expo/scripts/run_cls.sh rename to sela/scripts/run_cls.sh diff --git a/expo/scripts/run_cls_mod.sh b/sela/scripts/run_cls_mod.sh similarity index 100% rename from expo/scripts/run_cls_mod.sh rename to sela/scripts/run_cls_mod.sh diff --git a/expo/scripts/run_reg.sh b/sela/scripts/run_reg.sh similarity index 100% rename from expo/scripts/run_reg.sh rename to sela/scripts/run_reg.sh diff --git a/expo/scripts/visualize_experiment.py b/sela/scripts/visualize_experiment.py similarity index 74% rename from expo/scripts/visualize_experiment.py rename to sela/scripts/visualize_experiment.py index 6cd84a0de..4b93b9f61 100644 --- a/expo/scripts/visualize_experiment.py +++ b/sela/scripts/visualize_experiment.py @@ -1,9 +1,9 @@ import networkx as nx -from expo.evaluation.visualize_mcts import build_tree_recursive, visualize_tree -from expo.MCTS import MCTS, create_initial_state, initialize_di_root_node -from expo.run_experiment import get_args -from expo.utils import DATA_CONFIG +from sela.evaluation.visualize_mcts import build_tree_recursive, visualize_tree +from sela.MCTS import MCTS, create_initial_state, initialize_di_root_node +from sela.run_experiment import get_args +from sela.utils import DATA_CONFIG if __name__ == "__main__": args = get_args() diff --git a/expo/utils.py b/sela/utils.py similarity index 100% rename from expo/utils.py rename to sela/utils.py From eb3d49dfddb9bc35ff0d50bc6a194a6ce1728062 Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Fri, 25 Oct 2024 22:18:43 +0800 Subject: [PATCH 142/160] move to ext/sela --- .gitignore | 4 ++-- {sela => metagpt/ext/sela}/Greedy.py | 2 +- {sela => metagpt/ext/sela}/MCTS.py | 12 ++++++------ {sela => metagpt/ext/sela}/README.md | 0 {sela => metagpt/ext/sela}/data.yaml | 2 +- {sela => metagpt/ext/sela}/data/custom_task.py | 4 ++-- {sela => metagpt/ext/sela}/data/dataset.py | 4 ++-- {sela => metagpt/ext/sela}/data/hf_data.py | 6 +++--- {sela => metagpt/ext/sela}/datasets.yaml | 0 .../ext/sela}/evaluation/evaluation.py | 0 .../ext/sela}/evaluation/visualize_mcts.py | 2 +- .../ext/sela}/experimenter/__init__.py | 0 {sela => metagpt/ext/sela}/experimenter/aide.py | 0 {sela => metagpt/ext/sela}/experimenter/aug.py | 8 ++++---- .../ext/sela}/experimenter/autogluon.py | 2 +- .../ext/sela}/experimenter/autosklearn.py | 4 ++-- {sela => metagpt/ext/sela}/experimenter/custom.py | 6 +++--- .../ext/sela}/experimenter/experimenter.py | 8 ++++---- {sela => metagpt/ext/sela}/experimenter/mcts.py | 10 +++++----- .../sela}/experimenter/mle_bench/instructions.py | 4 ++-- .../ext/sela}/insights/fixed_insights.json | 0 .../ext/sela}/insights/instruction_generator.py | 4 ++-- .../ext/sela}/insights/solution_designer.py | 2 +- {sela => metagpt/ext/sela}/requirements.txt | 0 {sela => metagpt/ext/sela}/research_assistant.py | 2 +- {sela => metagpt/ext/sela}/run_experiment.py | 14 +++++++------- {sela => metagpt/ext/sela}/scripts/run_cls.sh | 0 {sela => metagpt/ext/sela}/scripts/run_cls_mod.sh | 0 {sela => metagpt/ext/sela}/scripts/run_reg.sh | 0 .../ext/sela}/scripts/visualize_experiment.py | 8 ++++---- {sela => metagpt/ext/sela}/utils.py | 0 31 files changed, 54 insertions(+), 54 deletions(-) rename {sela => metagpt/ext/sela}/Greedy.py (93%) rename {sela => metagpt/ext/sela}/MCTS.py (97%) rename {sela => metagpt/ext/sela}/README.md (100%) rename {sela => metagpt/ext/sela}/data.yaml (59%) rename {sela => metagpt/ext/sela}/data/custom_task.py (94%) rename {sela => metagpt/ext/sela}/data/dataset.py (99%) rename {sela => metagpt/ext/sela}/data/hf_data.py (96%) rename {sela => metagpt/ext/sela}/datasets.yaml (100%) rename {sela => metagpt/ext/sela}/evaluation/evaluation.py (100%) rename {sela => metagpt/ext/sela}/evaluation/visualize_mcts.py (99%) rename {sela => metagpt/ext/sela}/experimenter/__init__.py (100%) rename {sela => metagpt/ext/sela}/experimenter/aide.py (100%) rename {sela => metagpt/ext/sela}/experimenter/aug.py (89%) rename {sela => metagpt/ext/sela}/experimenter/autogluon.py (98%) rename {sela => metagpt/ext/sela}/experimenter/autosklearn.py (96%) rename {sela => metagpt/ext/sela}/experimenter/custom.py (92%) rename {sela => metagpt/ext/sela}/experimenter/experimenter.py (95%) rename {sela => metagpt/ext/sela}/experimenter/mcts.py (91%) rename {sela => metagpt/ext/sela}/experimenter/mle_bench/instructions.py (98%) rename {sela => metagpt/ext/sela}/insights/fixed_insights.json (100%) rename {sela => metagpt/ext/sela}/insights/instruction_generator.py (97%) rename {sela => metagpt/ext/sela}/insights/solution_designer.py (98%) rename {sela => metagpt/ext/sela}/requirements.txt (100%) rename {sela => metagpt/ext/sela}/research_assistant.py (99%) rename {sela => metagpt/ext/sela}/run_experiment.py (88%) rename {sela => metagpt/ext/sela}/scripts/run_cls.sh (100%) rename {sela => metagpt/ext/sela}/scripts/run_cls_mod.sh (100%) rename {sela => metagpt/ext/sela}/scripts/run_reg.sh (100%) rename {sela => metagpt/ext/sela}/scripts/visualize_experiment.py (67%) rename {sela => metagpt/ext/sela}/utils.py (100%) diff --git a/.gitignore b/.gitignore index f22e826a3..46c9b0dd4 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,7 @@ share/python-wheels/ MANIFEST metagpt/tools/schemas/ examples/data/search_kb/*.json -sela/AutogluonModels +metagpt/ext/sela/AutogluonModels # PyInstaller # Usually these files are written by a python scripts from a template @@ -189,4 +189,4 @@ cov.xml *-structure.json *.dot .python-version -sela/results/* +metagpt/ext/sela/results/* diff --git a/sela/Greedy.py b/metagpt/ext/sela/Greedy.py similarity index 93% rename from sela/Greedy.py rename to metagpt/ext/sela/Greedy.py index 05bf1dfa5..c10c4248c 100644 --- a/sela/Greedy.py +++ b/metagpt/ext/sela/Greedy.py @@ -1,6 +1,6 @@ import random -from sela.MCTS import MCTS +from metagpt.ext.sela.MCTS import MCTS class Greedy(MCTS): diff --git a/sela/MCTS.py b/metagpt/ext/sela/MCTS.py similarity index 97% rename from sela/MCTS.py rename to metagpt/ext/sela/MCTS.py index 8368f3918..ac4b68024 100644 --- a/sela/MCTS.py +++ b/metagpt/ext/sela/MCTS.py @@ -8,12 +8,12 @@ import numpy as np import pandas as pd -from sela.data.custom_task import get_mle_bench_requirements, get_mle_task_id -from sela.data.dataset import generate_task_requirement, get_split_dataset_path -from sela.evaluation.evaluation import evaluate_score -from sela.insights.instruction_generator import InstructionGenerator -from sela.research_assistant import ResearchAssistant, TimeoutException -from sela.utils import get_exp_pool_path, load_execute_notebook, mcts_logger +from metagpt.ext.sela.data.custom_task import get_mle_bench_requirements, get_mle_task_id +from metagpt.ext.sela.data.dataset import generate_task_requirement, get_split_dataset_path +from metagpt.ext.sela.evaluation.evaluation import evaluate_score +from metagpt.ext.sela.insights.instruction_generator import InstructionGenerator +from metagpt.ext.sela.research_assistant import ResearchAssistant, TimeoutException +from metagpt.ext.sela.utils import get_exp_pool_path, load_execute_notebook, mcts_logger from metagpt.tools.tool_recommend import ToolRecommender from metagpt.utils.common import read_json_file diff --git a/sela/README.md b/metagpt/ext/sela/README.md similarity index 100% rename from sela/README.md rename to metagpt/ext/sela/README.md diff --git a/sela/data.yaml b/metagpt/ext/sela/data.yaml similarity index 59% rename from sela/data.yaml rename to metagpt/ext/sela/data.yaml index 4c6549490..989a07966 100644 --- a/sela/data.yaml +++ b/metagpt/ext/sela/data.yaml @@ -1,3 +1,3 @@ -datasets_dir: "D:/work/automl/datasets" # path to the datasets directory +datasets_dir: "path/to/datasets" # path to the datasets directory work_dir: ../workspace # path to the workspace directory role_dir: storage/SELA # path to the role directory \ No newline at end of file diff --git a/sela/data/custom_task.py b/metagpt/ext/sela/data/custom_task.py similarity index 94% rename from sela/data/custom_task.py rename to metagpt/ext/sela/data/custom_task.py index cbbcd8f0e..3371d5b1c 100644 --- a/sela/data/custom_task.py +++ b/metagpt/ext/sela/data/custom_task.py @@ -1,7 +1,7 @@ import os -from sela.data.dataset import SPECIAL_INSTRUCTIONS -from sela.experimenter.mle_bench.instructions import ( +from metagpt.ext.sela.data.dataset import SPECIAL_INSTRUCTIONS +from metagpt.ext.sela.experimenter.mle_bench.instructions import ( ADDITIONAL_NOTES, INSTRUCTIONS, INSTRUCTIONS_OBFUSCATED, diff --git a/sela/data/dataset.py b/metagpt/ext/sela/data/dataset.py similarity index 99% rename from sela/data/dataset.py rename to metagpt/ext/sela/data/dataset.py index 7f5ead88b..ef4179011 100644 --- a/sela/data/dataset.py +++ b/metagpt/ext/sela/data/dataset.py @@ -9,8 +9,8 @@ import yaml from sklearn.model_selection import train_test_split -from sela.insights.solution_designer import SolutionDesigner -from sela.utils import DATA_CONFIG +from metagpt.ext.sela.insights.solution_designer import SolutionDesigner +from metagpt.ext.sela.utils import DATA_CONFIG BASE_USER_REQUIREMENT = """ This is a {datasetname} dataset. Your goal is to predict the target column `{target_col}`. diff --git a/sela/data/hf_data.py b/metagpt/ext/sela/data/hf_data.py similarity index 96% rename from sela/data/hf_data.py rename to metagpt/ext/sela/data/hf_data.py index 355815628..9645796af 100644 --- a/sela/data/hf_data.py +++ b/metagpt/ext/sela/data/hf_data.py @@ -7,14 +7,14 @@ from datasets import load_dataset from PIL import Image -from sela.data.dataset import ( +from metagpt.ext.sela.data.dataset import ( ExpDataset, parse_args, process_dataset, save_datasets_dict_to_yaml, ) -from sela.insights.solution_designer import SolutionDesigner -from sela.utils import DATA_CONFIG +from metagpt.ext.sela.insights.solution_designer import SolutionDesigner +from metagpt.ext.sela.utils import DATA_CONFIG HFDATSETS = [ {"name": "sms_spam", "dataset_name": "ucirvine/sms_spam", "target_col": "label", "modality": "text"}, diff --git a/sela/datasets.yaml b/metagpt/ext/sela/datasets.yaml similarity index 100% rename from sela/datasets.yaml rename to metagpt/ext/sela/datasets.yaml diff --git a/sela/evaluation/evaluation.py b/metagpt/ext/sela/evaluation/evaluation.py similarity index 100% rename from sela/evaluation/evaluation.py rename to metagpt/ext/sela/evaluation/evaluation.py diff --git a/sela/evaluation/visualize_mcts.py b/metagpt/ext/sela/evaluation/visualize_mcts.py similarity index 99% rename from sela/evaluation/visualize_mcts.py rename to metagpt/ext/sela/evaluation/visualize_mcts.py index 9a88937b4..62efed917 100644 --- a/sela/evaluation/visualize_mcts.py +++ b/metagpt/ext/sela/evaluation/visualize_mcts.py @@ -3,7 +3,7 @@ import matplotlib.pyplot as plt import networkx as nx -from sela.MCTS import Node +from metagpt.ext.sela.MCTS import Node NODE_TEMPLATE = """\ [Node {id}] diff --git a/sela/experimenter/__init__.py b/metagpt/ext/sela/experimenter/__init__.py similarity index 100% rename from sela/experimenter/__init__.py rename to metagpt/ext/sela/experimenter/__init__.py diff --git a/sela/experimenter/aide.py b/metagpt/ext/sela/experimenter/aide.py similarity index 100% rename from sela/experimenter/aide.py rename to metagpt/ext/sela/experimenter/aide.py diff --git a/sela/experimenter/aug.py b/metagpt/ext/sela/experimenter/aug.py similarity index 89% rename from sela/experimenter/aug.py rename to metagpt/ext/sela/experimenter/aug.py index d19412b47..99b1e700a 100644 --- a/sela/experimenter/aug.py +++ b/metagpt/ext/sela/experimenter/aug.py @@ -1,7 +1,7 @@ -from sela.experimenter.experimenter import Experimenter -from sela.insights.instruction_generator import InstructionGenerator -from sela.research_assistant import ResearchAssistant -from sela.utils import get_exp_pool_path +from metagpt.ext.sela.experimenter.experimenter import Experimenter +from metagpt.ext.sela.insights.instruction_generator import InstructionGenerator +from metagpt.ext.sela.research_assistant import ResearchAssistant +from metagpt.ext.sela.utils import get_exp_pool_path EXPS_PROMPT = """ When doing the tasks, you can refer to the insights below: diff --git a/sela/experimenter/autogluon.py b/metagpt/ext/sela/experimenter/autogluon.py similarity index 98% rename from sela/experimenter/autogluon.py rename to metagpt/ext/sela/experimenter/autogluon.py index 7ea1b37f6..e7593cfb9 100644 --- a/sela/experimenter/autogluon.py +++ b/metagpt/ext/sela/experimenter/autogluon.py @@ -1,5 +1,5 @@ from datetime import datetime -from sela.experimenter.custom import CustomExperimenter +from metagpt.ext.sela.experimenter.custom import CustomExperimenter import os import pandas as pd diff --git a/sela/experimenter/autosklearn.py b/metagpt/ext/sela/experimenter/autosklearn.py similarity index 96% rename from sela/experimenter/autosklearn.py rename to metagpt/ext/sela/experimenter/autosklearn.py index 6079a6813..718e758d4 100644 --- a/sela/experimenter/autosklearn.py +++ b/metagpt/ext/sela/experimenter/autosklearn.py @@ -1,7 +1,7 @@ from datetime import datetime import pandas as pd -from sela.experimenter.custom import CustomExperimenter -from sela.evaluation.evaluation import evaluate_score +from metagpt.ext.sela.experimenter.custom import CustomExperimenter +from metagpt.ext.sela.evaluation.evaluation import evaluate_score from functools import partial diff --git a/sela/experimenter/custom.py b/metagpt/ext/sela/experimenter/custom.py similarity index 92% rename from sela/experimenter/custom.py rename to metagpt/ext/sela/experimenter/custom.py index f6dde6c59..4d69e286f 100644 --- a/sela/experimenter/custom.py +++ b/metagpt/ext/sela/experimenter/custom.py @@ -2,9 +2,9 @@ import pandas as pd -from sela.evaluation.evaluation import evaluate_score -from sela.experimenter.experimenter import Experimenter -from sela.MCTS import create_initial_state +from metagpt.ext.sela.evaluation.evaluation import evaluate_score +from metagpt.ext.sela.experimenter.experimenter import Experimenter +from metagpt.ext.sela.MCTS import create_initial_state class CustomExperimenter(Experimenter): diff --git a/sela/experimenter/experimenter.py b/metagpt/ext/sela/experimenter/experimenter.py similarity index 95% rename from sela/experimenter/experimenter.py rename to metagpt/ext/sela/experimenter/experimenter.py index 32a88016a..03671c9ff 100644 --- a/sela/experimenter/experimenter.py +++ b/metagpt/ext/sela/experimenter/experimenter.py @@ -5,10 +5,10 @@ import numpy as np import pandas as pd -from sela.evaluation.evaluation import evaluate_score -from sela.MCTS import create_initial_state -from sela.research_assistant import ResearchAssistant -from sela.utils import DATA_CONFIG, save_notebook +from metagpt.ext.sela.evaluation.evaluation import evaluate_score +from metagpt.ext.sela.MCTS import create_initial_state +from metagpt.ext.sela.research_assistant import ResearchAssistant +from metagpt.ext.sela.utils import DATA_CONFIG, save_notebook class Experimenter: diff --git a/sela/experimenter/mcts.py b/metagpt/ext/sela/experimenter/mcts.py similarity index 91% rename from sela/experimenter/mcts.py rename to metagpt/ext/sela/experimenter/mcts.py index 1065bd0d9..b83aa1392 100644 --- a/sela/experimenter/mcts.py +++ b/metagpt/ext/sela/experimenter/mcts.py @@ -1,13 +1,13 @@ import shutil -from sela.evaluation.evaluation import ( +from metagpt.ext.sela.evaluation.evaluation import ( node_evaluate_score_mlebench, node_evaluate_score_sela, ) -from sela.evaluation.visualize_mcts import get_tree_text -from sela.experimenter.experimenter import Experimenter -from sela.Greedy import Greedy, Random -from sela.MCTS import MCTS +from metagpt.ext.sela.evaluation.visualize_mcts import get_tree_text +from metagpt.ext.sela.experimenter.experimenter import Experimenter +from metagpt.ext.sela.Greedy import Greedy, Random +from metagpt.ext.sela.MCTS import MCTS class MCTSExperimenter(Experimenter): diff --git a/sela/experimenter/mle_bench/instructions.py b/metagpt/ext/sela/experimenter/mle_bench/instructions.py similarity index 98% rename from sela/experimenter/mle_bench/instructions.py rename to metagpt/ext/sela/experimenter/mle_bench/instructions.py index 515fd9d34..5c9c7b70f 100644 --- a/sela/experimenter/mle_bench/instructions.py +++ b/metagpt/ext/sela/experimenter/mle_bench/instructions.py @@ -8,7 +8,7 @@ BENCHMARK INSTRUCTIONS ------ -First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `sela.experimenter.mle_bench.instructions.py`. +First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `experimenter/mle_bench/instructions.py`. - **Competition-specific instructions**: The instructions for the competition are available in `{dataset_dir}/description.md`. Read this file to understand the problem setup for the competition you are currently solving. - **Dataset**: The dataset files for the competition are available in `{dataset_dir}`. @@ -28,7 +28,7 @@ BENCHMARK INSTRUCTIONS ------ -First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `sela.experimenter.mle_bench.instructions.py`. +First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `experimenter/mle_bench/instructions.py`. - **Task-specific instructions**: The instructions for the task are available in `{dataset_dir}/description.md`. Read this file to understand the problem setup for the task you are currently solving. - **Dataset**: The dataset files for the task are available in `{dataset_dir}/`. diff --git a/sela/insights/fixed_insights.json b/metagpt/ext/sela/insights/fixed_insights.json similarity index 100% rename from sela/insights/fixed_insights.json rename to metagpt/ext/sela/insights/fixed_insights.json diff --git a/sela/insights/instruction_generator.py b/metagpt/ext/sela/insights/instruction_generator.py similarity index 97% rename from sela/insights/instruction_generator.py rename to metagpt/ext/sela/insights/instruction_generator.py index 90555761b..d5d24c74d 100644 --- a/sela/insights/instruction_generator.py +++ b/metagpt/ext/sela/insights/instruction_generator.py @@ -3,8 +3,8 @@ import random from difflib import SequenceMatcher -from sela.insights.solution_designer import SolutionDesigner -from sela.utils import clean_json_from_rsp, load_data_config, mcts_logger +from metagpt.ext.sela.insights.solution_designer import SolutionDesigner +from metagpt.ext.sela.utils import clean_json_from_rsp, load_data_config, mcts_logger from metagpt.llm import LLM from metagpt.schema import Message diff --git a/sela/insights/solution_designer.py b/metagpt/ext/sela/insights/solution_designer.py similarity index 98% rename from sela/insights/solution_designer.py rename to metagpt/ext/sela/insights/solution_designer.py index 6685f1ed5..1b61c2141 100644 --- a/sela/insights/solution_designer.py +++ b/metagpt/ext/sela/insights/solution_designer.py @@ -1,6 +1,6 @@ import json -from sela.utils import clean_json_from_rsp, load_data_config +from metagpt.ext.sela.utils import clean_json_from_rsp, load_data_config from metagpt.llm import LLM DATA_CONFIG = load_data_config() diff --git a/sela/requirements.txt b/metagpt/ext/sela/requirements.txt similarity index 100% rename from sela/requirements.txt rename to metagpt/ext/sela/requirements.txt diff --git a/sela/research_assistant.py b/metagpt/ext/sela/research_assistant.py similarity index 99% rename from sela/research_assistant.py rename to metagpt/ext/sela/research_assistant.py index 45326a28a..5ce1be68e 100644 --- a/sela/research_assistant.py +++ b/metagpt/ext/sela/research_assistant.py @@ -6,7 +6,7 @@ from pydantic import model_validator -from sela.utils import mcts_logger, save_notebook +from metagpt.ext.sela.utils import mcts_logger, save_notebook from metagpt.actions.di.write_analysis_code import WriteAnalysisCode from metagpt.const import SERDESER_PATH from metagpt.roles.di.data_interpreter import DataInterpreter diff --git a/sela/run_experiment.py b/metagpt/ext/sela/run_experiment.py similarity index 88% rename from sela/run_experiment.py rename to metagpt/ext/sela/run_experiment.py index 168dc2160..4dbb1a4ef 100644 --- a/sela/run_experiment.py +++ b/metagpt/ext/sela/run_experiment.py @@ -1,13 +1,13 @@ import argparse import asyncio -from sela.data.custom_task import get_mle_is_lower_better, get_mle_task_id -from sela.experimenter.aug import AugExperimenter -from sela.experimenter.autogluon import GluonExperimenter -from sela.experimenter.autosklearn import AutoSklearnExperimenter -from sela.experimenter.custom import CustomExperimenter -from sela.experimenter.experimenter import Experimenter -from sela.experimenter.mcts import MCTSExperimenter +from metagpt.ext.sela.data.custom_task import get_mle_is_lower_better, get_mle_task_id +from metagpt.ext.sela.experimenter.aug import AugExperimenter +from metagpt.ext.sela.experimenter.autogluon import GluonExperimenter +from metagpt.ext.sela.experimenter.autosklearn import AutoSklearnExperimenter +from metagpt.ext.sela.experimenter.custom import CustomExperimenter +from metagpt.ext.sela.experimenter.experimenter import Experimenter +from metagpt.ext.sela.experimenter.mcts import MCTSExperimenter def get_args(cmd=True): diff --git a/sela/scripts/run_cls.sh b/metagpt/ext/sela/scripts/run_cls.sh similarity index 100% rename from sela/scripts/run_cls.sh rename to metagpt/ext/sela/scripts/run_cls.sh diff --git a/sela/scripts/run_cls_mod.sh b/metagpt/ext/sela/scripts/run_cls_mod.sh similarity index 100% rename from sela/scripts/run_cls_mod.sh rename to metagpt/ext/sela/scripts/run_cls_mod.sh diff --git a/sela/scripts/run_reg.sh b/metagpt/ext/sela/scripts/run_reg.sh similarity index 100% rename from sela/scripts/run_reg.sh rename to metagpt/ext/sela/scripts/run_reg.sh diff --git a/sela/scripts/visualize_experiment.py b/metagpt/ext/sela/scripts/visualize_experiment.py similarity index 67% rename from sela/scripts/visualize_experiment.py rename to metagpt/ext/sela/scripts/visualize_experiment.py index 4b93b9f61..4133942fc 100644 --- a/sela/scripts/visualize_experiment.py +++ b/metagpt/ext/sela/scripts/visualize_experiment.py @@ -1,9 +1,9 @@ import networkx as nx -from sela.evaluation.visualize_mcts import build_tree_recursive, visualize_tree -from sela.MCTS import MCTS, create_initial_state, initialize_di_root_node -from sela.run_experiment import get_args -from sela.utils import DATA_CONFIG +from metagpt.ext.sela.evaluation.visualize_mcts import build_tree_recursive, visualize_tree +from metagpt.ext.sela.MCTS import MCTS, create_initial_state, initialize_di_root_node +from metagpt.ext.sela.run_experiment import get_args +from metagpt.ext.sela.utils import DATA_CONFIG if __name__ == "__main__": args = get_args() diff --git a/sela/utils.py b/metagpt/ext/sela/utils.py similarity index 100% rename from sela/utils.py rename to metagpt/ext/sela/utils.py From a62ae88187d3c71b8ef7ee2a68845a258171e4cc Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Fri, 25 Oct 2024 22:30:10 +0800 Subject: [PATCH 143/160] update readme.md --- metagpt/ext/sela/README.md | 58 ++++++-------------------------------- 1 file changed, 9 insertions(+), 49 deletions(-) diff --git a/metagpt/ext/sela/README.md b/metagpt/ext/sela/README.md index 800afc3cc..bf83f558a 100644 --- a/metagpt/ext/sela/README.md +++ b/metagpt/ext/sela/README.md @@ -2,16 +2,15 @@ - ## 1. Data Preparation - Download Datasets:https://deepwisdom.feishu.cn/drive/folder/RVyofv9cvlvtxKdddt2cyn3BnTc?from=from_copylink - Download and prepare datasets from scratch: - ``` - cd expo/data - python dataset.py --save_analysis_pool - python hf_data.py --save_analysis_pool - ``` +``` +cd expo/data +python dataset.py --save_analysis_pool +python hf_data.py --save_analysis_pool +``` ## 2. Configs @@ -28,7 +27,7 @@ llm: api_type: 'openai' model: deepseek-coder - base_url: "https://oneapi.deepwisdom.ai/v1" + base_url: "https://your_base_url" api_key: sk-xxx temperature: 0.5 ``` @@ -109,45 +108,6 @@ python run_experiment.py --exp_mode mcts --custom_dataset_dir "$TASK".txt 2>&1 - -python -u evaluation.py --path "deepseek-coder_True_1" --task $TASK --device 0 > "$TASK"_eval.txt 2>&1 -``` ### AIDE @@ -155,6 +115,7 @@ python -u evaluation.py --path "deepseek-coder_True_1" --task $TASK --device 0 ``` git clone https://github.com/WecoAI/aideml.git +git checkout 77953247ea0a5dc1bd502dd10939dd6d7fdcc5cc ``` Modify `aideml/aide/utils/config.yaml` - change `k_fold_validation`, `code model`, and `feedback model` as follows: @@ -240,8 +201,7 @@ python experimenter/aide.py ``` pip install -U pip pip install -U setuptools wheel -pip install autogluon - +pip install autogluon==1.1.1 ``` For Tabular data: @@ -273,7 +233,7 @@ For an explanation of missing Microsoft Windows and macOS support please check t #### Setup ``` -pip install auto-sklearn +pip install auto-sklearn==0.15.0 ``` #### Run From 76029782cc4f721a51b999d8761db0d02ce34e34 Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Fri, 25 Oct 2024 23:07:14 +0800 Subject: [PATCH 144/160] rename aug to rs --- metagpt/ext/sela/README.md | 4 ++-- .../sela/experimenter/{aug.py => random_search.py} | 12 ++++++------ metagpt/ext/sela/run_experiment.py | 14 +++++++------- 3 files changed, 15 insertions(+), 15 deletions(-) rename metagpt/ext/sela/experimenter/{aug.py => random_search.py} (88%) diff --git a/metagpt/ext/sela/README.md b/metagpt/ext/sela/README.md index bf83f558a..c8a1adea8 100644 --- a/metagpt/ext/sela/README.md +++ b/metagpt/ext/sela/README.md @@ -77,10 +77,10 @@ In addition to the generated insights, include the fixed insights saved in `expo **DI RandomSearch** - Single insight -`python run_experiment.py --exp_mode aug --task titanic --aug_mode single` +`python run_experiment.py --exp_mode rs --task titanic --rs_mode single` - Set insight -`python run_experiment.py --exp_mode aug --task titanic --aug_mode set` +`python run_experiment.py --exp_mode rs --task titanic --rs_mode set` ## 4. Evaluation diff --git a/metagpt/ext/sela/experimenter/aug.py b/metagpt/ext/sela/experimenter/random_search.py similarity index 88% rename from metagpt/ext/sela/experimenter/aug.py rename to metagpt/ext/sela/experimenter/random_search.py index 99b1e700a..94fb6960b 100644 --- a/metagpt/ext/sela/experimenter/aug.py +++ b/metagpt/ext/sela/experimenter/random_search.py @@ -10,8 +10,8 @@ """ -class AugExperimenter(Experimenter): - result_path: str = "results/aug" +class RandomSearchExperimenter(Experimenter): + result_path: str = "results/random_search" async def run_experiment(self): # state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") @@ -20,17 +20,17 @@ async def run_experiment(self): exp_pool = InstructionGenerator.load_analysis_pool( exp_pool_path, use_fixed_insights=self.args.use_fixed_insights ) - if self.args.aug_mode == "single": + if self.args.rs_mode == "single": exps = InstructionGenerator._random_sample(exp_pool, self.args.num_experiments) exps = [exp["Analysis"] for exp in exps] - elif self.args.aug_mode == "set": + elif self.args.rs_mode == "set": exps = [] for i in range(self.args.num_experiments): exp_set = InstructionGenerator.sample_instruction_set(exp_pool) exp_set_text = "\n".join([f"{exp['task_id']}: {exp['Analysis']}" for exp in exp_set]) exps.append(exp_set_text) else: - raise ValueError(f"Invalid mode: {self.args.aug_mode}") + raise ValueError(f"Invalid mode: {self.args.rs_mode}") results = [] for i in range(self.args.num_experiments): @@ -45,7 +45,7 @@ async def run_experiment(self): { "idx": i, "score_dict": score_dict, - "aug_mode": self.args.aug_mode, + "rs_mode": self.args.rs_mode, "insights": exps[i], "user_requirement": requirement, "args": vars(self.args), diff --git a/metagpt/ext/sela/run_experiment.py b/metagpt/ext/sela/run_experiment.py index 4dbb1a4ef..7d2a317c6 100644 --- a/metagpt/ext/sela/run_experiment.py +++ b/metagpt/ext/sela/run_experiment.py @@ -2,7 +2,7 @@ import asyncio from metagpt.ext.sela.data.custom_task import get_mle_is_lower_better, get_mle_task_id -from metagpt.ext.sela.experimenter.aug import AugExperimenter +from metagpt.ext.sela.experimenter.random_search import RandomSearchExperimenter from metagpt.ext.sela.experimenter.autogluon import GluonExperimenter from metagpt.ext.sela.experimenter.autosklearn import AutoSklearnExperimenter from metagpt.ext.sela.experimenter.custom import CustomExperimenter @@ -17,12 +17,12 @@ def get_args(cmd=True): "--exp_mode", type=str, default="mcts", - choices=["mcts", "aug", "base", "custom", "greedy", "autogluon", "random", "autosklearn"], + choices=["mcts", "rs", "base", "custom", "greedy", "autogluon", "random", "autosklearn"], ) parser.add_argument("--role_timeout", type=int, default=1000) get_di_args(parser) get_mcts_args(parser) - get_aug_exp_args(parser) + get_rs_exp_args(parser) if cmd: args = parser.parse_args() else: @@ -56,8 +56,8 @@ def get_mcts_args(parser): parser.add_argument("--max_depth", type=int, default=4) -def get_aug_exp_args(parser): - parser.add_argument("--aug_mode", type=str, default="single", choices=["single", "set"]) +def get_rs_exp_args(parser): + parser.add_argument("--rs_mode", type=str, default="single", choices=["single", "set"]) parser.add_argument("--is_multimodal", action="store_true", help="Specify if the model is multi-modal") @@ -79,8 +79,8 @@ async def main(args): experimenter = MCTSExperimenter(args, tree_mode="greedy") elif args.exp_mode == "random": experimenter = MCTSExperimenter(args, tree_mode="random") - elif args.exp_mode == "aug": - experimenter = AugExperimenter(args) + elif args.exp_mode == "rs": + experimenter = RandomSearchExperimenter(args) elif args.exp_mode == "base": experimenter = Experimenter(args) elif args.exp_mode == "autogluon": From 4d1a6f4c2bc88d2fd1c0f13b084679ad23be2333 Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Sat, 26 Oct 2024 01:06:48 +0800 Subject: [PATCH 145/160] rename classes and functions --- metagpt/ext/sela/Greedy.py | 19 ----- metagpt/ext/sela/experimenter/custom.py | 2 +- metagpt/ext/sela/experimenter/mcts.py | 3 +- metagpt/ext/sela/search/search_algorithm.py | 31 ++++++++ .../sela/{MCTS.py => search/tree_search.py} | 78 +++++++++++++------ 5 files changed, 86 insertions(+), 47 deletions(-) delete mode 100644 metagpt/ext/sela/Greedy.py create mode 100644 metagpt/ext/sela/search/search_algorithm.py rename metagpt/ext/sela/{MCTS.py => search/tree_search.py} (87%) diff --git a/metagpt/ext/sela/Greedy.py b/metagpt/ext/sela/Greedy.py deleted file mode 100644 index c10c4248c..000000000 --- a/metagpt/ext/sela/Greedy.py +++ /dev/null @@ -1,19 +0,0 @@ -import random - -from metagpt.ext.sela.MCTS import MCTS - - -class Greedy(MCTS): - def best_child(self): - if len(self.children) == 0: - return self.root_node - all_children = [child for children in self.children.values() for child in children] - return max(all_children, key=lambda x: x.normalized_reward.get("dev_score", 0)) - - -class Random(MCTS): - def best_child(self): - if len(self.children) == 0: - return self.root_node - all_children = [child for children in self.children.values() for child in children] - return random.choice(all_children) diff --git a/metagpt/ext/sela/experimenter/custom.py b/metagpt/ext/sela/experimenter/custom.py index 4d69e286f..70df1a78e 100644 --- a/metagpt/ext/sela/experimenter/custom.py +++ b/metagpt/ext/sela/experimenter/custom.py @@ -4,7 +4,7 @@ from metagpt.ext.sela.evaluation.evaluation import evaluate_score from metagpt.ext.sela.experimenter.experimenter import Experimenter -from metagpt.ext.sela.MCTS import create_initial_state +from metagpt.ext.sela.search.tree_search import create_initial_state class CustomExperimenter(Experimenter): diff --git a/metagpt/ext/sela/experimenter/mcts.py b/metagpt/ext/sela/experimenter/mcts.py index b83aa1392..f8f9f9fd1 100644 --- a/metagpt/ext/sela/experimenter/mcts.py +++ b/metagpt/ext/sela/experimenter/mcts.py @@ -6,8 +6,7 @@ ) from metagpt.ext.sela.evaluation.visualize_mcts import get_tree_text from metagpt.ext.sela.experimenter.experimenter import Experimenter -from metagpt.ext.sela.Greedy import Greedy, Random -from metagpt.ext.sela.MCTS import MCTS +from metagpt.ext.sela.search.search_algorithm import Greedy, Random, MCTS class MCTSExperimenter(Experimenter): diff --git a/metagpt/ext/sela/search/search_algorithm.py b/metagpt/ext/sela/search/search_algorithm.py new file mode 100644 index 000000000..675cc7c5f --- /dev/null +++ b/metagpt/ext/sela/search/search_algorithm.py @@ -0,0 +1,31 @@ +import numpy as np +from metagpt.ext.sela.search.tree_search import BaseTreeSearch, Node + + +class Greedy(BaseTreeSearch): + def best_child(self): + if len(self.children) == 0: + return self.root_node + all_children = [child for children in self.children.values() for child in children] + return max(all_children, key=lambda x: x.normalized_reward.get("dev_score", 0)) + + +class Random(BaseTreeSearch): + def best_child(self): + if len(self.children) == 0: + return self.root_node + all_children = [child for children in self.children.values() for child in children] + return np.random.choice(all_children) + + +class MCTS(BaseTreeSearch): + def best_child(self): + def uct(node: Node): + n_visits = node.visited if node.visited else self.c_unvisited + avg_value = node.avg_value() if node.visited else node.value / self.c_unvisited + return avg_value + self.c_explore * np.sqrt(np.log(node.parent.visited) / n_visits) + + if len(self.children) == 0: + return self.root_node + all_children = [child for children in self.children.values() for child in children] + return max(all_children, key=uct) diff --git a/metagpt/ext/sela/MCTS.py b/metagpt/ext/sela/search/tree_search.py similarity index 87% rename from metagpt/ext/sela/MCTS.py rename to metagpt/ext/sela/search/tree_search.py index ac4b68024..a27495230 100644 --- a/metagpt/ext/sela/MCTS.py +++ b/metagpt/ext/sela/search/tree_search.py @@ -1,8 +1,6 @@ import json -import math import os import pickle -import random import shutil import numpy as np @@ -18,7 +16,30 @@ from metagpt.utils.common import read_json_file -def initialize_di_root_node(state, reflection: bool = True): +def initialize_di_root_node(state: dict, reflection: bool = True): + """ + Initialize the root node of the decision tree. + + Args: + state (dict): The initial state of the tree, containing: + - task (str): The task to be performed (e.g., "titanic"). + - work_dir (str): The working directory. + - node_dir (str): The directory for the node. + - dataset_config (dict): The configuration of the dataset. + - datasets_dir (str): The directory of the datasets. + - exp_pool_path (str): The path to the experiment pool. + - requirement (str): The requirement for the task. + - has_run (bool): Whether the task has run. + - start_task_id (int): The ID of the starting task. + - low_is_better (bool): Whether a lower score is better. + - role_timeout (int): The timeout for the role. + - external_eval (bool): Whether to use external evaluation. + - custom_dataset_dir (str): The directory of the custom dataset. + reflection (bool, optional): Whether to use reflection. Defaults to True. + + Returns: + tuple: A tuple containing the ResearchAssistant role and the root Node. + """ role = ResearchAssistant( node_id="0", start_task_id=state["start_task_id"], @@ -29,7 +50,21 @@ def initialize_di_root_node(state, reflection: bool = True): return role, Node(parent=None, state=state, action=None, value=0) -def create_initial_state(task, start_task_id, data_config, args): +def create_initial_state(task: str, start_task_id: int, data_config: dict, args): + """ + Create the initial state of the tree. + + Args: + task (str): The task to be performed. + start_task_id (int): The ID of the starting task. + data_config (dict): The configuration of the data. + Expected keys: 'datasets', 'work_dir', 'role_dir'. + args (Namespace): The arguments passed to the program. + Expected attributes: 'external_eval', 'custom_dataset_dir', 'special_instruction', 'name', 'low_is_better', 'role_timeout'. + + Returns: + dict: The initial state of the tree. + """ external_eval = args.external_eval if args.custom_dataset_dir: @@ -69,7 +104,6 @@ def create_initial_state(task, start_task_id, data_config, args): os.makedirs(initial_state["node_dir"], exist_ok=True) return initial_state - class Node: state: dict = {} action: str = None @@ -225,7 +259,7 @@ def evaluate_simulation(self, score_dict): self.get_and_move_predictions("test") return score_dict - async def run_node(self, role=None): + async def run_node(self, role: ResearchAssistant = None): if self.is_terminal() and role is not None: if role.state_saved: return self.raw_reward @@ -272,7 +306,9 @@ def normalize_score(score): return score_dict, result_dict -class MCTS: + + +class BaseTreeSearch: # data_path root_node: Node = None children: dict = {} @@ -283,7 +319,7 @@ class MCTS: # insight generator instruction_generator: InstructionGenerator = None - def __init__(self, root_node, max_depth, use_fixed_insights): + def __init__(self, root_node: Node, max_depth: int, use_fixed_insights: bool): self.root_node = root_node self.max_depth = max_depth self.use_fixed_insights = use_fixed_insights @@ -294,15 +330,7 @@ def select(self, node: Node): return node def best_child(self): - def uct(node: Node): - n_visits = node.visited if node.visited else self.c_unvisited - avg_value = node.avg_value() if node.visited else node.value / self.c_unvisited - return avg_value + self.c_explore * math.sqrt(math.log(node.parent.visited) / n_visits) - - if len(self.children) == 0: - return self.root_node - all_children = [child for children in self.children.values() for child in children] - return max(all_children, key=uct) + raise NotImplementedError async def expand(self, node: Node, max_children=5): await node.expand(max_children, self.instruction_generator) @@ -314,13 +342,13 @@ async def simulate(self, node: Node, role=None): "Returns the reward for a random simulation (to completion) of `node`" mcts_logger.log("MCTS", f"Start simulating node {node.id}:") while node.children: - node = random.choice(node.children) + node = np.random.choice(node.children) reward, result_dict = await node.run_node(role) mcts_logger.log("MCTS", f"Simulated node's reward: {reward}") # TODO: add new insights return reward - def backpropagate(self, node: Node, reward): + def backpropagate(self, node: Node, reward: dict): child_node = node node.update(reward) node = node.parent @@ -333,7 +361,7 @@ def best_path(self, root: Node): global_best_score = root.normalized_reward["test_score"] dev_best_score = root.normalized_reward["dev_score"] - def bfs(node: Node, best_score, best_child: Node, split): + def bfs(node: Node, best_score: float, best_child: Node, split: str): assert split in ["test_score", "dev_score"] if node not in self.children: return best_score, best_child @@ -354,7 +382,7 @@ def bfs(node: Node, best_score, best_child: Node, split): def get_num_simulations(self): return self.root_node.visited - def save_node_order(self, node_id): + def save_node_order(self, node_id: str): self.node_order.append(node_id) with open(os.path.join(self.root_node.state["node_dir"], "node_order.json"), "w") as f: json.dump(self.node_order, f) @@ -375,7 +403,7 @@ def get_score_order_dict(self): scores["test_raw"].append(node.raw_reward["test_score"]) return scores - async def search(self, state, args): + async def search(self, state: dict, args): reflection = args.reflection load_tree = args.load_tree rollouts = args.rollouts @@ -424,17 +452,17 @@ async def search(self, state, args): self.save_node_order(node.id) return self.best_path(root) - async def expand_and_simulate(self, node): + async def expand_and_simulate(self, node: Node): # Expand and randomly select a child node, then simulate it if node.visited > 0: children = await self.expand(node) - node = random.choice(children) + node = np.random.choice(children) reward = await self.simulate(node) self.backpropagate(node, reward) return node, reward def load_tree(self): - def load_children_node(node): + def load_children_node(node: Node): mcts_logger.log("MCTS", f"Load node {node.id}'s child: {node.children}") if node.is_terminal() or not node.children: return From 61492d9ff5db96e232c2ad70835a409b715973ba Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Mon, 28 Oct 2024 14:11:53 +0800 Subject: [PATCH 146/160] add data type; fix instruction generation --- metagpt/ext/sela/data.yaml | 2 +- metagpt/ext/sela/evaluation/visualize_mcts.py | 2 +- metagpt/ext/sela/experimenter/experimenter.py | 2 +- metagpt/ext/sela/experimenter/random_search.py | 2 +- metagpt/ext/sela/research_assistant.py | 2 +- metagpt/ext/sela/search/tree_search.py | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/metagpt/ext/sela/data.yaml b/metagpt/ext/sela/data.yaml index 989a07966..5f4a290ea 100644 --- a/metagpt/ext/sela/data.yaml +++ b/metagpt/ext/sela/data.yaml @@ -1,3 +1,3 @@ datasets_dir: "path/to/datasets" # path to the datasets directory -work_dir: ../workspace # path to the workspace directory +work_dir: ../../workspace # path to the workspace directory role_dir: storage/SELA # path to the role directory \ No newline at end of file diff --git a/metagpt/ext/sela/evaluation/visualize_mcts.py b/metagpt/ext/sela/evaluation/visualize_mcts.py index 62efed917..6f803a91c 100644 --- a/metagpt/ext/sela/evaluation/visualize_mcts.py +++ b/metagpt/ext/sela/evaluation/visualize_mcts.py @@ -3,7 +3,7 @@ import matplotlib.pyplot as plt import networkx as nx -from metagpt.ext.sela.MCTS import Node +from metagpt.ext.sela.search.tree_search import Node NODE_TEMPLATE = """\ [Node {id}] diff --git a/metagpt/ext/sela/experimenter/experimenter.py b/metagpt/ext/sela/experimenter/experimenter.py index 03671c9ff..fd9122d29 100644 --- a/metagpt/ext/sela/experimenter/experimenter.py +++ b/metagpt/ext/sela/experimenter/experimenter.py @@ -6,7 +6,7 @@ import pandas as pd from metagpt.ext.sela.evaluation.evaluation import evaluate_score -from metagpt.ext.sela.MCTS import create_initial_state +from metagpt.ext.sela.search.tree_search import create_initial_state from metagpt.ext.sela.research_assistant import ResearchAssistant from metagpt.ext.sela.utils import DATA_CONFIG, save_notebook diff --git a/metagpt/ext/sela/experimenter/random_search.py b/metagpt/ext/sela/experimenter/random_search.py index 94fb6960b..5617ee601 100644 --- a/metagpt/ext/sela/experimenter/random_search.py +++ b/metagpt/ext/sela/experimenter/random_search.py @@ -17,7 +17,7 @@ async def run_experiment(self): # state = create_initial_state(self.args.task, start_task_id=1, data_config=self.data_config, low_is_better=self.args.low_is_better, name="") user_requirement = self.state["requirement"] exp_pool_path = get_exp_pool_path(self.args.task, self.data_config, pool_name="ds_analysis_pool") - exp_pool = InstructionGenerator.load_analysis_pool( + exp_pool = InstructionGenerator.load_insight_pool( exp_pool_path, use_fixed_insights=self.args.use_fixed_insights ) if self.args.rs_mode == "single": diff --git a/metagpt/ext/sela/research_assistant.py b/metagpt/ext/sela/research_assistant.py index 5ce1be68e..21cc46447 100644 --- a/metagpt/ext/sela/research_assistant.py +++ b/metagpt/ext/sela/research_assistant.py @@ -71,7 +71,7 @@ def get_node_name(self): return f"Node-{self.node_id}" def get_next_instruction(self): - return self.planner.plan.tasks[self.start_task_id] + return self.planner.plan.tasks[self.start_task_id].instruction def change_next_instruction(self, new_instruction): if new_instruction is not None: diff --git a/metagpt/ext/sela/search/tree_search.py b/metagpt/ext/sela/search/tree_search.py index a27495230..08f4abb5d 100644 --- a/metagpt/ext/sela/search/tree_search.py +++ b/metagpt/ext/sela/search/tree_search.py @@ -113,7 +113,7 @@ class Node: normalized_reward: dict = {"train_score": 0, "dev_score": 0, "test_score": 0} parent = None - def __init__(self, parent=None, state=None, action=None, value=0, max_depth=4, **kwargs): + def __init__(self, parent=None, state: dict = None, action: str = None, value: float = 0, max_depth: int = 4, **kwargs): self.state = state self.action = action self.value = value From 9abb8db954966a04516912c23a036d1c3a5978df Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Mon, 28 Oct 2024 15:03:51 +0800 Subject: [PATCH 147/160] edit-baseline-experimenter --- metagpt/ext/sela/experimenter/aide.py | 8 ++++++-- metagpt/ext/sela/experimenter/autogluon.py | 18 ++++++++++-------- metagpt/ext/sela/experimenter/autosklearn.py | 10 +++++----- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/metagpt/ext/sela/experimenter/aide.py b/metagpt/ext/sela/experimenter/aide.py index fb71dbdab..50fae94c1 100644 --- a/metagpt/ext/sela/experimenter/aide.py +++ b/metagpt/ext/sela/experimenter/aide.py @@ -1,11 +1,15 @@ -import aide import os import time +import aide + os.environ["OPENAI_API_KEY"] = "sk-xxx" os.environ["OPENAI_BASE_URL"] = "your url" + start_time = time.time() + data_dir = "xxx/data/titanic" + goal = f""" # User requirement ({data_dir}, 'This is a 04_titanic dataset. Your goal is to predict the target column `Survived`.\nPerform data analysis, data preprocessing, feature engineering, and modeling to predict the target. \nReport f1 on the eval data. Do not plot or make any visualizations.\n') @@ -28,4 +32,4 @@ end_time = time.time() execution_time = end_time - start_time -print(f"run time : {execution_time} seconds") \ No newline at end of file +print(f"run time : {execution_time} seconds") diff --git a/metagpt/ext/sela/experimenter/autogluon.py b/metagpt/ext/sela/experimenter/autogluon.py index e7593cfb9..f547ce4ba 100644 --- a/metagpt/ext/sela/experimenter/autogluon.py +++ b/metagpt/ext/sela/experimenter/autogluon.py @@ -1,8 +1,10 @@ -from datetime import datetime -from metagpt.ext.sela.experimenter.custom import CustomExperimenter import os +from datetime import datetime + import pandas as pd +from metagpt.ext.sela.experimenter.custom import CustomExperimenter + class AGRunner: def __init__(self, state=None): @@ -11,6 +13,7 @@ def __init__(self, state=None): def run(self): from autogluon.tabular import TabularDataset, TabularPredictor + train_path = self.datasets["train"] dev_path = self.datasets["dev"] dev_wo_target_path = self.datasets["dev_wo_target"] @@ -32,6 +35,7 @@ def run(self): def run_multimodal(self): from autogluon.multimodal import MultiModalPredictor + target_col = self.state["dataset_config"]["target_col"] train_path = self.datasets["train"] dev_path = self.datasets["dev"] @@ -56,10 +60,7 @@ def run_multimodal(self): test_preds = predictor.predict(test_data) # Return predictions for dev and test datasets - return { - "dev_preds": dev_preds, - "test_preds": test_preds - } + return {"dev_preds": dev_preds, "test_preds": test_preds} def load_split_dataset(self, train_path, dev_path, dev_wo_target_path, test_wo_target_path): """ @@ -94,7 +95,8 @@ def load_split_dataset(self, train_path, dev_path, dev_wo_target_path, test_wo_t train_data[image_column] = train_data[image_column].apply(lambda x: os.path.join(root_folder, x)) dev_data[image_column] = dev_data[image_column].apply(lambda x: os.path.join(root_folder, x)) dev_wo_target_data[image_column] = dev_wo_target_data[image_column].apply( - lambda x: os.path.join(root_folder, x)) + lambda x: os.path.join(root_folder, x) + ) test_data[image_column] = test_data[image_column].apply(lambda x: os.path.join(root_folder, x)) return train_data, dev_data, dev_wo_target_data, test_data @@ -106,7 +108,7 @@ class GluonExperimenter(CustomExperimenter): def __init__(self, args, **kwargs): super().__init__(args, **kwargs) self.framework = AGRunner(self.state) - self.is_multimodal = args.is_multimodal if hasattr(args, 'is_multimodal') else False + self.is_multimodal = args.is_multimodal if hasattr(args, "is_multimodal") else False async def run_experiment(self): if not self.is_multimodal: diff --git a/metagpt/ext/sela/experimenter/autosklearn.py b/metagpt/ext/sela/experimenter/autosklearn.py index 718e758d4..f6ff267e7 100644 --- a/metagpt/ext/sela/experimenter/autosklearn.py +++ b/metagpt/ext/sela/experimenter/autosklearn.py @@ -1,8 +1,10 @@ from datetime import datetime +from functools import partial + import pandas as pd -from metagpt.ext.sela.experimenter.custom import CustomExperimenter + from metagpt.ext.sela.evaluation.evaluation import evaluate_score -from functools import partial +from metagpt.ext.sela.experimenter.custom import CustomExperimenter def custom_scorer(y_true, y_pred, metric_name): @@ -19,9 +21,7 @@ def __init__(self, state=None): def create_autosklearn_scorer(self, metric_name): from autosklearn.metrics import make_scorer - return make_scorer( - name=metric_name, score_func=partial(custom_scorer, metric_name=metric_name) - ) + return make_scorer(name=metric_name, score_func=partial(custom_scorer, metric_name=metric_name)) def run(self): import autosklearn.classification From e41f9342cdeb9d5f8efafe61d7da8e1376715e69 Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Mon, 28 Oct 2024 17:14:26 +0800 Subject: [PATCH 148/160] update readme --- metagpt/ext/sela/README.md | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/metagpt/ext/sela/README.md b/metagpt/ext/sela/README.md index c8a1adea8..c8df4eeba 100644 --- a/metagpt/ext/sela/README.md +++ b/metagpt/ext/sela/README.md @@ -7,7 +7,7 @@ - Download Datasets:https://deepwisdom.feishu.cn/drive/folder/RVyofv9cvlvtxKdddt2cyn3BnTc?from=from_copylink - Download and prepare datasets from scratch: ``` -cd expo/data +cd data python dataset.py --save_analysis_pool python hf_data.py --save_analysis_pool ``` @@ -60,16 +60,36 @@ pip install -r requirements.txt #### Run -- `python run_experiment.py --exp_mode mcts --task titanic --rollouts 10` +- Examples + ``` + python run_experiment.py --exp_mode mcts --task titanic --rollouts 10 + python run_experiment.py --exp_mode mcts --task house-prices --rollouts 10 --low_is_better + ``` -If the dataset has reg metric, remember to use `--low_is_better`: -- `python run_experiment.py --exp_mode mcts --task house-prices --rollouts 10 --low_is_better` +- `--rollouts` - The number of rollouts - -In addition to the generated insights, include the fixed insights saved in `expo/insights/fixed_insights.json` -- `--use_fixed_insights` +- `--use_fixed_insights` - In addition to the generated insights, include the fixed insights saved in `expo/insights/fixed_insights.json` +- `--low_is_better` - If the dataset has reg metric, remember to use `--low_is_better` + +- `--from_scratch` - Do not use pre-processed insight pool, generate new insight pool based on dataset before running MCTS, facilitating subsequent tuning to propose search space prompts + +- `--role_timeout` - The timeout for the role + - This feature limits the duration of a single simulation, making the experiment duration more controllable (for example, if you do ten rollouts and set role_timeout to 1,000, the experiment will stop at the latest after 10,000s) + + +- `--max_depth` - The maximum depth of MCTS, default is 4 (nodes at this depth directly return the previous simulation result without further expansion) + +- `--load_tree` - If MCTS was interrupted due to certain reasons but had already run multiple rollouts, you can use `--load_tree`. + - For example: + ``` + python run_experiment.py --exp_mode mcts --task titanic --rollouts 10 + ``` + - If this was interrupted after running three rollouts, you can use `--load_tree`: + ``` + python run_experiment.py --exp_mode mcts --task titanic --rollouts 7 --load_tree + ``` #### Ablation Study @@ -112,7 +132,7 @@ python run_experiment.py --exp_mode mcts --custom_dataset_dir Date: Mon, 28 Oct 2024 17:47:27 +0800 Subject: [PATCH 149/160] reorder import --- metagpt/ext/sela/experimenter/experimenter.py | 2 +- metagpt/ext/sela/experimenter/mcts.py | 2 +- metagpt/ext/sela/research_assistant.py | 2 +- metagpt/ext/sela/run_experiment.py | 2 +- metagpt/ext/sela/search/search_algorithm.py | 1 + metagpt/ext/sela/search/tree_search.py | 21 ++++++++++++------- 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/metagpt/ext/sela/experimenter/experimenter.py b/metagpt/ext/sela/experimenter/experimenter.py index fd9122d29..3df46b74b 100644 --- a/metagpt/ext/sela/experimenter/experimenter.py +++ b/metagpt/ext/sela/experimenter/experimenter.py @@ -6,8 +6,8 @@ import pandas as pd from metagpt.ext.sela.evaluation.evaluation import evaluate_score -from metagpt.ext.sela.search.tree_search import create_initial_state from metagpt.ext.sela.research_assistant import ResearchAssistant +from metagpt.ext.sela.search.tree_search import create_initial_state from metagpt.ext.sela.utils import DATA_CONFIG, save_notebook diff --git a/metagpt/ext/sela/experimenter/mcts.py b/metagpt/ext/sela/experimenter/mcts.py index f8f9f9fd1..9fd66121d 100644 --- a/metagpt/ext/sela/experimenter/mcts.py +++ b/metagpt/ext/sela/experimenter/mcts.py @@ -6,7 +6,7 @@ ) from metagpt.ext.sela.evaluation.visualize_mcts import get_tree_text from metagpt.ext.sela.experimenter.experimenter import Experimenter -from metagpt.ext.sela.search.search_algorithm import Greedy, Random, MCTS +from metagpt.ext.sela.search.search_algorithm import MCTS, Greedy, Random class MCTSExperimenter(Experimenter): diff --git a/metagpt/ext/sela/research_assistant.py b/metagpt/ext/sela/research_assistant.py index 21cc46447..2c698c1d2 100644 --- a/metagpt/ext/sela/research_assistant.py +++ b/metagpt/ext/sela/research_assistant.py @@ -6,9 +6,9 @@ from pydantic import model_validator -from metagpt.ext.sela.utils import mcts_logger, save_notebook from metagpt.actions.di.write_analysis_code import WriteAnalysisCode from metagpt.const import SERDESER_PATH +from metagpt.ext.sela.utils import mcts_logger, save_notebook from metagpt.roles.di.data_interpreter import DataInterpreter from metagpt.schema import Message, Task, TaskResult from metagpt.utils.common import CodeParser, write_json_file diff --git a/metagpt/ext/sela/run_experiment.py b/metagpt/ext/sela/run_experiment.py index 7d2a317c6..4cced19c3 100644 --- a/metagpt/ext/sela/run_experiment.py +++ b/metagpt/ext/sela/run_experiment.py @@ -2,12 +2,12 @@ import asyncio from metagpt.ext.sela.data.custom_task import get_mle_is_lower_better, get_mle_task_id -from metagpt.ext.sela.experimenter.random_search import RandomSearchExperimenter from metagpt.ext.sela.experimenter.autogluon import GluonExperimenter from metagpt.ext.sela.experimenter.autosklearn import AutoSklearnExperimenter from metagpt.ext.sela.experimenter.custom import CustomExperimenter from metagpt.ext.sela.experimenter.experimenter import Experimenter from metagpt.ext.sela.experimenter.mcts import MCTSExperimenter +from metagpt.ext.sela.experimenter.random_search import RandomSearchExperimenter def get_args(cmd=True): diff --git a/metagpt/ext/sela/search/search_algorithm.py b/metagpt/ext/sela/search/search_algorithm.py index 675cc7c5f..ca47d8cf6 100644 --- a/metagpt/ext/sela/search/search_algorithm.py +++ b/metagpt/ext/sela/search/search_algorithm.py @@ -1,4 +1,5 @@ import numpy as np + from metagpt.ext.sela.search.tree_search import BaseTreeSearch, Node diff --git a/metagpt/ext/sela/search/tree_search.py b/metagpt/ext/sela/search/tree_search.py index 08f4abb5d..cde8dc82a 100644 --- a/metagpt/ext/sela/search/tree_search.py +++ b/metagpt/ext/sela/search/tree_search.py @@ -6,8 +6,14 @@ import numpy as np import pandas as pd -from metagpt.ext.sela.data.custom_task import get_mle_bench_requirements, get_mle_task_id -from metagpt.ext.sela.data.dataset import generate_task_requirement, get_split_dataset_path +from metagpt.ext.sela.data.custom_task import ( + get_mle_bench_requirements, + get_mle_task_id, +) +from metagpt.ext.sela.data.dataset import ( + generate_task_requirement, + get_split_dataset_path, +) from metagpt.ext.sela.evaluation.evaluation import evaluate_score from metagpt.ext.sela.insights.instruction_generator import InstructionGenerator from metagpt.ext.sela.research_assistant import ResearchAssistant, TimeoutException @@ -57,9 +63,9 @@ def create_initial_state(task: str, start_task_id: int, data_config: dict, args) Args: task (str): The task to be performed. start_task_id (int): The ID of the starting task. - data_config (dict): The configuration of the data. + data_config (dict): The configuration of the data. Expected keys: 'datasets', 'work_dir', 'role_dir'. - args (Namespace): The arguments passed to the program. + args (Namespace): The arguments passed to the program. Expected attributes: 'external_eval', 'custom_dataset_dir', 'special_instruction', 'name', 'low_is_better', 'role_timeout'. Returns: @@ -104,6 +110,7 @@ def create_initial_state(task: str, start_task_id: int, data_config: dict, args) os.makedirs(initial_state["node_dir"], exist_ok=True) return initial_state + class Node: state: dict = {} action: str = None @@ -113,7 +120,9 @@ class Node: normalized_reward: dict = {"train_score": 0, "dev_score": 0, "test_score": 0} parent = None - def __init__(self, parent=None, state: dict = None, action: str = None, value: float = 0, max_depth: int = 4, **kwargs): + def __init__( + self, parent=None, state: dict = None, action: str = None, value: float = 0, max_depth: int = 4, **kwargs + ): self.state = state self.action = action self.value = value @@ -306,8 +315,6 @@ def normalize_score(score): return score_dict, result_dict - - class BaseTreeSearch: # data_path root_node: Node = None From 6321b793a1036ff7e2d29dbe99de6bcc8d946cd8 Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Mon, 28 Oct 2024 18:14:59 +0800 Subject: [PATCH 150/160] reoreder --- metagpt/ext/sela/scripts/visualize_experiment.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/metagpt/ext/sela/scripts/visualize_experiment.py b/metagpt/ext/sela/scripts/visualize_experiment.py index 4133942fc..a6d980d11 100644 --- a/metagpt/ext/sela/scripts/visualize_experiment.py +++ b/metagpt/ext/sela/scripts/visualize_experiment.py @@ -1,6 +1,9 @@ import networkx as nx -from metagpt.ext.sela.evaluation.visualize_mcts import build_tree_recursive, visualize_tree +from metagpt.ext.sela.evaluation.visualize_mcts import ( + build_tree_recursive, + visualize_tree, +) from metagpt.ext.sela.MCTS import MCTS, create_initial_state, initialize_di_root_node from metagpt.ext.sela.run_experiment import get_args from metagpt.ext.sela.utils import DATA_CONFIG From 1aac79c3b379c47b0b80ad8efa216d417b3494a2 Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Mon, 28 Oct 2024 21:05:59 +0800 Subject: [PATCH 151/160] identation on readme --- metagpt/ext/sela/README.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/metagpt/ext/sela/README.md b/metagpt/ext/sela/README.md index c8df4eeba..3fa03ee08 100644 --- a/metagpt/ext/sela/README.md +++ b/metagpt/ext/sela/README.md @@ -179,25 +179,25 @@ model_kwargs = model_kwargs | { "temperature": temperature, "max_tokens": max_tokens, } - if "claude-" in model: - query_func = backend_anthropic.query - else: - query_func = backend_openai.query +if "claude-" in model: + query_func = backend_anthropic.query +else: + query_func = backend_openai.query ``` Since deepseekV2.5 no longer supports system message using function call, modify `aideml/aide/agent.py`'s line 312: ```python response = cast( - dict, - query( - system_message=None, - user_message=prompt, - func_spec=review_func_spec, - model=self.acfg.feedback.model, - temperature=self.acfg.feedback.temp, - ), - ) + dict, + query( + system_message=None, + user_message=prompt, + func_spec=review_func_spec, + model=self.acfg.feedback.model, + temperature=self.acfg.feedback.temp, + ), +) ``` Modify and install: From e0cbbf82f437a525412a730436acd6923ca4e75d Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Mon, 28 Oct 2024 21:42:46 +0800 Subject: [PATCH 152/160] rename research assistant to experimenter --- metagpt/ext/sela/README.md | 2 +- metagpt/ext/sela/data/custom_task.py | 2 +- ...{research_assistant.py => experimenter.py} | 0 metagpt/ext/sela/run_experiment.py | 30 +++++++++---------- .../sela/{experimenter => runner}/__init__.py | 0 .../ext/sela/{experimenter => runner}/aide.py | 0 .../{experimenter => runner}/autogluon.py | 4 +-- .../{experimenter => runner}/autosklearn.py | 4 +-- .../sela/{experimenter => runner}/custom.py | 4 +-- .../ext/sela/{experimenter => runner}/mcts.py | 4 +-- .../mle_bench/instructions.py | 4 +-- .../{experimenter => runner}/random_search.py | 6 ++-- .../experimenter.py => runner/runner.py} | 4 +-- metagpt/ext/sela/search/tree_search.py | 2 +- 14 files changed, 33 insertions(+), 33 deletions(-) rename metagpt/ext/sela/{research_assistant.py => experimenter.py} (100%) rename metagpt/ext/sela/{experimenter => runner}/__init__.py (100%) rename metagpt/ext/sela/{experimenter => runner}/aide.py (100%) rename metagpt/ext/sela/{experimenter => runner}/autogluon.py (98%) rename metagpt/ext/sela/{experimenter => runner}/autosklearn.py (96%) rename metagpt/ext/sela/{experimenter => runner}/custom.py (95%) rename metagpt/ext/sela/{experimenter => runner}/mcts.py (96%) rename metagpt/ext/sela/{experimenter => runner}/mle_bench/instructions.py (98%) rename metagpt/ext/sela/{experimenter => runner}/random_search.py (92%) rename metagpt/ext/sela/{experimenter/experimenter.py => runner/runner.py} (98%) diff --git a/metagpt/ext/sela/README.md b/metagpt/ext/sela/README.md index 3fa03ee08..829306e36 100644 --- a/metagpt/ext/sela/README.md +++ b/metagpt/ext/sela/README.md @@ -213,7 +213,7 @@ Run the following script to get the running results, a `log` folder and a `works The `log` folder will contain the experimental configuration and the generated scheme, and the `workspace` folder will save the final results generated by aide ``` -python experimenter/aide.py +python runner/aide.py ``` ### Autogluon diff --git a/metagpt/ext/sela/data/custom_task.py b/metagpt/ext/sela/data/custom_task.py index 3371d5b1c..08a7cbabb 100644 --- a/metagpt/ext/sela/data/custom_task.py +++ b/metagpt/ext/sela/data/custom_task.py @@ -1,7 +1,7 @@ import os from metagpt.ext.sela.data.dataset import SPECIAL_INSTRUCTIONS -from metagpt.ext.sela.experimenter.mle_bench.instructions import ( +from metagpt.ext.sela.runner.mle_bench.instructions import ( ADDITIONAL_NOTES, INSTRUCTIONS, INSTRUCTIONS_OBFUSCATED, diff --git a/metagpt/ext/sela/research_assistant.py b/metagpt/ext/sela/experimenter.py similarity index 100% rename from metagpt/ext/sela/research_assistant.py rename to metagpt/ext/sela/experimenter.py diff --git a/metagpt/ext/sela/run_experiment.py b/metagpt/ext/sela/run_experiment.py index 4cced19c3..32130a6fb 100644 --- a/metagpt/ext/sela/run_experiment.py +++ b/metagpt/ext/sela/run_experiment.py @@ -2,12 +2,12 @@ import asyncio from metagpt.ext.sela.data.custom_task import get_mle_is_lower_better, get_mle_task_id -from metagpt.ext.sela.experimenter.autogluon import GluonExperimenter -from metagpt.ext.sela.experimenter.autosklearn import AutoSklearnExperimenter -from metagpt.ext.sela.experimenter.custom import CustomExperimenter -from metagpt.ext.sela.experimenter.experimenter import Experimenter -from metagpt.ext.sela.experimenter.mcts import MCTSExperimenter -from metagpt.ext.sela.experimenter.random_search import RandomSearchExperimenter +from metagpt.ext.sela.runner.autogluon import GluonRunner +from metagpt.ext.sela.runner.autosklearn import AutoSklearnRunner +from metagpt.ext.sela.runner.custom import CustomRunner +from metagpt.ext.sela.runner.mcts import MCTSRunner +from metagpt.ext.sela.runner.random_search import RandomSearchRunner +from metagpt.ext.sela.runner.runner import Runner def get_args(cmd=True): @@ -74,24 +74,24 @@ def get_di_args(parser): async def main(args): if args.exp_mode == "mcts": - experimenter = MCTSExperimenter(args) + runner = MCTSRunner(args) elif args.exp_mode == "greedy": - experimenter = MCTSExperimenter(args, tree_mode="greedy") + runner = MCTSRunner(args, tree_mode="greedy") elif args.exp_mode == "random": - experimenter = MCTSExperimenter(args, tree_mode="random") + runner = MCTSRunner(args, tree_mode="random") elif args.exp_mode == "rs": - experimenter = RandomSearchExperimenter(args) + runner = RandomSearchRunner(args) elif args.exp_mode == "base": - experimenter = Experimenter(args) + runner = Runner(args) elif args.exp_mode == "autogluon": - experimenter = GluonExperimenter(args) + runner = GluonRunner(args) elif args.exp_mode == "custom": - experimenter = CustomExperimenter(args) + runner = CustomRunner(args) elif args.exp_mode == "autosklearn": - experimenter = AutoSklearnExperimenter(args) + runner = AutoSklearnRunner(args) else: raise ValueError(f"Invalid exp_mode: {args.exp_mode}") - await experimenter.run_experiment() + await runner.run_experiment() if __name__ == "__main__": diff --git a/metagpt/ext/sela/experimenter/__init__.py b/metagpt/ext/sela/runner/__init__.py similarity index 100% rename from metagpt/ext/sela/experimenter/__init__.py rename to metagpt/ext/sela/runner/__init__.py diff --git a/metagpt/ext/sela/experimenter/aide.py b/metagpt/ext/sela/runner/aide.py similarity index 100% rename from metagpt/ext/sela/experimenter/aide.py rename to metagpt/ext/sela/runner/aide.py diff --git a/metagpt/ext/sela/experimenter/autogluon.py b/metagpt/ext/sela/runner/autogluon.py similarity index 98% rename from metagpt/ext/sela/experimenter/autogluon.py rename to metagpt/ext/sela/runner/autogluon.py index f547ce4ba..48737da04 100644 --- a/metagpt/ext/sela/experimenter/autogluon.py +++ b/metagpt/ext/sela/runner/autogluon.py @@ -3,7 +3,7 @@ import pandas as pd -from metagpt.ext.sela.experimenter.custom import CustomExperimenter +from metagpt.ext.sela.runner.custom import CustomRunner class AGRunner: @@ -102,7 +102,7 @@ def load_split_dataset(self, train_path, dev_path, dev_wo_target_path, test_wo_t return train_data, dev_data, dev_wo_target_data, test_data -class GluonExperimenter(CustomExperimenter): +class GluonRunner(CustomRunner): result_path: str = "results/autogluon" def __init__(self, args, **kwargs): diff --git a/metagpt/ext/sela/experimenter/autosklearn.py b/metagpt/ext/sela/runner/autosklearn.py similarity index 96% rename from metagpt/ext/sela/experimenter/autosklearn.py rename to metagpt/ext/sela/runner/autosklearn.py index f6ff267e7..7d0eb364e 100644 --- a/metagpt/ext/sela/experimenter/autosklearn.py +++ b/metagpt/ext/sela/runner/autosklearn.py @@ -4,7 +4,7 @@ import pandas as pd from metagpt.ext.sela.evaluation.evaluation import evaluate_score -from metagpt.ext.sela.experimenter.custom import CustomExperimenter +from metagpt.ext.sela.runner.custom import CustomRunner def custom_scorer(y_true, y_pred, metric_name): @@ -69,7 +69,7 @@ def run(self): return {"test_preds": test_preds, "dev_preds": dev_preds} -class AutoSklearnExperimenter(CustomExperimenter): +class AutoSklearnRunner(CustomRunner): result_path: str = "results/autosklearn" def __init__(self, args, **kwargs): diff --git a/metagpt/ext/sela/experimenter/custom.py b/metagpt/ext/sela/runner/custom.py similarity index 95% rename from metagpt/ext/sela/experimenter/custom.py rename to metagpt/ext/sela/runner/custom.py index 70df1a78e..e9a8ee276 100644 --- a/metagpt/ext/sela/experimenter/custom.py +++ b/metagpt/ext/sela/runner/custom.py @@ -3,11 +3,11 @@ import pandas as pd from metagpt.ext.sela.evaluation.evaluation import evaluate_score -from metagpt.ext.sela.experimenter.experimenter import Experimenter +from metagpt.ext.sela.runner.runner import Runner from metagpt.ext.sela.search.tree_search import create_initial_state -class CustomExperimenter(Experimenter): +class CustomRunner(Runner): result_path: str = "results/custom" def __init__(self, args, **kwargs): diff --git a/metagpt/ext/sela/experimenter/mcts.py b/metagpt/ext/sela/runner/mcts.py similarity index 96% rename from metagpt/ext/sela/experimenter/mcts.py rename to metagpt/ext/sela/runner/mcts.py index 9fd66121d..8b6c14100 100644 --- a/metagpt/ext/sela/experimenter/mcts.py +++ b/metagpt/ext/sela/runner/mcts.py @@ -5,11 +5,11 @@ node_evaluate_score_sela, ) from metagpt.ext.sela.evaluation.visualize_mcts import get_tree_text -from metagpt.ext.sela.experimenter.experimenter import Experimenter +from metagpt.ext.sela.runner.runner import Runner from metagpt.ext.sela.search.search_algorithm import MCTS, Greedy, Random -class MCTSExperimenter(Experimenter): +class MCTSRunner(Runner): result_path: str = "results/mcts" def __init__(self, args, tree_mode=None, **kwargs): diff --git a/metagpt/ext/sela/experimenter/mle_bench/instructions.py b/metagpt/ext/sela/runner/mle_bench/instructions.py similarity index 98% rename from metagpt/ext/sela/experimenter/mle_bench/instructions.py rename to metagpt/ext/sela/runner/mle_bench/instructions.py index 5c9c7b70f..136726bcf 100644 --- a/metagpt/ext/sela/experimenter/mle_bench/instructions.py +++ b/metagpt/ext/sela/runner/mle_bench/instructions.py @@ -8,7 +8,7 @@ BENCHMARK INSTRUCTIONS ------ -First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `experimenter/mle_bench/instructions.py`. +First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `runner/mle_bench/instructions.py`. - **Competition-specific instructions**: The instructions for the competition are available in `{dataset_dir}/description.md`. Read this file to understand the problem setup for the competition you are currently solving. - **Dataset**: The dataset files for the competition are available in `{dataset_dir}`. @@ -28,7 +28,7 @@ BENCHMARK INSTRUCTIONS ------ -First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `experimenter/mle_bench/instructions.py`. +First, please read the instructions below which are specific to our benchmark setup. Note, these instructions can be found again in `runner/mle_bench/instructions.py`. - **Task-specific instructions**: The instructions for the task are available in `{dataset_dir}/description.md`. Read this file to understand the problem setup for the task you are currently solving. - **Dataset**: The dataset files for the task are available in `{dataset_dir}/`. diff --git a/metagpt/ext/sela/experimenter/random_search.py b/metagpt/ext/sela/runner/random_search.py similarity index 92% rename from metagpt/ext/sela/experimenter/random_search.py rename to metagpt/ext/sela/runner/random_search.py index 5617ee601..8ce42f0ff 100644 --- a/metagpt/ext/sela/experimenter/random_search.py +++ b/metagpt/ext/sela/runner/random_search.py @@ -1,6 +1,6 @@ -from metagpt.ext.sela.experimenter.experimenter import Experimenter +from metagpt.ext.sela.experimenter import ResearchAssistant from metagpt.ext.sela.insights.instruction_generator import InstructionGenerator -from metagpt.ext.sela.research_assistant import ResearchAssistant +from metagpt.ext.sela.runner.runner import Runner from metagpt.ext.sela.utils import get_exp_pool_path EXPS_PROMPT = """ @@ -10,7 +10,7 @@ """ -class RandomSearchExperimenter(Experimenter): +class RandomSearchRunner(Runner): result_path: str = "results/random_search" async def run_experiment(self): diff --git a/metagpt/ext/sela/experimenter/experimenter.py b/metagpt/ext/sela/runner/runner.py similarity index 98% rename from metagpt/ext/sela/experimenter/experimenter.py rename to metagpt/ext/sela/runner/runner.py index 3df46b74b..7ab83c6c3 100644 --- a/metagpt/ext/sela/experimenter/experimenter.py +++ b/metagpt/ext/sela/runner/runner.py @@ -6,12 +6,12 @@ import pandas as pd from metagpt.ext.sela.evaluation.evaluation import evaluate_score -from metagpt.ext.sela.research_assistant import ResearchAssistant +from metagpt.ext.sela.experimenter import ResearchAssistant from metagpt.ext.sela.search.tree_search import create_initial_state from metagpt.ext.sela.utils import DATA_CONFIG, save_notebook -class Experimenter: +class Runner: result_path: str = "results/base" data_config = DATA_CONFIG start_task_id = 1 diff --git a/metagpt/ext/sela/search/tree_search.py b/metagpt/ext/sela/search/tree_search.py index cde8dc82a..684426fe6 100644 --- a/metagpt/ext/sela/search/tree_search.py +++ b/metagpt/ext/sela/search/tree_search.py @@ -15,8 +15,8 @@ get_split_dataset_path, ) from metagpt.ext.sela.evaluation.evaluation import evaluate_score +from metagpt.ext.sela.experimenter import ResearchAssistant, TimeoutException from metagpt.ext.sela.insights.instruction_generator import InstructionGenerator -from metagpt.ext.sela.research_assistant import ResearchAssistant, TimeoutException from metagpt.ext.sela.utils import get_exp_pool_path, load_execute_notebook, mcts_logger from metagpt.tools.tool_recommend import ToolRecommender from metagpt.utils.common import read_json_file From 25299e1f127b9fa3baa04ac057122375a5bde6ee Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Tue, 29 Oct 2024 14:24:38 +0800 Subject: [PATCH 153/160] change research assistant to experimenter --- metagpt/ext/sela/data.yaml | 2 +- metagpt/ext/sela/experimenter.py | 4 ++-- metagpt/ext/sela/runner/random_search.py | 6 ++---- metagpt/ext/sela/runner/runner.py | 6 ++---- metagpt/ext/sela/search/tree_search.py | 12 ++++++------ 5 files changed, 13 insertions(+), 17 deletions(-) diff --git a/metagpt/ext/sela/data.yaml b/metagpt/ext/sela/data.yaml index 5f4a290ea..7da5dbb3c 100644 --- a/metagpt/ext/sela/data.yaml +++ b/metagpt/ext/sela/data.yaml @@ -1,3 +1,3 @@ datasets_dir: "path/to/datasets" # path to the datasets directory -work_dir: ../../workspace # path to the workspace directory +work_dir: ../../../workspace # path to the workspace directory role_dir: storage/SELA # path to the role directory \ No newline at end of file diff --git a/metagpt/ext/sela/experimenter.py b/metagpt/ext/sela/experimenter.py index 2c698c1d2..b05ea2fc3 100644 --- a/metagpt/ext/sela/experimenter.py +++ b/metagpt/ext/sela/experimenter.py @@ -60,7 +60,7 @@ async def wrapper(self, *args, **kwargs): return decorator -class ResearchAssistant(DataInterpreter): +class Experimenter(DataInterpreter): node_id: str = "0" start_task_id: int = 1 state_saved: bool = False @@ -78,7 +78,7 @@ def change_next_instruction(self, new_instruction): self.planner.plan.task_map[str(self.start_task_id)].instruction = new_instruction self.remap_tasks() - def update_til_start_task(self, role: ResearchAssistant, backward: bool = True): + def update_til_start_task(self, role: Experimenter, backward: bool = True): if backward: # make sure the previous task instructions are matched assert ( diff --git a/metagpt/ext/sela/runner/random_search.py b/metagpt/ext/sela/runner/random_search.py index 8ce42f0ff..b1f43ac0c 100644 --- a/metagpt/ext/sela/runner/random_search.py +++ b/metagpt/ext/sela/runner/random_search.py @@ -1,4 +1,4 @@ -from metagpt.ext.sela.experimenter import ResearchAssistant +from metagpt.ext.sela.experimenter import Experimenter from metagpt.ext.sela.insights.instruction_generator import InstructionGenerator from metagpt.ext.sela.runner.runner import Runner from metagpt.ext.sela.utils import get_exp_pool_path @@ -34,9 +34,7 @@ async def run_experiment(self): results = [] for i in range(self.args.num_experiments): - di = ResearchAssistant( - node_id=str(i), use_reflection=self.args.reflection, role_timeout=self.args.role_timeout - ) + di = Experimenter(node_id=str(i), use_reflection=self.args.reflection, role_timeout=self.args.role_timeout) di.role_dir = f"{di.role_dir}_{self.args.task}" requirement = user_requirement + EXPS_PROMPT.format(experience=exps[i]) print(requirement) diff --git a/metagpt/ext/sela/runner/runner.py b/metagpt/ext/sela/runner/runner.py index 7ab83c6c3..4b5504e09 100644 --- a/metagpt/ext/sela/runner/runner.py +++ b/metagpt/ext/sela/runner/runner.py @@ -6,7 +6,7 @@ import pandas as pd from metagpt.ext.sela.evaluation.evaluation import evaluate_score -from metagpt.ext.sela.experimenter import ResearchAssistant +from metagpt.ext.sela.experimenter import Experimenter from metagpt.ext.sela.search.tree_search import create_initial_state from metagpt.ext.sela.utils import DATA_CONFIG, save_notebook @@ -83,9 +83,7 @@ async def run_experiment(self): results = [] for i in range(self.args.num_experiments): - di = ResearchAssistant( - node_id="0", use_reflection=self.args.reflection, role_timeout=self.args.role_timeout - ) + di = Experimenter(node_id="0", use_reflection=self.args.reflection, role_timeout=self.args.role_timeout) score_dict = await self.run_di(di, user_requirement, run_idx=i) results.append( {"idx": i, "score_dict": score_dict, "user_requirement": user_requirement, "args": vars(self.args)} diff --git a/metagpt/ext/sela/search/tree_search.py b/metagpt/ext/sela/search/tree_search.py index 684426fe6..eac26c86c 100644 --- a/metagpt/ext/sela/search/tree_search.py +++ b/metagpt/ext/sela/search/tree_search.py @@ -15,7 +15,7 @@ get_split_dataset_path, ) from metagpt.ext.sela.evaluation.evaluation import evaluate_score -from metagpt.ext.sela.experimenter import ResearchAssistant, TimeoutException +from metagpt.ext.sela.experimenter import Experimenter, TimeoutException from metagpt.ext.sela.insights.instruction_generator import InstructionGenerator from metagpt.ext.sela.utils import get_exp_pool_path, load_execute_notebook, mcts_logger from metagpt.tools.tool_recommend import ToolRecommender @@ -44,9 +44,9 @@ def initialize_di_root_node(state: dict, reflection: bool = True): reflection (bool, optional): Whether to use reflection. Defaults to True. Returns: - tuple: A tuple containing the ResearchAssistant role and the root Node. + tuple: A tuple containing the Experimenter role and the root Node. """ - role = ResearchAssistant( + role = Experimenter( node_id="0", start_task_id=state["start_task_id"], use_reflection=reflection, @@ -204,14 +204,14 @@ def load_role(self): role_dict["tool_recommender"] = ToolRecommender() elif isinstance(role_dict.get("tool_recommender", {}).get("tools"), dict): role_dict["tool_recommender"]["tools"] = list(role_dict["tool_recommender"]["tools"].keys()) - role = ResearchAssistant(**role_dict) + role = Experimenter(**role_dict) if self.parent is not None: # TODO: Check this parent_role = self.parent.load_role() role.update_til_start_task(parent_role, backward=False) role.remap_tasks() return role - def save_new_role(self, role: ResearchAssistant): + def save_new_role(self, role: Experimenter): role.node_id = self.id role.start_task_id = self.state["start_task_id"] role.state_saved = False @@ -268,7 +268,7 @@ def evaluate_simulation(self, score_dict): self.get_and_move_predictions("test") return score_dict - async def run_node(self, role: ResearchAssistant = None): + async def run_node(self, role: Experimenter = None): if self.is_terminal() and role is not None: if role.state_saved: return self.raw_reward From 37698b3f636d7d2ffdd8f8f754084b99b81b2158 Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Tue, 29 Oct 2024 14:55:39 +0800 Subject: [PATCH 154/160] update readme - put baseline readme in /runner --- metagpt/ext/sela/README.md | 279 +++++------------------------- metagpt/ext/sela/runner/README.md | 198 +++++++++++++++++++++ 2 files changed, 246 insertions(+), 231 deletions(-) create mode 100644 metagpt/ext/sela/runner/README.md diff --git a/metagpt/ext/sela/README.md b/metagpt/ext/sela/README.md index 829306e36..a942fdb7d 100644 --- a/metagpt/ext/sela/README.md +++ b/metagpt/ext/sela/README.md @@ -1,29 +1,26 @@ # SELA: Tree-Search Enhanced LLM Agents for Automated Machine Learning - - ## 1. Data Preparation -- Download Datasets:https://deepwisdom.feishu.cn/drive/folder/RVyofv9cvlvtxKdddt2cyn3BnTc?from=from_copylink -- Download and prepare datasets from scratch: -``` -cd data -python dataset.py --save_analysis_pool -python hf_data.py --save_analysis_pool -``` +You can either download the datasets from the link or prepare the datasets from scratch. +- **Download Datasets:** [Dataset Link](https://deepwisdom.feishu.cn/drive/folder/RVyofv9cvlvtxKdddt2cyn3BnTc?from=from_copylink) +- **Download and prepare datasets from scratch:** + ```bash + cd data + python dataset.py --save_analysis_pool + python hf_data.py --save_analysis_pool + ``` -## 2. Configs +## 2. Configurations ### Data Config -`datasets.yaml` Provide base prompts, metrics, target columns for respective datasets - -- Modify `datasets_dir` to the root directory of all the datasets in `data.yaml` - +- **`datasets.yaml`:** Provide base prompts, metrics, and target columns for respective datasets. +- **`data.yaml`:** Modify `datasets_dir` to the base directory of all prepared datasets. ### LLM Config -``` +```yaml llm: api_type: 'openai' model: deepseek-coder @@ -32,237 +29,57 @@ llm: temperature: 0.5 ``` -### Budget -Experiment rollouts k = 5, 10, 20 - - -### Prompt Usage - -- Use the function `generate_task_requirement` in `dataset.py` to get task requirement. - - If the method is non-DI-based, set `is_di=False`. - - Use `utils.DATA_CONFIG` as `data_config` - ## 3. SELA ### Run SELA #### Setup -In the root directory, -``` +```bash pip install -e . -cd expo +cd metagpt/ext/sela pip install -r requirements.txt ``` -#### Run - -- Examples - ``` - python run_experiment.py --exp_mode mcts --task titanic --rollouts 10 - python run_experiment.py --exp_mode mcts --task house-prices --rollouts 10 --low_is_better - ``` - - -- `--rollouts` - The number of rollouts - -- `--use_fixed_insights` - In addition to the generated insights, include the fixed insights saved in `expo/insights/fixed_insights.json` - -- `--low_is_better` - If the dataset has reg metric, remember to use `--low_is_better` - -- `--from_scratch` - Do not use pre-processed insight pool, generate new insight pool based on dataset before running MCTS, facilitating subsequent tuning to propose search space prompts - -- `--role_timeout` - The timeout for the role - - This feature limits the duration of a single simulation, making the experiment duration more controllable (for example, if you do ten rollouts and set role_timeout to 1,000, the experiment will stop at the latest after 10,000s) +#### Running Experiments - -- `--max_depth` - The maximum depth of MCTS, default is 4 (nodes at this depth directly return the previous simulation result without further expansion) - -- `--load_tree` - If MCTS was interrupted due to certain reasons but had already run multiple rollouts, you can use `--load_tree`. - - For example: - ``` +- **Examples:** + ```bash python run_experiment.py --exp_mode mcts --task titanic --rollouts 10 + python run_experiment.py --exp_mode mcts --task house-prices --rollouts 10 --low_is_better ``` - - If this was interrupted after running three rollouts, you can use `--load_tree`: - ``` - python run_experiment.py --exp_mode mcts --task titanic --rollouts 7 --load_tree - ``` - - -#### Ablation Study - -**DI RandomSearch** - -- Single insight -`python run_experiment.py --exp_mode rs --task titanic --rs_mode single` - -- Set insight -`python run_experiment.py --exp_mode rs --task titanic --rs_mode set` - - -## 4. Evaluation - -Each baseline needs to produce `dev_predictions.csv`和`test_predictions.csv`. Each csv file only needs a `target` column. - -- Use the function `evaluate_score` to evaluate. - -#### MLE-Bench -**Note: mle-bench requires python 3.11 or higher** -``` -git clone https://github.com/openai/mle-bench.git -cd mle-bench -pip install -e . -``` - -``` -mlebench prepare -c --data-dir -``` - -Enter the following command to run the experiment: -``` -python run_experiment.py --exp_mode mcts --custom_dataset_dir --rollouts 10 --from_scratch --role_timeout 3600 -``` - - -## 5. Baselines - -### AIDE - -#### Setup -The version of AIDE we use is dated September 30, 2024 -``` -git clone https://github.com/WecoAI/aideml.git -git checkout 77953247ea0a5dc1bd502dd10939dd6d7fdcc5cc -``` -Modify `aideml/aide/utils/config.yaml` - change `k_fold_validation`, `code model`, and `feedback model` as follows: - -```yaml -# agent hyperparams -agent: - # how many improvement iterations to run - steps: 10 - # whether to instruct the agent to use CV (set to 1 to disable) - k_fold_validation: 1 - # LLM settings for coding - code: - model: deepseek-coder - temp: 0.5 - - # LLM settings for evaluating program output / tracebacks - feedback: - model: deepseek-coder - temp: 0.5 - - # hyperparameters for the tree search - search: - max_debug_depth: 3 - debug_prob: 0.5 - num_drafts: 5 -``` - -Since Deepseek is compatible to OpenAI's API, change `base_url` into `your own url`,`api_key` into `your api key` - -``` -export OPENAI_API_KEY="your api key" -export OPENAI_BASE_URL="your own url" -``` - -Modify `aideml/aide/backend/__init__.py`'s line 30 and below: - -```python -model_kwargs = model_kwargs | { - "model": model, - "temperature": temperature, - "max_tokens": max_tokens, - } -if "claude-" in model: - query_func = backend_anthropic.query -else: - query_func = backend_openai.query -``` - -Since deepseekV2.5 no longer supports system message using function call, modify `aideml/aide/agent.py`'s line 312: - -```python -response = cast( - dict, - query( - system_message=None, - user_message=prompt, - func_spec=review_func_spec, - model=self.acfg.feedback.model, - temperature=self.acfg.feedback.temp, - ), -) -``` - -Modify and install: - -``` -cd aideml -pip install -e . -``` - -#### Run - -Run the following script to get the running results, a `log` folder and a `workspace` folder will be generated in the current directory -The `log` folder will contain the experimental configuration and the generated scheme, and the `workspace` folder will save the final results generated by aide - -``` -python runner/aide.py -``` - -### Autogluon -#### Setup -``` -pip install -U pip -pip install -U setuptools wheel -pip install autogluon==1.1.1 -``` - -For Tabular data: -``` -python run_expriment.py --exp_mode autogluon --task {task_name} -``` -For Multimodal data: -``` -python run_expriment.py --exp_mode autogluon --task {task_name} --is_multimodal -``` -Replace {task_name} with the specific task you want to run. - - -### AutoSklearn -#### System requirements -auto-sklearn has the following system requirements: - -- Linux operating system (for example Ubuntu) - -- Python (>=3.7) - -- C++ compiler (with C++11 supports) - -In case you try to install Auto-sklearn on a system where no wheel files for the pyrfr package are provided (see here for available wheels) you also need: - -- SWIG [(get SWIG here).](https://www.swig.org/survey.html) - -For an explanation of missing Microsoft Windows and macOS support please check the Section [Windows/macOS compatibility](https://automl.github.io/auto-sklearn/master/installation.html#windows-macos-compatibility). - -#### Setup -``` -pip install auto-sklearn==0.15.0 -``` - -#### Run -``` -python run_experiment.py --exp_mode autosklearn --task titanic -``` +#### Parameters + +- **`--rollouts`:** The number of rollouts. +- **`--use_fixed_insights`:** Include fixed insights saved in `expo/insights/fixed_insights.json`. +- **`--low_is_better`:** Use this if the dataset has a regression metric. +- **`--from_scratch`:** Generate a new insight pool based on the dataset before running MCTS. +- **`--role_timeout`:** Limits the duration of a single simulation (e.g., `10 rollouts with timeout 1,000` = max 10,000s). +- **`--max_depth`:** Set the maximum depth of MCTS (default is 4). +- **`--load_tree`:** Load an existing MCTS tree if the previous experiment was interrupted. + - Example: + ```bash + python run_experiment.py --exp_mode mcts --task titanic --rollouts 10 + ``` + - To resume: + ```bash + python run_experiment.py --exp_mode mcts --task titanic --rollouts 7 --load_tree + ``` + +### Ablation Study + +**RandomSearch** + +- **Use a single insight:** + ```bash + python run_experiment.py --exp_mode rs --task titanic --rs_mode single + ``` -### Base DI -For setup, check 4. -- `python run_experiment.py --exp_mode base --task titanic --num_experiments 10` -- Specifically instruct DI to use AutoGluon: `--special_instruction ag` -- Specifically instruct DI to use the stacking ensemble method: `--special_instruction stacking` \ No newline at end of file +- **Use a set of insights:** + ```bash + python run_experiment.py --exp_mode rs --task titanic --rs_mode set + ``` \ No newline at end of file diff --git a/metagpt/ext/sela/runner/README.md b/metagpt/ext/sela/runner/README.md new file mode 100644 index 000000000..7c031f1ee --- /dev/null +++ b/metagpt/ext/sela/runner/README.md @@ -0,0 +1,198 @@ +# SELA: Tree-Search Enhanced LLM Agents for Automated Machine Learning + +This document provides instructions for running baseline models. To start with, ensure that you prepare the datasets as instructed in `sela/README.md`. + +## Baselines + +### 1. AIDE + +#### Setup + +We use the AIDE version from September 30, 2024. Clone the repository and check out the specified commit: + +```bash +git clone https://github.com/WecoAI/aideml.git +git checkout 77953247ea0a5dc1bd502dd10939dd6d7fdcc5cc +``` + + +Modify `aideml/aide/utils/config.yaml` to set the following parameters: + +```yaml +# agent hyperparams +agent: + steps: 10 # Number of improvement iterations + k_fold_validation: 1 # Set to 1 to disable cross-validation + code: + model: deepseek-coder + temp: 0.5 + feedback: + model: deepseek-coder + temp: 0.5 + search: + max_debug_depth: 3 + debug_prob: 0.5 + num_drafts: 5 +``` + +Update your OpenAI API credentials in the environment: + +```bash +export OPENAI_API_KEY="your api key" +export OPENAI_BASE_URL="your own url" +``` + +Modify `aideml/aide/backend/__init__.py` (line 30 and below): + +```python +model_kwargs = model_kwargs | { + "model": model, + "temperature": temperature, + "max_tokens": max_tokens, + } +if "claude-" in model: + query_func = backend_anthropic.query +else: + query_func = backend_openai.query +``` + +Since Deepseek V2.5 no longer supports system messages using function calls, modify `aideml/aide/agent.py` (line 312): + +```python +response = cast( + dict, + query( + system_message=None, + user_message=prompt, + func_spec=review_func_spec, + model=self.acfg.feedback.model, + temperature=self.acfg.feedback.temp, + ), +) +``` + +Finally, install AIDE: + +```bash +cd aideml +pip install -e . +``` + +#### Run + +Execute the following script to generate results. A `log` folder (containing experimental configurations) and a `workspace` folder (storing final results) will be created: + +```bash +python runner/aide.py +``` + +--- + +### 2. Autogluon + +#### Setup + +Install Autogluon: + +```bash +pip install -U pip +pip install -U setuptools wheel +pip install autogluon==1.1.1 +``` + +#### Run + +For Tabular data: + +```bash +python run_experiment.py --exp_mode autogluon --task {task_name} +``` + +For Multimodal data: + +```bash +python run_experiment.py --exp_mode autogluon --task {task_name} --is_multimodal +``` + +Replace `{task_name}` with the specific task you want to run. + +--- + +### 3. AutoSklearn + +**Note:** +AutoSklearn requires: +- Linux operating system (e.g., Ubuntu) +- Python (>=3.7) +- C++ compiler (with C++11 support) + +If installing on a system without wheel files for the `pyrfr` package, you also need: + +- [SWIG](https://www.swig.org/survey.html) + +Refer to the [Windows/macOS compatibility](https://automl.github.io/auto-sklearn/master/installation.html#windows-macos-compatibility) section for further details. + +#### Setup + +Install AutoSklearn: + +```bash +pip install auto-sklearn==0.15.0 +``` + +#### Run + +Execute the following command for the Titanic task: + +```bash +python run_experiment.py --exp_mode autosklearn --task titanic +``` + +--- + +### 4. Base Data Interpreter + +Run the following command for the Titanic task: + +```bash +python run_experiment.py --exp_mode base --task titanic --num_experiments 10 +``` + +--- + +### 5. Custom Baselines + +To run additional baselines: + +- Each baseline must produce `dev_predictions.csv` and `test_predictions.csv` with a `target` column. +- Use the `evaluate_score` function for evaluation. + +--- + +## MLE-Bench + +**Note:** MLE-Bench requires Python 3.11 or higher. + +#### Setup + +Clone the repository and install: + +```bash +git clone https://github.com/openai/mle-bench.git +cd mle-bench +pip install -e . +``` + +Prepare the data: + +```bash +mlebench prepare -c --data-dir +``` + +#### Run the MLE-Bench Experiment + +Run the following command to execute the experiment: + +```bash +python run_experiment.py --exp_mode mcts --custom_dataset_dir --rollouts 10 --from_scratch --role_timeout 3600 +``` \ No newline at end of file From 74041bd5cb3d8a0b9e0e2f8c45b421510c5558fa Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Wed, 30 Oct 2024 15:56:06 +0800 Subject: [PATCH 155/160] update readme intro --- metagpt/ext/sela/README.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/metagpt/ext/sela/README.md b/metagpt/ext/sela/README.md index a942fdb7d..d0fbcf4b8 100644 --- a/metagpt/ext/sela/README.md +++ b/metagpt/ext/sela/README.md @@ -1,5 +1,7 @@ # SELA: Tree-Search Enhanced LLM Agents for Automated Machine Learning +SELA is an innovative framework that enhances Automated Machine Learning (AutoML) by integrating Monte Carlo Tree Search (MCTS) with LLM-based agents. Traditional AutoML methods often generate low-diversity and suboptimal code, limiting their effectiveness in model selection and ensembling. SELA addresses these challenges by representing pipeline configurations as trees, enabling agents to intelligently explore the solution space and iteratively refine their strategies based on experimental feedback. + ## 1. Data Preparation You can either download the datasets from the link or prepare the datasets from scratch. @@ -82,4 +84,18 @@ pip install -r requirements.txt - **Use a set of insights:** ```bash python run_experiment.py --exp_mode rs --task titanic --rs_mode set - ``` \ No newline at end of file + ``` + +## 4. Citation + +```bibtex +@misc{chi2024selatreesearchenhancedllm, + title={SELA: Tree-Search Enhanced LLM Agents for Automated Machine Learning}, + author={Yizhou Chi and Yizhang Lin and Sirui Hong and Duyi Pan and Yaying Fei and Guanghao Mei and Bangbang Liu and Tianqi Pang and Jacky Kwok and Ceyao Zhang and Bang Liu and Chenglin Wu}, + year={2024}, + eprint={2410.17238}, + archivePrefix={arXiv}, + primaryClass={cs.AI}, + url={https://arxiv.org/abs/2410.17238}, +} +``` From 73d2358d108f6ebdcf25afadc4f86a43d075a51d Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Wed, 30 Oct 2024 16:11:42 +0800 Subject: [PATCH 156/160] update intro and citation --- metagpt/ext/sela/README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/metagpt/ext/sela/README.md b/metagpt/ext/sela/README.md index d0fbcf4b8..ceb832a98 100644 --- a/metagpt/ext/sela/README.md +++ b/metagpt/ext/sela/README.md @@ -1,6 +1,10 @@ # SELA: Tree-Search Enhanced LLM Agents for Automated Machine Learning -SELA is an innovative framework that enhances Automated Machine Learning (AutoML) by integrating Monte Carlo Tree Search (MCTS) with LLM-based agents. Traditional AutoML methods often generate low-diversity and suboptimal code, limiting their effectiveness in model selection and ensembling. SELA addresses these challenges by representing pipeline configurations as trees, enabling agents to intelligently explore the solution space and iteratively refine their strategies based on experimental feedback. + +Official implementation for paper [SELA: Tree-Search Enhanced LLM Agents for Automated Machine Learning](https://arxiv.org/abs/2410.17238). + + +SELA is an innovative system that enhances Automated Machine Learning (AutoML) by integrating Monte Carlo Tree Search (MCTS) with LLM-based agents. Traditional AutoML methods often generate low-diversity and suboptimal code, limiting their effectiveness in model selection and ensembling. SELA addresses these challenges by representing pipeline configurations as trees, enabling agents to intelligently explore the solution space and iteratively refine their strategies based on experimental feedback. ## 1. Data Preparation @@ -87,6 +91,7 @@ pip install -r requirements.txt ``` ## 4. Citation +Please cite our paper if you use SELA or find it useful! ```bibtex @misc{chi2024selatreesearchenhancedllm, From 0a215a960bd436da478f4ac93d68242390b63b6d Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Wed, 30 Oct 2024 16:31:13 +0800 Subject: [PATCH 157/160] update readme --- metagpt/ext/sela/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/ext/sela/README.md b/metagpt/ext/sela/README.md index ceb832a98..2a4054c64 100644 --- a/metagpt/ext/sela/README.md +++ b/metagpt/ext/sela/README.md @@ -91,7 +91,7 @@ pip install -r requirements.txt ``` ## 4. Citation -Please cite our paper if you use SELA or find it useful! +Please cite our paper if you use SELA or find it cool or useful! ```bibtex @misc{chi2024selatreesearchenhancedllm, From 261a39d5479e117e77e034055dd8bcf5b2d237f8 Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Wed, 30 Oct 2024 23:01:38 +0800 Subject: [PATCH 158/160] remove autogluon models in gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 46c9b0dd4..0d6be14ad 100644 --- a/.gitignore +++ b/.gitignore @@ -29,7 +29,6 @@ share/python-wheels/ MANIFEST metagpt/tools/schemas/ examples/data/search_kb/*.json -metagpt/ext/sela/AutogluonModels # PyInstaller # Usually these files are written by a python scripts from a template From 776ee4455cc59e49b46d0cda1c965c4b9a789328 Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Thu, 31 Oct 2024 00:05:44 +0800 Subject: [PATCH 159/160] remove mlebench --- metagpt/ext/sela/runner/README.md | 32 +------------------------------ 1 file changed, 1 insertion(+), 31 deletions(-) diff --git a/metagpt/ext/sela/runner/README.md b/metagpt/ext/sela/runner/README.md index 7c031f1ee..4867aa4f0 100644 --- a/metagpt/ext/sela/runner/README.md +++ b/metagpt/ext/sela/runner/README.md @@ -165,34 +165,4 @@ python run_experiment.py --exp_mode base --task titanic --num_experiments 10 To run additional baselines: - Each baseline must produce `dev_predictions.csv` and `test_predictions.csv` with a `target` column. -- Use the `evaluate_score` function for evaluation. - ---- - -## MLE-Bench - -**Note:** MLE-Bench requires Python 3.11 or higher. - -#### Setup - -Clone the repository and install: - -```bash -git clone https://github.com/openai/mle-bench.git -cd mle-bench -pip install -e . -``` - -Prepare the data: - -```bash -mlebench prepare -c --data-dir -``` - -#### Run the MLE-Bench Experiment - -Run the following command to execute the experiment: - -```bash -python run_experiment.py --exp_mode mcts --custom_dataset_dir --rollouts 10 --from_scratch --role_timeout 3600 -``` \ No newline at end of file +- Use the `evaluate_score` function for evaluation. \ No newline at end of file From b710365fabc4a150b2a6cb90ca0ec44d01f1e38b Mon Sep 17 00:00:00 2001 From: Cyzus Chi Date: Thu, 31 Oct 2024 00:06:37 +0800 Subject: [PATCH 160/160] use google drive link --- metagpt/ext/sela/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/metagpt/ext/sela/README.md b/metagpt/ext/sela/README.md index 2a4054c64..6fb47b42c 100644 --- a/metagpt/ext/sela/README.md +++ b/metagpt/ext/sela/README.md @@ -9,7 +9,7 @@ SELA is an innovative system that enhances Automated Machine Learning (AutoML) b ## 1. Data Preparation You can either download the datasets from the link or prepare the datasets from scratch. -- **Download Datasets:** [Dataset Link](https://deepwisdom.feishu.cn/drive/folder/RVyofv9cvlvtxKdddt2cyn3BnTc?from=from_copylink) +- **Download Datasets:** [Dataset Link](https://drive.google.com/drive/folders/151FIZoLygkRfeJgSI9fNMiLsixh1mK0r?usp=sharing) - **Download and prepare datasets from scratch:** ```bash cd data
  • W-?1%Gx*uueG5Yim&Pyj@%D?i#U;^A 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�l4Baf`6WtB#W0XuT3zmv;#ME%49#oeq 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 FvU%>lLEa^u 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