mirror of
https://github.com/jingyaogong/minimind.git
synced 2026-09-25 12:27:22 +00:00
[fix] train_tokenizer 读预训练语料时产出 0 条文本,静默训出空词表
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)。
This commit is contained in:
@@ -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())
|
||||
|
||||
Reference in New Issue
Block a user