[천재교육] 프로젝트 기반 빅데이터 서비스 개발자 양성 과정 9기
학습일 : 2024.09.27
📕 학습 목록
- RNN
- LSTM
📗 기억할 내용
1) LSTM 정의
- LSTM(Long Short-Term Memory) : 순환 신경망(RNN, Recurrent Neural Network)의 일종으로, 긴 시퀀스 데이터를 처리하는 데 적합한 신경망
- RNN은 시퀀스 데이터를 처리하기 위해 설계된 신경망으로, 텍스트, 음성, 시계열 데이터 등 순차적인 데이터를 학습할 때 사용됨
- 하지만 RNN은 장기 의존성 문제(long-term dependency problem)를 가지고 있음. 이는 긴 시퀀스에서 먼 과거의 정보를 잘 기억하지 못하는 문제를 의미
- LSTM은 이러한 문제를 해결하기 위해 고안되었으며, 정보를 장기적으로 기억할 수 있는 셀 상태(Cell State)와 이를 제어하는 게이트 구조를 도입함
2) LSTM의 주요 구성 요소
① 셀 상태(Cell State)
- Cell State : LSTM의 핵심 구성 요소로, 정보가 흐르는 경로임. 셀 상태는 중요한 정보를 오랜 시간 동안 유지하거나 잊어버릴 수 있음
- 셀 상태는 시점 간의 정보 흐름을 유지하며, 이전 시점에서 중요한 정보가 현재 시점에도 필요할 경우 이 정보를 계속 유지함
- 셀 상태는 각 게이트(Input, Forget, Output)에 의해 제어됨
② 입력 게이트(Input Gate)
- Input Gate : 새로운 입력 정보가 셀 상태에 얼마나 중요한지 판단하고, 그 정보를 셀 상태에 추가할지 여부를 결정함
- 중요한 정보만 선택하여 셀 상태로 전달하며, 덜 중요한 정보는 입력되지 않도록 제어
③ 포겟 게이트(Forget Gate)
- Forget Gate : 셀 상태에서 이전 시점의 정보 중 얼마나 잊을지를 결정함
- 입력된 정보가 더 이상 유효하지 않거나 중요하지 않은 경우, 포겟 게이트가 이를 걸러냄
- 예를 들어, 문장의 앞부분에서 중요한 정보가 나오지 않았을 경우, 포겟 게이트는 그 정보를 삭제함
④ 출력 게이트(Output Gate)
- Output Gate : 셀 상태에서 중요한 정보를 선택하여 최종 출력값으로 변환함
- 셀 상태와 입력 데이터를 바탕으로 현재 시점에서 어떤 정보를 출력할지 결정하며, 예측 결과를 도출하는 데 중요한 역할
3) LSTM의 작동 과정
① 입력 데이터 처리
- 시퀀스의 각 시점에서 입력 게이트는 현재 입력된 정보가 얼마나 중요한지를 분석하고, 셀 상태에 저장할지 여부를 결정
② 포겟 게이트의 역할
- 셀 상태에서 과거의 정보 중 불필요한 정보를 제거하여, 중요한 정보만 셀 상태에 남김. 이는 이전 시점에서 온 정보 중 현재 시점에 유효하지 않은 정보를 잊도록 함
③ 셀 상태 업데이트
- 입력 게이트와 포겟 게이트의 결과에 따라 셀 상태는 중요한 정보를 유지하거나 업데이트함. 새로운 정보가 입력되면, 기존 셀 상태에 이 정보를 추가함
④ 최종 출력
- 출력 게이트는 현재 상태에서 필요한 정보를 선택하여 최종 출력을 생성. 이는 주어진 작업에 맞는 예측 결과로 출력되며, 이를 통해 텍스트 분류, 시계열 예측 등 다양한 작업이 수행됨
4) LSTM의 장점
- 장기 의존성 해결: LSTM은 셀 상태와 게이트 구조를 통해 긴 시퀀스에서도 중요한 정보를 기억할 수 있어, RNN의 장기 의존성 문제를 해결
- 문맥 정보 반영: LSTM은 이전 시점의 정보를 유지하고 활용할 수 있어, 문맥을 반영한 예측이 가능
- 다양한 응용 분야: LSTM은 텍스트 분류, 음성 인식, 시계열 예측, 번역 등의 다양한 분야에서 활용
📘 코드 실습
< LSTM 기반 텍스트 분류 모델 구축 >
1. 모델 구축 과정
1) 모델의 목적
- 텍스트 분류: 주어진 텍스트 데이터를 긍정적 또는 부정적으로 분류하는 모델. 예를 들어, 사용자 리뷰나 피드백을 분석하여 긍정적인지 부정적인지를 판단
2) 모델의 구조
- 임베딩 레이어 (Embedding Layer)
- 역할: 단어를 고차원 벡터로 변환하여 모델이 텍스트 데이터를 수치적으로 처리할 수 있도록 함
- 설명: 각 단어는 고유한 인덱스에 매핑되고, 임베딩 레이어는 이 인덱스를 고정된 크기의 벡터로 변환함. 이를 통해 단어 간의 유사성을 반영할 수 있음
- LSTM 레이어 (Long Short-Term Memory Layer)
- 역할: 시퀀스 데이터(문장)를 처리하고, 단어 간의 순서와 문맥 정보를 학습
- 설명: LSTM은 RNN의 한 종류로, 긴 시퀀스에서도 중요한 정보를 기억할 수 있는 셀 상태와 게이트 구조를 가지고 있어, 장기 의존성 문제를 해결
- 완전 연결 레이어 (Fully Connected Layer)
- 역할: LSTM 레이어의 출력을 받아 최종 분류 결과(긍정/부정)를 출력
- 설명: 마지막 LSTM의 출력값을 선형 변환하여 각 클래스(예: 긍정, 부정)에 대한 확률을 계산
3) 모델의 전체 흐름
① 데이터 로드 및 전처리
- 학습 데이터(ratings_train.txt)와 테스트 데이터(ratings_test.txt)를 pandas를 이용해 불러옴
- 데이터의 중복 제거, 특수 문자 제거, 불용어 처리 등을 통해 텍스트를 정제
② 토큰화 및 단어 인덱싱
- Okt 형태소 분석기를 사용하여 문장을 단어 단위로 분해하고, 불용어를 제거
- 단어들을 고유 인덱스로 변환하여 숫자형 벡터로 만듦
③ 모델 설계
- 임베딩 레이어: 단어 인덱스를 벡터로 변환
- LSTM 레이어: 시퀀스 데이터를 처리하여 문맥 정보를 학습
- 완전 연결 레이어: 최종 분류 결과를 출력
④ 모델 학습
- 학습 데이터셋을 통해 모델을 훈련시키고, 각 에포크(epoch)마다 손실과 정확도를 계산
- 검증 데이터셋을 사용하여 모델의 성능을 평가하고, 가장 좋은 성능을 보이는 모델을 저장
⑤ 모델 평가 및 테스트
- 저장된 최적의 모델을 로드하여 테스트 데이터셋에 대해 성능을 평가
- 학습되지 않은 새로운 데이터에 대해 긍정/부정 예측을 수행할 수 있는 함수를 작성
2. 모델 생성 코드
1) 데이터 로드 및 전처리
import pandas as pd # 학습 데이터 및 테스트 데이터 로드 (TSV 파일) train = pd.read_csv('./data/ratings_train.txt', sep='\t') test = pd.read_csv('./data/ratings_test.txt', sep='\t') # 각 데이터셋에 대한 정보 확인 (크기, 결측치, 중복 등) for table in (train, test): print('데이터 개수') print(table.shape) print('결측치 여부') print(table.isnull()['label'].unique()) print('중복 데이터 개수') print(table.shape[0] - table['document'].nunique()) print('클래스별 데이터 개수') print(table.groupby('label').size()) print() # 전처리: 중복 제거, 특수 문자 제거, 불필요한 공백 제거, 결측치 처리 for table in (train, test): table.drop_duplicates(subset=['document'], inplace=True) # 중복된 문장 제거 table['document'] = table['document'].str.replace('[^가-힣ㄱ-ㅎㅏ-ㅣ ]', '', regex=True) # 한글 이외의 문자는 모두 제거 table['document'] = table['document'].str.replace('[ +]', '', regex=True) # 여러 공백을 하나로 table['document'].replace('', float('nan'), inplace=True) # 빈 문자열을 결측치로 변환 table.dropna(how='any', inplace=True) # 결측치가 있는 행 제거 print('전처리 후 데이터 개수:', table.shape)
2) 불용어 처리 및 토큰화from konlpy.tag import Okt from tqdm import tqdm import numpy as np okt = Okt() # 한국어 텍스트를 위한 형태소 분석기 Okt 초기화 # 불용어 리스트 불러오기 with open('../stopwords-ko.txt', encoding='utf8') as f: stopwords = f.readlines() stopwords = [s.strip() for s in stopwords] # 각 불용어의 끝에 있는 줄바꿈 문자 제거 # 데이터 초기화 train.reset_index(drop=True, inplace=True) test.reset_index(drop=True, inplace=True) # 학습 데이터와 테스트 데이터를 토큰화하고 불용어 제거 X_train, y_train = [], [] for i in tqdm(range(len(train))): # tqdm으로 진행 상황을 보여줌 sentence = train['document'][i] y_train.append(train['label'][i]) # 형태소 분석 및 불용어 제거 tokenized = okt.morphs(sentence, stem=True) stopwords_removed_sentence = [word for word in tokenized if word not in stopwords and len(word) >= 2] # 2글자 이상만 사용 if stopwords_removed_sentence: X_train.append(stopwords_removed_sentence) X_test, y_test = [], [] for i in tqdm(range(len(test))): sentence = test['document'][i] y_test.append(test['label'][i]) tokenized = okt.morphs(sentence, stem=True) stopwords_removed_sentence = [word for word in tokenized if word not in stopwords and len(word) >= 2] if stopwords_removed_sentence: X_test.append(stopwords_removed_sentence)
3) LSTM 모델 학습import torch import torch.nn as nn import torch.optim as optim # LSTM 기반의 텍스트 분류 모델 설계 class LSTMModel(nn.Module): def __init__(self, vocab_size, embedding_dim, hidden_size, num_classes): super(LSTMModel, self).__init__() self.embedding = nn.Embedding(vocab_size, embedding_dim) # 임베딩 레이어 self.lstm = nn.LSTM(embedding_dim, hidden_size, batch_first=True) # LSTM 레이어 self.fc = nn.Linear(hidden_size, num_classes) # 출력 레이어 def forward(self, x): x = self.embedding(x) # 임베딩 _, (hn, _) = self.lstm(x) # LSTM 연산 out = self.fc(hn[-1]) # 출력값 return out # 모델, 손실 함수, 옵티마이저 정의 vocab_size = 10000 # 예시 embedding_dim = 128 hidden_size = 256 num_classes = 2 model = LSTMModel(vocab_size, embedding_dim, hidden_size, num_classes) criterion = nn.CrossEntropyLoss() # 교차 엔트로피 손실 함수 optimizer = optim.Adam(model.parameters(), lr=0.001) # Adam 옵티마이저
4) 모델 학습 과정# 학습과 검증 함수 정의 def train_model(model, train_dataloader, valid_dataloader, num_epochs): best_val_loss = float('inf') # 초기값을 무한대로 설정 for epoch in range(num_epochs): model.train() # 학습 모드로 전환 train_loss, train_correct, train_total = 0, 0, 0 for batch_X, batch_y in train_dataloader: batch_X, batch_y = batch_X.to(device), batch_y.to(device) logits = model(batch_X) # 모델에 데이터를 입력하여 예측값 계산 loss = criterion(logits, batch_y) # 손실 계산 optimizer.zero_grad() # 이전의 기울기 초기화 loss.backward() # 역전파 optimizer.step() # 옵티마이저로 파라미터 업데이트 train_loss += loss.item() train_correct += (logits.argmax(1) == batch_y).sum().item() # 정확도 계산 train_total += batch_y.size(0) train_acc = train_correct / train_total train_loss /= len(train_dataloader) val_loss, val_acc = evaluate(model, valid_dataloader) # 검증 데이터로 평가 print(f'Epoch {epoch+1}/{num_epochs}, Train Loss: {train_loss:.3f}, Train Acc: {train_acc:.3f}') print(f'Validation Loss: {val_loss:.3f}, Validation Acc: {val_acc:.3f}') # 최상의 검증 성능을 기록 if val_loss < best_val_loss: best_val_loss = val_loss torch.save(model.state_dict(), './best_model.pth') # 모델 저장
5) 테스트 및 리뷰 예측# 학습된 모델을 로드하고 테스트 데이터로 평가 model.load_state_dict(torch.load('./best_model.pth')) # 저장된 최상의 모델 불러오기 model.to(device) test_loss, test_acc = evaluate(model, test_dataloader) # 테스트 데이터로 평가 print(f'Test Loss: {test_loss:.3f}, Test Acc: {test_acc:.3f}') # 새로운 리뷰에 대한 긍정/부정 예측 함수 정의 def predict(text, model, word_to_index): model.eval() # 평가 모드로 전환 tokens = okt.morphs(text, stem=True) # 문장을 형태소 분석하여 토큰화 token_indices = [word_to_index.get(token, 1) for token in tokens] # 단어 인덱스 변환 input_tensor = torch.tensor([token_indices], dtype=torch.long).to(device) with torch.no_grad(): logits = model(input_tensor) # 예측값 계산 pred_ind = torch.argmax(logits, dim=1) # 가장 높은 확률을 가진 클래스로 예측 return pred_ind # 예시: '싫어요'라는 문장이 입력된 경우 if int(predict('싫어요', model)) == 0: print('부정적 리뷰') else: print('긍정적 리뷰')
📙 내일 일정
- SQL 개론
'TIL _Today I Learned > 2024.09' 카테고리의 다른 글
[DAY 53] SQL의 이해 및 활용 (1) | 2024.09.30 |
---|---|
[DAY 51] TF-IDF, Deep Learning 실습 (0) | 2024.09.26 |
[DAY 50] 구문 분석 (Syntactic Parsing) (0) | 2024.09.25 |
[DAY 49] 자연어 처리(NLP) (0) | 2024.09.24 |
[DAY 48] Deep Learning 실습 (0) | 2024.09.23 |