fix: model/factor experiment filtering in Qlib proposals (#1257)

* Fix model/factor experiment filtering in Qlib proposals

* fix(dockerfile): install coreutils to resolve timeout command error (#1260)

* chore: remove unused experiment_list and target_list from Qlib proposals

* fix CI

* fix target list

---------

Co-authored-by: Xu Yang <peteryang@vip.qq.com>
This commit is contained in:
Utsab Dahal
2025-10-10 20:34:46 +05:45
committed by GitHub
parent 35580cbdf8
commit 9e34b4e855
7 changed files with 155 additions and 64 deletions
@@ -64,13 +64,14 @@ class QlibFactorHypothesis2Experiment(FactorHypothesis2Experiment):
scenario = trace.scen.get_scenario_all_desc(action="factor")
else:
scenario = trace.scen.get_scenario_all_desc()
experiment_output_format = T("scenarios.qlib.prompts:factor_experiment_output_format").r()
if len(trace.hist) == 0:
hypothesis_and_feedback = "No previous hypothesis and feedback available since it's the first round."
else:
specific_trace = Trace(trace.scen)
for i in range(len(trace.hist) - 1, -1, -1): # Reverse iteration
for i in range(len(trace.hist) - 1, -1, -1):
if not hasattr(trace.hist[i][0].hypothesis, "action") or trace.hist[i][0].hypothesis.action == "factor":
specific_trace.hist.insert(0, trace.hist[i])
if len(specific_trace.hist) > 0:
@@ -81,18 +82,12 @@ class QlibFactorHypothesis2Experiment(FactorHypothesis2Experiment):
else:
hypothesis_and_feedback = "No previous hypothesis and feedback available."
experiment_list: List[FactorExperiment] = [t[0] for t in trace.hist]
factor_list = []
for experiment in experiment_list:
factor_list.extend(experiment.sub_tasks)
return {
"target_hypothesis": str(hypothesis),
"scenario": scenario,
"hypothesis_and_feedback": hypothesis_and_feedback,
"experiment_output_format": experiment_output_format,
"target_list": factor_list,
"target_list": [],
"RAG": None,
}, True
@@ -114,10 +109,11 @@ class QlibFactorHypothesis2Experiment(FactorHypothesis2Experiment):
)
exp = QlibFactorExperiment(tasks, hypothesis=hypothesis)
exp.based_experiments = [QlibFactorExperiment(sub_tasks=[])] + [t[0] for t in trace.hist if t[1]]
exp.based_experiments = [QlibFactorExperiment(sub_tasks=[])] + [
t[0] for t in trace.hist if t[1] and isinstance(t[0], FactorExperiment)
]
unique_tasks = []
for task in tasks:
duplicate = False
for based_exp in exp.based_experiments:
@@ -87,7 +87,7 @@ class QlibModelHypothesis2Experiment(ModelHypothesis2Experiment):
hypothesis_and_feedback = "No previous hypothesis and feedback available since it's the first round."
else:
specific_trace = Trace(trace.scen)
for i in range(len(trace.hist) - 1, -1, -1): # Reverse iteration
for i in range(len(trace.hist) - 1, -1, -1):
if not hasattr(trace.hist[i][0].hypothesis, "action") or trace.hist[i][0].hypothesis.action == "model":
if last_experiment is None:
last_experiment = trace.hist[i][0]
@@ -120,12 +120,6 @@ class QlibModelHypothesis2Experiment(ModelHypothesis2Experiment):
else "No SOTA hypothesis and feedback available since previous experiments were not accepted."
)
experiment_list: List[ModelExperiment] = [t[0] for t in trace.hist]
model_list = []
for experiment in experiment_list:
model_list.extend(experiment.sub_tasks)
return {
"target_hypothesis": str(hypothesis),
"scenario": scenario,
@@ -133,7 +127,7 @@ class QlibModelHypothesis2Experiment(ModelHypothesis2Experiment):
"last_hypothesis_and_feedback": last_hypothesis_and_feedback,
"SOTA_hypothesis_and_feedback": sota_hypothesis_and_feedback,
"experiment_output_format": experiment_output_format,
"target_list": model_list,
"target_list": [],
"RAG": "Note, the training data consists of less than 1 million samples for the training set and approximately 250,000 samples for the validation set. Please design the hyperparameters accordingly and control the model size. This has a significant impact on the training results. If you believe that the previous model itself is good but the training hyperparameters or model hyperparameters are not optimal, you can return the same model and adjust these parameters instead.",
}, True
@@ -161,5 +155,5 @@ class QlibModelHypothesis2Experiment(ModelHypothesis2Experiment):
)
)
exp = QlibModelExperiment(tasks, hypothesis=hypothesis)
exp.based_experiments = [t[0] for t in trace.hist if t[1]]
exp.based_experiments = [t[0] for t in trace.hist if t[1] and isinstance(t[0], ModelExperiment)]
return exp
+8 -11
View File
@@ -1,24 +1,21 @@
import argparse
import os
import random
import sys
import time
import random
import albumentations as A
import cv2
import numpy as np
import pandas as pd
import timm
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
import timm
import albumentations as A
from albumentations.pytorch import ToTensorV2
from sklearn.metrics import confusion_matrix, roc_auc_score
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import roc_auc_score, confusion_matrix
import cv2
import argparse
from torch.utils.data import DataLoader, Dataset
parser = argparse.ArgumentParser()
parser.add_argument('--debug', action='store_true', help='Run in debug mode')
+10 -12
View File
@@ -1,24 +1,22 @@
import os
import time
import argparse
import os
import random
import numpy as np
import pandas as pd
from PIL import Image
import time
from glob import glob
import albumentations as A
import cv2
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
import torchvision
import albumentations as A
from albumentations.pytorch import ToTensorV2
import cv2
from sklearn.model_selection import StratifiedShuffleSplit
from PIL import Image
from sklearn.metrics import log_loss
from sklearn.model_selection import StratifiedShuffleSplit
from torch.utils.data import DataLoader, Dataset
# ========= Debug mode handling ==========
parser = argparse.ArgumentParser()
@@ -233,7 +231,7 @@ def main():
class EfficientNetB0_4ch(nn.Module):
def __init__(self, pretrained=True):
super().__init__()
from torchvision.models import efficientnet_b0, EfficientNet_B0_Weights
from torchvision.models import EfficientNet_B0_Weights, efficientnet_b0
if pretrained:
wts = EfficientNet_B0_Weights.DEFAULT
net = efficientnet_b0(weights=wts)
@@ -1,24 +1,21 @@
import argparse
import os
import random
import sys
import time
import random
import albumentations as A
import cv2
import numpy as np
import pandas as pd
import timm
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
import timm
import albumentations as A
from albumentations.pytorch import ToTensorV2
from sklearn.metrics import confusion_matrix, roc_auc_score
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import roc_auc_score, confusion_matrix
import cv2
import argparse
from torch.utils.data import DataLoader, Dataset
parser = argparse.ArgumentParser()
parser.add_argument('--debug', action='store_true', help='Run in debug mode')
@@ -1,24 +1,21 @@
import argparse
import os
import random
import sys
import time
import random
import albumentations as A
import cv2
import numpy as np
import pandas as pd
import timm
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import Dataset, DataLoader
import timm
import albumentations as A
from albumentations.pytorch import ToTensorV2
from sklearn.metrics import confusion_matrix, roc_auc_score
from sklearn.model_selection import StratifiedKFold
from sklearn.metrics import roc_auc_score, confusion_matrix
import cv2
import argparse
from torch.utils.data import DataLoader, Dataset
parser = argparse.ArgumentParser()
parser.add_argument('--debug', action='store_true', help='Run in debug mode')
+112
View File
@@ -0,0 +1,112 @@
from unittest.mock import Mock, patch
import pytest
from rdagent.core.proposal import Hypothesis, Trace
from rdagent.scenarios.qlib.proposal.factor_proposal import (
QlibFactorHypothesis2Experiment,
)
from rdagent.scenarios.qlib.proposal.model_proposal import (
QlibModelHypothesis2Experiment,
)
@pytest.fixture
def mixed_model_trace():
trace = Trace(scen=Mock())
model_task = Mock()
model_task.name = "model_task_1"
factor_task = Mock()
factor_task.name = "factor_task_1"
trace.hist = [
(Mock(sub_tasks=[model_task], hypothesis=Mock(action="model")), Mock()),
(Mock(sub_tasks=[factor_task], hypothesis=Mock(action="factor")), Mock()),
]
return trace
@pytest.fixture
def mixed_factor_trace():
trace = Trace(scen=Mock())
factor_task = Mock()
factor_task.factor_name = "factor_task_1"
model_task = Mock()
model_task.name = "model_task_1"
trace.hist = [
(Mock(sub_tasks=[factor_task], hypothesis=Mock(action="factor")), Mock()),
(Mock(sub_tasks=[model_task], hypothesis=Mock(action="model")), Mock()),
]
return trace
def test_model_proposal_import():
assert QlibModelHypothesis2Experiment is not None
def test_factor_proposal_import():
assert QlibFactorHypothesis2Experiment is not None
def test_model_filtering(mixed_model_trace):
converter = QlibModelHypothesis2Experiment()
hypothesis = Hypothesis(
hypothesis="test",
reason="r",
concise_reason="cr",
concise_observation="co",
concise_justification="cj",
concise_knowledge="ck",
)
with patch("rdagent.utils.agent.tpl.T.r", return_value="mocked"):
context, ok = converter.prepare_context(hypothesis, mixed_model_trace)
target_list = context.get("target_list", [])
assert ok is True
names = [getattr(task, "name", "") for task in target_list]
assert all("model" in name for name in names)
def test_factor_filtering(mixed_factor_trace):
converter = QlibFactorHypothesis2Experiment()
hypothesis = Hypothesis(
hypothesis="test",
reason="r",
concise_reason="cr",
concise_observation="co",
concise_justification="cj",
concise_knowledge="ck",
)
with patch("rdagent.utils.agent.tpl.T.r", return_value="mocked"):
context, ok = converter.prepare_context(hypothesis, mixed_factor_trace)
target_list = context.get("target_list", [])
assert ok is True
factor_names = [getattr(task, "factor_name", "") for task in target_list]
assert all("factor" in name for name in factor_names)
@pytest.mark.parametrize(
"converter_class, trace_fixture, expected_type",
[
(QlibModelHypothesis2Experiment, "mixed_model_trace", "ModelExperiment"),
(QlibFactorHypothesis2Experiment, "mixed_factor_trace", "FactorExperiment"),
],
)
def test_code_inspection(converter_class, trace_fixture, request, expected_type):
converter = converter_class()
trace = request.getfixturevalue(trace_fixture)
hypothesis = Hypothesis(
hypothesis="test",
reason="r",
concise_reason="cr",
concise_observation="co",
concise_justification="cj",
concise_knowledge="ck",
)
with patch("rdagent.utils.agent.tpl.T.r", return_value="mocked"):
context, ok = converter.prepare_context(hypothesis, trace)
target_list = context.get("target_list", [])
assert ok is True
if target_list:
assert target_list[0].__class__.__name__ == expected_type