728x90
언어 모델의 진화와 혁신
오늘은 자연어처리의 전이학습에 대해 알아보자.
오늘의 배움 |
|
1. 전이학습이란?
- 정의: 하나의 문제를 해결하기 위해 학습한 지식을 다른 관련 문제 해결에 적용하는 기계학습 방법이다.
- 한 줄 요약: "배운 지식을 새로운 과제에 재활용하는 효율적인 학습 방식이다."
- 특징:
- 사전 훈련된 모델의 가중치를 활용한다
- 소량의 데이터로도 좋은 성능을 낸다
- 학습 시간과 자원을 절약한다
- 필요성: 모든 NLP 작업마다 처음부터 학습하기에는 비용과 시간이 많이 소요된다.
- 장점/단점:
- 장점: 학습 효율성 증가, 적은 데이터로도 높은 성능
- 단점: 도메인 차이에 따른 성능 저하 가능성, 모델 크기가 커질수록 자원 소모 증가
- 예시:
- 중국어를 배우는 학생이 이미 알고 있는 한자 지식을 활용하는 것과 같다. 기존에 알던 한자 지식(사전 학습된 지식)을 새로운 언어 학습(다운스트림 태스크)에 적용하는 것이다.
- BERT로 대규모 코퍼스에서 사전 학습 후, 감정 분석 태스크에 미세 조정하는 방식
2. 핵심 개념 정리
2-1. ELMo - 문맥 기반 임베딩의 시작
- 정의: Embeddings from Language Models의 약자로, 문맥에 따라 단어의 의미를 다르게 표현하는 딥러닝 모델이다.
- 작동 원리:
- 양방향 LSTM(BiLSTM)을 사용하여 문맥 정보를 캡처한다
- 문자 수준의 CNN을 사용해 단어 내부 구조를 파악한다
- 각 층의 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 - 주의집중 메커니즘의 혁신
- 정의: 순환 신경망 없이 자기 주의 메커니즘만으로 시퀀스 데이터를 처리하는 신경망 구조이다.
- 작동 원리:
- 단어를 임베딩하고 위치 정보를 추가한다
- 자기 주의(Self-Attention) 메커니즘으로 단어 간 관계를 계산한다
- 여러 개의 주의 헤드로 다양한 측면에서 관계를 분석한다
- 인코더는 문맥을 이해하고, 디코더는 출력을 생성한다
- 핵심 구성요소 상세 분석: :
- 어텐션 메커니즘 심층 분석
- 쿼리(Q), 키(K), 값(V) 개념:
- 쿼리: "무엇을 알고 싶은가?"를 나타낸다
- 키: "어떤 정보가 있는가?"를 나타낸다
- 값: "실제 정보 내용"을 담고 있다
- 쿼리(Q), 키(K), 값(V) 개념:
- 여러 개의 어텐션 헤드를 병렬로 실행하여 다양한 관점에서 정보를 추출한다
- 단어의 위치 정보를 모델에 주입하는 방법
- 사인-코사인 함수 사용
- 인코더: 입력 시퀀스를 처리하여 표현 벡터 생성
- 셀프 어텐션으로 각 단어가 다른 단어들과 어떻게 관련되는지 파악
- 정규화(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의 약자로, 양방향으로 문맥을 이해하는 사전 훈련된 언어 모델이다.
- 작동 원리:
- Transformer 인코더 여러 층을 쌓아 구성한다
- 마스크 언어 모델링(MLM)과 다음 문장 예측(NSP) 작업으로 사전 학습한다
- 다양한 NLP 작업에 미세 조정하여 활용한다
- 구조 상세 분석:
- 입력 임베딩: 토큰 + 세그먼트 + 위치 임베딩의 합
- 특수 토큰:
- [CLS]: 문장 시작 토큰, 분류 작업에 사용
- [SEP]: 문장 구분 토큰
- [MASK]: 마스킹된 단어 위치 표시
- 레이어 구성:
- 12개(BERT-Base) 또는 24개(BERT-Large) 트랜스포머 인코더 층
- 각 층은 멀티헤드 어텐션과 피드포워드 네트워크로 구성
- 각 층 사이에 LayerNorm과 잔차 연결(Residual Connection) 적용
- 학습 방법 심층 분석:
- 마스크 언어 모델링(MLM):
- 입력의 약 15%를 마스킹하고 원래 단어 예측
- 마스킹된 토큰의 80%는 [MASK]로 대체
- 10%는 무작위 단어로 대체
- 10%는 변경 없이 그대로 유지 (모델의 정확도 향상 목적)
- 다음 문장 예측(NSP):
- 50%는 실제 연속된 문장 쌍(IsNext)
- 50%는 무작위로 선택된 문장 쌍(NotNext)
- 문장 쌍 사이에 [SEP] 토큰으로 구분
- 손실 함수: L=LMLM+LNSP
- 마스크 언어 모델링(MLM):
# 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
'Develop > AI' 카테고리의 다른 글
LLM 사용에 대해 알아보자. (0) | 2025.03.15 |
---|---|
LLM 개요를 알아보자 (1) | 2025.03.15 |
신경망 기계번역-Input Feeding을 알아보자. (0) | 2025.03.09 |
신경망 기계번역-attention을 알아보자. (0) | 2025.03.09 |
신경망 기계번역-seq2seq를 알아보자. (0) | 2025.03.03 |