HuggingFace Transformers 4.6 : ノートブック : Getting Started Transformers

HuggingFace Transformers 4.6 : ノートブック : Getting Started Transformers (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 06/12/2021 (4.6.1)

* 本ページは、HuggingFace Transformers の以下のドキュメントを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

無料 Web セミナー開催中 クラスキャット主催 人工知能 & ビジネス Web セミナー

人工知能とビジネスをテーマに WEB セミナーを定期的に開催しています。
スケジュールは弊社 公式 Web サイト でご確認頂けます。
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • ウェビナー運用には弊社製品「ClassCat® Webinar」を利用しています。
クラスキャットは人工知能・テレワークに関する各種サービスを提供しております :

人工知能研究開発支援 人工知能研修サービス テレワーク & オンライン授業を支援
PoC(概念実証)を失敗させないための支援 (本支援はセミナーに参加しアンケートに回答した方を対象としています。)

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com  ;  WebSite: https://www.classcat.com/  ;  Facebook

 

 

ノートブック : Getting Started Transformers

イントロダクション

transformers ライブラリは、Bert (Devlin & al., 2018), Roberta (Liu & al., 2019), GPT2 (Radford & al., 2019), XLNet (Yang & al., 2019) 等のような Transformer アーキテクチャ (Vaswani & al., 2017) に基づくモデルを訓練し、利用して共有するためのオープンソース、コミュニティ・ベースのレポジトリです。

これらのモデルとともに、ライブラリは 固有表現認識 (NER)、センチメント分析言語モデリング質問応答 等のような多種多様な下流タスクのためにそれらの各々の複数のバリエーションを含みます。

 

Transformer 以前

2017 に戻ると、ニューラルネットワークを使用する人々の殆どは自然言語処理で作業するとき リカレント・ニューラルネットワーク (RNN) を通した入力のシーケンシャルな処理に依存していました。

RNN は入力シークエンスに渡るシーケンシャルな依存性を含む様々なタスク上で上手く遂行しました。けれども、シーケンシャルな依存プロセスは非常に長い範囲の依存性をモデル化する問題を持っていて、悪い並列化機能ゆえに現在活用している種類のハードウェアのためには上手く適合しませんでした。

双方向 RNN ( Schuster & Paliwal., 1997, Graves & al., 2005 ) のような、幾つかの拡張が学術的なコミュニティから提供されました、これは 2 つのシーケンシャルなプロセスの結合として見ることができて、シークエンス入力に対して一つは順方向に進み、他方は逆方向に進みます。

そしてまた、Attention メカニズムは、シークエンスの各要素に学習された、重み付けされた重要性を与えることにより “raw” RNN を越える良い改良を導入し、モデルが重要な要素にフォーカスすることを可能にしました。

 

そして Transformer の登場

Transformer の時代は元々は翻訳タスク上で リカレント・ニューラルネットワーク (RNN) を越える優位性を実演した ( Vaswani & al., 2017 ) のワークから始まりましたが、それは迅速に当時 RNN が最先端であった殆ど総てのタスクに拡張されました。

RNN のカウンターパートを越える Transformer の一つの優位点は非シーケンシャルな attention モデルでした。忘れないでください、RNN は入力シークエンスの各要素に渡り一つ一つ反復して各跳躍 (= hop) 間で「更新可能な状態」を持ち運ばなければなりませんでした。Transformer では、モデルはシークエンスの総ての位置を同時に、一つの演算で見ることができます。

Transformer アーキテクチャの詳細については、The Annotated Transformer が論文の総ての詳細に沿って貴方を導きます。

 

Getting started with transformers

このノートブックの残りについては、BERT (Devlin & al., 2018) アーキテクチャを使用します、それは最も単純でインターネット上にそれについて多くのコンテンツがあるので、貴方が望めばこのアーキテクチャについて更に掘り下げることは容易です。

transformers ライブラリは巨大でコストのかかる計算インフラを必要とすることなく、大規模な、事前訓練された言語モデルから恩恵を受けることを可能にします。殆どの最先端モデルがその author から直接提供され、透過的で交換可能な方法で PyTorch と TensorFlow のライブラリで利用可能です。

このノートブックを Colab で実行する場合、transformers ライブラリをインストールする必要があります。このコマンドでそれを行なうことができます :

# !pip install transformers
import torch
from transformers import AutoModel, AutoTokenizer, BertTokenizer

torch.set_grad_enabled(False)
<torch.autograd.grad_mode.set_grad_enabled at 0x7ff0cc2a2c50>
# Store the model we want to use
MODEL_NAME = "bert-base-cased"

# We need to create the model and tokenizer
model = AutoModel.from_pretrained(MODEL_NAME)
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)

上のコードの 2 行だけで、BERT 事前訓練モデルを利用する準備ができました。トークナイザーは raw テキスト入力を (モデルが操作できる方法でテキスト入力を表す) 整数のシークエンスにマップすることを可能にします。PyTorch モデルを使用しますので、トークナイザーに PyTorch tensor を返すように求めます。

tokens_pt = tokenizer("This is an input example", return_tensors="pt")
for key, value in tokens_pt.items():
    print("{}:\n\t{}".format(key, value))

input_ids:
	tensor([[ 101, 1188, 1110, 1126, 7758, 1859,  102]])
token_type_ids:
	tensor([[0, 0, 0, 0, 0, 0, 0]])
attention_mask:
	tensor([[1, 1, 1, 1, 1, 1, 1]])

トークナイザーは入力をモデルにより想定される総ての入力に自動的に変換しました。それは幾つかの追加の tensor を ID の上に生成しました。

  • token_type_ids : この tensor は総てのトークンを対応するセグメントにマップします (下参照)。

  • attention_mask: この tensor は異なる長さを持つシークエンスのバッチでパッドされた値を「マスク」するために使用されます (下参照)。

それらのキーの各々についてのより多くの情報のためには 用語集 を確認できます。

これをモデルに単に直接供給できます。

outputs = model(**tokens_pt)
last_hidden_state = outputs.last_hidden_state
pooler_output = outputs.pooler_output

print("Token wise output: {}, Pooled output: {}".format(last_hidden_state.shape, pooler_output.shape))
Token wise output: torch.Size([1, 7, 768]), Pooled output: torch.Size([1, 768])

ご覧のように、BERT は 2 つの tensor を出力します :

  • 一つは入力の総てのトークンのための生成された表現です (1, NB_TOKENS, REPRESENTATION_SIZE)。
  • 一つは入力全体のための集約表現です (1, REPRESENTATION_SIZE)。

タスクがシークエンス表現を保持することを必要としトークンレベルで操作することを望む場合、最初のトークンベースの表現が活用できます。これは固有表現認識と質問応答のために特に有用です。

シークエンスのコンテキスト全体を抽出する必要があり極め細かいトークンレベルを必要としない場合、2 番目の集約表現が特別に有用です。これはシークエンスのセンチメント分析や情報検索に当てはまります。


# Single segment input
single_seg_input = tokenizer("This is a sample input")

# Multiple segment input
multi_seg_input = tokenizer("This is segment A", "This is segment B")

print("Single segment token (str): {}".format(tokenizer.convert_ids_to_tokens(single_seg_input['input_ids'])))
print("Single segment token (int): {}".format(single_seg_input['input_ids']))
print("Single segment type       : {}".format(single_seg_input['token_type_ids']))

# Segments are concatened in the input to the model, with 
print()
print("Multi segment token (str): {}".format(tokenizer.convert_ids_to_tokens(multi_seg_input['input_ids'])))
print("Multi segment token (int): {}".format(multi_seg_input['input_ids']))
print("Multi segment type       : {}".format(multi_seg_input['token_type_ids']))
Single segment token (str): ['[CLS]', 'This', 'is', 'a', 'sample', 'input', '[SEP]']
Single segment token (int): [101, 1188, 1110, 170, 6876, 7758, 102]
Single segment type       : [0, 0, 0, 0, 0, 0, 0]

Multi segment token (str): ['[CLS]', 'This', 'is', 'segment', 'A', '[SEP]', 'This', 'is', 'segment', 'B', '[SEP]']
Multi segment token (int): [101, 1188, 1110, 6441, 138, 102, 1188, 1110, 6441, 139, 102]
Multi segment type       : [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1]
# Padding highlight
tokens = tokenizer(
    ["This is a sample", "This is another longer sample text"], 
    padding=True  # First sentence will have some PADDED tokens to match second sequence length
)

for i in range(2):
    print("Tokens (int)      : {}".format(tokens['input_ids'][i]))
    print("Tokens (str)      : {}".format([tokenizer.convert_ids_to_tokens(s) for s in tokens['input_ids'][i]]))
    print("Tokens (attn_mask): {}".format(tokens['attention_mask'][i]))
    print()
Tokens (int)      : [101, 1188, 1110, 170, 6876, 102, 0, 0]
Tokens (str)      : ['[CLS]', 'This', 'is', 'a', 'sample', '[SEP]', '[PAD]', '[PAD]']
Tokens (attn_mask): [1, 1, 1, 1, 1, 1, 0, 0]

Tokens (int)      : [101, 1188, 1110, 1330, 2039, 6876, 3087, 102]
Tokens (str)      : ['[CLS]', 'This', 'is', 'another', 'longer', 'sample', 'text', '[SEP]']
Tokens (attn_mask): [1, 1, 1, 1, 1, 1, 1, 1]

 

フレームワーク相互運用性

transformers の最もパワフルな特徴の一つはユーザの痛みなく PyTorch から TensorFlow にシームレスに移動できる能力です。

PyTorch モデル内に TensorFlow 事前訓練重みをロードする (そしてその反対の) ための幾つかの便利なメソッドを提供しています。

from transformers import TFBertModel, BertModel

# Let's load a BERT model for TensorFlow and PyTorch
model_tf = TFBertModel.from_pretrained('bert-base-cased')
model_pt = BertModel.from_pretrained('bert-base-cased')
# transformers generates a ready to use dictionary with all the required parameters for the specific framework.
input_tf = tokenizer("This is a sample input", return_tensors="tf")
input_pt = tokenizer("This is a sample input", return_tensors="pt")

# Let's compare the outputs
output_tf, output_pt = model_tf(input_tf), model_pt(**input_pt)

# Models outputs 2 values (The value for each tokens, the pooled representation of the input sentence)
# Here we compare the output differences between PyTorch and TensorFlow.
for name in ["last_hidden_state", "pooler_output"]:
    print("{} differences: {:.5}".format(name, (output_tf[name].numpy() - output_pt[name].numpy()).sum()))
last_hidden_state differences: 1.2933e-05
pooler_output differences: 2.9691e-06

 

より軽量であることを望みますか?より高速であることを?distillation について話しましょう!

これらの Transformer ベースのモデルを使用するときの主要な懸念の一つはそれらが必要とする計算パワーです。このノートブック全体に渡り BERT モデルを使用しています、それは一般的なマシン上で実行できるからですが、モデルの総てのために当てはまりません。

例えば、Google は数ヶ月前に Transformer ベースのエンコーダ/デコーダ・アーキテクチャ T5 をリリースしました、そして 110 億に過ぎないパラメータを持つ transformers で利用可能です。マイクロソフトはまた最近 170 億パラメータを使用して Turing-NLG によりゲームに参加しました。この種類のモデルは重みをストアするために数十 GB を必要としそして一般的な人のためには実行不可能とする、そのようなモデルを実行するために非常に膨大な計算インフラを必要とします。

Transformer ベースの NLP を総ての人にアクセス可能にする目標により、@huggingface は Distillation と呼ばれる訓練プロセスを活用するモデルを開発しました、これはパフォーマンスを殆ど落とすことなくそのようなモデルを実行するために必要なリソースを劇的に削減することを可能にします。

Distillation プロセス全体を調べることはこのノートブックの範囲外ですが、主題についてより多くの情報を望む場合には、DistilBERT 論文の著者である、私の同僚 Victor SANH により書かれたこの Medium の記事 を参照することができます、論文 ( Sanh & al., 2019 ) を直接見ることもまた望むかもしれません。

もちろん、transformers では幾つかのモデルを蒸留してライブラリで直接利用可能にしました!

from transformers import DistilBertModel

bert_distil = DistilBertModel.from_pretrained('distilbert-base-cased')
input_pt = tokenizer(
    'This is a sample input to demonstrate performance of distiled models especially inference time', 
    return_tensors="pt"
)


%time _ = bert_distil(input_pt['input_ids'])
%time _ = model_pt(input_pt['input_ids'])
HBox(children=(FloatProgress(value=0.0, description='Downloading', max=411.0, style=ProgressStyle(description_…
HBox(children=(FloatProgress(value=0.0, description='Downloading', max=263273408.0, style=ProgressStyle(descri…
CPU times: user 64.4 ms, sys: 0 ns, total: 64.4 ms
Wall time: 72.9 ms
CPU times: user 130 ms, sys: 124 µs, total: 130 ms
Wall time: 131 ms

 

コミュニティ提供モデル

最後になりましたが、このノートブックの前の方で Hugging Face transformers を NLP コミュニティが事前訓練モデルを交換するためのレポジトリとして紹介しました。この機能とそれがエンドユーザに供給する総ての可能性を強調することを望みました。

コミュニティの事前訓練モデルを活用するためには、組織名とモデルの名前を from_pretrained に単に提供するだけでそしてそれは総てのマジックを貴方のために行ないます!

現在コミュニティにより提供された 50 モデル以上を持ちそして更に多くが毎日追加されています、試すことを躊躇しないでください!

# Let's load German BERT from the Bavarian State Library
de_bert = BertModel.from_pretrained("dbmdz/bert-base-german-cased")
de_tokenizer = BertTokenizer.from_pretrained("dbmdz/bert-base-german-cased")

de_input = de_tokenizer(
    "Hugging Face ist eine französische Firma mit Sitz in New-York.",
    return_tensors="pt"
)
print("Tokens (int)      : {}".format(de_input['input_ids'].tolist()[0]))
print("Tokens (str)      : {}".format([de_tokenizer.convert_ids_to_tokens(s) for s in de_input['input_ids'].tolist()[0]]))
print("Tokens (attn_mask): {}".format(de_input['attention_mask'].tolist()[0]))
print()

outputs_de = de_bert(**de_input)
last_hidden_state_de = outputs_de.last_hidden_state
pooler_output_de = outputs_de.pooler_output

print("Token wise output: {}, Pooled output: {}".format(last_hidden_state_de.shape, pooler_output_de.shape))
Tokens (int)      : [102, 12272, 9355, 5746, 30881, 215, 261, 5945, 4118, 212, 2414, 153, 1942, 232, 3532, 566, 103]
Tokens (str)      : ['[CLS]', 'Hug', '##ging', 'Fac', '##e', 'ist', 'eine', 'französische', 'Firma', 'mit', 'Sitz', 'in', 'New', '-', 'York', '.', '[SEP]']
Tokens (attn_mask): [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]

Token wise output: torch.Size([1, 17, 768]), Pooled output: torch.Size([1, 768])
 

以上