【PyTorch→TensorFlow変換】変換できるレイヤー／できないレイヤー＆おすすめバックボーン全部まとめました

![アイキャッチ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4046428/651c4914-7779-47c3-b366-8ce6b4d3749c.png)


## はじめに

転職活動中の近藤です。\
物体検出モデルを「**PyTorchで学習 → TensorFlowで推論**」する流れに興味を持ち、\
変換時にどこで詰まりやすいのか、どうすれば現実的に解決できるのかを調べています。

レイヤー選びを間違えてしまうと、変換時にエラーが発生したり、\
一見うまく変換できたように見えても、推論時に想定外の動作が起きることがあります。\
また、バックボーンに使用するモデル選びにも注意が必要です。**構造やレイヤーの種類によっては、変換がうまくいかないケースもあるからです。**

この記事では、**PyTorchのモデルをTensorFlow形式に変換するうえで、実装時に注意したいレイヤーとバックボーンモデル**についてまとめました。

## ✅ この記事でわかること

- PyTorch→TensorFlowに変換しやすいレイヤー
- 変換に注意すべきレイヤー・書き方
- バックボーンとして使いやすく、変換もしやすいモデル
- よく使われがちだけど変換でハマりやすいモデル

## 🎯 1. 変換しやすい「標準レイヤー」一覧

以下は、PyTorch → ONNX → TensorFlow の変換でよく使われる、変換しやすいレイヤーをカテゴリ別にまとめたものです。

### 🔹 畳み込み系（CNN）

| **レイヤー名** | **変換のしやすさ** | **内容** |
|----------------|--------------------|----------|
| nn.Conv1d / nn.Conv2d / nn.Conv3d | ◎ ONNX変換安定 | 畳み込み層（1〜3次元対応） |
| nn.ConvTranspose2d | ○ TF側のUpSamplingにマッピングされる | 転置畳み込み（アップサンプリング） |

### 🔹 全結合系（MLP）

| **レイヤー名** | **変換のしやすさ** | **内容** |
|---------------|--------------------|----------|
| nn.Linear | ◎ ONNX変換OK | 全結合（Fully Connected）層 |

### 🔹 活性化関数（非線形変換）

| **レイヤー名** | **変換のしやすさ** | **内容** |
|----------------------------|--------------------|-------------------------------|
| nn.ReLU, nn.LeakyReLU     | ◎ 安定変換           | 非線形変換（ReLU系活性化関数） |
| nn.Sigmoid, nn.Tanh        | ◎ 変換OK             | 非線形変換（Sigmoid/Tanh関数） |
| nn.Softmax                 | △ 注意：dim を明示すると安全 | 非線形変換（Softmax関数）     |

### 🔹 プーリング層

| **レイヤー名** | **変換のしやすさ** | **内容** |
|----------------|--------------------|----------|
| nn.MaxPool1d / nn.MaxPool2d / nn.MaxPool3d | ◎ 変換安定 | 最大プーリング（1〜3次元） |
| nn.AvgPool1d / nn.AvgPool2d               | ◎ ONNX変換OK | 平均プーリング（1〜2次元） |
| nn.AdaptiveAvgPool2d                      | ◎ TF対応 | 出力サイズ固定に便利 |

### 🔹 正規化

| **レイヤー名** | **変換のしやすさ** | **内容** |
|-------------------------------|---------------------------|------------------|
| nn.BatchNorm1d / nn.BatchNorm2d | ◎ 推論時は変換OK         | バッチ正規化 |
| nn.LayerNorm                   | ◎ TF対応                 | 層正規化 |
| nn.GroupNorm                   | ○ 注意：num_groupsに注意 | グループ正規化 |

### 🔹 その他ユーティリティ

| **レイヤー名** | **変換のしやすさ** | **内容** |
|----------------|--------------------|----------|
| nn.Dropout     | ◎ 推論モードでは除外 | ドロップアウト（正則化） |
| nn.Identity    | ◎ パススルー        | 恒等変換 |
| nn.Flatten     | ◎ ONNX変換に強い    | テンソルの平坦化 |
| nn.Upsample    | △ 注意：mode依存（nearest/bilinear） | アップサンプリング |
| nn.Sequential  | ◎ 非常に変換優しい   | 複数モジュールの順序定義（構成に最適） |


> 💡 **これらのレイヤーだけで構成することで変換成功率が上がります。\
> 特に `nn.Sequential` を使って構成することで、変換の安定性がさらに高まります。**

## ❌ 2. 変換に注意が必要なレイヤー・書き方

以下のような書き方やレイヤーは注意が必要です。

| **書き方・レイヤー** | **注意点・備考** |
|------------------------|------------------|
| `view()` | ONNXで失敗しやすい。`nn.Flatten()` を使うと安全。 |
| `forward()` に `if`, `for`, `while` | 動的な制御構文はONNXでサポートされず変換不可。 |
| `torch.cat`, `torch.stack` | 次元不一致や axis 指定ミスに注意。ONNXで形状が壊れることがある。 |
| `Softmax` の `dim` を省略 | `dim` を明示しないと変換エラー・警告が出る可能性あり。 |
| 自作レイヤー（クラスや関数） | 非対応Opを含みやすく変換失敗の原因に。例：`class MyCustomBlock(nn.Module): ...` |
| `einsum`, `masked_fill`, `gather` など | ONNX非対応 or opset依存が強い。TensorFlow変換でも未サポートの可能性あり。 |
| `torch.nn.functional.grid_sample()` | 非対応Op。ONNX経由で変換するのは非常に困難。 |
| `permute()` | 次元の扱いに注意。変換後の形状がおかしくなるケースあり。 |
| 外部ライブラリ由来のレイヤー（例：`timm`等） | PyTorch標準でない実装はONNXが非対応のことが多い。 |


### 🛠️ 修正例（NG → OK）

#### 🔸 `view()` の代わりに `nn.Flatten()`

```python
# ❌ NG
x = x.view(x.size(0), -1)

# ✅ OK
x = nn.Flatten()(x)
```

#### 🔸 `Softmax` は dim を明示する

```python
# ❌ NG（dimを省略）
x = torch.nn.Softmax()(x)

# ✅ OK（dimを明示）
x = torch.nn.Softmax(dim=1)(x)
```

#### 🔸 動的な `if` は避けて構造を分ける

```python
# ❌ NG（動的制御）
def forward(self, x):
    if x.shape[1] > 100:
        x = self.block1(x)
    else:
        x = self.block2(x)
    return x
```

```python
# ✅ OK（構造で分けて外部で制御）
class ModelA(nn.Module):
    def __init__(self):
        super().__init__()
        self.block = Block1()

    def forward(self, x):
        return self.block(x)

class ModelB(nn.Module):
    def __init__(self):
        super().__init__()
        self.block = Block2()

    def forward(self, x):
        return self.block(x)

# 外部で条件を分けてモデルを選ぶ
model = ModelA() if input_tensor.shape[1] > 100 else ModelB()
output = model(input_tensor)
```

> 💡 **レイヤーだけでなく「どう書くか」も変換成功率に大きく影響します。
> ONNXでトレース可能な静的でシンプルな構造を意識することが大事！**



## 🎯 3. バックボーンに適しており変換しやすいモデル

「構造がシンプル」「変換の実績がある」「検出モデルに組み込みやすい」などの観点から、特におすすめできるモデルをまとめました。

| **モデル名** | **特徴** | **変換のしやすさ** | **理由・補足** |
|--------------|----------|----------------------|------------------|
| ResNet (18/34/50/101) | 標準的なCNN構造、FPNとの相性が良い | ◎ 実績豊富 | ONNX・TensorFlow変換の実績多数。PyTorch公式でも検出モデルに利用されている。精度◎ / 速度○ |
| MobileNet (V1/V2/V3) | 軽量で高速、組み込み用途にも強い | ◎ 軽量高速 | SSD/SSDLite系での使用実績あり。ONNX変換も比較的安定。精度○ / 速度◎ |
| VGG(16/19) | 古典的で構造がシンプル | ◎ 構造単純 | SSDなどで使用実績あり。構造が直列でONNX変換しやすいが、モデルサイズはやや大きめ。精度○ / 速度△ |

> 💡 **これらのモデルを選んでおけば、変換のトラブルを避けやすいので安心**

## ❌ 4. よく使われるが変換時に詰まりやすい画像分類モデル（バックボーンに使うと危険）

ここでは、**変換時のトラブルが多いが、選ばれがちなバックボーンモデル**をまとめました。

| **モデル名** | **特徴** | **変換のしやすさ** | **理由・補足** |
|----------------------------|-----------------------------|------------------|--------------------------------------------------------------------|
| 自作CNNやResNet改造版 | 独自構造・カスタムレイヤーを含む場合が多い | × 非推奨 | 制御構文（if/for）や未対応OpでONNX変換に失敗しやすい。検出モデル構築にも一貫性が欠けることが多い。精度△〜？ / 速度？ |
| EfficientNet (B0~B7) | 高精度・軽量だが構造が複雑 | △〜○ 実装依存 | 実装依存でONNX変換に失敗することがある。TensorFlow側と構造の差異が出やすい。精度◎ / 速度○ |
| DenseNet (121/169/201/161) | 特徴マップの連結が複雑 | △ 複雑構造 | ONNXの演算サポートと相性が悪いケースあり。接続先（検出ヘッド）設計にも工夫が必要。精度○ / 速度△ |
| Inception v3 / GoogLeNet | 枝分かれ構造、出力が複数になる場合がある | △ 分岐構造注意 | 変換自体は可能だが複雑。検出用バックボーンとして扱いにくい構造。精度○ / 速度△ |


> 💡 **見慣れた高精度モデルでも、構造が複雑だったりONNX未対応の処理を含んでいると、変換エラーや不具合の原因になるので注意**

## ✍️ おわりに

物体検出モデルを PyTorch → TensorFlow へ変換するためには、
**TensorFlowへの変換を前提としたモデル設計**が何より大切です。

そのために何を想定して設計すべきかを知る手助けができたらいいなと思っていますので、
これからも記事を書いていきます🌱
参考になった方は、**いいね・ストック・フォロー**してもらえると嬉しいです！

---
<!--
本記事は、**「PyTorch→TensorFlowモデル変換」シリーズ**の一部です。\
変換で詰まりやすいポイントや、変換精度・互換性の話題についても別記事でまとめています。

🔗 上位記事はこちら\
→ [PyTorch→TensorFlow モデル変換 全体ガイド（記事リンク）]

📚 詰まりシリーズはこちらから

- [PyTorch→TensorFlow変換で精度が落ちる理由とその対策（記事リンク）]
- [tf2onnxで変換時に詰まった演算子まとめⅡ回避策（記事リンク）]

-->