2017-02-02 8 views
1

最近、ニューラルネットワークについて学び始め、自分の単純な2層ANNをコード化し、MNISTデータセットを使用してベンチマークすることに決めました。バッチサイズがユーザーによって提供されるバッチSGDを使用してプログラムを作成しようとしました。次のように私のコードは次のとおりです。自己コード化された2層人工ニューラルネットワークの最適化

class NeuralNetwork: 
    def __init__(self, inodes, hnodes, outnodes, activation_func, learning_rate): 
     self.inodes = inodes 
     self.hnodes = hnodes 
     self.onodes = outnodes 
     self.activation_function = activation_func 
     self.lr = learning_rate 
     self.wih = np.random.randn(self.hnodes, self.inodes)/pow(self.inodes, 0.5) 
     self.who = np.random.randn(self.onodes, self.hnodes)/pow(self.hnodes, 0.5) 

    def train(self, training_data, target_labels, batch=1, l2_penalty=0, verbose=False): 
     batch_size = len(training_data)/batch 
     print "Starting to train........" 
     for i in range(batch): 
      train_data_batch = training_data[batch_size*i : batch_size*(i+1)] 
      label_batch = target_labels[batch_size*i : batch_size*(i+1)] 
      batch_error = self.train_batch(train_data_batch, label_batch, l2_penalty) 
      if verbose: 
       print "Batch : " + str(i+1) + " ; Error : " + str(batch_error) 
     print "..........Finished!" 

    def train_batch(self, training_data, target_labels, l2_penalty=0): 
     train = np.array(training_data, ndmin=2).T 
     label = np.array(target_labels, ndmin=2).T 

     inputs = train # IxN 
     hidden_input = np.dot(self.wih, inputs) # (HxI).(IxN) = HxN 
     hidden_ouputs = self.activation_function(hidden_input) # (HxN) -> (HxN) 

     final_input = np.dot(self.who, hidden_ouputs) # (OxH).(HxN) -> OxN 
     final_outputs = self.activation_function(final_input) # OxN -> OxN 

     final_outputs = np.exp(final_outputs) # OxN 
     for f in range(len(final_outputs)): 
      final_outputs[f] = final_outputs[f]/sum(final_outputs[f]) 

     final_error_wrt_out = label - final_outputs # OxN 
     hidden_error_wrt_out = np.dot(self.who.T, final_outputs) # HxN 

     final_in_wrt_out = self.activation_function(final_input, der=True) # OxN 
     hidden_in_wrt_out = self.activation_function(hidden_input, der=True) # HxN 

     grad_who = np.dot(final_error_wrt_out * final_in_wrt_out, hidden_ouputs.T) # (OxN).(NxH) -> OxH 
     grad_wih = np.dot(hidden_error_wrt_out * hidden_in_wrt_out, inputs.T) # (HxN).(NxI) -> HxI 

     self.who = self.who - self.lr * (grad_who + l2_penalty*(self.who)) 
     self.wih = self.wih - self.lr * (grad_wih + l2_penalty*(self.wih)) 

     return np.sum(final_error_wrt_out * final_error_wrt_out)/(2*len(training_data)) 

    def query(self, inputs): 
     if len(inputs) != self.inodes: 
      print "Invalid input size" 
      return 
     inputs = np.array(inputs) 
     hidden_input = np.dot(self.wih, inputs) 
     hidden_ouputs = self.activation_function(hidden_input) 

     final_input = np.dot(self.who, hidden_ouputs) 
     final_outputs = self.activation_function(final_input) 

     final_outputs = np.exp(final_outputs) 
     total = sum(final_outputs) 
     probs = final_outputs/total 

     return probs 

私は約95%の精度を与えるgithubにタリク・ラシッドによって同様のコードを発見しました。一方、私のコードは10%しか与えていません。

Backpropogationに関するさまざまなチュートリアルを参照してコードを複数回デバッグしようとしましたが、精度を向上させることができませんでした。私はこの問題についての洞察に感謝します。

編集1: これはmattdeakの回答に続いています。

私は以前、ソフトマックスレイヤーのネガティブ対数尤度エラーの代わりにMSEを使用していましたが、私のエラーです。答えに続いて、私は列車の機能を次のように変更しました:

def train_batch(self, training_data, target_labels, l2_penalty=0): 
    train = np.array(training_data, ndmin=2).T 
    label = np.array(target_labels, ndmin=2).T 

    inputs = train # IxN 
    hidden_input = np.dot(self.wih, inputs) # (HxI).(IxN) = HxN 
    hidden_ouputs = self.activation_function(hidden_input) # (HxN) -> (HxN) 

    final_input = np.dot(self.who, hidden_ouputs) # (OxH).(HxN) -> OxN 
    final_outputs = self.activation_function(final_input) # OxN -> OxN 

    final_outputs = np.exp(final_outputs) # OxN 
    for f in range(len(final_outputs)): 
     final_outputs[f] = final_outputs[f]/sum(final_outputs[f]) 

    error = label - final_outputs 

    final_error_wrt_out = final_outputs - 1 # OxN 
    hidden_error_wrt_out = np.dot(self.who.T, -np.log(final_outputs)) # (HxO).(OxN) -> HxN 

    final_in_wrt_out = self.activation_function(final_input, der=True) # OxN 
    hidden_in_wrt_out = self.activation_function(hidden_input, der=True) # HxN 

    grad_who = np.dot(final_error_wrt_out * final_in_wrt_out, hidden_ouputs.T) # (OxN).(NxH) -> OxH 
    grad_wih = np.dot(hidden_error_wrt_out * hidden_in_wrt_out, inputs.T) # (HxN).(NxI) -> HxI 

    self.who = self.who - self.lr * (grad_who + l2_penalty*(self.who)) 
    self.wih = self.wih - self.lr * (grad_wih + l2_penalty*(self.wih)) 

    return np.sum(final_error_wrt_out * final_error_wrt_out)/(2*len(training_data)) 

しかし、これでパフォーマンスは向上しませんでした。

+0

(np.exp(final_outputs))。この結果は、final_outputs = np.exp(final_outputs)の直後に 'for'ループを使用して 'probs'変数に格納されます。私はプログラムをよりよくデバッグするのに役立つので、複数の行でこの操作を行う方が簡単だと分かった。 – Chaitanya

答えて

1

私は、あなたの列車ステップでsoftmaxレイヤーをバックプロパゲーションしているとは思わない。 私は間違っていない場合、私はソフトマックスの勾配は、単にとして計算することができると考えている。それは確かにソフトマックス回帰であると私は最終的にnp.exp(final_outputs)/np.sumをしています@mattdeak

grad_softmax = final_outputs - 1