Level 4: 실전 프로젝트
🚀

Level 4

텍스트 분류

감성 분석 모델 만들기

약 4분
텍스트 분류 강의 영상
강의 영상 보기 (새 탭에서 재생)YouTube

📓Google Colab에서 실습하기

이 레슨은 PyTorch/GPU가 필요합니다. 노트북을 다운로드 후 Google Colab에서 열어주세요.

학습 내용

텍스트 분류

학습 목표

이 레슨을 완료하면:

  • 텍스트를 신경망에 입력하기 위한 전처리 과정을 설명합니다
  • 토큰화, 어휘 사전, 임베딩의 개념과 관계를 이해합니다
  • LSTM 기반 감성 분석 모델을 구현합니다
  • RNN과 LSTM의 차이점을 설명할 수 있습니다

텍스트를 숫자로 바꾸는 과정

비유: 신경망은 오직 숫자만 이해합니다. 텍스트를 신경망에 넣는 것은, 한국어를 모르는 외국인에게 한국 소설을 읽히는 것과 같습니다. 먼저 외국인이 이해할 수 있는 형태(숫자)로 "번역"해야 합니다.

텍스트 전처리는 세 단계를 거칩니다:

단계입력출력설명
1. 토큰화문장단어 목록문장을 단어로 쪼갬
2. 인덱싱단어 목록숫자 목록각 단어에 번호 부여
3. 임베딩숫자 목록벡터 목록번호를 의미 벡터로 변환

전체 과정 예시

python
# 원본 텍스트 "This movie is really great!" # 1단계: 토큰화 -> 단어로 분리 ["this", "movie", "is", "really", "great", "!"] # 2단계: 인덱싱 -> 숫자로 변환 [42, 156, 11, 89, 2341, 5] # 3단계: 임베딩 -> 의미 벡터로 변환 (각 숫자가 128차원 벡터로) [[0.2, -0.1, 0.5, ...], # "this"의 의미 벡터 [0.8, 0.3, -0.2, ...], # "movie"의 의미 벡터 [0.1, 0.0, 0.3, ...], # "is"의 의미 벡터 ...]

1단계: 토큰화 (Tokenization)

비유: 토큰화는 문장을 레고 블록처럼 작은 조각으로 분해하는 것입니다. 어떤 크기로 분해하느냐에 따라 결과가 달라집니다.

python
from torchtext.data.utils import get_tokenizer # 영어 기본 토크나이저 tokenizer = get_tokenizer("basic_english") tokens = tokenizer("This movie is great!") # 결과: ["this", "movie", "is", "great", "!"]

토큰화 방식 비교:

방식예시 입력결과특징
단어 단위"I love cats"["I", "love", "cats"]직관적, 어휘 크기 큼
서브워드"playing"["play", "##ing"]어휘 크기 적당
문자 단위"cat"["c", "a", "t"]어휘 매우 작음, 시퀀스 길어짐

현대 모델(BERT, GPT)은 대부분 서브워드 토큰화를 사용합니다.


2단계: 어휘 사전 (Vocabulary)

비유: 어휘 사전은 "단어-번호 변환표"입니다. 도서관에서 각 책에 고유 번호를 붙이듯, 각 단어에 고유 번호를 부여합니다.

python
# 간단한 어휘 사전 구축 예시 vocab = { "<pad>": 0, # 패딩 (빈 칸 채우기용) "<unk>": 1, # 미등록 단어 (사전에 없는 단어) "this": 2, "movie": 3, "is": 4, "great": 5, "terrible": 6, # ... 수천~수만 단어 } # 문장을 숫자로 변환 def encode(tokens, vocab): return [vocab.get(token, vocab["<unk>"]) for token in tokens] encoded = encode(["this", "movie", "is", "great"], vocab) # 결과: [2, 3, 4, 5]

왜 pad와 unk가 필요한가?

특수 토큰역할예시
pad길이가 다른 문장을 같은 길이로 맞춤"hi" -> [24, 0, 0, 0, 0]
unk학습 시 본 적 없는 단어 처리"supercalifragilistic" -> 1

3단계: 임베딩 (Embedding)

비유: 임베딩은 단어의 "주민등록번호"를 "성격 프로필"로 바꾸는 과정입니다. 숫자 42는 아무런 의미 정보를 담고 있지 않지만, [0.2, -0.1, 0.5, ...]같은 벡터는 그 단어의 의미를 담고 있습니다.

python
⚠️ 로컬 실행 필요
# 임베딩 레이어: 숫자 인덱스 -> 의미 벡터 embedding = nn.Embedding( num_embeddings=10000, # 어휘 크기 (단어 수) embedding_dim=128 # 벡터 차원 (의미 공간의 차원 수) ) # 사용 예시 import torch word_indices = torch.tensor([2, 3, 4, 5]) # "this movie is great" word_vectors = embedding(word_indices) # 결과 크기: [4, 128] - 4개 단어, 각각 128차원 벡터

임베딩의 핵심 특성:

  • 비슷한 의미의 단어는 비슷한 벡터를 가지게 됩니다
  • 예: "good"과 "great"의 벡터가 가까움
  • 이 벡터 값들은 학습 과정에서 자동으로 조정됩니다

LSTM 이해하기

RNN의 문제점

비유: 기본 RNN은 "금붕어 기억력"을 가지고 있습니다. 긴 문장의 앞부분을 읽을 때쯤이면 뒷부분은 까먹습니다. 이것이 "기울기 소실(vanishing gradient)" 문제입니다.

LSTM의 해결책

비유: LSTM은 "수첩을 가진 학생"입니다. 중요한 정보는 수첩(셀 상태)에 적어두고, 필요 없는 정보는 지우며, 필요할 때 꺼내 봅니다.

구성 요소역할비유
셀 상태 (Cell State)장기 기억 저장소수첩
망각 게이트 (Forget Gate)불필요한 정보 삭제수첩에서 줄 긋기
입력 게이트 (Input Gate)새 정보 저장 결정수첩에 새로 적기
출력 게이트 (Output Gate)현재 필요한 정보 출력수첩에서 읽기
python
# LSTM 레이어 생성 lstm = nn.LSTM( input_size=128, # 입력 벡터 크기 (= 임베딩 차원) hidden_size=256, # 은닉 상태 크기 batch_first=True, # 입력 형태: [배치, 시퀀스길이, 특성] num_layers=1 # LSTM 층 수 )

감성 분석 모델 구현

감성 분석(Sentiment Analysis)은 텍스트가 긍정적인지 부정적인지 판단하는 작업입니다.

python
class SentimentClassifier(nn.Module): def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes): super().__init__() # 1. 임베딩: 단어 인덱스 -> 의미 벡터 self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0) # 2. LSTM: 순서 정보를 활용한 문맥 파악 self.lstm = nn.LSTM( embed_dim, # 입력 크기 = 임베딩 차원 hidden_dim, # 은닉 상태 크기 batch_first=True, bidirectional=False # 단방향 (앞에서 뒤로만 읽음) ) # 3. 분류기: 최종 은닉 상태 -> 클래스 예측 self.fc = nn.Linear(hidden_dim, num_classes) self.dropout = nn.Dropout(0.3) def forward(self, x): # x: [배치크기, 시퀀스길이] - 숫자로 인코딩된 문장 embedded = self.embedding(x) # embedded: [배치크기, 시퀀스길이, embed_dim] # LSTM에 임베딩 벡터를 순서대로 입력 output, (hidden, cell) = self.lstm(embedded) # hidden: [1, 배치크기, hidden_dim] - 마지막 은닉 상태 # 마지막 은닉 상태로 감성 분류 hidden = self.dropout(hidden.squeeze(0)) logits = self.fc(hidden) # logits: [배치크기, num_classes] return logits # 모델 생성 model = SentimentClassifier( vocab_size=10000, # 어휘 크기 embed_dim=128, # 임베딩 차원 hidden_dim=256, # LSTM 은닉 크기 num_classes=2 # 긍정(1) / 부정(0) )

왜 마지막 은닉 상태만 사용하는가?

LSTM의 마지막 은닉 상태(hidden)는 전체 문장을 읽은 후의 "요약"입니다. 문장 전체의 맥락을 압축한 벡터이므로, 감성 분류에 적합합니다.


학습 코드

python
criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001) for epoch in range(10): model.train() total_loss = 0 for texts, labels in train_loader: texts, labels = texts.to(device), labels.to(device) optimizer.zero_grad() output = model(texts) loss = criterion(output, labels) loss.backward() # 그래디언트 클리핑: RNN 계열에서 기울기 폭발 방지 torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) optimizer.step() total_loss += loss.item() avg_loss = total_loss / len(train_loader) print("Epoch {}, Loss: {:.4f}".format(epoch+1, avg_loss))

왜 그래디언트 클리핑이 필요한가?

RNN/LSTM은 시퀀스가 길어지면 역전파 시 그래디언트가 폭발적으로 커질 수 있습니다. clip_grad_norm_은 그래디언트의 크기를 max_norm 이하로 제한하여 안정적인 학습을 보장합니다.


이미지 분류 vs 텍스트 분류 비교

항목이미지 분류 (CNN)텍스트 분류 (LSTM)
입력 형태2D 픽셀 격자1D 단어 시퀀스
전처리정규화, 크기 조정토큰화, 임베딩
핵심 레이어Conv2dLSTM/GRU
패턴 유형공간적 패턴순차적 패턴
주요 과제위치/크기 변화문맥 이해

핵심 요약

개념설명비유
토큰화문장을 단어로 분리문장을 레고 블록으로 분해
어휘 사전단어에 고유 번호 부여도서관 분류 번호
임베딩번호를 의미 벡터로 변환번호를 성격 프로필로 변환
LSTM순서 정보로 문맥 파악수첩 들고 읽는 학생
감성 분석텍스트의 감정 판단영화 리뷰 평가

학습 체크리스트

  • 텍스트 전처리 3단계(토큰화 -> 인덱싱 -> 임베딩)를 설명할 수 있다
  • pad 토큰과 unk 토큰의 역할을 안다
  • 임베딩 레이어가 하는 일을 설명할 수 있다
  • LSTM이 RNN의 어떤 문제를 해결하는지 안다
  • 그래디언트 클리핑이 필요한 이유를 설명할 수 있다
  • 감성 분석 모델의 전체 구조를 이해한다

레슨 정보

레벨
Level 4: 실전 프로젝트
예상 소요 시간
약 4분
참고 영상
YouTube 링크

💡실습 환경 안내

이 레벨은 PyTorch/GPU가 필요하여 Google Colab 사용을 권장합니다.

Colab은 무료 GPU를 제공하여 PyTorch, CNN, Transformer 등을 실행할 수 있습니다.