PipeGraphPy 2.0.23__py3-none-win_amd64.whl → 3.0.4__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.
Files changed (100) hide show
  1. PipeGraphPy/__init__.py +17 -4
  2. PipeGraphPy/__main__.py +430 -0
  3. PipeGraphPy/common.py +2 -1
  4. PipeGraphPy/config/__init__.py +3 -3
  5. PipeGraphPy/config/default_settings.py +30 -11
  6. PipeGraphPy/constants.py +158 -164
  7. PipeGraphPy/core/modules/DataCharts/__init__.py +3 -0
  8. PipeGraphPy/core/modules/ImportData/__init__.py +1 -0
  9. PipeGraphPy/core/modules/Preprocessor/__init__.py +5 -0
  10. PipeGraphPy/core/modules/Preprocessor/sqlfilter.py +41 -0
  11. PipeGraphPy/core/modules/Special/__init__.py +6 -0
  12. PipeGraphPy/core/modules/Special/emptydata.py +28 -0
  13. PipeGraphPy/core/modules/Special/graphmod.py +39 -0
  14. PipeGraphPy/core/modules/Transformer/__init__.py +3 -0
  15. PipeGraphPy/core/modules/__init__.py +0 -63
  16. PipeGraphPy/core/modules/merge/mergemany.py +97 -0
  17. PipeGraphPy/core/modules/merge/mergetwo.py +81 -0
  18. PipeGraphPy/db/utils.py +231 -43
  19. PipeGraphPy/logger.py +61 -16
  20. PipeGraphPy/plot/draw.py +2 -2
  21. PipeGraphPy/storage/__init__.py +10 -2
  22. PipeGraphPy/storage/base.py +2 -0
  23. PipeGraphPy/storage/file_backend.py +99 -90
  24. PipeGraphPy/tools.py +122 -143
  25. PipeGraphPy/utils/file_operate.py +24 -10
  26. PipeGraphPy/utils/format.py +102 -37
  27. PipeGraphPy/utils/str_handle.py +32 -31
  28. pipegraphpy-3.0.4.dist-info/METADATA +18 -0
  29. pipegraphpy-3.0.4.dist-info/RECORD +59 -0
  30. {pipegraphpy-2.0.23.dist-info → pipegraphpy-3.0.4.dist-info}/WHEEL +1 -1
  31. PipeGraphPy/core/anchor.cp310-win_amd64.pyd +0 -0
  32. PipeGraphPy/core/edge.cp310-win_amd64.pyd +0 -0
  33. PipeGraphPy/core/graph.cp310-win_amd64.pyd +0 -0
  34. PipeGraphPy/core/graph_base.cp310-win_amd64.pyd +0 -0
  35. PipeGraphPy/core/modcls/base.cp310-win_amd64.pyd +0 -0
  36. PipeGraphPy/core/modcls/branchselect.cp310-win_amd64.pyd +0 -0
  37. PipeGraphPy/core/modcls/classifier.cp310-win_amd64.pyd +0 -0
  38. PipeGraphPy/core/modcls/cluster.cp310-win_amd64.pyd +0 -0
  39. PipeGraphPy/core/modcls/datacharts.cp310-win_amd64.pyd +0 -0
  40. PipeGraphPy/core/modcls/deeplearning.cp310-win_amd64.pyd +0 -0
  41. PipeGraphPy/core/modcls/endscript.cp310-win_amd64.pyd +0 -0
  42. PipeGraphPy/core/modcls/ensemble.cp310-win_amd64.pyd +0 -0
  43. PipeGraphPy/core/modcls/evaluate.cp310-win_amd64.pyd +0 -0
  44. PipeGraphPy/core/modcls/exportdata.cp310-win_amd64.pyd +0 -0
  45. PipeGraphPy/core/modcls/handlescript.cp310-win_amd64.pyd +0 -0
  46. PipeGraphPy/core/modcls/importdata.cp310-win_amd64.pyd +0 -0
  47. PipeGraphPy/core/modcls/merge.cp310-win_amd64.pyd +0 -0
  48. PipeGraphPy/core/modcls/mergescript.cp310-win_amd64.pyd +0 -0
  49. PipeGraphPy/core/modcls/metrics.cp310-win_amd64.pyd +0 -0
  50. PipeGraphPy/core/modcls/postprocessor.cp310-win_amd64.pyd +0 -0
  51. PipeGraphPy/core/modcls/preprocessor.cp310-win_amd64.pyd +0 -0
  52. PipeGraphPy/core/modcls/pythonscript.cp310-win_amd64.pyd +0 -0
  53. PipeGraphPy/core/modcls/regressor.cp310-win_amd64.pyd +0 -0
  54. PipeGraphPy/core/modcls/selector.cp310-win_amd64.pyd +0 -0
  55. PipeGraphPy/core/modcls/selectscript.cp310-win_amd64.pyd +0 -0
  56. PipeGraphPy/core/modcls/special.cp310-win_amd64.pyd +0 -0
  57. PipeGraphPy/core/modcls/split.cp310-win_amd64.pyd +0 -0
  58. PipeGraphPy/core/modcls/splitscript.cp310-win_amd64.pyd +0 -0
  59. PipeGraphPy/core/modcls/startscript.cp310-win_amd64.pyd +0 -0
  60. PipeGraphPy/core/modcls/transformer.cp310-win_amd64.pyd +0 -0
  61. PipeGraphPy/core/module.cp310-win_amd64.pyd +0 -0
  62. PipeGraphPy/core/modules/branchselect/__ini__.py +0 -0
  63. PipeGraphPy/core/modules/datacharts/__init__.py +0 -5
  64. PipeGraphPy/core/node.cp310-win_amd64.pyd +0 -0
  65. PipeGraphPy/core/pipegraph.cp310-win_amd64.pyd +0 -0
  66. PipeGraphPy/db/models.cp310-win_amd64.pyd +0 -0
  67. PipeGraphPy/storage/dict_backend.py +0 -102
  68. PipeGraphPy/storage/redis_backend.py +0 -183
  69. PipeGraphPy/utils/check.py +0 -180
  70. PipeGraphPy/utils/core.py +0 -295
  71. PipeGraphPy/utils/examine.py +0 -259
  72. PipeGraphPy/utils/handle_graph.py +0 -31
  73. PipeGraphPy/utils/lock.py +0 -1
  74. PipeGraphPy/utils/mq.py +0 -64
  75. PipeGraphPy/utils/osutil.py +0 -29
  76. PipeGraphPy/utils/redis_operate.py +0 -195
  77. PipeGraphPy/utils/version.py +0 -108
  78. pipegraphpy-2.0.23.dist-info/METADATA +0 -21
  79. pipegraphpy-2.0.23.dist-info/RECORD +0 -95
  80. /PipeGraphPy/core/modules/{cluster → BranchSelect}/__init__.py +0 -0
  81. /PipeGraphPy/core/modules/{classifier → Classifier}/__init__.py +0 -0
  82. /PipeGraphPy/core/modules/{custom → Cluster}/__init__.py +0 -0
  83. /PipeGraphPy/core/modules/{custom/classifier → Common}/__init__.py +0 -0
  84. /PipeGraphPy/core/modules/{datacharts → DataCharts}/dataview.py +0 -0
  85. /PipeGraphPy/core/modules/{deeplearning → Deeplearning}/__init__.py +0 -0
  86. /PipeGraphPy/core/modules/{ensemble → EndScript}/__init__.py +0 -0
  87. /PipeGraphPy/core/modules/{evaluate → Ensemble}/__init__.py +0 -0
  88. /PipeGraphPy/core/modules/{exportdata → Evaluate}/__init__.py +0 -0
  89. /PipeGraphPy/core/modules/{importdata → ExportData}/__init__.py +0 -0
  90. /PipeGraphPy/core/modules/{merge → HandleScript}/__init__.py +0 -0
  91. /PipeGraphPy/core/modules/{postprocessor → MergeScript}/__init__.py +0 -0
  92. /PipeGraphPy/core/modules/{preprocessor → Postprocessor}/__init__.py +0 -0
  93. /PipeGraphPy/core/modules/{pythonscript → PythonScript}/__init__.py +0 -0
  94. /PipeGraphPy/core/modules/{regressor → Regressor}/__init__.py +0 -0
  95. /PipeGraphPy/core/modules/{selector → SelectScript}/__init__.py +0 -0
  96. /PipeGraphPy/core/modules/{model_selector → Selector}/__init__.py +0 -0
  97. /PipeGraphPy/core/modules/{special → Split}/__init__.py +0 -0
  98. /PipeGraphPy/core/modules/{split → SplitScript}/__init__.py +0 -0
  99. /PipeGraphPy/core/modules/{transformer → StartScript}/__init__.py +0 -0
  100. {pipegraphpy-2.0.23.dist-info → pipegraphpy-3.0.4.dist-info}/top_level.txt +0 -0
PipeGraphPy/__init__.py CHANGED
@@ -1,10 +1,23 @@
1
1
  # coding:utf-8
2
2
 
3
- __version__ = "2.0.23"
3
+ __version__ = "3.0.4"
4
4
 
5
- from PipeGraphPy.core.graph import Graph, graph_predict, graph_evaluate, online_graph_evaluate, online_graph_predict, graph_run, graph_backtest
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__ = ['Node', 'Module', 'Graph', 'graph_predict', 'graph_evaluate',
10
- 'online_graph_evaluate', 'online_graph_predict', 'graph_run', 'graph_backtest']
15
+ __all__ = [
16
+ "Node",
17
+ "Module",
18
+ "Graph",
19
+ "graph_predict",
20
+ "graph_evaluate",
21
+ "graph_run",
22
+ "graph_backtest",
23
+ ]
@@ -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
@@ -1,4 +1,5 @@
1
1
  # 本文件存储通用变量或全局变量
2
2
 
3
-
3
+ # 全局图注册表: {graph_id: Graph}, 供 Anchor/tools 等按图id反查图对象。
4
+ # 注意: 图对象会常驻内存, 生命周期与进程一致。
4
5
  multi_graph = dict()
@@ -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['PGP_SETTINGS_MODULE'] = 'PipeGraphPy.config.custom'
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
- # self._wrapped = Settings(settings_module)
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 = default_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
- RUN_MODEL_SAVE_PATH = os.environ.get("PGP_RUN_MODEL_SAVE_PATH", "")
25
- SDK_MODEL_SAVE_PATH = os.environ.get("PGP_SDK_MODEL_SAVE_PATH", "")
26
- PREDICT_MODEL_SAVE_PATH = os.environ.get("PGP_PREDICT_MODEL_SAVE_PATH", '')
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@172.18.59.136:32662/mlf_vhost')
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.join(os.path.expanduser('~'), '.cache', 'PipeGraphPy','sdk')
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.join(os.path.expanduser('~'), '.cache', 'PipeGraphPy')
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
- MODEL_SAVE_NAME = "graph_model_{graph_id}_{object_id}"
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
- SDK_CLOSE_LOG = True
145
+ SDK_SHOW_LOG = False
127
146