CUBE SUGAR CONTAINER

技術系のこと書きます。

Ubuntu 16.04 LTS のキーボードを日本語 (JIS) に変更する

たまにインストールしたとき間違えて英語 (US) に設定して後から直すことになるので。

使った環境は次の通り。

$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
$ uname -r
4.4.0-66-generic

キーボードの設定を変更するには dpkg-reconfigure コマンドを keyboard-configuration 引数をつけて実行する。

$ sudo dpkg-reconfigure keyboard-configuration

表示されるウィザードに沿って設定を入力していく。 二番目の設定で Japanese にするのがポイント。

Generic 105-key (Intel) PC > Japanese > Japanese > The default for the keyboard layout > No compose key

いじょう。

Python: Keras/TensorFlow の学習を CPU の拡張命令で高速化する (Mac OS X)

今回のネタは TensorFlow を使っていると、いつも目にしていた警告について。

それは、次のようなもの。

W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.1 instructions, but these are available on your machine and could speed up CPU computations.
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations.
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX instructions, but these are available on your machine and could speed up CPU computations.

どうやら TensorFlow は CPU の拡張命令を使うことができるらしい。 しかし、PyPI 経由で一般に配布されているバイナリパッケージでは、それらは有効になっていない。 もちろん、同じ x86 系 CPU であってもどんな拡張命令セットを持っているかは製品によって異なるので、これは当然の判断だろう。

そこで、今回は CPU の拡張命令を有効にして TensorFlow をソースコードからコンパイルしてみることにした。 そうすることで、どれくらい学習を高速化できるだろうか?というのが主題となる。

先に結論から書いてしまうと、今回使った環境では大体 30% くらい学習が速くなった。 ぶっちゃけ、これくらいであれば素直に GPU を使える環境を用意した方が良いと思う。

blog.amedama.jp

ただし、自分で使う環境向けにチューンするという面において、自前でコンパイルするのは良い選択肢かもしれないと感じた。

今回使った環境は次の通り。

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.12.3
BuildVersion:   16D32
$ python --version
Python 3.6.0

あらかじめ、計算速度のベンチマークに使うアプリケーションをダウンロードしておこう。

$ curl -O https://raw.githubusercontent.com/fchollet/keras/master/examples/mnist_cnn.py
$ echo 'K.clear_session()' >> mnist_cnn.py

拡張命令を使わない場合

まずは、CPU の拡張命令を使わない場合にかかる時間を調べておこう。

PyPI から汎用のバイナリパッケージで TensorFlow をインストールする。 ベンチマークにするアプリケーションを実行するために Keras も入れておく。

$ pip install keras tensorflow

インストールできたら Python の REPL を起動する。

$ python

REPL が起動したら MNIST のデータセットをダウンロードしておこう。 これには少し時間がかかる。

>>> from keras.datasets import mnist
Using TensorFlow backend.
>>> mnist.load_data()
Downloading data from https://s3.amazonaws.com/img-datasets/mnist.pkl.gz

終わったら REPL から抜けておく。

>>> exit()

それでは、まずは CPU の拡張命令を使わない場合にかかる時間を測ろう。 time コマンド経由でベンチマーク用のアプリケーションを実行する。

$ time python mnist_cnn.py
Using TensorFlow backend.
X_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples
Train on 60000 samples, validate on 10000 samples
Epoch 1/12
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.1 instructions, but these are available on your machine and could speed up CPU computations.
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations.
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX instructions, but these are available on your machine and could speed up CPU computations.
60000/60000 [==============================] - 92s - loss: 0.3741 - acc: 0.8859 - val_loss: 0.0880 - val_acc: 0.9718
...(省略)...
python mnist_cnn.py  2452.87s user 859.80s system 302% cpu 18:15.11 total

CPU の拡張命令を使わない場合には 2450 秒かかった。

拡張命令を使う場合

次は CPU の拡張命令を使う場合について。 警告メッセージを見る限り、今回使うマシンでは SSE4.1 SSE4.2 AVX という拡張命令が使える。

それらの拡張命令を有効にするためにソースコードから TensorFlow をインストールする。 ソースコードからのインストールには次のドキュメントを参照すると良い。 Download and Setup  |  TensorFlow

まずは、Homebrew を使って依存パッケージをインストールする。

$ brew install bazel swig

次に TensorFlow のソースコードをクローンする。

$ git clone https://github.com/tensorflow/tensorflow.git
$ cd tensorflow

先ほどベンチマークしたのと同じバージョンのタグをチェックアウトしよう。

$ git checkout v1.0.1

次にインストールするための環境を configure で設定していく。 今回は CUDA を使わないので、さほど難しくはない。

$ ./configure
Please specify the location of python. [Default is /Users/amedama/.virtualenvs/py36/bin/python]:
Please specify optimization flags to use during compilation [Default is -march=native]:
Do you wish to use jemalloc as the malloc implementation? (Linux only) [Y/n] n
jemalloc disabled on Linux
Do you wish to build TensorFlow with Google Cloud Platform support? [y/N]
No Google Cloud Platform support will be enabled for TensorFlow
Do you wish to build TensorFlow with Hadoop File System support? [y/N]
No Hadoop File System support will be enabled for TensorFlow
Do you wish to build TensorFlow with the XLA just-in-time compiler (experimental)? [y/N]
No XLA support will be enabled for TensorFlow
Found possible Python library paths:
  /Users/amedama/.virtualenvs/py36/lib/python3.6/site-packages
Please input the desired Python library path to use.  Default is [/Users/amedama/.virtualenvs/py36/lib/python3.6/site-packages]

Using python library path: /Users/amedama/.virtualenvs/py36/lib/python3.6/site-packages
Do you wish to build TensorFlow with OpenCL support? [y/N]
No OpenCL support will be enabled for TensorFlow
Do you wish to build TensorFlow with CUDA support? [y/N]
No CUDA support will be enabled for TensorFlow
Configuration finished
Extracting Bazel installation...
..........................................................
INFO: Starting clean (this may take a while). Consider using --expunge_async if the clean takes more than several minutes.
.........................................................
INFO: All external dependencies fetched successfully.

設定が終わったら次に bazel を使ってビルドする。 ここで使用する CPU の拡張命令を指定する。 --copt=-m の後ろに拡張命令の名前を入力しよう。 この処理には時間がかかるので気長に待つ。

$ bazel build -c opt --copt=-mavx --copt=-msse4.1 --copt=-msse4.2 //tensorflow/tools/pip_package:build_pip_package

もし、ここで指定しなければ CPU 拡張命令を使わない汎用なパッケージになる。

続けて Wheel パッケージをビルドする。 二番目に渡している /tmp/tensorflow_pkg が Wheel パッケージを保存する場所になる。

$ bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg

これで CPU の拡張命令が有効になった Wheel パッケージができた。

$ ls /tmp/tensorflow_pkg
tensorflow-1.0.1-cp36-cp36m-macosx_10_12_x86_64.whl

既にインストールされている汎用なパッケージをアンインストールした上で、インストールしよう。

$ pip uninstall -y tensorflow
$ pip install /tmp/tensorflow_pkg/tensorflow-1.0.1-cp36-cp36m-macosx_10_12_x86_64.whl
$ pip list --format=columns | grep -i tensorflow
tensorflow       1.0.1

さて、先ほどと同じようにベンチマークとなるアプリケーションを実行してみよう。 どれくらい速くなるだろうか。

$ time python mnist_cnn.py
Using TensorFlow backend.
X_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples
Train on 60000 samples, validate on 10000 samples
Epoch 1/12
60000/60000 [==============================] - 75s - loss: 0.3735 - acc: 0.8859 - val_loss: 0.0888 - val_acc: 0.9724
...(省略)...
python mnist_cnn.py  1871.67s user 901.15s system 296% cpu 15:34.90 total

今度は 1870 秒で終わった。

結果は CPU の拡張命令を使わない場合が 2450 秒で、使った場合が 1870 秒となった。 つまり、処理速度が 30% ほど速くなった。 また、一回のエポックでいうと、だいたい 20% ほど速くなっているようだ。

まとめ

今回は、普段 TensorFlow を使っているときに表示される警告が気になってソースコードからビルドを試してみた。 PyPI で配布されている TensorFlow は CPU に依存しない汎用なバイナリになっている。 もし、自分の CPU に合わせて拡張命令を使ったバイナリを作りたいときは、ソースコードからコンパイルする必要がある。 CPU の拡張命令を有効にすると、今回のケースでは 30% ほど学習が速くなった。 GPU を使った学習の高速化に比べると、これは微々たるもののように感じる。 しかし、自分の環境に向けてチューンされたバイナリを作るという意味では、ソースコードからのコンパイルは有用かもしれない。

いじょう。

macOS Sierra から Ubuntu 16.04 LTS のディスクを NFS でマウントする

普段の開発環境として Mac を使っているものの、一部の作業を別の Linux マシンでやりたい、という場面があった。 そこで Mac から Ubuntu のディスクを NFS でマウントすることにした。 こうすれば開発環境としては Mac を使いつつ、成果物を使った作業は Ubuntu に SSH でログインして実施できる。

今回使った環境は次の通り。 まず、NFS クライアントとなる Mac の OS は macOS Sierra を使っている。

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.12.3
BuildVersion:   16D32

そして、NFS サーバとなる Ubuntu については 16.04 LTS を使った。 クライアントからマウントするときに IP アドレスの指定が必要なので 192.168.0.10/24 を割り振ってある。

$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
$ uname -r
4.4.0-64-generic

Ubuntu

まずは Ubuntu で NFS サーバを構築していく。 NFS サーバの構築に必要なパッケージをインストールしよう。

$ sudo apt-get install -y nfs-kernel-server

次に NFS サーバで外部に公開するディレクトリを作成する。 ここでは /srv/nfs を使うことにした。 クライアントからファイルの書き込みができるように権限も調整しておく。

$ sudo mkdir -p /srv/nfs
$ sudo chmod a+w /srv/nfs

ディレクトリが用意できたら、次に NFS サーバの設定を行う。 これには /etc/exports という設定ファイルを使う。 公開するディレクトリと、公開先のネットワーク、そしてオプションを指定する。

$ cat << 'EOF' | sudo tee -a /etc/exports > /dev/null
/srv/nfs 192.168.0.0/24(rw,no_subtree_check)
EOF

上記で指定しているオプションの意味は次の通り。 - rw: 書き込み権限を付与する - no_subtree_check: ファイルが見つからないときに子ディレクトリを探さない

設定が終わったら NFS サーバのサービスを起動する。

$ sudo systemctl start nfs-kernel-server
$ sudo systemctl enable nfs-kernel-server

exportfs コマンドを使って状況を確認する。 次のように公開しているディレクトリやネットワーク、オプションが表示されれば上手くいっている。

$ sudo exportfs -v
/srv/nfs        192.168.0.0/24(rw,wdelay,root_squash,no_subtree_check,sec=sys,rw,root_squash,no_all_squash)

手動マウント

これで NFS サーバは構築ができたので、次は NFS クライアントの Mac からそれをマウントしていく。 まずは手動で一時的にマウントするやり方から。

最初に NFS でマウントするための空ディレクトリを用意しておく。

$ sudo mkdir -p /Volumes/NFS

あとは mount_nfs コマンドを使ってマウントするだけ。 NFS でマウントする元となる IP アドレスとディレクトリと、マウントする先のディレクトリを指定する。

$ sudo mount_nfs -P 192.168.0.10:/srv/nfs /Volumes/NFS

あとは、マウントしたディレクトリで色々と作業すれば良い。

$ cat << 'EOF' > /Volumes/NFS/greeting.txt
Hello, World!
EOF

ちゃんとファイルの書き込みもできている。

$ cat /Volumes/NFS/greeting.txt
Hello, World!

Ubuntu の方で確認しても、ちゃんとファイルが書き込まれている。

$ cat /srv/nfs/greeting.txt
Hello, World!

使い終わったらアンマウントしよう。

$ sudo umount /Volumes/NFS

自動マウント

先ほどのやり方では再起動するとアンマウントされてしまうので、主に一時的に使うための方法だった。 しかし、今回のユースケースでは恒常的にマウントしておきたい。 次は再起動してもマウントされ続けるやり方について。

まずは、先ほどと同じようにマウントするための空ディレクトリを用意しておく。 ただし、今回は新しくルートディレクトリ配下にディレクトリを用意することにした。 なぜなら、このやり方では指定したディレクトリを丸ごと使ってしまうため。 /Volumes は外付けハードディスクなどもマウントされる場所なので、専有してしまうのは困る。

$ sudo mkdir -p /mnt

次に /etc/auto_master という設定ファイルに先ほど用意したマウントポイントを書く。 そして、そのマウントポイントの設定ファイルの場所を指定する。 今回の例では設定ファイルを /etc/autofs_nfs という名前にしたけど、これは別になんでも構わない。

$ cat << EOF | sudo tee -a /etc/auto_master > /dev/null
/mnt                    /etc/autofs_nfs
EOF

上記のコマンドを実行すると、設定ファイルは次のようになる。 書式については auto_master(5) を参照のこと。

$ cat /etc/auto_master
#
# Automounter master map
#
+auto_master       # Use directory service
/net            -hosts      -nobrowse,hidefromfinder,nosuid
/home           auto_home   -nobrowse,hidefromfinder
/Network/Servers    -fstab
/-          -static
/mnt                    /etc/autofs_nfs

次に、先ほど指定したマウントポイントに対する設定ファイルを記述する。 ここはなんとなく NFS のオプションと近いので分かりやすいと思う。 最初に指定した NFS というのがマウントするときのディレクトリ名になる。 つまり、/mnt/NFS という形でマウントされる。

$ cat << 'EOF' | sudo tee -a /etc/autofs_nfs > /dev/null
NFS -fstype=nfs,rw,resvport 192.168.0.10:/srv/nfs
EOF

できあがる設定ファイルは、こんな感じ。

$ cat /etc/autofs_nfs
NFS -fstype=nfs,rw,resvport 192.168.0.10:/srv/nfs

あとは autofsd のサービスを起動する。

$ sudo launchctl stop com.apple.autofsd
$ sudo launchctl start com.apple.autofsd

これでマウントされる。

$ mount | grep mnt
map /etc/autofs_nfs on /mnt (autofs, automounted, nobrowse)
192.168.0.10:/srv/nfs on /mnt/NFS (nfs, nodev, nosuid, automounted, nobrowse)
$ df -ah | grep mnt
map /etc/autofs_nfs     0Bi    0Bi    0Bi   100%        0          0  100%   /mnt
192.168.0.10:/srv/nfs  901Gi  7.6Gi  848Gi     1%   169558   59836842    0%   /mnt/NFS

マウントされたディレクトリを見ると、先ほど書き込んだファイルも見えている。

$ cat /mnt/NFS/greeting.txt
Hello, World!

これで、恒常的に NFS をマウントして作業するための準備が整った。

めでたしめでたし。

Ubuntu 16.04 LTS のデフォルトエディタを nano から変更する

Ubuntu 16.04 LTS を使っていたところ、デフォルトのエディタが nano になっていた。 普段 nano は使っていないので vim に変更したい、というのが今回のお話。

使った環境は次の通り。

$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.2 LTS"
$ uname -r
4.4.0-64-generic

下準備として、ひとまず vim をインストールしておく。

$ sudo apt-get install -y vim

デフォルトのエディタが nano になってしまう

例えば visudo コマンドとかを実行すると nano が起動してしまう。

$ sudo visudo

起動するエディタを指定して実行する

システムが起動するエディタは環境変数 EDITOR で指定できる。 ただし sudo コマンドと組み合わせて使うときは注意が必要で -E オプションもつけなきゃいけない。 このオプションがあると特権ユーザの権限で visudo コマンドを実行するときに環境変数を引き継ぐことができる。

$ EDITOR=vim sudo -E visudo

デフォルトのエディタを変更する

ただ、さっきのように毎回環境変数を指定したり -E オプションをつけて…とするのはめんどくさい。 なのでデフォルトのエディタを変更してしまおう。

デフォルトのエディタを変更するには update-alternatives コマンドに --config editor オプションを指定する。 選ぶことのできるエディタが選択肢の形で表示されるので、どれを使うか番号を入力しよう。

$ sudo update-alternatives --config editor
There are 4 choices for the alternative editor (providing /usr/bin/editor).

  Selection    Path                Priority   Status
------------------------------------------------------------
* 0            /bin/nano            40        auto mode
  1            /bin/ed             -100       manual mode
  2            /bin/nano            40        manual mode
  3            /usr/bin/vim.basic   30        manual mode
  4            /usr/bin/vim.tiny    10        manual mode

Press <enter> to keep the current choice[*], or type selection number: 3
update-alternatives: using /usr/bin/vim.basic to provide /usr/bin/editor (editor) in manual mode

これで、特に環境変数などを指定することなく vim が起動するようになる。

$ sudo visudo

めでたしめでたし。

Python: python-fire の CLI 自動生成を試す

今回は Google が公開した python-fire というパッケージを試してみた。 python-fire では、クラスやモジュールを渡すことで、定義されている関数やメソッドを元に CLI を自動で生成してくれる。

ただし、一つ注意すべきなのは、できあがる CLI はそこまで親切な作りではない、という点だ。 実際にユーザに提供するような CLI を実装するときは、従来通り Click のようなフレームワークを使うことになるだろう。 では python-fire はどういったときに活躍するかというと、これは開発時のテストだと思う。 実装した内容をトライアンドエラーするための CLI という用途であれば python-fire は非常に強力なパッケージだと感じた。

今回使った環境は次の通り。

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.12.3
BuildVersion:   16D32
$ python --version
Python 3.6.0

インストール

インストールは Python のパッケージツールの pip を使ってできる。

$ pip install fire

もし pip がインストールされていないときは、あらかじめ入れておこう。

$ curl https://bootstrap.pypa.io/get-pip.py | sudo python

あと、これは完全に蛇足だけどシステムの Python 実行環境にそのまま入れるのはおすすめしない。 色んなパッケージを試すときは、システムからは独立した Python 仮想環境を作って入れた方が良い。 以下は、例えば virtualenv を使ったやり方。

$ sudo pip install virtualenv
$ mkdir -p venv
$ virtualenv venv

これで、最低限のパッケージの入った Python 仮想環境ができる。

(venv) $ pip list --format=columns
Package    Version
---------- -------
appdirs    1.4.3
packaging  16.8
pip        9.0.1
pyparsing  2.2.0
setuptools 34.3.1
six        1.10.0
wheel      0.29.0

python-fire をインストールすると、次のようなパッケージが入る。 意外と依存パッケージが多い。

(venv) $ pip install fire
(venv) $ pip list --format=columns
Package          Version
---------------- -------
appdirs          1.4.2
appnope          0.1.0
decorator        4.0.11
fire             0.1.0
ipython          5.3.0
ipython-genutils 0.1.0
packaging        16.8
pexpect          4.2.1
pickleshare      0.7.4
pip              9.0.1
prompt-toolkit   1.0.13
ptyprocess       0.5.1
Pygments         2.2.0
pyparsing        2.2.0
setuptools       34.3.1
simplegeneric    0.8.1
six              1.10.0
traitlets        4.3.2
wcwidth          0.1.7
wheel            0.29.0

基本的な使い方

まずは python-fire の基本的な使い方から見ていく。 とはいっても、その使い方は至ってシンプル。 例えば CLI を自動生成したいクラスがあるなら、それを fire.Fire() コマンドに渡すだけ。

次のコマンドを実行すると helloworld.py というファイルでサンプルコードが保存される。

$ cat << 'EOF' > helloworld.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-


class MyClass(object):

    def greet(self):
        print('Hello, World!')


if __name__ == '__main__':
    # fire をインポートする
    import fire
    # コマンドラインで呼び出したいクラスを指定する
    fire.Fire(MyClass)
EOF

上記のサンプルコードでは MyClass というクラスを python-fire に渡している。 このクラスには greet() というメソッドがあって、文字列をプリントする内容になっている。

上記を何の引数も渡さずに実行してみよう。

$ python helloworld.py
Type:        MyClass
String form: <__main__.MyClass object at 0x10bb64f28>
File:        ~/Documents/temporary/helloworld.py

Usage:       helloworld.py
             helloworld.py greet

すると Usage が出力されることがわかる。

上記の出力に沿ってオプションを渡してみよう。 具体的には、MyClass の持つメソッド名である greet を指定する。

$ python helloworld.py greet
Hello, World!

これだけで MyClass#greet() の処理が呼び出された。

関数・メソッドに引数を渡す

先ほどの例ではメソッドの呼び出しを試した。 ただ、そのメソッドには引数がなかった。 次は引数のあるメソッドを呼び出すときについて見てみる。

以下のコマンドを実行すると、引数のあるメソッドを定義したサンプルコードができる。

$ cat << 'EOF' > calculate.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-


class MyClass(object):

    def add(self, a, b=1):
        return a + b


if __name__ == '__main__':
    import fire
    fire.Fire(MyClass)
EOF

上記では MyClass#add() メソッドが引数として ab を受け取る。 そのうち b に関してはデフォルト引数が指定されている。

上記で用意したサンプルコードを先ほどと同じようにメソッド名だけ入れて実行してみよう。 この場合、必要な引数 a が入力されていないためトレースが表示される。

$ python calculate.py add
Fire trace:
1. Initial component
2. Instantiated class "MyClass" (calculate.py:5)
3. Accessed property "add" (calculate.py:7)
4. ('The function received no value for the required argument:', 'a')

Type:        method
String form: <bound method MyClass.add of <__main__.MyClass object at 0x109fba518>>
File:        ~/Documents/temporary/calculate.py
Line:        7

Usage:       calculate.py add A [B]
             calculate.py add --a A [--b B]

Usage を見ると、メソッド名である add に続いて引数を渡せば良いことがわかる。

上記にもとづいてメソッド名に続いて引数を渡してみよう。 それぞれ引数の ab に対応する。

$ python calculate.py add 1 2
3

引数の b はデフォルト引数が指定されているので省略できる。

$ python calculate.py add 1
2

引数に渡す内容の指定を順不定にしたいときは -- を使って引数名を指定してやる。

$ python calculate.py add --a=1 --b=2
3

コンストラクタに引数を渡す

先ほどまでの例では CLI を自動生成したいクラスのコンストラクタに引数がなかった。 現実には、何も引数を取らないコンストラクタの方が珍しいと思う。 そこで、次はコンストラクタに引数を取る場合を試してみる。

次のコマンドを実行するとコンストラクタに引数を取るサンプルコードができる。

$ cat << 'EOF' > constructor.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-


class MyClass(object):

    def __init__(self, msg):
        """コンストラクタに引数 msg を取る"""
        self.msg = msg

    def greet(self):
        print(self.msg)


if __name__ == '__main__':
    import fire
    fire.Fire(MyClass)
EOF

先ほどの例と同じように引数を何も指定せず実行してみよう。 するとコンストラクタの引数が指定されていないという内容でトレースが表示される。

$ python constructor.py
Fire trace:
1. Initial component
2. ('The function received no value for the required argument:', 'msg')

Type:        type
String form: <class '__main__.MyClass'>
File:        ~/Documents/temporary/constructor.py
Line:        5

Usage:       constructor.py MSG
             constructor.py --msg MSG

どうやらコンストラクタの引数をまずは指定する必要があるらしい。

上記の表示に沿って、コンストラクタの --msg 引数を指定してみよう。

$ python constructor.py --msg="Hello World"
Type:           MyClass
String form:    <__main__.MyClass object at 0x102b303c8>
File:           ~/Documents/temporary/constructor.py
Init docstring: コンストラクタに引数 msg を取る

Usage:          constructor.py --msg='Hello World' 
                constructor.py --msg='Hello World' greet
                constructor.py --msg='Hello World' msg

すると、今度は実行できるメソッド名が表示されるようになった。 メソッド greet() の下にある msg はインスタンスのメンバとして引数を保存しているため表示されているんだろう。

上記の表示に沿ってコンストラクタの引数を入力しながらメソッドを指定する。

$ python constructor.py --msg='Hello World' greet
Hello World

これでコンストラクタに引数を取るクラスであっても実行できるようになった。

関数から自動生成する

これまでの例ではクラスに対して CLI を自動生成する例だった。 次は関数に対して試してみることにしよう。

次のコマンドを実行すると関数を使うパターンのサンプルコードができる。

$ cat << 'EOF' > function.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-


def greet(msg='Hello, World!'):
    print(msg)


if __name__ == '__main__':
    import fire
    fire.Fire(greet)
EOF

とはいえ、やっていることはこれまでと変わらない。 渡すものがクラスの代わりに関数になっただけ。

今度も、特に何も指定せずまずは実行してみよう。

$ python function.py
Hello, World!

今回に関しては、これだけで事足りてしまった。

ちなみに、上記の場合はデフォルト引数が指定されていたので動いた。 もちろん、次のようにして引数を指定することもできる。

$ python function.py --msg="Hajimemashite Sekai"
Hajimemashite Sekai

ちなみに、ここで面白い挙動に気づいた。 引数を指定するときに内容にカンマが含まれていると、型が自動的にタプルになるようだ。

$ python function.py --msg="Hajimemashite, Sekai"
('Hajimemashite', 'Sekai')

型の自動判別

先ほどの例を元に、どういった内容を引数に指定すると、どの型と判別するのか調べてみた。

次のサンプルコードでは受け取った引数の型を出力する。

$ cat << 'EOF' > argtype.py 
#!/usr/bin/env python
# -*- coding: utf-8 -*-


def print_type(obj):
    print(type(obj))


if __name__ == '__main__':
    import fire
    fire.Fire(print_type)
EOF

色んな内容を渡してみた結果が次の通り。 いきなりタプルになったのは驚いたけど、文字列を指定したいなら '' で囲むのがセオリーなようだ。

$ python argtype.py --obj="foo"
<class 'str'>

$ python argtype.py --obj="1"  
<class 'int'>
$ python argtype.py --obj="1.0"
<class 'float'>
$ python argtype.py --obj="'1'"
<class 'str'>

$ python argtype.py --obj="1,2"
<class 'tuple'>
$ python argtype.py --obj="'1,2'"
<class 'str'>

$ python argtype.py --obj="[1, 2]"
<class 'list'>
$ python argtype.py --obj="{1, 2}"
<class 'set'>
$ python argtype.py --obj="{1: 2}"
<class 'dict'>

モジュールから自動生成する

次は、おそらく一番使う場面が多そうなやり方。 モジュールをそのまま指定して CLI を自動生成してしまうやり方。

次のコマンドを実行するとモジュールをそのまま指定するサンプルコードができる。 この場合は、特に何も指定せず fire.Fire() をインスタンス化すれば良い。

$ cat << 'EOF' > module.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-

class MyClass(object):

    def greet(self):
        print('Hello, World!')


def greet(msg='Hello, World!'):
    print(msg)


def add(a, b):
    return a + b


if __name__ == '__main__':
    import fire
    fire.Fire()
EOF

モジュールには MyClass クラスや greet() および add() 関数が定義されている。

次も、特に何も指定せずまずは実行してみよう。 ただ、これだけだと何が指定できるのかよく分からない出力になる。

$ python module.py
MyClass: <class '__main__.MyClass'>
greet:   <function greet at 0x10e53ee18>
add:     <function add at 0x10e67d378>
fire:    <module 'fire' from '/Users/amedama/.virtualenvs/fire/lib/python3.6/site-packages/fire/__init__.py'>

そこで --help オプションを指定してみよう。 間にある -- は python-fire 自体に渡す引数と、自動生成した CLI に渡す引数を区別するために使う。

$ python module.py -- --help
Type:        dict
String form: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_e <...> ule 'fire' from '/Users/amedama/.virtualenvs/fire/lib/python3.6/site-packages/fire/__init__.py'>}
Length:      13

Usage:       module.py 
             module.py MyClass
             module.py greet
             module.py add
             module.py fire

すると、実行できる内容が出力されるようになった。

関数に関しては、これまでと特に変わらないやり方で実行できる。

$ python module.py greet
Hello, World!
$ python module.py add 1 2
3

インスタンスメソッドについては、クラス名とメソッド名を続けて入力すれば良い。

$ python module.py MyClass greet
Hello, World!

インタラクティブシェルに入る

最初に依存パッケージの中に ipython があったことに気づいていたかもしれない。 ipython は高機能な Python の REPL で python-fire の引数に --interactive を指定すると、それが起動するようになっている。 起動した ipython では特にインポートなどをすることなくモジュールの中で定義されているクラスなどを使うことができる。

$ python helloworld.py -- --interactive
Fire is starting a Python REPL with the following objects:
Modules: fire
Objects: MyClass, component, helloworld.py, result, trace

Python 3.6.0 (default, Feb 25 2017, 20:17:10)
Type "copyright", "credits" or "license" for more information.

IPython 5.3.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: MyClass
Out[1]: __main__.MyClass

In [2]: MyClass().greet()
Hello, World!

In [3]: exit()

通常通り ipython を指定しただけでは、こうはならない。 インポートしていないオブジェクトを使おうとするとエラーになる。

$ ipython
Python 3.6.0 (default, Feb 25 2017, 20:17:10)
Type "copyright", "credits" or "license" for more information.

IPython 5.3.0 -- An enhanced Interactive Python.
?         -> Introduction and overview of IPython's features.
%quickref -> Quick reference.
help      -> Python's own help system.
object?   -> Details about 'object', use 'object??' for extra details.

In [1]: MyClass
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-d1eb688265f8> in <module>()
----> 1 MyClass

NameError: name 'MyClass' is not defined

本来であれば次のようにしてインポートしてやる必要がある。 python-fire 経由で起動すると、この手間をはぶくことができるようだ。

In [2]: from helloworld import MyClass

In [3]: MyClass
Out[3]: helloworld.MyClass

呼び出しをトレースする

これまでの例でもエラーになったときに何度か表示されていたけど、呼び出し内容をトレースすることもできる。 これには python-fire の引数として --trace オプションを指定する。

$ python helloworld.py greet -- --trace
Fire trace:
1. Initial component
2. Instantiated class "MyClass" (helloworld.py:5)
3. Accessed property "greet" (helloworld.py:7)

上記のように各ステップでどういったことが実行されているのかが表示される。

補完スクリプトを出力する

python-fire では bash 用の補完スクリプトを出力することもできる。 それには、次のようにして --completion を python-fire の引数に渡す。

$ python helloworld.py -- --completion
# bash completion support for helloworld.py
# DO NOT EDIT.
# This script is autogenerated by fire/completion.py.

_complete-helloworldpy()
{
  local start cur opts
  COMPREPLY=()
  start="${COMP_WORDS[@]:0:COMP_CWORD}"
  cur="${COMP_WORDS[COMP_CWORD]}"

  opts=""


  if [[ "$start" == "helloworld.py" ]] ; then
    opts="greet"
  fi

  if [[ "$start" == "helloworld.py greet" ]] ; then
    opts="--self"
  fi

  COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
  return 0
}

complete -F _complete-helloworldpy helloworld.py

ここで出力される内容を bash の設定ファイルに入れて読み込んでやるとシェルで補完が効くようになる。

$ python helloworld.py -- --completion >> ~/.bashrc
$ source ~/.bashrc

ただし、出力される補完スクリプトはパスが通っている場所で、かつスクリプトに実行権限がついていることを前提になっている。 そのため、上記のように python コマンド経由で実行する限りでは補完が効かない。 もし使うとしたら、こんな感じにしないとダメそう。

$ chmod +x helloworld.py
$ sudo mv helloworld.py /usr/local/bin/

ちなみに bashcompinit を有効にして試してみたけど zsh では動かなかった。

詳細を出力する

これまで見てきたように --help オプションを指定すると詳しい使い方が出力された。 次のように --verbose オプションを指定すると、特殊メソッドやアトリビュートを含むさらに詳しい内容が出力される。

$ python helloworld.py -- --verbose
Type:        MyClass
String form: <__main__.MyClass object at 0x1055cc4a8>
File:        ~/Documents/temporary/helloworld.py

Usage:       helloworld.py
             helloworld.py __class__
             helloworld.py __delattr__
             helloworld.py __dict__
             helloworld.py __dir__
             helloworld.py __doc__
             helloworld.py __eq__
             helloworld.py __format__
             helloworld.py __ge__
             helloworld.py __getattribute__
             helloworld.py __gt__
             helloworld.py __hash__
             helloworld.py __init__
             helloworld.py __init_subclass__
             helloworld.py __le__
             helloworld.py __lt__
             helloworld.py __module__
             helloworld.py __ne__
             helloworld.py __new__
             helloworld.py __reduce__
             helloworld.py __reduce_ex__
             helloworld.py __repr__
             helloworld.py __setattr__
             helloworld.py __sizeof__
             helloworld.py __str__
             helloworld.py __subclasshook__
             helloworld.py __weakref__
             helloworld.py greet

まとめ

今回は Google の作った CLI を自動生成するパッケージである python-fire を試してみた。 生成される CLI は割りと雑だけど、開発者が使う分には全く問題ないレベルだと思う。 これまでは挙動を軽く試すのにもスクリプトを毎回書き換えたり ipython を起動したりと面倒が多かった。 一つ一つの作業にかかる時間は短くとも、長い目で見れば多くの時間を浪費していることだろう。 そんなとき python-fire を使えば、スクリプトに数行を追加するだけでその手間をはぶくことができるのは大きいと感じた。

いじょう。

Python: Keras/TensorFlow の学習を GPU で高速化する (Mac OS X)

Keras というのは Python を使ってニューラルネットワークを組むためのフレームワーク。 Python でニューラルネットワークのフレームワークというと、他にも TensorFlow とか Chainer なんかが有名どころ。 Keras はそれらに比べると、より高い抽象度の API を提供しているところが特徴みたい。 実のところ Keras はデフォルトで TensorFlow をバックエンドとして動作する。 バックエンドとしては、他にも Theano が選べるらしい。

今回は Keras で組んだニューラルネットワークを GPU で学習させてみることにした。 そのとき CPU と比べて、どれくらい速くなるかを試してみたい。

使った環境は次の通り。

$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.12.3
BuildVersion:   16D32
$ python --version
Python 3.5.3

当然だけどマシンには dGPU のグラフィックカードが載っている必要がある。 今回は GeForce GTX 675MX を使った。

まずは CPU で学習させるための下準備

まずは Python のパッケージ管理システムである pip を使って Keras をインストールする。

$ pip install keras

もし pip がそもそも入っていないときは、インストールしておこう。

$ curl -O https://bootstrap.pypa.io/get-pip.py | sudo python

ついでに言うと、システムにサードパーティ製のパッケージをどんどん突っ込んでいくと混乱の元になる。 なので virtualenv などを使って仮想環境を作れるようにした方が良い。 ここらへんに何を使うかは好みによる。

続いてバックエンドとして動作する TensorFlow (CPU 版) もインストールしておこう。

$ pip install tensorflow

インストールが終わったら python コマンドを実行して REPL を立ち上げよう。

$ python

REPL が起動したら Keras の API を使って MNIST データセットをダウンロードしよう。 これにはちょっと時間がかかる。

>>> from keras.datasets import mnist
Using TensorFlow backend.
>>> mnist.load_data()
Downloading data from https://s3.amazonaws.com/img-datasets/mnist.pkl.gz

ダウンロードが終わったら exit() 関数で REPL から抜けよう。

>>> exit()

続いて Keras のサンプルプログラムをダウンロードする。 これは Convolutional Neural Network (CNN) を使って MNIST データセットの画像を分類するものになっている。

$ curl -O https://raw.githubusercontent.com/fchollet/keras/master/examples/mnist_cnn.py

ちなみに、このプログラムにはリソースの開放関係でちょっとした問題があるっぽいのでパッチを当てておく。 これはそのうちいらなくなるかもしれないけど、余分にあったとしても特に問題はないコードなので。

$ echo 'K.clear_session()' >> mnist_cnn.py

CPU を使って学習させる

さて、上記でまずは Keras/TensorFlow を CPU を使って学習させる下準備が整った。

早速、先ほどダウンロードしてきたサンプルプログラムを実行してみよう。 time コマンドを先頭に入れることで学習にかかった時間を測ることにする。

$ time python mnist_cnn.py
Using TensorFlow backend.
X_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples
Train on 60000 samples, validate on 10000 samples
Epoch 1/12
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.1 instructions, but these are available on your machine and could speed up CPU computations.
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations.
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX instructions, but these are available on your machine and could speed up CPU computations.
60000/60000 [==============================] - 78s - loss: 0.3744 - acc: 0.8854 - val_loss: 0.0887 - val_acc: 0.9716
Epoch 2/12
60000/60000 [==============================] - 78s - loss: 0.1357 - acc: 0.9595 - val_loss: 0.0634 - val_acc: 0.9800
Epoch 3/12
60000/60000 [==============================] - 79s - loss: 0.1033 - acc: 0.9693 - val_loss: 0.0572 - val_acc: 0.9818
Epoch 4/12
60000/60000 [==============================] - 78s - loss: 0.0890 - acc: 0.9740 - val_loss: 0.0448 - val_acc: 0.9853
Epoch 5/12
60000/60000 [==============================] - 76s - loss: 0.0784 - acc: 0.9767 - val_loss: 0.0423 - val_acc: 0.9865
Epoch 6/12
60000/60000 [==============================] - 80s - loss: 0.0710 - acc: 0.9788 - val_loss: 0.0384 - val_acc: 0.9871
Epoch 7/12
60000/60000 [==============================] - 84s - loss: 0.0656 - acc: 0.9808 - val_loss: 0.0364 - val_acc: 0.9891
Epoch 8/12
60000/60000 [==============================] - 84s - loss: 0.0606 - acc: 0.9818 - val_loss: 0.0354 - val_acc: 0.9888
Epoch 9/12
60000/60000 [==============================] - 79s - loss: 0.0555 - acc: 0.9839 - val_loss: 0.0336 - val_acc: 0.9883
Epoch 10/12
60000/60000 [==============================] - 76s - loss: 0.0545 - acc: 0.9837 - val_loss: 0.0325 - val_acc: 0.9895
Epoch 11/12
60000/60000 [==============================] - 76s - loss: 0.0500 - acc: 0.9856 - val_loss: 0.0325 - val_acc: 0.9900
Epoch 12/12
60000/60000 [==============================] - 78s - loss: 0.0476 - acc: 0.9860 - val_loss: 0.0328 - val_acc: 0.9893
Test score: 0.0328271338152
Test accuracy: 0.9893
python mnist_cnn.py  2470.93s user 664.19s system 326% cpu 16:00.66 total

MNIST を分類したときの正解率が 98.93% となっている。 かかった時間は 2471 秒で、ようするに 40 分もかかったことになる。

もし仮に一からニューラルネットワークを組むとしたら、もちろんこんな風に一発では上手くいかない。 たくさんのトライアンドエラーを繰り返して、その毎回にこんな時間がかかるとしたら気が遠くなってくる。

TensorFlow (GPU 版) をインストールする

CPU での学習にかかる時間に絶望したところで、次は代わりに GPU を使って学習させてみよう。 GPU は並列化しやすい単純な計算が得意で、ニューラルネットワークで内部的に用いられる行列演算もそれに当たる。

ニューラルネットワークがどうして行列演算を使うのかを知りたいときは、次の本を読むのがおすすめ。 この本はディープラーニングについて理論と実装の両面から理解を深められる貴重な本だと思う。

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

ゼロから作るDeep Learning ―Pythonで学ぶディープラーニングの理論と実装

話が脱線したけど Keras/TensorFlow で組むニューラルネットワークを GPU で学習させるには CUDA が必要になる。 また、バックエンドとして動作する TensorFlow についても GPU 対応版のものをインストールする必要がある。 代わりに、サンプルコードに関しては、学習に CPU を使うときと GPU を使うときで同じものが使える。

まずは Homebrew を使って coreutils をインストールしておく。

$ brew install coreutils

もし Homebrew が入っていないという場合には公式サイトの記述をもとにインストールしておこう。

続いて CUDA をインストールする。 これも Homebrew Cask を使って入れられる。

$ brew tap caskroom/cask
$ brew cask install cuda

続いて cuDNN をインストールする。 これだけはコマンドラインでちょちょいとインストールすることができない。 なぜかというと、サイトでユーザ登録をしないとダウンロードできないようになっているから。

なので、まずは以下の公式サイトでユーザ登録をしよう。

NVIDIA cuDNN | NVIDIA Developer

その上で、今なら “cuDNN v5.1 Library for OSX” というバージョンをダウンロードしてくる。

ダウンロードできたら、それを CUDA のディレクトリに解凍して放り込む。

$ sudo tar -xvf cudnn-8.0-osx-x64-v5.1.tgz -C /usr/local

ここで念のためマシンを再起動しておこう。

$ sudo shutdown -r now

再起動が終わったら、まずは CPU 版の TensorFlow をアンインストールする。 そして、改めて GPU 版の TensorFlow をインストールしよう。 これには名前のサフィックスとして -gpu がついている。

$ pip uninstall -y tensorflow
$ pip install tensorflow-gpu

GPU を使って学習させる

さて、これで GPU を使ってニューラルネットワークを学習させるための準備が整った。 先ほどと同じサンプルコードを実行して GPU を使った学習を試してみよう。

$ time python mnist_cnn.py
Using TensorFlow backend.
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcublas.8.0.dylib locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcudnn.5.dylib locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcufft.8.0.dylib locally
I tensorflow/stream_executor/dso_loader.cc:126] Couldn't open CUDA library libcuda.1.dylib. LD_LIBRARY_PATH: :/usr/local/cuda/lib
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcuda.dylib locally
I tensorflow/stream_executor/dso_loader.cc:135] successfully opened CUDA library libcurand.8.0.dylib locally
X_train shape: (60000, 28, 28, 1)
60000 train samples
10000 test samples
Train on 60000 samples, validate on 10000 samples
Epoch 1/12
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.1 instructions, but these are available on your machine and could speed up CPU computations.
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use SSE4.2 instructions, but these are available on your machine and could speed up CPU computations.
W tensorflow/core/platform/cpu_feature_guard.cc:45] The TensorFlow library wasn't compiled to use AVX instructions, but these are available on your machine and could speed up CPU computations.
I tensorflow/stream_executor/cuda/cuda_gpu_executor.cc:874] OS X does not support NUMA - returning NUMA node zero
I tensorflow/core/common_runtime/gpu/gpu_device.cc:885] Found device 0 with properties:
name: GeForce GTX 675MX
major: 3 minor: 0 memoryClockRate (GHz) 0.719
pciBusID 0000:01:00.0
Total memory: 1023.69MiB
Free memory: 639.50MiB
I tensorflow/core/common_runtime/gpu/gpu_device.cc:906] DMA: 0
I tensorflow/core/common_runtime/gpu/gpu_device.cc:916] 0:   Y
I tensorflow/core/common_runtime/gpu/gpu_device.cc:975] Creating TensorFlow device (/gpu:0) -> (device: 0, name: GeForce GTX 675MX, pci bus id: 0000:01:00.0)
60000/60000 [==============================] - 16s - loss: 0.3717 - acc: 0.8865 - val_loss: 0.0904 - val_acc: 0.9714
Epoch 2/12
60000/60000 [==============================] - 13s - loss: 0.1365 - acc: 0.9596 - val_loss: 0.0616 - val_acc: 0.9809
Epoch 3/12
60000/60000 [==============================] - 13s - loss: 0.1048 - acc: 0.9694 - val_loss: 0.0515 - val_acc: 0.9841
Epoch 4/12
60000/60000 [==============================] - 13s - loss: 0.0894 - acc: 0.9736 - val_loss: 0.0439 - val_acc: 0.9857
Epoch 5/12
60000/60000 [==============================] - 13s - loss: 0.0769 - acc: 0.9771 - val_loss: 0.0401 - val_acc: 0.9869
Epoch 6/12
60000/60000 [==============================] - 13s - loss: 0.0700 - acc: 0.9790 - val_loss: 0.0360 - val_acc: 0.9885
Epoch 7/12
60000/60000 [==============================] - 13s - loss: 0.0646 - acc: 0.9806 - val_loss: 0.0364 - val_acc: 0.9881
Epoch 8/12
60000/60000 [==============================] - 13s - loss: 0.0611 - acc: 0.9819 - val_loss: 0.0345 - val_acc: 0.9882
Epoch 9/12
60000/60000 [==============================] - 13s - loss: 0.0553 - acc: 0.9838 - val_loss: 0.0323 - val_acc: 0.9889
Epoch 10/12
60000/60000 [==============================] - 13s - loss: 0.0534 - acc: 0.9841 - val_loss: 0.0307 - val_acc: 0.9898
Epoch 11/12
60000/60000 [==============================] - 13s - loss: 0.0504 - acc: 0.9851 - val_loss: 0.0297 - val_acc: 0.9895
Epoch 12/12
60000/60000 [==============================] - 13s - loss: 0.0474 - acc: 0.9860 - val_loss: 0.0298 - val_acc: 0.9899
Test score: 0.0298051692682
Test accuracy: 0.9899
python mnist_cnn.py  138.85s user 22.06s system 91% cpu 2:55.87 total

今度は 139 秒で学習が終わった。

CPU を使ったときが 2471 秒なので、およそ 17.8 倍も学習が高速化できた。 学習時間が約 5.6% に短縮されているともいえる。 これだけ違うとトライアンドエラーを繰り返すときにかかる時間は全く変わってくる。

まとめ

  • Keras/TensorFlow で学習に GPU を使いたいときは、以下のものを入れる
    • CUDA
    • cuDNN
    • GPU 対応版 TensorFlow
  • ソースコードは CPU/GPU どちらを学習に使うときであっても共用できる
  • 学習にかかる時間を CPU と GPU で比較してみた
    • 今回試したパターンでは GPU を使うと CPU 比で 17.8 倍も高速化できた
  • 結論: ディープラーニングをやるなら GPU は絶対に使おう

めでたしめでたし。

統計: 統計検定2級に合格した

先日、当面の目標にしていた統計検定2級に合格することができた。 今回は、受験に関する諸々について書いてみることにする。

受験のきっかけ

以前から、データ分析や機械学習に興味があった。 そして、それらの書籍を読んだり手法を調べていくうちに、だんだんと統計学に対する興味が湧いてきた。 統計学は、データ分析や機械学習に深い関わりがある。

その後は、初心者向けの統計に関する書籍などを読んで学び始めた。 とはいえ、それだけでは統計が身についているのかがよく分からない状況に陥ってしまう。 そんなとき、統計検定の存在を知った。

ウェブで下調べしたところ、概ね統計検定の 2 級に合格すれば統計の基礎は分かっていると胸を張れるらしい。 現在、統計検定は 1 級 (数理・応用)、準 1 級、2 級、3 級、4 級と五つのレベルに分かれている。 公式では 2 級の試験内容を「大学基礎課程で習得すべきこと」と位置づけている。

www.toukei-kentei.jp

その上で出題範囲を見ると、なかなか広いので体系的な知識が得られそうな感じ。 勉強する過程で例題をたくさん解くことになるので身にもつきやすそうだ。 そこで、これをひとまず当面の目標に勉強を進めてみることにした。

以上が、統計検定2級を受験したきっかけ。 ちなみにスタート地点としては「標準偏差ってどう計算するんだっけ?」というレベルだった。 大学は情報系の学科だったものの、統計については全く触れていない。 ちなみに、今は高校の数学でも統計が範囲に入っているらしい。 統計検定でいうと 3 級に対応するかな。

やってきた勉強

次に、合格までにどんな勉強をしたかについて。 念のため、統計検定を意識する以前の内容についても書いておく。

完全独習 統計学入門

まず、統計学を学び始めるとっかかりとして、次の本を読み始めた。

完全独習 統計学入門

完全独習 統計学入門

上記の本は、統計に関する本を色々と読んだ中でも抜群に分かりやすかった。 本の中に、自身の説明として「これ以上内容を削ると統計ではなくなる」とあるけど、正にという感じ。 最初の一冊としては、ぜひおすすめしたいものになっている。

この本は二部構成になっていて、前半では記述統計を、後半では推測統計を扱っている。 前半の記述統計というのは、データの要約や可視化などを扱うもの。 この本では、基本的な統計量 (平均・分散・標準偏差など) の知識と正規分布について学ぶことができる。 後半の推測統計というのは、標本 (サンプル) を元に母集団を推測するもの。 この本では、ステップバイステップで正規分布・カイ二乗分布・t分布を用いた推定と検定について学ぶことができる。

ちなみに、推定と検定は統計検定2級でも必ず出題される。 その割に分かりやすく解説しているものは少ないので、この本はその意味でも貴重な存在といえる。 基礎的な考え方さえ身につけば、あとはその応用なので別の本も読みやすくなる。

完全独習 ベイズ統計学入門

読んだのはだいぶ後だけど、同じ作者でこの本もおすすめできる。 先ほどの本と何が違うかというと、この本はベイズ推定に焦点を当てた説明になっている。

完全独習 ベイズ統計学入門

完全独習 ベイズ統計学入門

実は統計学はスタンダードな統計 (ネイマン・ピアソン統計とも呼ばれる) とベイズ統計に分かれている。 先ほどの本はスタンダードな統計を扱っていて、こちらはベイズ統計を扱っているという違い。

統計検定2級では確率の問題でベイズ推定も頻繁に出題されるので、この本は強い味方になる。 確率を面積で計算するテクニックを身につけると問題を解くのに大いに役立つだろう。

統計検定 2級 公式問題集

次は、みんな大好き過去問題集。 この問題を解きながら、分からないところを解説を読みながら調べていくのが勉強の基本だった。

日本統計学会公式認定 統計検定 2級 公式問題集[2013〜2015年]

日本統計学会公式認定 統計検定 2級 公式問題集[2013〜2015年]

最初に取り組んだときはあまりに解けなくて絶望したけど、苦手な分野を早めに把握するのが重要。 分からなかったところは、付属している解説を読んだり後述する公式の教科書を参照しながら進めよう。

改訂版 統計検定2級対応 統計学基礎

統計検定2級の教科書的な位置づけの本がこちら。

改訂版 日本統計学会公式認定 統計検定2級対応 統計学基礎

改訂版 日本統計学会公式認定 統計検定2級対応 統計学基礎

  • 作者: 田中豊,中西寛子,姫野哲人,酒折文武,山本義郎,日本統計学会
  • 出版社/メーカー: 東京図書
  • 発売日: 2015/12/10
  • メディア: 単行本
  • この商品を含むブログを見る

ただ、これを頭から読んで勉強していくのはおすすめしない。 なぜなら、ストーリー性が皆無なので順番には読みづらいから。 個人的には辞書として使うのがおすすめ。 分からなかったところをピンポイントに読んで理解の助けにするのが良い。

統計検定3級対応 データの分析

最初に紹介した「完全独習 統計学入門」は素晴らしい本なんだけど、ちょっと残念なところもある。 それは、本のコンセプトからして仕方ないんだけど、推定や検定を説明するまでが最短距離すぎて、色々と省いているところ。 例えば色々な種類のグラフとか、箱ひげ図と五数要約みたいな一般的な話が出てこない。 そういった点では、この本で前提知識をまとめてインプットしておくと良いと思う。

日本統計学会公式認定 統計検定3級対応  データの分析

日本統計学会公式認定 統計検定3級対応 データの分析

  • 作者: 藤井良宜,竹内光悦,後藤智弘,日本統計学会,竹村彰通,岩崎学,美添泰人
  • 出版社/メーカー: 東京図書
  • 発売日: 2012/07/07
  • メディア: 単行本(ソフトカバー)
  • 購入: 1人 クリック: 65回
  • この商品を含むブログ (6件) を見る

この本は割りとサクサク読めるので頭から教科書的に使っても大丈夫そうだった。

高校数学の美しい物語

こちらは書籍ではなくウェブサイトだけど、統計に関する記事が豊富にある。 特定のトピックについてよく分からないなーと思ってぐぐると、こちらがヒットする確率が高い。

mathtrain.jp

役に立つ薬の情報~専門薬学 (統計)

こちらも書籍ではなくウェブサイトだけど、ぐぐるとヒットする確率が高い。 特にカイ二乗検定やt検定の説明の分かりやすさはピカイチだと思う。

統計学

ブログ執筆

あとは、理解があやふやだったり重要だと思ったところはこのブログで積極的に記事にした。 他の人に説明することは自分が理解していないとできないことなので、解説することは理解につながる。

受験について

統計検定は年に二回、夏と冬に定期的に開催されている。 ただし、2級と3級に関しては CBT 方式の試験も随時受けることができる。

www.toukei-kentei.jp

今回合格したのは、上記の CBT 方式の試験だった。 公式サイトにも記述があるけど、受けた後の感想としては問題の難易度は定期試験のそれと変わらない。 だいたい 35 問くらいが出題されて、そのうち 60% (21 問) を正解すれば合格となる。

CBT 方式の試験では、あらかじめテストセンターに予約を入れて、その時間に現地に赴いて受験する。 今回は有楽町にあるオデッセイ公式のテストセンターで受けた。

otc.odyssey-com.co.jp

持参するものは受験票と電卓と身分証だけで、紙とペンは会場で用意してもらえる。 あとはディスプレイに表示される問題を解いて、ひたすら答えをマウスとキーボードで入力していく。

実際に受けた感想としては (他の人はどうか分からないけど) 個人的には試験時間が全然足りなかった。 選択肢から、あらかじめありえないものは枝刈りするなどして時間を節約していくのが重要そう。

受験を終えて

勉強を通して、統計について最低限知っておくべき知識は身につけることができたと思う。 現実世界でも使われている統計の色々な手法について知ることができたのは面白かった。 例えば、選挙の当選確実をどうやって出しているか (母比率の推定) とか、治験の効果の調べ方 (一標本t検定) とか。

とはいえ、あくまで最低限なのは確かで、より専門的な知識をこれから身につけていかないとなという感じ。 これからは、より実務的に使われているデータ分析や機械学習の手法についての勉強にシフトしていこうと思う。

いじょう。