태그 보관물: MNIST

PyTorch MNIST Example

파이토치(PyTorch)로 텐서플로우 튜토리얼에 있는 MNIST 예제를 재현해 보았습니다. 이 코드는 파이토치의 MNIST 예제를 참고했으며 주피터 노트북으로 작성되어 깃허브에 올려져 있습니다. 당연하지만 분류 결과는 텐서플로우로 만든 예제와 큰 차이가 없습니다.

파이토치는 버전 0.1.6을 사용하였습니다. 파이토치 설치는 이 포스트를 참고하세요. 파이토치는 코어 C 라이브러리를 토치(Torch)와 공유하고 있습니다. 그 외에는 모두 파이썬으로 구현되어 있으며 별도로 C API 가 없습니다. 오직 파이썬을 위해서 구현된 라이브러리이고 또 다이나믹 컴퓨테이션 그래프(Dynamic Computation Graph)를 지원하기 때문에 코드 모습이 일반 프로그래밍처럼 자연스러웠습니다.

파이토치의 예제와 유사하게 torch.nn.Module 클래스를 상속받아 모델 클래스를 만들었습니다.

class MnistModel(nn.Module):
    def __init__(self):
        super(MnistModel, self).__init__()
        # input is 28x28
        # padding=2 for same padding
        self.conv1 = nn.Conv2d(1, 32, 5, padding=2)
        # feature map size is 14*14 by pooling
        # padding=2 for same padding
        self.conv2 = nn.Conv2d(32, 64, 5, padding=2)
        # feature map size is 7*7 by pooling
        self.fc1 = nn.Linear(64*7*7, 1024)
        self.fc2 = nn.Linear(1024, 10)
        
    def forward(self, x):
        x = F.max_pool2d(F.relu(self.conv1(x)), 2)
        x = F.max_pool2d(F.relu(self.conv2(x)), 2)
        x = x.view(-1, 64*7*7)   # reshape Variable
        x = F.relu(self.fc1(x))
        x = F.dropout(x, training=self.training)
        x = self.fc2(x)
        return F.log_softmax(x)
    
model = MnistModel()

Module 를 상속받은 클래스는 뉴럴 네트워크의 정방향 계산을 수행하는 forward() 메소드를 구현해야만 합니다. 이 forward() 메소드는 model 오브젝트를 데이터와 함께 호출하면 자동으로 실행이됩니다.

풀링과 활성화 함수들은 torch.nn.functional 아래에 있고 함수처럼 불러서 사용할 수 있습니다. 이런 함수들은 Conv2d 클래스처럼 유사하게 forward(), backward() 메소드를 가진 클래스를 감싸고 있는 형태입니다. 최종 loss 에서 backward() 함수가 호출되면 거꾸로 짚어가면서 역방향 계산이 연쇄적으로 일어납니다. Conv2d 함수의 파라미터에서 볼 수 있듯이 패딩과 스트라이드를 명시적으로 지정해 주어야 합니다. 콘볼루션의 패딩 계산은 이 포스트를 참고하세요.

Linear 함수는 일반적인 완전연결(fully connected) 레이어를 의미하는 것으로 이것뿐만 아니라 많은 요소들이 토치의 네이밍을 따르고 있는 것 같습니다. 아마도 토치를 썼던 사람은 쉽게 사용할 수 있을 것 같습니다.

model.train()
train_loss = []
train_accu = []
i = 0
for epoch in range(15):
    for data, target in train_loader:
        data, target = Variable(data), Variable(target)
        optimizer.zero_grad()
        output = model(data)
        loss = F.nll_loss(output, target)
        loss.backward()    # calc gradients
        train_loss.append(loss.data[0])
        optimizer.step()   # update gradients
        prediction = output.data.max(1)[1]   # first column has actual prob.
        accuracy = prediction.eq(target.data).sum()/batch_size*100
        train_accu.append(accuracy)
        if i % 1000 == 0:
            print('Train Step: {}\tLoss: {:.3f}\tAccuracy: {:.3f}'.format(i, loss.data[0], accuracy))
        i += 1

모델을 학습시키는 부분도 직관적입니다. Negative Log Likelihood를 의미하는 nll_loss 함수에 출력과 타겟값을 넣고 loss.backward() 를 호출한 다음 optimizer.step() 으로 파라미터를 업데이트 합니다. 여기서 target 은 원-핫-인코딩이 아닌 타겟 레이블을 그대로 넣습니다. loss 와 accuracy 를 리스트에 저장하여 맷플롯립으로 그래프를 그렸습니다.

pytorch-mnist-loss

loss

pytorch-mnist-accuracy

accuracy

모델을 훈련할 땐 model.train() , 테스트할 땐 model.eval() 함수를 호출하면 Module 클래스의 훈련 상태 여부를 바꾸게 되며 이는 dropout 같은 함수가 드롭아웃을 적용할지 안할지를 결정하게 만듭니다.

model.eval()
correct = 0
for data, target in test_loader:
    data, target = Variable(data, volatile=True), Variable(target)
    output = model(data)
    prediction = output.data.max(1)[1]
    correct += prediction.eq(target.data).sum()

print('Test set: Accuracy: {:.2f}%'.format(100. * correct / len(test_loader.dataset)))

최종결과는 99.29%를 얻었습니다. 전체 코드는 깃허브에서 확인할 수 있습니다.

PixelCNN [1601.06759] Summary

Pixel Recurrent Neural Networks‘[1601.06759] 페이퍼에는 세개의 네트워크 모델을 설명하고 있습니다. PixelRNN 으로 명명된 Row LSTM, Diagonal BiLSTM 과 PixelCNN 입니다. 이 중에 PixelCNN 부분을 재현해 보았습니다. PixelCNN 은 PixelRNN 에 비해 비교적 학습 속도가 빠르고 PixelCNN 후속 페이퍼도 계속 나오고 있습니다. 문제를 단순화하기 위해 MNIST 손글씨 숫자 흑백 이지미를 대상으로 하였습니다.

PixelCNN 의 몇가지 주요한 핵심 중 하나는 입력 특성을 콘볼루션할 때 필터의 일부분만 사용한다는 점입니다. 입력 특성맵과 출력 특성맵이 같은 크기이고(텐서플로우에서 ‘SAME’ 패딩) 스트라이드가 1일 때 홀수 크기의 필터는 필터의 중심이 입력 특성맵의 모든 픽셀을 지나가게 됩니다. 이 때 필터의 중심에서 오른쪽 그리고 아래 부분의 가중치를 0 으로 셋팅하면 입력 특성맵에서 필터의 중심의 왼쪽과 위쪽의 픽셀에만 가중치를 적용(콘볼빙)하게 됩니다. 이를 그림으로 나타낸 것이 페이퍼에 잘 나와 있습니다.

왼쪽 그림에서 아래 레이에에서 콘볼빙하여 윗 레이어의 특성 맵을 만들 때 아래 레이어의 3 x 3 필터 중심(붉은색)의 왼쪽과 위의 픽셀에만 가중치가 곱해지는 것을 보이고 있습니다. 이를 마스크 콘볼루션(masked convolution)이라고 부르고 있습니다. 필터를 마스크하기 위해서는 필터의 중심에서 우측과 아래의 가중치를 0 으로 셋팅하면 됩니다.

계속 읽기

TensorFlow 코딩게임(Codingame)

스크린샷 2016-04-26 오후 1.20.35

여러가지 프로그래밍을 게임하듯 즐기면서 배울 수 있는 코딩게임(www.condingame.com)에 텐서플로우 코딩게임이 생겼습니다.

이 게임은 텐서플로우를 이용해서 MNIST 데이터셋인 손글씨 숫자(handwritten digits)를 인식하는 뉴럴 네트워크를 만드는 것입니다. 목표 정확도는 50% 입니다. “Enter your code between here” 와 “And here” 사이에 있는 print 문을 주석 처리하고 텐서플로우 튜토리얼을 참고하여 알고리즘을 파이썬 코드로 작성하시면 됩니다. 작성이 완료되면 맨 아래 두개 문장의 주석을 제거해 주시고 오른쪽 아래 부분에 위치한 ‘PLAY TESTCASE’ 버튼을 눌러 주시면 됩니다.

튜토리얼을 무난하게 따라 하셨다면 91.2% 의 정확도를 얻으실 수 있습니다. 좀 더 정확도를 높이려면 콘볼루션 네트워크를 만드는 텐서플로우의 다른 튜토리얼도 참고하세요.