2016-10-30 3 views
2

Ich gehe durch Daniel Nouri tutorial auf Gesichtserkennung mit CNNs, und ich bin auf ein Stück des Codes gestoßen, die ich nicht verstehe. Daniel ist die Definition einer Klasse am Ende jeder Iteration während des Trainings des Netzes genannt zu werden, das wird entscheiden, ob das Training vorzeitig beenden sollte:Wie funktioniert die __call__-Methode dieser Klasse, wenn sie ohne geeignete Argumente aufgerufen wird?

class EarlyStopping(object): 
    def __init__(self, patience=100): 
     self.patience = patience 
     self.best_valid = np.inf 
     self.best_valid_epoch = 0 
     self.best_weights = None 

    def __call__(self, nn, train_history): 
     current_valid = train_history[-1]['valid_loss'] 
     current_epoch = train_history[-1]['epoch'] 
     if current_valid < self.best_valid: 
      self.best_valid = current_valid 
      self.best_valid_epoch = current_epoch 
      self.best_weights = nn.get_all_params_values() 
     elif self.best_valid_epoch + self.patience < current_epoch: 
      print("Early stopping.") 
      print("Best valid loss was {:.6f} at epoch {}.".format(
       self.best_valid, self.best_valid_epoch)) 
      nn.load_params_from(self.best_weights) 
      raise StopIteration() 

Dies macht einen Sinn, aber die tatsächliche Umsetzung in der Code sieht so aus:

net8 = NeuralNet(
# ... 
on_epoch_finished=[ 
    AdjustVariable('update_learning_rate', start=0.03, stop=0.0001), 
    AdjustVariable('update_momentum', start=0.9, stop=0.999), 
    EarlyStopping(patience=200), 
    ], 
# ... 
) 

Offensichtlich nennt Daniel die Klasse als eine Funktion. Allerdings verstehe ich nicht, wie er es ohne die Argumente nennt, die in __call__(args) gezeigt werden. Ist es so, dass die Dinge im Quellcode von nolearn implementiert werden sollen? Ich bin verwirrt, wie das Netzwerk nn und train_history verwenden kann, ohne dass diese in die Funktion übergeben werden.

Antwort

4

Er beruft nicht __call__ mit EarlyStopping(patience=200), sondern er *EarlyStopping.__init__ mit einer Signatur von Aufrufen ist:

def __init__(self, patience=100): 

und bietet einen alternativen Wert für patience; Dies entspricht vollständig den verfügbaren Argumenten für __init__.

EarlyStopping.__call__ wird auf der Instanz aufgerufen; das heißt, wenn die Reihenfolge der Aufrufe war:

e = EarlyStopping(patience = 200) 
e(patience=50) # TypeError Raised 

würde ein entsprechender Fehler ausgelöst werden.


* Die Klammern, die Sie aus werfen machen tatsächlich einen Anruf. Der Aufruf erfolgt nicht an EarlyStopping.__call__, sondern an type.__call__, die (Meta-) Klasse EarlyStopping. type.__call__ ist die erste Aktion, die von Python ausgeführt wird, wenn Sie ein Objekt initialisieren. Es wird aufgerufen, akzeptiert alle übergebenen Argumente und ruft (nach einigen anderen Aktionen) __new__ und __init__ in dieser Reihenfolge auf; im Wesentlichen __init__ wird indirekt mit einem Argument von patience=100 aufgerufen.

+0

Ok, sollte ich also annehmen, dass das initialisierte Objekt dann irgendwo im Quellcode für NeuralNet mit den entsprechenden Argumenten aufgerufen wird? Es scheint mir eine seltsame Art, diese Funktion zu implementieren. Warum nicht einfach eine Funktion übergeben? –

Verwandte Themen