2.4 정규화와 피드 포워드 층
✔️ 정규화: 딥러닝 모델에서 입력이 일정한 분포를 갖도록 만들어 학습이 안정적이고 빨라질 수 있도록 하는 기법
- 과거: 배치 정규화(batch normalization) - 배치 입력 데이터 사이에 정규화 진행
- 현재: 층 정규화(layer normalization) - 트랜스포머 아키텍처에서 사용하는 특정 차원에서 정규화 수행
✔️ 어텐션 연산: 입력 단어 사이의 관계를 계산해 토큰 임베딩을 조정하는 역할
✔️ 피드 포워드 층: 전체 입력 문장을 이해하는 연산(완전 연결 층, fully connected layer)
2.4.1 층 정규화 이해하기
데이터를 정규화하여 모든 입력 변수가 비슷한 범위의 분포를 갖도록 조정해 특정 변수를 과도하게 반영하는 것을 방지한다.
✔️ 입력 데이터가 딥러닝 모델의 각 층을 거치면서
- 어떤 특성은 좁은 분포를 갖는다 (데이터 값이 특정 범위에 집중됨)
- 어떤 특성은 넓은 분포를 갖는다 (데이터 값이 넓게 퍼짐)
✔️ 층이 깊어진다 = 은닉층 개수가 늘어난다.
층이 깊은 모델에서는 분포의 차이가 발생할 가능성이 높아지는데 학습이 잘 되지 않는다.
➡️ 층과 층 사이에 정규화를 추가해 학습을 안정적으롤 만든다.
📍 정규화: (x - 평균) / 표준편차
데이터를 어떻게 묶는지에 따라 배치 정규화와 층 정규화로 나뉜다.
- 배치 정규화: 이미지 처리
- 층 정규화: 자연어 처리
배치 정규화
모델에 입력으로 들어가는 미니 배치 사이에 정규화 수행
➡️ 자연어 처리에서는 입력으로 들어가는 문장의 길이가 다양하므로 배치 정규화 사용 시 정규화에 포함된 데이터 수가 달라 정규화 효과를 보장할 수 없다.
e.g. 패딩 토큰이고 1개의 실제 데이터만으로 정규화하는 경우도 있으므로, 이런 경우 정규화의 효과가 없다.
층 정규화
각 토큰 임베딩의 평균과 표준편차를 구해 정규화 수행
➡️ 토큰 임베딩별로 정규화를 수행하므로 문장별로 실제 데이터가 달라도 정규화 효과에 차이가 없다.
사전 정규화 vs 사후 정규화
- 사후 정규화(post-norm)
- 어텐션과 피드 포워드 층 이후에 층 정규화 적용
- 사전 정규화(pre-norm)
- 층 정규화 적용 후 어텐션과 피드 포워드 층 통과
- 학습이 더 안정적
- 파이토치가 제공하는 LayerNorm 클래스로 층 정규화 레이어 만든다.
- 토큰 임베딩 차원을 인자로 전달
- 입력 임베딩을 층 정규화 레이어에 통과시켜 정규화된 임베딩으로 만든다.
2.4.2 피드 포워드 층
데이터의 특징을 학습하는 완전 연결 층(fully connected layer)
- 멀티 헤드 어텐션: 단어 사이의 관계 파악
- 피드 포워드 층: 입력 테스트 전체를 이해하는 역할
- 선형 층, 드롭아웃 층, 층 정규화, 활성 함수로 구성
- 임베딩 차원을 동일하게 유지해야 쉽게 층을 쌓아 확장이 가능하므로 입력과 출력의 형태가 동일하도록 맞춤
- 일반적으로 d_model 차원에서 d_model보다 2~3배 큰 dim_feedforward 차원으로 확장했다가 다시 d_model로 변환
class PreLayerNormFeedForward(nn.Module):
def __init__(self, d_model, dim_feedforward, dropout):
super().__init__()
self.linear1 = nn.Linear(d_model, dim_feedforward) # 선형 층 1
self.linear2 = nn.Linear(dim_feedforward, d_model) # 선형 층 2
self.dropout1 = nn.Dropout(dropout) # 드롭아웃 층 1 (과적합 방지)
self.dropout2 = nn.Dropout(dropout) # 드롭아웃 층 2
self.activation = nn.GELU() # 활성 함수(트렌스포머 모델에서 많이 사용)
self.norm = nn.LayerNorm(d_model) # 층 정규화
def forward(self, src): # src: 입력 텐서 크기
x = self.norm(src) # 입력 먼저 정규화
x = x+self.linear2(self.dropout1(self.activation(self.linear1(x))))
x = self.dropout2(x)
return x
2.5 인코더
멀티 헤드 어텐션, 층 정규화, 피드 포워드 층이 반복되는 형태
- 잔차 연결(residual connection): 안정적인 학습을 돕는다.
- 화살표 모양 그대로 입력을 다시 더해주는 형태로 구현
- 블록이 N_e번 반복
- 트랜스포머 인코더는 인코더 블록을 반복해 쌓아 만든다
- 인코더 층 코드로 구현
- __init__ 메서드에서 멀티 헤드 어텐션 층, 층 정규화, 드롭아웃, 피드 포워드 층을 불러옴
- 입력인 src를 self.norm1을 통해 정규화
- 멀티 헤드 어텐션 클래스를 인스턴스화한 self.attn으로 멀티 헤드 어텐션 연산 수행
- 잔차 연결을 위해 어텐션 결과에 드롭아웃 취한 self.dropout1(attn_output)과 입력(src) 더하기
- self.feed_forward(x)로 피드 포워드 연산 취하기
2.6 디코더
- 마스크 멀티 헤드 어텐션 사용
- 인과적(causal), 자기 회귀적(auto-regressive)특징
- 디코더는 생성을 담당하므로 트랜스포머 모델도 앞에서 생성한 토큰을 기반으로 다음 토큰 생성
- 실제 텍스트 생성 시 디코더는 이전까지 생성한 텍스트만 확인할 수 있는데, 인코더와 디코더는 모두 완성된 텍스트를 입력으로 받는다.
- ❓ 어텐션 사용하는 경우 미래 시점에 작성해야 하는데 텍스트를 미리 확인하게 되는 문제 발생
- ❗️ 특정 시점에는 그 이전에 생성된 토큰까지만 확인할 수 있도록 마스크 추가(미래 시점의 토큰 제거)
✔️ 크로스 어텐션(cross attention)
: 인코더의 결과를 디코더가 활용하는 연산
e.g. 영어 -> 한국어 번역 시 인코더가 영어 문장을 입력으로 받아 처리한 결과를 번역한 한국어를 생성하는 디코더가 받아 활용
2.7 BERT, GPT, T5 등 트랜스포머를 활용한 아키텍처
- 인코더만 활용: 자연어 이해(Natural Language Understanding, NLU) 집중
- 디코더만 활용: 자연어 생성(Natural Language Generation, NLG) 집중
- 인코더와 디코더 모두 활용해 더 넓은 범위의 작업 수행
2.8 주요 사전 학습 메커니즘
디코더 모델을 학습시키는 인과적 언어 모델링과 인코더 모델을 학습시키는 마스크 언어 모델링 이해
2.8.1 인과적 언어 모델링
문장의 시작부터 끝까지 순차적으로 단어를 예측하는 방식
안녕 뒤에 하세요가 나올 확률과 다른 단어가 나올 확률 중 높은 확률이 뒤에 나오게 되는데, 하세요가 확률이 훨씬 크므로 안녕 뒤에는 하세요가 나온다. 언어 모델도 인과적 언어 모델링 방식으로 많은 데이터에 대해 다음 단어를 예측하는 방법을 학습해 더 가능성 있는(확신이 있는) 단어를 생성한다.
➡️ GPT 같은 트랜스포머 모델에서는 인과적 언어 모델링이 핵심적이다.
2.8.2 마스크 언어 모델링
중간 중간 빠진 단어를 채울 수 있게 된다.
- 입력 단어의 일부를 마스크 처리해 해당 단어를 맞추는 작업으로 모델 학습
- 언어 모델은 마스크 처리된 토큰을 맞춰야 한다.
- 인과적 언어 모델링은 단방향 예측이라 다음 단어 예측이라는 목표가 자연스럽게 정해져 생성한 문맥만 활용 가능
- 양방향 방식은 새로운 작업 목표가 필요해 토큰 사이에 일부를 마스크 처리해 맞추는 방식을 사용한다. (앞뒤 문맥 모두 사용 가능)
최근의 LLM은 디코더만을 사용한 GPT 모델 그룹이고 인과적 학습 모델링 통해 학습한다.
'AI > LLM을 활용한 실전 AI 애플리케이션 개발' 카테고리의 다른 글
[LLM의 기초 뼈대 세우기] 01 - LLM 지도 : 언어 모델이 챗GPT가 되기까지 | LLM 애플리케이션의 시대가 열리다 (0) | 2025.02.12 |
---|---|
[LLM의 기초 뼈대 세우기] 01 - LLM 지도 : 딥러닝과 언어 모델링 (0) | 2025.02.11 |
[INTRO] (0) | 2025.02.11 |