LLM 课程文档
遇到错误怎么办
并获得增强的文档体验
开始使用
遇到错误怎么办
在本节中,我们将介绍在使用刚调优的 Transformer 模型生成预测时可能出现的一些常见错误。这将为第 4 节做准备,在该节中我们将探讨如何调试训练阶段本身。
我们为本节准备了一个模板模型仓库,如果您想运行本章中的代码,您首先需要将该模型复制到您的 Hugging Face Hub 帐户中。为此,首先在 Jupyter notebook 中运行以下代码进行登录:
from huggingface_hub import notebook_login
notebook_login()
或者在您喜欢的终端中运行以下代码:
huggingface-cli login
这将提示您输入用户名和密码,并将令牌保存在 ~/.cache/huggingface/ 下。登录后,您可以使用以下函数复制模板仓库:
from distutils.dir_util import copy_tree
from huggingface_hub import Repository, snapshot_download, create_repo, get_full_repo_name
def copy_repository_template():
# Clone the repo and extract the local path
template_repo_id = "lewtun/distilbert-base-uncased-finetuned-squad-d5716d28"
commit_hash = "be3eaffc28669d7932492681cd5f3e8905e358b4"
template_repo_dir = snapshot_download(template_repo_id, revision=commit_hash)
# Create an empty repo on the Hub
model_name = template_repo_id.split("/")[1]
create_repo(model_name, exist_ok=True)
# Clone the empty repo
new_repo_id = get_full_repo_name(model_name)
new_repo_dir = model_name
repo = Repository(local_dir=new_repo_dir, clone_from=new_repo_id)
# Copy files
copy_tree(template_repo_dir, new_repo_dir)
# Push to Hub
repo.push_to_hub()
现在,当您调用 copy_repository_template()
时,它将在您的帐户下创建模板仓库的副本。
调试 🤗 Transformers 的 pipeline
为了开启我们奇妙的 Transformer 模型调试之旅,请考虑以下场景:您正在与一位同事合作进行一个问答项目,以帮助电子商务网站的客户找到有关消费品的答案。您的同事给您发了这样一条消息:
你好!我刚用 Hugging Face 课程第 7 章中的技术进行了一项实验,在 SQuAD 上取得了很好的结果!我认为我们可以使用这个模型作为我们项目的起点。Hub 上的模型 ID 是“lewtun/distillbert-base-uncased-finetuned-squad-d5716d28”。随时可以测试它:)
您首先想到的是使用 🤗 Transformers 的 pipeline
加载模型:
from transformers import pipeline
model_checkpoint = get_full_repo_name("distillbert-base-uncased-finetuned-squad-d5716d28")
reader = pipeline("question-answering", model=model_checkpoint)
"""
OSError: Can't load config for 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28'. Make sure that:
- 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models'
- or 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
哦不,好像出了点问题!如果您刚开始编程,这类错误一开始可能会有点神秘(OSError
是什么鬼?!)。这里显示的错误只是一个更大错误报告的最后一部分,这个报告称为 Python traceback(即堆栈跟踪)。例如,如果您在 Google Colab 上运行此代码,您应该会看到以下截图:

这些报告中包含大量信息,因此让我们一起看看关键部分。首先要注意的是,traceback 应该从下往上阅读。如果您习惯从上往下阅读英文文本,这听起来可能很奇怪,但这反映了 traceback 显示的是 pipeline
在下载模型和分词器时所做的函数调用序列。(查看第 2 章以获取有关 pipeline
内部工作原理的更多详细信息。)
🚨 看到 Google Colab 的 traceback 中“6 帧”周围的蓝色框了吗?这是 Colab 的一个特殊功能,它将 traceback 压缩成“帧”。如果您似乎找不到错误的来源,请务必单击那两个小箭头展开完整的 traceback。
这意味着 traceback 的最后一行表示最后的错误消息,并给出引发的异常的名称。在本例中,异常类型是 OSError
,表示系统相关错误。如果我们阅读附带的错误消息,我们可以看到模型的 config.json 文件似乎有问题,并给出了两个解决建议:
"""
Make sure that:
- 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models'
- or 'lewtun/distillbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
💡 如果您遇到难以理解的错误消息,只需将该消息复制并粘贴到 Google 或 Stack Overflow 搜索栏中(是的,真的!)。您很有可能不是第一个遇到此错误的人,这是一种寻找社区中其他人发布的解决方案的好方法。例如,在 Stack Overflow 上搜索 OSError: Can't load config for
会得到几个可以作为解决问题起点的结果。
第一个建议是让我们检查模型 ID 是否正确,所以首要任务是将标识符复制并粘贴到 Hub 的搜索栏中:

嗯,看起来我们同事的模型确实不在 Hub 上……啊哈,但是模型名称中有个拼写错误!DistilBERT 的名称中只有一个“l”,所以我们来修正它,改为搜索“lewtun/distilbert-base-uncased-finetuned-squad-d5716d28”:

好的,这找到了一个。现在我们尝试用正确的模型 ID 再次下载模型:
model_checkpoint = get_full_repo_name("distilbert-base-uncased-finetuned-squad-d5716d28")
reader = pipeline("question-answering", model=model_checkpoint)
"""
OSError: Can't load config for 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28'. Make sure that:
- 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28' is a correct model identifier listed on 'https://huggingface.co/models'
- or 'lewtun/distilbert-base-uncased-finetuned-squad-d5716d28' is the correct path to a directory containing a config.json file
"""
啊,又失败了——欢迎来到机器学习工程师的日常生活!既然我们已经修复了模型 ID,那么问题一定出在仓库本身。访问 🤗 Hub 上仓库内容的一种快速方法是通过 huggingface_hub
库的 list_repo_files()
函数:
from huggingface_hub import list_repo_files
list_repo_files(repo_id=model_checkpoint)
['.gitattributes', 'README.md', 'pytorch_model.bin', 'special_tokens_map.json', 'tokenizer_config.json', 'training_args.bin', 'vocab.txt']
有趣的是——仓库中似乎没有 config.json 文件!难怪我们的 pipeline
无法加载模型;我们的同事可能在微调模型后忘记将此文件推送到 Hub。在这种情况下,问题似乎很容易解决:我们可以要求他们添加文件,或者,既然我们可以从模型 ID 看出使用的预训练模型是 distilbert-base-uncased
,我们可以下载该模型的配置并将其推送到我们的仓库,看看是否能解决问题。我们来试试。使用我们在第 2 章中学到的技术,我们可以使用 AutoConfig
类下载模型的配置:
from transformers import AutoConfig
pretrained_checkpoint = "distilbert-base-uncased"
config = AutoConfig.from_pretrained(pretrained_checkpoint)
🚨 我们这里采用的方法并非万无一失,因为我们的同事可能在微调模型之前调整了 distilbert-base-uncased
的配置。在现实生活中,我们会先与他们核实,但为了本节的目的,我们假设他们使用了默认配置。
然后我们可以使用配置的 push_to_hub()
函数将其推送到我们的模型仓库:
config.push_to_hub(model_checkpoint, commit_message="Add config.json")
现在我们可以通过从 main
分支的最新提交加载模型来测试这是否有效:
reader = pipeline("question-answering", model=model_checkpoint, revision="main")
context = r"""
Extractive Question Answering is the task of extracting an answer from a text
given a question. An example of a question answering dataset is the SQuAD
dataset, which is entirely based on that task. If you would like to fine-tune a
model on a SQuAD task, you may leverage the
examples/pytorch/question-answering/run_squad.py script.
🤗 Transformers is interoperable with the PyTorch, TensorFlow, and JAX
frameworks, so you can use your favourite tools for a wide variety of tasks!
"""
question = "What is extractive question answering?"
reader(question=question, context=context)
{'score': 0.38669535517692566,
'start': 34,
'end': 95,
'answer': 'the task of extracting an answer from a text given a question'}
哇哦,成功了!让我们回顾一下您刚刚学到的内容:
- Python 中的错误消息被称为 tracebacks,它们从下往上阅读。错误消息的最后一行通常包含您需要的信息来定位问题的根源。
- 如果最后一行不包含足够的信息,请向上追溯 traceback,看看是否可以确定错误发生在源代码的哪个位置。
- 如果所有错误消息都无法帮助您调试问题,请尝试在线搜索类似问题的解决方案。
huggingface_hub
// 🤗 Hub? 库提供了一套工具,您可以使用它们与 Hub 上的仓库进行交互和调试。
现在您知道如何调试 pipeline 了,让我们看看模型本身的前向传播中一个更棘手的例子。
调试模型的前向传播
虽然 pipeline
对于大多数需要快速生成预测的应用程序来说非常棒,但有时您需要访问模型的 logits(例如,如果您想应用一些自定义后处理)。为了了解在这种情况下可能出现的问题,我们首先从 pipeline
中获取模型和分词器:
tokenizer = reader.tokenizer model = reader.model
接下来我们需要一个问题,所以我们来看看我们最喜欢的框架是否受支持:
question = "Which frameworks can I use?"
正如我们在第 7 章中看到的,我们通常需要执行的步骤是:对输入进行分词,提取起始和结束标记的 logits,然后解码答案跨度:
import torch
inputs = tokenizer(question, context, add_special_tokens=True)
input_ids = inputs["input_ids"][0]
outputs = model(**inputs)
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
# Get the most likely beginning of answer with the argmax of the score
answer_start = torch.argmax(answer_start_scores)
# Get the most likely end of answer with the argmax of the score
answer_end = torch.argmax(answer_end_scores) + 1
answer = tokenizer.convert_tokens_to_string(
tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
)
print(f"Question: {question}")
print(f"Answer: {answer}")
"""
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/var/folders/28/k4cy5q7s2hs92xq7_h89_vgm0000gn/T/ipykernel_75743/2725838073.py in <module>
1 inputs = tokenizer(question, text, add_special_tokens=True)
2 input_ids = inputs["input_ids"]
----> 3 outputs = model(**inputs)
4 answer_start_scores = outputs.start_logits
5 answer_end_scores = outputs.end_logits
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/torch/nn/modules/module.py in _call_impl(self, *input, **kwargs)
1049 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
1050 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1051 return forward_call(*input, **kwargs)
1052 # Do not call functions when jit is used
1053 full_backward_hooks, non_full_backward_hooks = [], []
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, start_positions, end_positions, output_attentions, output_hidden_states, return_dict)
723 return_dict = return_dict if return_dict is not None else self.config.use_return_dict
724
--> 725 distilbert_output = self.distilbert(
726 input_ids=input_ids,
727 attention_mask=attention_mask,
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/torch/nn/modules/module.py in _call_impl(self, *input, **kwargs)
1049 if not (self._backward_hooks or self._forward_hooks or self._forward_pre_hooks or _global_backward_hooks
1050 or _global_forward_hooks or _global_forward_pre_hooks):
-> 1051 return forward_call(*input, **kwargs)
1052 # Do not call functions when jit is used
1053 full_backward_hooks, non_full_backward_hooks = [], []
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, output_attentions, output_hidden_states, return_dict)
471 raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
472 elif input_ids is not None:
--> 473 input_shape = input_ids.size()
474 elif inputs_embeds is not None:
475 input_shape = inputs_embeds.size()[:-1]
AttributeError: 'list' object has no attribute 'size'
"""
哦,天哪,看来我们的代码有 bug!但我们不怕一点点调试。您可以在 notebook 中使用 Python 调试器:
或在终端中:
在这里,阅读错误消息告诉我们 'list' object has no attribute 'size'
,我们可以看到一个 -->
箭头指向 model(**inputs)
中引发问题的行。您可以使用 Python 调试器以交互方式调试此问题,但现在我们只打印 inputs
的一个切片以查看我们有什么:
inputs["input_ids"][:5]
[101, 2029, 7705, 2015, 2064]
这看起来确实像一个普通的 Python list
,但我们再检查一下类型:
type(inputs["input_ids"])
list
是的,这确实是一个 Python list
。那么哪里出了问题呢?回顾第 2 章,🤗 Transformers 中的 AutoModelForXxx
类对张量(PyTorch 或 TensorFlow 中的)进行操作,一个常见的操作是使用 PyTorch 中的 Tensor.size()
等来提取张量的维度。让我们再看看 traceback,看看是哪一行触发了异常:
~/miniconda3/envs/huggingface/lib/python3.8/site-packages/transformers/models/distilbert/modeling_distilbert.py in forward(self, input_ids, attention_mask, head_mask, inputs_embeds, output_attentions, output_hidden_states, return_dict)
471 raise ValueError("You cannot specify both input_ids and inputs_embeds at the same time")
472 elif input_ids is not None:
--> 473 input_shape = input_ids.size()
474 elif inputs_embeds is not None:
475 input_shape = inputs_embeds.size()[:-1]
AttributeError: 'list' object has no attribute 'size'
看起来我们的代码尝试调用 input_ids.size()
,但这对于仅作为容器的 Python list
显然不起作用。我们如何解决这个问题呢?在 Stack Overflow 上搜索错误消息会得到很多相关的结果。点击第一个显示了一个与我们类似的问题,答案如下面的截图所示:

答案建议我们在分词器中添加 return_tensors='pt'
,所以让我们看看这是否对我们有效:
inputs = tokenizer(question, context, add_special_tokens=True, return_tensors="pt")
input_ids = inputs["input_ids"][0]
outputs = model(**inputs)
answer_start_scores = outputs.start_logits
answer_end_scores = outputs.end_logits
# Get the most likely beginning of answer with the argmax of the score
answer_start = torch.argmax(answer_start_scores)
# Get the most likely end of answer with the argmax of the score
answer_end = torch.argmax(answer_end_scores) + 1
answer = tokenizer.convert_tokens_to_string(
tokenizer.convert_ids_to_tokens(input_ids[answer_start:answer_end])
)
print(f"Question: {question}")
print(f"Answer: {answer}")
"""
Question: Which frameworks can I use?
Answer: pytorch, tensorflow, and jax
"""
太好了,成功了!这是一个很好的例子,说明 Stack Overflow 是多么有用:通过发现类似问题,我们能够受益于社区中其他人的经验。然而,这样的搜索并非总能得到相关的答案,那么在这种情况下您该怎么办呢?幸运的是,Hugging Face 论坛上有一个友好的开发者社区可以帮助您!在下一节中,我们将看看如何提出好的论坛问题,这些问题很可能得到回答。
< > 在 GitHub 上更新