에코프로.AI

[Hugging Face - 6] Handling multiple sequences(여러 시퀀스 처리하기) 본문

AI Tutorial

[Hugging Face - 6] Handling multiple sequences(여러 시퀀스 처리하기)

AI_HitchHiker 2025. 1. 1. 14:43

https://www.youtube.com/watch?v=M6adb1j2jPI


이전 섹션에서는 가장 간단한 사용 사례인 짧은 길이의 단일 시퀀스에 대해 추론을 수행하는 방법을 살펴보았습니다. 하지만 이미 몇 가지 의문이 생겼습니다:

  • 여러 개의 시퀀스를 어떻게 처리하나요?
  • 길이가 다른 여러 시퀀스를 어떻게 처리하나요 ?
  • 어휘 색인이 모델이 잘 작동하는 데 필요한 유일한 입력일까요?
  • 시퀀스가 너무 길다는 게 있을까?

이러한 질문이 어떤 종류의 문제를 제기하는지 살펴보고, 🤗 Transformers API를 사용하여 이러한 문제를 어떻게 해결할 수 있는지 알아보겠습니다.

 

모델은 일괄 입력을 예상합니다.

이전 연습에서 시퀀스가 ​​숫자 목록으로 변환되는 방식을 살펴보았습니다. 이 숫자 목록을 텐서로 변환하여 모델로 보내 보겠습니다.

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)

문제는 우리가 모델에 단일 시퀀스를 전송한 반면, 🤗 트랜스포머 모델은 기본적으로 여러 문장을 기대한다는 것입니다.

 

여기서는 토큰라이저를 시퀀스에 적용할 때 토큰라이저가 하는 모든 작업을 백그라운드에서 수행하려고 했습니다. 하지만 자세히 살펴보면 토큰화 도구가 입력 ID 목록을 텐서로 변환한 것이 아니라 그 위에 차원을 추가했다는 것을 알 수 있습니다:

tokenized_inputs = tokenizer(sequence, return_tensors="pt")
print(tokenized_inputs["input_ids"])
tensor([[  101,  1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,
          2607,  2026,  2878,  2166,  1012,   102]])

vscode 실행화면

 

다시 시도하여 새로운 차원을 추가해 보겠습니다:

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)

 

입력 ID와 결과 로그를 인쇄합니다. 출력은 다음과 같습니다:

Input  IDs :  tensor([[ 1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,  2607,
          2026,  2878,  2166,  1012]])
Logits :  tensor([[-2.7276,  2.8789]], grad_fn=<AddmmBackward0>)

vscode 실행화면

 

일괄 처리는 모델을 통해 여러 문장을 한 번에 전송하는 작업입니다. 문장이 하나만 있는 경우 단일 시퀀스로 일괄 처리를 구축하면 됩니다:

batched_ids = [ids, 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)

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

tokens = tokenizer.tokenize(sequence)
ids = tokenizer.convert_tokens_to_ids(tokens)

batched_ids = torch.tensor([ids, ids])
print('batched_ids : ', batched_ids)

output = model(batched_ids)
print('Logits : ', output.logits)
batched_ids :  tensor([[ 1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,  2607,
          2026,  2878,  2166,  1012],
        [ 1045,  1005,  2310,  2042,  3403,  2005,  1037, 17662, 12172,  2607,
          2026,  2878,  2166,  1012]])
Logits :  tensor([[-2.7276,  2.8789],
        [-2.7276,  2.8789]], grad_fn=<AddmmBackward0>

vscode 실행화면

 

 

일괄 처리를 사용하면 모델에 여러 문장을 입력할 때 모델이 작동할 수 있습니다. 여러 개의 시퀀스를 사용하는 것은 단

일 시퀀스로 배치를 구축하는 것만큼이나 간단합니다. 하지만 두 번째 문제가 있습니다. 두 개 이상의 문장을 일괄 처리하려고 할 때 문장의 길이가 다를 수 있습니다. 텐서를 사용해 본 적이 있다면 텐서는 직사각형 모양이어야 하므로 입력 ID 목록을 텐서로 직접 변환할 수 없다는 것을 알고 있을 것입니다. 이 문제를 해결하기 위해 보통 입력을 패딩합니다.

 

Padding the inputs (입력값 채우기)

다음 목록은 텐서로 변환할 수 없습니다:

batched_ids = [
    [200, 200, 200],
    [200, 200]
]

 

이 문제를 해결하기 위해 패딩을 사용해 텐서를 직사각형 모양으로 만들겠습니다. 패딩은 값이 적은 문장에 패딩 토큰이라는 특수 단어를 추가하여 모든 문장의 길이를 동일하게 만듭니다. 예를 들어 단어가 10개인 문장이 10개, 단어가 20개인 문장이 1개인 경우 패딩을 사용하면 모든 문장의 단어가 20개가 되도록 합니다. 이 예제에서 결과 텐서는 다음과 같습니다:

padding_id = 100

batched_ids = [
    [200, 200, 200],
    [200, 200, padding_id],
]

 

패딩 토큰 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)
We strongly recommend passing in an `attention_mask` since your input_ids may be padded. See https://huggingface.co/docs/transformers/troubleshooting#incorrect-output-when-padding-tokens-arent-masked.
tensor([[ 1.5694, -1.3895]], grad_fn=<AddmmBackward0>)
tensor([[ 0.5803, -0.4125]], grad_fn=<AddmmBackward0>)
tensor([[ 1.5694, -1.3895],
        [ 1.3374, -1.2163]], grad_fn=<AddmmBackward0>)

vscode 실행화면

세 번째 일괄 예측의 로그에 문제가 있습니다. 세 번째의 두 번째 행 [ 1.3374, -1.2163] 은 두 번째 예측의 로그 [ 0.5803, -0.4125] 와 같아야 하지만 완전히 다른 값이 있습니다!

 

이는 트랜스포머 모델의 핵심 기능이 각 토큰을 컨텍스트화하는 attention 레이어이기 때문입니다. 이는 시퀀스의 모든 토큰에 관심을 기울이기 때문에 패딩 토큰을 고려합니다. 길이가 다른 개별 문장을 모델에 전달할 때나 동일한 문장과 패딩이 적용된 배치를 전달할 때 동일한 결과를 얻으려면 해당 attention 계층에 패딩 토큰을 무시하도록 지시해야 합니다. 이 작업은 attention mask 를 사용하여 수행됩니다.

 

Attention masks (주의 마스크)

attention mask 는 입력 ID 텐서와 정확히 동일한 모양의 텐서로, 0과 1로 채워져 있습니다. 1은 해당 토큰이 어텐션되어야 함을 나타내고 0은 해당 토큰이 어텐션되지 않아야 함을 나타냅니다(즉, 0은 모델의 어텐션 계층에서 무시되어야 함).

 

attention mask  를 사용하여 이전 예제를 완성해 보겠습니다:

batche_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=<AddmmBackward0>)

vscode 실행화면

※ 이제 배치의 두 번째 문장에 대해 동일한 로그를 얻습니다.

두 번째 시퀀스의 마지막 값이 padding ID이고,attention mask 에서는 0 값인 점에 주목하세요.

 

Longer sequences (더 긴 시퀀스)

트랜스포머 모델에서는 모델에 전달할 수 있는 시퀀스 길이에 제한이 있습니다. 대부분의 모델은 최대 512개 또는 1024개 토큰의 시퀀스를 처리하며, 그보다 긴 시퀀스를 처리하도록 요청하면 충돌이 발생합니다. 이 문제에 대한 두 가지 해결책이 있습니다:
  • 지원되는 시퀀스 길이가 더 긴 모델을 사용합니다. 
  • 시퀀스를 잘라냅니다.
모델마다 지원되는 시퀀스 길이가 다르고, 일부는 매우 긴 시퀀스를 처리하는 데 특화되어 있습니다. Longformer 가 한 예이고, 또 다른 예는 LED 입니다 . 매우 긴 시퀀스가 ​​필요한 작업을 진행 중이라면 해당 모델을 살펴보는 것이 좋습니다.
그렇지 않으면 max_sequence_length 매개변수를 지정하여 시퀀스를 잘라내는 것이 좋습니다:
max_sequence_length = 512  # 512 or 1024
sequence = sequence[:max_sequence_length]
 

 

참고사이트 : https://huggingface.co/learn/nlp-course/chapter2/5?fw=pt

 

끝~