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 上更新