Buckets:
| # 處理多個序列 | |
| {#if fw === 'pt'} | |
| {:else} | |
| {/if} | |
| {#if fw === 'pt'} | |
| {:else} | |
| {/if} | |
| 在上一節中,我們探討了最簡單的用例:對一個小長度的序列進行推理。然而,一些問題已經出現: | |
| * 我們如何處理多個序列? | |
| * 我們如何處理多個序列不同長度? | |
| * 詞彙索引是讓模型正常工作的唯一輸入嗎? | |
| * 是否存在序列太長的問題? | |
| 讓我們看看這些問題會帶來什麼樣的問題,以及如何使用🤗 Transformers API解決它們 | |
| ## 模型需要一批輸入 | |
| 在上一個練習中,您看到了序列如何轉換為數字列表。讓我們將此數字列表轉換為張量,並將其發送到模型: | |
| {#if fw === 'pt'} | |
| ```py | |
| import torch | |
| from transformers import AutoTokenizer, AutoModelForSequenceClassification | |
| checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" | |
| tokenizer = AutoTokenizer.from_pretrained(checkpoint) | |
| model = AutoModelForSequenceClassification.from_pretrained(checkpoint) | |
| sequence = "I've been waiting for a HuggingFace course my whole life." | |
| tokens = tokenizer.tokenize(sequence) | |
| ids = tokenizer.convert_tokens_to_ids(tokens) | |
| input_ids = torch.tensor(ids) | |
| # This line will fail. | |
| model(input_ids) | |
| ``` | |
| ```python out | |
| IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1) | |
| ``` | |
| {:else} | |
| ```py | |
| import tensorflow as tf | |
| from transformers import AutoTokenizer, TFAutoModelForSequenceClassification | |
| checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" | |
| tokenizer = AutoTokenizer.from_pretrained(checkpoint) | |
| model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint) | |
| sequence = "I've been waiting for a HuggingFace course my whole life." | |
| tokens = tokenizer.tokenize(sequence) | |
| ids = tokenizer.convert_tokens_to_ids(tokens) | |
| input_ids = tf.constant(ids) | |
| # This line will fail. | |
| model(input_ids) | |
| ``` | |
| ```py out | |
| InvalidArgumentError: Input to reshape is a tensor with 14 values, but the requested shape has 196 [Op:Reshape] | |
| ``` | |
| {/if} | |
| 哦不!為什麼失敗了?我們遵循了第2節中管道的步驟。 | |
| 問題是我們向模型發送了一個序列,而 🤗Transformers 模型默認情況下需要多個句子。在這裡,當我們將分詞器應用於一個應用程序時,我們嘗試在幕後完成分詞器所做的一切,但如果仔細觀察,您會發現它不僅將輸入ID列表轉換為張量,還在其頂部添加了一個維度: | |
| {#if fw === 'pt'} | |
| ```py | |
| tokenized_inputs = tokenizer(sequence, return_tensors="pt") | |
| print(tokenized_inputs["input_ids"]) | |
| ``` | |
| ```python out | |
| tensor([[ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, | |
| 2607, 2026, 2878, 2166, 1012, 102]]) | |
| ``` | |
| {:else} | |
| ```py | |
| tokenized_inputs = tokenizer(sequence, return_tensors="tf") | |
| print(tokenized_inputs["input_ids"]) | |
| ``` | |
| ```py out | |
| tf.Tensor: shape=(1, 16), dtype=int32, numpy= | |
| array([[ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, | |
| 12172, 2607, 2026, 2878, 2166, 1012, 102]], dtype=int32)> | |
| ``` | |
| {/if} | |
| 讓我們重試並添加一個新維度: | |
| {#if fw === 'pt'} | |
| ```py | |
| import torch | |
| from transformers import AutoTokenizer, AutoModelForSequenceClassification | |
| checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" | |
| tokenizer = AutoTokenizer.from_pretrained(checkpoint) | |
| model = AutoModelForSequenceClassification.from_pretrained(checkpoint) | |
| sequence = "I've been waiting for a HuggingFace course my whole life." | |
| tokens = tokenizer.tokenize(sequence) | |
| ids = tokenizer.convert_tokens_to_ids(tokens) | |
| input_ids = torch.tensor([ids]) | |
| print("Input IDs:", input_ids) | |
| output = model(input_ids) | |
| print("Logits:", output.logits) | |
| ``` | |
| {:else} | |
| ```py | |
| import tensorflow as tf | |
| from transformers import AutoTokenizer, TFAutoModelForSequenceClassification | |
| checkpoint = "distilbert-base-uncased-finetuned-sst-2-english" | |
| tokenizer = AutoTokenizer.from_pretrained(checkpoint) | |
| model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint) | |
| sequence = "I've been waiting for a HuggingFace course my whole life." | |
| tokens = tokenizer.tokenize(sequence) | |
| ids = tokenizer.convert_tokens_to_ids(tokens) | |
| input_ids = tf.constant([ids]) | |
| print("Input IDs:", input_ids) | |
| output = model(input_ids) | |
| print("Logits:", output.logits) | |
| ``` | |
| {/if} | |
| 我們打印輸入ID以及生成的logits-以下是輸出: | |
| {#if fw === 'pt'} | |
| ```python out | |
| Input IDs: [[ 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]] | |
| Logits: [[-2.7276, 2.8789]] | |
| ``` | |
| {:else} | |
| ```py out | |
| Input IDs: tf.Tensor( | |
| [[ 1045 1005 2310 2042 3403 2005 1037 17662 12172 2607 2026 2878 | |
| 2166 1012]], shape=(1, 14), dtype=int32) | |
| Logits: tf.Tensor([[-2.7276208 2.8789377]], shape=(1, 2), dtype=float32) | |
| ``` | |
| {/if} | |
| *Batching* 是一次通過模型發送多個句子的行為。如果你只有一句話,你可以用一個序列構建一個批次: | |
| ``` | |
| batched_ids = [ids, ids] | |
| ``` | |
| 這是一批兩個相同的序列! | |
| > [!TIP] | |
| > ✏️ **試試看!** 將此列表轉換為張量並通過模型傳遞。檢查您是否獲得與之前相同的登錄(但是隻有兩次) | |
| 批處理允許模型在輸入多個句子時工作。使用多個序列就像使用單個序列構建批一樣簡單。不過,還有第二個問題。當你試圖將兩個(或更多)句子組合在一起時,它們的長度可能不同。如果您以前使用過張量,那麼您知道它們必須是矩形,因此無法將輸入ID列表直接轉換為張量。為了解決這個問題,我們通常填充輸入。 | |
| ## 填充輸入 | |
| 以下列表不能轉換為張量: | |
| ```py no-format | |
| batched_ids = [ | |
| [200, 200, 200], | |
| [200, 200] | |
| ] | |
| ``` | |
| 為了解決這個問題,我們將使用填充使張量具有矩形。Padding通過在值較少的句子中添加一個名為Padding token的特殊單詞來確保我們所有的句子長度相同。例如,如果你有10個包含10個單詞的句子和1個包含20個單詞的句子,填充將確保所有句子都包含20個單詞。在我們的示例中,生成的張量如下所示: | |
| ```py no-format | |
| padding_id = 100 | |
| batched_ids = [ | |
| [200, 200, 200], | |
| [200, 200, padding_id], | |
| ] | |
| ``` | |
| 可以在tokenizer.pad_token_id中找到填充令牌ID. 讓我們使用它,將我們的兩句話分別發送到模型中,並分批發送到一起: | |
| {#if fw === 'pt'} | |
| ```py no-format | |
| model = AutoModelForSequenceClassification.from_pretrained(checkpoint) | |
| sequence1_ids = [[200, 200, 200]] | |
| sequence2_ids = [[200, 200]] | |
| batched_ids = [ | |
| [200, 200, 200], | |
| [200, 200, tokenizer.pad_token_id], | |
| ] | |
| print(model(torch.tensor(sequence1_ids)).logits) | |
| print(model(torch.tensor(sequence2_ids)).logits) | |
| print(model(torch.tensor(batched_ids)).logits) | |
| ``` | |
| ```python out | |
| tensor([[ 1.5694, -1.3895]], grad_fn=) | |
| tensor([[ 0.5803, -0.4125]], grad_fn=) | |
| tensor([[ 1.5694, -1.3895], | |
| [ 1.3373, -1.2163]], grad_fn=) | |
| ``` | |
| {:else} | |
| ```py no-format | |
| model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint) | |
| sequence1_ids = [[200, 200, 200]] | |
| sequence2_ids = [[200, 200]] | |
| batched_ids = [ | |
| [200, 200, 200], | |
| [200, 200, tokenizer.pad_token_id], | |
| ] | |
| print(model(tf.constant(sequence1_ids)).logits) | |
| print(model(tf.constant(sequence2_ids)).logits) | |
| print(model(tf.constant(batched_ids)).logits) | |
| ``` | |
| ```py out | |
| tf.Tensor([[ 1.5693678 -1.3894581]], shape=(1, 2), dtype=float32) | |
| tf.Tensor([[ 0.5803005 -0.41252428]], shape=(1, 2), dtype=float32) | |
| tf.Tensor( | |
| [[ 1.5693681 -1.3894582] | |
| [ 1.3373486 -1.2163193]], shape=(2, 2), dtype=float32) | |
| ``` | |
| {/if} | |
| 我們批處理預測中的logits有點問題:第二行應該與第二句的logits相同,但我們得到了完全不同的值! | |
| 這是因為Transformer模型的關鍵特性是關注層,它將每個標記上下文化。這些將考慮填充標記,因為它們涉及序列中的所有標記。為了在通過模型傳遞不同長度的單個句子時,或者在傳遞一批應用了相同句子和填充的句子時獲得相同的結果,我們需要告訴這些注意層忽略填充標記。這是通過使用 attention mask來實現的。 | |
| ## 注意力遮罩(Attention masks) | |
| *Attention masks* 是與輸入 ID 張量形狀完全相同的張量,用0和1填充:1s表示應注意相應的標記,0s表示不應注意相應的標記(即,模型的注意力層應忽略它們)。 | |
| 讓我們用 attention mask 完成上一個示例: | |
| {#if fw === 'pt'} | |
| ```py no-format | |
| batched_ids = [ | |
| [200, 200, 200], | |
| [200, 200, tokenizer.pad_token_id], | |
| ] | |
| attention_mask = [ | |
| [1, 1, 1], | |
| [1, 1, 0], | |
| ] | |
| outputs = model(torch.tensor(batched_ids), attention_mask=torch.tensor(attention_mask)) | |
| print(outputs.logits) | |
| ``` | |
| ```python out | |
| tensor([[ 1.5694, -1.3895], | |
| [ 0.5803, -0.4125]], grad_fn=) | |
| ``` | |
| {:else} | |
| ```py no-format | |
| batched_ids = [ | |
| [200, 200, 200], | |
| [200, 200, tokenizer.pad_token_id], | |
| ] | |
| attention_mask = [ | |
| [1, 1, 1], | |
| [1, 1, 0], | |
| ] | |
| outputs = model(tf.constant(batched_ids), attention_mask=tf.constant(attention_mask)) | |
| print(outputs.logits) | |
| ``` | |
| ```py out | |
| tf.Tensor( | |
| [[ 1.5693681 -1.3894582 ] | |
| [ 0.5803021 -0.41252586]], shape=(2, 2), dtype=float32) | |
| ``` | |
| {/if} | |
| 現在我們得到了該批中第二個句子的相同登錄。 | |
| 請注意,第二個序列的最後一個值是一個填充ID,它在attention mask中是一個0值。 | |
| > [!TIP] | |
| > ✏️ 試試看!在第2節中使用的兩個句子上手動應用標記化(“我一生都在等待Hugging Face課程。”和“我非常討厭這個!”)。通過模型傳遞它們,並檢查您是否獲得與第2節中相同的登錄。現在使用填充標記將它們批處理在一起,然後創建適當的注意掩碼。檢查通過模型時是否獲得相同的結果! | |
| ## 長序列 | |
| 對於Transformers模型,我們可以通過模型的序列長度是有限的。大多數模型處理多達512或1024個令牌的序列,當要求處理更長的序列時,會崩潰。此問題有兩種解決方案: | |
| * 使用支持的序列長度較長的模型。 | |
| * 截斷序列。 | |
| 模型有不同的支持序列長度,有些模型專門處理很長的序列。 | |
| [Longformer](https://huggingface.co/transformers/model_doc/longformer.html) | |
| 這是一個例子,另一個是 | |
| [LED](https://huggingface.co/transformers/model_doc/led.html) | |
| . 如果您正在處理一項需要很長序列的任務,我們建議您查看這些模型。 | |
| 否則,我們建議您通過指定max_sequence_length參數: | |
| ```py | |
| sequence = sequence[:max_sequence_length] | |
| ``` | |
Xet Storage Details
- Size:
- 10.5 kB
- Xet hash:
- 9568d1e8d3b359d40bfba796ca2d4e0d1ea7bfd2b0baaf170124748bcde6ed35
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.