当没有正确的参数调用时,这个类的__call__方法如何工作?

时间:2016-10-30 08:51:20

标签: python python-2.7 class python-3.x nolearn

我正在通过Daniel Nouri的tutorial使用CNN进行面部识别,我遇到了一些我不理解的代码。 Daniel正在定义一个在网络培训期间每次迭代结束时调用的类,它将决定培训是否应该提前停止:

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的源代码中实现了什么?我很困惑网络如何知道使用nntrain_history而不将这些传递给函数。

1 个答案:

答案 0 :(得分: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}类EarlyStoppingtype.__call__是Python初始化对象时执行的第一个操作,它被称为接受传递的任何参数,然后(在其他一些操作之后)按此顺序调用__new____init__;实质上__init__是通过patience=100的参数间接调用的。