Llama2-Chinese项目给出pretrain的data为QA数据格式,可能会有疑问pretrain不应该是Text数据格式吗?而在Chinese-LLaMA-Alpaca-2和open-llama2预训练使用的LoRA技术,给出pretrain的data为Text数据格式。所以推测应该pretrain时QA和Text数据格式都应该支持。然后马上就会有一个疑问,两者有什么区别呢?再回答这个问题之前,先来看看Llama2-Chinese和open-llama2是如何处理QA和Text数据的。
一.Llama2-Chineses是如何处理QA数据格式的?
1.raw_datasets数据
首先使用raw_datasets = load_dataset()
加载原始数据,如下所示:
然后通过tokenize_function
分词函数对raw_datasets
进行处理,如下所示:
def tokenize_function(examples): # 分词函数
with CaptureLogger(tok_logger) as cl: # 捕获日志记录器
output = tokenizer( [ '<s>'+item+'</s>' for item in examples[text_column_name]]) # 分词
return output # 输出
对应的控制台输出日志,如下所示:
Running tokenizer on dataset: 0%| | 0/9861 [00:00<?, ? examples/s]Caching processed dataset at L:\20230825_NLP工程化公众号\nlp-engineering\20230916_Llama2-Chinese\train\pretrain\output_model\dataset_cache\csv\default-0be939ed6ae746cd\0.0.0\eea64c71ca8b46dd3f537ed218fc9bf495d5707789152eb2764f5c78fa66d59d\cache-5995c58fe2972c10.arrow
09/20/2023 21:52:13 - INFO - datasets.arrow_dataset - Caching processed dataset at L:\20230825_NLP工程化公众号\nlp-engineering\20230916_Llama2-Chinese\train\pretrain\output_model\dataset_cache\csv\default-0be939ed6ae746cd\0.0.0\eea64c71ca8b46dd3f537ed218fc9bf495d5707789152eb2764f5c78fa66d59d\cache-5995c58fe2972c10.arrow
Running tokenizer on dataset: 100%|██████████| 9861/9861 [00:11<00:00, 886.04 examples/s]
Running tokenizer on dataset: 0%| | 0/400 [00:00<?, ? examples/s]Caching processed dataset at L:\20230825_NLP工程化公众号\nlp-engineering\20230916_Llama2-Chinese\train\pretrain\output_model\dataset_cache\csv\default-0be939ed6ae746cd\0.0.0\eea64c71ca8b46dd3f537ed218fc9bf495d5707789152eb2764f5c78fa66d59d\cache-44181180d09c5991.arrow
09/20/2023 21:52:21 - INFO - datasets.arrow_dataset - Caching processed dataset at L:\20230825_NLP工程化公众号\nlp-engineering\20230916_Llama2-Chinese\train\pretrain\output_model\dataset_cache\csv\default-0be939ed6ae746cd\0.0.0\eea64c71ca8b46dd3f537ed218fc9bf495d5707789152eb2764f5c78fa66d59d\cache-44181180d09c5991.arrow
Running tokenizer on dataset: 100%|██████████| 400/400 [00:01<00:00, 251.86 examples/s]
2.tokenized_datasets数据
然后tokenized_datasets = raw_datasets.map()
如下所示:
可以看到tokenized_datasets
主要是包含['input_ids', 'attention_mask']
这2列数据,拿出其中1条如下所示:
'input_ids':[1, 1, 1, 12968, 29901, 29871, 31999, 30544, 30287, 30502, 31658, 31596, 30214, 30698, 31376, 31931, 30880, 30742, 234, 176, 151, 31751, 31658, 31596, 30210, 30594, 31016, 31175, 31072, 30267, 30847, 30801, 30742, 234, 176, 151, 31480, 30544, 30594, 31016, 31175, 31072, 30214, 31915, 31229, 31370, 31751, 31050, 30780, 30768, 31043, 30267, 30004, 13, 31658, 31596, 29901, 376, 30287, 30408, 30417, 30923, 31022, 234, 170, 149, 30882, 29908, 30594, 31016, 31175, 31072, 29901, 29871, 29896, 29900, 234, 170, 149, 30004, 13, 2, 1, 4007, 22137, 29901, 29871, 30287, 30408, 30417, 29947, 29953, 29946, 29900, 29900, 234, 170, 149, 30267, 30004, 13, 2, 2]
# 'input_ids':'<s><s><s> Human: 给出一个问题,要求助手回答该问题的时间限制。如果回答超出时间限制,客户应该得到通知。问题: "一天有多少秒?"时间限制: 10秒</s><s> Assistant: 一天有86400秒。</s></s>'
'attention_mask':[1, 1, 1, 1, 1, ... 1, 1, 1, 1, 1]
3.lm_datasets数据
接下来得到lm_datasets = tokenized_datasets.map()
,其中group_text()
函数如下所示:
def group_texts(examples):
# 翻译:连接所有文本。
concatenated_examples = {k: list(chain(*examples[k])) for k in examples.keys()} # 连接所有文本
# concatenated_examples = {k: sum(examples[k], []) for k in examples.keys()}
total_length = len(concatenated_examples[list(examples.keys())[0]]) # 总长度
# We drop the small remainder, we could add padding if the model supported it instead of this drop, you can customize this part to your needs.
# 翻译:我们丢弃了小的余数,如果模型支持,我们可以添加填充,而不是这个丢弃,您可以根据需要自定义这部分。
if total_length >= block_size: # 如果总长度大于块大小
total_length = (total_length // block_size) * block_size # 总长度
# Split by chunks of max_len.
# 翻译:按max_len分割。
result = { # 结果
k: [t[i : i + block_size] for i in range(0, total_length, block_size)] # 拼接的示例
for k, t in concatenated_examples.items() # 拼接的示例
}
# print(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
logger.info("group texts input examples length%d after_group size%d"%(len(examples['input_ids']),len(result["input_ids"]))) # 组文本输入示例长度%d后组大小%d
result["labels"] = result["input_ids"].copy() # 标签
return result # 返回结果
对应的控制台输出日志,如下所示:
09/20/2023 21:52:21 - WARNING - __main__ - The chosen tokenizer supports a `model_max_length` that is longer than the default `block_size` value of 1024. If you would like to use a longer `block_size` up to `tokenizer.model_max_length` you can override this default with `--block_size xxx`.
Grouping texts in chunks of 1024: 0%| | 0/9861 [00:00<?, ? examples/s]09/20/2023 21:52:29 - INFO - __main__ - group texts input examples length9861 after_group size3246
Caching processed dataset at L:\20230825_NLP工程化公众号\nlp-engineering\20230916_Llama2-Chinese\train\pretrain\output_model\dataset_cache\csv\default-0be939ed6ae746cd\0.0.0\eea64c71ca8b46dd3f537ed218fc9bf495d5707789152eb2764f5c78fa66d59d\cache-18dbcb518f2766e1.arrow
09/20/2023 21:52:29 - INFO - datasets.arrow_dataset - Caching processed dataset at L:\20230825_NLP工程化公众号\nlp-engineering\20230916_Llama2-Chinese\train\pretrain\output_model\dataset_cache\csv\default-0be939ed6ae746cd\0.0.0\eea64c71ca8b46dd3f537ed218fc9bf495d5707789152eb2764f5c78fa66d59d\cache-18dbcb518f2766e1.arrow
Grouping texts in chunks of 1024: 100%|██████████| 9861/9861 [00:11<00:00, 843.75 examples/s]
Grouping texts in chunks of 1024: 0%| | 0/400 [00:00<?, ? examples/s]Caching processed dataset at L:\20230825_NLP工程化公众号\nlp-engineering\20230916_Llama2-Chinese\train\pretrain\output_model\dataset_cache\csv\default-0be939ed6ae746cd\0.0.0\eea64c71ca8b46dd3f537ed218fc9bf495d5707789152eb2764f5c78fa66d59d\cache-962b32747bcb1aec.arrow
09/20/2023 21:52:35 - INFO - __main__ - group texts input examples length400 after_group size738
09/20/2023 21:52:35 - INFO - datasets.arrow_dataset - Caching processed dataset at L:\20230825_NLP工程化公众号\nlp-engineering\20230916_Llama2-Chinese\train\pretrain\output_model\dataset_cache\csv\default-0be939ed6ae746cd\0.0.0\eea64c71ca8b46dd3f537ed218fc9bf495d5707789152eb2764f5c78fa66d59d\cache-962b32747bcb1aec.arrow
Grouping texts in chunks of 1024: 100%|██████████| 400/400 [00:02<00:00, 153.36 examples/s]
lm_datasets的列名包括[‘input_ids’, ‘attention_mask’, ‘labels’],如下所示:
train_dataset就是lm_datasets中train的部分,如下所示:
4.定义训练器Trainer训练模型
通过trainer.train(resume_from_checkpoint=checkpoint)
训练模型,如下所示:
二.open-llama2是如何处理Text数据格式的?
1.raw_dataset数据
raw_dataset = load_dataset("text", data_files=data_file, cache_dir=cache_dir, keep_in_memory=False)
加载预训练数据,如下所示:
2.tokenized_dataset数据
对raw_dataset
进行text转id处理,如下所示:
3.grouped_datasets和processed_dataset数据
这两者包含[‘input_ids’, ‘attention_mask’, ‘labels’]三列数据,并且processed_dataset = grouped_datasets
。如下所示:
4.lm_datasets数据
lm_datasets = processed_dataset['train']
,如下所示:
通过tokenizer.decode(train_dataset[0]['input_ids'])
对数据进行解码,如下所示:
<s> 青海省人民政府是中华人民共和国青海省的最高地方行政机构。1950年1月由原青海省人民军政委员会改组成立。1955年1月改称青海省人民委员会。1967年8月改为青海省革命委员会。1979年8月,青海省革命委员会撤销,复设青海省人民政府。<s>湖南省人民政府驻北京办事处,是中华人民共和国湖南省人民政府驻北京市的办事处,该办事处负责领导联络协调、招商引资、信息调研、对外宣传、接待服务以及服务驻北京市天津企业等相关事项。该办事处为副局级单位。
综上所述,加载QA数据时,train_dataset的shape为[3246, 3],而加载Text数据时,train_dataset的shape为[1, 3],可见前者是按照逐行处理的,而后者是合并后处理的。最后思考最开始的那个疑问,两者有什么区别呢?从数据处理上来说,无论是QA还是Text数据格式,都是把它们当做Text数据处理的。看似没有区别,实际上LLM有着强大的模式识别能力,从QA数据集上是可以识别到问答模式的,尽管做的都是无监督学习,没有明确的特征X和标签y。问了下ChatGPT增量训练使用QA数据集和Text数据集间的区别,如下所示:
特征 | QA 数据集增量训练 | Text 数据集增量训练 |
---|---|---|
数据格式 | 问题和答案对的形式 | 连续的文本段落、句子或单词序列 |
目标任务 | 提高问题回答性能 | 提高文本理解和生成性能 |
数据内容 | 问题和答案对,通常领域特定 | 通常为通用文本,如新闻文章、小说等 |
数据预处理 | 问题和答案的提取、分词、标记化等 | 文本清理、标记化、分段等 |
应用示例 | 问答、领域特定任务 | 文本生成、文章摘要、翻译等 |
这个回答参考下就行了,还是要以场景为导向来思考,比如要训练一个心理咨询机器人,现在已经有了一个LLM的base模型,如果要增量预训练肯定也是在医疗非结构化文本(比如教材、文献等)上预训练,然后拿心理咨询问答数据进行SFT。应该很少会有人直接拿医疗QA数据集来预训练base模型吧,因为标注的成本还是太高了。若有不当之处,欢迎拍砖。
参考文献:
[1]https://github.com/FlagAlpha/Llama2-Chinese
[2]https://github.com/huxiaosheng123/open-llama2
[3]https://github.com/ymcui/Chinese-LLaMA-Alpaca-2
[4]https://github.com/tatsu-lab/stanford_alpaca
[5]https://github.com/huggingface/transformers/blob/main/examples/pytorch/language-modeling/run_clm.py
[6]https://github.com/ai408/nlp-engineering/blob/main/20230916_Llama2-Chinese/train/pretrain/pretrain_clm.py