#動機
研究室でPytorchを使っている人がいるのでその方向けの説明用に
#本題 - 「なぜdatasetが必要なのか」
- 深層学習の入力データはバッチごとであるため
- バカでかいデータを一気に扱うとメモリが死ぬので細かく扱えるdatasetが便利だから
- とにかくシャッフルとかも勝手にやってくれるから
#簡易的なコードと説明
import torch
from sklearn.datasets import load_iris
class Dataset(torch.utils.data.Dataset):
def __init__(self, transform=None):
self.iris = load_iris() #irisdatasetの読み込み
self.data = self.iris['data']
self.label = self.iris['target']
self.datanum = len(self.label) #データの総数
self.transform = transform #データに対する特別な処理
def __len__(self):
return self.datanum
def __getitem__(self, index):
data = self.data[index]
label = self.label[index]
if self.transform:
data = self.transform(data)
return data, label
if __name__ == "__main__":
batch_size = 20
dataset = Dataset()
dataloader = torch.utils.data.DataLoader(dataset, batch_size=batch_size, shuffle=True)
for data in dataloader:
print("データ件数: " + str(len(data[0])))
print("data: {}".format(data[0]))
print("レーベル件数: " + str(len(data[1])))
print("label: {}".format(data[1]) + "\n")
以上がコードの全容です。
わかりやすくするために本当に最小限だけにしてます。
__init__はクラスを定義した際の処理です。
今回はとても小さなデータなのでinit内で全部定義しちゃってますが、
もしとても大きなデータをストレージからイテレーションしたい場合はここでパスなどを指定し、getitemで順番にイテレーションするといい感じです。
__len__はデータの総数を返します。
__getitem__はindexで指定されたデータを返します。
Dataloaderでイテレーションする際は後述するバッチサイズ分だけデータをまとめて指定して返します。
#Dataloaderについて
第一引数にDatasetクラスのインスタンスであるdatasetを渡します。
第二引数や第三引数には、バッチサイズやshuffleをするか__(True/False)__を渡します。
特別な理由がない限りshuffleはTrueにしましょう。
for 以外にもDataloaderをイテレーションする方法はありますが、ここではforを使います。
上記のプログラムを実行したのが以下です。
データ件数: 20
data: tensor([[6.8000, 3.0000, 5.5000, 2.1000],
[6.7000, 3.1000, 5.6000, 2.4000],
[5.4000, 3.9000, 1.3000, 0.4000],
[5.5000, 2.4000, 3.7000, 1.0000],
[5.1000, 3.7000, 1.5000, 0.4000],
[4.5000, 2.3000, 1.3000, 0.3000],
[6.6000, 2.9000, 4.6000, 1.3000],
[6.5000, 3.0000, 5.8000, 2.2000],
[7.0000, 3.2000, 4.7000, 1.4000],
[4.4000, 3.2000, 1.3000, 0.2000],
[5.0000, 3.4000, 1.5000, 0.2000],
[5.4000, 3.4000, 1.5000, 0.4000],
[4.9000, 2.4000, 3.3000, 1.0000],
[6.3000, 3.4000, 5.6000, 2.4000],
[7.7000, 2.6000, 6.9000, 2.3000],
[6.2000, 2.8000, 4.8000, 1.8000],
[6.2000, 3.4000, 5.4000, 2.3000],
[5.6000, 2.7000, 4.2000, 1.3000],
[6.1000, 3.0000, 4.9000, 1.8000],
[6.7000, 3.0000, 5.0000, 1.7000]], dtype=torch.float64)
レーベル件数: 20
label: tensor([2, 2, 0, 1, 0, 0, 1, 2, 1, 0, 0, 0, 1, 2, 2, 2, 2, 1, 2, 1],
dtype=torch.int32)
~~~~~途中省略~~~~~~
データ件数: 10
data: tensor([[4.8000, 3.4000, 1.6000, 0.2000],
[6.1000, 2.8000, 4.7000, 1.2000],
[5.1000, 3.8000, 1.9000, 0.4000],
[6.7000, 3.3000, 5.7000, 2.1000],
[6.4000, 2.9000, 4.3000, 1.3000],
[7.4000, 2.8000, 6.1000, 1.9000],
[6.4000, 3.2000, 5.3000, 2.3000],
[5.0000, 3.3000, 1.4000, 0.2000],
[5.0000, 3.2000, 1.2000, 0.2000],
[5.8000, 2.7000, 4.1000, 1.0000]], dtype=torch.float64)
レーベル件数: 10
label: tensor([0, 1, 0, 2, 1, 2, 2, 0, 0, 1], dtype=torch.int32)
データもレーベルもバッチサイズとして定義した20ずつで正しく出力してくれています。
150/20はあまり10なのですがDataloaderでは、エラーなく10件出力してくれています。
特別な処理なしでこのように合わせてくれるのも便利な点です。
#最後に
簡単な例ですので別途質問があればコメントください。
誤りがあった場合もお願いします。