目录
Update 2024.11.14 可随意转载
一、研究背景
研究者常常阅读英文论文并进行编程验证来寻找潜在的研究方向,一般来说,这一过程工作负担较重。过去十年间,随着科学的发展,实验也越来越复杂,研究者阅读论文和编程验证的负担日益加剧,不利于从大量的论文中找出有价值的点。因此,自动化这一Reasearch & Development(R&D)流程变得很迫切。
二、研究框架和研究方法
2.1 研究框架
标签为“r”的模块根据预设的模板阅读文档,并生成任务(ModelTask)。
标签为’d’的模块共有两个部分:
- 组合任务、RAG、知识图谱等文字内容,与GPT-4对话,得到反馈和代码
- 根据反馈和代码部署docker运行环境,并评估它们。
2.2 研究方法
将pdf内容格式化为json,并生成研究代码,读取数据集,并通过多轮循环修改bug,提出假设,作出评价最终得到验证结果。
2.3 研究成果展示
fin_model
三、源码阅读
https://github.com/microsoft/RD-Agent
vscode运行配置如下:
{
"version": "0.2.0",
"configurations": [
{
"name": "Python:Algorithem",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/rdagent/app/cli.py",
"console": "integratedTerminal",
"args": [
"general_model", "https://arxiv.org/pdf/2210.09789"
],
"env": {
"PYTHONPATH": "${workspaceFolder}"
}
},
{
"name": "Python:fin_factor",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/rdagent/app/cli.py",
"console": "integratedTerminal",
"args": [
"fin_factor"
],
"env": {
"PYTHONPATH": "${workspaceFolder}"
}
},
{
"name": "Python:UI",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/rdagent/app/cli.py",
"console": "integratedTerminal",
"args": [
"ui"
],
"env": {
"PYTHONPATH": "${workspaceFolder}"
}
}
]
}
3.1 general_model任务源码
任务目标:读取指定论文,并写出论文研究课题对应的代码,并且运行代码,评估代码的效果。
在rdagent/app/general_model/general_model.py文件中。
初始化代码(tag init):
with logger.tag("init"):
scenario = GeneralModelScenario()
logger.log_object(scenario, tag="scenario")
主要是用来加载预先设定的prompt,是项目作这精挑细选的。包括以下主要提示词:
- background
- interface
- output_format
- rich_style_description
- simulator
- source_data
Reasearch部分代码(tag r):
with logger.tag("r"):
# Save Relevant Images
img = extract_first_page_screenshot_from_pdf(report_file_path)
logger.log_object(img, tag="pdf_image")
exp = ModelExperimentLoaderFromPDFfiles().load(report_file_path)
logger.log_object(exp, tag="load_experiment")
用来提取pdf中的内容,并为了下一步模型使用,格式化成model_dict,代码如下:
# rdagent/components/coder/model_coder/task_loader.py
def load(self, file_or_folder_path: str) -> dict:
docs_dict = load_and_process_pdfs_by_langchain(file_or_folder_path) # dict{file_path:content}
model_dict = extract_model_from_docs(
docs_dict
) # dict{file_name: dict{model_name: dict{description, formulation, variables}}}
model_dict = merge_file_to_model_dict_to_model_dict(
model_dict
) # dict {model_name: dict{description, formulation, variables}}
return ModelExperimentLoaderFromDict().load(model_dict)
再次转换格式为ModelTask:
# rdagent/components/coder/model_coder/task_loader.py
def load(self, model_dict: dict) -> list:
"""Load data from a dict."""
task_l = []
for model_name, model_data in model_dict.items():
task = ModelTask(
name=model_name,
description=model_data["description"],
formulation=model_data["formulation"],
architecture=model_data["architecture"],
variables=model_data["variables"],
hyperparameters=model_data["hyperparameters"],
model_type=model_data["model_type"],
)
task_l.append(task)
return QlibModelExperiment(sub_tasks=task_l)
Development部分代码(tag d):
先初始化类QlibModelCoSTEER
# rdagent/components/coder/model_coder/CoSTEER/__init__.py
初始化参数:max_loop,knowledge_base_path,new_knowledge_base_path,with_knowledge,with_feedback,knowledge_self_gen,filter_final_evo,evolving_strategy,model_evaluator
再将ModelTask的封装传入develop方法,方法中调用multistep_evolve方法,执行下面的代码:
for _ in tqdm(range(self.max_loop), "Implementing"):
...
# 3. evolve
evo = self.evolving_strategy.evolve(
evo=evo,
evolving_trace=self.evolving_trace,
queried_knowledge=queried_knowledge,
)
...
return evo
然后evolve会调用implement_one_model方法,构造上下文并和GPT-4的对话:
# rdagent/components/coder/model_coder/CoSTEER/evolving_strategy.py
def implement_one_model(...):
model_information_str = target_task.get_task_information()
model_type = target_task.model_type
...
queried_former_failed_knowledge_to_render = queried_former_failed_knowledge
system_prompt = (...)
...
for _ in range(10): # max attempt to reduce the length of user_prompt
user_prompt = (...)
...
code = json.loads(
APIBackend(use_chat_cache=MODEL_IMPL_SETTINGS.coder_use_cache)
.build_messages_and_create_chat_completion(
user_prompt=user_prompt,
system_prompt=system_prompt,
json_mode=True,
),
)["code"]
return code
最后,建立docker镜像并把代码插入进入,然后运行镜像评估GPT-4编写的代码正确与否:
# 5. Evaluation
if self.with_feedback:
es.feedback = (
eva
if isinstance(eva, Feedback)
else eva.evaluate(evo, queried_knowledge=queried_knowledge)
)
logger.log_object(es.feedback, tag="evolving feedback")
3.2 fin_factor任务源码
任务目标:持续挖掘金融量化因子。
rdagent/app/qlib_rd_loop/factor.py中的代码:
def main(path=None, step_n=None):
"""
自动进行金融科技的研发迭代循环
"""
if path is None:
model_loop = FactorRDLoop(FACTOR_PROP_SETTING)
else:
model_loop = FactorRDLoop.load(path)
model_loop.run(step_n=step_n)
其中FactorRDLoop初始化调用了RDLoop类的__init__方法,初始化了下面变量:
其中run方法会按照steps里面定义的workflow各个阶段逐一执行。
def run(self, step_n: int | None = None):
with tqdm(total=len(self.steps), desc="Workflow Progress", unit="step") as pbar:
while True:
...
# 取出每个step的名字,通过名字动态调用对应的方法。
name = self.steps[si]
func = getattr(self, name)
try:
self.loop_prev_out[name] = func(self.loop_prev_out)
except self.skip_loop_error as e:
按设定好的workflow:[‘propose’, ‘exp_gen’, ‘coding’, ‘running’, ‘feedback’] 执行对应的代码:
@measure_time
def propose(self, prev_out: dict[str, Any]):
with logger.tag("r"): # research
hypothesis = self.hypothesis_gen.gen(self.trace)
logger.log_object(hypothesis, tag="hypothesis generation")
return hypothesis
@measure_time
def exp_gen(self, prev_out: dict[str, Any]):
with logger.tag("r"): # research
exp = self.hypothesis2experiment.convert(prev_out["propose"], self.trace)
logger.log_object(exp.sub_tasks, tag="experiment generation")
return exp
@measure_time
def coding(self, prev_out: dict[str, Any]):
with logger.tag("d"): # develop
exp = self.coder.develop(prev_out["exp_gen"])
logger.log_object(exp.sub_workspace_list, tag="coder result")
return exp
@measure_time
def running(self, prev_out: dict[str, Any]):
with logger.tag("ef"): # evaluate and feedback
exp = self.runner.develop(prev_out["coding"])
logger.log_object(exp, tag="runner result")
return exp
@measure_time
def feedback(self, prev_out: dict[str, Any]):
feedback = self.summarizer.generate_feedback(prev_out["running"], prev_out["propose"], self.trace)
with logger.tag("ef"): # evaluate and feedback
logger.log_object(feedback, tag="feedback")
self.trace.hist.append((prev_out["propose"], prev_out["running"], feedback))
比如propose阶段的GPT-4上下文简化如下:
[
{
"role": "system",
"content": "用户正在为数据驱动的量化投资研究开发假设,这些因子用于解释投资组合或单一资产的回报和风险。因子帮助识别超额回报,并是量化策略的核心。用户的模型将基于过去的因子值预测未来几天的回报。每个因子包含:1) 名称,2) 描述,3) 公式,4) 变量。每个因子定义静态输出和固定数据源。例如,10天和20天的动量应视为不同因子。数据源包括 daily_pv.h5,包含一段时间内的股票价格和成交量数据。\n\n代码接口应包括:导入部分、函数部分和主函数部分,主函数命名为 calculate_{function_name}。Python代码应将因子值保存为 result.h5 文件,文件内容为一个按日期和股票代码索引的 pandas DataFrame。用户将使用 Qlib 进行模型训练和投资组合评估。用户已提出一些假设,任务是验证并改进这些假设。开始时因子应简单,之后逐步增加复杂度,每次生成 1-3 个因子。确保每个假设符合给定的指引。\n\n输出格式:JSON,包含假设、推理、简明理由、观察、论证和知识等信息。"
},
{
"role": "user",
"content": "生成推理和提炼知识的相关键,特别是在领域特定的背景下解释,而非泛泛的理论知识。"
}
]
比如exp_gen阶段的GPT-4上下文简化如下:
[{
"role": "system",
"content": "用户生成因子用于量化投资,关注组合的回报与风险。因子用于训练模型,基于过去的数据预测回报。因子包含名称、描述、公式和变量。每个因子定义一个输出,使用特定的数据集。数据包括每日的价格和成交量(开盘价、收盘价、最高价、最低价、成交量、因子值)以HDF5格式存储。Python代码应计算并将因子结果保存到一个HDF5文件,文件中使用日期时间和证券代码作为索引,因子值作为唯一列。Qlib将用于评估和基于因子构建投资组合。输出格式为JSON,包含因子的详细信息。"
}, {
"role": "user",
"content": "生成因子的目标假设是:引入一个30日的MACD因子,基于12日和26日指数加权移动平均(EMA)收盘价的差值。MACD有助于识别短期动量和趋势变化,是未来价格走势的有效预测指标。简洁知识:MACD结合短期和长期EMA,能够信号未来价格变化的方向。"
}]
比如coding阶段的GPT-4上下文简化如下:
[
{
"role": "system",
"content": "用户正在实现量化投资中的因子,因子用于解释资产或投资组合的回报与风险。用户将基于前几天的因子值训练模型预测未来几天的回报。因子包括名称、描述、公式和变量,可能不完全包含所有部分。因子应静态定义一个输出和数据源。代码需按照给定接口格式编写,并保存结果为HDF5文件。输出的DataFrame包含时间戳和证券代码,因子值为数据列。"
},
{
"role": "user",
"content": "目标因子:MACD_30,描述:基于12日与26日指数移动平均(EMA)差值的30日MACD,用于预测未来回报。公式:MACD_{30} = EMA_{12}(Close) - EMA_{26}(Close)。变量:EMA_{12}为12日EMA,EMA_{26}为26日EMA,Close为收盘价。"
}
]
coding包括子阶段:evolve,它GPT-4上下文简化如下:
[
{
"role": "system",
"content": "用户正在实现量化投资中的因子。因子用于解释资产或投资组合的回报和风险,帮助识别超额回报来源,是量化策略的核心。每个因子在特定日期对一个工具产生物理值。用户将训练模型,基于历史因子值预测未来回报。因子包括:名称、描述、公式和变量,并指定如窗口大小和回溯期等超参数。不同的因子应基于静态数据计算,例如‘过去10天的动量’和‘过去20天的动量’应作为不同的因子。源数据为 daily_pv.h5,包含多个工具的每日价格和因子值。结果应保存在 ‘result.h5’ 文件中。用户的代码将计算因子值并保存为 HDF5 格式,以便后续在 Qlib 中进行模型训练,预测回报并评估投资组合表现。"
},
{
"role": "user",
"content": "目标因子信息:因子名称:Signal_Line_9。描述:MACD的9日信号线,用于平滑值并识别买卖信号。公式:Signal_{9} = EMA_{9}(MACD_{30})。变量:EMA_{9}为MACD的9日指数移动平均,MACD_{30}为30日MACD。"
}
]
之后,它会调用FactorEvolvingStrategyWithGraph类中的方法implement_one_factor,而这个方法的GPT-4上下文简化如下:
[
{
"role": "system",
"content": "用户正在为量化投资实现因子。因子帮助解释投资组合的收益和风险。用户将训练模型,根据历史因子预测收益。每个因子包括名称、描述、公式和变量。因子应具有窗口大小和回溯期等超参数。数据来源是'daily_pv.h5'文件。用户编写代码计算因子值并保存在'result.h5'文件中。代码需遵循接口,生成因子、训练模型、评估投资组合表现,使用Qlib平台进行。"
},
{
"role": "user",
"content": {
"factor_name": "MACD_Histogram",
"factor_description": "MACD柱状图,衡量MACD与信号线之间的距离。",
"factor_formulation": "MACDHist = MACD_{30} - Signal_{9}",
"variables": {
"MACD_{30}": "30日移动平均收敛/发散指标。",
"Signal_{9}": "MACD的9日信号线。"
}
}
}
]
coding包括子阶段:evaluate(多个),它GPT-4上下文简化如下:
{
"背景": {
"描述": "用户正在实现量化投资中的因子,用于根据因子值预测资产的回报。因子由名称、描述、公式和变量定义,帮助解释投资组合或单一资产的回报和风险。",
"数据格式": {
"类型": "HDF5",
"列": ["$open", "$close", "$high", "$low", "$volume", "$factor"],
"数据": "调整后的每日价格和成交量数据,使用多重索引: (datetime, instrument)"
},
"任务": {
"模型": "基于因子值训练模型来预测回报。",
"文件": "result.h5",
"输出格式": "DataFrame,使用多重索引(datetime, instrument),单列存储因子值。"
}
},
"接口": {
"python代码": "代码必须包含导入部分、函数部分和主函数部分,主函数名为 'calculate_{function_name}',并将输出保存到 'result.h5'。",
"要求": "因子的计算应针对特定周期,比如 '过去10天动量' 和 '过去20天动量' 应定义为不同因子。"
},
"因子数据": {
"示例输出": {
"数据框": {
"多重索引": "(Timestamp, 'instrument')",
"列": "因子名称(单列)",
"数据类型": "float64"
},
"模拟": "因子用于在Qlib中训练模型,预测回报、管理投资组合并评估性能。"
}
},
"用户状态": {
"任务": "用户正在处理与某个特性相关的任务。",
"输出示例": {
"数据框信息": {
"多重索引": "48700条记录,(Timestamp('2018-01-02 00:00:00'), 'SH000300') 到 (Timestamp('2019-12-31 00:00:00'), 'SH600121')",
"列": "MACD_30",
"非空计数": 48548,
"数据类型": "float64"
}
}
}
}
running阶段不会调用GPT-4,它会创建docker并运行
比如feedback阶段的GPT-4上下文简化如下:
{
"Observations": "新因子(MACD_30、Signal_Line_9 和 MACD_Histogram)在年化回报和信息比率上优于 SOTA,但最大回撤有所恶化。IC 值在当前和 SOTA 结果之间相似。",
"Feedback for Hypothesis": "使用 30 天 MACD 预测未来回报的假设得到了支持,因为这些因子在年化回报和信息比率上表现出色。然而,较高的最大回撤表明在降低风险方面仍需进一步优化。",
"New Hypothesis": "通过整合波动率调整的移动平均线或加入额外的平滑技术来优化 MACD 因子,以在降低风险的同时保持高回报。",
"Reasoning": "尽管年化回报和信息比率有所提升,但较高的最大回撤表明风险管理需要改进。通过对 MACD 加入波动率调整或平滑方法,可以在不牺牲回报的情况下减少风险。",
"Replace Best Result": "yes"
}