2

我正在阅读 Daniel Nouri 关于使用 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),
    ],
# ...
)

显然,Daniel 将类作为函数调用。但是,我不明白他是如何在没有显示的参数的情况下调用它的__call__(args)。这就是在 nolearn 的源代码中实现的方式吗?我对网络如何知道如何使用nn以及train_history没有将它们传递给函数感到困惑。

4

1 回答 1

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

于 2016-10-30T08:55:44.623 回答