dtflow 0.5.7__py3-none-any.whl → 0.5.9__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.
- dtflow/SKILL.md +39 -5
- dtflow/__init__.py +1 -1
- dtflow/__main__.py +137 -8
- dtflow/cli/clean.py +294 -9
- dtflow/cli/commands.py +17 -1
- dtflow/cli/eval.py +288 -0
- dtflow/cli/export.py +81 -0
- dtflow/cli/sample.py +90 -3
- dtflow/cli/split.py +138 -0
- dtflow/cli/stats.py +224 -30
- dtflow/eval.py +276 -0
- dtflow/utils/text_parser.py +124 -0
- {dtflow-0.5.7.dist-info → dtflow-0.5.9.dist-info}/METADATA +34 -2
- {dtflow-0.5.7.dist-info → dtflow-0.5.9.dist-info}/RECORD +16 -11
- {dtflow-0.5.7.dist-info → dtflow-0.5.9.dist-info}/WHEEL +0 -0
- {dtflow-0.5.7.dist-info → dtflow-0.5.9.dist-info}/entry_points.txt +0 -0
dtflow/SKILL.md
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dtflow
|
|
3
|
-
description:
|
|
3
|
+
description: >
|
|
4
|
+
当用户需要处理 JSONL/CSV/Parquet/JSON/Arrow 数据文件时使用此 skill。
|
|
5
|
+
提供 CLI 工具 `dt` 和 Python API `DataTransformer`。
|
|
6
|
+
适用场景:(1) 查看数据:dt sample/head/tail 采样预览,dt slice 按行范围查看,dt stats 统计字段分布;
|
|
7
|
+
(2) 数据清洗:dt clean 支持 --drop-empty/--min-len/--max-len 过滤行,--keep/--drop/--rename/--promote/--add-field/--fill/--reorder 操作字段;
|
|
8
|
+
(3) 去重:dt dedupe 精确去重或 --similar 相似度去重;
|
|
9
|
+
(4) 格式转换:dt transform 预设模板(openai_chat/alpaca/sharegpt/dpo)或自定义配置;
|
|
10
|
+
(5) Schema 验证:dt validate --preset 验证数据格式;
|
|
11
|
+
(6) 数据集切分:dt split 按比例切分 train/test/val;
|
|
12
|
+
(7) 训练框架导出:dt export / export_for() 一键导出到 llama-factory/swift/axolotl;
|
|
13
|
+
(8) 大文件流式处理:load_stream() O(1) 内存处理 100GB+ 文件。
|
|
14
|
+
注意:此工具专注数据文件的结构化处理,不涉及 LLM 调用(LLM 调用请用 flexllm)。
|
|
4
15
|
---
|
|
5
16
|
|
|
6
17
|
# dtflow - 机器学习训练数据格式转换工具
|
|
@@ -135,6 +146,8 @@ dt.stats() # 统计
|
|
|
135
146
|
dt stats data.jsonl # 基本统计(文件大小、条数、字段)
|
|
136
147
|
dt stats data.jsonl --full # 完整模式:值分布、唯一值、非空率
|
|
137
148
|
dt stats data.jsonl --full -n 20 # 显示 Top 20 值分布
|
|
149
|
+
dt stats data.jsonl --field=meta.source # 只统计指定字段(支持嵌套路径,可多次使用)
|
|
150
|
+
dt stats data.jsonl --expand=tags # 展开 list 字段统计(可多次使用)
|
|
138
151
|
|
|
139
152
|
# Token 统计
|
|
140
153
|
dt token-stats data.jsonl # 默认统计 messages 字段
|
|
@@ -149,7 +162,7 @@ dt sample data.jsonl 1000 --by=category # 分层采样
|
|
|
149
162
|
dt sample data.jsonl 1000 --by=category --uniform # 均匀分层采样
|
|
150
163
|
dt sample data.jsonl --where="messages.#>=2" # 条件筛选
|
|
151
164
|
dt sample data.jsonl 10 -f input,output # 只显示指定字段
|
|
152
|
-
dt sample data.jsonl 10 --
|
|
165
|
+
dt sample data.jsonl 10 --pretty # 表格预览模式(默认原始 JSON)
|
|
153
166
|
dt sample data.jsonl 100 --seed=42 -o out.jsonl # 固定随机种子并保存
|
|
154
167
|
|
|
155
168
|
# 去重
|
|
@@ -165,8 +178,26 @@ dt clean data.jsonl --max-len=text:2000 # 最大长度过滤
|
|
|
165
178
|
dt clean data.jsonl --min-len=messages.#:2 # 最少 2 条消息
|
|
166
179
|
dt clean data.jsonl --keep=question,answer # 只保留指定字段
|
|
167
180
|
dt clean data.jsonl --drop=metadata # 删除指定字段
|
|
181
|
+
dt clean data.jsonl --rename=question:instruction,answer:output # 重命名字段
|
|
182
|
+
dt clean data.jsonl --promote=meta.label # 提升嵌套字段到顶层
|
|
183
|
+
dt clean data.jsonl --promote=meta.label:tag # 提升并自定义名称
|
|
184
|
+
dt clean data.jsonl --add-field=source:web # 添加常量字段
|
|
185
|
+
dt clean data.jsonl --fill=label:unknown # 填充空值/缺失字段
|
|
186
|
+
dt clean data.jsonl --reorder=id,text,label # 控制字段输出顺序
|
|
168
187
|
dt clean data.jsonl --strip # 去除字符串首尾空白
|
|
169
|
-
dt clean data.jsonl --
|
|
188
|
+
dt clean data.jsonl --min-tokens=content:10 # 最少 10 tokens
|
|
189
|
+
dt clean data.jsonl --max-tokens=content:1000 -m gpt-4 # 最多 1000 tokens(指定分词器)
|
|
190
|
+
dt clean data.jsonl --promote=meta.label --drop=meta --fill=label:unknown # 组合使用
|
|
191
|
+
|
|
192
|
+
# 数据集切分
|
|
193
|
+
dt split data.jsonl --ratio=0.8 --seed=42 # 二分: train/test
|
|
194
|
+
dt split data.jsonl --ratio=0.7,0.15,0.15 # 三分: train/val/test
|
|
195
|
+
dt split data.jsonl --ratio=0.8 -o /tmp/output # 指定输出目录
|
|
196
|
+
|
|
197
|
+
# 训练框架导出
|
|
198
|
+
dt export data.jsonl --framework=llama-factory # 导出到 LLaMA-Factory
|
|
199
|
+
dt export data.jsonl -f swift -o ./swift_out # 导出到 ms-swift
|
|
200
|
+
dt export data.jsonl -f llama-factory --check # 仅检查兼容性
|
|
170
201
|
|
|
171
202
|
# 验证
|
|
172
203
|
dt validate data.jsonl --preset=openai_chat # 预设: openai_chat/alpaca/dpo/sharegpt
|
|
@@ -185,10 +216,13 @@ dt diff a.jsonl b.jsonl --key=id # 对比差异
|
|
|
185
216
|
dt diff a.jsonl b.jsonl --key=id -o report.md # 输出对比报告
|
|
186
217
|
|
|
187
218
|
# 查看数据
|
|
188
|
-
dt head data.jsonl 10 # 前 10
|
|
219
|
+
dt head data.jsonl 10 # 前 10 条(默认原始 JSON)
|
|
189
220
|
dt head data.jsonl 10 -f input,output # 只显示指定字段
|
|
190
|
-
dt head data.jsonl 10 --
|
|
221
|
+
dt head data.jsonl 10 --pretty # 表格预览模式
|
|
191
222
|
dt tail data.jsonl 10 # 后 10 条
|
|
223
|
+
dt slice data.jsonl 10:20 # 第 10-19 行(Python 切片语法)
|
|
224
|
+
dt slice data.jsonl :100 # 前 100 行
|
|
225
|
+
dt slice data.jsonl 100: # 第 100 行到末尾
|
|
192
226
|
|
|
193
227
|
# 其他
|
|
194
228
|
dt run pipeline.yaml # Pipeline 执行
|
dtflow/__init__.py
CHANGED
dtflow/__main__.py
CHANGED
|
@@ -18,6 +18,8 @@ Commands:
|
|
|
18
18
|
clean 数据清洗
|
|
19
19
|
run 执行 Pipeline 配置文件
|
|
20
20
|
history 显示数据血缘历史
|
|
21
|
+
split 分割数据集
|
|
22
|
+
export 导出到训练框架
|
|
21
23
|
validate 使用 Schema 验证数据格式
|
|
22
24
|
logs 日志查看工具使用说明
|
|
23
25
|
install-skill 安装 dtflow skill 到 Claude Code
|
|
@@ -33,12 +35,16 @@ from .cli.commands import clean as _clean
|
|
|
33
35
|
from .cli.commands import concat as _concat
|
|
34
36
|
from .cli.commands import dedupe as _dedupe
|
|
35
37
|
from .cli.commands import diff as _diff
|
|
38
|
+
from .cli.commands import eval as _eval
|
|
39
|
+
from .cli.commands import export as _export
|
|
36
40
|
from .cli.commands import head as _head
|
|
37
41
|
from .cli.commands import history as _history
|
|
38
42
|
from .cli.commands import install_skill as _install_skill
|
|
39
43
|
from .cli.commands import run as _run
|
|
40
44
|
from .cli.commands import sample as _sample
|
|
41
45
|
from .cli.commands import skill_status as _skill_status
|
|
46
|
+
from .cli.commands import slice_data as _slice_data
|
|
47
|
+
from .cli.commands import split as _split
|
|
42
48
|
from .cli.commands import stats as _stats
|
|
43
49
|
from .cli.commands import tail as _tail
|
|
44
50
|
from .cli.commands import token_stats as _token_stats
|
|
@@ -69,12 +75,12 @@ def sample(
|
|
|
69
75
|
by: Optional[str] = typer.Option(None, "--by", help="分层采样字段"),
|
|
70
76
|
uniform: bool = typer.Option(False, "--uniform", help="均匀采样模式"),
|
|
71
77
|
fields: Optional[str] = typer.Option(None, "--fields", "-f", help="只显示指定字段(逗号分隔)"),
|
|
72
|
-
|
|
78
|
+
pretty: bool = typer.Option(False, "--pretty", "-R", help="使用表格预览(默认原始 JSON)"),
|
|
73
79
|
where: Optional[List[str]] = typer.Option(None, "--where", "-w", help="筛选条件 (可多次使用)"),
|
|
74
80
|
):
|
|
75
81
|
"""从数据文件中采样指定数量的数据"""
|
|
76
82
|
actual_num = num_arg if num_arg is not None else num
|
|
77
|
-
_sample(filename, actual_num, type, output, seed, by, uniform, fields,
|
|
83
|
+
_sample(filename, actual_num, type, output, seed, by, uniform, fields, not pretty, where)
|
|
78
84
|
|
|
79
85
|
|
|
80
86
|
@app.command()
|
|
@@ -84,12 +90,12 @@ def head(
|
|
|
84
90
|
num: int = typer.Option(10, "--num", "-n", help="显示数量", show_default=True),
|
|
85
91
|
output: Optional[str] = typer.Option(None, "--output", "-o", help="输出文件路径"),
|
|
86
92
|
fields: Optional[str] = typer.Option(None, "--fields", "-f", help="只显示指定字段"),
|
|
87
|
-
|
|
93
|
+
pretty: bool = typer.Option(False, "--pretty", "-R", help="使用表格预览(默认原始 JSON)"),
|
|
88
94
|
):
|
|
89
95
|
"""显示文件的前 N 条数据"""
|
|
90
96
|
# 位置参数优先于选项参数
|
|
91
97
|
actual_num = num_arg if num_arg is not None else num
|
|
92
|
-
_head(filename, actual_num, output, fields,
|
|
98
|
+
_head(filename, actual_num, output, fields, not pretty)
|
|
93
99
|
|
|
94
100
|
|
|
95
101
|
@app.command()
|
|
@@ -99,12 +105,31 @@ def tail(
|
|
|
99
105
|
num: int = typer.Option(10, "--num", "-n", help="显示数量", show_default=True),
|
|
100
106
|
output: Optional[str] = typer.Option(None, "--output", "-o", help="输出文件路径"),
|
|
101
107
|
fields: Optional[str] = typer.Option(None, "--fields", "-f", help="只显示指定字段"),
|
|
102
|
-
|
|
108
|
+
pretty: bool = typer.Option(False, "--pretty", "-R", help="使用表格预览(默认原始 JSON)"),
|
|
103
109
|
):
|
|
104
110
|
"""显示文件的后 N 条数据"""
|
|
105
111
|
# 位置参数优先于选项参数
|
|
106
112
|
actual_num = num_arg if num_arg is not None else num
|
|
107
|
-
_tail(filename, actual_num, output, fields,
|
|
113
|
+
_tail(filename, actual_num, output, fields, not pretty)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@app.command("slice")
|
|
117
|
+
def slice_cmd(
|
|
118
|
+
filename: str = typer.Argument(..., help="输入文件路径"),
|
|
119
|
+
range_str: str = typer.Argument(..., help="行号范围 (start:end),如 10:20、:100、100:、-10:"),
|
|
120
|
+
output: Optional[str] = typer.Option(None, "--output", "-o", help="输出文件路径"),
|
|
121
|
+
fields: Optional[str] = typer.Option(None, "--fields", "-f", help="只显示指定字段"),
|
|
122
|
+
pretty: bool = typer.Option(False, "--pretty", "-R", help="使用表格预览(默认原始 JSON)"),
|
|
123
|
+
):
|
|
124
|
+
"""按行号范围查看数据(Python 切片语法)
|
|
125
|
+
|
|
126
|
+
示例:
|
|
127
|
+
dt slice data.jsonl 10:20 第 10-19 行
|
|
128
|
+
dt slice data.jsonl :100 前 100 行
|
|
129
|
+
dt slice data.jsonl 100: 第 100 行到末尾
|
|
130
|
+
dt slice data.jsonl -10: 最后 10 行
|
|
131
|
+
"""
|
|
132
|
+
_slice_data(filename, range_str, output, fields, not pretty)
|
|
108
133
|
|
|
109
134
|
|
|
110
135
|
# ============ 数据转换命令 ============
|
|
@@ -164,11 +189,44 @@ def clean(
|
|
|
164
189
|
max_len: Optional[str] = typer.Option(None, "--max-len", help="最大长度过滤 (字段:长度)"),
|
|
165
190
|
keep: Optional[str] = typer.Option(None, "--keep", help="只保留指定字段"),
|
|
166
191
|
drop: Optional[str] = typer.Option(None, "--drop", help="删除指定字段"),
|
|
192
|
+
rename: Optional[str] = typer.Option(None, "--rename", help="重命名字段 (old:new,old2:new2)"),
|
|
193
|
+
promote: Optional[str] = typer.Option(
|
|
194
|
+
None, "--promote", help="提升嵌套字段到顶层 (meta.label 或 meta.label:tag)"
|
|
195
|
+
),
|
|
196
|
+
add_field: Optional[str] = typer.Option(None, "--add-field", help="添加常量字段 (key:value)"),
|
|
197
|
+
fill: Optional[str] = typer.Option(None, "--fill", help="填充空值 (field:default_value)"),
|
|
198
|
+
reorder: Optional[str] = typer.Option(
|
|
199
|
+
None, "--reorder", help="控制字段顺序 (field1,field2,...)"
|
|
200
|
+
),
|
|
167
201
|
strip: bool = typer.Option(False, "--strip", help="去除字符串首尾空白"),
|
|
202
|
+
min_tokens: Optional[str] = typer.Option(
|
|
203
|
+
None, "--min-tokens", help="最小 token 数过滤 (字段:数量)"
|
|
204
|
+
),
|
|
205
|
+
max_tokens: Optional[str] = typer.Option(
|
|
206
|
+
None, "--max-tokens", help="最大 token 数过滤 (字段:数量)"
|
|
207
|
+
),
|
|
208
|
+
model: str = typer.Option("cl100k_base", "--model", "-m", help="分词器模型 (默认 cl100k_base)"),
|
|
168
209
|
output: Optional[str] = typer.Option(None, "--output", "-o", help="输出文件路径"),
|
|
169
210
|
):
|
|
170
211
|
"""数据清洗"""
|
|
171
|
-
_clean(
|
|
212
|
+
_clean(
|
|
213
|
+
filename,
|
|
214
|
+
drop_empty,
|
|
215
|
+
min_len,
|
|
216
|
+
max_len,
|
|
217
|
+
keep,
|
|
218
|
+
drop,
|
|
219
|
+
rename,
|
|
220
|
+
promote,
|
|
221
|
+
add_field,
|
|
222
|
+
fill,
|
|
223
|
+
reorder,
|
|
224
|
+
strip,
|
|
225
|
+
min_tokens,
|
|
226
|
+
max_tokens,
|
|
227
|
+
model,
|
|
228
|
+
output,
|
|
229
|
+
)
|
|
172
230
|
|
|
173
231
|
|
|
174
232
|
# ============ 数据统计命令 ============
|
|
@@ -179,9 +237,15 @@ def stats(
|
|
|
179
237
|
filename: str = typer.Argument(..., help="输入文件路径"),
|
|
180
238
|
top: int = typer.Option(10, "--top", "-n", help="显示 Top N 值"),
|
|
181
239
|
full: bool = typer.Option(False, "--full", "-f", help="完整模式:统计值分布、唯一值等详细信息"),
|
|
240
|
+
field: Optional[List[str]] = typer.Option(
|
|
241
|
+
None, "--field", help="指定统计字段(可多次使用),支持嵌套路径"
|
|
242
|
+
),
|
|
243
|
+
expand: Optional[List[str]] = typer.Option(
|
|
244
|
+
None, "--expand", help="展开 list 字段统计(可多次使用)"
|
|
245
|
+
),
|
|
182
246
|
):
|
|
183
247
|
"""显示数据文件的统计信息"""
|
|
184
|
-
_stats(filename, top, full)
|
|
248
|
+
_stats(filename, top, full, field, expand)
|
|
185
249
|
|
|
186
250
|
|
|
187
251
|
@app.command("token-stats")
|
|
@@ -217,6 +281,71 @@ def history(
|
|
|
217
281
|
_history(filename, json)
|
|
218
282
|
|
|
219
283
|
|
|
284
|
+
# ============ 切分与导出命令 ============
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
@app.command()
|
|
288
|
+
def split(
|
|
289
|
+
filename: str = typer.Argument(..., help="输入文件路径"),
|
|
290
|
+
ratio: str = typer.Option("0.8", "--ratio", "-r", help="分割比例,如 0.8 或 0.7,0.15,0.15"),
|
|
291
|
+
seed: Optional[int] = typer.Option(None, "--seed", help="随机种子"),
|
|
292
|
+
output: Optional[str] = typer.Option(None, "--output", "-o", help="输出目录(默认同目录)"),
|
|
293
|
+
):
|
|
294
|
+
"""分割数据集为 train/test (或 train/val/test)"""
|
|
295
|
+
_split(filename, ratio, seed, output)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
@app.command()
|
|
299
|
+
def export(
|
|
300
|
+
filename: str = typer.Argument(..., help="输入文件路径"),
|
|
301
|
+
framework: str = typer.Option(
|
|
302
|
+
..., "--framework", "-f", help="目标框架: llama-factory, swift, axolotl"
|
|
303
|
+
),
|
|
304
|
+
output: Optional[str] = typer.Option(None, "--output", "-o", help="输出目录"),
|
|
305
|
+
name: Optional[str] = typer.Option(None, "--name", "-n", help="数据集名称"),
|
|
306
|
+
check: bool = typer.Option(False, "--check", help="仅检查兼容性,不导出"),
|
|
307
|
+
):
|
|
308
|
+
"""导出数据到训练框架 (LLaMA-Factory, ms-swift, Axolotl)"""
|
|
309
|
+
_export(filename, framework, output, name, check)
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
# ============ 评估命令 ============
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
@app.command()
|
|
316
|
+
def eval(
|
|
317
|
+
result_file: str = typer.Argument(..., help="模型输出的 .jsonl 文件路径"),
|
|
318
|
+
source: Optional[str] = typer.Option(
|
|
319
|
+
None, "--source", "-s", help="原始输入文件,按行号对齐合并"
|
|
320
|
+
),
|
|
321
|
+
response_col: str = typer.Option("content", "--response-col", "-r", help="模型响应字段名"),
|
|
322
|
+
label_col: Optional[str] = typer.Option(
|
|
323
|
+
None, "--label-col", "-l", help="标签字段名(不指定时自动检测)"
|
|
324
|
+
),
|
|
325
|
+
extract: str = typer.Option(
|
|
326
|
+
"direct",
|
|
327
|
+
"--extract",
|
|
328
|
+
"-e",
|
|
329
|
+
help="管道式提取规则,算子: direct/tag:X/json_key:X/index:N/line:N/lines/regex:X",
|
|
330
|
+
),
|
|
331
|
+
sep: Optional[str] = typer.Option(None, "--sep", help="配合 index 算子使用的分隔符"),
|
|
332
|
+
mapping: Optional[str] = typer.Option(None, "--mapping", "-m", help="值映射 (k1:v1,k2:v2)"),
|
|
333
|
+
output_dir: str = typer.Option("record", "--output-dir", "-o", help="指标报告输出目录"),
|
|
334
|
+
):
|
|
335
|
+
"""对模型输出进行解析和指标评估
|
|
336
|
+
|
|
337
|
+
两阶段解析:自动清洗(去 think 标签、提取代码块)+ 管道式提取。
|
|
338
|
+
|
|
339
|
+
示例:
|
|
340
|
+
dt eval result.jsonl --label-col=label
|
|
341
|
+
dt eval result.jsonl --extract="tag:标签" --mapping="是:1,否:0"
|
|
342
|
+
dt eval result.jsonl --source=input.jsonl --response-col=api_output.content
|
|
343
|
+
dt eval result.jsonl --extract="json_key:result | index:0" --sep=","
|
|
344
|
+
dt eval result.jsonl --extract="lines | index:1" --sep="|"
|
|
345
|
+
"""
|
|
346
|
+
_eval(result_file, source, response_col, label_col, extract, sep, mapping, output_dir)
|
|
347
|
+
|
|
348
|
+
|
|
220
349
|
# ============ 验证命令 ============
|
|
221
350
|
|
|
222
351
|
|