目的是在标记化的过程中监控可能的问题,特别是针对输入文本长度的问题。通过记录警告,可以更好地了解标记化过程中可能发生的问题,并在必要时采取适当的措施,例如将长输入文本分块。
"""
目的是在标记化的过程中监控可能的问题,特别是针对输入文本长度的问题。通过记录警告,可以更好地了解标记化过程中可能发生的问题,并在必要时采取适当的措施,例如将长输入文本分块。
"""
def tokenize_function(examples):
"""
使用 CaptureLogger(tok_logger) 上下文管理器捕获 tok_logger 中的日志输出。
如果捕获到了特定的警告消息:"Token indices sequence length is longer than the",则说明输入文本的标记序列长度超过了某个限制。
"""
with CaptureLogger(tok_logger) as cl:
#使用 tokenizer 进行标记化:
# output = tokenizer(examples[text_column_name]) 使用给定的 tokenizer 对输入文本进行标记化,结果存储在 output 中。
output = tokenizer(examples[text_column_name])
# clm input could be much much longer than block_size
"""
记录警告:
如果存在上述警告,通过 tok_logger.warning 记录一条新的警告消息,说明输入文本的长度较长,将在传递给模型之前被分块成较小的部分
"""
"""
返回标记化的输出:
返回标记化后的输出 output。
"""
if "Token indices sequence length is longer than the" in cl.out:
tok_logger.warning(
"^^^^^^^^^^^^^^^^ Please ignore the warning above - this long input will be chunked into smaller bits"
" before being passed to the model."
)
return output
通常用于在训练之前对数据集进行标记化处理。
使用多进程可以加速处理过程,特别是对于大规模的数据集。流式处理则适用于无法将整个数据集一次性加载到内存中的情况
"""
通常用于在训练之前对数据集进行标记化处理。
使用多进程可以加速处理过程,特别是对于大规模的数据集。流式处理则适用于无法将整个数据集一次性加载到内存中的情况。
"""
"""
with training_args.main_process_first(desc="dataset map tokenization")::
使用 main_process_first 上下文管理器,确保在主进程中执行以下代码块。
desc="dataset map tokenization" 提供了一个描述,用于在进度条中显示。
"""
with training_args.main_process_first(desc="dataset map tokenization"):
# 如果不是流式处理(not data_args.streaming),执行以下操作:
if not data_args.streaming:
# 使用 raw_datasets.map 函数对原始数据集进行标记化处理
# batched=True 表示对数据集进行批处理。
# num_proc=data_args.preprocessing_num_workers 表示使用多进程进行处理,可以提高效率。
tokenized_datasets = raw_datasets.map(
tokenize_function,
batched=True,
num_proc=data_args.preprocessing_num_workers,
# remove_columns=column_names 表示移除不再需要的列,即之前确定的 column_names
remove_columns=column_names,
# load_from_cache_file=not data_args.overwrite_cache
# 表示尝试从缓存文件加载数据,如果缓存文件不存在或用户选择覆盖缓存,则重新生成。
load_from_cache_file=not data_args.overwrite_cache,
# desc="Running tokenizer on dataset" 提供了一个进度描述。
desc="Running tokenizer on dataset",
)
else:
tokenized_datasets = raw_datasets.map(
tokenize_function,
batched=True,
remove_columns=column_names,
)
这段代码的目的是在模型配置中查找 max_position_embeddings 属性,并将其值存储在变量 max_pos_embeddings 中。这个属性通常表示模型支持的最大位置嵌入数,即模型能够处理的最大序列长度。如果模型配置中未定义该属性,则使用默认值 1024
# 这段代码的目的是在模型配置中查找 max_position_embeddings 属性,并将其值存储在变量 max_pos_embeddings 中。
# 这个属性通常表示模型支持的最大位置嵌入数,即模型能够处理的最大序列长度。如果模型配置中未定义该属性,则使用默认值 1024
# 使用 hasattr(config, "max_position_embeddings") 检查模型配置 config 是否具有名为 max_position_embeddings 的属性。
if hasattr(config, "max_position_embeddings"):
max_pos_embeddings = config.max_position_embeddings
else:
# Define a default value if the attribute is missing in the config.
max_pos_embeddings = 1024
代码确保了 block_size 的合理设置,以适应模型的最大长度,并在用户指定块大小时进行相应的处理和警告。这对于确保输入数据的长度适应模型输入的限制是很重要的。
"""
代码确保了 block_size 的合理设置,以适应模型的最大长度,并在用户指定块大小时进行相应的处理和警告。这对于确保输入数据的长度适应模型输入的限制是很重要的。
"""
"""
检查 data_args.block_size 是否为 None:
如果 data_args.block_size 为 None,表示用户没有指定块大小,执行以下操作:
将 block_size 设置为 tokenizer.model_max_length,即 tokenizer 所使用的模型的最大长度。
如果 block_size 大于 max_pos_embeddings(模型的最大位置嵌入数),则发出警告,并将 block_size 重新设置为 min(1024, max_pos_embeddings)。
如果 max_pos_embeddings 大于 0,则将 block_size 重新设置为 min(1024, max_pos_embeddings),否则将 block_size 设置为 1024。
"""
if data_args.block_size is None:# 检查 data_args.block_size 是否为 None:
block_size = tokenizer.model_max_length # 最大长度
if block_size > max_pos_embeddings:
logger.warning(
f"The tokenizer picked seems to have a very large `model_max_length` ({tokenizer.model_max_length}). "
f"Using block_size={min(1024, max_pos_embeddings)} instead. You can change that default value by passing --block_size xxx."
)
if max_pos_embeddings > 0:
block_size = min(1024, max_pos_embeddings)
else:
block_size = 1024
else:
if data_args.block_size > tokenizer.model_max_length:
logger.warning(
f"The block_size passed ({data_args.block_size}) is larger than the maximum length for the model "
f"({tokenizer.model_max_length}). Using block_size={tokenizer.model_max_length}."
)
block_size = min(data_args.block_size, tokenizer.model_max_length)
这段代码定义了一个名为 group_texts 的函数,该函数的主要目的是将文本数据组合成固定长度的块(chunks)。函数的解释如下:
# 这段代码定义了一个名为 group_texts 的函数,该函数的主要目的是将文本数据组合成固定长度的块(chunks)。函数的解释如下:
# Main data processing function that will concatenate all texts from our dataset and generate chunks of block_size.
def group_texts(examples):
# Concatenate all texts.
# 拼接所有的文本
# 将输入的 examples 中的文本进行拼接。这是通过将每个键(例如,"input_ids")对应的文本列表展平成一个长列表来完成的。
concatenated_examples = {k: list(chain(*examples[k])) for k in examples.keys()}
# 计算拼接后文本的总长度。
# 接着,将 total_length 除以 block_size 并取整,以确保整个文本序列被均匀地划分成大小为 block_size 的块。
total_length = len(concatenated_examples[list(examples.keys())[0]])
# We drop the small remainder, and if the total_length < block_size we exclude this batch and return an empty dict.
# We could add padding if the model supported it instead of this drop, you can customize this part to your needs.
total_length = (total_length // block_size) * block_size
# result 是一个字典,其中包含了将文本拆分成块的结果。
# 对于每个键 k,通过列表推导式将拼接的文本按 block_size 大小切割成块,存储在 result[k] 中。
# 最终的 result["labels"] 被设置为 result["input_ids"] 的副本。
# Split by chunks of 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()
}
#返回一个包含拆分后文本的字典。
result["labels"] = result["input_ids"].copy()
return result
这段代码使用 group_texts 函数将标记化后的数据集 tokenized_datasets 组织成适应模型输入的块,并生成一个新的数据集 lm_datasets。
这样的处理过程通常用于准备数据集以用于训练语言模型。通过将文本按块组合,确保输入的序列长度符合模型的要求,并且在处理大规模数据集时能够高效地进行。
# 这段代码定义了一个名为 group_texts 的函数,该函数的主要目的是将文本数据组合成固定长度的块(chunks)。函数的解释如下:
# Main data processing function that will concatenate all texts from our dataset and generate chunks of block_size.
def group_texts(examples):
# Concatenate all texts.
# 拼接所有的文本
# 将输入的 examples 中的文本进行拼接。这是通过将每个键(例如,"input_ids")对应的文本列表展平成一个长列表来完成的。
concatenated_examples = {k: list(chain(*examples[k])) for k in examples.keys()}
# 计算拼接后文本的总长度。
# 接着,将 total_length 除以 block_size 并取整,以确保整个文本序列被均匀地划分成大小为 block_size 的块。
total_length = len(concatenated_examples[list(examples.keys())[0]])
# We drop the small remainder, and if the total_length < block_size we exclude this batch and return an empty dict.
# We could add padding if the model supported it instead of this drop, you can customize this part to your needs.
total_length = (total_length // block_size) * block_size
# result 是一个字典,其中包含了将文本拆分成块的结果。
# 对于每个键 k,通过列表推导式将拼接的文本按 block_size 大小切割成块,存储在 result[k] 中。
# 最终的 result["labels"] 被设置为 result["input_ids"] 的副本。
# Split by chunks of 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()
}
#返回一个包含拆分后文本的字典。
result["labels"] = result["input_ids"].copy()
return result
数用于预处理模型输出的logits以便进行度量评估
if training_args.do_eval:
if "validation" not in tokenized_datasets:
raise ValueError("--do_eval requires a validation dataset")
eval_dataset = lm_datasets["validation"]
if data_args.max_eval_samples is not None:
max_eval_samples = min(len(eval_dataset), data_args.max_eval_samples)
eval_dataset = eval_dataset.select(range(max_eval_samples))
# 模型输出的 logits,这里假设是一个张量(tensor)
# 标签,用于计算评估指标。
def preprocess_logits_for_metrics(logits, labels):
# 如果 logits 是一个元组(tuple),则说明模型输出的 logits 包含了额外的张量
# ,例如 past_key_values,但是我们只关心 logits 本身,因此取元组中的第一个元素:logits = logits[0]。
if isinstance(logits, tuple):
# Depending on the model and config, logits may contain extra tensors,
# like past_key_values, but logits always come first
logits = logits[0]
# 返回预处理后的 logits,通过 logits.argmax(dim=-1) 取得在最后一个维度上的最大值的索引,即模型预测的标签。
return logits.argmax(dim=-1)
"""
evaluate.load("accuracy"):
evaluate.load 是 Transformers 库中用于加载评估指标的函数。
通过传递字符串参数 "accuracy",该函数加载了一个计算准确度的评估指标。
metric = evaluate.load("accuracy"):
将加载的 "accuracy" 评估指标存储在变量 metric 中,以便后续使用。
"""
metric = evaluate.load("accuracy")
# 函数的目的是计算在评估阶段使用的指标,通常用于评估模型在测试集上的性能。
# 在计算指标时,需要将模型的预测与真实标签进行比较,并将结果传递给特定的评估指标计算函数(例如,准确度、精确度、召回率等)。
def compute_metrics(eval_preds):
# 调用预先加载的评估指标 (metric) 的 compute 方法,传递模型的预测 (predictions) 和真实标签 (references)。
# 返回计算得到的指标值。
preds, labels = eval_preds
# preds have the same shape as the labels, after the argmax(-1) has been calculated
# by preprocess_logits_for_metrics but we need to shift the labels
labels = labels[:, 1:].reshape(-1)
preds = preds[:, :-1].reshape(-1)
return metric.compute(predictions=preds, references=labels)
"""
检查是否执行评估 (training_args.do_eval):如果 training_args.do_eval 为 True,表示要执行评估,执行以下操作。
打印评估信息 (logger.info("*** Evaluate ***")):
使用 logger 打印评估的信息。执行评估 (trainer.evaluate):
调用 trainer.evaluate 方法对验证集进行评估,获取评估指标。
获取评估结果的指标 (metrics):
将评估结果中的指标存储在变量 metrics 中。
限制最大评估样本数 (max_eval_samples):
如果用户指定了 data_args.max_eval_samples,则将最大评估样本数设置为该值。
否则,将最大评估样本数设置为验证集的长度。
计算困惑度 (perplexity)
通过将指数函数应用于评估损失 (metrics["eval_loss"]) 计算困惑度。困惑度是语言模型性能的一种度量,它衡量模型对给定数据的预测的不确定性。
记录评估指标 (trainer.log_metrics):
调用 trainer.log_metrics 方法记录评估指标,包括损失、困惑度等。
保存评估指标 (trainer.save_metrics):
调用 trainer.save_metrics 方法将评估指标保存到磁盘。
"""
# Evaluation
# 检查是否执行评估
if training_args.do_eval:
# 打印评估信息
logger.info("*** Evaluate ***")
# 获取评估结果的指标:执行评估
metrics = trainer.evaluate()
# 限制最大评估样本数
# 如果用户指定了data_args.max_eval_samples,则将最大评估样本数设置为该值
# 否则,将最大评估样本数设置为验证机的长度
max_eval_samples = data_args.max_eval_samples if data_args.max_eval_samples is not None else len(eval_dataset)
# 计算困惑度
metrics["eval_samples"] = min(max_eval_samples, len(eval_dataset))
try:
perplexity = math.exp(metrics["eval_loss"])
except OverflowError:
perplexity = float("inf")
metrics["perplexity"] = perplexity
# 记录评估指标
trainer.log_metrics("eval", metrics)
# 保存评估指标
trainer.save_metrics("eval", metrics)