Chainer에서 Autoencoder (+ trainer 사용법의 비망록)

소개



지난번 Chainer의 새로운 기능, trainer를 사용해 CIFAR-10의 화상 분류에 도전하려고 했습니다만, 머신 파워의 사정상, 동작을 확인할 수 없게 끝났습니다.
그래서 이번에는 MNIST를 사용한 Autoencoder의 작성을 통해 trainer의 사용법을 확인하겠습니다.

Autoencoder에 관해서는 이 기사를 참고로 했습니다.
  • 【딥 러닝】Chainer에서 Autoencoder를 시험해 결과를 가시화해 본다.
  • Chainer에서 Deep Autoencoder 만들기

  • 구현



    MNIST의 필기 문자 1000개를 입력으로 하고, 숨겨진 계층을 1층 통과시켜 입력과 같게 되는 출력을 얻는 네트워크를 작성합니다.
    코드 전체는 여기 에 올리고 있습니다.

    네트워크 부분



    숨겨진 층의 단위 수는 64까지 좁혀 있습니다.
    또한 hidden=True로 호출하면 숨겨진 레이어를 출력할 수 있도록 하고 있습니다.
    class Autoencoder(chainer.Chain):
        def __init__(self):
            super(Autoencoder, self).__init__(
                    encoder = L.Linear(784, 64),
                    decoder = L.Linear(64, 784))
    
        def __call__(self, x, hidden=False):
            h = F.relu(self.encoder(x))
            if hidden:
                return h
            else:
                return F.relu(self.decoder(h))
    

    데이터 작성 부분



    MNIST의 데이터를 읽어 교사 데이터와 테스트 데이터를 만듭니다.
    교사 데이터의 레이블은 필요하지 않으며 출력은 입력과 동일하므로 약간의 데이터 형태를 낳습니다.
    # MNISTのデータの読み込み
    train, test = chainer.datasets.get_mnist()
    
    # 教師データ
    train = train[0:1000]
    train = [i[0] for i in train]
    train = tuple_dataset.TupleDataset(train, train)
    train_iter = chainer.iterators.SerialIterator(train, 100)
    
    # テスト用データ
    test = test[0:25]
    

    모델링


    model = L.Classifier(Autoencoder(), lossfun=F.mean_squared_error)
    model.compute_accuracy = False
    optimizer = chainer.optimizers.Adam()
    optimizer.setup(model)
    

    여기서 주의하는 것이 2점
  • loss 함수 정의
    L.Classifier로 모델을 정의했을 때, 디폴트에서는 loss 함수는 softmax_cross_entropy가 되는 것 같습니다만, 이번은 mean_squared_error를 사용하고 싶으므로, lossfun로 정의하지 않으면 안됩니다.
  • accuracy를 계산하지 않음
    이번에는 교사 데이터에 레이블을 사용하지 않으므로 accuracy 계산이 필요하지 않습니다. 그래서 compute_accuracy를 False로 둘 필요가 있습니다.

  • 학습 부분



    특히 설명이 필요 없다고 생각합니다.
    trainer를 사용할 수 있게 되고 나서, 이 부분이 간단하게 쓸 수 있게 되어 도움이 됩니다^^
    updater = training.StandardUpdater(train_iter, optimizer, device=-1)
    trainer = training.Trainer(updater, (N_EPOCH, 'epoch'), out="result")
    trainer.extend(extensions.LogReport())
    trainer.extend(extensions.PrintReport( ['epoch', 'main/loss']))
    trainer.extend(extensions.ProgressBar())
    
    trainer.run()
    

    결과 확인



    함수를 만들어 matplotlib에서 결과를 플로팅합니다.
    이미지 상단에 빨간색 문자로 원래 라벨을 인쇄합니다. 좌표의 조정을 제대로 하고 있지 않기 때문에 약간 쓰고 있는 부분도 있습니다만...

    참고로 이 함수에 test용 데이터를 그대로 입력하면 원래 데이터의 이미지가 출력됩니다.
    def plot_mnist_data(samples):
        for index, (data, label) in enumerate(samples):
            plt.subplot(5, 5, index + 1)
            plt.axis('off')
            plt.imshow(data.reshape(28, 28), cmap=cm.gray_r, interpolation='nearest')
            n = int(label)
            plt.title(n, color='red')
        plt.show()
    
    pred_list = []
    for (data, label) in test:
        pred_data = model.predictor(np.array([data]).astype(np.float32)).data
        pred_list.append((pred_data, label))
    plot_mnist_data(pred_list)
    

    결과



    epoch를 늘려가면 어떻게 변화해 나가는지를 살펴볼 것입니다.

    원본 이미지





    0~9까지 모두 포함한 16개의 이미지입니다. 이 16 종류의 변화를 살펴 보겠습니다.

    epoch = 1





    텔레비전의 모래 폭풍처럼 이 시점에서 무엇이 무엇인지 모르겠습니다.

    epoch = 5





    드디어 숫자와 같은 것이 보였지만 아직 숫자로는 알 수 없습니다.

    epoch = 10





    0, 1, 3 등은 점점 형태가 보였습니다. 2단째의 6은 아직 부서져서 잘 모르겠습니다.

    epoch = 20





    거의 숫자가 보였습니다.

    epoch = 100





    단번에 100까지 진행해 보았습니다. 거의 무너지고 있던 2단째의 6도 형태가 보였습니다.
    더 epoch를 늘리면 분명하게 보일 것입니다 만, 이번은 여기까지.

    결론



    네트워크가 숫자를 숫자로 인식하는 과정을 보는 것은 즐거웠습니다.
    trainer는 편리합니다만, loss 함수등 여러가지 부분이 자동으로 정해져 버리므로 주의가 필요하네요.
    (2016.08.10 수정)
    loss 함수가 기본적으로 soft_max_cross_entropy로 설정되는 것은 trainer가 아니라 Classifer의 사양이었습니다. trainer에서 사용하는 updater를 정의할 때 loss 함수를 지정하게 됩니다만, 일반적으로는 optimizer에 세트한 것이 링크되는 것 같습니다.

    좋은 웹페이지 즐겨찾기