Geminiとの対話をベースに、記事を構成しています。
参考書:
ゼロから作るDeepLearning Pythonで学ぶディープラーニングの理論と実践 斎藤康毅 著
開発環境:
VScode + 拡張機能Python(microsoft) + anaconda(統計処理、参考書の推薦ライブラリ)
この記事は、ゼロから作るDeep Learning 第5章の学習記録と、補足知識の記録になります。
内容は、徐々に追記していきます。
Affineレイヤ 計算グラフ
複数のレイヤが存在しますが、最も重要なAffineレイヤの計算グラフを、分解してみました。
入力X(2x2)、重みW(2x3)として、Affineレイヤの順方向と逆伝播の様子を書いてみました。

slice(分割):行列の分割
concatenate(結合):行列の結合
$(X \cdot W)_1$:X・Wの一行目
$(X \cdot W)_2$:X・Wのニ行目
- 「Slice/concatenate」ノードで分配され、順伝播で1つの大きな行列積の結果が、$(X \cdot W)_1$ と $(X \cdot W)_2$ に切り分けられて各加算器へ行きます。
順伝播(分割): 行列から「行」を取り出す操作。
逆伝播(結合): 各パスから戻ってきた勾配 $\frac{\partial L}{\partial y_1}, \frac{\partial L}{\partial y_2}$ が「Slice/concatenate」ノードで一つにまとまり、再び行列の形 $\frac{\partial L}{\partial Y}$ に戻ります。
これにより、コード上の dout(形状: $N \times M$)が、各データの勾配を縦に積み上げたものであることが物理的なイメージになります。 - 「B(バイアス)」への逆伝播、バイアス $B$ に向かう矢印。2つのルートからそれぞれ $\frac{\partial L}{\partial y_1}$ と $\frac{\partial L}{\partial y_2}$ が $B$ へ流れ込んでいます。これにより、「なぜバイアスの勾配が合計(np.sum(axis=0))になるのか」という疑問に対し、「同じ $B$ というメモリ領域が複数の計算ルートに使い回されたから、責任(勾配)も全員分を合計して戻ってくる」 という答えが、図から自明のものとなっています。
- dot ノードでの形状復元、$\frac{\partial L}{\partial Y} \cdot W^T$($X$の勾配)と $X^T \cdot \frac{\partial L}{\partial Y}$($W$の勾配)。ここで $\frac{\partial L}{\partial Y}$ が「Slice」で集約された後の**「行列の形」**として扱われているため、行列積の微分の公式(転置を掛けるルール)がそのまま適用できる状態になっています。
Bバイアス(3.)は、ノードの数だけの一次配列ですが、実装すると、ブロードキャストされます。バイアスのshapeの推移:(3.)=>(1, 3)=>(2,3)これで、X・Wのshape(2,3)にすべて足されます。
なので、逆伝播において、X・Wすべての要素にかかわるバイアスの微分値をもれなくかき集めて、元の(3.)に戻します。