概要
個人的な備忘録を兼ねたPyTorchの基本的な解説とまとめになります.PyTorchの1次元畳み込みとプーリング層の概略についてまとめていきたいと思います。
1. 1次元畳み込み層 (Conv1d)
1次元畳み込み層は、画像処理で広く使われる2次元畳み込みを1次元データに適用したものです。特に系列データの局所的なパターンを効率的に捉えることができるため、センサーデータ解析、音声処理、自然言語処理などの時系列データで幅広く利用されています。
適用データの例
具体的なデータのパターンを紹介します。1種類目は特徴量が1つの系列データです。連続する時刻で観測されるデータに対して、時間的な依存関係やトレンドを学習するのに利用されます。
- 気象データ: 20.5°C, 21.2°C, 19.8°C, 18.3°C,...
- 売上データ: 1000円, 1200円, 950円, 1100円,...
- 株価データ: 1500円, 1520円, 1480円, 1550円,...
- 音声信号: 0.1, -0.2, 0.3, -0.11, ...
実際の応用では、同じ時刻で複数の特徴量を同時に観測することが一般的です。2種類目は特徴量が複数ある系列データです。マルチチャンネルデータと呼ばれることもあります。Conv1dでは、複数の特徴量(チャンネル)を持つデータを扱うこともできます。
例:センサーデータ(2チャンネル)
時刻 | t=0 | t=1 | t=2 | t=3 | ... |
---|---|---|---|---|---|
気温 | 20.5°C | 21.2°C | 19.8°C | 18.3°C | ... |
湿度 | 40% | 51% | 55% | 48% | ... |
各時刻で複数の情報を同時に考慮しながら、時間方向のパターンを学習するのに利用するのが1次元畳み込み層の基本的な活用方法になります。
PyTorchのConv1d
- nn.Conv1d(in_channels, out_channels, kernel_size, stride)
- 入力データの形状:(バッチサイズ, チャンネル数, 系列長)
実際の計算は、2次元の畳み込み層と同様の精神で、カーネルサイズの枠を右側へ移動しながら畳み込み計算をしていく形になります。1次元畳み込み層は(バッチサイズ、チャンネル数、系列長)という形状の入力データに対して畳み込み演算を行う操作になります。
2. in_channelsの数と実際の計算結果
時系列データを想定して実際の計算方法を確認していきます。
2.1 特徴量が1つ(in_channels=1)での計算方法
入力データが5つ
入力データを$x = (x_1, x_2, x_3, x_4, x_5)$、カーネルを$(w_1, w_2, w_3)$とします。
conv1d = nn.Conv1d(in_channels=1, out_channels=1, kernel_size=3, bias=False)
conv1d(x)の値$(y_1, y_2, y_3)$は、
$$
(w_1 x_1 + w_2 x_2 + w_3 x_3,~w_1 x_2 + w_2 x_3 + w_3 x_4,~w_1 x_3 + w_2 x_4 + w_3 x_5)
$$
となります。形状は(out_channels, 3)でout_channelsの数だけ、カーネルが作成されます。
一般的な形
入力データを
$$x=(x_1, x_2,..., x_n)、$$
カーネル$w$とバイアス項$b$をそれぞれ、
$$w=(w_1, w_2,...,w_k),~ b\in\mathbb{R}$$
と表記します。入力データの系列長の方がカーネルサイズ以上でないと計算できないので、$n\ge k$とします。$i$番目の出力は
$$
y_i = \sum_{j=1}^{k} w_j x_{i+j-1} + b
$$
と計算されます。
入力データを時系列データと考えると、1次元畳み込みはデータの前後関係(局所的な関係)を抽出しながら、新しい特徴量へ変換している操作になります。時刻方向の局所的なパターンを捉えると解釈できます。
具体的な計算例1
入力データ(1, 0, 2, 0, 3)を、カーネル(1, 0, -1)とします。バイアス項は「なし」で計算してみます。
nn.parameterを使ってカーネルの値を指定することで、図と等しい計算結果を確認できるはずです。
import torch
import torch.nn as nn
from torch.nn.parameter import Parameter
# 入力データは(バッチサイズ、チャンネル数、系列長)
x = torch.FloatTensor([1, 0, 2, 0, 3]).reshape(1,1,5)
# Conv1dの設定
cnn1 = nn.Conv1d(in_channels=1, out_channels=1, kernel_size=3, bias=False)
# カーネルの値を(1, 0, -1)に指定
param = torch.FloatTensor([1,0,-1]).reshape(1,1,3)
cnn1.weight = Parameter(param)
# 実際に計算
y = cnn1(x)
#
# y= tensor([[[-1., 0., -1.]]])
# y.shape = torch.Size([1, 1, 3])
2.2 特徴量が2つ(in_channels=2)での計算方法
入力データを
x=
\begin{pmatrix}
x_{1,1} & x_{1,2} & ... & x_{1,n} \\
x_{2,1} & x_{2,2} & ... & x_{2,n} \\
\end{pmatrix}
カーネルを
w =
\begin{pmatrix}
w_{1,1} & w_{1,2} & ... & w_{1,k} \\
w_{2,1} & w_{2,2} & ... & w_{2,k} \\
\end{pmatrix}
定数$b$はバイアス項で、$n\ge k$とします。$i$番目の出力値は、
$$
y_i = \sum_{c=1}^{2}\sum_{j=1}^{k} w_{c,j} x_{c,i+j-1} + b
$$
となります。
具体的な計算例2
入力データが2チャンネルのデータを準備します。
x=
\begin{pmatrix}
1& 0& 2& 0& 3 \\
0& 1& 0& 2& 0
\end{pmatrix}
カーネルは、kernel_size=3とすれば、自動的に2x3の形状になります。サンプルでは
x=
\begin{pmatrix}
1 & 0& -1 \\
-1& 0& 1
\end{pmatrix}
と指定します。バイアス項は「なし」で計算してみます。
カーネルの値はnn.parameterで指定しています。機械学習時には、このカーネルの値が更新されることになります。
import torch
import torch.nn as nn
from torch.nn.parameter import Parameter
# 入力データは(バッチサイズ、チャンネル数、系列長)
x = torch.FloatTensor([[1,0,2,0,3],[0,1,0,2,0]]).reshape(1,2,5)
# Conv1dの設定
# kernel_size=3でkernel_size=(2,3)に設定される
cnn2 = nn.Conv1d(in_channels=2, out_channels=1, kernel_size=3, bias=False)
# カーネルの値を(1, 0, -1), (-1,0,1)に指定
param = torch.FloatTensor([[1,0,-1],[-1,0,1]]).reshape(1,2,3)
cnn2.weight = Parameter(param)
# 実際に計算
y = cnn2(x)
#
# y= tensor([[[-1., 1., -1.]]])
# y.shape = torch.Size([1, 1, 3])
3. strideの数と実際の計算結果
カーネルの横に移動する量をstrideと呼んでいます。デフォルト値はstride=1で、右に1個ずつ移動ながら計算してくことになります。この移動幅のstrideとカーネルサイズを調整することで、大まかな特徴・細かな特徴などを捉えることが可能となります。
stride=1の場合
- 入力データのすべての位置で演算を行うため、最も細かい特徴を保持できる
- 時系列データでは短期的な変化やノイズも捉えてしまう
stride≧2の場合
- データをダウンサンプリングする効果がある
- 出力サイズが小さくなり、計算量を大幅に削減できます
- より大局的な特徴やパターンを捉えやすくなります
- 一方で、細かい情報や局所的な変化は見落とす可能性があります
strideの設定は特徴抽出の粒度を調整する値と解釈できます。
具体的な計算例3(stride=2のケース)
入力データ(1, 0, 2, 0, 3)を、カーネル(1, 0, -1)とします。バイアス項は「なし」で計算してみます。図の入力データの右端「0」がない形となります。stride=2なので、右に2個移動して計算が終わりとなります。
続いて、図と同様の入力データ(1, 0, 2, 0, 3, 0)で計算してみます。stride=2なので、右に2個移動しても、右端の「0」が残っています。右側に数値が残ってしまっている場合、黄色で囲まれた部分は計算できないので、青色の部分までで畳み込み演算は終了となります。右端の「0」は使われず、実質、(1, 0, 2, 0, 3)と同じ結果になります。
下記のコードをx = torch.FloatTensor([1,0,2,0,3]).reshape(1,1,5)
と変更すれば1つ目の例になり、同じ出力結果であることが確認できます。
import torch
import torch.nn as nn
from torch.nn.parameter import Parameter
# 入力データは(バッチサイズ、チャンネル数、系列長)
x = torch.FloatTensor([1,0,2,0,3,0]).reshape(1,1,6)
# Conv1dの設定 stride=2
cnn3 = nn.Conv1d(in_channels=1, out_channels=1, kernel_size=3, bias=False, stride=2)
# カーネルの値を(1, 0, -1)に指定
param = torch.FloatTensor([1,0,-1]).reshape(1,1,3)
cnn3.weight = Parameter(param)
y = cnn3(x)
#
# y = tensor([[[-1., -1.]]])
# y.shape = torch.Size([1, 1, 2])
出力される系列長の数は、
出力される系列長 = ((入力データの系列長 - kernel_size)//stride) + 1
と計算されるようです。
4. プーリング層
1次元畳み込み層と似たような概念に、1次元プーリング層があります。こちらは2次元プーリング層の1次元版となります。
4.1 1次元プーリング層
カーネルサイズや移動幅 (stride) を指定してプーリングの値を求めるタイプです。
名前 | クラス | 説明 |
---|---|---|
最大プーリング | MaxPool1d(kernel_size, stride) | カーネルサイズの中で最大値を求める |
平均プーリング | AvgPool1d(kernel_size, stride) | カーネルサイズの中での平均値を求める |
入力するデータは、1次元畳み込みと同じで、(バッチサイズ、チャンネル数、系列長)となります。注意点は1点、stride=kernel_sizeがデフォルト値となります。特に指定しない限り、カーネルサイズ分、右へ移動する形になります。
図の内容をコードで確認してみます。プーリング層は値の最大値や平均値を計算するだけなので重みの概念はありません。
import torch
import torch.nn as nn
x = torch.FloatTensor([1,2,3,4,5,6]).reshape(1,1,6)
max_pool = nn.MaxPool1d(kernel_size=3, stride=1)
avg_pool = nn.AvgPool1d(kernel_size=3, stride=1)
max_y = max_pool(x)
avg_y = avg_pool(x)
#
# max_y = tensor([[[3., 4., 5., 6.]]])
# avg_y = tensor([[[2., 3., 4., 5.]]])
MaxPool1dがカーネルの範囲内での最大値を、AvgPool1dがカーネルの範囲内での平均値を与えていることがわかります。kernel_sizeやstrideの数値を変更して、右側が残る場合は「具体的な計算例3」のように右側が計算に使われないことも確認できます。
4.2 適応的プーリング (Adaptive Pooling Layer)
出力サイズを指定して、それ以外は自動で調整してくれるプーリング層となります。
名前 | クラス | 説明 |
---|---|---|
適応的最大プーリング | AdaptiveMaxPool1d(output_size) | 最大値で指定したサイズに出力 |
適応的平均プーリング | AdaptiveAvgPool1d(output_size) | 平均値で指定したサイズに出力 |
入力サイズに関係なく、output_size
に指定した出力サイズへ自動調整してくれるというプーリング層となります。入力が長さ1000でも500でもAdaptiveAvgPool1d(8)なら必ず長さ8に調整してくれるので分類するネットワーク層classifierの前に使うと便利かな
次回とその他
- conv1dを利用した演習を行う予定です
- 因果畳み込みなども触れたいな
PyTorchの1次元畳み込み層とプーリング層に関する公式のページ
Conv1dの解説ページ
目次ページ