08 November 2015

1. 導入

これは確率的勾配降下法についてまとめたものです.

私達が実際に機械学習で行っていることは誤差関数を最小にすることや尤度関数を最大にするパラメータを求めることです.しかしながら,関数の最小点を見つけることを「最適化」とすると,関数によっては最適化はとても難しいものとなり,単なる行列計算では求めることができない場合があります( むしろそのような場合がほとんどです ).

その際に勾配を用いたアプローチが効果的と考えられています.


2. 勾配降下法

関数の勾配を用いて,最適化を図る手法を勾配降下法( Gradient Descent: GD )と呼びます.

grad

横軸を重み\( w \),縦軸を誤差\( E(w) \)とした時,誤差が最小となる重みを\( w^{opt} \)とし,現在の重みを\( w^{old} \)とします. 学習によって最適な重み\( w^{opt} \)へ近づくように重みの更新を行うのですが,その際知りたいのは重みをどの方向へ動かせば良いのかということで,それには\( w^{old} \)上での傾き(勾配)を用いることで解決されます. よって,\( w^{old} \)の地点の傾きから,変化量 \( \Delta w \)を求め,新たな重み\( w^{new} \)を求めます.

\begin{equation} w^{new}\; =\; w^{old}\; +\; \Delta w \end{equation}

このとき傾きは重み\( w \)のみで\( E(w) \)を微分したものとして,\( \frac{\partial E}{\partial w} \)と表すことができます.

図のように傾きが正のとき\( w \)を減少させ,負のときは\( w \)を増加させます. したがって,変化量\( \Delta w \)は関数値(誤差)を減らすため,学習係数\( \eta \)を用いて

\begin{equation} \Delta w\; =\; -\eta \frac{\partial E}{\partial w} \end{equation}

と表すことができます. 勾配降下法はこれを繰り返すことで関数値を減らし,\( w^{opt} \)に近づいていきます.


2.1 学習係数\( \eta \)について

\( E(w + \Delta w) < E(w) \)が成り立つ,つまり移動後に関数値がより低くなるためには学習係数\( \eta \)は正の小さな値でなければなりません.

目的関数\( E(w) \)について,例えば一変数の関数では以下のように近似できます.

\begin{equation} E\left( w +\Delta w \right)\; \approx \; E\left( w \right)\; +\; \Delta w\frac{d E\left( w \right)}{d w} ( \Delta w は小さい値) \end{equation}

このとき\( \Delta w\;=\;-\eta \frac{d E(w)}{d w} \)にとると

\begin{equation} E\left( w +\Delta w \right)\; \approx \; E\left( w \right)\; -\; \eta \left( \frac{d E\left( w \right)}{d w} \right)^{2}\; <\; E\left( w \right) \end{equation}

が成立し,\( E(w + \Delta w) < E(w) \)と言えます.

そのため,関数の値を確実に減らすには

\begin{equation} E\left( w +\Delta w \right)\; \approx \; E\left( w \right)\; +\; \Delta w\frac{d E\left( w \right)}{d w} ( \Delta w は小さい値) \end{equation}

が成り立たなければならないため,\( \eta \)は正の小さい値である必要があります.


2.2 勾配降下法の問題点

勾配降下法はデータ量が多いとその分の計算量も非常に大きなものになってしまいます.

例えば目的関数\( E(\mathbf{x}) \)を以下のものとします.

このとき目的関数を勾配降下法で解くために重み\( w \)で微分を求めても,\( N \)の分だけの和をとる形になってしまいます.

\begin{equation} \frac{\partial E\left( \mathbf{w} \right)}{\partial \mathbf{w}}=\; \sum^{N}{\frac{\partial y\left( \mathbf{x}_n \right)}{\partial \mathbf{w}}} \end{equation}

\begin{equation} \mathbf{w}=\mathbf{w} -\eta \sum^{N}{\frac{\partial y\left( \mathbf{x}_n \right)}{\partial \mathbf{w}}} \end{equation}

つまり,勾配降下法でパラメータを更新するにはこの微分を毎回求める必要があるため,その回数分\( \frac{\partial \; y\left( \mathbf{x}_n \right)}{\partial \mathbf{w}} \)を計算しなければなりません.


3. 確率的勾配降下法

勾配降下法の問題を解決する,新たな関数最適化の手法が確率的勾配降下法( Stochastic Gradient Descent: SGD )です.

単純に1つのデータごとに \( \frac{\partial y\left( \mathbf{x}_{n} \right)}{\partial \mathbf{w}} \) を計算し,重み\( w \)を更新する手法なのですが 勾配降下法よりも正確さや速度において優れていると言われています. 勾配降下法同様,こちらもデータ全体を何周かまわす必要がありますが,勾配降下法の1回分と同じ計算量でN回移動できるため,非常に高速に解にたどり着くことが可能です.

\begin{equation} \mathbf{w}=\mathbf{w} -\eta \frac{\partial E_n\left( \mathbf{w} \right)}{\partial \mathbf{w}} \end{equation}

利点

  • 局所最適解にトラップしにくい(勾配法の初期値依存問題への解決)
  • 冗長な学習データがある場合,勾配降下法よりも学習が高速
  • 学習データを収集しながら逐次的に学習可能
  • 新しいデータのみを使って更新を行えばよい
  • 大量のメモリを必要としない
  • 数TB程度の大規模なデータから学習を行う場合等に有効

欠点

  • パラメータの収束が不安定
  • データによっては学習率をうまい具合に設定しないと収束しません

ちなみに,ここでの「確率的」とは学習するデータの順番がランダムであり,実行するたびに結果が変わること示すものです.


3.1 ミニバッチ

確率的勾配降下法について,データを全て見るのではなく計算可能なある程度の固まりを単位として勾配を計算していく方法をミニバッチ(Minibatch)と言います.

サンプルの数を\( m \)としたとき,それが十分なサイズならば\( \nabla E\)について

\begin{equation} \nabla E \approx \frac{\sum^{M}{\nabla E_{j}}}{m} \end{equation}

と近似することができます. このときのパラメータの更新は以下のようになります.

\begin{equation} \mathbf{w} = \mathbf{w} - \frac{\eta }{M}\sum_{j}^{M}{\frac{\partial E}{\partial \mathbf{w}}} \end{equation}


3.2 オンライン学習とバッチ学習

勾配降下法と確率的勾配降下法について説明しましたが,これらの決定的な違いは勾配の使い方であり,この記事のテーマである確率的勾配降下法はオンライン学習,勾配降下法はバッチ学習と分けて考えることができます.

オンライン学習とはデータを1つ見たらパラメータを更新する作業を繰り返すことを指し,対してバッチ学習はデータを全部読み込んでからパラメータを更新する作業を繰り返すことを指します.


4. ロジスティック回帰の学習

1次元のロジスティック回帰の学習を確率的勾配降下法を使って導きます.

ロジスティック回帰は簡単に言えば\(\mathbf{x}\)について2値分類を行う手法です. \( f\left( \mathbf{x} \right)\;=\;\mathbf{w}^{T}\mathbf{x}\; +\; b \)によって変換されたベクトルをシグモイド関数\(\sigma \left( x \right)=\frac{1}{1+\exp \left( -x \right)}\)により確率値としたとき,ラベル\(d \in 0, 1\)に対する尤度は以下のように表すことができます.

\begin{eqnarray} p\left( d | \mathbf{x} \right)\; & = & p(d=1|\mathbf{x})^{d} p(d=0|\mathbf{x})^{1-d}\\ & = & y\left( \mathbf{x} \right)^{d}\{ 1\; -\; y\left( \mathbf{x} \right) \}^{1-d} \end{eqnarray}

ここで入力からラベルを推定するために重み\( \mathbf{w} \)について最尤推定を行うとき,目的関数は以下のようになります.

\begin{equation} E(\mathbf{w}) = -\sum^{N}_{n=1}{\left[d_n\log y(\mathbf{x}_n) +(1-d_n)\log\{1-y(\mathbf{x}_n)\}\right]} \end{equation}

この関数の最小化を考えます.


4.1 \( \frac{\partial E}{\partial \mathbf{w}} \)の導出

対数尤度関数\(E\)は

\begin{equation} E(\mathbf{w}) = -\sum^{N}{\left[d_n\log y(\mathbf{x}_n) +(1-d_n)\log\{1-y(\mathbf{x}_n)\}\right]} \end{equation}

ここで,

\begin{eqnarray} \log\{1-y\left( \mathbf{x}_{n} \right)\} & = & \log\left[ 1-\frac{1}{1+\exp \left\{ -(\mathbf{w}^{T}\mathbf{x}+b) \right\}}\right] \\ & = & \log\left[ \frac{\exp \left\{ -(\mathbf{w}^{T}\mathbf{x}+b) \right\}}{1+\exp \left\{ -(\mathbf{w}^{T}\mathbf{x}+b) \right\}} \right]\\ & = & \log\left[\exp \left\{ -(\mathbf{w}^{T}\mathbf{x}+b) \right\}y\left( \mathbf{x}_n \right)\right]\\ & = & \log\left[\exp \left\{ -(\mathbf{w}^{T}\mathbf{x}+b) \right\}\right] + \log y\left( \mathbf{x}_n \right)\\ & = & -(\mathbf{w}^{T}\mathbf{x}+b) + y\left( \mathbf{x} \right) \end{eqnarray}

より,

\begin{eqnarray} E(\mathbf{w}) & = & -\sum^{N}{\left[d_n\log y(\mathbf{x}n) +(1-d_n)\{ -(\mathbf{w}^{T}\mathbf{x}+b) + \log y\left( \mathbf{x}_n \right) \}\right]}\\ & = & -\sum^{N}{\left[\log y(\mathbf{x}_n) +(1-d_n)\{ -(\mathbf{w}^{T}\mathbf{x}+b)\}\right]}\\ & = & \sum^{N}{\left[\log \left[1+\exp \left\{ -(\mathbf{w}^{T}\mathbf{x}+b) \right\}\right] +(1-d_n)\{ -(\mathbf{w}^{T}\mathbf{x}+b)\}\right]} \end{eqnarray}

ここで\(w\)に対して微分すると,

\begin{eqnarray} \frac{\partial E}{\partial \mathbf{w}} & = & \sum^{N}{\left[\frac{y\left( \mathbf{x}_n \right) \left[ 1+\exp \left\{ -(\mathbf{w}^{T}\mathbf{x}+b)\right\} -1\right]}{1+\exp \left\{ -(\mathbf{w}^{T}\mathbf{x}+b) \right\}} + (1-d_n)(-0 -\mathbf{x}_n)\right]} \\ & = & \sum^{N}{\left[\{\mathbf{x}_n - \mathbf{x}_n \cdot y\left( \mathbf{x}_n \right)\} - (\mathbf{x}_n - d_n \cdot \mathbf{x}_n)\right]}\\ & = & -\sum^{N}{\{ d_n - y\left( \mathbf{x}_n \right)\}\mathbf{x}_n} \end{eqnarray}

となります. よって,

\begin{equation} \frac{\partial E}{\partial \mathbf{w}} = -\sum^{N}{\{ d_n - y\left( \mathbf{x}_n \right)\}\mathbf{x}_n} \end{equation}


4.2 \( \frac{\partial E}{\partial b} \)の導出

\(b\)についても同様に計算すると

\begin{equation} \frac{\partial E}{\partial b} = -\sum^{N}{\{ d_n - y\left( \mathbf{x}_n \right)\}} \end{equation}


4.3 パラメータの更新

このように求めた勾配を用いて,以下のようにして\(\mathbf{w}, b\)を更新し,目的関数\(E\)の最小化を行います.

\begin{equation} \mathbf{w}\; =\; \mathbf{w}\; -\; \eta \frac{\partial E}{\partial \mathbf{w}} \end{equation}

\begin{equation} b\; =\; b\; -\; \eta \frac{\partial E}{\partial b} \end{equation}


5. Pythonで実装

5.1 データの生成

0または1でラベル付けされた2種類のデータ集合を生成し,それらをランダムに並べ替えます.

def main():
    '''
    Function: main
    Summary: メイン関数
    '''
    m, N = 2, 10000
    x1, x2 = np.random.randn(N, m), np.random.randn(N, m) + np.array([5, 5])
    x = np.vstack((x1, x2))
    d1, d2 = np.zeros(N), np.ones(N)
    d = np.hstack((d1, d2))
    dataset = np.column_stack((x, d))
    np.random.shuffle(dataset)

    x, d = dataset[:, :2], dataset[:, 2]
    w, b = np.random.rand(m), np.random.random()
>>> import numpy as np
>>> m, N = 2, 1000
>>> x1, x2 = np.random.randn(N, m), np.random.randn(N, m) + np.array([5, 5])
>>> x = np.vstack((x1, x2))
>>> d1, d2 = np.zeros(N), np.ones(N)
>>> d = np.hstack((d1, d2))
>>> dataset = np.column_stack((x, d))
>>> np.random.shuffle(dataset)
>>> x, d = dataset[:, :2], dataset[:, 2]
>>> x
array([[ 1.89631613, -0.36255756],
       [ 0.12282228,  1.12186762],
       [ 5.55456376,  3.58391658],
       ...,
       [-0.2250628 , -1.09651331],
       [-2.26790372, -0.06684712],
       [-0.15449889,  0.34766331]])
>>> d
array([ 0.,  0.,  1., ...,  0.,  0.,  0.])

5.2 勾配の計算

\begin{equation} \frac{\partial E}{\partial \mathbf{w}} = -\sum^{N}{\{ d_n - y\left( \mathbf{x}_n \right)\}\mathbf{x}_n} \end{equation}

\begin{equation} \frac{\partial E}{\partial b} = -\sum^{N}{\{ d_n - y\left( \mathbf{x}_n \right)\}} \end{equation}

def p_y_given_x(x, w, b):
    '''
    Function: p_y_given_x
    Summary: 写像
    Attributes:
        @param (x):データ
        @param (w):重み
        @param (b):バイアス
    Returns: 写像後の値
    '''
    def sigmoid(a):
        return 1.0 / (1.0 + np.exp(-a))
    return sigmoid(np.dot(x, w) + b)

def grad(x, d, w, b):
    '''
    Function: grad
    Summary: 勾配の計算
    Attributes:
        @param (x):データ
        @param (d):ラベル
        @param (w):重み
        @param (b):バイアス
    Returns: 重みの勾配の平均, バイアスの勾配の平均
    '''
    error = d - p_y_given_x(x, w, b)
    w_grad = -np.mean(x.T * error, axis=1)
    b_grad = -np.mean(error)
    return w_grad, b_grad

5.3 確率的勾配降下法

\begin{equation} \mathbf{w} = \mathbf{w} - \frac{\eta }{M}\sum_{j}^{M}{\frac{\partial E}{\partial \mathbf{w}}} \end{equation}

\begin{equation} \mathbf{b} = \mathbf{b} - \frac{\eta }{M}\sum_{j}^{M}{\frac{\partial E}{\partial b}} \end{equation}

def SGD(x, d, w, b, e, eta=0.10, iteration=5, minibatch_size=10):
    '''
    Function: SGD
    Summary: 確率的勾配法 + ミニバッチ
    Attributes:
        @param (x):データ
        @param (d):ラベル
        @param (w):重み
        @param (b):バイアス
        @param (e):誤差を保存
        @param (eta) default=0.10: 学習係数
        @param (iteration) default=5: イテレーション
        @param (minibatch_size) default=10: ミニバッチのサイズ
    Returns: 誤差, 重み, バイアス
    '''
    for _ in range(iteration):
        for index in range(0, x.shape[0], minibatch_size):
            _x = x[index:index + minibatch_size]
            _d = d[index:index + minibatch_size]
            w_grad, b_grad = grad(_x, _d, w, b)
            w -= eta * w_grad
            b -= eta * b_grad
            e.append(np.mean(np.abs(d - p_y_given_x(x, w, b))))
    return e, w, b

5.4 出力結果 - イテレーション毎の誤差(SGD)

error


5.5 出力結果 - 学習されたパラメータによる\( \mathbf{w}^{T}\mathbf{x} + b = 0 \)

scatter


6. おまけ

6.1 正則化

モデルの過学習を防ぎ,汎化能力を高めるために追加の項を導入する手法を正則化( Regularization )と言います. 過学習は既知のデータに対する間違い(経験損失)と未知のデータに対する間違い(期待損失,汎化誤差)が離れている状態を指します. 誤解を恐れずに言うと学習データ専用のパラメータになってしまった状態です.

正則化によりもたらされることはモデルに重みを追加することへの罰則です.具体的には,目的関数に正則化項を入れることにより,パラメータの値が大きくなることを抑制しようとします.

正則化にはいくつか種類があり,その中でもL1正則化とL2正則化は代表的でよく使用されます.

  1. L1正則化(Lasso)
    • 大小に関わらず同等の罰則
    • 多くの重みが0になるため小さなモデルが学習可能
  2. L2正則化(Ridge)
    • 大きな重みに対して大きな罰則を与え,小さな重みに対して小さな罰則を与える
    • 精度が少々高い

自然言語処理を扱う場合どうしても次元数が大きくなってしまい,計算量の膨大になってしまう場合があります.

\begin{equation} \mathbf{x} = \begin{pmatrix} x^{apple}\cr x^{google}\cr .\cr .\cr x^{iias} \end{pmatrix}\hskip3em \mathbf{w} = \begin{pmatrix} w^{apple}\cr w^{google}\cr .\cr .\cr w^{iias} \end{pmatrix} \end{equation}

しかし,ここでL1正則化を用いると目的関数にとって重要でないパラメータは0となるため,多くのパラメータが0になる解つまり疎な解を得ることができます.

正則化は機械学習において非常に重要であるため,機会があればまとめたいと思います.


6.2 モメンタム

勾配降下法の収束性能を向上させる手法として,モメンタム( momentum )があります. 勾配のブレが大きいとなかなか収束しないため,それを低減させるために重みの修正量に前回の重みの修正量をいくらか加算する方法です.

def SGD_momentum(x, d, w, b, e, eta=0.10, mu=0.65, iteration=50, minibatch_size=10):
    '''
    Function: SGD_momentum
    Summary: 確率的勾配法 + ミニバッチ + モメンタム
    Attributes:
        @param (x):データ
        @param (d):ラベル
        @param (w):重み
        @param (b):バイアス
        @param (e):誤差を保存
        @param (eta) default=0.10: 誤差を保存
        @param (mu) default=0.65: 係数
        @param (iteration) default=50: イテレーション
        @param (minibatch_size) default=10: ミニバッチのサイズ
    Returns: 誤差, 重み, バイアス
    '''
    wlist, blist = [w], [b]
    def momentum(mu, list):
        return mu * (list[1] - list[0])
    for _ in range(iteration):
        for index in range(0, x.shape[0], minibatch_size):
            _x = x[index:index + minibatch_size]
            _d = d[index:index + minibatch_size]
            w_grad, b_grad = grad(_x, _d, w, b)

            if len(wlist) > 1:
                w -= eta * w_grad + momentum(mu, wlist)
                b -= eta * b_grad + momentum(mu, blist)
                wlist.pop(0)
                blist.pop(0)
            else:
                w -= eta * w_grad
                b -= eta * b_grad
            wlist.append(w)
            blist.append(b)
            e.append(np.mean(np.abs(d - p_y_given_x(x, w, b))))
    return e, w, b

6.3 AdaGrad

学習係数を自動的に決定する手法の一つにAdaGradがあります. 過去すべての勾配の2乗和の平方根の逆数を利用します.

def SGD_adagrad(x, d, w, b, e, eta=0.10, iteration=50, minibatch_size=10):
    '''
    Function: SGD_adagrad
    Summary: 確率的勾配法 + ミニバッチ + アダグラッド
    Attributes:
        @param (x):データ
        @param (d):ラベル
        @param (w):重み
        @param (b):バイアス
        @param (e):誤差を保存
        @param (eta) default=0.10: 学習係数
        @param (iteration) default=50: イテレーション
        @param (minibatch_size) default=10: ミニバッチのサイズ
    Returns: 誤差, 重み, バイアス
    '''
    wgrad2sum = np.zeros(x.shape[1])
    bgrad2sum = 0
    for _ in range(iteration):
        for index in range(0, x.shape[0], minibatch_size):
            _x = x[index:index + minibatch_size]
            _d = d[index:index + minibatch_size]
            w_grad, b_grad = grad(_x, _d, w, b)
            wgrad2sum += np.power(w_grad, 2)
            bgrad2sum += np.power(b_grad, 2)
            w -= (eta/np.sqrt(wgrad2sum)) * w_grad
            b -= (eta/np.sqrt(bgrad2sum)) * b_grad
            e.append(np.mean(np.abs(d - p_y_given_x(x, w, b))))
    return e, w, b

全掲

1_dim_logistic.py

# coding:utf-8

import numpy as np
import matplotlib.pyplot as plt

def plot(x, d, x1, x2, e, w, b):
    '''
    Function: plot
    Summary: 描画
    Attributes:
        @param (x):データ
        @param (d):ラベル
        @param (x1):グループgのデータ
        @param (x2):グループrのデータ
        @param (e):誤差
        @param (w):重み
        @param (b):バイアス
    '''
    print np.mean(np.abs(d - p_y_given_x(x, w, b)))
    plt.plot(e)
    plt.show()
    bx = np.arange(-6, 10, 0.1)
    by = -b/w[1] - w[0]/w[1]*bx
    plt.xlim([-5, 10])
    plt.ylim([-5, 9])
    plt.plot(bx, by)
    plt.scatter(x1[:, 0], x1[:, 1], c='g')
    plt.scatter(x2[:, 0], x2[:, 1], c='r')
    plt.show()

def p_y_given_x(x, w, b):
    '''
    Function: p_y_given_x
    Summary: 写像
    Attributes:
        @param (x):データ
        @param (w):重み
        @param (b):バイアス
    Returns: 写像後の値
    '''
    def sigmoid(a):
        return 1.0 / (1.0 + np.exp(-a))
    return sigmoid(np.dot(x, w) + b)

def grad(x, d, w, b):
    '''
    Function: grad
    Summary: 勾配の計算
    Attributes:
        @param (x):データ
        @param (d):ラベル
        @param (w):重み
        @param (b):バイアス
    Returns: 重みの勾配の平均, バイアスの勾配の平均
    '''
    error = d - p_y_given_x(x, w, b)
    w_grad = -np.mean(x.T * error, axis=1)
    b_grad = -np.mean(error)
    return w_grad, b_grad

def GD(x, d, w, b, e, eta=0.10, iteration=700):
    '''
    Function: GD
    Summary: 勾配法
    Attributes:
        @param (x):データ
        @param (d):ラベル
        @param (w):重み
        @param (b):バイアス
        @param (e):誤差を保存
        @param (eta) default=0.10: 学習係数
        @param (iteration) default=700: イテレーション
    Returns: 誤差, 重み, バイアス
    '''
    for _ in range(iteration):
        w_grad, b_grad = grad(x, d, w, b)
        w -= eta * w_grad
        b -= eta * b_grad
        e.append(np.mean(np.abs(d - p_y_given_x(x, w, b))))
    return e, w, b

def SGD(x, d, w, b, e, eta=0.10, iteration=5, minibatch_size=10):
    '''
    Function: SGD
    Summary: 確率的勾配法 + ミニバッチ
    Attributes:
        @param (x):データ
        @param (d):ラベル
        @param (w):重み
        @param (b):バイアス
        @param (e):誤差を保存
        @param (eta) default=0.10: 学習係数
        @param (iteration) default=5: イテレーション
        @param (minibatch_size) default=10: ミニバッチのサイズ
    Returns: 誤差, 重み, バイアス
    '''
    for _ in range(iteration):
        for index in range(0, x.shape[0], minibatch_size):
            _x = x[index:index + minibatch_size]
            _d = d[index:index + minibatch_size]
            w_grad, b_grad = grad(_x, _d, w, b)
            w -= eta * w_grad
            b -= eta * b_grad
            e.append(np.mean(np.abs(d - p_y_given_x(x, w, b))))
    return e, w, b

def SGD_momentum(x, d, w, b, e, eta=0.10, mu=0.65, iteration=50, minibatch_size=10):
    '''
    Function: SGD_momentum
    Summary: 確率的勾配法 + ミニバッチ + モメンタム
    Attributes:
        @param (x):データ
        @param (d):ラベル
        @param (w):重み
        @param (b):バイアス
        @param (e):誤差を保存
        @param (eta) default=0.10: 誤差を保存
        @param (mu) default=0.65: 係数
        @param (iteration) default=50: イテレーション
        @param (minibatch_size) default=10: ミニバッチのサイズ
    Returns: 誤差, 重み, バイアス
    '''
    wlist, blist = [w], [b]
    def momentum(mu, list):
        return mu * (list[1] - list[0])
    for _ in range(iteration):
        for index in range(0, x.shape[0], minibatch_size):
            _x = x[index:index + minibatch_size]
            _d = d[index:index + minibatch_size]
            w_grad, b_grad = grad(_x, _d, w, b)

            if len(wlist) > 1:
                w -= eta * w_grad + momentum(mu, wlist)
                b -= eta * b_grad + momentum(mu, blist)
                wlist.pop(0)
                blist.pop(0)
            else:
                w -= eta * w_grad
                b -= eta * b_grad
            wlist.append(w)
            blist.append(b)
            e.append(np.mean(np.abs(d - p_y_given_x(x, w, b))))
    return e, w, b

def SGD_adagrad(x, d, w, b, e, eta=0.10, iteration=50, minibatch_size=10):
    '''
    Function: SGD_adagrad
    Summary: 確率的勾配法 + ミニバッチ + アダグラッド
    Attributes:
        @param (x):データ
        @param (d):ラベル
        @param (w):重み
        @param (b):バイアス
        @param (e):誤差を保存
        @param (eta) default=0.10: 学習係数
        @param (iteration) default=50: イテレーション
        @param (minibatch_size) default=10: ミニバッチのサイズ
    Returns: 誤差, 重み, バイアス
    '''
    wgrad2sum = np.zeros(x.shape[1])
    bgrad2sum = 0
    for _ in range(iteration):
        for index in range(0, x.shape[0], minibatch_size):
            _x = x[index:index + minibatch_size]
            _d = d[index:index + minibatch_size]
            w_grad, b_grad = grad(_x, _d, w, b)
            wgrad2sum += np.power(w_grad, 2)
            bgrad2sum += np.power(b_grad, 2)
            w -= (eta/np.sqrt(wgrad2sum)) * w_grad
            b -= (eta/np.sqrt(bgrad2sum)) * b_grad
            e.append(np.mean(np.abs(d - p_y_given_x(x, w, b))))
    return e, w, b

def main():
    '''
    Function: main
    Summary: メイン関数
    '''
    m, N = 2, 10000
    x1, x2 = np.random.randn(N, m), np.random.randn(N, m) + np.array([5, 5])
    x = np.vstack((x1, x2))
    d1, d2 = np.zeros(N), np.ones(N)
    d = np.hstack((d1, d2))
    dataset = np.column_stack((x, d))
    np.random.shuffle(dataset)

    x, d = dataset[:, :2], dataset[:, 2]
    w, b = np.random.rand(m), np.random.random()

    # e, w, b = GD(x, d, w, b, list())
    e, w, b = SGD(x, d, w, b, list())
    # e, w, b = SGD_momentum(x, d, w, b, list())
    # e, w, b = SGD_adagrad(x, d, w, b, list())
    plot(x, d, x1, x2, e, w, b)

if __name__ == "__main__":
    main()

Stochastic Average Gradientなんてのも提案されているみたいですね. 速いらしいです.

Stochastic Average Gradient法を解説する

あと,間違いがあったらすいません.


Categories

Tags



blog comments powered by Disqus