【PyTorch】自作のDataset, DataLoader のつくり方!
まえがき
機械学習モデルの検証の際にPyTorchに付属のMNISTなどのデータセットを使用することも多いと思いますが、
実際の現場では独自のデータを使う機会の方が多いと思います。
今回は機械学習フレームワークPyTorchで自作のDataset, DataLoaderのつくり方をご紹介していきます!
Dataset のつくり方
まず必要なライブラリをインポートします。
Dataset, DataLoader はまとめてインポートできます。
import torch from torch.utils.data import Dataset, DataLoader from torchvision import transforms
サンプルデータを作成します。
numpy でデータを扱うことが多いと思うので、numpyで作成します。
import numpy as np sample_data = np.random.rand(100, 32, 32, 3)
sample_data.shape ### ------------------------- (100, 32, 32, 3)
では実際にDatasetを作成しましょう。雛形はこちらです。
class MyDataset(Dataset): def __init__(self, input_data): self.input_data = input_data self.len = input_data.shape[0] def __getitem__(self, index): img = self.input_data[index] return img def __len__(self): return self.len
必要な要素は2つです。
__getitem__(self, index)
①指定されたindexのデータを返すメソットです。
__len__(self)
②データセットの大きさ(長さ)を返すメソットです。
こちらに先ほどのサンプルデータを渡し、データセットを作成します。
dataset = MyDataset(sample_data) print(type(dataset[0])) print(dataset[0].shape) ### ---------------------- <class 'numpy.ndarray'> (32, 32, 3)
ただしこのままだと元のnumpy配列のままのデータセットです。
PyTorchで扱うには、Tensor に直さなくてなならないのでDataset内部でTensorに直す処理を加えましょう。
そのように修正したDatasetがこちらです。
class MyDataset(Dataset): def __init__(self, input_data, transform=None): self.input_data = input_data self.len = input_data.shape[0] self.transform = transform def __getitem__(self, index): img = self.input_data[index] if (self.transform is not None): img = self.transform(img) return img def __len__(self): return self.len
transform = transforms.Compose([transforms.ToTensor()]) dataset = MyDataset(sample_data, transform) print(type(dataset[0])) print(dataset[0].size()) ### ---------------------------- <class 'torch.Tensor'> torch.Size([3, 32, 32])
配列もnumpy(H, W, C) → tensor(C, H, W) にきちんと変換されてますね。
このtransforms.Compose()内にデータセットに加えたい一連の処理を順番に記述できます。
例えば、
transform = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor()])
とすると、
①画像サイズを256に変換
②画像の中心を切り取ってサイズを224にする
③テンソルに変換
というような一連の処理を加えることができます。
どのような処理があるかは他の参考ページをご参照ください。
ちなみに教師あり学習の場合、入力データとその教師ラベルをセットでデータセットにすることが多いと思うのでそのコードも載せておきます。
class MyDataset(Dataset): def __init__(self, input_data, label_data, transform=None): self.input_data = input_data self.label_data = label_data self.len = input_data.shape[0] self.transform = transform def __getitem__(self, index): img = self.input_data[index] label = self.label_data[index] if (self.transform is not None): img = self.transform(img) return img, label def __len__(self): return self.len
transform = transforms.Compose([transforms.ToTensor()]) label_data = np.ones(sample_data.shape[0]) dataset = MyDataset(sample_data, label_data, transform) print(dataset[0][0].size()) print(dataset[0][1]) ### ---------------------------- torch.Size([3, 32, 32]) 1.0
DataLoader のつくり方
Dataset が作成できたので、続けてDataLoaderを作成しましょう。
DataLoader はとても簡単です。
dataloader = DataLoader(dataset, batch_size=32, shuffle=True)
batch_size を指定することで、1回のiterateで何個のデータが渡されるかを指定できます。
shuffleはデフォルトはFalseになっており、datasetの順番通りにデータが渡されるのですが、
過学習を防ぎたい場合に訓練データをランダムな順番で渡して欲しい場合もあるので、
その際はshuffle=Trueにしておきましょう。
for img, label in dataloader: print(img.size(), label.size()) ### --------------------------------------- torch.Size([32, 3, 32, 32]) torch.Size([32]) torch.Size([32, 3, 32, 32]) torch.Size([32]) torch.Size([32, 3, 32, 32]) torch.Size([32]) torch.Size([4, 3, 32, 32]) torch.Size([4])
モデルへの入力時のエラー
for img, label in dataloader: img = img.to(device) output = model(img)
というような形でデータローダからモデルへ入力する際に
RuntimeError: expected scalar type Double but found Float
というエラーが出されることがあります。
基本的にモデル内のパラメータの型と入力データの型は一致している必要があります。
この際の解決法は入力データを img.float() として型変換することです。
for img, label in dataloader: img = img.to(device).float() output = model(img)
あとがき
いかがだったでしょうか。
DatasetやDataLoaderは自作でモデル作成をやり始めると必ず通る最初の関門なので、
(モデル作成のメイン部分ではないですし(>_<))簡単にコーディングできるようなっておきたいですね。