Develop/AI

전이학습을 알아보자.

YOOZI. 2025. 3. 15. 18:35
728x90
언어 모델의 진화와 혁신

 

 

 

오늘은 자연어처리의 전이학습에 대해 알아보자.

오늘의 배움
  • 자연어처리의 전이학습 개념
  • 문맥 기반 임베딩의 발전과정
  • Transformer 아키텍처의 핵심 구성요소

1. 전이학습이란?

 

  • 정의: 하나의 문제를 해결하기 위해 학습한 지식을 다른 관련 문제 해결에 적용하는 기계학습 방법이다.
  • 한 줄 요약: "배운 지식을 새로운 과제에 재활용하는 효율적인 학습 방식이다."
  • 특징:
    • 사전 훈련된 모델의 가중치를 활용한다
    • 소량의 데이터로도 좋은 성능을 낸다
    • 학습 시간과 자원을 절약한다
  • 필요성: 모든 NLP 작업마다 처음부터 학습하기에는 비용과 시간이 많이 소요된다.
  • 장점/단점:
    • 장점: 학습 효율성 증가, 적은 데이터로도 높은 성능
    • 단점: 도메인 차이에 따른 성능 저하 가능성, 모델 크기가 커질수록 자원 소모 증가
  • 예시:
    • 중국어를 배우는 학생이 이미 알고 있는 한자 지식을 활용하는 것과 같다. 기존에 알던 한자 지식(사전 학습된 지식)을 새로운 언어 학습(다운스트림 태스크)에 적용하는 것이다.
    • BERT로 대규모 코퍼스에서 사전 학습 후, 감정 분석 태스크에 미세 조정하는 방식

 

2. 핵심 개념 정리

2-1. ELMo - 문맥 기반 임베딩의 시작

 

  • 정의: Embeddings from Language Models의 약자로, 문맥에 따라 단어의 의미를 다르게 표현하는 딥러닝 모델이다.
  • 작동 원리:
    1. 양방향 LSTM(BiLSTM)을 사용하여 문맥 정보를 캡처한다
    2. 문자 수준의 CNN을 사용해 단어 내부 구조를 파악한다
    3. 각 층의 hidden state를 결합하여 최종 임베딩을 생성한다
  • 내부 구조 심층 분석:
    • 입력층: 문자 수준 CNN으로 단어 표현을 생성한다
    • 중간층: 양방향 LSTM 여러 층을 쌓아 문맥 처리한다
    • 출력층: 각 층의 출력을 가중합하여 최종 임베딩을 만든다
# ELMo 모델 사용 예시
from allennlp.modules.elmo import Elmo, batch_to_ids

# 설정 및 가중치 파일 경로
options_file = "elmo_options.json"
weight_file = "elmo_weights.hdf5"

# ELMo 모델 초기화 (2개 층 사용)
elmo = Elmo(options_file, weight_file, num_output_representations=2, dropout=0)

# 입력 문장
sentences = [["I", "love", "NLP"], ["ELMo", "is", "context", "dependent"]]
character_ids = batch_to_ids(sentences)

# ELMo 임베딩 계산
embeddings = elmo(character_ids)

# 출력: 각 토큰의 ELMo 표현 (batch_size, sequence_length, embedding_dim)
elmo_embeddings = embeddings['elmo_representations'][0]
print(elmo_embeddings.shape)

 

 

 

  • 특징:
    • 계층적 구조: 각 층이 다른 유형의 언어적 특성을 포착한다
    • 하위 층: 구문적 정보 (문법, 품사 등)
    • 상위 층: 의미적 정보 (단어 의미, 문맥적 관계)
  • 장점/단점:
    • 장점: 동음이의어 처리 가능, 문맥에 따른 단어 의미 구분
    • 단점: RNN 기반이라 병렬 처리에 한계, 계산 비용이 높음

 

2-2. Transformer - 주의집중 메커니즘의 혁신

 

  • 정의: 순환 신경망 없이 자기 주의 메커니즘만으로 시퀀스 데이터를 처리하는 신경망 구조이다.
  • 작동 원리:
    1. 단어를 임베딩하고 위치 정보를 추가한다
    2. 자기 주의(Self-Attention) 메커니즘으로 단어 간 관계를 계산한다
    3. 여러 개의 주의 헤드로 다양한 측면에서 관계를 분석한다
    4. 인코더는 문맥을 이해하고, 디코더는 출력을 생성한다
  • 핵심 구성요소 상세 분석: :
    1. 어텐션 메커니즘 심층 분석
      • 쿼리(Q), 키(K), 값(V) 개념:
        • 쿼리: "무엇을 알고 싶은가?"를 나타낸다
        • 키: "어떤 정보가 있는가?"를 나타낸다
        • 값: "실제 정보 내용"을 담고 있다
    2. 멀티헤드 어텐션:
    • 여러 개의 어텐션 헤드를 병렬로 실행하여 다양한 관점에서 정보를 추출한다
    3. 포지셔널 인코딩:
    • 단어의 위치 정보를 모델에 주입하는 방법
    • 사인-코사인 함수 사용
    4. 인코더-디코더 구조:
    • 인코더: 입력 시퀀스를 처리하여 표현 벡터 생성
      • 셀프 어텐션으로 각 단어가 다른 단어들과 어떻게 관련되는지 파악
      • 정규화(LayerNorm)와 잔차 연결(Residual Connection)로 학습 안정화
    • 디코더: 인코더 출력과 이전 출력을 사용해 새 토큰 생성
      • 마스크된 셀프 어텐션으로 미래 토큰을 보지 않도록 제한
      • 인코더-디코더 어텐션으로 입력 시퀀스의 정보 활용
# PyTorch로 구현한 셀프 어텐션 메커니즘
import torch
import torch.nn as nn
import math

class SelfAttention(nn.Module):
    def __init__(self, embed_size, heads):
        super(SelfAttention, self).__init__()
        self.embed_size = embed_size
        self.heads = heads
        self.head_dim = embed_size // heads
        
        assert (self.head_dim * heads == embed_size), "임베딩 크기는 헤드 수로 나누어 떨어져야 한다"
        
        # 선형 투영 레이어
        self.values = nn.Linear(self.head_dim, self.head_dim, bias=False)
        self.keys = nn.Linear(self.head_dim, self.head_dim, bias=False)
        self.queries = nn.Linear(self.head_dim, self.head_dim, bias=False)
        self.fc_out = nn.Linear(heads * self.head_dim, embed_size)
    
    def forward(self, values, keys, query, mask=None):
        N = query.shape[0]  # 배치 크기
        value_len, key_len, query_len = values.shape[1], keys.shape[1], query.shape[1]
        
        # 다중 헤드로 분할
        values = values.reshape(N, value_len, self.heads, self.head_dim)
        keys = keys.reshape(N, key_len, self.heads, self.head_dim)
        queries = query.reshape(N, query_len, self.heads, self.head_dim)
        
        # 선형 투영
        values = self.values(values)
        keys = self.keys(keys)
        queries = self.queries(queries)
        
        # 어텐션 스코어 계산
        energy = torch.einsum("nqhd,nkhd->nhqk", [queries, keys])
        # queries: [N, query_len, heads, head_dim]
        # keys: [N, key_len, heads, head_dim]
        # energy: [N, heads, query_len, key_len]
        
        # 스케일링
        energy = energy / math.sqrt(self.head_dim)
        
        # 마스크 적용 (필요한 경우)
        if mask is not None:
            energy = energy.masked_fill(mask == 0, float("-1e20"))
        
        # 소프트맥스로 정규화
        attention = torch.softmax(energy, dim=3)
        
        # 가중치 합 계산
        out = torch.einsum("nhql,nlhd->nqhd", [attention, values])
        # attention: [N, heads, query_len, key_len]
        # values: [N, value_len, heads, head_dim]
        # out: [N, query_len, heads, head_dim]
        
        # 헤드 결합
        out = out.reshape(N, query_len, self.heads * self.head_dim)
        out = self.fc_out(out)
        
        return out

 

 

 

  • 장점/단점:
    • 장점: 병렬 처리 가능, 장거리 의존성 포착, 학습 효율성
    • 단점: 메모리 요구량 증가(O(n²)), 위치 정보 명시적 주입 필요
  • 필요성: RNN의 순차적 처리와 장거리 의존성 문제를 해결하기 위해 개발되었다.

 

2-3. BERT - 양방향 문맥 이해의 혁신

 

  • 정의: Bidirectional Encoder Representations from Transformers의 약자로, 양방향으로 문맥을 이해하는 사전 훈련된 언어 모델이다.
  • 작동 원리:
    1. Transformer 인코더 여러 층을 쌓아 구성한다
    2. 마스크 언어 모델링(MLM)과 다음 문장 예측(NSP) 작업으로 사전 학습한다
    3. 다양한 NLP 작업에 미세 조정하여 활용한다
  • 구조 상세 분석:
    • 입력 임베딩: 토큰 + 세그먼트 + 위치 임베딩의 합
    • 특수 토큰:
      • [CLS]: 문장 시작 토큰, 분류 작업에 사용
      • [SEP]: 문장 구분 토큰
      • [MASK]: 마스킹된 단어 위치 표시
    • 레이어 구성:
      • 12개(BERT-Base) 또는 24개(BERT-Large) 트랜스포머 인코더 층
      • 각 층은 멀티헤드 어텐션과 피드포워드 네트워크로 구성
      • 각 층 사이에 LayerNorm과 잔차 연결(Residual Connection) 적용
  • 학습 방법 심층 분석:
    1. 마스크 언어 모델링(MLM):
      • 입력의 약 15%를 마스킹하고 원래 단어 예측
      • 마스킹된 토큰의 80%는 [MASK]로 대체
      • 10%는 무작위 단어로 대체
      • 10%는 변경 없이 그대로 유지 (모델의 정확도 향상 목적)
    2. 다음 문장 예측(NSP):
      • 50%는 실제 연속된 문장 쌍(IsNext)
      • 50%는 무작위로 선택된 문장 쌍(NotNext)
      • 문장 쌍 사이에 [SEP] 토큰으로 구분
    3. 손실 함수: L=LMLM+LNSP
# BERT 사용 예시 - 문장 분류
from transformers import BertTokenizer, BertForSequenceClassification
import torch

# 토크나이저와 모델 초기화
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertForSequenceClassification.from_pretrained('bert-base-uncased', num_labels=2)

# 입력 문장
sentence = "자연어 처리는 인공지능의 핵심 분야이다."

# 토큰화 및 모델 입력 준비
inputs = tokenizer(sentence, return_tensors="pt", padding=True, truncation=True, max_length=512)

# 모델 예측
with torch.no_grad():
    outputs = model(**inputs)

# 예측 결과 (로짓값)
logits = outputs.logits
predicted_class = torch.argmax(logits, dim=1).item()
print(f"예측된 클래스: {predicted_class}")

# 미세 조정 예시
# model.train()
# optimizer = torch.optim.AdamW(model.parameters(), lr=5e-5)
# loss = outputs.loss
# loss.backward()
# optimizer.step()

 

  • BERT의 변형 모델:
    • RoBERTa: 더 큰 배치, 더 많은 데이터, NSP 제거로 개선
    • DistilBERT: 더 작고 빠른 경량화 모델
    • ALBERT: 파라미터 공유로 효율성 향상
    • ELECTRA: 생성자-판별자 구조로 효율적인 사전 학습
  • 장점/단점:
    • 장점: 양방향 문맥 이해, 다양한 NLP 작업에 적용 가능, 미세 조정 용이
    • 단점: 큰 모델 크기(110M-340M 파라미터), 많은 연산 필요, 512 토큰 입력 제한
  • 필요성: 상황에 따라 단어의 의미가 달라지는 문제 해결을 위해 양방향 문맥 이해가 필요하다.

 

3. 실제 적용 예시

1. ELMo 적용 예시 - 감정 분석

# ELMo를 사용한 감정 분석 예시
import tensorflow as tf
import tensorflow_hub as hub

# ELMo 모듈 로드
elmo = hub.Module("https://tfhub.dev/google/elmo/2", trainable=True)

# 입력 문장
sentences = tf.placeholder(tf.string, shape=[None])

# ELMo 임베딩 계산
embeddings = elmo(sentences, signature="default", as_dict=True)["elmo"]

# 분류 레이어 추가
logits = tf.layers.dense(tf.reduce_mean(embeddings, axis=1), 2)
predictions = tf.argmax(logits, axis=-1)

# 손실 함수 및 최적화
labels = tf.placeholder(tf.int32, shape=[None])
loss = tf.reduce_mean(tf.nn.sparse_softmax_cross_entropy_with_logits(labels=labels, logits=logits))
train_op = tf.train.AdamOptimizer().minimize(loss)

# 사용 예시
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    example_sentences = ["이 영화는 정말 재미있다.", "이 영화는 시간 낭비였다."]
    example_labels = [1, 0]  # 긍정, 부정
    
    # 학습
    _, loss_val = sess.run([train_op, loss], 
                          feed_dict={sentences: example_sentences, labels: example_labels})
    
    # 예측
    pred = sess.run(predictions, feed_dict={sentences: ["새로운 영화는 꽤 좋았다."]})
    print("예측된 감정:", "긍정" if pred[0] == 1 else "부정")

 

 

 

 

 

2. Transformer 적용 예시 - 기계 번역

# Transformer를 사용한 기계 번역 예시
from transformers import MarianMTModel, MarianTokenizer

# 모델 및 토크나이저 로드
model_name = "Helsinki-NLP/opus-mt-en-ko"
tokenizer = MarianTokenizer.from_pretrained(model_name)
model = MarianMTModel.from_pretrained(model_name)

# 번역할 텍스트
text = "Natural language processing is a fascinating field of artificial intelligence."

# 토큰화
inputs = tokenizer(text, return_tensors="pt", padding=True)

# 번역
translated = model.generate(**inputs)
translated_text = tokenizer.decode(translated[0], skip_special_tokens=True)

print(f"원문: {text}")
print(f"번역: {translated_text}")

 

3. BERT 적용 예시 - 개체명 인식(NER)

# BERT를 사용한 개체명 인식 예시
from transformers import BertTokenizer, BertForTokenClassification
import torch

# 토크나이저와 모델 초기화
tokenizer = BertTokenizer.from_pretrained('bert-base-cased')
model = BertForTokenClassification.from_pretrained('bert-base-cased', num_labels=9)  # 9개 NER 태그

# 입력 문장
text = "마이크로소프트의 CEO인 사티아 나델라는 시애틀에 있는 본사에서 연설했다."

# 토큰화
tokens = tokenizer.tokenize(tokenizer.decode(tokenizer.encode(text)))
inputs = tokenizer(text, return_tensors="pt", is_split_into_words=False)

# 모델 예측
with torch.no_grad():
    outputs = model(**inputs)

# 예측 결과 처리
predictions = torch.argmax(outputs.logits, dim=2)
predicted_token_class = predictions[0].numpy()

# NER 태그 매핑 (예시)
tag_names = ["O", "B-PER", "I-PER", "B-ORG", "I-ORG", "B-LOC", "I-LOC", "B-MISC", "I-MISC"]

# 결과 출력
for i, token in enumerate(tokens):
    if i >= len(predicted_token_class) - 1:  # 특수 토큰 제외
        break
    print(f"{token}: {tag_names[predicted_token_class[i]]}")

 


 

4. 비교분석표

구분 ELMo Transformer BERT
아키텍처 BiLSTM + CNN 인코더-디코더 Transformer 인코더
문맥 이해 양방향 전역 주의 양방향
학습 방법 언어 모델링 자기회귀 예측 MLM + NSP
파라미터 수 약 94M 약 65M (base) 110M-340M
입력 제한 가변적 가변적 512 토큰
병렬 처리 제한적 우수 우수
강점 문자 수준 정보 장거리 의존성 양방향 문맥 이해
약점 계산 비용 위치 정보 필요 고비용, 입력 제한
주요 응용분야 개체명 인식, 감정 분석 기계 번역, 텍스트 생성 텍스트 분류, 질의응답
연도 2018 2017 2018

 

728x90