LLM·트랜스포머

LLM / 9번째 글

Encoder-Decoder 사전학습: T5와 BART

2019년 같은 구조에 서로 다른 목표를 얹은 T5와 BART를 한 편에서 본다. Span Corruption과 노이즈 복원이 무엇을 바꾸는지, 디코더가 써야 하는 토큰 수를 숫자로 따라가며 정리한다.

PALDYN Team46 MIN READ

지난 글에서 GPT가 디코더만으로 다음 토큰을 이어 붙이는 방법과 In-Context Learning의 등장을 봤다. 그 앞에는 인코더만 쓰는 BERT가 있었다. 둘 다 2017년 트랜스포머에서 절반을 떼어 낸 구조다. 그러면 떼지 않고 둘 다 쓰는 모델은 무엇으로 사전학습해야 하는가. 2019년에 이 질문에 서로 다른 답을 낸 것이 Google의 T5(Text-To-Text Transfer Transformer)와 Meta(당시 Facebook AI Research)의 BART(Bidirectional and Auto-Regressive Transformers)다.

둘을 한 편에 묶는 이유가 있다. 아키텍처가 사실상 같기 때문이다. 인코더가 양방향으로 읽고 디코더가 자기 회귀로 쓰며 Cross-Attention이 둘을 잇는다 — 여기까지는 원논문 그대로다. 다른 것은 사전학습 목표 하나뿐이고, 그 하나가 두 모델이 잘하는 자리를 갈랐다. 그래서 이 글은 두 모델을 차례로 소개하는 글이 아니라 사전학습 목표를 무엇으로 고르는가를 두 사례로 따라가는 글이다.

인코더와 디코더의 사전학습

인코더·디코더 분업

Encoder-Decoder 구조에서 두 부분이 하는 일은 명확히 갈라져 있다. 인코더는 입력 시퀀스 전체를 한 번에 받아 양방향 Self-Attention을 돌린다. 각 토큰이 앞뒤를 모두 보고 자기 표현을 만든다. 디코더는 이미 쓴 토큰만 볼 수 있는 Causal Mask를 쓰면서, 매 층에서 Cross-Attention으로 인코더의 표현을 참조한다. Cross-Attention은 질의(Q)를 디코더의 상태에서, 키와 값(K, V)을 인코더의 출력에서 가져오는 어텐션이다. 이 한 겹이 두 부분의 이음매다.

이 분업이 주는 것은 읽는 방식과 쓰는 방식을 따로 정할 자유다. BERT는 읽기만 하니 출력이 시퀀스인 태스크를 못 하고, GPT는 쓰기만 하니 입력을 볼 때도 왼쪽만 본다. Encoder-Decoder는 입력을 양방향으로 읽으면서 출력은 왼쪽에서 오른쪽으로 쓴다. 입력과 출력이 둘 다 시퀀스이고 길이가 서로 다른 태스크 — 번역, 요약, 질의응답 — 가 이 구조의 자리다.

계산 배분도 다르다. 인코더는 입력을 한 번만 훑으면 되지만 디코더는 출력 토큰 수만큼 스텝을 돈다. 출력이 짧고 입력이 길면 비용의 대부분이 인코더에 몰리고, 그 반대면 디코더에 몰린다. 뒤에서 사전학습 목표의 비용을 따질 때 이 비대칭이 그대로 돌아온다.

사전학습 목표의 공백

BERT에는 MLM이 있었고 GPT에는 자기 회귀 언어 모델링이 있었다. MLM(Masked Language Modeling)은 입력 토큰 일부를 가린 뒤 그 자리에 무엇이 있었는지 맞히게 하는 목표이고, 자기 회귀 언어 모델링은 앞 토큰들만 보고 다음 토큰을 맞히게 하는 목표다. 각 구조에 맞는 사전학습 목표가 하나씩 정해져 있었다는 뜻이다. 그런데 Encoder-Decoder에는 그 자리가 비어 있었다. 2017년 원논문은 번역 데이터로 지도학습만 했고, 레이블 없는 웹 텍스트로 인코더와 디코더를 함께 학습시키는 방법은 정해진 것이 없었다.

빈칸을 채우기 어려웠던 이유는 기존 목표 둘 다 이 구조에 안 맞기 때문이다. MLM을 그대로 얹으면 인코더가 가려진 자리를 맞히고 끝나므로 디코더가 할 일이 없다. 자기 회귀 언어 모델링을 그대로 얹으면 디코더가 앞 토큰만 보고 다음 토큰을 쓰면 되므로 인코더에 줄 입력이 없다. 둘 다 쓰게 하려면 인코더에 넣는 것과 디코더가 내는 것이 달라야 한다.

그래서 답이 한 모양으로 모인다. 원본 텍스트를 어떤 식으로든 망가뜨려 인코더에 주고, 디코더가 원본을 되살리게 하는 것이다. 레이블은 필요 없다 — 망가뜨리기 전의 텍스트가 곧 정답이다. T5도 BART도 이 틀 안에 있고, 다른 것은 어떻게 망가뜨리고 무엇까지 되살리게 하는가뿐이다.

잡음 제거 오토인코더

이런 형태를 잡음 제거 오토인코더(denoising autoencoder)라 부른다. 원본 xx 에 손상 함수 gg 를 씌운 g(x)g(x) 를 입력으로 받아 xx 를 되살리도록 학습하는 모델이다. 손실은 늘 하던 토큰 단위 Cross-Entropy 하나다.

L=−∑tlog⁡P(yt∣y<t, g(x))\mathcal{L} = -\sum_{t} \log P(y_t \mid y_{<t},\, g(x))

여기서 자유도는 두 곳이다. 하나는 손상 함수 gg 를 어떻게 정할지이고, 다른 하나는 타깃 yy 를 원본 전체로 둘지 손상된 부분만으로 줄일지다. T5와 BART는 두 자리에서 각각 다른 선택을 했다.

목표를 고를 때 실제로 보는 것은 두 가지다. 첫째, 디코더가 몇 토큰을 써야 하는가 — 이게 사전학습 비용을 정한다. 둘째, 복원하려면 무엇을 알아야 하는가 — 이게 모델이 배우게 될 능력을 정한다. 손상을 세게 할수록 복원에 필요한 능력이 늘지만, 너무 세면 정보가 남지 않아 아무것도 못 배운다. 이 두 자를 들고 아래 절들을 읽으면 두 모델의 차이가 한 줄로 정리된다.

T5의 통일 인터페이스

태스크 프리픽스

T5의 출발점은 사전학습 목표가 아니라 인터페이스였다. 기존 NLP는 태스크마다 출력 형식이 달랐다. 분류는 클래스 개수만큼의 확률 분포, 회귀는 실수 하나, 번역은 토큰 시퀀스. 출력 형식이 다르면 모델 위에 붙이는 헤드가 다르고, 헤드가 다르면 손실 함수도 학습 루프도 갈라진다. 태스크를 하나 더할 때마다 코드를 한 벌 더 쓰게 된다.

T5는 이 모든 출력을 텍스트 토큰 시퀀스로 통일한다. 입력 앞에 태스크 이름을 문자열로 붙이고, 출력은 무엇이든 문자열로 받는다.

태스크 입력 출력
번역 translate English to Korean: I love AI 나는 AI를 사랑한다
감성 분류 sst2 sentence: This movie is terrible! negative
자연어 추론 mnli hypothesis: ... premise: ... entailment
문장 유사도 stsb sentence1: ... sentence2: ... 4.2
요약 summarize: [긴 기사] [한 줄 요약]

여기서 중요한 것은 프리픽스가 특수 토큰이 아니라 그냥 문자열이라는 점이다. 어휘에 새 토큰을 추가하지 않고, 임베딩 테이블도 손대지 않는다. 태스크를 하나 더한다는 것은 새 문자열을 하나 정하는 일이 전부이고, 모델 구조에는 아무 변화가 없다. 태스크 이름을 무엇으로 쓸지도 임의다 — 논문은 프리픽스 문구를 바꿔 봐도 성능이 크게 흔들리지 않는다고 보고했다.

T5: Text-To-Text Transfer Transformer

회귀와 문자열 출력

가장 어색해 보이는 자리가 회귀다. STS-B는 두 문장의 유사도를 1에서 5 사이 실수로 매기는 태스크인데, T5는 이걸 4.2 같은 문자열로 낸다. 논문은 점수를 0.2 단위로 반올림해 후보를 스물한 개로 줄이고, 그중 하나를 토큰으로 생성하게 했다. 실수 하나를 내면 될 일을 위해 토큰 셋을 순서대로 뽑는 셈이다.

이 방식에는 명백한 구멍이 있다. 모델이 레이블 집합 밖의 문자열을 뱉으면 어떻게 되는가. positive도 negative도 아닌 pisitive가 나오면 그건 틀린 것으로 센다. 논문은 파인튜닝을 마친 모델에서는 그런 출력을 관찰하지 못했다고 적었다 — 정답 형식을 몇 천 번 보고 나면 형식 자체는 모델이 금방 배운다. 사전학습만 한 모델이나 Zero-shot으로 쓰는 상황에서는 여전히 조심할 자리다.

그럼에도 이 대가를 치른 이유는, 통일이 주는 것이 코드의 간결함만이 아니기 때문이다. 출력이 전부 문자열이면 태스크 사이에 전이가 일어난다. 분류에서 배운 것이 번역에 쓰이는 경로가 헤드에 막히지 않는다. 헤드가 태스크마다 따로 있으면 공유되는 것은 본체까지이고 마지막 한 겹은 늘 새로 배워야 하는데, T5에는 그 마지막 한 겹이 없다.

통합 학습 루프

모든 태스크가 토큰 예측이므로 손실도 Cross-Entropy 하나다. 그래서 여러 태스크를 한 배치에 섞어 동시에 학습할 수 있고, 사람이 정할 것은 태스크를 어떤 비율로 섞을지뿐이다. 파인튜닝도 마찬가지라, 태스크가 바뀌어도 바뀌는 것은 프리픽스 문자열 하나다.

from transformers import T5ForConditionalGeneration, T5Tokenizer

model     = T5ForConditionalGeneration.from_pretrained('t5-base')
tokenizer = T5Tokenizer.from_pretrained('t5-base')

def t5_run(prefix: str, text: str, max_new: int = 100) -> str:
    ids = tokenizer(f"{prefix}: {text}", return_tensors='pt').input_ids
    out = model.generate(ids, max_new_tokens=max_new, num_beams=4)
    return tokenizer.decode(out[0], skip_special_tokens=True)

print(t5_run("translate English to German", "I love artificial intelligence"))
print(t5_run("summarize", "Google Brain team proposed T5 in 2019..."))

t5-base 체크포인트가 실제로 다루는 태스크는 사전학습과 함께 섞어 학습한 것들이라, 아무 프리픽스나 붙인다고 다 되지는 않는다. 위 예의 translate English to German처럼 학습에 들어간 조합이어야 쓸 만한 출력이 나온다. 한국어처럼 학습 데이터에 거의 없던 언어를 요구하면 형식만 맞고 내용이 비는 출력이 나온다 — 인터페이스가 통일되어 있다는 것과 모델이 그 태스크를 배웠다는 것은 다른 이야기다.

T5 파이프라인 구현

Span Corruption

스팬 마스킹

인터페이스를 정했으니 이제 사전학습 목표 차례다. T5가 고른 것은 Span Corruption이다. 이름 그대로 토큰 하나가 아니라 연속된 구간(span)을 지운다. 마스킹 비율은 15%, 스팬의 평균 길이는 3이다.

왜 구간인가. BERT의 MLM은 토큰 15%를 서로 독립적으로 골라 가린다. 그러면 서브워드로 쪼개진 한 단어에서 조각 하나만 가려지는 일이 흔히 생긴다. 「트랜스포머」가 트랜스와 포머로 쪼개져 있고 포머만 가려지면, 앞 조각이 그대로 남아 있으므로 문맥을 이해할 필요가 없다. 남은 철자로 맞히면 된다. 이런 예제가 배치의 상당 부분을 차지하면 학습 신호가 그만큼 묽어진다.

구간을 통째로 지우면 그 지름길이 막힌다. 사라진 자리를 채우려면 앞뒤 문맥에서 그 자리에 무엇이 와야 하는지를 세워야 하고, 여러 토큰을 서로 맞게 이어서 써야 한다. 마침 이건 디코더가 하는 일과 같은 모양이다 — 목표 하나로 인코더에는 문맥 이해를, 디코더에는 이어 쓰기를 동시에 시키는 것이다.

sentinel 토큰과 타깃 길이

지운 자리마다 sentinel 토큰을 하나씩 놓는다. sentinel은 「여기에 무언가가 있었다」를 표시하는 특수 토큰으로, T5 어휘에는 <extra_id_0>부터 시작하는 sentinel이 백 개 들어 있다. 논문 표기로는 <X>, <Y>, <Z> 순이다.

원본        : 트랜스포머는 인코더와 디코더로 이루어진 구조다
인코더 입력 : 트랜스포머는 <X> 디코더로 <Y> 구조다
디코더 타깃 : <X> 인코더와 <Y> 이루어진 <Z>

핵심은 디코더 타깃이다. 원본 전체가 아니라 sentinel과 그 자리에 있던 토큰만 번갈아 이어 붙인 것이다. 마지막 <Z>는 「여기서 끝」을 알리는 표시다. 이렇게 하면 디코더가 쓸 토큰 수가 크게 줄어든다.

숫자를 넣어 한 번 따라가 보자. 입력 길이 512, 마스킹 15%, 평균 스팬 길이 3인 경우다.

  • 가려지는 토큰: 512×0.15≈77512 \times 0.15 \approx 77 개
  • 스팬 개수: 77÷3≈2677 \div 3 \approx 26 개
  • 인코더 입력: 512−77+26=461512 - 77 + 26 = 461 개 (지운 만큼 빠지고 sentinel이 들어온다)
  • 디코더 타깃: sentinel 26개 + 원래 토큰 77개 + 끝 sentinel 1개 = 104개

원본 512와 비교하면 디코더가 쓰는 양이 5분의 1이다. 사전학습 비용의 상당 부분이 디코더 스텝에서 나오므로, 같은 GPU 시간에 훨씬 많은 예제를 볼 수 있다는 뜻이다.

sentinel 백 개라는 한도도 여기서 계산이 맞는다. 512 토큰에 스팬이 26개면 여유가 넉넉하다. 하지만 평균 스팬 길이를 1로 낮추면 스팬이 77개가 되어 한도에 바짝 붙고, 입력을 1024로 늘리면 그 설정에서 한도를 넘는다. 평균 길이 3이라는 값은 학습 신호의 질만 보고 정한 것이 아니라 이런 실무 제약과도 맞물려 있다.

Span Corruption의 길이 계산

상대 위치 인코딩

T5가 원논문에서 바꾼 또 하나가 위치 정보다. 절대 위치를 임베딩으로 더하는 대신, 토큰 사이의 거리에 따른 편향을 어텐션 점수에 직접 더한다. ii 번째 질의가 jj 번째 키를 볼 때의 점수는 eij=qi⋅kjdk+bf(i−j)e_{ij} = \frac{q_i \cdot k_j}{\sqrt{d_k}} + b_{f(i-j)} 가 되고, bb 는 학습되는 스칼라 표다. 헤드마다 따로 하나씩 둔다.

ff 는 거리를 버킷으로 묶는 함수다. 가까운 거리는 하나씩 따로 두고, 멀어질수록 여러 거리를 한 버킷에 몰아넣는다. T5 구현은 버킷을 32개 쓴다. 「바로 앞 토큰」과 「두 칸 앞 토큰」은 구별해야 하지만 「117칸 앞」과 「118칸 앞」은 구별할 이유가 없다는 판단이다. 그리고 이 편향 표는 층마다 새로 학습하지 않는다. 인코더는 인코더끼리, 디코더는 디코더끼리 첫 층에서 만든 표를 나머지 층이 그대로 나눠 쓴다.

이 방식의 이득은 길이 일반화다. 절대 위치 임베딩은 학습 때 본 최대 길이만큼만 존재하므로 그보다 긴 입력에는 줄 값이 없다. 상대 편향은 먼 거리가 한 버킷으로 묶여 있어서, 학습 때 못 본 거리도 이미 본 버킷에 떨어진다. 대가는 절대 위치를 잃는 것이다 — 「문서의 세 번째 토큰」을 직접 가리키는 능력이 구조에 없다. 대부분의 언어 태스크에서는 상대 거리가 훨씬 유용해서 남는 장사였다.

C4 코퍼스

T5 논문의 기여 절반은 데이터에 있다. C4(Colossal Clean Crawled Corpus)는 Common Crawl 웹 데이터를 규칙으로 정제해 만든 약 750GB 텍스트 코퍼스다. 한 달치 Common Crawl 원본이 20TB 남짓이니 스무 배 넘게 걸러 낸 것이다.

거른 규칙은 소박하다. 문장부호로 끝나는 줄만 남기고, 자바스크립트 안내 문구가 있는 줄을 빼고, {가 들어간 페이지나 유해어 목록에 걸리는 페이지는 통째로 버리고, 같은 세 문장 뭉치가 두 번 넘게 나오면 하나만 남기고, 영어 판별기를 통과하지 못하면 제외한다. 줄 단위로 거르는 규칙과 페이지 단위로 버리는 규칙이 섞여 있는데, 중괄호가 페이지 단위인 것은 그게 들어 있으면 문서 전체가 코드일 공산이 크기 때문이다. 각 규칙은 한 줄짜리지만 20TB에 걸면 남는 것의 성격이 완전히 달라진다.

여기서 짚어 둘 것은 T5 논문이 목표와 데이터와 크기를 함께 바꿨다는 점이다. 그래서 「T5가 BERT보다 좋다」는 비교는 어느 요인 덕인지 갈라 말하기 어렵다. 논문 자체가 이 문제를 알고 있어서, 사전학습 목표·데이터·모델 크기·파인튜닝 방식을 하나씩 바꿔 가며 비교한 실험표가 본문의 상당 부분을 차지한다. 모델 하나를 내놓는 논문이라기보다 무엇이 성능을 만드는지 체계적으로 훑은 보고서에 가깝다.

BART의 노이즈 기법

원본 전체 복원

같은 2019년, Meta는 같은 구조에 다른 목표를 얹었다. BART의 사전학습은 한 줄로 적힌다.

노이즈가 추가된 텍스트 → Encoder → Decoder → 원본 텍스트 복원

이름에 구조가 그대로 들어 있다. Bidirectional은 인코더가 BERT처럼 양방향으로 읽는다는 뜻이고, Auto-Regressive는 디코더가 GPT처럼 왼쪽에서 오른쪽으로 쓴다는 뜻이다. 어휘는 GPT-2와 같은 BPE를 쓴다.

T5와 결정적으로 갈리는 자리는 디코더가 원본 전체를 쓴다는 점이다. 지워진 부분만이 아니라 문서를 통째로 다시 쓴다. 이 선택은 비용을 늘리지만 대신 손상 함수를 자유롭게 만든다. 타깃이 항상 원본 전체이므로, 어떻게 망가뜨리든 정답의 모양은 바뀌지 않는다. 토큰을 지워도, 문장을 섞어도, 문서를 통째로 회전시켜도 디코더가 내야 할 것은 그대로 원본이다.

T5의 Span Corruption에서는 이게 안 된다. 타깃이 「sentinel과 그 자리의 토큰」이라는 형식에 묶여 있어서, 문장 순서를 섞는 식의 손상은 애초에 타깃으로 표현할 방법이 없다. 타깃을 줄인 대가로 손상의 종류가 제한된 것이다. BART가 다섯 가지를 나란히 시험해 볼 수 있었던 것은 이 자유 덕이다.

기법별 요구 능력

기법 손상 복원에 필요한 것
Token Masking 토큰을 [MASK]로 교체 가려진 자리의 내용
Token Deletion 토큰을 그냥 삭제 빠진 위치까지 찾기
Text Infilling 구간을 단일 [MASK] 하나로 교체 빠진 구간의 길이 예측
Sentence Permutation 문장 순서를 무작위로 섞기 문서의 논리 구조
Document Rotation 임의 토큰을 문서 시작점으로 회전 문서가 어디서 시작하는지

셋째 열이 이 표의 핵심이다. 같은 「빈칸 채우기」처럼 보여도 요구하는 능력이 층층이 다르다.

Token Masking은 BERT의 MLM과 같다. [MASK]가 자리에 남아 있으므로 몇 개가 사라졌는지 세면 안다. 가장 쉬운 손상이다.

Token Deletion은 자리 표시를 없앤다. 「나는 좋아한다」를 보고 「나는 AI를 좋아한다」를 만들려면, 무엇이 빠졌는지 이전에 어디가 빠졌는지를 먼저 정해야 한다. 사람이 보면 사소한 차이 같지만 모델에는 결정 하나가 더 붙는 것이다.

Text Infilling은 구간을 [MASK] 하나로 바꾼다. 세 토큰이 사라졌든 한 토큰이 사라졌든 남는 표시는 하나뿐이니, 길이를 문맥으로 추정해야 한다. 구간 길이는 포아송 분포에서 뽑고 평균은 3이다 — T5가 정한 평균 스팬 길이와 같은 값이다. 여기에 길이 0인 구간도 섞는다. 지운 것이 없는데 [MASK]만 끼워 넣는 경우이고, 모델은 「여기는 아무것도 넣지 말라」는 답도 낼 줄 알아야 한다.

Sentence Permutation은 문장 단위로 순서를 섞는다. 토큰 수준이 아니라 문서 수준의 손상이고, 되돌리려면 문장 사이의 논리적 순서를 이해해야 한다. Document Rotation은 임의의 토큰을 골라 그 지점이 문서의 시작이 되도록 돌린다. 원래 시작점을 찾아내는 것이 과제다.

BART 사전학습 노이즈 기법

최적 노이즈 조합

다섯을 다 시험해 본 결과, Text Infilling이 가장 일관되게 좋았다. 그리고 요약 태스크에서는 여기에 Sentence Permutation을 함께 쓴 조합이 최고 성능을 냈다. 최종 BART-large가 쓴 것이 이 조합이고, 설정은 문서마다 토큰 30%를 구간 단위로 지우고 문장은 전부 섞는 것이다. T5의 15%와 견주면 손상이 배로 세다.

조합이 이긴 이유는 요약이라는 태스크의 모양에 있다. 요약은 문서 전체를 읽고 중요한 것을 골라 다시 배열해 쓰는 일이다. 문장 사이의 순서 관계를 다루는 능력이 직접 필요하고, Sentence Permutation이 정확히 그 능력을 요구한다. 사전학습 과제가 파인튜닝 태스크와 같은 모양일수록 전이가 잘 된다는 것이 여기서 한 번 확인된 셈이다.

반대로 Sentence Permutation이나 Document Rotation을 단독으로 쓴 설정은 약했다. 이유를 짐작하기 어렵지 않다. 둘 다 토큰 수준에서는 아무것도 지우지 않는다. 어휘와 문법을 배울 신호가 없고 배치 순서만 다루니, 언어 모델로서 배우는 것이 얇다. 문서 구조를 다루는 능력은 토큰을 지우는 손상 위에 얹을 때 값을 한다.

여기서 T5와의 접점이 드러난다. Text Infilling과 Span Corruption은 손상의 방식이 사실상 같다 — 연속 구간을 지우고 표시 하나를 남기며, 평균 길이도 3으로 같다. 두 팀이 독립적으로 같은 자리에 도착한 것이다. 남은 차이는 지우는 비율과 디코더에게 무엇까지 쓰게 하는가 둘인데, 뒤엣것이 훨씬 크다.

두 목표의 차이

디코더 타깃 길이

차이를 숫자로 놓고 보면 분명하다. 512 토큰짜리 문서 하나를 사전학습 예제로 만들었을 때다.

T5 Span Corruption BART Text Infilling
지우는 비율 15% 30%
인코더 입력 461 토큰 409 토큰
디코더 타깃 104 토큰 512 토큰
타깃의 내용 지워진 조각만 원본 문서 전체

BART 쪽 인코더 입력은 앞서 T5에 했던 계산을 30%로 다시 돌린 값이다. 154토큰이 51개 구간으로 빠지고 그 자리에 [MASK] 51개가 들어와 409가 된다. 인코더는 어느 쪽이든 원본 512보다 짧아지고, 둘 사이의 차이도 쉰 토큰 남짓이다. 갈라지는 것은 디코더다. BART는 T5의 다섯 배를 쓴다. 같은 예산이면 T5가 훨씬 많은 문서를 볼 수 있고, 실제로 T5는 이 여유를 규모를 키우는 데 썼다.

그런데 이 비효율이 BART에서는 그대로 손해가 아니다. BART의 디코더는 매 예제에서 문서 하나를 처음부터 끝까지 이어 쓰는 연습을 한다. T5의 디코더는 빈칸을 채우는 연습만 하고, 그 빈칸은 평균 세 토큰짜리 조각이다. 요약이나 번역처럼 긴 출력을 매끄럽게 이어 쓰는 태스크로 옮겨 갈 때, 사전학습에서 그 연습을 해 둔 쪽과 안 해 둔 쪽의 출발선이 다르다.

한 줄로 정리하면 이렇다. T5는 싸게 많이 배우고, BART는 비싸게 생성에 가깝게 배운다. 앞 절에서 목표를 고를 때 보는 두 자로 「디코더가 쓰는 양」과 「복원에 필요한 능력」을 들었는데, 두 모델은 그 둘을 정확히 맞바꿨다.

요약과 번역

구분 BART T5 PEGASUS
사전학습 목표 노이즈 복원 Span Corruption 문장 추출 마스킹
아키텍처 Enc-Dec Enc-Dec Enc-Dec
디코더 타깃 원본 전체 지워진 조각 골라 지운 문장
요약 강함 무난 강함
번역 무난 강함 약함
파라미터 400M (large) 220M~11B 568M

BART가 요약에서 앞서는 것은 앞 절의 이야기 그대로다. 사전학습 자체가 「긴 입력을 읽고 긴 출력을 생성」이라 파인튜닝이 하는 일이 적다. 번역에서 T5가 앞서는 데는 두 가지가 겹친다. text-to-text 인터페이스가 번역과 애초에 같은 모양이라는 것, 그리고 T5는 11B까지 올라갔다는 것이다.

두 번째 이유를 가볍게 보면 안 된다. BART-large는 400M이고 T5-Large는 770M, T5-XXL은 11B다. 이 둘을 나란히 놓고 「BART가 졌다」고 말하면 구조를 비교한 것이 아니라 크기를 비교한 것이 된다. BART-large와 견줄 자리는 T5-Base(220M)와 T5-Large(770M) 사이다. 층 구성으로 보면 BART-base가 인코더·디코더 각 6층, BART-large가 각 12층이다.

PEGASUS와 GSG

PEGASUS(Google, 2020)는 같은 질문에 셋째 답을 냈다. GSG(Gap Sentence Generation)는 문서에서 중요한 문장을 통째로 골라 지우고, 그 문장을 생성하게 하는 목표다. 어느 문장이 중요한지는 사람이 정하지 않는다 — 각 문장을 나머지 문서와 견주어 ROUGE 점수를 재고, 높은 것을 고른다. ROUGE는 두 글이 몇 개의 단어 뭉치를 공유하는지로 겹침을 재는 요약 평가 지표다. 나머지 문서와 많이 겹치는 문장이 곧 그 문서를 대표하는 문장이라는 가정이다.

이 목표는 요약과 거의 같은 모양이다. 「문서를 읽고 그 문서를 대표하는 문장을 쓴다」가 곧 요약이기 때문이다. 그래서 PEGASUS는 파인튜닝 데이터가 적을 때 특히 강하다 — 논문은 예시 1,000건만으로 파인튜닝해도 좋은 결과가 나온다고 보고했다. 사전학습에서 이미 요약을 해 본 모델이니 파인튜닝은 형식만 맞추면 되는 것이다.

세 모델을 나란히 놓으면 규칙이 하나 보인다. 사전학습 목표를 특정 태스크에 가깝게 만들수록 그 태스크에서 강해지고 대신 범용성이 준다. PEGASUS는 요약에 붙었고 번역에서는 약하다. T5는 반대쪽 끝이라 어느 태스크에도 특별히 붙지 않은 대신 프리픽스 하나로 어디에나 간다. BART는 중간이다. 어느 쪽이 옳은 것이 아니라, 무엇을 할지 미리 알고 있는가가 답을 정한다.

구현과 모델 선택

크기 라인업

모델 파라미터 인코더/디코더 레이어 dmodeld_{model} dffd_{ff}
T5-Small 60M 6 / 6 512 2048
T5-Base 220M 12 / 12 768 3072
T5-Large 770M 24 / 24 1024 4096
T5-XL (3B) 3B 24 / 24 1024 16384
T5-XXL (11B) 11B 24 / 24 1024 65536

여기서 dmodeld_{model} 은 토큰 하나가 층 사이를 오갈 때의 벡터 길이이고, dffd_{ff} 는 각 층의 피드포워드 블록이 안에서 잠깐 펼치는 폭이다.

Small에서 Base로 갈 때 레이어가 배로 늘고, Base에서 Large로 갈 때 또 배로 는다. 눈여겨볼 것은 그다음이다. XL과 XXL은 레이어 수도 표현 벡터 길이도 Large와 똑같다 — 24층이고 dmodeld_{model} 은 1024다. 늘어난 것은 dffd_{ff} 와 어텐션 헤드 수뿐이고, 특히 XXL의 dffd_{ff} 는 Large의 열여섯 배다. 깊이를 더하지도 표현 벡터를 넓히지도 않고 피드포워드 한 겹만 부풀려 규모를 올린 것이라, 「크기를 키웠다」는 말이 어디를 키웠다는 뜻인지가 모델마다 다르다는 사실을 보여 주는 사례이기도 하다.

고르는 기준은 대체로 메모리다. 파라미터 하나가 fp32에서 4바이트이므로 11B는 파라미터만 44GB다. 파인튜닝에는 여기에 옵티마이저 상태와 기울기와 활성값이 더 붙으므로 실제로는 그 몇 배가 든다. 단일 GPU에서 XXL을 통째로 파인튜닝하는 것은 사실상 어렵고, 그래서 큰 체크포인트는 추론용으로 쓰거나 어댑터 방식의 부분 학습과 함께 쓴다. 실무의 출발점은 대개 Base이고, 생성 품질이 모자라면 Large로 한 칸 올린다.

요약 파이프라인

BART는 요약 파인튜닝 체크포인트가 잘 갖춰져 있어 그대로 쓰기 좋다.

from transformers import BartForConditionalGeneration, BartTokenizer
import torch

model = BartForConditionalGeneration.from_pretrained('facebook/bart-large-cnn')
tokenizer = BartTokenizer.from_pretrained('facebook/bart-large-cnn')

article = """
The encoder reads the whole document at once with bidirectional attention.
The decoder then writes the summary one token at a time, attending back to
the encoder states at every layer through cross-attention.
"""

inputs = tokenizer(article, return_tensors='pt', max_length=1024, truncation=True)
with torch.no_grad():
    summary_ids = model.generate(
        inputs['input_ids'],
        num_beams=4,
        max_length=130,
        min_length=30,
        length_penalty=2.0,
    )
print(tokenizer.decode(summary_ids[0], skip_special_tokens=True))

생성 인자 셋이 요약에서 특히 중요하다. min_length가 필요한 이유는 요약 모델이 짧게 끊으려는 성향을 갖기 때문이다. 빔 서치는 확률이 높은 후보를 고르는데 문장이 짧을수록 곱해지는 확률이 적어 총점이 유리해진다. 하한을 두지 않으면 한 문장 반 만에 끝나는 요약이 자주 나온다. length_penalty는 같은 문제를 다른 방식으로 다룬다 — 후보 점수를 길이의 거듭제곱으로 나누는데, 1보다 크면 긴 후보에 유리하게 기운다. 위 값 2.0은 요약을 넉넉하게 뽑겠다는 설정이다.

max_length=1024는 임의로 고른 값이 아니다. BART의 위치 임베딩이 1024개라 그보다 긴 입력은 넣을 자리가 없다. 그리고 이건 절대 위치 임베딩이라 늘릴 수도 없다 — T5의 상대 위치 편향이 학습 길이를 넘겨도 버티는 것과 정확히 대비되는 자리다. 긴 문서를 요약하려면 나눠서 처리하고 결과를 다시 합치는 식으로 밖에서 처리해야 한다.

BART 요약 파이프라인

Flan-T5와 mBART

두 계열 모두 뒤에 확장이 붙었다. Flan-T5(Google, 2022)는 T5를 1,800개 이상의 태스크로 지시 조정한 모델이다. Zero-shot 성능이 크게 올랐고, 한동안 오픈 웨이트 Seq2Seq 중 가장 쓸 만한 선택지였다.

T5에 지시 조정이 잘 붙은 데는 이유가 있다. text-to-text 프레임워크는 이미 「지시 문자열 + 입력 → 출력」 모양이다. 프리픽스를 summarize처럼 기계적인 태그에서 「이 글을 두 문장으로 요약해 주세요」 같은 자연어로 바꾼 것에 가깝고, 구조도 손실도 그대로다. 인터페이스를 통일해 둔 결정이 3년 뒤에 배당을 받은 셈이다. 코드도 체크포인트 이름만 google/flan-t5-base로 바꾸면 위의 t5_run이 그대로 돈다.

mBART(2020)는 BART를 25개 언어 코퍼스로 확장한 다국어 모델이고, 뒤이은 mBART-50이 50개 언어로 늘렸다. 한국어가 포함되어 있어 기계 번역과 다국어 요약의 베이스라인으로 쓸 만하다.

from transformers import MBartForConditionalGeneration, MBart50TokenizerFast

name = "facebook/mbart-large-50-many-to-many-mmt"
model = MBartForConditionalGeneration.from_pretrained(name)
tokenizer = MBart50TokenizerFast.from_pretrained(name)

tokenizer.src_lang = "en_XX"
inputs = tokenizer("I love AI", return_tensors="pt")
translated = model.generate(
    **inputs,
    forced_bos_token_id=tokenizer.convert_tokens_to_ids("ko_KR"),
)
print(tokenizer.decode(translated[0], skip_special_tokens=True))

여기서 눈여겨볼 것은 src_lang과 forced_bos_token_id 둘이다. 앞의 것은 입력 끝에 원본 언어 코드를 붙이게 하고, 뒤의 것은 디코더의 첫 토큰을 목표 언어 코드로 강제한다. 다대다 번역 모델은 어느 언어로 낼지를 스스로 알 방법이 없으므로, 이 인자를 빼면 모델이 아무 언어로나 낸다. 오류가 아니라 엉뚱한 언어의 문장이 나오는 형태로 틀리기 때문에 놓치기 쉬운 자리다.

선택지의 지도

Enc-only·Dec-only·Enc-Dec

구분 BERT GPT T5 BART
아키텍처 Enc-only Dec-only Enc-Dec Enc-Dec
사전학습 MLM + NSP 자기 회귀 LM Span Corruption 노이즈 복원
디코더 타깃 — 다음 토큰 지워진 조각 원본 전체
위치 정보 학습형 절대 학습형 절대 상대 편향 학습형 절대
강점 이해 생성 Seq2Seq 전반 요약·생성
통일 인터페이스 없음 부분적 있음 없음

이 표에서 읽을 것은 승패가 아니라 선택지의 지도다. 아키텍처가 목표를 제한하고, 목표가 타깃의 모양을 정하고, 타깃의 모양이 비용과 잘하는 태스크를 정한다. 사슬이 한 방향으로 흐르므로, 새 모델을 만난다면 아키텍처부터 짚어 내려가면 나머지가 대체로 따라온다.

목표 선택 기준

정리하면 물어볼 것이 셋이다.

첫째, 디코더가 필요한가. 출력이 레이블 하나면 인코더만으로 충분하고 디코더는 비용일 뿐이다. 출력이 시퀀스여야 비로소 Encoder-Decoder가 값을 한다.

둘째, 얼마나 세게 망가뜨릴 것인가. 손상이 약하면 지름길이 생겨 학습 신호가 묽어지고, 세면 복원 자체가 불가능해진다. 연속 구간을 평균 세 토큰씩 15% 지우는 설정에 두 팀이 독립적으로 도착했다는 사실이 이 자리의 답을 어느 정도 말해 준다.

셋째, 타깃을 어디까지 쓰게 할 것인가. 조각만 쓰게 하면 싸고, 전체를 쓰게 하면 생성 연습이 된다. 다운스트림에서 긴 출력을 낼 계획이면 후자 쪽에 값이 있다.

지금 대형 모델의 흐름이 Dec-only로 기울어 있는 것은 사실이다. 규모를 올리기 쉽고, 지시 조정과 In-Context Learning이 Encoder-Decoder가 맡던 태스크를 대부분 흡수했다. 다만 Encoder-Decoder가 사라진 것은 아니다. 입력이 길고 출력이 짧은 대량 처리 — 번역 서비스, 문서 요약 배치 — 에서는 인코더를 한 번만 돌리는 구조가 여전히 싸다. T5 계열 체크포인트가 검색용 임베딩과 리랭킹 모델의 바탕으로 계속 쓰이는 것도 같은 이유다.

다음 걸음

앞에서 BART의 입력이 1024 토큰에서 잘린다고 했다. 위치 임베딩 개수가 직접적인 이유였지만, 그 뒤에는 더 근본적인 벽이 있다. Self-Attention은 모든 토큰 쌍의 점수를 계산하므로 시퀀스 길이의 제곱으로 시간과 메모리를 먹는다. 길이를 512에서 8192로 열여섯 배 늘리면 어텐션 행렬의 원소는 256배가 된다. 위치 임베딩을 늘려 봤자 그 앞에서 먼저 막힌다는 뜻이다.

다음 글은 이 비용 자체를 줄이는 방법들을 다룬다. 모든 쌍 대신 일부만 보는 희소 어텐션, 계산은 그대로 두고 메모리 이동을 줄이는 구현 기법, 그리고 어텐션을 아예 다른 연산으로 바꾸려는 상태 공간 모델까지 — 길이라는 벽을 어느 방향에서 미는지가 각각 다르다.


읽어주셔서 감사합니다. 😊

LATEST

LLM·트랜스포머의 최신 글

LLM·트랜스포머2026.08.14

작은 모델을 우리 일에 맞추는 법

파인튜닝이 실제로 고치는 것은 지식이 아니라 행동입니다. 데이터 몇 건이 필요한지, LoRA가 무엇을 바꾸는지, 학습 전에 무엇을 먼저 만들어야 하는지를 정리합니다.

15 MIN
LLM·트랜스포머2026.08.13

작은 모델로 내려도 되는지 판단하는 법

비용·지연·품질은 같은 방향으로 움직이지 않습니다. 폴백을 붙였을 때의 손익분기, 격차가 벌어지는 작업 유형, 그리고 내리기 전에 통과해야 할 네 관문을 정리합니다.

10 MIN
LLM·트랜스포머2026.08.13

작은 모델이 다시 쓸 만해진 이유

10억에서 100억 파라미터 사이의 모델이 다시 실무에 들어오고 있습니다. 무엇이 달라졌는지, 메모리는 어떻게 계산하는지, 무엇을 잘하고 무엇을 못하는지 정리합니다.

10 MIN