🎃

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: 畳み込み演算の実行

入力データxconv1dに渡して結果を確認します:

>>> 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. カーネルが入力データ上を移動(スライド)
  2. カーネルと入力データの要素ごとの積の和を計算
  3. バイアスを加算

出力の1要素は以下の計算で得られます:

y[バッチ番号, 出力チャネル番号, 出力データ位置] = 
  sum(x[バッチ番号, 入力チャネル0, 入力データ位置:入力データ位置+カーネルサイズ] @ conv1d.weight[出力チャネル番号, 入力チャネル0]) +
  sum(x[バッチ番号, 入力チャネル1, 入力データ位置:入力データ位置+カーネルサイズ] @ conv1d.weight[出力チャネル番号, 入力チャネル1]) +
  ... +
  conv1d.bias[出力チャネル番号]

@は行列の内積を表します。A @ BA.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