Hugging Face 的 Transformers 库快速入门 (一)开箱即用的 pipelines
注:本系列教程僅供學習使用, 由原作者授權, 均轉載自小昇的博客 。
文章目錄
- 前言
- 開箱即用的 pipelines
- 情感分析
- 零訓練樣本分類
- 文本生成
- 遮蓋詞填充
- 命名實體識別
- 自動問答
- 自動摘要
- 這些 pipeline 背后做了什么?
- 使用分詞器進行預處理
- 將預處理好的輸入送入模型
- 對模型輸出進行后處理
- 總結
前言
Transformers 是由 Hugging Face 開發的一個 NLP 包,支持加載目前絕大部分的預訓練模型。隨著 BERT、GPT 等大規模語言模型的興起,越來越多的公司和研究者采用 Transformers 庫來構建 NLP 應用,因此熟悉 Transformers 庫的使用方法很有必要。
注:本系列教程只專注于處理文本,多模態方法請查閱相關文檔。
開箱即用的 pipelines
Transformers 庫將目前的 NLP 任務歸納為幾下幾類:
- 文本分類: 例如情感分析、句子對關系判斷等;
- 對文本中的詞語進行分類: 例如詞性標注 (POS)、命名實體識別 (NER) 等;
- 文本生成: 例如填充預設的模板 (prompt)、預測文本中被遮掩掉 (masked) 的詞語;
- 從文本中抽取答案: 例如根據給定的問題從一段文本中抽取出對應的答案;
- 根據輸入文本生成新的句子: 例如文本翻譯、自動摘要等。
Transformers 庫最基礎的對象就是 pipeline() 函數,它封裝了預訓練模型和對應的前處理和后處理環節。我們只需輸入文本,就能得到預期的答案。目前常用的 pipelines 有:
- feature-extraction (獲得文本的向量化表示)
- fill-mask (填充被遮蓋的詞、片段)
- ner (命名實體識別)
- question-answering (自動問答)
- sentiment-analysis (情感分析)
- summarization (自動摘要)
- text-generation (文本生成)
- translation (機器翻譯)
- zero-shot-classification (零訓練樣本分類)
下面我們以常見的幾個 NLP 任務為例,展示如何調用這些 pipeline 模型。
情感分析
借助情感分析 pipeline,我們只需要輸入文本,就可以得到其情感標簽(積極/消極)以及對應的概率:
from transformers import pipelineclassifier = pipeline("sentiment-analysis") result = classifier("I've been waiting for a HuggingFace course my whole life.") print(result) results = classifier(["I've been waiting for a HuggingFace course my whole life.", "I hate this so much!"] ) print(results) No model was supplied, defaulted to distilbert-base-uncased-finetuned-sst-2-english (https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)[{'label': 'POSITIVE', 'score': 0.9598048329353333}] [{'label': 'POSITIVE', 'score': 0.9598048329353333}, {'label': 'NEGATIVE', 'score': 0.9994558691978455}]pipeline 模型會自動完成以下三個步驟:
pipeline 會自動選擇合適的預訓練模型來完成任務。例如對于情感分析,默認就會選擇微調好的英文情感模型 distilbert-base-uncased-finetuned-sst-2-english。
Transformers 庫會在創建對象時下載并且緩存模型,只有在首次加載模型時才會下載,后續會直接調用緩存好的模型。
零訓練樣本分類
零訓練樣本分類 pipeline 允許我們在不提供任何標注數據的情況下自定義分類標簽。
from transformers import pipelineclassifier = pipeline("zero-shot-classification") result = classifier( "This is a course about the Transformers library", candidate_labels=["education", "politics", "business"], ) print(result) No model was supplied, defaulted to facebook/bart-large-mnli (https://huggingface.co/facebook/bart-large-mnli){'sequence': 'This is a course about the Transformers library', 'labels': ['education', 'business', 'politics'], 'scores': [0.8445973992347717, 0.11197526752948761, 0.043427325785160065]}可以看到,pipeline 自動選擇了預訓練好的 facebook/bart-large-mnli 模型來完成任務。
文本生成
我們首先根據任務需要構建一個模板 (prompt),然后將其送入到模型中來生成后續文本。注意,由于文本生成具有隨機性,因此每次運行都會得到不同的結果。
這種模板被稱為前綴模板 (Pre?x Prompt),了解更多詳細信息可以查看《Prompt 方法簡介》。
from transformers import pipelinegenerator = pipeline("text-generation") results = generator("In this course, we will teach you how to") print(results) results = generator("In this course, we will teach you how to",num_return_sequences=2,max_length=50 ) print(results) No model was supplied, defaulted to gpt2 (https://huggingface.co/gpt2)[{'generated_text': "In this course, we will teach you how to use data and models that can be applied in any real-world, everyday situation. In most cases, the following will work better than other courses I've offered for an undergrad or student. In order"}] [{'generated_text': 'In this course, we will teach you how to make your own unique game called "Mono" from scratch by doing a game engine, a framework and the entire process starting with your initial project. We are planning to make some basic gameplay scenarios and'}, {'generated_text': 'In this course, we will teach you how to build a modular computer, how to run it on a modern Windows machine, how to install packages, and how to debug and debug systems. We will cover virtualization and virtualization without a programmer,'}]可以看到,pipeline 自動選擇了預訓練好的 gpt2 模型來完成任務。我們也可以指定要使用的模型。對于文本生成任務,我們可以在 Model Hub 頁面左邊選擇 Text Generation tag 查詢支持的模型。例如,我們在相同的 pipeline 中加載 distilgpt2 模型:
from transformers import pipelinegenerator = pipeline("text-generation", model="distilgpt2") results = generator("In this course, we will teach you how to",max_length=30,num_return_sequences=2, ) print(results) [{'generated_text': 'In this course, we will teach you how to use React in any form, and how to use React without having to worry about your React dependencies because'}, {'generated_text': 'In this course, we will teach you how to use a computer system in order to create a working computer. It will tell you how you can use'}]還可以通過左邊的語言 tag 選擇其他語言的模型。例如加載專門用于生成中文古詩的 gpt2-chinese-poem 模型:
from transformers import pipelinegenerator = pipeline("text-generation", model="uer/gpt2-chinese-poem") results = generator("[CLS] 萬 疊 春 山 積 雨 晴 ,",max_length=40,num_return_sequences=2, ) print(results) [{'generated_text': '[CLS] 萬 疊 春 山 積 雨 晴 , 孤 舟 遙 送 子 陵 行 。 別 情 共 嘆 孤 帆 遠 , 交 誼 深 憐 一 座 傾 。 白 日 風 波 身 外 幻'}, {'generated_text': '[CLS] 萬 疊 春 山 積 雨 晴 , 滿 川 煙 草 踏 青 行 。 何 人 喚 起 傷 春 思 , 江 畔 畫 船 雙 櫓 聲 。 桃 花 帶 雨 弄 晴 光'}] 模型輸入與輸出中的 CLS 并沒有什么意義, 主要是為了與預訓練模型中的輸入輸出格式保持一致 。遮蓋詞填充
給定一段部分詞語被遮蓋掉 (masked) 的文本,使用預訓練模型來預測能夠填充這些位置的詞語。
與前面介紹的文本生成類似,這個任務其實也是先構建模板然后運用模型來完善模板,稱為填充模板 (Cloze Prompt)。了解更多詳細信息可以查看《Prompt 方法簡介》。
from transformers import pipelineunmasker = pipeline("fill-mask") results = unmasker("This course will teach you all about <mask> models.", top_k=2) print(results) No model was supplied, defaulted to distilroberta-base (https://huggingface.co/distilroberta-base)[{'sequence': 'This course will teach you all about mathematical models.', 'score': 0.19619858264923096, 'token': 30412, 'token_str': ' mathematical'}, {'sequence': 'This course will teach you all about computational models.', 'score': 0.04052719101309776, 'token': 38163, 'token_str': ' computational'}]可以看到,pipeline 自動選擇了預訓練好的 distilroberta-base 模型來完成任務。
命名實體識別
命名實體識別 (NER) pipeline 負責從文本中抽取出指定類型的實體,例如人物、地點、組織等等。
from transformers import pipelinener = pipeline("ner", grouped_entities=True) results = ner("My name is Sylvain and I work at Hugging Face in Brooklyn.") print(results) No model was supplied, defaulted to dbmdz/bert-large-cased-finetuned-conll03-english (https://huggingface.co/dbmdz/bert-large-cased-finetuned-conll03-english)[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18}, {'entity_group': 'ORG', 'score': 0.97960186, 'word': 'Hugging Face', 'start': 33, 'end': 45}, {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]可以看到,模型正確地識別出了 Sylvain 是一個人物,Hugging Face 是一個組織,Brooklyn 是一個地名。
這里通過設置參數 grouped_entities=True ,使得 pipeline 自動合并屬于同一個實體的多個子詞 (token),例如這里將“Hugging”和“Face”合并為一個組織實體,實際上 Sylvain 也進行了子詞合并,因為分詞器會將 Sylvain 切分為 S 、 ##yl 、 ##va 和 ##in 四個 token。
自動問答
自動問答 pipeline 可以根據給定的上下文回答問題,例如:
from transformers import pipelinequestion_answerer = pipeline("question-answering") answer = question_answerer(question="Where do I work?",context="My name is Sylvain and I work at Hugging Face in Brooklyn", ) print(answer) No model was supplied, defaulted to distilbert-base-cased-distilled-squad (https://huggingface.co/distilbert-base-cased-distilled-squad){'score': 0.6949771046638489, 'start': 33, 'end': 45, 'answer': 'Hugging Face'}可以看到,pipeline 自動選擇了在 SQuAD 數據集上訓練好的 distilbert-base 模型來完成任務。這里的自動問答 pipeline 實際上是一個抽取式問答模型,即從給定的上下文中抽取答案,而不是生成答案。
根據形式的不同,自動問答 (QA) 系統可以分為三種:
- 抽取式 QA (extractive QA): 假設答案就包含在文檔中,因此直接從文檔中抽取答案;
- 多選 QA (multiple-choice QA): 從多個給定的選項中選擇答案,相當于做閱讀理解題;
- 無約束 QA (free-form QA): 直接生成答案文本,并且對答案文本格式沒有任何限制。
自動摘要
自動摘要 pipeline 旨在將長文本壓縮成短文本,并且還要盡可能保留原文的主要信息,例如:
from transformers import pipelinesummarizer = pipeline("summarization") results = summarizer("""America has changed dramatically during recent years. Not only has the number of graduates in traditional engineering disciplines such as mechanical, civil, electrical, chemical, and aeronautical engineering declined, but in most of the premier American universities engineering curricula now concentrate on and encourage largely the study of engineering science. As a result, there are declining offerings in engineering subjects dealing with infrastructure, the environment, and related issues, and greater concentration on high technology subjects, largely supporting increasingly complex scientific developments. While the latter is important, it should not be at the expense of more traditional engineering.Rapidly developing economies such as China and India, as well as other industrial countries in Europe and Asia, continue to encourage and advance the teaching of engineering. Both China and India, respectively, graduate six and eight times as many traditional engineers as does the United States. Other industrial countries at minimum maintain their output, while America suffers an increasingly serious decline in the number of engineering graduates and a lack of well-educated engineers.""" ) print(results) No model was supplied, defaulted to sshleifer/distilbart-cnn-12-6 (https://huggingface.co/sshleifer/distilbart-cnn-12-6)[{'summary_text': ' America has changed dramatically during recent years . The number of engineering graduates in the U.S. has declined in traditional engineering disciplines such as mechanical, civil, electrical, chemical, and aeronautical engineering . Rapidly developing economies such as China and India, as well as other industrial countries in Europe and Asia, continue to encourage and advance engineering .'}]可以看到,pipeline 自動選擇了預訓練好的 distilbart-cnn-12-6 模型來完成任務。與文本生成類似,我們也可以通過 max_length 或 min_length 參數來控制返回摘要的長度。
這些 pipeline 背后做了什么?
這些簡單易用的 pipeline 模型實際上封裝了許多操作,下面我們就來了解一下它們背后究竟做了啥。以第一個情感分析 pipeline 為例,我們運行下面的代碼:
from transformers import pipelineclassifier = pipeline("sentiment-analysis") result = classifier("I've been waiting for a HuggingFace course my whole life.") print(result)就會得到結果:
[{'label': 'POSITIVE', 'score': 0.9598048329353333}]實際上它的背后經過了三個步驟:
使用分詞器進行預處理
因為神經網絡模型無法直接處理文本,因此首先需要通過預處理環節將文本轉換為模型可以理解的數字。具體地,我們會使用每個模型對應的分詞器 (tokenizer) 來進行:
我們對輸入文本的預處理需要與模型自身預訓練時的操作完全一致,只有這樣模型才可以正常地工作。注意,每個模型都有特定的預處理操作,如果對要使用的模型不熟悉,可以通過 Model Hub 查詢。這里我們使用 AutoTokenizer 類和它的 from_pretrained() 函數,它可以自動根據模型 checkpoint 名稱來獲取對應的分詞器。
情感分析 pipeline 的默認 checkpoint 是 distilbert-base-uncased-finetuned-sst-2-english,下面我們手工下載并調用其分詞器:
from transformers import AutoTokenizercheckpoint = "distilbert-base-uncased-finetuned-sst-2-english" tokenizer = AutoTokenizer.from_pretrained(checkpoint)raw_inputs = ["I've been waiting for a HuggingFace course my whole life.","I hate this so much!", ] inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt") print(inputs) {'input_ids': tensor([[ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102],[ 101, 1045, 5223, 2023, 2061, 2172, 999, 102, 0, 0,0, 0, 0, 0, 0, 0]]), 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]]) }可以看到,輸出中包含兩個鍵 input_ids 和 attention_mask,其中 input_ids 對應分詞之后的 tokens 映射到的數字編號列表,而 attention_mask 則是用來標記哪些 tokens 是被填充的(這里“1”表示是原文,“0”表示是填充字符)。
先不要關注 padding、truncation 這些參數,以及 attention_mask 項,后面我們會詳細介紹:)。
將預處理好的輸入送入模型
預訓練模型的下載方式和分詞器 (tokenizer) 類似,Transformers 包提供了一個 AutoModel 類和對應的 from_pretrained() 函數。下面我們手工下載這個 distilbert-base 模型:
from transformers import AutoModelcheckpoint = "distilbert-base-uncased-finetuned-sst-2-english" model = AutoModel.from_pretrained(checkpoint)預訓練模型的本體只包含基礎的 Transformer 模塊,對于給定的輸入,它會輸出一些神經元的值,稱為 hidden states 或者特征 (features)。對于 NLP 模型來說,可以理解為是文本的高維語義表示。這些 hidden states 通常會被輸入到其他的模型部分(稱為 head),以完成特定的任務,例如送入到分類頭中完成文本分類任務。
其實前面我們舉例的所有 pipelines 都具有類似的模型結構,只是模型的最后一部分會使用不同的 head 以完成對應的任務。
Transformers 庫封裝了很多不同的結構,常見的有:
Transformer 模塊的輸出是一個維度為 (Batch size, Sequence length, Hidden size) 的三維張量,其中 Batch size 表示每次輸入的樣本(文本序列)數量,即每次輸入多少個句子,上例中為 2;Sequence length 表示文本序列的長度,即每個句子被分為多少個 token,上例中為 16;Hidden size 表示每一個 token 經過模型編碼后的輸出向量(語義表示)的維度。
預訓練模型編碼后的輸出向量的維度通常都很大,例如 Bert 模型 base 版本的輸出為 768 維,一些大模型的輸出維度為 3072 甚至更高。
我們可以打印出這里使用的 distilbert-base 模型的輸出維度:
from transformers import AutoTokenizer, AutoModelcheckpoint = "distilbert-base-uncased-finetuned-sst-2-english" tokenizer = AutoTokenizer.from_pretrained(checkpoint) model = AutoModel.from_pretrained(checkpoint)raw_inputs = ["I've been waiting for a HuggingFace course my whole life.","I hate this so much!", ] inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt") outputs = model(**inputs) print(outputs.last_hidden_state.shape) torch.Size([2, 16, 768])Transformers 模型的輸出格式類似 namedtuple 或字典,可以像上面那樣通過屬性訪問,也可以通過鍵(outputs["last_hidden_state"]),甚至索引訪問(outputs[0])。
對于情感分析任務,很明顯我們最后需要使用的是一個文本分類 head。因此,實際上我們不會使用 AutoModel 類,而是使用 AutoModelForSequenceClassification:
from transformers import AutoTokenizer from transformers import AutoModelForSequenceClassificationcheckpoint = "distilbert-base-uncased-finetuned-sst-2-english" tokenizer = AutoTokenizer.from_pretrained(checkpoint) model = AutoModelForSequenceClassification.from_pretrained(checkpoint)raw_inputs = ["I've been waiting for a HuggingFace course my whole life.","I hate this so much!", ] inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt") outputs = model(**inputs) print(outputs.logits.shape) torch.Size([2, 2])可以看到,對于 batch 中的每一個樣本,模型都會輸出一個兩維的向量(每一維對應一個標簽,positive 或 negative)。
對模型輸出進行后處理
由于模型的輸出只是一些數值,因此并不適合人類閱讀。例如我們打印出上面例子的輸出:
from transformers import AutoTokenizer from transformers import AutoModelForSequenceClassificationcheckpoint = "distilbert-base-uncased-finetuned-sst-2-english" tokenizer = AutoTokenizer.from_pretrained(checkpoint) model = AutoModelForSequenceClassification.from_pretrained(checkpoint)raw_inputs = ["I've been waiting for a HuggingFace course my whole life.","I hate this so much!", ] inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt") outputs = model(**inputs) print(outputs.logits) tensor([[-1.5607, 1.6123],[ 4.1692, -3.3464]], grad_fn=<AddmmBackward0>)模型對第一個句子輸出 [?1.5607,1.6123],對第二個句子輸出 [4.1692,?3.3464],它們并不是概率值,而是模型最后一層輸出的 logits 值。要將他們轉換為概率值,還需要讓它們經過一個 SoftMax 層,例如:
import torch predictions = torch.nn.functional.softmax(outputs.logits, dim=-1) print(predictions) tensor([[4.0195e-02, 9.5980e-01],[9.9946e-01, 5.4418e-04]], grad_fn=<SoftmaxBackward0>)所有 Transformers 模型都會輸出 logits 值,因為訓練時的損失函數通常會自動結合激活函數(例如 SoftMax)與實際的損失函數(例如交叉熵 cross entropy)。
這樣模型的預測結果就是容易理解的概率值:第一個句子 [0.0402,0.9598],第二個句子 [0.9995,0.0005]。最后,為了得到對應的標簽,可以讀取模型 config 中提供的 id2label 屬性:
print(model.config.id2label) {0: 'NEGATIVE', 1: 'POSITIVE'}于是我們可以得到最終的預測結果:
總結
在本文中我們初步介紹了如何使用 Transformers 包提供的 pipeline 對象來處理各種 NLP 任務,并且對 pipeline 背后的工作原理進行了簡單的說明。在下一篇轉載文章中,我們會具體介紹組成 pipeline 的兩個重要組件模型(Models 類)和分詞器(Tokenizers 類)的參數以及使用方式。
參考資料:
小昇的博客
Transformers 官方文檔
HuggingFace 在線教程
小昇的Github項目
總結
以上是生活随笔為你收集整理的Hugging Face 的 Transformers 库快速入门 (一)开箱即用的 pipelines的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaScript的输出语句
- 下一篇: 计算机在中医学有哪些最新应用,计算机与中