torch.nn.Embedding을 더 공부해야할 것 같다.
(bohyun) user@server:~/Desktop/BOHYUN/speedrun$ CUDA_VISIBLE_DEVICES=6 python navermovie.py
PyTorch version:[2.3.1+cu121].
device:[cuda:0].
/home/user/miniconda3/envs/bohyun/lib/python3.8/site-packages/huggingface_hub/file_download.py:1132: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
warnings.warn(
Epoch [1/5], Loss: 0.6933, Train Acc: 0.4988, Test Acc: 0.5035
Epoch [2/5], Loss: 0.6932, Train Acc: 0.4989, Test Acc: 0.5034
Epoch [3/5], Loss: 0.6932, Train Acc: 0.4988, Test Acc: 0.5035
Epoch [4/5], Loss: 0.6932, Train Acc: 0.4988, Test Acc: 0.5035
Epoch [5/5], Loss: 0.6932, Train Acc: 0.4988, Test Acc: 0.5035
결과가 좋지않다..
왜 그런지에 대해 고찰해보았다.
일단 임포트한 모듈은
import os
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import pandas as pd
from transformers import BertTokenizer
import urllib.request
다음과 같다.
def download_nsmc_data():
url_train = "https://raw.githubusercontent.com/e9t/nsmc/master/ratings_train.txt"
url_test = "https://raw.githubusercontent.com/e9t/nsmc/master/ratings_test.txt"
os.makedirs('./data', exist_ok=True)
urllib.request.urlretrieve(url_train, './data/ratings_train.txt')
urllib.request.urlretrieve(url_test, './data/ratings_test.txt')
# Download the dataset
download_nsmc_data()
깃허브에서

데이터를 로드해오면 같은 디렉토리에 data폴더가 생기고 하위에 txt파일이 두개가 만들어진다.
SSH서버를 사용해서 CUDA에서 돌렸다.
# Check for device
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print("PyTorch version:[%s]."%(torch.__version__))
print("device:[%s]."%(device))
# Load NSMC dataset
class NSMCDataset(torch.utils.data.Dataset):
def __init__(self, file_path, tokenizer, max_len=128):
if not os.path.isfile(file_path):
raise FileNotFoundError(f"File {file_path} not found.")
self.data = pd.read_csv(file_path, sep='\t').dropna()
self.tokenizer = tokenizer
self.max_len = max_len
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
text = self.data.iloc[idx]['document']
label = self.data.iloc[idx]['label']
encoding = self.tokenizer.encode_plus(
text,
add_special_tokens=True,
max_length=self.max_len,
return_token_type_ids=False,
padding='max_length',
truncation=True,
return_attention_mask=True,
return_tensors='pt'
)
return {
'input_ids': encoding['input_ids'].squeeze(),
'attention_mask': encoding['attention_mask'].squeeze(),
'labels': torch.tensor(label, dtype=torch.long)
}
NSMCDataset 클래스는 NSMC(Naver Sentiment Movie Corpus) 데이터를 처리하기 위해 만들어진 사용자 정의 데이터셋 클래스다.
1. 초기화 (__init__ 메서드):
file_path 매개변수로 데이터셋 파일 경로를 받는다.tokenizer 매개변수로 텍스트를 토큰화할 토크나이저를 받는다.max_len 매개변수로 토큰화된 텍스트의 최대 길이를 지정한다.길이 반환 (__len__ 메서드):
샘플 반환 (__getitem__ 메서드):
input_ids, attention_mask, labels를 반환한다.# Load tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-multilingual-cased')
# Create datasets
train_dataset = NSMCDataset('./data/ratings_train.txt', tokenizer)
test_dataset = NSMCDataset('./data/ratings_test.txt', tokenizer)
# Create DataLoader
BATCH_SIZE = 256
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=1)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=1)
아래 코드는 BERT 토크나이저를 사용하여 NSMC(Naver Sentiment Movie Corpus) 데이터셋을 로드하고, 데이터 로더를 생성하는 과정을 설명하고 있어.
# 토크나이저 로드
tokenizer = BertTokenizer.from_pretrained('bert-base-multilingual-cased')
# 데이터셋 생성
train_dataset = NSMCDataset('./data/ratings_train.txt', tokenizer)
test_dataset = NSMCDataset('./data/ratings_test.txt', tokenizer)
# DataLoader 생성
BATCH_SIZE = 256
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=1)
test_loader = torch.utils.data.DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False, num_workers=1)
토크나이저 로드:
BertTokenizer를 'bert-base-multilingual-cased' 모델로부터 로드하여 텍스트 데이터를 토큰화하는데 사용한다.데이터셋 생성:
NSMCDataset 클래스를 사용하여 학습(train_dataset)과 테스트(test_dataset) 데이터셋을 생성한다.DataLoader 생성:
torch.utils.data.DataLoader를 사용하여 학습 데이터셋과 테스트 데이터셋을 로드할 수 있도록 데이터 로더를 생성한다.BATCH_SIZE를 256으로 설정하고, 학습 데이터 로더는 데이터를 무작위로 섞어 로드하고(shuffle=True), 테스트 데이터 로더는 순차적으로 로드한다.(shuffle=False).class RecurrentNeuralNetworkClass(nn.Module):
def __init__(self, name='rnn', vocab_size=30522, emb_dim=128, hdim=256, ydim=2, n_layer=3):
super(RecurrentNeuralNetworkClass, self).__init__()
self.name = name
self.vocab_size = vocab_size
self.emb_dim = emb_dim
self.hdim = hdim
self.ydim = ydim
self.n_layer = n_layer
self.embedding = nn.Embedding(self.vocab_size, self.emb_dim)
self.rnn = nn.LSTM(input_size=self.emb_dim, hidden_size=self.hdim, num_layers=self.n_layer, batch_first=True)
self.lin = nn.Linear(self.hdim, self.ydim)
def forward(self, x):
x = self.embedding(x)
h0 = torch.zeros(self.n_layer, x.size(0), self.hdim).to(device)
c0 = torch.zeros(self.n_layer, x.size(0), self.hdim).to(device)
rnn_out, (hn, cn) = self.rnn(x, (h0, c0))
out = self.lin(rnn_out[:, -1, :])
return out
R = RecurrentNeuralNetworkClass(name='rnn', vocab_size=tokenizer.vocab_size, emb_dim=128, hdim=256, ydim=2, n_layer=2).to(device)
loss_fn = nn.CrossEntropyLoss()
optimizer = optim.Adam(R.parameters(), lr=1e-3)
클래스 정의:
RecurrentNeuralNetworkClass라는 이름의 RNN 모델 클래스를 정의하였음.nn.Module을 상속받아 모델을 구현하였음.초기화 메서드:
__init__ 메서드에서 모델의 주요 파라미터들을 초기화함.name, vocab_size, emb_dim, hdim, ydim, n_layer 등 모델의 구조와 관련된 변수를 설정함.임베딩 레이어:
self.embedding을 사용하여 단어를 임베딩 벡터로 변환함.nn.Embedding을 사용하여 vocab_size와 emb_dim을 파라미터로 설정함.RNN 레이어:
self.rnn은 nn.LSTM을 사용하여 순환 신경망을 정의함.input_size, hidden_size, num_layers, batch_first 등의 파라미터를 설정함.선형 레이어:
self.lin은 nn.Linear를 사용하여 RNN의 출력을 최종 출력으로 변환함.hidden_dim과 output_dim을 설정함.순전파 메서드:
forward 메서드에서 입력 데이터를 임베딩하고, RNN을 통과시킴.hidden state와 cell state를 정의함.모델 초기화 및 설정:
R 객체를 생성하여 모델을 초기화함.loss_fn)와 옵티마이저(optimizer)를 설정함.device에 할당함. (GPU 또는 CPU)class RecurrentNeuralNetworkClass(nn.Module):
def __init__(self, name='rnn', vocab_size=30522, emb_dim=128, hdim=256, ydim=2, n_layer=3):
super(RecurrentNeuralNetworkClass, self).__init__()
self.name = name
self.vocab_size = vocab_size
self.emb_dim = emb_dim
self.hdim = hdim
self.ydim = ydim
self.n_layer = n_layer
self.embedding = nn.Embedding(self.vocab_size, self.emb_dim)
self.rnn = nn.LSTM(input_size=self.emb_dim, hidden_size=self.hdim, num_layers=self.n_layer, batch_first=True)
self.lin = nn.Linear(self.hdim, self.ydim)
def forward(self, x):
x = self.embedding(x)
h0 = torch.zeros(self.n_layer, x.size(0), self.hdim).to(device)
c0 = torch.zeros(self.n_layer, x.size(0), self.hdim).to(device)
rnn_out, (hn, cn) = self.rnn(x, (h0, c0))
out = self.lin(rnn_out[:, -1, :])
return out
R = RecurrentNeuralNetworkClass(name='rnn', vocab_size=tokenizer.vocab_size, emb_dim=128, hdim=256, ydim=2, n_layer=2).to(device)
loss_fn = nn.CrossEntropyLoss()
optimizer = optim.Adam(R.parameters(), lr=1e-3)
클래스 정의:
RecurrentNeuralNetworkClass라는 이름의 RNN 모델 클래스를 정의하였음.nn.Module을 상속받아 모델을 구현하였음.초기화 메서드:
__init__ 메서드에서 모델의 주요 파라미터들을 초기화함.name, vocab_size, emb_dim, hdim, ydim, n_layer 등 모델의 구조와 관련된 변수를 설정함.임베딩 레이어:
self.embedding을 사용하여 단어를 임베딩 벡터로 변환함.nn.Embedding을 사용하여 vocab_size와 emb_dim을 파라미터로 설정함.RNN 레이어:
self.rnn은 nn.LSTM을 사용하여 순환 신경망을 정의함.input_size, hidden_size, num_layers, batch_first 등의 파라미터를 설정함.선형 레이어:
self.lin은 nn.Linear를 사용하여 RNN의 출력을 최종 출력으로 변환함.hidden_dim과 output_dim을 설정함.순전파 메서드:
forward 메서드에서 입력 데이터를 임베딩하고, RNN을 통과시킴.hidden state와 cell state를 정의함.모델 초기화 및 설정:
R 객체를 생성하여 모델을 초기화함.loss_fn)와 옵티마이저(optimizer)를 설정함.device에 할당함. (GPU 또는 CPU)def func_eval(model, data_iter, device):
with torch.no_grad():
n_total, n_correct = 0, 0
model.eval()
for batch in data_iter:
input_ids = batch['input_ids'].to(device)
labels = batch['labels'].to(device)
outputs = model(input_ids)
_, preds = torch.max(outputs, 1)
n_correct += (preds == labels).sum().item()
n_total += input_ids.size(0)
val_accr = (n_correct / n_total)
model.train()
return val_accr
이 함수는 모델을 평가하는 함수로, data_iter에서 배치를 받아 모델의 예측값과 실제 라벨을 비교하여 정확도를 계산함.
EPOCHS = 5
log_file = open("training_log.txt", "w")
for epoch in range(EPOCHS):
R.train()
loss_val_sum = 0
for batch in train_loader:
input_ids = batch['input_ids'].to(device)
labels = batch['labels'].to(device)
optimizer.zero_grad()
outputs = R(input_ids)
loss = loss_fn(outputs, labels)
loss.backward()
optimizer.step()
loss_val_sum += loss.item()
loss_val_avg = loss_val_sum / len(train_loader)
train_accr = func_eval(R, train_loader, device)
test_accr = func_eval(R, test_loader, device)
log_file.write(f"Epoch [{epoch+1}/{EPOCHS}], Loss: {loss_val_avg:.4f}, Train Acc: {train_accr:.4f}, Test Acc: {test_accr:.4f}\n")
print(f"Epoch [{epoch+1}/{EPOCHS}], Loss: {loss_val_avg:.4f}, Train Acc: {train_accr:.4f}, Test Acc: {test_accr:.4f}")
log_file.close()
이 부분은 모델을 5 에포크 동안 학습시키는 코드임. 각 에포크마다 손실값을 계산하고, 학습 데이터와 테스트 데이터에 대한 정확도를 측정하여 로그 파일에 기록함.
n_sample = 25
sample_indices = np.random.choice(len(test_dataset), n_sample, replace=False)
test_samples = [test_dataset[i] for i in sample_indices]
test_x = torch.stack([sample['input_ids'] for sample in test_samples]).to(device)
test_y = torch.tensor([sample['labels'] for sample in test_samples]).to(device)
with torch.no_grad():
R.eval()
y_pred = R(test_x)
y_pred = y_pred.argmax(axis=1)
with open("test_results.txt", "w") as result_file:
for idx in range(n_sample):
result_file.write(f"Pred: {y_pred[idx].item()}, Label: {test_y[idx].item()}, Text: {tokenizer.decode(test_x[idx])}\n")
이 부분은 테스트 데이터셋에서 임의의 샘플을 선택하여 모델의 예측 결과를 기록함. 예측값, 실제 라벨, 텍스트를 파일에 저장하여 결과를 확인할 수 있음.