Chainer環境をAWSのUbuntu16.04+CUDA8.0上に構築する

目的

AWSUbuntu 16.04上にChainer環境を構築する

構成

  • OS: Ubuntu 16.04 (AMI ID: ami-0567c164)
  • CUDA 8.0
  • Cudnn8.0
  • Chainer 1.18

手順

cudaのインストール

$ wget https://developer.nvidia.com/compute/machine-learning/cudnn/secure/v5.1/prod/8.0/cudnn-8.0-linux-x64-v5.1-tgz
$ sudo dpkg -i cuda-repo-ubuntu1604_8.0.44-1_amd64.deb
$ sudo apt-get update
$ sudo apt-get install cuda

cudnnのインストール

agreementのせいでwgetで落とせないので、 https://developer.nvidia.com/rdp/cudnn-download から一旦PCに落としてscpでupした。

$ tar zxvf cudnn-8.0-linux-x64-v5.1.tgz

cudaディレクトリが解凍されるので、

$ sudo cp -r cuda/include/* /usr/local/cuda/include
$ sudo cp -r cuda/lib64/* /usr/local/cuda/lib64

として、既存の/usr/local/cuda配下に配置する

cudaへのパスを通す

/usr/local/cuda/bin と /usr/local/cuda/lib64 にパスを通すために、

export PATH="/usr/local/cuda/bin:$PATH"
export LD_LIBRARY_PATH="/usr/local/cuda/lib64:$LD_LIBRARY_PATH"

を.bashrc (ログインシェル環境なら.bash_profileに)追記する

linux-genericのインストール

AWSのイメージには、GPUを動かすのに必要なlinux-genericがインストールされていない という情報があったため、実施。(やらなくても動くかも?未検証)

$ sudo apt install linux-generic
$ sudo reboot 
(再起動後)
$ sudo apt remove linux-virtual
$ sudo apt remove linux-virtual
$ sudo apt autoremove

pipのインストール / アップグレード

$ sudo apt install python-pip
$ pip install --upgrade pip

pyenvで必要になるので、opensslをインストール

$ sudo apt-get install libssl-dev

pyenv環境の構築

  • Chainer等のバージョンアップが今後多くなることを想定し、pyenv環境を作っておく。
$ cd ~
$ git clone git://github.com/yyuu/pyenv.git ~/.pyenv
  • 以下コマンドによって.bash_rc(もしくは.bash_profile)に追記し、読み込まれるようにしておく
echo 'export PYENV_ROOT="${HOME}/.pyenv"' >> ~/.bashrc
echo 'if [ -d "${PYENV_ROOT}" ]; then' >> ~/.bashrc
echo '    export PATH=${PYENV_ROOT}/bin:$PATH' >> ~/.bashrc
echo '    export PATH=${PYENV_ROOT}/shims:$PATH' >> ~/.bashrc
echo '    eval "$(pyenv init -)"' >> ~/.bashrc
echo 'fi' >> ~/.bashrc
  • その場でも読み込んでおく(再ログインでも可)
$ source ~/.bashrc

上記は、インタラクティブシェルの場合。ログインシェル環境なら.bash_profileに追記し、source ~/.bash_profileする

  • pyenv-virtualenvもインストールする
$ cd ~/.pyenv/plugins
$ git clone git://github.com/yyuu/pyenv-virtualenv.git
  • pyenvで利用するpythonをインストール(インストールできるものはpyenv install -l で確認)
$ pyenv install 3.5.2
  • インストール完了の確認
$ pyenv versions
* system (set by /home/ubuntu/.pyenv/version)
  3.5.2
  • pipもpyenv化されていることを確認する
$ which pip
/home/ubuntu/.pyenv/shims/pip

/home/ubuntu/.local/bin/pip となっていたらおそらくパス設定をミスしている。

  • 利用するpyenvを設定する
$ pyenv global 3.5.2
  • 念のためログインし直しても同じ状態をキープしているか確認しておく
$ pyenv versions
  system
* 3.5.2 (set by /home/ubuntu/.pyenv/version)

pip パッケージのインストール

下記の内容をrequirements.txtとして保存して、依存パッケージのインストール

appdirs==1.4.0
chainer==1.18.0
cupy==1.0.0
cycler==0.10.0
decorator==4.0.10
filelock==2.0.7
matplotlib==1.5.3
nose==1.3.7
numpy==1.11.2
Pillow==3.4.2
protobuf==3.1.0.post1
py==1.4.31
pyparsing==2.1.10
pytest==3.0.4
python-dateutil==2.6.0
pytools==2016.2.4
pytz==2016.7
six==1.10.0

Pillowとかは最初なくてもよいかも。

$ pip install -r requirements.txt

これで、3.5.2 env配下にpipがインストールされた。chainerのバージョンをあげたりした場合は、 envを消して別のenvを定義して再インストールすればよい。

動作確認

  • CUDAの動作確認を行う。
$ python
>>> import chainer
>>> chainer.cuda.available
True
>>> chainer.cuda.cudnn_enabled
True
  • 公式サンプルを落として、実際に動かしてみる。
$ git clone https://github.com/pfnet/chainer.git
$ cd chainer/examples/mnist/
$ python train_mnist.py -g=0
  • 実行できることを確認
  • 標準出力冒頭で表示されたGPUGPU: 0であることを確認

  • -g=0オプションありと無しで実行時間を比較してみた。

GPUなし:
real     12m44.315s
user     30m1.920s
sys     60m23.720s
GPUあり:
real     1m20.846s
user     1m22.468s
sys     0m1.708s

10倍速になっており、確かにCUDAによる高速化が実現できている。

お掃除

不要なディレクトリやファイルを削除する

$ cd ~
$ rm cuda-repo-ubuntu1604_8.0.44-1_amd64.deb
$ rm cudnn-8.0-linux-x64-v5.1.tgz
$ rm -r cuda
$ rm requirements.txt

経緯:

AWSGPU演算環境を確保しようと思い、適切なAMIを探していた。CUDAが最初からインストールされたAMIを使おうとしたらスポットリクエストには対応していなかったため、デフォルトのUbuntuでAMIを作成するのが結局割安に付きそうだったためクリーンインストールしてマイAMIを作成した

できるだけ丁寧にGANとDCGANを理解する

目的

  • Chainerの扱いに慣れてきたので、ニューラルネットワークを使った画像生成に手を出してみたい
  • いろいろな手法が提案されているが、まずは今年始めに話題になったDCGANを実際に試してみるたい
  • そのために、 DCGANをできるだけ丁寧に理解することがこのエントリの目的
  • 将来GAN / DCGANを触る人の助けになったり、理解間違ってるところにツッコミがあると嬉しい

本エントリの構成

  1. DCGANの前提となっているGANの論文の要点をまとめる
  2. DCGANでGANとの差分として提案されている要点をまとめる
  3. DCGANのmattyaさんの実装を読み通して詳細を理解する

1. GANについて

  • GANは、サンプル群と似たような性質を持つ出力を生成するためのフレームワーク
  • 2014年にIan J. Goodfellowらによって提案された
  • 論文: Generative Adversarial Nets [リンク]

  • 以下の2つのモデルの訓練を同時に進め、互いに競わせる

    • D: Discriminator(鑑別器): (生成したいサンプルとGの出力物を正しく鑑別できることを目指す)
    • G: Generator(生成器):(ランダムノイズを入力として、Dが誤ってサンプルであると認識する率を高めることを目指す)
  • GANは下記の式の価値関数V(G, D)で表現されるminimaxゲームとして定義できる
 
\begin{eqnarray} 
\min_{G}\max_{D} V(D, G) =  \mathbb{E}_{x \sim p_{data}(x)}\big[\log D(x)\big] +  \mathbb{E}_{x \sim p_{z}(x)}\big[\log (1-D(G(z)))\big]
\end{eqnarray} 

  • この式を、前提含め日本語で書き下してみると
[前提]
・ p_zはある種の任意の分布(例えば一様分布)を表す。zは個々のノイズサンプルを表す。
・Gはzを入力とし、 p_gに分布させる。 p_gはジェネレータGから生成された出力の分布を示す
・Dは入力がサンプルから来た確率を表す(1であれば入力はサンプル分布 p_xから、0であればGの出力分布 p_gからと判断)
[左辺]
・価値関数Vは関数DとGを引数に取る、右辺で表される関数である
・Dについての最大、Gについての最小となるようD, Gを定める
[右辺]
・確率変数xは確率分布 p_{data}に従う
・確率変数zは確率分布 p_zに従う
・このとき、 log D(x)の期待値と、 log(1-D(G(z)))の期待値の和を評価関数とする
(Dがサンプルを正しくサンプルと判定できれば log D(x)が大きくなり、DがGの出力をサンプルだと判定すると log(1-D(G(z)))が小さくなる)

となる。(と思うのですが、間違っていたらご指摘いただければ嬉しいです..)

  • この式自体は、G、Dがニューラルネットワークであることを前提とはしていない(言い換えれば、別な関数最適化手法であっても適用できる、かもしれない)

  • 論文ではD, Gにニューラルネットワークを使うことで、既存の最尤推定による生成モデルで手に負えないほど計算量が増える問題をbackpropagationで回避できるとしている

  • 論文掲載のアルゴリズムは下記となる

    1. ミニバッチサイズm個のノイズ {z_1}...{z_m} p_g(z)から取り出す(生成する)
      (論文は p_gからになってるけど、 p_zの誤植のような..?)
    2. ミニバッチサイズm個のサンプル {x_1}...{x_m}をデータ生成分布 p_{data}(x)から取り出す
    3. 下記式の、  \theta_d における確率的勾配を上るように鑑別器Dを更新する
       \begin{eqnarray}   \frac{1}{m}\sum_{i=0}^m \big(\log D(x^{(i)}) + \big(\log (1 - D(G(z^{(i)} ))) \big) \end{eqnarray}
    4. 上記までをk回繰り返す
    5. ミニバッチサイズm個のノイズ {z_1}...{z_m} p_g(z)から取り出す (ここも p_zが正しいような..?)
    6. 下記式の、 \theta_g における確率的勾配を下るように生成器Gを更新する
       \begin{eqnarray}   \frac{1}{m}\sum_{i=0}^m \log (1 - D(G(z^{(i)} ))) \end{eqnarray}
    7. ここまで全てを、訓練回数分だけ繰り返す
  • 鑑別器Dを十分な回数(k回)更新した上で生成器Gを1回更新することで、常に鑑別器が新しいGの状態に適用できるように学習を進める

  • 4.1 ~ 4.2 [tex: p_g = p{data} ] の時にD, Gそれぞれについての最適化が達成される
    ==> このため、 p_g を [tex: p
    {data} ] に近似させることが上記評価関数の解への近似として正当化される

  • 利点と欠点

    • 欠点: 明示的な p_g が最初は存在せず、DはGとシンクロさせて訓練しなければならない (特に、DをupdateせずにGだけを訓練すると、Gが入力ノイズzの多くをxと同じ値に収束させてしまう点に注意)
    • 利点:
      • マルコフ鎖で複数のモデルを混ぜるためにぼやけたものになるが、GANではマルコフ鎖が不要でシャープな画像が生成できる。
      • 勾配を得るためにBPが使えるため、学習に近似が不要。
      • 様々なモデルを用いることができる
      • そして何より、「計算可能(computational)である」。
      • サンプルと直接比較するのではなく、Discriminatorの評価を介して生成するため、inputの部品をGがそのまま丸覚えすることを避けられる。

2. DCGANについて

  • GANは具体的なネットワークの構成に言及していない。(少なくとも論文中では)
  • DCGAN(Deep Convolutional Generative Adversarial Networks) は、GANに対して畳み込みニューラルネットワークを適用して、うまく学習が成立するベストプラクティスについて提案したもの。
  • 元になった論文 Unsupervised Representation Learning with Deep Convolutional Generative Adversarial Networks リンク
  • 要点をまとめると下記のようになる
    • プーリング層を全て下記で置き換える
      • D(鑑別器): 畳み込み層(strided convolutions)(これはいわゆる普通の畳み込み層のことらしい)
      • G(生成器): 分数的にストライドする畳み込み層 (fractional-strieded convolutions)(これはすなわちdeconvolution a.k.a. transposed convolutionsのことらしい...)
    • バッチノルムを使用する(重要らしい)
    • 深い構成では全結合層を除去する
    • 生成器ではReLUを出力層以外の全ての層で活性化関数として使用し、出力層ではtanhを使用する
    • 識別器ではLeakyReLUを全ての層で使用する

3. DCGANのコードを読む

前処理

  • import / 定数宣言は解説省略
  • image_dirから全てのイメージを読み込んで、dataset配列に追加している。
  • ELU: exponential Linear Unitの定義。 数式でいうと \alpha > 0の元で
 
\begin{eqnarray} 

f(x) = \left\{
\begin{array}{ll}
x  & \text{if }  x > 0 \\
\alpha(\exp(x)-1) & \text{if } x \leq 0
\end{array}
\right.
,
f'(x) = \left\{
\begin{array}{ll}
1  & \text{if }  x > 0 \\
f(x)+\alpha & \text{if } x \leq 0
\end{array}
\right.

\end{eqnarray} 

グラフでいうと

f:id:mizti:20161210164101p:plain (出典: Djork-Arne Clevert, Thomas Unterthiner & Sepp Hochreiter 2016 https://arxiv.org/pdf/1511.07289v5.pdf )

となるもので、LeakyReLUをなめらかにした感じのものらしい。

Generatorの定義:

  • 一様分布ノイズz(1次元 / 100要素)を入力として、
    => 100入力、66512 出力のLinear Unit で(100, 512, 6, 6 )に=> BatchNormalization => relu => => 512チャネル入力、256チャネル出力、pad1、stride2、フィルタサイズ4のDeconvolution2D で(100, 256, 12, 12)に=> BN => relu
    => 256チャネル入力、128チャネル出力、pad1、stride2、フィルタサイズ4のDeconvolution2D で(100, 128, 24, 24)に=> BN => relu
    => 128チャネル入力、64チャネル出力、pad1、stride2、フィルタサイズ4のDeconvolution2D で(100, 64, 48, 48)に=> BN => relu
    => 64チャネル入力、3チャネル出力、pad1、stride2、フィルタサイズ4のDeconvolution2D
    として、最終的に(100, 3, 96, 96) (つまり、ミニバッチ100枚 x RGB3チャネル x w96 x h96)のデータが得られる。

Discriminatorの定義:

  • サンプルイメージ or Gの出力(3, 96, 96)を入力として、
    => 3チャネル入力、64チャネル出力、pad1、stride2、フィルタサイズ4のConvolution2Dで (64, 48, 48)に => 上述のELU
    => 64チャネル入力、128チャネル出力、pad1、stride2、フィルタサイズ4のConvolution2Dで(128, 24, 24)に => BN => ELU
    => 128チャネル入力、256チャネル出力、pad1、stride2、フィルタサイズ4のConvolution2Dで(256, 12, 12)に => BN => ELU
    => 256チャネル入力、512チャネル出力、pad1、stride2、フィルタサイズ4のConvolution2Dで(516, 6, 6)に => BN => ELU
    => 512 x 6 x 6 入力、2出力の全結合層

訓練

  • Gの訓練
    • Gの訓練時の入力zは、-1から1の間で一様分布の (100 x 100 )二次元のランダム値
    • Gからの出力xをDの入力とする
    • Dの出力ylは「Dが入力をGのものだと思う確率」を示している(元論文とは逆に設定されてるので注意)  すなわち、Dの出力が0であればデータセットからの出力、1であればGが"偽造"した出力と判断
    • Gの損失: ylとbatch数だけ並んだ0とのソフトマックスクロスエントロピー関数出力(ylが全て0であれば損失なし)
    • Dの損失: ylとbatch数だけ並んだ1とのソフトマックスクロスエントロピー関数出力(ylが全て1であれば損失なし)
  • Dの訓練
    • x2をDの入力にして、yl2を出力。
    • Dの損失に「yl2とbatch数だけ並んだ0とのソフトマックスクロスエントロピー関数出力」を足し合わせる
  • 勾配初期化とbackpropagateによる重み更新
  • image_save_interval回の訓練毎にGENを使って画像を100枚。
    この際の生成のタネにする乱数は訓練開始前に生成したzvisを常に利用する
  • 毎epoch完了ごとにdis, gen, o_dis, o_genを保存

わからなかった点

  • 何故Discriminatorが「2つ」の出力を持つようにしているのかわからない。入力の p_{data}かららしさだけを出力するのなら、1出力でよさそうに思えてしまう。損失関数の計算方法から見るに、どちらか1つの要素がG、もう一つがdataからの入力というわけでもなさそうに思える。

その他

Chainerでフォントを識別してみる (2)

前回、二つのフォントをいい感じに識別できていたので、識別対象フォントを12に増やしてみた。

今回識別対象にしたフォントたち

Helvetica
f:id:mizti:20161204123136p:plain
Bodoni
f:id:mizti:20161126200655p:plain
Futura
f:id:mizti:20161204123102p:plain
Optima
f:id:mizti:20161204123045p:plain
Clarendon
f:id:mizti:20161204123149p:plain
GillSans
f:id:mizti:20161204123203p:plain
Caslon
f:id:mizti:20161204123216p:plain
Garamond
f:id:mizti:20161204123227p:plain
Avenir
f:id:mizti:20161204123240p:plain
Times New Roman
f:id:mizti:20161204123251p:plain
American Typewriter
f:id:mizti:20161204123304p:plain
Trajan
f:id:mizti:20161204123322p:plain

訓練データとテストデータの構成

  • 訓練データは[a-mA-M]の64x64 グレースケール画像。画像ごとに位置はランダム
  • テストデータは[n-zN-Z]の64x64 グレースケール画像。同じく画像ごとに位置はランダム化してある

実験

試行1: 3層多層パーセプトロン(隠れ層ニューロン数100)

前回使ったのと々、隠れ層2層、出力層1層の多層パーセプトロン

=> 87%程度で識別率が頭打ちになった。2クラスへの識別だけであれば、同条件で97%まで行っていたので、
12クラスへの識別になって認識率が落ちたことが確認できる。

試行2: 3層多層パーセプトロン(隠れ層ニューロン数200)

2つの隠れ層の表現能力が足りないのかもしれないと思い、ニューロン数を200に増やしてみたパターン。

=> 90%程度で頭打ち。試行1よりかすかに識別率が上がった

試行3: CNN

CNN(畳み込みニューラルネットワーク)を導入。畳み込み+relu+Max Poolingを3セットの後にLinear層を2層。
epoch数100を超えたあたりで識別率が99.9%以上となった。

f:id:mizti:20161204134938p:plain

今後

・十分な汎化性能がありそうなので、多種多様な日本語フォントの識別を行ってみる
・各フォントにはItalicやBoldなどいろいろなスタイルもあるため、スタイル変更した際にも識別を行えるか試す
・スタイル転写とか使って、英字フォントから日本語フォント作れたら面白い気がする

Chainerでフォントを識別してみる (1)

Chainerで何か分類してみるにあたって、

1. 分類できて嬉しい
2. サンプル収集の目処が立ちそう
3. 莫大なデータ量にならない

な分類対象として、文字のフォントの分類をしてみようと考えた。

1. 分類対象クラス

分類クラス数は今後増やしていくとして、まずはセリフ体とサンセリフ体一種類ずつ、BodoniとHelveticaを分類してみることにした。

Bodoni

f:id:mizti:20161126200655p:plain

Helvetica

f:id:mizti:20161126200706p:plain

2. データセットの作成
  • 大文字もしくは小文字を一文字ずつ64*64の画像に印字したデータを作成する。これより画像が小さいと、ジャギが立ってしまうので、フォント判別に良く無いと判断した
  • 用意するデータ毎にx位置y位置をずらしておく。(フォントによって、ベースライン位置が異なっていたりするため、これを手掛かりにさせず、あくまで字形で分類させたいため)
  • Train用のデータセットでは[a-mA-M]、Test用のデータセットでは[n-zN-Z]の文字を用いる

train用
f:id:mizti:20161126180043p:plain
test用
f:id:mizti:20161126180016p:plain
(※データをちゃんと作れたか確認のため画像出力したが、実際にはメモリ上でリスト保持するのみ)

  • ChainerのOptimizer、Trainerを用いる事ができるように、DatasetMixinを継承したクラスとしてデータセットクラスを作成した。

(DataMixinを継承したクラスで__len__とget_exampleだけ定義してあげればChainerのIterationに渡せるようになる)

  • Datasetのコンストラクト引数で、サンプル数とtrain / testの指定を行えるようにした
import sys
import random
import numpy as np
from PIL import ImageFont
from PIL import Image
from PIL import ImageDraw
import chainer
from chainer import cuda, Function, gradient_check, report, training, utils, Variable
from chainer import datasets, iterators, optimizers, serializers
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L
from chainer.training import extensions

class FontImageDataset(chainer.dataset.DatasetMixin):
	def __init__(self, datanum=10, normalize=True, flatten=True, train=True):
		self._normalize = normalize
		self._flatten = flatten
		self._train = train
		pairs = []
		for _ in range(datanum):
			image_array, label = self.generate_image()
			pairs.append([image_array, label])
		self._pairs = pairs

	def __len__(self):
		return len(self._pairs)

	def generate_image(self):
		fonts = [
		    'font_files/Helvetica.ttf',
		    'font_files/BodoniSvtyTwoITCTT-Book.ttf',
			'font_files/Futura-Medium.ttf',
			'font_files/Optima-Regular.ttf'
		]
		label = random.randint(0,len(fonts)-1)
		fontFile = fonts[label]
		font = ImageFont.truetype(fontFile, 60)

		train_characters = [
			'A','B','C','D','E','F','G','H','I','J','K','L','M',
			'a','b','c','d','e','f','g','h','i','j','k','l','m'
		]
		test_characters  = [
			'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
			'n','o','p','q','r','s','t','u','v','w','x','y','z'
		]
		text = ''

		if self._train:
			text = random.choice(train_characters)
		else:
			text = random.choice(test_characters)

		w, h = 64, 64
		text_w, text_h = font.getsize(text)
		text_x, text_y = (w - text_w) * random.random(), (h - text_h) * random.random()

		im = Image.new('L', (w, h), 255)
		draw = ImageDraw.Draw(im)
		draw.text((text_x, text_y), text, fill=(0), font=font)
		#im.save('image' + str(random.randint(0, 100)) + '.png')
		#if self._train:
		#	im.save('temp/image_train' + str(random.randint(0, 100)) + '.png')
		#else:
		#	im.save('temp/image_test' + str(random.randint(0, 100)) + '.png')

		image_array = np.asarray(im)

		if self._normalize:
		    image_array = image_array / np.max(image_array)

		if self._flatten:
			image_array = image_array.flatten()

		# type cast
		image_array = image_array.astype('float32')
		label = np.int32(label)
		return image_array, label

	def get_example(self, i):
		image_array, label = self._pairs[i][0], self._pairs[i][1]
		return image_array, label
3. 学習モデルと訓練

ほとんどまだチュートリアルのまま回した感じ。これから分類クラス数を増やしたり
モデルを差し替えたりハイパーパラメータをあれこれして楽しみたい

import numpy as np
import chainer
from chainer import cuda, Function, gradient_check, report, training, utils, Variable
from chainer import datasets, iterators, optimizers, serializers
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L
from chainer.training import extensions
from font_image_dataset import *

train_data = FontImageDataset(5000, train=True)
test_data = FontImageDataset(5000, train=False)
train_iter = iterators.SerialIterator(train_data, batch_size=200, shuffle=True)
test_iter = iterators.SerialIterator(test_data, batch_size=200, repeat=False, shuffle=False)

class MLP(Chain):
    def __init__(self, n_units, n_out):
        super(MLP, self).__init__(
            l1 = L.Linear(None, n_units),
            l2 = L.Linear(None, n_units),
            l3 = L.Linear(None, n_out)
        )

    def __call__(self, x):
        h1 = F.relu(self.l1(x))
        h2 = F.relu(self.l2(x))
        y = self.l3(h2)
        return y

class Classifier(Chain):
    def __init__(self, predictor):
        super(Classifier, self).__init__(predictor=predictor)

    def __call__(self, x, t):
        y = self.predictor(x)
        loss = F.softmax_cross_entropy(y, t)
        accuracy = F.accuracy(y, t)
        report({'loss': loss, 'accuracy': accuracy}, self)
        return loss

model = L.Classifier(MLP(100, 2))
optimizer = optimizers.SGD()

optimizer.setup(model)

updater = training.StandardUpdater(train_iter, optimizer, device=-1)
trainer = training.Trainer(updater, (300, 'epoch'), out='result')
print("start running")
trainer.extend(extensions.Evaluator(test_iter, model))
trainer.extend(extensions.LogReport())
trainer.extend(extensions.PrintReport(['epoch', 'main/accuracy', 'validation/main/accuracy']))
trainer.extend(extensions.ProgressBar())
trainer.run()
print("end running")
4. 結果
  • この設定なら非力なMacBookのCPUでも5分程度で学習完了。
  • エポック250くらいで識別率97%くらいまで上がった。

f:id:mizti:20161126193903p:plain

5. この後
  • 分類クラス数を増やしてみると識別率上限が低くなると思うのでいろんなモデルを試して見る

「ゼロから作るDeep Learning」を読み終わった

「ゼロから作るDeep Learning」を読み終わった。

Pros:
1ニューロンパーセプトロンを作るところから始めて、3層NN、誤差逆伝搬法、CNNまで手作りしていく。
理論と実践のバランスが良く、該当分野の入門書としてとてもお勧めできると感じた。

中でも誤差逆伝搬法の説明が懇切丁寧で、他のNeural Network本を読んで
「で、全結合層以外の誤差逆伝搬はどうやって考えればいいんだ..」と悩んでいた自分にはぴったりだった。

あと、NNへ学習を導入する流れについても
=> 学習するっていうのはつまりどういうことか
=> 微分とは何か、勾配とは何か
=> 数値微分があればどんな関数でも勾配計算できるんだぜ
=> これでSGDすれば学習できるよ
=> でも遅いよね
=> じゃあ誤差逆伝搬を使ってみよう
という感じで、目的と手段をちゃんと切り分けて解説をしているところがとてもわかりやすい。

Cons:
贅沢を言えば、CNN層、Pooling層の逆伝搬についても解説があると嬉しかった。

(※ちなみに、入門用のもう一つのお勧めはオンラインテキスト "Neural networks and deep learning" 途中まで日本語版はこちら。こちらは、「なぜニューラルネットワークが画像を認識できるのか」の解説がとてもわかりやすかった。)

ブロックチェーンとは (bitcoin wiki 和訳)

ブロックチェーンとはbitcoin wikiの文章を和訳してみる。
誤訳等指摘いただければ喜びます
Block chain - Bitcoin Wiki

A block chain is a transaction database shared by all nodes participating in a system based on the Bitcoin protocol. A full copy of a currency's block chain contains every transaction ever executed in the currency. With this information, one can find out how much value belonged to each address at any point in history.

ブロックチェーンとはBitcoinプロトコルに則ってシステムに参加しているすべてのノードの間でシェアされている取引(transaction)のデータベースである。ある通貨のブロックチェーンのフルコピーは、その通貨において今までに行われたすべての取引を含んでいる。この情報から、それぞれのアドレスが、歴史上の任意の時点においてどれだけの価値を持っているかを各自が見出すことができる。

Every block contains a hash of the previous block. This has the effect of creating a chain of blocks from the genesis block to the current block. Each block is guaranteed to come after the previous block chronologically because the previous block's hash would otherwise not be known. Each block is also computationally impractical to modify once it has been in the chain for a while because every block after it would also have to be regenerated. These properties are what make double-spending of bitcoins very difficult. The block chain is the main innovation of Bitcoin.

それぞれのブロックは前のブロックのハッシュを含んでいる。これは原初のブロック(the genesis block)から現在のブロックまでを一つのブロックの連なり(a chain of blocks)にする効果を持っている。それぞれのブロックは時系列的に前のブロックの後ろにくることが保証されている、というのはもしそうでなければ前のブロックのハッシュが知られることができないからである。また、それぞれのブロックは一度チェーンに含まれてしばらくすると改変することは、その後のそれぞれのブロックを再生成しなければならないため計算量的に実行不可能である。これらの性質によりBitcoinの多重利用(double spending)はとても難しいものとなる。ブロックチェーンはBitcoinの主たるイノベーションである。


Honest generators only build onto a block (by referencing it in blocks they create) if it is the latest block in the longest valid chain. "Length" is calculated as total combined difficulty of that chain, not number of blocks, though this distinction is only important in the context of a few potential attacks. A chain is valid if all of the blocks and transactions within it are valid, and only if it starts with the genesis block.

もしそれが最長のValidなチェーンの最新のブロックである場合、正直な生成者達はブロックの上に(それを彼らが生成するブロックの中で参照しながら)構築するのみである。「長さ」はブロックの数ではなく、チェーンの難しさの総和として計算されるが、この区別はいくつかの潜在的な攻撃の文脈においてのみ重要になるものである。あるチェーンは、それに含まれるすべてのブロックと取引が有効であり、原初のブロックから始まっている場合においてのみ有効である。

For any block on the chain, there is only one path to the genesis block. Coming from the genesis block, however, there can be forks. One-block forks are created from time to time when two blocks are created just a few seconds apart. When that happens, generating nodes build onto whichever one of the blocks they received first. Whichever block ends up being included in the next block becomes part of the main chain because that chain is longer. More serious forks have occurred after fixing bugs that required backward-incompatible changes.

チェーン上のどのブロックにも、原初のブロックに至るたった一つのパスがある。しかしながら、原初のブロックから来るときには分岐が存在しうる。2つのブロックがほんの数秒の違いで生成されたときに、一つのブロック分岐が生まれることが時々ある。これが起きたときには、ノードはどちらでも先に受信した方のブロックの上に構築を行う。次のブロックに含まれるブロックはそれがより長くなるため結局はメインチェーンの一部となる。後方互換性の無い変更が必要なバグの修正後により深刻なフォークが起こったこともある。


Blocks in shorter chains (or invalid chains) are not used for anything. When the bitcoin client switches to another, longer chain, all valid transactions of the blocks inside the shorter chain are re-added to the pool of queued transactions and will be included in another block. The reward for the blocks on the shorter chain will not be present in the longest chain, so they will be practically lost, which is why a network-enforced 100-block maturation time for generations exists.

短い方のチェーン(もしくは無効なチェーン)に含まれるブロックは何にも用いられない。Bitcoinクライアントが他のより長いチェーンに切り替わった場合、短い方のチェーン内のすべての有効な取引のブロックはキューに積まれた取引のプールに再度追加され、他のブロックに組み込まれることとなる。短い方のチェーンへの報酬は最長のチェーンにおいては贈呈されないため、それらは事実上失われる。これが、生成に対してネットワークにより強制された100ブロックの熟成期間が存在する理由である。

These blocks on the shorter chains are often called "orphan" blocks. This is because the generation transactions do not have a parent block in the longest chain, so these generation transactions show up as orphan in the listtransactions RPC call. Several pools have misinterpreted these messages and started calling their blocks "orphans". In reality, these blocks have a parent block, and might even have children.

短いチェーン上のこれらのブロックはしばしば「孤児」(orphan) ブロックと呼ばれる。これはその生成取引(?: 原文 the generation transactions )が最長ブロックにおいて親ブロックを持っておらず、そのためこれらの生成取引はlisttransaction RPCコールにおいて孤児となって現れる。幾つかのプールはこれらのメッセージを誤解して、これらのブロックを「孤児」と呼び始める。実際にはこれらのブロックは親ブロックを持っており、子供さえ持っているかもしれないのだ。

Because a block can only reference one previous block, it is impossible for two forked chains to merge.

ブロックは前のブロック一つを参照することしかできないため、二つの分岐したチェーンをマージすることは不可能である。

It's possible to use the block chain algorithm for non-financial purposes: see Alternative chain.

ブロックチェーンアルゴリズムを金融ではない目的に使うことは可能である:Alternative chain を見よ

The block chain is broadcast to all nodes on the networking using a flood protocol: see Block chain download.

ブロックチェーンはfloodプロトコルを使ってネットワーク上のすべてのノードにブロードキャストされる: Block chain download を見よ

ーーー
この文書のライセンスは
http://creativecommons.org/licenses/by/3.0/

bitcoinのアドレスについて[和訳]

例によってあんまり日本語で転がってなかった情報のbitcoin wikiの和訳シリーズ。Bitcoinクライアントをダウンロードして使ってみたらアドレスが沢山あってどういう仕組みかわからなかったので勉強がてら翻訳してみた。

      • -

Address - Bitcoin Wiki

Bitcoinアドレス、もしくは単にアドレス、は数字1か3から始まる、英数字26-35文字の識別子であり、あり得るBitcoin支払の宛先を表す。アドレスはいかなるBitcoinユーザもコスト無しに作ることができる。例えば、Bitcion-Qt (訳者注: Bitcoinのクライアント名) を使うと、「New Address」を一回押すとアドレスが一つ割り当てられる。Bitcoinアドレスは取引所やオンラインウォレットサービスのアカウントを用いても作ることができる。

Bitcoinアドレスの一つの例は 3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy である。

  1. Bitcoinアドレスは使い捨てトークンである

Emailアドレスのように、あなたはBitcoinをアドレスのうち一つに送ることでBitcoinを人に送ることができる。しかしながら、Emailアドレスとはことなり、人は沢山の異なるBitcoinアドレスを持ちそれぞれの取引でユニークなアドレスが用いられるはずである。ほとんどのBitcoinソフトウェアやウェブサイトであなたが請求や支払リクエストを行うたびに真新しいアドレスを利用できるようになっている。

  1. アドレスはオフラインで生成できる

アドレスの生成はインターネット接続無しに行うことができ、Bitcoinネットワークとの接続や登録も求められない。無料で入手できるソフトウェアツールを使って大量のアドレスの束をオフラインで作ることも可能である。大量のアドレス束を生成することは、eコマースウェブサイトとしてあらかじめ生成したユニークなアドレスを「Bitcoinで支払う」オプションを選択したそれぞれの顧客に分配するなど幾つかのシナリオで有用である。最近の「 決定論的ウォレット(HD wallets) 」は(Webサーバのような)信頼できないシステムに受け取ったBitcoinを消費させることなく限りない数のアドレスを作らせられるように「シード」となるトークンを生成することができる。

  1. アドレスはケースセンシティブで厳格である

Bitcoinアドレスはケースセンシティブ(大文字/小文字を区別する)である。Bitcoinアドレスはどこであれれ可能であればコンピューターのクリップボードを用いてコピー、ペーストするべきである。もしBitcoinアドレスを手写し、それぞれの文字が厳密にー大文字小文字含めてー書き写されなかったら、間違ったアドレスはBitcoinソフトウェアによって大抵は拒否されるだろう。もう一度自分の入力を確認しもう一度試さなければならないだろう。

ミスタイプしたアドレスが妥当であるとして受理される可能性は1/2^32であり、おおよそ429万回に1回である。

  1. あるアドレスであなたが受信することを示す

ほとんどのBitcoinウォレットはメッセージに「署名」する機能を持っており、そのアドレスで資金を受け取った存在がメッセージに賛成したことを示すことができる。

特定のアドレスを認可専用とし、そのアドレスが実際のBitcoin取引に決して利用されることがないようにすることでこの能力に便乗するサービスも出てくるだろう。あなたがこういったサービスにログイン、もしくは使用しようとするときには、あなたが前に署名したアドレスを持った人と同一であることを署名を提供して示すことになるだろう。

これらの署名はあるアドレスが受け取ることを示すだけであるということは重要な留意すべきことである。というのはBitcoin取引は"from"アドレスを持っておらず、あなたが資金の送り元であることを示すことはできないからである。

Current standards for message signatures are only compatible with "version zero" bitcoin addresses (that begin with the number 1).
現在のメッセージ署名の標準は「バージョンゼロ」Bitcoinアドレス(番号1から始まる)のみと互換性がある。

  1. アドレスの検証

If you would like to validate a Bitcoin address in an application, it is advisable to use a method from this thread rather than to just check for string length, allowed characters, or that the address starts with a 1 or 3. Validation may also be done using open source code available in various languages or with an online validating tool.

  1. Multi-signature addresses

Addresses can be created that require a combination of multiple private keys. Since these take advantage of newer features, they begin with the newer prefix of 3 instead of the older 1. These can be thought of as the equivalent of writing a check to two parties - "pay to the order of somebody AND somebody else" - where both parties must endorse the check in order to receive the funds.

The actual requirement (number of private keys needed, their corresponding public keys, etc.) that must be satisfied to spend the funds is decided in advance by the person generating this type of address, and once an address is created, the requirement cannot be changed without generating a new address.

  1. What's in an address

Most Bitcoin addresses are 34 characters. They consist of random digits and uppercase and lowercase letters, with the exception that the uppercase letter "O", uppercase letter "I", lowercase letter "l", and the number "0" are never used to prevent visual ambiguity.

Some Bitcoin addresses can be shorter than 34 characters (as few as 26) and still be valid. A significant percentage of Bitcoin addresses are only 33 characters, and some addresses may be even shorter. Every Bitcoin address stands for a number. These shorter addresses are valid simply because they stand for numbers that happen to start with zeroes, and when the zeroes are omitted, the encoded address gets shorter.

Several of the characters inside a Bitcoin address are used as a checksum so that typographical errors can be automatically found and rejected. The checksum also allows Bitcoin software to confirm that a 33-character (or shorter) address is in fact valid and isn't simply an address with a missing character.

  1. Testnet

Addresses on the Bitcoin Testnet are generated with a different address version, which results in a different prefix. See List of address prefixes and Testnet for more details.

  1. Misconceptions
    1. Address reuse

Addresses are not intended to be used more than once, and doing so has numerous problems associated. See the dedicated article on address reuse for more details.

    1. Address balances

Addresses are not wallets nor accounts, and do not carry balances. They only receive funds, and you do not send "from" an address at any time. Various confusing services and software display bitcoins received with an address, minus bitcoins sent in random unrelated transactions as an "address balance", but this number is not meaningful: it does not infer the recipient of the bitcoins sent to the address has spent them, nor that they still have the bitcoins received.

An example of bitcoin loss resulting from this misunderstanding is when people believed their address contained 3btc. They spent 0.5btc and believed the address now contained 2.5btc when actually it contained zero. The remaining 2.5btc was transfered to a change address which was not backed up and therefore lost. This has happened on a few occasions to users of Paper wallets.

    1. "From" addresses

Bitcoin transactions do not have any kind of origin-, source- or "from" address. See the dedicated article on "from address" for more details.

  1. See Also