初始化
在某些情况下,用户想以特定方式初始化模型参数(例如,使用Xavier
方法初始化)。不用担心, AllenNLP
在包含初始化逻辑的模型构造函数中提供了一种方便的抽象方法,该方法使得基于regex
匹配的参数名应用到特定的初始化变得简单。
为了初始化单个模型参数,可以在AllenNLP
中使用Initializers
,Initializers
基本上只是Python方法,它们接受张量参数并对其应用一些特定的操作。在大多数情况下,它们只是PyTorch
的initializers
(在torch.nn.init
中的方法)的弱包装。Initializers
将参数(比如mean
和std
)进行转换,使它们成为应用于张量的单参数函数。AllenNLP
提供了一个方便的抽象,允许用户将它们应用到整个模型中。
用户可以根据需要在Model
的构造函数中手动实例化特定的Initializers
,尽管可以直接调用torch.nn.init
函数来这么做。本着将代码与配置分离的精神(这是AllenNLP
中的常见设计原则),AllenNLP
提供了一个名为InitializerApplicator
的类,该类给出了正则表达式及其对应的初始化器的列表,并基于模型参数名称上的正则表达式匹配项应用了初始化。 AllenNLP
模型通常将 initializer
用作构造函数参数,以便可以轻松配置初始化。以下是实例化InitializerApplicator
的构造函数调用:
applicator = InitializerApplicator(
regexes = [
("parameter_regex_match1", NormalInitializer(mean=0.01, std=0.1)),
("parameter_regex_match2", UniformInitializer())],
prevent_regexes = ["prevent_init_regex"])
第一个参数是(regex
,initializer
)的列表,其中regex
是与模型参数名匹配的正则表达式,initializer
是用于初始化的初始值设定项。用户也可以将regex
的列表指定为prevent_regex
,在这种情况下,任何匹配正则表达式的参数都将被阻止初始化。在下面的代码示例中,创建了一个玩具模型,并用两种不同的方式初始化它的参数:使用单独的初始值设定项和使用InitializerApplicator
:
import torch
from allennlp.nn.initializers import InitializerApplicator, \
XavierUniformInitializer, ConstantInitializer, NormalInitializer
class Net(torch.nn.Module):
def __init__(self):
super().__init__()
self.linear1 = torch.nn.Linear(2, 3)
self.linear2 = torch.nn.Linear(3, 2)
self.conv = torch.nn.Conv1d(2, 2, 2)
def forward(self, inputs):
pass
model = Net()
print('Initial parameters:')
for name, param in model.named_parameters():
print(name, param)
init_uniform = XavierUniformInitializer()
init_uniform(model.linear1.weight)
init_uniform(model.linear2.weight)
init_const = ConstantInitializer(val=10.)
init_const(model.linear1.bias)
init_const(model.linear2.bias)
init_normal = NormalInitializer(mean=0., std=10.)
init_normal(model.conv.weight)
init_normal(model.conv.bias)
print('\nAfter applying initializers individually:')
for name, param in model.named_parameters():
print(name, param)
model = Net()
applicator = InitializerApplicator(
regexes=[
('linear.*weight', init_uniform),
('linear.*bias', init_const),
('conv.*', init_normal)
])
applicator(model)
print('\nAfter applying an applicator:')
for name, param in model.named_parameters():
print(name, param)
值得注意的是,在模型的构造函数中需要将 InitializerApplicator
作为参数,如下:
class YourModel(Model):
def __init__(
self,
vocab: Vocabulary,
...
initializer: InitializerApplicator = InitializerApplicator()
) -> None:
super().__init__(vocab)
...
initializer(self)
正则化
正则化,就是通过收缩的办法,限制模型变的越来越「大」,牺牲样本内误差,降低模型(参数)的误差,从而提高样本外的预测效果,防止过拟合,又称模型复杂度惩罚项。
一般用的比较多的有L1和L2正则,L1正则化就是在loss function后边所加正则项为L1范数,加上L1范数容易得到稀疏解(0比较多)。L2正则化就是loss function后边所加正则项为L2范数的平方,加上L2正则相比于L1正则来说,得到的解比较平滑(不是稀疏),但是同样能够保证解中接近于0(但不是等于0,所以相对平滑)的维度比较多,降低模型的复杂度。
AllenNLP
中的正则化与初始化的工作方式类似。AllenNLP
提供了一个称为 Regularizers
的抽象,它是计算并返回给定模型参数的正则项(标量张量)方法的一个薄封装。
在许多情况下,用户可能希望只对模型的一部分应用正则(例如,只对权重应用L2正则,而不对偏差应用L2正则)。AllenNLP
包含一个RegularizerApplicator
类,它类似于InitializerApplicator
,只是它返回惩罚项而不是修改模型参数。RegularizerApplicator
,给定正则表达式及其相应正则化器的列表,基于正则表达式匹配对模型参数名称应用正则化,并返回所有计算的惩罚的总和。下面是实例化RegularizerApplicator
的构造函数调用:
applicator = RegularizerApplicator(
regexes=[
("parameter_regex_match1", L1Regularizer(alpha=.01)),
("parameter_regex_match2", L2Regularizer())
])
这将对匹配“parameter_regex_match1”的所有模型参数应用L1正则化方法(alpha=0.01),对匹配“parameter_regex_match2”的模型参数应用L1正则化方法(使用其默认参数)。模型返回通过get_Regulation_Pension()
方法计算的惩罚项,传给trainer
使用并添加到损失中。
要使模型支持正则化器,您需要做的就是确保模型构造函数接受kwargs
参数,并通过在构造函数中调用super().__init__(**kwargs)
将其传递给超类。然后,当构造模型(在python代码中或通过配置文件)时,传递一个名为regularizer
的RegularizerApplicater
参数。通过这种方式,RegularizerApplicator
会自动传递到基础模型。
在下面的代码中,以两种不同的正则化方式创建包括正则项的玩具模型为例:
import torch
from allennlp.nn.initializers import ConstantInitializer
from allennlp.nn.regularizers import L1Regularizer, L2Regularizer, RegularizerApplicator
class Net(torch.nn.Module):
def __init__(self):
super().__init__()
self.linear1 = torch.nn.Linear(2, 3)
self.linear2 = torch.nn.Linear(3, 2)
self.conv = torch.nn.Conv1d(2, 2, 2)
def forward(self, inputs):
pass
print('Using individual regularizers:')
model = Net()
init_const = ConstantInitializer(val=10.)
init_const(model.linear1.weight)
print(model.linear1.weight)
init_const(model.linear2.weight)
print(model.linear2.weight)
l1_regularizer = L1Regularizer(alpha=0.01)
print(l1_regularizer(model.linear1.weight)) # 0.01 * 10 * 6 = 0.6
l2_regularizer = L2Regularizer(alpha=0.01)
print(l2_regularizer(model.linear2.weight)) # 0.01 * (10)^2 * 6
print('Using an applicator:')
applicator = RegularizerApplicator(
regexes=[
('linear1.weight', L1Regularizer(alpha=.01)),
('linear2.weight', L2Regularizer())
])
print(applicator(model)) # 0.6 + 6
需要注意的是,这种方法使用的条件是在allennlp
的默认的trainer
中,如果是自定义的trainer
,是需要将正则项写入Model.forward()
方法中。
参考资料