chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
from typing import Literal
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.CoSTEER.config import CoSTEERSettings
|
||||
from rdagent.utils.env import (
|
||||
CondaConf,
|
||||
DockerEnv,
|
||||
DSDockerConf,
|
||||
Env,
|
||||
LocalEnv,
|
||||
MLEBDockerConf,
|
||||
MLECondaConf,
|
||||
)
|
||||
|
||||
|
||||
class DSCoderCoSTEERSettings(CoSTEERSettings):
|
||||
"""Data Science CoSTEER settings"""
|
||||
|
||||
class Config:
|
||||
env_prefix = "DS_Coder_CoSTEER_"
|
||||
|
||||
max_seconds_multiplier: int = 4
|
||||
env_type: str = "docker"
|
||||
# TODO: extract a function for env and conf.
|
||||
extra_evaluator: list[str] = []
|
||||
"""Extra evaluators to use"""
|
||||
|
||||
extra_eval: list[str] = []
|
||||
"""
|
||||
Extra evaluators
|
||||
|
||||
The evaluator follows the following assumptions:
|
||||
- It runs after previous evaluator (So the running results are already there)
|
||||
|
||||
It is not a complete feature due to it is only implemented in DS Pipeline & Coder.
|
||||
|
||||
TODO: The complete version should be implemented in the CoSTEERSettings.
|
||||
"""
|
||||
|
||||
|
||||
def get_ds_env(
|
||||
conf_type: Literal["kaggle", "mlebench"] = "kaggle",
|
||||
extra_volumes: dict = {},
|
||||
running_timeout_period: int | None = DS_RD_SETTING.debug_timeout,
|
||||
enable_cache: bool | None = None,
|
||||
) -> Env:
|
||||
"""
|
||||
Retrieve the appropriate environment configuration based on the env_type setting.
|
||||
|
||||
Returns:
|
||||
Env: An instance of the environment configured either as DockerEnv or LocalEnv.
|
||||
|
||||
Raises:
|
||||
ValueError: If the env_type is not recognized.
|
||||
"""
|
||||
conf = DSCoderCoSTEERSettings()
|
||||
assert conf_type in ["kaggle", "mlebench"], f"Unknown conf_type: {conf_type}"
|
||||
|
||||
if conf.env_type == "docker":
|
||||
env_conf = DSDockerConf() if conf_type == "kaggle" else MLEBDockerConf()
|
||||
env = DockerEnv(conf=env_conf)
|
||||
elif conf.env_type == "conda":
|
||||
env = LocalEnv(
|
||||
conf=(
|
||||
CondaConf(conda_env_name=conf_type) if conf_type == "kaggle" else MLECondaConf(conda_env_name=conf_type)
|
||||
)
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Unknown env type: {conf.env_type}")
|
||||
env.conf.extra_volumes = extra_volumes.copy()
|
||||
env.conf.running_timeout_period = running_timeout_period
|
||||
if enable_cache is not None:
|
||||
env.conf.enable_cache = enable_cache
|
||||
env.prepare()
|
||||
return env
|
||||
|
||||
|
||||
def get_clear_ws_cmd(stage: Literal["before_training", "before_inference"] = "before_training") -> str:
|
||||
"""
|
||||
Clean the files in workspace to a specific stage
|
||||
"""
|
||||
assert stage in ["before_training", "before_inference"], f"Unknown stage: {stage}"
|
||||
if DS_RD_SETTING.enable_model_dump and stage == "before_training":
|
||||
cmd = "rm -r submission.csv scores.csv models trace.log"
|
||||
else:
|
||||
cmd = "rm submission.csv scores.csv trace.log"
|
||||
return cmd
|
||||
@@ -0,0 +1,164 @@
|
||||
"""
|
||||
File structure
|
||||
- ___init__.py: the entrance/agent of coder
|
||||
- evaluator.py
|
||||
- conf.py
|
||||
- exp.py: everything under the experiment, e.g.
|
||||
- Task
|
||||
- Experiment
|
||||
- Workspace
|
||||
- test.py
|
||||
- Each coder could be tested.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from jinja2 import Environment, StrictUndefined
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEERMultiEvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.evolving_strategy import (
|
||||
MultiProcessEvolvingStrategy,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.knowledge_management import (
|
||||
CoSTEERQueriedKnowledge,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import DSCoderCoSTEERSettings
|
||||
from rdagent.components.coder.data_science.ensemble.eval import EnsembleCoSTEEREvaluator
|
||||
from rdagent.components.coder.data_science.ensemble.exp import EnsembleTask
|
||||
from rdagent.components.coder.data_science.share.ds_costeer import DSCoSTEER
|
||||
from rdagent.core.exception import CoderError
|
||||
from rdagent.core.experiment import FBWorkspace
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.ret import PythonAgentOut
|
||||
from rdagent.utils.agent.tpl import T
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
|
||||
class EnsembleMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
|
||||
def implement_one_task(
|
||||
self,
|
||||
target_task: EnsembleTask,
|
||||
queried_knowledge: CoSTEERQueriedKnowledge | None = None,
|
||||
workspace: FBWorkspace | None = None,
|
||||
prev_task_feedback: CoSTEERSingleFeedback | None = None,
|
||||
) -> dict[str, str]:
|
||||
# Get task information for knowledge querying
|
||||
ensemble_information_str = target_task.get_task_information()
|
||||
|
||||
# Query knowledge
|
||||
queried_similar_successful_knowledge = (
|
||||
queried_knowledge.task_to_similar_task_successful_knowledge[ensemble_information_str]
|
||||
if queried_knowledge is not None
|
||||
else []
|
||||
)
|
||||
queried_former_failed_knowledge = (
|
||||
queried_knowledge.task_to_former_failed_traces[ensemble_information_str]
|
||||
if queried_knowledge is not None
|
||||
else []
|
||||
)
|
||||
queried_former_failed_knowledge = (
|
||||
[
|
||||
knowledge
|
||||
for knowledge in queried_former_failed_knowledge[0]
|
||||
if knowledge.implementation.file_dict.get("ensemble.py") != workspace.file_dict.get("ensemble.py")
|
||||
],
|
||||
queried_former_failed_knowledge[1],
|
||||
)
|
||||
|
||||
# Generate code with knowledge integration
|
||||
competition_info = self.scen.get_scenario_all_desc(eda_output=workspace.file_dict.get("EDA.md", None))
|
||||
system_prompt = T(".prompts:ensemble_coder.system").r(
|
||||
task_desc=ensemble_information_str,
|
||||
competition_info=competition_info,
|
||||
queried_similar_successful_knowledge=queried_similar_successful_knowledge,
|
||||
queried_former_failed_knowledge=(
|
||||
queried_former_failed_knowledge[0] if queried_former_failed_knowledge else None
|
||||
),
|
||||
all_code=workspace.all_codes,
|
||||
out_spec=PythonAgentOut.get_spec(),
|
||||
)
|
||||
|
||||
if DS_RD_SETTING.spec_enabled:
|
||||
code_spec = workspace.file_dict["spec/ensemble.md"]
|
||||
else:
|
||||
test_code = (
|
||||
Environment(undefined=StrictUndefined)
|
||||
.from_string((DIRNAME / "eval_tests" / "ensemble_test.txt").read_text())
|
||||
.render(
|
||||
model_names=[
|
||||
fn[:-3] for fn in workspace.file_dict.keys() if fn.startswith("model_") and "test" not in fn
|
||||
],
|
||||
metric_name=self.scen.metric_name,
|
||||
)
|
||||
)
|
||||
code_spec = T("scenarios.data_science.share:component_spec.general").r(
|
||||
spec=T("scenarios.data_science.share:component_spec.Ensemble").r(), test_code=test_code
|
||||
)
|
||||
user_prompt = T(".prompts:ensemble_coder.user").r(
|
||||
code_spec=code_spec,
|
||||
latest_code=workspace.file_dict.get("ensemble.py"),
|
||||
latest_code_feedback=prev_task_feedback,
|
||||
)
|
||||
|
||||
for _ in range(5):
|
||||
ensemble_code = PythonAgentOut.extract_output(
|
||||
APIBackend().build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
)
|
||||
)
|
||||
if ensemble_code != workspace.file_dict.get("ensemble.py"):
|
||||
break
|
||||
else:
|
||||
user_prompt = user_prompt + "\nPlease avoid generating same code to former code!"
|
||||
else:
|
||||
raise CoderError("Failed to generate a new ensemble code.")
|
||||
|
||||
return {
|
||||
"ensemble.py": ensemble_code,
|
||||
}
|
||||
|
||||
def assign_code_list_to_evo(self, code_list: list[dict[str, str]], evo):
|
||||
"""
|
||||
Assign the code list to the evolving item.
|
||||
|
||||
The code list is aligned with the evolving item's sub-tasks.
|
||||
If a task is not implemented, put a None in the list.
|
||||
"""
|
||||
for index in range(len(evo.sub_tasks)):
|
||||
if code_list[index] is None:
|
||||
continue
|
||||
if evo.sub_workspace_list[index] is None:
|
||||
# evo.sub_workspace_list[index] = FBWorkspace(target_task=evo.sub_tasks[index])
|
||||
evo.sub_workspace_list[index] = evo.experiment_workspace
|
||||
evo.sub_workspace_list[index].inject_files(**code_list[index])
|
||||
return evo
|
||||
|
||||
|
||||
class EnsembleCoSTEER(DSCoSTEER):
|
||||
def __init__(
|
||||
self,
|
||||
scen: Scenario,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
settings = DSCoderCoSTEERSettings()
|
||||
eva = CoSTEERMultiEvaluator(EnsembleCoSTEEREvaluator(scen=scen), scen=scen)
|
||||
es = EnsembleMultiProcessEvolvingStrategy(scen=scen, settings=settings)
|
||||
|
||||
super().__init__(
|
||||
*args,
|
||||
settings=settings,
|
||||
eva=eva,
|
||||
es=es,
|
||||
evolving_version=2,
|
||||
scen=scen,
|
||||
max_loop=DS_RD_SETTING.coder_max_loop,
|
||||
**kwargs,
|
||||
)
|
||||
@@ -0,0 +1,2 @@
|
||||
# Configuration file for ensemble component
|
||||
# Currently empty as no specific configuration is needed
|
||||
@@ -0,0 +1,100 @@
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from jinja2 import Environment, StrictUndefined
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEEREvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import get_ds_env
|
||||
from rdagent.components.coder.data_science.utils import remove_eda_part
|
||||
from rdagent.core.evolving_framework import QueriedKnowledge
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
EnsembleEvalFeedback = CoSTEERSingleFeedback
|
||||
|
||||
|
||||
class EnsembleCoSTEEREvaluator(CoSTEEREvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
target_task: Task,
|
||||
implementation: FBWorkspace,
|
||||
gt_implementation: FBWorkspace,
|
||||
queried_knowledge: QueriedKnowledge = None,
|
||||
**kwargs,
|
||||
) -> EnsembleEvalFeedback:
|
||||
|
||||
target_task_information = target_task.get_task_information()
|
||||
metric_name = self.scen.metric_name
|
||||
|
||||
if (
|
||||
queried_knowledge is not None
|
||||
and target_task_information in queried_knowledge.success_task_to_knowledge_dict
|
||||
):
|
||||
return queried_knowledge.success_task_to_knowledge_dict[target_task_information].feedback
|
||||
elif queried_knowledge is not None and target_task_information in queried_knowledge.failed_task_info_set:
|
||||
return EnsembleEvalFeedback(
|
||||
execution="This task has failed too many times, skip implementation.",
|
||||
code="This task has failed too many times, skip implementation.",
|
||||
return_checking="This task has failed too many times, skip implementation.",
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
env = get_ds_env(
|
||||
extra_volumes={self.scen.debug_path: T("scenarios.data_science.share:scen.input_path").r()},
|
||||
running_timeout_period=self.scen.real_debug_timeout(),
|
||||
)
|
||||
|
||||
fname = "test/ensemble_test.txt"
|
||||
test_code = (DIRNAME / "eval_tests" / "ensemble_test.txt").read_text()
|
||||
test_code = (
|
||||
Environment(undefined=StrictUndefined)
|
||||
.from_string(test_code)
|
||||
.render(
|
||||
model_names=[
|
||||
fn[:-3] for fn in implementation.file_dict.keys() if fn.startswith("model_") and "test" not in fn
|
||||
],
|
||||
metric_name=metric_name,
|
||||
)
|
||||
)
|
||||
|
||||
implementation.inject_files(**{fname: test_code})
|
||||
result = implementation.run(env=env, entry=f"python {fname}")
|
||||
stdout = result.stdout
|
||||
ret_code = result.exit_code
|
||||
|
||||
stdout += f"\nNOTE: the above scripts run with return code {ret_code}"
|
||||
|
||||
if "main.py" in implementation.file_dict and ret_code == 0:
|
||||
workflow_stdout = implementation.execute(env=env, entry="python main.py")
|
||||
workflow_stdout = remove_eda_part(workflow_stdout)
|
||||
else:
|
||||
workflow_stdout = None
|
||||
|
||||
system_prompt = T(".prompts:ensemble_eval.system").r(
|
||||
task_desc=target_task_information,
|
||||
test_code=test_code,
|
||||
metric_name=metric_name,
|
||||
code=implementation.file_dict["ensemble.py"],
|
||||
workflow_stdout=workflow_stdout,
|
||||
workflow_code=implementation.all_codes,
|
||||
)
|
||||
user_prompt = T(".prompts:ensemble_eval.user").r(
|
||||
stdout=stdout,
|
||||
workflow_stdout=workflow_stdout,
|
||||
)
|
||||
efb = build_cls_from_json_with_retry(
|
||||
EnsembleEvalFeedback,
|
||||
system_prompt=system_prompt,
|
||||
user_prompt=user_prompt,
|
||||
init_kwargs_update_func=EnsembleEvalFeedback.val_and_update_init_dict,
|
||||
)
|
||||
efb.final_decision = efb.final_decision and ret_code == 0
|
||||
return efb
|
||||
@@ -0,0 +1,137 @@
|
||||
"""
|
||||
Tests for `ensemble_workflow` in ensemble.py
|
||||
|
||||
A qualified ensemble_workflow implementation should:
|
||||
- Return predictions
|
||||
- Have correct shapes for inputs and outputs
|
||||
- Use validation data appropriately
|
||||
- Generate a scores.csv file
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from pathlib import Path
|
||||
from sklearn.model_selection import train_test_split
|
||||
import torch
|
||||
import tensorflow as tf
|
||||
from load_data import load_data
|
||||
from feature import feat_eng
|
||||
from ensemble import ensemble_workflow
|
||||
|
||||
def print_preds_info(model_name, data_type, preds):
|
||||
if preds is None:
|
||||
print(f"Model {model_name} {data_type} predictions: None")
|
||||
else:
|
||||
print(f"Model {model_name} {data_type} predictions shape: {preds.shape}")
|
||||
|
||||
print("Showing a preview of the predictions (first few entries only):")
|
||||
if isinstance(preds, (pd.DataFrame, pd.Series)):
|
||||
print(preds.head())
|
||||
elif isinstance(preds, (np.ndarray, torch.Tensor, tf.Tensor)):
|
||||
print(preds[:2])
|
||||
elif isinstance(preds, list):
|
||||
print(pd.DataFrame(preds[:5]))
|
||||
else:
|
||||
print(f"Unknown prediction type: {type(preds)}")
|
||||
|
||||
def get_length(data):
|
||||
return data.shape[0] if hasattr(data, 'shape') else len(data)
|
||||
|
||||
X, y, test_X, test_ids = load_data()
|
||||
X, y, test_X = feat_eng(X, y, test_X)
|
||||
train_X, val_X, train_y, val_y = train_test_split(X, y, test_size=0.2, random_state=42)
|
||||
|
||||
# Print the types of train_y and val_y
|
||||
print(f"train_y type: {type(train_y)}, val_y type: {type(val_y)}")
|
||||
|
||||
test_preds_dict = {}
|
||||
val_preds_dict = {}
|
||||
{% for mn in model_names %}
|
||||
from {{mn}} import model_workflow as {{mn}}_workflow
|
||||
val_preds_dict["{{mn}}"], test_preds_dict["{{mn}}"], _ = {{mn}}_workflow(
|
||||
X=train_X,
|
||||
y=train_y,
|
||||
val_X=val_X,
|
||||
val_y=val_y,
|
||||
test_X=test_X
|
||||
)
|
||||
|
||||
print_preds_info("{{mn}}", "test", test_preds_dict["{{mn}}"])
|
||||
{% endfor %}
|
||||
|
||||
for key in val_preds_dict.keys():
|
||||
if val_preds_dict[key] is None:
|
||||
print(f"Model {key} validation predictions (val_preds_dict[key]) is None.")
|
||||
elif isinstance(val_preds_dict[key], list):
|
||||
print(f"Model {key} validation predictions (val_preds_dict[key]) (list type) length: {len(val_preds_dict[key])}")
|
||||
else:
|
||||
print(f"Model {key} validation predictions (val_preds_dict[key]) shape: {val_preds_dict[key].shape}")
|
||||
|
||||
if test_preds_dict[key] is None:
|
||||
print(f"Model {key} test predictions (test_preds_dict[key]) is None.")
|
||||
elif isinstance(test_preds_dict[key], list):
|
||||
print(f"Model {key} test predictions (test_preds_dict[key]) (list type) length: {len(test_preds_dict[key])}")
|
||||
else:
|
||||
print(f"Model {key} test predictions (test_preds_dict[key]) shape: {test_preds_dict[key].shape}")
|
||||
|
||||
print(f"val_y.shape: {val_y.shape}" if not isinstance(val_y, list) else f"val_y(list)'s length: {len(val_y)}")
|
||||
|
||||
import sys
|
||||
import reprlib
|
||||
def debug_info_print(func):
|
||||
aRepr = reprlib.Repr()
|
||||
aRepr.maxother=300
|
||||
def wrapper(*args, **kwargs):
|
||||
def local_trace(frame, event, arg):
|
||||
if event == "return" and frame.f_code == func.__code__:
|
||||
print("\n" + "="*20 + "Running ensemble code, local variable values:" + "="*20)
|
||||
for k, v in frame.f_locals.items():
|
||||
printed = aRepr.repr(v)
|
||||
print(f"{k}:\n {printed}")
|
||||
print("="*20 + "Local variable values end" + "="*20)
|
||||
return local_trace
|
||||
|
||||
sys.settrace(local_trace)
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
finally:
|
||||
sys.settrace(None)
|
||||
return wrapper
|
||||
|
||||
|
||||
# Run ensemble
|
||||
final_pred = debug_info_print(ensemble_workflow)(test_preds_dict, val_preds_dict, val_y)
|
||||
|
||||
print_preds_info("ensemble", "test", final_pred)
|
||||
|
||||
# Check type
|
||||
pred_type = type(next(iter(test_preds_dict.values())))
|
||||
assert isinstance(final_pred, pred_type), (
|
||||
f"Type mismatch: 'final_pred' is of type {type(final_pred)}, but expected {pred_type} "
|
||||
)
|
||||
|
||||
# Check shape
|
||||
if isinstance(final_pred, (list, np.ndarray, pd.DataFrame, torch.Tensor, tf.Tensor)):
|
||||
assert get_length(final_pred) == get_length(test_X), (
|
||||
f"Wrong output sample size: get_length(final_pred)={get_length(final_pred)} "
|
||||
f"vs. get_length(test_X)={get_length(test_X)}"
|
||||
)
|
||||
|
||||
# check scores.csv
|
||||
assert Path("scores.csv").exists(), "scores.csv is not generated"
|
||||
score_df = pd.read_csv("scores.csv", index_col=0)
|
||||
model_set_in_scores = set(score_df.index)
|
||||
|
||||
assert model_set_in_scores == set({{model_names}}).union({"ensemble"}), (
|
||||
f"The scores dataframe does not contain the correct model names as index.\ncorrect model names are: {{model_names}} + ['ensemble']\nscore_df is:\n{score_df}"
|
||||
)
|
||||
assert score_df.index.is_unique, "The scores dataframe has duplicate model names."
|
||||
assert score_df.columns.tolist() == ["{{metric_name}}"], f"The column names of the scores dataframe should be ['{{metric_name}}'], but is '{score_df.columns.tolist()}'"
|
||||
|
||||
# Check for NaN values in score_df
|
||||
assert not score_df.isnull().values.any(), (
|
||||
f"The scores dataframe contains NaN values at the following locations:\n{score_df[score_df.isnull().any(axis=1)]}"
|
||||
)
|
||||
|
||||
|
||||
print("Ensemble test end.")
|
||||
@@ -0,0 +1,13 @@
|
||||
import pickle
|
||||
import site
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
|
||||
from rdagent.components.coder.CoSTEER.task import CoSTEERTask
|
||||
from rdagent.core.utils import cache_with_pickle
|
||||
|
||||
|
||||
# Because we use isinstance to distinguish between different types of tasks, we need to use sub classes to represent different types of tasks
|
||||
class EnsembleTask(CoSTEERTask):
|
||||
pass
|
||||
@@ -0,0 +1,124 @@
|
||||
ensemble_coder:
|
||||
system: |-
|
||||
You are a world-class data scientist and machine learning engineer with deep expertise in statistics, mathematics, and computer science.
|
||||
Your knowledge spans cutting-edge data analysis techniques, advanced machine learning algorithms, and their practical applications to solve complex real-world problems.
|
||||
|
||||
## Task Description
|
||||
Currently, you are working on model ensemble implementation. Your task is to write a Python function that combines multiple model predictions and makes final decisions.
|
||||
|
||||
Your specific task as follows:
|
||||
{{ task_desc }}
|
||||
|
||||
## Competition Information for This Task
|
||||
{{ competition_info }}
|
||||
|
||||
{% if queried_similar_successful_knowledge|length != 0 or queried_former_failed_knowledge|length != 0 %}
|
||||
## Relevant Information for This Task
|
||||
{% endif %}
|
||||
|
||||
{% if queried_similar_successful_knowledge|length != 0 %}
|
||||
--------- Successful Implementations for Similar Models ---------
|
||||
====={% for similar_successful_knowledge in queried_similar_successful_knowledge %} Model {{ loop.index }}:=====
|
||||
{{ similar_successful_knowledge.target_task.get_task_information() }}
|
||||
=====Code:=====
|
||||
{{ similar_successful_knowledge.implementation.file_dict["ensemble.py"] }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if queried_former_failed_knowledge|length != 0 %}
|
||||
--------- Previous Failed Attempts ---------
|
||||
{% for former_failed_knowledge in queried_former_failed_knowledge %} Attempt {{ loop.index }}:
|
||||
=====Code:=====
|
||||
{{ former_failed_knowledge.implementation.file_dict["ensemble.py"] }}
|
||||
=====Feedback:=====
|
||||
{{ former_failed_knowledge.feedback }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
## Guidelines
|
||||
1. The function's code is associated with several other functions including a data loader, feature engineering, and model training. all codes are as follows:
|
||||
{{ all_code }}
|
||||
2. You should avoid using logging module to output information in your generated code, and instead use the print() function.
|
||||
{% include "scenarios.data_science.share:guidelines.coding" %}
|
||||
|
||||
## Output Format
|
||||
{% if out_spec %}
|
||||
{{ out_spec }}
|
||||
{% else %}
|
||||
Please response the code in the following json format. Here is an example structure for the JSON output:
|
||||
{
|
||||
"code": "The Python code as a string."
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
user: |-
|
||||
--------- Code Specification ---------
|
||||
{{ code_spec }}
|
||||
|
||||
{% if latest_code %}
|
||||
--------- Former code ---------
|
||||
{{ latest_code }}
|
||||
{% if latest_code_feedback is not none %}
|
||||
--------- Feedback to former code ---------
|
||||
{{ latest_code_feedback }}
|
||||
{% endif %}
|
||||
The former code contains errors. You should correct the code based on the provided information, ensuring you do not repeat the same mistakes.
|
||||
{% endif %}
|
||||
|
||||
|
||||
ensemble_eval:
|
||||
system: |-
|
||||
You are a data scientist responsible for evaluating ensemble implementation code generation.
|
||||
|
||||
## Task Description
|
||||
{{ task_desc }}
|
||||
|
||||
## Ensemble Code
|
||||
```python
|
||||
{{ code }}
|
||||
```
|
||||
|
||||
## Testing Process
|
||||
The ensemble code is tested using the following script:
|
||||
```python
|
||||
{{ test_code }}
|
||||
```
|
||||
You will analyze the execution results based on the test output provided.
|
||||
|
||||
{% if workflow_stdout is not none %}
|
||||
### Whole Workflow Consideration
|
||||
The ensemble code is part of the whole workflow. The user has executed the entire pipeline and provided additional stdout.
|
||||
|
||||
**Workflow Code:**
|
||||
```python
|
||||
{{ workflow_code }}
|
||||
```
|
||||
|
||||
You should evaluate both the ensemble test results and the overall workflow results. **Approve the code only if both tests pass.**
|
||||
{% endif %}
|
||||
|
||||
The metric used for scoring the predictions:
|
||||
**{{ metric_name }}**
|
||||
|
||||
## Evaluation Criteria
|
||||
- You will be given the standard output (`stdout`) from the ensemble test and, if applicable, the workflow test.
|
||||
- Code should have no try-except blocks because they can hide errors.
|
||||
- Check whether the code implement the scoring process using the given metric.
|
||||
- The stdout includes the local variable values from the ensemble code execution. Check whether the validation score is calculated correctly.
|
||||
|
||||
Please respond with your feedback in the following JSON format and order
|
||||
```json
|
||||
{
|
||||
"execution": "Describe how well the ensemble executed, including any errors or issues encountered. Append all error messages and full traceback details without summarizing or omitting any information.",
|
||||
"return_checking": "Detail the checks performed on the ensemble results, including shape and value validation.",
|
||||
"code": "Assess code quality, readability, and adherence to specifications.",
|
||||
"final_decision": <true/false>
|
||||
}
|
||||
```
|
||||
user: |-
|
||||
--------- Ensemble test stdout ---------
|
||||
{{ stdout }}
|
||||
{% if workflow_stdout is not none %}
|
||||
--------- Whole workflow test stdout ---------
|
||||
{{ workflow_stdout }}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,58 @@
|
||||
"""
|
||||
Helper functions for testing the ensemble coder(CoSTEER-based) component.
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from rdagent.components.coder.data_science.ensemble import EnsembleCoSTEER
|
||||
from rdagent.components.coder.data_science.ensemble.exp import EnsembleTask
|
||||
from rdagent.scenarios.data_science.experiment.experiment import DSExperiment
|
||||
from rdagent.scenarios.data_science.scen import KaggleScen
|
||||
|
||||
# Add the competition folder to path
|
||||
COMPETITION_PATH = (
|
||||
Path(__file__).parent.parent.parent.parent.parent
|
||||
/ "scenarios"
|
||||
/ "kaggle"
|
||||
/ "tpl_ex"
|
||||
/ "aerial-cactus-identification"
|
||||
)
|
||||
sys.path.append(str(COMPETITION_PATH))
|
||||
|
||||
EnsembleExperiment = DSExperiment
|
||||
|
||||
|
||||
def load_ensemble_spec():
|
||||
spec_path = COMPETITION_PATH / "spec" / "ensemble.md"
|
||||
with open(spec_path, "r") as f:
|
||||
return f.read()
|
||||
|
||||
|
||||
def develop_one_competition(competition: str):
|
||||
# Initialize scenario and coder
|
||||
scen = KaggleScen(competition=competition)
|
||||
ensemble_coder = EnsembleCoSTEER(scen)
|
||||
# Load ensemble specification
|
||||
ensemble_spec = load_ensemble_spec()
|
||||
|
||||
# Create the ensemble task with actual data context and specification
|
||||
task = EnsembleTask(
|
||||
name="EnsembleTask",
|
||||
description="""
|
||||
Implement ensemble and decision making for model predictions.
|
||||
""",
|
||||
)
|
||||
|
||||
exp = EnsembleExperiment(pending_tasks_list=[task])
|
||||
|
||||
# Injecting the corresponding specification
|
||||
exp.experiment_workspace.inject_files(**{"spec/ensemble.md": ensemble_spec})
|
||||
|
||||
# Develop the experiment
|
||||
exp = ensemble_coder.develop(exp)
|
||||
return exp
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
develop_one_competition("aerial-cactus-identification")
|
||||
@@ -0,0 +1,140 @@
|
||||
from pathlib import Path
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEERMultiEvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.evolving_strategy import (
|
||||
MultiProcessEvolvingStrategy,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.knowledge_management import (
|
||||
CoSTEERQueriedKnowledge,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import DSCoderCoSTEERSettings
|
||||
from rdagent.components.coder.data_science.feature.eval import FeatureCoSTEEREvaluator
|
||||
from rdagent.components.coder.data_science.feature.exp import FeatureTask
|
||||
from rdagent.components.coder.data_science.share.ds_costeer import DSCoSTEER
|
||||
from rdagent.core.exception import CoderError
|
||||
from rdagent.core.experiment import FBWorkspace
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.ret import PythonAgentOut
|
||||
from rdagent.utils.agent.tpl import T
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
|
||||
class FeatureMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
|
||||
def implement_one_task(
|
||||
self,
|
||||
target_task: FeatureTask,
|
||||
queried_knowledge: CoSTEERQueriedKnowledge | None = None,
|
||||
workspace: FBWorkspace | None = None,
|
||||
prev_task_feedback: CoSTEERSingleFeedback | None = None,
|
||||
) -> dict[str, str]:
|
||||
# return a workspace with "load_data.py", "spec/load_data.md" inside
|
||||
# assign the implemented code to the new workspace.
|
||||
feature_information_str = target_task.get_task_information()
|
||||
|
||||
# 1. query
|
||||
queried_similar_successful_knowledge = (
|
||||
queried_knowledge.task_to_similar_task_successful_knowledge[feature_information_str]
|
||||
if queried_knowledge is not None
|
||||
else []
|
||||
)
|
||||
queried_former_failed_knowledge = (
|
||||
queried_knowledge.task_to_former_failed_traces[feature_information_str]
|
||||
if queried_knowledge is not None
|
||||
else []
|
||||
)
|
||||
queried_former_failed_knowledge = (
|
||||
[
|
||||
knowledge
|
||||
for knowledge in queried_former_failed_knowledge[0]
|
||||
if knowledge.implementation.file_dict.get("feature.py") != workspace.file_dict.get("feature.py")
|
||||
],
|
||||
queried_former_failed_knowledge[1],
|
||||
)
|
||||
|
||||
# 2. code
|
||||
system_prompt = T(".prompts:feature_coder.system").r(
|
||||
competition_info=self.scen.get_scenario_all_desc(eda_output=workspace.file_dict.get("EDA.md", None)),
|
||||
task_desc=feature_information_str,
|
||||
data_loader_code=workspace.file_dict.get("load_data.py"),
|
||||
queried_similar_successful_knowledge=queried_similar_successful_knowledge,
|
||||
queried_former_failed_knowledge=queried_former_failed_knowledge[0],
|
||||
out_spec=PythonAgentOut.get_spec(),
|
||||
)
|
||||
code_spec = (
|
||||
workspace.file_dict["spec/feature.md"]
|
||||
if DS_RD_SETTING.spec_enabled
|
||||
else T("scenarios.data_science.share:component_spec.general").r(
|
||||
spec=T("scenarios.data_science.share:component_spec.FeatureEng").r(),
|
||||
test_code=(DIRNAME / "eval_tests" / "feature_test.txt").read_text(),
|
||||
)
|
||||
)
|
||||
user_prompt = T(".prompts:feature_coder.user").r(
|
||||
code_spec=code_spec,
|
||||
latest_code=workspace.file_dict.get("feature.py"),
|
||||
latest_code_feedback=prev_task_feedback,
|
||||
)
|
||||
|
||||
for _ in range(5):
|
||||
feature_code = PythonAgentOut.extract_output(
|
||||
APIBackend().build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
)
|
||||
)
|
||||
if feature_code != workspace.file_dict.get("feature.py"):
|
||||
break
|
||||
else:
|
||||
user_prompt = user_prompt + "\nPlease avoid generating same code to former code!"
|
||||
else:
|
||||
raise CoderError("Failed to generate a new feature code.")
|
||||
|
||||
return {
|
||||
"feature.py": feature_code,
|
||||
}
|
||||
|
||||
def assign_code_list_to_evo(self, code_list: list[dict[str, str]], evo):
|
||||
"""
|
||||
Assign the code list to the evolving item.
|
||||
|
||||
The code list is aligned with the evolving item's sub-tasks.
|
||||
If a task is not implemented, put a None in the list.
|
||||
"""
|
||||
for index in range(len(evo.sub_tasks)):
|
||||
if code_list[index] is None:
|
||||
continue
|
||||
if evo.sub_workspace_list[index] is None:
|
||||
# evo.sub_workspace_list[index] = FBWorkspace(target_task=evo.sub_tasks[index])
|
||||
evo.sub_workspace_list[index] = evo.experiment_workspace
|
||||
evo.sub_workspace_list[index].inject_files(**code_list[index])
|
||||
return evo
|
||||
|
||||
|
||||
class FeatureCoSTEER(DSCoSTEER):
|
||||
def __init__(
|
||||
self,
|
||||
scen: Scenario,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
settings = DSCoderCoSTEERSettings()
|
||||
eva = CoSTEERMultiEvaluator(
|
||||
FeatureCoSTEEREvaluator(scen=scen), scen=scen
|
||||
) # Please specify whether you agree running your eva in parallel or not
|
||||
es = FeatureMultiProcessEvolvingStrategy(scen=scen, settings=settings)
|
||||
|
||||
super().__init__(
|
||||
*args,
|
||||
settings=settings,
|
||||
eva=eva,
|
||||
es=es,
|
||||
evolving_version=2,
|
||||
scen=scen,
|
||||
max_loop=DS_RD_SETTING.coder_max_loop,
|
||||
**kwargs,
|
||||
)
|
||||
@@ -0,0 +1,80 @@
|
||||
from pathlib import Path
|
||||
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEEREvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import get_ds_env
|
||||
from rdagent.components.coder.data_science.utils import remove_eda_part
|
||||
from rdagent.core.evolving_framework import QueriedKnowledge
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
FeatureEvalFeedback = CoSTEERSingleFeedback
|
||||
|
||||
|
||||
class FeatureCoSTEEREvaluator(CoSTEEREvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
target_task: Task,
|
||||
implementation: FBWorkspace,
|
||||
gt_implementation: FBWorkspace,
|
||||
queried_knowledge: QueriedKnowledge = None,
|
||||
**kwargs,
|
||||
) -> FeatureEvalFeedback:
|
||||
target_task_information = target_task.get_task_information()
|
||||
if (
|
||||
queried_knowledge is not None
|
||||
and target_task_information in queried_knowledge.success_task_to_knowledge_dict
|
||||
):
|
||||
return queried_knowledge.success_task_to_knowledge_dict[target_task_information].feedback
|
||||
elif queried_knowledge is not None and target_task_information in queried_knowledge.failed_task_info_set:
|
||||
return FeatureEvalFeedback(
|
||||
execution="This task has failed too many times, skip implementation.",
|
||||
return_checking="This task has failed too many times, skip implementation.",
|
||||
code="This task has failed too many times, skip implementation.",
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
env = get_ds_env(
|
||||
extra_volumes={self.scen.debug_path: T("scenarios.data_science.share:scen.input_path").r()},
|
||||
running_timeout_period=self.scen.real_debug_timeout(),
|
||||
)
|
||||
|
||||
# TODO: do we need to clean the generated temporary content?
|
||||
fname = "test/feature_test.py"
|
||||
test_code = (DIRNAME / "eval_tests" / "feature_test.txt").read_text()
|
||||
implementation.inject_files(**{fname: test_code})
|
||||
|
||||
result = implementation.run(env=env, entry=f"python {fname}")
|
||||
|
||||
if "main.py" in implementation.file_dict and result.exit_code == 0:
|
||||
workflow_stdout = implementation.execute(env=env, entry="python main.py")
|
||||
workflow_stdout = remove_eda_part(workflow_stdout)
|
||||
else:
|
||||
workflow_stdout = None
|
||||
|
||||
system_prompt = T(".prompts:feature_eval.system").r(
|
||||
task_desc=target_task.get_task_information(),
|
||||
test_code=test_code,
|
||||
code=implementation.file_dict["feature.py"],
|
||||
workflow_stdout=workflow_stdout,
|
||||
workflow_code=implementation.all_codes,
|
||||
)
|
||||
user_prompt = T(".prompts:feature_eval.user").r(
|
||||
stdout=result.stdout,
|
||||
workflow_stdout=workflow_stdout,
|
||||
)
|
||||
|
||||
fb = build_cls_from_json_with_retry(
|
||||
FeatureEvalFeedback,
|
||||
system_prompt=system_prompt,
|
||||
user_prompt=user_prompt,
|
||||
init_kwargs_update_func=FeatureEvalFeedback.val_and_update_init_dict,
|
||||
)
|
||||
fb.final_decision = fb.final_decision and result.exit_code == 0
|
||||
|
||||
return fb
|
||||
@@ -0,0 +1,114 @@
|
||||
"""
|
||||
Tests for `feat_eng` in feature.py
|
||||
"""
|
||||
|
||||
|
||||
from copy import deepcopy
|
||||
import sys
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from feature import feat_eng
|
||||
from load_data import load_data
|
||||
import reprlib
|
||||
aRepr = reprlib.Repr()
|
||||
aRepr.maxother=300
|
||||
|
||||
X, y, X_test, test_ids = load_data()
|
||||
print("X:", aRepr.repr(X))
|
||||
print("y:", aRepr.repr(y))
|
||||
print("X_test:", aRepr.repr(X_test))
|
||||
print("test_ids", aRepr.repr(test_ids))
|
||||
|
||||
print(f"X.shape: {X.shape}" if hasattr(X, 'shape') else f"X length: {len(X)}")
|
||||
print(f"y.shape: {y.shape}" if hasattr(y, 'shape') else f"y length: {len(y)}")
|
||||
print(f"X_test.shape: {X_test.shape}" if hasattr(X_test, 'shape') else f"X_test length: {len(X_test)}")
|
||||
print(f"test_ids length: {len(test_ids)}")
|
||||
|
||||
X_loaded = deepcopy(X)
|
||||
y_loaded = deepcopy(y)
|
||||
X_test_loaded = deepcopy(X_test)
|
||||
|
||||
import sys
|
||||
import reprlib
|
||||
from joblib.memory import MemorizedFunc
|
||||
|
||||
|
||||
def get_original_code(func):
|
||||
if isinstance(func, MemorizedFunc):
|
||||
return func.func.__code__
|
||||
return func.__code__
|
||||
|
||||
|
||||
def debug_info_print(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
original_code = get_original_code(func)
|
||||
def local_trace(frame, event, arg):
|
||||
if event == "return" and frame.f_code == original_code:
|
||||
print("\n" + "="*20 + "Running feat_eng code, local variable values:" + "="*20)
|
||||
for k, v in frame.f_locals.items():
|
||||
printed = aRepr.repr(v)
|
||||
print(f"{k}:\n {printed}")
|
||||
print("="*20 + "Local variable values end" + "="*20)
|
||||
return local_trace
|
||||
|
||||
sys.settrace(local_trace)
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
finally:
|
||||
sys.settrace(None)
|
||||
return wrapper
|
||||
X, y, X_test = debug_info_print(feat_eng)(X, y, X_test)
|
||||
|
||||
|
||||
def get_length(data):
|
||||
return data.shape[0] if hasattr(data, 'shape') else len(data)
|
||||
|
||||
|
||||
def get_width(data):
|
||||
return 1 if isinstance(data, list) else data.shape[1:]
|
||||
|
||||
|
||||
def get_column_list(data):
|
||||
return data.columns.tolist() if isinstance(data, pd.DataFrame) else None
|
||||
|
||||
|
||||
assert X is not None, "The feature engineering function returned None for X."
|
||||
assert y is not None, "The feature engineering function returned None for y."
|
||||
assert X_test is not None, "The feature engineering function returned None for X_test."
|
||||
|
||||
assert get_length(X_test) == get_length(
|
||||
test_ids
|
||||
), f"Mismatch in length of test images and test IDs: X_test ({get_length(X_test)}) and test_ids ({get_length(test_ids)})"
|
||||
assert get_length(X) == get_length(
|
||||
y
|
||||
), f"Mismatch in length of training images and labels: X ({get_length(X)}) and y ({get_length(y)})"
|
||||
|
||||
assert get_length(X) != 0, f"Training data is empty."
|
||||
assert get_length(y) != 0, f"Training labels are empty."
|
||||
assert get_length(X_test) != 0, f"Test data is empty."
|
||||
|
||||
assert get_width(X) == get_width(
|
||||
X_test
|
||||
), "Mismatch in width of training and test data. Width means the number of features."
|
||||
|
||||
if isinstance(X, pd.DataFrame) and isinstance(X_test, pd.DataFrame):
|
||||
assert get_column_list(X) == get_column_list(X_test), "Mismatch in column names of training and test data."
|
||||
|
||||
if isinstance(X, pd.DataFrame):
|
||||
def normalize_dtype(dtype):
|
||||
return "numeric" if np.issubdtype(dtype, np.number) else str(dtype)
|
||||
|
||||
X_dtypes_unique_sorted = sorted(set(normalize_dtype(dt) for dt in X.dtypes.unique()))
|
||||
X_loaded_dtypes_unique_sorted = sorted(set(normalize_dtype(dt) for dt in X_loaded.dtypes.unique()))
|
||||
|
||||
X_dtypes_unique_sorted_new = [
|
||||
dt for dt in X_dtypes_unique_sorted if dt not in X_loaded_dtypes_unique_sorted and dt != "object"
|
||||
]
|
||||
assert (
|
||||
np.dtypes.ObjectDType in X_loaded_dtypes_unique_sorted or len(X_dtypes_unique_sorted_new) == 0
|
||||
), f"feature engineering has produced new data types which is not allowed, data loader data types are {X_loaded_dtypes_unique_sorted} and feature engineering data types are {X_dtypes_unique_sorted}"
|
||||
|
||||
|
||||
print(
|
||||
"Feature Engineering test passed successfully. All checks including length, width, and data types have been validated."
|
||||
)
|
||||
@@ -0,0 +1,13 @@
|
||||
import pickle
|
||||
import site
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
|
||||
from rdagent.components.coder.CoSTEER.task import CoSTEERTask
|
||||
from rdagent.core.utils import cache_with_pickle
|
||||
|
||||
|
||||
# Because we use isinstance to distinguish between different types of tasks, we need to use sub classes to represent different types of tasks
|
||||
class FeatureTask(CoSTEERTask):
|
||||
pass
|
||||
@@ -0,0 +1,131 @@
|
||||
feature_coder:
|
||||
system: |-
|
||||
You are a world-class data scientist and machine learning engineer with deep expertise in statistics, mathematics, and computer science.
|
||||
Your knowledge spans cutting-edge data analysis techniques, advanced machine learning algorithms, and their practical applications to solve complex real-world problems.
|
||||
|
||||
## Task Description
|
||||
{{ task_desc }}
|
||||
|
||||
## Competition Information for This Task
|
||||
{{ competition_info }}
|
||||
|
||||
{% if queried_similar_successful_knowledge|length != 0 or queried_former_failed_knowledge|length != 0 %}
|
||||
## Relevant Information for This Task
|
||||
{% endif %}
|
||||
|
||||
{% if queried_similar_successful_knowledge|length != 0 %}
|
||||
--------- Successful Implementations for Similar Models ---------
|
||||
====={% for similar_successful_knowledge in queried_similar_successful_knowledge %} Model {{ loop.index }}:=====
|
||||
{{ similar_successful_knowledge.target_task.get_task_information() }}
|
||||
=====Code:=====
|
||||
{{ similar_successful_knowledge.implementation.file_dict["feature.py"] }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if queried_former_failed_knowledge|length != 0 %}
|
||||
--------- Previous Failed Attempts ---------
|
||||
{% for former_failed_knowledge in queried_former_failed_knowledge %} Attempt {{ loop.index }}:
|
||||
=====Code:=====
|
||||
{{ former_failed_knowledge.implementation.file_dict["feature.py"] }}
|
||||
=====Feedback:=====
|
||||
{{ former_failed_knowledge.feedback }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
## Guidelines
|
||||
1. If feature engineering is unnecessary or should be combined with model training, you may skip this step.
|
||||
2. Be cautious of any column drop in the code. Dropping a column easily without any more attempts, it may not be a good practice.
|
||||
3. The function input is the output of the following data loader:
|
||||
```python
|
||||
{{ data_loader_code }}
|
||||
```
|
||||
4. **Additional Guidance:**
|
||||
- If a previous attempt exists, improve upon it without repeating mistakes.
|
||||
- If errors indicate a missing file, find a way to download it or implement an alternative solution.
|
||||
- You should avoid using logging module to output information in your generated code, and instead use the print() function.
|
||||
5. You should use the following cache decorator to cache the results of the function:
|
||||
```python
|
||||
from joblib import Memory
|
||||
memory = Memory(location='{% include "scenarios.data_science.share:scen.cache_path" %}', verbose=0)
|
||||
@memory.cache```
|
||||
6. Coding tricks:
|
||||
- If the input consists of a batch of file paths and you need to modify the file contents to complete your feature engineering task, you can accomplish your feature engineering task by modifying these files and creating new files in a subfolder within "{% include "scenarios.data_science.share:scen.cache_path" %}" (this path is persistent, otherwise you may lose your created file). Then the new file paths are returned.
|
||||
|
||||
{% include "scenarios.data_science.share:guidelines.coding" %}
|
||||
|
||||
## Output Format
|
||||
{% if out_spec %}
|
||||
{{ out_spec }}
|
||||
{% else %}
|
||||
Please response the code in the following json format. Here is an example structure for the JSON output:
|
||||
{
|
||||
"code": "The Python code as a string."
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
user: |-
|
||||
--------- Code Specification ---------
|
||||
{{ code_spec }}
|
||||
|
||||
{% if latest_code %}
|
||||
--------- Former code ---------
|
||||
{{ latest_code }}
|
||||
{% if latest_code_feedback is not none %}
|
||||
--------- Feedback to former code ---------
|
||||
{{ latest_code_feedback }}
|
||||
{% endif %}
|
||||
The former code contains errors. You should correct the code based on the provided information, ensuring you do not repeat the same mistakes.
|
||||
{% endif %}
|
||||
|
||||
|
||||
feature_eval:
|
||||
system: |-
|
||||
You are a data scientist responsible for evaluating feature engineering code generation.
|
||||
|
||||
## Task Description
|
||||
{{ task_desc }}
|
||||
|
||||
## Feature Engineering Code
|
||||
```python
|
||||
{{ code }}
|
||||
```
|
||||
|
||||
## Testing Process
|
||||
The feature engineering code is tested using the following script:
|
||||
```python
|
||||
{{ test_code }}
|
||||
```
|
||||
You will analyze the execution results based on the test output provided.
|
||||
|
||||
{% if workflow_stdout is not none %}
|
||||
### Whole Workflow Consideration
|
||||
The feature engineering code is part of the whole workflow. The user has executed the entire pipeline and provided additional stdout.
|
||||
|
||||
**Workflow Code:**
|
||||
```python
|
||||
{{ workflow_code }}
|
||||
```
|
||||
|
||||
You should evaluate both the feature engineering test results and the overall workflow results. **Approve the code only if both tests pass.**
|
||||
{% endif %}
|
||||
|
||||
## Evaluation Criteria
|
||||
You will be given the standard output (`stdout`) from the feature engineering test and, if applicable, the workflow test.
|
||||
|
||||
Please respond with your feedback in the following JSON format and order
|
||||
```json
|
||||
{
|
||||
"execution": "Describe how well the feature engineering executed, including any errors or issues encountered. Append all error messages and full traceback details without summarizing or omitting any information.",
|
||||
"return_checking": "Evaluate the correctness and integrity of processed data, checking for missing values, incorrect transformations, and data consistency.",
|
||||
"code": "Assess code quality, readability, and adherence to specifications. Consider efficiency, including whether the code utilizes multi-threading or GPU acceleration for optimization.",
|
||||
"final_decision": <true/false>
|
||||
}
|
||||
```
|
||||
|
||||
user: |-
|
||||
--------- Feature engineering test stdout ---------
|
||||
{{ stdout }}
|
||||
{% if workflow_stdout is not none %}
|
||||
--------- Whole workflow test stdout ---------
|
||||
{{ workflow_stdout }}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,37 @@
|
||||
"""
|
||||
Helper functions for testing the feature coder(CoSTEER-based) component.
|
||||
- Does the developer loop work correctly
|
||||
|
||||
It is NOT:
|
||||
- it is not interface unittest(i.e. workspace evaluator in the CoSTEER Loop)
|
||||
"""
|
||||
|
||||
from rdagent.components.coder.data_science.feature import FeatureCoSTEER
|
||||
from rdagent.components.coder.data_science.feature.exp import FeatureTask
|
||||
from rdagent.scenarios.data_science.experiment.experiment import DSExperiment
|
||||
from rdagent.scenarios.data_science.scen import KaggleScen
|
||||
|
||||
|
||||
def develop_one_competition(competition: str): # -> experiment
|
||||
scen = KaggleScen(competition=competition)
|
||||
feature_coder = FeatureCoSTEER(scen)
|
||||
|
||||
with open("./rdagent/scenarios/kaggle/tpl_ex/aerial-cactus-identification/spec/feature.md", "r") as file:
|
||||
feat_spec = file.read()
|
||||
|
||||
# Create the experiment
|
||||
ft = FeatureTask(name="FeatureTask", description=scen.get_competition_full_desc())
|
||||
exp = DSExperiment(
|
||||
sub_tasks=[ft],
|
||||
)
|
||||
|
||||
with open("./rdagent/scenarios/kaggle/tpl_ex/aerial-cactus-identification/load_data.py", "r") as file:
|
||||
load_data_code = file.read()
|
||||
exp.experiment_workspace.inject_files(**{"load_data.py": load_data_code, "spec/feature.md": feat_spec})
|
||||
|
||||
# Develop the experiment
|
||||
exp = feature_coder.develop(exp)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
develop_one_competition("aerial-cactus-identification")
|
||||
@@ -0,0 +1,173 @@
|
||||
from pathlib import Path
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEERMultiEvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.evolving_strategy import (
|
||||
MultiProcessEvolvingStrategy,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.knowledge_management import (
|
||||
CoSTEERQueriedKnowledge,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import DSCoderCoSTEERSettings
|
||||
from rdagent.components.coder.data_science.model.eval import (
|
||||
ModelGeneralCaseSpecEvaluator,
|
||||
)
|
||||
from rdagent.components.coder.data_science.model.exp import ModelTask
|
||||
from rdagent.components.coder.data_science.share.ds_costeer import DSCoSTEER
|
||||
from rdagent.core.exception import CoderError
|
||||
from rdagent.core.experiment import FBWorkspace
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.ret import PythonBatchEditOut
|
||||
from rdagent.utils.agent.tpl import T
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
|
||||
class ModelMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
|
||||
def implement_one_task(
|
||||
self,
|
||||
target_task: ModelTask,
|
||||
queried_knowledge: CoSTEERQueriedKnowledge | None = None,
|
||||
workspace: FBWorkspace | None = None,
|
||||
prev_task_feedback: CoSTEERSingleFeedback | None = None,
|
||||
) -> dict[str, str]:
|
||||
model_information_str = target_task.get_task_information()
|
||||
|
||||
# 1. query
|
||||
queried_similar_successful_knowledge = (
|
||||
queried_knowledge.task_to_similar_task_successful_knowledge[model_information_str]
|
||||
if queried_knowledge is not None
|
||||
else []
|
||||
)
|
||||
queried_former_failed_knowledge = (
|
||||
queried_knowledge.task_to_former_failed_traces[model_information_str]
|
||||
if queried_knowledge is not None
|
||||
else []
|
||||
)
|
||||
queried_former_failed_knowledge = (
|
||||
[
|
||||
knowledge
|
||||
for knowledge in queried_former_failed_knowledge[0]
|
||||
if knowledge.implementation.file_dict.get(f"{target_task.name}.py")
|
||||
!= workspace.file_dict.get(f"{target_task.name}.py")
|
||||
],
|
||||
queried_former_failed_knowledge[1],
|
||||
)
|
||||
|
||||
# 2. code
|
||||
system_prompt = T(".prompts:model_coder.system").r(
|
||||
task_desc=model_information_str,
|
||||
competition_info=self.scen.get_scenario_all_desc(eda_output=workspace.file_dict.get("EDA.md", None)),
|
||||
data_loader_code=workspace.file_dict.get("load_data.py"),
|
||||
feature_code=workspace.file_dict["feature.py"],
|
||||
queried_similar_successful_knowledge=queried_similar_successful_knowledge,
|
||||
queried_former_failed_knowledge=queried_former_failed_knowledge[0],
|
||||
out_spec=PythonBatchEditOut.get_spec(),
|
||||
)
|
||||
# user_prompt = T(".prompts:model_coder.user").r(
|
||||
# model_spec=workspace.file_dict["spec/model.md"],
|
||||
# feature_code=workspace.file_dict["feature.py"],
|
||||
# latest_code=workspace.file_dict.get(f"{target_task.name}.py", None),
|
||||
# )
|
||||
# We want to use a simpler way to
|
||||
code_spec = (
|
||||
workspace.file_dict["spec/model.md"]
|
||||
if DS_RD_SETTING.spec_enabled
|
||||
else T("scenarios.data_science.share:component_spec.general").r(
|
||||
spec=T("scenarios.data_science.share:component_spec.Model").r(),
|
||||
test_code=(DIRNAME / "eval_tests" / "model_test.txt").read_text().replace("model01", target_task.name),
|
||||
)
|
||||
)
|
||||
user_prompt = T(".prompts:model_coder.user_general").r(
|
||||
code_spec=code_spec,
|
||||
latest_model_code=workspace.get_codes(
|
||||
r"^model_(?!test)\w+\.py$"
|
||||
), # TODO: If we have high failure rate here, we should clean this step with less information.
|
||||
latest_code_feedback=prev_task_feedback,
|
||||
)
|
||||
|
||||
for _ in range(5):
|
||||
batch_edit = PythonBatchEditOut.extract_output(
|
||||
APIBackend().build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
)
|
||||
)
|
||||
|
||||
if not all(i.startswith("model_") for i in batch_edit.keys()):
|
||||
user_prompt += "\nYou should only update model codes!"
|
||||
continue
|
||||
|
||||
# 3. post process to align file name to the task name
|
||||
# we assumpt batch_edit only contains one model file update.
|
||||
batch_edit = {
|
||||
(f"{target_task.name}.py" if value != "__DEL__" and key != f"{target_task.name}.py" else key): value
|
||||
for key, value in batch_edit.items()
|
||||
}
|
||||
|
||||
user_prompt = user_prompt + "\nPlease avoid generating same code to former code!"
|
||||
# TODO: besides same code problem, we should also consider other problems lead to retry.
|
||||
if f"{target_task.name}.py" not in batch_edit:
|
||||
continue
|
||||
|
||||
if batch_edit and max(len(i.encode("utf-8")) for i in batch_edit.keys()) > 255:
|
||||
continue
|
||||
|
||||
if batch_edit[f"{target_task.name}.py"] != "__DEL__" and batch_edit[
|
||||
f"{target_task.name}.py"
|
||||
] != workspace.file_dict.get(f"{target_task.name}.py"):
|
||||
break
|
||||
|
||||
# If the task involves model removal, assume it can only process one model at a time.
|
||||
if len(batch_edit) == 1 and batch_edit[f"{target_task.name}.py"] == "__DEL__":
|
||||
break
|
||||
else:
|
||||
raise CoderError("Failed to generate a new model code.")
|
||||
|
||||
return batch_edit
|
||||
|
||||
def assign_code_list_to_evo(self, code_list: list[dict[str, str]], evo):
|
||||
"""
|
||||
Assign the code list to the evolving item.
|
||||
|
||||
The code list is aligned with the evolving item's sub-tasks.
|
||||
If a task is not implemented, put a None in the list.
|
||||
"""
|
||||
for index in range(len(evo.sub_tasks)):
|
||||
if code_list[index] is None:
|
||||
continue
|
||||
if evo.sub_workspace_list[index] is None:
|
||||
# evo.sub_workspace_list[index] = FBWorkspace(target_task=evo.sub_tasks[index])
|
||||
evo.sub_workspace_list[index] = evo.experiment_workspace
|
||||
evo.sub_workspace_list[index].inject_files(**code_list[index])
|
||||
return evo
|
||||
|
||||
|
||||
class ModelCoSTEER(DSCoSTEER):
|
||||
def __init__(
|
||||
self,
|
||||
scen: Scenario,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
settings = DSCoderCoSTEERSettings()
|
||||
eva = CoSTEERMultiEvaluator(
|
||||
ModelGeneralCaseSpecEvaluator(scen=scen), scen=scen
|
||||
) # Please specify whether you agree running your eva in parallel or not
|
||||
# eva = ModelGeneralCaseSpecEvaluator(scen=scen)
|
||||
es = ModelMultiProcessEvolvingStrategy(scen=scen, settings=settings)
|
||||
|
||||
super().__init__(
|
||||
*args,
|
||||
settings=settings,
|
||||
eva=eva,
|
||||
es=es,
|
||||
evolving_version=2,
|
||||
scen=scen,
|
||||
max_loop=DS_RD_SETTING.coder_max_loop,
|
||||
**kwargs,
|
||||
)
|
||||
@@ -0,0 +1,123 @@
|
||||
"""
|
||||
Beyond previous tests
|
||||
-
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEEREvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import get_ds_env
|
||||
from rdagent.components.coder.data_science.utils import remove_eda_part
|
||||
from rdagent.core.evolving_framework import QueriedKnowledge
|
||||
from rdagent.core.exception import CoderError
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
ModelSingleFeedback = CoSTEERSingleFeedback
|
||||
|
||||
|
||||
# Below are unit tests for testing the specification of the implemented model ------------------
|
||||
class ModelGeneralCaseSpecEvaluator(CoSTEEREvaluator):
|
||||
"""
|
||||
Motivation case:
|
||||
- Simplest case, we already split the data into train_data, valid_data, and test_data. We require the model to learn (optionally validate on valid data), and infer on test data.
|
||||
|
||||
Test workflow:
|
||||
- Build train, valid, and test data to run it, and test the output (e.g., shape, etc.)
|
||||
"""
|
||||
|
||||
def evaluate(
|
||||
self,
|
||||
target_task: Task,
|
||||
implementation: FBWorkspace,
|
||||
gt_implementation: FBWorkspace,
|
||||
queried_knowledge: QueriedKnowledge = None,
|
||||
**kwargs,
|
||||
) -> ModelSingleFeedback:
|
||||
target_task_information = target_task.get_task_information()
|
||||
if (
|
||||
queried_knowledge is not None
|
||||
and target_task_information in queried_knowledge.success_task_to_knowledge_dict
|
||||
):
|
||||
return queried_knowledge.success_task_to_knowledge_dict[target_task_information].feedback
|
||||
elif queried_knowledge is not None and target_task_information in queried_knowledge.failed_task_info_set:
|
||||
return ModelSingleFeedback(
|
||||
execution="This task has failed too many times, skip implementation.",
|
||||
return_checking="This task has failed too many times, skip implementation.",
|
||||
code="This task has failed too many times, skip implementation.",
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
env = get_ds_env(
|
||||
extra_volumes={self.scen.debug_path: T("scenarios.data_science.share:scen.input_path").r()},
|
||||
running_timeout_period=self.scen.real_debug_timeout(),
|
||||
)
|
||||
|
||||
if_model_removed = False
|
||||
|
||||
if f"{target_task.name}.py" in implementation.file_dict:
|
||||
fname = "test/model_test.py"
|
||||
test_code = (
|
||||
(DIRNAME / "eval_tests" / "model_test.txt").read_text().replace("model01", target_task.name)
|
||||
) # only check the model changed this time
|
||||
implementation.inject_files(**{fname: test_code})
|
||||
result = implementation.run(env=env, entry=f"python {fname}")
|
||||
stdout = result.stdout
|
||||
ret_code = result.exit_code
|
||||
|
||||
if stdout is None:
|
||||
raise CoderError(
|
||||
"The execution output contains too many progress bars and results in the LLM's token size exceeding the limit."
|
||||
)
|
||||
else:
|
||||
ret_code = 0
|
||||
if_model_removed = True
|
||||
stdout = f"Model {target_task.name} removal succeeded."
|
||||
|
||||
if "main.py" in implementation.file_dict and ret_code == 0:
|
||||
workflow_stdout = implementation.execute(env=env, entry="python main.py")
|
||||
workflow_stdout = remove_eda_part(workflow_stdout)
|
||||
else:
|
||||
workflow_stdout = None
|
||||
|
||||
if if_model_removed:
|
||||
system_prompt = T(".prompts:model_eval_rm.system").r(
|
||||
task_desc=target_task.get_task_information(),
|
||||
workflow_stdout=workflow_stdout,
|
||||
workflow_code=implementation.all_codes,
|
||||
)
|
||||
user_prompt = T(".prompts:model_eval_rm.user").r(
|
||||
stdout=stdout,
|
||||
workflow_stdout=workflow_stdout,
|
||||
)
|
||||
else:
|
||||
system_prompt = T(".prompts:model_eval.system").r(
|
||||
task_desc=target_task.get_task_information(),
|
||||
test_code=test_code,
|
||||
code=implementation.file_dict[f"{target_task.name}.py"],
|
||||
workflow_stdout=workflow_stdout,
|
||||
workflow_code=implementation.all_codes,
|
||||
)
|
||||
user_prompt = T(".prompts:model_eval.user").r(
|
||||
stdout=stdout,
|
||||
workflow_stdout=workflow_stdout,
|
||||
)
|
||||
|
||||
fb = build_cls_from_json_with_retry(
|
||||
ModelSingleFeedback,
|
||||
system_prompt=system_prompt,
|
||||
user_prompt=user_prompt,
|
||||
init_kwargs_update_func=ModelSingleFeedback.val_and_update_init_dict,
|
||||
)
|
||||
fb.final_decision = fb.final_decision and ret_code == 0
|
||||
|
||||
return fb
|
||||
@@ -0,0 +1,105 @@
|
||||
"""
|
||||
Tests for `model_workflow` in model01.py
|
||||
"""
|
||||
import sys
|
||||
import time
|
||||
|
||||
from feature import feat_eng
|
||||
from load_data import load_data
|
||||
from model01 import model_workflow
|
||||
from sklearn.model_selection import train_test_split
|
||||
|
||||
|
||||
def log_execution_results(start_time, val_pred, test_pred, hypers, execution_label):
|
||||
"""Log the results of a single model execution."""
|
||||
feedback_str = f"{execution_label} end.\n"
|
||||
feedback_str += f"Validation predictions shape: {val_pred.shape if val_pred is not None else 'None'}\n"
|
||||
feedback_str += f"Test predictions shape: {test_pred.shape if test_pred is not None else 'None'}\n"
|
||||
feedback_str += f"Hyperparameters: {hypers if hypers is not None else 'None'}\n"
|
||||
feedback_str += f"Execution time: {time.time() - start_time:.2f} seconds.\n"
|
||||
print(feedback_str)
|
||||
|
||||
|
||||
import reprlib
|
||||
aRepr = reprlib.Repr()
|
||||
aRepr.maxother=300
|
||||
|
||||
# Load and preprocess data
|
||||
X, y, test_X, test_ids = load_data()
|
||||
X, y, test_X = feat_eng(X, y, test_X)
|
||||
|
||||
print(f"X.shape: {X.shape}" if hasattr(X, 'shape') else f"X length: {len(X)}")
|
||||
print(f"y.shape: {y.shape}" if hasattr(y, 'shape') else f"y length: {len(y)}")
|
||||
print(f"test_X.shape: {test_X.shape}" if hasattr(test_X, 'shape') else f"test_X length: {len(test_X)}")
|
||||
print(f"test_ids length: {len(test_ids)}")
|
||||
|
||||
train_X, val_X, train_y, val_y = train_test_split(X, y, test_size=0.8, random_state=42)
|
||||
|
||||
|
||||
import sys
|
||||
import reprlib
|
||||
from joblib.memory import MemorizedFunc
|
||||
|
||||
|
||||
def get_original_code(func):
|
||||
if isinstance(func, MemorizedFunc):
|
||||
return func.func.__code__
|
||||
return func.__code__
|
||||
|
||||
print("train_X:", aRepr.repr(train_X))
|
||||
print("train_y:", aRepr.repr(train_y))
|
||||
print("val_X:", aRepr.repr(val_X))
|
||||
print("val_y:", aRepr.repr(val_y))
|
||||
|
||||
print(f"train_X.shape: {train_X.shape}" if hasattr(train_X, 'shape') else f"train_X length: {len(train_X)}")
|
||||
print(f"train_y.shape: {train_y.shape}" if hasattr(train_y, 'shape') else f"train_y length: {len(train_y)}")
|
||||
print(f"val_X.shape: {val_X.shape}" if hasattr(val_X, 'shape') else f"val_X length: {len(val_X)}")
|
||||
print(f"val_y.shape: {val_y.shape}" if hasattr(val_y, 'shape') else f"val_y length: {len(val_y)}")
|
||||
|
||||
|
||||
|
||||
def debug_info_print(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
original_code = get_original_code(func)
|
||||
def local_trace(frame, event, arg):
|
||||
if event == "return" and frame.f_code == original_code:
|
||||
print("\n" + "="*20 + "Running model training code, local variable values:" + "="*20)
|
||||
for k, v in frame.f_locals.items():
|
||||
printed = aRepr.repr(v)
|
||||
print(f"{k}:\n {printed}")
|
||||
print("="*20 + "Local variable values end" + "="*20)
|
||||
return local_trace
|
||||
|
||||
sys.settrace(local_trace)
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
finally:
|
||||
sys.settrace(None)
|
||||
return wrapper
|
||||
|
||||
# First execution
|
||||
print("The first execution begins.\n")
|
||||
start_time = time.time()
|
||||
val_pred, test_pred, hypers = debug_info_print(model_workflow)(
|
||||
X=train_X,
|
||||
y=train_y,
|
||||
val_X=val_X,
|
||||
val_y=val_y,
|
||||
test_X=None,
|
||||
)
|
||||
log_execution_results(start_time, val_pred, test_pred, hypers, "The first execution")
|
||||
|
||||
# Second execution
|
||||
print("The second execution begins.\n")
|
||||
start_time = time.time()
|
||||
val_pred, test_pred, final_hypers = debug_info_print(model_workflow)(
|
||||
X=train_X,
|
||||
y=train_y,
|
||||
val_X=None,
|
||||
val_y=None,
|
||||
test_X=test_X,
|
||||
hyper_params=hypers,
|
||||
)
|
||||
log_execution_results(start_time, val_pred, test_pred, final_hypers, "The second execution")
|
||||
|
||||
print("Model code test end.")
|
||||
@@ -0,0 +1,21 @@
|
||||
from typing import Dict, Optional
|
||||
|
||||
from rdagent.components.coder.CoSTEER.task import CoSTEERTask
|
||||
|
||||
|
||||
# Because we use isinstance to distinguish between different types of tasks, we need to use sub classes to represent different types of tasks
|
||||
class ModelTask(CoSTEERTask):
|
||||
def __init__(
|
||||
self,
|
||||
name: str,
|
||||
description: str,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
super().__init__(name=name, description=description, *args, **kwargs)
|
||||
|
||||
def get_task_information(self):
|
||||
task_desc = f"""name: {self.name}
|
||||
description: {self.description}
|
||||
"""
|
||||
return task_desc
|
||||
@@ -0,0 +1,186 @@
|
||||
model_coder:
|
||||
system: |-
|
||||
You are a world-class data scientist and machine learning engineer with deep expertise in statistics, mathematics, and computer science.
|
||||
Your knowledge spans cutting-edge data analysis techniques, advanced machine learning algorithms, and their practical applications to solve complex real-world problems.
|
||||
|
||||
## Task Description
|
||||
{{ task_desc }}
|
||||
|
||||
## Competition Information for This Task
|
||||
{{ competition_info }}
|
||||
|
||||
{% if queried_similar_successful_knowledge|length != 0 or queried_former_failed_knowledge|length != 0 %}
|
||||
## Relevant Information for This Task
|
||||
{% endif %}
|
||||
|
||||
{% if queried_similar_successful_knowledge|length != 0 %}
|
||||
--------- Successful Implementations for Similar Models ---------
|
||||
====={% for similar_successful_knowledge in queried_similar_successful_knowledge %} Model {{ loop.index }}:=====
|
||||
{{ similar_successful_knowledge.target_task.get_task_information() }}
|
||||
=====Code:=====
|
||||
{{ similar_successful_knowledge.implementation.file_dict[similar_successful_knowledge.target_task.name ~ '.py'] }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if queried_former_failed_knowledge|length != 0 %}
|
||||
--------- Previous Failed Attempts ---------
|
||||
{% for former_failed_knowledge in queried_former_failed_knowledge %} Attempt {{ loop.index }}:
|
||||
=====Code:=====
|
||||
{{ former_failed_knowledge.implementation.file_dict[former_failed_knowledge.target_task.name ~ '.py'] }}
|
||||
=====Feedback:=====
|
||||
{{ former_failed_knowledge.feedback }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
## Guidelines
|
||||
1. The function's input is from the output of a feature engineering function whose input is the output of a data loading function. The data loader function and feature engineering function code is as follows:
|
||||
--------- Data Loader Code ---------
|
||||
{{ data_loader_code }}
|
||||
--------- Feature Engineering Code ---------
|
||||
{{ feature_code }}
|
||||
2. You should avoid using logging module to output information in your generated code, and instead use the print() function.
|
||||
3. If the model can both be implemented by PyTorch and Tensorflow, please use pytorch for broader compatibility.
|
||||
4. You should use the following cache decorator to cache the results of the function:
|
||||
```python
|
||||
from joblib import Memory
|
||||
memory = Memory(location='{% include "scenarios.data_science.share:scen.cache_path" %}', verbose=0)
|
||||
@memory.cache``
|
||||
{% include "scenarios.data_science.share:guidelines.coding" %}
|
||||
|
||||
## Output Format
|
||||
{% if out_spec %}
|
||||
{{ out_spec }}
|
||||
The file name should be the model name described in the model task in the format "{task_name}.py". You should always follow this name format.
|
||||
{% else %}
|
||||
Please response the code in the following json format. Here is an example structure for the JSON output:
|
||||
{
|
||||
"code": "The Python code as a string."
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
user_general: |-
|
||||
--------- Code Specification ---------
|
||||
{{ code_spec }}
|
||||
|
||||
--------- Former model code ---------
|
||||
{% if latest_model_code|length == 0 %}
|
||||
So far the workspace is empty. No model code has been implemented yet.
|
||||
{% else %}
|
||||
{{ latest_model_code }}
|
||||
{% if latest_code_feedback is not none %}
|
||||
--------- Feedback to former code ---------
|
||||
{{ latest_code_feedback }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
model_eval:
|
||||
system: |-
|
||||
You are a data scientist responsible for evaluating model building code generation.
|
||||
|
||||
## Task Description
|
||||
{{ task_desc }}
|
||||
|
||||
## Model Building Code
|
||||
```python
|
||||
{{ code }}
|
||||
```
|
||||
|
||||
## Testing Process
|
||||
The model building code is tested using the following script:
|
||||
```python
|
||||
{{ test_code }}
|
||||
```
|
||||
|
||||
### Execution Phases
|
||||
The model is tested in two phases:
|
||||
|
||||
1. Initial Training Phase:
|
||||
- The model receives **train and valid inputs** with **empty hyperparameters**.
|
||||
- The focus is on verifying whether the model successfully trains and produces **valid outputs and hyperparameter outputs**.
|
||||
|
||||
2. Retraining Phase:
|
||||
- The model receives **train and test inputs** (without valid inputs).
|
||||
- The hyperparameters generated from the first phase are passed back for **retraining**.
|
||||
|
||||
|
||||
### Key Requirements for Approval
|
||||
A model can only be approved if it meets all of the following conditions:
|
||||
1. Hyperparameter Handling
|
||||
- If hyperparameters are returned, they must include an early stop round.
|
||||
- The hyperparameters must be correctly utilized in the model for retraining.
|
||||
- If the early stop round is provided, it must be used in the model implementation.
|
||||
2. The model output shape must strictly match the specifications in `spec.md`.
|
||||
|
||||
{% if workflow_stdout is not none %}
|
||||
### Whole Workflow Consideration
|
||||
The model building code is part of the whole workflow. The user has executed the entire pipeline and provided additional stdout.
|
||||
|
||||
**Workflow Code:**
|
||||
```python
|
||||
{{ workflow_code }}
|
||||
```
|
||||
|
||||
You should evaluate both the model building test results and the overall workflow results. **Approve the code only if both tests pass.**
|
||||
{% endif %}
|
||||
|
||||
## Evaluation Criteria
|
||||
You will be given the standard output (`stdout`) from the model building test and, if applicable, the workflow test.
|
||||
[Note] If no stdout for model buidling test is provided, the model failed due to a timeout or out-of-memory error. You should analyze potential optimizations.
|
||||
|
||||
Please respond with your feedback in the following JSON format and order
|
||||
```json
|
||||
{
|
||||
"execution": "Describe how well the model building executed, including any errors or issues encountered. Append all error messages and full traceback details without summarizing or omitting any information.",
|
||||
"return_checking": "Check the generated value, including whether the value is generated and comparing the shape of the model output with the requirement in spec.md. You also need to check whether the hyperparameters used for retraining are correctly returned during the test execution of the model.",
|
||||
"code": "Assess code quality, readability, and adherence to specifications. Consider efficiency, including whether the code utilizes multi-threading or GPU acceleration for optimization.",
|
||||
"final_decision": <true/false>
|
||||
}
|
||||
```
|
||||
|
||||
user: |-
|
||||
--------- Model building test stdout ---------
|
||||
{{ stdout }}
|
||||
{% if workflow_stdout is not none %}
|
||||
--------- Whole workflow test stdout ---------
|
||||
{{ workflow_stdout }}
|
||||
{% endif %}
|
||||
|
||||
model_eval_rm:
|
||||
system: |-
|
||||
You are a data scientist responsible for evaluating model removal process.
|
||||
|
||||
## Task Description
|
||||
{{ task_desc }}
|
||||
|
||||
{% if workflow_stdout is not none %}
|
||||
## Whole Workflow Consideration
|
||||
The model building code is part of the whole workflow. The user has executed the entire pipeline and provided additional stdout.
|
||||
|
||||
**Workflow Code:**
|
||||
```python
|
||||
{{ workflow_code }}
|
||||
```
|
||||
|
||||
You should evaluate both the model removal test results and the overall workflow results. **Approve the code only if both tests pass.**
|
||||
{% endif %}
|
||||
|
||||
## Evaluation Criteria
|
||||
You will be given the standard output (`stdout`) from the model removal test and, if applicable, the workflow test.
|
||||
|
||||
Please respond with your feedback in the following JSON format and order
|
||||
```json
|
||||
{
|
||||
"execution": "Describe how well the model removal executed, including any errors or issues encountered. Append all error messages and full traceback details without summarizing or omitting any information.",
|
||||
"return_checking": "Check the generated value, including whether the value is generated and comparing the shape of the model output with the requirement in spec.md.",
|
||||
"code": "Assess code quality, readability, and adherence to specifications.",
|
||||
"final_decision": <true/false>
|
||||
}
|
||||
```
|
||||
|
||||
user: |-
|
||||
--------- Model removal test stdout ---------
|
||||
{{ stdout }}
|
||||
{% if workflow_stdout is not none %}
|
||||
--------- Whole workflow test stdout ---------
|
||||
{{ workflow_stdout }}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,67 @@
|
||||
"""
|
||||
Generate dataset to test the model workflow output
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from rdagent.components.coder.CoSTEER.config import CoSTEER_SETTINGS
|
||||
from rdagent.components.coder.data_science.model import ModelCoSTEER
|
||||
from rdagent.components.coder.data_science.model.eval import (
|
||||
ModelGeneralCaseSpecEvaluator,
|
||||
)
|
||||
from rdagent.components.coder.data_science.model.exp import ModelTask
|
||||
from rdagent.core.experiment import FBWorkspace
|
||||
from rdagent.scenarios.data_science.experiment.experiment import DSExperiment
|
||||
from rdagent.scenarios.data_science.scen import KaggleScen
|
||||
|
||||
|
||||
# Take tasks, spec.md and feat as input, generate a feedback as output
|
||||
def develop_one_competition(competition: str):
|
||||
scen = KaggleScen(competition=competition)
|
||||
model_coder = ModelCoSTEER(scen)
|
||||
|
||||
# Create the task
|
||||
mt = ModelTask(
|
||||
name="ModelTask",
|
||||
description="A CNN Model",
|
||||
model_type="CNN",
|
||||
architecture="\hat{y}_u = CNN(X_u)",
|
||||
# variables="variables: {'\\hat{y}_u': 'The predicted output for node u', 'X_u': 'The input features for node u'}",
|
||||
hyperparameters="...",
|
||||
base_code="",
|
||||
)
|
||||
|
||||
tpl_ex_path = Path(__file__).resolve() / Path("rdagent/scenarios/kaggle/tpl_ex").resolve() / competition
|
||||
injected_file_names = ["spec/model.md", "load_data.py", "feature.py", "model01.py"]
|
||||
|
||||
modelexp = FBWorkspace()
|
||||
for file_name in injected_file_names:
|
||||
file_path = tpl_ex_path / file_name
|
||||
modelexp.inject_files(**{file_name: file_path.read_text()})
|
||||
|
||||
mt.base_code += modelexp.file_dict["model01.py"]
|
||||
exp = DSExperiment(
|
||||
sub_tasks=[mt],
|
||||
)
|
||||
|
||||
# Test the evaluator:
|
||||
"""eva = ModelGeneralCaseSpecEvaluator(scen=scen)
|
||||
exp.feedback = eva.evaluate(target_task=mt, queried_knowledge=None, implementation=modelexp, gt_implementation=None)
|
||||
print(exp.feedback)"""
|
||||
|
||||
# Test the evolving strategy:
|
||||
"""es = ModelMultiProcessEvolvingStrategy(scen=scen, settings=CoSTEER_SETTINGS)
|
||||
new_code = es.implement_one_task(target_task=mt, queried_knowledge=None, workspace=modelexp)
|
||||
print(new_code)"""
|
||||
|
||||
# Run the experiment
|
||||
for file_name in injected_file_names:
|
||||
file_path = tpl_ex_path / file_name
|
||||
exp.experiment_workspace.inject_files(**{file_name: file_path.read_text()})
|
||||
|
||||
exp = model_coder.develop(exp)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
develop_one_competition("aerial-cactus-identification")
|
||||
# dotenv run -- python rdagent/components/coder/data_science/model/test.py
|
||||
@@ -0,0 +1,165 @@
|
||||
"""
|
||||
|
||||
Loop should not large change exclude
|
||||
- Action Choice[current data loader & spec]
|
||||
- other should share
|
||||
- Propose[choice] => Task[Choice] => CoSTEER =>
|
||||
-
|
||||
|
||||
Extra feature:
|
||||
- cache
|
||||
|
||||
|
||||
File structure
|
||||
- ___init__.py: the entrance/agent of coder
|
||||
- evaluator.py
|
||||
- conf.py
|
||||
- exp.py: everything under the experiment, e.g.
|
||||
- Task
|
||||
- Experiment
|
||||
- Workspace
|
||||
- test.py
|
||||
- Each coder could be tested.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEERMultiEvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.evolving_strategy import (
|
||||
MultiProcessEvolvingStrategy,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.knowledge_management import (
|
||||
CoSTEERQueriedKnowledge,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import DSCoderCoSTEERSettings
|
||||
from rdagent.components.coder.data_science.pipeline.eval import PipelineCoSTEEREvaluator
|
||||
from rdagent.components.coder.data_science.pipeline.exp import PipelineTask
|
||||
from rdagent.components.coder.data_science.share.ds_costeer import DSCoSTEER
|
||||
from rdagent.components.coder.data_science.share.eval import ModelDumpEvaluator
|
||||
from rdagent.core.exception import CoderError
|
||||
from rdagent.core.experiment import FBWorkspace
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.core.utils import import_class
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.ret import PythonAgentOut
|
||||
from rdagent.utils.agent.tpl import T
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
|
||||
class PipelineMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
|
||||
def implement_one_task(
|
||||
self,
|
||||
target_task: PipelineTask,
|
||||
queried_knowledge: CoSTEERQueriedKnowledge | None = None,
|
||||
workspace: FBWorkspace | None = None,
|
||||
prev_task_feedback: CoSTEERSingleFeedback | None = None,
|
||||
) -> dict[str, str]:
|
||||
competition_info = self.scen.get_scenario_all_desc(eda_output=workspace.file_dict.get("EDA.md", None))
|
||||
data_folder_info = self.scen.processed_data_folder_description
|
||||
pipeline_task_info = target_task.get_task_information()
|
||||
|
||||
queried_former_failed_knowledge = (
|
||||
queried_knowledge.task_to_former_failed_traces[pipeline_task_info] if queried_knowledge is not None else []
|
||||
)
|
||||
queried_former_failed_knowledge = (
|
||||
[
|
||||
knowledge
|
||||
for knowledge in queried_former_failed_knowledge[0]
|
||||
if knowledge.implementation.file_dict.get("main.py") != workspace.file_dict.get("main.py")
|
||||
],
|
||||
queried_former_failed_knowledge[1],
|
||||
)
|
||||
|
||||
system_prompt = T(".prompts:pipeline_coder.system").r(
|
||||
task_desc=pipeline_task_info,
|
||||
queried_former_failed_knowledge=queried_former_failed_knowledge[0],
|
||||
out_spec=PythonAgentOut.get_spec(),
|
||||
runtime_environment=self.scen.get_runtime_environment(),
|
||||
package_info=target_task.package_info,
|
||||
enable_model_dump=DS_RD_SETTING.enable_model_dump,
|
||||
enable_debug_mode=DS_RD_SETTING.sample_data_by_LLM,
|
||||
spec=T("scenarios.data_science.share:component_spec.Pipeline").r(
|
||||
metric_name=self.scen.metric_name,
|
||||
enable_notebook_conversion=DS_RD_SETTING.enable_notebook_conversion,
|
||||
),
|
||||
)
|
||||
user_prompt = T(".prompts:pipeline_coder.user").r(
|
||||
competition_info=competition_info,
|
||||
folder_spec=data_folder_info,
|
||||
latest_code=workspace.file_dict.get("main.py"),
|
||||
latest_code_feedback=prev_task_feedback,
|
||||
)
|
||||
|
||||
for _ in range(5):
|
||||
pipeline_code = PythonAgentOut.extract_output(
|
||||
APIBackend().build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
)
|
||||
)
|
||||
if pipeline_code != workspace.file_dict.get("main.py"):
|
||||
break
|
||||
else:
|
||||
user_prompt = user_prompt + "\nPlease avoid generating same code to former code!"
|
||||
else:
|
||||
raise CoderError("Failed to generate a new pipeline code.")
|
||||
|
||||
return {
|
||||
"main.py": pipeline_code,
|
||||
}
|
||||
|
||||
def assign_code_list_to_evo(self, code_list: list[dict[str, str]], evo):
|
||||
"""
|
||||
Assign the code list to the evolving item.
|
||||
|
||||
The code list is aligned with the evolving item's sub-tasks.
|
||||
If a task is not implemented, put a None in the list.
|
||||
"""
|
||||
for index in range(len(evo.sub_tasks)):
|
||||
if code_list[index] is None:
|
||||
continue
|
||||
if evo.sub_workspace_list[index] is None:
|
||||
# evo.sub_workspace_list[index] = FBWorkspace(target_task=evo.sub_tasks[index])
|
||||
evo.sub_workspace_list[index] = evo.experiment_workspace
|
||||
evo.sub_workspace_list[index].inject_files(**code_list[index])
|
||||
return evo
|
||||
|
||||
|
||||
class PipelineCoSTEER(DSCoSTEER):
|
||||
def __init__(
|
||||
self,
|
||||
scen: Scenario,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
settings = DSCoderCoSTEERSettings()
|
||||
eval_l = [PipelineCoSTEEREvaluator(scen=scen)]
|
||||
if DS_RD_SETTING.enable_model_dump:
|
||||
eval_l.append(ModelDumpEvaluator(scen=scen, data_type="sample"))
|
||||
for evaluator in settings.extra_evaluator:
|
||||
eval_l.append(import_class(evaluator)(scen=scen))
|
||||
|
||||
for extra_eval in DSCoderCoSTEERSettings().extra_eval:
|
||||
kls = import_class(extra_eval)
|
||||
eval_l.append(kls(scen=scen))
|
||||
|
||||
eva = CoSTEERMultiEvaluator(
|
||||
single_evaluator=eval_l, scen=scen
|
||||
) # Please specify whether you agree running your eva in parallel or not
|
||||
es = PipelineMultiProcessEvolvingStrategy(scen=scen, settings=settings)
|
||||
|
||||
super().__init__(
|
||||
*args,
|
||||
settings=settings,
|
||||
eva=eva,
|
||||
es=es,
|
||||
evolving_version=2,
|
||||
scen=scen,
|
||||
max_loop=DS_RD_SETTING.coder_max_loop,
|
||||
**kwargs,
|
||||
)
|
||||
@@ -0,0 +1,348 @@
|
||||
# tess successfully running.
|
||||
# (GPT) if it aligns with the spec & rationality of the spec.
|
||||
import json
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.agent.context7 import Agent as DocAgent
|
||||
from rdagent.components.coder.CoSTEER import CoSTEERMultiFeedback
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEEREvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.knowledge_management import (
|
||||
CoSTEERQueriedKnowledgeV2,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import get_clear_ws_cmd, get_ds_env
|
||||
from rdagent.components.coder.data_science.share.notebook import NotebookConverter
|
||||
from rdagent.components.coder.data_science.utils import remove_eda_part
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
from rdagent.scenarios.data_science.test_eval import get_test_eval
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
|
||||
@dataclass
|
||||
class DSCoderFeedback(CoSTEERSingleFeedback):
|
||||
"""
|
||||
Feedback for Data Science CoSTEER evaluation.
|
||||
This feedback is used to evaluate the code and execution of the Data Science CoSTEER task.
|
||||
"""
|
||||
|
||||
requires_documentation_search: bool | None = None # Keep None means the feature is disabled
|
||||
error_message: str | None = None
|
||||
|
||||
@staticmethod
|
||||
def val_and_update_init_dict(data: dict) -> dict:
|
||||
# First call parent class validation method to handle base fields
|
||||
data = CoSTEERSingleFeedback.val_and_update_init_dict(data)
|
||||
|
||||
# Validate new fields
|
||||
if "requires_documentation_search" in data:
|
||||
if isinstance(data["requires_documentation_search"], str):
|
||||
if data["requires_documentation_search"] == "false" or data["requires_documentation_search"] == "False":
|
||||
data["requires_documentation_search"] = False
|
||||
elif data["requires_documentation_search"] == "true" or data["requires_documentation_search"] == "True":
|
||||
data["requires_documentation_search"] = True
|
||||
else:
|
||||
raise ValueError(
|
||||
f"'requires_documentation_search' string value must be 'true', 'True', 'false', or 'False', not '{data['requires_documentation_search']}'"
|
||||
)
|
||||
elif data["requires_documentation_search"] is not None and not isinstance(
|
||||
data["requires_documentation_search"], bool
|
||||
):
|
||||
raise ValueError(
|
||||
f"'requires_documentation_search' must be a boolean, string, or None, not {type(data['requires_documentation_search'])}"
|
||||
)
|
||||
|
||||
if "error_message" in data:
|
||||
if data["error_message"] is not None and not isinstance(data["error_message"], str):
|
||||
raise ValueError(f"'error_message' must be a string or None, not {type(data['error_message'])}")
|
||||
|
||||
return data
|
||||
|
||||
def __str__(self) -> str:
|
||||
base_str = super().__str__()
|
||||
|
||||
if self.requires_documentation_search is not None:
|
||||
base_str += f"-------------------Documentation Search Required------------------\n{self.requires_documentation_search}\n"
|
||||
|
||||
if self.error_message is not None:
|
||||
# Check if error_message contains Context7 documentation results
|
||||
if "### API Documentation Reference:" in self.error_message:
|
||||
base_str += f"-------------------Error Analysis & Documentation Search Results ------------------\n{self.error_message}\n"
|
||||
else:
|
||||
base_str += f"-------------------Error Message------------------\n{self.error_message}\n"
|
||||
|
||||
return base_str
|
||||
|
||||
@classmethod
|
||||
def merge(cls, feedback_li: list[CoSTEERSingleFeedback]) -> "DSCoderFeedback":
|
||||
# Call parent class merge method to handle base fields
|
||||
merged_fb = super().merge(feedback_li)
|
||||
|
||||
# Convert to DSCoderFeedback type if needed
|
||||
if not isinstance(merged_fb, DSCoderFeedback):
|
||||
merged_fb = DSCoderFeedback(
|
||||
execution=merged_fb.execution,
|
||||
return_checking=merged_fb.return_checking,
|
||||
code=merged_fb.code,
|
||||
final_decision=merged_fb.final_decision,
|
||||
)
|
||||
|
||||
# Merge error_message fields
|
||||
error_messages = [
|
||||
fb.error_message for fb in feedback_li if isinstance(fb, DSCoderFeedback) and fb.error_message is not None
|
||||
]
|
||||
if error_messages:
|
||||
merged_fb.error_message = "\n\n".join(error_messages)
|
||||
|
||||
# Merge requires_documentation_search fields (True if any is True)
|
||||
requires_search = [
|
||||
fb.requires_documentation_search
|
||||
for fb in feedback_li
|
||||
if isinstance(fb, DSCoderFeedback) and fb.requires_documentation_search is not None
|
||||
]
|
||||
if requires_search:
|
||||
merged_fb.requires_documentation_search = any(requires_search)
|
||||
|
||||
return merged_fb
|
||||
|
||||
|
||||
PipelineSingleFeedback = DSCoderFeedback # Only for compatible
|
||||
PipelineMultiFeedback = CoSTEERMultiFeedback
|
||||
|
||||
|
||||
class PipelineCoSTEEREvaluator(CoSTEEREvaluator):
|
||||
|
||||
def evaluate(
|
||||
self,
|
||||
target_task: Task,
|
||||
implementation: FBWorkspace,
|
||||
gt_implementation: FBWorkspace,
|
||||
queried_knowledge: CoSTEERQueriedKnowledgeV2 = None,
|
||||
**kwargs,
|
||||
) -> PipelineSingleFeedback:
|
||||
|
||||
target_task_information = target_task.get_task_information()
|
||||
if (
|
||||
queried_knowledge is not None
|
||||
and target_task_information in queried_knowledge.success_task_to_knowledge_dict
|
||||
):
|
||||
return queried_knowledge.success_task_to_knowledge_dict[target_task_information].feedback
|
||||
elif queried_knowledge is not None and target_task_information in queried_knowledge.failed_task_info_set:
|
||||
return PipelineSingleFeedback(
|
||||
execution="This task has failed too many times, skip implementation.",
|
||||
return_checking="This task has failed too many times, skip implementation.",
|
||||
code="This task has failed too many times, skip implementation.",
|
||||
error_message="This task has failed too many times, skip implementation.",
|
||||
requires_documentation_search=None,
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
env = get_ds_env(
|
||||
extra_volumes={self.scen.debug_path: T("scenarios.data_science.share:scen.input_path").r()},
|
||||
running_timeout_period=self.scen.real_debug_timeout(),
|
||||
)
|
||||
|
||||
stdout = ""
|
||||
implementation.execute(env=env, entry=get_clear_ws_cmd())
|
||||
if DS_RD_SETTING.sample_data_by_LLM:
|
||||
# Because coder runs on full data, we need to run debug mode in advance to save time
|
||||
result = implementation.run(
|
||||
env=env, entry=f"strace -e trace=file -f -o trace.log python -m coverage run main.py --debug"
|
||||
)
|
||||
else:
|
||||
result = implementation.run(
|
||||
env=env, entry=f"strace -e trace=file -f -o trace.log python -m coverage run main.py"
|
||||
)
|
||||
result_stdout = result.stdout
|
||||
|
||||
nb_conversion_ret_code = 0
|
||||
nb_conversion_check_text = ""
|
||||
if DS_RD_SETTING.enable_notebook_conversion:
|
||||
notebook_converter = NotebookConverter()
|
||||
code = implementation.file_dict["main.py"]
|
||||
error_msg = notebook_converter.validate_code_format(code)
|
||||
if error_msg is not None:
|
||||
nb_conversion_check_text = error_msg
|
||||
nb_conversion_ret_code = 1
|
||||
else:
|
||||
notebook_converter.convert(
|
||||
task=target_task,
|
||||
code=code,
|
||||
stdout=result_stdout,
|
||||
outfile=implementation.workspace_path / "main.ipynb",
|
||||
use_debug_flag=DS_RD_SETTING.sample_data_by_LLM,
|
||||
)
|
||||
|
||||
sample_submission_check = True
|
||||
test_eval = get_test_eval()
|
||||
if (sample_submission_file_name := test_eval.get_sample_submission_name(self.scen.competition)) is not None:
|
||||
# check whether code ever opens the sample submission file
|
||||
if (implementation.workspace_path / "trace.log").exists():
|
||||
opened_trace_lines = [
|
||||
line
|
||||
for line in (implementation.workspace_path / "trace.log").read_text().splitlines()
|
||||
if "openat" in line and sample_submission_file_name in line
|
||||
]
|
||||
if len(opened_trace_lines) > 0:
|
||||
stdout += f"Code opened the sample submission file '{sample_submission_file_name}' during execution.\n Reject the implementation!\n"
|
||||
sample_submission_check = False
|
||||
|
||||
result_stdout = remove_eda_part(result_stdout)
|
||||
if result.exit_code != 0:
|
||||
stdout += f"Code failed to run. Please check the stdout:\n Following the stdout of the debug mode run:\n{result_stdout.strip()}\n"
|
||||
else:
|
||||
stdout += f"Code ran successfully.\n Following the stdout of the debug mode run:\n{result_stdout.strip()}\n"
|
||||
if DS_RD_SETTING.sample_data_by_LLM:
|
||||
debug_time, full_estimated_time = None, None
|
||||
if match := re.search(r"debug_time:\s*(\d+(?:.\d+)?)", result_stdout, re.DOTALL):
|
||||
debug_time = float(match.group(1))
|
||||
if match := re.search(r"estimated_time:\s*(\d+(?:.\d+)?)", result_stdout, re.DOTALL):
|
||||
full_estimated_time = float(match.group(1))
|
||||
if debug_time is not None and full_estimated_time is not None:
|
||||
stdout += f"Debug mode ran in {debug_time:.2f} seconds, estimated full run time is {full_estimated_time:.2f} seconds. The estimated time is {full_estimated_time / env.conf.running_timeout_period * 100:.2f}% the debug time."
|
||||
else:
|
||||
stdout += "Debug mode did not provide debug_time or estimated_time, it's a buggy implementation.\n"
|
||||
|
||||
score_fp = implementation.workspace_path / "scores.csv"
|
||||
score_ret_code = 0
|
||||
score_check_text = ""
|
||||
if not score_fp.exists():
|
||||
score_check_text = "[Error] Metrics file (scores.csv) is not generated!"
|
||||
score_ret_code = 1
|
||||
else:
|
||||
try:
|
||||
score_df = pd.read_csv(score_fp, index_col=0)
|
||||
model_set_in_scores = set(score_df.index)
|
||||
|
||||
# Check model names (index)
|
||||
if not score_df.index.is_unique:
|
||||
score_check_text += "\n[Error] The file 'scores.csv' contains duplicate model names."
|
||||
score_ret_code = 1
|
||||
if "ensemble" not in model_set_in_scores:
|
||||
score_check_text += "\n[Error] The file 'scores.csv' doesn't contain the ensemble model."
|
||||
score_ret_code = 1
|
||||
if score_ret_code != 0:
|
||||
score_check_text += f"The dataframe in file 'scores.csv' is:\n{score_df}"
|
||||
|
||||
# Check metric name (columns) - case insensitive
|
||||
if [col.lower() for col in score_df.columns.tolist()] != [self.scen.metric_name.lower()]:
|
||||
score_check_text += f"\n[Error] The scores dataframe does not contain the correct column names.\nCorrect columns is: ['{self.scen.metric_name}']\nBut got: {score_df.columns.tolist()}"
|
||||
score_ret_code = 1
|
||||
|
||||
# Check if scores contain NaN (values)
|
||||
if score_df.isnull().values.any():
|
||||
nan_locations = score_df[score_df.isnull().any(axis=1)]
|
||||
score_check_text += f"\n[Error] The scores dataframe contains NaN values at the following locations:\n{nan_locations}"
|
||||
score_ret_code = 1
|
||||
|
||||
except Exception as e:
|
||||
score_check_text += f"\n[Error] in checking the scores.csv file: {e}\nscores.csv's content:\n-----\n{score_fp.read_text()}\n-----"
|
||||
score_ret_code = 1
|
||||
|
||||
test_eval = get_test_eval()
|
||||
if DS_RD_SETTING.sample_data_by_LLM and test_eval.enabled(self.scen.competition):
|
||||
submission_check_out, submission_ret_code = test_eval.valid(self.scen.competition, implementation)
|
||||
stdout += f"\n### Submission check:\n{submission_check_out}\nIf Submission check returns a 'Submission is valid' or similar message, despite some warning messages, you should still consider the submission as valid and give a positive final decision. "
|
||||
elif not test_eval.is_sub_enabled(self.scen.competition):
|
||||
submission_ret_code = 0
|
||||
else:
|
||||
# Check submission file
|
||||
base_check_code = T(".eval_tests.submission_format_test", ftype="txt").r()
|
||||
implementation.inject_files(**{"test/submission_format_test.py": base_check_code})
|
||||
# stdout += "----Submission Check 1-----\n"
|
||||
submission_result = implementation.run(env=env, entry="python test/submission_format_test.py")
|
||||
submission_check_out = submission_result.stdout
|
||||
submission_ret_code = submission_result.exit_code
|
||||
stdout += "\n" + submission_check_out
|
||||
|
||||
if not isinstance(implementation, FBWorkspace):
|
||||
eda_output = None
|
||||
else:
|
||||
eda_output = implementation.file_dict.get("EDA.md", None)
|
||||
|
||||
# extract enable_mcp_documentation_search from data science configuration
|
||||
enable_mcp_documentation_search = DS_RD_SETTING.enable_mcp_documentation_search
|
||||
|
||||
queried_similar_successful_knowledge = (
|
||||
queried_knowledge.task_to_similar_task_successful_knowledge[target_task.get_task_information()]
|
||||
if queried_knowledge is not None
|
||||
else []
|
||||
)
|
||||
|
||||
system_prompt = T(".prompts:pipeline_eval.system").r(
|
||||
is_sub_enabled=test_eval.is_sub_enabled(self.scen.competition),
|
||||
debug_mode=DS_RD_SETTING.sample_data_by_LLM,
|
||||
enable_mcp_documentation_search=enable_mcp_documentation_search,
|
||||
mle_check=DS_RD_SETTING.sample_data_by_LLM,
|
||||
queried_similar_successful_knowledge=queried_similar_successful_knowledge,
|
||||
)
|
||||
user_prompt = T(".prompts:pipeline_eval.user").r(
|
||||
scenario=self.scen.get_scenario_all_desc(eda_output=eda_output),
|
||||
task_desc=target_task.get_task_information(),
|
||||
stdout=stdout.strip(),
|
||||
spec=T("scenarios.data_science.share:component_spec.Pipeline").r(
|
||||
metric_name=self.scen.metric_name,
|
||||
enable_notebook_conversion=DS_RD_SETTING.enable_notebook_conversion,
|
||||
),
|
||||
code=implementation.file_dict["main.py"],
|
||||
)
|
||||
wfb = build_cls_from_json_with_retry(
|
||||
PipelineSingleFeedback,
|
||||
system_prompt=system_prompt,
|
||||
user_prompt=user_prompt,
|
||||
init_kwargs_update_func=PipelineSingleFeedback.val_and_update_init_dict,
|
||||
)
|
||||
|
||||
# judge whether we should perform documentation search
|
||||
do_documentation_search = enable_mcp_documentation_search and wfb.requires_documentation_search
|
||||
|
||||
if do_documentation_search:
|
||||
# Use MCPAgent for clean, user-friendly interface
|
||||
try:
|
||||
# Create agent targeting Context7 service - model config comes from mcp_config.json
|
||||
doc_agent = DocAgent()
|
||||
|
||||
# Synchronous query - perfect for evaluation context
|
||||
if wfb.error_message: # Type safety check
|
||||
context7_result = doc_agent.query(query=wfb.error_message)
|
||||
|
||||
if context7_result:
|
||||
logger.info("Context7: Documentation search completed successfully")
|
||||
wfb.error_message += f"\n\n### API Documentation Reference:\nThe following API documentation was retrieved based on the error. This provides factual information about API changes or parameter specifications only:\n\n{context7_result}"
|
||||
else:
|
||||
logger.warning("Context7: Documentation search failed or no results found")
|
||||
else:
|
||||
logger.warning("Context7: No error message to search for")
|
||||
|
||||
# TODO: confirm what exception will be raised when timeout
|
||||
# except concurrent.futures.TimeoutError:
|
||||
# logger.error("Context7: Query timed out after 180 seconds")
|
||||
except Exception as e:
|
||||
error_msg = str(e) if str(e) else type(e).__name__
|
||||
logger.error(f"Context7: Query failed - {error_msg}")
|
||||
|
||||
if score_ret_code != 0 and wfb.final_decision is True:
|
||||
wfb.final_decision = False
|
||||
wfb.return_checking += "\n" + score_check_text
|
||||
if submission_ret_code != 0 and wfb.final_decision is True:
|
||||
wfb.final_decision = False
|
||||
wfb.return_checking += "\nSubmission file check failed."
|
||||
if sample_submission_check is False and wfb.final_decision is True:
|
||||
wfb.final_decision = False
|
||||
wfb.return_checking += (
|
||||
"\nSample submission file check failed. Code should not open the sample submission file."
|
||||
)
|
||||
if nb_conversion_ret_code != 0 and wfb.final_decision is True:
|
||||
wfb.final_decision = False
|
||||
wfb.return_checking += "\n" + nb_conversion_check_text
|
||||
return wfb
|
||||
@@ -0,0 +1,94 @@
|
||||
import hashlib
|
||||
from pathlib import Path
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def calculate_md5(file_path):
|
||||
with open(file_path, "rb") as f:
|
||||
file_hash = hashlib.md5(f.read()).hexdigest()
|
||||
return file_hash
|
||||
|
||||
|
||||
if Path("scores.csv").exists():
|
||||
file_md5 = calculate_md5("scores.csv")
|
||||
else:
|
||||
print("Warning: scores.csv does not exist. MD5 check will be skipped.")
|
||||
file_md5 = None
|
||||
|
||||
"""
|
||||
find . | grep -i sample | grep -i submission | grep -v sample_submission.csv | grep -v zip_files | grep -v 'sample/'
|
||||
./denoising-dirty-documents/sampleSubmission.csv
|
||||
./the-icml-2013-whale-challenge-right-whale-redux/sampleSubmission.csv
|
||||
./text-normalization-challenge-russian-language/ru_sample_submission_2.csv.zip
|
||||
./text-normalization-challenge-russian-language/ru_sample_submission_2.csv
|
||||
./random-acts-of-pizza/sampleSubmission.csv
|
||||
./text-normalization-challenge-english-language/en_sample_submission_2.csv.zip
|
||||
./text-normalization-challenge-english-language/en_sample_submission_2.csv
|
||||
./detecting-insults-in-social-commentary/sample_submission_null.csv
|
||||
"""
|
||||
|
||||
# Find sample submission file dynamically
|
||||
input_dir = Path('{% include "scenarios.data_science.share:scen.input_path" %}')
|
||||
sample_submission_files = list(input_dir.glob("*sample_submission*.csv")) + list(
|
||||
input_dir.glob("*sampleSubmission*.csv")
|
||||
) + list(input_dir.glob("*randomPredictions*.tsv"))
|
||||
|
||||
if not sample_submission_files:
|
||||
print(f'Error: No sample submission file found in {% include "scenarios.data_science.share:scen.input_path" %}')
|
||||
sample_submission_name = None
|
||||
SAMPLE_SUBMISSION_PATH = None
|
||||
else:
|
||||
sample_submission_name = sample_submission_files[0].name
|
||||
SAMPLE_SUBMISSION_PATH = str(sample_submission_files[0])
|
||||
print(f"Using sample submission file: {sample_submission_name}")
|
||||
|
||||
if SAMPLE_SUBMISSION_PATH is not None and not Path(SAMPLE_SUBMISSION_PATH).exists():
|
||||
print(f"Error: {sample_submission_name} not found at {SAMPLE_SUBMISSION_PATH}")
|
||||
|
||||
if not Path("submission.csv").exists():
|
||||
print("Error: submission.csv not found")
|
||||
|
||||
if SAMPLE_SUBMISSION_PATH is not None and Path(SAMPLE_SUBMISSION_PATH).exists() and Path("submission.csv").exists():
|
||||
sample_submission = pd.read_csv(SAMPLE_SUBMISSION_PATH)
|
||||
our_submission = pd.read_csv("submission.csv")
|
||||
|
||||
success = True
|
||||
print(f"Columns in {sample_submission_name}:", sample_submission.columns)
|
||||
print("Columns in our_submission.csv:", our_submission.columns)
|
||||
|
||||
for col in sample_submission.columns:
|
||||
if col not in our_submission.columns:
|
||||
success = False
|
||||
print(f"Column {col} not found in submission.csv")
|
||||
|
||||
if success:
|
||||
print(f"submission.csv's columns aligns with {sample_submission_name} .")
|
||||
else:
|
||||
print(f"submission.csv's columns does not align with {sample_submission_name} .")
|
||||
|
||||
|
||||
def print_first_rows(file_path, file_name, num_rows=5):
|
||||
print(f"\nFirst {num_rows} rows of {file_name}:")
|
||||
try:
|
||||
with open(file_path, "r") as file:
|
||||
for i, line in enumerate(file):
|
||||
if i < num_rows:
|
||||
print(line.strip())
|
||||
else:
|
||||
break
|
||||
except FileNotFoundError:
|
||||
print(f"Error: {file_name} not found.")
|
||||
|
||||
|
||||
print_first_rows(SAMPLE_SUBMISSION_PATH, sample_submission_name)
|
||||
print_first_rows("submission.csv", "submission.csv")
|
||||
|
||||
if file_md5 is not None:
|
||||
if calculate_md5("scores.csv") != file_md5:
|
||||
print("Warning: scores.csv has been rewritten in the test script!")
|
||||
else:
|
||||
print("Skipping comparison and preview due to missing files.")
|
||||
|
||||
print(
|
||||
f"\nPlease Checked the content of the submission file(submission.csv should has the same format with {sample_submission_name} but might not the same index with {sample_submission_name}). "
|
||||
)
|
||||
@@ -0,0 +1,8 @@
|
||||
from rdagent.components.coder.CoSTEER.task import CoSTEERTask
|
||||
|
||||
|
||||
# Because we use isinstance to distinguish between different types of tasks, we need to use sub classes to represent different types of tasks
|
||||
class PipelineTask(CoSTEERTask):
|
||||
def __init__(self, name: str = "Pipeline", package_info: str | None = None, *args, **kwargs) -> None:
|
||||
super().__init__(name=name, *args, **kwargs)
|
||||
self.package_info = package_info
|
||||
@@ -0,0 +1,347 @@
|
||||
pipeline_coder:
|
||||
system: |-
|
||||
You are a grandmaster-level data scientist and machine learning engineer with deep expertise in statistics, mathematics, and computer science.
|
||||
Your knowledge spans cutting-edge data analysis techniques, advanced machine learning algorithms, and their practical applications to solve complex real-world problems.
|
||||
Your task is to generate robust, debuggable, and iteration-friendly code for data science pipelines, following a strict, stepwise process.
|
||||
|
||||
**Important Context**: You are working on sample datasets and your code will go through automated iterations. Design your code to be iteration-friendly with comprehensive print statements and clear debugging information to facilitate the automatic improvement process.
|
||||
|
||||
# Task Description
|
||||
{{ task_desc }}
|
||||
|
||||
## The runtime environment your code will running on
|
||||
{{ runtime_environment }}
|
||||
|
||||
{% if package_info is not none %}
|
||||
To help you write the runnable code, the user has provided the package information which contains the package names and versions.
|
||||
You should be careful about the package versions, as the code will be executed in the environment with the specified version and the api might be different from the latest version.
|
||||
The user might provide the packages the environment doesn't have, you should avoid using any of them.
|
||||
## Package Information
|
||||
{{ package_info }}
|
||||
{% endif %}
|
||||
|
||||
## Hyperparameters Specification
|
||||
Follow the hyperparameter choices if they are specified in the task description, unless they are unreasonable or incorrect.
|
||||
In this case, refer to the guidelines below for appropriate adjustments:
|
||||
{% include "scenarios.data_science.share:spec.hyperparameter" %}
|
||||
|
||||
# Specification your code should follow
|
||||
{{ spec }}
|
||||
|
||||
{% if queried_former_failed_knowledge|length != 0 %}
|
||||
## Previous Failed Attempts
|
||||
{% for former_failed_knowledge in queried_former_failed_knowledge %} Attempt {{ loop.index }}:
|
||||
=====Code:=====
|
||||
{{ former_failed_knowledge.implementation.all_codes }}
|
||||
=====Feedback:=====
|
||||
{{ former_failed_knowledge.feedback }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
# Workflow Overview
|
||||
You must complete the following stages in order.
|
||||
|
||||
## Data Loading
|
||||
- Load the dataset strictly from `{% include "scenarios.data_science.share:scen.input_path" %}` as described in the **Data Folder Description**. DO NOT attempt to load data from the current directory (`./`).
|
||||
- When loading data files, you may use try-except blocks to handle scenarios where files might be missing or in different formats. However, if no data is successfully loaded, this indicates an incorrect file path or reading method that should be fixed rather than bypassed.
|
||||
- **Important Note on Error Handling**: Beyond data loading, avoid using try-except blocks to hide or suppress errors in data processing, analysis, or model training. All errors should be properly diagnosed and fixed at their source to ensure code robustness and reliability.
|
||||
|
||||
## Exploratory Data Analysis (EDA) (Required)
|
||||
Please follow this systematic methodology (in the required schema) for your analysis.
|
||||
1. Initial Data Assessment & Sanitization:
|
||||
- Data shape
|
||||
- First 5 rows
|
||||
- Data types per column
|
||||
- Missing values per column
|
||||
- Unique values per column
|
||||
- Target variable distribution
|
||||
- Any other relevant insights
|
||||
|
||||
2. Detailed Feature Analysis (A Non-Exhaustive Guide):
|
||||
For Numerical & Categorical Features:
|
||||
- Central Tendency & Dispersion
|
||||
- Distribution Shape & Imbalance
|
||||
- Outliers & Anomalies
|
||||
- Cardinality & Granularity
|
||||
For Text Features:
|
||||
- Text Granularity & Scale
|
||||
- Core Content & Topicality
|
||||
- Linguistic Structure & Style
|
||||
- Vocabulary Richness & Redundancy
|
||||
|
||||
3. The EDA part should be drafted in plain text sending to standard output with command print or other similar functions with no more than ten thousand characters in the following schema:
|
||||
=== Start of EDA part ===
|
||||
{EDA content}
|
||||
=== End of EDA part ===
|
||||
User will use the following code to match: re.search(r"(.*?)=== Start of EDA part ===(.*)=== End of EDA part ===", stdout, re.DOTALL).groups()[1]
|
||||
- An evaluation agent will help to check whether the EDA part is added correctly.
|
||||
- During the EDA part, you should try to avoid any irrelevant information sending to the standard output.
|
||||
{% include "scenarios.data_science.share:guidelines.coding" %}
|
||||
|
||||
{% if enable_model_dump %}
|
||||
## Model Dumping
|
||||
{% include "components.coder.data_science.share.prompts:dump_model_coder.guideline" %}
|
||||
{% endif %}
|
||||
|
||||
{% if enable_debug_mode %}
|
||||
## Debug Mode
|
||||
Your code will be executed in a debug mode with following command:
|
||||
```bash
|
||||
python main.py --debug
|
||||
```
|
||||
Please simulate the following code to check whether the code is running in debug mode:
|
||||
```python
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--debug', action='store_true', help='Run in debug mode')
|
||||
args = parser.parse_args()
|
||||
DEBUG = False
|
||||
if args.debug:
|
||||
DEBUG = True
|
||||
```
|
||||
In debug mode, you should only sample ten percent of the training data and run the minimum epochs to quickly test the correctness of the code.
|
||||
In debug mode, you should implement a timer to measure the time taken for your debug configuration and estimate the time required for the full run. Your timer should only measure the time taken for the training part, not the data loading or feature engineering part.
|
||||
For example:
|
||||
```python
|
||||
# Read data, feature engineering, etc.
|
||||
start_time = time.time()
|
||||
# Train your model
|
||||
end_time = time.time()
|
||||
debug_time = end_time - start_time
|
||||
# post processing, saving model, etc.
|
||||
```
|
||||
In debug mode, your code should run faster, so the environment will set a shorter time limit than the standard time limit for your code.
|
||||
For example, you can sample ten percent of the training data and run for one epoch, then the full run with ten epochs will take one hundred times the time taken for the debug run. The scale is calculated by yourself depending on the data sampling and epoch number you choose. If your full run enables early stopping, the scale should be smaller considering the early stopping will stop the training earlier than the full epochs.
|
||||
Be careful about the train-valid split strategy. Stratified related split is highly risk since the data has some categories with only one sample. If you use Stratified related split, you should consider using a try-except block to catch the error and use a different split strategy if the error occurs. Example code:
|
||||
```python
|
||||
try:
|
||||
fold_indices = StratifiedKFold(...).split(train_X, train_y) or StratifiedShuffleSplit or StratifiedSubsetSampler etc.
|
||||
except Exception as e:
|
||||
fold_indices = KFold(...).split(train_X, train_y) or other split strategy
|
||||
```
|
||||
You should sample the data after train valid split. When you split the data after sampling, you might get a class with only one sample which might cause the split strategy to fail.
|
||||
Your debug code should run exactly the same as the full run, except for the data sampling and epoch number, to ensure the correctness of the code.
|
||||
You should print total time and estimated time in standard output using print function in the following schema:
|
||||
=== Start of Debug Information ===
|
||||
debug_time: time_taken_for_debug_run_in_seconds (e.g., 'debug_time: 10.0')
|
||||
estimated_time: estimated_time_for_full_run_in_seconds (e.g., 'estimated_time: 100.0')
|
||||
=== End of Debug Information ===
|
||||
User will use the following code to match: re.search(r"(.*?)=== Start of Debug Information ===(.*)=== End of Debug Information ===", stdout, re.DOTALL).groups()[1]
|
||||
Notice, data sampling should only be applied in debug mode. Always use the full data in the full run!
|
||||
Example code:
|
||||
```python
|
||||
if args.debug:
|
||||
sample_size = int(0.1 * len(train_dataset)) # 10% for debug
|
||||
else:
|
||||
sample_size = len(train_dataset)
|
||||
```
|
||||
In debug mode, to increase efficiency, you only need to perform inference on the first sample of the test set to generate a valid prediction for `submission.csv`. For all other samples in the test set, you should use a placeholder value (e.g., 0 or a default value) to fill the prediction column. This ensures that the generated `submission.csv` has the same number of rows as the full run and passes the format check.
|
||||
Example code:
|
||||
```python
|
||||
all_preds = []
|
||||
for i, batch in enumerate(test_loader):
|
||||
# In debug mode, use placeholders for all batches after the first one to improve efficiency.
|
||||
if args.debug and i > 0:
|
||||
# The shape and data type of the placeholder must match the model's actual output.
|
||||
# Here, we assume `predictions` is a NumPy array.
|
||||
placeholder = np.zeros_like(predictions)
|
||||
all_preds.append(placeholder)
|
||||
continue
|
||||
|
||||
# In full mode, or for the first batch in debug mode, perform actual model inference.
|
||||
predictions = model.predict(batch)
|
||||
all_preds.append(predictions)
|
||||
|
||||
# final_predictions = np.concatenate(all_preds)
|
||||
# ... then create and save submission.csv
|
||||
```
|
||||
You should be very careful about the label classes number in the debug mode. The label classes should be the same as the full run even when you are in the debug mode. The label classes number is often used to build the model.
|
||||
{% endif %}
|
||||
|
||||
## General Guidelines
|
||||
1. Code correctness is the top priority. Ensure your code is runnable and produces the expected output even if some task requirements are not fully met because the task itself might contain some errors like the wrong package name or wrong package function names.
|
||||
2. Use the print() function for all output; do not use the logging module.
|
||||
3. **Avoid all hard-coded values (e.g., fixed dataset sizes)**. Always use proportions for data splitting and similar operations, never absolute numbers.
|
||||
4. Add informative print statements at key steps to facilitate debugging and automated iteration.
|
||||
5. For model training, use reasonable epoch numbers. ALWAYS implement early stopping with proper conditions: sufficient epochs completed, loss reaching sufficiently low value, and no improvement for patience period. Save best model checkpoints based on validation performance.
|
||||
6. Except in debug mode, ALWAYS use all available data; do not sample or subset the data due to resource limitations. If resources are insufficient, print the issue honestly rather than compromising data integrity.
|
||||
7. Do not use tqdm or similar progress bar tools.
|
||||
8. **Try-except blocks are ONLY allowed when reading files. If no files are successfully read, it indicates incorrect file paths or reading methods, not a try-except issue. Try-except is PROHIBITED elsewhere in the code. Assert statements are PROHIBITED throughout the entire code.**
|
||||
9. ATTENTION: ALWAYS use the best saved model (not necessarily final epoch) for predictions. **NEVER create dummy/placeholder submissions (e.g., all 1s, random values)**. If training fails, report failure honestly rather than generating fake submission files.
|
||||
10. You should ALWAYS generate the complete code rather than partial code.
|
||||
11. If the task contains any user instructions, you must strictly follow them. User instructions have the highest priority and should be followed even if they conflict with other specifications or guidelines.
|
||||
12. Strictly follow all specifications and general guidelines described above.
|
||||
|
||||
### Output Format
|
||||
{% if out_spec %}
|
||||
{{ out_spec }}
|
||||
{% else %}
|
||||
Please response the code in the following json format. Here is an example structure for the JSON output:
|
||||
{
|
||||
"code": "The Python code as a string."
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
user: |-
|
||||
# Competition Information
|
||||
{{ competition_info }}
|
||||
|
||||
# Data Folder Description (All path are relative to the data folder, i.e. "{% include "scenarios.data_science.share:scen.input_path" %}")
|
||||
{{ folder_spec }}
|
||||
|
||||
{% if latest_code %}
|
||||
# Former code
|
||||
```
|
||||
{{ latest_code }}
|
||||
```
|
||||
{% if latest_code_feedback is not none %}
|
||||
## Feedback to former code
|
||||
{{ latest_code_feedback }}
|
||||
|
||||
## Improvement Planning
|
||||
Before modifying the code, carefully analyze the feedback and identify no more than three key areas requiring changes. Plan your modifications strategically:
|
||||
1. Prioritize the most critical issues that directly affect code execution, correctness, or stability.
|
||||
2. Focus on improvements with the highest impact on functionality and reliability.
|
||||
3. Preserve existing working components. Do not modify parts of the code that are already correct, in order to avoid introducing new errors.
|
||||
|
||||
The previous version of the code contained errors. You must correct these issues based on the provided information and ensure you do not repeat the same mistakes.
|
||||
|
||||
{% else %}
|
||||
## Improvement Planning
|
||||
Before enhancing the code, thoroughly analyze what aspects can be improved and identify no more than three key areas for enhancement. Plan your improvements strategically:
|
||||
1. Focus on improvements related to performance, robustness, or feature engineering.
|
||||
2. Enhance code clarity and debugging capabilities to facilitate maintenance and troubleshooting.
|
||||
3. Optimize model configuration or validation strategy to improve overall effectiveness.
|
||||
|
||||
The previous version of the code is correct. You should improve the code based on the provided task while ensuring that unrelated parts remain unchanged.
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
pipeline_eval:
|
||||
system: |-
|
||||
{% include "scenarios.data_science.share:scen.role" %}
|
||||
You will be provided with:
|
||||
1. A detailed competition scenario description.
|
||||
2. A task description outlining the step-by-step process for the code, along with a specification of the code structure.
|
||||
3. A code implementation and its execution output.
|
||||
Your task is to rigorously evaluate the code implementation against the provided scenario and task description, ensuring it meets all requirements, adheres to the specified structure, and executes successfully.
|
||||
|
||||
## Evaluation Aspects
|
||||
|
||||
### Execution Success
|
||||
- Goal: Ensure the code executes successfully without any errors.
|
||||
- Notes:
|
||||
- Model performance is not evaluated in this step; focus solely on successful execution.
|
||||
- Warnings are acceptable if they do not interfere with successful code execution.
|
||||
- If the code execute successfully:
|
||||
- Proceed to Step 2.
|
||||
- If the code does not execute successfully:
|
||||
- Set the "final_decision" to false.
|
||||
{% if enable_mcp_documentation_search %}
|
||||
- Given that my package/environment is fixed and unchangeable, first you should go through the code and the execution output,if the problem could be solved by looking up the official documentation to confirm feature/API availability, compatible usage, or official alternatives in the fixed environment, set the "requires_documentation_search" to true.
|
||||
{% endif %}
|
||||
- Write complete analysis in the "execution" field.
|
||||
|
||||
### Competition Alignment
|
||||
- Goal: Confirm strict adherence to the competition's evaluation rules and experimental setup.
|
||||
- Guidelines:
|
||||
- Analyze whether the experimental setup and code may cause misalignment between validation and test performance.
|
||||
- Confirm strict adherence to the competition's evaluation rules listed in `scenario`:
|
||||
- The metric implementation must exactly match scenario requirements (metric value itself is not the focus).
|
||||
- Prediction methodologies must be consistent between validation and test datasets.
|
||||
- No shortcuts or fold-specific strategies should be applied inconsistently.
|
||||
- Check for corner-case consistency.
|
||||
- Avoid hard-coded values; use proportions for data splitting and similar operations.
|
||||
- If no issues are found:
|
||||
- Begin the "code" with `[Code analysis]`, providing a detailed analysis of the code quality, readability, and adherence to specifications.
|
||||
- If discrepancies or risks are found:
|
||||
- Set the "final_decision" to false.
|
||||
- Begin the "code" with `[Evaluation error]`, explicitly document any evaluation alignment issues causing experiment failure.
|
||||
|
||||
{% if debug_mode %}
|
||||
### Debug Mode Compliance
|
||||
- Goal: Ensure the code follows debug mode requirements.
|
||||
- Guidelines:
|
||||
- Sufficient debugging information (print statements, clear error messages) should be included to facilitate automatic improvement processes.
|
||||
- The code should be executed in debug mode with the command `python main.py --debug`.
|
||||
- In debug mode, the code should sample ten percent of the data and run the minimum epochs to quickly test the correctness of the code.
|
||||
- Check whether the code follows these requirements. If not, emphasize it in your feedback and reject this implementation.
|
||||
- Execution time and estimated time for the full run should be checked. Estimated time should not be too large to finish in the given time limit.
|
||||
- Consider the early stopping mechanism in the code. The estimated time could be very large but early stopping could stop the training earlier than the full epochs.
|
||||
- Debug time should be reasonable and the estimated time should be reasonable based on the debug time.
|
||||
- Data sampling should only be applied in debug mode. Always use the full data in the full run.
|
||||
- The label classes number should be the same as the full run even in debug mode.
|
||||
- If the code passes this step: Proceed to Next Aspects.
|
||||
- If the code does not pass this step: Clearly document the debug mode compliance issues and reject the implementation.{% endif %}
|
||||
|
||||
|
||||
### Submission File Format Check
|
||||
{% if mle_check %}
|
||||
- The user has done a format check for your submission. Since you didn't sample any test data, your debug mode output should be the same format as the full run.
|
||||
- The user will put the check result in the "Submission check" section of the execution output.
|
||||
- If the submission check returns a 'Submission is valid' or similar message, despite some warning messages, you should give the conclusion that the code executed successfully. If no other code related issues are found, set the "final_decision" to true.
|
||||
- If the submission check returns an error message, you should set the "final_decision" to false and clearly document the issues in the "return_checking" field.
|
||||
{% elif is_sub_enabled %}
|
||||
- Goal: Verify that the code correctly generates the final submission in the expected format and that the submission is authentic.
|
||||
- Guidelines:
|
||||
- The submission file must strictly match the required structure (correct columns, index format, data types). The index names and column names must be identical to the format specified in the Competition Information's '====== Submission Format ======' section.
|
||||
- Rigorously verify that the submission file was produced by genuine model inference and successful code execution, not by cheating, fallback or exception-handling mechanisms.
|
||||
- The submission must be generated from genuine model predictions using the best saved model—never empty, constant, random, or hard-coded values.
|
||||
- Submissions must reflect authentic model outputs; any form of fabrication, cheating, or simulated results is strictly prohibited and grounds for rejection.
|
||||
- Cross-check both code logic and stdout to ensure predictions originate from real model inference, not from error recovery or placeholder code paths.
|
||||
- Only check the format of the submission since only part of the data is provided; the submission might have a different index than expected due to data sampling.
|
||||
- Verify honest failure reporting if training issues occur.
|
||||
- If the code passes this step, Finalize evaluation.
|
||||
- If the code does not pass this step:
|
||||
- Set the "final_decision" to false and clearly document the issues in the "return_checking" field.
|
||||
{% else %}
|
||||
Submission File Format Check is not conducted since no target submission format is provided. You should consider this submission file is valid.
|
||||
{% endif %}
|
||||
|
||||
{% if queried_similar_successful_knowledge|length != 0 %}
|
||||
### Step 6: Similar Successful Implementations to help Code Improvement
|
||||
The user has done several similar tasks and get some successful implementations. These code might not be implemented to the same task, but they are similar to your task and they might work well on your dataset.
|
||||
Please refer to these successful implementation and provide your suggestions in your response on how to correct your current code based on these successful implementations.
|
||||
## Successful Implementations for Similar Tasks
|
||||
====={% for similar_successful_knowledge in queried_similar_successful_knowledge %} Similar Task {{ loop.index }}:=====
|
||||
{{ similar_successful_knowledge.target_task.get_task_information() }}
|
||||
=====Code:=====
|
||||
{{ similar_successful_knowledge.implementation.all_codes }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
## Output Format
|
||||
Please respond with your feedback in the following JSON format without anything else.
|
||||
```json
|
||||
{
|
||||
{% if enable_mcp_documentation_search %}
|
||||
"requires_documentation_search": <true/false>,
|
||||
{% endif %}"execution": "Describe whether the code executed successfully. Include any errors or issues encountered, and append all error messages and full traceback details without summarizing or omitting any information. If errors occurred, analyze the root causes: (1) Are they fundamental algorithmic/approach issues, or (2) Implementation details that can be easily fixed, or (3) Environment/dependency problems?",
|
||||
"return_checking": "Examine the generated files by cross-referencing the code logic and stdout output. Verify: (1) Format matches required submission format (index, column names, CSV content); (2) **File generation authenticity**: Is the file genuinely produced by successful model execution, or is it a result of exception handling/fallback mechanisms? Cite specific code sections and stdout evidence.",
|
||||
"code": "Begin explicitly with [Code analysis] or [Evaluation error]. Provide structured analysis: (1) **Technical Appropriateness**: Does the chosen approach (algorithms, data processing, validation strategy) match this problem's data characteristics and competition requirements? (2) **Effective Components**: What specific parts work well and why are they effective for this problem type? (3) **Issues & Improvements**: Identify concrete problems and suggest actionable improvement directions (without providing actual code). (4) **Code Quality**: Assess readability, structure, and adherence to specifications.",
|
||||
{% if enable_mcp_documentation_search %}
|
||||
"error_message": "If the code execution has problems, extract the error information in the following format, otherwise set to empty string: ### TRACEBACK: <full relevant traceback extracted from execution output> ### SUPPLEMENTARY_INFO: <only if TRACEBACK is unclear - copy exact code fragments: import statements, variable=value assignments, function calls with parameters as they appear in code>",
|
||||
{% endif %}"final_decision": <true/false>
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
user: |-
|
||||
# Competition Information
|
||||
{{ scenario }}
|
||||
|
||||
# Task Description
|
||||
{{ task_desc }}
|
||||
|
||||
## Task Specification for Code Structure
|
||||
{{ spec }}
|
||||
|
||||
# Code
|
||||
```
|
||||
{{ code }}
|
||||
```
|
||||
|
||||
## Execution Output
|
||||
```
|
||||
{{ stdout }}
|
||||
```
|
||||
@@ -0,0 +1,15 @@
|
||||
# CoSTEER
|
||||
|
||||
- subworkspace使用主experiment_workspace `RD-Agent/rdagent/scenarios/data_science/experiment/experiment.py`
|
||||
|
||||
## evolving_strategy ( implement_one_task() )
|
||||
|
||||
1. xxxTask (in exp.py)
|
||||
- spec
|
||||
- description
|
||||
2.
|
||||
|
||||
## evaluator
|
||||
|
||||
1. queried_knowledge部分 共用
|
||||
2. eval_test脚本
|
||||
@@ -0,0 +1,242 @@
|
||||
"""
|
||||
|
||||
Loop should not large change exclude
|
||||
- Action Choice[current data loader & spec]
|
||||
- other should share
|
||||
- Propose[choice] => Task[Choice] => CoSTEER =>
|
||||
-
|
||||
|
||||
Extra feature:
|
||||
- cache
|
||||
|
||||
|
||||
File structure
|
||||
- ___init__.py: the entrance/agent of coder
|
||||
- evaluator.py
|
||||
- conf.py
|
||||
- exp.py: everything under the experiment, e.g.
|
||||
- Task
|
||||
- Experiment
|
||||
- Workspace
|
||||
- test.py
|
||||
- Each coder could be tested.
|
||||
"""
|
||||
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEERMultiEvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.evolving_strategy import (
|
||||
MultiProcessEvolvingStrategy,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.knowledge_management import (
|
||||
CoSTEERQueriedKnowledge,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import (
|
||||
DSCoderCoSTEERSettings,
|
||||
get_ds_env,
|
||||
)
|
||||
from rdagent.components.coder.data_science.raw_data_loader.eval import (
|
||||
DataLoaderCoSTEEREvaluator,
|
||||
)
|
||||
from rdagent.components.coder.data_science.raw_data_loader.exp import DataLoaderTask
|
||||
from rdagent.components.coder.data_science.share.ds_costeer import DSCoSTEER
|
||||
from rdagent.core.exception import CoderError
|
||||
from rdagent.core.experiment import FBWorkspace
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.ret import PythonAgentOut
|
||||
from rdagent.utils.agent.tpl import T
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
|
||||
class DataLoaderMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
|
||||
def implement_one_task(
|
||||
self,
|
||||
target_task: DataLoaderTask,
|
||||
queried_knowledge: CoSTEERQueriedKnowledge | None = None,
|
||||
workspace: FBWorkspace | None = None,
|
||||
prev_task_feedback: CoSTEERSingleFeedback | None = None,
|
||||
) -> dict[str, str]:
|
||||
# return a workspace with "load_data.py", "spec/load_data.md" inside
|
||||
# assign the implemented code to the new workspace.
|
||||
competition_info = self.scen.get_scenario_all_desc(eda_output=workspace.file_dict.get("EDA.md", None))
|
||||
data_folder_info = self.scen.processed_data_folder_description
|
||||
data_loader_task_info = target_task.get_task_information()
|
||||
|
||||
queried_similar_successful_knowledge = (
|
||||
queried_knowledge.task_to_similar_task_successful_knowledge[data_loader_task_info]
|
||||
if queried_knowledge is not None
|
||||
else []
|
||||
)
|
||||
queried_former_failed_knowledge = (
|
||||
queried_knowledge.task_to_former_failed_traces[data_loader_task_info]
|
||||
if queried_knowledge is not None
|
||||
else []
|
||||
)
|
||||
queried_former_failed_knowledge = (
|
||||
[
|
||||
knowledge
|
||||
for knowledge in queried_former_failed_knowledge[0]
|
||||
if knowledge.implementation.file_dict.get("load_data.py") != workspace.file_dict.get("load_data.py")
|
||||
],
|
||||
queried_former_failed_knowledge[1],
|
||||
)
|
||||
|
||||
# 1. specifications
|
||||
# TODO: We may move spec into a separated COSTEER task
|
||||
if DS_RD_SETTING.spec_enabled:
|
||||
if "spec/data_loader.md" not in workspace.file_dict: # Only generate the spec once
|
||||
system_prompt = T(".prompts:spec.system").r(
|
||||
runtime_environment=self.scen.get_runtime_environment(),
|
||||
task_desc=data_loader_task_info,
|
||||
competition_info=competition_info,
|
||||
folder_spec=data_folder_info,
|
||||
)
|
||||
data_loader_prompt = T(".prompts:spec.user.data_loader").r(
|
||||
latest_spec=workspace.file_dict.get("spec/data_loader.md")
|
||||
)
|
||||
feature_prompt = T(".prompts:spec.user.feature").r(
|
||||
latest_spec=workspace.file_dict.get("spec/feature.md")
|
||||
)
|
||||
model_prompt = T(".prompts:spec.user.model").r(latest_spec=workspace.file_dict.get("spec/model.md"))
|
||||
ensemble_prompt = T(".prompts:spec.user.ensemble").r(
|
||||
latest_spec=workspace.file_dict.get("spec/ensemble.md")
|
||||
)
|
||||
workflow_prompt = T(".prompts:spec.user.workflow").r(
|
||||
latest_spec=workspace.file_dict.get("spec/workflow.md")
|
||||
)
|
||||
|
||||
spec_session = APIBackend().build_chat_session(session_system_prompt=system_prompt)
|
||||
|
||||
data_loader_spec = spec_session.build_chat_completion(user_prompt=data_loader_prompt)
|
||||
feature_spec = spec_session.build_chat_completion(user_prompt=feature_prompt)
|
||||
model_spec = spec_session.build_chat_completion(user_prompt=model_prompt)
|
||||
ensemble_spec = spec_session.build_chat_completion(user_prompt=ensemble_prompt)
|
||||
workflow_spec = spec_session.build_chat_completion(user_prompt=workflow_prompt)
|
||||
else:
|
||||
data_loader_spec = workspace.file_dict["spec/data_loader.md"]
|
||||
feature_spec = workspace.file_dict["spec/feature.md"]
|
||||
model_spec = workspace.file_dict["spec/model.md"]
|
||||
ensemble_spec = workspace.file_dict["spec/ensemble.md"]
|
||||
workflow_spec = workspace.file_dict["spec/workflow.md"]
|
||||
|
||||
# 2. code
|
||||
system_prompt = T(".prompts:data_loader_coder.system").r(
|
||||
task_desc=data_loader_task_info,
|
||||
queried_similar_successful_knowledge=queried_similar_successful_knowledge,
|
||||
queried_former_failed_knowledge=queried_former_failed_knowledge[0],
|
||||
out_spec=PythonAgentOut.get_spec(),
|
||||
)
|
||||
code_spec = (
|
||||
data_loader_spec
|
||||
if DS_RD_SETTING.spec_enabled
|
||||
else T("scenarios.data_science.share:component_spec.general").r(
|
||||
spec=T("scenarios.data_science.share:component_spec.DataLoadSpec").r(),
|
||||
test_code=(DIRNAME / "eval_tests" / "data_loader_test.txt").read_text(),
|
||||
)
|
||||
)
|
||||
user_prompt = T(".prompts:data_loader_coder.user").r(
|
||||
competition_info=competition_info,
|
||||
code_spec=code_spec,
|
||||
folder_spec=data_folder_info,
|
||||
latest_code=workspace.file_dict.get("load_data.py"),
|
||||
latest_code_feedback=prev_task_feedback,
|
||||
)
|
||||
|
||||
for _ in range(5):
|
||||
data_loader_code = PythonAgentOut.extract_output(
|
||||
APIBackend().build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
)
|
||||
)
|
||||
if data_loader_code != workspace.file_dict.get("load_data.py"):
|
||||
break
|
||||
else:
|
||||
user_prompt = user_prompt + "\nPlease avoid generating same code to former code!"
|
||||
else:
|
||||
raise CoderError("Failed to generate a new data loader code.")
|
||||
|
||||
return (
|
||||
{
|
||||
"spec/data_loader.md": data_loader_spec,
|
||||
"spec/feature.md": feature_spec,
|
||||
"spec/model.md": model_spec,
|
||||
"spec/ensemble.md": ensemble_spec,
|
||||
"spec/workflow.md": workflow_spec,
|
||||
"load_data.py": data_loader_code,
|
||||
}
|
||||
if DS_RD_SETTING.spec_enabled
|
||||
else {
|
||||
"load_data.py": data_loader_code,
|
||||
}
|
||||
)
|
||||
|
||||
def assign_code_list_to_evo(self, code_list: list[dict[str, str]], evo):
|
||||
"""
|
||||
Assign the code list to the evolving item.
|
||||
|
||||
The code list is aligned with the evolving item's sub-tasks.
|
||||
If a task is not implemented, put a None in the list.
|
||||
"""
|
||||
for index in range(len(evo.sub_tasks)):
|
||||
if code_list[index] is None:
|
||||
continue
|
||||
if evo.sub_workspace_list[index] is None:
|
||||
# evo.sub_workspace_list[index] = FBWorkspace(target_task=evo.sub_tasks[index])
|
||||
evo.sub_workspace_list[index] = evo.experiment_workspace
|
||||
evo.sub_workspace_list[index].inject_files(**code_list[index])
|
||||
return evo
|
||||
|
||||
|
||||
class DataLoaderCoSTEER(DSCoSTEER):
|
||||
def __init__(
|
||||
self,
|
||||
scen: Scenario,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
settings = DSCoderCoSTEERSettings()
|
||||
eva = CoSTEERMultiEvaluator(
|
||||
DataLoaderCoSTEEREvaluator(scen=scen), scen=scen
|
||||
) # Please specify whether you agree running your eva in parallel or not
|
||||
es = DataLoaderMultiProcessEvolvingStrategy(scen=scen, settings=settings)
|
||||
|
||||
super().__init__(
|
||||
*args,
|
||||
settings=settings,
|
||||
eva=eva,
|
||||
es=es,
|
||||
evolving_version=2,
|
||||
scen=scen,
|
||||
max_loop=DS_RD_SETTING.coder_max_loop,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
def develop(self, exp):
|
||||
new_exp = super().develop(exp)
|
||||
|
||||
env = get_ds_env(
|
||||
extra_volumes={
|
||||
f"{DS_RD_SETTING.local_data_path}/{self.scen.competition}": T(
|
||||
"scenarios.data_science.share:scen.input_path"
|
||||
).r()
|
||||
},
|
||||
running_timeout_period=self.scen.real_full_timeout(),
|
||||
)
|
||||
|
||||
stdout = new_exp.experiment_workspace.execute(env=env, entry=f"python test/data_loader_test.py")
|
||||
match = re.search(r"(.*?)=== Start of EDA part ===(.*)=== End of EDA part ===", stdout, re.DOTALL)
|
||||
eda_output = match.groups()[1] if match else None
|
||||
if eda_output is not None:
|
||||
new_exp.experiment_workspace.inject_files(**{"EDA.md": eda_output})
|
||||
else:
|
||||
eda_output = "No EDA output."
|
||||
new_exp.experiment_workspace.inject_files(**{"EDA.md": eda_output})
|
||||
return new_exp
|
||||
@@ -0,0 +1,94 @@
|
||||
# tess successfully running.
|
||||
# (GPT) if it aligns with the spec & rationality of the spec.
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEEREvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.knowledge_management import (
|
||||
CoSTEERQueriedKnowledgeV2,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import get_ds_env
|
||||
from rdagent.components.coder.data_science.utils import remove_eda_part
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
DataLoaderEvalFeedback = CoSTEERSingleFeedback
|
||||
|
||||
|
||||
class DataLoaderCoSTEEREvaluator(CoSTEEREvaluator):
|
||||
def evaluate(
|
||||
self,
|
||||
target_task: Task,
|
||||
implementation: FBWorkspace,
|
||||
gt_implementation: FBWorkspace,
|
||||
queried_knowledge: CoSTEERQueriedKnowledgeV2 = None,
|
||||
**kwargs,
|
||||
) -> DataLoaderEvalFeedback:
|
||||
target_task_information = target_task.get_task_information()
|
||||
if (
|
||||
queried_knowledge is not None
|
||||
and target_task_information in queried_knowledge.success_task_to_knowledge_dict
|
||||
):
|
||||
return queried_knowledge.success_task_to_knowledge_dict[target_task_information].feedback
|
||||
elif queried_knowledge is not None and target_task_information in queried_knowledge.failed_task_info_set:
|
||||
return DataLoaderEvalFeedback(
|
||||
execution="This task has failed too many times, skip implementation.",
|
||||
return_checking="This task has failed too many times, skip implementation.",
|
||||
code="This task has failed too many times, skip implementation.",
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
env = get_ds_env(
|
||||
extra_volumes={self.scen.debug_path: T("scenarios.data_science.share:scen.input_path").r()},
|
||||
running_timeout_period=self.scen.real_debug_timeout(),
|
||||
)
|
||||
|
||||
# TODO: do we need to clean the generated temporary content?
|
||||
fname = "test/data_loader_test.py"
|
||||
test_code = (DIRNAME / "eval_tests" / "data_loader_test.txt").read_text()
|
||||
implementation.inject_files(**{fname: test_code})
|
||||
result = implementation.run(env=env, entry=f"python {fname}")
|
||||
stdout = result.stdout
|
||||
ret_code = result.exit_code
|
||||
match = re.search(r"(.*?)=== Start of EDA part ===(.*)=== End of EDA part ===(.*)", stdout, re.DOTALL)
|
||||
stdout_part_1, eda_output, stdout_part_2 = match.groups() if match else (stdout, None, "")
|
||||
stdout = stdout_part_1 + stdout_part_2
|
||||
if eda_output is not None and len(eda_output.split(" ")) > 10000:
|
||||
eda_output += "Length of EDA output is too long, truncated. Please reject this implementation and motivate it to reduce the length of EDA output."
|
||||
|
||||
if "main.py" in implementation.file_dict and ret_code == 0:
|
||||
workflow_stdout = implementation.execute(env=env, entry="python main.py")
|
||||
workflow_stdout = remove_eda_part(workflow_stdout)
|
||||
else:
|
||||
workflow_stdout = None
|
||||
|
||||
system_prompt = T(".prompts:data_loader_eval.system").r(
|
||||
task_desc=target_task.get_task_information(),
|
||||
test_code=test_code,
|
||||
code=implementation.file_dict["load_data.py"],
|
||||
workflow_stdout=workflow_stdout,
|
||||
workflow_code=implementation.all_codes,
|
||||
)
|
||||
user_prompt = T(".prompts:data_loader_eval.user").r(
|
||||
stdout=stdout,
|
||||
eda_output=eda_output,
|
||||
workflow_stdout=workflow_stdout,
|
||||
)
|
||||
|
||||
fb = build_cls_from_json_with_retry(
|
||||
DataLoaderEvalFeedback,
|
||||
system_prompt=system_prompt,
|
||||
user_prompt=user_prompt,
|
||||
init_kwargs_update_func=DataLoaderEvalFeedback.val_and_update_init_dict,
|
||||
)
|
||||
fb.final_decision = fb.final_decision and ret_code == 0
|
||||
|
||||
return fb
|
||||
@@ -0,0 +1,83 @@
|
||||
"""
|
||||
Tests for `load_data` in load_data.py
|
||||
"""
|
||||
|
||||
import pickle
|
||||
|
||||
import pandas as pd
|
||||
from load_data import load_data
|
||||
|
||||
import sys
|
||||
import reprlib
|
||||
from joblib.memory import MemorizedFunc
|
||||
|
||||
|
||||
def get_original_code(func):
|
||||
if isinstance(func, MemorizedFunc):
|
||||
return func.func.__code__
|
||||
return func.__code__
|
||||
|
||||
|
||||
def debug_info_print(func):
|
||||
aRepr = reprlib.Repr()
|
||||
aRepr.maxother=300
|
||||
def wrapper(*args, **kwargs):
|
||||
original_code = get_original_code(func)
|
||||
def local_trace(frame, event, arg):
|
||||
if event == "return" and frame.f_code == original_code:
|
||||
print("\n" + "="*20 + "Running data_load code, local variable values:" + "="*20)
|
||||
for k, v in frame.f_locals.items():
|
||||
printed = aRepr.repr(v)
|
||||
print(f"{k}:\n {printed}")
|
||||
print("="*20 + "Local variable values end" + "="*20)
|
||||
return local_trace
|
||||
|
||||
sys.settrace(local_trace)
|
||||
try:
|
||||
return func(*args, **kwargs)
|
||||
finally:
|
||||
sys.settrace(None)
|
||||
return wrapper
|
||||
|
||||
X, y, X_test, test_ids = debug_info_print(load_data)()
|
||||
|
||||
|
||||
def get_length(data):
|
||||
return data.shape[0] if hasattr(data, 'shape') else len(data)
|
||||
|
||||
|
||||
def get_width(data):
|
||||
return data.shape[1:] if hasattr(data, 'shape') else 1
|
||||
|
||||
|
||||
def get_column_list(data):
|
||||
return data.columns.tolist() if isinstance(data, pd.DataFrame) else None
|
||||
|
||||
assert X is not None, "Training data (X) is None."
|
||||
assert y is not None, "Training labels (y) are None."
|
||||
assert X_test is not None, "Test data (X_test) is None."
|
||||
assert test_ids is not None, "Test IDs (test_ids) are None."
|
||||
|
||||
assert get_length(X_test) == get_length(
|
||||
test_ids
|
||||
), f"Mismatch in length of test images and test IDs: X_test ({get_length(X_test)}) and test_ids ({get_length(test_ids)})"
|
||||
assert get_length(X) == get_length(
|
||||
y
|
||||
), f"Mismatch in length of training images and labels: X ({get_length(X)}) and y ({get_length(y)})"
|
||||
|
||||
assert get_length(X) != 0, f"Training data is empty."
|
||||
assert get_length(y) != 0, f"Training labels are empty."
|
||||
assert get_length(X_test) != 0, f"Test data is empty."
|
||||
|
||||
assert get_width(X) == get_width(
|
||||
X_test
|
||||
), "Mismatch in width of training and test data. Width means the number of features."
|
||||
|
||||
if isinstance(X, pd.DataFrame) and isinstance(X_test, pd.DataFrame):
|
||||
assert get_column_list(X) == get_column_list(X_test), "Mismatch in column names of training and test data."
|
||||
|
||||
assert get_width(X) == get_width(
|
||||
X_test
|
||||
), "Mismatch in width of training and test data. Width means the number of features."
|
||||
|
||||
print("Data loader test passed successfully. Length of test images matches length of test IDs.")
|
||||
@@ -0,0 +1,6 @@
|
||||
from rdagent.components.coder.CoSTEER.task import CoSTEERTask
|
||||
|
||||
|
||||
# Because we use isinstance to distinguish between different types of tasks, we need to use sub classes to represent different types of tasks
|
||||
class DataLoaderTask(CoSTEERTask):
|
||||
pass
|
||||
@@ -0,0 +1,402 @@
|
||||
|
||||
spec:
|
||||
system: |-
|
||||
You are a world-class data scientist and machine learning engineer with deep expertise in statistics, mathematics, and computer science.
|
||||
Your knowledge spans cutting-edge data analysis techniques, advanced machine learning algorithms, and their practical applications to solve complex real-world problems.
|
||||
|
||||
Currently, you are working on a Kaggle competition project.
|
||||
This project involves analyzing data and building models to beat other competitors, with the code being generated by large language models.
|
||||
|
||||
The runtime environment you are working in includes the following libraries and their respective versions:
|
||||
{{ runtime_environment }}
|
||||
|
||||
Your overall task is provided below:
|
||||
{{ task_desc }}
|
||||
|
||||
Your task is to write five specification texts (in markdown format) for the following tasks, based on the competition information provided
|
||||
- Data loading (and preprocessing)
|
||||
- Feature Engineering
|
||||
- Model Building
|
||||
- Ensemble
|
||||
- The overall workflow
|
||||
|
||||
The specifications for each step should be tailored to the competition information provided.
|
||||
|
||||
Your specification should consists two parts:
|
||||
1. The function definition in code format, including type annotations and a clear, complete docstring that describes the function's purpose, input parameters, return value, and any relevant exceptions.
|
||||
2. Additional information or notes that the coder should consider while implementing the function.
|
||||
|
||||
Your specifications should include only the function definition and docstring, without any code implementation or inline comments.
|
||||
|
||||
## Competition Information for This Task
|
||||
{{ competition_info }}
|
||||
|
||||
----------- Folder Description (All path are relative to the data folder) ---------
|
||||
- Ensure that all columns in sample_submission can be generated.
|
||||
{{ folder_spec }}
|
||||
|
||||
user:
|
||||
data_loader: |-
|
||||
Data loader specification text should follow these detailed requirements:
|
||||
1. Function Interface:
|
||||
- Function Name: `load_data`
|
||||
- Input: No input arguments.
|
||||
- Output:
|
||||
- `X` (DT, define based on competition information): Feature matrix for training data.
|
||||
- `y` (DT): Target vector for training data.
|
||||
- `X_test` (DT): Feature matrix for test data.
|
||||
- `test_ids` (DT): Identifiers for the test data.
|
||||
- Docstring Requirements:
|
||||
- Describe the purpose of the function.
|
||||
- Specify the data source location (`{% include "scenarios.data_science.share:scen.input_path" %}`).
|
||||
- Clearly define the structure and type of the output.
|
||||
- Inferred data shape to each input and output data variables. To uncertain dimension, use -1.
|
||||
2. Notes:
|
||||
- Update `DT` (data type) based on the specific competition dataset. This can include `pd.DataFrame`, `np.array`, `torch.Tensor`, etc.
|
||||
- Only set the DT of variables without inferring the shape of these variables since you don't know the shape of the data.
|
||||
|
||||
Responsibilities and notes of an implemented data loader that aligns with the generated specification.
|
||||
{% include "scenarios.data_science.share:component_spec.DataLoadSpec" %}
|
||||
|
||||
{% if latest_spec %}
|
||||
6. Former Specification:
|
||||
{{ latest_spec }}
|
||||
You should follow the provided specifications to improve this task.
|
||||
{% endif %}
|
||||
|
||||
## Output Format
|
||||
You should return the specification in markdown format directly, while the **function definition** within it should be in code format, tailored to the Competition Information, with detailed explanations provided in the docstring.
|
||||
|
||||
feature: |-
|
||||
Feature engineering specification text should adhere to the following requirements:
|
||||
1. Function Interface:
|
||||
- Function Name: `feat_eng`
|
||||
- Parameters:
|
||||
- `X` (DT): Train data to be transformed.
|
||||
- `y` (DT): Train label data.
|
||||
- `X_test` (DT): Test data.
|
||||
- Output:
|
||||
- `X_transformed` (DT): Transformed train data.
|
||||
- `y_transformed` (DT): Transformed train label data.
|
||||
- `X_test_transformed` (DT): Transformed test data.
|
||||
- Docstring Requirements:
|
||||
- Describe the purpose of the function.
|
||||
- Clarify the input parameters and their data types.
|
||||
- Define the structure and format of the output.
|
||||
- Inferred data shape to each input and output data variables. To uncertain dimension, use -1.
|
||||
|
||||
2. Precautions for Feature Engineering:
|
||||
- Well handle the shape of the data:
|
||||
- The sample size of the train data and the test data should be the same in all scenarios.
|
||||
- To some tabular or time-series data, you may add or remove some columns so your inferred column number may be unsure.
|
||||
- For scenarios where each dimension does not have a special meaning (like image, audio, and so on), the input shape and the output shape should be exactly the same in most cases unless there is a compelling reason to change them.
|
||||
- Integration with the Model Pipeline:
|
||||
- If feature engineering is deferred to the model pipeline for better overall performance, state explicitly that it will be handled at the model stage.
|
||||
- Model-related operations should not be implemented in this step. (e.g., it uses tools combined with models like torch.Dataset with rich data transformation/augmentation)
|
||||
- Otherwise, ensure this function applies all required transformations while avoiding data leakage.
|
||||
- General Considerations:
|
||||
- Ensure scalability for large datasets.
|
||||
- Handle missing values and outliers appropriately (e.g., impute, remove, or replace).
|
||||
- Ensure consistency between feature data types and transformations.
|
||||
- Prevent data leakage: Do not use information derived from the test set when transforming training data.
|
||||
- Domain-Specific Features:
|
||||
- Apply logic for competition-specific features (e.g., text vectorization, image augmentations, categorical encoding).
|
||||
|
||||
3. Code Standards:
|
||||
- Avoid using progress bars (e.g., `tqdm`) in the implementation.
|
||||
|
||||
4. Notes:
|
||||
- Align `DT` (data type) definitions with those in the Data Loader specification.
|
||||
- GPU and multiprocessing are available and are encouraged to use for accelerating transformations.
|
||||
- Only set the DT of variables without inferring the shape of these variables since you don't know the shape of the data.
|
||||
|
||||
{% if latest_spec %}
|
||||
5. Former Specification:
|
||||
{{ latest_spec }}
|
||||
You should follow the provided specifications to improve this task.
|
||||
{% endif %}
|
||||
|
||||
## Output Format
|
||||
You should return the specification in markdown format directly, while the **function definition** within it should be in code format, tailored to the Competition Information, with detailed explanations provided in the docstring.
|
||||
|
||||
model: |-
|
||||
Model building specification text should adhere to the following requirements:
|
||||
|
||||
1. Function Interface:
|
||||
- Function Name: `model_workflow`
|
||||
- Parameters:
|
||||
- `X` (DT): Training feature data.
|
||||
- `y` (DT): Training label data.
|
||||
- `val_X` (Optional[DT]): Validation feature data.
|
||||
- `val_y` (Optional[DT]): Validation label data.
|
||||
- `test_X` (Optional[DT]): Test feature data.
|
||||
- `hyper_params` (dict): Dictionary of hyperparameters for model configuration.
|
||||
- Output:
|
||||
- `pred_val` (Optional[DT]): Predictions on validation data.
|
||||
- `pred_test` (Optional[DT]): Predictions on test data.
|
||||
- `hyper_params` (dict): Updated dictionary of hyperparameters after training.
|
||||
- Docstring Requirements:
|
||||
- Describe the purpose of the function.
|
||||
- Clarify the input parameters and their data types.
|
||||
- Define the structure and format of the output.
|
||||
- Inferred data shape to each input and output data variables. To uncertain dimension, use -1.
|
||||
|
||||
2. Code Standards:
|
||||
- Do not use progress bars (e.g., `tqdm`) in the implementation.
|
||||
|
||||
3. Precautions:
|
||||
- Ensure input arrays (`X`, `y`, `val_X`, `val_y`, `test_X`) have consistent dimensions and shapes.
|
||||
- Use default values for hyperparameters if `hyper_params` is not provided.
|
||||
- Train the model on `X` and `y`.
|
||||
- Evaluate the model using `val_X` and `val_y` if validation data is available.
|
||||
- If `test_X` is provided, generate predictions for it.
|
||||
|
||||
4. Notes:
|
||||
- Align `DT` (data type) with the definitions used in Feature Engineering specifications.
|
||||
- The device has GPU support, so you are encouraged to use it for training if necessary to accelerate the process.
|
||||
- Some data transformations/augmentations can be included in this step (e.g., data tools provided by TensorFlow and Torch)
|
||||
|
||||
{% if latest_spec %}
|
||||
5. Former Specification:
|
||||
{{ latest_spec }}
|
||||
You should follow the provided specifications to improve this task.
|
||||
{% endif %}
|
||||
|
||||
## Output Format
|
||||
You should return the specification in markdown format directly, while the **function definition** within it should be in code format, tailored to the Competition Information, with detailed explanations provided in the docstring.
|
||||
|
||||
ensemble: |-
|
||||
Ensemble specification text adhere to the following requirements:
|
||||
1. Function Interface:
|
||||
- Function Name: `ensemble_workflow`
|
||||
- Parameters:
|
||||
- `test_preds_dict` (Dict[str, DT]): A dictionary of test predictions from different models. The key is the model file name.
|
||||
- `val_preds_dict` (Dict[str, DT]): A dictionary of validation predictions from different models. The key is the model file name.
|
||||
- `val_label` (DT): Validation label.
|
||||
- Output:
|
||||
- `final_pred` (DT): Ensemble prediction for the test data.
|
||||
- Docstring Requirements:
|
||||
- Describe the purpose of the function.
|
||||
- Clarify the input parameters and their data types.
|
||||
- Define the structure and format of the output.
|
||||
- Inferred data shape to each input and output data variables. To uncertain dimension, use -1.
|
||||
|
||||
2. Precautions:
|
||||
- Input Validation:
|
||||
- Ensure all predictions in `test_preds_dict` and `val_preds_dict` have consistent shapes and dimensions.
|
||||
- Verify that `val_label` is provided and matches the length of `val_preds_dict` predictions.
|
||||
- Handle empty or invalid inputs gracefully with appropriate error messages.
|
||||
- Metric Calculation and Storage:
|
||||
- Calculate the metric (mentioned in the evaluation section of the competition information) for each model and ensemble strategy on valid, and save the results in `scores.csv`, e.g.:
|
||||
```python
|
||||
scores = {}
|
||||
for model_name, val_pred in val_preds_dict.items():
|
||||
scores[model_name] = calculate_metric(val_label, val_pred)
|
||||
|
||||
...
|
||||
some code about ensemble strategy
|
||||
...
|
||||
ensemble_val_pred = ...
|
||||
|
||||
ensemble_score = calculate_metric(val_label, ensemble_val_pred)
|
||||
scores["ensemble"] = ensemble_score # Ensure "ensemble" is explicitly stored
|
||||
|
||||
scores_df = pd.DataFrame(scores.items(), columns=["Model", <metric_name>])
|
||||
scores_df.to_csv("scores.csv", index=False)
|
||||
```
|
||||
- Even if only one model is present, compute the ensemble score and store it under `"ensemble"`.
|
||||
|
||||
3. Code Standards:
|
||||
- Do not use progress bars (e.g., tqdm) in the code.
|
||||
|
||||
4. Notes:
|
||||
- Align `DT` (data type) definitions with those used in model specifications.
|
||||
- Ensure flexibility to handle multiple ensemble strategies based on competition requirements.
|
||||
- Only set the DT of variables without inferring the shape of these variables since you don't know the shape of the data.
|
||||
|
||||
{% if latest_spec %}
|
||||
5. Former Specification:
|
||||
{{ latest_spec }}
|
||||
You should follow the provided specifications to improve this task.
|
||||
{% endif %}
|
||||
|
||||
## Output Format
|
||||
You should return the specification in markdown format directly, while the **function definition** within it should be in code format, tailored to the Competition Information, with detailed explanations provided in the docstring.
|
||||
|
||||
workflow: |-
|
||||
{% include "scenarios.data_science.share:component_spec.Workflow" %}
|
||||
|
||||
{% if latest_spec %}
|
||||
7. Former Specification:
|
||||
{{ latest_spec }}
|
||||
You should follow the provided specifications to improve this task.
|
||||
{% endif %}
|
||||
|
||||
## Output Format
|
||||
You should return the specification in markdown format directly.
|
||||
You should create the rules based on the competition information instead of copying the requirements.
|
||||
|
||||
data_loader_coder:
|
||||
system: |-
|
||||
You are a world-class data scientist and machine learning engineer with deep expertise in statistics, mathematics, and computer science.
|
||||
Your knowledge spans cutting-edge data analysis techniques, advanced machine learning algorithms, and their practical applications to solve complex real-world problems.
|
||||
|
||||
## Task Description
|
||||
{{ task_desc }}
|
||||
|
||||
{% if queried_similar_successful_knowledge|length != 0 or queried_former_failed_knowledge|length != 0 %}
|
||||
## Relevant Information for This Task
|
||||
{% endif %}
|
||||
|
||||
{% if queried_similar_successful_knowledge|length != 0 %}
|
||||
--------- Successful Implementation Examples for Similar Task ---------
|
||||
====={% for similar_successful_knowledge in queried_similar_successful_knowledge %} Example {{ loop.index }}:=====
|
||||
{{ similar_successful_knowledge.target_task.get_task_information() }}
|
||||
=====Code:=====
|
||||
{{ similar_successful_knowledge.implementation.all_codes }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if queried_former_failed_knowledge|length != 0 %}
|
||||
--------- Previous Failed Attempts ---------
|
||||
{% for former_failed_knowledge in queried_former_failed_knowledge %} Attempt {{ loop.index }}:
|
||||
=====Code:=====
|
||||
{{ former_failed_knowledge.implementation.all_codes }}
|
||||
=====Feedback:=====
|
||||
{{ former_failed_knowledge.feedback }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
## Guidelines
|
||||
1. Ensure that the dataset is loaded strictly from `{% include "scenarios.data_science.share:scen.input_path" %}`, following the exact folder structure described in the **Data Folder Description**, and do not attempt to load data from the current directory (`./`).
|
||||
2. You should avoid using logging module to output information in your generated code, and instead use the print() function.
|
||||
3. You should use the following cache decorator to cache the results of the function:
|
||||
```python
|
||||
from joblib import Memory
|
||||
memory = Memory(location='{% include "scenarios.data_science.share:scen.cache_path" %}', verbose=0)
|
||||
@memory.cache```
|
||||
{% include "scenarios.data_science.share:guidelines.coding" %}
|
||||
|
||||
## Exploratory Data Analysis (EDA) part(Required):
|
||||
- Before returning the data, you should always add an EDA part describing the data to help the following steps understand the data better.
|
||||
- The EDA part should include but not limited in the following information in plain text:
|
||||
- The shape of the data.
|
||||
- The first 5 rows of the data.
|
||||
- The data types of each column.
|
||||
- The number of missing values in each column.
|
||||
- The number of unique values in each column.
|
||||
- The distribution of the target variable.
|
||||
- Any other information that you think is important for the following steps.
|
||||
- The EDA part should be drafted in plain text sending to standard output with command print or other similar functions with no more than ten thousand characters in the following schema:
|
||||
=== Start of EDA part ===
|
||||
{ You EDA output content }
|
||||
=== End of EDA part ===
|
||||
User will use the following code to match: re.search(r"(.*?)=== Start of EDA part ===(.*)=== End of EDA part ===", stdout, re.DOTALL).groups()[1]
|
||||
- An evaluation agent will help to check whether the EDA part is added correctly.
|
||||
- During the EDA part, you should try to avoid any irrelevant information sending to the standard output.
|
||||
|
||||
## Output Format
|
||||
{% if out_spec %}
|
||||
{{ out_spec }}
|
||||
{% else %}
|
||||
Please response the code in the following json format. Here is an example structure for the JSON output:
|
||||
{
|
||||
"code": "The Python code as a string."
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
user: |-
|
||||
--------- Competition Information ---------
|
||||
{{ competition_info }}
|
||||
|
||||
--------- Code Specification ---------
|
||||
{{ code_spec }}
|
||||
|
||||
--------- Data Folder Description (All path are relative to the data folder, i.e. "{% include "scenarios.data_science.share:scen.input_path" %}") ---------
|
||||
{{ folder_spec }}
|
||||
|
||||
{% if latest_code %}
|
||||
--------- Former code ---------
|
||||
{{ latest_code }}
|
||||
{% if latest_code_feedback is not none %}
|
||||
--------- Feedback to former code ---------
|
||||
{{ latest_code_feedback }}
|
||||
{% endif %}
|
||||
The former code contains errors. You should correct the code based on the provided information, ensuring you do not repeat the same mistakes.
|
||||
{% endif %}
|
||||
|
||||
You should strictly follow the code specifications provided by the specification to implement the function.
|
||||
|
||||
|
||||
data_loader_eval:
|
||||
system: |-
|
||||
You are a data scientist responsible for evaluating data loader code for a Kaggle-style machine learning competition project.
|
||||
|
||||
## Task Description
|
||||
{{ task_desc }}
|
||||
|
||||
## Data Loader Code
|
||||
The data loader code is located in `load_data.py`:
|
||||
```python
|
||||
{{ code }}
|
||||
```
|
||||
|
||||
## Testing Process
|
||||
The data loader is tested using the following script:
|
||||
```python
|
||||
{{ test_code }}
|
||||
```
|
||||
|
||||
{% if workflow_stdout is not none %}
|
||||
### Whole Workflow Consideration
|
||||
The data loader is part of the whole workflow. The user has executed the entire pipeline and provided additional stdout.
|
||||
|
||||
**Workflow Code:**
|
||||
{{ workflow_code }}
|
||||
|
||||
You should evaluate both the data loader test results and the overall workflow execution. **Approve the code only if both tests pass.**
|
||||
{% endif %}
|
||||
|
||||
## Evaluation Criteria
|
||||
You will be given the standard output (`stdout`) from the data loader test and, if applicable, the workflow test.
|
||||
|
||||
## Exploratory Data Analysis (EDA) Part evaluation
|
||||
- The code has also generated some EDA output to help understand the data better.
|
||||
- The EDA part should be drafted in plain text sending to standard output with command print or other similar functions with no more than ten thousand characters in the following schema:
|
||||
=== Start of EDA part ===
|
||||
{ You EDA output content }
|
||||
=== End of EDA part ===
|
||||
User will use the following code to match: re.search(r"(.*?)=== Start of EDA part ===(.*)=== End of EDA part ===", stdout, re.DOTALL).groups()[1]
|
||||
- The EDA part should include but not limited in the following information in plain text:
|
||||
- The shape of the data.
|
||||
- The first 5 rows of the data.
|
||||
- The data types of each column.
|
||||
- The number of missing values in each column.
|
||||
- The number of unique values in each column.
|
||||
- The distribution of the target variable.
|
||||
- Any other information that you think is important for the following steps.
|
||||
You will be given the EDA output, your job is to check whether the output contains the required and sufficient information. If no EDA output is provided, you should consider it as a failure. Put this evaluation result in the return_checking part.
|
||||
|
||||
Your response must follow this structured JSON format:
|
||||
```json
|
||||
{
|
||||
"execution": "Describe how well the data loader executed, including any errors or issues encountered. Append all error messages and full traceback details without summarizing or omitting any information.",
|
||||
"return_checking": "Evaluate the correctness and integrity of the loaded data. Check for issues like missing values, incorrect data types, outliers, or formatting inconsistencies.",
|
||||
"code": "Assess code quality, readability, and adherence to best practices. Consider efficiency, including whether the code utilizes multi-threading or GPU acceleration for faster data loading.",
|
||||
"final_decision": <true/false>
|
||||
}
|
||||
```
|
||||
|
||||
user: |-
|
||||
--------- Data loader test stdout ---------
|
||||
{{ stdout }}
|
||||
--------- Data loader EDA stdout ---------
|
||||
{% if eda_output is not none %}
|
||||
{{ eda_output }}
|
||||
{% else %}
|
||||
No EDA output is provided.
|
||||
{% endif %}
|
||||
{% if workflow_stdout is not none %}
|
||||
--------- Whole workflow test stdout ---------
|
||||
{{ workflow_stdout }}
|
||||
{% endif %}
|
||||
@@ -0,0 +1,30 @@
|
||||
"""
|
||||
Helper functions for testing the raw_data_loader coder(CoSTEER-based) component.
|
||||
- Does the developer loop work correctly
|
||||
|
||||
It is NOT:
|
||||
- it is not interface unittest(i.e. workspace evaluator in the CoSTEER Loop)
|
||||
"""
|
||||
|
||||
from rdagent.components.coder.data_science.raw_data_loader import DataLoaderCoSTEER
|
||||
from rdagent.components.coder.data_science.raw_data_loader.exp import DataLoaderTask
|
||||
from rdagent.scenarios.data_science.experiment.experiment import DSExperiment
|
||||
from rdagent.scenarios.data_science.scen import KaggleScen
|
||||
|
||||
|
||||
def develop_one_competition(competition: str): # -> experiment
|
||||
scen = KaggleScen(competition=competition)
|
||||
data_loader_coder = DataLoaderCoSTEER(scen)
|
||||
|
||||
# Create the experiment
|
||||
dlt = DataLoaderTask(name="DataLoaderTask", description="")
|
||||
exp = DSExperiment(
|
||||
sub_tasks=[dlt],
|
||||
)
|
||||
|
||||
# Develop the experiment
|
||||
exp = data_loader_coder.develop(exp)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
develop_one_competition("aerial-cactus-identification")
|
||||
@@ -0,0 +1,37 @@
|
||||
"""
|
||||
Developers concentrating on writing documents for a workspace
|
||||
"""
|
||||
|
||||
from rdagent.core.developer import Developer
|
||||
from rdagent.core.experiment import Experiment, FBWorkspace
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.ret import MarkdownAgentOut
|
||||
from rdagent.utils.agent.tpl import T
|
||||
|
||||
|
||||
class DocDev(Developer[Experiment]):
|
||||
"""
|
||||
The developer is responsible for writing documents for a workspace.
|
||||
"""
|
||||
|
||||
def develop(self, exp: Experiment) -> None:
|
||||
"""
|
||||
Write documents for the workspace.
|
||||
"""
|
||||
ws: FBWorkspace = exp.experiment_workspace
|
||||
|
||||
file_li = [str(file.relative_to(ws.workspace_path)) for file in ws.workspace_path.rglob("*") if file.is_file()]
|
||||
|
||||
key_file_list = ["main.py", "scores.csv"]
|
||||
|
||||
system_prompt = T(".prompts:docdev.system").r()
|
||||
user_prompt = T(".prompts:docdev.user").r(
|
||||
file_li=file_li,
|
||||
key_files={f: (ws.workspace_path / f).read_text() for f in key_file_list},
|
||||
)
|
||||
|
||||
resp = APIBackend().build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt, system_prompt=system_prompt
|
||||
)
|
||||
markdown = MarkdownAgentOut.extract_output(resp)
|
||||
ws.inject_files(**{"README.md": markdown})
|
||||
@@ -0,0 +1,9 @@
|
||||
from rdagent.components.coder.CoSTEER import CoSTEER
|
||||
|
||||
|
||||
class DSCoSTEER(CoSTEER):
|
||||
def get_develop_max_seconds(self) -> int | None:
|
||||
"""
|
||||
The coder uses the scenario's real debug timeout as the maximum seconds for development.
|
||||
"""
|
||||
return int(self.scen.real_debug_timeout() * self.settings.max_seconds_multiplier)
|
||||
@@ -0,0 +1,176 @@
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import Literal
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.CoSTEER import CoSTEERMultiFeedback
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEEREvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import get_clear_ws_cmd, get_ds_env
|
||||
from rdagent.components.coder.data_science.utils import remove_eda_part
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
PipelineSingleFeedback = CoSTEERSingleFeedback
|
||||
PipelineMultiFeedback = CoSTEERMultiFeedback
|
||||
|
||||
NO_SUB = "<No submission.csv file found.>"
|
||||
NO_SCORE = "<No scores.csv file found.>"
|
||||
|
||||
|
||||
class ModelDumpEvaluator(CoSTEEREvaluator):
|
||||
"""This evaluator assumes that it runs after the model"""
|
||||
|
||||
def __init__(self, scen: Scenario, data_type: Literal["sample", "full"]):
|
||||
super().__init__(scen)
|
||||
self.data_type = data_type
|
||||
|
||||
def evaluate(
|
||||
self, target_task: Task, implementation: FBWorkspace, gt_implementation: FBWorkspace, *kargs, **kwargs
|
||||
) -> CoSTEERSingleFeedback:
|
||||
|
||||
model_folder = implementation.workspace_path / "models"
|
||||
# 1) Check if the model_folder is not empty
|
||||
if not model_folder.exists() or not any(model_folder.iterdir()):
|
||||
err_msg = "Model folder (`models` sub folder) is empty or does not exist. The model is not dumped."
|
||||
return CoSTEERSingleFeedback(
|
||||
execution=err_msg,
|
||||
return_checking=err_msg,
|
||||
code=err_msg,
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
data_source_path = (
|
||||
f"{DS_RD_SETTING.local_data_path}/{self.scen.competition}"
|
||||
if self.data_type == "full"
|
||||
else self.scen.debug_path
|
||||
)
|
||||
env = get_ds_env(
|
||||
extra_volumes={data_source_path: T("scenarios.data_science.share:scen.input_path").r()},
|
||||
running_timeout_period=(
|
||||
self.scen.real_full_timeout() if self.data_type == "full" else self.scen.real_debug_timeout()
|
||||
),
|
||||
)
|
||||
|
||||
# 2) check the result and stdout after reruning the model.
|
||||
|
||||
# Read the content of files submission.csv and scores.csv before execution
|
||||
submission_content_before = (
|
||||
(implementation.workspace_path / "submission.csv").read_text()
|
||||
if (implementation.workspace_path / "submission.csv").exists()
|
||||
else NO_SUB
|
||||
)
|
||||
scores_content_before = (
|
||||
(implementation.workspace_path / "scores.csv").read_text()
|
||||
if (implementation.workspace_path / "scores.csv").exists()
|
||||
else NO_SCORE
|
||||
)
|
||||
|
||||
# Remove the files submission.csv and scores.csv
|
||||
implementation.execute(env=env, entry=get_clear_ws_cmd(stage="before_inference"))
|
||||
|
||||
# Execute the main script
|
||||
stdout = remove_eda_part(
|
||||
implementation.execute(env=env, entry="strace -e trace=file -f -o trace.log python main.py --inference")
|
||||
)
|
||||
|
||||
# walk model_folder and list the files
|
||||
model_folder_files = [
|
||||
str(file.relative_to(implementation.workspace_path)) for file in model_folder.iterdir() if file.is_file()
|
||||
]
|
||||
|
||||
opened_trace_lines = None
|
||||
if (implementation.workspace_path / "trace.log").exists():
|
||||
input_path = T("scenarios.data_science.share:scen.input_path").r()
|
||||
abs_input_path = str(Path(input_path).resolve())
|
||||
# matching path in string like `openat(AT_FDCWD, "/home/user/project/main.py", O_RDONLY) = 5`
|
||||
path_regex = re.compile(r'openat\(.+?,\s*"([^"]+)"')
|
||||
log_content = (implementation.workspace_path / "trace.log").read_text()
|
||||
|
||||
opened_files = set()
|
||||
for line in log_content.splitlines():
|
||||
if "openat" not in line or (abs_input_path not in line and input_path not in line):
|
||||
continue
|
||||
|
||||
match = path_regex.search(line)
|
||||
if match:
|
||||
full_path = Path(match.group(1)).resolve()
|
||||
if str(full_path).startswith(abs_input_path):
|
||||
opened_files.add(Path(data_source_path).resolve() / full_path.relative_to(abs_input_path))
|
||||
|
||||
from rdagent.scenarios.data_science.scen.utils import FileTreeGenerator
|
||||
|
||||
tree_gen = FileTreeGenerator(allowed_paths=opened_files) # pass opened files filter
|
||||
opened_trace_lines = tree_gen.generate_tree(Path(data_source_path).resolve())
|
||||
# Limitation: training and test are expected to be different files.
|
||||
|
||||
# this will assert the generation of necessary files
|
||||
for f in ["submission.csv", "scores.csv"]:
|
||||
if not (implementation.workspace_path / f).exists():
|
||||
err_msg = f"{f} does not exist. The model is not dumped. Make sure that the required files, like submission.csv and scores.csv, are created even if you bypass the model training step by loading the saved model file directly."
|
||||
return CoSTEERSingleFeedback(
|
||||
execution=err_msg,
|
||||
return_checking=err_msg,
|
||||
code=err_msg,
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
# Check if scores contain NaN (values)
|
||||
score_df = pd.read_csv((implementation.workspace_path / "scores.csv"), index_col=0)
|
||||
if score_df.isnull().values.any():
|
||||
nan_locations = score_df[score_df.isnull().any(axis=1)]
|
||||
err_msg = f"\n[Error] The scores dataframe contains NaN values at the following locations:\n{nan_locations}"
|
||||
return CoSTEERSingleFeedback(
|
||||
execution=err_msg,
|
||||
return_checking=err_msg,
|
||||
code=err_msg,
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
submission_content_after = (
|
||||
(implementation.workspace_path / "submission.csv").read_text()
|
||||
if (implementation.workspace_path / "submission.csv").exists()
|
||||
else NO_SUB
|
||||
)
|
||||
scores_content_after = (
|
||||
(implementation.workspace_path / "scores.csv").read_text()
|
||||
if (implementation.workspace_path / "scores.csv").exists()
|
||||
else NO_SCORE
|
||||
)
|
||||
|
||||
system_prompt = T(".prompts:dump_model_eval.system").r()
|
||||
user_prompt = T(".prompts:dump_model_eval.user").r(
|
||||
stdout=stdout.strip(),
|
||||
code=implementation.all_codes,
|
||||
model_folder_files=model_folder_files,
|
||||
scores_content_before=scores_content_before,
|
||||
scores_content_after=scores_content_after,
|
||||
opened_trace_lines=opened_trace_lines,
|
||||
)
|
||||
|
||||
csfb = build_cls_from_json_with_retry(
|
||||
CoSTEERSingleFeedback,
|
||||
system_prompt=system_prompt,
|
||||
user_prompt=user_prompt,
|
||||
)
|
||||
|
||||
if DS_RD_SETTING.model_dump_check_level == "high":
|
||||
# Read the content of files submission.csv and scores.csv after execution
|
||||
# Check if the content has changed
|
||||
# excactly same checking. But it will take more user's time
|
||||
if scores_content_before != scores_content_after:
|
||||
return_msg = "\n[Error] The content of scores.csv has changed. Please check the code to ensure that the model is dumped correctly, and rerun the code to use the model directly without retraining it."
|
||||
return_msg += f"\nBefore:\n{scores_content_before}\nAfter:\n{scores_content_after}"
|
||||
if submission_content_before != submission_content_after:
|
||||
# If the scores file changes, display the two contents and append it into the return_checking
|
||||
return_msg = "[Error] The content of submission.csv has changed. Please check the code to ensure that the model is dumped correctly, and rerun the code to use the model directly without retraining it."
|
||||
csfb.return_checking = (csfb.return_checking or "") + return_msg
|
||||
return csfb
|
||||
@@ -0,0 +1,135 @@
|
||||
"""
|
||||
Handles conversion from a Python file to a Jupyter notebook.
|
||||
"""
|
||||
|
||||
import argparse
|
||||
from typing import Optional
|
||||
|
||||
import nbformat
|
||||
|
||||
from rdagent.components.coder.data_science.share.util import (
|
||||
extract_first_section_name_from_code,
|
||||
extract_function_body,
|
||||
split_code_and_output_into_sections,
|
||||
)
|
||||
from rdagent.core.experiment import Task
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.ret import MarkdownAgentOut
|
||||
from rdagent.utils.agent.tpl import T
|
||||
|
||||
|
||||
class NotebookConverter:
|
||||
"""
|
||||
Builder responsible for writing a Jupyter notebook for a workspace.
|
||||
"""
|
||||
|
||||
def validate_code_format(self, code: str) -> str | None:
|
||||
"""
|
||||
Returns None if the code format is valid, otherwise returns an error message.
|
||||
"""
|
||||
main_function_body = extract_function_body(code, "main")
|
||||
if not main_function_body:
|
||||
return "[Error] No main function found in the code. Please ensure that the main function is defined and contains the necessary print statements to divide sections."
|
||||
|
||||
found_section_name = extract_first_section_name_from_code(main_function_body)
|
||||
if not found_section_name:
|
||||
return "[Error] No sections found in the code. Expected to see 'print(\"Section: <section name>\")' as section dividers. Also make sure that they are actually run and not just comments."
|
||||
|
||||
return None
|
||||
|
||||
def convert(
|
||||
self,
|
||||
task: Optional[Task],
|
||||
code: str,
|
||||
stdout: str,
|
||||
outfile: Optional[str] = None,
|
||||
use_debug_flag: bool = False,
|
||||
) -> str:
|
||||
"""
|
||||
Build a notebook based on the current progression.
|
||||
"""
|
||||
# Handle argparse in the code to ensure it works in a notebook environment
|
||||
should_handle_argparse = "argparse" in code
|
||||
sections = split_code_and_output_into_sections(code=code, stdout=stdout)
|
||||
notebook = nbformat.v4.new_notebook()
|
||||
|
||||
# Use LLM to generate an intro cell for the notebook
|
||||
if task:
|
||||
system_prompt = T(".prompts:notebookconverter.system").r()
|
||||
user_prompt = T(".prompts:notebookconverter.user").r(
|
||||
plan=task.get_task_information(),
|
||||
code=code,
|
||||
)
|
||||
resp = APIBackend().build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt, system_prompt=system_prompt
|
||||
)
|
||||
intro_content = MarkdownAgentOut.extract_output(resp)
|
||||
notebook.cells.append(nbformat.v4.new_markdown_cell(intro_content))
|
||||
|
||||
if should_handle_argparse:
|
||||
# Remove extra `import sys` since it will be added for argparse handling
|
||||
if "import sys\n" in sections[0]["code"]:
|
||||
sections[0]["code"] = sections[0]["code"].replace("import sys\n", "")
|
||||
|
||||
# Add sys.argv modification for argparse handling
|
||||
sections[0]["code"] = (
|
||||
"\n".join(
|
||||
[
|
||||
"import sys",
|
||||
"# hack to allow argparse to work in notebook",
|
||||
('sys.argv = ["main.py", "--debug"]' if use_debug_flag else 'sys.argv = ["main.py"]'),
|
||||
]
|
||||
)
|
||||
+ "\n\n"
|
||||
+ sections[0]["code"].lstrip()
|
||||
)
|
||||
|
||||
for section in sections:
|
||||
# Create a markdown cell for the section name and comments
|
||||
markdown_content = ""
|
||||
if section["name"]:
|
||||
markdown_content += f"## {section['name']}\n"
|
||||
if section["comments"]:
|
||||
markdown_content += f"{section['comments']}\n"
|
||||
if markdown_content:
|
||||
notebook.cells.append(nbformat.v4.new_markdown_cell(markdown_content))
|
||||
|
||||
# Create a code cell for the section code and output
|
||||
if section["code"]:
|
||||
cell = nbformat.v4.new_code_cell(section["code"])
|
||||
if section["output"]:
|
||||
# For simplicity, treat all output as coming from stdout
|
||||
# TODO: support Jupyter kernel execution and handle outputs appropriately here
|
||||
cell.outputs = [nbformat.v4.new_output("stream", name="stdout", text=section["output"])]
|
||||
notebook.cells.append(cell)
|
||||
|
||||
# Save the notebook or return it as a string
|
||||
if outfile:
|
||||
with open((outfile), "w", encoding="utf-8") as f:
|
||||
nbformat.write(notebook, f)
|
||||
logger.info(f"Notebook written to {outfile}")
|
||||
|
||||
return nbformat.writes(notebook)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
converter = NotebookConverter()
|
||||
parser = argparse.ArgumentParser(description="Convert Python code to Jupyter notebook.")
|
||||
parser.add_argument("inputfile", type=str, help="Path to the input Python file.")
|
||||
parser.add_argument("outfile", type=str, help="Path to the output Notebook file.")
|
||||
parser.add_argument(
|
||||
"--stdout",
|
||||
type=str,
|
||||
default="",
|
||||
help="Standard output from the code execution.",
|
||||
)
|
||||
parser.add_argument("--debug", action="store_true", help="Use debug flag to modify sys.argv.")
|
||||
args = parser.parse_args()
|
||||
converter.convert(
|
||||
task=None,
|
||||
code=open(args.inputfile, "r").read(),
|
||||
stdout=args.stdout,
|
||||
outfile=args.outfile,
|
||||
use_debug_flag=False,
|
||||
)
|
||||
@@ -0,0 +1,123 @@
|
||||
dump_model_coder:
|
||||
guideline: |-
|
||||
Your code will be executed in a inference mode with following command:
|
||||
```bash
|
||||
python main.py --inference
|
||||
```
|
||||
Please dump the model in a "models/" subfolder in the first running, and the script rerun performs inference without needing to retrain the model when running the code again.
|
||||
In inference Mode, the script MUST NOT load any training data.
|
||||
If there are parameters generated from the training data that might be needed for inference on test data, please save them in the "models/" subfolder as well.
|
||||
If no test set is provided, reserve a portion of the data as your test set and save the generated test files in the models/ subfolder for use in submission and inference.
|
||||
Make sure that the required files, like submission.csv and scores.csv, are created without model training step through loading the saved model and test data file directly.
|
||||
|
||||
|
||||
dump_model_eval:
|
||||
system: |-
|
||||
You are a data scientist tasked with evaluating code generation. You've developed a Kaggle competition code that can produce a submission file.
|
||||
The code should follow the guideline below:
|
||||
{% include "components.coder.data_science.share.prompts:dump_model_coder.guideline" %}
|
||||
|
||||
You will receive the following information:
|
||||
- The implemented code
|
||||
- The stdout from running the code
|
||||
- The file list in "models/" subfolder
|
||||
- The scores.csv file generated during both training and inference (if it exists)
|
||||
|
||||
Focus on these aspects:
|
||||
- Check if the code saves the model in the "models/" subfolder.
|
||||
- Check if the code saves the test data in the "models/" subfolder when there is no test data specified.
|
||||
- Ensure that when the code is rerun in inference mode, it skips the training process and loads the model from the "models/" subfolder for direct inference.
|
||||
- Verify that there is no training activity in the output.
|
||||
- Verify that the script does not load the original training data.
|
||||
- Ensure that even if you skip the model training by loading saved models, the files like scores.csv and submission.csv are still correctly created.
|
||||
- The model's performance should remain consistent and not vary unreasonably between training and inference.
|
||||
|
||||
Please respond with your feedback in the following JSON format and order
|
||||
```json
|
||||
{
|
||||
"execution": "Describe whether the code executed successfully. Include any errors or issues encountered, and append all error messages and full traceback details without summarizing or omitting any information. Carefully check the stdout to ensure that when the code is rerun, it skips the training process and loads the model from the 'models/' subfolder for direct inference. Append the information that makes you think that the model is still being retrained when rerunning the code."
|
||||
"return_checking": "Verify the generated files include necessary files. Make sure scores.csv file does not change unreasonably between training and inference",
|
||||
"code": "The code has explicity dump the model into 'models/' subfolder; When the modes files are already in 'models/' subfolder, the code will explicity skip the training process.",
|
||||
"final_decision": <true or false in boolean type; only return true when ensuring that the code saves the model in a 'models/' subfolder, and the script rerun performs inference without needing to retrain the model.>
|
||||
}
|
||||
```
|
||||
|
||||
user: |-
|
||||
------------ The implemented code ------------
|
||||
{{code}}
|
||||
|
||||
------------ The stdout from running the code ------------
|
||||
{{stdout}}
|
||||
|
||||
------------ File opened by the code ------------
|
||||
{{opened_trace_lines}}
|
||||
|
||||
------------ The file list in "models/" subfolder ------------
|
||||
{% for f in model_folder_files %}
|
||||
- {{ f }}
|
||||
{% endfor %}
|
||||
|
||||
------------ The scores.csv file generated ------------
|
||||
# Training:
|
||||
{{scores_content_before}}
|
||||
|
||||
# Inference:
|
||||
{{scores_content_after}}
|
||||
|
||||
|
||||
docdev:
|
||||
system: |-
|
||||
{% include "scenarios.data_science.share:scen.role" %} Your task is to create documentation for a data science solution.
|
||||
|
||||
You will be given:
|
||||
- a list of files in the folder.
|
||||
- content from some important files.
|
||||
|
||||
Please explain the trained models in the "models/" folder. The training and inference processes are detailed in the `main.py` file. The models' evaluation results are in `scores.csv`. Please respond with a markdown file that includes the following information:
|
||||
- Explain the purpose of each model. If some models are part of a group (like those from cross-validation), describe them together.
|
||||
- Provide key details for each model group:
|
||||
- Important training parameters
|
||||
- Model details
|
||||
- Performance of each model
|
||||
|
||||
Be brief. Mention the file path when you introduce files.
|
||||
Don't introduce anything other than models.
|
||||
|
||||
{% include "utils.agent.tpl:MarkdownOut" %}
|
||||
|
||||
user: |-
|
||||
--------------- The file list in the workspace ---------------
|
||||
{% for f in file_li %}
|
||||
- {{ f }}
|
||||
{% endfor %}
|
||||
|
||||
--------------- File content of each file ---------------
|
||||
{% for fname, content in key_files.items() %}
|
||||
File Path: {{fname}}
|
||||
```
|
||||
{{content}}
|
||||
```
|
||||
{% endfor %}
|
||||
|
||||
notebookconverter:
|
||||
system: |-
|
||||
{% include "scenarios.data_science.share:scen.role" %} Your task is to provide a summary for a data science solution.
|
||||
|
||||
You will be given:
|
||||
- The original implementation plan for the script.
|
||||
- A Python script that contains code and output.
|
||||
|
||||
Your task is to generate markdown content that includes a title and a short paragraph summarizing the technique in model training, the type of model produced and any other noteworthy details in the solution.
|
||||
|
||||
The return content should be like the format below(Please note that "````" is used to avoid confliction of "```" in markdown file)
|
||||
````markdown
|
||||
# <The title of the notebook>
|
||||
<the content of markdown file>
|
||||
````
|
||||
|
||||
user: |-
|
||||
--------------- The implementation plan ---------------
|
||||
{{plan}}
|
||||
|
||||
--------------- The Python script content ---------------
|
||||
{{code}}
|
||||
@@ -0,0 +1,365 @@
|
||||
import ast
|
||||
import io
|
||||
import re
|
||||
import tokenize
|
||||
from itertools import zip_longest
|
||||
from typing import List, Optional, Set, Tuple, TypedDict
|
||||
|
||||
|
||||
class CodeSection(TypedDict):
|
||||
"""
|
||||
Represents a section of the original Python source code, to be converted to a notebook cell.
|
||||
"""
|
||||
|
||||
name: Optional[str]
|
||||
code: Optional[str]
|
||||
comments: Optional[str]
|
||||
output: Optional[str]
|
||||
|
||||
|
||||
def extract_function_body(source_code: str, function_name: str) -> Optional[str]:
|
||||
"""
|
||||
Extracts the body of a function from the source code.
|
||||
Returns None if the function is not found.
|
||||
|
||||
Assumption: The function is multiline and defined at the top level.
|
||||
"""
|
||||
tree = ast.parse(source_code)
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.FunctionDef) and node.name == function_name:
|
||||
lines = source_code.splitlines()
|
||||
start = node.body[0].lineno
|
||||
end = node.body[-1].end_lineno
|
||||
body_lines = lines[start - 1 : end]
|
||||
indent_level = len(body_lines[0]) - len(body_lines[0].lstrip())
|
||||
return "\n".join(line[indent_level:] for line in body_lines)
|
||||
return None
|
||||
|
||||
|
||||
def split_sections(
|
||||
text: str, section_header_regex: str, known_sections: Optional[list[str]] = None
|
||||
) -> tuple[Optional[str], list[str], list[str]]:
|
||||
"""
|
||||
Split text into sections based on the section headers.
|
||||
"""
|
||||
sections = []
|
||||
section_names = []
|
||||
current_section = []
|
||||
next_section_name_index = 0
|
||||
for line in text.splitlines():
|
||||
match = re.match(section_header_regex, line)
|
||||
extracted_section_name = match.group(1).strip() if match else None
|
||||
if extracted_section_name and (
|
||||
not known_sections
|
||||
or (
|
||||
next_section_name_index < len(known_sections)
|
||||
and extracted_section_name == known_sections[next_section_name_index]
|
||||
)
|
||||
):
|
||||
if current_section:
|
||||
sections.append("\n".join(current_section))
|
||||
current_section = []
|
||||
current_section.append(line)
|
||||
section_names.append(extracted_section_name)
|
||||
next_section_name_index += 1
|
||||
else:
|
||||
current_section.append(line)
|
||||
if current_section:
|
||||
sections.append("\n".join(current_section))
|
||||
|
||||
# If the first section does not match the header regex, treat it as a header section.
|
||||
header_section = None
|
||||
if sections and not re.search(section_header_regex, sections[0]):
|
||||
header_section = sections[0]
|
||||
sections = sections[1:]
|
||||
|
||||
return header_section, sections, section_names
|
||||
|
||||
|
||||
def split_code_sections(source_code: str) -> tuple[Optional[str], list[str]]:
|
||||
"""
|
||||
Split code into sections based on the section headers.
|
||||
"""
|
||||
return split_sections(source_code, r'^print\(["\']Section: (.+)["\']\)')
|
||||
|
||||
|
||||
def split_output_sections(stdout: str, known_sections: list[str]) -> tuple[Optional[str], list[str]]:
|
||||
"""
|
||||
Split output into sections based on the section headers.
|
||||
"""
|
||||
header_section, sections, _ = split_sections(stdout, r"^Section: (.+)", known_sections=known_sections)
|
||||
return header_section, sections
|
||||
|
||||
|
||||
def extract_comment_under_first_print(source_code) -> tuple[Optional[str], str]:
|
||||
"""
|
||||
Extract comments from the source code after the first print statement.
|
||||
"""
|
||||
lines = source_code.splitlines()
|
||||
lines_to_remove = set()
|
||||
all_comments = []
|
||||
|
||||
parsed = ast.parse(source_code)
|
||||
# Find the first print statement only
|
||||
first_print_lineno = None
|
||||
for node in ast.walk(parsed):
|
||||
if isinstance(node, ast.Expr) and isinstance(node.value, ast.Call):
|
||||
if getattr(node.value.func, "id", None) == "print":
|
||||
first_print_lineno = node.lineno
|
||||
break
|
||||
|
||||
if first_print_lineno is None:
|
||||
# No print statement found, return empty comments and original code
|
||||
return None, source_code
|
||||
|
||||
for i in range(first_print_lineno, len(lines)):
|
||||
stripped = lines[i].strip()
|
||||
if stripped.startswith("#"):
|
||||
comment_text = stripped.lstrip("# ").strip()
|
||||
all_comments.append(comment_text)
|
||||
lines_to_remove.add(i)
|
||||
elif stripped == "":
|
||||
continue
|
||||
elif i > first_print_lineno:
|
||||
break # stop after hitting actual code line
|
||||
|
||||
cleaned_lines = [line for idx, line in enumerate(lines) if idx not in lines_to_remove]
|
||||
cleaned_code = "\n".join(cleaned_lines)
|
||||
comments_str = "\n".join(all_comments) if all_comments else None
|
||||
|
||||
return comments_str, cleaned_code
|
||||
|
||||
|
||||
def extract_first_section_name_from_code(source_code):
|
||||
"""
|
||||
Extract the first section name from the source code.
|
||||
"""
|
||||
parsed = ast.parse(source_code)
|
||||
for node in ast.walk(parsed):
|
||||
if isinstance(node, ast.Expr) and isinstance(node.value, ast.Call):
|
||||
call = node.value
|
||||
if getattr(call.func, "id", None) == "print" and call.args:
|
||||
arg0 = call.args[0]
|
||||
if isinstance(arg0, ast.Constant) and isinstance(arg0.value, str):
|
||||
# Match "Section: ..." pattern
|
||||
m = re.match(r"Section:\s*(.+)", arg0.value)
|
||||
if m:
|
||||
return m.group(1).strip()
|
||||
return None
|
||||
|
||||
|
||||
def extract_first_section_name_from_output(stdout: str) -> Optional[str]:
|
||||
"""
|
||||
Extract the first section name from the output string.
|
||||
"""
|
||||
match = re.search(r"Section:\s*(.+)", stdout)
|
||||
if match:
|
||||
return match.group(1).strip()
|
||||
return None
|
||||
|
||||
|
||||
def is_function_called(source_code: str, func_name: str) -> bool:
|
||||
"""
|
||||
Returns True if the function named `func_name` is called in `source_code`.
|
||||
"""
|
||||
tree = ast.parse(source_code)
|
||||
for node in ast.walk(tree):
|
||||
if isinstance(node, ast.Call):
|
||||
# For simple function calls like func()
|
||||
if isinstance(node.func, ast.Name) and node.func.id == func_name:
|
||||
return True
|
||||
|
||||
# For calls like module.func()
|
||||
elif isinstance(node.func, ast.Attribute) and node.func.attr == func_name:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def remove_function(source_code: str, function_name: str) -> str:
|
||||
"""
|
||||
Remove a function definition from the source code.
|
||||
"""
|
||||
tree = ast.parse(source_code)
|
||||
lines = source_code.splitlines()
|
||||
|
||||
for node in tree.body:
|
||||
if isinstance(node, ast.FunctionDef) and node.name == function_name:
|
||||
start_lineno = node.lineno - 1
|
||||
end_lineno = node.end_lineno
|
||||
return "\n".join(lines[:start_lineno] + lines[end_lineno:])
|
||||
|
||||
return source_code
|
||||
|
||||
|
||||
def remove_main_block(source_code: str) -> str:
|
||||
"""
|
||||
Remove the if __name__ == "__main__": block from the source code.
|
||||
"""
|
||||
tree = ast.parse(source_code)
|
||||
lines = source_code.splitlines()
|
||||
|
||||
# Find the main block and note its line numbers
|
||||
for node in tree.body:
|
||||
if isinstance(node, ast.If):
|
||||
test = node.test
|
||||
if (
|
||||
isinstance(test, ast.Compare)
|
||||
and isinstance(test.left, ast.Name)
|
||||
and test.left.id == "__name__"
|
||||
and len(test.ops) == 1
|
||||
and isinstance(test.ops[0], ast.Eq)
|
||||
and len(test.comparators) == 1
|
||||
and isinstance(test.comparators[0], ast.Constant)
|
||||
and test.comparators[0].value == "__main__"
|
||||
):
|
||||
|
||||
# Remove lines corresponding to this block
|
||||
start_lineno = node.lineno - 1
|
||||
end_lineno = node.end_lineno
|
||||
return "\n".join(lines[:start_lineno] + lines[end_lineno:])
|
||||
|
||||
return source_code
|
||||
|
||||
|
||||
def extract_top_level_functions_with_decorators_and_comments(
|
||||
code: str,
|
||||
) -> List[Tuple[str, str]]:
|
||||
"""
|
||||
Returns list of (function_name, source_segment) for top-level functions (excluding "main"),
|
||||
including decorators and contiguous preceding comments.
|
||||
"""
|
||||
# Parse AST to get function nodes
|
||||
tree = ast.parse(code)
|
||||
lines = code.splitlines(keepends=True)
|
||||
|
||||
# Precompute which line numbers have comment tokens
|
||||
comment_lines: Set[int] = set()
|
||||
lines = code.splitlines(keepends=True) # preserve exact line content for prefix checks
|
||||
|
||||
tokgen = tokenize.generate_tokens(io.StringIO(code).readline) # yields (type, string, start, end, line)
|
||||
for tok_type, _, (srow, scol), _, _ in tokgen:
|
||||
if tok_type == tokenize.COMMENT:
|
||||
# everything before the comment on that line must be whitespace
|
||||
prefix = lines[srow - 1][:scol]
|
||||
if prefix.strip() == "":
|
||||
comment_lines.add(srow)
|
||||
|
||||
functions = []
|
||||
|
||||
for node in tree.body: # only top-level
|
||||
if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
||||
continue
|
||||
if node.name == "main":
|
||||
continue
|
||||
|
||||
# Determine the starting line: earliest decorator if present, else the def/async line
|
||||
if node.decorator_list:
|
||||
start_lineno = min(d.lineno for d in node.decorator_list)
|
||||
else:
|
||||
start_lineno = node.lineno
|
||||
|
||||
# Extend upward to include contiguous comment lines (no intervening non-blank/non-comment)
|
||||
span_start = start_lineno
|
||||
curr = span_start - 1 # check line above; lines are 1-based
|
||||
while curr > 0:
|
||||
line_text = lines[curr - 1]
|
||||
if curr in comment_lines:
|
||||
span_start = curr
|
||||
curr -= 1
|
||||
continue
|
||||
if line_text.strip() == "":
|
||||
# blank line: include it and keep scanning upward
|
||||
span_start = curr
|
||||
curr -= 1
|
||||
continue
|
||||
break # encountered code or something else; stop
|
||||
|
||||
# Determine end line of the function definition including its body
|
||||
# Prefer end_lineno if available (Python 3.8+)
|
||||
if hasattr(node, "end_lineno") and node.end_lineno is not None:
|
||||
span_end = node.end_lineno
|
||||
else:
|
||||
# Fallback: get last lineno from the deepest child in body
|
||||
def _max_lineno(n):
|
||||
max_ln = getattr(n, "lineno", 0)
|
||||
for child in ast.iter_child_nodes(n):
|
||||
ln = _max_lineno(child)
|
||||
if ln > max_ln:
|
||||
max_ln = ln
|
||||
return max_ln
|
||||
|
||||
span_end = _max_lineno(node)
|
||||
|
||||
# Slice the original source lines
|
||||
segment = "".join(lines[span_start - 1 : span_end])
|
||||
functions.append((node.name, segment))
|
||||
|
||||
return functions
|
||||
|
||||
|
||||
def split_code_and_output_into_sections(code: str, stdout: str) -> list[CodeSection]:
|
||||
"""
|
||||
Converts a Python script and its output into a list of CodeSections.
|
||||
Pre-condition: The code in the main() function contains print statements that indicate section names, e.g., `print("Section: <section name>")`.
|
||||
"""
|
||||
# This will hold all top-level code and by default all function definitions.
|
||||
# Functions will later be moved to more relevant sections if needed.
|
||||
# The first step is to remove both the if __name__ == "__main__": block and the main function
|
||||
top_level_code = remove_main_block(remove_function(code, "main"))
|
||||
|
||||
main_function_body = extract_function_body(code, "main")
|
||||
functions = extract_top_level_functions_with_decorators_and_comments(top_level_code)
|
||||
|
||||
# Split the main function body into sections based on print("Section: <section name>") code
|
||||
main_fn_top_level_section, main_fn_sections, known_section_names = (
|
||||
split_code_sections(main_function_body) if main_function_body else (None, [], [])
|
||||
)
|
||||
|
||||
# Split the output into sections based on "Section: " headers
|
||||
output_top_level_section, output_sections = split_output_sections(stdout, known_section_names)
|
||||
|
||||
# Merge code and outputs into code sections
|
||||
result_sections: list[CodeSection] = []
|
||||
for output_section, code_section in zip_longest(output_sections, main_fn_sections):
|
||||
name = None
|
||||
if code_section is not None:
|
||||
# If code section is available, extract the section name from it
|
||||
name = extract_first_section_name_from_code(code_section)
|
||||
elif output_section:
|
||||
# If only output section is available, extract the section name from it
|
||||
name = extract_first_section_name_from_output(output_section)
|
||||
comments, cleaned_code = (
|
||||
extract_comment_under_first_print(code_section) if code_section is not None else (None, None)
|
||||
)
|
||||
# Strip whitespaces for the cell
|
||||
if cleaned_code is not None:
|
||||
cleaned_code = cleaned_code.strip()
|
||||
result_sections.append(CodeSection(name=name, code=cleaned_code, comments=comments, output=output_section))
|
||||
|
||||
# Small optimization: move function definitions to the sections where they are first called
|
||||
# TODO: this doesn't handle nested function references, e.g., fn A calls fn B which calls fn C
|
||||
# currently will not move C to the section where A is called
|
||||
for name, segment in functions:
|
||||
for section in result_sections:
|
||||
if section["code"] and is_function_called(section["code"], name):
|
||||
section["code"] = segment.strip() + "\n\n" + section["code"].lstrip()
|
||||
top_level_code = top_level_code.replace(segment, "")
|
||||
break
|
||||
|
||||
# Inject the top-level code at the beginning of the sections
|
||||
top_level_code = (
|
||||
top_level_code.rstrip() + "\n\n" + main_fn_top_level_section.lstrip()
|
||||
if main_fn_top_level_section
|
||||
else top_level_code
|
||||
)
|
||||
result_sections.insert(
|
||||
0,
|
||||
CodeSection(
|
||||
name=None,
|
||||
code=top_level_code,
|
||||
comments=None,
|
||||
output=output_top_level_section,
|
||||
),
|
||||
)
|
||||
|
||||
return result_sections
|
||||
@@ -0,0 +1,6 @@
|
||||
import re
|
||||
|
||||
|
||||
def remove_eda_part(stdout: str) -> str:
|
||||
"""Data Science scenario have a LLM-based EDA feature. We can remove it when current task does not involve EDA"""
|
||||
return re.sub(r"=== Start of EDA part ===(.*)=== End of EDA part ===", "", stdout, flags=re.DOTALL)
|
||||
@@ -0,0 +1,132 @@
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEERMultiEvaluator,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.evolving_strategy import (
|
||||
MultiProcessEvolvingStrategy,
|
||||
)
|
||||
from rdagent.components.coder.CoSTEER.knowledge_management import (
|
||||
CoSTEERQueriedKnowledge,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import DSCoderCoSTEERSettings
|
||||
from rdagent.components.coder.data_science.share.ds_costeer import DSCoSTEER
|
||||
from rdagent.components.coder.data_science.workflow.eval import (
|
||||
WorkflowGeneralCaseSpecEvaluator,
|
||||
)
|
||||
from rdagent.components.coder.data_science.workflow.exp import WorkflowTask
|
||||
from rdagent.core.exception import CoderError
|
||||
from rdagent.core.experiment import FBWorkspace
|
||||
from rdagent.core.scenario import Scenario
|
||||
from rdagent.oai.llm_utils import APIBackend
|
||||
from rdagent.utils.agent.ret import PythonAgentOut
|
||||
from rdagent.utils.agent.tpl import T
|
||||
|
||||
|
||||
class WorkflowMultiProcessEvolvingStrategy(MultiProcessEvolvingStrategy):
|
||||
def implement_one_task(
|
||||
self,
|
||||
target_task: WorkflowTask,
|
||||
queried_knowledge: CoSTEERQueriedKnowledge | None = None,
|
||||
workspace: FBWorkspace | None = None,
|
||||
prev_task_feedback: CoSTEERSingleFeedback | None = None,
|
||||
) -> dict[str, str]:
|
||||
workflow_information_str = target_task.get_task_information()
|
||||
|
||||
# 1. query
|
||||
queried_similar_successful_knowledge = (
|
||||
queried_knowledge.task_to_similar_task_successful_knowledge[workflow_information_str]
|
||||
if queried_knowledge is not None
|
||||
else []
|
||||
)
|
||||
queried_former_failed_knowledge = (
|
||||
queried_knowledge.task_to_former_failed_traces[workflow_information_str]
|
||||
if queried_knowledge is not None
|
||||
else []
|
||||
)
|
||||
queried_former_failed_knowledge = (
|
||||
[
|
||||
knowledge
|
||||
for knowledge in queried_former_failed_knowledge[0]
|
||||
if knowledge.implementation.file_dict.get("main.py") != workspace.file_dict.get("main.py")
|
||||
],
|
||||
queried_former_failed_knowledge[1],
|
||||
)
|
||||
|
||||
# 2. code
|
||||
system_prompt = T(".prompts:workflow_coder.system").r(
|
||||
task_desc=workflow_information_str,
|
||||
competition_info=self.scen.get_scenario_all_desc(eda_output=workspace.file_dict.get("EDA.md", None)),
|
||||
queried_similar_successful_knowledge=queried_similar_successful_knowledge,
|
||||
queried_former_failed_knowledge=queried_former_failed_knowledge[0],
|
||||
out_spec=PythonAgentOut.get_spec(),
|
||||
)
|
||||
user_prompt = T(".prompts:workflow_coder.user").r(
|
||||
load_data_code=workspace.file_dict["load_data.py"],
|
||||
feature_code=workspace.file_dict["feature.py"],
|
||||
model_codes=workspace.get_codes(r"^model_(?!test)\w+\.py$"),
|
||||
ensemble_code=workspace.file_dict["ensemble.py"],
|
||||
latest_code=workspace.file_dict.get("main.py"),
|
||||
code_spec=(
|
||||
workspace.file_dict["spec/workflow.md"]
|
||||
if DS_RD_SETTING.spec_enabled
|
||||
else T("scenarios.data_science.share:component_spec.Workflow").r()
|
||||
),
|
||||
latest_code_feedback=prev_task_feedback,
|
||||
)
|
||||
|
||||
for _ in range(5):
|
||||
workflow_code = PythonAgentOut.extract_output(
|
||||
APIBackend().build_messages_and_create_chat_completion(
|
||||
user_prompt=user_prompt,
|
||||
system_prompt=system_prompt,
|
||||
)
|
||||
)
|
||||
if workflow_code != workspace.file_dict.get("main.py"):
|
||||
break
|
||||
else:
|
||||
user_prompt = user_prompt + "\nPlease avoid generating same code to former code!"
|
||||
else:
|
||||
raise CoderError("Failed to generate a new workflow code.")
|
||||
|
||||
return {"main.py": workflow_code}
|
||||
|
||||
def assign_code_list_to_evo(self, code_list: list[dict[str, str]], evo):
|
||||
"""
|
||||
Assign the code list to the evolving item.
|
||||
|
||||
The code list is aligned with the evolving item's sub-tasks.
|
||||
If a task is not implemented, put a None in the list.
|
||||
"""
|
||||
for index in range(len(evo.sub_tasks)):
|
||||
if code_list[index] is None:
|
||||
continue
|
||||
if evo.sub_workspace_list[index] is None:
|
||||
# evo.sub_workspace_list[index] = FBWorkspace(target_task=evo.sub_tasks[index])
|
||||
evo.sub_workspace_list[index] = evo.experiment_workspace
|
||||
evo.sub_workspace_list[index].inject_files(**code_list[index])
|
||||
return evo
|
||||
|
||||
|
||||
class WorkflowCoSTEER(DSCoSTEER):
|
||||
def __init__(
|
||||
self,
|
||||
scen: Scenario,
|
||||
*args,
|
||||
**kwargs,
|
||||
) -> None:
|
||||
settings = DSCoderCoSTEERSettings()
|
||||
eva = CoSTEERMultiEvaluator(
|
||||
WorkflowGeneralCaseSpecEvaluator(scen=scen), scen=scen
|
||||
) # Please specify whether you agree running your eva in parallel or not
|
||||
es = WorkflowMultiProcessEvolvingStrategy(scen=scen, settings=settings)
|
||||
super().__init__(
|
||||
*args,
|
||||
settings=settings,
|
||||
eva=eva,
|
||||
es=es,
|
||||
evolving_version=2,
|
||||
scen=scen,
|
||||
max_loop=DS_RD_SETTING.coder_max_loop,
|
||||
**kwargs,
|
||||
)
|
||||
@@ -0,0 +1,158 @@
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from rdagent.app.data_science.conf import DS_RD_SETTING
|
||||
from rdagent.components.coder.CoSTEER.evaluators import (
|
||||
CoSTEEREvaluator,
|
||||
CoSTEERMultiFeedback,
|
||||
CoSTEERSingleFeedback,
|
||||
)
|
||||
from rdagent.components.coder.data_science.conf import get_clear_ws_cmd, get_ds_env
|
||||
from rdagent.components.coder.data_science.utils import remove_eda_part
|
||||
from rdagent.core.evolving_framework import QueriedKnowledge
|
||||
from rdagent.core.experiment import FBWorkspace, Task
|
||||
from rdagent.log import rdagent_logger as logger
|
||||
from rdagent.utils.agent.tpl import T
|
||||
from rdagent.utils.agent.workflow import build_cls_from_json_with_retry
|
||||
|
||||
DIRNAME = Path(__file__).absolute().resolve().parent
|
||||
|
||||
WorkflowSingleFeedback = CoSTEERSingleFeedback
|
||||
WorkflowMultiFeedback = CoSTEERMultiFeedback
|
||||
|
||||
|
||||
class WorkflowGeneralCaseSpecEvaluator(CoSTEEREvaluator):
|
||||
"""
|
||||
Motivation case:
|
||||
- Simplest case, we already split the data into train_data, valid_data, and test_data. We require the model to learn (optionally validate on valid data), and infer on test data.
|
||||
|
||||
Test workflow:
|
||||
- Build train, valid, and test data to run it, and test the output (e.g., shape, etc.)
|
||||
"""
|
||||
|
||||
def evaluate(
|
||||
self,
|
||||
target_task: Task,
|
||||
implementation: FBWorkspace,
|
||||
gt_implementation: FBWorkspace,
|
||||
queried_knowledge: QueriedKnowledge = None,
|
||||
**kwargs,
|
||||
) -> CoSTEERSingleFeedback:
|
||||
target_task_information = target_task.get_task_information()
|
||||
if (
|
||||
queried_knowledge is not None
|
||||
and target_task_information in queried_knowledge.success_task_to_knowledge_dict
|
||||
):
|
||||
return queried_knowledge.success_task_to_knowledge_dict[target_task_information].feedback
|
||||
elif queried_knowledge is not None and target_task_information in queried_knowledge.failed_task_info_set:
|
||||
return WorkflowSingleFeedback(
|
||||
execution="This task has failed too many times, skip implementation.",
|
||||
return_checking="This task has failed too many times, skip implementation.",
|
||||
code="This task has failed too many times, skip implementation.",
|
||||
final_decision=False,
|
||||
)
|
||||
|
||||
env = get_ds_env(
|
||||
extra_volumes={self.scen.debug_path: T("scenarios.data_science.share:scen.input_path").r()},
|
||||
running_timeout_period=self.scen.real_debug_timeout(),
|
||||
)
|
||||
|
||||
# # DockerEnv for MLEBench submission validation
|
||||
# mle_de_conf = MLEBDockerConf()
|
||||
# mle_de_conf.extra_volumes = {
|
||||
# f"{DS_RD_SETTING.local_data_path}/zip_files": "/mle/data",
|
||||
# }
|
||||
# mde = DockerEnv(conf=mle_de_conf)
|
||||
# mde.prepare()
|
||||
|
||||
# Clean the scores.csv & submission.csv.
|
||||
implementation.execute(env=env, entry=get_clear_ws_cmd())
|
||||
|
||||
stdout = implementation.execute(env=env, entry=f"python -m coverage run main.py")
|
||||
|
||||
# remove EDA part
|
||||
stdout = remove_eda_part(stdout)
|
||||
|
||||
# Check score file
|
||||
score_fp = implementation.workspace_path / "scores.csv"
|
||||
score_ret_code = 0
|
||||
score_check_text = ""
|
||||
if not score_fp.exists():
|
||||
score_check_text = "[Error] Metrics file (scores.csv) is not generated!"
|
||||
score_ret_code = 1
|
||||
implementation.execute(env=env, entry="python -m coverage json -o coverage.json")
|
||||
coverage_report_path = implementation.workspace_path / "coverage.json"
|
||||
if coverage_report_path.exists():
|
||||
used_files = set(json.loads(coverage_report_path.read_text())["files"].keys())
|
||||
coverage_report_path.unlink()
|
||||
logger.info(f"All used scripts: {used_files}")
|
||||
if len(used_files) == 1:
|
||||
score_check_text += f"\n[Error] The only used script is {used_files}.\nPlease check if you have implemented entry point in 'main.py'."
|
||||
else:
|
||||
try:
|
||||
score_df = pd.read_csv(score_fp, index_col=0)
|
||||
model_set_in_scores = set(score_df.index)
|
||||
# We assume that model names in `score_df` are stored without the '.py' file extension.
|
||||
model_set_in_folder = set(
|
||||
f[:-3] for f in implementation.file_dict.keys() if re.match(r"^model_(?!test)\w+\.py$", f)
|
||||
)
|
||||
|
||||
# Check model names (index)
|
||||
if model_set_in_scores != model_set_in_folder.union({"ensemble"}):
|
||||
score_check_text += f"\n[Error] The scores dataframe does not contain the correct model names as index.\ncorrect model names are: {model_set_in_folder.union({'ensemble'})}\nscore_df is:\n{score_df}"
|
||||
score_ret_code = 1
|
||||
|
||||
# Check metric name (columns) - case insensitive
|
||||
if [col.lower() for col in score_df.columns.tolist()] != [self.scen.metric_name.lower()]:
|
||||
score_check_text += f"\n[Error] The scores dataframe does not contain the correct column names.\nCorrect columns is: ['{self.scen.metric_name}']\nBut got: {score_df.columns.tolist()}"
|
||||
score_ret_code = 1
|
||||
|
||||
# Check if scores contain NaN (values)
|
||||
if score_df.isnull().values.any():
|
||||
nan_locations = score_df[score_df.isnull().any(axis=1)]
|
||||
score_check_text += f"\n[Error] The scores dataframe contains NaN values at the following locations:\n{nan_locations}"
|
||||
score_ret_code = 1
|
||||
|
||||
except Exception as e:
|
||||
score_check_text += f"\n[Error] in checking the scores.csv file: {e}\nscores.csv's content:\n-----\n{score_fp.read_text()}\n-----"
|
||||
score_ret_code = 1
|
||||
|
||||
# Check submission file
|
||||
base_check_code = T(".eval_tests.submission_format_test", ftype="txt").r()
|
||||
implementation.inject_files(**{"test/submission_format_test.py": base_check_code})
|
||||
# stdout += "----Submission Check 1-----\n"
|
||||
submission_result = implementation.run(env=env, entry="python test/submission_format_test.py")
|
||||
submission_check_out = submission_result.stdout
|
||||
submission_ret_code = submission_result.exit_code
|
||||
stdout += "\n" + submission_check_out
|
||||
|
||||
system_prompt = T(".prompts:workflow_eval.system").r(
|
||||
# here we pass `None` to `eda_output` because we do not have nor need EDA output for workflow.
|
||||
scenario=self.scen.get_scenario_all_desc(eda_output=None),
|
||||
task_desc=target_task.get_task_information(),
|
||||
spec=(
|
||||
implementation.file_dict["spec/workflow.md"]
|
||||
if DS_RD_SETTING.spec_enabled
|
||||
else T("scenarios.data_science.share:component_spec.Workflow").r()
|
||||
),
|
||||
)
|
||||
user_prompt = T(".prompts:workflow_eval.user").r(
|
||||
stdout=stdout.strip(),
|
||||
code=implementation.file_dict["main.py"],
|
||||
)
|
||||
wfb = build_cls_from_json_with_retry(
|
||||
WorkflowSingleFeedback,
|
||||
system_prompt=system_prompt,
|
||||
user_prompt=user_prompt,
|
||||
init_kwargs_update_func=WorkflowSingleFeedback.val_and_update_init_dict,
|
||||
)
|
||||
if score_ret_code != 0:
|
||||
wfb.final_decision = False
|
||||
wfb.return_checking += "\n" + score_check_text
|
||||
if submission_ret_code != 0:
|
||||
wfb.final_decision = False
|
||||
wfb.return_checking += "\nSubmission file check failed."
|
||||
return wfb
|
||||
@@ -0,0 +1,77 @@
|
||||
from pathlib import Path
|
||||
import pandas as pd
|
||||
import hashlib
|
||||
|
||||
def calculate_md5(file_path):
|
||||
with open(file_path, "rb") as f:
|
||||
file_hash = hashlib.md5(f.read()).hexdigest()
|
||||
return file_hash
|
||||
|
||||
file_md5 = calculate_md5("scores.csv")
|
||||
|
||||
"""
|
||||
find . | grep -i sample | grep -i submission | grep -v sample_submission.csv | grep -v zip_files | grep -v 'sample/'
|
||||
./denoising-dirty-documents/sampleSubmission.csv
|
||||
./the-icml-2013-whale-challenge-right-whale-redux/sampleSubmission.csv
|
||||
./text-normalization-challenge-russian-language/ru_sample_submission_2.csv.zip
|
||||
./text-normalization-challenge-russian-language/ru_sample_submission_2.csv
|
||||
./random-acts-of-pizza/sampleSubmission.csv
|
||||
./text-normalization-challenge-english-language/en_sample_submission_2.csv.zip
|
||||
./text-normalization-challenge-english-language/en_sample_submission_2.csv
|
||||
./detecting-insults-in-social-commentary/sample_submission_null.csv
|
||||
"""
|
||||
|
||||
# Find sample submission file dynamically
|
||||
input_dir = Path("{% include "scenarios.data_science.share:scen.input_path" %}")
|
||||
# Look for common variations of sample submission filenames
|
||||
sample_submission_files = list(input_dir.glob("*sample_submission*.csv")) + \
|
||||
list(input_dir.glob("*sampleSubmission*.csv"))
|
||||
|
||||
assert sample_submission_files, "Error: No sample submission file found in {% include "scenarios.data_science.share:scen.input_path" %}"
|
||||
|
||||
# Use first matching file
|
||||
sample_submission_name = sample_submission_files[0].name
|
||||
SAMPLE_SUBMISSION_PATH = str(sample_submission_files[0])
|
||||
print(f"Using sample submission file: {sample_submission_name}")
|
||||
|
||||
# Check if the sample submission file exists
|
||||
assert Path(SAMPLE_SUBMISSION_PATH).exists(), f"Error: {sample_submission_name} not found at {SAMPLE_SUBMISSION_PATH}"
|
||||
|
||||
# Check if our submission file exists
|
||||
assert Path('submission.csv').exists(), "Error: submission.csv not found"
|
||||
|
||||
sample_submission = pd.read_csv(SAMPLE_SUBMISSION_PATH)
|
||||
our_submission = pd.read_csv('submission.csv')
|
||||
|
||||
success = True
|
||||
# Print the columns of the sample submission file
|
||||
print(f"Columns in {sample_submission_name}:", sample_submission.columns)
|
||||
print("Columns in our_submission.csv:", our_submission.columns)
|
||||
|
||||
for col in sample_submission.columns:
|
||||
if col not in our_submission.columns:
|
||||
success = False
|
||||
print(f'Column {col} not found in submission.csv')
|
||||
|
||||
if success:
|
||||
print(f'submission.csv\'s columns aligns with {sample_submission_name} .')
|
||||
|
||||
|
||||
# Print the first 5 rows of the two submission files, with columns separated by commas.
|
||||
def print_first_rows(file_path, file_name, num_rows=5):
|
||||
print(f"\nFirst {num_rows} rows of {file_name}:")
|
||||
try:
|
||||
with open(file_path, 'r') as file:
|
||||
for i, line in enumerate(file):
|
||||
if i < num_rows:
|
||||
print(line.strip())
|
||||
else:
|
||||
break
|
||||
except FileNotFoundError:
|
||||
print(f"Error: {file_name} not found.")
|
||||
|
||||
print_first_rows(SAMPLE_SUBMISSION_PATH, sample_submission_name)
|
||||
print_first_rows('submission.csv', 'submission.csv')
|
||||
|
||||
assert calculate_md5("scores.csv") == file_md5, "scores.csv should not be rewritten"
|
||||
print(f"\nPlease Checked the content of the submission file(submission.csv should align with {sample_submission_name}). ")
|
||||
@@ -0,0 +1,14 @@
|
||||
import pickle
|
||||
import site
|
||||
import traceback
|
||||
from pathlib import Path
|
||||
from typing import Dict, Optional
|
||||
|
||||
from rdagent.components.coder.CoSTEER.task import CoSTEERTask
|
||||
from rdagent.core.utils import cache_with_pickle
|
||||
|
||||
|
||||
# Because we use isinstance to distinguish between different types of tasks, we need to use sub classes to represent different types of tasks
|
||||
class WorkflowTask(CoSTEERTask):
|
||||
def __init__(self, name: str = "Workflow", *args, **kwargs) -> None:
|
||||
super().__init__(name=name, *args, **kwargs)
|
||||
@@ -0,0 +1,137 @@
|
||||
workflow_coder:
|
||||
system: |-
|
||||
You are a world-class data scientist and machine learning engineer with deep expertise in statistics, mathematics, and computer science.
|
||||
Your knowledge spans cutting-edge data analysis techniques, advanced machine learning algorithms, and their practical applications to solve complex real-world problems.
|
||||
|
||||
## Task Description
|
||||
{{ task_desc }}
|
||||
|
||||
Here is the competition information for this task:
|
||||
{{ competition_info }}
|
||||
|
||||
{% if queried_similar_successful_knowledge|length != 0 or queried_former_failed_knowledge|length != 0 %}
|
||||
## Relevant Information for This Task
|
||||
{% endif %}
|
||||
|
||||
{% if queried_similar_successful_knowledge|length != 0 %}
|
||||
--------- Successful Implementations for Similar Models ---------
|
||||
====={% for similar_successful_knowledge in queried_similar_successful_knowledge %} Model {{ loop.index }}:=====
|
||||
{{ similar_successful_knowledge.target_task.get_task_information() }}
|
||||
=====Code:=====
|
||||
{{ similar_successful_knowledge.implementation.file_dict["main.py"] }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
{% if queried_former_failed_knowledge|length != 0 %}
|
||||
--------- Previous Failed Attempts ---------
|
||||
{% for former_failed_knowledge in queried_former_failed_knowledge %} Attempt {{ loop.index }}:
|
||||
=====Code:=====
|
||||
{{ former_failed_knowledge.implementation.file_dict["main.py"] }}
|
||||
=====Feedback:=====
|
||||
{{ former_failed_knowledge.feedback }}
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
## Guidelines
|
||||
1. Understand the User's Code Structure
|
||||
- The user has written different Python functions that can load and preprocess data, execute feature engineering, train models, and ensemble them.
|
||||
- Each functionality is in a separate Python file.
|
||||
2. Your task is only to integrate the existing processes of load_data, feature, model, and ensemble into a complete workflow. Do not edit or modify the existing Python files. The final step should output the predictions in the required format.
|
||||
3. The user may provide specific code organization rules and instructions. Ensure that the integration follows the given framework and structure.
|
||||
4. After predicting the output, print the shape and other information of the output to stdout to help the evaluator assess the code.
|
||||
5. You should avoid using logging module to output information in your generated code, and instead use the print() function.
|
||||
{% include "scenarios.data_science.share:guidelines.coding" %}
|
||||
|
||||
## Output Format
|
||||
{% if out_spec %}
|
||||
{{ out_spec }}
|
||||
{% else %}
|
||||
Please response the code in the following json format. Here is an example structure for the JSON output:
|
||||
{
|
||||
"code": "The Python code as a string."
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
user: |-
|
||||
--------- Code Specification ---------
|
||||
{{ code_spec }}
|
||||
|
||||
--------- load data code ---------
|
||||
file: load_data.py
|
||||
{{ load_data_code }}
|
||||
|
||||
--------- feature engineering code ---------
|
||||
file: feature.py
|
||||
{{ feature_code }}
|
||||
|
||||
--------- model training code ---------
|
||||
Attention: The input and output of the model function is flexible. Training dataset is necessary, but validation and test dateset might be optional. The hyperparameters can either be passed as arguments or be set as default values in the function. You need to use the function correctly.
|
||||
All model files share the same function name. Please import the model files with their name like: from {file_name} import {function_name}
|
||||
{{ model_codes }}
|
||||
|
||||
--------- ensemble code ---------
|
||||
Note, we will check the index of the score.csv, so please use the model name as the index to feed into ensemble function.
|
||||
file: ensemble.py
|
||||
{{ ensemble_code }}
|
||||
|
||||
{% if latest_code %}
|
||||
--------- Former code ---------
|
||||
{{ latest_code }}
|
||||
{% if latest_code_feedback is not none %}
|
||||
--------- Feedback to former code ---------
|
||||
{{ latest_code_feedback }}
|
||||
{% endif %}
|
||||
The former code contains errors. You should correct the code based on the provided information, ensuring you do not repeat the same mistakes.
|
||||
{% endif %}
|
||||
|
||||
workflow_eval:
|
||||
system: |-
|
||||
You are a data scientist responsible for evaluating workflow code generation.
|
||||
|
||||
## Task Description
|
||||
The user is trying to build a workflow in the following scenario:
|
||||
{{ scenario }}
|
||||
|
||||
The main code generation task is as follows:
|
||||
{{ task_desc }}
|
||||
|
||||
The user provides workflow information and its components.
|
||||
The details on how to structure the workflow are given in the specification file:
|
||||
```markdown
|
||||
{{ spec }}
|
||||
```
|
||||
|
||||
This workflow integrates multiple stages, including:
|
||||
- Data loading
|
||||
- Feature engineering
|
||||
- Model training
|
||||
- Ensembling
|
||||
|
||||
## Evaluation Scope
|
||||
Your focus is to check whether the workflow code:
|
||||
1. Executes successfully, correctly organizing components and generating a final submission.
|
||||
2. Generates predictions in the correct format, ensuring they align with the **sample submission** structure!
|
||||
|
||||
[Note]
|
||||
1. The individual components (data loading, feature engineering, model tuning, etc.) have already been evaluated by the user. You should only evaluate and improve the workflow code, unless there are critical issues in the components.
|
||||
2. Model performance is NOT a concern in this evaluation—only correct execution and formatting matter.
|
||||
3. As long as the execution does not exceed the time limit, ensure that the code uses cross-validation to split the training data and train the model. If cross-validation is not used, mention it in the execution section and set `final_decision` to `false`.
|
||||
|
||||
## Evaluation Criteria
|
||||
You will be given the workflow execution output (`stdout`) to determine correctness.
|
||||
|
||||
Please respond with your feedback in the following JSON format and order
|
||||
```json
|
||||
{
|
||||
"execution": "Describe whether the main workflow executed successfully, correctly integrating all components and generating the final submission. Include any errors or issues encountered, and append all error messages and full traceback details without summarizing or omitting any information.",
|
||||
"return_checking": "Verify the generated files, particularly the submission file. Ensure that its format matches the sample submission, checking the index, column names, and CSV content.",
|
||||
"code": "Provide feedback on code quality, readability, and adherence to the given specifications.",
|
||||
"final_decision": <true/false>
|
||||
}
|
||||
```
|
||||
|
||||
user: |-
|
||||
--------- Workflow test stdout ---------
|
||||
{{ stdout }}
|
||||
--------- Workflow code generated by user ---------
|
||||
{{ code }}
|
||||
@@ -0,0 +1,59 @@
|
||||
"""
|
||||
Generate dataset to test the workflow output
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from rdagent.components.coder.CoSTEER.config import CoSTEER_SETTINGS
|
||||
from rdagent.components.coder.data_science.workflow import WorkflowCoSTEER
|
||||
from rdagent.components.coder.data_science.workflow.eval import (
|
||||
WorkflowGeneralCaseSpecEvaluator,
|
||||
)
|
||||
from rdagent.components.coder.data_science.workflow.exp import WorkflowTask
|
||||
from rdagent.core.experiment import FBWorkspace
|
||||
from rdagent.scenarios.data_science.experiment.experiment import DSExperiment
|
||||
from rdagent.scenarios.data_science.scen import KaggleScen
|
||||
|
||||
|
||||
def develop_one_competition(competition: str):
|
||||
scen = KaggleScen(competition=competition)
|
||||
workflow_coder = WorkflowCoSTEER(scen)
|
||||
|
||||
wt = WorkflowTask(
|
||||
name="WorkflowTask",
|
||||
description="Integrate the existing processes of load_data, feature, model, and ensemble into a complete workflow.",
|
||||
base_code="",
|
||||
)
|
||||
|
||||
tpl_ex_path = Path(__file__).resolve() / Path("rdagent/scenarios/kaggle/tpl_ex").resolve() / competition
|
||||
injected_file_names = ["spec/workflow.md", "load_data.py", "feature.py", "model01.py", "ensemble.py", "main.py"]
|
||||
|
||||
workflowexp = FBWorkspace()
|
||||
for file_name in injected_file_names:
|
||||
file_path = tpl_ex_path / file_name
|
||||
workflowexp.inject_files(**{file_name: file_path.read_text()})
|
||||
|
||||
wt.base_code += workflowexp.file_dict["main.py"]
|
||||
exp = DSExperiment(
|
||||
sub_tasks=[wt],
|
||||
)
|
||||
|
||||
"""es = WorkflowMultiProcessEvolvingStrategy(scen=scen, settings=CoSTEER_SETTINGS)
|
||||
new_code = es.implement_one_task(target_task=wt, queried_knowledge=None, workspace = workflowexp)
|
||||
print(new_code)"""
|
||||
|
||||
"""eva = WorkflowGeneralCaseSpecEvaluator(scen=scen)
|
||||
exp.feedback = eva.evaluate(target_task=wt, queried_knowledge=None, implementation=workflowexp, gt_implementation=None)
|
||||
print(exp.feedback)"""
|
||||
|
||||
# Run the experiment
|
||||
for file_name in injected_file_names:
|
||||
file_path = tpl_ex_path / file_name
|
||||
exp.experiment_workspace.inject_files(**{file_name: file_path.read_text()})
|
||||
|
||||
exp = workflow_coder.develop(exp)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
develop_one_competition("aerial-cactus-identification")
|
||||
# dotenv run -- python rdagent/components/coder/data_science/workflow/test.py
|
||||
Reference in New Issue
Block a user