如何让英文大语言模型支持中文:继续预训练
前言
在上一篇文章中,我们介绍了如何构建中文领域的 Tokenization。本文将介绍如何进行继续预训练(Continual Pre-training)。
我们在词表中新增加了一些中文词汇,这些词汇在原始模型中未得到充分训练。因此,在进行指令微调之前,我们需要进行预训练。预训练的基本原理是根据上一个字预测下一个字是什么。为了简化流程,本文直接使用 IDEA-CCNL/Wenzhong2.0-GPT2-110M-BertTokenizer-chinese 模型,并沿用其自带的 tokenizer。
相关代码已上传至 GitHub:https://github.com/taishan1994/chinese_llm_pretrained
数据处理
我们使用的数据依然是《斗破苍穹》小说数据。数据位于 data 目录下,包含 corpus.txt 和 test_corpus.txt,每一行包含一句或多句话。
数据预处理逻辑
在 test_dataset.py 脚本中,主要完成了以下工作:
- 加载 Tokenizer:使用
LlamaTokenizer或BertTokenizer加载预训练模型的词表。 - 分词处理:对文本进行 tokenize,注意不同模型的 tokenizer 可能会在文本前后添加特殊标记(如
bos_token_id和eos_token_id)。 - 文本拼接与切分:将所有文本的
input_ids拼接起来,设定最大长度block_size,将长文本切分为固定长度的块。 - 标签设置:将
input_ids复制为labels,用于计算损失函数。
以下是核心代码实现:
import os
import logging
import datasets
import transformers
from pprint import pprint
from itertools import chain
from datasets import load_dataset, concatenate_datasets
from transformers.testing_utils import CaptureLogger
from transformers import AutoTokenizer, LlamaTokenizer
tok_logger = transformers.utils.logging.get_logger("transformers.tokenization_utils_base")
logger = logging.getLogger(__name__)
lm_datasets = []
files = ["data/test_corpus.txt"]
data_cache_dir = "./cache_data"
preprocessing_num_workers =
tokenizer = AutoTokenizer.from_pretrained()
():
k, v adict.items():
(k, v)
():
CaptureLogger(tok_logger) cl:
output = tokenizer(examples[])
cl.out:
tok_logger.warning(
)
output
block_size =
():
concatenated_examples = {k: (chain(*examples[k])) k examples.keys()}
total_length = (concatenated_examples[(examples.keys())[]])
total_length >= block_size:
total_length = (total_length // block_size) * block_size
result = {
k: [t[i : i + block_size] i (, total_length, block_size)]
k, t concatenated_examples.items()
}
result[] = result[].copy()
result
idx, file (files):
data_file = file
filename = .join(file.split()[:-])
cache_path = os.path.join(data_cache_dir, filename)
os.makedirs(cache_path, exist_ok=)
:
processed_dataset = datasets.load_from_disk(cache_path, keep_in_memory=)
()
Exception:
cache_dir = os.path.join(data_cache_dir, filename + )
os.makedirs(cache_dir, exist_ok=)
raw_dataset = load_dataset(, data_files=data_file, cache_dir=cache_dir, keep_in_memory=)
print_dict(raw_dataset[][])
tokenized_dataset = raw_dataset.(
tokenize_function,
batched=,
num_proc=preprocessing_num_workers,
remove_columns=,
load_from_cache_file=,
keep_in_memory=,
cache_file_names={k: os.path.join(cache_dir, ) k raw_dataset},
desc=,
)
print_dict(tokenized_dataset[][])
grouped_datasets = tokenized_dataset.(
group_texts,
batched=,
num_proc=preprocessing_num_workers,
load_from_cache_file=,
keep_in_memory=,
cache_file_names={k: os.path.join(cache_dir, ) k tokenized_dataset},
desc=,
)
processed_dataset = grouped_datasets
print_dict(processed_dataset[][])
processed_dataset.save_to_disk(cache_path)
idx == :
lm_datasets = processed_dataset[]
:
lm_datasets.features. == processed_dataset[].features.
lm_datasets = concatenate_datasets([lm_datasets, processed_dataset[]])
lm_datasets = lm_datasets.train_test_split(test_size=)
print_dict(lm_datasets[][])


