PipeGraphPy 2.0.23__py3-none-win_amd64.whl → 3.0.5__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- PipeGraphPy/__init__.py +17 -4
- PipeGraphPy/__main__.py +430 -0
- PipeGraphPy/common.py +2 -1
- PipeGraphPy/config/__init__.py +3 -3
- PipeGraphPy/config/default_settings.py +30 -11
- PipeGraphPy/constants.py +158 -164
- PipeGraphPy/core/anchor.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/edge.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/graph.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/graph_base.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/base.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/branchselect.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/classifier.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/cluster.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/common.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/datacharts.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/deeplearning.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/endscript.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/ensemble.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/evaluate.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/exportdata.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/handlescript.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/importdata.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/merge.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/mergescript.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/metrics.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/postprocessor.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/preprocessor.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/pythonscript.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/regressor.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/selector.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/selectscript.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/special.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/split.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/splitscript.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/startscript.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modcls/transformer.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/module.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/modules/DataCharts/__init__.py +3 -0
- PipeGraphPy/core/modules/ImportData/__init__.py +1 -0
- PipeGraphPy/core/modules/Preprocessor/__init__.py +5 -0
- PipeGraphPy/core/modules/Preprocessor/sqlfilter.py +41 -0
- PipeGraphPy/core/modules/Special/__init__.py +6 -0
- PipeGraphPy/core/modules/Special/emptydata.py +28 -0
- PipeGraphPy/core/modules/Special/graphmod.py +39 -0
- PipeGraphPy/core/modules/Transformer/__init__.py +3 -0
- PipeGraphPy/core/modules/__init__.py +0 -63
- PipeGraphPy/core/modules/merge/mergemany.py +97 -0
- PipeGraphPy/core/modules/merge/mergetwo.py +81 -0
- PipeGraphPy/core/node.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/core/pipegraph.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/db/models.cp310-win_amd64.pyd +0 -0
- PipeGraphPy/db/utils.py +231 -43
- PipeGraphPy/logger.py +61 -16
- PipeGraphPy/plot/draw.py +2 -2
- PipeGraphPy/storage/__init__.py +10 -2
- PipeGraphPy/storage/base.py +2 -0
- PipeGraphPy/storage/file_backend.py +99 -90
- PipeGraphPy/tools.py +122 -143
- PipeGraphPy/utils/file_operate.py +24 -10
- PipeGraphPy/utils/format.py +102 -37
- PipeGraphPy/utils/str_handle.py +32 -31
- pipegraphpy-3.0.5.dist-info/METADATA +18 -0
- pipegraphpy-3.0.5.dist-info/RECORD +94 -0
- {pipegraphpy-2.0.23.dist-info → pipegraphpy-3.0.5.dist-info}/WHEEL +1 -1
- PipeGraphPy/core/modules/branchselect/__ini__.py +0 -0
- PipeGraphPy/core/modules/datacharts/__init__.py +0 -5
- PipeGraphPy/storage/dict_backend.py +0 -102
- PipeGraphPy/storage/redis_backend.py +0 -183
- PipeGraphPy/utils/check.py +0 -180
- PipeGraphPy/utils/core.py +0 -295
- PipeGraphPy/utils/examine.py +0 -259
- PipeGraphPy/utils/handle_graph.py +0 -31
- PipeGraphPy/utils/lock.py +0 -1
- PipeGraphPy/utils/mq.py +0 -64
- PipeGraphPy/utils/osutil.py +0 -29
- PipeGraphPy/utils/redis_operate.py +0 -195
- PipeGraphPy/utils/version.py +0 -108
- pipegraphpy-2.0.23.dist-info/METADATA +0 -21
- pipegraphpy-2.0.23.dist-info/RECORD +0 -95
- /PipeGraphPy/core/modules/{cluster → BranchSelect}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{classifier → Classifier}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{custom → Cluster}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{custom/classifier → Common}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{datacharts → DataCharts}/dataview.py +0 -0
- /PipeGraphPy/core/modules/{deeplearning → Deeplearning}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{ensemble → EndScript}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{evaluate → Ensemble}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{exportdata → Evaluate}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{importdata → ExportData}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{merge → HandleScript}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{postprocessor → MergeScript}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{preprocessor → Postprocessor}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{pythonscript → PythonScript}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{regressor → Regressor}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{selector → SelectScript}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{model_selector → Selector}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{special → Split}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{split → SplitScript}/__init__.py +0 -0
- /PipeGraphPy/core/modules/{transformer → StartScript}/__init__.py +0 -0
- {pipegraphpy-2.0.23.dist-info → pipegraphpy-3.0.5.dist-info}/top_level.txt +0 -0
PipeGraphPy/__init__.py
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
# coding:utf-8
|
|
2
2
|
|
|
3
|
-
__version__ = "
|
|
3
|
+
__version__ = "3.0.5"
|
|
4
4
|
|
|
5
|
-
from PipeGraphPy.core.graph import
|
|
5
|
+
from PipeGraphPy.core.graph import (
|
|
6
|
+
Graph,
|
|
7
|
+
graph_predict,
|
|
8
|
+
graph_evaluate,
|
|
9
|
+
graph_run,
|
|
10
|
+
graph_backtest,
|
|
11
|
+
)
|
|
6
12
|
from PipeGraphPy.core.module import Module
|
|
7
13
|
from PipeGraphPy.core.node import Node
|
|
8
14
|
|
|
9
|
-
__all__ = [
|
|
10
|
-
|
|
15
|
+
__all__ = [
|
|
16
|
+
"Node",
|
|
17
|
+
"Module",
|
|
18
|
+
"Graph",
|
|
19
|
+
"graph_predict",
|
|
20
|
+
"graph_evaluate",
|
|
21
|
+
"graph_run",
|
|
22
|
+
"graph_backtest",
|
|
23
|
+
]
|
PipeGraphPy/__main__.py
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import json
|
|
3
|
+
import os
|
|
4
|
+
import sys
|
|
5
|
+
import traceback
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
from PipeGraphPy import Graph
|
|
10
|
+
from PipeGraphPy.config import settings
|
|
11
|
+
from PipeGraphPy.core.graph import graph_evaluate
|
|
12
|
+
from PipeGraphPy.logger import log
|
|
13
|
+
from PipeGraphPy.storage import store
|
|
14
|
+
from PipeGraphPy.utils.format import parse_params
|
|
15
|
+
|
|
16
|
+
# 任务执行结果
|
|
17
|
+
EXECUTE_SUCCESS = 1
|
|
18
|
+
EXECUTE_FAIL = 0
|
|
19
|
+
# result_file内容标记: 1成功, 0失败
|
|
20
|
+
RESULT_FILE_SUCCESS = "1"
|
|
21
|
+
RESULT_FILE_FAIL = "0"
|
|
22
|
+
|
|
23
|
+
# 模型保存路径配置
|
|
24
|
+
HOME = os.getcwd()
|
|
25
|
+
RUN_MODEL_SAVE_PATH = (
|
|
26
|
+
os.environ.get("PGP_RUN_MODEL_SAVE_PATH")
|
|
27
|
+
or os.environ.get("PGP_MODEL_SAVE_PATH")
|
|
28
|
+
or ""
|
|
29
|
+
)
|
|
30
|
+
SDK_MODEL_SAVE_PATH = os.environ.get("PGP_SDK_MODEL_SAVE_PATH", "")
|
|
31
|
+
PREDICT_MODEL_SAVE_PATH = os.environ.get("PGP_PREDICT_MODEL_SAVE_PATH", "")
|
|
32
|
+
MODULES_CACHE_PATH = os.environ.get("PGP_MODULES_CACHE_PATH", "")
|
|
33
|
+
settings.RUN_MODEL_SAVE_PATH = RUN_MODEL_SAVE_PATH or os.path.join(HOME, "models")
|
|
34
|
+
settings.SDK_MODEL_SAVE_PATH = SDK_MODEL_SAVE_PATH or RUN_MODEL_SAVE_PATH
|
|
35
|
+
settings.PREDICT_MODEL_SAVE_PATH = PREDICT_MODEL_SAVE_PATH or RUN_MODEL_SAVE_PATH
|
|
36
|
+
settings.MODULES_CACHE_PATH = MODULES_CACHE_PATH or os.path.join(HOME, "modules")
|
|
37
|
+
|
|
38
|
+
# 数据库配置
|
|
39
|
+
USE_DB = os.environ.get("PGP_USE_DB") == "1"
|
|
40
|
+
DB_DATABASE = os.environ.get("PGP_DB_DATABASE")
|
|
41
|
+
DB_HOST = os.environ.get("PGP_DB_HOST")
|
|
42
|
+
DB_PORT = int(os.environ.get("PGP_DB_PORT") or 0)
|
|
43
|
+
DB_USER = os.environ.get("PGP_DB_USER")
|
|
44
|
+
DB_PWD = os.environ.get("PGP_DB_PWD")
|
|
45
|
+
DB_POOL_NAME = os.environ.get("PGP_DB_POOL_NAME", "mlf")
|
|
46
|
+
|
|
47
|
+
log.init(pgp_handlers=["console"])
|
|
48
|
+
|
|
49
|
+
if USE_DB:
|
|
50
|
+
import psycopg2
|
|
51
|
+
|
|
52
|
+
if not (DB_DATABASE and DB_HOST and DB_PORT and DB_USER and DB_PWD):
|
|
53
|
+
raise Exception("连接数据的配置不全")
|
|
54
|
+
settings.DATABASES_POOL = {
|
|
55
|
+
DB_POOL_NAME: {
|
|
56
|
+
"engine": psycopg2,
|
|
57
|
+
"dbtype": "postgresql",
|
|
58
|
+
"database": DB_DATABASE,
|
|
59
|
+
"host": DB_HOST,
|
|
60
|
+
"port": DB_PORT,
|
|
61
|
+
"user": DB_USER,
|
|
62
|
+
"password": DB_PWD,
|
|
63
|
+
"mincached": 0,
|
|
64
|
+
"maxconnections": 10,
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
settings.DATABASE_NAME = DB_POOL_NAME
|
|
68
|
+
from PipeGraphPy.db.models import db_init
|
|
69
|
+
|
|
70
|
+
db_init()
|
|
71
|
+
|
|
72
|
+
def read_jsonfile(jsonfile_path):
|
|
73
|
+
"""读取json内容
|
|
74
|
+
Args:
|
|
75
|
+
jsonfile_path: json文件路径
|
|
76
|
+
"""
|
|
77
|
+
if not os.path.isfile(jsonfile_path):
|
|
78
|
+
raise Exception("模型json文件不存在: %s" % jsonfile_path)
|
|
79
|
+
if not str(jsonfile_path).endswith(".json"):
|
|
80
|
+
raise Exception("%s不是json文件" % jsonfile_path)
|
|
81
|
+
try:
|
|
82
|
+
with open(jsonfile_path, "r", encoding="utf-8") as f:
|
|
83
|
+
return json.load(f)
|
|
84
|
+
except Exception:
|
|
85
|
+
raise Exception("载入json文件报错: \n%s" % traceback.format_exc())
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def json_default(obj):
|
|
89
|
+
"""json序列化兜底: numpy/pandas等类型转Python基础类型"""
|
|
90
|
+
for attr in ("item", "tolist"):
|
|
91
|
+
if hasattr(obj, attr):
|
|
92
|
+
try:
|
|
93
|
+
return getattr(obj, attr)()
|
|
94
|
+
except Exception:
|
|
95
|
+
pass
|
|
96
|
+
return str(obj)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def save_output(output, filepath, index=True):
|
|
100
|
+
"""保存output文件"""
|
|
101
|
+
if not isinstance(output, pd.DataFrame):
|
|
102
|
+
raise Exception("输出结果必须是DataFrame")
|
|
103
|
+
try:
|
|
104
|
+
output.to_csv(filepath, encoding="utf-8", index=index)
|
|
105
|
+
except Exception:
|
|
106
|
+
raise Exception("输出数据保存失败: \n%s" % traceback.format_exc())
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def write_result(result, result_file=None):
|
|
110
|
+
"""写入运行结果(成功标记为1, 失败标记为0)"""
|
|
111
|
+
if result_file:
|
|
112
|
+
with open(result_file, "w", encoding="utf-8") as f:
|
|
113
|
+
f.write(str(result))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class ModelExecutor:
|
|
117
|
+
all_jobs = ["run", "predict", "evaluate", "backtest"]
|
|
118
|
+
# 运行器级参数, 不传给模型执行
|
|
119
|
+
run_kw_skip_keys = ("evaluate_module", "load_ready_model", "is_backtest", "use_db")
|
|
120
|
+
default_evaluate_module = "CommonEvaluate"
|
|
121
|
+
|
|
122
|
+
def __init__(self, model_json, **kw):
|
|
123
|
+
self.params = kw
|
|
124
|
+
self.model_json_path = model_json
|
|
125
|
+
self.model_info = self.load_model_json()
|
|
126
|
+
self.run_kw = {}
|
|
127
|
+
self.result_path = kw.get("result_path")
|
|
128
|
+
self.output_path = kw.get("output_path")
|
|
129
|
+
self.log_path = kw.get("log_path")
|
|
130
|
+
self.init_params()
|
|
131
|
+
|
|
132
|
+
def load_model_json(self):
|
|
133
|
+
try:
|
|
134
|
+
model_json = read_jsonfile(self.model_json_path)
|
|
135
|
+
if not isinstance(model_json, dict):
|
|
136
|
+
raise Exception("model_json内的内容格式错误, 必须为json对象")
|
|
137
|
+
return model_json
|
|
138
|
+
except Exception as e:
|
|
139
|
+
raise Exception("model_json内的内容格式错误:\n%s" % e)
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def _to_score_df(score):
|
|
143
|
+
"""把日评分统一转成DataFrame(dict/Series/DataFrame)"""
|
|
144
|
+
if isinstance(score, pd.DataFrame):
|
|
145
|
+
return score
|
|
146
|
+
if isinstance(score, pd.Series):
|
|
147
|
+
return score.to_frame("score")
|
|
148
|
+
if isinstance(score, dict):
|
|
149
|
+
return pd.DataFrame(
|
|
150
|
+
list(score.items()), columns=["time", "score"]
|
|
151
|
+
).set_index("time")
|
|
152
|
+
return None
|
|
153
|
+
|
|
154
|
+
def update_model_score(self, score):
|
|
155
|
+
"""把评估分数更新到模型json的score字段(其余内容不变)"""
|
|
156
|
+
self.model_info["score"] = score
|
|
157
|
+
tmp_path = "%s.tmp.%s" % (self.model_json_path, os.getpid())
|
|
158
|
+
try:
|
|
159
|
+
with open(tmp_path, "w", encoding="utf-8") as f:
|
|
160
|
+
json.dump(
|
|
161
|
+
self.model_info,
|
|
162
|
+
f,
|
|
163
|
+
ensure_ascii=False,
|
|
164
|
+
indent=4,
|
|
165
|
+
default=json_default,
|
|
166
|
+
)
|
|
167
|
+
os.replace(tmp_path, self.model_json_path)
|
|
168
|
+
except Exception:
|
|
169
|
+
if os.path.isfile(tmp_path):
|
|
170
|
+
os.remove(tmp_path)
|
|
171
|
+
raise Exception("模型json的score更新失败: \n%s" % traceback.format_exc())
|
|
172
|
+
|
|
173
|
+
def init_params(self):
|
|
174
|
+
for path in (self.result_path, self.output_path, self.log_path):
|
|
175
|
+
if path and not os.path.exists(path):
|
|
176
|
+
os.makedirs(path, exist_ok=True)
|
|
177
|
+
if self.output_path and os.path.isdir(self.output_path):
|
|
178
|
+
settings.PREDICT_RESULT_SAVE_PATH = self.output_path
|
|
179
|
+
|
|
180
|
+
# params解析, params应传json格式的字符串
|
|
181
|
+
try:
|
|
182
|
+
params_str = self.params.get("params")
|
|
183
|
+
self.run_kw = json.loads(params_str) if params_str else {}
|
|
184
|
+
except Exception:
|
|
185
|
+
raise Exception("params传值不是json格式")
|
|
186
|
+
|
|
187
|
+
if self.model_info.get("params") and isinstance(self.model_info["params"], dict):
|
|
188
|
+
self.run_kw.update(self.model_info["params"])
|
|
189
|
+
|
|
190
|
+
self._parse_biz_info(self.model_info)
|
|
191
|
+
|
|
192
|
+
# 处理params_file
|
|
193
|
+
if self.params.get("params_file"):
|
|
194
|
+
try:
|
|
195
|
+
file_params = read_jsonfile(self.params["params_file"])
|
|
196
|
+
if not isinstance(file_params, dict):
|
|
197
|
+
raise Exception("params_file内的内容格式错误")
|
|
198
|
+
except Exception as e:
|
|
199
|
+
raise Exception("params_file内的内容格式错误:\n%s" % e)
|
|
200
|
+
if file_params:
|
|
201
|
+
if file_params.get("biz_info"):
|
|
202
|
+
self.model_info["biz_info"] = file_params.pop("biz_info")
|
|
203
|
+
self._parse_biz_info(self.model_info)
|
|
204
|
+
if file_params.get("nodes") and isinstance(file_params["nodes"], list):
|
|
205
|
+
self.model_info["nodes"] = file_params.pop("nodes")
|
|
206
|
+
if file_params.get("edges") and isinstance(file_params["edges"], list):
|
|
207
|
+
self.model_info["edges"] = file_params.pop("edges")
|
|
208
|
+
self.run_kw.update(file_params)
|
|
209
|
+
self.run_kw = parse_params(self.run_kw)
|
|
210
|
+
# 模型json/params_file 中的 biz_info, 供预测/评估载入已训练模型时覆盖使用
|
|
211
|
+
self.biz_info = self.model_info.get("biz_info")
|
|
212
|
+
|
|
213
|
+
@staticmethod
|
|
214
|
+
def _parse_biz_info(model_info):
|
|
215
|
+
"""从biz_info中取出biz_id"""
|
|
216
|
+
biz_info = model_info.get("biz_info")
|
|
217
|
+
if biz_info and biz_info.get("id"):
|
|
218
|
+
model_info["biz_id"] = biz_info["id"]
|
|
219
|
+
|
|
220
|
+
@classmethod
|
|
221
|
+
def _clean_run_kw(cls, run_kw):
|
|
222
|
+
"""剔除运行器级参数, 避免透传给模型执行"""
|
|
223
|
+
return {k: v for k, v in run_kw.items() if k not in cls.run_kw_skip_keys}
|
|
224
|
+
|
|
225
|
+
def get_file_path(self, job, model_id):
|
|
226
|
+
"""获取预测结果保存路径"""
|
|
227
|
+
filename = f"{job}_{model_id}"
|
|
228
|
+
output_file = result_file = None
|
|
229
|
+
if self.output_path:
|
|
230
|
+
if os.path.isfile(self.output_path):
|
|
231
|
+
output_file = self.output_path
|
|
232
|
+
elif os.path.isdir(self.output_path):
|
|
233
|
+
output_file = os.path.join(self.output_path, filename + ".csv")
|
|
234
|
+
if self.result_path:
|
|
235
|
+
if os.path.isfile(self.result_path):
|
|
236
|
+
result_file = self.result_path
|
|
237
|
+
elif os.path.isdir(self.result_path):
|
|
238
|
+
result_file = os.path.join(self.result_path, filename + ".txt")
|
|
239
|
+
return output_file, result_file
|
|
240
|
+
|
|
241
|
+
def get_score_file(self, job, model_id):
|
|
242
|
+
"""获取评估分数保存路径, 优先output目录, 其次result目录"""
|
|
243
|
+
for base in (self.output_path, self.result_path):
|
|
244
|
+
if base and os.path.isdir(base):
|
|
245
|
+
return os.path.join(base, f"{job}_score_{model_id}.csv")
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
def get_log_path(self, model_id):
|
|
249
|
+
"""获取日志保存路径"""
|
|
250
|
+
log_file = None
|
|
251
|
+
if self.log_path:
|
|
252
|
+
if os.path.isfile(self.log_path):
|
|
253
|
+
log_file = self.log_path
|
|
254
|
+
elif os.path.isdir(self.log_path):
|
|
255
|
+
log_file = os.path.join(self.log_path, f"{model_id}.log")
|
|
256
|
+
return log_file
|
|
257
|
+
|
|
258
|
+
def init_log_file(self, model_id):
|
|
259
|
+
log_file = self.get_log_path(model_id)
|
|
260
|
+
if log_file:
|
|
261
|
+
log.init(
|
|
262
|
+
pgp_handlers=["console", "file"],
|
|
263
|
+
pgp_logfile=log_file,
|
|
264
|
+
replace_logger=True,
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
def run(self):
|
|
268
|
+
"""执行训练"""
|
|
269
|
+
model_id = self.model_info["id"]
|
|
270
|
+
self.init_log_file(model_id)
|
|
271
|
+
log.info(">>训练开始<<")
|
|
272
|
+
result_file = None
|
|
273
|
+
try:
|
|
274
|
+
_, result_file = self.get_file_path("run", model_id)
|
|
275
|
+
model = Graph.create_by_dict(self.model_info)
|
|
276
|
+
log.info("@@开始训练:模型id:%s@@" % model.id)
|
|
277
|
+
# biz_info 优先取 -p 传入, 其次取模型json/params_file 中的
|
|
278
|
+
run_kw = dict(self.run_kw)
|
|
279
|
+
biz_info = run_kw.pop("biz_info", None) or self.biz_info
|
|
280
|
+
model.run(biz_info=biz_info, **self._clean_run_kw(run_kw))
|
|
281
|
+
store.save_graph(model)
|
|
282
|
+
if result_file:
|
|
283
|
+
write_result(RESULT_FILE_SUCCESS, result_file)
|
|
284
|
+
log.info("@@结束训练:模型id:%s@@" % model.id)
|
|
285
|
+
log.info(">>训练执行成功<<")
|
|
286
|
+
return EXECUTE_SUCCESS
|
|
287
|
+
except Exception:
|
|
288
|
+
log.error(traceback.format_exc())
|
|
289
|
+
if result_file:
|
|
290
|
+
write_result(RESULT_FILE_FAIL, result_file)
|
|
291
|
+
log.error("@@训练报错:模型id:%s@@" % model_id)
|
|
292
|
+
log.error(">>训练执行失败<<")
|
|
293
|
+
return EXECUTE_FAIL
|
|
294
|
+
|
|
295
|
+
def predict(self):
|
|
296
|
+
"""执行预测"""
|
|
297
|
+
model_id = self.model_info["id"]
|
|
298
|
+
self.init_log_file(model_id)
|
|
299
|
+
log.info(">>预测开始<<")
|
|
300
|
+
result_file = None
|
|
301
|
+
try:
|
|
302
|
+
output_file, result_file = self.get_file_path("predict", model_id)
|
|
303
|
+
log.info("@@开始预测:模型id:%s@@" % model_id)
|
|
304
|
+
graph_model = store.load_graph(model_id)
|
|
305
|
+
# biz_info 优先取 -p 传入, 其次取模型json/params_file 中的
|
|
306
|
+
run_kw = dict(self.run_kw)
|
|
307
|
+
biz_info = run_kw.pop("biz_info", None) or self.biz_info
|
|
308
|
+
predict_res = graph_model.predict(
|
|
309
|
+
biz_info=biz_info, **self._clean_run_kw(run_kw)
|
|
310
|
+
)
|
|
311
|
+
if result_file:
|
|
312
|
+
write_result(RESULT_FILE_SUCCESS, result_file)
|
|
313
|
+
if output_file:
|
|
314
|
+
log.info("保存输出结果:%s" % model_id)
|
|
315
|
+
save_output(predict_res, output_file)
|
|
316
|
+
log.info("@@结束预测:模型id:%s@@" % model_id)
|
|
317
|
+
log.info(">>预测执行成功<<")
|
|
318
|
+
return EXECUTE_SUCCESS
|
|
319
|
+
except Exception:
|
|
320
|
+
log.error(traceback.format_exc())
|
|
321
|
+
if result_file:
|
|
322
|
+
write_result(RESULT_FILE_FAIL, result_file)
|
|
323
|
+
log.error("@@预测报错:模型id:%s@@" % model_id)
|
|
324
|
+
log.error(">>预测执行失败<<")
|
|
325
|
+
return EXECUTE_FAIL
|
|
326
|
+
|
|
327
|
+
def evaluate(self, is_backtest=False):
|
|
328
|
+
"""执行评估/回测"""
|
|
329
|
+
job_type = "回测" if is_backtest else "评估"
|
|
330
|
+
job = "backtest" if is_backtest else "evaluate"
|
|
331
|
+
model_id = self.model_info["id"]
|
|
332
|
+
# 使用数据库时日志由评估组件记录, 不额外写文件日志
|
|
333
|
+
if not USE_DB:
|
|
334
|
+
self.init_log_file(model_id)
|
|
335
|
+
log.info(f">>{job_type}开始<<")
|
|
336
|
+
result_file = None
|
|
337
|
+
try:
|
|
338
|
+
output_file, result_file = self.get_file_path(job, model_id)
|
|
339
|
+
score_file = self.get_score_file(job, model_id)
|
|
340
|
+
log.info("@@开始%s:模型id:%s@@" % (job_type, model_id))
|
|
341
|
+
# 评估参数
|
|
342
|
+
run_kw = dict(self.run_kw)
|
|
343
|
+
evaluate_module_cls = run_kw.pop("evaluate_module", None) or self.default_evaluate_module
|
|
344
|
+
# 是否训练, 默认需要训练
|
|
345
|
+
load_ready_model = run_kw.pop("load_ready_model", True) if run_kw.get("load_ready_model") is not None else True
|
|
346
|
+
# biz_info 优先取 -p 传入, 其次取模型json/params_file 中的
|
|
347
|
+
biz_info = run_kw.pop("biz_info", None) or self.biz_info
|
|
348
|
+
# 剔除is_backtest/use_db等运行器级参数, 避免重复传参
|
|
349
|
+
run_kw = self._clean_run_kw(run_kw)
|
|
350
|
+
if not load_ready_model:
|
|
351
|
+
model = Graph.create_by_dict(self.model_info)
|
|
352
|
+
else:
|
|
353
|
+
try:
|
|
354
|
+
model = store.load_graph(model_id)
|
|
355
|
+
except Exception as e:
|
|
356
|
+
raise Exception("载入建模对象的模型错误\n%s" % e)
|
|
357
|
+
|
|
358
|
+
evaluate_res = graph_evaluate(
|
|
359
|
+
model_id,
|
|
360
|
+
model,
|
|
361
|
+
is_backtest=is_backtest,
|
|
362
|
+
use_db=USE_DB,
|
|
363
|
+
biz_info=biz_info,
|
|
364
|
+
evaluate_module_cls=evaluate_module_cls,
|
|
365
|
+
**run_kw,
|
|
366
|
+
)
|
|
367
|
+
daily_score = None
|
|
368
|
+
if len(evaluate_res) == 3:
|
|
369
|
+
evaluate_data, evaluate_score, daily_score = evaluate_res
|
|
370
|
+
else:
|
|
371
|
+
evaluate_data, evaluate_score = evaluate_res
|
|
372
|
+
score_df = self._to_score_df(daily_score)
|
|
373
|
+
if score_file and score_df is not None:
|
|
374
|
+
log.info("保存评估分数:%s" % model_id)
|
|
375
|
+
save_output(score_df, score_file)
|
|
376
|
+
elif not USE_DB:
|
|
377
|
+
log.warning("未配置可写的output_path/result_path或日评分为空, 评估分数未落盘:%s" % model_id)
|
|
378
|
+
if output_file:
|
|
379
|
+
log.info("保存评估数据:%s" % model_id)
|
|
380
|
+
save_output(evaluate_data, output_file)
|
|
381
|
+
# 评估分数回写模型json(仅更新score字段)
|
|
382
|
+
if not is_backtest:
|
|
383
|
+
self.update_model_score(evaluate_score)
|
|
384
|
+
log.info("更新模型json分数:%s" % model_id)
|
|
385
|
+
if result_file:
|
|
386
|
+
write_result(RESULT_FILE_SUCCESS, result_file)
|
|
387
|
+
log.info("@@结束%s:模型id:%s@@" % (job_type, model_id))
|
|
388
|
+
log.info(f">>{job_type}执行成功<<")
|
|
389
|
+
return EXECUTE_SUCCESS
|
|
390
|
+
except Exception:
|
|
391
|
+
log.error(traceback.format_exc())
|
|
392
|
+
if result_file:
|
|
393
|
+
write_result(RESULT_FILE_FAIL, result_file)
|
|
394
|
+
log.error("@@%s报错:模型id:%s@@" % (job_type, model_id))
|
|
395
|
+
log.error(f">>{job_type}执行失败<<")
|
|
396
|
+
return EXECUTE_FAIL
|
|
397
|
+
|
|
398
|
+
def backtest(self):
|
|
399
|
+
return self.evaluate(is_backtest=True)
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
if __name__ == "__main__":
|
|
403
|
+
parser = argparse.ArgumentParser()
|
|
404
|
+
parser.add_argument("-j", "--jobs", required=True, help="任务类型:run,predict,evaluate,backtest执行多个需要-分割")
|
|
405
|
+
parser.add_argument("-m", "--model", required=True, help="模型json文件,要执行的model路径")
|
|
406
|
+
parser.add_argument("-o", "--output_path", required=False, help="输出数据文件路径, csv文件")
|
|
407
|
+
parser.add_argument("-r", "--result_path", required=False, help="模型运行结果文件路径")
|
|
408
|
+
parser.add_argument("-l", "--log_path", required=False, help="日志文件路径")
|
|
409
|
+
parser.add_argument("-p", "--params", required=False, help="额外参数, json格式的字符串")
|
|
410
|
+
parser.add_argument("-pf", "--params_file", required=False, help="额外参数, json文件的路径")
|
|
411
|
+
args = parser.parse_args()
|
|
412
|
+
job_flow = args.jobs.split("-")
|
|
413
|
+
not_exist_jobs = set(job_flow) - set(ModelExecutor.all_jobs)
|
|
414
|
+
if not_exist_jobs:
|
|
415
|
+
raise Exception(f"不存在这些任务:{not_exist_jobs}")
|
|
416
|
+
ins = ModelExecutor(
|
|
417
|
+
args.model,
|
|
418
|
+
output_path=args.output_path,
|
|
419
|
+
result_path=args.result_path,
|
|
420
|
+
log_path=args.log_path,
|
|
421
|
+
params=args.params,
|
|
422
|
+
params_file=args.params_file,
|
|
423
|
+
)
|
|
424
|
+
failed = False
|
|
425
|
+
for job in job_flow:
|
|
426
|
+
func = getattr(ins, job)
|
|
427
|
+
if func() != EXECUTE_SUCCESS:
|
|
428
|
+
failed = True
|
|
429
|
+
break
|
|
430
|
+
sys.exit(1 if failed else 0)
|
PipeGraphPy/common.py
CHANGED
PipeGraphPy/config/__init__.py
CHANGED
|
@@ -16,7 +16,7 @@ from PipeGraphPy.config import default_settings
|
|
|
16
16
|
from PipeGraphPy.utils.functional import LazyObject, empty
|
|
17
17
|
from dbpoolpy import settings as dbsettings
|
|
18
18
|
|
|
19
|
-
os.environ
|
|
19
|
+
os.environ.setdefault('PGP_SETTINGS_MODULE', 'PipeGraphPy.config.custom')
|
|
20
20
|
|
|
21
21
|
ENVIRONMENT_VARIABLE = "PGP_SETTINGS_MODULE"
|
|
22
22
|
|
|
@@ -67,7 +67,7 @@ class LazySettings(LazyObject):
|
|
|
67
67
|
"or call settings.configure() before accessing settings."
|
|
68
68
|
% (desc, ENVIRONMENT_VARIABLE))
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
self._wrapped = Settings(settings_module)
|
|
71
71
|
|
|
72
72
|
def __repr__(self):
|
|
73
73
|
# Hardcode the class name as otherwise it yields 'Settings'.
|
|
@@ -273,4 +273,4 @@ class UserSettingsHolder:
|
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
|
|
276
|
-
settings =
|
|
276
|
+
settings = LazySettings()
|
|
@@ -18,12 +18,15 @@ class PGP_ENV:
|
|
|
18
18
|
NONE = "none" # 非执行训练和预测环境
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
MODEL_LOG_SAVE_TYPE = os.environ.get("PGP_MODEL_LOG_SAVE_TYPE", 'file') # file, db
|
|
22
|
+
MODEL_LOG_SAVE_PATH = os.environ.get("PGP_MODEL_LOG_SAVE_PATH", '')
|
|
21
23
|
DEBUG = False if str(os.environ.get("PGP_NOT_DEBUG")) == "1" else True
|
|
22
24
|
ISDEV = False if str(os.environ.get("PGP_NOT_DEV")) == "1" else True
|
|
23
25
|
ISJUPYTER = True if str(os.environ.get("PGP_JUPYTER")) == "1" else False
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
RUN_PERMISSION = True if str(os.environ.get("PGP_RUN_PERMISSION")) == "1" else False
|
|
27
|
+
RUN_MODEL_SAVE_PATH = os.environ.get("PGP_RUN_MODEL_SAVE_PATH") or os.environ.get("PGP_MODEL_SAVE_PATH")
|
|
28
|
+
SDK_MODEL_SAVE_PATH = os.environ.get("PGP_SDK_MODEL_SAVE_PATH") or os.environ.get("PGP_MODEL_SAVE_PATH")
|
|
29
|
+
PREDICT_MODEL_SAVE_PATH = os.environ.get("PGP_PREDICT_MODEL_SAVE_PATH") or os.environ.get("PGP_MODEL_SAVE_PATH")
|
|
27
30
|
PREDICT_RESULT_SAVE_PATH = os.environ.get("PGP_PREDICT_RESULT_SAVE_PATH", '')
|
|
28
31
|
BACKTEST_RESULT_SAVE_PATH = os.environ.get("PGP_BACKTEST_RESULT_SAVE_PATH", '')
|
|
29
32
|
RUN_RESULT_SAVE_PATH = os.environ.get("PGP_RUN_RESULT_SAVE_PATH", '')
|
|
@@ -34,7 +37,7 @@ REDIS_HOST = os.environ.get("REDIS_HOST", '39.105.185.60')
|
|
|
34
37
|
REDIS_PORT = int(os.environ.get("REDIS_PORT", '6371'))
|
|
35
38
|
PGP_DB_HOST = os.environ.get("PGP_DB_HOST", '39.105.185.60')
|
|
36
39
|
PGP_DB_PORT = int(os.environ.get("PGP_DB_PORT", '33061'))
|
|
37
|
-
AMQP_URL = os.environ.get("PGP_AMQP_URL", 'amqp://mlf:mlfadmin@
|
|
40
|
+
AMQP_URL = os.environ.get("PGP_AMQP_URL", 'amqp://mlf:mlfadmin@47.94.251.175:6296/mlf_vhost')
|
|
38
41
|
RUN_TIMEOUT = int(os.environ.get("RUN_TIMEOUT", 60 * 60 * 6)) # 训练超时2小时
|
|
39
42
|
PREDICT_TIMEOUT = int(os.environ.get("PREDICT_TIMEOUT", 60 * 10)) # 预测超时5分钟
|
|
40
43
|
RT_DB_CONNECTOR = os.environ.get("RT_DB_CONNECTOR", '')
|
|
@@ -43,19 +46,18 @@ DEPLOYMENT_ENV = os.environ.get("PGP_DEPLOYMENT_ENV", "none")
|
|
|
43
46
|
RUN_ENV = os.environ.get("PGP_RUN_ENV", 'test') # 运行环境, test, online, dev, sdk, image
|
|
44
47
|
DBPOOL_SERVER_NAME = os.environ.get("PGP_DBPOOL_SERVER_NAME", 'mlf')
|
|
45
48
|
PGP_PDB_PORT = int(os.environ.get("PGP_PDB_PORT", '6688'))
|
|
46
|
-
PREDICT_OUTPUT_EXCHANGE = os.environ.get("PGP_PREDICT_OUTPUT_EXCHANGE", "PipeGraphPy_predict_output_e")
|
|
47
|
-
PREDICT_OUTPUT_ROUTING_KEY = os.environ.get("PGP_PREDICT_OUTPUT_ROUTING_KEY", "PipeGraphPy_predict_output_k")
|
|
48
|
-
PREDICT_OUTPUT_QUEUE = os.environ.get("PGP_PREDICT_OUTPUT_QUEUE", "PipeGraphPy_predict_output_q")
|
|
49
49
|
|
|
50
50
|
# 模型保存配置
|
|
51
51
|
if DEPLOYMENT_ENV == PGP_ENV.SDK:
|
|
52
|
-
HOME_CACHE_PATH = os.path.
|
|
52
|
+
HOME_CACHE_PATH = os.path.expanduser('~/.cache/PipeGraphPy/sdk/')
|
|
53
53
|
if not SDK_MODEL_SAVE_PATH:
|
|
54
54
|
SDK_MODEL_SAVE_PATH = os.path.join(HOME_CACHE_PATH, "sdk")
|
|
55
55
|
if not os.path.exists(SDK_MODEL_SAVE_PATH):
|
|
56
56
|
os.makedirs(SDK_MODEL_SAVE_PATH)
|
|
57
57
|
else:
|
|
58
|
-
HOME_CACHE_PATH = os.path.
|
|
58
|
+
HOME_CACHE_PATH = os.path.expanduser('~/.cache/PipeGraphPy/')
|
|
59
|
+
if not MODEL_LOG_SAVE_PATH:
|
|
60
|
+
MODEL_LOG_SAVE_PATH = os.path.join(HOME_CACHE_PATH, "logs")
|
|
59
61
|
if not RUN_MODEL_SAVE_PATH:
|
|
60
62
|
RUN_MODEL_SAVE_PATH = os.path.join(HOME_CACHE_PATH, "models")
|
|
61
63
|
if not PREDICT_MODEL_SAVE_PATH:
|
|
@@ -78,7 +80,24 @@ if not os.path.exists(MODULES_CACHE_PATH):
|
|
|
78
80
|
with open(_init_file, "w") as f:
|
|
79
81
|
f.write("#")
|
|
80
82
|
sys.path.append(os.path.dirname(MODULES_CACHE_PATH))
|
|
81
|
-
|
|
83
|
+
|
|
84
|
+
# 确保所有目录存在
|
|
85
|
+
_dir_vars = [
|
|
86
|
+
MODEL_LOG_SAVE_PATH,
|
|
87
|
+
RUN_MODEL_SAVE_PATH,
|
|
88
|
+
SDK_MODEL_SAVE_PATH,
|
|
89
|
+
PREDICT_MODEL_SAVE_PATH,
|
|
90
|
+
PREDICT_RESULT_SAVE_PATH,
|
|
91
|
+
BACKTEST_RESULT_SAVE_PATH,
|
|
92
|
+
RUN_RESULT_SAVE_PATH,
|
|
93
|
+
EVALUATE_RESULT_SAVE_PATH,
|
|
94
|
+
TEMP_SAVE_PATH,
|
|
95
|
+
]
|
|
96
|
+
for _dir in _dir_vars:
|
|
97
|
+
if _dir and not os.path.exists(_dir):
|
|
98
|
+
os.makedirs(_dir)
|
|
99
|
+
|
|
100
|
+
MODEL_SAVE_NAME = "graph_model_{graph_id}"
|
|
82
101
|
ALGO_MODEL_SAVE_NAME = "algo_model_{graph_id}_{node_id}_{algo_mod_type}_{idx}"
|
|
83
102
|
|
|
84
103
|
# 运行模式 1: 包引用 2:grpc
|
|
@@ -123,5 +142,5 @@ DATABASE_DB_NAME = None
|
|
|
123
142
|
|
|
124
143
|
|
|
125
144
|
# SDK是否显示log日志
|
|
126
|
-
|
|
145
|
+
SDK_SHOW_LOG = False
|
|
127
146
|
|