PythonのPyTorchで学ぶ機械学習

## Chapter 1: PyTorchの概要
PyTorchは、ディープラーニングモデルを構築、トレーニング、評価するための強力なライブラリです。Pythonをベースにしており、動的計算グラフを使用するため、柔軟性と直感的なデバッグが可能です。

```python
import torch

# Tensorの作成
x = torch.tensor([1.0, 2.0, 3.0])
print("Tensor x:", x)
```

## Chapter 2: Tensorの基本操作
TensorはPyTorchの基本データ構造で、N次元配列として機能します。NumPyの配列と似ていますが、GPUでの計算をサポートしています。

```python
# Tensorの加算
a = torch.tensor([1, 2])
b = torch.tensor([3, 4])
c = a + b
print("Tensor a:", a)
print("Tensor b:", b)
print("Sum c:", c)
```

## Chapter 3: 自動微分
PyTorchの自動微分機能は、`requires_grad=True`を設定したTensorに対して勾配を計算します。これはニューラルネットワークのトレーニングに不可欠です。

```python
x = torch.tensor(1.0, requires_grad=True)
y = x ** 2
y.backward()
print("Gradient of y with respect to x:", x.grad)
```

## Chapter 4: ニューラルネットワークの構築
PyTorchの`nn.Module`クラスを使用してニューラルネットワークを構築します。ネットワークはレイヤーで構成され、`forward`メソッドでデータの流れを定義します。

```python
import torch.nn as nn

class SimpleNN(nn.Module):
    def __init__(self):
        super(SimpleNN, self).__init__()
        self.fc = nn.Linear(2, 1)  # 2入力、1出力の全結合層

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

model = SimpleNN()
print("Simple Neural Network Model:", model)
```

## Chapter 5: データローダーの使用
`DataLoader`は、データセットをバッチに分割し、トレーニングや評価のために効率的にデータを提供します。

```python
from torch.utils.data import DataLoader, TensorDataset

# ダミーデータ
data = torch.tensor([[1.0, 2.0], [3.0, 4.0]])
labels = torch.tensor([0, 1])

dataset = TensorDataset(data, labels)
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)

for batch in dataloader:
    print("Batch:", batch)
```

## Chapter 6: トレーニングループの作成
トレーニングループは、モデルのパラメータを更新するプロセスです。損失関数を最小化するために、勾配降下法を使用します。

```python
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
criterion = nn.MSELoss()

for epoch in range(5):
    for inputs, targets in dataloader:
        optimizer.zero_grad()  # 勾配の初期化
        outputs = model(inputs)  # フォワードパス
        loss = criterion(outputs, targets.float().unsqueeze(1))  # 損失の計算
        loss.backward()  # バックプロパゲーション
        optimizer.step()  # パラメータの更新
        print(f"Epoch {epoch}, Loss: {loss.item()}")
```

## Chapter 7: モデルの評価
トレーニング後、モデルの性能を評価するために、テストデータを使用します。評価中は勾配を計算しないため、`torch.no_grad()`を使用します。

```python
with torch.no_grad():
    for inputs, targets in dataloader:
        outputs = model(inputs)
        print("Model outputs:", outputs)
```

## Chapter 8: GPUでのトレーニング
PyTorchはGPUを使用して計算を加速できます。`torch.device`を使用して、モデルとデータをGPUに転送します。

```python
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model.to(device)

for inputs, targets in dataloader:
    inputs, targets = inputs.to(device), targets.to(device)
    optimizer.zero_grad()
    outputs = model(inputs)
    loss = criterion(outputs, targets.float().unsqueeze(1))
    loss.backward()
    optimizer.step()
```

## Chapter 9: カスタムデータセットの作成
独自のデータセットを定義するために、`torch.utils.data.Dataset`をサブクラス化します。`__len__`と`__getitem__`メソッドを実装します。

```python
from torch.utils.data import Dataset

class CustomDataset(Dataset):
    def __init__(self, data, labels):
        self.data = data
        self.labels = labels

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

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

custom_dataset = CustomDataset(data, labels)
print("Custom Dataset Length:", len(custom_dataset))
```

## Chapter 10: 転移学習
転移学習は、既存のモデルを新しいタスクに適用する手法です。事前にトレーニングされたモデルの一部を固定し、新しいデータに合わせて調整します。

```python
from torchvision import models

pretrained_model = models.resnet18(pretrained=True)
for param in pretrained_model.parameters():
    param.requires_grad = False  # パラメータを固定

pretrained_model.fc = nn.Linear(pretrained_model.fc.in_features, 2)  # 新しい出力層
print("Modified ResNet Model:", pretrained_model)
```

## Chapter 11: モデルの保存と読み込み
トレーニング済みモデルを保存し、後で再利用するために読み込みます。`torch.save`と`torch.load`を使用します。

```python
# モデルの保存
torch.save(model.state_dict(), 'model.pth')
print("Model saved to 'model.pth'.")

# モデルの読み込み
model.load_state_dict(torch.load('model.pth'))
print("Model loaded from 'model.pth'.")
```

## Chapter 12: ハイパーパラメータのチューニング
ハイパーパラメータは、モデルのトレーニングに影響を与える設定です。学習率やバッチサイズを調整してモデルの性能を向上させます。

```python
# 学習率の変更
optimizer = torch.optim.SGD(model.parameters(), lr=0.001)
print("Optimizer learning rate set to 0.001.")
```

## Chapter 13: 畳み込みニューラルネットワーク (CNN)
CNNは、画像データの処理に特化したニューラルネットワークです。畳み込み層とプーリング層を使用して特徴を抽出します。

```python
class SimpleCNN(nn.Module):
    def __init__(self):
        super(SimpleCNN, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)
        self.fc1 = nn.Linear(32 * 28 * 28, 10)

    def forward(self, x):
        x = self.conv1(x)
        x = x.view(x.size(0), -1)
        x = self.fc1(x)
        return x

cnn_model = SimpleCNN()
print("Simple CNN Model:", cnn_model)
```

## Chapter 14: 再帰型ニューラルネットワーク (RNN)
RNNは、時系列データや自然言語処理に適したネットワークです。シーケンスデータを処理するために内部状態を保持します。

```python
class SimpleRNN(nn.Module):
    def __init__(self, input_size, hidden_size, output_size):
        super(SimpleRNN, self).__init__()
        self.rnn = nn.RNN(input_size, hidden_size, batch_first=True)
        self.fc = nn.Linear(hidden_size, output_size)

    def forward(self, x):
        h0 = torch.zeros(1, x.size(0), hidden_size)
        out, _ = self.rnn(x, h0)
        out = self.fc(out[:, -1, :])
        return out

rnn_model = SimpleRNN(input_size=10, hidden_size=20, output_size=1)
print("Simple RNN Model:", rnn_model)
```

## Chapter 15: 自然言語処理 (NLP) の基礎
PyTorchを使用して基本的なNLPタスクを実行します。テキストデータをトークン化し、埋め込み層を使用してベクトル化します。

```python
# トークン化と埋め込み
from torchtext.data.utils import get_tokenizer
tokenizer = get_tokenizer('basic_english')
tokens = tokenizer("Hello PyTorch")
print("Tokens:", tokens)
```

## Chapter 16: PyTorchのデバッグ
PyTorchでのデバッグ方法を学びます。`print`ステートメントを使用して、モデルの出力や中間計算を確認します。

```python
# デバッグ用のprintステートメント
print(f"Input: {inputs}, Output: {outputs}")
```

## Chapter 17: PyTorchの最適化
モデルの最適化手法を説明します。異なるオプティマイザーを使用して、トレーニングプロセスを改善します。

```python
# Adamオプティマイザーの使用
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
print("Using Adam optimizer with learning rate 0.001.")
```

## Chapter 18: PyTorchのベストプラクティス
PyTorchを使用する際のベストプラクティスを紹介します。コードの可読性と再利用性を向上させるための方法を学びます。

```python
# モデルのトレーニングと評価を分離する
def train(model, dataloader, criterion, optimizer):
    model.train()
    for inputs, targets in dataloader:
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, targets)
        loss.backward()
        optimizer.step()

def evaluate(model, dataloader, criterion):
    model.eval()
    total_loss = 0
    with torch.no_grad():
        for inputs, targets in dataloader:
            outputs = model(inputs)
            loss = criterion(outputs, targets)
            total_loss += loss.item()
    return total_loss / len(dataloader)
```

## Chapter 19: PyTorchの最新機能
PyTorchの最新機能を紹介します。`torch.jit`を使用してモデルをスクリプト化し、パフォーマンスを向上させます。

```python
# torch.jitを使用したモデルのスクリプト化
scripted_model = torch.jit.script(model)
print("Scripted Model:", scripted_model)
```

## Chapter 20: 実際のユースケース
PyTorchを使用した実際のユースケースを紹介します。ここでは、MNISTデータセットを使用して画像分類モデルを構築します。

```python
from torchvision import datasets, transforms

# データセットの準備
transform = transforms.Compose([transforms.ToTensor()])
train_dataset = datasets.MNIST(root='./data', train=True, transform=transform, download=True)
train_loader = DataLoader(train_dataset, batch_size=64, shuffle=True)

# モデルの定義
class MNISTModel(nn.Module):
    def __init__(self):
        super(MNISTModel, self).__init__()
        self.conv1 = nn.Conv2d(1, 32, kernel_size=3)
        self.fc1 = nn.Linear(32 * 26 * 26, 10)

    def forward(self, x):
        x = self.conv1(x)
        x = x.view(x.size(0), -1)
        x = self.fc1(x)
        return x

# モデルのトレーニング
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = MNISTModel().to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()

for epoch in range(10):
    for inputs, targets in train_loader:
        inputs, targets = inputs.to(device), targets.to(device)
        optimizer.zero_grad()
        outputs = model(inputs)
        loss = criterion(outputs, targets)
        loss.backward()
        optimizer.step()
        print(f"Epoch {epoch}, Loss: {loss.item()}")
```