File size: 3,875 Bytes
aacc29a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
"""CLI: QLoRA fine-tune a DARAG GEC adapter, optionally per ablation variant (paper §5).

    # full DARAG:
    python scribe/training/scripts/train.py \
      --pairs artifacts/gec_pairs/darag_augmented.jsonl \
      --output-dir artifacts/gec_lora/qwen3_full --variant full --max-steps 300

    # train every ablation into <output-dir>/<variant>:
    python scribe/training/scripts/train.py --pairs ... --output-dir artifacts/gec_lora/qwen3 --all-variants
"""

from __future__ import annotations

import argparse
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[3] / "scribe" / "training"))
sys.path.insert(0, str(Path(__file__).resolve().parents[3] / "scribe"))

from gec.cliutil import configure_stdout  # noqa: E402

configure_stdout()

from gec.config import DEFAULT_BASE_MODEL, FALLBACK_BASE_MODEL, VARIANTS  # noqa: E402
from gec.train import TrainArgs, train  # noqa: E402


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--pairs", required=True, help="Augmented GEC pairs JSONL.")
    parser.add_argument("--output-dir", default="artifacts/gec_lora/qwen3_gec")
    parser.add_argument("--variant", default="full", choices=list(VARIANTS))
    parser.add_argument("--variants", nargs="+", choices=list(VARIANTS))
    parser.add_argument("--all-variants", action="store_true",
                        help="Train full + every ablation into <output-dir>/<variant>.")
    parser.add_argument("--base-model", default=DEFAULT_BASE_MODEL)
    parser.add_argument("--fallback-model", default=FALLBACK_BASE_MODEL)
    parser.add_argument("--max-steps", type=int, default=300)
    parser.add_argument("--per-device-train-batch-size", type=int, default=1)
    parser.add_argument("--gradient-accumulation-steps", type=int, default=8)
    parser.add_argument("--learning-rate", type=float, default=2e-4)
    parser.add_argument("--max-seq-length", type=int, default=768)
    parser.add_argument(
        "--seeds",
        type=int,
        nargs="+",
        default=[13],
        help="One or more seeds (paper averages 3). >1 writes <dir>/<variant>/seed-<s>.",
    )
    parser.add_argument(
        "--no-resume",
        dest="resume",
        action="store_false",
        help="Ignore any existing checkpoint and train from scratch.",
    )
    parser.set_defaults(resume=True)
    args = parser.parse_args()

    variants = list(VARIANTS) if args.all_variants else (args.variants or [args.variant])
    multi_variant = len(variants) > 1
    multi_seed = len(args.seeds) > 1
    for variant in variants:
        variant_dir = (
            Path(args.output_dir) / variant
            if args.all_variants or multi_variant
            else Path(args.output_dir)
        )
        for seed in args.seeds:
            # Single seed keeps the flat layout the predict/gate steps expect;
            # multi-seed nests each run so per-seed metrics can be averaged.
            output_dir = variant_dir / f"seed-{seed}" if multi_seed else variant_dir
            print(f"\n=== Training variant '{variant}' seed={seed} -> {output_dir} ===")
            train(
                TrainArgs(
                    pairs=Path(args.pairs),
                    output_dir=output_dir,
                    variant=variant,
                    base_model=args.base_model,
                    fallback_model=args.fallback_model,
                    max_steps=args.max_steps,
                    per_device_train_batch_size=args.per_device_train_batch_size,
                    gradient_accumulation_steps=args.gradient_accumulation_steps,
                    learning_rate=args.learning_rate,
                    max_seq_length=args.max_seq_length,
                    seed=seed,
                    resume=args.resume,
                )
            )


if __name__ == "__main__":
    main()