こんにちは。深層学習の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)
ポイント
-
torchvision
:torchvision.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()
ポイント
-
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件で揃っているようです。
ポイント
-
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回分のデータを取得する
上記の処理を行った後、モデルを定義していきます。
以上です。読んでいただきありがとうございました。