2017-04-26 4 views
0

Je suis en train d'apprendre Theano, et construit un NN très simple qui apprend XOR. Un problème simple devrait apprendre assez rapidement. Mais ce que j'ai trouvé est que la réponse n'est pas correcte parfois, mais une erreur curieusement spécifique.Apprentissage NN de base XOR tend vers des réponses correctes environ la moitié du temps, d'autres fois il tend à des réponses constantes mais étranges

Je saisis [[0,0], [1,0], [0,1], [1,1]] pour tester le XOR. Je devrais m'attendre à une sortie [0,1,1,0]. Et en effet, je reçois cela à peu près la moitié du temps.

[[ 0.01763905] 
[ 0.99207316] 
[ 0.99207316] 
[ 0.00663723]] 

Mais d'autres fois, je reçois une sorte de combinaison où deux des sorties sont presque exactement 0,5, on est près de 1, et l'autre presque 0. Comme si,

[[ 0.49998723] 
[ 0.49998723] 
[ 0.99430759] 
[ 0.00622013]] 

Ou quelque chose de similaire comme

[[ 0.49957245] 
[ 0.98064991] 
[ 0.49957245] 
[ 0.02422073]] 

Voici mon code. S'il vous plaît ne hésitez pas à me faire savoir si je ne fais pas correctement la formation (comme je l'ai complètement devinai sur l'opportunité de calculer les gradients pour chaque couche individuellement)

import theano 
import theano.tensor as T 
import numpy as np 

class HiddenLayer(object): 
    def __init__(self, input, layerShape): 
     # Input should be matrix of size (batch size, input nodes) 
     # Layer shape is (input nodes, hidden nodes) 
     self.input = input 
     self.W = theano.shared(np.random.normal(0,1,layerShape)) 
     self.b = theano.shared(np.random.normal(0,1,layerShape[1])) 
     self.output = T.nnet.nnet.relu(T.dot(self.input,self.W)+self.b) 

    def train(self, cost, rate): 
     WGrad = T.grad(cost=cost, wrt=self.W) 
     bGrad = T.grad(cost=cost, wrt=self.b) 
     return [[self.W, self.W - WGrad * rate], 
       [self.b, self.b - bGrad * rate]] 


class OutputLayer(object): 
    def __init__(self, input, layerShape): 
     self.input = input 
     self.W = theano.shared(np.random.normal(0,1,layerShape)) 
     self.b = theano.shared(np.random.normal(0,1,layerShape[1])) 
     self.output = T.nnet.nnet.sigmoid(T.dot(self.input,self.W)+self.b) 

    def train(self, cost, rate): 
     WGrad = T.grad(cost=cost, wrt=self.W) 
     bGrad = T.grad(cost=cost, wrt=self.b) 
     return [[self.W, self.W - WGrad * rate], 
       [self.b, self.b - bGrad * rate]] 

class Model(object): 
    def __init__(self, inputNodes, hiddenNodes, outputNodes, rate): 
     self.x = T.matrix() 
     self.y_ = T.matrix() 

     hiddenLayer = HiddenLayer(self.x, (inputNodes, hiddenNodes)) 
     outputLayer = OutputLayer(hiddenLayer.output, (hiddenNodes, outputNodes)) 
     self.layers = [hiddenLayer, outputLayer] 

     self.y = outputLayer.output 

     self.train = self.buildTrainOp(rate) 
     self.infer = self.buildInferenceOp() 

    def buildTrainOp(self, rate): 
     cost = T.mean(T.sqr(self.y - self.y_)) 
     updates = [update for updates in [layer.train(cost, rate) for layer in self.layers] for update in updates] 
     return theano.function(inputs=[self.x, self.y_], outputs=cost, updates=updates) 

    def buildInferenceOp(self): 
     return theano.function(inputs=[self.x], outputs=self.y) 

nn = Model(2, 3, 1, 0.05) 
print(nn.infer([[0,0],[1,0],[0,1],[1,1]])) 

xorTableX = [[0,0],[1,0],[0,1],[1,1]] 
xorTableY = [[i!=j] for i,j in xorTableX] 
for i in range(100000): 
    batchX = xorTableX 
    batchY = xorTableY 
    nn.train(batchX, batchY) 

print(nn.infer([[0,0],[1,0],[0,1],[1,1]])) 

Et si vous avez une pointe sur les bonnes pratiques et les conventions en En utilisant Theano, j'adorerais en entendre parler aussi. Merci! ** Éditer: ** Fait intéressant, lors de l'ajout d'une deuxième couche cachée de la même taille, la "mauvaise" sortie est [0.5, 0.5, 0.5, 0.5]. Mais encore une fois, je reçois toujours la sortie correcte environ la moitié du temps.

+0

avez-vous essayé d'utiliser l'entropie croisée pour le coût ('T.nnet.binary_crossentropy')? Et à quoi ressemblent vos courbes d'entraînement? – Simon

Répondre

0

D'oh! C'était douloureusement évident. C'est pourquoi je ne devrais pas programmer tard dans la nuit.

Je suis passé à ReLU pour ma couche cachée pour voir quel effet cela aurait sur le NN mais j'ai oublié de revenir à Tanh. Evidemment, ReLU ne tiendra pas compte des poids négatifs requis.