AllenNLP框架学习笔记(模型篇之初始化与正则化)

初始化

在某些情况下,用户想以特定方式初始化模型参数(例如,使用Xavier方法初始化)。不用担心, AllenNLP在包含初始化逻辑的模型构造函数中提供了一种方便的抽象方法,该方法使得基于regex匹配的参数名应用到特定的初始化变得简单。

为了初始化单个模型参数,可以在AllenNLP中使用InitializersInitializers基本上只是Python方法,它们接受张量参数并对其应用一些特定的操作。在大多数情况下,它们只是PyTorchinitializers(在torch.nn.init中的方法)的弱包装。Initializers将参数(比如meanstd)进行转换,使它们成为应用于张量的单参数函数。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"])

第一个参数是(regexinitializer)的列表,其中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代码中或通过配置文件)时,传递一个名为regularizerRegularizerApplicater参数。通过这种方式,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()方法中。

参考资料

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值