train_with_split.py 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. #!/usr/bin/env python3
  2. """Train BERT multi-task model with explicit 90/10 train/test split on real case data."""
  3. from __future__ import annotations
  4. import json
  5. import os
  6. import random
  7. import sys
  8. from pathlib import Path
  9. sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
  10. # NOTE: transformers must import before torch on Windows
  11. from transformers import AutoTokenizer, get_linear_schedule_with_warmup
  12. import torch
  13. from torch.utils.data import DataLoader
  14. from tqdm import tqdm
  15. from app.services.bert_multi_task_model import ChineseRobertaMultiTask
  16. from train_bert import LaborCaseDataset, collate_batch, train_epoch, evaluate
  17. DATASET = Path(__file__).resolve().parent.parent.parent / "backend" / "data" / "training_dataset.json"
  18. OUTPUT = Path(__file__).resolve().parent.parent / "models" / "chinese_roberta_labor_extractor_v2"
  19. MODEL_NAME = "hfl/chinese-roberta-wwm-ext"
  20. EPOCHS = 10
  21. BATCH_SIZE = 4
  22. LR = 2e-5
  23. MAX_LENGTH = 512
  24. PATIENCE = 3
  25. TEST_RATIO = 0.1 # 10% for test
  26. random.seed(42)
  27. torch.manual_seed(42)
  28. device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
  29. print(f"Device: {device}")
  30. # Load data
  31. with open(DATASET, encoding="utf-8") as f:
  32. data = json.load(f)
  33. cases = data["dataset"]
  34. random.shuffle(cases)
  35. n_test = max(1, int(len(cases) * TEST_RATIO))
  36. n_train = len(cases) - n_test
  37. train_cases = cases[:n_train]
  38. test_cases = cases[n_train:]
  39. print(f"Total: {len(cases)}, Train: {n_train}, Test: {n_test}")
  40. # Tokenizer
  41. tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
  42. # Datasets
  43. train_ds = LaborCaseDataset(train_cases, tokenizer, MAX_LENGTH, for_training=True)
  44. test_ds = LaborCaseDataset(test_cases, tokenizer, MAX_LENGTH, for_training=True)
  45. train_loader = DataLoader(train_ds, batch_size=BATCH_SIZE, shuffle=True,
  46. collate_fn=collate_batch, num_workers=0)
  47. test_loader = DataLoader(test_ds, batch_size=BATCH_SIZE, shuffle=False,
  48. collate_fn=collate_batch, num_workers=0)
  49. # Model
  50. model = ChineseRobertaMultiTask(model_name=MODEL_NAME)
  51. model.to(device)
  52. print(f"Model params: {sum(p.numel() for p in model.parameters()):,}")
  53. # Optimizer
  54. no_decay = ["bias", "LayerNorm.weight"]
  55. opt_params = [
  56. {"params": [p for n, p in model.named_parameters() if not any(nd in n for nd in no_decay)],
  57. "weight_decay": 0.01},
  58. {"params": [p for n, p in model.named_parameters() if any(nd in n for nd in no_decay)],
  59. "weight_decay": 0.0},
  60. ]
  61. optimizer = torch.optim.AdamW(opt_params, lr=LR)
  62. total_steps = len(train_loader) * EPOCHS
  63. scheduler = get_linear_schedule_with_warmup(optimizer, num_warmup_steps=int(total_steps * 0.1),
  64. num_training_steps=total_steps)
  65. # Training
  66. best_loss = float("inf")
  67. patience_cnt = 0
  68. for epoch in range(1, EPOCHS + 1):
  69. train_loss = train_epoch(model, train_loader, optimizer, scheduler, device, epoch)
  70. dev_metrics = evaluate(model, test_loader, device)
  71. print(f"Epoch {epoch:>2}: train_loss={train_loss:.4f}, "
  72. f"test_loss={dev_metrics['loss']:.4f}, "
  73. f"test_cause_acc={dev_metrics['cause_accuracy']:.3f}")
  74. if dev_metrics["loss"] < best_loss:
  75. best_loss = dev_metrics["loss"]
  76. patience_cnt = 0
  77. os.makedirs(OUTPUT, exist_ok=True)
  78. model.save_pretrained(str(OUTPUT))
  79. tokenizer.save_pretrained(str(OUTPUT))
  80. print(f" -> Saved to {OUTPUT}")
  81. else:
  82. patience_cnt += 1
  83. if patience_cnt >= PATIENCE:
  84. print(f"Early stop at epoch {epoch}")
  85. break
  86. print(f"\nDone. Best test loss: {best_loss:.4f}")
  87. print(f"Model: {OUTPUT}")