我正在通过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的源代码中实现了什么?我很困惑网络如何知道使用nn
和train_history
而不将这些传递给函数。
答案 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}类EarlyStopping
。 type.__call__
是Python初始化对象时执行的第一个操作,它被称为接受传递的任何参数,然后(在其他一些操作之后)按此顺序调用__new__
和__init__
;实质上__init__
是通过patience=100
的参数间接调用的。