kzfastino commited on
Commit
a762a42
·
verified ·
1 Parent(s): 6608994

Upload train_gliner_pii.py with huggingface_hub

Browse files
Files changed (1) hide show
  1. train_gliner_pii.py +269 -0
train_gliner_pii.py ADDED
@@ -0,0 +1,269 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # /// script
2
+ # dependencies = [
3
+ # "gliner>=0.2.0",
4
+ # "datasets>=2.14.0",
5
+ # "torch>=2.0.0",
6
+ # "transformers>=4.30.0",
7
+ # "trackio>=0.1.0",
8
+ # "huggingface-hub>=0.19.0",
9
+ # "accelerate>=0.20.0"
10
+ # ]
11
+ # ///
12
+
13
+ """
14
+ Fine-tune GLiNER large-v2.1 on nvidia/Nemotron-PII for PII detection
15
+ """
16
+
17
+ from datasets import load_dataset
18
+ from gliner import GLiNER, GLiNERConfig
19
+ from gliner.training import Trainer, TrainingArguments
20
+ import json
21
+ import os
22
+ from huggingface_hub import HfApi
23
+ import torch
24
+
25
+ print("=" * 80)
26
+ print("GLiNER Fine-tuning for PII Detection")
27
+ print("Model: urchade/gliner_largev2.1")
28
+ print("Dataset: nvidia/Nemotron-PII")
29
+ print("=" * 80)
30
+
31
+ # Configuration
32
+ BASE_MODEL = "urchade/gliner_largev2.1"
33
+ OUTPUT_DIR = "gliner-pii-detector"
34
+ HUB_MODEL_ID = "gliner-pii-nemotron" # Will be prefixed with username
35
+ MAX_SAMPLES = 10000 # Limit for demo, set to None for full dataset
36
+ BATCH_SIZE = 4
37
+ LEARNING_RATE = 5e-6
38
+ NUM_EPOCHS = 3
39
+ MAX_LENGTH = 384
40
+
41
+ print(f"\nConfiguration:")
42
+ print(f" Base model: {BASE_MODEL}")
43
+ print(f" Output directory: {OUTPUT_DIR}")
44
+ print(f" Max samples: {MAX_SAMPLES if MAX_SAMPLES else 'All'}")
45
+ print(f" Batch size: {BATCH_SIZE}")
46
+ print(f" Learning rate: {LEARNING_RATE}")
47
+ print(f" Epochs: {NUM_EPOCHS}")
48
+ print(f" Max length: {MAX_LENGTH}")
49
+
50
+ # Load the Nemotron-PII dataset
51
+ print("\n" + "=" * 80)
52
+ print("Loading nvidia/Nemotron-PII dataset...")
53
+ print("=" * 80)
54
+
55
+ train_dataset = load_dataset("nvidia/Nemotron-PII", split="train")
56
+ eval_dataset = load_dataset("nvidia/Nemotron-PII", split="test")
57
+
58
+ print(f"Train samples: {len(train_dataset)}")
59
+ print(f"Eval samples: {len(eval_dataset)}")
60
+
61
+ # Limit dataset size for faster training (optional)
62
+ if MAX_SAMPLES:
63
+ train_dataset = train_dataset.select(range(min(MAX_SAMPLES, len(train_dataset))))
64
+ eval_dataset = eval_dataset.select(range(min(MAX_SAMPLES // 10, len(eval_dataset))))
65
+ print(f"\nLimited to {len(train_dataset)} train and {len(eval_dataset)} eval samples")
66
+
67
+ # Print example to understand format
68
+ print("\nExample from dataset:")
69
+ example = train_dataset[0]
70
+ print(f"Keys: {list(example.keys())}")
71
+ print(f"Text snippet: {example['text'][:200]}...")
72
+ print(f"Spans: {example['spans'][:3] if len(example['spans']) > 0 else 'No spans'}...")
73
+
74
+ def convert_nemotron_to_gliner_format(examples):
75
+ """
76
+ Convert Nemotron-PII format to GLiNER training format.
77
+
78
+ Nemotron format:
79
+ {
80
+ "text": "John Doe lives at...",
81
+ "spans": [
82
+ {"start": 0, "end": 8, "label": "PERSON", "text": "John Doe"},
83
+ ...
84
+ ]
85
+ }
86
+
87
+ GLiNER format:
88
+ {
89
+ "tokenized_text": ["John", "Doe", "lives", "at", ...],
90
+ "ner": [[0, 1, "PERSON"], ...] # token indices
91
+ }
92
+ """
93
+ converted = []
94
+
95
+ for text, spans in zip(examples["text"], examples["spans"]):
96
+ # Simple whitespace tokenization (GLiNER will handle proper tokenization)
97
+ # We'll use character-level spans and convert them
98
+
99
+ if not text or not spans:
100
+ continue
101
+
102
+ # Sort spans by start position
103
+ sorted_spans = sorted(spans, key=lambda x: x["start"])
104
+
105
+ # Create a simple word-based tokenization
106
+ tokens = text.split()
107
+
108
+ # Map character positions to token indices
109
+ char_to_token = {}
110
+ current_pos = 0
111
+ for token_idx, token in enumerate(tokens):
112
+ # Find this token in the original text
113
+ token_start = text.find(token, current_pos)
114
+ if token_start >= 0:
115
+ token_end = token_start + len(token)
116
+ for char_pos in range(token_start, token_end):
117
+ char_to_token[char_pos] = token_idx
118
+ current_pos = token_end
119
+
120
+ # Convert character spans to token spans
121
+ ner_annotations = []
122
+ for span in sorted_spans:
123
+ start_char = span["start"]
124
+ end_char = span["end"]
125
+ label = span["label"]
126
+
127
+ # Find corresponding token indices
128
+ start_token = char_to_token.get(start_char)
129
+ end_token = char_to_token.get(end_char - 1) # end is exclusive
130
+
131
+ if start_token is not None and end_token is not None:
132
+ ner_annotations.append([start_token, end_token, label])
133
+
134
+ if ner_annotations: # Only include examples with valid annotations
135
+ converted.append({
136
+ "tokenized_text": tokens,
137
+ "ner": ner_annotations
138
+ })
139
+
140
+ return converted
141
+
142
+ print("\n" + "=" * 80)
143
+ print("Converting dataset to GLiNER format...")
144
+ print("=" * 80)
145
+
146
+ train_data = convert_nemotron_to_gliner_format(train_dataset)
147
+ eval_data = convert_nemotron_to_gliner_format(eval_dataset)
148
+
149
+ print(f"Converted train samples: {len(train_data)}")
150
+ print(f"Converted eval samples: {len(eval_data)}")
151
+
152
+ if len(train_data) > 0:
153
+ print("\nExample converted data:")
154
+ print(f"Tokens (first 10): {train_data[0]['tokenized_text'][:10]}")
155
+ print(f"NER annotations: {train_data[0]['ner'][:3]}")
156
+
157
+ # Save converted data to JSON files
158
+ print("\nSaving converted data...")
159
+ os.makedirs(OUTPUT_DIR, exist_ok=True)
160
+
161
+ with open(f"{OUTPUT_DIR}/train_data.json", "w") as f:
162
+ json.dump(train_data, f, indent=2)
163
+
164
+ with open(f"{OUTPUT_DIR}/eval_data.json", "w") as f:
165
+ json.dump(eval_data, f, indent=2)
166
+
167
+ print(f"Saved to {OUTPUT_DIR}/train_data.json and {OUTPUT_DIR}/eval_data.json")
168
+
169
+ # Load pre-trained GLiNER model
170
+ print("\n" + "=" * 80)
171
+ print(f"Loading base model: {BASE_MODEL}")
172
+ print("=" * 80)
173
+
174
+ model = GLiNER.from_pretrained(BASE_MODEL)
175
+
176
+ print("Model loaded successfully!")
177
+ print(f"Model config: {model.config}")
178
+
179
+ # Configure training arguments
180
+ print("\n" + "=" * 80)
181
+ print("Configuring training...")
182
+ print("=" * 80)
183
+
184
+ training_args = TrainingArguments(
185
+ output_dir=OUTPUT_DIR,
186
+ learning_rate=LEARNING_RATE,
187
+ weight_decay=0.01,
188
+ others_lr=1e-5,
189
+ others_weight_decay=0.01,
190
+ lr_scheduler_type="linear",
191
+ warmup_ratio=0.1,
192
+ per_device_train_batch_size=BATCH_SIZE,
193
+ per_device_eval_batch_size=BATCH_SIZE,
194
+ num_train_epochs=NUM_EPOCHS,
195
+ evaluation_strategy="steps",
196
+ eval_steps=500,
197
+ save_steps=500,
198
+ save_total_limit=3,
199
+ dataloader_num_workers=0,
200
+ use_cpu=False,
201
+ report_to=["trackio"],
202
+ logging_steps=50,
203
+ # Trackio configuration
204
+ project_name="gliner-pii-detection",
205
+ run_name="nemotron-pii-finetune",
206
+ )
207
+
208
+ print("Training arguments configured!")
209
+
210
+ # Initialize trainer
211
+ print("\n" + "=" * 80)
212
+ print("Initializing trainer...")
213
+ print("=" * 80)
214
+
215
+ trainer = Trainer(
216
+ model=model,
217
+ args=training_args,
218
+ train_dataset=train_data,
219
+ eval_dataset=eval_data,
220
+ tokenizer=model.data_processor.transformer_tokenizer,
221
+ data_collator=model.data_processor.collate_fn,
222
+ )
223
+
224
+ print("Trainer initialized!")
225
+
226
+ # Start training
227
+ print("\n" + "=" * 80)
228
+ print("Starting training...")
229
+ print("=" * 80)
230
+
231
+ trainer.train()
232
+
233
+ print("\n" + "=" * 80)
234
+ print("Training completed!")
235
+ print("=" * 80)
236
+
237
+ # Save the final model
238
+ print("\nSaving final model...")
239
+ model.save_pretrained(OUTPUT_DIR)
240
+
241
+ print(f"Model saved to {OUTPUT_DIR}")
242
+
243
+ # Push to Hub
244
+ print("\n" + "=" * 80)
245
+ print("Pushing model to Hugging Face Hub...")
246
+ print("=" * 80)
247
+
248
+ try:
249
+ api = HfApi()
250
+ username = api.whoami()["name"]
251
+ full_repo_id = f"{username}/{HUB_MODEL_ID}"
252
+
253
+ print(f"Pushing to: {full_repo_id}")
254
+
255
+ model.push_to_hub(
256
+ full_repo_id,
257
+ use_auth_token=True,
258
+ commit_message="Fine-tuned GLiNER on nvidia/Nemotron-PII for PII detection"
259
+ )
260
+
261
+ print(f"\n✅ Model successfully pushed to: https://huggingface.co/{full_repo_id}")
262
+
263
+ except Exception as e:
264
+ print(f"\n❌ Failed to push to Hub: {e}")
265
+ print("Model is saved locally in:", OUTPUT_DIR)
266
+
267
+ print("\n" + "=" * 80)
268
+ print("Training completed successfully!")
269
+ print("=" * 80)