2016-10-30 5 views
2

Я просматриваю Даниэль Нури tutorial на распознавание лица с помощью CNN, и я натолкнулся на немного кода, который я не понимаю. Даниэль является определение класса будет называться в конце каждой итерации во время обучения сети, которая будет решать, следует ли обучение остановить рано:Как работает этот метод __call__ этого класса, когда он вызван без правильных аргументов?

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() 

Это имеет некоторый смысл, однако фактическое осуществление в код выглядит так:

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), 
    ], 
# ... 
) 

Очевидно, что Даниэль вызывает класс как функцию. Однако я не понимаю, как он вызывает это без аргументов, приведенных в __call__(args). Это как раз то, как вещи должны быть реализованы в исходном коде nolearn? Я смущен тем, как сеть знает, как использовать nn и train_history без передачи данных в функцию.

ответ

4

Он не вызывая __call__ с EarlyStopping(patience=200), скорее, он вызова *EarlyStopping.__init__ с подписью:

def __init__(self, patience=100): 

и обеспечивая другое значение для patience; это полностью соответствует аргументам, доступным для __init__.

EarlyStopping.__call__ вызывается на примере; то есть, если последовательность вызовов была:

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

соответствующая ошибка была бы поднята.


* Скобки, которые бросают тебя на самом деле делает вызов. Вызов не производится до EarlyStopping.__call__, но до type.__call__, (meta) класс EarlyStopping. type.__call__ - это первое действие, выполняемое Python при инициализации объекта, оно получает вызванное принятие любых переданных аргументов и затем (после некоторых других действий) вызывает в этом порядке __new__ и __init__; по существу __init__ ссылается косвенно с аргументом patience=100.

+0

Хорошо, я должен предположить, что инициализированный объект затем вызывается где-то в исходном коде для NeuralNet с соответствующими аргументами? Мне кажется странным способом реализовать эту функцию. Почему бы просто не передать функцию? –