werry-chanの日記.料理とエンジニアリング

料理!コーディング!研究!日常!飯!うんち!睡眠!人間の全て!

Pytorch公式のDataLoaderなしで学習を回す

Pytorch公式のtorch.utils.data.DataLoaderって, torch.utils.data.Datasetクラス継承してiteratorとかlen改造したりと, 初心者の心を折る要素がいっぱいあります。

今回は, DataLoaderなしでNumpyを基本として学習させる方法をメモします。

要点だけ言えば, numpy.array(dtype=np.float32)からtorch.tensor()へcast(型変換)すればOKです。
torch.nn.Module系のモデルは, 基本的にtorch.tensor(dtype=torch.float32)しか受け付けません。
mobile-net系とかは量子化されてそうだから違うのかもしれない...


では実装, まずは簡易データを作ります。

# make_easy_dataset.py
import numpy as np


def make_easy_dataset(X_length):
    theta = np.linspace(0,10*np.pi, 5000)
    sin   = np.sin(theta)
    X, y  = [], []
    for i in range(len(theta) - X_length - 1):
        X.append([sin[i : i + X_length]])
        label = [0,1]
        if sin[i + X_length + 1] > sin[i + X_length]:
            label = [1,0]
        y.append(label)
    return np.array(X, dtype=np.float32), np.array(y, dtype=np.float32)

Xは, sin波の時系列データ,
yは, 次に上昇するか否かの判定結果です。

この時, 注意すべきは, Xの型を dtype=np.float32 に設定しておくことです。
np.float32に設定しておかないと, 後ほどtorch.tensorへの変換でエラーが出ます。


次にモデルを適当に作っておきます。

以下は, 自作の任意層数1dConvolution簡易モデル, 任意層数FullConnect簡易モデル, 簡易結合networkです。
自分で使いたいモデルがある人は, 自分のモデルでOKです。

# DNN_module.py
import torch
from   torch import nn


class SimpleCNN1d(nn.Module):
    def __init__(self, in_channel:int, out_channel:int, num_block:int, kernel_size:int, stride:int=1, dropout:float=0.0):
        super(SimpleCNN1d, self).__init__()
        self.in_channel  = in_channel
        self.out_channel = out_channel
        self.num_block   = num_block
        self.kernel_size = kernel_size
        self.dropout     = dropout
        layers = []
        for i in range(num_block):
            if i != 0:
                layers += [nn.Conv1d(out_channel, out_channel, kernel_size, stride), nn.ReLU()]
            else:
                layers += [nn.Conv1d( in_channel, out_channel, kernel_size, stride), nn.ReLU()]
        if dropout > 0:
            layers += [nn.Dropout(dropout)]
        self.net = nn.Sequential(*layers)
    def forward(self, x):
        return self.net(x)


class FC_classifier(nn.Module):
    def __init__(self, in_channel:int, in_length:int, out_channel:int, num_block:int):
        super(FC_classifier, self).__init__()
        self.in_dim      = in_channel * in_length
        self.out_channel = out_channel
        self.num_block   = num_block
        self.flatten     = nn.Flatten()
        layers = []
        for i in range(num_block):
            in_dim_  = self.in_dim // (i + 1)
            if in_dim_ < 1:
                in_dim_ = 1
            out_dim_ = in_dim_ // 2
            if out_dim_ < out_channel or i == num_block-1:
                out_dim_ = out_channel
            layers += [nn.Linear(self.in_dim, out_dim_)]
        layers += [nn.Hardsigmoid()]
        self.net = nn.Sequential(*layers)
    def forward(self, x):
        x = self.flatten(x)
        out = self.net(x)
        return out


class CombinedNet(nn.Module):
    def __init__(self, models):
        super(CombinedNet, self).__init__()
        layers = []
        for model in models:
            layers += [model]
        self.net = nn.Sequential(*layers)
    def forward(self, x):
        return self.net(x)


それではnumpyベースのデータセットとモデルを定義して, 実際に学習を回しましょう。

import torch
import make_easy_dataset as med
import DNN_module


if __name__ == "__main__":
    data_len    = 10
    X, y = med.make_easy_dataset(data_len)

    num_block   = 1
    base_model        = DNN_module.SimpleCNN1d( in_channel = 1,
                                                out_channel = 1,
                                                num_block   = num_block,
                                                kernel_size = 3,
                                                dropout     = 0.2,
                                                )

    base_out_data_len = data_len - 2*num_block
    classifier        = DNN_module.FC_classifier(   in_channel  = 1,
                                                    in_length   = base_out_data_len,
                                                    out_channel = 2,
                                                    num_block   = 1,
                                                    )

    combined_model    = DNN_module.CombinedNet([base_model, classifier])


    batch_size = 256
    epochs = 10
    for t in range(epochs):
        for num_batch in range(len(X) // batch_size + 1):
            index_start =   num_batch * batch_size
            index_end   = index_start + batch_size
            # torch.tensorへcast(型変換)
            X_batch     = torch.tensor(X[index_start : index_end]) # float32
            y_batch     = torch.tensor(y[index_start : index_end], dtype=torch.long) # classiferなのでintでOK, float 32でもOK
            out = combined_model(X_batch)
            # after processing ....

以上です。

基本的にnumpy.float32型データでデータセットを作っていれば, torch.tensor()へ変換できます。