torchtext.legacy.data.Dataset을 상속하여 데이터프레임을 로드할 수 있습니다.
class DataFrameDataset(data.Dataset):def__init__(self, df, fields, is_test=False, **kwargs): examples = []for i, row in df.iterrows():# text, label 컬럼명은 필요시 변경하여 사용합니다 label = row['label'] ifnot is_test elseNone text = row['text'] examples.append(data.Example.fromlist([text, label], fields))super().__init__(examples, fields, **kwargs)@staticmethoddef sort_key(ex):returnlen(ex.text)@classmethoddef splits(cls, fields, train_df, val_df=None, test_df=None, **kwargs): train_data, val_data, test_data = (None, None, None) data_field = fieldsif train_df isnotNone: train_data = cls(train_df.copy(), data_field, **kwargs)if val_df isnotNone: val_data = cls(val_df.copy(), data_field, **kwargs)if test_df isnotNone: test_data = cls(test_df.copy(), data_field, False, **kwargs)returntuple(d for d in (train_data, val_data, test_data) if d isnotNone)
# 토크나이저 정의 (다른 토크나이저로 대체 가능)tokenizer = get_tokenizer('basic_english')
앞선 내용과 마찬가지로 Field를 구성합니다.
TEXT = data.Field(sequential=True, # 순서를 반영 tokenize=tokenizer, # tokenizer 지정 fix_length=120, # 한 문장의 최대 길이 지정 lower=True, # 소문자화 batch_first=True) # batch 를 가장 먼저 출력LABEL = data.Field(sequential=False)# fiels 변수에 List(tuple(컬럼명, 변수)) 형식으로 구성 후 대입fields = [('text', TEXT), ('label', LABEL)]
# 단어 사전 생성TEXT.build_vocab(train_ds, max_size=1000, # 최대 vocab_size 지정 (미지정시 전체 단어사전 개수 대입) min_freq=5, # 최소 빈도 단어수 지정 vectors='glove.6B.100d') # 워드임베딩 vector 지정, None으로 지정시 vector 사용 안함LABEL.build_vocab(train_ds)
# 단어 사전 개수 출력NUM_VOCABS =len(TEXT.vocab)print(f'Number of Vocabs: {NUM_VOCABS}')# 개수 1000 + <unk> + <pad> : 총 1002개EMBEDDING_DIM =25print(f'Embedding Dimension: {EMBEDDING_DIM}')SEQ_LENGTH =120print(f'Sequence Length: {MAX_SEQ_LENGTH}')print(f'Number of Batch Size: {BATCH_SIZE}')
Number of Vocabs: 1002
Embedding Dimension: 25
Sequence Length: 120
Number of Batch Size: 32
x.shape
torch.Size([32, 120])
import torch.nn as nn# Number of Vocabs, Embedding Dimension as an inputembedding = nn.Embedding(num_embeddings=NUM_VOCABS, embedding_dim=EMBEDDING_DIM, padding_idx=1, device=device)embedding
입력 텐서와 출력 텐서의 shape를 (batch, seq, feature) 형태를 가지도록 합니다. 만약 False로 설정된 경우에는 (seq, batch, feature)로 입출력이 됩니다. 일반적인 경우 batch가 첫 번째 shape에 위치하기 때문에 batch_first=True로 주로 설정합니다.
def model_train(model, data_loader, loss_fn, optimizer, config, device):# 모델을 훈련모드로 설정합니다. training mode 일 때 Gradient 가 업데이트 됩니다. 반드시 train()으로 모드 변경을 해야 합니다. model.train()# loss와 accuracy 계산을 위한 임시 변수 입니다. 0으로 초기화합니다. running_loss =0 corr =0 counts =0 total_counts =0# 예쁘게 Progress Bar를 출력하면서 훈련 상태를 모니터링 하기 위하여 tqdm으로 래핑합니다. prograss_bar = tqdm(data_loader, unit='batch', total=len(data_loader), mininterval=1)# mini-batch 학습을 시작합니다.for idx, data inenumerate(prograss_bar):# text, label 데이터를 device 에 올립니다. (cuda:0 혹은 cpu) text = data.text.to(device) label = data.label.to(device) label.sub_(1)# 누적 Gradient를 초기화 합니다. optimizer.zero_grad() initial_hidden = torch.zeros(2*config['num_layers'], len(text), config['hidden_size']).to(device) initial_cell = torch.zeros(2*config['num_layers'], len(text), config['hidden_size']).to(device)# Forward Propagation을 진행하여 결과를 얻습니다. output = model(text, (initial_hidden, initial_cell))# 손실함수에 output, label 값을 대입하여 손실을 계산합니다. loss = loss_fn(output, label)# 오차역전파(Back Propagation)을 진행하여 미분 값을 계산합니다. loss.backward()# 계산된 Gradient를 업데이트 합니다. optimizer.step()# output의 max(dim=1)은 max probability와 max index를 반환합니다.# max probability는 무시하고, max index는 pred에 저장하여 label 값과 대조하여 정확도를 도출합니다. _, pred = output.max(dim=1)# pred.eq(lbl).sum() 은 정확히 맞춘 label의 합계를 계산합니다. item()은 tensor에서 값을 추출합니다.# 합계는 corr 변수에 누적합니다. corr += pred.eq(label).sum().item() counts += label.size(0)# loss 값은 1개 배치의 평균 손실(loss) 입니다. img.size(0)은 배치사이즈(batch size) 입니다.# loss 와 img.size(0)를 곱하면 1개 배치의 전체 loss가 계산됩니다.# 이를 누적한 뒤 Epoch 종료시 전체 데이터셋의 개수로 나누어 평균 loss를 산출합니다. running_loss += (loss.item() * label.size(0)) total_counts += label.size(0)# 프로그레스바에 학습 상황 업데이트 prograss_bar.set_description(f"training loss: {running_loss/total_counts:.5f}, training accuracy: {corr / counts:.5f}")# 누적된 정답수를 전체 개수로 나누어 주면 정확도가 산출됩니다. acc = corr / total_counts# 평균 손실(loss)과 정확도를 반환합니다.# train_loss, train_accreturn running_loss / total_counts, acc
def model_evaluate(model, data_loader, loss_fn, config, device):# model.eval()은 모델을 평가모드로 설정을 바꾸어 줍니다. # dropout과 같은 layer의 역할 변경을 위하여 evaluation 진행시 꼭 필요한 절차 입니다. model.eval()# Gradient가 업데이트 되는 것을 방지 하기 위하여 반드시 필요합니다.with torch.no_grad():# loss와 accuracy 계산을 위한 임시 변수 입니다. 0으로 초기화합니다. corr =0 running_loss =0 total_counts =0# 배치별 evaluation을 진행합니다.for data in data_loader:# text, label 데이터를 device 에 올립니다. (cuda:0 혹은 cpu) text = data.text.to(device) label = data.label.to(device) label.data.sub_(1) initial_hidden = torch.zeros(2*config['num_layers'], len(text), config['hidden_size']).to(device) initial_cell = torch.zeros(2*config['num_layers'], len(text), config['hidden_size']).to(device)# 모델에 Forward Propagation을 하여 결과를 도출합니다. output = model(text, (initial_hidden, initial_cell))# output의 max(dim=1)은 max probability와 max index를 반환합니다.# max probability는 무시하고, max index는 pred에 저장하여 label 값과 대조하여 정확도를 도출합니다. _, pred = output.max(dim=1)# pred.eq(lbl).sum() 은 정확히 맞춘 label의 합계를 계산합니다. item()은 tensor에서 값을 추출합니다.# 합계는 corr 변수에 누적합니다. corr += torch.sum(pred.eq(label)).item()# loss 값은 1개 배치의 평균 손실(loss) 입니다. img.size(0)은 배치사이즈(batch size) 입니다.# loss 와 img.size(0)를 곱하면 1개 배치의 전체 loss가 계산됩니다.# 이를 누적한 뒤 Epoch 종료시 전체 데이터셋의 개수로 나누어 평균 loss를 산출합니다. running_loss += loss_fn(output, label).item() * label.size(0) total_counts += label.size(0)# validation 정확도를 계산합니다.# 누적한 정답숫자를 전체 데이터셋의 숫자로 나누어 최종 accuracy를 산출합니다. acc = corr / total_counts# 결과를 반환합니다.# val_loss, val_accreturn running_loss / total_counts, acc
# 최대 Epoch을 지정합니다.num_epochs =50# checkpoint로 저장할 모델의 이름을 정의 합니다.model_name ='LSTM-Text-Classification'min_loss = np.inf# Epoch 별 훈련 및 검증을 수행합니다.for epoch inrange(num_epochs):# Model Training# 훈련 손실과 정확도를 반환 받습니다. train_loss, train_acc = model_train(model, train_iterator, loss_fn, optimizer, config, device)# 검증 손실과 검증 정확도를 반환 받습니다. val_loss, val_acc = model_evaluate(model, test_iterator, loss_fn, config, device) # val_loss 가 개선되었다면 min_loss를 갱신하고 model의 가중치(weights)를 저장합니다.if val_loss < min_loss:print(f'[INFO] val_loss has been improved from {min_loss:.5f} to {val_loss:.5f}. Saving Model!') min_loss = val_loss torch.save(model.state_dict(), f'{model_name}.pth')# Epoch 별 결과를 출력합니다.print(f'epoch {epoch+1:02d}, loss: {train_loss:.5f}, acc: {train_acc:.5f}, val_loss: {val_loss:.5f}, val_accuracy: {val_acc:.5f}')
training loss: 1.61319, training accuracy: 0.18371: 100% 56/56 [00:01<00:00, 50.33batch/s]
[INFO] val_loss has been improved from inf to 1.60184. Saving Model!
epoch 01, loss: 1.61319, acc: 0.18371, val_loss: 1.60184, val_accuracy: 0.24270
training loss: 1.60056, training accuracy: 0.25056: 100% 56/56 [00:01<00:00, 50.71batch/s]
[INFO] val_loss has been improved from 1.60184 to 1.58833. Saving Model!
epoch 02, loss: 1.60056, acc: 0.25056, val_loss: 1.58833, val_accuracy: 0.25393
training loss: 1.56228, training accuracy: 0.30618: 100% 56/56 [00:01<00:00, 51.45batch/s]
[INFO] val_loss has been improved from 1.58833 to 1.56553. Saving Model!
epoch 03, loss: 1.56228, acc: 0.30618, val_loss: 1.56553, val_accuracy: 0.30112
training loss: 1.48211, training accuracy: 0.35506: 100% 56/56 [00:01<00:00, 51.08batch/s]
[INFO] val_loss has been improved from 1.56553 to 1.46125. Saving Model!
epoch 04, loss: 1.48211, acc: 0.35506, val_loss: 1.46125, val_accuracy: 0.39551
training loss: 1.36006, training accuracy: 0.40506: 100% 56/56 [00:01<00:00, 51.70batch/s]
[INFO] val_loss has been improved from 1.46125 to 1.35223. Saving Model!
epoch 05, loss: 1.36006, acc: 0.40506, val_loss: 1.35223, val_accuracy: 0.42022
training loss: 1.24720, training accuracy: 0.46629: 100% 56/56 [00:01<00:00, 51.67batch/s]
[INFO] val_loss has been improved from 1.35223 to 1.34175. Saving Model!
epoch 06, loss: 1.24720, acc: 0.46629, val_loss: 1.34175, val_accuracy: 0.41798
training loss: 1.17133, training accuracy: 0.50393: 100% 56/56 [00:01<00:00, 51.09batch/s]
[INFO] val_loss has been improved from 1.34175 to 1.24888. Saving Model!
epoch 07, loss: 1.17133, acc: 0.50393, val_loss: 1.24888, val_accuracy: 0.48539
training loss: 1.06987, training accuracy: 0.53539: 100% 56/56 [00:01<00:00, 50.98batch/s]
[INFO] val_loss has been improved from 1.24888 to 1.23465. Saving Model!
epoch 08, loss: 1.06987, acc: 0.53539, val_loss: 1.23465, val_accuracy: 0.48315
training loss: 1.03832, training accuracy: 0.55730: 100% 56/56 [00:01<00:00, 50.75batch/s]