지도학습: 양방향 LSTM (bidirectional LSTM, Bi-LSTM)

pppanghyun·2022년 7월 31일
0

Pytorch 기본

목록 보기
13/21

양방향 LSTM (Bi-LSTM)은

양방향 LSTM(Bi-LSTM)의 핵심 개념은 정방향으로만 학습을 진행하는 대신, 마지막 노드에서 뒤에서 앞으로(역방향) 실행되는 다른 LSTM을 추가하는 것이다. Bi-LSTM은 일반 LSTM 대비 역방향으로 정보를 전달하는 hidden layer를 추가해서 이러한 정보를 보다 유연하게 처리한다. (유연하게 = 성능이 좋다 아니겠습니까?)

결론적으로, 각 시점에서 hidden state가 이전 시점과 미래 시점의 정보를 모두 갖는 효과가 있기 때문에 모델을 전체 시계열 데이터로부터 학습할 수 있도록 하려는 경우 유용하다.
(코드도 LSTM과 매우 유사하고, hidden layer 부분만 조금 다르다)

RNN 모델을 사용한 이미지 분류

1. 데이터 불러오기 (MNIST classification)

# Load Data
tensor_mode = torchvision.transforms.ToTensor()
trainset = torchvision.datasets.MNIST(root="./data", train=True, transform=tensor_mode, download=True)
testset = torchvision.datasets.MNIST(root="./data", train=False, transform=tensor_mode, download=True)
trainloader = DataLoader(trainset, batch_size=128, shuffle=True)
testloader = DataLoader(testset, batch_size=128, shuffle=False)

2. 모델(코드는 LSTM과 매우 유사 (nn.LSTM 사용) forward 부분 수정 필요)

bidirectional = True 설정만 해주면 끝!

class BiLSTM(nn.Module):
    def __init__(self, input_size, hidden_size, num_layers, seq_length, num_classes, device):
        super(BiLSTM, self).__init__()
        self.device = device
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        self.seq_length = seq_length
        self.lstm = nn.LSTM(input_size, hidden_size, num_layers, batch_first=True, bidirectional=True) 
        # bidirectional 옵션만 사용하면 끝 !!
        self.fc = nn.Linear(seq_length*hidden_size * 2, num_classes)

    def forward(self, x): # layer의 개수도 두 배가 됨
        h0 = torch.zeros(self.num_layers * 2, x.size(0), self.hidden_size).to(self.device) 
        c0 = torch.zeros(self.num_layers * 2, x.size(0), self.hidden_size).to(self.device)
        out, _ = self.lstm(x, (h0, c0))
        #out = self.fc(out[:, -1, :])
        out = out.reshape(-1,self.seq_length*self.hidden_size * 2) # 배열을 1열로 만들어주기
        out = self.fc(out)
        return out

3. 파라미터 설정

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
sequence_length = trainset.data.size(1) # (28, 이미지의 가로축)
input_size = trainset.data.size(2) # (28, 이미지의 세로축)
num_layers = 2
hidden_size = 12
num_classes = 10

trainset.data.size()

# result
torch.Size([60000, 28, 28])

4. 학습

model = BiLSTM(input_size, hidden_size, num_layers, sequence_length, num_classes, device)
model = model.to(device)  

criterion = nn.CrossEntropyLoss() # 분류 문제
optimizer = optim.Adam(model.parameters(), lr=5e-3)

for epoch in range(11):
    correct = 0
    total = 0
    for data in trainloader:
        optimizer.zero_grad()
        inputs, labels = data[0].to(device).squeeze(1), data[1].to(device)  
        outputs = model(inputs)
        loss = criterion(outputs, labels)        
        loss.backward()
        optimizer.step()

        _, predicted = torch.max(outputs.detach(), 1)
        total += labels.size(0)
        correct += (predicted == labels).sum().item()

    print('[%d] train acc: %.2f' %(epoch, 100*correct/total))

5. 결과 보기

def accuracy(dataloader):
    correct = 0
    total = 0
    with torch.no_grad():
        model.eval()
        for data in dataloader:
            inputs, labels = data[0].to(device).squeeze(1), data[1].to(device)      
            outputs = model(inputs)
            _, predicted = torch.max(outputs, 1)
            total += labels.size(0)      
            correct += (predicted == labels).sum().item()

    acc = 100*correct/total
    model.train()
    return acc
    
train_acc = accuracy(trainloader)
test_acc = accuracy(testloader)
print("Train Acc: %.1f, Test Acc: %.1f" %(train_acc, test_acc)) 

# result
Train Acc: 99.5, Test Acc: 98.6

결론: 이미지를 처리할 때 굳이 CNN을 사용하지 않아도됨. (추후에 업로드할 Vit(Vision in transformer)도 성능상으로는 CNN 이김.

profile
pppanghyun

0개의 댓글