Ch2. Using Transformers
Last updated
Was this helpful?
Last updated
Was this helpful?
How to use tokenizers and models to replicate the pipeline API's behavior
How to load and save models and tokenizers
Different tokenization approaches, such as word-based, character-based, and subword-based
How to handle multiple sentences of varying lengths
Transformer library를 통해 쉽고, 유동적인 사용을 할 수 있도록 하고자 하였다.
이번 챕터를 통해 model과 tokenizer의 사용에 대해 시작부터 끝까지 배워보는 시간을 가진다.
학습과정은 위와 같이 주어진 Raw Text를 Tokenizer로 가공하고 Model 학습을 진행하고 예측결과를 출력하는 단계로 이루어져 있다. 이 과정에 대해 살펴보도록 하자.
학습 모델은 주어진 문장을 가지고 바로 학습을 진행할 수 있다. 그렇기에 우선 raw text를 token화 하는 작업을 진행하게 되는데 이를 Tokenization 이라고 한다.
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
Pre-Trained Model과 동일한 Preprocessing 과정을 거쳐야하므로 Model Hub에서 해당 정보를 다운로드 받아야한다. 아래 예시 코드를 참고하자.
from transformers import AutoTokenizer
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
tokenizer = AutoTokenizer.from_pretrained(checkpoint)
AutoTokenizer
클래스의 from_pretrained
함수를 사용하여 해당 pipeline의 토큰화 방법을 가져온다.
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]
])
}
만들어진 tokenizer를 통해 raw text를 손쉽게 토큰화 할 수 있다.
Tokenizer와 마찬가지로 pre-trained model을 가져올 수 있다.
from transformers import AutoModel
checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
model = AutoModel.from_pretrained(checkpoint)
Batch Size the number of sequence processed at a time
Sequence length The length of the numerical representation of the sequence
Hidden size The Vector dimension of each model input
outputs = model(**inputs)
print(outputs.last_hidden_state.shape)
torch.Size([2, 16, 768])
보통 마지막 hidden size 값으로 인해 매우 큰 차원의 값이 생성된다.
[잘 이해 하지 못함]
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])
output data에 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=<SoftmaxBackward>)
model.config.id2label
{0: 'NEGATIVE', 1: 'POSITIVE'}
즉, 첫문장의 'NEGATIVE': 0.041, 'POSITIVE': 0.9598 의 확률로 labels의 값을 나타내게 됨을 확인할 수 있다.
모델의 생성과 사용법에 대해 좀 더 자세히 알아보자. AutoModel
클래스를 사용하여
Configuration 객체를 호출하여 BERT 모델을 초기화한다.
from transformers import BertConfig, BertModel
# Building the config
config = BertConfig()
# Building the model from the config
model = BertModel(config)
BertConfig {
"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",
"transformers_version": "4.10.2",
"type_vocab_size": 2,
"use_cache": true,
"vocab_size": 30522
}
config에는 위와 같이 parameter에 대한 정보가 담겨져 있다.
3.1. 과 같이 모델을 호출하게되면 임의로 초기화 된 모델이 생성이 된다. 하지만 우리는 Pre-Trained Model을 사용하여 학습 성능향상과 시간 단축을 할 수 있다.
from transformers import BertModel
model = BertModel.from_pretrained("bert-base-cased")
"bert-base-cased"의 weights로 초기화 된다.
model.save_pretrained("directory_on_my_computer")
config.json attributes necessary to build the model architecture contains metadata such as where the checkpoint originated
pytorch_model.bin state dictionary cotains all your model's weights
Tokenizer Encoding
Make it to Tensor
Get output by call the model with the inputs
토큰화하는 방식도 Boostcamp를 통해 배운 바 있다. 간략하게 사용법에 대해 익히도록 한다.
Word-based
각 단어별로 나누는 방식
"Jim Henson was a puppeteer" => [Jim, Henson, was, a, puppeteer]
Unknown Token([UNK])이 많아지게 된다.
Charater-based
알바벳 단위로 나누는 방식
Jim Henson was a puppeteer => [J, i, m, ..., t, e, e, r]
단어로서의 의미가 사라지게 되는 문제가 있다. (중국어는 한 자마다 의미가 있어 좋은 성능을 낼 수 있다.)
Subword-Tokenization
단어를 의미단위로 나누는 방식
Let's do tokenization! => [Let's, do, token, ization, !]
이 방법으로 [UNK]를 줄일 수도 있고, 단어로써의 의미도 보존할 수 있다.
그 외에도 Byte-level BPE(GPT-2), WordPiece(BERT), SentencePiece or Unigram 등 기법이 존재한다.
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]}
# Saving Tokenizer
tokenizer.save_pretrained("directory_on_my_computer")
STEP1) split the text into tokens
STEP2) adds special tokens the model expects
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']
Convert tokens to integer
ids = tokenizer.convert_tokens_to_ids(tokens)
print(ids)
[7993, 170, 11303, 1200, 2443, 1110, 3014]
decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
print(decoded_string)
'Using a Transformer network is simple'
앞서배운 내용으로 코드를 작성하고 실행하게 되면 오류가 발생한다.
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)
그 이유는 model의 parameter로 input_ids 즉 list가 들어와야 하기 때문이다.
input_ids = torch.tensor([ids])
위에서는 입력문장이 1개여서 변환이 필요했지만, 여러 문장을 가지고 생각하면 더 간단하게 느껴질 수 있다. 모델이 처리할 문장을 batch에 담아서 input parameter로 전달한다고 생각하면 된다.
batched_ids = [ids, ids]
입력값들의 size를 동일하게 맞춰주기 위한 padding 작업이다.
batched_ids = [
[200, 200, 200],
[200, 200]
]
##############################
padding_id = 100
batched_ids = [
[200, 200, 200],
[200, 200, padding_id]
]
실제로는 tokenizer.pad_token_id
를 가지고 사용한다.
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>)
여기서 참고할만한 내용은 두번째 입력값에 대한 마지막 출력값이 다르게 나오는데 이는 문맥의 흐름에 대한 가중치가 포함되기 때문이다. 이를 만약 같은 값을 추출하고 싶다면 attention mask 를 사용하면 된다.
[Mask] Token을 사용하여 학습에 영향을 주지않도록 제외 시킨다.
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)
너무 문장의 길이가 긴 경우, 긴 문장을 감당할 수 있는 모델을 사용하거나 최고 길이 만큼 잘라서 사용하기도 한다.
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)
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")
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)
print(tokenizer.decode(model_inputs["input_ids"]))
print(tokenizer.decode(ids))
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)