【読書メモ】ゼロから作るDeep Learning 6章

f:id:taigok:20160908173332j:plain

ゼロから作るDeep Learningの読書メモとしてまとめました。

本を読む目的

Coursera Machine Learningを受講してニューラルネットワークを理解したので、次はディープラーニングについて深く理解をしたいため。

前章までのまとめ

本記事で利用しているソースコードGitHub - oreilly-japan/deep-learning-from-scratch: 『ゼロから作る Deep Learning』のリポジトリを参考にしています。

最適化とは

最適化とは、損失関数の値を出来るだけ小さくするパラメータを見つけるような問題を解くこと。前章まではパラメータの勾配(微分)を使って勾配方向にパラメータを更新するステップを何回も繰り返す、確率的勾配降下法(Stochastic Gradient Descent:SGD)を最適化の手法として利用していたが、ここではSGDの欠点を改善する手法について説明。

SGDの欠点

目的とする関数の形状が等方的でないと、非効率な経路で探索してしまうため、学習に時間がかかってしまう。

SGDに代わる手法

Momentum

ボールがお椀を転がるように物理法則に準ずる動き。

v \leftarrow  \alpha v - \eta\frac{\partial L}{\partial W}\

W \leftarrow  W + v

class Momentum:

    def __init__(self, lr=0.01, momentum=0.9):
        self.lr = lr
        self.momentum = momentum
        self.v = None
        
    def update(self, params, grads):
        if self.v is None:
            self.v = {}
            for key, val in params.items():                                
                self.v[key] = np.zeros_like(val)
                
        for key in params.keys():
            self.v[key] = self.momentum*self.v[key] - self.lr*grads[key] 
            params[key] += self.v[key]

AdaGrad

パラメータの要素の中で大きく更新された要素は学習係数が小さくなるような更新方法。

h \leftarrow  h + \frac{\partial L}{\partial W}\odot\frac{\partial L}{\partial W}\

W \leftarrow  W - \eta \frac{1}{\sqrt{h}} \frac{\partial L}{\partial W}

class AdaGrad:

    def __init__(self, lr=0.01):
        self.lr = lr
        self.h = None
        
    def update(self, params, grads):
        if self.h is None:
            self.h = {}
            for key, val in params.items():
                self.h[key] = np.zeros_like(val)
            
        for key in params.keys():
            self.h[key] += grads[key] * grads[key]
            params[key] -= self.lr * grads[key] / (np.sqrt(self.h[key]) + 1e-7)

Adam

MomentumとAdaGradの融合。ソースコードは割愛。

どの更新手法を用いるか

多くの研究では今でもSGDを使っている。最近はAdamを好んで使っている人も増えた。全ての問題で優れた手法は無いので、色々試す必要がある。


重みの初期値

重みの初期値によって、ニューラルネットワークの学習がうまくいくかが決定することが多い。 結論からいうと、活性化関数にsigmoid、tanhなどのS字カーブを用いる場合はXavierの初期値、ReLUを用いる場合は、Heの初期値を用いるとよい。

Weight decay(荷重減衰)

重みパラメータの値が小さくなるように学習を行うことを目的とした手法。重みパラメータを小さくするのに小さい初期値から始める。単純に初期値を0(均一な値)にすればいいのではと思うが、それはダメ。理由としては、すべての重みの値が均一に更新されてしまい、ニューラルネットワークの表現力がなくなるため。よって、ランダムな初期値が必要。

勾配消失問題

重みの初期値に標準偏差が1のガウス分布を用いると、活性化関数がシグモイド関数の場合、各層のアクティベーションの結果は0と1に偏った分布になってしまう。こうなると、逆伝播で微分(勾配)の値が0に近くため、勾配の値が伝播するにつれ小さくなってしまい、層が深いほど0に近づいてしまうこと。よって、初期値を適切な値にして上げる必要があり、XavierやHeの初期値が考えられた。

Xavierの初期値

Xaivierの初期値は一般的なディープラーニングフレームワークで使われている。各層のアクティベーションを同じ広がりのある分布にすることを目的として、前層のノードの個数をnとして、{1\sqrt{n}}標準偏差をもつ分布を使う。Xavierの初期値は活性化関数が線形の場合に有効。

num = 100
# random.randn(num, num):標準正規(ガウシアン)分布による num × num の行列
w = np.random.randn(num, num) / np.sqrt(1.0 / num)

Heの初期値

活性化関数がReLUの場合に推奨されるのがHeの初期値。前層のノードの個数をnとして、{2\sqrt{n}}標準偏差をもつ分布を使う。

num = 100
w = np.random.randn(num, num) / np.sqrt(2.0 / num)

コード

初期値や活性化関数を変えて動かしてみる。

# coding: utf-8
import numpy as np
import matplotlib.pyplot as plt


def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def ReLU(x):
    return np.maximum(0, x)

def tanh(x):
    return np.tanh(x)
    
input_data = np.random.randn(1000, 100)  
node_num = 100 
hidden_layer_size = 5
activations = {}

x = input_data

for i in range(hidden_layer_size):
    if i != 0:
        x = activations[i-1]

    # 初期値の値を変える
    w = np.random.randn(node_num, node_num) * 1
    # w = np.random.randn(node_num, node_num) * 0.01
    # w = np.random.randn(node_num, node_num) * np.sqrt(1.0 / node_num) # 
    # w = np.random.randn(node_num, node_num) * np.sqrt(2.0 / node_num)

    a = np.dot(x, w)

    # 活性化関数
    z = sigmoid(a)
    # z = ReLU(a)
    # z = tanh(a)

    activations[i] = z

for i, a in activations.items():
    plt.subplot(1, len(activations), i+1)
    plt.title(str(i+1) + "-layer")
    if i != 0: plt.yticks([], [])
    # plt.xlim(0.1, 1)
    # plt.ylim(0, 7000)
    plt.hist(a.flatten(), 30, range=(0,1))
plt.show()


Batch Normalization

重みの初期値によって、各層のアクティベーションの結果が変わり適切な重みの初期値を設定することで各層のアクティベーションの分布は適度な広がりを持ち、学習がスムーズに進んだことを確認した。Batch Normalizationは、アクティベーションの分布を強制的に適度な広がりを持つように調整しようというアイディアのことアクティベーションの分布が適度な広がりを持たないと、ニューラルネットワークが表現力を無くす、つまりたくさんのニューロンを使用している意味がなくなる。

Batch Normalizationのメリット

  • 学習を早くできる
  • 初期値にあまり依存しない
  • Overfitしない

Batch Normalizationレイヤの挿入

活性化関数レイヤの前(or後)にBatch Normalizationレイヤを挿入する。

Batch Normalizationの詳細

ミニバッチを単位として、ミニバッチごとにaデータの分布が平均が0で分散が1になるように正規化すること。

Batch Normalizationの評価

重みの初期値の標準偏差を様々な値に変えた時の学習経過が下記。Batch Normalizationを使用することで、重みの初期値にロバストになる。

f:id:taigok:20190202120733p:plain


過学習(Overfitting)

過学習とは訓練データだけに適応しすぎて、他のデータに適応できない状態のこと。過学習の原因としては、下記の2つが考えられる。

  • パラメータをたくさん持っている
  • 訓練データが少ない

Weight decay

過学習は重みパラメータが大きな値を取ることで起こることがしばしばある。そこで学習において大きな重みを取ることにペナルティを与えることで過学習を抑制することができる。この手法をWeight decay(荷重減衰)という。 具体的には損失関数に重みの二乗ノルム(L2ノルム)を加算する。 (ノルムとはベクトル空間における長さを示すもの。二乗ノルムはベクトルの長さ)

w \leftarrow w -\eta \frac{\partial C(w)}{\partial w} - \eta \lambda w

Dropout

過学習を抑制するためにニューロンを削除しながら学習する手法。訓練時にデータが流れるたびに消去するニューロンをランダムに選択する。 テスト時には全てのニューロンの信号を伝達するが、各ニューロンの出力に対して訓練時に削除した割合を乗算して出力。 Dropoutは学習時にランダムにニューロンを削除することによって、毎回別のモデルを学習させていることになるので、アンサンブル学習に近い発想。


ハイパーパラメータの検証

ハイパーパラメータとは、重みやバイアスといった訓練の結果取得できるパラメータとは異なり、人が試行錯誤をして決定しなければいけないパラメータのこと。例えば下記のパラメータがハイパーパラメータ

検証データ(Validation data)

テストデータを使ってハイパーパラメータを調整するとテストデータに対して過学習してしまう。よって別途、検証データを用意してハイパーパラメータの調整を行う。まとめると下記になる。

  • 訓練データは重みやバイアスの学習に使用
  • 検証データはハイパーパラメータの性能評価のため使用
  • テストデータは汎化性能をチェックするために使用

ハイパーパラメータの最適化

ハイパーパラメータの最適化のポイントは、いい値が存在する範囲を徐々に絞り込んでいくこと。

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from common.multi_layer_net import MultiLayerNet
from common.util import shuffle_dataset
from common.trainer import Trainer

(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

# 高速化のため訓練データの削減
x_train = x_train[:500]
t_train = t_train[:500]

# 検証データの分離
validation_rate = 0.20
validation_num = int(x_train.shape[0] * validation_rate)
x_train, t_train = shuffle_dataset(x_train, t_train)
x_val = x_train[:validation_num]
t_val = t_train[:validation_num]
x_train = x_train[validation_num:]
t_train = t_train[validation_num:]


def __train(lr, weight_decay, epocs=50):
    network = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100, 100, 100],
                            output_size=10, weight_decay_lambda=weight_decay)
    trainer = Trainer(network, x_train, t_train, x_val, t_val,
                      epochs=epocs, mini_batch_size=100,
                      optimizer='sgd', optimizer_param={'lr': lr}, verbose=False)
    trainer.train()

    return trainer.test_acc_list, trainer.train_acc_list


# ハイパーパラメータのランダム探索======================================
optimization_trial = 100
results_val = {}
results_train = {}
for _ in range(optimization_trial):
    # 探索したハイパーパラメータの範囲を指定===============
    weight_decay = 10 ** np.random.uniform(-8, -4)
    lr = 10 ** np.random.uniform(-6, -2)
    # ================================================

    val_acc_list, train_acc_list = __train(lr, weight_decay)
    print("val acc:" + str(val_acc_list[-1]) + " | lr:" + str(lr) + ", weight decay:" + str(weight_decay))
    key = "lr:" + str(lr) + ", weight decay:" + str(weight_decay)
    results_val[key] = val_acc_list
    results_train[key] = train_acc_list

# グラフの描画========================================================
print("=========== Hyper-Parameter Optimization Result ===========")
graph_draw_num = 20
col_num = 5
row_num = int(np.ceil(graph_draw_num / col_num))
i = 0

for key, val_acc_list in sorted(results_val.items(), key=lambda x:x[1][-1], reverse=True):
    print("Best-" + str(i+1) + "(val acc:" + str(val_acc_list[-1]) + ") | " + key)

    plt.subplot(row_num, col_num, i+1)
    plt.title("Best-" + str(i+1))
    plt.ylim(0.0, 1.0)
    if i % 5: plt.yticks([])
    plt.xticks([])
    x = np.arange(len(val_acc_list))
    plt.plot(x, val_acc_list)
    plt.plot(x, results_train[key], "--")
    i += 1

    if i >= graph_draw_num:
        break

plt.show()

f:id:taigok:20190202133532p:plain

AWS スポットインスタンスでJupyterを起動するまで

f:id:taigok:20190122213253p:plain

背景

Kaggleの Microsoft Malware Prediction に参加しています。分析にはローカルPCを使用していましたが、リソースが足りなくなることが多いため、パブリッククラウドで1番慣れているAWSのスポットインスタンスを使ってみたので、Jupyterを起動するまでの手順について書こうと思います。

AWS スポットインスタンスとは

スポットインスタンスはオンデマンド価格より低価で利用できる未使用の EC2 インスタンスで、需要と供給によって利用料金が変動します。オンデマンドに比べて大幅に割引されますが、いくつかの理由によりインスタンスを中断されることがあります。

参考:AWS ドキュメント スポットインスタンス

手順

スポットインスタンスのリクエス

EC2のページのスポットインスタンスからスポットインスタンスのリクエストを選択します。まずどんなタスクに使用するかの選択をします。ここではBig data workloadsを選択します。

f:id:taigok:20190103111250p:plain

AMIやコンピュートユニットを選択します。ここでは、既存のインスタンスタイプからr3.xlargeを選択します。様々なインスタンスタイプがあるので、Amazon EC2 料金表などを参考に決めるといいでしょう。

f:id:taigok:20190103112123p:plain

その他もろもろの設定をしてリクエストの作成をする時に割引率が提示されます。この時は81%offの価格で利用できることが分かります。

f:id:taigok:20190103112502j:plain

少し時間がたつとインスタンスが作成されるので、ターミナル(Macの場合)などからsshします。インスタンスsshするには、インスタンスのセキュリティグループ(ファイアウォールのようなもの)の設定が必要です。

セキュリティグループの設定

インスタンスのセキュリティグループが下記赤枠から確認できます。

f:id:taigok:20190106193717j:plain

対象のセキュリティグループのインバウンドルールにクライアントのIPを設定します。 個々の環境によって異なるかと思いますが、下記のようにMy IPを選択すれば今使っている端末のグローバルIPが自動で設定されるようです。

f:id:taigok:20190106195249j:plain

SSH接続する

作成したインスタンスSSH接続します。SSH接続するためのコマンドは接続をクリックすると表示されます。

f:id:taigok:20190106193406j:plain

f:id:taigok:20190106200039j:plain

$ chmod 400 spot-instance-key.pem 

$ ssh -i "spot-instance-key.pem" root@ec2-54-248-31-178.ap-northeast-1.compute.amazonaws.com

# SSH完了
[ec2-user@ip-172-31-14-83 ~]$

これでスポットインスタンスへのSSHが完了しました。

ここからは各自自由にJupyterを起動するまでの手順を進めてください。
今回自分の場合は、ローカル環境でDockerを使って環境構築して開発していたものをGithubリポジトリで管理していたので、git cloneをして、docker runをしました。 Dockerのインストールは Get Docker CE for Ubuntu を参考に進めます(Ubuntuの場合)。

AMI作成

スポットインスタンスは停止をすることができないため、計算が完了したら削除をしないといけません。よって、2度目以降に同じ環境を構築するためにAMIを作成しておきます。
AMIを作成しておけば、再度スポットインスタンスを作成するときに、テンプレートとしてそのAMIを指定することで同じ環境を構築することができます。

参考:インスタンスと AMI

Pythonの内包表記について

Pythonには内包表記という、リストなどの iterableな オブジェクトのループ処理をシンプルに記述できる記法があります。今回は基本的な内包表記についてまとめてみました。

通常のリスト

Pythonでリストに追加するには下記のように下記のが一般的ですが、内包表記というものもあります。

squares = []
for x in range(10):
    squares.append(x**2)

print(squares)
# 結果
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

内包表記

内包表記の場合、下記のように1行で書くことができます。

squares = [x**2 for x in range(10)]
print(squares)
# 結果
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

map()

リストの各要素に関数を適用するmap関数を用いて書くこともできます。

# map(関数, リスト)
squares = list(map(lambda x: x**2, range(10)))
# 結果
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

参考:4. Map, Filter and Reduce

【読書メモ】ゼロから作るDeep Learning 5章

f:id:taigok:20160908173332j:plain

ゼロから作るDeep Learningの読書メモとしてまとめました。

本を読む目的

Coursera Machine Learningを受講してニューラルネットワークを理解したので、次はディープラーニングについて深く理解をしたいため。

前章までのまとめ

はじめに

本章では、重みパラメータの勾配の計算を高速に行う手法である 誤差逆伝播 について学ぶ。4章のニューラルネットワークの学習では、重みパラメータの勾配は数値微分で求めたが、時間がかかるので効率的に計算を行う方法として誤差逆伝播法が編み出されました。

2層のニューラルネットワーク

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
from common.layers import *
from common.gradient import numerical_gradient
from collections import OrderedDict


class TwoLayerNet:

    def __init__(self, input_size, hidden_size, output_size, weight_init_std = 0.01):
        # 重みの初期化
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size) 
        self.params['b2'] = np.zeros(output_size)

        # レイヤの生成
        self.layers = OrderedDict()
        self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
        self.layers['Relu1'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])

        self.lastLayer = SoftmaxWithLoss()
        
    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)
        
        return x
        
    def loss(self, x, t):
        y = self.predict(x)
        return self.lastLayer.forward(y, t)
    
    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        if t.ndim != 1 : t = np.argmax(t, axis=1)
        
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy
      
        
    def gradient(self, x, t):
        # forward
        self.loss(x, t)

        # backward
        dout = 1
        dout = self.lastLayer.backward(dout)
        
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)

        # 設定
        grads = {}
        grads['W1'], grads['b1'] = self.layers['Affine1'].dW, self.layers['Affine1'].db
        grads['W2'], grads['b2'] = self.layers['Affine2'].dW, self.layers['Affine2'].db

        return grads

誤差逆伝播法を使った学習

# coding: utf-8
import sys, os
sys.path.append(os.pardir)

import numpy as np
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet

# データの読み込み
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

iters_num = 10000
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1

train_loss_list = []
train_acc_list = []
test_acc_list = []

iter_per_epoch = max(train_size / batch_size, 1)

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    
    # 勾配
    grad = network.gradient(x_batch, t_batch)
    
    # 更新
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]
    
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)
    
    if i % iter_per_epoch == 0:
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)
        print(train_acc, test_acc)

【読書メモ】ゼロから作るDeep Learning 4章

f:id:taigok:20160908173332j:plain

ゼロから作るDeep Learningの読書メモとしてまとめました。

本を読む目的

Coursera Machine Learningを受講してニューラルネットワークを理解したので、次はディープラーニングについて深く理解をしたいため。

前章までのまとめ

ニューラルネットワークの学習

  • ニューラルネットワークでいう学習とは、訓練データから最適な重みパラメータの値を自動で獲得すること
  • 特徴量とはデータから本質的なデータを的確に抽出できるように設計された変換器のこと
  • SVN, KNN等の機械学習では特徴量自体は人が設計するが、ニューラルネットワークの場合はデータをそのまま学習する

損失関数

最適なパラメータを探索(学習)するための指標。一般的にはニューラルネットワークの損失関数は二乗和誤差か交差エントロピーを用いる。損失関数の値をもっとも小さくする重みパラメータを探し出すことを学習という。

二乗和誤差

ニューラルネットワークの出力と教師データの各要素の差の二乗の総和

def mean_squared_error(y, t):
    return 0.5 * np.sum((y - t) ** 2 )

t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0] # 教師データ
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0] # ニューラルネットワークの出力

mean_squared_error(np.array(y), np.array(t))

結果

0.097500000000000031

交差エントロピー誤差

正解ラベルのインデックスのみ1となるので、それに対応するニューラルネットワークの出力の自然対数を計算。deltaを使っているのは、log(0)となった時にマイナス無限大となってしまうのを防ぐため。

def cross_entropy_error(y, t):
   delta = 1e - 7
   return -np.sum(t * np.log(y + delta))

t = [0, 0, 1, 0, 0, 0, 0, 0, 0, 0] # 教師データ
y = [0.1, 0.05, 0.6, 0.0, 0.05, 0.1, 0.0, 0.6, 0.0, 0.0] # ニューラルネットワークの出力
cross_entropy_error(np.array(y), np.array(t))

結果

2.3025840929945458

ミニバッチ学習

全てのデータを対象に損失関数の和を求めるのは時間がかかるため、データの中から一部を取り出して学習させることをミニバッチ学習という。

勾配

全ての変数の偏微分をベクトルとしてまとめたもので、各地点において低くなる方向のこと。勾配が指す方向は、各場所において関数の値を最も減らす方向。勾配法とは、勾配方向へ進むことを繰り返すことで関数の値を減らして行くこと。

勾配降下法

import numpy as np

# 勾配
def numerical_gradient(f, x):
    h = 0.0001
    grad = np.zeros_like(x)

    for idx in range(x.size):
        tmp_val = x[idx]
        x[idx] = tmp_val + h
        fxh1 = f(x)

        x[idx] = tmp_val -h
        fxh2 = f(x)

        grad[idx] = (fxh1 - fxh2) / (2*h)
        x[idx] = tmp_val

    return grad

# 勾配法
def gradient_descent(f, init_x, lr=0.01, step_num=100):
    x = init_x

    for i in range(step_num):
        grad = numerical_gradient(f, x)
        x = x - lr * grad

    return x

# 関数
def function_2(x):
    return x[0]**2 + x[1]**2
# 適切な学習率は最小値に近い値が取得できる
>>> init_x = np.array([-3.0, 4.0])
>>> gradient_descent(function_2, init_x=init_x, lr=0.1, step_num=100)
[-6.11110793e-10 8.14814391e-10]

# 学習率が大きすぎる大きな値に発散してしまう
>>> init_x = np.array([-3.0, 4.0])
>>> gradient_descent(function_2, init_x=init_x, lr=10, step_num=100)
[-2.58983747e+13 -1.29524862e+12]

# 学習率が小さすぎるとほとんど更新されずに終わる
>>> init_x = np.array([-3.0, 4.0])
>>> gradient_descent(function_2, init_x=init_x, lr=le-10, step_num=100)
[-2.999999994, 3.99999992]

二層ニューラルネットワーク

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
from common.functions import *
from common.gradient import numerical_gradient


class TwoLayerNet:

    def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
        # 重みの初期化
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
        self.params['b2'] = np.zeros(output_size)

    def predict(self, x):
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
    
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)
        
        return y
        
    # x:入力データ, t:教師データ
    def loss(self, x, t):
        y = self.predict(x)
        
        return cross_entropy_error(y, t)
    
    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        t = np.argmax(t, axis=1)
        
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy
        
    # x:入力データ, t:教師データ
    def numerical_gradient(self, x, t):
        loss_W = lambda W: self.loss(x, t)
        
        grads = {}
        grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
        grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
        grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
        grads['b2'] = numerical_gradient(loss_W, self.params['b2'])
        
        return grads
        
    def gradient(self, x, t):
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
        grads = {}
        
        batch_num = x.shape[0]
        
        # forward
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)
        
        # backward
        dy = (y - t) / batch_num
        grads['W2'] = np.dot(z1.T, dy)
        grads['b2'] = np.sum(dy, axis=0)
        
        dz1 = np.dot(dy, W2.T)
        da1 = sigmoid_grad(a1) * dz1
        grads['W1'] = np.dot(x.T, da1)
        grads['b1'] = np.sum(da1, axis=0)

        return grads
# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 親ディレクトリのファイルをインポートするための設定
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet

# データの読み込み
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

iters_num = 10000  # 繰り返しの回数を適宜設定する
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1

train_loss_list = []
train_acc_list = []
test_acc_list = []

iter_per_epoch = max(train_size / batch_size, 1)

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]
    
    # 勾配の計算
    #grad = network.numerical_gradient(x_batch, t_batch)
    grad = network.gradient(x_batch, t_batch)
    
    # パラメータの更新
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]
    
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)
    
    if i % iter_per_epoch == 0:
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(test_acc)
        print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))

# グラフの描画
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, label='train acc')
plt.plot(x, test_acc_list, label='test acc', linestyle='--')
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()

まとめ

ニューラルネットワークの学習手順は下記。

  1. ミニバッチ
    訓練データからランダムに一部のデータを選び(ミニバッチ)、そのミニバッチの損失関数の値を減らすことを目的とする。

  2. 勾配
    ミニバッチの損失関数を減らすために、重みパラメータの勾配を求める。

  3. パラメータの更新
    重みパラメータを勾配方向に更新する。更新量はハイパーパラメータの学習率で設定する。

  4. 1~3を繰り返す

DockerコンテナでPython3、Jupyterの入ったデータ分析環境を作成

f:id:taigok:20190101162357j:plain

背景

データ分析コンペに参加した時に、爆速で Docker コンテナにデータ分析環境を生成するテンプレート を作りましたを参考に、DockerコンテナにJupyter notebookが使える環境構築をしたのでその手順についてまとめました。めちゃくちゃ早く環境構築ができるのと、Dockerコンテナを利用することで環境の再現性が出る等のメリットがあります。

プロジェクトディレクトリ作成

cookiecutterのインストール

$ pip install cookiecutter

プロジェクトディレクトリの作成

$ cookiecutter git@github.com:docker-science/cookiecutter-docker-science.git
project_name [project_name]: xxxxxxxxxx
project_slug [national_park_prediction]: national_park_prediction
jupyter_host_port [8888]: 8888
description [Please Input a short description]: This repository is for predicting the number of accumdation in national park
Select data_source_type:
1 - s3
2 - nfs
3 - url
Choose from 1, 2, 3 [1]: 1 # AWSのS3を使用する
data_source [Please Input data source]: s3://project/national_park_prediction/data # S3のデータ格納ディレクトリ
Select use_nvidia_docker:
1 - no
2 - yes
Choose from 1, 2 [1]: 1 # GPUは使用しない

プロジェクトディレクトリが作成されていることを確認

$ ls
Makefile            config              docker              national_park_prediction    requirements.txt        tests
README.md           data                model               notebook            scripts

イメージ作成・コンテナ起動・修正

作成されたディレクトリでDockerfileからimageの作成、コンテナの起動を行います。 requirements.txtをみるとわかるように、デフォルトでは必要最低限のPythonモジュールをインストールする設定なので必要に応じて追記を行ってimageを作成する必要がありそうです。

イメージの作成

$ make init-docker

docker build -t national_park_prediction-image -f docker/Dockerfile --build-arg UID=501 .
Sending build context to Docker daemon  20.48kB
Step 1/10 : FROM ubuntu:16.04
...
...
...
Successfully built ade3fb3ef298
Successfully tagged national_park_prediction-image:latest

イメージが作成されていることを確認

$ docker images
REPOSITORY                       TAG                 IMAGE ID            CREATED             SIZE
national_park_prediction-image   latest              2e8a7ad94a6a        9 seconds ago       797MB
# コンテナ起動・自動ログイン
$ make create-container
docker run -it -v `pwd`:/work -p 8888:8888 --name national_park_prediction-container national_park_prediction-image
docker@187ca128cd75:/work$ 

コンテナ内にプロジェクトディレクトリがマウントされている

$ ls
Makefile  README.md  config  data  docker  forecast_tourist_accomdation_in_national_park  model  notebook  requirements.txt  scripts  tests

コンテナ内でPythonモジュールをインストールした場合、Dockerfileにも反映をさせて、再度イメージファイルを作成して環境の再現性を保つようにしましょう。例えば、モデル作成に利用したxgboostをコンテナ上でインストールした場合、下記のようにrequirements.txtに追記してビルドを行い、そのイメージファイルを元に再度コンテナを起動します。

awscli
click
matplotlib
jupyter
notebook
Sphinx
flake8
xgboost

起動中のコンテナを表示

$ docker ps
CONTAINER ID        IMAGE                            COMMAND             CREATED             STATUS              PORTS                    NAMES
187ca128cd75        national_park_prediction-image   "/bin/bash"         8 days ago          Up 8 days           0.0.0.0:8888->8888/tcp   national_park_prediction-container

対象のコンテナを停止

$ docker stop 187ca128cd75
187ca128cd75

イメージの削除

$ make clean-docker
docker rm national_park_prediction-container
national_park_prediction-container
docker image rm national_park_prediction-image
Untagged: national_park_prediction-image:latest
Deleted: sha256:2e8a7ad94a6a4e75cbd3c67d6a87327108c356bd5add3e5851a9221f45231ad5
・・・
・・・
・・・

ビルド

$ make init-docker

Jupyter notebookの起動

コンテナ上でJupyter notebookの起動を行います。起動後は http://localhost:8888/?token=xxxxxxxxxxxxxxxx にアクセスするとJupyter notebookに入ることができます。 xxxxxの部分はmake jupyterコマンド時に表示されており、下記の例でいうと 7fa9347363b180321b65bac24b900b31d0df6e12a6504fb7 になります。

$ make jupyter
jupyter-notebook --ip=0.0.0.0 --port=8888
[I 01:04:36.114 NotebookApp] Writing notebook server cookie secret to /home/docker/.local/share/jupyter/runtime/notebook_cookie_secret
[I 01:04:36.435 NotebookApp] Serving notebooks from local directory: /work
[I 01:04:36.435 NotebookApp] The Jupyter Notebook is running at:
[I 01:04:36.436 NotebookApp] http://(187ca128cd75 or 127.0.0.1):8888/?token=7fa9347363b180321b65bac24b900b31d0df6e12a6504fb7
[I 01:04:36.436 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[W 01:04:36.437 NotebookApp] No web browser found: could not locate runnable browser.
[C 01:04:36.438 NotebookApp] 
    
    Copy/paste this URL into your browser when you connect for the first time,
    to login with a token:
        http://(187ca128cd75 or 127.0.0.1):8888/?token=7fa9347363b180321b65bac24b900b31d0df6e12a6504fb7

参考:爆速で Docker コンテナにデータ分析環境を生成するテンプレート を作りました

AWS Redshiftを使ってデータをロードしてクエリを実行するまで

f:id:taigok:20190101112401p:plain

AWS Redshiftとは

完全マネージド型、ペタバイトスケールのAWSデータウェアハウスサービス。

AWS Athenaとの違い

「Amazon Athena よくある質問」には下記記載あり。大きな違いは、課金体系でRedshiftはクラスタの起動時間課金に対して、Athenaの場合はデータ使用量に対する課金になります。

Amazon Redshift のクエリエンジンは、非常に大規模なデータベーステーブルを多数結合する複雑なクエリを実行する必要がある場合のユースケースで、パフォーマンスを特に発揮するように最適化されています。非常に大規模な多数のテーブル間で多数の結合がある、高度に構造化されたデータに対してクエリを実行する必要がある場合は、Amazon Redshift をお勧めします。

これに対して、Amazon Athena のようなクエリサービスを使用すると、データの形式化やインフラストラクチャの管理について心配することなく、Amazon S3 のデータに対して直接インタラクティブにクエリを実行できます。例えば、Athena は、一部のウェブログですばやくクエリを実行し、サイトのパフォーマンス問題のトラブルシューティングのみが必要とされる場合に適しています。クエリサービスを使用すると、迅速に開始できます。データのテーブルを定義し、標準 SQL を使用してクエリを開始するのみです。

課金体系

Redshiftはオンデマンドの場合、クラスタの起動時間課金になります。Redshift Spectrumの場合、スキャンされたバイト数に対して課金されます。Athenaの場合、スキャンされたデータ 1TB あたり 5USDがかかります。

準備

入門ガイドである Amazon Redshift の使用開始 を参考に進めます。 ガイド通りにSQL Workbench/JというSQL クライアントツールをインストールをしました。

Amazon S3 のサンプルデータをロードする

CREATE TABLE

CREATE TABLE を参考に、Workbench/Jから下記DDL(Data Definition Language)を実行して、新しいテーブルを作成します。

create table users(
    userid integer not null distkey sortkey,
    username char(8),
    firstname varchar(30),
    lastname varchar(30),
    city varchar(30),
    state char(2),
    email varchar(100),
    phone char(14),
    likesports boolean,
    liketheatre boolean,
    likeconcerts boolean,
    likejazz boolean,
    likeclassical boolean,
    likeopera boolean,
    likerock boolean,
    likevegas boolean,
    likebroadway boolean,
    likemusicals boolean);

create table venue(
    venueid smallint not null distkey sortkey,
    venuename varchar(100),
    venuecity varchar(30),
    venuestate char(2),
    venueseats integer);

create table category(
    catid smallint not null distkey sortkey,
    catgroup varchar(10),
    catname varchar(10),
    catdesc varchar(50));

create table date(
    dateid smallint not null distkey sortkey,
    caldate date not null,
    day character(3) not null,
    week smallint not null,
    month character(5) not null,
    qtr character(5) not null,
    year smallint not null,
    holiday boolean default('N'));

create table event(
    eventid integer not null distkey,
    venueid smallint not null,
    catid smallint not null,
    dateid smallint not null sortkey,
    eventname varchar(200),
    starttime timestamp);

create table listing(
    listid integer not null distkey,
    sellerid integer not null,
    eventid integer not null,
    dateid smallint not null  sortkey,
    numtickets smallint not null,
    priceperticket decimal(8,2),
    totalprice decimal(8,2),
    listtime timestamp);

create table sales(
    salesid integer not null,
    listid integer not null distkey,
    sellerid integer not null,
    buyerid integer not null,
    eventid integer not null,
    dateid smallint not null sortkey,
    qtysold smallint not null,
    pricepaid decimal(8,2),
    commission decimal(8,2),
    saletime timestamp);

s3にあるサンプルデータをロード

copy users from 's3://awssampledbuswest2/tickit/allusers_pipe.txt' 
credentials 'aws_iam_role=<iam-role-arn>' 
delimiter '|' region 'us-west-2';

copy venue from 's3://awssampledbuswest2/tickit/venue_pipe.txt' 
credentials 'aws_iam_role=<iam-role-arn>' 
delimiter '|' region 'us-west-2';

copy category from 's3://awssampledbuswest2/tickit/category_pipe.txt' 
credentials 'aws_iam_role=<iam-role-arn>' 
delimiter '|' region 'us-west-2';

copy date from 's3://awssampledbuswest2/tickit/date2008_pipe.txt' 
credentials 'aws_iam_role=<iam-role-arn>' 
delimiter '|' region 'us-west-2';

copy event from 's3://awssampledbuswest2/tickit/allevents_pipe.txt' 
credentials 'aws_iam_role=<iam-role-arn>' 
delimiter '|' timeformat 'YYYY-MM-DD HH:MI:SS' region 'us-west-2';

copy listing from 's3://awssampledbuswest2/tickit/listings_pipe.txt' 
credentials 'aws_iam_role=<iam-role-arn>' 
delimiter '|' region 'us-west-2';

copy sales from 's3://awssampledbuswest2/tickit/sales_tab.txt'
credentials 'aws_iam_role=<iam-role-arn>'
delimiter '\t' timeformat 'MM/DD/YYYY HH:MI:SS' region 'us-west-2';

クエリ実行

SELECT *    
FROM pg_table_def    
WHERE tablename = 'sales';   
public   sales   salesid integer    delta32k    false   0  true
public  sales   listid  integer    delta32k    true    0  true
public  sales   sellerid    integer    delta32k    false   0  true
public  sales   buyerid integer    delta32k    false   0  true
public  sales   eventid integer    delta32k    false   0  true
public  sales   dateid  smallint    lzo false   1  true
public  sales   qtysold smallint    lzo false   0  true
public  sales   pricepaid   numeric(8,2)  lzo false   0  false
public  sales   commission  numeric(8,2)  delta32k    false   0  false
public  sales   saletime    timestamp without time zone lzo false   0  false

クラスターのリセット

ステップ 7: 他のリソースの検索と環境のリセットを参考にクラスターを削除します。クラスターをするまで、そのクラスターについて Amazon Redshift サービスの使用料が継続して発生するので注意しましょう。

pandasのread_csv時にdtypeを指定してメモリの節約

f:id:taigok:20190122215238j:plain

Kaggleで大きいデータの取り扱い

Microsoft Malware Predictionというコンペに参加しています。 このコンペでは与えられたデータが大きく、普通にread_csvだとローカルPCではメモリに乗りきりません。 こんな時いくつか方法がありますが、今回はread_csvのdtype指定についてまとめます。データは4.08 GBです。

pandasのread_csv時にdtypeを指定

pandasの仕様上、read_csv時にdtypeを指定しないと、整数はint64 、小数はfloat64 が勝手に割り当てられるようです。なので、必要最低限の型を割り当てる必要があります。

dtypes = {
        'MachineIdentifier':             'category',
        'ProductName':                   'category',
       ・
       ・
       ・
        'OsSuite':                             'int16',
        'OsPlatformSubRelease':   'category',
        'Census_IsVirtualDevice':  'float16',
        'Census_IsTouchEnabled': 'int8',
        'Census_IsPenCapable':     'int8',
        'Wdft_IsGamer':                   'float16',
        'Wdft_RegionIdentifier':      'float16',
        'HasDetections':                  'int8'
        }
df_train = pd.read_csv('../input/train.csv', dtype=dtypes)

ではそもそもメモリに乗らないデータのカラムごとの型をどう見極めればいいでしょうか。方法としてはいくつかあると思いますが、read_csv時に読み込む行数を少なくしてデータを確認して必要なデータ型を調べる方法です。

df_train = pd.read_csv('../input/train.csv', nrows=10000)
df_train.head(10000)

参考

初コンペ SIGNATE 国立公園の観光宿泊者数予測で18位でした

参加の背景

f:id:taigok:20181221001639p:plain

国立公園の観光宿泊者数予測というデータ分析コンペに初めて参加しました。目的としてはデータ分析の一通りの流れを試行錯誤しながら学びたかったためです。データ分析コンペは初参加で、このコンペ後にMicrosoft Malware Predictionに参加予定です。

コンペ概要

タスク

前日以前のデータを元に翌日以降の宿泊者数が予測可能なモデルを作成することです。
予測対象は8つの国立公園付近の1年間(2017/1/1~2017/12/31)の宿泊者数になります。

データ

  • SNSデータ
  • ロケーション付SNSデータ
  • メッシュ型流動人口データ
  • 公共交通検索ログデータ
  • 国別月別来訪者数集計データ
  • 気象データ
  • 積雪気象観測データ
  • 地域経済分析システム(RESAS)

最終順位の決定

コンペ最終日までの評価(暫定評価)は評価用データセットの一部で評価し、コンペ終了後の評価(最終評価)は評価用データセットの残りの部分で評価します。

これは特定のデータに過学習(Overfitting)するのを防ぐためで、暫定評価用のデータセットに最適化したモデルを作成するとコンペ終了後の評価用データセットに適合しないモデルになってしまいます。つまりモデルが汎化していないといけないということになります。この辺りの話は Machine Learning | Courseraで学びました。

また、順位確定の際に下記の情報を提出する必要があります。予測結果の再現の為の手順書は、コメントかJupyter NotebookのセルにMarkdownで記載すれば良さそうです。

  • 予測モデルのソースコード
  • 学習済モデル
  • 予測結果の再現の為の手順書(前処理部分、学習部分、予測部分が分かるよう明記)
  • 実行環境(OSのバージョン、使用ソフトウェア及び解析手法)
  • 乱数シード(Random Forest等の乱数を利用した手法の場合)
  • 各説明変数の予測モデルへの寄与度(寄与度の算出が可能な手法を用いた場合)
  • データの解釈、工夫点、モデリングから得られる示唆等

評価

精度評価は、評価関数「Mean Absolute Error(MAE)」です。
MAEは平均絶対誤差ともいい、外れ値をより大きな誤差として扱う傾向があるそうです。 また、Pythonの代表的な機械学習のライブラリであるscikit-learn には MAE を計算する関数が実装されています。

モデル作成までの流れ

データのDL

国立公園の観光宿泊者数予測のデータページからデータをダウンロードしました。

分析環境作成

プロジェクトフォルダの作成

ディレクトリ構成を作るためのライブラリである Cookiecutter を使用して、機械学習用のプロジェクトディレクトリを作成しました。ディレクトリ作成後、ダウンロードしたデータを data/raw ディレクトリに格納しました。

参考:DockerコンテナでPython3、Jupyterの入ったデータ分析環境を作成する

初めはローカル環境(Macbook Pro)で分析を進めていましたが、リソースが足りない時が時々あったため、途中からはソース管理していたGitHubから読み込んでGoogle Colabrataryを使用して進めました。
Google Colabのスペックは下記になります。

  • n1-highmem-2 instance
  • 2vCPU @ 2.2GHz
  • 13GB RAM
  • 33GB Free Space
  • idle cut-off 90 minutes
  • maximum 12 hours

ソース管理

ソース管理はgitを使いました。リポジトリの作成方法は、https://github.com/new において New Reposotryを作成し、下記コマンドを対象ディレクトリで行えばいいでしょう。これ以降は適宜commit, pushをしていきます。(コンペ開催期間でのPublicリポジトリでの管理は有りなのでしょうか。)

$ pwd
/Users/username/national_park_prediction

$ git init # ワーキングディレクトリにgitリポジトリ作成
$ git add . # 全ファイル、全ディレクトリをインデックスに追加
$ git commit -m "first commit" # インデックスにあるファイル・ディレクトリをローカルリポジトリに記録

# リモートリポジトリ(national_park_prediction.git)をoriginという名前で登録 
$ git remote add origin git@github.com:TaigoKuriyama/national_park_prediction.git 
$ git push -u origin master # リモートリポジトリにアップロード

issue管理

issue管理もGithub上で行いました。issueとは例えば、モデル作成中や環境構築時に出たエラーなどをあげて、どうやって解消したかを記載していきます。 今後似たようなことが起きた時に参考にできます。

f:id:taigok:20181220233135p:plain
Issue

ToDo管理

ToDo管理もGithubのProjects機能を使い、Inbox, In Progress, Doneと分けてタスクを管理しました。 f:id:taigok:20181220233557p:plain

コンペへの取り組み方理解

kaggleのチュートリアル。機械学習の入門に最適!に取り組み、コンペへどう取り組むかについて理解しました。こちらのnoteはkaggle(データ分析コンペプラットフォーム)を対象としたチュートリアルになりますが、コンペの取り組み方という意味ではとても参考になります。有料ですので詳細については書くことができませんがコンペに取り組んでみたいが、どうすればいいかわからないという方は購入してみてもいいかもしれません。
有料が厳しい場合は、こちら 一から始める機械学習(Kaggleで学ぶ機械学習) で学ぶのがいいかもしれません。

分析は主に下記の流れを繰り返すことと理解しました。

  1. データ前処理
  2. アルゴリズム選択
  3. パラメータ選択
  4. モデルの学習
  5. 評価
  6. 提出

データ概要理解

データ説明

コンペページのデータ説明に内容が書かれているのでまずはそれを読みつつ、実際のデータを確認しました。

データ詳細

データ分析のライブラリであるpandasを使って各データの詳細について理解を深めました。read_csv時には ファイル形式はcsv ( 項目間をカンマで区切ったデータ ) ではなく、tsv形式 ( 項目間をタブで区切ったデータ )なので、read_csvには sep='\t' というオプションが必要です。

# データの確認(観光宿泊者学習データの場合)
import pandas as pd
df_train = pd.read_csv('../data/raw/train.tsv',sep='\t')
df_train.head()
# データ確認
df.head(n)        # データフレームの上からn行を表示
df.shape          # データフレームの行数と列数
df.columns        # カラム
df.info()         # データの概要
df.isnull().sum() # 欠損値の数
df.describe()     # 要約統計量

日付ごとの平均visitor数を降順に並べると、当たり前ですがGWや年末年始が多いことがわかります。よって祝日フラグデータがあればいいと考えました。

ネットで見つからなかったのでGoogle SpreadSheetで休日データの作成をしました。 まずは、土日フラグを下記の式で立てます。

=IF(( WEEKDAY(A2, 2) >= 6 ),1,0)

祝日のリストはこちらいただきました。土日リストと祝日リストからholiday.csvを作成しました。

特徴量について

最終的に使用した特徴量は下記になります。
仮説を立ててデータから試行錯誤して特徴量を作成し、モデルを作成、Cross Validationの結果を見てSubmitを繰り返すという流れを続けましたが最終的に使用したのは気象データ(気象庁)のみでした。

  • 曜日
  • 公園
  • 休日
  • sin, cos, tan
  • 気温
  • 降水量
  • 降雪量

結果

終結果は18位/124人となりました。

f:id:taigok:20181220235733p:plain

学び・反省・疑問

自分の足りないスキル、知識が明確になったのでとてもいい機会になりました。具体的には下記が学び・反省・疑問になります。

  • データ探索のスキルが浅く与えられたデータから有益な特徴量が発見できなかった
  • フォーラムのようなものが無く、完全に個人で進める感じだったので周りから勉強をする機会が無く残念だった
  • モデルのアンサンブルをする時間が無かった
  • 特徴量、モデル、提出ファイルの管理をどうすればいいか分からなかった
  • 時系列データへの取り組み方が正しいのか分かっていない
  • コンペ中はずっと Jupyter Notebookでコーディングするのだろうか?

当該Gitリポジトリ

全然まとまっていませんが、下記がコンペのリポジトリになります。 https://github.com/TaigoKuriyama/national_park_prediction

参考

【読書メモ】ゼロから作るDeep Learning 3章

f:id:taigok:20160908173332j:plain

ゼロから作るDeep Learning

ゼロから作るDeep Learningの読書メモとしてまとめました。

本を読む目的

Coursera Machine Learningを受講してニューラルネットワークを理解したので、次はディープラーニングについて深く理解をしたいため。

前章までのまとめ

内容

ステップ関数

# coding: utf-8
import numpy as np
import matplotlib.pylab as plt


def step_function(x):
    return np.array(x > 0, dtype=np.int)

X = np.arange(-5.0, 5.0, 0.1)
Y = step_function(X)
plt.plot(X, Y)
plt.ylim(-0.1, 1.1)  # 図で描画するy軸の範囲を指定
plt.show()

f:id:taigok:20181215114538p:plain

シグモイド関数

# coding: utf-8
import numpy as np
import matplotlib.pylab as plt


def sigmoid(x):
    return 1 / (1 + np.exp(-x))    

X = np.arange(-5.0, 5.0, 0.1)
Y = sigmoid(X)
plt.plot(X, Y)
plt.ylim(-0.1, 1.1)
plt.show()

f:id:taigok:20181215115130p:plain

ステップ関数・シグモイド関数の共通点

  • 入力が小さい時に出力は0に近く、入力が大きい時に出力は1に近づく
  • 出力信号は0から1の間
  • 非線形関数

Relu関数

最近のニューラルネットワークはRelu(Rectified Linear Unit)関数を主に用いている。

# coding: utf-8
import numpy as np
import matplotlib.pylab as plt


def relu(x):
    return np.maximum(0, x)

x = np.arange(-5.0, 5.0, 0.1)
y = relu(x)
plt.plot(x, y)
plt.ylim(-1.0, 5.5)
plt.show()

f:id:taigok:20181215120547p:plain

ニューラルネットワークのnumpy実装

  • 回帰問題の場合、出力層の活性化関数を恒等関数(入力をそのまま出力する関数)として利用する
  • 分類問題の場合、ソフトマックス関数を利用する
## Neural Network
import numpy as np

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

def indentity_function(x):
    return x

def init_network():
    network = {}
    network['W1'] = np.array([[0.1,0.3,0.5], [0.2,0.4,0.6]])
    network['b1'] = np.array([0.1,0.2,0.3])
    network['W2'] = np.array([[0.1,0.4], [0.2,0.5], [0.3,0.6]])
    network['b2'] = np.array([0.1,0.2])
    network['W3'] = np.array([[0.1,0.3], [0.2,0.4]])
    network['b3'] = np.array([0.1,0.2])

    return network

def forward(network, x):
    W1, W2, W3 = network['W1'], network['W2'], network['W3']
    b1, b2, b3 = network['b1'], network['b2'], network['b3']

    a1 = np.dot(x, W1) + b1
    z1 = sigmoid(a1)
    a2 = np.dot(z1, W2) + b2
    z2 = sigmoid(a2)
    a3 = np.dot(z2, W3) + b3
    y = indentity_function(a3)

    return y

network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
print(y)
$  python nn.py
[0.31682708 0.69627909]

ソフトマックス関数

  • ソフトマックス関数の出力は0〜1
  • 出力の総和は1になる
  • よって、ソフトマックス関数の出力を確率として解釈できる
import numpy as np

# softmax function
def softmax(a):

    exp_a = np.exp(a) # 指数関数
    sum_exp_a = np.sum(exp_a)
    y = exp_a / sum_exp_a

    return y

a = np.array([0.3,2.9,4.0])
y = softmax(a)
print(y)
$ python softmax.py
[0.01821127 0.24519181 0.73659691]

【読書メモ】ゼロから作るDeep Learning 2章

f:id:taigok:20160908173332j:plain

ゼロから作るDeep Learning

ゼロから作るDeep Learningの読書メモとしてまとめました。

本を読む目的

Coursera Machine Learningを受講してニューラルネットワークを理解したので、次はディープラーニングについて深く理解をしたいため。

前章までのまとめ

内容

パーセプトロンとは

パーセプトロンの実装

ANDゲート

# coding: utf-8
import numpy as np

def AND(x1, x2):
    x = np.array([x1, x2])
    w = np.array([0.5, 0.5])
    b = -0.7
    tmp = np.sum(w*x) + b
    if tmp <= 0:
        return 0
    else:
        return 1

if __name__ == '__main__':
    for xs in [(0, 0), (1, 0), (0, 1), (1, 1)]:
        y = AND(xs[0], xs[1])
        print(str(xs) + " -> " + str(y))
$ python and_gate.py
(0, 0) -> 0
(1, 0) -> 0
(0, 1) -> 0
(1, 1) -> 1

NANDゲート

# coding: utf-8
import numpy as np

def NAND(x1, x2):
    x = np.array([x1, x2])
    w = np.array([-0.5, -0.5])
    b = 0.7
    tmp = np.sum(w*x) + b
    if tmp <= 0:
        return 0
    else:
        return 1

if __name__ == '__main__':
    for xs in [(0, 0), (1, 0), (0, 1), (1, 1)]:
        y = NAND(xs[0], xs[1])
        print(str(xs) + " -> " + str(y))
$ python nand_gate.py
(0, 0) -> 1
(1, 0) -> 1
(0, 1) -> 1
(1, 1) -> 0

ORゲート

# coding: utf-8
import numpy as np

def OR(x1, x2):
    x = np.array([x1, x2])
    w = np.array([0.5, 0.5])
    b = -0.2
    tmp = np.sum(w*x) + b
    if tmp <= 0:
        return 0
    else:
        return 1

if __name__ == '__main__':
    for xs in [(0, 0), (1, 0), (0, 1), (1, 1)]:
        y = OR(xs[0], xs[1])
        print(str(xs) + " -> " + str(y))
 python or_gate.py
(0, 0) -> 0
(1, 0) -> 1
(0, 1) -> 1
(1, 1) -> 1

XORゲート

XORゲートは第一層にNANDとORゲート、第二層にAND。

# coding: utf-8
from and_gate import AND
from or_gate import OR
from nand_gate import NAND

def XOR(x1, x2):
    s1 = NAND(x1, x2)
    s2 = OR(x1, x2)
    y = AND(s1, s2)
    return y

if __name__ == '__main__':
    for xs in [(0, 0), (1, 0), (0, 1), (1, 1)]:
        y = XOR(xs[0], xs[1])
        print(str(xs) + " -> " + str(y))

【読書メモ】ゼロから作るDeep Learning 1章

f:id:taigok:20160908173332j:plain

ゼロから作るDeep Learning

ゼロから作るDeep Learningの読書メモとしてまとめました。

本を読む目的

Coursera Machine Learningを受講してニューラルネットワークを理解したので、次はディープラーニングについて深く理解をしたいため。

内容

プログラムのダウンロード

本を内容のプログラムはgit clone してダウンロードしました。

$ git clone https://github.com/oreilly-japan/deep-learning-from-scratch

Pythonのクラス

# man.py
# coding: utf-8
class Man:

    def __init__(self, name):
        self.name = name
        print("Initilized!")

    def hello(self):
        print("Hello " + self.name + "!")

    def goodbye(self):
        print("Good-bye " + self.name + "!")

m = Man("David")
m.hello()
m.goodbye()
$ python man.py
Initilized!
Hello David!
Good-bye David!