じぶんメモ

プログラミングのメモ、日常のメモとか。

anyenvを使って~env系をひとまとめにする

ruby, pythonを使用しているとrbenv, pyenv等を使ってバージョン管理をするが、
これらのバージョン管理システムは、言語ごとに導入、PATHを通す必要がある。
anyenvを使用すれば、これらの言語ごとのバージョン管理システムを一元管理できる。

1.以前使用していた~env系を削除

念の為。以前使用していた~env系とanyenvが競合しないように削除しておく。
brewでインストールしている場合は以下のコマンドでOK.

$ brew uninstall rbenv

他のツールが依存していると削除できないこともあるので、
その時はenvに依存しているツールを先に削除することでenvも削除することができる。
ディレクトリは残るのでこれも削除しておく。

$ rm -rf ~/.rbenv

2.anyenvのインストール

githubからcloneしてくる

$ git clone https://github.com/riywo/anyenv ~/.anyenv

PATHを通す。(bash_profileに記載)

if [ -d ${HOME}/.anyenv ] ; then
    export PATH="$HOME/.anyenv/bin:$PATH"
    eval "$(anyenv init -)"
fi

おそらくanyenv導入前は、各env毎に↑のコードを書いていたと思う。

3.各種envをインストール

以下のコマンドで各envを導入する。

$ source ~/.bash_profile
$ anyenv install rbenv
$ anyenv install pyenv

あとは以前と同じように各言語のversionをインストールすればOK。

$ rbenv install 2.4.0

gitで歴史を改ざんするには

直前の履歴を改ざんする

1. コミットをなかったことに

HEADの一つ前に戻る。

$ git reset HEAD~1
$ git push -f origin

2. コミットを修正

対象ファイルを修正し、commit --amendする。

$ git commit --amend
$ git push -f origin

特定の歴史を改ざんする

rebase -iを使い、改ざんしたいコミットまで戻り、修正する。

$ git rebase -i <改ざんしたいコミットのID>
pick c832e Add comment
pick f983e Initial commit

戻りたいコミットを指定し、editにし保存する。

edit c832e Add comment
pick f983e Initial commit

改ざんしたいファイルを修正し、commit --amendする。 その後、git rebase --continueで元に戻す。

$ git rebase --continue

途中、コンフリクトが発生することがあるので、そのときは解消して、
rebase --continueする。
Successfully rebased and updated refs/heads/ブランチ名 と出れば完了。

途中で訳わからなくなってもとに戻したいと思ったら↓のコマンドでOK。

$ git rebase --abort

n-gramによる文章の類似率を求める

N-gramとは、テキストで隣り合ったN文字のことを示す。
以下では2つ文章を指定された文字数で分割し、2つの文章間で分割した文字がどれだけマッチするかの頻度から、2つの文章の類似率を求める。

def ngram(str, num):
    res  = []
    slen = len(str) - num + 1
    for i in range(slen):
        extract_str = str[i:i+num]
        res.append(extract_str)
    return res

# 2つの文章の類似率を調べる
def diff_ngram(str1, str2, num):
    str1_ngram   = ngram(str1, num)
    str2_ngram   = ngram(str2, num)
    match_result = []
    count        = 0
    for str1_unit in str1_ngram:
        for str2_unit in str2_ngram:
            if str1_unit == str2_unit:
                count += 1
                match_result.append(str1_unit)
    return count / len(str1_ngram), match_result

# 2つの文章がどれだけ似ているかテストする
# 文字の分割を2文字ごと3文字ごとにして調べる。
a = '今日、渋谷で美味しいトンカツを食べた。'
b = '渋谷で食べた今日のトンカツは美味しかった。'

result2, word_list2 = diff_ngram(a, b, 2)
result3, word_list3 = diff_ngram(a, b, 3)

print('2-gram:', result2, word_list2)
print('3-gram:', result3, word_list3)

# 2-gram: 0.6111111111111112 ['今日', '渋谷', '谷で', '美味', '味し', 'トン', 'ンカ', 'カツ', '食べ', 'べた', 'た。']
# 3-gram: 0.29411764705882354 ['渋谷で', '美味し', 'トンカ', 'ンカツ', '食べた']

これだと単純な接続詞とかでもヒットしてしまうのであまり精度は高く無い気がする。 もっと本格的に文章解析するならば、RNNやLSTMに手をつかけるのがいいのかな。。。

TensorflowとKerasを用いてmnistのCNNを構築してみる

コードは以下の通り。

Tensorflow・Kerasを使ってmnistの訓練を行う

from keras.models               import Sequential
from keras.datasets             import mnist
from keras.layers.convolutional import Conv2D
from keras.layers.pooling       import MaxPooling2D
from keras.layers.core          import Dense, Dropout, Activation, Flatten
from keras.optimizers           import Adam
from keras.utils                import np_utils

from keras import backend as K

# ------------------------------
# データ準備
# ------------------------------
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# データをfloat型にして正規化する
X_train = X_train.astype('float32') / 255.0
X_test  = X_test.astype('float') / 255.0

img_rows = 28
img_cols = 28

# image_data_formatによって畳み込みに使用する入力データのサイズが違う
if K.image_data_format() == 'channels_first':
    X_train     = X_train.reshape(-1, 1, img_rows, img_cols)
    X_test      = X_test.reshape(-1, 1, img_rows, img_cols)
    input_shape = (1, img_rows, img_cols)
else:
    X_train     = X_train.reshape(-1, img_rows, img_cols, 1)
    X_test      = X_test.reshape(-1, img_rows, img_cols, 1)
    input_shape = (img_rows, img_cols, 1)

# ラベルはone-hot encodingを施す
y_train = y_train.astype('int32')
y_test  = y_test.astype('int32')
y_train = np_utils.to_categorical(y_train, 10)
y_test  = np_utils.to_categorical(y_test, 10)

# ------------------------------
# モデルの定義
# ------------------------------
# 場合分けでインプットデータのテンソルの形を変える
# mnistデータは28×28ピクセルで60000個のデータで、28×28ピクセルで1チャネルのデータに変える
# reshapeの-1は、28×28ピクセルで60000個のデータを28×28ピクセルで1チャネルによしなに変えてくれる
model = Sequential()

# 畳み込み層1
# フィルタは5×5ピクセルで28個→出力データは32チャネル
# 入力データは28×28ピクセルの1チャンネル
# input_shapeを指定するのは1層目だけ
model.add(Conv2D(32, (5, 5), input_shape=input_shape))
model.add(Activation('relu'))
# プーリング層1
model.add(MaxPooling2D(pool_size=(2, 2)))

# 畳み込み層2
# フィルタは5×5ピクセルで64個→出力データは64チャネル
model.add(Conv2D(64, (5, 5)))
model.add(Activation('relu'))
# プーリング層2
model.add(MaxPooling2D(pool_size=(2, 2)))

# Flattenして全結合する
model.add(Flatten())
model.add(Dense(128))
model.add(Activation('relu'))

# 過学習予防のためドロップアウトして最後に出力(出力データ数はone-hotなので10個)
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))

# ------------------------------
# 学習の開始
# ------------------------------
epochs = 20
batch_size = 100

adam = Adam(lr=1e-4)
model.compile(optimizer=adam, loss='categorical_crossentropy', metrics=["accuracy"])
history = model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs, verbose=1, validation_split=0.1)

loss, accuracy = model.evaluate(X_test, y_test, verbose=1)
print('loss=', loss)
print('accuracy=', accuracy)

以下、注意点。

  • mnistは28×28の1チャネルだが、RGBの画像とかだと3チャネルになる。
  • Conv2Dの引数は出力サイズ(フィルタのデータ数), フィルタの縦横サイズ、入力データのサイズ
  • KerasのConv2Dでは、1層目のみ入力データのサイズの指定が必要
  • KerasのバックエンジンによってConv2Dに渡す入力データのサイズの指定形式が異なる。

3つめの入力形式に関しては~/.keras/keras.jsonに記載されているimage_data_formatをみて判断する。

コードでは、1層目でmnistのデータを(データの個数, 28ピクセル, 28ピクセル , 1チャネル)のテンソルに変換し、
(5ピクセル, 5ピクセル, 1チャネル)のデータ32個のフィルタにかけていて、その結果をプーリングして次の層へ渡す。
最終的な出力の前に全結合してドロップアウトした結果をソフトマックス関数にかけてone-hotのラベル形式で出力している。

TensorflowとKerasを使ってmnistの訓練と評価をしてみる

Tensorflow・Kerasとは(ザックリ)

  • Tensorflowはpythonで使える機械学習のためのライブラリ
  • Kerasは更にラッパーライブラリで、Tensorflowだと数百行かかるコードが数十行で済んだりする。

インストール方法

とりあえずpipで入れてみる。

$ pip install -U tensorflow
$ pip install -U keras

Tensorflow・Kerasを使ってmnistの訓練を行う

from keras.datasets    import mnist
from keras.models      import Sequential
from keras.layers.core import Dense, Dropout, Activation
from keras.optimizers  import Adam
from keras.utils       import np_utils

# mnistデータの読み込み
(X_train, y_train), (X_test, y_test) = mnist.load_data()

# データをfloat型にして正規化する
# データ数×縦ピクセル数×横ピクセル数のデータをデータ数×784の方にする
X_train = X_train.reshape(60000, 784).astype('float32') / 255
X_test  = X_test.reshape(10000, 784).astype('float') / 255

# ラベルをone_hot形式に変換
y_train = np_utils.to_categorical(y_train, 10)
y_test  = np_utils.to_categorical(y_test, 10)

# モデルの構造を定義
model = Sequential()
# 1層目
model.add(Dense(512, input_shape=(784,)))
model.add(Activation('relu'))

# 2層目
model.add(Dense(512))
model.add(Activation('relu'))

# 出力
model.add(Dense(10))
model.add(Dropout(0.2))
model.add(Activation('softmax'))

# モデルの構築
model.compile(
    loss='categorical_crossentropy',
    optimizer=Adam(),
    metrics=['accuracy'])

# データで訓練
hist = model.fit(X_train, y_train)

# テストデータで評価する
loss, accuracy = model.evaluate(X_test, y_test, verbose=1)
print('loss=', loss)
print('accuracy=', accuracy)

特に注目したいのがmodelを用いて直感的に学習の手順を記述することができる。
コードでは3層のニューラルネットワークを構築している。
Denseは全結合を行うモジュールで、1層目のみ入力データのサイズを指定する必要がある。

モデルを定義し、コンパイルし、訓練をし、テストデータで精度を評価するという流れ。

【ゼロから作るDeep Learning】要点まとめ 第7章

7章 - 畳み込みニューラルネットワーク

CNN(Convolutional Neural Network)は、畳み込み層、プーリング層からなる学習方法
基本的にはconv→relu→conv→relu→...→pool→conv→relu→conv→relu→...→pool→affine→softmaxの流れ
conv→relu or conv→relu→poolが隠れ層になるイメージ。

このサイトがとてもわかり易い。
http://postd.cc/how-do-convolutional-neural-networks-work/

畳み込み層

  • 畳み込み層は、用意された入力データ(画像データなど)をフィルタ(重み)を使って畳み込む
  • 入力データ(縦×横×チャネル→RGB画像なら3チャネル)に対して、フィルタを少しずつづらしながら行列演算をしていく。
  • 入力データのチャネル数とフィルタのチャネル数は合わせておく必要がある。 例) 入力データxは(32,32,3)、フィルタwは(5,5,3)
  • 入力データxは(32,32,3)、フィルタwは(5,5,3)を1幅ずつスライドさせていくと、出力される値は(28,28,1)のデータになる。
  • フィルタの数が増えれば(4次元配列)、出力されるデータのチャネル数も同じ数だけ増える。
  • 例) 入力データx(32,32,3)、フィルタw(10,5,5,3)(10はフィルタの数)を畳み込むと出力される値は(28,28,10)のデータになる。
  • 入力データxを行列に変換 × フィルタwを行列に変換 + バイアス →reluに突っ込む → 再度4次元データに変換して次の隠れ層へ送る。
  • ストライド == スライド幅
  • パディング == スライドした時にはみ出る箇所のデータを0として扱う

プーリング層

  • 縦横の幅を決めてその範囲内での最大値を取る(maxプーリング)プーリングを行う層。
  • 大きな画像を、重要な情報は残しつつ縮小する利点がある。
  • maxプーリングの他にもaverageプーリングがある。

【ゼロから作るDeep Learning】要点まとめ 第6章

6章 - 学習に関するテクニック

勾配法の種類

  • 勾配法の種類はSGD, Momentum, AdaGrad, Adamがある。(AdaGrad,Adamが学習スピードが早い)

重みパラメータと活性化関数での出力値

  • 重みパラメータの初期値は小さくすることで過学習を防げるが、0にすると各層での誤差逆伝播法の出力が均等になってしまうので避けること。
  • 重みパラメータの初期値はランダムである必要がある。
  • 各隠れ層のアクティベーション(活性化関数で出力された値)が、0と1に傾くと勾配損失が発生し、層を深くする意味がなくなってしまう。
  • アクティベーションを分散させるには重みパラメータのスケール(標準偏差)にHeの定数、Xavierの定数を適用させる。
  • 活性化関数が線形(sigmoid, tanh等)の場合はXavierの定数、ReLUの場合はHeの定数が適している。
  • 強制的にアクティベーションの分布を制御するには活性化関数の前にBatch Normalization(正規化)をする。
  • 正規化をしない場合、適切なスケールを設定しないといつまでたってもしっかりと学習されない。
  • 正規化を使用すると重みの初期値にロバストする。(初期値に依存しない)

過学習

  • 特定の訓練データにだけ適応しすぎてしまい、訓練データに含まれないデータにはうまく対応できない状態。
  • 訓練データが少なかったり、パラメータが大量で表現力の高いモデル(隠れ層のノードが多い)場合に発生しやすい。
  • 過学習を避けるためには重みパラメータを抑制するためにWeigt Decay(荷重減衰)または、Dropoutを使用する。
  • Weight Decayは損失関数に対して重みの1.2ノルムを加算する。(ただしハイパーパラメータの設定が必要)
  • Dropoutはニューロンをランダムに消去しながら学習を行う。
  • ハイパーパラメータを適正な値に設定するには検証データを使用する。
  • テストデータをもとにしてハイパーパラメータを設定するのはNG。テストデータのみに適したハイパーパラメータになってしまうため。
  • 検証データを用意する最も簡単な方法は訓練データの20%を分離して使用すること。
  • 最適なハイパーパラメータを求めるには、ハイパーパラメータの範囲を定め、その範囲の中からランダムにサンプリングし、認識精度の評価を行う。それを数回繰り返して最適な値を狭めていく。