ディープラーニングのお勉強体験記”30:番外編「XP」の各sin波コード”(リカレントニューラルネットワーク”RNN”を中心にバックプロパゲーション”BPTT”を数式を使って理解したい!)

「ゼロから作るDeep Learning 2 ―自然言語処理編」のRNNコード、P216、第5章「5.5.3 RNNLMの学習コード」から作ったWindows XPの各sin波コード

前回「Windows xp」でAnacondaを使えるようにしたと紹介しました。でもって、ここではそこで動く「sin波学習コード」紹介します。わざわざ紹介する内容でもないかもしれませんが、、、、、、
「Jupyter Notebook」を使うということと(普段は「Jupyter Lab」を使ってます)、コードの先頭部分に「%matplotlib inline」を追加する、グラフの表示部分をちょっと書き換えるだけ(1波形1コマンドラインにする)と、簡単ですから、、、、、
でもまあ、一応書かせてもらいます!(簡単なことでも、それなりに苦労はしていますので)



目次

  1. 1:xp用のRNNのSIN波コード
  2. 2:xp用のLSTMのSIN波コード
  3. 3:xp用のGRUのSIN波コード


1:xp用のRNNのSIN波コード

 # 完成版 Windows xp用 RNNコード
# ゼロから作る Deep Learning2のP216、第5章「5.5.3 RNNLMの学習コード」でコード全体が見えるようにできるだけ「import」を外した
# プログラムの動作検証用にSINカーブを学習させるため変更したコード
# coding: utf-8
# import sys
# sys.path.append('C:\\kojin\\資料\\AI関連\\ゼロから作る Deep Learning\\ゼロから作る Deep Learning2\\deep-learning-from-scratch-2-master\\')
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
# from common.optimizer import SGD
# from dataset import ptb                 # このimportを有効にするには上記パス設定「sys.path.append('C:\\kojin\\AI関連\\・・・」が必要!
# from simple_rnnlm import SimpleRnnlm

np.random.seed(seed=255)                       # 発生する乱数を固定する(255)

# ハイパーパラメータの設定
batch_size = 10
time_size = 10  # Truncated BPTTの展開する時間サイズ

n_in = 1  # 入力層のニューロン数
n_mid = 45  # 中間層のニューロン数
n_out = 1  # 出力層のニューロン数

# できるだけオリジナルのコードに変更を加えないため、既存の変数に代入する
vocab_size = n_out
wordvec_size = n_in
hidden_size = n_mid

lr = 0.008
max_epoch = 301

# 学習データの読み込み
## -------------------学習用データ------------------------------------
# -- 訓練データの作成 --
sin_x = np.linspace(-2*np.pi, 2*np.pi, 101)  # -2πから2πまで
sin_y = np.sin(sin_x)                                            # sin関数 predict用で使用する(なんかバグがある、predict部分で再宣言必要)
sin_y_noise = np.sin(sin_x)  + 0.05*np.random.randn(len(sin_x))  # sin関数に乱数でノイズを加える
x_data = sin_y_noise.reshape(-1, 1)
t_data = sin_y_noise.reshape(-1, 1)
#------------------------------------------------------------------------------------------------


xs = x_data[:-1]  # 入力
ts = t_data[1:]  # 出力(教師ラベル)
data_size = len(xs)

#///////////////////RNNに対応した「time_size」行列の学習用データを作成 開始部分/////////////////////////
x_time = np.empty((data_size - time_size, time_size, wordvec_size), dtype='f')
t_time = np.empty((data_size - time_size, time_size, vocab_size), dtype='f')

for i in range(data_size - time_size):
    for j in range(time_size):
        x_time[i, j, :] = xs[i + j]
        t_time[i, j, :] = ts[i + j]
#///////////////////RNNに対応した「time_size」行列の学習用データを作成 終了部分/////////////////////////

# 学習時に使用する変数
# max_iters = data_size // (batch_size * time_size)
max_iters = data_size // batch_size                # SIN波学習のデータ量として、「time_size」で割ってしまうと足りなくなる
time_idx = 0
total_loss = 0
loss_count = 0
ppl_list = []


# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# できるだけimportを外すため、classをコピペした箇所の「開始」部分
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

# GPUを定義しておく(コードのどこかでこの定義を参照しているらしいけど、PCにNVIDIA無いので、下記定義をするだけ)
GPU = False

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# optimizer.py の抜粋「開始」部分
# ---------------------------------------------------------------------------------------------------------------------------
class SGD:
    '''
    確率的勾配降下法(Stochastic Gradient Descent)
    '''
    def __init__(self, lr=0.01):
        self.lr = lr
        
    def update(self, params, grads):
        for i in range(len(params)):
            params[i] -= self.lr * grads[i]
# ---------------------------------------------------------------------------------------------------------------------------
# optimizer.py の抜粋「終了」部分
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# time_layers.py の抜粋「開始」部分
# ---------------------------------------------------------------------------------------------------------------------------
class TimeAffine:
    def __init__(self, W, b):
        self.params = [W, b]
        self.grads = [np.zeros_like(W), np.zeros_like(b)]
        self.x = None

    def forward(self, x):
        N, T, D = x.shape
        W, b = self.params

        rx = x.reshape(N*T, -1)
        out = np.dot(rx, W) + b
        self.x = x
        return out.reshape(N, T, -1)

    def backward(self, dout):
        x = self.x
        N, T, D = x.shape
        W, b = self.params

        dout = dout.reshape(N*T, -1)
        rx = x.reshape(N*T, -1)

        db = np.sum(dout, axis=0)
        dW = np.dot(rx.T, dout)
        dx = np.dot(dout, W.T)
        dx = dx.reshape(*x.shape)

        self.grads[0][...] = dW
        self.grads[1][...] = db

        return dx

class RNN:
    def __init__(self, Wx, Wh, b):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.cache = None

    def forward(self, x, h_prev):
        Wx, Wh, b = self.params
        t = np.dot(h_prev, Wh) + np.dot(x, Wx) + b
        h_next = np.tanh(t)

        self.cache = (x, h_prev, h_next)
        return h_next

    def backward(self, dh_next):
        Wx, Wh, b = self.params
        x, h_prev, h_next = self.cache

        dt = dh_next * (1 - h_next ** 2)
        db = np.sum(dt, axis=0)
        dWh = np.dot(h_prev.T, dt)
        dh_prev = np.dot(dt, Wh.T)
        dWx = np.dot(x.T, dt)
        dx = np.dot(dt, Wx.T)

        self.grads[0][...] = dWx
        self.grads[1][...] = dWh
        self.grads[2][...] = db

        return dx, dh_prev

class TimeRNN:
    def __init__(self, Wx, Wh, b, stateful=False):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.layers = None

        self.h, self.dh = None, None
        self.stateful = stateful

    def forward(self, xs):
        Wx, Wh, b = self.params
        N, T, D = xs.shape
        D, H = Wx.shape

        self.layers = []
        hs = np.empty((N, T, H), dtype='f')

        if not self.stateful or self.h is None:
            self.h = np.zeros((N, H), dtype='f')

        for t in range(T):
            layer = RNN(*self.params)
            self.h = layer.forward(xs[:, t, :], self.h)
            hs[:, t, :] = self.h
            self.layers.append(layer)

        return hs

    def backward(self, dhs):
        Wx, Wh, b = self.params
        N, T, H = dhs.shape
        D, H = Wx.shape

        dxs = np.empty((N, T, D), dtype='f')
        dh = 0
        grads = [0, 0, 0]
        for t in reversed(range(T)):
            layer = self.layers[t]
            dx, dh = layer.backward(dhs[:, t, :] + dh)
            dxs[:, t, :] = dx

            for i, grad in enumerate(layer.grads):
                grads[i] += grad

        for i, grad in enumerate(grads):
            self.grads[i][...] = grad
        self.dh = dh

        return dxs

    def set_state(self, h):
        self.h = h

    def reset_state(self):
        self.h = None


class TimeOutputWithLoss:
# このコードは「class TimeSoftmaxWithLoss」の代わりに実装する

    def __init__(self):
        self.cache = None

    def forward(self, xs, ts):
        N, T, D = xs.shape # ここでDは1

# RNNのタイプに合わせて「#1」か「#2」を選択する
#        # ------------------RNN many to many start---------------#1
#        loss = 0.5 * np.sum((xs - ts)**2)                        #1
#        loss /= N # 1データ分での誤差                              #1
#        # -------------------RNN many to many end----------------#1

        # ------------------RNN many to one start----------------#2
        loss = 0.5 * np.sum((xs[:, T-1, :] - ts[:, T-1, :])**2)  #2
        loss /= N # 1データ分での誤差                              #2
        # -------------------RNN many to one end-----------------#2
     
        self.cache = (ts, xs, (N, T, D))

        return loss

    def backward(self, dout=1):
        ts, xs, (N, T, D) = self.cache

# RNNのタイプに合わせて「#1」か「#2」を選択する
#        # ------------------RNN many to many start---------------#1
#        dout = xs - ts                                           #1
#        dout /= N                                                #1
#        # -------------------RNN many to many end----------------#1

        # ------------------RNN many to one start----------------#2
        dout = np.zeros([N, T, D], dtype='float')                #2
        dout[:, T-1, :] = xs[:, T-1, :] - ts[:, T-1, :]          #2
        # -------------------RNN many to one end-----------------#2
        
        return dout
# ---------------------------------------------------------------------------------------------------------------------------
# time_layers.py の抜粋「終了」部分
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# simple_rnnlm.py の抜粋「開始」部分
# ---------------------------------------------------------------------------------------------------------------------------
class SimpleRnnlm:
    def __init__(self, vocab_size, wordvec_size, hidden_size):
        V, D, H = vocab_size, wordvec_size, hidden_size
        rn = np.random.randn

        # 重みの初期化
#        embed_W = (rn(V, D) / 100).astype('f')                    # embedingレイヤは無効にする
        rnn_Wx = (rn(D, H) / np.sqrt(D)).astype('f')
        rnn_Wh = (rn(H, H) / np.sqrt(H)).astype('f')
        rnn_b = np.zeros(H).astype('f')
        affine_W = (rn(H, V) / np.sqrt(H)).astype('f')
        affine_b = np.zeros(V).astype('f')

        # レイヤの生成
        self.layers = [
#            TimeEmbedding(embed_W),                               # embedingレイヤは無効にする
            TimeRNN(rnn_Wx, rnn_Wh, rnn_b, stateful=True),
            TimeAffine(affine_W, affine_b)
#            Simple_TimeAffine(affine_W, affine_b)
        ]
#        self.loss_layer = TimeSoftmaxWithLoss()                   # TimeSoftmaxWithLossレイヤは無効にする
        self.loss_layer = TimeOutputWithLoss()
        self.rnn_layer = self.layers[0]                            # 「TimeEmbedding」を外したので「TimeRNN」を「self.rnn_layer」にするため「self.layers[1]」を「self.layers[0]」にした

        # すべての重みと勾配をリストにまとめる
        self.params, self.grads = [], []
        for layer in self.layers:
            self.params += layer.params
            self.grads += layer.grads

#------オリジナルコードに予測(predict)が無いので、LSTMのコードから持ってくる------
    def predict(self, xs):
        for layer in self.layers:
            xs = layer.forward(xs)
        return xs

    def forward(self, xs, ts):
        for layer in self.layers:
            xs = layer.forward(xs)
        loss = self.loss_layer.forward(xs, ts)
        return loss

    def backward(self, dout=1):
        dout = self.loss_layer.backward(dout)
        for layer in reversed(self.layers):
            dout = layer.backward(dout)
        return dout

    def reset_state(self):
        self.rnn_layer.reset_state()
# ---------------------------------------------------------------------------------------------------------------------------
# simple_rnnlm.py の抜粋「終了」部分
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#  できるだけimportを外すため、classをコピペした箇所の「終了」部分
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


# モデルの生成
model = SimpleRnnlm(vocab_size, wordvec_size, hidden_size)
optimizer = SGD(lr)

# ミニバッチの各サンプルの読み込み開始位置を計算
# jump = (corpus_size - 1) // batch_size
# offset = [i * jump for i in range(batch_size)]

for epoch in range(max_epoch):

    #---「common」の「trainer.py」から「class Trainer」よりデータをシャッフルする部分を抜粋---
    time_idx = 0                                 # インデックス追加部分
    # シャッフル
    idx = np.random.permutation(np.arange(data_size - time_size))
    x_shuffle = x_time[idx, ]
    t_shuffle = t_time[idx, ]
    #-----------------------------------------------------------------------------------
    
# /////////////////////////////////////////////////////////////////////////////////////////////////////////
    for iter in range(max_iters - 1):
        # ミニバッチの取得

        batch_x = np.empty((batch_size, time_size, wordvec_size), dtype='f')
        batch_t = np.empty((batch_size, time_size, vocab_size), dtype='f')

        for i in range(batch_size):
            batch_x[i, :, :] = x_shuffle[time_idx + i, ]
            batch_t[i, :, :] = t_shuffle[time_idx + i, ]
        time_idx += batch_size
# /////////////////////////////////////////////////////////////////////////////////////////////////////////

        # 勾配を求め、パラメータを更新
        loss = model.forward(batch_x, batch_t)
        model.backward()
        optimizer.update(model.params, model.grads)
        total_loss += loss
        loss_count += 1
    # エポックごとにパープレキシティの評価         「パープレキシティ」はここでは「ロス」として扱う
#    ppl = np.exp(total_loss / loss_count)
    ppl = total_loss
#    print('| epoch %d | perplexity %.2f'
#          % (epoch+1, ppl))
    ppl_list.append(float(ppl))
    total_loss, loss_count = 0, 0

#----------------予測部分開始-------------------------------------
# -- 予測 --
    # 順伝播 RNN層

# x_predict = xs[:]
x_predict = sin_y.reshape(-1, 1)                                       # x_predictのデータ構造をsin_yと同じにしている
x_predict.fill(0.0)
sin_y = np.sin(sin_x)                                                  # sin関数 なぜかここで再定義しないとsinデータを読み込まない
for i in range(time_size):
    x_predict[i, ] = sin_y[i, ]                                        # 「x_predict」の最初の「time_size」分SIN波データを組み込む

# y_predict = np.empty((batch_size,  wordvec_size, vocab_size), dtype='f')      
y_predict = np.empty((batch_size,  time_size, vocab_size), dtype='f')      
pred_batch_x = np.empty((batch_size, time_size, wordvec_size), dtype='f')
# batch_x = np.empty((batch_size, time_size, wordvec_size), dtype='f')

# 「batch_size」 ありきでコードが組まれているので、入力データを「batch_size」分、重複させて作る
for j in range(data_size - time_size + 1):
    for t in range(time_size):
        for i in range(batch_size):
            pred_batch_x[i, t, :] = x_predict[j + t, ]                               # データは時系列に作成、「batch_size」分、重複させている
    y_predict = model.predict(pred_batch_x) 
    x_predict[j + time_size, :] = y_predict[0, time_size-1, 0]
#-----------------予測部分終了------------------------------------
    
# グラフの描画 誤差の表示部分
x = np.arange(len(ppl_list))
plt.plot(x, ppl_list, color='c', label='train')
plt.xlabel('epochs')
plt.ylabel('loss')
plt.show()

# グラフの描画 学習結果の波形パターンの一致度合いを表示
td = ts.reshape(-1)
x_pre = x_predict[:-1]
x = np.arange(len(td))
# x = np.arange(len(x_predict))
# plt.plot(x, td, x_predict, label='train')
plt.plot(x, td, color='b', label='target')
plt.plot(x, x_pre, color='r', label='predict')
plt.xlabel('epochs')
plt.ylabel('sin(x)')
plt.show() 

実行結果は
です。

目次へ戻る


2:xp用のLSTMのSIN波コード

 # 完成版 Windows xp用 LSTMコード
# ゼロから作る Deep Learning2のP216、第5章「5.5.3 RNNLMの学習コード」でコード全体が見えるようにできるだけ「import」を外した
# プログラムの動作検証用にSINカーブを学習させるため変更したコード
# coding: utf-8
# import sys
# sys.path.append('C:\\kojin\\資料\\AI関連\\ゼロから作る Deep Learning\\ゼロから作る Deep Learning2\\deep-learning-from-scratch-2-master\\')
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
# from common.optimizer import SGD
# from dataset import ptb                 # このimportを有効にするには上記パス設定「sys.path.append('C:\\kojin\\AI関連\\・・・」が必要!
# from simple_rnnlm import SimpleRnnlm

np.random.seed(seed=773)                       # 発生する乱数を固定する(773 326 512 138)

# ハイパーパラメータの設定
batch_size = 10
time_size = 10  # Truncated BPTTの展開する時間サイズ

n_in = 1  # 入力層のニューロン数
n_mid = 45  # 中間層のニューロン数
n_out = 1  # 出力層のニューロン数

# できるだけオリジナルのコードに変更を加えないため、既存の変数に代入する
vocab_size = n_out
wordvec_size = n_in
hidden_size = n_mid

lr = 0.008
max_epoch = 301

# 学習データの読み込み
## -------------------学習用データ------------------------------------
# -- 訓練データの作成 --
sin_x = np.linspace(-2*np.pi, 2*np.pi, 101)  # -2πから2πまで
sin_y = np.sin(sin_x)                                            # sin関数 predict用で使用する(なんかバグがある、predict部分で再宣言必要)
sin_y_noise = np.sin(sin_x)  + 0.05*np.random.randn(len(sin_x))  # sin関数に乱数でノイズを加える
x_data = sin_y_noise.reshape(-1, 1)
t_data = sin_y_noise.reshape(-1, 1)
#------------------------------------------------------------------------------------------------


xs = x_data[:-1]  # 入力
ts = t_data[1:]  # 出力(教師ラベル)
data_size = len(xs)

#///////////////////RNNに対応した「time_size」行列の学習用データを作成 開始部分/////////////////////////
x_time = np.empty((data_size - time_size, time_size, wordvec_size), dtype='f')
t_time = np.empty((data_size - time_size, time_size, vocab_size), dtype='f')

for i in range(data_size - time_size):
    for j in range(time_size):
        x_time[i, j, :] = xs[i + j]
        t_time[i, j, :] = ts[i + j]
#///////////////////RNNに対応した「time_size」行列の学習用データを作成 終了部分/////////////////////////

# 学習時に使用する変数
# max_iters = data_size // (batch_size * time_size)
max_iters = data_size // batch_size                # SIN波学習のデータ量として、「time_size」で割ってしまうと足りなくなる
time_idx = 0
total_loss = 0
loss_count = 0
ppl_list = []


# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# できるだけimportを外すため、classをコピペした箇所の「開始」部分
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

# GPUを定義しておく(コードのどこかでこの定義を参照しているらしいけど、PCにNVIDIA無いので、下記定義をするだけ)
GPU = False

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# optimizer.py の抜粋「開始」部分
# ---------------------------------------------------------------------------------------------------------------------------
class SGD:
    '''
    確率的勾配降下法(Stochastic Gradient Descent)
    '''
    def __init__(self, lr=0.01):
        self.lr = lr
        
    def update(self, params, grads):
        for i in range(len(params)):
            params[i] -= self.lr * grads[i]
# ---------------------------------------------------------------------------------------------------------------------------
# optimizer.py の抜粋「終了」部分
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# functions.py の全部抜粋「開始」部分
# ---------------------------------------------------------------------------------------------------------------------------
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
# ---------------------------------------------------------------------------------------------------------------------------
# functions.py の全部抜粋「終了」部分
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# time_layers.py の抜粋「開始」部分
# ---------------------------------------------------------------------------------------------------------------------------
class TimeAffine:
    def __init__(self, W, b):
        self.params = [W, b]
        self.grads = [np.zeros_like(W), np.zeros_like(b)]
        self.x = None

    def forward(self, x):
        N, T, D = x.shape
        W, b = self.params

        rx = x.reshape(N*T, -1)
        out = np.dot(rx, W) + b
        self.x = x
        return out.reshape(N, T, -1)

    def backward(self, dout):
        x = self.x
        N, T, D = x.shape
        W, b = self.params

        dout = dout.reshape(N*T, -1)
        rx = x.reshape(N*T, -1)

        db = np.sum(dout, axis=0)
        dW = np.dot(rx.T, dout)
        dx = np.dot(dout, W.T)
        dx = dx.reshape(*x.shape)

        self.grads[0][...] = dW
        self.grads[1][...] = db

        return dx

class LSTM:
    def __init__(self, Wx, Wh, b):
        '''

        Parameters
        ----------
        Wx: 入力`x`用の重みパラーメタ(4つ分の重みをまとめる)
        Wh: 隠れ状態`h`用の重みパラメータ(4つ分の重みをまとめる)
        b: バイアス(4つ分のバイアスをまとめる)
        '''
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.cache = None

    def forward(self, x, h_prev, c_prev):
        Wx, Wh, b = self.params
        N, H = h_prev.shape

        A = np.dot(x, Wx) + np.dot(h_prev, Wh) + b

        f = A[:, :H]
        g = A[:, H:2*H]
        i = A[:, 2*H:3*H]
        o = A[:, 3*H:]

        f = sigmoid(f)
        g = np.tanh(g)
        i = sigmoid(i)
        o = sigmoid(o)

        c_next = f * c_prev + g * i
        h_next = o * np.tanh(c_next)

        self.cache = (x, h_prev, c_prev, i, f, g, o, c_next)
        return h_next, c_next

    def backward(self, dh_next, dc_next):
        Wx, Wh, b = self.params
        x, h_prev, c_prev, i, f, g, o, c_next = self.cache

        tanh_c_next = np.tanh(c_next)

        ds = dc_next + (dh_next * o) * (1 - tanh_c_next ** 2)

        dc_prev = ds * f

        di = ds * g
        df = ds * c_prev
        do = dh_next * tanh_c_next
        dg = ds * i

        di *= i * (1 - i)
        df *= f * (1 - f)
        do *= o * (1 - o)
        dg *= (1 - g ** 2)

        dA = np.hstack((df, dg, di, do))

        dWh = np.dot(h_prev.T, dA)
        dWx = np.dot(x.T, dA)
        db = dA.sum(axis=0)

        self.grads[0][...] = dWx
        self.grads[1][...] = dWh
        self.grads[2][...] = db

        dx = np.dot(dA, Wx.T)
        dh_prev = np.dot(dA, Wh.T)

        return dx, dh_prev, dc_prev


class TimeLSTM:
    def __init__(self, Wx, Wh, b, stateful=False):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.layers = None

        self.h, self.c = None, None
        self.dh = None
        self.stateful = stateful

    def forward(self, xs):
        Wx, Wh, b = self.params
        N, T, D = xs.shape
        H = Wh.shape[0]

        self.layers = []
        hs = np.empty((N, T, H), dtype='f')

        if not self.stateful or self.h is None:
            self.h = np.zeros((N, H), dtype='f')
        if not self.stateful or self.c is None:
            self.c = np.zeros((N, H), dtype='f')

        for t in range(T):
            layer = LSTM(*self.params)
            self.h, self.c = layer.forward(xs[:, t, :], self.h, self.c)
            hs[:, t, :] = self.h

            self.layers.append(layer)

        return hs

    def backward(self, dhs):
        Wx, Wh, b = self.params
        N, T, H = dhs.shape
        D = Wx.shape[0]

        dxs = np.empty((N, T, D), dtype='f')
        dh, dc = 0, 0

        grads = [0, 0, 0]
        for t in reversed(range(T)):
            layer = self.layers[t]
            dx, dh, dc = layer.backward(dhs[:, t, :] + dh, dc)
            dxs[:, t, :] = dx
            for i, grad in enumerate(layer.grads):
                grads[i] += grad

        for i, grad in enumerate(grads):
            self.grads[i][...] = grad
        self.dh = dh
        return dxs

    def set_state(self, h, c=None):
        self.h, self.c = h, c

    def reset_state(self):
        self.h, self.c = None, None


class TimeOutputWithLoss:
# このコードは「class TimeSoftmaxWithLoss」の代わりに実装する

    def __init__(self):
        self.cache = None

    def forward(self, xs, ts):
        N, T, D = xs.shape # ここでDは1

# RNNのタイプに合わせて「#1」か「#2」を選択する
#        # ------------------RNN many to many start---------------#1
#        loss = 0.5 * np.sum((xs - ts)**2)                        #1
#        loss /= N # 1データ分での誤差                              #1
#        # -------------------RNN many to many end----------------#1

        # ------------------RNN many to one start----------------#2
        loss = 0.5 * np.sum((xs[:, T-1, :] - ts[:, T-1, :])**2)  #2
        loss /= N # 1データ分での誤差                              #2
        # -------------------RNN many to one end-----------------#2
     
        self.cache = (ts, xs, (N, T, D))

        return loss

    def backward(self, dout=1):
        ts, xs, (N, T, D) = self.cache

# RNNのタイプに合わせて「#1」か「#2」を選択する
#        # ------------------RNN many to many start---------------#1
#        dout = xs - ts                                           #1
#        dout /= N                                                #1
#        # -------------------RNN many to many end----------------#1

        # ------------------RNN many to one start----------------#2
        dout = np.zeros([N, T, D], dtype='float')                #2
        dout[:, T-1, :] = xs[:, T-1, :] - ts[:, T-1, :]          #2
        # -------------------RNN many to one end-----------------#2
        
        return dout
# ---------------------------------------------------------------------------------------------------------------------------
# time_layers.py の抜粋「終了」部分
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# simple_rnnlm.py の抜粋「開始」部分
# ---------------------------------------------------------------------------------------------------------------------------
class SimpleRnnlm:
    def __init__(self, vocab_size=10000, wordvec_size=100, hidden_size=100):
        V, D, H = vocab_size, wordvec_size, hidden_size
        rn = np.random.randn

        # 重みの初期化
#        embed_W = (rn(V, D) / 100).astype('f')                    # embedingレイヤは無効にする
        lstm_Wx = (rn(D, 4 * H) / np.sqrt(D)).astype('f')
        lstm_Wh = (rn(H, 4 * H) / np.sqrt(H)).astype('f')
        lstm_b = np.zeros(4 * H).astype('f')
        affine_W = (rn(H, V) / np.sqrt(H)).astype('f')
        affine_b = np.zeros(V).astype('f')

        # レイヤの生成
        self.layers = [
#            TimeEmbedding(embed_W),                              # embedingレイヤは無効にする
            TimeLSTM(lstm_Wx, lstm_Wh, lstm_b, stateful=True),
            TimeAffine(affine_W, affine_b)
        ]

#        self.loss_layer = TimeSoftmaxWithLoss()                   # TimeSoftmaxWithLossレイヤは無効にする
        self.loss_layer = TimeOutputWithLoss()
        self.lstm_layer = self.layers[0]                           # 「TimeEmbedding」を外したので「TimeLSTM」を「self.lstm_layer」にするため「self.layers[1]」を「self.layers[0]」にした
                                                                   # 「rnn」を「lstm」に書き換え
        # すべての重みと勾配をリストにまとめる
        self.params, self.grads = [], []
        for layer in self.layers:
            self.params += layer.params
            self.grads += layer.grads

#------オリジナルコードに予測(predict)が無いので、LSTMのコードから持ってくる------
    def predict(self, xs):
        for layer in self.layers:
            xs = layer.forward(xs)
        return xs

    def forward(self, xs, ts):
        for layer in self.layers:
            xs = layer.forward(xs)
        loss = self.loss_layer.forward(xs, ts)
        return loss

    def backward(self, dout=1):
        dout = self.loss_layer.backward(dout)
        for layer in reversed(self.layers):
            dout = layer.backward(dout)
        return dout

    def reset_state(self):
        self.lstm_layer.reset_state()                              # 「rnn」を「lstm」に書き換え
# ---------------------------------------------------------------------------------------------------------------------------
# simple_rnnlm.py の抜粋「終了」部分
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#  できるだけimportを外すため、classをコピペした箇所の「終了」部分
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


# モデルの生成
model = SimpleRnnlm(vocab_size, wordvec_size, hidden_size)
optimizer = SGD(lr)

# ミニバッチの各サンプルの読み込み開始位置を計算
# jump = (corpus_size - 1) // batch_size
# offset = [i * jump for i in range(batch_size)]

for epoch in range(max_epoch):

    #---「common」の「trainer.py」から「class Trainer」よりデータをシャッフルする部分を抜粋---
    time_idx = 0                                 # インデックス追加部分
    # シャッフル
    idx = np.random.permutation(np.arange(data_size - time_size))
    x_shuffle = x_time[idx, ]
    t_shuffle = t_time[idx, ]
    #-----------------------------------------------------------------------------------
    
# /////////////////////////////////////////////////////////////////////////////////////////////////////////
    for iter in range(max_iters - 1):
        # ミニバッチの取得

        batch_x = np.empty((batch_size, time_size, wordvec_size), dtype='f')
        batch_t = np.empty((batch_size, time_size, vocab_size), dtype='f')

        for i in range(batch_size):
            batch_x[i, :, :] = x_shuffle[time_idx + i, ]
            batch_t[i, :, :] = t_shuffle[time_idx + i, ]
        time_idx += batch_size
# /////////////////////////////////////////////////////////////////////////////////////////////////////////

        # 勾配を求め、パラメータを更新
        loss = model.forward(batch_x, batch_t)
        model.backward()
        optimizer.update(model.params, model.grads)
        total_loss += loss
        loss_count += 1
    # エポックごとにパープレキシティの評価         「パープレキシティ」はここでは「ロス」として扱う
#    ppl = np.exp(total_loss / loss_count)
    ppl = total_loss
#    print('| epoch %d | perplexity %.2f'
#          % (epoch+1, ppl))
    ppl_list.append(float(ppl))
    total_loss, loss_count = 0, 0

#----------------予測部分開始-------------------------------------
# -- 予測 --
    # 順伝播 RNN層

# x_predict = xs[:]
x_predict = sin_y.reshape(-1, 1)                                       # x_predictのデータ構造をsin_yと同じにしている
x_predict.fill(0.0)
sin_y = np.sin(sin_x)                                                  # sin関数 なぜかここで再定義しないとsinデータを読み込まない
for i in range(time_size):
    x_predict[i, ] = sin_y[i, ]                                        # 「x_predict」の最初の「time_size」分SIN波データを組み込む

y_predict = np.empty((batch_size,  time_size, vocab_size), dtype='f')      
pred_batch_x = np.empty((batch_size, time_size, wordvec_size), dtype='f')
# batch_x = np.empty((batch_size, time_size, wordvec_size), dtype='f')

# 「batch_size」 ありきでコードが組まれているので、入力データを「batch_size」分、重複させて作る
for j in range(data_size - time_size + 1):
    for t in range(time_size):
        for i in range(batch_size):
            pred_batch_x[i, t, :] = x_predict[j + t, ]                               # データは時系列に作成、「batch_size」分、重複させている
    y_predict = model.predict(pred_batch_x) 
    x_predict[j + time_size, :] = y_predict[0, time_size-1, 0]
#-----------------予測部分終了------------------------------------
    
# グラフの描画 誤差の表示部分
x = np.arange(len(ppl_list))
plt.plot(x, ppl_list, color='c', label='train')
plt.xlabel('epochs')
plt.ylabel('loss')
plt.show()

# グラフの描画 学習結果の波形パターンの一致度合いを表示
td = ts.reshape(-1)
x_pre = x_predict[:-1]
x = np.arange(len(td))
# x = np.arange(len(x_predict))
# plt.plot(x, td, x_predict, label='train')
plt.plot(x, td, color='b', label='target')
plt.plot(x, x_pre, color='r', label='predict')
plt.xlabel('epochs')
plt.ylabel('sin(x)')
plt.show() 

実行結果は
です。

目次へ戻る


3:xp用のGRUのSIN波コード

 # 完成版 Windows xp用 GRUコード
# ゼロから作る Deep Learning2のP216、第5章「5.5.3 RNNLMの学習コード」でコード全体が見えるようにできるだけ「import」を外した
# プログラムの動作検証用にSINカーブを学習させるため変更したコード
# coding: utf-8
# import sys
# sys.path.append('C:\\kojin\\資料\\AI関連\\ゼロから作る Deep Learning\\ゼロから作る Deep Learning2\\deep-learning-from-scratch-2-master\\')
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
# from common.optimizer import SGD
# from dataset import ptb                 # このimportを有効にするには上記パス設定「sys.path.append('C:\\kojin\\AI関連\\・・・」が必要!
# from simple_rnnlm import SimpleRnnlm

np.random.seed(seed=7724)                       # 発生する乱数を固定する(7724)

# ハイパーパラメータの設定
batch_size = 10
time_size = 10  # Truncated BPTTの展開する時間サイズ

n_in = 1  # 入力層のニューロン数
n_mid = 45  # 中間層のニューロン数
n_out = 1  # 出力層のニューロン数

# できるだけオリジナルのコードに変更を加えないため、既存の変数に代入する
vocab_size = n_out
wordvec_size = n_in
hidden_size = n_mid

lr = 0.005
max_epoch = 501

# 学習データの読み込み
## -------------------学習用データ------------------------------------
# -- 訓練データの作成 --
sin_x = np.linspace(-2*np.pi, 2*np.pi, 101)  # -2πから2πまで
sin_y = np.sin(sin_x)                                            # sin関数 predict用で使用する(なんかバグがある、predict部分で再宣言必要)
sin_y_noise = np.sin(sin_x)  + 0.05*np.random.randn(len(sin_x))  # sin関数に乱数でノイズを加える
x_data = sin_y_noise.reshape(-1, 1)
t_data = sin_y_noise.reshape(-1, 1)
#------------------------------------------------------------------------------------------------


xs = x_data[:-1]  # 入力
ts = t_data[1:]  # 出力(教師ラベル)
data_size = len(xs)

#///////////////////RNNに対応した「time_size」行列の学習用データを作成 開始部分/////////////////////////
x_time = np.empty((data_size - time_size, time_size, wordvec_size), dtype='f')
t_time = np.empty((data_size - time_size, time_size, vocab_size), dtype='f')

for i in range(data_size - time_size):
    for j in range(time_size):
        x_time[i, j, :] = xs[i + j]
        t_time[i, j, :] = ts[i + j]
#///////////////////RNNに対応した「time_size」行列の学習用データを作成 終了部分/////////////////////////

# 学習時に使用する変数
# max_iters = data_size // (batch_size * time_size)
max_iters = data_size // batch_size                # SIN波学習のデータ量として、「time_size」で割ってしまうと足りなくなる
time_idx = 0
total_loss = 0
loss_count = 0
ppl_list = []


# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# できるだけimportを外すため、classをコピペした箇所の「開始」部分
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

# GPUを定義しておく(コードのどこかでこの定義を参照しているらしいけど、PCにNVIDIA無いので、下記定義をするだけ)
GPU = False

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# optimizer.py の抜粋「開始」部分
# ---------------------------------------------------------------------------------------------------------------------------
class SGD:
    '''
    確率的勾配降下法(Stochastic Gradient Descent)
    '''
    def __init__(self, lr=0.01):
        self.lr = lr
        
    def update(self, params, grads):
        for i in range(len(params)):
            params[i] -= self.lr * grads[i]
# ---------------------------------------------------------------------------------------------------------------------------
# optimizer.py の抜粋「終了」部分
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# functions.py の全部抜粋「開始」部分
# ---------------------------------------------------------------------------------------------------------------------------
def sigmoid(x):
    return 1 / (1 + np.exp(-x))
# ---------------------------------------------------------------------------------------------------------------------------
# functions.py の全部抜粋「終了」部分
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# time_layers.py の抜粋「開始」部分
# ---------------------------------------------------------------------------------------------------------------------------
class TimeAffine:
    def __init__(self, W, b):
        self.params = [W, b]
        self.grads = [np.zeros_like(W), np.zeros_like(b)]
        self.x = None

    def forward(self, x):
        N, T, D = x.shape
        W, b = self.params

        rx = x.reshape(N*T, -1)
        out = np.dot(rx, W) + b
        self.x = x
        return out.reshape(N, T, -1)

    def backward(self, dout):
        x = self.x
        N, T, D = x.shape
        W, b = self.params

        dout = dout.reshape(N*T, -1)
        rx = x.reshape(N*T, -1)

        db = np.sum(dout, axis=0)
        dW = np.dot(rx.T, dout)
        dx = np.dot(dout, W.T)
        dx = dx.reshape(*x.shape)

        self.grads[0][...] = dW
        self.grads[1][...] = db

        return dx

class GRU:
    def __init__(self, Wx, Wh, b):
        '''
        Parameters
        ----------
        Wx: 入力`x`用の重みパラーメタ(3つ分の重みをまとめる)
        Wh: 隠れ状態`h`用の重みパラメータ(3つ分の重みをまとめる)
        b: バイアス(3つ分のバイアスをまとめる)
        '''
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.cache = None

    def forward(self, x, h_prev):
        Wx, Wh, b = self.params
        H = Wh.shape[0]
        Wxz, Wxr, Wxh = Wx[:, :H], Wx[:, H:2 * H], Wx[:, 2 * H:]
        Whz, Whr, Whh = Wh[:, :H], Wh[:, H:2 * H], Wh[:, 2 * H:]
        bz, br, bh = b[:H], b[H:2 * H], b[2 * H:]

        z = sigmoid(np.dot(x, Wxz) + np.dot(h_prev, Whz) + bz)
        r = sigmoid(np.dot(x, Wxr) + np.dot(h_prev, Whr) + br)
        h_hat = np.tanh(np.dot(x, Wxh) + np.dot(r*h_prev, Whh) + bh)
        h_next = (1-z) * h_prev + z * h_hat

        self.cache = (x, h_prev, z, r, h_hat)

        return h_next

    def backward(self, dh_next):
        Wx, Wh, b = self.params
        H = Wh.shape[0]
        Wxz, Wxr, Wxh = Wx[:, :H], Wx[:, H:2 * H], Wx[:, 2 * H:]
        Whz, Whr, Whh = Wh[:, :H], Wh[:, H:2 * H], Wh[:, 2 * H:]
        x, h_prev, z, r, h_hat = self.cache

        dh_hat =dh_next * z
        dh_prev = dh_next * (1-z)

        # tanh
        dt = dh_hat * (1 - h_hat ** 2)
        dbh = np.sum(dt, axis=0)
        dWhh = np.dot((r * h_prev).T, dt)
        dhr = np.dot(dt, Whh.T)
        dWxh = np.dot(x.T, dt)
        dx = np.dot(dt, Wxh.T)
        dh_prev += r * dhr

        # update gate(z)
        dz = dh_next * h_hat - dh_next * h_prev
        dt = dz * z * (1-z)
        dbz = np.sum(dt, axis=0)
        dWhz = np.dot(h_prev.T, dt)
        dh_prev += np.dot(dt, Whz.T)
        dWxz = np.dot(x.T, dt)
        dx += np.dot(dt, Wxz.T)

        # rest gate(r)
        dr = dhr * h_prev
        dt = dr * r * (1-r)
        dbr = np.sum(dt, axis=0)
        dWhr = np.dot(h_prev.T, dt)
        dh_prev += np.dot(dt, Whr.T)
        dWxr = np.dot(x.T, dt)
        dx += np.dot(dt, Wxr.T)

        self.dWx = np.hstack((dWxz, dWxr, dWxh))
        self.dWh = np.hstack((dWhz, dWhr, dWhh))
        self.db = np.hstack((dbz, dbr, dbh))

        self.grads[0][...] = self.dWx
        self.grads[1][...] = self.dWh
        self.grads[2][...] = self.db

        return dx, dh_prev

class TimeGRU:
    def __init__(self, Wx, Wh, b, stateful=False):
        self.params = [Wx, Wh, b]
        self.grads = [np.zeros_like(Wx), np.zeros_like(Wh), np.zeros_like(b)]
        self.layers = None
        self.h, self.dh = None, None
        self.stateful = stateful

    def forward(self, xs):
        Wx, Wh, b = self.params
        N, T, D = xs.shape
        H = Wh.shape[0]
        self.layers = []
        hs = np.empty((N, T, H), dtype='f')

        if not self.stateful or self.h is None:
            self.h = np.zeros((N, H), dtype='f')

        for t in range(T):
            layer = GRU(*self.params)
            self.h = layer.forward(xs[:, t, :], self.h)
            hs[:, t, :] = self.h
            self.layers.append(layer)
        return hs

    def backward(self, dhs):
        Wx, Wh, b = self.params
        N, T, H = dhs.shape
        D = Wx.shape[0]

        dxs = np.empty((N, T, D), dtype='f')

        dh = 0
        grads = [0, 0, 0]
        for t in reversed(range(T)):
            layer = self.layers[t]
            dx, dh = layer.backward(dhs[:, t, :] + dh)
            dxs[:, t, :] = dx

            for i, grad in enumerate(layer.grads):
                grads[i] += grad

        for i, grad in enumerate(grads):
            self.grads[i][...] = grad

        self.dh = dh
        return dxs

    def set_state(self, h):
        self.h = h

    def reset_state(self):
        self.h = None

class TimeOutputWithLoss:
# このコードは「class TimeSoftmaxWithLoss」の代わりに実装する

    def __init__(self):
        self.cache = None

    def forward(self, xs, ts):
        N, T, D = xs.shape # ここでDは1

# RNNのタイプに合わせて「#1」か「#2」を選択する
#        # ------------------RNN many to many start---------------#1
#        loss = 0.5 * np.sum((xs - ts)**2)                        #1
#        loss /= N # 1データ分での誤差                              #1
#        # -------------------RNN many to many end----------------#1

        # ------------------RNN many to one start----------------#2
        loss = 0.5 * np.sum((xs[:, T-1, :] - ts[:, T-1, :])**2)  #2
        loss /= N # 1データ分での誤差                              #2
        # -------------------RNN many to one end-----------------#2
     
        self.cache = (ts, xs, (N, T, D))

        return loss

    def backward(self, dout=1):
        ts, xs, (N, T, D) = self.cache

# RNNのタイプに合わせて「#1」か「#2」を選択する
#        # ------------------RNN many to many start---------------#1
#        dout = xs - ts                                           #1
#        dout /= N                                                #1
#        # -------------------RNN many to many end----------------#1

        # ------------------RNN many to one start----------------#2
        dout = np.zeros([N, T, D], dtype='float')                #2
        dout[:, T-1, :] = xs[:, T-1, :] - ts[:, T-1, :]          #2
        # -------------------RNN many to one end-----------------#2
        
        return dout
# ---------------------------------------------------------------------------------------------------------------------------
# time_layers.py の抜粋「終了」部分
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
# simple_rnnlm.py の抜粋「開始」部分
# ---------------------------------------------------------------------------------------------------------------------------
class SimpleRnnlm:
    def __init__(self, vocab_size=10000, wordvec_size=100, hidden_size=100):
        V, D, H = vocab_size, wordvec_size, hidden_size
        rn = np.random.randn

        # 重みの初期化
#        embed_W = (rn(V, D) / 100).astype('f')                    # embedingレイヤは無効にする
        gru_Wx = (rn(D, 3 * H) / np.sqrt(D)).astype('f')                # 「lstm」を「gru」に書き換えつつ、「4」を「3」にした
        gru_Wh = (rn(H, 3 * H) / np.sqrt(H)).astype('f')                # 「lstm」を「gru」に書き換えつつ、「4」を「3」にした
        gru_b = np.zeros(3 * H).astype('f')                             # 「lstm」を「gru」に書き換えつつ、「4」を「3」にした
        affine_W = (rn(H, V) / np.sqrt(H)).astype('f')
        affine_b = np.zeros(V).astype('f')

        # レイヤの生成                                              # 「TimeLSTM」を「TimeGRU」に書き換え
        self.layers = [
#            TimeEmbedding(embed_W),                               # embedingレイヤは無効にする
            TimeGRU(gru_Wx, gru_Wh,gru_b, stateful=True),
            TimeAffine(affine_W, affine_b)
        ]

#        self.loss_layer = TimeSoftmaxWithLoss()                   # TimeSoftmaxWithLossレイヤは無効にする
        self.loss_layer = TimeOutputWithLoss()
        self.gru_layer = self.layers[0]                            # 「TimeEmbedding」を外したので「TimeGRU」を「self.gru_layer」にするため「self.layers[1]」を「self.layers[0]」にした
                                                                   # 「rnn」を「gru」に書き換え
        # すべての重みと勾配をリストにまとめる
        self.params, self.grads = [], []
        for layer in self.layers:
            self.params += layer.params
            self.grads += layer.grads

#------オリジナルコードに予測(predict)が無いので、LSTMのコードから持ってくる------
    def predict(self, xs):
        for layer in self.layers:
            xs = layer.forward(xs)
        return xs

    def forward(self, xs, ts):
        for layer in self.layers:
            xs = layer.forward(xs)
        loss = self.loss_layer.forward(xs, ts)
        return loss

    def backward(self, dout=1):
        dout = self.loss_layer.backward(dout)
        for layer in reversed(self.layers):
            dout = layer.backward(dout)
        return dout

    def reset_state(self):
        self.gru_layer.reset_state()                                    # 「rnn」を「gru」に書き換え
# ---------------------------------------------------------------------------------------------------------------------------
# simple_rnnlm.py の抜粋「終了」部分
# +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#  できるだけimportを外すため、classをコピペした箇所の「終了」部分
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
# ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


# モデルの生成
model = SimpleRnnlm(vocab_size, wordvec_size, hidden_size)
optimizer = SGD(lr)

# ミニバッチの各サンプルの読み込み開始位置を計算
# jump = (corpus_size - 1) // batch_size
# offset = [i * jump for i in range(batch_size)]

for epoch in range(max_epoch):

    #---「common」の「trainer.py」から「class Trainer」よりデータをシャッフルする部分を抜粋---
    time_idx = 0                                 # インデックス追加部分
    # シャッフル
    idx = np.random.permutation(np.arange(data_size - time_size))
    x_shuffle = x_time[idx, ]
    t_shuffle = t_time[idx, ]
    #-----------------------------------------------------------------------------------
    
# /////////////////////////////////////////////////////////////////////////////////////////////////////////
    for iter in range(max_iters - 1):
        # ミニバッチの取得

        batch_x = np.empty((batch_size, time_size, wordvec_size), dtype='f')
        batch_t = np.empty((batch_size, time_size, vocab_size), dtype='f')

        for i in range(batch_size):
            batch_x[i, :, :] = x_shuffle[time_idx + i, ]
            batch_t[i, :, :] = t_shuffle[time_idx + i, ]
        time_idx += batch_size
# /////////////////////////////////////////////////////////////////////////////////////////////////////////

        # 勾配を求め、パラメータを更新
        loss = model.forward(batch_x, batch_t)
        model.backward()
        optimizer.update(model.params, model.grads)
        total_loss += loss
        loss_count += 1
    # エポックごとにパープレキシティの評価         「パープレキシティ」はここでは「ロス」として扱う
#    ppl = np.exp(total_loss / loss_count)
    ppl = total_loss
#    print('| epoch %d | perplexity %.2f'
#          % (epoch+1, ppl))
    ppl_list.append(float(ppl))
    total_loss, loss_count = 0, 0

#----------------予測部分開始-------------------------------------
# -- 予測 --
    # 順伝播 RNN層

# x_predict = xs[:]
x_predict = sin_y.reshape(-1, 1)                                       # x_predictのデータ構造をsin_yと同じにしている
x_predict.fill(0.0)
sin_y = np.sin(sin_x)                                                  # sin関数 なぜかここで再定義しないとsinデータを読み込まない
for i in range(time_size):
    x_predict[i, ] = sin_y[i, ]                                        # 「x_predict」の最初の「time_size」分SIN波データを組み込む

y_predict = np.empty((batch_size,  time_size, vocab_size), dtype='f')      
pred_batch_x = np.empty((batch_size, time_size, wordvec_size), dtype='f')
# batch_x = np.empty((batch_size, time_size, wordvec_size), dtype='f')

# 「batch_size」 ありきでコードが組まれているので、入力データを「batch_size」分、重複させて作る
for j in range(data_size - time_size + 1):
    for t in range(time_size):
        for i in range(batch_size):
            pred_batch_x[i, t, :] = x_predict[j + t, ]                               # データは時系列に作成、「batch_size」分、重複させている
    y_predict = model.predict(pred_batch_x) 
    x_predict[j + time_size, :] = y_predict[0, time_size-1, 0]
#-----------------予測部分終了------------------------------------
    
# グラフの描画 誤差の表示部分
x = np.arange(len(ppl_list))
plt.plot(x, ppl_list, color='c', label='train')
plt.xlabel('epochs')
plt.ylabel('loss')
plt.show()

# グラフの描画 学習結果の波形パターンの一致度合いを表示
td = ts.reshape(-1)
x_pre = x_predict[:-1]
x = np.arange(len(td))
# x = np.arange(len(x_predict))
# plt.plot(x, td, x_predict, label='train')
plt.plot(x, td, color='b', label='target')
plt.plot(x, x_pre, color='r', label='predict')
plt.xlabel('epochs')
plt.ylabel('sin(x)')
plt.show() 

実行結果は
です。

目次へ戻る


以上です。



コメント

このブログの人気の投稿

ディープラーニングのお勉強体験記”17:LSTM数式導出”(リカレントニューラルネットワーク”RNN”を中心にバックプロパゲーション”BPTT”を数式を使って理解したい!)

ディープラーニングのお勉強体験記”18:LSTMコードと数式”(リカレントニューラルネットワーク”RNN”を中心にバックプロパゲーション”BPTT”を数式を使って理解したい!)