• トップ
  • ブログ一覧
  • 機械学習の要「誤差逆伝播学習法」を解説・実装してみる!
  • 機械学習の要「誤差逆伝播学習法」を解説・実装してみる!

    広告メディア事業部広告メディア事業部
    2019.06.06

    IT技術

    「誤差逆伝播学習法」とは?

    誤差逆伝播学習法(BP: Backpropagation)とは、ニューラルネットワークの学習法の1つで、今現在もっとも主流で強力な学習法を指します。

    その名の通り、ネットワークを誤差情報が逆伝播することから名前がつけられていますが、ちょっとそれだけでは分かりづらいですね。

    この記事では、誤差逆伝播学習法の仕組みとその実装を解説していきます。

    解説部では、少し数式が多いですが、ひとつひとつ丁寧に見ていけば必ず理解できると思います。

    また、誤差逆伝播学習法の考え方は、様々な学習方法に応用されている学習法なのでしっかりと理解しておきましょう!

    実行環境

    以下は筆者の実行環境です。

    今回も数値計算用のNumPyとグラフ描画用のmatplotlibを使います。

    1. Python 3.7.3
    2. NumPy 1.16.3
    3. matplotlib 3.0.3

    「誤差逆伝播学習法」実装の前準備

    ネットワークの初期化に関するもの

    実装を踏まえて解説していきますが、今回も、多層パーセプトロンの時と同じようにNetwork クラスを定義してオブジェクト指向で実装していきます。

    Networkクラスのコンストラクタ__init__() や重みの初期化関数init_weights() 、今回用いるIrisデータセットを読みこむ関数load_iris() は以下のようになります。

    1class Network:
    2    def __init__(self, *args):
    3        self.layers = list(args)  # 各層のニューロン数
    4        self.weights = ([])       # 各層間の重みベクトル
    5        self.patterns = ([])      # 入力パターンベクトル
    6        self.labels = ([])        # 教師ニューロンインデックス
    7        self.outputs = ([])       # 各ニューロンの出力値ベクトル
    8
    9    def init_weights(self, a=0.0, b=1.0):
    10        """
    11        重みを[a, b]の一様乱数で生成
    12        :param a: minimum = 0.0
    13        :param b: maximum = 1.0
    14        :return:
    15        """
    16        for i in range(len(self.layers)):
    17            if i + 1 >= len(self.layers):
    18                break
    19            self.weights.append((b - a) * np.random.rand(self.layers[i + 1], self.layers[i]) + a)
    20
    21    def load_iris(self):
    22        data = open('iris.data', 'r')
    23        lines = data.read().split()
    24        dataset = ([])
    25
    26        for line in lines:
    27            pattern = line.split(',')
    28            dataset.append(pattern)
    29        data.close()
    30
    31        for pattern in dataset:
    32            self.patterns.append(list(map(float, pattern[0:-1])))
    33            # setosaは0番目ニューロン、versicolorは1番目ニューロン、virginicaは2番目ニューロンが担当する
    34            if pattern[-1] == 'Iris-setosa':
    35                self.labels.append(0)
    36            elif pattern[-1] == 'Iris-versicolor':
    37                self.labels.append(1)
    38            else:
    39                self.labels.append(2)

    実装の前準備【形式ニューロン編】

    次の前準備として、形式ニューロンの処理に関するものを定義していきます。

    形式ニューロンは以下の図と式で表されるような性質を持っています。

    【図. 形式ニューロン】

    (1)i=i=1nwixi\displaystyle i = \sum_{i=1}^n{w_i x_i}
    (2)o=f(i)\displaystyle o = f(i)

    上の図と式の意味は、ある外部からの入力 xi{i=1,2,3,,n}x_i \{i=1,2,3,\cdots,n\} に対して、それぞれ重み wi{i=1,2,3,,n}w_i \{i=1,2,3,\cdots,n\} がかけられ、その和 ii がニューロンへの最終的な入力値になります。

    その後、その入力を引数とする活性化関数 f(x)f(x) の値がそのニューロンの最終的な出力値 oo となります。

    シグモイド関数

    形式ニューロンでは活性化関数はステップ関数でしたが、ここではシグモイド関数と呼ばれる活性化関数を用います。

    (3)f(x)=11+exp(ϵx)f(x)=\frac{1}{1+exp(-\epsilon x)}

    ここで ϵ\epsilon は、シグモイド関数の傾きと言い、関係性は以下のような図になります。

    なぜ、シグモイド関数を用いるかは、後で詳しく説明したいと思います!

    【図. シグモイド関数】

    実装

    とりあえず、これらの特性を実装してみます。

    1    @staticmethod
    2    def input_sum(inputs, weights):
    3        """
    4        前ニューロンからの入力和を返す関数.式(1)
    5        :param inputs:
    6        :param weights:
    7        :return inputs dot weights (float):
    8        """
    9        return np.dot(inputs, weights)
    10
    11    def output(self, inputs, weights):
    12        """
    13        ニューロンの出力を返す関数.式(2)
    14        :param inputs:
    15        :param weights:
    16        :return (0, 1):
    17        """
    18        return self.sigmoid(self.input_sum(inputs, weights))
    19
    20    def sigmoid(self, x):
    21        """
    22        シグモイド関数 (3)
    23        :param x:
    24        :return:
    25        """
    26        return 1.0/(1.0 + np.exp(-self.epsilon * x))

    このようになります!

    ここでは、シグモイド関数の傾きはクラスインスタンスとして定義しています。

    1class Network:
    2    epsilon = 1.0  # シグモイド関数の傾き

    「誤差逆伝播学習法」について解説!

    それでは、「誤差逆伝播学習法」について解説していきます。

    考え方自体はシンプルですが、実装するために序盤は式変形など数学的な内容が多いです。

    少し気合いが必要ですが頑張っていきましょう!

    誤差逆伝播学習法についてザックリ知ろう!

    まずは、誤差逆伝播学習法について大枠だけ理解しましょう。

    先ほども言ったように、誤差逆伝播学習法は、教師信号と実際と出力信号との間に生じる誤差情報を使ってネットワーク全体を学習していきます。

    【図. 誤差逆伝播学習法の概略】

    今までの学習法と違い、全ての重みの学習が可能で、その性能の良さから現在でも主流な学習法です。

    この記事では最終層(出力層)を第 mm 層とし、中間層を第 kk 層のように表記します。

    誤差関数の定義

    まずは、教師信号と実際の出力との誤差情報を示す、誤差関数を定義します。

    1. 誤差関数が大きいほど、理想状態とは遠い

    誤差関数が大きいほど理想状態とは遠いということを示し、学習の要と言えます。

    誤差逆伝播学習法では一般的に誤差関数は以下のような式で表されます。

    (4)E=12i=1nm(tioim)2E=\frac{1}{2}\sum_{i=1}^{n_m}(t_i-o^m_i)^2

    ここで、 oimo^m_i は出力層( 第mm層 ) ii 番目ニューロンの出力値で、 tit_i はそれに対応する教師信号を指します。

    1/21/2 が付いている理由は、後々計算を楽にするためだけなので、深い意味はありません。

    このような誤差関数を、「二乗誤差関数」と言います。

    1. 誤差関数が小さくなれば、学習ができている

    この誤差関数が小さくなれば、学習ができていると言えます。

    勾配降下法

    次に、先ほど定義した誤差関数を小さくするために、勾配降下法(または最急降下法)と呼ばれる手法をとります。

    勾配降下法のイメーシとしては、以下のような図がよく用いられます。

    【図. 勾配降下法の概略図】

    上の図で示すように、最小化させたい誤差関数の傾き(勾配)を計算し、その傾きの大きさとは逆方向に重みを調整すれば、誤差関数の値を小さくできます。

    「誤差逆伝播学習法」の重みの更新式

    したがって、誤差逆伝播学習法では、「重みの更新式」は以下のように定義します。

    (5)Δwi,jk1,k=ηEwi,jk1,k\Delta w_{i,j}^{k-1,k}=-\eta\frac{\partial E}{\partial w_{i,j}^{k-1,k}}

    パっと見ると複雑そうですが、そんなに難しくありません。

    ここで wi,jk1,kw_{i,j}^{k-1,k} は、第 k1k-1ii 番目ニューロンと、第 kkjj 番目ニューロンとの間の重みです。

    【図. 重みの見方】

    つまり式(5)は、現在の重みで誤差関数を偏微分して得られた傾きとは、逆方向に重みを更新している式を表しています。

    ちなみに η\eta は学習率で、通常0.1や0.01などの小さな値を使います。

    これは学習の進行速度を表しています。

    しかし式(5)の形では、まだ実装するには難しそうです。

    そもそも EE は、出力値 oo の関数なので、重みで偏微分できません。

    では、どのように計算したらよいでしょうか?

    式変形をする

    ここから少しややこしく、複雑になっていきますが、順を追ってゆっくり理解していきましょう。

    まず、式(5)の右辺は、以下のように変形してみます(連鎖率)。

    (6)Ewi,jk1,k=Eiikiikwi,jk1,k\frac{\partial E}{\partial w_{i,j}^{k-1,k}}=\frac{\partial E}{\partial i_{i}^{k}}\cdot\frac{\partial i_{i}^{k}}{\partial w_{i,j}^{k-1,k}}

    ここで iiki_{i}^{k} は、第 kk層目jj 番目ニューロンの入力値です。

    さらに今出てきた、式(6)右辺について考えてみましょう。

    まずは、入力値の偏微分の部分については、以下のように簡単な形に導出できます。

    (7)iikwi,jk1,kamp;=l=1nk1wi,lk1,kolk1wi,jk1,kamp;=ojk1\begin{align}\frac{\partial i_{i}^{k}}{\partial w_{i,j}^{k-1,k}}& =\frac{\partial \sum_{l=1}^{n_{k-1}}{w_{i,l}^{k-1,k}o_l^{k-1}}}{\partial w_{i,j}^{k-1,k}} \\ & = o^{k-1}_j\end{align}

    問題は、式(6)右辺の前半です。

    ここで一旦以下のように、新たに δ\delta という変数を定義してみます。

    (8)δik=Eiik \delta_i^{k} = -\frac{\partial E}{\partial i_{i}^{k}}

    このように定義したことにより、式(5)は、以下のようなジンプルな形で一旦書き換えることができます。

    (9)Δwi,jk1,k=ηδikojk1\Delta w_{i,j}^{k-1,k}=\eta\delta_i^k o^{k-1}_j

    あとは、この δ\delta が解決できれば実装ができそうです!

    もう少し頑張っていきましょう。

    δについて

    この δ\delta の導出が、「誤差逆伝播学習法の要」と言えます。

    まず、δ\delta について、連鎖率を使って分解してみます。

    (10)δikamp;=Eiikamp;=Eoikoikiik\begin{align}\delta^k_i &= -\frac{\partial E}{\partial i^k_i} \\ &= -\frac{\partial E}{\partial o^k_i} \cdot \frac{\partial o^k_i}{\partial i^k_i}\end{align}

    ここで、式(10)右辺の後半については、

    (11)oikiik=f(iik)iik=f(iik)\frac{\partial o^k_i}{\partial i^k_i} = \frac{\partial f(i^k_i)}{\partial i^k_i}=f’(i^k_i)

    とシンプルな活性化関数の導関数になります。

    ですが、重要なのは、 Eoik\frac{\partial E}{\partial o^k_i} の部分です。

    ここで以下の2パターンの場合で導出方法が異なってきます。

    1. 最終層(第 mm 層)のとき:δim\delta^m_i
    2. 中間層(第 kk 層)のとき:δik\delta^k_i

    最終層のとき

    最終層(第 mm 層)のときは、シンプルです。

    (4)E=12i=1nm(tioim)2E=\frac{1}{2}\sum_{i=1}^{n_m}(t_i-o^m_i)^2

    ですので、

    (12)Eoim=oimti\frac{\partial E}{\partial o^m_i}=o^m_i - t_i

    となります。

    したがって、δ\delta は、以下のようになります。

    (13)δim=(oimti)f(iik)\delta^m_i=-(o^m_i - t_i)f’(i^k_i)

    中間層のとき

    中間層(第 kk 層)のときは、少し工夫が必要です。

    Eoik-\frac{\partial E}{\partial o^k_i} が中間層の時の δ\delta ですが、EE には oik o^k_i が含まれていないのでこのままでは偏微分不可能です。

    そこで、また「連鎖率」を使って式変形を行なっていきます。

    (14)Eoikamp;=l=1nk+1(Eilk+1ilk+1oik)amp;=l=1nk+1(Eilk+1(h=1nk+1wl,hk,k+1ohk)oik)amp;=l=1nk+1(Eilk+1wl,ik,k+1)amp;=l=1nk+1(δlk+1wl,ik,k+1)\begin{align}\frac{\partial E}{\partial o^k_i} &= \sum_{l=1}^{n_{k+1}}(\frac{\partial E}{\partial i^{k+1}_l}\cdot \frac{\partial i^{k+1}_l}{\partial o^k_i}) \\ &= \sum_{l=1}^{n_{k+1}}(\frac{\partial E}{\partial i^{k+1}_l}\cdot \frac{\partial (\sum_{h=1}^{n_{k+1}}w_{l,h}^{k,k+1}o_h^k)}{\partial o^k_i}) \\ &= \sum_{l=1}^{n_{k+1}}(\frac{\partial E}{\partial i^{k+1}_l}w_{l,i}^{k,k+1}) \\ &= -\sum_{l=1}^{n_{k+1}}(\delta_l^{k+1}w_{l,i}^{k,k+1})\end{align}

    このように少し複雑ですが変形ができます。

    したがって中間層(第kk層)の時の δ\delta は、以下のようになります。

    (15)δik=f(iik)l=1nk+1(δlk+1wl,ik,k+1)\delta^k_i = f’(i_i^k)\sum_{l=1}^{n_{k+1}}(\delta_l^{k+1}w_{l,i}^{k,k+1})

    ここで重要なのは、最後に得られた式で、一つ後ろの層の情報δlk+1\delta_l^{k+1}が含まれていることです。

    すなわち、最終層で計算した誤差情報 δ\delta が連鎖的に入力層側に伝わっていくことがわかります。

    これが「誤差逆伝播学習法」という名前の由縁です。

    誤差逆伝播学習法の重み更新式

    それでは、まとめると、誤差逆伝播学習法の重み更新式は以下のようになります。

    (16)Δwi,jk1,k=ηδikojk1\Delta w^{k-1,k}_{i,j}=\eta\delta^k_io^{k-1}_j
    1. 最終層(第mm層)のとき
      δim=(oimti)f(iik)\delta^m_i=-(o^m_i - t_i)f’(i^k_i)
    2. 中間層(第kk層)のとき
      δik=f(iik)l=1nk+1(δlk+1wl,ik,k+1)\delta^k_i = f’(i_i^k)\sum_{l=1}^{n_{k+1}}(\delta_l^{k+1}w_{l,i}^{k,k+1})

    活性化関数(シグモイド関数)の微分

    解説の最後に「活性化関数(シグモイド関数)の微分」を考えます。

    ここで、「なぜステップ関数ではダメだったのか」をお話します。

    それは、ずばり不連続な関数(微分不可能)だからです。

    また、f(x)=xf(x)=x のような単純な線形関数でもよいのですが、勾配=傾きなので常に一定になってしまい不適切です。

    そこで、今回用いるようなシグモイド関数を使っているのです。

    他にも、tanh(x)tanh(x)(ハイパボリックタンジェント)などもよく使われます。

    シグモイド関数を微分すると以下のようになります。

    f(x)=ϵ(1f(x))f(x)f’(x)=\epsilon(1-f(x))f(x)

    このように微分するとまた中にシグモイド関数が出てくるので、無限に微分が可能です。

    これがシグモイド関数の良いところです。

    シグモイド関数を使う場合の誤差逆伝播学習法の重み更新式

    したがって、シグモイド関数を使う場合の誤差逆伝播学習法の重み更新式は、

    (16’)Δwi,jk1,k=ηδikojk1\Delta w^{k-1,k}_{i,j}=\eta\delta^k_io^{k-1}_j
    1. 最終層(第mm層)のとき
      δimamp;=ϵ(oimti)(1f(iim))f(iim)amp;=ϵ(oimti)(1oim)oim\begin{align}\delta^m_i &=-\epsilon(o^m_i - t_i)(1-f(i^m_i))f(i^m_i) \\ &= -\epsilon(o^m_i - t_i)(1-o^m_i)o^m_i\end{align}
    2. 中間層(第kk層)のとき
      δikamp;=ϵ(1f(iik))f(iik)l=1nk+1(δlk+1wl,ik,k+1)amp;=ϵ(1oik)oikl=1nk+1(δlk+1wl,ik,k+1)\begin{align}\delta^k_i &= \epsilon(1-f(i^k_i))f(i^k_i)\sum_{l=1}^{n_{k+1}}(\delta_l^{k+1}w_{l,i}^{k,k+1}) \\ &= \epsilon(1-o^k_i)o^k_i\sum_{l=1}^{n_{k+1}}(\delta_l^{k+1}w_{l,i}^{k,k+1}) \end{align}

    以上が誤差逆伝播学習法の学習法導出になります。

    お疲れ様でした!

    「誤差逆伝播学習法」を実装する!

    それでは、誤差逆伝播学習法を実際に実装していきたいと思います。

    誤差逆伝播学習法は、導出は複雑なものの、アルゴリズム自体はいたって簡単です。

    アルゴリズム

    1. 入力パターンをネットワークに順伝播して出力値を得る
       ↓
    2. 出力値と教師信号を用いて、 δ\delta を計算
       ↓
    3. δ\delta を用いて重み更新量を出力層側から計算
       ↓
    4. 重みを更新
       ↓
    5. 学習終了基準を満たしていなければ、1. に戻る

    実装例

    今回は、学習率減衰というものを導入して、学習が進むごとに減衰させることにしてみます。

    学習率減衰は、適切に設定をすれば収束を早める効果があります。

    また、最後に、学習途中の誤答率をプロットするようにしています。

    ちなみに、 δ\delta を計算する関数はcalc_delta() で、誤差逆伝播の関数はbackward() としています。

    教師信号は、以前は0や1を使っていましたが、シグモイド関数では重みが発散しないように0.1や0.9を代わりに用いることが多いです。

    1import numpy as np
    2import matplotlib.pyplot as plt
    3
    4
    5class Network:
    6    rate = 0.1     # 学習率
    7    decay = 0.1    # 学習率減衰
    8    per = 50       # 何エポックごとに学習率を減衰させるか
    9
    10    epsilon = 1.0  # シグモイド関数の傾き
    11
    12    def __init__(self, *args):
    13        self.layers = list(args)  # 各層のニューロン数
    14        self.weights = ([])       # 各層間の重みベクトル
    15        self.patterns = ([])      # 入力パターンベクトル
    16        self.labels = ([])        # 教師ニューロンインデックス
    17        self.outputs = ([])       # 各ニューロンの出力値ベクトル
    18
    19    def init_weights(self, a=0.0, b=1.0):
    20        """
    21        重みを[a, b]の一様乱数で生成
    22        :param a: minimum = 0.0
    23        :param b: maximum = 1.0
    24        :return:
    25        """
    26        for i in range(len(self.layers)):
    27            if i + 1 >= len(self.layers):
    28                break
    29            self.weights.append((b - a) * np.random.rand(self.layers[i + 1], self.layers[i]) + a)
    30
    31    def load_iris(self):
    32        data = open('iris.data', 'r')
    33        lines = data.read().split()
    34        dataset = ([])
    35
    36        for line in lines:
    37            pattern = line.split(',')
    38            dataset.append(pattern)
    39        data.close()
    40
    41        for pattern in dataset:
    42            self.patterns.append(list(map(float, pattern[0:-1])))
    43            # setosaは0番目ニューロン、versicolorは1番目ニューロン、virginicaは2番目ニューロンが担当する
    44            if pattern[-1] == 'Iris-setosa':
    45                self.labels.append(0)
    46            elif pattern[-1] == 'Iris-versicolor':
    47                self.labels.append(1)
    48            else:
    49                self.labels.append(2)
    50
    51    @staticmethod
    52    def input_sum(inputs, weights):
    53        """
    54        前ニューロンからの入力和を返す関数.式(1)
    55        :param inputs:
    56        :param weights:
    57        :return inputs dot weights (float):
    58        """
    59        return np.dot(inputs, weights)
    60
    61    def output(self, inputs, weights):
    62        """
    63        ニューロンの出力を返す関数.式(2)
    64        :param inputs:
    65        :param weights:
    66        :return (0, 1):
    67        """
    68        return self.sigmoid(self.input_sum(inputs, weights))
    69
    70    def sigmoid(self, x):
    71        """
    72        シグモイド関数 (3)
    73        :param x:
    74        :return:
    75        """
    76        return 1.0/(1.0 + np.exp(-self.epsilon * x))
    77
    78    def forward(self, pattern):
    79        """
    80        順方向処理をする関数.
    81        :param pattern:
    82        :return 出力層ニューロンの出力値ベクトル [0,1]:
    83        """
    84        self.outputs.clear()  # まず出力値情報をクリア
    85
    86        out = ([])  # 前層ニューロンの出力ベクトル
    87        self.outputs.append(pattern)  # まず入力層ニューロンの出力(入力パターン)を追加
    88
    89        for layer in range(len(self.layers)):
    90            out = ([])
    91            if layer == 1:  # 第1中間層ならば入力パターンが前層ニューロンの出力となる
    92                for n in range(self.layers[layer]):
    93                    out.append(self.output(pattern, self.weights[layer-1][n]))
    94
    95                self.outputs.append(out)  # 出力値を追加
    96
    97            elif layer > 1:  # 第1中間層以降の層では前層の出力を使う
    98                for n in range(self.layers[layer]):
    99                    out.append(self.output(self.outputs[-1], self.weights[layer-1][n]))
    100
    101                self.outputs.append(out)  # 出力値を追加
    102
    103        return out
    104
    105    def backward(self, pattern):
    106        """
    107        誤差逆伝播学習法の大枠
    108        :param pattern:
    109        :return:
    110        """
    111        deltas = self.calc_delta(pattern)  # δを計算
    112        for l in reversed(range(1, len(self.layers))):  # ネットワークを逆順処理していく
    113            for j, jj in enumerate(self.outputs[l]):
    114                for i, ii in enumerate(self.outputs[l-1]):
    115                    self.weights[l-1][j][i] += self.rate * deltas[l-1][j] * ii  # 重みを更新
    116
    117    def calc_delta(self, pattern):
    118        """
    119        δを計算する関数
    120        :param pattern: パターンインデックス
    121        :return: δベクトル
    122        """
    123        teacher = ([0.1, 0.1, 0.1])  # 教師ニューロンを作成
    124        teacher[self.labels[pattern]] = 0.9  # ラベルに該当する教師ニューロンの出力を0.9125        deltas = ([])  # 全ニューロンのδ
    126
    127        for l in reversed(range(1, len(self.layers))):  # ネットワークを逆順処理していく
    128            tmp_delta = ([])
    129            for j in range(self.layers[l]):
    130                if l == len(self.layers) - 1:  # 最終層ならば
    131
    132                    delta = self.epsilon * \
    133                            (teacher[j] - self.outputs[l][j]) * \
    134                            self.outputs[l][j] * (1 - self.outputs[l][j])
    135
    136                    tmp_delta.append(delta)
    137
    138                else:  # 最終層以外
    139                    t_weights = np.array(self.weights[l]).T
    140                    delta = self.epsilon * \
    141                            self.outputs[l][j] * (1 - self.outputs[l][j]) * \
    142                            np.dot(deltas[-1], t_weights[j])
    143
    144                    tmp_delta.append(delta)
    145
    146            deltas.append(tmp_delta)
    147
    148        return deltas[::-1]  # δは逆順で返す
    149
    150    def train(self, epoch):
    151        """
    152        訓練関数
    153        :param epoch:
    154        :return:
    155        """
    156        errors = ([])
    157        for e in range(epoch):  # 最大学習回数で回す
    158            error = 100 - self.test()
    159            for p, pat in enumerate(self.patterns):  # 入力パターンごとに回す
    160
    161                self.forward(pat)  # 順伝播処理で出力値を計算
    162
    163                self.backward(p)  # 逆伝播で重みを更新
    164
    165            print(str(e+1) + ' / ' + str(epoch) + ' epoch.' + ' [ error: ' + str(error) + ' ]')
    166
    167            if (e+1) % self.per == 0:  # 学習率減衰
    168                self.rate *= self.decay
    169
    170            errors.append(error)  # 不正解率
    171
    172        #  不正解立を描画
    173        plt.xlabel('epochs')
    174        plt.ylabel('error (%)')
    175        plt.plot(errors)
    176        plt.savefig('error.png', dpi=300)
    177
    178    def test(self):
    179        """
    180        テスト関数
    181        :return: accuracy (%)
    182        """
    183        correct = 0
    184        for p in range(len(self.patterns)):
    185            self.forward(self.patterns[p])
    186            max = 0
    187            ans = -1
    188            # 一番出力値の高いニューロンを取ってくる
    189            for o, out in enumerate(self.outputs[len(self.layers)-1]):
    190                if max < out:
    191                    max = out
    192                    ans = o
    193            # もしそのニューロンの番号とラベルの番号があっていれば正解!
    194            if ans == self.labels[p]:
    195                correct += 1
    196
    197        accuracy = correct / len(self.patterns) * 100
    198        return accuracy
    199
    200
    201if __name__ == '__main__':
    202    net = Network(4, 20, 3)
    203    net.init_weights(-1.0, 1.0)
    204    net.load_iris()
    205    net.train(300)
    206    acc = net.test()
    207    print('Accuracy: ' + str(acc) + '%')

    いざ、実行!

    それでは、実際に上記コードを実行してみましょう。

    11 / 300 epoch. [ error: 66.66666666666667 ]
    22 / 300 epoch. [ error: 66.66666666666667 ]
    33 / 300 epoch. [ error: 66.66666666666667 ]
    44 / 300 epoch. [ error: 66.66666666666667 ]
    55 / 300 epoch. [ error: 66.66666666666667 ]
    66 / 300 epoch. [ error: 59.333333333333336 ]
    77 / 300 epoch. [ error: 43.333333333333336 ]
    88 / 300 epoch. [ error: 34.0 ]
    99 / 300 epoch. [ error: 34.0 ]
    1010 / 300 epoch. [ error: 34.0 ]
    11# --- 省略 --- #
    12299 / 300 epoch. [ error: 2.666666666666657 ]
    13300 / 300 epoch. [ error: 2.666666666666657 ]
    14Accuracy: 97.33333333333334%

     

    【図. 誤答率の遷移】

    見事学習がうまくいっていることが分かります!

    また安定感もよく、高確率で高精度を得られることも実際に動かしてみるとわかります。

    実際に10試行やってみると平均精度97.4%でした。

    もう少しパラメータなどを工夫してみると良い結果が得られるかもしれません。

    また、Irisデータセットは普通98%あたりが限度なので、別のデータセットを使ってみるのも良いですね。

    こちらの記事もオススメ!

    featureImg2020.07.28機械学習 特集知識編人工知能・機械学習でよく使われるワード徹底まとめ!機械学習の元祖「パーセプトロン」とは?【人工知能】ニューラルネ...

    featureImg2020.07.17ライトコード的「やってみた!」シリーズ「やってみた!」を集めました!(株)ライトコードが今まで作ってきた「やってみた!」記事を集めてみました!※作成日が新し...

    広告メディア事業部

    広告メディア事業部

    おすすめ記事