Jetbot & PyTorch CNN（２）衝突回避用の学習プログラム

今回は２）の衝突回避用の学習プログラムです。
train_model_resnet18.ipynbをベースにCNNモデルに変更と写真サイスを小さくします。

まずはimport（追加あり）。

```ruby:train_model_resnet18.ipynb
import torch
import torch.optim as optim
import torch.nn.functional as F
import torchvision
import torchvision.datasets as datasets
import torchvision.models as models
import torchvision.transforms as transforms

import torch.nn as nn
```

Create dataset instance（1回目）

```ruby:train_model_resnet18.ipynb
dataset = datasets.ImageFolder(
    'dataset',
    transforms.Compose([
        transforms.ColorJitter(0.1, 0.1, 0.1, 0.1),
        #transforms.Resize((224, 224)),             # 写真サイズ変更
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
)
```

Split dataset into train and test sets

```ruby:train_model_resnet18.ipynb
train_dataset, test_dataset = torch.utils.data.random_split(dataset, [len(dataset) - 50, 50])
```

Create data loaders to load data in batches（変更なし）

```ruby:train_model_resnet18.ipynb
train_loader = torch.utils.data.DataLoader(
    train_dataset,
    batch_size=8,
    shuffle=True,
    num_workers=0,
)

test_loader = torch.utils.data.DataLoader(
    test_dataset,
    batch_size=8,
    shuffle=True,
    num_workers=0,
)
```

Define the neural network（新規）

```ruby:train_model_resnet18.ipynb
class miniNet(nn.Module):

    def __init__(self, num_classes=2):
        super(miniNet, self).__init__()

        self.conv1 = nn.Conv2d(3, 6, 3)
        self.pool1 = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 3)
        self.pool2 = nn.MaxPool2d(2, 2)
        self.conv3 = nn.Conv2d(16, 32, 3)
        self.pool3 = nn.MaxPool2d(2, 2)

        #self.fc1 = nn.Linear(32 * 26 * 26, 1024) # 写真サイズ変更
        self.fc1 = nn.Linear(4160, 1024)          # 写真サイズ変更
        self.fc2 = nn.Linear(1024, 512)
        self.fc3 = nn.Linear(512, num_classes)


    def forward(self, x):
        x = F.relu(self.conv1(x))
        x = self.pool1(x)
        x = F.relu(self.conv2(x))
        x = self.pool2(x)
        x = F.relu(self.conv3(x))
        x = self.pool3(x)
        
        #x = x.view(-1, 32 * 26 * 26)   # 写真サイズ変更
        x = x.view(-1, 4160)            # 写真サイズ変更
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)

        return x
```

ネットワークを作って、optimizerを設定（新規）

```ruby:train_model_resnet18.ipynb
net = miniNet()
print(net)
optimizer = optim.Adam(net.parameters(), lr=0.001)
criterion = nn.CrossEntropyLoss()
```

GPUに渡す（修正）

```ruby:train_model_resnet18.ipynb
device = torch.device('cuda')
net.to(device)
```

Create dataset instance（もう1回走らす）

```ruby:train_model_resnet18.ipynb
dataset = datasets.ImageFolder(
    'dataset',
    transforms.Compose([
        transforms.ColorJitter(0.1, 0.1, 0.1, 0.1),
        #transforms.Resize((224, 224)),             # 写真サイズ変更
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ])
)
```

Train the neural network（修正あり）

```ruby:train_model_resnet18.ipynb
NUM_EPOCHS = 30
BEST_MODEL_PATH = 'best_model_resnet18a.pth'
best_accuracy = 0.0

#optimizer = optim.SGD(model.parameters(), lr=0.001, momentum=0.9)
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)  # モデル変更
for epoch in range(NUM_EPOCHS):
    
    for images, labels in iter(train_loader):
        images = images.to(device)
        labels = labels.to(device)
        optimizer.zero_grad()
        #outputs = model(images)
        outputs = net(images)               # モデル変更
        loss = F.cross_entropy(outputs, labels)
        loss.backward()
        optimizer.step()
    
    test_error_count = 0.0
    for images, labels in iter(test_loader):
        images = images.to(device)
        labels = labels.to(device)
        #outputs = model(images)
        outputs = net(images)               # モデル変更
        test_error_count += float(torch.sum(torch.abs(labels - outputs.argmax(1))))
    
    test_accuracy = 1.0 - float(test_error_count) / float(len(test_dataset))
    print('%d: %f' % (epoch, test_accuracy))
    if test_accuracy > best_accuracy:
        #torch.save(model.state_dict(), BEST_MODEL_PATH)
        torch.save(net.state_dict(), BEST_MODEL_PATH)       # モデル変更
        best_accuracy = test_accuracy
```

Eval（追加しました）

```ruby:train_model_resnet18.ipynb
trained_net = miniNet()
trained_net.load_state_dict(torch.load('best_model_resnet18a.pth'))

trained_net.eval()
```

check with sample jpg（追加しました）
printでtensor([[ A, B]]でAがblocked, Bがfreeで、数値が大きい方が計算結果を示します。jpgのファイルパスは実際のファイルパスに変更してください。

```ruby:train_model_resnet18.ipynb
from PIL import Image

transform = torchvision.transforms.Compose([
     torchvision.transforms.ToTensor(),
     torchvision.transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])

def image_loader(image_path):
  im = Image.open(image_path)
  im = transform(im)
  im = im.unsqueeze(0)
  return im

c2_path = 'dataset/blocked/000ba162-1319-11eb-9a46-16f63a1aa8c9.jpg' #jpgのパス。
#c2_path = 'dataset/free/acc1eb9c-1318-11eb-9a46-16f63a1aa8c9.jpg'　#jpgのパス

x = image_loader(c2_path)
output = trained_net(x)
print(output)
```

PTHファイルはResNet18の40MBに対し、18MBで済みました。