Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions examples/train_full/mixers/odm_dynamic_qwen_pt_full.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,8 @@ ddp_timeout: 180000000
### dynamic_train - ODM: Online Data Mixing with Multi-Armed Bandits
train_type: dynamic_mix
components_cfg_file: src/dataflex/configs/components.yaml
component_name: odm # 使用ODM混合器 (Online Data Mixing with Exp3)
mixture_sample_rule: mixture # 初始采样规则,mixture为根据init_mixture_proportions比例混合
component_name: odm # use ODM mixer (Online Data Mixing with Exp3)
mixture_sample_rule: mixture # initial sampling rule, mixture is according to the init_mixture_proportions ratio
init_mixture_proportions: [0.5, 0.5] # initial weights
warmup_step: 10
update_step: 10
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ finetuning_type: full
deepspeed: examples/deepspeed/ds_z2_config.json

### dataset
# 动态排序不需要原始数据带分数字段:分数由当前模型在线算出来。
# Dynamic reorder does not require original data to have a score field: the score is calculated online by the current model.
dataset: alpaca_en_demo
template: qwen
cutoff_len: 2048
Expand Down Expand Up @@ -43,8 +43,8 @@ train_type: dynamic_reorder
components_cfg_file: src/dataflex/configs/components.yaml
component_name: dynamic_saw
warmup_step: 10
update_step: 50 # 50 步用当前模型重新给剩余样本打分并重排
update_step: 50 # every 50 steps, use the current model to re-score and re-order the remaining samples
update_times: 20

# 开销提醒:每个 update 间隔要对样本池做一次前向。用 components.yaml 里的
# score_params.max_samples 限制打分规模,或调大 reorder_every 降低打分频率。
# Cost reminder: every update interval, the sample pool needs to do one forward pass. Use score_params.max_samples in components.yaml to limit the scoring scale, or increase reorder_every to reduce the scoring frequency.
# Use score_params.max_samples in components.yaml to limit the scoring scale, or increase reorder_every to reduce the scoring frequency.
62 changes: 62 additions & 0 deletions examples/train_full/reorder/saw_qwen_sft_full.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
### model
model_name_or_path: Qwen/Qwen3-1.7B-Base
trust_remote_code: true

### method
stage: sft
do_train: true
finetuning_type: full
deepspeed: examples/deepspeed/ds_z2_config.json

### dataset
# Original jsonl must contain a score field (see score_field in components.yaml).
# The paper uses "quality scores" like FineWeb-Edu's education score / QuRating score.
dataset: alpaca_en_demo
template: qwen
cutoff_len: 2048
overwrite_cache: true
preprocessing_num_workers: 16
dataloader_num_workers: 4
seed: 42

# Constraints related to order:
# - val_size must be 0 (train_test_split will shuffle, shuffle the order); use eval_dataset for validation
# - mix_strategy must be concat (interleave_* will shuffle); use eval_dataset for validation
# - num_samples in dataset_info.json must not be set (it uses an unseeded random permutation)
val_size: 0
mix_strategy: concat

# packing will re-pack samples within a window of preprocessing_batch_size, which is equivalent to an implicit JIT of that width.
# To make a clean comparison, turn it off.
packing: false

# It is recommended to use a separate tokenized_path for each reorder variant to avoid mixing up HF cache and avoid duplicate tokenization.
# tokenized_path: ../dataflex_saves/tokenized/alpaca_saw

### output
output_dir: ../dataflex_saves/Qwen3-1.7B/reorder_saw
logging_steps: 10
save_steps: 500
plot_loss: true
overwrite_output_dir: true
report_to: none

### train
per_device_train_batch_size: 4
gradient_accumulation_steps: 8
learning_rate: 2.0e-5
num_train_epochs: 1.0
lr_scheduler_type: cosine
warmup_ratio: 0.1
bf16: true
ddp_timeout: 180000000

### Dataflex args
train_type: dynamic_reorder
components_cfg_file: src/dataflex/configs/components.yaml
component_name: saw # preset name in reorders in components.yaml
# optional: sorting(CL) / shuffle / segment / folding / zigzag / stair / saw
warmup_step: 10
update_step: 50
update_times: 20 # to run a full round, let warmup_step + update_step*update_times
# approximately equal to len(dataset) / (per_device_bs * grad_accum * world_size)
62 changes: 0 additions & 62 deletions examples/train_full/reorderers/saw_qwen_sft_full.yaml

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ ddp_timeout: 180000000
train_type: dynamic_mix
components_cfg_file: src/dataflex/configs/components.yaml
component_name: doremi
mixture_sample_rule: mixture # 初始采样规则,mixture为根据init_mixture_proportions比例混合(可动态调整),stratified为固定按源数据集大小比例分层,uniform为固定均匀分布
init_mixture_proportions: [0.5, 0.5] # 对应初始的比例,可通过额外算法自行调整
mixture_sample_rule: mixture # initial sampling rule, mixture is according to the init_mixture_proportions ratio (can be dynamically adjusted), stratified is fixed according to the source dataset size ratio, uniform is fixed uniform distribution
init_mixture_proportions: [0.5, 0.5] # corresponding initial proportions, can be adjusted by additional algorithms
warmup_step: 100
update_step: 200
update_times: 3
Expand Down
72 changes: 36 additions & 36 deletions src/dataflex/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,23 @@ def patch_trainer(train_type: str):
TrainerCls = None

if TrainerCls is not None:
# 1) 替换源头模块
# 1) Replace source module
tmod = importlib.import_module("llamafactory.train.sft.trainer")
tmod.CustomSeq2SeqTrainer = TrainerCls

# 2) 替换包层 re-export
# 2) Replace package layer re-export
sft_pkg = importlib.import_module("llamafactory.train.sft")
setattr(sft_pkg, "CustomSeq2SeqTrainer", TrainerCls)

# 3) 替换 workflow 内部引用
# 3) Replace workflow internal references
wflow = importlib.import_module("llamafactory.train.sft.workflow")
setattr(wflow, "CustomSeq2SeqTrainer", TrainerCls)

# 4) 替换 PT 训练器
# 4) Replace PT trainer
pt_tmod = importlib.import_module("llamafactory.train.pt.trainer")
pt_tmod.CustomTrainer = TrainerCls

# 5) 替换 PT workflow 内部引用
# 5) Replace PT workflow internal references
pt_wflow = importlib.import_module("llamafactory.train.pt.workflow")
setattr(pt_wflow, "CustomTrainer", TrainerCls)

Expand All @@ -100,42 +100,42 @@ def patch_trainer(train_type: str):

def patch_get_dataset(do_uncache_reload: bool = False):
"""
LlamaFactoryget_dataset 替换为 dataflex 版本。
- 源头: llamafactory.data.loader.get_dataset -> dataflex.train.data.loader.get_dataset
- 包层 re-export: 覆盖 llamafactory.data.get_dataset(如有)
- 就地覆盖: 对已 from-import 的使用方(包含 workflow)直接改其全局符号
Replace LlamaFactory's get_dataset with dataflex version.
- Source: llamafactory.data.loader.get_dataset -> dataflex.train.data.loader.get_dataset
- Package layer re-export: Overwrite llamafactory.data.get_dataset (if any)
- In-place overwrite: Directly modify the global symbol for already from-imported users (including workflow)

Args:
do_uncache_reload: True 时,会清理下游依赖缓存并预热导入,以确保后续 import 也拿到新函数。
默认为 False(与“就地打补丁”策略一致)。
do_uncache_reload: When True, will clear downstream dependency cache and warm up imports to ensure subsequent imports also get the new function.
Default is False (consistent with "in-place patching" strategy).
"""
# 1) 引入新实现
# 1) Introduce new implementation
from dataflex.train.data.loader import get_dataset as _new_get_dataset
# 2) 覆盖源头模块
# 2) Overwrite source module
data_loader_mod = importlib.import_module("llamafactory.data.loader")
setattr(data_loader_mod, "get_dataset", _new_get_dataset)
# 3) 覆盖包层 re-export(若其它代码从包层 import)
# 3) Overwrite package layer re-export (if other code imports from package layer)
data_pkg = importlib.import_module("llamafactory.data")
setattr(data_pkg, "get_dataset", _new_get_dataset)
# 4) 就地覆盖已 from-import 的使用方(包含 workflow
# 4) In-place overwrite already from-imported users (including workflow)
wflow = importlib.import_module("llamafactory.train.sft.workflow")
setattr(wflow, "get_dataset", _new_get_dataset)

# 5) 也要patch PT workflow
# 5) Also patch PT workflow
pt_wflow = importlib.import_module("llamafactory.train.pt.workflow")
setattr(pt_wflow, "get_dataset", _new_get_dataset)

def patch_reorder_get_dataset(cfg):
"""
get_dataset 替换为"先按分数重排原始行、再做预处理"的版本。
Replace get_dataset with the version that "first reorder raw rows by score, then preprocess".

只有 apply_at == 'raw' 时才需要:分数字段在 align_dataset 里就被删掉了,
而预处理不保 index(脏样本会被丢弃、packing 会合并行),所以直接重排原始
数据集,让顺序自然传递下去。apply_at == 'index' 时顺序在 trainer 里施加,
数据加载流程无需改动。
Only needed when apply_at == 'raw': the score field is removed in align_dataset,
and preprocessing does not preserve index (dirty samples are discarded, packing merges rows),
so we directly reorder the raw dataset to pass the order naturally.
When apply_at == 'index', the order is applied in trainer, and the data loading process remains unchanged.

Returns:
bool: 是否真的打了补丁。
bool: Whether the patch is actually applied.
"""
from dataflex.utils.load_component import load_component

Expand All @@ -146,24 +146,24 @@ def patch_reorder_get_dataset(cfg):

from dataflex.core.registry import REGISTRY
from dataflex.train.data.loader import make_reorder_get_dataset
from dataflex.train.reorder import resolve_reorderer_kind # also registers the reorderers
from dataflex.train.reorder import resolve_reorder_kind # also registers the reorders

params = load_component('reorderers', cfg_file, name, runtime_vars={})
kind = resolve_reorderer_kind(name, params)
params = load_component('reorders', cfg_file, name, runtime_vars={})
kind = resolve_reorder_kind(name, params)

# 只有"静态 + 在原始行上重排"才需要改数据加载。动态排序的分数来自当前模型,
# 顺序必然是在 trainer 里按 dataset index 施加的。
# Only "static + reorder on raw rows" needs to modify data loading. The dynamic sorting scores come from the current model,
# the order must be applied in trainer by dataset index.
if kind != 'static' or params.get('apply_at', 'raw') != 'raw':
print(f"[PatchReorder] reorderer '{name}' orders by dataset index; dataset loading left untouched.")
print(f"[PatchReorder] reorder '{name}' orders by dataset index; dataset loading left untouched.")
return False

def reorderer_factory():
return REGISTRY.build('reorderer', kind, runtime={}, cfg=params)
def reorder_factory():
return REGISTRY.build('reorder', kind, runtime={}, cfg=params)

_new_get_dataset = make_reorder_get_dataset(reorderer_factory)
_new_get_dataset = make_reorder_get_dataset(reorder_factory)

# patch_get_dataset 同样的四处覆盖:源头模块、包层 re-export、以及
# sft/pt 两个 workflow 里已经 from-import 过的全局符号。
# Same four patches as patch_get_dataset: source module, package layer re-export, and
# already from-imported global symbols in sft/pt workflows.
data_loader_mod = importlib.import_module("llamafactory.data.loader")
setattr(data_loader_mod, "get_dataset", _new_get_dataset)
data_pkg = importlib.import_module("llamafactory.data")
Expand All @@ -173,7 +173,7 @@ def reorderer_factory():
pt_wflow = importlib.import_module("llamafactory.train.pt.workflow")
setattr(pt_wflow, "get_dataset", _new_get_dataset)

print(f"[PatchReorder] reorderer '{name}' will permute raw rows before preprocessing.")
print(f"[PatchReorder] reorder '{name}' will permute raw rows before preprocessing.")
return True

def read_args():
Expand All @@ -184,7 +184,7 @@ def read_args():
dict_config = OmegaConf.load(Path(file_path).absolute())
cfg = OmegaConf.merge(dict_config, override_config)
else:
cfg = OmegaConf.create({}) # CLI 直接传参时
cfg = OmegaConf.create({}) # When passing CLI arguments directly

return OmegaConf.to_container(cfg)

Expand All @@ -211,7 +211,7 @@ def print_welcome():
def main():
command = sys.argv.pop(1)
if command == "version":
# 只打印版本和欢迎
# Only print version and welcome
print_welcome()
return
elif command != 'train':
Expand Down
38 changes: 19 additions & 19 deletions src/dataflex/configs/components.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -170,37 +170,37 @@ weighters:
adapt:
name: adapt
params:
tau: 1.0 # 温度,越小权重区分越锐利
refresh_interval: 50 # 每多少步用当前模型刷新一次 anchor 向量
anchor_batch_size: 8 # 计算句向量时的前向 batch 大小
clip: null # 可选权重上限,防梯度爆炸
tau: 1.0 # Temperature, smaller values make weight distinction sharper
refresh_interval: 50 # Every how many steps to refresh the anchor vector with the current model
anchor_batch_size: 8 # Forward batch size for computing sentence embeddings
clip: null # Optional weight upper bound, to prevent gradient explosion

joint_update_aware:
name: joint_update_aware
params:
# 求解 max_w s^T w - (beta / 2) w^T S w + tau * H(w)
# S 为归一化 embedding 的余弦 Gram 矩阵,s_i = <u, z_i> 为对 anchor 均值 u 的对齐度
beta: 0.1 # 交互/冗余强度
tau: 0.05 # 熵温度(需 > 0),越小权重越锐利
fixed_point_iters: 5 # 阻尼定点迭代次数
damping: 1.0 # 阻尼系数 rho ∈ (0, 1]1.0 表示不阻尼
target_update_step: 50 # 每多少步用 eval 集刷新一次目标向量
target_batch_size: 1 # 计算目标向量时的前向 batch 大小
target_num_batches: 1 # 每次刷新平均多少个 eval batch;设为 0 表示遍历整个 eval
embed_normalize: true # 是否对 embedding 做 L2 归一化
pooling: last_token # 句向量池化方式:last_token / mean_pool
embed_layer: -1 # 取哪一层 hidden state-1 为最后一层
# Solve max_w s^T w - (beta / 2) w^T S w + tau * H(w)
# S is the cosine Gram matrix of normalized embeddings, s_i = <u, z_i> is the alignment degree of the anchor mean u
beta: 0.1 # Interaction/redundancy strength
tau: 0.05 # Entropy temperature (must be > 0), smaller values make weights sharper
fixed_point_iters: 5 # Damping fixed point iterations
damping: 1.0 # Damping coefficient rho ∈ (0, 1], 1.0 means no damping
target_update_step: 50 # Every how many steps to refresh the target vector with the eval set
target_batch_size: 1 # Forward batch size for computing target embeddings
target_num_batches: 1 # Average how many eval batches to refresh per target; set to 0 to iterate through the entire eval set
embed_normalize: true # Whether to normalize embeddings with L2 norm
pooling: last_token # Sentence vector pooling: last_token / mean_pool
embed_layer: -1 # Which hidden state layer to take, -1 means the last layer
objective_mode: full # full / align_only / diverse_only / uniform

custom:
name: custom
params:
strategy: uniform

reorderers:
# Data reorderers change sample order only; the number of samples is untouched.
reorders:
# Data reorders change sample order only; the number of samples is untouched.
#
# Every reorderer is defined along three axes:
# Every reorder is defined along three axes:
# pattern : one of seven orderings, covering the four guidances of the paper
# [shuffle, sorting, folding, zigzag, segment, stair, saw]
# score_source : where the scores come from [precomputed, cached_selection, model_loss]
Expand Down
8 changes: 4 additions & 4 deletions src/dataflex/core/registry.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ def get(self, kind: str, name: str) -> Type:
def build(self, kind: str, name: str, *, runtime: Dict[str, Any], cfg: Optional[Dict[str, Any]] = None):
cls = self.get(kind, name)
cfg = cfg or {}
merged = {**cfg, **runtime} # 运行期依赖优先
merged = {**cfg, **runtime} # Runtime dependencies take precedence
sig = inspect.signature(cls.__init__)
accepted = {p.name for p in list(sig.parameters.values())[1:]} # 跳过 self
filtered = {k: v for k, v in merged.items() if k in accepted} # 只喂需要的
accepted = {p.name for p in list(sig.parameters.values())[1:]} # Skip self
filtered = {k: v for k, v in merged.items() if k in accepted} # Only feed needed
return cls(**filtered)

REGISTRY = Registry()
def register_selector(name: str): return REGISTRY.register("selector", name)
def register_mixer(name: str): return REGISTRY.register("mixer", name)
def register_weighter(name: str): return REGISTRY.register("weighter", name)
def register_reorderer(name: str): return REGISTRY.register("reorderer", name)
def register_reorder(name: str): return REGISTRY.register("reorder", name)
Loading
Loading