0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【PyTorchで深層学習】前処理の流れ(RNN、CNN)

Last updated at Posted at 2025-01-22

こんにちは。深層学習のRNNとCNNにおいて、どのような前処理をしていくのかをまとめていきます!モジュールとしては、PyTorchを使用します。

RNN

RNNは時系列データや自然言語処理等さまざまな場面で使われますが、今回は1時間ごと電力消費量というテーブルデータを例にします。
RNNの仕組みについては是非こちらをご参考ください!
【深層学習】RNN、LSTM、GRUまとめ(仕組み、ちょっとだけ数式)

1. データをtensor型に変換する

深層学習ではテンソル型が使われます。
多次元配列を表現するためのデータ構造となっており、ベクトルや行列もテンソル型です。
そのため、ここではまず最初に、事前にスケーリングしたデータをテンソル型へと変換します。

data_train = torch.tensor(X_train_scaled, dtype=torch.float32)
data_test = torch.tensor(X_test_scaled, dtype=torch.float32)
targets_train = torch.tensor(y_train_scaled, dtype=torch.float32)
targets_test = torch.tensor(y_test_scaled, dtype=torch.float32)

ポイント

  • dataは説明変数、targetは目的変数
  • torch.Tenor()を使う
  • 型(dtype)を明示する
  • Numpy配列を変換する際はfrom_numpyを使う

2. シーケンスデータを作る関数を定義

シーケンスとは、一定の順序をもつデータのことです。
テンソル型に変換したデータをシーケンスデータの形へと整形しRNNに適した入力形式を作ることで、順序や時間依存性を学習できるようになります。
まず関数を作っていきます。

def create_sequences(data, targets, sequence_length):
    sequences = []
    labels = []
    for i in range(len(data) - sequence_length):
        seq = data[i:i+sequence_length] # モデルの入力
        label = targets[i+sequence_length] # 予測すべき次の値
        sequences.append(seq)
        labels.append(label)
    return torch.stack(sequences), torch.stack(labels)

ポイント

  • create_sequences関数の作成
    • 引数:
      data → 説明変数
      targets → 目的変数
      sequence_length → シーケンスの長さ(指定する)
    • (データの長さlen(data)-シーケンスの長さsequence_length)分のfor文をまわす
    • 説明変数:スタート地点からシーケンスの長さ分をseqとして空リストsequencesに足していく
    • 目的変数:現在のシーケンスの次のラベルを空リストlabelsに足していく
  • stackメソッドで次元を増やしてテンソル同士を結合する。次元追加の必要がない場合はtorch.catを使う
  • この関数を使うと説明変数と目的変数のシーケンスデータがそれぞれ返ってくる

3. 入力シーケンスの長さを定義

# 過去の一定期間(24時間分)のデータを1つの入力シーケンとしてまとめる
sequence_length = 24

ポイント

  • ドメイン知識に基づいて適切な値を選ぶ
  • 小さい場合は過去情報が不足、大きい場合は計算不可が増加

4. シーケンスデータの作成

さきほど定義したcreate_sequences関数を使ってシーケンスデータを作成します。

train_sequences, train_labels = create_sequences(data_train, targets_train, sequence_length)
test_sequences, test_labels = create_sequences(data_test, targets_test, sequence_length)

print(train_sequences.shape)  # (n_samples, sequence_length, num_features)
print(train_labels.shape)     # (n_samples, 1)

ポイント

  • モデルの入力に適した形にしないとエラーになるので、ここで確認

5. データローダーの作成

データローダーとは、データセットを元に、データの取り出し方法(バッチ化、シャッフル、並列処理など)を指定するものです。

train_loader = DataLoader(TensorDataset(train_sequences, train_labels), batch_size=128, shuffle=True)
test_loader = DataLoader(TensorDataset(test_sequences, test_labels), batch_size=128, shuffle=False)

ポイント

  • バッチサイズはモデルの性能に影響するため、パラメータ調整に含まれる
  • batch_size:1つのバッチ(かたまり)に含めるデータ数
  • shuffle=True:学習時にはデータの偏りを防ぐためにシャッフルする

上記の処理を行った後、モデルを定義していきます。

CNN

CNNは主に画像認識に使われるモデルです。
ここでは、例として、CIFAR10(10種類の「物体カラー写真」(乗り物や動物など)の画像データセット)を使用するとします。
CNNの仕組みについては是非こちらをご参考ください!
【深層学習】CNNまとめ(仕組み、ちょっとだけ数式)

1. データの変換を定義

本来、画像データにはいろいろな大きさのものがあります。今回使う画像はサイズが揃っていますが、揃っていない場合はここで定義します。その他にもモデルがデータを読み込んでくれるように変換を施していきます。

transform = transforms.Compose(
    [transforms.ToTensor(),
     transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))])

ポイント

  • 複数の前処理を順番に適用するもの(sklearnのpiplineをイメージ)
  • ToTensor():テンソル型に変換
  • Normalize():標準化する((平均1,平均2,平均3)), ((標準偏差1,標準偏差2,標準偏差3)) ※3チャンネル分だが、同じ場合は一つだけ記載すればよい
  • データ拡張(回転、ズーム、反転など)をしたい場合はここで設定
    https://www.kikagaku.co.jp/kikagaku-blog/pytorch-torchvision/
    • 例えば、以下のように設定する
transform = transforms.Compose([
    transforms.RandomHorizontalFlip(),  # 水平反転
    transforms.RandomRotation(15),     # 15度回転
    transforms.ToTensor(),
    transforms.Normalize((0.5,), (0.5,))  # 正規化
])

2. データセットの作成

今のディレクトリにdataディレクトリが完成し、そこから画像を読み込んでいくことになります。

train_set = torchvision.datasets.CIFAR10(root='./data', train=True, download=True, transform=transform)
test_set = torchvision.datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)

ポイント

  • torchvisiontorchvision.datasetsモジュールに多くの組み込みデータセットを提供しており、そこからCIFAR10のデータをダウンロード
  • root='./data':同じディレクトリの下層にdataディレクトリができる
  • transform:先ほど作った一連の変換を適用する
  • download:trueにすると、ウェブからPCにダウンロードされる(すでにデータセットがダウンロードされている場合、再ダウンロードはされない)

3. データローダーの作成

RNNと共通で、データセットからのデータの取得方法を指定します。

# 訓練用データ
train_loader = torch.utils.data.DataLoader(train_set, batch_size=4, shuffle=True, num_workers=1)
# テスト用データ
test_loader = torch.utils.data.DataLoader(test_set, batch_size=4, shuffle=False, num_workers=1)

ポイント

  • 並列処理num_workersを設定することでデータローディングが高速化される。データの読み込み時に利用するプロセス数のため、num_workers=os.cpu_count()としてもOK

4. ラベル名の定義

class_labels = train_set.classes
class_labels
# 出力結果
['airplane',
 'automobile',
 'bird',
 'cat',
 'deer',
 'dog',
 'frog',
 'horse',
 'ship',
 'truck']

ポイント

  • 可視化や確認のために使うので変数に代入しておく
  • 今回はtrain_setオブジェクトから引っ張ってこれましたが、難しい場合はclass_labels = ("","",,)のように自分で定義していきます

5. 画像の確認

どのような画像が入っているかを可視化します。

class_images = {}  # クラスIDをキー、画像データを値とする辞書
for image, label in train_set:
    # 正規化解除(データが -1 ~ 1 の場合、0 ~ 1 に戻す)
    image = image / 2 + 0.5
    np_image = image.numpy()  # テンソルをNumPy配列に変換
    tr_image = np.transpose(np_image, (1, 2, 0))  # 画像の次元を (C, H, W) から (H, W, C) に変換

    # クラスIDを取得
    class_id = int(label) 

    # クラスごとに1枚の画像を収集
    if class_id not in class_images:
        class_images[class_id] = tr_image

    # 全クラス分の画像が収集できたら終了
    if len(class_images) == len(class_labels):
        break

# 画像を3×3のグリッドで表示
fig, axes = plt.subplots(2, 5, figsize=(10, 5))

# 各クラスの画像をプロット
for ax, (class_id, img) in zip(axes.ravel(), class_images.items()):
    ax.imshow(img)
    ax.set_title(class_labels[class_id])
    ax.axis("off")

# プロット領域を整える
plt.tight_layout(rect=[0, 0, 1, 0.95])
plt.show()

このように出力されます。
image.png

ポイント

  • axes:サブプロットオブジェクトの配列
  • ravel() :2次元配列を1次元にフラット化 → フラット化することで、ループ処理が簡単になり、zip() で対応する画像やクラス名を割り当てやすくなる
  • tight_layout(): プロット内の要素が重ならないように自動調整するもの

6. 分布の確認

今回はオープンデータであらかじめtrainとtestに分割されたデータなので必要ありませんが、本来であれば自分でtrainとtestに分割するので、クラス分布が不均衡になっていないか確認します。分布が均等でない場合、データのリサンプリングや重み付けが必要です。

from collections import Counter
import matplotlib.pyplot as plt
import japanize_matplotlib

# クラスIDをカウントする
label_counts = Counter(label for _, label in train_set)

fig, ax = plt.subplots(figsize=(8,5))
ax.bar(class_labels, [label_counts[i] for i in range(len(class_labels))])
ax.set_title("クラスごとのラベル分布")
ax.set_xlabel("クラス名")
ax.set_ylabel("サンプル数")
plt.xticks(rotation=45)
plt.show()

こんな感じで出力されます。すべて5000件で揃っているようです。
image.png

ポイント

  • Counter:クラスIDごとのカウントが保存される

7. 形状の確認

最後に、モデルに入力する形状と一致しているか確認します。

# 1バッチ分のデータを取り出し、形状を確認
train_features_batch, train_labels_batch = next(iter(train_loader))

# データローダーから取得したバッチは特徴量(batch_size, channels, height, width)とラベル(batch_size,1)のペアとして返される
train_features_batch.shape, train_labels_batch.shape

ポイント

  • iter():データローダーからイテレータを作成
  • next():イテレータから1回分のデータを取得する

上記の処理を行った後、モデルを定義していきます。


以上です。読んでいただきありがとうございました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?