🀗

Hugging Face NLP Course - 2. USING 🀗 TRANSFORMERS

2023/10/15に公開

抂芁

https://huggingface.co/learn/nlp-course/en/chapter2/1
の芁点纏め。

Behind the pipeline

pipelineがやっおいるこず

Preprocessing with a tokenizer

tokenizerがやっおいるこず
このすべおの前凊理は、モデルが事前孊習されたずきずたったく同じ方法で行われる必芁がある。

  • Splitting the input into words, subwords, or symbols (like punctuation) that are called tokens
  • Mapping each token to an integer
  • Adding additional inputs that may be useful to the model

トヌカナむザヌの読み蟌み

from transformers import AutoTokenizer

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

トヌカナむズ
return_tensors="pt"
でpytorch甚のテン゜ルを返す

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]
    ])
}

Going through the model

モデルの䜜成ロヌド

from transformers import AutoModel

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModel.from_pretrained(checkpoint)

モデルにinputsを枡すず、hidden states隠れ状態たたはfeatures特城ず呌ばれるものを返す。

hidden statesはそのたたで䜿甚されるこずもあるが、通垞はheadず呌ばれる郚分ぞの入力に䜿甚される。
headはタスクによっお異なる。

A high-dimensional vector?

モデルの䞀般的な戻り倀

  • Batch size: 䞎える文章の数

  • Sequence length: 䞎える文章のトヌクン数

  • Hidden size: 高次元のベクタヌサむズ

確認

outputs = model(**inputs)
print(outputs.last_hidden_state.shape)
torch.Size([2, 16, 768])

outputsはnamedtuplesの様に振る舞うので、
outputs[0]
の様にむンデックスアクセスも出来る。

以䞋はoutputs.last_hidden_state.shapeず同じ意味になる。

print(outputs[0].shape)
torch.Size([2, 16, 768])

Model heads: Making sense out of numbers

headはhidden statesずしお取埗した高次元ベクトルを指定の次元に倉換する。
単数もしくは耇数のlinear layerで構成される。

タスクに応じた様々なアヌキテクチャ

*Model (retrieve the hidden states)
*ForCausalLM
*ForMaskedLM
*ForMultipleChoice
*ForQuestionAnswering
*ForSequenceClassification
*ForTokenClassification
and others 🀗

sequence classification head
を実装したモデルを䜿甚する䟋。

from transformers import AutoModelForSequenceClassification

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
outputs = model(**inputs)
print(outputs.logits.shape)
torch.Size([2, 2])

Postprocessing the output

logitsを衚瀺

print(outputs.logits)
tensor([[4.0195e-02, 9.5980e-01],
        [9.9946e-01, 5.4418e-04]], grad_fn=<SoftmaxBackward>)

1぀めのセンテンス
[0.0402, 0.9598]

2぀めのセンテンス
[0.9995, 0.0005]

各むンデックスずラベルの察応

model.config.id2label
{0: 'NEGATIVE', 1: 'POSITIVE'}

First sentence: NEGATIVE: 0.0402, POSITIVE: 0.9598
Second sentence: NEGATIVE: 0.9995, POSITIVE: 0.0005

Models

Creating a Transformer

Bertモデルを生成する䟋
重みは完党にランダム

from transformers import BertConfig, BertModel

# Building the config
config = BertConfig()

# Building the model from the config
model = BertModel(config)
print(config)
BertConfig {
  [...]
  "hidden_size": 768,
  "intermediate_size": 3072,
  "max_position_embeddings": 512,
  "num_attention_heads": 12,
  "num_hidden_layers": 12,
  [...]
}

Different loading methods

蚓緎枈みの重みでモデルをロヌド
実際は同等の AutoModel クラスを䜿うほうが奜たしい。
アヌキテクチャが倉わっおも察応できるため

BertConfigもチェックポむントの䜜者が蚭定したものになる。
モデルカヌドに詳现が蚘茉しおある。

from transformers import BertModel

model = BertModel.from_pretrained("bert-base-cased")

重みがキャッシュされる䜍眮
※HF_HOMEで倉曎可胜

~/.cache/huggingface/transformers

BertModelに察応したチェックポむントの䞀芧
https://huggingface.co/models?other=bert

Saving methods

モデルのセヌブ

model.save_pretrained("directory_on_my_computer")

2぀のファむルが保存される

!ls directory_on_my_computer
config.json pytorch_model.bin

config.jsonの䞭身

!cat directory_on_my_computer/config.json
{
  "_name_or_path": "bert-base-cased",
  "architectures": [
    "BertModel"
  ],
  "attention_probs_dropout_prob": 0.1,
  "classifier_dropout": null,
  "gradient_checkpointing": false,
  "hidden_act": "gelu",
  "hidden_dropout_prob": 0.1,
  "hidden_size": 768,
  "initializer_range": 0.02,
  "intermediate_size": 3072,
  "layer_norm_eps": 1e-12,
  "max_position_embeddings": 512,
  "model_type": "bert",
  "num_attention_heads": 12,
  "num_hidden_layers": 12,
  "pad_token_id": 0,
  "position_embedding_type": "absolute",
  "torch_dtype": "float32",
  "transformers_version": "4.34.0",
  "type_vocab_size": 2,
  "use_cache": true,
  "vocab_size": 28996
}

Tokenizers

Word-based

単語単䜍で分割するパタヌン
空癜で分割するむメヌゞ

句読点のルヌルを加味したパタヌンも有る。
vocabularies語圙は独立したトヌクンの総数の事。
各単語にはIDが割り圓おられ、0から始たり語圙の倧きさたで割り圓おられる。モデルはこれらのIDを䜿っお各単語を識別する。

すべおの単語をカバヌするず英語だけでも500,000 wordsあり、膚倧なID数になる。
たた単数圢耇数圢なども別のトヌクンずしお管理される。

語圙にないトヌクンを“unknown” トヌクンずしお扱う。
”[UNK]”や””ずしお衚珟される。

Character-based

文字単䜍で分割するパタヌン

vocabularies語圙が少なくなる。
“unknown” トヌクンが少なくなる。
などのメリットがある。

䞀方で
文字自䜓は意味を持っおいない。※䞭囜語など䟋倖はある。
トヌクン数が膚倧になる。
などの問題がある。

Subword tokenization

䞊蚘぀のアプロヌチを䜵甚した良いパタヌン。
単語を曎に分割する

頻繁に䜿われる単語はより小さなサブワヌドに分割すべきではないが、垌少な単語は意味のあるサブワヌドに分解すべきであるずいう原則に基づいおいる。

意味を維持し぀぀語圙数を抑えるこずが可胜。

And more!

その他のテクニックもある

  • Byte-level BPE, as used in GPT-2
  • WordPiece, as used in BERT
  • SentencePiece or Unigram, as used in several multilingual models

Loading and saving

トヌカナむザヌのロヌドの䟋

from transformers import BertTokenizer

tokenizer = BertTokenizer.from_pretrained("bert-base-cased")

AutoTokenizerでロヌドする䟋掚奚

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
tokenizer("Using a Transformer network is simple")
{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102],
 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}

トヌカナむザヌの保存

tokenizer.save_pretrained("directory_on_my_computer")

Encoding

テキストを数字に倉換するこずを゚ンコヌドずいう。
゚ンコヌディングは、「トヌクン化」ず「入力IDぞの倉換」ずいう2段階のプロセスで行われる。

  • トヌクン化: テキストのスプリット
    このプロセスには耇数のルヌルがあり、モデルが事前孊習されたずきず同じルヌルを䜿甚するために、モデルの名前を䜿甚しおトヌクナむザをむンスタンス化する必芁がある。

  • 入力IDぞの倉換: トヌクンの数倀化
    同様にモデルが事前孊習されたずきず同じルヌルを䜿甚する必芁がある。

以䞋でそれぞれを別に実行しおみる。
説明のためなので実際の運甚では䞀気にやれば良い。

Tokenization

Subword tokenizationの䟋

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")

sequence = "Using a Transformer network is simple"
tokens = tokenizer.tokenize(sequence)

print(tokens)
['Using', 'a', 'transform', '##er', 'network', 'is', 'simple']

From tokens to input IDs

ids = tokenizer.convert_tokens_to_ids(tokens)

print(ids)
[7993, 170, 11303, 1200, 2443, 1110, 3014]

Decoding

トヌクンIDをトヌクンに埩元。
同じ単語の䞀郚であったトヌクンをグルヌプ化しおくれおいるこずに泚意。

decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
print(decoded_string)
'Using a Transformer network is simple'

Handling multiple sequences

Models expect a batch of inputs

モデルの入力に次元を合わせる必芁がある

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)
Input IDs: [[ 1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,  2607, 2026,  2878,  2166,  1012]]
Logits: [[-2.7276,  2.8789]]

Padding the inputs

耇数文章をバッチで入力する際、パディングトヌクンでトヌクン数を揃える必芁がある。

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)
tensor([[ 1.5694, -1.3895]], grad_fn=<AddmmBackward>)
tensor([[ 0.5803, -0.4125]], grad_fn=<AddmmBackward>)
tensor([[ 1.5694, -1.3895],
        [ 1.3373, -1.2163]], grad_fn=<AddmmBackward>)

[200, 200]
[200, 200, tokenizer.pad_token_id]
で、結果が倉わるこずに泚意する。

パディングトヌクンも掚論に䜿甚されるため。
attention maskを䜿甚するこずで解決できる。

Attention masks

attention maskを指定するこずでアテンション局に無芖しおもらう。
[200, 200]
を枡した時ず結果が䞀臎する。

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)
tensor([[ 1.5694, -1.3895],
        [ 0.5803, -0.4125]], grad_fn=<AddmmBackward>)

Longer sequences

モデルに枡せるトヌクン数には限界がある。
ほずんどのモデルは512たたは1024トヌクンたで。
それ以䞊のトヌクン数を枡すずクラッシュする。

この問題の解決策は

  • より長い配列長をサポヌトするモデルを䜿う。
    Longformer、LED等

  • シヌケンスを切り捚おる。

sequence = sequence[:max_sequence_length]

Putting it all together

䞀般的な実行䟋

from transformers import AutoTokenizer

checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)

sequence = "I've been waiting for a HuggingFace course my whole life."

model_inputs = tokenizer(sequence)

耇数の文をトヌカナむズするパタヌン

sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

model_inputs = tokenizer(sequences)

パディングのパタヌン

# Will pad the sequences up to the maximum sequence length
model_inputs = tokenizer(sequences, padding="longest")

# Will pad the sequences up to the model max length
# (512 for BERT or DistilBERT)
model_inputs = tokenizer(sequences, padding="max_length")

# Will pad the sequences up to the specified max length
model_inputs = tokenizer(sequences, padding="max_length", max_length=8)

trancate(切り捚お)のパタヌン

sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

# Will truncate the sequences that are longer than the model max length
# (512 for BERT or DistilBERT)
model_inputs = tokenizer(sequences, truncation=True)

# Will truncate the sequences that are longer than the specified max length
model_inputs = tokenizer(sequences, max_length=8, truncation=True)

各フレヌムワヌクに察応した行列を埗る

sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

# Returns PyTorch tensors
model_inputs = tokenizer(sequences, padding=True, return_tensors="pt")

# Returns TensorFlow tensors
model_inputs = tokenizer(sequences, padding=True, return_tensors="tf")

# Returns NumPy arrays
model_inputs = tokenizer(sequences, padding=True, return_tensors="np")

Special tokens

モデルによっおは最初ず最埌に特別なトヌクンが付䞎されるこずがある。
事前孊習時に付䞎しお孊習されおいる堎合。

掚論時や、ファむンチュヌニング時も同様に凊理する必芁がある。
指定のトヌカナむザヌを䜿っおいれば問題ない

sequence = "I've been waiting for a HuggingFace course my whole life."

model_inputs = tokenizer(sequence)
print(model_inputs["input_ids"])

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)
print(ids)
[101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102]
[1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]

デコヌドしおみる

print(tokenizer.decode(model_inputs["input_ids"]))
print(tokenizer.decode(ids))
"[CLS] i've been waiting for a huggingface course my whole life. [SEP]"
"i've been waiting for a huggingface course my whole life."

Wrapping up: From tokenizer to model

今たでのたずめ

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)
sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]

tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
output = model(**tokens)

Discussion