Add tokenizer training script and save trained tokenizer into checkpoint dir
Browse files- runs/train_tokenizer.sh +8 -0
- src/trainers/lm/train_tokenizer.py +40 -13
runs/train_tokenizer.sh
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
# Tokenizer 训练启动脚本(仅供学习和参考,不建议重复训练 tokenizer)
|
| 3 |
+
# 用法: bash runs/train_tokenizer.sh [额外参数...]
|
| 4 |
+
set -e
|
| 5 |
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
| 6 |
+
cd "$ROOT"
|
| 7 |
+
export PYTHONPATH="$ROOT/src:${PYTHONPATH}"
|
| 8 |
+
exec python -m trainers.lm.train_tokenizer --config "$ROOT/configs/model/lm.yaml" "$@"
|
src/trainers/lm/train_tokenizer.py
CHANGED
|
@@ -2,10 +2,11 @@
|
|
| 2 |
# Note: It is not recommended to re-train the tokenizer. MiniMind already includes one. This script is for learning and reference only. Training models with different tokenizers will lead to inconsistent outputs and reduce model reusability in the community.
|
| 3 |
import os
|
| 4 |
import json
|
|
|
|
| 5 |
from tokenizers import decoders, models, pre_tokenizers, trainers, Tokenizer
|
| 6 |
|
| 7 |
DATA_PATH = '../dataset/sft_t2t_mini.jsonl'
|
| 8 |
-
|
| 9 |
VOCAB_SIZE = 6400
|
| 10 |
SPECIAL_TOKENS_NUM = 36
|
| 11 |
|
|
@@ -21,25 +22,30 @@ def get_texts(data_path):
|
|
| 21 |
except json.JSONDecodeError:
|
| 22 |
continue
|
| 23 |
|
| 24 |
-
def
|
| 25 |
-
tokenizer = Tokenizer(models.BPE())
|
| 26 |
-
tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
|
| 27 |
-
|
| 28 |
special_tokens_list = [
|
| 29 |
-
"<|endoftext|>", "<|im_start|>", "<|im_end|>",
|
| 30 |
-
"<|object_ref_start|>", "<|object_ref_end|>", "<|box_start|>", "<|box_end|>", "<|quad_start|>", "<|quad_end|>",
|
| 31 |
-
"<|vision_start|>", "<|vision_end|>", "<|vision_pad|>", "<|image_pad|>", "<|video_pad|>",
|
| 32 |
"<|audio_start|>", "<|audio_end|>", "<|audio_pad|>", "<tts_pad>", "<tts_text_bos>", "<tts_text_eod>", "<tts_text_bos_single>"
|
| 33 |
]
|
| 34 |
-
|
| 35 |
additional_tokens_list = [
|
| 36 |
"<tool_call>", "</tool_call>",
|
| 37 |
"<tool_response>", "</tool_response>",
|
| 38 |
"<think>", "</think>"
|
| 39 |
]
|
| 40 |
num_buffer = special_tokens_num - len(special_tokens_list + additional_tokens_list)
|
| 41 |
-
buffer_tokens = [f"<|buffer{i}|>" for i in range(1, num_buffer + 1)]
|
| 42 |
all_special_tokens = special_tokens_list + additional_tokens_list + buffer_tokens
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
trainer = trainers.BpeTrainer(
|
| 44 |
vocab_size=vocab_size,
|
| 45 |
show_progress=True,
|
|
@@ -51,6 +57,12 @@ def train_tokenizer(data_path, tokenizer_dir, vocab_size, special_tokens_num=SPE
|
|
| 51 |
tokenizer.decoder = decoders.ByteLevel()
|
| 52 |
tokenizer.add_special_tokens(special_tokens_list)
|
| 53 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 54 |
os.makedirs(tokenizer_dir, exist_ok=True)
|
| 55 |
tokenizer.save(os.path.join(tokenizer_dir, "tokenizer.json"))
|
| 56 |
tokenizer.model.save(tokenizer_dir)
|
|
@@ -103,7 +115,7 @@ def train_tokenizer(data_path, tokenizer_dir, vocab_size, special_tokens_num=SPE
|
|
| 103 |
|
| 104 |
with open(os.path.join(tokenizer_dir, "tokenizer_config.json"), "w", encoding="utf-8") as f:
|
| 105 |
json.dump(config, f, ensure_ascii=False, indent=4)
|
| 106 |
-
print("Tokenizer
|
| 107 |
|
| 108 |
def eval_tokenizer(tokenizer_dir):
|
| 109 |
from transformers import AutoTokenizer
|
|
@@ -164,5 +176,20 @@ def eval_tokenizer(tokenizer_dir):
|
|
| 164 |
token_cache = []
|
| 165 |
|
| 166 |
if __name__ == '__main__':
|
| 167 |
-
|
| 168 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2 |
# Note: It is not recommended to re-train the tokenizer. MiniMind already includes one. This script is for learning and reference only. Training models with different tokenizers will lead to inconsistent outputs and reduce model reusability in the community.
|
| 3 |
import os
|
| 4 |
import json
|
| 5 |
+
import argparse
|
| 6 |
from tokenizers import decoders, models, pre_tokenizers, trainers, Tokenizer
|
| 7 |
|
| 8 |
DATA_PATH = '../dataset/sft_t2t_mini.jsonl'
|
| 9 |
+
CHECKPOINT_DIR = '../checkpoint/'
|
| 10 |
VOCAB_SIZE = 6400
|
| 11 |
SPECIAL_TOKENS_NUM = 36
|
| 12 |
|
|
|
|
| 22 |
except json.JSONDecodeError:
|
| 23 |
continue
|
| 24 |
|
| 25 |
+
def build_special_tokens(special_tokens_num=SPECIAL_TOKENS_NUM):
|
|
|
|
|
|
|
|
|
|
| 26 |
special_tokens_list = [
|
| 27 |
+
"<|endoftext|>", "<|im_start|>", "<|im_end|>",
|
| 28 |
+
"<|object_ref_start|>", "<|object_ref_end|>", "<|box_start|>", "<|box_end|>", "<|quad_start|>", "<|quad_end|>",
|
| 29 |
+
"<|vision_start|>", "<|vision_end|>", "<|vision_pad|>", "<|image_pad|>", "<|video_pad|>",
|
| 30 |
"<|audio_start|>", "<|audio_end|>", "<|audio_pad|>", "<tts_pad>", "<tts_text_bos>", "<tts_text_eod>", "<tts_text_bos_single>"
|
| 31 |
]
|
| 32 |
+
|
| 33 |
additional_tokens_list = [
|
| 34 |
"<tool_call>", "</tool_call>",
|
| 35 |
"<tool_response>", "</tool_response>",
|
| 36 |
"<think>", "</think>"
|
| 37 |
]
|
| 38 |
num_buffer = special_tokens_num - len(special_tokens_list + additional_tokens_list)
|
| 39 |
+
buffer_tokens = [f"<|buffer{i}|>" for i in range(1, num_buffer + 1)] # 预留一定数量的token位置
|
| 40 |
all_special_tokens = special_tokens_list + additional_tokens_list + buffer_tokens
|
| 41 |
+
return special_tokens_list, all_special_tokens
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def train_tokenizer(data_path, vocab_size, special_tokens_num=SPECIAL_TOKENS_NUM, checkpoint_dir=None):
|
| 45 |
+
tokenizer = Tokenizer(models.BPE())
|
| 46 |
+
tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
|
| 47 |
+
|
| 48 |
+
special_tokens_list, all_special_tokens = build_special_tokens(special_tokens_num)
|
| 49 |
trainer = trainers.BpeTrainer(
|
| 50 |
vocab_size=vocab_size,
|
| 51 |
show_progress=True,
|
|
|
|
| 57 |
tokenizer.decoder = decoders.ByteLevel()
|
| 58 |
tokenizer.add_special_tokens(special_tokens_list)
|
| 59 |
|
| 60 |
+
if checkpoint_dir:
|
| 61 |
+
ckpt_tokenizer_dir = os.path.join(checkpoint_dir, "tokenizer")
|
| 62 |
+
save_tokenizer(tokenizer, ckpt_tokenizer_dir, special_tokens_list, all_special_tokens)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
def save_tokenizer(tokenizer, tokenizer_dir, special_tokens_list, all_special_tokens):
|
| 66 |
os.makedirs(tokenizer_dir, exist_ok=True)
|
| 67 |
tokenizer.save(os.path.join(tokenizer_dir, "tokenizer.json"))
|
| 68 |
tokenizer.model.save(tokenizer_dir)
|
|
|
|
| 115 |
|
| 116 |
with open(os.path.join(tokenizer_dir, "tokenizer_config.json"), "w", encoding="utf-8") as f:
|
| 117 |
json.dump(config, f, ensure_ascii=False, indent=4)
|
| 118 |
+
print(f"Tokenizer saved to {tokenizer_dir}.")
|
| 119 |
|
| 120 |
def eval_tokenizer(tokenizer_dir):
|
| 121 |
from transformers import AutoTokenizer
|
|
|
|
| 176 |
token_cache = []
|
| 177 |
|
| 178 |
if __name__ == '__main__':
|
| 179 |
+
parser = argparse.ArgumentParser(description="MiniMind Tokenizer Training (学习用)")
|
| 180 |
+
parser.add_argument("--data_path", type=str, default=DATA_PATH, help="训练语料路径 (.jsonl)")
|
| 181 |
+
parser.add_argument("--checkpoint_dir", type=str, default=CHECKPOINT_DIR, help="tokenizer 保存到该 checkpoint 目录下的 tokenizer/ 子目录")
|
| 182 |
+
parser.add_argument("--vocab_size", type=int, default=VOCAB_SIZE, help="词表大小")
|
| 183 |
+
parser.add_argument("--special_tokens_num", type=int, default=SPECIAL_TOKENS_NUM, help="特殊 token 预留数量")
|
| 184 |
+
parser.add_argument("--no_eval", action="store_true", help="训练后跳过评估")
|
| 185 |
+
args = parser.parse_args()
|
| 186 |
+
|
| 187 |
+
ckpt_tokenizer_dir = os.path.join(args.checkpoint_dir, "tokenizer")
|
| 188 |
+
train_tokenizer(
|
| 189 |
+
args.data_path,
|
| 190 |
+
args.vocab_size,
|
| 191 |
+
special_tokens_num=args.special_tokens_num,
|
| 192 |
+
checkpoint_dir=args.checkpoint_dir,
|
| 193 |
+
)
|
| 194 |
+
if not args.no_eval:
|
| 195 |
+
eval_tokenizer(ckpt_tokenizer_dir)
|