chore: import upstream snapshot with attribution
This commit is contained in:
@@ -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")
|
||||
Reference in New Issue
Block a user