콘텐츠로 이동

P6-5.1 디코더 기반 누적 생성 구조로 보는 GPT 계열

Section ID: P6-5.1 Version: v2026.07.23

여기까지는 Transformer를 LLM 관점으로 다시 읽고, context window와 attention 제약을 확인했습니다. 이제 같은 Transformer 계열 안에서도 입력을 읽는 흐름계속 이어서 생성하는 흐름을 구분해야 합니다.

GPT 계열을 제품 이름으로만 읽으면, 왜 이 계열이 생성형 LLM의 대표 흐름이 되었는지 놓치기 쉽습니다. 여기서는 GPT를 디코더(decoder) 중심 생성 흐름으로 읽고, BERT처럼 입력 전체를 읽어 판단하는 흐름과 무엇이 다른지 먼저 구분합니다.

계속 이어서 생성하는 모델은 Transformer 계열 안에서 어디에 놓이는가? GPT 계열은 Transformer의 디코더(decoder)를 중심으로 이전 토큰 문맥을 보고 다음 토큰을 예측하며, 이 반복을 통해 긴 텍스트를 생성하는 흐름입니다.

디코더 기반 순차 생성 구조

순차 생성 구조는 다음 질문에서 시작합니다.

  • GPT 계열은 Transformer 안에서 어떤 위치를 가지는가?
  • 왜 GPT는 계속 이어서 생성하는 모델처럼 보이는가?
  • BERT 계열과 비교하면 무엇이 가장 크게 다른가?

GPT의 생성 구조를 먼저 잡으면, 사전학습, 다음 토큰 예측, 지시 튜닝, 정렬 문제도 같은 흐름 위에서 읽을 수 있습니다. 즉, 여기서 필요한 것은 GPT를 제품 계보보다 디코더 기반 생성 구조로 보는 기준입니다.

GPT를 제품 이름이 아니라 디코더 기반 생성 모델이라는 구조적 위치에서 읽습니다. 따라서 먼저 잡아야 할 것은 유명한 모델 이름이 아니라 왜 GPT가 순차 생성 구조로 읽히는가입니다.

지금 읽는 것 이후 넓어지는 질문
GPT가 Transformer 계열 안에서 왜 계속 이어 쓰는 생성 흐름으로 읽히는가 사전학습이 이 구조를 어떻게 키우는가
BERT와 GPT를 입력 읽기와 순차 생성 관점에서 어떻게 구분하는가 instruction tuning, alignment, 상용 모델 버전 차이가 무엇을 더 바꾸는가

이 절이 Part 6 본류 요청 흐름에서 맡는 역할은 Transformer 계산 엔진이 어떻게 계속 이어 쓰는 생성 흐름이 되는지 보여 주는 데 있습니다. 이 구조가 잡혀야 P6-6.1의 다음 토큰 예측과 P6-7.1의 사전학습을 사용자 경험으로 바로 건너뛰지 않고 계산 흐름 위에서 읽을 수 있습니다.

디코더 기반 누적 생성의 구분

  • GPT 계열을 decoder 중심 Transformer 흐름으로 설명할 수 있습니다.
  • GPT가 왜 다음 토큰 예측(next-token prediction)과 직접 연결되는지 말할 수 있습니다.
  • BERT와 GPT의 차이를 문장 전체 읽기순차 생성 관점으로 설명할 수 있습니다.
  • 이 생성 구조 위에 사용자 경험을 바꾸는 조정층이 붙을 수 있다는 점을 설명할 수 있습니다.

GPT를 생성 구조로 이해해야, 왜 사용자가 자연어로 요구를 적고 결과를 받는 경험이 가능해졌는지 설명할 수 있습니다. 다만 GPT의 순차 생성 구조와 대화형 LLM의 사용자 경험은 같은 층위가 아닙니다. GPT는 이전 토큰을 바탕으로 다음 토큰을 이어 붙이는 구조이고, 대화형 경험은 그 구조 위에 지시 따르기, 역할, 안전 제약, 인터페이스가 더해질 때 만들어집니다.

순차 생성 구조의 비교 기준

GPT를 제품 이름이 아니라 순차 생성 구조로 읽으려면 네 층위를 구분해야 합니다.

구분할 층위 확인할 기준
구조적 위치 GPT를 encoder가 아니라 decoder 중심 Transformer 흐름으로 읽을 수 있는가
생성 방식 완성 문장 출력보다 다음 토큰 반복 예측으로 설명할 수 있는가
비교 기준 BERT와의 차이를 입력 전체 읽기와 출력 이어 쓰기로 구분할 수 있는가
실제 장면 초반 선택이 뒤 생성 경로를 계속 밀어 가는지 사례에서 확인할 수 있는가

GPT는 무엇의 약자인가

GPT는 Generative Pre-Trained Transformer의 약자입니다. 이름 안에 이미 세 가지 핵심이 들어 있습니다.

  • Generative
  • Pre-Trained
  • Transformer

즉:

  • 생성(generation)을 목표로 하고
  • 먼저 큰 데이터에서 사전학습(pretraining)하며
  • Transformer 구조를 사용한다

는 뜻입니다.

왜 GPT는 생성 모델처럼 읽히나

GPT 계열은 보통 이전 토큰들을 보고 다음 토큰을 예측하는 autoregressive language model 흐름으로 설명합니다.

예를 들어 입력이 다음과 같다고 해 봅시다.

오늘 회의는 오후

모델은 여기서 다음 후보를 예측합니다.

그리고 하나를 고른 뒤, 그 새 토큰까지 포함해서 다시 다음 토큰을 예측합니다.

즉, GPT 계열의 핵심 감각은 다음과 같습니다.

한 번에 완성 문장을 꺼내는 것이 아니라, 다음 토큰을 계속 이어서 예측하며 출력을 만든다.

왜 디코더(decoder) 중심이라고 하나

Part 6 앞부분에서 본 것처럼 Transformer는 encoder, decoder, encoder-decoder 구조로 나뉘어 읽을 수 있습니다.

GPT 계열은 이 중 decoder 중심 흐름으로 보는 것이 안전합니다.

이 구조의 핵심은:

  • 이전까지의 문맥을 보고
  • 현재 위치에서 다음 토큰을 생성할 수 있도록
  • 생성 방향에 맞는 attention 제약을 둔다는 점입니다

다음처럼 이해할 수 있습니다.

GPT는 문장 전체를 한 번에 다 읽고 판단하는 모델이라기보다, 앞에서 쓴 것을 바탕으로 뒤를 계속 이어 쓰는 모델이다.

BERT와 비교하면 무엇이 다르나

다시 정리하면:

구분 BERT 계열 GPT 계열
중심 구조 encoder decoder
기본 감각 입력 전체 문맥을 읽어 표현 생성 이전 토큰을 바탕으로 다음 토큰 생성
대표 사용 흐름 분류, 검색, 임베딩 생성, 대화, 요약, 초안 작성
출력 성격 라벨, 점수, 표현 새 토큰, 새 문장, 새 문단

이 표의 핵심은 다음입니다.

BERT는 입력을 읽고 판단하는 쪽에, GPT는 출력을 계속 이어 만드는 쪽에 더 자연스럽다.

왜 GPT 계열이 사용자 경험을 크게 바꿨나

GPT 계열은 구조적으로 생성에 잘 맞습니다. 그래서 사용자는 모델에게 자연어로 요구를 적고, 모델이 그 뒤를 이어 긴 응답을 만들어 내는 경험을 하게 됩니다.

예를 들어:

  • 질문에 대한 답변 작성
  • 이메일 초안 작성
  • 문서 요약
  • 코드 자동완성
  • 역할 기반 대화

이런 경험은 모두 다음 토큰 생성의 반복으로 설명할 수 있습니다.

즉, GPT 계열은 기술적으로는 다음 토큰 예측 모델이지만, 사용자 입장에서는 문장을 계속 써 주는 인터페이스처럼 느껴집니다.

아주 단순하게 그리면

flowchart TD
  A["문맥 토큰"]
  B["다음 토큰 예측"]
  C["토큰 추가"]
  D["더 긴 문맥으로 반복"]

  A --> B
  B --> C
  C --> D

이 도식에서 확인해야 할 결과는 GPT 계열이 완성 문장을 한 번에 꺼내는 구조가 아니라, 앞 토큰을 바탕으로 다음 토큰 후보를 반복해서 이어 붙이는 생성 구조라는 점입니다.

사례 및 예시

아래 도식은 이 절의 세 사례를 생성 결과가 무엇인가보다 초기 토큰 선택이 뒤 경로를 어떻게 밀어 가는가라는 공통 질문으로 다시 묶은 것입니다.

flowchart TD
  A["같은 다음 토큰 질문"]
  B["이메일 초안 작성<br/>어떤 말투가 첫 표현에서 정해지는가?"]
  C["요약 작성<br/>어떤 첫 문장이 초점을 잡는가?"]
  D["코드 생성<br/>어떤 초반 토큰이 뒤 구조를 제한하는가?"]

  A --> B
  A --> C
  A --> D

이 도식에서 확인해야 할 점은 과업이 달라도 생성 감각이 비슷하다는 것입니다. 모두 지금 한 번 고른 토큰이나 문장이 뒤 출력의 다음 입력 일부가 된다는 구조를 가지며, 그래서 초반 선택이 뒤 경로 전체를 계속 밀어 갑니다.

사례 1. 자동완성

사용자가 회의는 내일 오후까지만 적었을 때, 모델이 문장 전체를 한 번에 떠올린다고 느끼기 쉽습니다. 하지만 실제 자동완성에서는 , , 처럼 다음 후보를 먼저 놓고, 그중 하나를 고른 뒤 다시 다음 토큰 후보를 이어 계산합니다. 즉, 모델은 완성 문장을 통째로 꺼내는 것이 아니라, 앞에 나온 토큰들을 보고 다음에 올 가능성이 큰 토큰을 차례로 이어 붙입니다.

여기서 바뀌는 점은 한 번에 완성된 문장을 기대하는 것에서 앞 선택이 뒤 문장을 계속 밀어 가는 구조를 보게 된다는 것입니다. 앞 단계에서 시간을 잘못 고르면 뒤 문장 전체도 그 시간 표현을 기준으로 이어지게 됩니다.

예를 들어 오후 세 대신 오후 네가 먼저 선택되면, 뒤의 회의실 안내나 참석 요청 문장도 그 시각을 전제로 이어질 수 있습니다. 여기서 바로잡아야 할 오해는 초반 한 단어는 나중에 쉽게 덮어써질 것이라는 감각입니다.

그래서 이 사례에서 확인해야 할 결과는 첫 몇 토큰 선택이 바뀌면 뒤 문장 전체도 그 선택을 따라 달라지는가, 그리고 초반 선택이 뒤 문장 방향을 실제로 잠가 버리는가입니다.

사례 2. 요약 초안 작성

사용자가 긴 회의록을 넣고 세 문장으로 요약해 줘라고 요청하면, 요약 전체가 먼저 정해지고 그대로 출력된다고 생각하기 쉽습니다. 하지만 내부에서는 첫 문장을 만들고, 그 문장 자체가 다시 다음 출력의 문맥 일부가 되면서 두 번째와 세 번째 문장이 이어집니다. 즉, 요약도 결국은 다음 토큰을 이어 가는 생성 구조 위에서 만들어집니다.

여기서 바뀌는 점은 요약 결과가 한 번에 결정된다는 감각보다, 첫 문장 선택이 뒤 문장 방향까지 연쇄적으로 정한다는 점을 보게 된다는 것입니다. 첫 문장이 핵심을 잘못 잡으면 뒤 문장들도 그 잘못된 초점을 이어받아 전체 요약 방향이 틀어질 수 있습니다.

예를 들어 첫 줄에서 배포 일정 확정이라고 잘못 단정하면, 실제로는 연기 논의가 중심이었던 회의도 뒤 문장들이 그 잘못된 결론을 보강하는 방향으로 이어질 수 있습니다. 여기서 바로잡아야 할 오해는 첫 문장이 조금 빗나가도 뒤에서 다시 균형을 잡아 줄 것이라는 기대입니다.

그래서 이 사례에서 확인해야 할 결과는 첫 문장 초점이 흔들리면 뒤 요약 문장들도 같은 방향으로 연쇄적으로 기울어지는가, 그리고 초반 단정이 뒤 문장들의 강조 순서까지 바꾸는가입니다.

사례 3. 코드 생성

개발자가 함수 이름, 입력 설명, 기대 동작을 주고 구현을 요청할 수 있습니다. 코드 생성도 정답 블록 하나를 통째로 꺼내는 일처럼 느끼기 쉽지만, 실제로는 함수 정의, 들여쓰기, 조건문, 반환문이 토큰 단위로 순서대로 이어집니다. 그래서 앞에서 잘못 생성한 변수명이나 조건식은 뒤 코드에도 계속 영향을 미치게 됩니다.

여기서 바뀌는 점은 한 번에 완성된 코드를 기대하는 것보다, 초반 토큰 하나가 뒤 구조 전체를 끌고 간다는 점을 먼저 보게 된다는 것입니다. 예를 들어 초반에 user_id를 잘못 잡아 두면 뒤 조회, 예외 처리, 반환문까지 같은 오류가 연쇄적으로 따라갈 수 있습니다.

괄호 하나가 어긋나도 뒤 블록 전체가 문법 오류로 무너질 수 있다는 점도 같은 구조를 보여 줍니다. 여기서 바로잡아야 할 오해는 초반 변수명이나 조건식은 사소한 선택이라는 감각입니다.

그래서 이 사례에서 확인해야 할 결과는 초반 토큰 선택 오류가 뒤 코드의 변수명, 분기, 문법까지 연쇄적으로 흔드는가, 그리고 앞 선택 하나가 뒤 여러 줄을 실제로 고정해 버리는가입니다.

세 사례를 누적 생성 관점으로 다시 묶으면 다음과 같습니다.

상황 초반 선택이 특히 크게 미는 것 뒤에서 함께 흔들리는 것
자동완성 시간·주제 같은 첫 표현 뒤 문장 전체의 전개
요약 초안 작성 첫 문장의 초점 뒤 요약 문장의 강조 순서
코드 생성 변수명, 조건식, 괄호 구조 분기, 반환, 문법 안정성

누적 생성 구조가 드러나는 장면

이 절을 읽은 뒤에는 아직 다음 토큰 예측이나 지시 튜닝 세부를 다 몰라도, 아래처럼 지금 보는 장면이 GPT의 누적 생성 구조 문제인가를 먼저 가르는 연습을 할 수 있습니다.

지금 보이는 장면 먼저 떠올리기 쉬운 오해 먼저 바꿔 물을 질문
문장 첫 표현이 바뀌자 뒤 설명 톤과 흐름도 같이 바뀐다 모델이 문장 전체를 한 번에 정해 두었다고 느끼기 쉽다 앞 토큰 선택이 뒤 후보 경로를 계속 바꾸는 구조인가
자동완성은 자연스러운데 세 문장으로 답해 줘 같은 형식은 자주 어긴다 GPT 구조만 커지면 곧바로 챗봇 경험도 해결된다고 느끼기 쉽다 지금 막히는 것은 생성 구조보다 대화형 조정층 문제인가
긴 코드 생성에서 초반 변수명 하나가 뒤 분기와 반환문까지 흔든다 앞부분 오류는 뒤에서 쉽게 덮어써질 것이라고 느끼기 쉽다 초반 토큰 선택이 뒤 코드 구조를 실제로 고정하는가

이 표에서 중요한 것은 GPT를 제품 이름으로 외우는 일이 아니라, 앞에서 생성한 것이 뒤 입력 일부가 된다는 구조를 실제 장면에 대입해 보는 일입니다.

여기서 자주 섞이는 것도 두 가지입니다.

  • GPT의 누적 생성 구조와 대화형 조정층을 같은 문제로 묶어 버리기 쉽습니다.
  • 앞 토큰 하나의 선택이 뒤 경로 전체를 얼마나 강하게 미는지 과소평가하기 쉽습니다.
  • 자동완성, 요약, 코드 생성을 서로 다른 마법처럼 보지만 실제로는 같은 순차 생성 구조 위에 있다고 놓치기 쉽습니다.

그래서 이 절의 닫힘은 GPT는 계속 이어 쓰는 생성 구조라는 말을 실제 사례 구분 기준으로 바꾸는 데 있습니다.

이 구분의 목적은 원인을 한 번에 확정하는 데 있지 않습니다. GPT가 이상하다는 한 문장으로 뭉개지 않고, 지금 보고 있는 현상이 순차 생성 구조에서 먼저 오는지, 아니면 대화형 조정층에서 먼저 오는지 짧게 가르는 데 있습니다.

연습 및 예제

이 예제의 목표는 GPT 계열 생성이 완성 문장을 한 번에 꺼내는 것이 아니라, 현재까지의 토큰열을 보고 다음 토큰 후보를 반복해서 선택하는 구조라는 점을 확인하는 것입니다. 특히 첫 번째 선택이 달라지면 뒤 후보표와 최종 문장 흐름도 함께 달라진다는 점을 직접 봅니다.

아래 코드는 시작 토큰 시퀀스, 현재 마지막 토큰에 따라 달라지는 다음 토큰 후보표, 서로 다른 첫 선택을 가진 두 개의 생성 경로를 사용합니다. 결과에서는 경로별 step별 현재 문맥, 다음 토큰 후보와 점수, 누적 점수 합, 첫 선택이 달라질 때 누적 생성 결과가 어떻게 갈라지는지를 확인합니다.

확인할 핵심은 자기회귀 생성에서는 초반 선택 하나가 이후 후보 경로와 최종 문장을 크게 갈라놓을 수 있다는 점입니다.

# GPT식 자기회귀 생성에서 첫 토큰 선택이 뒤 후보표와 최종 문장 경로를 어떻게 갈라놓는지 보는 예제입니다.
start_sequence = ["오늘", "회의는"]

next_token_scores = {
    "회의는": [("오후", 0.62), ("온라인", 0.27), ("취소", 0.11)],
    "오후": [("세", 0.55), ("네", 0.28), ("다섯", 0.17)],
    "온라인": [("으로", 0.64), ("회의실은", 0.21), ("공지합니다", 0.15)],
    "세": [("시입니다", 0.58), ("시에", 0.25), ("시부터", 0.17)],
    "으로": [("진행합니다", 0.67), ("변경되었습니다", 0.21), ("안내합니다", 0.12)],
}

paths = {
    "path_a_time_flow": ["오후", "세", "시입니다"],
    "path_b_online_flow": ["온라인", "으로", "진행합니다"],
}

def render_korean_text(tokens):
    """토큰 목록은 그대로 보되, 조사처럼 앞말에 붙는 조각은 최종 표시에서 붙입니다."""
    attached_particles = {"은", "는", "이", "가", "을", "를", "에", "에서", "으로", "로"}
    rendered = ""
    for token in tokens:
        if not rendered:
            rendered = token
        elif token in attached_particles:
            rendered += token
        else:
            rendered += " " + token
    return rendered

print("start =", start_sequence)

for path_name, chosen_tokens in paths.items():
    sequence = start_sequence[:]
    cumulative_score = 0.0
    print("=" * 80)
    print("[path]", path_name)
    for step, token in enumerate(chosen_tokens, start=1):
        current_last_token = sequence[-1]
        candidates = next_token_scores.get(current_last_token, [])
        print(f"step {step} context =", sequence)
        print(f"step {step} candidates after '{current_last_token}' =", candidates)
        chosen_score = dict(candidates)[token]
        cumulative_score += chosen_score
        sequence.append(token)
        print(f"step {step} chosen =", token)
        print(f"step {step} chosen_score =", chosen_score)
        print(f"step {step} cumulative_score =", round(cumulative_score, 2))
    print("final_sequence =", sequence)
    print("final_text =", render_korean_text(sequence))
    print("path_score_total =", round(cumulative_score, 2))

아래 출력은 로컬 .venv의 Python 실행으로 본문 코드와 같은 값을 확인했습니다.

실행 결과 예시는 다음처럼 읽을 수 있습니다.

start = ['오늘', '회의는']
================================================================================
[path] path_a_time_flow
step 1 context = ['오늘', '회의는']
step 1 candidates after '회의는' = [('오후', 0.62), ('온라인', 0.27), ('취소', 0.11)]
step 1 chosen = 오후
step 1 chosen_score = 0.62
step 1 cumulative_score = 0.62
step 2 context = ['오늘', '회의는', '오후']
step 2 candidates after '오후' = [('세', 0.55), ('네', 0.28), ('다섯', 0.17)]
step 2 chosen = 세
step 2 chosen_score = 0.55
step 2 cumulative_score = 1.17
step 3 context = ['오늘', '회의는', '오후', '세']
step 3 candidates after '세' = [('시입니다', 0.58), ('시에', 0.25), ('시부터', 0.17)]
step 3 chosen = 시입니다
step 3 chosen_score = 0.58
step 3 cumulative_score = 1.75
final_sequence = ['오늘', '회의는', '오후', '세', '시입니다']
final_text = 오늘 회의는 오후 세 시입니다
path_score_total = 1.75
================================================================================
[path] path_b_online_flow
step 1 context = ['오늘', '회의는']
step 1 candidates after '회의는' = [('오후', 0.62), ('온라인', 0.27), ('취소', 0.11)]
step 1 chosen = 온라인
step 1 chosen_score = 0.27
step 1 cumulative_score = 0.27
step 2 context = ['오늘', '회의는', '온라인']
step 2 candidates after '온라인' = [('으로', 0.64), ('회의실은', 0.21), ('공지합니다', 0.15)]
step 2 chosen = 으로
step 2 chosen_score = 0.64
step 2 cumulative_score = 0.91
step 3 context = ['오늘', '회의는', '온라인', '으로']
step 3 candidates after '으로' = [('진행합니다', 0.67), ('변경되었습니다', 0.21), ('안내합니다', 0.12)]
step 3 chosen = 진행합니다
step 3 chosen_score = 0.67
step 3 cumulative_score = 1.58
final_sequence = ['오늘', '회의는', '온라인', '으로', '진행합니다']
final_text = 오늘 회의는 온라인으로 진행합니다
path_score_total = 1.58

첫 토큰 선택 뒤 갈라지는 누적 생성 경로

그래서 이 예제에서 확인해야 할 결과는 생성이 한 번에 완성된 문장을 꺼내는 것이 아니라, 이전 출력이 다음 후보군을 바꾸며 한 토큰씩 누적된다는 점입니다. 특히 첫 번째 선택이 오후온라인이냐에 따라 두 번째 후보표부터 이미 달라지고, cumulative_score도 서로 다른 경로를 따라 쌓입니다. GPT 계열 생성은 이런 의미에서 앞선 선택이 뒤 경로와 누적 점수 흐름을 계속 밀어 가는 구조로 읽는 편이 정확합니다.

누적 생성 경로가 갈리는 지점

앞의 예제는 GPT를 구현하는 코드가 아니라, 생성이 완성 문장을 통째로 꺼내는 일이 아니라 이전 출력이 다음 입력 일부가 되는 누적 과정이라는 점을 가장 짧게 보여 주는 장면입니다. 여기서 읽어야 할 핵심은 문장 품질 이전에, 생성이 한 단계씩 이어 붙는 구조라는 점입니다.

GPT 계열은 Transformer decoder 기반 생성 모델이 실제 사용자 인터페이스를 바꾸는 흐름으로 이어졌다는 점에서 중요합니다.

역사적으로 중요한 지점은 다음과 같습니다.

  • generative pretraining이 여러 과업으로 전이될 수 있음을 보여 주었고
  • 모델 규모가 커질수록 zero-shot, few-shot 사용 경험이 강해졌으며
  • 이후 instruction tuning과 대화형 인터페이스로 이어질 기반을 만들었습니다

이 예제를 판단 기준으로 다시 줄이면 아래 세 질문이 먼저 떠올라야 합니다.

장면 먼저 답해야 하는 질문
첫 표현이 왜 뒤 문장 전체를 밀고 가는가 이전 출력이 다음 입력 일부가 되는 누적 생성 구조인가
왜 자동완성은 되는데 챗봇처럼 형식을 잘 맞추지 못하는가 생성 구조와 대화형 조정층을 분리해서 보고 있는가
왜 BERT와 GPT를 같은 Transformer라고만 묶으면 부족한가 입력 전체 읽기보다 순차 생성이 핵심인 구조 차이를 보고 있는가

체크리스트

  • GPT를 이전 출력이 다음 입력 일부가 되는 누적 생성 구조로 설명할 수 있는가?
  • BERT와 GPT를 구조와 과업 기준으로 다시 구분할 수 있는가?
  • 생성 구조와 대화형 조정층을 구분해 이어지는 설명을 읽을 준비가 되었는가?

출처와 참고 자료