エンジニア4年目のブログ

Javaエンジニア4年目のブログです。

Chainerに入門してみる:Links

どうもShZgZです。

shzgz.hatenablog.com
↑前回に引き続きChainerの公式ガイドを進めていきます。

↓Chainer公式ガイド
Links — Chainer 4.5.0 documentation

"補足"ってなってるところは付け足しているところで、
それ以外は大体原文の和訳になってます。

Links

NN(ニューラルネット)をコーディングするためには、
パラメータを持った関数を結合し、そのパラメータを
最適化しなければならない。
このために(最適化の目標となるような)パラメータを持つ
Linkクラスのオブジェクトを用いる。

リンクの中で最も基本的なものはパラメータによって
いくつかの引数の値を置き換える正則関数のような
振る舞いをするリンクです。今後、よりハイレベルなリンクを導入
するが、ここではリンクはパラメータを持つシンプルな関数とみなす。

もっとも頻繁に使うリンクはLinearリンクです。(Linearリンクは
Fully-connected Layerもしくはアフィン変換としても知られている)
Linearリンクは数学的な関数f(x) = Wx + bを表しており、
行列W、ベクトルbはパラメータである。このリンクは
純粋にx, W, bを引数にとるlinear()に対応している。
3次元から2次元への線形リンクは以下のように定義される。

f = L.Linear(3,2)

~~~!Note
ほとんどの関数やリンクは、入力列の第1次元がバッチの次元と
見なされるようなミニバッチの入力のみを受けとる。
上の線形リンクの場合は入力は(N, 3)という形にならなければならない。
(Nはミニバッチのサイズ)
~~~

リンクのパラメータは属性として保存され、それぞれの
パラメータはVariableのインスタンスとなる。線形リンクの
場合はパラメータW,bが保存される。デフォルトでは
行列Wはランダムに、ベクトルbは0で初期化される。
これらのパラメータを初期化する上では、この方法は
よく使われるものです。

f.W.data # array([[ 1.0184761 ,  0.23103087,  0.5650746 ],
         #        [ 1.2937803 ,  1.0782351 , -0.56423163]], dtype=float32)
f.b.data # array([0., 0.], dtype=float32)

線形リンクのインスタンスの挙動は普通の関数と似ている

x = Variable(np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32))
y = f(x)
y.data # array([[3.1757617, 1.7575557],
       #        [8.619507 , 7.1809077]], dtype=float32)

~~~!Note
入力空間の次元を計算することは能率の悪いことがある。
線形リンクといくつかの(逆)畳み込み((de)convolution)は
そのインスタンスの入力次元を省略し、最初のミニバッチの
入力から推論することができる。

例えば、以下のリンクは出力層の次元が2の線形リンクとなる。

f = L.Linear(2)

もし、(2,M)の形のミニバッチを渡せば、入力次元はMとして
推論される。これはl.Wが2×W行列となるということ。
最初のミニバッチにおいてゆっくりとパラメータが初期化される。
そのため、データがリンクに与えられない限りlはW属性を持たない。
~~~

パラメータの勾配はbackward()メソッドによって計算される。
勾配がメソッドによって上書きではなく、積み重ねられることに
注意すると、まずは計算を更新するために勾配をクリアしなければならない。
それはcleargrads()メソッドで行う。

f.cleargrads()

~~~!Note
cleargrads()は効率化のためzerograds()に代わるため
v1.15で導入された。zerograds()は後方互換性のために残っている。
~~~

y.grad = np.ones((2, 2), dtype=np.float32)
y.backward()
f.W.grad # array([[5., 7., 9.],
         #        [5., 7., 9.]], dtype=float32)
f.b.grad # array([2., 2.], dtype=float32)

Chainerに入門してみる:Variables and Derivatives

どうもShZgZです。

機械学習の概要はなんとなくわかってるけども、
実際にフレームワーク使ってやってみようとなると
全然重い腰が上がらない状態でした

流石にこのままじゃまずい、せめて入門くらいしよう!

ということでハッカソン申し込みました。
dllab.connpass.com
(参加する皆さん宜しくお願いします)

入門ぐらいは当然終わってるよね?という運営さんの
熱いプレッシャーを糧に入門していきます
(正確には参加対象が"Chainer入門レベルが理解できる方"です)

MNISTとかの写経はしましたが、どうにもChainer自体が
よくわからんと思ったので公式のガイドを追っていこうと
思います
Variables and Derivatives — Chainer 4.5.0 documentation

"補足"ってなってるところは付け足しているところで、
それ以外は大体原文の和訳になってます。

Variables and Derivatives

まずはインポート

import numpy as np
import chainer
from chainer.backends import cuda
from chainer import 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

Chainerは"Define-by-Run"スキームを利用しているため、forward計算自体がネットワークを定義します。
forward計算を始めるために、まずは入力配列をchainer.Variableオブジェクトにセットします。
ここでは1要素のndarray

x = Variable(np.array([5], dtype=np.float32))

Variableオブジェクトは基本的な算術演算子をサポートしているので、
y = x^2 + 2x + 1は以下のように書けます

y = x**2 - 2 * x + 1

この計算結果yもまたVariableオブジェクトです。
array属性にアクセスすることで値を取得できます。

y.array # array([16.], dtype=float32)

~~~補足:
公式にも書いてありますが、Variableオブジェクトの
array属性とdata属性は同じものを参照していますが、
data属性はnumpy.ndarray.data属性と
混同してしまうかもしれないのでarray属性を用いることが
推奨されているそうです。
実際、

y.data # array([16.], dtype=float32)

~~~

yは結果の値のみではなく計算の履歴(または計算グラフ:computational graph)も
保持しています。このおかげで微分できる。
微分する際には

y.backward()

これは誤差逆伝播を行います。この時、勾配が計算され、
xのgrad属性に代入されます。

x.grad # array([8.], dtype=float32)

~~~補足:
高校の数学ですね。texの練習も兼ねて書いてみます。
 x = 5
y = x^2 + 2x + 1
\frac{dy}{dx} = 2x - 2
なので微分した値は
 2 * 5 - 2 = 8
ですね。
~~~

中間変数の勾配計算も可能です。Chainerはデフォルトでは、
メモリの効率化のために中間変数の勾配配列を解放してしまいます。

z = 2*x
y = x**2 - z + 1
y.backward()
print(z.grad) # None

なので、勾配情報を保持するためにはy.backwardにretain_grad属性を渡しておきます。

z = 2*x
y = x**2 - z + 1
y.backward(retain_grad=True)
z.grad # array([-1.], dtype=float32)

これらの計算は全て複数要素の配列に一般化できます。
複数要素の配列を持つ変数からbackward計算を始める場合は
最初の誤差を手動でセットしなければなりません。

x = Variable(np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32))
y = x**2 - 2*x + 1
y.grad = np.ones((2, 3), dtype=np.float32)
y.backward()
x.grad # array([[ 0.,  2.,  4.],
       #        [ 6.,  8., 10.]], dtype=float32)

~~~補足:
誤差の手動セットのところですが

x = Variable(np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32))
y = x**2 - 2*x + 1
y.grad = np.array([[0, 0, 0],[1,2,3]], dtype=np.float32)
y.backward()
x.grad # array([[ 0.,  0.,  0.],
       #        [ 6., 16., 30.]], dtype=float32)

元のnp.onesは全て1の配列です。そこから今回のように
変えた場合、それぞれの要素で微分した結果にセットした値を
掛けたものとなるようです。
~~~

高階微分
Variableオブジェクトは高階微分をサポートしている。
(高階とはいえ2階微分までです。)
まずは、1階微分の計算です。
y.backward()にenable_double_backprop=Trueが
渡されているところに注目して下さい。

x = chainer.Variable(np.array([[0, 2, 3], [4, 5, 6]], dtype=np.float32))
y = x ** 3
y.grad = np.ones((2, 3), dtype=np.float32)
y.backward(enable_double_backprop=True)
print(x.grad_var) # variable([[  0.  12.  27.]
                  #           [ 48.  75. 108.]])
print(x.grad_var.array is x.grad) # True
print((x.grad == (3 * x**2).array).all()) # True

chainer.Variable.grad_varはchainer.Variable.gradの
Variableオブジェクトです。(型はndarray)backward()に
enable_double_backprop=Trueを渡すことにより、
backward計算のための計算グラフが記憶されます。
これにより、2階微分を計算するためにx.grad_varから
逆伝播を始めることができる。

gx = x.grad_var
x.cleargrad()
gx.grad = np.ones((2, 3), dtype=np.float32)
gx.backward()
print(x.grad) # array([[ 0., 12., 18.],
              #        [24., 30., 36.]], dtype=float32)
print((x.grad == (6 * x).array).all()) # True

~~~補足:
この辺り個人的にわかりずらかったので、いくつかまとめます。

・enable_double_backprop
まず、backward()に渡されるenable_double_backpropですが、これが無いと

gx.backward()
print(x.grad) # None

となり、意味のまんまですが2階微分が計算できません。

・cleargrad
途中にあるcleargradですが、これはx.gradをクリアする、
つまりNoneにするという操作です。
これをしないと、

gx = x.grad_var
print(x.grad) # array([[  0.  12.  27.]
              #        [ 48.  75. 108.]], dtype=float32)
#x.cleargrad()
gx.grad = np.ones((2, 3), dtype=np.float32)
gx.backward()
print(x.grad) # array([[  0.  24.  45.]
              #        [ 72. 105. 144.]], dtype=float32)

となり、1階微分に2階微分の値が加わってしまいます。

・数学的な微分との対応
数学的な微分とx.gradとの対応がはっきりしなかったためまとめます。
まず、xとその属性の型について
x:chainer.variable.Variable
x.array:np.ndarray
x.grad:np.ndarray
x.grad_var:chainer.variable.Variable
要するに、

Variable ndarray
x x.array
grad_var grad

また、backwardした後は
x.grad_varは大体\frac{dy}{dx}と対応しています。
そのため、上記の例でも2階微分をする際には

gx = x.grad_var

として

gxx = gx.backward()

というのは
gx = \frac{dy}{dx}
を表し、
gxx = \frac{d}{dx}(\frac{dy}{dx})
または
\frac{d^2 y}{(dx)^2}
を表します。

macでtensorflowやろうと思って戸惑った話

どうもShZgZです。
環境構築はやっぱり難しい。

今回は、macでtensorflowを触ってみようと思って環境構築してみる話です。

とりあえず、今回のこの記事で伝えたいことは、


2018/08/17現在、Python3.7でtensorflow試すのはかなり大変そう

ということ

丁度、触ってみようと思ってた人、環境構築で戸惑ってる人の参考になればと思います。
まず、Pythonのバージョンを確認してください!
(今の所、3.6なら大丈夫です)

環境:macOS High Sierra 10.13.4

ちゃっちゃとやっていきましょう。

因みにOfficialの方法試したけど、色々あって上手くいかなかったので今回の対応です。


Pythonのバージョンを3.6に下げる! 以上

本当に殆どコレに尽きます。
こちらも書いたので参考にどうぞ
shzgz.hatenablog.com


これでPythonのバージョンを3.6.6にしてから以下です。

$ pip install —upgrade pip virtualenv
$ python3 -m virtualenv {dir_name}
$ cd {dir_name}
$ source bin/activate
# 念のためバージョンを確認
$ python —version
3.6.6
$ pip install —upgrade tensorflow matplotlib

無事成功
(3.7で色々試して結局ダメだったけど3.6に下げたら全部すごいすんなりいった。長かった)


最後の確認

$ python
>>> import tensorflow
>>>

成功。。。しかし、

>>> import matplotlib.pyplot

を実行したところ

RuntimeError: Python is not installed as a framework. 
The Mac OS X backend will not be able to function correctly if Python is not installed as a framework. 
See the Python documentation for more information on installing Python as a framework on Mac OS X. 
Please either reinstall Python as a framework, or try one of the other backends. 
If you are using (Ana)Conda please install python.app and replace the use of 'python' with 'pythonw'. 
See 'Working with Matplotlib on OSX' in the Matplotlib FAQ for more information.

(長かったので所々改行)


aggの設定周りっぽかったので、
~/.matplotlib
にmatplotlibrcファイルを作成し、
~/.matplotlib/matplotlibrc

backend : TkAgg
>>> import matplotlib.pyplot

成功!!!


今の所はこんな感じでできました。
バージョン調べることの大切さ

でわー

pyenvでPythonのバージョン管理しようと思って戸惑った話

どうもShZgZです。
今日はちょっと涼しいですね。。。気のせいか。

tensorflow触ってみようと思って紆余曲折ありPythonのバージョンを下げなくてはならなくなりpyenvを使ってみようとしたときの話です

とりあえず、

Python3.7でTensorflowやろうと思うと上手くいかないことが多い(2018/08/17時点)

どうしてもこれだけは言っておきたかった。

というわけで、今回はpyenvを入れていきます。まぁやっぱりbrewです。

環境:macOS High Sierra 10.13.4

$ brew install pyenv
$ pyenv install 3.6.6

pyenvは入ったんだけど、ここでエラー

zipimport.ZipImportError: can't decompress data; zlib not available
make: *** [install] Error 1

調べてみると、いくつかのサイトでx-codeのコマンドライン開発ツールをインストールすればいけるとのことだったので

$ xcode-select —install

その後、

$ pyenv install 3.6.6
$ pyenv versions
* system
  3.6.6

無事成功!

で、とりあえずlocalのPythonのバージョンを変更
(ディレクトリを指定して、その中にいる時のPythonバージョンを変更)

Tensorflowの開発用ディレクトリに移動し、
$ pyenv local 3.6.6
$ python —version
3.7.0

!?
変わらん。

PATHでpyenvが優先されてないので.bash_profileに

eval "$(pyenv init -)”

を追加し、

$ source ~/.bash_profile
$ python —version
3.6.6

無事成功!!!

まとめ
今まで特にpythonのバージョンとか気にする必要なかったけど、
必要になった時に簡単にバージョン管理できるのはとても良い。
他の言語をやるときにもバージョン管理は最初からしといたほうがいいと思った。

言語のバージョン管理は大事!!

でわ。

Macにlemを入れてみる

どうもShZgZです!

 

まぁ暑い、水分と塩分はしっかり補給するようにします!

 

最近lemという言葉をtwitterのフォローしてる人の中でちょいちょい耳にしたので、

調べてみました。

 

結果、

Emacsライクなエディタ
拡張がCommon Lispで書ける
起動が早い
ひとまずこんなのらしいです。

 

とりあえず入れてみようということで、Githubはこれ

環境:
OS:macOS 10.13.4
 

手順に従って、まずroswell*1のインストール

$ brew install roswell

次に、roswellを使ってlemをインストール

$ ros install cxxxr/lem

しようとしたけど、

Installing sbcl-bin...
No SBCL version specified. Downloading platform-table.html to see the available versions...
[##########################################################################]100%
Installing sbcl-bin/1.4.10...
Downloading https://github.com/roswell/sbcl_bin/releases/download/1.4.10/sbcl-1.4.10-x86-64-darwin-binary.tar.bz2
Download Failed with status 2. See download_simple in src/download.c
Segmentation fault: 11

こんな感じでエラー。。。(2018/08/15 11時時点)

roswellが上手く動かない。無念。*2

ということで、roswellソース引っ張ってきてインストールしました。

# brewのroswellをアンインストール
$ brew uninstall roswell
# clone
$ git clone https://github.com/roswell/roswell
# ビルド
$ cd roswell
$ ./bootstrap
$ ./configure
$ make
# インストール
$ sudo make install

無事roswellのインストールに成功。

では気を取り直して

$ ros install cxxxr/lem

無事成功!

$ lem

でlemを開くことができました!

因みにアップデートは

ros update lem

設定ファイルは

~/.lemrc

使い勝手はまた。
でわ。

*1:roswell:Common Lisp環境のセットアップユーティリティ

*2:Homebrewで入れると上手くいかないそうです。Githubのissueにもなってました。

ReactNativeのmodule削除の手順でちょっと詰まったのでメモ

暑いですね。

最近ReactNativeを使っていて、

$ npm i {パッケージ名}
$ react-native link {パッケージ名}

こんな感じでmodule追加したんだけどこのリンクを切るのに

$ npm uninstall {パッケージ名}

をやっちゃうとダメだよっていう話

 

正しくは、先にlinkを切ってあげなきゃいけないから

$ react-native unlink {パッケージ名}
$ npm uninstall {パッケージ名}

間違ってnpm uninstallしちゃった場合は、

もう一度npm installしてあげてから正しい手順で消してあげればOK!!

(iOSの方は試してないけど、Androidの方はMainApplication.javaの中でNewされてたりして落ちた。Newしてるところを削除すればOK!)

Rust:Floatについてもうちょっと

どうもShZgZです。

 

寒いです。みなさん体調にはくれぐれもお気をつけて。

 

PartialEqについて調べていた時に

・i32:Eq

・f32:PartialEq

となっていて、なんぞ?と思ったらサンプルのところで

NaNがあるからって書いてある。

 

・・・ほう。わからん。

 

NaNって恥ずかしながらあんまり意識してこなかったため

Nilとかnullみたいなもんやろと思っていました。

大学のときに先生が説明してくれていたらごめんなさい。

 

で、とりあえずNaNについてWikiさん曰く

NaN - Wikipedia

NaNNot a Number、非数、ナン)は、コンピュータにおいて、主に浮動小数点演算の結果として、不正なオペランドを与えられたために生じた結果を表す値またはシンボルである。NaNの体系的仕様は、無限大の表現などと共に1985年の IEEE 754 浮動小数点規格で標準が与えられている。 

 なんかエラーがあったときの値ってことみたいな感じ。

浮動小数点数は値を丸めちゃうから0で割るような状況が起き得るからかな?

なんとなくわかったんだけど、ここでさらに不穏な"無限大の表現などと共に"って言葉。

 

・・・無限大ってあるの?

 

もしかしたらとても基本的なことなのかもしれないけどとりあえずこれまで気にした事なかった。証明支援系で使う時に実装してたのは知ってたけど、一般的に実装されてるものだと思ってなかった。

 

で、とりあえずRustでどう実装されてるか見てみた。

/// Not a Number (NaN).
#[stable(feature = "rust1", since = "1.0.0")]
pub const NAN: f32 = 0.0_f32 / 0.0_f32;
/// Infinity (∞).
#[stable(feature = "rust1", since = "1.0.0")]
pub const INFINITY: f32 = 1.0_f32 / 0.0_f32;
/// Negative infinity (-∞).
#[stable(feature = "rust1", since = "1.0.0")]
pub const NEG_INFINITY: f32 = -1.0_f32 / 0.0_f32;

要するに、

0/0・・・NaN

1/0・・・無限大

-1/0・・・マイナス無限大

ってことらしい。

 

とりあえずいくつか試してみる。

use std::f32::{NAN, INFINITY, NEG_INFINITY};
fn main() {
println!("INFINITY:{}", INFINITY);
println!("INFINITY + 1000:{}", INFINITY + 1000_f32);
println!("INFINITY - 1000:{}", INFINITY - 1000_f32);
println!("INFINITY * 1000:{}", INFINITY * 1000_f32);
println!("INFINITY / 1000:{}", INFINITY / 1000_f32);
println!("INFINITY % 1000:{}", INFINITY % 1000_f32);
println!("NEG_INFINITY:{}", NEG_INFINITY);
println!("NEG_INFINITY + 1000:{}", NEG_INFINITY + 1000_f32);
println!("NEG_INFINITY - 1000:{}", NEG_INFINITY - 1000_f32);
println!("NEG_INFINITY * 1000:{}", NEG_INFINITY * 1000_f32);
println!("NEG_INFINITY / 1000:{}", NEG_INFINITY / 1000_f32);
println!("NEG_INFINITY % 1000:{}", NEG_INFINITY % 1000_f32);
println!("NAN:{}", NAN);
println!("NAN + 1000:{}", NAN + 1000_f32);
println!("NAN - 1000:{}", NAN - 1000_f32);
println!("NAN * 1000:{}", NAN * 1000_f32);
println!("NAN / 1000:{}", NAN / 1000_f32);
println!("NAN % 1000:{}", NAN % 1000_f32);
println!("INFINITY - INFINITY:{}", INFINITY - INFINITY);
}

INFINITY:inf

INFINITY + 1000:inf

INFINITY - 1000:inf

INFINITY * 1000:inf

INFINITY / 1000:inf

INFINITY % 1000:NaN

NEG_INFINITY:-inf

NEG_INFINITY + 1000:-inf

NEG_INFINITY - 1000:-inf

NEG_INFINITY * 1000:-inf

NEG_INFINITY / 1000:-inf

NEG_INFINITY % 1000:NaN

NAN:NaN

NAN + 1000:NaN

NAN - 1000:NaN

NAN * 1000:NaN

NAN / 1000:NaN

NAN % 1000:NaN

INFINITY - INFINITY:NaN

 大体直感通りな感じ。

 

因みに、traitのEq, PartialEqは実装する関数が全く一緒です。というか、EqはPartialEqを実装してればOKってやつです。

ただ、ここには意味上の違いがあり数学的には半同値関係と同値関係を表すものとなっています。(Eq・・・同値, PartialEq・・・半同値)

↑の意味は、興味あったら調べてみて下さい。

 

簡単にいうと、それぞれの要素aに関して

 a == a

が成り立つならEq, そうじゃない要素があるならPartialEqですよってことです。

 

具体的にいきます。

u32で考えてみると、u32の要素(1, 2, 3, 4)はそれぞれ

1 == 1 // true
2 == 2 // true
3 == 3 // true
4 == 4 // true

となり、

まぁそれ以外でも成り立ちますから、u32はEqを実装していると言えます。ただ、ここでも実際に実装する関数はPartialEqと同じですから、自分が新しく構造体を作りPartialEqを実装した場合に、PartialEqの対象となる要素a全てに対して

 a == a

としておくと 、後で自分や他人が見た時に意味が判り易くて助かるよってことです。

 

f32はどうかというと、f32の要素(1.1_f32, 2.52_f32, INFINITY, NaN)などの数値)はそれぞれ

1.1_f32 == 1.1_f32 // true
2.52_f32 == 2.52_f32 // true
INFINITY == INFINITY // true
NAN == NAN // false

となり、NANに関して等号が成り立ちません。

そのため、f32, f64は要素a全てに対して

 a == a

が成り立たないため、PartialEqとなっています。要するに、PartialEq実装したけど、同じ要素を比較した時に等号が成り立たない奴いるよってことを表してくれているわけです。

 

・まとめ

floatには小数要素の他に、数じゃない要素と無限大を表す要素あるよ。

(しかもIEEE 754で定まってるから結構色んな言語であるのかも。知ってたら教えて下さい。)

EqとPartialEqの違いは実装上は無い。ただ、意味として全要素について同じ要素 同士の等号が成り立つ(数学だと反射律って言います。)かどうかっていう違いがあるよ。