世界の隅からhello world

元情報系大学生の備忘録

ひらがなくずし字のMNIST,KMNISTを試してみた

ひらがなくずし字のデータセットKMNISTが公開されたので,早速試してみました.

KMNISTとは?

https://github.com/rois-codh/kmnist/blob/master/images/kmnist_examples.png?raw=true (画像引用元:https://github.com/rois-codh/kmnist

人文学オープンデータ共同利用センターから公開された,MNIST形式のひらがなくずし字データセットです.

「お,き,す,つ,な,は,ま,や,れ,を」計10種のひらがなくずし文字について,28x28のグレースケールの画像が訓練データ60,000枚,評価データ10,000枚収められています.このとおりMNISTの形式に揃えられており,Fashion-MNIST[1]のように,いわば"alt-MNIST"として利用できます.データセットは以下のgithubリポジトリからダウンロード可能です.

github.com

NeurIPS 2018のworkshopでお披露目されたようで,論文はarXivで公開されています[2] (https://arxiv.org/abs/1812.01718).

人文学オープンデータ共同利用センターのアナウンスページで,ひらがなくずし字のデータセットをMNISTの形式に合わせを公開した意図が説明されています.

codh.rois.ac.jp

しかしKuzushiji-MNISTは、10個の文字種と十分なサンプルを揃えており、機械学習の研究などにすぐ使えるデータセットとなっています。MNISTの変種としてのKMNISTを気軽に使ってみながら、くずし字を対象とした研究に興味をもつ研究者が増えていってほしいと思います。またこのデータセットは、文字認識に限らず文字生成など様々な研究にも利用できるため、自由な発想でデータセットを使いながらくずし字を対象とした研究の多様性が拡大することを望んでいます。さらにそうした研究の広がりを通して、くずし字×機械学習の研究に関する社会の認識が向上することで、KMNISTの基礎となるくずし字データセットへの協力と活用がさらに広がっていくことが、我々の長期的な目標です。

ぜひ元ページを読んでいただきたいのですが,中々にエモいマニフェストになっています.言葉の端々から,「機械学習界隈の人たちにも是非人文学の世界へ目を向けてもらい,新しい世界を一緒に作っていきたい」という熱い思いを感じます.

KMNISTを隠れ層2層の多層パーセプトロンで分類

それならば是非試してみよう,ということで,おなじみchainerのMNISTサンプルコードを改変し,KMNISTを試してみました.

chainer/examples/mnist at master · chainer/chainer · GitHub

モデルは1000ユニットの隠れ層が2層の多層パーセプトロンです.学習にはCPUを使います.

実験環境は以下の通りです.

Thinkpad X1 Carbon 2017 (intel Corei7 7th-Gen)
ubuntu 18.08 LTS
python 3.6.1
chainer 6.0.0b1

なお,大したものではないですが,本記事で使ったコードはGithubリポジトリで公開しています. github.com

データのダウンロード・データの確認

https://github.com/rois-codh/kmnistで公開されているデータセットから,npz形式のものをダウンロードしました.訓練・評価ごとに画像とラベルの計4つのnpzがあります.

$ mkdir -p data
$ wget http://codh.rois.ac.jp/kmnist/dataset/kmnist/kmnist-train-imgs.npz -P data
$ wget http://codh.rois.ac.jp/kmnist/dataset/kmnist/kmnist-train-labels.npz -P data
$ wget http://codh.rois.ac.jp/kmnist/dataset/kmnist/kmnist-test-imgs.npz -P data
$ wget http://codh.rois.ac.jp/kmnist/dataset/kmnist/kmnist-test-labels.npz -P data

データの形式を確認します.

>>> train_imgs = np.load("data/kmnist-train-imgs.npz")["arr_0"]
>>> train_imgs.shape
(60000, 28, 28)
>>> train_imgs.dtype
dtype('uint8')
>>> train_imgs[0]
array([[  0,   0,   0,   0,   0,   0,   0,   0,  10,   7, 233,  86,   0,
          0,  32,  99,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0,  54, 255,  84,   0,
          0,   7, 151,  19,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0,   0, 152, 251,  14,   0,
          0,  25, 155,  98,   0,   0,   0,   0,   0,   0,   0,   0,   0,
          0,   0],
       [  0,   0,   0,   0,   0,   0,   0,   0, 129, 248, 188,   0,   0,
          0,  80, 250, 191,   1,  78,  51,   0,   0,   0,   0,   0,   0,
          0,   0], ...
  • 値は0-255のnp.unit8
  • 1画像は(28, 28)の2次元配列

で保存されているようです.

matplotlibで可視化してみた結果がこちら. や

データの前処理

chainer mnistのサンプルに合わせ,次の処理を行います.

  • 255.で割って [0, 1] に正規化
  • (28, 28)の2次元配列を(784,)の1次元ベクトルにflatten
    train_data = np.load(args.train_imgs)['arr_0'].\
                 reshape((60000, 784)).astype(np.float32)/255.
    train_labels = [int(n) for n in np.load(args.train_label)['arr_0']]
    train = TupleDataset(train_data, train_labels)

    test_data = np.load(args.test_imgs)['arr_0'].\
                reshape((10000, 784)).astype(np.float32)/255.
    test_labels = [int(n) for n in np.load(args.test_label)['arr_0']]
    test = TupleDataset(test_data, test_labels)

Datasetクラスは,mnistのサンプルと同様にTupleDatasetを使っています.

学習

以上でchainerのmnistサンプルの改変は終わりです.簡単です.では,学習を回してみます.

$ python train_kmnist.py
Device: <CpuDevice (numpy)>
# unit: 1000
# Minibatch-size: 100
# epoch: 20

epoch main/loss validation/main/loss main/accuracy validation/main/accuracy elapsed_time
1 0.301962 0.403972 0.9072 0.8797 19.6462
2 0.112909 0.344197 0.9651 0.9063 41.2962
3 0.0662489 0.326582 0.979183 0.9127 65.2197
4 0.0421354 0.363997 0.9866 0.9126 87.8175
5 0.0315507 0.428015 0.990017 0.9074 111.145
6 0.0290646 0.468858 0.99055 0.9088 134.062
7 0.0266913 0.439887 0.991733 0.9149 155.707
8 0.0216323 0.485592 0.993117 0.911 178.109
9 0.0213224 0.486939 0.9936 0.9149 200.794
10 0.0204854 0.460457 0.994033 0.9201 225.533
11 0.0177287 0.547921 0.994717 0.9106 249.207
12 0.017653 0.508776 0.99485 0.9183 272.926
13 0.0115246 0.521045 0.996683 0.9208 297.484
14 0.0167068 0.510293 0.995117 0.9251 330.113
15 0.0166416 0.544714 0.995233 0.9212 363.777
16 0.0128668 0.611589 0.996233 0.9147 390.308
17 0.0136382 0.545968 0.9963 0.9185 419.165
18 0.0130388 0.627543 0.996267 0.9132 446.094
19 0.0092608 0.570508 0.997167 0.9236 472.661
20 0.0103855 0.663491 0.99695 0.914 498.62

グラフはこちら.

kmnist_loss_cpukmnist_accuracy_cpu
KMNIST学習時のロスとaccuracyの変化(CPU)

validation accuracyは3 epoch前後で0.92付近に達し,それ以降のepochではvalidation lossは上昇していますね.

ちなみに,本家のMNISTの学習時のロスとaccuracyの変化はこちら.

元祖MNIST学習時のロスとaccuracyの変化(CPU)

KMNISTに比べ,元祖MNISTの方がaccuracyは高く,lossは小さくなっています.KMNISTのほうが難しいデータセットと言えそうです.

まとめ

ひらがなくずし字のデータセットKMNISTを,chainerのMNIST用サンプルコードをごく一部改変し,学習を行いました.

MNISTの形式に合わせくずし文字のデータセットを公開したのは,とても素敵なアイディアだと感じます. 本稿での実験のように,ほんの少しコード修正でAlt-MNISTとして使えますし, 日本で育った人間としては,くずし文字のデータセットを実験に使えるのはとてもわくわくするものです.

深層学習界隈は,精度競争をはじめとして,ときに心をすり減らすような場面も多々ありますが, 少し目を転じてみれば,童心に返るような,素敵なテーマはたくさんあるものだなと感じた一時でした.

reference

[1] Xiao et al, "Fashion-MNIST: a Novel Image Dataset for Benchmarking Machine Learning Algorithms", arXiv:1708.07747, 2017.

[2] Clanuwat et al, "Deep Learning for Classical Japanese Literature", NeurIPS 2018 Workshop on Machine Learning for Creativity and Design, 2018.