サーチ…


はじめに:Pythonを使った簡単なANN

以下のコードリストは、MNISTデータセットから手書き数字を分類しようとしています。数字は次のようになります。

MNIST

コードはこれらの数字を前処理し、各画像を0と1の2次元配列に変換し、97%精度(50エポック)までニューラルネットワークを訓練するためにこのデータを使用します。

"""
Deep Neural Net 

(Name: Classic Feedforward)

"""

import numpy as np
import pickle, json
import sklearn.datasets
import random
import time
import os


def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

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

def relU(z):
    return np.maximum(z, 0, z)
    
def relU_prime(z):
    return z * (z <= 0)
    
def tanh(z):
    return np.tanh(z)

def tanh_prime(z):
    return 1 - (tanh(z) ** 2)

def transform_target(y):
    t = np.zeros((10, 1))
    t[int(y)] = 1.0
    return t

"""--------------------------------------------------------------------------------"""

class NeuralNet:

    def __init__(self, layers, learning_rate=0.05, reg_lambda=0.01):
        self.num_layers = len(layers)
        self.layers = layers
        self.biases = [np.zeros((y, 1)) for y in layers[1:]]
        self.weights = [np.random.normal(loc=0.0, scale=0.1, size=(y, x)) for x, y in zip(layers[:-1], layers[1:])]
        self.learning_rate = learning_rate
        self.reg_lambda = reg_lambda
        self.nonlinearity = relU
        self.nonlinearity_prime = relU_prime

    def __feedforward(self, x):
        """ Returns softmax probabilities for the output layer """
        for w, b in zip(self.weights, self.biases):
            x = self.nonlinearity(np.dot(w, np.reshape(x, (len(x), 1))) + b)

        return np.exp(x) / np.sum(np.exp(x))

    def __backpropagation(self, x, y):
        """
        :param x: input
        :param y: target
        """
        weight_gradients = [np.zeros(w.shape) for w in self.weights]
        bias_gradients = [np.zeros(b.shape) for b in self.biases]

        # forward pass
        activation = x
        hidden_activations = [np.reshape(x, (len(x), 1))]
        z_list = []

        for w, b in zip(self.weights, self.biases):    
            z = np.dot(w, np.reshape(activation, (len(activation), 1))) + b
            z_list.append(z)
            activation = self.nonlinearity(z)
            hidden_activations.append(activation)

        t = hidden_activations[-1] 
        hidden_activations[-1] = np.exp(t) / np.sum(np.exp(t))

        # backward pass
        delta = (hidden_activations[-1] - y) * (z_list[-1] > 0)
        weight_gradients[-1] = np.dot(delta, hidden_activations[-2].T)
        bias_gradients[-1] = delta

        for l in range(2, self.num_layers):
            z = z_list[-l]
            delta = np.dot(self.weights[-l + 1].T, delta) * (z > 0)
            weight_gradients[-l] = np.dot(delta, hidden_activations[-l - 1].T)
            bias_gradients[-l] = delta

        return (weight_gradients, bias_gradients)

    def __update_params(self, weight_gradients, bias_gradients):
        for i in xrange(len(self.weights)):
            self.weights[i] += -self.learning_rate * weight_gradients[i]
            self.biases[i] += -self.learning_rate * bias_gradients[i]

    def train(self, training_data, validation_data=None, epochs=10):
        bias_gradients = None
        for i in xrange(epochs):
            random.shuffle(training_data)
            inputs = [data[0] for data in training_data]
            targets = [data[1] for data in training_data]

            for j in xrange(len(inputs)):
                (weight_gradients, bias_gradients) = self.__backpropagation(inputs[j], targets[j])
                self.__update_params(weight_gradients, bias_gradients)

            if validation_data: 
                random.shuffle(validation_data)
                inputs = [data[0] for data in validation_data]
                targets = [data[1] for data in validation_data]

                for j in xrange(len(inputs)):
                    (weight_gradients, bias_gradients) = self.__backpropagation(inputs[j], targets[j])
                    self.__update_params(weight_gradients, bias_gradients)

            print("{} epoch(s) done".format(i + 1))

        print("Training done.")

    def test(self, test_data):
        test_results = [(np.argmax(self.__feedforward(x[0])), np.argmax(x[1])) for x in test_data]
        return float(sum([int(x == y) for (x, y) in test_results])) / len(test_data) * 100

    def dump(self, file):
        pickle.dump(self, open(file, "wb"))

"""--------------------------------------------------------------------------------"""

if __name__ == "__main__":
    total = 5000
    training = int(total * 0.7)
    val = int(total * 0.15)
    test = int(total * 0.15)

    mnist = sklearn.datasets.fetch_mldata('MNIST original', data_home='./data')

    data = zip(mnist.data, mnist.target)
    random.shuffle(data)
    data = data[:total]
    data = [(x[0].astype(bool).astype(int), transform_target(x[1])) for x in data]

    train_data = data[:training]
    val_data = data[training:training+val]
    test_data = data[training+val:]

    print "Data fetched"

    NN = NeuralNet([784, 32, 10]) # defining an ANN with 1 input layer (size 784 = size of the image flattened), 1 hidden layer (size 32), and 1 output layer (size 10, unit at index i will predict the probability of the image being digit i, where 0 <= i <= 9)  

    NN.train(train_data, val_data, epochs=5)

    print "Network trained"

    print "Accuracy:", str(NN.test(test_data)) + "%"

これは自己完結型のコードサンプルであり、それ以上修正することなく実行できます。お使いのPythonのバージョンにnumpyscikitがインストールされていることを確認してください。

バックプロパゲーション - ニューラルネットワークの心臓

バックプロパゲーションの目的は、ニューラルネットワークが任意の入力を出力に正しくマップする方法を学習できるように重みを最適化することです。

各レイヤーにはそれぞれ独自の重みがあり、正しい出力を与えられた入力を正確に予測できるように、これらの重みを調整する必要があります。

バックプロパゲーションの概要は、次のとおりです。

  1. フォワードパス - 入力はある出力に変換されます。各レイヤーで、入力と重みとの間のドット積を用いて活性化が計算され、続いてその結果がバイアスと加算されます。最後に、この値は、次の層への入力となるその層の活性化を得るために、活性化関数を通る。
  2. 最後のレイヤーでは、出力はその入力に対応する実際のラベルと比較され、エラーが計算されます。通常、それは平均自乗誤差です。
  3. バックワードパス - ステップ2で計算された誤差は内側の層に伝搬され、すべての層の重みがこの誤差を考慮して調整されます。

1.重みの初期化

ウェイトの初期化の簡単な例を以下に示します。

layers = [784, 64, 10] 
weights = np.array([(np.random.randn(y, x) * np.sqrt(2.0 / (x + y))) for x, y in zip(layers[:-1], layers[1:])])
biases = np.array([np.zeros((y, 1)) for y in layers[1:]])
  • 隠しレイヤー1は、次元[64,784]の重みと次元64の偏りを持ちます。

  • 出力層は次元[10,64]の重みと次元の偏りを持つ

上のコードで重みを初期化するときに何が起こっているのだろうかと疑問に思うかもしれません。これはXavier初期化と呼ばれ、ランダムに体重マトリックスを初期化するよりも優れたステップです。はい、初期化は重要です。あなたの初期化に基づいて、勾配降下中のより良い局所極小を見つけることができるかもしれません(逆伝播は勾配降下の栄光のあるバージョンです)。

2.フォワードパス

activation = x
hidden_activations = [np.reshape(x, (len(x), 1))]
z_list = []

for w, b in zip(self.weights, self.biases):    
    z = np.dot(w, np.reshape(activation, (len(activation), 1))) + b
    z_list.append(z)
    activation = relu(z)
    hidden_activations.append(activation)

t = hidden_activations[-1] 
hidden_activations[-1] = np.exp(t) / np.sum(np.exp(t))

このコードは、上記の変換を実行します。 hidden_activations[-1]hidden_activations[-1]確率が含まれています。すべてのクラスの予測。その合計は1です。数字を予測する場合、出力は次元10の確率のベクトルになります。その合計は1です。

3.バックワードパス

weight_gradients = [np.zeros(w.shape) for w in self.weights]
bias_gradients = [np.zeros(b.shape) for b in self.biases]

delta = (hidden_activations[-1] - y) * (z_list[-1] > 0) # relu derivative
weight_gradients[-1] = np.dot(delta, hidden_activations[-2].T)
bias_gradients[-1] = delta

for l in range(2, self.num_layers):
    z = z_list[-l]
    delta = np.dot(self.weights[-l + 1].T, delta) * (z > 0) # relu derivative
    weight_gradients[-l] = np.dot(delta, hidden_activations[-l - 1].T)
    bias_gradients[-l] = delta

最初の2行はグラデーションを初期化します。これらの勾配は計算され、後で重みおよびバイアスを更新するために使用されます。

次の3行は、ターゲットから予測を差し引いてエラーを計算します。この誤差は、内部層に逆伝搬される。

今、慎重にループの作業をトレースします。 2行目と3行目でエラーをlayer[i]からlayer[i - 1]変換します。理解するために乗算される行列の形をトレースします。

4.重み付け/パラメータ更新

for i in xrange(len(self.weights)):
    self.weights[i] += -self.learning_rate * weight_gradients[i]
    self.biases[i] += -self.learning_rate * bias_gradients[i] 

self.learning_rateは、ネットワークが学習するレートを指定します。それが収束しないかもしれないので、あなたはそれがあまりにも速く学ぶことを望んでいません。良好な最小値を見つけるために滑らかな降下が好まれる。通常、 0.010.1間のレートは良好とみなされます。

アクティベーション機能

伝達関数とも呼ばれる起動関数は、入力ノードを出力ノードに特定の方法でマッピングするために使用されます。

それらは、ニューラルネットワーク層の出力に非線形性を与えるために使用される。

一般的に使用される関数とその曲線を以下に示します。 アクティベーション機能


シグモイド関数

シグモイドは、出力が[0, 1]範囲にあるスカッシュ関数です。

ここに画像の説明を入力

numpynumpyで微分して実装するコードを以下に示します。

def sigmoid(z):
    return 1.0 / (1.0 + np.exp(-z))

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

双曲線正接関数(tanh)

tanhとsigmoid関数の基本的な違いは、tanhは0の中心にあり、[-1、1]の範囲に入力を潰して計算する方が効率的です。

ここに画像の説明を入力

np.tanhまたはmath.tanh関数を使用して、隠れたレイヤーのアクティベーションを簡単に計算することができます。

ReLU機能

整流された線形ユニットは単にmax(0,x)ます。ニューラルネットワークユニットの活性化機能の最も一般的な選択肢の1つです。

ここに画像の説明を入力

ReLUsは、シグモイド/双曲線タンジェントユニットの消失勾配問題に対処し、深いネットワークで効率的な勾配伝搬を可能にします。

ReLUという名前は、Nair and Hintonの論文「 整流された線形ユニット改良型ボルツマン機械の改良」に由来しています。

漏れたReLUs(LReLUs)やExponential Linear Units(ELU)などいくつかのバリエーションがあります。

vanilla ReLUをnumpyで微分して実装するコードを以下に示します。

def relU(z):
    return z * (z > 0)

def relU_prime(z):
    return z > 0

ソフトマックス機能

Softmax回帰(または多項ロジスティック回帰)は、複数のクラスを扱う場合のロジスティック回帰の一般化です。非バイナリ分類を適用したいニューラルネットワークに特に有用です。この場合、単純なロジスティック回帰では不十分です。すべてのラベルに確率分布が必要です。これはsoftmaxが私たちに与えるものです。

Softmaxは以下の式で計算されます。

式

___________________________どこに収まるのですか? _____________________________

ソフトマックスでそれにソフトマックス関数を適用することにより、ベクトルを正規化するためにnumpy 、使用します。

np.exp(x) / np.sum(np.exp(x))

ここで、 xはANNの最終層からの活性化である。



Modified text is an extract of the original Stack Overflow Documentation
ライセンスを受けた CC BY-SA 3.0
所属していない Stack Overflow