質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.61%

シンプルな多層パーセプトロンにおける役に立たない中間層のニューロンの再利用について

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 899

picker413

score 18

 前提・実現したいこと

以前はscikit-learnなどを使っていましたが、現在研究テーマのためにライブラリーを用いらずにシンプルな多層パーセプトロンを誤差逆伝搬によって学習させています。

深層学習(MLP出版)やゼロから作るDeepLeaningなどを参考に作ったプログラムに試しにorタスクを学習させてみたのですが、学習はできたものの中間層のニューロンを10個ほど用意しておかないと学習が進まないという問題に直面しました。

自分で各ニューロンの出力を確認してみると、適当に重みを割り振った時に有用な中間層のニューロンがあった場合学習は進みますが、なかった場合、入力に対する中間層の出力は一向に変化せず、使えないニューロンはいつまでたっても使えないままという状態です。

機械学習を学び始めて数か月しかたっていないのでよくわからないですが、中間層の状態に有用なニューロンがなければ大きな誤差の勾配が伝わり有用なニューロンが現れると思っています。しかし、今のネットワークでは無益なニューロンは無益なままです。orのタスクは理論的には2つのニューロン(そもそも中間層が必要ない)でいいと思うので3,4個中間層のニューロンがあればいいと思うのですが・・・

今後はもっと複雑なタスクをやらせすつもりですし、RNN化するつもりなので重みの初期値に頼る今の状態は改善したいです。

ちなみに、学習率は固定で0.1~0.01を試したのですがダメでした。

こういう風にすればいいという漠然とした意見でもいいので、アドバイスを頂ければと思います。

コードはここに上げてあります。

もし、読んでいただけるのでしたら、これ以外のもんだでも突っ込んでいただけたら幸いです。

 該当のソースコード

ネットワークの定義

class test_network:
    def __init__(self, I, H, O, W01_size, W12_size):
        # 重みとバイアスの定義
        W01 = W01_size*np.random.rand(I, H)-W01_size/2
        W12 = W12_size*np.random.rand(H, O)-W12_size/2
        b1 = np.zeros(H)
        b2 = np.zeros(O)

        # モデルの生成
        self.Seccond_layer = BPneuron(W01, b1)
        self.Third_layer = BPneuron(W12, b2)
        self.test_loss_layer = Loss()

        self.loss_memo = []
        self.dx_memo = np.empty(0)
        self.bo_memo = np.empty(0)
        self.dbo_memo = np.empty(0)
        self.W12_memo = np.empty(0)

    def traning(self, test_data, target_data, epoch):
        # 学習に使う配列の決定
        for i in range(epoch):
            test_number = np.random.randint(target_data.shape[0])
            x = np.array([test_data[test_number]])
            t = np.array([target_data[test_number]])

            out1, container1 = self.Seccond_layer.forward(x)
            out2, container2 = self.Third_layer.forward(out1)
            W, b, x, y, z = container1
            W, b, x, y, z = container2

            loss = self.test_loss_layer.forward(out2, t)
            dout = self.test_loss_layer.backward()
            if i % 500 == 0:
                print("loss:", dout, "epoch:", i)
                print("_________")
                print("b:", b)
                print("_________")

            if i % 5000 == 0:
                clear_output()

            self.loss_memo.append(dout[0][0])
            dx, containe = self.Third_layer.backward(dout)
            dinput, containe = self.Seccond_layer.backward(dx)
            self.dx_memo = np.append(self.dx_memo, dx)
            self.bo_memo = np.append(self.bo_memo, b)
            self.W12_memo = np.append(self.W12_memo, W)

        return self.loss_memo

    def dx(self):
        return self.dx_memo

    def bo(self):
        return self.bo_memo

    def W_12(self):
        return self.W12_memo


各層の定義

lr = 0.1
class Loss:
    def __init__(self):
        self.Loss = None
        self.dout = None

    def forward(self, out, t):
        self.Loss = 1/2 * np.sum((out - t)**2)
        self.dout = out - t
        return self.Loss

    def backward(self):
        return self.dout


class BPneuron:
    def __init__(self, W, b):
        # 引数として受けた重みとバイアスをself.aramsに格納
        self.params = [W, b]
        # 更新前に勾配をまとめてオプティマイザーに送るための入れ物(中身はparamsに対応している必要あり)
        self.grads = [np.zeros_like(W), np.zeros_like(b)]
        # クラス外へ中身を持っていくための入れ物
        self.container = np.empty(0)

    def forward(self, x):
        # クラスの初期化時に格納した重みとバイアスの取り出し
        W, b = self.params
        # yはニューロン内部の値
        y = np.dot(x, W)+b
        # Zが出力
        z = sigmoid(y)
        self.container = [W, b, x, y, z]
        return z, self.container

    def backward(self, dz):
        W, b, x, y, z = self.container
        # 出力部の逆伝搬(シグモイド版)
        dy = sigmoid_back(z, dz)
        db = dy
        dW = np.dot(x.T, dy)
        dx = np.dot(dy, W.T)

        # self.gradsに更新に行かう勾配を格納
        self.grads[0][...] = dW
        self.grads[1][...] = db

        # オプティマイザーによりself.paramsの値を更新
        self.params = optimizer_SGD(lr, self.params, self.grads)
        # すべての結果をself.containerに格納
        self.container = [dy, db, dW, dx]

        return dx, self.container


その他の関数

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


def sigmoid_grad(z):
    return sigmoid(z) * (1 - sigmoid(z))


def sigmoid_back(z, dz):
    return dz*sigmoid(z) * (1 - sigmoid(z))

def optimizer_SGD(lr, params, grads):
    for i in range(len(params)):
        params[i] -= lr * grads[i]

    return params


よろしくお願いします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

0

学習が進むかどうか逆伝搬時に勾配の情報が正しく伝わるかどうかなので、初期化の仕方が結構重要だと思います。

一様分布でなく、一般的に広く使われている Xavier や He の初期化を試してみればどうでしょうか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/19 16:32

    初期化に関するアルゴリズムは全く知らないので紹介していただいたXavier や Heを勉強してみようと思います。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.61%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る