machine-learning
ニューラルネットワーク
サーチ…
はじめに:Pythonを使った簡単なANN
以下のコードリストは、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のバージョンにnumpy
とscikit
がインストールされていることを確認してください。
バックプロパゲーション - ニューラルネットワークの心臓
バックプロパゲーションの目的は、ニューラルネットワークが任意の入力を出力に正しくマップする方法を学習できるように重みを最適化することです。
各レイヤーにはそれぞれ独自の重みがあり、正しい出力を与えられた入力を正確に予測できるように、これらの重みを調整する必要があります。
バックプロパゲーションの概要は、次のとおりです。
- フォワードパス - 入力はある出力に変換されます。各レイヤーで、入力と重みとの間のドット積を用いて活性化が計算され、続いてその結果がバイアスと加算されます。最後に、この値は、次の層への入力となるその層の活性化を得るために、活性化関数を通る。
- 最後のレイヤーでは、出力はその入力に対応する実際のラベルと比較され、エラーが計算されます。通常、それは平均自乗誤差です。
- バックワードパス - ステップ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.01
と0.1
間のレートは良好とみなされます。
アクティベーション機能
伝達関数とも呼ばれる起動関数は、入力ノードを出力ノードに特定の方法でマッピングするために使用されます。
それらは、ニューラルネットワーク層の出力に非線形性を与えるために使用される。
シグモイド関数
シグモイドは、出力が[0, 1]
範囲にあるスカッシュ関数です。
numpy
をnumpy
で微分して実装するコードを以下に示します。
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の最終層からの活性化である。