MartialTerran commited on
Commit
b5293bc
·
verified ·
1 Parent(s): d88c91d

Create Surgical_Vocab_Reduction_for_QWEN2B_DotOCRv1.5_V1.0untested.py

Browse files
Surgical_Vocab_Reduction_for_QWEN2B_DotOCRv1.5_V1.0untested.py ADDED
@@ -0,0 +1,482 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ print("starting Surgical_Vocab_Reduction_for_QWEN2B_DotOCRv1.5_V1.0untested.py")
2
+ r"""
3
+ surgical_vocab_reduction.py
4
+
5
+ Description:
6
+ Surgically prunes the text vocabulary of Qwen2/dots.ocr-1.5 based models.
7
+ Removes Chinese, Cyrillic, Arabic, and other non-European characters from
8
+ the tokenizer, while preserving English, French, Spanish, special vision
9
+ tokens, layout coordinate symbols, and byte fallback sequences.
10
+
11
+ Dynamically prunes model weight files (safetensors/bin) to adjust
12
+ the input embeddings and output LM head dimensions to the new vocabulary size,
13
+ updating config.json and generation configs.
14
+
15
+ Dependencies:
16
+ - torch
17
+ - safetensors
18
+ - transformers
19
+
20
+ Surgical Vocabulary & Parameter Reduction for Qwen2 / dots.ocr-1.5 (V1.0 Untested)
21
+
22
+ This repository contains details and scripts for
23
+ Surgical_Vocab_Reduction_for_QWEN2B_DotOCRv1.5_V1.0untested.py, a pipeline
24
+ designed to prune the large multilingual vocabulary of the dots.ocr-1.5 model
25
+ (and other Qwen2-based Vision-Language Models).
26
+
27
+ By surgically removing non-target Unicode blocks (such as Chinese, Cyrillic,
28
+ Arabic, Hebrew, Japanese, and Korean) while keeping English, French, Spanish,
29
+ special layout coordinate symbols, and byte-fallback sequences, this script
30
+ significantly reduces both the model's disk/VRAM footprint and its output logit
31
+ calculation latency during local edge inference (such as on Intel Core Ultra
32
+ NPUs or mobile devices).
33
+
34
+ This implementation is inspired by and built upon the methodology outlined in
35
+ Method for Dynamically Reducing Logit Computation in LLMs.
36
+
37
+ 1. The Core Problem
38
+
39
+ Modern, state-of-the-art multimodal models like dots.ocr-1.5 are designed for
40
+ global multilingual coverage. To achieve this, they utilize very large
41
+ vocabularies (Qwen2 series, for example, features a vocabulary of 151,936
42
+ tokens).
43
+
44
+ While this makes the model highly capable globally, it introduces significant
45
+ overhead for deployment scenarios limited to Western European scripts (e.g.,
46
+ English, French, Spanish):
47
+
48
+ 1. Parameter Inflation: The input embedding layer (model.embed_tokens.weight)
49
+ and the final Language Modeling head (lm_head.weight) scale linearly with
50
+ vocabulary size. At 151,936 tokens, these two layers alone consume hundreds
51
+ of millions of parameters, adding several gigabytes of unnecessary storage
52
+ and VRAM.
53
+ 2. Computational Overhead (Logit Latency): During autoregressive token
54
+ generation, the final layer performs a matrix multiplication of shape
55
+ [batch_size * seq_len, hidden_dim] @ [hidden_dim, vocab_size] to compute
56
+ logits. Performing this massive projection on every decoded token introduces
57
+ latency, particularly on resource-constrained CPUs and edge NPUs.
58
+
59
+ 2. The Surgical Reduction Method
60
+
61
+ The script executes a targeted, multi-stage compression process that shrinks the
62
+ model's text processing layers without degrading its fundamental visual-spatial,
63
+ OCR, or layout parsing intelligence:
64
+
65
+ [ Original VLM ] ──> [ 1. Scan Tokenizer & Filter Vocab ] ──> [ 2. Filter BPE Merges ]
66
+ │ (ASCII, Latin-1, & Special Tags Only) │
67
+ ▼ ▼
68
+ [ Pruned Weights ] <── [ 4. Prune Embedding/Head Tensors ] <── [ 3. Re-index IDs ]
69
+
70
+ Stage A: Tokenizer Parsing & Robust Filtering
71
+
72
+ The script reads tokenizer.json and evaluates each token string against strict,
73
+ multi-tier criteria to determine which to keep:
74
+
75
+ - Preserve Special & Coordinate Tokens: Bounding box markers, segment
76
+ boundaries, and visual layout tags (e.g., <|image_1|>, <|box_start|>, etc.)
77
+ are detected and kept to protect the spatial parsing mechanism.
78
+ - Preserve Byte-Fallback Mappings: To prevent the tokenizer from failing when
79
+ encountering out-of-vocabulary characters, any token representing a single
80
+ byte (Latin-1 range 0x00 - 0xFF or length-1 strings) is retained.
81
+ - Targeted Script Deletion: Multi-character tokens containing Chinese
82
+ characters, Japanese Kana, Hangul, Cyrillic, Arabic, Devanagari, or Thai are
83
+ discarded.
84
+ - Targeted Language Retention: Standard ASCII punctuation, numbers, and the
85
+ accented character sets of English, French, and Spanish (using Latin-1 and
86
+ Latin Extended-A Unicode ranges) are preserved.
87
+
88
+ Stage B: Re-Indexing & Merge List Sanitization
89
+
90
+ - Discarded tokens are stripped, and the remaining tokens are sequentially
91
+ re-indexed from 0 to N_{\text{new}} - 1.
92
+ - The Byte Pair Encoding (BPE) "merges" rules are filtered. If a merge pair
93
+ contains a sub-token or output token that was deleted, the merge rule is
94
+ safely omitted. This prevents tokenizer errors or lookup warnings.
95
+
96
+ Stage C: Surgical Tensor Pruning
97
+
98
+ - The script scans the model directory for weights files (both PyTorch .bin
99
+ and Safetensors .safetensors formats).
100
+ - It locates the input embedding weights (model.embed_tokens.weight or
101
+ transformer.wte.weight) and the output projection head weights
102
+ (lm_head.weight).
103
+ - It extracts only the slices of these matrices corresponding to the indices
104
+ of the retained vocabulary, updating their shapes dynamically.
105
+
106
+ Stage D: Configuration Re-alignment
107
+
108
+ - Updates config.json with the new "vocab_size".
109
+ - Remaps bos_token_id, eos_token_id, and pad_token_id to their new values
110
+ inside generation_config.json.
111
+
112
+ 3. Anticipated Benefits
113
+
114
+ - VRAM and Storage Compression: Shifting from 151,936 tokens down to an
115
+ estimated 30,000 \text{--} 40,000 tokens significantly shrinks the
116
+ parameters of the text layers. For a hidden size of D = 3584, this reduction
117
+ strips away over 800 million parameters, translating to immediate savings of
118
+ up to 1.6 GB of VRAM/storage in FP16 (or comparable savings under quantized
119
+ configurations).
120
+ - Improved Generation Latency: Computing output logits against a vocabulary
121
+ that is roughly 75\% smaller reduces the arithmetic intensity of the final
122
+ lm_head projection layer by up to 75\%, improving token-generation speed on
123
+ resource-constrained CPUs and edge NPUs.
124
+ - Codebase & Edge Independence: By outputting standard, structurally valid
125
+ Hugging Face files, the resulting compressed model can be loaded natively by
126
+ downstream optimization toolkits (such as OpenVINO or ONNX Runtime) without
127
+ requiring custom code layers.
128
+
129
+ 4. How to Use
130
+
131
+ Setup & Dependencies
132
+
133
+ Ensure your python environment has the required libraries installed:
134
+
135
+ pip install torch safetensors transformers
136
+
137
+ Directory Structure
138
+
139
+ Place your original downloaded weights for dots.ocr-1.5 in a source directory,
140
+ and designate a destination directory for the pruned output. Example:
141
+
142
+ ./weights/
143
+ ├── dots_ocr_1_5/ <-- Original weights directory
144
+ │ ├── config.json
145
+ │ ├── tokenizer.json
146
+ │ ├── model-00001-of-00002.safetensors
147
+ │ └── ...
148
+ └── dots_ocr_1_5_pruned/ <-- Target destination for pruned output
149
+
150
+ Execution
151
+
152
+ Run the script from your terminal:
153
+
154
+ python Surgical_Vocab_Reduction_for_QWEN2B_DotOCRv1.5_V1.0untested.py
155
+
156
+ Upon successful execution, the script will output the pruned weights, tokenizer
157
+ structures, and updated configurations directly into your target directory. You
158
+ can then load, quantize (e.g., using OpenVINO for Intel NPUs), or test the
159
+ compressed model.
160
+
161
+ 5. Disclaimer & Untested Notice
162
+
163
+ Please note that this script is marked as V1.0 Untested.
164
+
165
+ While designed with strong safeguards—such as preserving all special layout
166
+ coordinates, maintaining the byte-fallback system, and pruning the BPE merge
167
+ sequences—empirical validation is required.
168
+
169
+ Users are encouraged to run evaluations and verification benchmarks (such as
170
+ measuring layout extraction accuracy, tokenization rate, and logit output
171
+ accuracy) on their pruned models to confirm that model performance meets
172
+ expectations for their targeted language profiles.
173
+
174
+ """
175
+
176
+ import os
177
+ import re
178
+ import json
179
+ import glob
180
+ import shutil
181
+ import torch
182
+ from pathlib import Path
183
+
184
+ # --- Configuration ---
185
+ MODEL_DIR = r"./weights/dots_ocr_1_5" # Source model folder
186
+ OUTPUT_DIR = r"./weights/dots_ocr_1_5_pruned" # Pruned output destination
187
+
188
+ # Unwanted Unicode blocks to target for deletion
189
+ UNWANTED_BLOCKS = [
190
+ (0x4E00, 0x9FFF), # CJK Unified Ideographs (Chinese)
191
+ (0x3400, 0x4DBF), # CJK Unified Ideographs Extension A
192
+ (0x0400, 0x04FF), # Cyrillic
193
+ (0x0600, 0x06FF), # Arabic
194
+ (0xAC00, 0xD7AF), # Hangul Syllables (Korean)
195
+ (0x3040, 0x30FF), # Hiragana & Katakana (Japanese)
196
+ (0x1100, 0x11FF), # Hangul Jamo
197
+ (0x0590, 0x05FF), # Hebrew
198
+ (0x0900, 0x097F), # Devanagari (Hindi)
199
+ (0x0A00, 0x0A7F), # Gurmukhi
200
+ (0x0B00, 0x0B7F), # Oriya
201
+ (0x0E00, 0x0E7F), # Thai
202
+ ]
203
+
204
+ # Keys used for embeddings in Hugging Face models
205
+ EMBED_KEYS = [
206
+ "model.embed_tokens.weight", # Llama/Qwen standard input embeddings
207
+ "transformer.wte.weight", # Alternate GPT/Qwen variant keys
208
+ ]
209
+ LM_HEAD_KEYS = [
210
+ "lm_head.weight", # Output LM Head mapping
211
+ ]
212
+
213
+ def should_keep_token(token_str: str) -> bool:
214
+ """
215
+ Evaluates whether a token should be retained based on character validation rules.
216
+ """
217
+ # 1. ALWAYS preserve special system & layout coordinate tokens
218
+ if token_str.startswith("<|") and token_str.endswith("|>"):
219
+ return True
220
+
221
+ # 2. ALWAYS keep single byte/single character tokens to prevent breaking byte fallbacks
222
+ if len(token_str) == 1:
223
+ return True
224
+
225
+ # 3. ALWAYS preserve raw byte fallbacks (e.g., <0xAF>)
226
+ if re.match(r"^<0x[0-9A-Fa-f]{2}>$", token_str):
227
+ return True
228
+
229
+ # 4. Check characters against unwanted Unicode script ranges
230
+ for char in token_str:
231
+ codepoint = ord(char)
232
+ for start, end in UNWANTED_BLOCKS:
233
+ if start <= codepoint <= end:
234
+ return False
235
+
236
+ return True
237
+
238
+ def prune_tokenizer_json(src_path: Path, dest_path: Path):
239
+ """
240
+ Parses, filters, re-indexes, and saves the HF fast tokenizer configurations.
241
+ """
242
+ print(f"Reading tokenizer config: {src_path.name}")
243
+ with open(src_path, "r", encoding="utf-8") as f:
244
+ data = json.load(f)
245
+
246
+ model_data = data.get("model", {})
247
+ if not model_data or model_data.get("type") != "BPE":
248
+ print("[Warning] Tokenizer is not BPE. Skipping structural modifications.")
249
+ return None, None
250
+
251
+ vocab = model_data.get("vocab", {})
252
+ merges = model_data.get("merges", [])
253
+
254
+ # Filter the vocabulary
255
+ kept_vocab = {}
256
+ kept_indices = []
257
+
258
+ # Determine which tokens to retain
259
+ for token_str, token_id in vocab.items():
260
+ if should_keep_token(token_str):
261
+ kept_vocab[token_str] = token_id
262
+ kept_indices.append(token_id)
263
+
264
+ # Re-index remaining tokens sequentially
265
+ kept_indices.sort()
266
+ old_to_new_id = {old_id: new_idx for new_idx, old_id in enumerate(kept_indices)}
267
+
268
+ new_vocab = {token_str: old_to_new_id[old_id] for token_str, old_id in kept_vocab.items()}
269
+
270
+ # Filter merges to avoid referencing deleted BPE components
271
+ new_merges = []
272
+ for merge_rule in merges:
273
+ parts = merge_rule.split(" ")
274
+ if len(parts) == 2:
275
+ left, right = parts[0], parts[1]
276
+ joined = left + right
277
+ # If sub-components are present in the vocab, retain the merge
278
+ if left in new_vocab and right in new_vocab and joined in new_vocab:
279
+ new_merges.append(merge_rule)
280
+
281
+ # Update Tokenizer structural dictionaries
282
+ data["model"]["vocab"] = new_vocab
283
+ data["model"]["merges"] = new_merges
284
+
285
+ # Re-index "added_tokens"
286
+ if "added_tokens" in data:
287
+ new_added = []
288
+ for token in data["added_tokens"]:
289
+ old_id = token.get("id")
290
+ if old_id in old_to_new_id:
291
+ token["id"] = old_to_new_id[old_id]
292
+ new_added.append(token)
293
+ data["added_tokens"] = new_added
294
+
295
+ # Save the modified JSON configuration
296
+ with open(dest_path, "w", encoding="utf-8") as f:
297
+ json.dump(data, f, indent=2, ensure_ascii=False)
298
+
299
+ print(f"Pruned tokenizer vocabulary: {len(vocab)} -> {len(new_vocab)} tokens.")
300
+ return kept_indices, old_to_new_id
301
+
302
+ def prune_legacy_vocab_files(src_dir: Path, dest_dir: Path, old_to_new_id: dict):
303
+ """
304
+ Prunes companion files like vocab.json or merges.json if present.
305
+ """
306
+ vocab_json_path = src_dir / "vocab.json"
307
+ if vocab_json_path.exists():
308
+ with open(vocab_json_path, "r", encoding="utf-8") as f:
309
+ vocab = json.load(f)
310
+ new_vocab = {}
311
+ for token, old_id in vocab.items():
312
+ if old_id in old_to_new_id:
313
+ new_vocab[token] = old_to_new_id[old_id]
314
+ with open(dest_dir / "vocab.json", "w", encoding="utf-8") as f:
315
+ json.dump(new_vocab, f, indent=2, ensure_ascii=False)
316
+
317
+ merges_json_path = src_dir / "merges.json"
318
+ if merges_json_path.exists():
319
+ with open(merges_json_path, "r", encoding="utf-8") as f:
320
+ merges = json.load(f)
321
+ new_merges = []
322
+ for rule in merges:
323
+ parts = rule.split(" ")
324
+ if len(parts) == 2 and parts[0] in new_vocab and parts[1] in new_vocab:
325
+ new_merges.append(rule)
326
+ with open(dest_dir / "merges.json", "w", encoding="utf-8") as f:
327
+ json.dump(new_merges, f, indent=2, ensure_ascii=False)
328
+
329
+ def update_model_configs(src_dir: Path, dest_dir: Path, new_vocab_size: int, old_to_new_id: dict):
330
+ """
331
+ Updates configuration variables and targets sequence tokens inside config and generation metadata files.
332
+ """
333
+ config_path = src_dir / "config.json"
334
+ if config_path.exists():
335
+ with open(config_path, "r", encoding="utf-8") as f:
336
+ config = json.load(f)
337
+ config["vocab_size"] = new_vocab_size
338
+ with open(dest_dir / "config.json", "w", encoding="utf-8") as f:
339
+ json.dump(config, f, indent=2)
340
+
341
+ # Remap base configuration targets inside Generation configuration lists
342
+ gen_config_path = src_dir / "generation_config.json"
343
+ if gen_config_path.exists():
344
+ with open(gen_config_path, "r", encoding="utf-8") as f:
345
+ gconfig = json.load(f)
346
+ for key in ["bos_token_id", "eos_token_id", "pad_token_id"]:
347
+ if key in gconfig:
348
+ val = gconfig[key]
349
+ if isinstance(val, int) and val in old_to_new_id:
350
+ gconfig[key] = old_to_new_id[val]
351
+ elif isinstance(val, list):
352
+ gconfig[key] = [old_to_new_id[v] for v in val if v in old_to_new_id]
353
+ with open(dest_dir / "generation_config.json", "w", encoding="utf-8") as f:
354
+ json.dump(gconfig, f, indent=2)
355
+
356
+ def prune_safetensors_weights(src_dir: Path, dest_dir: Path, kept_indices: list):
357
+ """
358
+ Loads Safetensors parameters dynamically, prunes target spatial dimensions,
359
+ and writes optimized bin components.
360
+ """
361
+ from safetensors import safe_open
362
+ from safetensors.torch import save_file
363
+
364
+ indices_tensor = torch.tensor(kept_indices, dtype=torch.long)
365
+ safetensor_files = glob.glob(str(src_dir / "*.safetensors"))
366
+
367
+ for file_path in safetensor_files:
368
+ p_path = Path(file_path)
369
+ print(f"Processing weights file: {p_path.name}")
370
+
371
+ tensors = {}
372
+ modified = False
373
+
374
+ with safe_open(file_path, framework="pt", device="cpu") as f:
375
+ for k in f.keys():
376
+ tensor_val = f.get_tensor(k)
377
+
378
+ # Check for input or output embedding weights
379
+ if k in EMBED_KEYS or k in LM_HEAD_KEYS:
380
+ print(f" -> Pruning target embedding layer: '{k}'")
381
+ print(f" Original dimensions: {list(tensor_val.shape)}")
382
+ tensor_val = tensor_val[indices_tensor]
383
+ print(f" New dimensions: {list(tensor_val.shape)}")
384
+ modified = True
385
+
386
+ tensors[k] = tensor_val
387
+
388
+ # Save output
389
+ out_file = dest_dir / p_path.name
390
+ save_file(tensors, str(out_file))
391
+
392
+ return modified
393
+
394
+ def prune_pytorch_bin_weights(src_dir: Path, dest_dir: Path, kept_indices: list):
395
+ """
396
+ Alternative handler for models using legacy PyTorch binary format weights files.
397
+ """
398
+ indices_tensor = torch.tensor(kept_indices, dtype=torch.long)
399
+ bin_files = glob.glob(str(src_dir / "*.bin"))
400
+
401
+ for file_path in bin_files:
402
+ p_path = Path(file_path)
403
+ print(f"Processing weights file: {p_path.name}")
404
+
405
+ state_dict = torch.load(file_path, map_location="cpu")
406
+ modified = False
407
+
408
+ for k in list(state_dict.keys()):
409
+ if k in EMBED_KEYS or k in LM_HEAD_KEYS:
410
+ print(f" -> Pruning target embedding layer: '{k}'")
411
+ print(f" Original dimensions: {list(state_dict[k].shape)}")
412
+ state_dict[k] = state_dict[k][indices_tensor]
413
+ print(f" New dimensions: {list(state_dict[k].shape)}")
414
+ modified = True
415
+
416
+ torch.save(state_dict, dest_dir / p_path.name)
417
+
418
+ return modified
419
+
420
+ def copy_miscellaneous_files(src_dir: Path, dest_dir: Path):
421
+ """
422
+ Copies companion weights files, structural classes, and configuration wrappers.
423
+ """
424
+ extensions = ["*.py", "*.png", "*.txt", "*.md", "preprocessor_config.json", "processor_config.json"]
425
+ for ext in extensions:
426
+ for f in glob.glob(str(src_dir / ext)):
427
+ shutil.copy(f, dest_dir)
428
+
429
+ def main():
430
+ print("=" * 80)
431
+ print(" Surgical Vocab and Weight Parameter Reducer")
432
+ print("=" * 80)
433
+
434
+ src_dir = Path(MODEL_DIR)
435
+ dest_dir = Path(OUTPUT_DIR)
436
+
437
+ if not src_dir.exists():
438
+ print(f"[Error] Source model folder path does not exist: {src_dir}")
439
+ return
440
+
441
+ dest_dir.mkdir(parents=True, exist_ok=True)
442
+
443
+ # 1. Prune the tokenizer vocabulary mapping
444
+ tokenizer_json = src_dir / "tokenizer.json"
445
+ if not tokenizer_json.exists():
446
+ print(f"[Error] 'tokenizer.json' was not found in: {src_dir}")
447
+ return
448
+
449
+ kept_indices, old_to_new_id = prune_tokenizer_json(tokenizer_json, dest_dir / "tokenizer.json")
450
+ if kept_indices is None:
451
+ print("[Error] Failed to prune tokenizer.")
452
+ return
453
+
454
+ new_vocab_size = len(kept_indices)
455
+
456
+ # 2. Prune alternative/legacy files if present
457
+ prune_legacy_vocab_files(src_dir, dest_dir, old_to_new_id)
458
+
459
+ # 3. Process structural weights
460
+ print("\nStarting weight parameter pruning...")
461
+ has_safetensors = len(glob.glob(str(src_dir / "*.safetensors"))) > 0
462
+
463
+ if has_safetensors:
464
+ prune_safetensors_weights(src_dir, dest_dir, kept_indices)
465
+ else:
466
+ prune_pytorch_bin_weights(src_dir, dest_dir, kept_indices)
467
+
468
+ # 4. Save metadata settings adjustments
469
+ update_model_configs(src_dir, dest_dir, new_vocab_size, old_to_new_id)
470
+
471
+ # 5. Bring over vision projections, license texts, and system processors
472
+ copy_miscellaneous_files(src_dir, dest_dir)
473
+
474
+ print("\n" + "=" * 80)
475
+ print("Reduction pipeline completed successfully.")
476
+ print(f" - Original Vocab Size (approx): {151936 if '1.5' in MODEL_DIR else 'Unknown'}")
477
+ print(f" - New Pruned Vocab Size: {new_vocab_size}")
478
+ print(f" - Reduced Weight Files Saved: {dest_dir}")
479
+ print("=" * 80)
480
+
481
+ if __name__ == "__main__":
482
+ main()