From cb08b9300b979f93563cdcd3b248b4e40fce1e5f Mon Sep 17 00:00:00 2001 From: Linxiushen <1787979356@qq.com> Date: Sun, 20 Sep 2026 10:33:51 +0800 Subject: [PATCH] =?UTF-8?q?[fix]=20train=5Ftokenizer=20=E8=AF=BB=E9=A2=84?= =?UTF-8?q?=E8=AE=AD=E7=BB=83=E8=AF=AD=E6=96=99=E6=97=B6=E4=BA=A7=E5=87=BA?= =?UTF-8?q?=200=20=E6=9D=A1=E6=96=87=E6=9C=AC=EF=BC=8C=E9=9D=99=E9=BB=98?= =?UTF-8?q?=E8=AE=AD=E5=87=BA=E7=A9=BA=E8=AF=8D=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit get_texts() 只解析 data.get('conversations'),但主线预训练数据 pretrain_t2t(_mini).jsonl 的每行是 {"text": ...}(README「数据集下载」一节)。 把 DATA_PATH 指向预训练语料时,生成器一条文本都不产出,BpeTrainer 仅基于 initial_alphabet 训练,得到一个没有任何 merge 的词表,全程不报错。 复现(改 DATA_PATH 为任意 pretrain_t2t*.jsonl,或直接调用): from train_tokenizer import get_texts sum(1 for _ in get_texts('../dataset/pretrain_t2t_mini.jsonl')) # 修复前: 0 # 修复后: 1270238 改动: - get_texts 同时兼容 {"text": ...} 与 {"conversations": [...]} 两种格式 - 产出 0 条时 raise ValueError,而不是让调用方拿到一个空词表 - 把硬编码的 `if i >= 10000: break` 提为模块级 MAX_LINES(默认 0 = 全量)。 原先这一行也会让 SFT 语料只用前 10000 行,且无从关闭; 另外它按「行号」计数而非「有效文本条数」,遇到空内容或解析失败的行会提前耗尽配额。 验证:用 pretrain_t2t 的前 3000 篇跑 train_tokenizer(vocab_size=2000), 得到 vocab=2000 / merges=1708;修复前同样输入得到 0 条 merge。 两种格式各 20000 行的合成数据上,产出条数均为 20000(修复前分别是 0 和 10000)。 --- trainer/train_tokenizer.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/trainer/train_tokenizer.py b/trainer/train_tokenizer.py index 336a688..682fd77 100644 --- a/trainer/train_tokenizer.py +++ b/trainer/train_tokenizer.py @@ -8,18 +8,39 @@ DATA_PATH = '../dataset/sft_t2t_mini.jsonl' TOKENIZER_DIR = '../model_learn_tokenizer/' VOCAB_SIZE = 6400 SPECIAL_TOKENS_NUM = 36 +MAX_LINES = 0 # 0 表示读取全部;设为正数则只取前 N 条(快速试跑) -def get_texts(data_path): +def get_texts(data_path, max_lines=MAX_LINES): + """逐行产出用于训练 BPE 的文本,兼容两种主线数据格式。 + + pretrain_t2t(_mini).jsonl 是 {"text": ...}, + sft_t2t(_mini).jsonl 是 {"conversations": [{"role":..., "content":...}, ...]}。 + 原先只解析 conversations,所以把 DATA_PATH 指向预训练语料时一条文本都取不到, + BPE 仅基于 initial_alphabet 训练,产出一个没有任何 merge 的词表,且全程不报错。 + """ + used = 0 with open(data_path, 'r', encoding='utf-8', errors='ignore') as f: - for i, line in enumerate(f): - if i >= 10000: break # 选10000行测试 + for line in f: + if max_lines and used >= max_lines: + break try: data = json.loads(line) - contents = [item.get('content') for item in data.get('conversations', []) if item.get('content')] - if contents: - yield "\n".join(contents) except json.JSONDecodeError: continue + if 'text' in data: + text = str(data['text']) + else: + contents = [item.get('content') for item in data.get('conversations', []) if item.get('content')] + text = "\n".join(contents) if contents else '' + if text.strip(): + used += 1 + yield text + if used == 0: + raise ValueError( + f'{data_path} 中没有可用文本:每行应为 {{"text": ...}} 或 ' + f'{{"conversations": [{{"role":..., "content":...}}, ...]}}。' + f'继续训练只会得到一个空词表。' + ) def train_tokenizer(data_path, tokenizer_dir, vocab_size, special_tokens_num=SPECIAL_TOKENS_NUM): tokenizer = Tokenizer(models.BPE())