RDAgent
🤖 自动化特征工程与模型调优演进
数据科学智能代理是一个可以自动执行特征工程和模型调优的智能体。它可以用于解决各种数据科学问题,例如图像分类、时间序列预测和文本分类。
🌟 简介
在此场景中,我们的自动化系统在一个持续、迭代的过程中,进行假设提出、行动选择、代码实现、验证,并利用反馈。
其目标是通过自主研发,自动优化验证集或 Kaggle 排行榜中的性能指标,最终发现最高效的特征和模型。
以下是增强版的步骤大纲:
-
第一步:假设生成 🔍
基于之前的实验分析和领域专业知识,生成并提出初始假设,并提供详尽的推理和财务依据。
-
第二步:实验创建 ✨
将假设转化为具体任务。
在特征工程或模型调优中选择一个具体行动。
开发、定义并实现一个新特征或新模型,包括其名称、描述和公式。
-
第三步:模型/特征实现 👨💻
根据详细描述实现模型代码。
像开发者一样对模型进行迭代演进,以确保其准确性和效率。
-
第四步:在测试集或 Kaggle 上验证 📉
使用测试集或 Kaggle 数据集验证新开发模型。
根据验证结果评估模型的有效性和性能。
-
第五步:反馈分析 🔍
分析验证结果以评估性能。
利用洞察力优化假设并增强模型。
-
第六步:假设优化 ♻️
根据验证反馈调整假设。
重复此过程以持续改进模型。
📖 数据科学背景
在不断发展的人工智能领域,数据科学代表了一种强大的范式,其中机器可以跨多个领域——从医疗保健和金融到物流和研究——自主进行探索、假设检验和模型开发。
数据科学智能代理是这场变革的核心引擎,使用户能够自动化整个机器学习工作流:从假设生成到代码实现、验证和优化——所有这些都由性能反馈来指导。
通过利用数据科学智能代理,研究人员和开发者可以加速实验周期。无论是微调自定义模型,还是在像 Kaggle 这样的高风险基准测试中竞争,数据科学智能代理都开启了智能、自主发现的新领域。
🧭 示例指南 - 自定义数据集
🔧 设置 RD-Agent 环境
在开始之前,请确保您已正确安装 RD-Agent 并配置好环境。如果想了解如何安装和配置 RD-Agent,请参考文档。
🔩 在 .env 文件中设置环境变量
确定数据将要存储的路径,并将其添加到 .env
文件中。
dotenv set DS_LOCAL_DATA_PATH <您的本地目录>/ds_data
dotenv set DS_SCEN rdagent.scenarios.data_science.scen.DataScienceScen
📥 准备自定义数据集
一个数据科学竞赛数据集通常由两部分组成:竞赛数据集和评估数据集。(我们提供了一个名为 arf-12-hours-prediction-task 的自定义数据集样本作为参考。)
-
竞赛数据集包含训练数据、测试数据、描述文件、格式化提交文件和数据采样代码。
-
评估数据集包含标准答案文件、数据检查代码和分数计算代码。
我们使用 arf-12-hours-prediction-task 数据作为样本,来介绍竞赛数据集的准备工作流。
创建 ds_data/source_data/arf-12-hours-prediction-task
文件夹,用于存储您的原始数据集。
arf-12-hours-prediction-task
竞赛的原始文件有两个:ARF_12h.csv
和 X.npz
。
创建 ds_data/source_data/arf-12-hours-prediction-task/prepare.py
文件,该文件将您的原始数据拆分为训练数据、测试数据、格式化提交文件和标准答案文件。(您需要根据您的原始数据编写一个脚本。)
以下是 arf-12-hours-prediction-task 原始数据的预处理代码:
ds_data/source_data/arf-12-hours-prediction-task/prepare.py
import random
from pathlib import Path
import numpy as np
import pandas as pd
import sparse
CURRENT_DIR = Path(__file__).resolve().parent
ROOT_DIR = CURRENT_DIR.parent.parent
raw_feature_path = CURRENT_DIR / "X.npz"
raw_label_path = CURRENT_DIR / "ARF_12h.csv"
public = ROOT_DIR / "arf-12-hours-prediction-task"
private = ROOT_DIR / "eval" / "arf-12-hours-prediction-task"
if not (public / "test").exists():
(public / "test").mkdir(parents=True, exist_ok=True)
if not (public / "train").exists():
(public / "train").mkdir(parents=True, exist_ok=True)
if not private.exists():
private.mkdir(parents=True, exist_ok=True)
SEED = 42
random.seed(SEED)
np.random.seed(SEED)
X_sparse = sparse.load_npz(raw_feature_path) # COO matrix, shape: [N, D, T]
df_label = pd.read_csv(raw_label_path) # Contains column 'ARF_LABEL'
N = X_sparse.shape[0]
indices = np.arange(N)
np.random.shuffle(indices)
split = int(0.7 * N)
train_idx, test_idx = indices[:split], indices[split:]
X_train = X_sparse[train_idx]
X_test = X_sparse[test_idx]
df_train = df_label.iloc[train_idx].reset_index(drop=True)
df_test = df_label.iloc[test_idx].reset_index(drop=True)
submission_df = df_test.copy()
submission_df["ARF_LABEL"] = 0
submission_df.drop(submission_df.columns.difference(["ID", "ARF_LABEL"]), axis=1, inplace=True)
submission_df.to_csv(public / "sample_submission.csv", index=False)
df_test.to_csv(private / "submission_test.csv", index=False)
df_test.drop(["ARF_LABEL"], axis=1, inplace=True)
df_test.to_csv(public / "test" / "ARF_12h.csv", index=False)
sparse.save_npz(public / "test" / "X.npz", X_test)
sparse.save_npz(public / "train" / "X.npz", X_train)
df_train.to_csv(public / "train" / "ARF_12h.csv", index=False)
assert (X_train.shape[0] == df_train.shape[0]), f"Mismatch: X_train rows ({X_train.shape[0]}) != df_train rows ({df_train.shape[0]})"
assert (X_test.shape[0] == df_test.shape[0]), f"Mismatch: X_test rows ({X_test.shape[0]}) != df_test rows ({df_test.shape[0]})"
assert df_test.shape[1] == 2, "Public test set should have 2 columns"
assert df_train.shape[1] == 3, "Public train set should have 3 columns"
assert len(df_train) + len(df_test) == len(df_label), "Length of new_train and new_test should equal length of old_train"
程序执行结束时,ds_data
文件夹结构将如下所示:
ds_data
├── arf-12-hours-prediction-task
│ ├── train
│ │ ├── ARF_12h.csv
│ │ └── X.npz
│ ├── test
│ │ ├── ARF_12h.csv
│ │ └── X.npz
│ └── sample_submission.csv
├── eval
│ └── arf-12-hours-prediction-task
│ └── submission_test.csv
└── source_data
└── arf-12-hours-prediction-task
├── ARF_12h.csv
├── prepare.py
└── X.npz
创建 ds_data/arf-12-hours-prediction-task/description.md
文件来描述您的比赛、目标、数据集以及其他信息。
以下是 arf-12-hours-prediction-task 的描述文件:
ds_data/arf-12-hours-prediction-task/description.md
# Competition name: ARF 12-Hour Prediction Task
## Overview
### Description
急性呼吸衰竭(Acute Respiratory Failure,简称
ARF)是一种危及生命的疾病,在危重病人中常迅速发展。在重症监护病房(ICU)中,准确地早期预测 ARF
对于及时进行临床干预和资源分配至关重要。在此任务中,您需要基于多变量临床时间序列数据,构建一个机器学习模型来预测患者是否会在接下来的 **12
小时内**发展为 ARF。
该数据集是从电子健康记录(EHRs)中提取的,并使用 **FIDDLE**
管道进行预处理,为每位患者生成结构化的时序特征。
### Objective
**您的目标**是开发一个二分类模型,该模型以 12 小时的时间序列作为输入,并预测 ARF 是否会在接下来的 12 小时内发生(1)或不发生(0)。
---
## Data Description
1. train/ARF_12h.csv:一个 CSV 文件,包含 ICU 住院 ID、ARF 发作的小时数以及指示 ARF 是否会在接下来的 12 小时内发生的二元标签。
* 列:ID, ARF_ONSET_HOUR, ARF_LABEL
2. train/X.npz:一个 N × T × D 的稀疏张量,包含时变特征。
* N:样本数量(ICU 住院次数)
* T:时间步长(每个样本 12 小时记录)
* D:动态特征维度(每小时有多少个特征)
3. test/ARF_12h.csv:真实标签(仅用于评估)。
4. test/X.npz:格式与训练数据相同的测试特征集。
---
## Data usage Notes
要加载特征,您需要 Python 和 sparse 包。
import sparse
X = sparse.load_npz("<url>/X.npz").todense()
要加载标签,请使用 pandas 或其他 csv 读取器。
import pandas as pd
df = pd.read_csv("<url>/ARF_12h.csv")
---
## Modeling
每个样本是一个 12 小时多变量 ICU 患者观察时间序列,表示为一个形状为 (12, D) 的张量。
目标是预测患者是否会在接下来的 12 小时内发展为 ARF(1)或不发展(0)。
* **输入**:12 × D 的临床特征矩阵
* **输出**:二元预测:0(无 ARF)或 1(ARF 发作)
* **损失函数**:BCEWithLogitsLoss、CrossEntropyLoss 或等效函数
* **评估指标**:**AUROC**(受试者工作特征曲线下面积)
注意:虽然输出是二元的,但 AUROC 评估的是预测分数的排名质量。因此,您的模型在训练期间应输出置信度分数,然后通过设定阈值来生成
0 或 1 作为最终提交。
---
## Evaluation
### Area Under the Receiver Operating Characteristic curve (AUROC)
提交结果将根据受试者工作特征曲线下面积进行评分。AUROC 的定义如下:
$$
\text{AUROC} = \frac{1}{|P| \cdot |N|} \sum_{i \in P} \sum_{j \in N} \left[ \mathbb{1}(s_i > s_j) + \frac{1}{2} \cdot \mathbb{1}(s_i = s_j) \right]
$$
AUROC 反映了模型将正样本排在负样本之前的能力。分数为 1.0 表示完美区分,0.5 表示随机猜测。
### Submission Format
对于测试数据集中
ARF_12h.csv文件中的每个
ID,您必须根据
X.npz(稀疏张量,时变特征)预测 ARF 是否会在接下来的 12 小时内发生(标签 = 1)或不发生(标签 =
0)(ARF_LABEL)。文件格式应为:
ID,ARF_LABEL
246505,0
291335,0
286713,0
etc.
注意:虽然提交是二元的,但 AUROC 评估的是模型的排名质量。建议在训练期间输出概率,并应用一个阈值(例如
0.5)将其转换为二元标签进行提交。
---
创建 ds_data/arf-12-hours-prediction-task/sample.py
文件来构建调试样本数据。
以下是基于 arf-12-hours-prediction-task 数据集实现的构建调试样本数据的脚本:
ds_data/arf-12-hours-prediction-task/sample.py
import shutil
from pathlib import Path
import numpy as np
import pandas as pd
import sparse
from tqdm import tqdm
def sample_and_copy_subfolder(
input_dir: Path,
output_dir: Path,
min_frac: float,
min_num: int,
seed: int = 42,
):
np.random.seed(seed)
feature_path = input_dir / "X.npz"
label_path = input_dir / "ARF_12h.csv"
# Load sparse features and label
X_sparse = sparse.load_npz(feature_path)
df_label = pd.read_csv(label_path)
N = X_sparse.shape[0]
n_keep = max(int(N * min_frac), min_num)
idx = np.random.choice(N, n_keep, replace=False)
X_sample = X_sparse[idx]
df_sample = df_label.iloc[idx].reset_index(drop=True)
output_dir.mkdir(parents=True, exist_ok=True)
sparse.save_npz(output_dir / "X.npz", X_sample)
df_sample.to_csv(output_dir / "ARF_12h.csv", index=False)
print(f"[INFO] Sampled {n_keep} of {N} from
{input_dir.name}")
# Copy additional files
for f in input_dir.glob("*"):
if f.name not in {"X.npz", "ARF_12h.csv"} and f.is_file():
shutil.copy(f, output_dir / f.name)
print(f"[COPY] Extra file: {f.name}")
def copy_other_file(source: Path, target: Path):
for item in source.iterdir():
if item.name in {"train", "test"}:
continue
relative_path = item.relative_to(source)
target_path = target / relative_path
if item.is_dir():
shutil.copytree(item, target_path, dirs_exist_ok=True)
print(f"[COPY DIR] {item} -> {target_path}")
elif item.is_file():
target_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(item, target_path)
print(f"[COPY FILE] {item} -> {target_path}")
def create_debug_data(
dataset_path: str,
output_path: str,
min_frac: float = 0.02,
min_num: int = 10,
):
dataset_root = Path(dataset_path) / "arf-12-hours-prediction-task"
output_root = Path(output_path)
for sub in ["train", "test"]:
input_dir = dataset_root / sub
output_dir = output_root / sub
print(f"\n[PROCESS] {sub} subset")
sample_and_copy_subfolder(
input_dir=input_dir,
output_dir=output_dir,
min_frac=min_frac,
min_num=min_num,
seed=42 if sub == "train" else 123,
)
print(dataset_root.resolve())
print(output_root.resolve())
copy_other_file(source=dataset_root, target=output_root)
print(f"\n[INFO] Sampling complete → Output in:
{output_root}")
if __name__ == "__main__" or globals().get("__name__") == "<run_path>":
dataset_path = globals().get("dataset_path", "./")
output_path = globals().get("output_path", "./sample")
create_debug_data(
dataset_path=dataset_path,
output_path=output_path,
min_frac=0.02,
min_num=10,
)
创建 ds_data/eval/arf-12-hours-prediction-task/valid.py
文件,用于检查提交文件的有效性,以确保其格式与参考文件一致。
以下脚本展示了如何基于 arf-12-hours-prediction-task 数据检查提交的有效性:
ds_data/eval/arf-12-hours-prediction-task/valid.py
from pathlib import Path
# 检查我们的提交文件是否存在
assert Path("submission.csv").exists(), "Error: submission.csv not found"
submission_lines = Path("submission.csv").read_text().splitlines()
test_lines = Path("submission_test.csv").read_text().splitlines()
is_valid = len(submission_lines) == len(test_lines)
if is_valid:
message = "submission.csv and submission_test.csv have the same number of lines."
else:
message = (f"submission.csv has {len(submission_lines)} lines, while submission_test.csv has {len(test_lines)} lines.")
print(message)
if not is_valid:
raise AssertionError("Submission is invalid")
创建 ds_data/eval/arf-12-hours-prediction-task/grade.py
文件,用于根据提交文件和标准答案文件计算得分,并以 JSON 格式输出结果。
以下是基于 arf-12-hours-prediction-task 数据实现的评分脚本:
ds_data/eval/arf-12-hours-prediction-task/grade.py
import json
import pandas as pd
from sklearn.metrics import roc_auc_score
def prepare_for_auroc_metric(submission: pd.DataFrame, answers: pd.DataFrame, id_col: str, target_col: str) -> dict:
# Answers checks
assert id_col in answers.columns, f"answers dataframe should have an {id_col} column"
assert target_col in answers.columns, f"answers dataframe should have a {target_col} column"
# Submission checks
if id_col not in submission.columns:
raise InvalidSubmissionError(f"Submission should have an {id_col} column")
if target_col not in submission.columns:
raise InvalidSubmissionError(f"Submission should have a {target_col} column")
if len(submission) != len(answers):
raise InvalidSubmissionError(f"Submission and answers should have the same number of rows")
try:
pd.to_numeric(submission[target_col])
except ValueError:
raise InvalidSubmissionError(
f"Expected {target_col} column to be numeric, got {submission[target_col].dtype} instead"
)
if submission[target_col].min() < 0 or submission[target_col].max() > 1:
raise InvalidSubmissionError(
f"Submission {target_col} column should contain probabilities,"
" and therefore contain values between 0 and 1 inclusive"
)
# Sort
submission = submission.sort_values(id_col)
answers = answers.sort_values(id_col)
if (submission[id_col].values != answers[id_col].values).any():
raise InvalidSubmissionError(f"Submission and answers should have the same {id_col} values")
roc_auc_inputs = {</p> <p>"y_true": answers[target_col].to_numpy(),</p> <p>"y_score": submission[target_col].to_numpy(),</p> <p>}
return roc_auc_inputs
def grade(submission: pd.DataFrame, answers: pd.DataFrame) -> float:
roc_auc_inputs = prepare_for_auroc_metric(
submission=submission, answers=answers, id_col="ID", target_col="ARF_LABEL"
)
return roc_auc_score(y_true=roc_auc_inputs["y_true"], y_score=roc_auc_inputs["y_score"])
if __name__ == "__main__":
submission_path = "submission.csv"
gt_submission_path = "submission_test.csv"
submission = pd.read_csv(submission_path)
answers = pd.read_csv(gt_submission_path)
score = grade(submission=submission, answers=answers)
print(
json.dumps(
{</p> <p>"competition_id": "arf-12-hours-prediction-task",</p> <p>"score": score,</p> <p>}
)
)
数据集结构与定制
在 RD-Agent 中,一个完整的数据集通常包含竞赛数据、评估数据和原始数据,其结构如下:
ds_data
├── arf-12-hours-prediction-task
│ ├── train
│ │ ├── ARF_12h.csv
│ │ └── X.npz
│ ├── test
│ │ ├── ARF_12h.csv
│ │ └── X.npz
│ ├── description.md
│ ├── sample_submission.csv
│ └── sample.py
├── eval
│ └── arf-12-hours-prediction-task
│ ├── grade.py
│ ├── submission_test.csv
│ └── valid.py
└── source_data
└── arf-12-hours-prediction-task
├── ARF_12h.csv
├── prepare.py
└── X.npz
可定制化选项
在实际操作中,您可以根据需求自定义数据集。并非所有文件都是必需的。
-
如果不需要测试集分数:在
prepare.py
脚本中,您可以选择不生成格式化提交文件和标准答案文件。同时,您也无需编写数据检查代码 (valid.py
) 和分数计算代码 (grade.py
)。 -
数据采样 (
sample.py
):可以根据实际需要创建。如果您不提供自定义的采样代码,RD-Agent 在运行时会将采样任务交给大型语言模型 (LLM) 处理。
默认数据采样方法
RD-Agent 提供了名为 create_debug_data
的默认采样方法。该方法的默认采样比例参数 min_frac
为 1%。如果数据总量的 1% 小于 5(参数 min_num
),则会采样 5 条数据。您可以通过调整这两个参数来控制采样比例。
使用默认采样方法
要使用 RD-Agent 提供的默认采样方法,您需要在 .env
文件中将 DS_SAMPLE_DATA_BY_LLM
设置为 False
(默认值为 True
)。
dotenv set DS_SAMPLE_DATA_BY_LLM False
这样,程序在运行时就会使用 RD-Agent 提供的采样代码。
定制采样参数
如果您觉得默认参数不合适,可以通过以下命令自定义参数并运行:
python rdagent/app/data_science/debug.py --dataset_path
<数据集路径> --competition <竞赛名称> --min_frac <采样比例>
--min_num <最小采样数量>
同时,也需要将 .env
文件中的 DS_SAMPLE_DATA_BY_LLM
设置为 False
。
dotenv set DS_SAMPLE_DATA_BY_LLM False
这样,程序在运行时将使用您提供的采样数据。
最小化数据集结构
如果您不需要测试集分数,并将数据采样交给 LLM 处理,或者使用 RD-Agent 提供的采样方法,那么您只需要准备一个最小化数据集。其结构如下:
ds_data
├── arf-12-hours-prediction-task
│ ├── train
│ │ ├── ARF_12h.csv
│ │ └── X.npz
│ ├── test
│ │ ├── ARF_12h.csv
│ │ └── X.npz
│ └── description.md
└── source_data
└── arf-12-hours-prediction-task
├── ARF_12h.csv
├── prepare.py
└── X.npz
您可以下载我们准备好的数据集作为参考:
环境设置与运行
⚙️ 为自定义数据集设置环境
dotenv set DS_SCEN rdagent.scenarios.data_science.scen.DataScienceScen
dotenv set DS_LOCAL_DATA_PATH <您的本地目录>/ds_data
dotenv set DS_CODER_ON_WHOLE_PIPELINE True
🚀 运行应用程序
您可以使用以下命令直接运行应用程序:
rdagent data_science --competition <竞赛ID>
例如,基于 arf-12-hours-prediction-task 数据运行的命令:
rdagent data_science --competition arf-12-hours-prediction-task
可视化与评分
📈 可视化研发过程
RD-Agent 提供了一个 Web UI 来可视化日志。运行以下命令即可:
rdagent ui --port <自定义端口> --log_dir <您的日志文件夹,例如 "log/"> --data_science True
然后您可以输入日志路径来查看研发过程。
🧪 测试结果评分
最后,关闭程序,并使用以下命令获取测试集分数:
dotenv run -- python rdagent/log/mle_summary.py grade <日志文件夹的URL>
这里的 <日志文件夹的URL>
指的是在运行期间生成的日志文件夹的父目录。
🕹️ Kaggle 智能代理
📖 背景
在数据科学竞赛领域,Kaggle 是数据爱好者们利用算法力量应对现实世界挑战的终极舞台。Kaggle 智能代理是一个关键工具,它赋能参与者无缝整合尖端模型和数据集,将原始数据转化为可操作的洞察。
通过利用 Kaggle 智能代理,数据科学家可以创建创新的解决方案,不仅能发掘隐藏的模式,还能显著提升预测准确性和模型健壮性。
🧭 示例指南 - Kaggle 数据集
🛠️ 准备比赛环境
🔨 配置 Kaggle API
-
在 Kaggle 网站注册并登录。
-
点击头像(通常在页面右上角) -> Settings -> Create New Token,将下载一个名为
kaggle.json
的文件。 -
将
kaggle.json
移动到~/.config/kaggle/
。 -
修改 kaggle.json 文件的权限:
chmod 600 ~/.config/kaggle/kaggle.json
更多关于 Kaggle API 设置的信息,请参阅 Kaggle API。
🔩 在 .env 文件中设置环境变量
确定数据存储路径并将其添加到 .env
文件中。
mkdir -p <您的本地目录>/ds_data
dotenv set KG_LOCAL_DATA_PATH <您的本地目录>/ds_data
🗳️ 加入比赛
如果您的 Kaggle API 账户尚未加入比赛,则需要在运行程序前先加入。
在比赛详情页底部,您可以找到 Join the competition(加入比赛)按钮,点击并选择 I Understand and Accept(我理解并接受)来加入比赛。
在下面的可用比赛列表中,您可以跳转到比赛详情页。
📥 准备竞赛数据与设置 RD-Agent 环境
作为数据科学的一个子集,Kaggle 的数据集仍遵循数据科学的格式。据此,Kaggle 数据集可以根据其是否受 MLE-Bench 支持分为两类。
什么是 MLE-Bench?
MLE-Bench 是一个全面的基准测试,旨在利用真实世界场景来评估 AI 系统的机器学习工程能力。该数据集包含了多个 Kaggle 比赛。由于 Kaggle 没有为这些比赛提供预留的测试集,该基准测试包含了一些准备脚本,用于将公开的训练数据拆分为新的训练集和测试集,以及为每个比赛编写评分脚本以准确评估提交分数。
我正在运行的比赛是否受 MLE-Bench 支持?
您可以在这里查看所有受 MLE-Bench 支持的比赛。
为受 MLE-Bench 支持的比赛准备数据集
如果您认同 MLE-Bench 的标准,则无需准备数据集,只需配置 .env
文件即可自动下载数据集。
-
配置环境变量,将 DS_IF_USING_MLE_DATA 添加到环境变量中,并将其设置为 True。
dotenv set DS_IF_USING_MLE_DATA True
-
配置环境变量,将 DS_SAMPLE_DATA_BY_LLM 添加到环境变量中,并将其设置为 True。
dotenv set DS_SAMPLE_DATA_BY_LLM True
-
配置环境变量,将 DS_SCEN 添加到环境变量中,并将其设置为 rdagent.scenarios.data_science.scen.KaggleScen。
dotenv set DS_SCEN rdagent.scenarios.data_science.scen.KaggleScen
此时,您已准备好开始运行比赛,程序将自动下载数据,LLM 将自动提取最小数据集。
运行程序后,ds_data
文件夹的结构应如下所示(以 tabular-playground-series-dec-2021 比赛为例):
ds_data
├── tabular-playground-series-dec-2021
│ ├── description.md
│ ├── sample_submission.csv
│ ├── test.csv
│ └── train.csv
└── zip_files
└── tabular-playground-series-dec-2021
└── tabular-playground-series-dec-2021.zip
ds_data/zip_files
文件夹包含从 Kaggle 网站下载的原始比赛数据的 zip 文件。
在运行时,RD-Agent 将自动构建 rdagent/scenarios/kaggle/docker/mle_bench_docker/Dockerfile
中指定的 Docker 镜像。该镜像负责下载 MLE-Bench 所需的数据集和评分文件。
注意:首次运行可能会比后续运行耗时更长,因为 Docker 镜像和数据是首次下载和设置。
为不受 MLE-Bench 支持的比赛准备数据集
作为数据科学的一个子集,我们可以遵循数据科学数据集的格式和步骤来准备 Kaggle 数据集。下面我们将以 playground-series-s4e9
比赛为例,描述准备 Kaggle 数据集的工作流。
创建 ds_data/source_data/playground-series-s4e9
文件夹,用于存储您的原始数据集。
playground-series-s4e9
比赛的原始文件包括 train.csv
、test.csv
和 sample_submission.csv
。获取原始数据有两种方法:
-
您可以从 Kaggle 官方网站找到比赛所需的原始数据。
-
或者,您可以使用命令行下载比赛的原始数据,下载命令如下:
kaggle competitions download -c playground-series-s4e9
创建 ds_data/source_data/playground-series-s4e9/prepare.py
文件,该文件将您的原始数据拆分为训练数据、测试数据、格式化提交文件和标准答案文件。(您需要根据您的原始数据编写一个脚本。)
以下是 playground-series-s4e9 原始数据的预处理代码:
ds_data/source_data/playground-series-s4e9/prepare.py
from pathlib import Path
import pandas as pd
from sklearn.model_selection import train_test_split
def prepare(raw: Path, public: Path, private: Path):
# 从训练集创建训练集和测试集拆分
old_train = pd.read_csv(raw / "train.csv")
new_train, new_test = train_test_split(old_train, test_size=0.1, random_state=0)
# 创建样本提交文件
sample_submission = new_test.copy()
sample_submission["price"] = 43878.016
sample_submission.drop(sample_submission.columns.difference(["id", "price"]), axis=1, inplace=True)
sample_submission.to_csv(public / "sample_submission.csv", index=False)
# 创建私有文件
new_test.to_csv(private / "submission_test.csv", index=False)
# 创建对智能代理可见的公共文件
new_train.to_csv(public / "train.csv", index=False)
new_test.drop(["price"], axis=1, inplace=True)
new_test.to_csv(public / "test.csv", index=False)
# 检查
assert new_test.shape[1] == 12, "公共测试集应有 12 列"
assert new_train.shape[1] == 13, "公共训练集应有 13 列"
assert len(new_train) + len(new_test) == len(old_train), "新训练集和新测试集的长度应等于旧训练集的长度"
if __name__ == "__main__":
competitions = "playground-series-s4e9"
raw = Path(__file__).resolve().parent
prepare(
raw=raw,
public=raw.parent.parent / competitions,
private=raw.parent.parent / "eval" / competitions,
)
程序执行结束时,ds_data
文件夹结构将如下所示:
ds_data
├── playground-series-s4e9
│ ├── train.csv
│ ├── test.csv
│ └── sample_submission.csv
├── eval
│ └── playground-series-s4e9
│ └── submission_test.csv
└── source_data
└── playground-series-s4e9
├── prepare.py
├── sample_submission.csv
├── test.csv
└── train.csv
创建 ds_data/playground-series-s4e9/description.md
文件来描述您的比赛、数据集描述以及其他信息。我们可以从 Kaggle 网站找到比赛描述信息和数据集描述信息。
以下是 playground-series-s4e9 的描述文件:
ds_data/playground-series-s4e9/description.md
# Competition name: playground-series-s4e9
## Overview
**欢迎来到 2024 年 Kaggle Playground Series!** 我们计划延续以往比赛的精神,为社区提供有趣且易于上手的赛题,以练习他们的机器学习技能,并预计每月举办一场比赛。
**您的目标:** 本次比赛的目标是根据各种属性预测二手车的价格。
## Evaluation
### Root Mean Squared Error (RMSE)
提交结果将根据均方根误差 (RMSE) 进行评分。RMSE 的定义如下:
$$
\mathrm{RMSE} = \left( \frac{1}{N} \sum_{i=1}^{N} (y_i - \hat{y}_i)^2 \right)^{\frac{1}{2}}
$$
其中 $\hat{y}_i$ 是预测值,$y_i$ 是每个实例 $i$ 的原始值。
### Submission File
对于测试集中的每个
id,您必须预测汽车的
price。提交文件应包含一个标题行,并遵循以下格式:
`id,price`
`188533,43878.0161`
`188534,43878.0161`
`188535,43878.016`
`etc.`
## Timeline
- **开始日期** - 2024 年 9 月 1 日
- **报名截止日期** - 与最终提交截止日期相同
- **团队合并截止日期** - 与最终提交截止日期相同
- **最终提交截止日期** - 2024 年 9 月 30 日
所有截止日期均为相应日期的 UTC 时间 晚上
11:59,除非另有说明。比赛组织者保留在必要时更新比赛时间表的权利。
## About the Tabular Playground Series
Tabular Playground Series 的目标是为 Kaggle
社区提供各种相对轻松的挑战,可用于学习和磨练机器学习和数据科学不同方面的技能。每场比赛的持续时间通常只有几周,也可能根据挑战而延长或缩短。这些挑战通常使用相对轻量级、从真实数据合成的数据集,并提供一个机会来快速迭代各种模型和特征工程思路,创建可视化等。
### Synthetically-Generated Datasets
在 Playground
比赛中使用合成数据,使我们能够在拥有真实世界数据(具有命名的特征)和确保测试标签不公开可用之间取得平衡。这使我们能够举办包含比以往更有趣的数据集的比赛。尽管合成数据生成仍然存在挑战,但与我们两年前开始
Tabular Playground Series
时相比,现在的技术水平已大大提高,我们的目标是生成具有更少人为因素的数据集。请随时就不同比赛的数据集向我们提供反馈,以便我们能够持续改进!
## Prizes
- **第一名** - 获得一件 Kaggle 商品
- **第二名** - 获得一件 Kaggle 商品
- **第三名** - 获得一件 Kaggle 商品
**请注意**:为了鼓励更多初学者参与,本系列赛每人只可获奖一次。如果某人之前已获奖,我们将顺延至下一个团队。
## Citation
Walter Reade and Ashley Chow. Regression of Used Car Prices.
https://kaggle.com/competitions/playground-series-s4e9, 2024.
Kaggle.
## Dataset Description
本次比赛的数据集(训练集和测试集)均由一个在
[二手车价格预测数据集](https://www.kaggle.com/datasets/taeefnajib/used-car-price-prediction-dataset)
上训练的深度学习模型生成。特征分布接近但不完全等同于原始数据集。欢迎使用原始数据集作为本次比赛的一部分,既可以探索差异,也可以看看在训练中加入原始数据是否能提高模型性能。
## Files
- **train.csv** - 训练数据集;price 是连续的目标变量
- **test.csv** - 测试数据集;您的目标是预测每一行的 price 值
- **sample_submission.csv** - 一个符合正确格式的样本提交文件
创建 ds_data/eval/playground-series-s4e9/valid.py
文件,用于检查提交文件的有效性,以确保其格式与参考文件一致。
以下脚本展示了如何基于 playground-series-s4e9 数据检查提交的有效性:
ds_data/eval/playground-series-s4e9/valid.py
from pathlib import Path
# 检查我们的提交文件是否存在
assert Path("submission.csv").exists(), "Error: submission.csv not found"
submission_lines = Path("submission.csv").read_text().splitlines()
test_lines = Path("submission_test.csv").read_text().splitlines()
is_valid = len(submission_lines) == len(test_lines)
if is_valid:
message = "submission.csv and submission_test.csv have the same number of lines."
else:
message = (f"submission.csv has {len(submission_lines)} lines, while submission_test.csv has {len(test_lines)} lines.")
print(message)
if not is_valid:
raise AssertionError("Submission is invalid")
创建 ds_data/eval/playground-series-s4e9/grade.py
文件,用于根据提交文件和标准答案文件计算得分,并以 JSON 格式输出结果。
以下是基于 playground-series-s4e9 数据实现的评分脚本:
ds_data/eval/playground-series-s4e9/grade.py
import datetime
import json
import numpy as np
import pandas as pd
from sklearn.metrics import mean_absolute_error, mean_squared_error, roc_auc_score
class InvalidSubmissionError(Exception):
"""
当智能代理的提交无法评分时,自定义的异常。
"""
pass
def prepare_for_metric(submission: pd.DataFrame, answers:
pd.DataFrame) -> dict:
if "id" not in submission.columns or "id" not in answers.columns:
raise InvalidSubmissionError("Both submission and answers DataFrames must contain an 'id' column.")
if "price" not in submission.columns:
raise InvalidSubmissionError("Submission DataFrame must contain 'price' columns.")
assert "price" in answers.columns, "Answers DataFrame must
contain 'price' columns."
if len(submission) != len(answers):
raise InvalidSubmissionError("Submission must be the same length as the answers.")
answers_sorted = answers.sort_values("id")
submission_sorted = submission.sort_values("id")
if (submission_sorted["id"].values != answers_sorted["id"].values).any():
raise InvalidSubmissionError("Submission and answers have mismatched 'id' columns")
y_true = answers_sorted[["price"]].to_numpy()
y_score = submission_sorted[["price"]].to_numpy()
return {"y_true": y_true, "y_score": y_score}
def grade(submission: pd.DataFrame, answers: pd.DataFrame) -> float:
metric_inputs = prepare_for_metric(submission, answers)
return np.sqrt(mean_squared_error(metric_inputs["y_true"], metric_inputs["y_score"]))
if __name__ == "__main__":
submission_path = "submission.csv"
gt_submission_path = "submission_test.csv"
submission = pd.read_csv(submission_path)
answers = pd.read_csv(gt_submission_path)
score = grade(submission=submission, answers=answers)
# 此thresholds 可以根据 Kaggle 网站的排行榜页面和您自己的需求进行自定义。
# 参考:https://www.kaggle.com/competitions/playground-series-s4e9/leaderboard
thresholds = {</p> <p>"gold": 62917.05988,</p> <p>"silver": 62945.91714,</p> <p>"bronze": 62958.13747,</p> <p>"median": 63028.69429,</p> <p>}
# 输出必须是 json 格式。要配置完整输出,
# 您可以运行rdagent grade_summary --log_folder 命令在程序结束时汇总分数。
# 如果您不需要,只需提供competition_id和score。
print(
json.dumps(
{</p> <p>"competition_id": "arf-12-hours-prediction-task",</p> <p>"score": score,</p> <p>"gold_threshold": thresholds["gold"],</p> <p>"silver_threshold": thresholds["silver"],</p> <p>"bronze_threshold": thresholds["bronze"],</p> <p>"median_threshold": thresholds["median"],</p> <p>"any_medal": bool(score >= thresholds["bronze"]),</p> <p>"gold_medal": bool(score >= thresholds["gold"]),</p> <p>"silver_medal": bool(score >= thresholds["silver"]),</p> <p>"bronze_medal": bool(score >= thresholds["bronze"]),</p> <p>"above_median": bool(score >= thresholds["median"]),</p> <p>"submission_exists": True,</p> <p>"valid_submission": True,</p> <p>"is_lower_better": False,</p> <p>"created_at": str(datetime.datetime.now().isoformat()),</p> <p>"submission_path": submission_path,</p> <p>}
)
)
在这个例子中,我们不创建 ds_data/eval/playground-series-s4e9/sample.py
,而是默认使用 RD-Agent 提供的样本方法。
至此,您已创建了一个完整的数据集。正确的数据集结构应如下所示:
ds_data
├── playground-series-s4e9
│ ├── train.csv
│ ├── test.csv
│ ├── description.md
│ └── sample_submission.csv
├── eval
│ └── playground-series-s4e9
│ ├── grade.py
│ ├── submission_test.csv
│ └── valid.py
└── source_data
└── playground-series-s4e9
├── prepare.py
├── sample_submission.csv
├── test.csv
└── train.csv
我们已根据上述描述准备了一个数据集供您参考。您可以使用以下命令下载:
wget https://github.com/SunsetWolf/rdagent_resource/releases/download/ds_data/playground-series-s4e9.zip
接下来,我们需要为 playground-series-s4e9 比赛配置环境。您可以通过在命令行执行以下命令来完成:
dotenv set DS_IF_USING_MLE_DATA False
dotenv set DS_SAMPLE_DATA_BY_LLM False
dotenv set DS_SCEN rdagent.scenarios.data_science.scen.KaggleScen
🚀 运行应用程序
🌏 您可以通过使用以下命令直接运行应用程序:
rdagent data_science --competition <比赛ID>
以下是基于 playground-series-s4e9 数据运行的命令:
rdagent data_science --competition playground-series-s4e9
📈 可视化研发过程
我们提供了一个 Web UI 来可视化日志。您只需运行:
rdagent ui --port <自定义端口> --log_dir <您的日志文件夹,例如 "log/"> --data_science True
然后您可以输入日志路径并可视化研发过程。
🧪 测试结果评分
最后,关闭程序,并使用此命令获取测试集分数:
dotenv run -- python rdagent/log/mle_summary.py grade <日志文件夹的URL>
如果您在 ds_data/eval/playground-series-s4e9/grade.py 中配置了完整输出,或者您正在运行一个获得 MLE-Bench 支持的比赛,您还可以通过运行以下命令来汇总分数:
rdagent grade_summary --log_folder=<日志文件夹的URL>
此处,<日志文件夹的URL>
指的是在运行期间生成的日志文件夹的父目录。
仅限高级开发人员。学习构建数字商务架构。参加免费研讨会。限 16 个名额。