Python対話モードでConv1dを理解する
Conv1dは、一次元データ(例:時系列データ、音声信号)に対して畳み込み演算を行うためのPyTorchの関数です。公式ドキュメントはこちらです。
以前、音声合成プロジェクトで初めてこの関数に触れた時、PyTorch初心者として、そのパラメータと動作に困惑しました。1次元畳み込みなので、入出力は単純な1次元配列だと思っていました。でも実際には、入出力は3次元のテンソルです。
その後、仕事の都合で一時期深層学習に触れない期間がありました。再びConv1d関数に遭遇した時、また同じ困惑に陥りました。そこで、同じような状況に遭遇する可能性のある読者(将来の自分も含む)の助けになるよう、自分の理解プロセスを書き留めることにしました。
本記事で使用するツールのバージョンは、Python 3.13.3、PyTorch 2.7.1です。
Pythonのバージョン番号はpython -Vコマンドで確認できます。
> python -V
Python 3.13.3
PyTorchのバージョン番号はpip show torchコマンドで確認できます。
> pip show torch
Name: torch
Version: 2.7.1
...
または、以下のようにPython対話モードでも確認できます。
> python
Python 3.13.3 (tags/v3.13.3:6280bb5, Apr 8 2025, 14:47:33) [MSC v.1943 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> torch.__version__
'2.7.1+cpu'
Conv1dの入出力形状を理解するには、深層学習におけるバッチ(batch)とチャンネル(channel)の概念を覚える必要があります。以下、Python対話モードで一歩ずつ実行してこの点を理解していきます。
ステップ1:Conv1dインスタンスの作成
Conv1dを使うには、まずインスタンスを作成する必要があります。インスタンス化の際には3つの主要パラメータを指定します:
- in_channels: 入力チャネル数。例えば、モノラル音声なら1、ステレオ音声なら2となります。
- out_channels: 出力チャネル数で、畳み込みフィルターの数を意味します。
- kernel_size: 畳み込みフィルタのサイズを指定します。
例えば、入力チャネル数2、出力チャネル数3、カーネルサイズ5のConv1dインスタンスは以下のように作成します。
>>> from torch import nn
>>> conv1d = nn.Conv1d(2, 3, 5)
ステップ2:重みとバイアスの確認
インスタンスを作成すると、畳み込み演算に用いられる重み(weight)とバイアス(bias)が自動的に初期化されます。これらの値は、機械学習の工程に、学習を通して少しずつ最適化されます。
>>> conv1d.weight.shape
torch.Size([3, 2, 5])
>>> conv1d.weight
Parameter containing:
tensor([[[ 0.2076, 0.1669, -0.0984, 0.0040, -0.0094],
[-0.0277, -0.0257, -0.3107, 0.2883, 0.2560]],
[[ 0.0501, -0.2257, 0.0464, -0.0605, 0.1463],
[-0.1195, 0.1820, 0.0764, 0.2922, -0.2718]],
[[ 0.2699, -0.2734, 0.1698, 0.2720, -0.2554],
[-0.0706, -0.2028, -0.2554, -0.1495, 0.2460]]], requires_grad=True)
>>> conv1d.bias
Parameter containing:
tensor([-0.1664, -0.0393, 0.2074], requires_grad=True)
-
conv1d.weightは各フィルタの重みを表し、形状は(出力チャネル数, 入力チャネル数, カーネルサイズ) -
conv1d.biasは、各フィルタのバイアスを表し、形状は(出力チャネル数)
ステップ3:入力データの準備
Conv1dへの入力データは3次元配列である必要があります:
- 1次元目: バッチサイズ (
batch_size) 、一度に処理するデータサンプル数を意味します。 - 2次元目: 入力チャネル数、
Conv1dインスタンス作成時に指定したin_channelsと一致する必要があります。 - 3次元目: 一つのデータサンプルの長さ。例えば、音声データの場合、時間軸のサンプル数(フレーム数)が一般的です。
>>> import torch
>>> x = torch.rand(4, 2, 6) # バッチサイズ4, 入力チャネル数2, データ長6のランダムデータ
>>> x
tensor([[[0.5487, 0.5584, 0.7142, 0.6534, 0.2741, 0.9281],
[0.8480, 0.2020, 0.3171, 0.4146, 0.8032, 0.4344]],
[[0.6481, 0.6134, 0.9704, 0.5787, 0.1803, 0.1568],
[0.6690, 0.8022, 0.2466, 0.2352, 0.2962, 0.2569]],
[[0.7968, 0.8019, 0.4341, 0.4369, 0.5804, 0.2109],
[0.8624, 0.1870, 0.9518, 0.7393, 0.3436, 0.4962]],
[[0.5005, 0.9210, 0.0010, 0.9487, 0.0381, 0.5954],
[0.5601, 0.9394, 0.3438, 0.5394, 0.6369, 0.4937]]])
ステップ4: 畳み込み演算の実行
入力データxをconv1dに渡して結果を確認します:
>>> y = conv1d(x)
>>> y.shape
torch.Size([4, 3, 2])
>>> y
tensor([[[ 0.1683, 0.1969],
[-0.2416, 0.1589],
[ 0.3856, -0.0863]],
[[ 0.0034, 0.1147],
[-0.0358, -0.2050],
[ 0.2556, 0.0671]],
[[ 0.0630, -0.0035],
[ 0.0246, 0.0918],
[-0.1203, 0.1590]],
[[ 0.2665, 0.0337],
[-0.1590, 0.1789],
[ 0.0974, 0.2275]]], grad_fn=<ConvolutionBackward0>)
出力yは3次元テンソルで、形状は(バッチサイズ, 出力チャンネル数, 出力サイズ)です。
ステップ5: 計算過程の理解
入力テンソルxのバッチサイズは4なので、4つのデータがあります。よって、yの1次元の長さも4になります。
即ち、それぞれのデータに対して畳み込み演算を行い、出力y[0]からy[3]が得られます。
畳み込み演算は出力チャンネルごとに行われます。それぞれの出力チャンネルに専属のカーネルとバイアスが対応します。例えば、conv1d.weight[0]とconv1d.bias[0]は出力チャネル0(y[:,0])に対応します。つまり、出力チャンネルと同じ数だけのカーネルとバイアスが存在します。
各出力チャンネルの畳み込み演算以下のように行われます:
- カーネルが入力データ上を移動(スライド)
- カーネルと入力データの要素ごとの積の和を計算
- バイアスを加算
出力の1要素は以下の計算で得られます:
y[バッチ番号, 出力チャネル番号, 出力データ位置] =
sum(x[バッチ番号, 入力チャネル0, 入力データ位置:入力データ位置+カーネルサイズ] @ conv1d.weight[出力チャネル番号, 入力チャネル0]) +
sum(x[バッチ番号, 入力チャネル1, 入力データ位置:入力データ位置+カーネルサイズ] @ conv1d.weight[出力チャネル番号, 入力チャネル1]) +
... +
conv1d.bias[出力チャネル番号]
@は行列の内積を表します。A @ BはA.dot(B)と同じです。
出力サイズは以下の公式で計算されます:
出力サイズ = (入力サイズ - カーネルサイズ + 1)
例えば、y[0, 0, 0]は以下のように計算されます。
>>> y[0,0,0] # 0番目のデータ, 0番目の出力チャネル, 0番目の出力データ位置
tensor(0.1683, grad_fn=<SelectBackward0>)
>>> x[0,0,0:5] @ conv1d.weight[0,0] + x[0,1,0:5] @ conv1d.weight[0,1] + conv1d.bias[0]
tensor(0.1683, grad_fn=<AddBackward0>)
y[0, 0, 1]は以下のように計算されます。
>>> y[0,0,1] # 0番目のデータ, 0番目の出力チャネル, 1番目の出力データ位置
tensor(0.1969, grad_fn=<SelectBackward0>)
>>> x[0,0,1:6] @ conv1d.weight[0,0] + x[0,1,1:6] @ conv1d.weight[0,1] + conv1d.bias[0]
tensor(0.1969, grad_fn=<AddBackward0>)
このように、Conv1dは入力データxに対して畳み込み操作を行い、新しい特徴量yを生成します。特徴量yは音声認識、自然言語処理などの様々な1次元(単一チャンネルまたは多チャンネル)データの処理タスクに使用できます。
まとめ
- 入力は3次元テンソル:
(バッチサイズ, 入力チャンネル数, 入力サイズ) - 出力も3次元テンソル:
(バッチサイズ, 出力チャンネル数, 出力サイズ) - 出力サイズは公式
入力サイズ - フィルタサイズ + 1で決まる - 各出力要素は入力データとフィルタ重みの内積にバイアスを加えたもの
Discussion