回答編集履歴

2 修正

tiitoi

tiitoi score 21402

2020/09/01 23:50  投稿

## 解決方法
Backbone() 内で使用する model を `forward()` ではなく、 `__init__()` 内で属性として定義する。
```python
class Backbone(nn.Module):
   def __init__(self, backbone_type="ResNet50"):
       super(Backbone, self).__init__()
       self.backbone_type = backbone_type
       self.model = models._utils.IntermediateLayerGetter(
           models.resnet50(), {"layer1": 2, "layer2": 3, "layer3": 4}
       )
       if self.backbone_type == "ResNet50":
           self.model = models._utils.IntermediateLayerGetter(
               models.resnet50(), {"layer1": 2, "layer2": 3, "layer3": 4}
           )
   def forward(self, x):
       if self.backbone_type == "ResNet50":
           return self.model(x)
       return self.model(x)
```
## 原因について
Pytorch ではモデルを構成する層などは nn.Module を継承しています。
複数の nn.Module を組み合わせてモデルを作るわけですが、nn.Module 内で使用する nn.Module は、`__init__()` 内で属性として定義しないと、その nn.Module の子として自動的に認識されません。
### `__init__()` 内で属性として定義した場合
```python
class Net(torch.nn.Module):
   def __init__(self):
       super().__init__()
       # このモデルで使用する nn.Module はここで属性として定義する
       self.linear1 = torch.nn.Linear(10, 10)
       self.linear2 = torch.nn.Linear(10, 10)
   def forward(self, x):
       y = self.linear1(x)
       y = self.linear2(y)
       return y
net = Net()
# 子として認識されている
for child in net.children():
   print(child)
# Linear(in_features=10, out_features=10, bias=True)
# Linear(in_features=10, out_features=10, bias=True)
```
### `__init__()` 内で属性として定義しなかった場合
```python
class Net(torch.nn.Module):
   def __init__(self):
       super().__init__()
       # このモデルで使用する nn.Module はここで属性として定義する
       self.linear2 = torch.nn.Linear(10, 10)
   def forward(self, x):
       linear1 = torch.nn.Linear(10, 10)
       y = linear1(x)
       y = self.linear2(y)
       return y
net = Net()
# linear1 が子として認識されていない
for child in net.children():
   print(child)
# Linear(in_features=10, out_features=10, bias=True)
```
to("cuda") や cuda() は nn.Module 内の子の nn.Module を GPU に転送するというものですが、そもそも子として認識されていない nn.Module は転送されないままということになります。
質問のコードだと models._utils.IntermediateLayerGetter() の返り値を `__init__()` 内で属性として定義していなかったので、子として認識されておらず、`cuda()` を実行しても GPU に転送されないままとなっています。
コードを読むと、nn.module 周りがどのように実現されているかがわかります。
[torch.nn.modules.module — PyTorch 1.6.0 documentation](https://pytorch.org/docs/stable/_modules/torch/nn/modules/module.html#Module.children)
1 修正

tiitoi

tiitoi score 21402

2020/09/01 23:49  投稿

## 解決方法
Backbone() 内で使用する model を `forward()` ではなく、 `__init__()` 内で属性として定義する。
```python
class Backbone(nn.Module):
   def __init__(self, backbone_type="ResNet50"):
       super(Backbone, self).__init__()
       self.backbone_type = backbone_type
       self.model = models._utils.IntermediateLayerGetter(
           models.resnet50(), {"layer1": 2, "layer2": 3, "layer3": 4}
       )
   def forward(self, x):
       if self.backbone_type == "ResNet50":
           return self.model(x)
```
## 原因について
Pytorch ではモデルを構成する層などは nn.Module を継承しています。
複数の nn.Module を組み合わせてモデルを作るわけですが、nn.Module 内で使用する nn.Module は、`__init__()` 内で属性として定義しないと、その nn.Module の子として自動的に認識されません。
### `__init__()` 内で属性として定義した場合
```python
class Net(torch.nn.Module):
   def __init__(self):
       super().__init__()
       # このモデルで使用する nn.Module はここで属性として定義する
       self.linear1 = torch.nn.Linear(10, 10)
       self.linear2 = torch.nn.Linear(10, 10)
   def forward(self, x):
       y = self.linear1(x)
       y = self.linear2(y)
       return y
net = Net()
# 子として認識されている
for child in net.children():
   print(child)
# Linear(in_features=10, out_features=10, bias=True)
# Linear(in_features=10, out_features=10, bias=True)
```
### `__init__()` 内で属性として定義しなかった場合
```python
class Net(torch.nn.Module):
   def __init__(self):
       super().__init__()
       # このモデルで使用する nn.Module はここで属性として定義する
       self.linear2 = torch.nn.Linear(10, 10)
   def forward(self, x):
       linear1 = torch.nn.Linear(10, 10)
       y = linear1(x)
       y = self.linear2(y)
       return y
net = Net()
# linear1 が子として認識されていない
for child in net.children():
   print(child)
# Linear(in_features=10, out_features=10, bias=True)
```
to("cuda") や cuda() は nn.Module 内の子の nn.Module を GPU に転送するというものですが、そもそも子として認識されていない nn.Module は転送されないままということになります。
質問のコードだと models._utils.IntermediateLayerGetter() の返り値を `__init__()` 内で属性として定義していなかったので、子として認識されておらず、`cuda()` を実行しても GPU に転送されないままとなっています。
質問のコードだと models._utils.IntermediateLayerGetter() の返り値を `__init__()` 内で属性として定義していなかったので、子として認識されておらず、`cuda()` を実行しても GPU に転送されないままとなっています。
コードを読むと、nn.module 周りがどのように実現されているかがわかります。
[torch.nn.modules.module — PyTorch 1.6.0 documentation](https://pytorch.org/docs/stable/_modules/torch/nn/modules/module.html#Module.children)

思考するエンジニアのためのQ&Aサイト「teratail」について詳しく知る