首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >PyTorch激活函数是否最好作为字段存储?

PyTorch激活函数是否最好作为字段存储?
EN

Stack Overflow用户
提问于 2022-07-03 22:10:06
回答 1查看 65关注 0票数 0

在PyTorch中,一个简单的神经网络的例子可以在https://visualstudiomagazine.com/articles/2020/10/14/pytorch-define-network.aspx上找到。

代码语言:javascript
运行
复制
class Net(T.nn.Module):
  def __init__(self):
    super(Net, self).__init__()
    self.hid1 = T.nn.Linear(4, 8)  # 4-(8-8)-1
    self.hid2 = T.nn.Linear(8, 8)
    self.oupt = T.nn.Linear(8, 1)

    T.nn.init.xavier_uniform_(self.hid1.weight)
    T.nn.init.zeros_(self.hid1.bias)
    T.nn.init.xavier_uniform_(self.hid2.weight)
    T.nn.init.zeros_(self.hid2.bias)
    T.nn.init.xavier_uniform_(self.oupt.weight)
    T.nn.init.zeros_(self.oupt.bias)

  def forward(self, x):
    z = T.tanh(self.hid1(x)) 
    z = T.tanh(self.hid2(z))
    z = T.sigmoid(self.oupt(z))
    return z

上面的一个显著特征是,层被存储为网络对象中的字段(正如它们所需要的那样,因为它们包含了需要跨培训阶段记住的权重),但是激活函子(如tanh )是在每次调用forward时重新创建的。作者说:

二进制分类网络最常见的结构是在__init__()方法中定义网络层及其相关的权重和偏差,并在forward()方法中定义输入输出计算。

当然可以。另一方面,也许存储函子比在每次调用forward时重新创建函子要快得多。第三,它不太可能产生任何可测量的差异,这意味着它可能最终是一个代码风格的问题。

这是最常见的做法吗?无论哪种方式都有任何技术优势,还是仅仅是风格问题?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-07-04 09:59:27

关于“存储”函子

代码段没有“重新创建”任何内容--调用torch.tanh(x)实际上只是调用由带有参数的torch包导出的函数x

其他方法

我认为这个片段是一个小的神经块的一个公平的例子,是使用和忘记,或只是不打算参数化。根据您的意图,当然还有其他的选择,但是您必须权衡增加的复杂性是否具有任何价值。

  • activation函数为strings

允许从固定集合中选择激活函数

代码语言:javascript
运行
复制
class Model(torch.nn.Module):
   def __init__(..., activation_function: Literal['tanh'] | Literal['relu']):
       ...
       if activation_function == 'tanh':
           self.activation_function = torch.tanh
       elif activation_function == 'relu':
           self.activation_function = torch.relu
       else: 
           raise ValueError(f'activation function {activation_function} not allowed, use tanh or relu.'}
      

    def forward(...) -> Tensor:
        output = ...
        return self.activation_function(output)

  • activation函数为callables

使用任意模块或函数作为激活

代码语言:javascript
运行
复制
class Model(torch.nn.Module):
   def __init__(..., activation_function: torch.nn.Module | Callable[[Tensor], Tensor]):
      self.activation_function = activation_function

   def forward(...) -> Tensor:
       output = ...
       return self.activation_function(output)

例如,它的工作方式如下

代码语言:javascript
运行
复制
def cube(x: Tensor) -> Tensor: return x**3

cubic_model = Model(..., activation_function=cube)

上述示例与代码片段之间的关键区别在于,后者是透明的和可调整的wrt。对于所使用的激活,您可以检查激活函数(即model.activation_function),并对其进行更改(在初始化之前或之后),而对于原始片段,它是不可见的,并被放入模型的功能中(为了用不同的函数复制模型,您需要从头开始定义它)。

总的来说,我认为最好的方法是创建小的、本地可调的块,这些块尽可能地是参数化的,并将它们封装到更大的块中,对包含的参数进行概括。也就是说,如果您的大模型由5个线性层组成,您可以为一个层(包括退出、层规范等)制作一个单一的激活参数包装器,然后为N层流制作另一个包装器,它要求使用哪一个激活函数来初始化其子层。换句话说,当您预料到这将使您避免额外的工作和将来复制粘贴代码时,请进行泛化和参数化,但不要做过头,否则您最终会远离您原来的规范和需求。

ps:我不知道调用激活函数函子是否合理。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/72850229

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档