ニューラル ネットワークのアルゴリズムとコードの概要

記事の内容

  • パーセプトロン (パーセプトロン)
  • バックプロパゲーションアルゴリズム (Back Propagation アルゴリズム)
  • RBF (Radial Basis Function、Radial Basis Function) ネットワーク:隠れ層ニューロンの活性化関数として放射基底を使用する単層フィードフォワード ネットワーク
  • ART (適応共鳴理論、適応共鳴理論) ネットワーク:競争学習、教師なし学習戦略
  • SOM(Self Organizing Map、自己組織化マップ)ネットワーク:競争学習型教師なしニューラルネットワーク
  • カスケード相関ネットワーク:構造適応ネットワーク
  • エルマン ネットワーク: リカレント ネットワーク
  • ボルツマンマシン

1.パーセプトロン

1. 意味

パーセプトロンには次のコンポーネントがあります。

  • 入力の重み:パーセプトロンは複数の入力 x を受け取ることができ、各入力には重み wがあり、上の図にあるようなバイアス項目 bもあります。
  • 活性化関数:ステップ関数(ステップ関数)、シグモイド関数など
  • 出力: y= f(wx+b)
  • 重みの更新: w_i\leftarrow w_i + \Delta w_i、ここで\デルタ w_i = \eta (y-\hat{y})x_iη は学習率です

単層パーセプトロンは線形分離可能な問題のみを解決できます。多層パーセプトロンはあらゆる線形関数に適合でき、あらゆる線形分類または線形回帰問題はパーセプトロンで解決できます。

2.アップデート方法

ここに画像の説明を挿入

3. コード

class Perceptron(object):
    def __init__(self, input_num, activator):
        '''
        初始化感知器,设置输入参数的个数,以及激活函数。
        激活函数的类型为double -> double
        '''
        self.activator = activator
        # 权重向量初始化为0
        self.weights = [0.0 for _ in range(input_num)]
        # 偏置项初始化为0
        self.bias = 0.0
    def __str__(self):
        '''
        打印学习到的权重、偏置项
        '''
        return 'weights\t:%s\nbias\t:%f\n' % (self.weights, self.bias)
    def predict(self, input_vec):
        '''
        输入向量,输出感知器的计算结果
        '''
        # 把input_vec[x1,x2,x3...]和weights[w1,w2,w3,...]打包在一起
        # 变成[(x1,w1),(x2,w2),(x3,w3),...]
        # 然后利用map函数计算[x1*w1, x2*w2, x3*w3]
        # 最后利用reduce求和
        return self.activator(
            reduce(lambda a, b: a + b,
                   map(lambda (x, w): x * w,  
                       zip(input_vec, self.weights))
                , 0.0) + self.bias)
    def train(self, input_vecs, labels, iteration, rate):
        '''
        输入训练数据:一组向量、与每个向量对应的label;以及训练轮数、学习率
        '''
        for i in range(iteration):
            self._one_iteration(input_vecs, labels, rate)
    def _one_iteration(self, input_vecs, labels, rate):
        '''
        一次迭代,把所有的训练数据过一遍
        '''
        # 把输入和输出打包在一起,成为样本的列表[(input_vec, label), ...]
        # 而每个训练样本是(input_vec, label)
        samples = zip(input_vecs, labels)
        # 对每个样本,按照感知器规则更新权重
        for (input_vec, label) in samples:
            # 计算感知器在当前权重下的输出
            output = self.predict(input_vec)
            # 更新权重
            self._update_weights(input_vec, output, label, rate)
    def _update_weights(self, input_vec, output, label, rate):
        '''
        按照感知器规则更新权重
        '''
        # 把input_vec[x1,x2,x3,...]和weights[w1,w2,w3,...]打包在一起
        # 变成[(x1,w1),(x2,w2),(x3,w3),...]
        # 然后利用感知器规则更新权重
        delta = label - output
        self.weights = map(
            lambda (x, w): w + rate * delta * x,
            zip(input_vec, self.weights))
        # 更新bias
        self.bias += rate * delta

トレーニングと予測:

def f(x):
    '''
    定义激活函数f
    '''
    return 1 if x > 0 else 0
def get_training_dataset():
    '''
    基于and真值表构建训练数据
    '''
    # 构建训练数据
    # 输入向量列表
    input_vecs = [[1,1], [0,0], [1,0], [0,1]]
    # 期望的输出列表,注意要与输入一一对应
    # [1,1] -> 1, [0,0] -> 0, [1,0] -> 0, [0,1] -> 0
    labels = [1, 0, 0, 0]
    return input_vecs, labels    
def train_and_perceptron():
    '''
    使用and真值表训练感知器
    '''
    # 创建感知器,输入参数个数为2(因为and是二元函数),激活函数为f
    p = Perceptron(2, f)
    # 训练,迭代10轮, 学习速率为0.1
    input_vecs, labels = get_training_dataset()
    p.train(input_vecs, labels, 10, 0.1)
    #返回训练好的感知器
    return p
if __name__ == '__main__': 
    # 训练and感知器
    and_perception = train_and_perceptron()
    # 打印训练获得的权重
    print and_perception
    # 测试
    print '1 and 1 = %d' % and_perception.predict([1, 1])
    print '0 and 0 = %d' % and_perception.predict([0, 0])
    print '1 and 0 = %d' % and_perception.predict([1, 0])
    print '0 and 1 = %d' % and_perception.predict([0, 1])

2. バックプロパゲーション (BP) アルゴリズム

1. 内容

  • 重みとバイアス
  • コスト関数 (コスト関数): 平均二乗誤差
  • 重みの更新: 勾配降下法
  • さらなる最適化手法: 「正則化」、ネットワークの複雑さを記述する誤差目的関数に部分を追加 (w² など)

2.パラメータの計算と更新

2.1 合計誤差

2.2 誤差は隠れ層で逆伝播される (出力層 -> 隠れ層)

重みパラメータ w5 を例にとると、w5 が全体の誤差にどの程度の影響を与えるかを知りたい場合は、全体の誤差を使用して w5 の偏導関数を計算できます: (連鎖規則) 

3 つの偏導関数を求めます。

 

 したがって、次のようになります。

記号 δ を使用して式を簡略化できます (オプション)。

最後に重みを更新します w:

2.3 誤差逆伝播 (隠れ層 -> 隠れ層)

メソッドは似ていますが、エラーは渡された以前のエラーに基づいて計算されます。

この図のδの意味は異なり、(ターゲットアウト) 誤差のみを表します。

out(h1) は E(o1) と E(o2) からのエラーを受け入れるため、両方のエラーをここで計算する必要があります。

最後に、重み w1 を更新します。

順伝播および逆伝播シーケンスの概要の説明図: 

3. 確率的勾配更新とバッチ更新

バッチを更新する場合、Δw はサンプルのバッチを計算した後に更新されます。

ここに画像の説明を挿入

バッチ勾配降下法確率的勾配降下法の比較: 

ミニバッチ勾配降下アルゴリズムは、バッチ勾配降下アルゴリズムと確率的勾配降下アルゴリズムの中間のアルゴリズムであり、定数トレーニング インスタンスが計算されるたびにパラメータが更新されます。

バッチ勾配降下法アルゴリズムの各反復では、m 個のサンプルすべてを使用する必要がありますが、確率的勾配降下法では、反復ごとに 1 つのサンプルのみが必要です。具体的には、ミニバッチ アルゴリズムは反復ごとに b 個のサンプルを使用し、この b はミニバッチ サイズ パラメーターと呼ばれます。

3. コード

#coding:utf-8
import random
import math

#
#   参数解释:
#   "pd_" :偏导的前缀
#   "d_" :导数的前缀
#   "w_ho" :隐含层到输出层的权重系数索引
#   "w_ih" :输入层到隐含层的权重系数的索引

class NeuralNetwork:
    LEARNING_RATE = 0.5

    def __init__(self, num_inputs, num_hidden, num_outputs, hidden_layer_weights = None, hidden_layer_bias = None, output_layer_weights = None, output_layer_bias = None):
        self.num_inputs = num_inputs

        self.hidden_layer = NeuronLayer(num_hidden, hidden_layer_bias)
        self.output_layer = NeuronLayer(num_outputs, output_layer_bias)

        self.init_weights_from_inputs_to_hidden_layer_neurons(hidden_layer_weights)
        self.init_weights_from_hidden_layer_neurons_to_output_layer_neurons(output_layer_weights)

    def init_weights_from_inputs_to_hidden_layer_neurons(self, hidden_layer_weights):
        weight_num = 0
        for h in range(len(self.hidden_layer.neurons)):
            for i in range(self.num_inputs):
                if not hidden_layer_weights:
                    self.hidden_layer.neurons[h].weights.append(random.random())
                else:
                    self.hidden_layer.neurons[h].weights.append(hidden_layer_weights[weight_num])
                weight_num += 1

    def init_weights_from_hidden_layer_neurons_to_output_layer_neurons(self, output_layer_weights):
        weight_num = 0
        for o in range(len(self.output_layer.neurons)):
            for h in range(len(self.hidden_layer.neurons)):
                if not output_layer_weights:
                    self.output_layer.neurons[o].weights.append(random.random())
                else:
                    self.output_layer.neurons[o].weights.append(output_layer_weights[weight_num])
                weight_num += 1

    def inspect(self):
        print('------')
        print('* Inputs: {}'.format(self.num_inputs))
        print('------')
        print('Hidden Layer')
        self.hidden_layer.inspect()
        print('------')
        print('* Output Layer')
        self.output_layer.inspect()
        print('------')

    def feed_forward(self, inputs):
        hidden_layer_outputs = self.hidden_layer.feed_forward(inputs)
        return self.output_layer.feed_forward(hidden_layer_outputs)

    def train(self, training_inputs, training_outputs):
        self.feed_forward(training_inputs)

        # 1. 输出神经元的值
        pd_errors_wrt_output_neuron_total_net_input = [0] * len(self.output_layer.neurons)
        for o in range(len(self.output_layer.neurons)):

            # ∂E/∂zⱼ
            pd_errors_wrt_output_neuron_total_net_input[o] = self.output_layer.neurons[o].calculate_pd_error_wrt_total_net_input(training_outputs[o])

        # 2. 隐含层神经元的值
        pd_errors_wrt_hidden_neuron_total_net_input = [0] * len(self.hidden_layer.neurons)
        for h in range(len(self.hidden_layer.neurons)):

            # dE/dyⱼ = Σ ∂E/∂zⱼ * ∂z/∂yⱼ = Σ ∂E/∂zⱼ * wᵢⱼ
            d_error_wrt_hidden_neuron_output = 0
            for o in range(len(self.output_layer.neurons)):
                d_error_wrt_hidden_neuron_output += pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].weights[h]

            # ∂E/∂zⱼ = dE/dyⱼ * ∂zⱼ/∂
            pd_errors_wrt_hidden_neuron_total_net_input[h] = d_error_wrt_hidden_neuron_output * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_input()

        # 3. 更新输出层权重系数
        for o in range(len(self.output_layer.neurons)):
            for w_ho in range(len(self.output_layer.neurons[o].weights)):

                # ∂Eⱼ/∂wᵢⱼ = ∂E/∂zⱼ * ∂zⱼ/∂wᵢⱼ
                pd_error_wrt_weight = pd_errors_wrt_output_neuron_total_net_input[o] * self.output_layer.neurons[o].calculate_pd_total_net_input_wrt_weight(w_ho)

                # Δw = α * ∂Eⱼ/∂wᵢ
                self.output_layer.neurons[o].weights[w_ho] -= self.LEARNING_RATE * pd_error_wrt_weight

        # 4. 更新隐含层的权重系数
        for h in range(len(self.hidden_layer.neurons)):
            for w_ih in range(len(self.hidden_layer.neurons[h].weights)):

                # ∂Eⱼ/∂wᵢ = ∂E/∂zⱼ * ∂zⱼ/∂wᵢ
                pd_error_wrt_weight = pd_errors_wrt_hidden_neuron_total_net_input[h] * self.hidden_layer.neurons[h].calculate_pd_total_net_input_wrt_weight(w_ih)

                # Δw = α * ∂Eⱼ/∂wᵢ
                self.hidden_layer.neurons[h].weights[w_ih] -= self.LEARNING_RATE * pd_error_wrt_weight

    def calculate_total_error(self, training_sets):
        total_error = 0
        for t in range(len(training_sets)):
            training_inputs, training_outputs = training_sets[t]
            self.feed_forward(training_inputs)
            for o in range(len(training_outputs)):
                total_error += self.output_layer.neurons[o].calculate_error(training_outputs[o])
        return total_error

class NeuronLayer:
    def __init__(self, num_neurons, bias):

        # 同一层的神经元共享一个截距项b
        self.bias = bias if bias else random.random()

        self.neurons = []
        for i in range(num_neurons):
            self.neurons.append(Neuron(self.bias))

    def inspect(self):
        print('Neurons:', len(self.neurons))
        for n in range(len(self.neurons)):
            print(' Neuron', n)
            for w in range(len(self.neurons[n].weights)):
                print('  Weight:', self.neurons[n].weights[w])
            print('  Bias:', self.bias)

    def feed_forward(self, inputs):
        outputs = []
        for neuron in self.neurons:
            outputs.append(neuron.calculate_output(inputs))
        return outputs

    def get_outputs(self):
        outputs = []
        for neuron in self.neurons:
            outputs.append(neuron.output)
        return outputs

class Neuron:
    def __init__(self, bias):
        self.bias = bias
        self.weights = []

    def calculate_output(self, inputs):
        self.inputs = inputs
        self.output = self.squash(self.calculate_total_net_input())
        return self.output

    def calculate_total_net_input(self):
        total = 0
        for i in range(len(self.inputs)):
            total += self.inputs[i] * self.weights[i]
        return total + self.bias

    # 激活函数sigmoid
    def squash(self, total_net_input):
        return 1 / (1 + math.exp(-total_net_input))


    def calculate_pd_error_wrt_total_net_input(self, target_output):
        return self.calculate_pd_error_wrt_output(target_output) * self.calculate_pd_total_net_input_wrt_input();

    # 每一个神经元的误差是由平方差公式计算的
    def calculate_error(self, target_output):
        return 0.5 * (target_output - self.output) ** 2


    def calculate_pd_error_wrt_output(self, target_output):
        return -(target_output - self.output)


    def calculate_pd_total_net_input_wrt_input(self):
        return self.output * (1 - self.output)


    def calculate_pd_total_net_input_wrt_weight(self, index):
        return self.inputs[index]


# 文中的例子:

nn = NeuralNetwork(2, 2, 2, hidden_layer_weights=[0.15, 0.2, 0.25, 0.3], hidden_layer_bias=0.35, output_layer_weights=[0.4, 0.45, 0.5, 0.55], output_layer_bias=0.6)
for i in range(10000):
    nn.train([0.05, 0.1], [0.01, 0.09])
    print(i, round(nn.calculate_total_error([[[0.05, 0.1], [0.01, 0.09]]]), 9))


#另外一个例子,可以把上面的例子注释掉再运行一下:

# training_sets = [
#     [[0, 0], [0]],
#     [[0, 1], [1]],
#     [[1, 0], [1]],
#     [[1, 1], [0]]
# ]

# nn = NeuralNetwork(len(training_sets[0][0]), 5, len(training_sets[0][1]))
# for i in range(10000):
#     training_inputs, training_outputs = random.choice(training_sets)
#     nn.train(training_inputs, training_outputs)
#     print(i, nn.calculate_total_error(training_sets))

3. SOM自己組織化マップネットワーク

SOM ネットワーク (別名コホーネン ネットワーク)は、自己組織化競争ニューラル ネットワークの一種であり、環境の特徴を識別して自動的にクラスタリングできる教師なし学習ネットワークです。サンプル内の内部法則と本質的な特性を自動的に検索することで、ネットワークのパラメーターと構造を自己組織化し、自己適応的に変更します。

1. SOM ネットワーク トポロジ

  • ネットワークは入力層と出力層の2層で構成されており、隠れ層を除いた出力層は競合層とも呼ばれます。
  • 入力層のすべての入力ノードは出力ノードに完全に接続されています。
  • 出力ノードは2 次元構造に分散されており、ノード間には横方向の接続があります。したがって、特定の出力ノードについては、特定の近傍範囲内に特定の数の「隣接セル」、つまり隣接ノードが存在します。

2. 原則

  1. 分類と入力パターンの類似性: 分類とは、カテゴリ知識などのメンターシグナルの指導の下、認識対象の入力パターンをそれぞれのパターンクラスに割り当てることです. メンターの指導なしで分類することをクラスタリングと呼びます. クラスタリングの目的は, 類似したパターンサンプルを取得することです.パターンサンプルのクラス内類似性とクラス間分離を実現するために、パターンサンプルを1つのクラスに分類し、異なるものを分離します教師なし学習のトレーニング サンプルには目的の出力が含まれていないため、特定の入力パターン サンプルがどのクラスに属するかについての事前知識はありません。入力パターンのセットは、それらの間の類似度に応じていくつかのカテゴリに分類することしかできないため、類似度は入力パターンのクラスタリングの基礎となります
  2. 類似性の尺度: ニューラル ネットワークの入力パターン ベクトルの類似性の尺度は、ベクトル間の距離によって測定できます。一般的に使用される方法は、ユークリッド距離法とコサイン法です。
  3. 競争学習原理:競争学習ルールの生理学的基礎は、神経細胞の側方抑制です。神経細胞が興奮すると、周囲の神経細胞を抑制します。最も強力な阻害要因は、勝つために競争するという独我論であり、勝者総取りとして知られる習慣です。競争学習ルールは、神経細胞の側方抑制現象から得られます。その学習ステップは次のとおりです: A. ベクトル正規化; B. 勝者ニューロンの検索; C. ネットワーク出力と重み調整; D. 再正規化処理
SOMネットワークの競争調整戦略、勝者ノードと隣接ノードの更新

3. 具体的なアルゴリズム

ネットワーク学習プロセス中、サンプルがネットワークに入力されると、競合層のニューロンは入力サンプルと競合層のニューロンの重みの間のユークリッド距離を計算し、距離が最小のニューロンが勝利します。ニューロン。勝利ニューロンとその周囲の重みが入力サンプルに近づくように、勝利ニューロンと隣接ニューロンの重みを調整します。繰り返しのトレーニングにより、各ニューロンの最終的な接続重みは一定の分布を持ちます。この分布は、データ間の類似性をさまざまなタイプを表すニューロンに整理するため、同じタイプのニューロンは同様の重み係数を持ち、異なるタイプのニューロンの重み係数の違いは異なります。重み係数は明らかです。学習プロセス中、重み値は学習率を変更し、ニューロン場は常に減少するため、同じ種類のニューロンが徐々に集中することに注意してください。

一般に、SOM ネットワークには、初期化、競合、反復の 3 つの部分が含まれます

3.1 初期化

  1. データセットを正規化します。
  2. 競争力のあるレイヤー構造を設計します。通常は、推定されるクラスタ数に応じて設定されます。たとえば、4 つのカテゴリに分割するには、競合レイヤーを 2*2、1*4、4*1 として設計できます。SOM ネットワークはクラスターの数を事前に提供する必要はなく、カテゴリーのデータはネットワークによって自動的に識別されます。
  3. 競合層ニューロンの重みノードを事前設定します。一般に、入力データの次元と競合層によって推定されたカテゴリの数に応じて重みノードを設定します。入力が 2 次元データで、最終的なクラスタリングが 4 カテゴリの場合、重み行列は 2 になります。 ※4.
  4. 近傍半径学習率を初期化します

3.2 競争プロセス

  1. サンプルをランダムに選択し、重みノードに従って競合層の各ニューロン ノードまでの距離を計算します。一般に、距離を計算するにはユークリッド距離式を選択します。

    (または別の方法:重み w と入力 x の内積が最大となり、勝者ノードを決定します)
  2. 勝者ノードを選択します。競争を通じて、距離が最小のニューロン ノードが勝利ノードとして選択されます。

ベスト マッチング ユニット (BMU)  : それ自体からサンプル ベクトルまでの距離が最も短い勝者ノード (または重み)。距離を決定する方法は多数ありますが、最も一般的に使用される方法はユークリッド距離です。これは、 重みとサンプル ベクトルの間の距離を計算する最も一般的なメトリックです。

3.3 反復プロセス

1. 勝利した近傍内のすべてのノードの輪郭を描きます。近傍半径に従って、距離が近傍半径より小さいノードが描画されて、勝ち近傍が形成されます。

2. 勝利した近傍のノードの重みを繰り返し更新します。更新の考え方は、勝者ノードに近づくほど更新範囲が大きくなり、勝者ノードから遠ざかるほど更新範囲が小さくなるということです。そのため、さまざまなノードに更新制約を追加する必要があります。ガウス関数で求められます。さらに、ノードを更新する目的は、勝者ノードをサンプル ポイントに近づけることであるため、次の式を使用してノードの重みを更新します。

さまざまな近傍関数:

  • 'バブル' の場合、H = (Ud<=radius(t));
  • 'ガウス' の場合、H = exp(-Ud/(2*radius(t)));
  • 'カットガウス' の場合、H = exp(-Ud/(2 radius(t))) 。 (Ud<=radius(t));
  • case 'ep', H = (1-Ud/radius(t)) .* (Ud<=radius(t));

3.4 減衰関数 減衰関数

SOM ネットワークのもう 1 つの特徴は、学習率と近傍範囲が反復回数とともに徐々に減衰することです。

最も一般的に使用される減衰関数は 1 / (1 + t/T) です。

シグマ(t) = シグマ / (1 + t/T)

学習率(t) = 学習率 / (1 + t/T)

t は現在の反復数を表します。
T = 反復の総数 / 2

4. 実践例

初期の重みは、 w1 = (0.45,0.89) 、 w2 = (0.55,0.83) 、 w3 = (0.95,0.32) 、および w4 = (0.62,0.78) です。4 つのニューロン (N=4)、N1、N2、N3、N4 をデカルト座標面の位置 (0,0)、(1,0)、(1,0)、(1,1) のグリッドに配置します。それぞれ真ん中。ネットワークが 2 次元 (A=2) 入力ベクトル (x1,x2) を受け入れるとします。w_i= (w1_i,w2_i) をニューロン i の重みとし、近傍半径 = 0.6 とします。入力ベクトル X=(3,1)。学習率 = 0.5 であり、全体を通じて一定であると仮定します。図に示すように初期重みを仮定します。

5. コード

参考文献

ゼロから始めるディープラーニング入門 (1) - Perceptron - 宿題部族 Cmd Markdown Editor Reader

ニューラル ネットワークのバックプロパゲーション手法を理解するための 1 つの記事 - BackPropagation

誤差逆伝播法

ニューラル ネットワーク アルゴリズムの概要_Qingqing_Amanda のブログ - CSDN ブログ

バッチ勾配降下法、確率的勾配降下法、ミニバッチ確率的勾配降下法比較の説明: - 知っている

機械学習アルゴリズムの導出と手書き実装 07——SOM Network-Knowledge

自己組織化マッピング ニューラル ネットワーク (SOM)_アイデア返信ブログ-CSDN ブログ

https://medium.com/analytics-vidhya/how-does-self-organizing-algorithm-works-f0664af9bf04

おすすめ

転載: blog.csdn.net/m0_64768308/article/details/129773449
おすすめ