训练的数据集预处理说明
以 llama2-13b 举例,运行:0_pl_pretrain_13b.sh 训练脚本后,脚本检查是否已经完成数据集预处理的过程。
若已完成数据集预处理,则直接执行预训练任务。若未进行数据集预处理,则会自动执行 scripts/llama2/1_preprocess_data.sh 。
预训练数据集预处理参数说明
预训练数据集预处理脚本scripts/llama2/1_preprocess_data.sh 中的具体参数如下:
- --input:原始数据集的存放路径。
- --output-prefix:处理后的数据集保存路径+数据集名称(例如:moss-003-sft-data)。
- --tokenizer-type:tokenizer的类型,可选项有['BertWordPieceLowerCase','BertWordPieceCase','GPT2BPETokenizer','PretrainedFromHF'],一般为PretrainedFromHF。
- --tokenizer-name-or-path:tokenizer的存放路径,与HF权重存放在一个文件夹下。
- --seq-length:要处理的最大seq length。
- --workers:设置数据处理使用执行卡数量 / 启动的工作进程数。
- --log-interval:是一个用于设置日志输出间隔的参数,表示输出日志的频率。在训练大规模模型时,可以通过设置这个参数来控制日志的输出。
输出数据预处理结果路径:
训练完成后,以 llama2-13b 为例,输出数据路径为:/home/ma-user/ws/llm_train/AscendSpeed/processed_for_input/llama2-13b/data/pretrain/
微调数据集预处理参数说明
微调包含SFT和LoRA微调。数据集预处理脚本参数说明如下:
- --input:原始数据集的存放路径。
- --output-prefix:处理后的数据集保存路径+数据集名称(例如:moss-003-sft-data)
- --tokenizer-type:tokenizer的类型,可选项有['BertWordPieceLowerCase','BertWordPieceCase','GPT2BPETokenizer','PretrainedFromHF'],一般为PretrainedFromHF。
- --tokenizer-name-or-path:tokenizer的存放路径,与HF权重存放在一个文件夹下。
- --handler-name:生成数据集的用途,这里是生成的指令数据集,用于微调。
- GeneralPretrainHandler:默认。用于预训练时的数据预处理过程中,将数据集根据key值进行简单的过滤。
- GeneralInstructionHandler:用于sft、lora微调时的数据预处理过程中,会对数据集full_prompt中的user_prompt进行mask操作。
- --seq-length:要处理的最大seq length。
- --workers:设置数据处理使用执行卡数量 / 启动的工作进程数。
- --log-interval:是一个用于设置日志输出间隔的参数,表示输出日志的频率。在训练大规模模型时,可以通过设置这个参数来控制日志的输出。
输出数据预处理结果路径:
训练完成后,以 llama2-13b 为例,输出数据路径为:/home/ma-user/ws/llm_train/AscendSpeed/processed_for_input/llama2-13b/data/fintune/
handler-name参数说明
数据集预处理中 --handler-name 都会传递参数,用于构建实际处理数据的hanler对象,并根据handler对象对数据集进行解析。文件路径在:ModelLink/modellink/data/data_handler.py。
- 基类BaseDatasetHandler解析
data_handler的基类是BaseDatasetHandler,其核心函数是serialize_to_disk:
def serialize_to_disk(self): """save idx and bin to disk""" startup_start = time.time() if not self.tokenized_dataset: self.tokenized_dataset = self.get_tokenized_data() output_bin_files = {} output_idx_files = {} builders = {} level = "document" if self.args.split_sentences: level = "sentence" logger.info("Vocab size: %s", self.tokenizer.vocab_size) logger.info("Output prefix: %s", self.args.output_prefix) for key in self.args.json_keys: ## 写入磁盘
- 先调用self.get_tokenized_data()对数据集进行encode
- self.get_tokenized_data()中调用self._filter方法处理每一个sample
- self._filter在基类中未定义,需要各个子类针对目标数据集格式进行实现
所有handler依据实际数据集实现self._filter方法,处理原始数据集中的单一sample,其余方法复用基类的实现。
- GeneralPretrainHandler解析
GeneralPretrainHandler是处理预训练数据集的一个类,继承自BaseDatasetHandler,实现对alpaca格式预训练数据集的处理。
def _filter(self, sample): sample = self._pre_process(sample) for key in self.args.json_keys: text = sample[key] doc_ids = [] for sentence in self.splitter.tokenize(text): if len(sentence) > 0: sentence_ids = self._tokenize(sentence) doc_ids.append(sentence_ids) if len(doc_ids) > 0 and self.args.append_eod: doc_ids[-1]['input_ids'].append(self.tokenizer.eod) doc_ids[-1]['attention_mask'].append(1) doc_ids[-1]['labels'].append(self.tokenizer.eod) sample[key] = doc_ids # for now, only input_ids are saved sample[key] = list(map(lambda x: x['input_ids'], sample[key])) return sample
- GeneralInstructionHandler解析
GeneralInstructionHandler是处理微调数据集的一个基本类,继承自BaseDatasetHandler,实现对alpaca格式微调数据集的处理。
def _filter(self, sample): messages = self._format_msg(sample) full_prompt = self.prompter.generate_training_prompt(messages) tokenized_full_prompt = self._tokenize(full_prompt) if self.args.append_eod: tokenized_full_prompt["input_ids"].append(self.tokenizer.eod) tokenized_full_prompt["attention_mask"].append(1) tokenized_full_prompt["labels"].append(self.tokenizer.eod) if not self.train_on_inputs: user_prompt = full_prompt.rsplit(self.prompter.template.assistant_token, maxsplit=1)[0] + \ self.prompter.template.assistant_token + "\n" tokenized_user_prompt = self._tokenize(user_prompt) user_prompt_len = len(tokenized_user_prompt["input_ids"]) tokenized_full_prompt["labels"][:user_prompt_len] = [self.ignored_label] * user_prompt_len for key in self.args.json_keys: tokenized_full_prompt[key] = [tokenized_full_prompt[key]] return tokenized_full_prompt
- 对数据集 full_prompt 中的 user_prompt 进行 mask 操作。
- MOSSMultiTurnHandler解析
MOSSMultiTurnHandler是处理微调数据集的一个类,继承自GeneralInstructionHandler,实现对moss格式微调数据集的处理。
def _filter(self, sample): input_ids, labels = [], [] for turn in sample["chat"].values(): if not turn: continue user = turn["Human"].replace("<eoh>", "").replace("<|Human|>: ", "").strip() assistant = turn["MOSS"].replace("<|MOSS|>:", "").replace("<eom>", "").strip() user_ids = self._unwrapped_tokenizer.encode(user) assistant_ids = self._unwrapped_tokenizer.encode(assistant) input_ids += self.user_token + user_ids + self.assistant_token + assistant_ids labels += [self._unwrapped_tokenizer.eos_token_id] + self.ignored_index * len(user_ids) + self.ignored_index + assistant_ids input_ids.append(self._unwrapped_tokenizer.eos_token_id) labels.append(self._unwrapped_tokenizer.eos_token_id) attention_mask = [1 for _ in range(len(input_ids))] return { "input_ids": [input_ids], "attention_mask": [attention_mask], "labels": [labels] }
- moss原始数据集是一个多轮对话的jsonl,filter的输入就是其中的一行
- 循环处理其中的单轮对话
- 在单轮对话中
- 对user和assiant的文本进行清洗
- 分别encode处理后的文本,获得对应的token序列,user_ids和assiantant_ids
- input_ids是user_ids和assiantant_ids的拼接
- labels与input_ids对应,用-100替换user_ids的token,只保留assiantant_ids
- attention_mask是和input_ids等长的全1序列
- 返回input_ids\attention_mask\labels的字典
- 处理完单一sample
注:labels中用-100填充的地方,表示会被loss_mask给mask掉
- 自定义handler
参考MOSSMultiTurnHandler的实现,继承想要的通用的父类,实现_filter方法,然后在数据预处理的参数里指定自己的handler名称即可
用户自定义执行数据处理脚本修改参数说明
若用户要自定义数据处理脚本并且单独执行,同样以 llama2 为例。
- 方法一:用户可打开scripts/llama2/1_preprocess_data.sh脚本,将执行的python命令复制下来,修改环境变量的值,进入到 /home/ma-user/ws/llm_train/AscendSpeed/ModelLink 路径中,再执行python命令。
- 方法二:用户在Notebook中直接编辑scripts/llama2/1_preprocess_data.sh脚本,自定义环境变量的值,并在脚本的首行中添加 cd /home/ma-user/ws/llm_train/AscendSpeed/ModelLink 命令,随后运行该脚本。
其中环境变量详细介绍如下:
环境变量 |
示例 |
参数说明 |
---|---|---|
RUN_TYPE |
pretrain、sft、lora |
数据预处理区分: 预训练场景下数据预处理,默认参数:pretrain 微调场景下数据预处理,默认:sft / lora |
ORIGINAL_TRAIN_DATA_PATH |
/home/ma-user/ws/llm_train/AscendSpeed/training_data/${用户自定义的数据集路径和名称} |
原始数据集的存放路径。 |
TOKENIZER_PATH |
/home/ma-user/ws/llm_train/AscendSpeed/tokenizers/llama2-13b |
tokenizer的存放路径,与HF权重存放在一个文件夹下。请根据实际规划修改。 |
PROCESSED_DATA_PREFIX |
/home/ma-user/ws/llm_train/AscendSpeed/processed_for_input/llama2-13b/data |
处理后的数据集保存路径+数据集前缀 |
TOKENIZER_TYPE |
PretrainedFromHF |
可选项有:['BertWordPieceLowerCase','BertWordPieceCase','GPT2BPETokenizer','PretrainedFromHF'],一般为 PretrainedFromHF 。 |
SEQ_LEN |
4096 |
要处理的最大seq length。脚本会检测超出SEQ_LEN长度的数据,并打印log。 |