<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Dogs vs Cats
https
Step1:  データ整形
https
Step2:  訓練データからランダムに選んだ2000画像をvalidationデータとする
Step3:  PyTorchで読み込みやすいようにクラスごとにサブディレクトリを作成する
Kaggleのテストデータは正解ラベルがついていないため unknown というサブディレクトリにいれる
Step4:  VGG16 の出力層のみ置き換える
分類層を除いたネットワークのパラメータを固定する
分類層のパラメータのみ学習対象
Step5:  層の置き換え
下のように (classifier) の (6) だけを置き換えることはできないみたい 
```
最後のfc層のみ2クラス分類できるように置き換える
num_features = vgg16.classifier[6].in_features
vgg16.classifier[6] = nn.Linear(num_features, 2)  # <= この代入はできない！
```
classifierをまるごと置き換える必要がある
Step6:  VGG用のデータ変換を定義
訓練もテストも (224, 224) にサイズ変更のみ
正方形の画像でないので Resize(224) は動作しない
最初はデータ拡張は使わないで試す
Step7:  データをロード
Step8:  クラスはアルファベット順？
Step9:  モデル訓練
optimizerには更新対象のパラメータのみ渡す必要がある！
requires_grad = False している vgg16.parameters() を指定するとエラーになる
<ASSISTANT_TASK:>
Python Code:
mkdir 
%matplotlib inline
Explanation: Dogs vs Cats
https://blog.keras.io/building-powerful-image-classification-models-using-very-little-data.html
https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition
http://aidiary.hatenablog.com/entry/20170108/1483876657
http://aidiary.hatenablog.com/entry/20170603/1496493646
End of explanation
!ls data/
import os
current_dir = os.getcwd()
data_dir = os.path.join(current_dir, 'data', 'dogscats')
train_dir = os.path.join(data_dir, 'train')
valid_dir = os.path.join(data_dir, 'valid')
test_dir = os.path.join(data_dir, 'test')
!mkdir $data_dir
!unzip train.zip -d $data_dir
!unzip test.zip -d $data_dir
!ls -1 $train_dir | wc -l
!ls -1 $test_dir | wc -l
Explanation: データ整形
https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition
train.zipとtest.zipをカレントディレクトリにダウンロードしておく
End of explanation
!mkdir $valid_dir
%cd $train_dir
import os
from glob import glob
import numpy as np
g = glob('*.jpg')
shuf = np.random.permutation(g)
for i in range(2000):
    os.rename(shuf[i], os.path.join(valid_dir, shuf[i]))
!ls -1 $valid_dir | wc -l
Explanation: 訓練データからランダムに選んだ2000画像をvalidationデータとする
End of explanation
# train
%cd $train_dir
%mkdir cats dogs
%mv cat.*.jpg cats/
%mv dog.*.jpg dogs/
# valid
%cd $valid_dir
%mkdir cats dogs
%mv cat.*.jpg cats/
%mv dog.*.jpg dogs/
# test
%cd $test_dir
%mkdir unknown
%mv *.jpg unknown
Explanation: PyTorchで読み込みやすいようにクラスごとにサブディレクトリを作成する
Kaggleのテストデータは正解ラベルがついていないため unknown というサブディレクトリにいれる
End of explanation
vgg16 = models.vgg16(pretrained=True)
vgg16.eval()  # eval mode!
Explanation: VGG16 の出力層のみ置き換える
分類層を除いたネットワークのパラメータを固定する
分類層のパラメータのみ学習対象
End of explanation
# 全層のパラメータを固定
for param in vgg16.parameters():
    param.requires_grad = False
vgg16.classifier = nn.Sequential(
    nn.Linear(25088, 4096),
    nn.ReLU(inplace=True),
    nn.Dropout(0.5),
    nn.Linear(4096, 4096),
    nn.ReLU(inplace=True),
    nn.Dropout(0.5),
    nn.Linear(4096, 2)    
)
use_gpu = torch.cuda.is_available()
if use_gpu:
    vgg16 = vgg16.cuda()
print(vgg16)
Explanation: 層の置き換え
下のように (classifier) の (6) だけを置き換えることはできないみたい 
```
最後のfc層のみ2クラス分類できるように置き換える
num_features = vgg16.classifier[6].in_features
vgg16.classifier[6] = nn.Linear(num_features, 2)  # <= この代入はできない！
```
classifierをまるごと置き換える必要がある
End of explanation
train_preprocess = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
test_preprocess = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor(),
    transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
Explanation: VGG用のデータ変換を定義
訓練もテストも (224, 224) にサイズ変更のみ
正方形の画像でないので Resize(224) は動作しない
最初はデータ拡張は使わないで試す
End of explanation
train_dataset = datasets.ImageFolder(train_dir, train_preprocess)
valid_dataset = datasets.ImageFolder(valid_dir, test_preprocess)
test_dataset = datasets.ImageFolder(test_dir, test_preprocess)
# DataSetのlenはサンプル数
print(len(train_dataset))
print(len(valid_dataset))
print(len(test_dataset))
Explanation: データをロード
End of explanation
classes = train_dataset.classes
print(train_dataset.classes)
print(valid_dataset.classes)
print(test_dataset.classes)
train_loader = torch.utils.data.DataLoader(train_dataset,
                                           batch_size=128,
                                           shuffle=True)
valid_loader = torch.utils.data.DataLoader(valid_dataset,
                                           batch_size=128,
                                           shuffle=False)
test_loader = torch.utils.data.DataLoader(test_dataset,
                                          batch_size=128,
                                          shuffle=False)
# DataLoaderのlenはミニバッチ数
print(len(train_loader))
print(len(valid_loader))
print(len(test_loader))
def imshow(images, title=None):
    images = images.numpy().transpose((1, 2, 0))  # (h, w, c)
    # denormalize
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    images = std * images + mean
    images = np.clip(images, 0, 1)
    plt.imshow(images)
    if title is not None:
        plt.title(title)
images, classes = next(iter(train_loader))
print(images.size(), classes.size())
images = torchvision.utils.make_grid(images[:25], nrow=5)
imshow(images)
Explanation: クラスはアルファベット順？
End of explanation
if use_gpu:
    vgg16 = vgg16.cuda()
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(vgg16.classifier.parameters(), lr=0.001, momentum=0.9)
def train(model, criterion, optimizer, train_loader):
    model.train()
    running_loss = 0
    for batch_idx, (images, labels) in enumerate(train_loader):
        if use_gpu:
            images = Variable(images.cuda())
            labels = Variable(labels.cuda())
        else:
            images = Variable(images)
            labels = Variable(labels)
        
        optimizer.zero_grad()
        outputs = model(images)
        
        loss = criterion(outputs, labels)
        running_loss += loss.data[0]
        
        loss.backward()
        optimizer.step()
    
    train_loss = running_loss / len(train_loader)
    
    return train_loss
def valid(model, criterion, valid_loader):
    model.eval()
    running_loss = 0
    correct = 0
    total = 0
    for batch_idx, (images, labels) in enumerate(valid_loader):
        if use_gpu:
            images = Variable(images.cuda())
            labels = Variable(labels.cuda())
        else:
            images = Variable(images)
            labels = Variable(labels)
        outputs = model(images)
        loss = criterion(outputs, labels)
        running_loss += loss.data[0]
        _, predicted = torch.max(outputs.data, 1)
        correct += (predicted == labels.data).sum()
        total += labels.size(0)
    val_loss = running_loss / len(valid_loader)
    val_acc = correct / total
    
    return val_loss, val_acc
%mkdir logs
num_epochs = 5
log_dir = './logs'
best_acc = 0
loss_list = []
val_loss_list = []
val_acc_list = []
for epoch in range(num_epochs):
    loss = train(vgg16, criterion, optimizer, train_loader)
    val_loss, val_acc = valid(vgg16, criterion, valid_loader)
    print('epoch %d, loss: %.4f val_loss: %.4f val_acc: %.4f'
          % (epoch, loss, val_loss, val_acc))
    
    if val_acc > best_acc:
        print('val_acc improved from %.5f to %.5f!' % (best_acc, val_acc))
        best_acc = val_acc
        model_file = 'epoch%03d-%.3f-%.3f.pth' % (epoch, val_loss, val_acc)
        torch.save(vgg16.state_dict(), os.path.join(log_dir, model_file))
    # logging
    loss_list.append(loss)
    val_loss_list.append(val_loss)
    val_acc_list.append(val_acc)
Explanation: モデル訓練
optimizerには更新対象のパラメータのみ渡す必要がある！
requires_grad = False している vgg16.parameters() を指定するとエラーになる
End of explanation
<END_TASK>