参考来源:
知乎问答:PyTorch 中,nn 与 nn.functional 有什么区别?
[

](https://www.zhihu.com/question/66782101/answer/579393790)

1. 两者的相同之处:

  • **nn.xxx****nn.functional.xxx** 的实际功能是相同的,即 nn.Conv2dnn.functional.conv2d 都是进行卷积,nn.Dropoutnn.functional.dropout 都是进行 dropout,。。。。。;
  • 运行效率也是近乎相同。

nn.functional.xxx 是函数接口,而 nn.xxxnn.functional.xxx 的类封装,并且 **nn.xxx** 都继承于一个共同祖先 **nn.Module** 。这一点导致 nn.xxx 除了具有 nn.functional.xxx 功能之外,内部附带了 nn.Module 相关的属性和方法,例如 train()eval()load_state_dictstate_dict 等。

2. 两者的差别之处:

  • 两者的调用方式不同。

nn.xxx 需要先实例化并传入参数,然后以函数调用的方式调用实例化的对象并传入输入数据。

  1. inputs = torch.rand(64, 3, 244, 244)
  2. conv = nn.Conv2d(in_channels=3, out_channels=64, kernel_size=3, padding=1)
  3. out = conv(inputs)

nn.functional.xxx 同时传入输入数据和 weight、bias 等其他参数 。

  1. weight = torch.rand(64,3,3,3)
  2. bias = torch.rand(64)
  3. out = nn.functional.conv2d(inputs, weight, bias, padding=1)
  • **nn.xxx** 继承于 **nn.Module**, 能够很好的与 **nn.Sequential** 结合使用, 而 **nn.functional.xxx** 无法与 **nn.Sequential** 结合使用。

    1. fm_layer = nn.Sequential(
    2. nn.Conv2d(3, 64, kernel_size=3, padding=1),
    3. nn.BatchNorm2d(num_features=64),
    4. nn.ReLU(),
    5. nn.MaxPool2d(kernel_size=2),
    6. nn.Dropout(0.2)
    7. )
  • **nn.xxx** 不需要你自己定义和管理 **weight**;而 **nn.functional.xxx** 需要你自己定义 **weight** ,每次调用的时候都需要手动传入 **weight** ,不利于代码复用。

使用 nn.xxx 定义一个CNN:

  1. class CNN(nn.Module):
  2. def __init__(self):
  3. super(CNN, self).__init__()
  4. self.cnn1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=5,padding=0)
  5. self.relu1 = nn.ReLU()
  6. self.maxpool1 = nn.MaxPool2d(kernel_size=2)
  7. self.cnn2 = nn.Conv2d(in_channels=16, out_channels=32, kernel_size=5, padding=0)
  8. self.relu2 = nn.ReLU()
  9. self.maxpool2 = nn.MaxPool2d(kernel_size=2)
  10. self.linear1 = nn.Linear(4 * 4 * 32, 10)
  11. def forward(self, x):
  12. x = x.view(x.size(0), -1)
  13. out = self.maxpool1(self.relu1(self.cnn1(x)))
  14. out = self.maxpool2(self.relu2(self.cnn2(out)))
  15. out = self.linear1(out.view(x.size(0), -1))
  16. return out

使用 nn.function.xxx 定义一个与上面相同的 CNN:

  1. class CNN(nn.Module):
  2. def __init__(self):
  3. super(CNN, self).__init__()
  4. self.cnn1_weight = nn.Parameter(torch.rand(16, 1, 5, 5))
  5. self.bias1_weight = nn.Parameter(torch.rand(16))
  6. self.cnn2_weight = nn.Parameter(torch.rand(32, 16, 5, 5))
  7. self.bias2_weight = nn.Parameter(torch.rand(32))
  8. self.linear1_weight = nn.Parameter(torch.rand(4 * 4 * 32, 10))
  9. self.bias3_weight = nn.Parameter(torch.rand(10))
  10. def forward(self, x):
  11. x = x.view(x.size(0), -1)
  12. out = F.conv2d(x, self.cnn1_weight, self.bias1_weight)
  13. out = F.relu(out)
  14. out = F.max_pool2d(out)
  15. out = F.conv2d(x, self.cnn2_weight, self.bias2_weight)
  16. out = F.relu(out)
  17. out = F.max_pool2d(out)
  18. out = F.linear(x, self.linear1_weight, self.bias3_weight)
  19. return out

上面两种定义方式得到 CNN 功能都是相同的,至于喜欢哪一种方式,是个人口味问题,但 PyTorch 官方推荐:具有学习参数的(例如,**conv2d****linear****batch_norm**)采用 nn.xxx 方式,没有学习参数的(例如,**maxpool****loss func****activation func**)等根据个人选择使用 nn.functional.xxx 或者 nn.xxx 方式。但关于 dropout,个人强烈推荐使用 nn.xxx 方式,因为一般情况下只有训练阶段才进行 dropout ,在 eval 阶段都不会进行 dropout 。使用 nn.xxx 方式定义 dropout ,在调用 model.eval() 之后,model 中所有的 dropout layer 都关闭,但以 nn.function.dropout 方式定义 dropout ,在调用 model.eval() 之后并不能关闭 dropout 。

  1. class Model1(nn.Module):
  2. def __init__(self):
  3. super(Model1, self).__init__()
  4. self.dropout = nn.Dropout(0.5)
  5. def forward(self, x):
  6. return self.dropout(x)
  7. class Model2(nn.Module):
  8. def __init__(self):
  9. super(Model2, self).__init__()
  10. def forward(self, x):
  11. return F.dropout(x)
  12. m1 = Model1()
  13. m2 = Model2()
  14. inputs = torch.rand(10)
  15. print(m1(inputs))
  16. print(m2(inputs))
  17. print(20 * '-' + "eval model:" + 20 * '-' + '\r\n')
  18. m1.eval()
  19. m2.eval()
  20. print(m1(inputs))
  21. print(m2(inputs))

输出:
image.png
从上面输出可以看出 m2 调用了 eval 之后,dropout 照样还在正常工作。当然如果你有强烈愿望坚持使用 nn.functional.dropout ,也可以采用下面方式来补救。

  1. class Model3(nn.Module):
  2. def __init__(self):
  3. super(Model3, self).__init__()
  4. def forward(self, x):
  5. return F.dropout(x, training=self.training)

3. 什么时候使用 nn.functional.xxx ,什么时候使用 nn.xxx

这个问题依赖于你要解决你问题的复杂度和个人风格喜好。在 nn.xxx 不能满足你的功能需求时, nn.functional.xxx 是更佳的选择,因为 nn.functional.xxx 更加的灵活(更加接近底层),你可以在其基础上定义出自己想要的功能。

个人偏向于在能使用 nn.xxx 情况下尽量使用,不行再换 nn.functional.xxx ,感觉这样更能显示出网络的层次关系,也更加的纯粹(所有 layermodel 本身都是 Module ,一种和谐统一的感觉)。