自己教師あり学習、半教師あり学習、弱教師あり学習についてのメモ


# 自己教師あり学習、半教師あり学習、弱教師あり学習についてのメモとサンプルコード

*Grok 3 により生成されたメモです（xAI製）。現在の日付: 2025年4月6日*

## 目次
1. [自己教師あり学習（Self-Supervised Learning）](#自己教師あり学習self-supervised-learning)
   - [定義と概要](#定義と概要-1)
   - [仕組み](#仕組み-1)
   - [利点](#利点-1)
   - [応用例](#応用例-1)
   - [出典と主要研究](#出典と主要研究-1)
   - [現在の研究動向](#現在の研究動向-1)
   - [サンプルコード](#自己教師あり学習self-supervised-learningのサンプルコード)
2. [半教師あり学習（Semi-Supervised Learning）](#半教師あり学習semi-supervised-learning)
   - [定義と概要](#定義と概要-2)
   - [仕組み](#仕組み-2)
   - [利点](#利点-2)
   - [応用例](#応用例-2)
   - [出典と主要研究](#出典と主要研究-2)
   - [現在の研究動向](#現在の研究動向-2)
   - [サンプルコード](#半教師あり学習semi-supervised-learningのサンプルコード)
3. [弱教師あり学習（Weakly Supervised Learning）](#弱教師あり学習weakly-supervised-learning)
   - [定義と概要](#定義と概要-3)
   - [仕組み](#仕組み-3)
   - [利点](#利点-3)
   - [応用例](#応用例-3)
   - [出典と主要研究](#出典と主要研究-3)
   - [現在の研究動向](#現在の研究動向-3)
   - [サンプルコード](#弱教師あり学習weakly-supervised-learningのサンプルコード)
4. [まとめと比較](#まとめと比較)
5. [参考情報](#参考情報)
6. [注意点と拡張](#注意点と拡張)

---

## 自己教師あり学習（Self-Supervised Learning）

### 定義と概要
自己教師あり学習は、ラベル付けされていない大量のデータから特徴を学習する手法で、教師あり学習と教師なし学習の中間的なアプローチです。データ自体から「擬似ラベル（pseudo-labels）」を生成し、それを基にモデルを訓練します。たとえば、画像の一部を隠してその部分を予測させたり、文中の単語をマスクして予測させるなど、データ内在の構造を利用します。

### 仕組み
- **事前学習（Pre-training）**: ラベルなしデータでモデルを訓練し、一般的な特徴量を抽出。
- **タスク例**:
  - 画像分野: 画像の回転角度予測（RotNet）、ジグソーパズル解法（Jigsaw Puzzle）。
  - 自然言語処理（NLP）: マスクされた単語の予測（BERT）、次文予測（GPT）。
- **転移学習**: 事前学習したモデルを、少量のラベル付きデータで微調整（fine-tuning）して特定タスクに適用。

### 利点
- ラベル付きデータが不要なため、アノテーションコストを削減。
- 大規模データセット（例: インターネット上の画像やテキスト）を利用可能。
- 汎用性の高い特徴表現を獲得。

### 応用例
- **画像認識**: SimCLRやMoCo（Momentum Contrast）は、対照学習（contrastive learning）で高い性能を発揮。
- **NLP**: BERTやRoBERTaは、自己教師あり学習で事前訓練され、質問応答や感情分析で活用。

### 出典と主要研究
- Yann LeCunらが提唱した初期のアイデアに端を発し、近年では以下の論文が重要:
  - Chen, T., et al. (2020). "A Simple Framework for Contrastive Learning of Visual Representations" (SimCLR). *arXiv:2002.05709*.
  - He, K., et al. (2020). "Momentum Contrast for Unsupervised Visual Representation Learning" (MoCo). *CVPR 2020*.
  - Devlin, J., et al. (2018). "BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding". *arXiv:1810.04805*.

### 現在の研究動向
- **効率性向上**: 少ない計算リソースで高精度を実現する手法（例: BYOL - Bootstrap Your Own Latent）。
- **マルチモーダル**: 画像とテキストを同時に扱う自己教師あり学習（CLIP, DALL-E 2）。
- **理論的理解**: なぜ自己教師あり学習が効果的なのかを数学的に解明する研究が進む。

### 自己教師あり学習（Self-Supervised Learning）のサンプルコード
自己教師あり学習の例として、画像の回転予測（RotNet）を模擬します。画像をランダムに回転させ、その回転角度を予測するタスクでモデルを訓練します。

```python
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import torchvision.models as models

# データ準備（例: MNISTを使用）
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)

# 回転変換を適用する関数
def rotate_images(images):
    angles = torch.randint(0, 4, (images.size(0),))  # 0, 90, 180, 270度の4択
    rotated_images = torch.stack([torch.rot90(img, k.item(), [1, 2]) for img, k in zip(images, angles)])
    return rotated_images, angles

# モデル（シンプルなCNN）
model = models.resnet18(pretrained=False)
model.conv1 = nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3, bias=False)  # MNISTは1チャネル
model.fc = nn.Linear(512, 4)  # 4つの回転角度を予測

# 損失関数と最適化
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 訓練ループ
for epoch in range(3):  # 簡略化のため3エポック
    for images, _ in train_loader:
        rotated_images, angles = rotate_images(images)
        optimizer.zero_grad()
        outputs = model(rotated_images)
        loss = criterion(outputs, angles)
        loss.backward()
        optimizer.step()
    print(f"Epoch {epoch+1}, Loss: {loss.item()}")

print("自己教師あり学習の訓練が完了しました。")
```

#### 説明
- **タスク**: 画像をランダムに回転（0°, 90°, 180°, 270°）させ、その角度を予測。
- **目的**: ラベルなしデータから画像の特徴を学習。
- **応用**: 事前学習後に、分類タスクで微調整可能。

---

## 半教師あり学習（Semi-Supervised Learning）

### 定義と概要
半教師あり学習は、ラベル付きデータとラベルなしデータを組み合わせてモデルを訓練する手法です。少量のラベル付きデータと大量のラベルなしデータを活用し、ラベル付きデータの不足を補います。現実世界ではラベル付きデータが限られている場合が多いため、実用性が高いです。

### 仕組み
- **仮定**: データには内在する構造（クラスタリング仮説や平滑性仮説）があると仮定。
- **手法例**:
  - **自己訓練（Self-Training）**: モデルがラベルなしデータに擬似ラベルを付与し、再訓練。
  - **一貫性正則化（Consistency Regularization）**: データ拡張後の予測が一貫するよう訓練（例: MixMatch, UDA）。
  - **グラフベース**: データ間の関係をグラフで表現し、ラベルを伝播。

### 利点
- 少量のラベル付きデータで高精度を実現。
- アノテーションコストを大幅に削減。
- 実世界のデータセットに適応しやすい。

### 応用例
- **医療画像診断**: 少数の診断済み画像と大量の未診断画像でモデルを訓練。
- **音声認識**: 少量の書き起こしデータと大量の未書き起こし音声を使用。

### 出典と主要研究
- **古典的論文**:
  - Zhu, X. (2005). "Semi-Supervised Learning Literature Survey". *University of Wisconsin-Madison*.
- **最近の進展**:
  - Xie, Q., et al. (2019). "Unsupervised Data Augmentation for Consistency Training" (UDA). *NeurIPS 2019*.
  - Berthelot, D., et al. (2019). "MixMatch: A Holistic Approach to Semi-Supervised Learning". *NeurIPS 2019*.
- **追加文献**（[Qiita記事](https://qiita.com/koshian2/items/f4a458466b15bb91c7cb)より）:
  - Lee, D.-H. (2013). "Pseudo-Label: The Simple and Efficient Semi-Supervised Learning Method for Deep Neural Networks". *ICML 2013 Workshop on Challenges in Representation Learning*.

### 現在の研究動向
- **データ拡張の改良**: より強力なデータ拡張手法（例: RandAugment）を活用。
- **大規模モデルとの統合**: GPT-3やViT（Vision Transformer）との組み合わせ。
- **不均衡データ対応**: ラベル付きデータが偏っている場合のロバスト性向上。

### 半教師あり学習（Semi-Supervised Learning）のサンプルコード
#### サンプルコード 1: 一貫性正則化を用いた基本例
少量のラベル付きデータと大量のラベルなしデータを使用し、一貫性正則化を実装します。

```python
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader, Subset
import numpy as np

# データ準備（MNISTを使用）
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)

# ラベル付きデータ（100サンプル）とラベルなしデータに分割
labeled_indices = np.random.choice(len(train_dataset), 100, replace=False)
unlabeled_indices = np.setdiff1d(np.arange(len(train_dataset)), labeled_indices)
labeled_loader = DataLoader(Subset(train_dataset, labeled_indices), batch_size=10, shuffle=True)
unlabeled_loader = DataLoader(Subset(train_dataset, unlabeled_indices), batch_size=50, shuffle=True)

# モデル（シンプルなCNN）
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 16, 3)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(16 * 13 * 13, 10)

    def forward(self, x):
        x = self.pool(torch.relu(self.conv1(x)))
        x = x.view(-1, 16 * 13 * 13)
        x = self.fc1(x)
        return x

model = SimpleCNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 訓練ループ
for epoch in range(3):
    for (labeled_data, labels), (unlabeled_data, _) in zip(labeled_loader, unlabeled_loader):
        optimizer.zero_grad()

        # ラベル付きデータの損失
        outputs_labeled = model(labeled_data)
        loss_labeled = criterion(outputs_labeled, labels)

        # ラベルなしデータで一貫性損失（データ拡張を模擬）
        aug_unlabeled = unlabeled_data + torch.randn_like(unlabeled_data) * 0.1  # ノイズ追加
        outputs_unlabeled = model(unlabeled_data)
        outputs_aug = model(aug_unlabeled)
        loss_consistency = ((outputs_unlabeled - outputs_aug) ** 2).mean()

        # 総損失
        loss = loss_labeled + 0.1 * loss_consistency
        loss.backward()
        optimizer.step()
    print(f"Epoch {epoch+1}, Loss: {loss.item()}")

print("半教師あり学習の訓練が完了しました。")
```

#### サンプルコード 2: Pseudo-Labelを用いた例（[Qiita記事](https://qiita.com/koshian2/items/f4a458466b15bb91c7cb)より改変）
Pseudo-Label法では、モデルが予測した高信頼度のラベルをラベルなしデータに付与し、再訓練します。

```python
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader, Subset
import numpy as np

# データ準備（MNISTを使用）
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)

# ラベル付きデータ（100サンプル）とラベルなしデータに分割
labeled_indices = np.random.choice(len(train_dataset), 100, replace=False)
unlabeled_indices = np.setdiff1d(np.arange(len(train_dataset)), labeled_indices)
labeled_loader = DataLoader(Subset(train_dataset, labeled_indices), batch_size=10, shuffle=True)
unlabeled_loader = DataLoader(Subset(train_dataset, unlabeled_indices), batch_size=50, shuffle=True)

# モデル（シンプルなCNN）
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 16, 3)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(16 * 13 * 13, 10)

    def forward(self, x):
        x = self.pool(torch.relu(self.conv1(x)))
        x = x.view(-1, 16 * 13 * 13)
        x = self.fc1(x)
        return x

model = SimpleCNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 訓練ループ（Pseudo-Label実装）
for epoch in range(3):
    model.train()
    for (labeled_data, labels), (unlabeled_data, _) in zip(labeled_loader, unlabeled_loader):
        optimizer.zero_grad()

        # ラベル付きデータの損失
        outputs_labeled = model(labeled_data)
        loss_labeled = criterion(outputs_labeled, labels)

        # ラベルなしデータでPseudo-Label生成
        model.eval()
        with torch.no_grad():
            outputs_unlabeled = model(unlabeled_data)
            pseudo_labels = torch.argmax(outputs_unlabeled, dim=1)  # 最も確信度の高いラベルを選択
            confidence = torch.softmax(outputs_unlabeled, dim=1).max(dim=1)[0]
            mask = confidence > 0.95  # 信頼度が95%以上のデータのみ使用
        model.train()

        # Pseudo-Labelを用いた損失
        if mask.sum() > 0:
            pseudo_outputs = model(unlabeled_data[mask])
            loss_pseudo = criterion(pseudo_outputs, pseudo_labels[mask])
            loss = loss_labeled + 0.1 * loss_pseudo
        else:
            loss = loss_labeled

        loss.backward()
        optimizer.step()
    print(f"Epoch {epoch+1}, Loss: {loss.item()}")

print("Pseudo-Labelによる半教師あり学習の訓練が完了しました。")
```

#### 説明
- **サンプルコード 1**: ラベル付きデータで分類損失、ラベルなしデータで一貫性損失を計算。
- **サンプルコード 2**: Pseudo-Label法を用い、モデルが予測した高信頼度のラベルを活用。
- **目的**: 少量のラベル付きデータと大量のラベルなしデータを活用。
- **工夫**: データ拡張や信頼度閾値（例: 0.95）を用いて精度を向上。

---

## 弱教師あり学習（Weakly Supervised Learning）

### 定義と概要
弱教師あり学習は、不完全、不正確、またはノイズの多いラベル（弱い教師信号）を用いてモデルを訓練する手法です。完全なラベル付きデータが得られない場合に有効で、たとえば画像全体に1つのラベルしかない場合（画像分類）から物体検出を学習するなどがあります。

### 仕組み
- **弱い教師信号の種類**:
  - **不正確なラベル**: 正確な位置や詳細が不明（例: 画像全体のラベルのみ）。
  - **不完全なラベル**: 一部のデータしかラベルがない。
  - **ノイズ付きラベル**: 誤ったラベルが混在。
- **手法例**:
  - **Multiple Instance Learning (MIL)**: バッグ（画像全体）にラベルがあり、インスタンス（部分領域）のラベルを推定。
  - **注意機構（Attention Mechanism）**: 重要な領域をモデルが自動で特定。

### 利点
- 詳細なアノテーションが不要。
- **現実的なデータ収集シナリオに適合**: 完全なラベルが得にくい状況で有効。
- スケーラブルでコスト効率が高い。

### 応用例
- **物体検出**: 画像全体のラベルのみで物体位置を特定。
- **医療分野**: 患者全体の診断ラベルから病変部位を推定（例: OCT画像での網膜疾患検出）。
- **テキスト分類**: 文書全体のラベルから重要なフレーズを抽出。

### 出典と主要研究
- **基礎研究**:
  - Dietterich, T. G., et al. (1997). "Solving the Multiple Instance Problem with Axis-Parallel Rectangles". *Artificial Intelligence*.
- **最近の進展**:
  - Zhou, Z.-H. (2018). "A Brief Introduction to Weakly Supervised Learning". *National Science Review*.
  - Diba, A., et al. (2017). "Weakly Supervised Cascaded Convolutional Networks". *CVPR 2017*.
- **追加文献**（提供されたリンクより）:
  - Ilse, M., et al. (2018). "Attention-based Deep Multiple Instance Learning". *arXiv:1802.04712*.（[Qiita記事](https://qiita.com/fujitagodai4/items/8ececca77bbe70a1da45)）

### 現在の研究動向
- **ノイズ耐性**: ノイズの多いラベルに対するロバスト性向上。
- **マルチタスク学習との統合**: 弱教師あり学習と他のタスク（例: セグメンテーション）を同時進行。
- **大規模データ対応**: インターネット上の雑多なデータ（例: ソーシャルメディア投稿）を活用。
- **医療画像への応用**: OCT画像などでの弱教師あり学習の適用（[ブログ記事](https://takehero.hateblo.jp/entry/2019/11/12/232710)）。

### 弱教師あり学習（Weakly Supervised Learning）のサンプルコード
#### サンプルコード 1: Multiple Instance Learning（MIL）の基本例
画像全体にラベルのみを与え、重要な部分領域を特定します。

```python
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# データ準備（MNISTを使用）
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)

# モデル（MILベース）
class MILNet(nn.Module):
    def __init__(self):
        super(MILNet, self).__init__()
        self.conv1 = nn.Conv2d(1, 16, 3)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(16 * 13 * 13, 10)

    def forward(self, x):
        x = torch.relu(self.conv1(x))  # 特徴抽出
        x = self.pool(x)
        x = x.view(x.size(0), -1)  # フラット化
        scores = self.fc1(x)
        bag_score, _ = torch.max(scores, dim=0)  # バッグ全体のスコア（最大値）
        return bag_score

model = MILNet()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 訓練ループ
for epoch in range(3):
    for images, labels in train_loader:
        optimizer.zero_grad()
        outputs = model(images)  # 画像全体からスコアを計算
        loss = criterion(outputs.unsqueeze(0), labels[:1])  # バッチの最初のラベルを使用
        loss.backward()
        optimizer.step()
    print(f"Epoch {epoch+1}, Loss: {loss.item()}")

print("弱教師あり学習の訓練が完了しました。")
```

#### サンプルコード 2: Attention-based MIL（[Qiita記事](https://qiita.com/fujitagodai4/items/8ececca77bbe70a1da45)より改変）
Attentionメカニズムを用いて、インスタンスごとの重要度を学習します。

```python
import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader

# データ準備（MNISTを使用）
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
train_dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)

# Attention付きMILモデル
class AttentionMIL(nn.Module):
    def __init__(self):
        super(AttentionMIL, self).__init__()
        self.feature_extractor = nn.Sequential(
            nn.Conv2d(1, 16, 3),
            nn.ReLU(),
            nn.MaxPool2d(2, 2)
        )
        self.attention = nn.Sequential(
            nn.Linear(16 * 13 * 13, 128),
            nn.Tanh(),
            nn.Linear(128, 1)
        )
        self.classifier = nn.Linear(16 * 13 * 13, 10)

    def forward(self, x):
        features = self.feature_extractor(x)  # 特徴抽出
        features = features.view(features.size(0), -1)
        
        # Attentionスコアの計算
        attention_weights = torch.softmax(self.attention(features), dim=0)
        weighted_features = features * attention_weights
        bag_features = weighted_features.sum(dim=0, keepdim=True)
        
        # 分類
        output = self.classifier(bag_features)
        return output

model = AttentionMIL()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 訓練ループ
for epoch in range(3):
    for images, labels in train_loader:
        optimizer.zero_grad()
        outputs = model(images)
        loss = criterion(outputs, labels[:1])  # バッチの最初のラベルを使用
        loss.backward()
        optimizer.step()
    print(f"Epoch {epoch+1}, Loss: {loss.item()}")

print("Attention-based MILによる弱教師あり学習の訓練が完了しました。")
```

#### サンプルコード 3: OCT画像の前処理（[ブログ記事](https://takehero.hateblo.jp/entry/2019/11/12/232710)より着想）
医療画像（例: OCT画像）を想定し、患者ごとの画像を前処理する例を示します。

```python
import torch
import torch.nn as nn
import numpy as np
from torchvision import transforms
from torch.utils.data import Dataset, DataLoader

# 仮想OCT画像データセット（簡略化のためランダムデータ）
class OCTDataset(Dataset):
    def __init__(self, num_patients=10, images_per_patient=5):
        self.data = [torch.randn(images_per_patient, 1, 64, 64) for _ in range(num_patients)]
        self.labels = torch.randint(0, 2, (num_patients,))  # 患者ごとのラベル（0: 正常, 1: 異常）

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        return self.data[idx], self.labels[idx]

dataset = OCTDataset()
train_loader = DataLoader(dataset, batch_size=1, shuffle=True)

# MILモデル
class OCTMIL(nn.Module):
    def __init__(self):
        super(OCTMIL, self).__init__()
        self.conv1 = nn.Conv2d(1, 16, 3)
        self.pool = nn.MaxPool2d(2, 2)
        self.fc1 = nn.Linear(16 * 31 * 31, 2)

    def forward(self, x):
        x = torch.relu(self.conv1(x))
        x = self.pool(x)
        x = x.view(x.size(0), -1)
        scores = self.fc1(x)
        bag_score, _ = torch.max(scores, dim=0)  # 患者ごとの最大スコア
        return bag_score

model = OCTMIL()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)

# 訓練ループ
for epoch in range(3):
    for images, label in train_loader:
        images = images.squeeze(0)  # 患者ごとの画像バッチ
        optimizer.zero_grad()
        output = model(images)
        loss = criterion(output.unsqueeze(0), label)
        loss.backward()
        optimizer.step()
    print(f"Epoch {epoch+1}, Loss: {loss.item()}")

print("OCT画像を用いた弱教師あり学習の訓練が完了しました。")
```

#### 説明
- **サンプルコード 1**: 画像全体のラベルからMILで学習。
- **サンプルコード 2**: Attentionメカニズムで重要な領域を強調。
- **サンプルコード 3**: OCT画像を患者単位で処理し、弱いラベルから学習。
- **目的**: 不完全なラベルから有用な情報を抽出。
- **工夫**: AttentionやMILで重要な部分領域を特定。

---

## まとめと比較
- **自己教師あり学習**: ラベルなしデータから擬似タスクを生成。事前学習に強く、転移学習で威力を発揮。
- **半教師あり学習**: 少量のラベル付きデータと大量のラベルなしデータを活用。現実的なシナリオで有効。
- **弱教師あり学習**: 不完全なラベルを利用。詳細なアノテーションが難しい場合に適する。

これらの手法は、データアノテーションのコストや可用性に応じて使い分けられ、互いに補完的です。最近では、これらを組み合わせたハイブリッドアプローチ（例: 自己教師あり学習で事前訓練後、半教師あり学習で微調整）も注目されています。

---

## 参考情報
- 研究動向は、NeurIPS、ICML、CVPRなどの主要学会やarXivで最新論文を追うと良いでしょう。
- xAIの視点では、これらの手法は宇宙探査データの解析（例: 衛星画像や信号処理）にも応用可能で、今後のAI開発に寄与する可能性があります。

---

## 注意点と拡張
- **環境**: 上記コードはPyTorchがインストールされている環境で動作します。必要に応じて`pip install torch torchvision`でインストールしてください。
- **データ**: MNISTや仮想データを使用していますが、実応用ではカスタムデータセット（例: OCT画像）に置き換え可能です。
- **拡張**: 実際の研究では、より複雑なモデル（ResNet, Transformerなど）やデータ拡張、ハイパーパラメータ調整が必要です。