evescaffold 0.1.0__py3-none-any.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.
- evescaffold/__init__.py +5 -0
- evescaffold/__main__.py +9 -0
- evescaffold/cli.py +80 -0
- evescaffold/scaffold.py +595 -0
- evescaffold-0.1.0.dist-info/METADATA +39 -0
- evescaffold-0.1.0.dist-info/RECORD +9 -0
- evescaffold-0.1.0.dist-info/WHEEL +5 -0
- evescaffold-0.1.0.dist-info/entry_points.txt +2 -0
- evescaffold-0.1.0.dist-info/top_level.txt +1 -0
evescaffold/__init__.py
ADDED
evescaffold/__main__.py
ADDED
evescaffold/cli.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Command line interface for evescaffold.
|
|
2
|
+
|
|
3
|
+
We intentionally use only the Python standard library here (argparse) to keep
|
|
4
|
+
the package lightweight and easy to install.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import argparse
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
from .scaffold import add_model, init_project, list_builtin_models
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
17
|
+
parser = argparse.ArgumentParser(
|
|
18
|
+
prog="evescaffold",
|
|
19
|
+
description="EVE ML 项目脚手架:init / addmodel / list-models",
|
|
20
|
+
)
|
|
21
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
22
|
+
|
|
23
|
+
p_init = sub.add_parser("init", help="初始化项目目录骨架")
|
|
24
|
+
p_init.add_argument(
|
|
25
|
+
"--path",
|
|
26
|
+
"-p",
|
|
27
|
+
type=Path,
|
|
28
|
+
default=None,
|
|
29
|
+
help="项目根目录(默认当前目录)",
|
|
30
|
+
)
|
|
31
|
+
p_init.add_argument(
|
|
32
|
+
"--force",
|
|
33
|
+
action="store_true",
|
|
34
|
+
help="覆盖已存在文件",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
p_add = sub.add_parser("addmodel", help="初始化一个模型模板目录")
|
|
38
|
+
p_add.add_argument("name", help="模型模板名,如 xgboost / randomForest / embeddingMLP")
|
|
39
|
+
p_add.add_argument(
|
|
40
|
+
"--path",
|
|
41
|
+
"-p",
|
|
42
|
+
type=Path,
|
|
43
|
+
default=None,
|
|
44
|
+
help="项目根目录(默认当前目录)",
|
|
45
|
+
)
|
|
46
|
+
p_add.add_argument(
|
|
47
|
+
"--force",
|
|
48
|
+
action="store_true",
|
|
49
|
+
help="覆盖已存在文件",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
sub.add_parser("list-models", help="列出内置模型模板")
|
|
53
|
+
|
|
54
|
+
return parser
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def main(argv: list[str] | None = None) -> None:
|
|
58
|
+
parser = _build_parser()
|
|
59
|
+
args = parser.parse_args(argv)
|
|
60
|
+
|
|
61
|
+
base_dir = Path.cwd() if getattr(args, "path", None) is None else args.path
|
|
62
|
+
|
|
63
|
+
if args.command == "init":
|
|
64
|
+
init_project(base_dir=base_dir, force=bool(args.force))
|
|
65
|
+
print(f"Initialized scaffold at: {base_dir}")
|
|
66
|
+
return
|
|
67
|
+
|
|
68
|
+
if args.command == "addmodel":
|
|
69
|
+
model_dir = add_model(base_dir=base_dir, name=str(args.name), force=bool(args.force))
|
|
70
|
+
print(f"Model scaffold created at: {model_dir}")
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
if args.command == "list-models":
|
|
74
|
+
for item in list_builtin_models():
|
|
75
|
+
print(f"- {item['name']}: {item['description']}")
|
|
76
|
+
return
|
|
77
|
+
|
|
78
|
+
# Should not happen due to argparse validation.
|
|
79
|
+
parser.print_help(sys.stderr)
|
|
80
|
+
raise SystemExit(2)
|
evescaffold/scaffold.py
ADDED
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
"""Project scaffolding logic.
|
|
2
|
+
|
|
3
|
+
This module implements:
|
|
4
|
+
- `init_project`: create the baseline project structure
|
|
5
|
+
- `add_model`: create model template code under `models/<ModelName>/`
|
|
6
|
+
- `list_builtin_models`: list built-in model templates
|
|
7
|
+
|
|
8
|
+
Design goals:
|
|
9
|
+
- Safe by default (do not overwrite existing files unless `force=True`)
|
|
10
|
+
- Minimal dependencies (stdlib only)
|
|
11
|
+
- Predictable, cross-platform directory names (ASCII)
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Dict, List, Optional
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# -------------------------
|
|
22
|
+
# Small filesystem helpers
|
|
23
|
+
# -------------------------
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _mkdir(path: Path) -> None:
|
|
27
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _write_text(path: Path, content: str, force: bool) -> bool:
|
|
31
|
+
"""Write `content` to `path`.
|
|
32
|
+
|
|
33
|
+
Returns True if a write happened; False if skipped due to existing file.
|
|
34
|
+
"""
|
|
35
|
+
if path.exists() and not force:
|
|
36
|
+
return False
|
|
37
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
38
|
+
path.write_text(content, encoding="utf-8")
|
|
39
|
+
return True
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
# -------------------------
|
|
43
|
+
# Model templates
|
|
44
|
+
# -------------------------
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@dataclass(frozen=True)
|
|
48
|
+
class BuiltinModel:
|
|
49
|
+
name: str
|
|
50
|
+
description: str
|
|
51
|
+
files: Dict[str, str] # relative paths under models/<name>/
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _normalize_model_name(raw: str) -> str:
|
|
55
|
+
"""Normalize user input into canonical built-in model directory name.
|
|
56
|
+
|
|
57
|
+
We accept a few common aliases to improve UX.
|
|
58
|
+
"""
|
|
59
|
+
s = raw.strip()
|
|
60
|
+
low = s.lower().replace("-", "").replace("_", "")
|
|
61
|
+
|
|
62
|
+
if low in {"embeddingmlp", "mlp", "embedding"}:
|
|
63
|
+
return "embeddingMLP"
|
|
64
|
+
if low in {"xgboost", "xgb", "xgbboost", "xgbbooster", "xgbtree"}:
|
|
65
|
+
return "xgBoost"
|
|
66
|
+
if low in {"randomforest", "rf"}:
|
|
67
|
+
return "randomForest"
|
|
68
|
+
|
|
69
|
+
# For unknown models, we keep the input as-is (directory name).
|
|
70
|
+
return s
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def list_builtin_models() -> List[dict]:
|
|
74
|
+
"""Return built-in model list for CLI display."""
|
|
75
|
+
return [{"name": m.name, "description": m.description} for m in _BUILTIN_MODELS.values()]
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def init_project(base_dir: Path, force: bool) -> None:
|
|
79
|
+
"""Initialize the project scaffold in `base_dir`."""
|
|
80
|
+
_mkdir(base_dir)
|
|
81
|
+
|
|
82
|
+
# 公共工具层 (Global Utilities)
|
|
83
|
+
_mkdir(base_dir / "global_utils" / "featureUtils")
|
|
84
|
+
_mkdir(base_dir / "global_utils" / "modelUtils")
|
|
85
|
+
|
|
86
|
+
_write_text(
|
|
87
|
+
base_dir / "global_utils" / "featureUtils" / "featureTransform.py",
|
|
88
|
+
_T_FEATURE_TRANSFORM,
|
|
89
|
+
force,
|
|
90
|
+
)
|
|
91
|
+
_write_text(
|
|
92
|
+
base_dir / "global_utils" / "featureUtils" / "featureStats.py",
|
|
93
|
+
_T_FEATURE_STATS,
|
|
94
|
+
force,
|
|
95
|
+
)
|
|
96
|
+
_write_text(
|
|
97
|
+
base_dir / "global_utils" / "modelUtils" / "modelConverter.py",
|
|
98
|
+
_T_MODEL_CONVERTER,
|
|
99
|
+
force,
|
|
100
|
+
)
|
|
101
|
+
_write_text(
|
|
102
|
+
base_dir / "global_utils" / "modelUtils" / "modelTrainer.py",
|
|
103
|
+
_T_MODEL_TRAINER,
|
|
104
|
+
force,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# 数据层
|
|
108
|
+
_mkdir(base_dir / "data" / "raw")
|
|
109
|
+
_mkdir(base_dir / "data" / "features")
|
|
110
|
+
_write_text(base_dir / "data" / "raw" / "README.md", _T_DATA_RAW_README, force)
|
|
111
|
+
_write_text(base_dir / "data" / "features" / "README.md", _T_DATA_FEATURES_README, force)
|
|
112
|
+
|
|
113
|
+
# 特征工程层
|
|
114
|
+
_mkdir(base_dir / "feature_engineering" / "feature_generation")
|
|
115
|
+
_mkdir(base_dir / "feature_engineering" / "feature_config")
|
|
116
|
+
_mkdir(base_dir / "feature_engineering" / "transform_scripts")
|
|
117
|
+
_write_text(
|
|
118
|
+
base_dir / "feature_engineering" / "feature_generation" / "README.md",
|
|
119
|
+
_T_FE_GEN_README,
|
|
120
|
+
force,
|
|
121
|
+
)
|
|
122
|
+
_write_text(
|
|
123
|
+
base_dir / "feature_engineering" / "feature_config" / "README.md",
|
|
124
|
+
_T_FE_CFG_README,
|
|
125
|
+
force,
|
|
126
|
+
)
|
|
127
|
+
_write_text(
|
|
128
|
+
base_dir / "feature_engineering" / "transform_scripts" / "README.md",
|
|
129
|
+
_T_FE_TRANSFORM_README,
|
|
130
|
+
force,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
# 模型层
|
|
134
|
+
_mkdir(base_dir / "models")
|
|
135
|
+
|
|
136
|
+
# Create all three built-in models by default to match the requested structure.
|
|
137
|
+
add_model(base_dir=base_dir, name="embeddingMLP", force=force)
|
|
138
|
+
add_model(base_dir=base_dir, name="xgBoost", force=force)
|
|
139
|
+
add_model(base_dir=base_dir, name="randomForest", force=force)
|
|
140
|
+
|
|
141
|
+
# pipeline.py
|
|
142
|
+
_write_text(base_dir / "pipeline.py", _T_PIPELINE, force)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def add_model(base_dir: Path, name: str, force: bool) -> Path:
|
|
146
|
+
"""Create model scaffold under `base_dir/models/<name>/`."""
|
|
147
|
+
model_name = _normalize_model_name(name)
|
|
148
|
+
model_dir = base_dir / "models" / model_name
|
|
149
|
+
|
|
150
|
+
_mkdir(model_dir)
|
|
151
|
+
_mkdir(model_dir / "save")
|
|
152
|
+
_mkdir(model_dir / "save" / "save1")
|
|
153
|
+
_write_text(model_dir / "save" / "README.md", _T_SAVE_README, force)
|
|
154
|
+
|
|
155
|
+
builtin = _BUILTIN_MODELS.get(model_name)
|
|
156
|
+
if builtin is None:
|
|
157
|
+
# Unknown model: generate a generic minimal template.
|
|
158
|
+
_write_text(model_dir / "model.py", _T_GENERIC_MODEL_PY.format(model_name=model_name), force)
|
|
159
|
+
_write_text(model_dir / "config.py", _t_generic_config_py(model_name), force)
|
|
160
|
+
_write_text(model_dir / "sampleDataUpload.py", _T_SAMPLE_UPLOAD_PY, force)
|
|
161
|
+
return model_dir
|
|
162
|
+
|
|
163
|
+
for rel_path, content in builtin.files.items():
|
|
164
|
+
_write_text(model_dir / rel_path, content, force)
|
|
165
|
+
|
|
166
|
+
return model_dir
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _t_generic_config_py(model_name: str) -> str:
|
|
170
|
+
safe = "".join(ch if ch.isalnum() else "_" for ch in model_name)
|
|
171
|
+
class_name = f"{safe[:1].upper()}{safe[1:]}Config"
|
|
172
|
+
return (
|
|
173
|
+
"from dataclasses import dataclass\n\n\n"
|
|
174
|
+
f"@dataclass\nclass {class_name}:\n"
|
|
175
|
+
" # TODO: fill config fields\n"
|
|
176
|
+
" pass\n"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
# -------------------------
|
|
181
|
+
# File templates (utilities / readmes / pipeline)
|
|
182
|
+
# -------------------------
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
_T_FEATURE_TRANSFORM = """\
|
|
186
|
+
\"\"\"特征转换工具(占位)。
|
|
187
|
+
|
|
188
|
+
建议职责:
|
|
189
|
+
- 纯函数/可复用转换(不直接依赖业务 I/O)
|
|
190
|
+
- 输入输出协议稳定(DataFrame/ndarray/自定义样本结构)
|
|
191
|
+
\"\"\"
|
|
192
|
+
|
|
193
|
+
from __future__ import annotations
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def example_transform(x: float) -> float:
|
|
197
|
+
\"\"\"示例:对数变换(请按业务替换)。\"\"\"
|
|
198
|
+
if x <= 0:
|
|
199
|
+
return 0.0
|
|
200
|
+
# 简化示例:避免额外依赖 numpy
|
|
201
|
+
import math
|
|
202
|
+
|
|
203
|
+
return math.log(x)
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
_T_FEATURE_STATS = """\
|
|
208
|
+
\"\"\"特征统计工具(占位)。\"\"\"
|
|
209
|
+
|
|
210
|
+
from __future__ import annotations
|
|
211
|
+
|
|
212
|
+
from dataclasses import dataclass
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@dataclass
|
|
216
|
+
class Stats:
|
|
217
|
+
count: int
|
|
218
|
+
mean: float
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def mean(values: list[float]) -> Stats:
|
|
222
|
+
if not values:
|
|
223
|
+
return Stats(count=0, mean=0.0)
|
|
224
|
+
return Stats(count=len(values), mean=sum(values) / len(values))
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
_T_MODEL_CONVERTER = """\
|
|
229
|
+
\"\"\"模型转换工具(占位)。
|
|
230
|
+
|
|
231
|
+
建议职责:
|
|
232
|
+
- 统一保存/加载协议(如:pickle / joblib / ONNX)
|
|
233
|
+
- 版本标记、兼容性校验、元信息记录
|
|
234
|
+
\"\"\"
|
|
235
|
+
|
|
236
|
+
from __future__ import annotations
|
|
237
|
+
|
|
238
|
+
from pathlib import Path
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def save_bytes(path: Path, payload: bytes) -> None:
|
|
242
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
243
|
+
path.write_bytes(payload)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def load_bytes(path: Path) -> bytes:
|
|
247
|
+
return path.read_bytes()
|
|
248
|
+
"""
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
_T_MODEL_TRAINER = """\
|
|
252
|
+
\"\"\"通用训练工具(占位)。
|
|
253
|
+
|
|
254
|
+
建议职责:
|
|
255
|
+
- 训练流程编排(数据读取 -> 特征 -> 训练 -> 评估 -> 保存)
|
|
256
|
+
- 统一日志/指标落盘协议
|
|
257
|
+
\"\"\"
|
|
258
|
+
|
|
259
|
+
from __future__ import annotations
|
|
260
|
+
|
|
261
|
+
from pathlib import Path
|
|
262
|
+
from typing import Any, Callable
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def run_training(train_fn: Callable[[Path], Any], save_dir: Path) -> Any:
|
|
266
|
+
\"\"\"统一的训练执行入口(示例)。\"\"\"
|
|
267
|
+
save_dir.mkdir(parents=True, exist_ok=True)
|
|
268
|
+
return train_fn(save_dir)
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
_T_DATA_RAW_README = """\
|
|
273
|
+
这里放原始数据(raw)。
|
|
274
|
+
建议:不要把大文件提交到 git,使用 DVC/对象存储/数据平台。
|
|
275
|
+
"""
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
_T_DATA_FEATURES_README = """\
|
|
279
|
+
这里放特征数据(features)。
|
|
280
|
+
建议:明确特征生成版本与数据 schema。
|
|
281
|
+
"""
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
_T_FE_GEN_README = """\
|
|
285
|
+
特征生成层:建议放生成逻辑(例如按天批处理/增量生成)。
|
|
286
|
+
"""
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
_T_FE_CFG_README = """\
|
|
290
|
+
特征配置层:建议放特征开关、schema、特征列表、分桶配置等。
|
|
291
|
+
"""
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
_T_FE_TRANSFORM_README = """\
|
|
295
|
+
转换脚本:建议放一次性迁移脚本/数据修复脚本/特征回填脚本等。
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
_T_SAVE_README = """\
|
|
300
|
+
这里存放多次训练的结果与图表。
|
|
301
|
+
建议结构:
|
|
302
|
+
- save1/ save2/ ... saveN/
|
|
303
|
+
每次训练固定落盘:metrics.json、模型文件、可视化图表等。
|
|
304
|
+
"""
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
_T_SAMPLE_UPLOAD_PY = """\
|
|
308
|
+
\"\"\"样本收集/上传占位脚本(通用)。
|
|
309
|
+
|
|
310
|
+
按你的业务实际改造:
|
|
311
|
+
- 从 data/raw 或线上拉取数据
|
|
312
|
+
- 做脱敏/采样
|
|
313
|
+
- 写入 data/features 或上传到对象存储
|
|
314
|
+
\"\"\"
|
|
315
|
+
|
|
316
|
+
from __future__ import annotations
|
|
317
|
+
|
|
318
|
+
from pathlib import Path
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def main() -> None:
|
|
322
|
+
project_root = Path(__file__).resolve().parents[2]
|
|
323
|
+
raw_dir = project_root / "data" / "raw"
|
|
324
|
+
print(f"[sampleDataUpload] raw data dir: {raw_dir}")
|
|
325
|
+
print("TODO: implement your sample upload logic")
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
if __name__ == "__main__":
|
|
329
|
+
main()
|
|
330
|
+
"""
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
_T_PIPELINE = """\
|
|
334
|
+
\"\"\"项目 pipeline 入口(占位)。
|
|
335
|
+
|
|
336
|
+
建议在这里做:
|
|
337
|
+
- 数据/特征/训练/评估的编排
|
|
338
|
+
- 统一参数解析与运行模式(本地/离线/线上)
|
|
339
|
+
\"\"\"
|
|
340
|
+
|
|
341
|
+
from __future__ import annotations
|
|
342
|
+
|
|
343
|
+
from pathlib import Path
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def main() -> None:
|
|
347
|
+
root = Path(__file__).resolve().parent
|
|
348
|
+
print(f"Project root: {root}")
|
|
349
|
+
print("TODO: implement pipeline orchestration")
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
if __name__ == "__main__":
|
|
353
|
+
main()
|
|
354
|
+
"""
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
_T_GENERIC_MODEL_PY = """\
|
|
358
|
+
\"\"\"{model_name} 模型占位模板(通用)。
|
|
359
|
+
|
|
360
|
+
此文件为脚手架生成的最小可运行骨架:
|
|
361
|
+
- 不引入任何第三方依赖
|
|
362
|
+
- 通过写 metrics.json 的方式示范训练输出
|
|
363
|
+
|
|
364
|
+
后续你可以在这里接入 sklearn / xgboost / torch 等。
|
|
365
|
+
\"\"\"
|
|
366
|
+
|
|
367
|
+
from __future__ import annotations
|
|
368
|
+
|
|
369
|
+
from dataclasses import dataclass
|
|
370
|
+
import json
|
|
371
|
+
from pathlib import Path
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
@dataclass
|
|
375
|
+
class Config:
|
|
376
|
+
# TODO: fill model hyperparameters
|
|
377
|
+
pass
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def train(cfg: Config, save_dir: Path) -> dict:
|
|
381
|
+
save_dir.mkdir(parents=True, exist_ok=True)
|
|
382
|
+
metrics = {{"status": "not_implemented", "model": "{model_name}"}}
|
|
383
|
+
(save_dir / "metrics.json").write_text(json.dumps(metrics, indent=2), encoding="utf-8")
|
|
384
|
+
return metrics
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def main() -> None:
|
|
388
|
+
here = Path(__file__).resolve().parent
|
|
389
|
+
out = here / "save" / "save1"
|
|
390
|
+
metrics = train(Config(), out)
|
|
391
|
+
print(f"[{model_name}] placeholder done, metrics saved to: {{out}}")
|
|
392
|
+
print(metrics)
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
if __name__ == "__main__":
|
|
396
|
+
main()
|
|
397
|
+
"""
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
# -------------------------
|
|
401
|
+
# Built-in model templates
|
|
402
|
+
# -------------------------
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
_BUILTIN_MODELS: Dict[str, BuiltinModel] = {
|
|
406
|
+
"embeddingMLP": BuiltinModel(
|
|
407
|
+
name="embeddingMLP",
|
|
408
|
+
description="最小可运行的 MLP 示例(占位版本,方便后续替换成真实 embedding/深度模型)",
|
|
409
|
+
files={
|
|
410
|
+
"model.py": """\
|
|
411
|
+
\"\"\"embeddingMLP 模型训练入口(最小可运行示例)。
|
|
412
|
+
|
|
413
|
+
目标:
|
|
414
|
+
- 给脚手架一个“能跑通”的示例,避免生成后用户无从下手
|
|
415
|
+
- 真实项目中你可以将此处替换为 PyTorch/TF/自研训练流程
|
|
416
|
+
\"\"\"
|
|
417
|
+
|
|
418
|
+
from __future__ import annotations
|
|
419
|
+
|
|
420
|
+
from dataclasses import dataclass
|
|
421
|
+
import json
|
|
422
|
+
from pathlib import Path
|
|
423
|
+
import random
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
@dataclass
|
|
427
|
+
class Config:
|
|
428
|
+
# 这里是占位配置;你可以扩展为从 yaml/cli 读取
|
|
429
|
+
seed: int = 42
|
|
430
|
+
epochs: int = 3
|
|
431
|
+
lr: float = 1e-3
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def train(cfg: Config, save_dir: Path) -> dict:
|
|
435
|
+
\"\"\"占位训练函数:模拟 loss 下降并写出 metrics.json。\"\"\"
|
|
436
|
+
random.seed(cfg.seed)
|
|
437
|
+
save_dir.mkdir(parents=True, exist_ok=True)
|
|
438
|
+
|
|
439
|
+
loss = 1.0
|
|
440
|
+
history = []
|
|
441
|
+
for _ in range(cfg.epochs):
|
|
442
|
+
loss *= (0.7 + random.random() * 0.1)
|
|
443
|
+
history.append(loss)
|
|
444
|
+
|
|
445
|
+
metrics = {"final_loss": loss, "history": history, "epochs": cfg.epochs, "lr": cfg.lr}
|
|
446
|
+
(save_dir / "metrics.json").write_text(json.dumps(metrics, indent=2), encoding="utf-8")
|
|
447
|
+
return metrics
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def main() -> None:
|
|
451
|
+
cfg = Config()
|
|
452
|
+
here = Path(__file__).resolve().parent
|
|
453
|
+
out = here / "save" / "save1"
|
|
454
|
+
metrics = train(cfg, out)
|
|
455
|
+
print(f"[embeddingMLP] done, metrics saved to: {out}")
|
|
456
|
+
print(metrics)
|
|
457
|
+
|
|
458
|
+
|
|
459
|
+
if __name__ == "__main__":
|
|
460
|
+
main()
|
|
461
|
+
""",
|
|
462
|
+
"config.py": """\
|
|
463
|
+
\"\"\"embeddingMLP 配置(可按需替换为 pydantic/yaml/hydra 等)\"\"\"
|
|
464
|
+
|
|
465
|
+
from dataclasses import dataclass
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
@dataclass
|
|
469
|
+
class EmbeddingMLPConfig:
|
|
470
|
+
seed: int = 42
|
|
471
|
+
epochs: int = 3
|
|
472
|
+
lr: float = 1e-3
|
|
473
|
+
""",
|
|
474
|
+
"sampleDataUpload.py": _T_SAMPLE_UPLOAD_PY,
|
|
475
|
+
},
|
|
476
|
+
),
|
|
477
|
+
"xgBoost": BuiltinModel(
|
|
478
|
+
name="xgBoost",
|
|
479
|
+
description="XGBoost 占位模板(只提供接口骨架,不强依赖 xgboost 包)",
|
|
480
|
+
files={
|
|
481
|
+
"model.py": """\
|
|
482
|
+
\"\"\"xgBoost 模型占位模板。
|
|
483
|
+
|
|
484
|
+
说明:
|
|
485
|
+
- 为了让脚手架保持轻量,这里不强制依赖 xgboost
|
|
486
|
+
- 你可以在此处接入 xgboost.XGBClassifier / XGBRegressor
|
|
487
|
+
\"\"\"
|
|
488
|
+
|
|
489
|
+
from __future__ import annotations
|
|
490
|
+
|
|
491
|
+
from dataclasses import dataclass
|
|
492
|
+
import json
|
|
493
|
+
from pathlib import Path
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
@dataclass
|
|
497
|
+
class Config:
|
|
498
|
+
# 占位参数:按需扩展
|
|
499
|
+
n_estimators: int = 200
|
|
500
|
+
max_depth: int = 6
|
|
501
|
+
learning_rate: float = 0.1
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def train(cfg: Config, save_dir: Path) -> dict:
|
|
505
|
+
save_dir.mkdir(parents=True, exist_ok=True)
|
|
506
|
+
metrics = {"status": "not_implemented", "cfg": cfg.__dict__}
|
|
507
|
+
(save_dir / "metrics.json").write_text(json.dumps(metrics, indent=2), encoding="utf-8")
|
|
508
|
+
return metrics
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def main() -> None:
|
|
512
|
+
here = Path(__file__).resolve().parent
|
|
513
|
+
out = here / "save" / "save1"
|
|
514
|
+
metrics = train(Config(), out)
|
|
515
|
+
print(f"[xgBoost] placeholder done, metrics saved to: {out}")
|
|
516
|
+
print(metrics)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
if __name__ == "__main__":
|
|
520
|
+
main()
|
|
521
|
+
""",
|
|
522
|
+
"config.py": """\
|
|
523
|
+
from dataclasses import dataclass
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
@dataclass
|
|
527
|
+
class XGBoostConfig:
|
|
528
|
+
n_estimators: int = 200
|
|
529
|
+
max_depth: int = 6
|
|
530
|
+
learning_rate: float = 0.1
|
|
531
|
+
""",
|
|
532
|
+
"sampleDataUpload.py": _T_SAMPLE_UPLOAD_PY,
|
|
533
|
+
},
|
|
534
|
+
),
|
|
535
|
+
"randomForest": BuiltinModel(
|
|
536
|
+
name="randomForest",
|
|
537
|
+
description="RandomForest 占位模板(可对接 sklearn)",
|
|
538
|
+
files={
|
|
539
|
+
"model.py": """\
|
|
540
|
+
\"\"\"randomForest 模型占位模板。
|
|
541
|
+
|
|
542
|
+
说明:
|
|
543
|
+
- 你可以在此处接入 sklearn.ensemble.RandomForestClassifier/Regressor
|
|
544
|
+
- 脚手架默认不引入 sklearn 依赖,避免安装过重;按需在项目侧添加
|
|
545
|
+
\"\"\"
|
|
546
|
+
|
|
547
|
+
from __future__ import annotations
|
|
548
|
+
|
|
549
|
+
from dataclasses import dataclass
|
|
550
|
+
import json
|
|
551
|
+
from pathlib import Path
|
|
552
|
+
from typing import Optional
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
@dataclass
|
|
556
|
+
class Config:
|
|
557
|
+
n_estimators: int = 300
|
|
558
|
+
max_depth: Optional[int] = None
|
|
559
|
+
random_state: int = 42
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def train(cfg: Config, save_dir: Path) -> dict:
|
|
563
|
+
save_dir.mkdir(parents=True, exist_ok=True)
|
|
564
|
+
metrics = {"status": "not_implemented", "cfg": cfg.__dict__}
|
|
565
|
+
(save_dir / "metrics.json").write_text(json.dumps(metrics, indent=2), encoding="utf-8")
|
|
566
|
+
return metrics
|
|
567
|
+
|
|
568
|
+
|
|
569
|
+
def main() -> None:
|
|
570
|
+
here = Path(__file__).resolve().parent
|
|
571
|
+
out = here / "save" / "save1"
|
|
572
|
+
metrics = train(Config(), out)
|
|
573
|
+
print(f"[randomForest] placeholder done, metrics saved to: {out}")
|
|
574
|
+
print(metrics)
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
if __name__ == "__main__":
|
|
578
|
+
main()
|
|
579
|
+
""",
|
|
580
|
+
"config.py": """\
|
|
581
|
+
from dataclasses import dataclass
|
|
582
|
+
from typing import Optional
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
@dataclass
|
|
586
|
+
class RandomForestConfig:
|
|
587
|
+
n_estimators: int = 300
|
|
588
|
+
max_depth: Optional[int] = None
|
|
589
|
+
random_state: int = 42
|
|
590
|
+
""",
|
|
591
|
+
"sampleDataUpload.py": _T_SAMPLE_UPLOAD_PY,
|
|
592
|
+
},
|
|
593
|
+
),
|
|
594
|
+
}
|
|
595
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: evescaffold
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: EVE ML project scaffold generator (init/addmodel/list-models)
|
|
5
|
+
Author: EVE Scaffold
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.9
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
|
|
10
|
+
## evescaffold
|
|
11
|
+
|
|
12
|
+
一个用于 EVE 机器学习项目的脚手架工具,提供三个命令:
|
|
13
|
+
|
|
14
|
+
- `evescaffold init`: 初始化项目目录骨架与必要示例文件
|
|
15
|
+
- `evescaffold addmodel <name>`: 初始化一个模型目录与初版代码
|
|
16
|
+
- `evescaffold list-models`: 列出内置模型模板
|
|
17
|
+
|
|
18
|
+
### 安装(开发模式)
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install -e .
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
### 用法
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
evescaffold init
|
|
28
|
+
evescaffold list-models
|
|
29
|
+
evescaffold addmodel xgboost
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
也可指定项目根目录:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
evescaffold init --path /path/to/project
|
|
36
|
+
evescaffold addmodel randomForest --path /path/to/project
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
默认不会覆盖已存在文件,如需强制覆盖使用 `--force`。
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
evescaffold/__init__.py,sha256=QKnrPa3yoW0D3IUE5ZyNTZV_tmqPBA9SHgTGyYRJ9FU,77
|
|
2
|
+
evescaffold/__main__.py,sha256=CGgVifDEENegRgvIMNnjc-LfgN2tdbUkjyYZPo0x9N8,163
|
|
3
|
+
evescaffold/cli.py,sha256=Bjlrd1St3PhlQY1qgPJr-ZUKatjt7D3WZh3YvZIyJPc,2317
|
|
4
|
+
evescaffold/scaffold.py,sha256=UF3zdXgmR-7WyYyUjexn5W6sJDSEY9NAVnQwJeEOWZM,15351
|
|
5
|
+
evescaffold-0.1.0.dist-info/METADATA,sha256=wy41o5ZYr7LOvMeeThJ3wa3ezV9qOh8ZSWKk09PzXLM,894
|
|
6
|
+
evescaffold-0.1.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
7
|
+
evescaffold-0.1.0.dist-info/entry_points.txt,sha256=Dhxg2MBhiPXpWPxgJBLOscKEoBHPP9h2oMFxjs-IPG0,53
|
|
8
|
+
evescaffold-0.1.0.dist-info/top_level.txt,sha256=WpXHf6Zx8hoXnnqyTVBbC6guUkMk4j3Stgb8zGPbXv4,12
|
|
9
|
+
evescaffold-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
evescaffold
|