[Pytorch]DatasetとDataloaderのイメージ図（自作Dataset版）

# 目的
Pytorchを使用していてDatasetとDataloaderが何なのかがイメージできなかった。イメージするために『<a href="https://qiita.com/TaigaMasuda/items/24d85860ffcd724de9eb">PyTorchでMNISTを扱ってみた (機械学習)</a>』で解説されているMNISTのスクリプトの中のDatasetとDataloadeのイメージ図を作成したので共有する。

尚、『<a href="https://qiita.com/akahoshi51/items/ed5f42d908b2882f9cff">[Pytorch]DatasetとDataloaderのイメージ図（自作Dataset版）</a>』で自作DataをDatasetに格納するスクリプトを紹介しています。



# DatasetとDataloaderを作成するスクリプトとイメージ図
```
#Datasetをロードするスクリプト
train_dataset = torchvision.datasets.MNIST(root='./data',
                                           train=True,
                                           transform=transforms.ToTensor(),
                                           download = True)
#Dataloaderを作成するスクリプト
batch_size = 256
train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                           batch_size=batch_size,
                                           shuffle=True)

#Dataloaderからmini batchを1ケずつ取り出すスクリプト
for epoch in range(1, num_epochs+1, 1):                                       
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/111514/15b7a02e-d17d-e1fe-5f50-3232ceb4369b.png)
(疑問点) mini batchにできないあまりは無視される？



# (参考)MNISTのスクリプト全体
```d13a_MNIST.ipynb
#----------------ライブラリのインポート--------------------
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms

#--------------乱数シードの固定---------------------------
def setup_all_seed(seed=0):
    # numpyに関係する乱数シードの設定
    np.random.seed(seed)
    
    # pytorch
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.backends.cudnn.deterministic = True

#--------------Datasetのロード----------------------------
#訓練データ
train_dataset = torchvision.datasets.MNIST(root='./data',
                                           train=True,
                                           transform=transforms.ToTensor(),
                                           download = True)
#検証データ
test_dataset = torchvision.datasets.MNIST(root='./data',
                                           train=False,
                                           transform=transforms.ToTensor(),
                                           download = True)

#-------------------Dataloaderの作成--------------------------------
batch_size = 256

train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                           batch_size=batch_size,
                                           shuffle=True)

test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
                                           batch_size=batch_size,
                                           shuffle=True)

#----------------ニューラルネットワーク(MLP)の定義-----------------
class Net(nn.Module):
    def __init__(self, input_size, hidden1_size, hidden2_size, output_size):
        super(Net, self).__init__()
        self.fc1 = nn.Linear(input_size, hidden1_size)
        self.fc2 = nn.Linear(hidden1_size, hidden2_size)
        self.fc3 = nn.Linear(hidden2_size, output_size)

    def forward(self, x): # x : 入力
        z1 = F.relu(self.fc1(x))
        z2 = F.relu(self.fc2(z1))
        y = self.fc3(z2)
        return y

#----------------モデルのインスタンス化-----------------
input_size = 28*28 #MNISTの画像サイズ x=28,y=28
hidden1_size = 1024
hidden2_size = 512
output_size = 10

device = 'cuda' if torch.cuda.is_available() else 'cpu'
model = Net(input_size, hidden1_size, hidden2_size, output_size).to(device)
#print(model)

# ---------------損失関数　criterion：基準-------------
# CrossEntropyLoss：交差エントロピー誤差関数
criterion = nn.CrossEntropyLoss()

#-------最適化法の指定　optimizer：最適化---------------
# SGD：確率的勾配降下法
optimizer = optim.SGD(model.parameters(), lr=0.01)

#------------1epochの学習を行う関数の定義------------------
def train_model(model, train_loader, criterion, optimizer, device='cpu'):

    train_loss = 0.0
    num_train = 0

    # 学習モデルに変換
    model.train()

    for i, (images, labels) in enumerate(train_loader):
        # mini batchに含まれるデータ数をカウント
        num_train += len(labels)

        # mini batch
        images, labels = images.view(-1, 28*28).to(device), labels.to(device)

        # 勾配を初期化
        optimizer.zero_grad()

        # 推論(順伝播)
        outputs = model(images)

        # 損失の算出
        loss = criterion(outputs, labels)

        # 誤差逆伝播
        loss.backward()

        # パラメータの更新
        optimizer.step()

        # lossを加算
        train_loss += loss.item()
    
    # lossの平均値を取る
    train_loss = train_loss / num_train

    return train_loss

#------------検証データによるモデル評価を行う関数の定義--------------
def test_model(model, test_loader, criterion, optimizer, device='cpu'):

    test_loss = 0.0
    num_test = 0

    # modelを評価モードに変更
    model.eval()

    with torch.no_grad(): # 勾配計算の無効化
        for i, (images, labels) in enumerate(test_loader):
            num_test += len(labels)
            images, labels = images.view(-1, 28*28).to(device), labels.to(device)
            outputs = model(images)
            loss = criterion(outputs, labels)
            test_loss += loss.item()
        
        # lossの平均値を取る
        test_loss = test_loss / num_test
    return test_loss

#----------学習を行う関数の定義 ------------
def lerning(model, train_loader, test_loader, criterion, opimizer, num_epochs, device='cpu'):

    train_loss_list = []
    test_loss_list = []

    # epoch数分繰り返す
    for epoch in range(1, num_epochs+1, 1):

        train_loss = train_model(model, train_loader, criterion, optimizer, device=device)
        test_loss = test_model(model, test_loader, criterion, optimizer, device=device)
        
        print("epoch : {}, train_loss : {:.5f}, test_loss : {:.5f}" .format(epoch, train_loss, test_loss))

        train_loss_list.append(train_loss)
        test_loss_list.append(test_loss)
    
    return train_loss_list, test_loss_list

#--------------学習の実施---------------------------
num_epochs = 10
train_loss_list, test_loss_list = lerning(model, train_loader, test_loader, criterion, optimizer, num_epochs, device=device)

#---------------学習推移のグラフ化-----------------
plt.plot(range(len(train_loss_list)), train_loss_list, c='b', label='train loss')
plt.plot(range(len(test_loss_list)), test_loss_list, c='r', label='test loss')
plt.xlabel("epoch")
plt.ylabel("loss")
plt.legend()
plt.grid()
plt.show()

#------------------学習結果の確認--------------------
plt.figure(figsize=(20, 10))
for i in range(10):
    image, label = test_dataset[i]
    image = image.view(-1, 28*28).to(device)

    # 推論
    prediction_label = torch.argmax(model(image))

    ax = plt.subplot(1, 10, i+1)

    plt.imshow(image.detach().to('cpu').numpy().reshape(28, 28), cmap='gray')
    ax.axis('off')
    ax.set_title('label : {}\n Prediction : {}'.format(label, prediction_label), fontsize=15)
plt.show()
```