第10章 形を合わせる:転置、reshape、ブロードキャスト
こんにちは。ゆうせいです。
第9章までで、線形代数の主要な計算は一通り揃いました。第10章は、少し性格の異なる章です。理論というより、実務のための章です。ディープラーニングのコードを書き始めると、最も頻繁に遭遇するのが、形が合わないというエラーです。私の経験でも、初学者がつまずいて先に進めなくなる原因の大半は、このエラーです。しかも、厄介なことに、形が合っていないのにエラーにならず、間違った結果が出るという場合もあります。この章では、形を操作する三つの道具、転置、reshape、ブロードキャストを扱います。この連載の中で、最も即効性のある内容になります。
転置
転置とは何か
転置とは、行列の行と列を入れ替える操作です。
この行列を転置すると、次のようになります。
2行3列だった行列が、3行2列になりました。
元の1行目である が、転置後の1列目になっています。
記法
転置は、右肩にTを付けて表します。
ダッシュを付ける流儀もありますが、ディープラーニングの文献ではTが一般的です。
形の変化
(m, n) の行列を転置すると、(n, m) になります。
行数と列数が入れ替わるだけです。要素の総数は変わりません。
転置の性質
いくつかの重要な性質があります。
二回転置すると、元に戻ります。
積の転置は、順序が逆になります。
この二つ目の性質は、第15章で誤差逆伝播を扱う際に、重要な役割を果たします。
なぜ転置が必要になるのか
行列積では、左の列数と右の行数が一致していなければなりません。
形が合わない場合、片方を転置することで、合わせられることがあります。
たとえば、(32, 768) と (3072, 768) は、そのままでは掛けられません。内側の768と3072が一致していないためです。
しかし、右側を転置して (768, 3072) にすれば、掛けられます。
第9章で と書いた理由が、これです。

PyTorchのnn.Linearにおける転置
ここで、実務でよく混乱する点を扱います。
PyTorchで nn.Linear(768, 3072) を作ると、内部の weight は (3072, 768) の形で保持されます。
一方、入力データは (batch_size, 768) の形で渡されます。
このままでは掛けられないため、内部で weight を転置してから計算しています。
# PyTorchの内部で行われている計算のイメージ
output = input @ weight.T + bias
なぜ最初から (768, 3072) の形で保持しないのでしょうか。
理由は、実装上の慣例と、メモリ配置の効率によるものです。深く追う必要はありませんが、weight の形は出力次元が先、という事実は覚えておいてください。
reshape
reshapeとは何か
reshapeとは、要素の総数を変えずに、形を変える操作です。
12個の要素があるとき、次のような形にできます。
(12,) という1次元
(3, 4) という2次元
(4, 3) という2次元
(2, 6) という2次元
(2, 2, 3) という3次元
いずれも要素数は12で共通しています。
要素の並び順
reshapeで重要なのは、要素がどの順序で並べ直されるかです。
NumPyやPyTorchでは、最後の軸から順に埋めていく方式が標準です。これをC順序と呼びます。
を (2, 3) にreshapeすると、次のようになります。
横方向に埋めてから、次の行に移ります。
(3, 2) にすると、次のようになります。
マイナス1の指定
reshapeでは、片方の次元にマイナス1を指定できます。
これは、残りは自動で計算してください、という意味です。
要素数が12のとき、reshape(3, -1) とすれば、自動的に (3, 4) になります。
バッチサイズが可変の場合など、実務では頻繁に使われます。
転置とreshapeの違い
ここが、最もつまずきやすい点です。
(2, 3) の行列を (3, 2) にする方法は、二つあります。転置とreshapeです。
しかし、結果はまったく異なります。
転置すると、次のようになります。
reshapeすると、次のようになります。
形は同じ (3, 2) ですが、中身が違います。
転置は、要素の位置関係を保ったまま、行と列の役割を入れ替えます。
reshapeは、要素を一列に並べ直してから、指定の形に詰め直します。
この違いを理解していないと、形は合っているのに結果がおかしい、という状況に陥ります。しかもエラーにならないため、原因の特定が困難です。
ディープラーニングでの用途
reshapeは、次のような場面で使われます。
画像を全結合層に入力する際の平坦化です。(28, 28) の画像を (784,) にします。
複数のヘッドに分割する処理です。(32, 128, 768) を (32, 128, 12, 64) にします。Transformerのマルチヘッド注意で使われます。
バッチ軸と系列軸をまとめる処理です。(32, 128, 768) を (4096, 768) にして、全結合層に通します。
ブロードキャスト
ブロードキャストとは何か
ブロードキャストとは、形の異なるテンソルどうしの演算を、自動的に形を合わせて実行する仕組みです。
最も単純な例は、スカラーとの演算です。
x = np.array([1, 2, 3])
y = x + 10 # [11, 12, 13]
10という1個の数値が、すべての要素に加算されています。
これは、10が [10, 10, 10] に自動的に拡張されたと考えられます。
行列とベクトルの場合
もう少し複雑な例を見ます。
(3, 4) の行列と、(4,) のベクトルを足すとします。
ブロードキャストにより、ベクトルが3行分に複製され、各行に加算されます。
第9章で、バイアスの加算がこの仕組みで動いていることに触れました。
が (32, 3072)、
が (3072,) です。ブロードキャストにより、バイアスが32行すべてに加算されます。
ブロードキャストの規則
ブロードキャストが成立する条件は、次のとおりです。
二つのテンソルの形を、右端から順に比較します。
各位置について、次のいずれかであれば、その軸は適合します。
両者の大きさが等しい。
どちらかの大きさが1である。
どちらかにその軸が存在しない。
すべての軸が適合すれば、ブロードキャストが成立します。
具体例で確認する
適合する例を見ます。
(3, 4) と (4,) の場合。右端から比較すると、4と4で一致します。次の軸は、左側だけに3があります。存在しない側は適合とみなされるため、成立します。結果は (3, 4) です。
(3, 1) と (1, 4) の場合。右端は1と4で、片方が1なので適合します。次は3と1で、これも片方が1なので適合します。結果は (3, 4) です。
適合しない例を見ます。
(3, 4) と (3,) の場合。右端から比較すると、4と3で一致せず、どちらも1ではありません。したがって、エラーになります。
この最後の例は、直感に反すると感じるかもしれません。3行あるから3個の要素を各行に足せるのでは、と考えがちですが、比較は右端から行われるため、成立しないのです。
ブロードキャストの危険性
ブロードキャストは便利な機能ですが、意図しない計算が行われる原因にもなります。
たとえば、(3, 1) と (1, 3) を足すと、(3, 3) になります。
3個の要素と3個の要素を足したつもりが、9個の要素を持つ行列ができてしまいます。
しかも、エラーにはなりません。計算は正常に完了し、それらしい数値が出ます。
この種のバグは、発見が非常に困難です。予防策は、計算の要所で shape を表示することに尽きます。
エラーメッセージの読み方
実務で遭遇する典型的なエラーを、読み解いてみます。
例1:行列積の形が合わない
RuntimeError: mat1 and mat2 shapes cannot be multiplied (32x128 and 256x64)
読み方は、次のとおりです。
一つ目の行列は32行128列、二つ目は256行64列です。
行列積では、一つ目の列数と二つ目の行数が一致していなければなりません。
128と256が一致していないため、エラーになっています。
対処法は、次のいずれかです。
一つ目の行列の列数を256にする。
二つ目の行列の行数を128にする。
いずれかを転置して形を合わせる。
例2:ブロードキャストできない
ValueError: operands could not be broadcast together with shapes (3,4) (3,)
読み方は、次のとおりです。
(3, 4) と (3,) をブロードキャストしようとして失敗しています。
右端から比較すると、4と3が一致せず、どちらも1ではありません。
対処法は、(3,) を (3, 1) にreshapeすることです。そうすれば、右端は4と1で適合し、次は3と3で一致します。
例3:reshapeできない
RuntimeError: shape '[3, 5]' is invalid for input of size 12
読み方は、次のとおりです。
要素数12のテンソルを、(3, 5) にしようとしています。
しかし、次のように、15と12は一致しません。
reshapeでは要素の総数が変わらないため、この変換は不可能です。
対処法は、要素数を確認し、その約数の組み合わせで形を指定することです。12であれば、(3, 4)、(4, 3)、(2, 6)、(6, 2)、(2, 2, 3) などが可能です。
形を合わせる手順
エラーに遭遇したときの、体系的な対処手順を示します。
第一に、エラーメッセージから、二つのテンソルの形を読み取ります。
第二に、自分が意図していた計算を、形の変化として書き出します。(32, 128) と (128, 64) を掛けて (32, 64) を得たい、といった形です。
第三に、実際の形と、意図した形を比較します。どこがずれているかを特定します。
第四に、ずれの原因を考えます。転置が必要なのか、reshapeが必要なのか、そもそも前の処理が間違っているのか。
第五に、修正して、shape を表示して確認します。
この手順を踏めば、当てずっぽうで数値を変える必要はありません。
Pythonで確認する
import numpy as np
print("=== 転置 ===")
A = np.array([[1, 2, 3],
[4, 5, 6]])
print(f"A(形{A.shape}):")
print(A)
print(f"\nA.T(形{A.T.shape}):")
print(A.T)
print(f"\n二回転置すると元に戻るか: {np.array_equal(A.T.T, A)}")
print("\n=== 積の転置の性質 ===")
B = np.array([[1, 0],
[0, 1],
[1, 1]])
print(f"A(形{A.shape}) @ B(形{B.shape})→ 形{(A @ B).shape}")
left = (A @ B).T
right = B.T @ A.T
print(f"(AB)^T:")
print(left)
print(f"B^T A^T:")
print(right)
print(f"一致するか: {np.array_equal(left, right)}")
print("\n=== reshape ===")
v = np.arange(1, 13)
print(f"元のベクトル(形{v.shape}): {v}")
for shape in [(3, 4), (4, 3), (2, 6), (2, 2, 3)]:
reshaped = v.reshape(shape)
print(f"\nreshape{shape}:")
print(reshaped)
print("\n=== -1の使用 ===")
print(f"reshape(3, -1) → 形{v.reshape(3, -1).shape}")
print(f"reshape(-1, 6) → 形{v.reshape(-1, 6).shape}")
print(f"reshape(2, -1, 3) → 形{v.reshape(2, -1, 3).shape}")
print("\n=== 転置とreshapeの決定的な違い ===")
M = np.array([[1, 2, 3],
[4, 5, 6]])
print(f"元の行列(形{M.shape}):")
print(M)
print(f"\n転置(形{M.T.shape}):")
print(M.T)
print(f"\nreshape(3, 2)(形{M.reshape(3, 2).shape}):")
print(M.reshape(3, 2))
print("\n形は同じ(3,2)ですが、中身がまったく違います。")
print("\n=== ブロードキャストの基本 ===")
x = np.array([1, 2, 3])
print(f"x = {x}")
print(f"x + 10 = {x + 10}")
print("スカラーが全要素に拡張されました。")
print("\n=== 行列とベクトルのブロードキャスト ===")
X = np.array([[1, 2, 3, 4],
[5, 6, 7, 8],
[9, 10, 11, 12]])
b = np.array([100, 200, 300, 400])
print(f"X(形{X.shape}):")
print(X)
print(f"b(形{b.shape}): {b}")
print(f"\nX + b(形{(X + b).shape}):")
print(X + b)
print("bが3行すべてに加算されました。")
print("\n=== ブロードキャストの成立条件 ===")
test_pairs = [
((3, 4), (4,)),
((3, 1), (1, 4)),
((3, 4), (3, 1)),
((3, 4), (3,)),
((2, 3, 4), (4,)),
]
for shape_a, shape_b in test_pairs:
a = np.zeros(shape_a)
b = np.zeros(shape_b)
try:
result = a + b
print(f"{shape_a} + {shape_b} → {result.shape} 成立")
except ValueError:
print(f"{shape_a} + {shape_b} → エラー 不成立")
print("\n=== ブロードキャストの危険な例 ===")
a = np.array([1, 2, 3]).reshape(3, 1)
b = np.array([10, 20, 30]).reshape(1, 3)
print(f"a(形{a.shape}):\n{a}")
print(f"b(形{b.shape}): {b}")
result = a + b
print(f"\na + b(形{result.shape}):")
print(result)
print("3要素と3要素を足したつもりが、9要素の行列になっています。")
print("エラーにならないため、気づきにくいバグの原因になります。")
print("\n=== エラーメッセージの実例 ===")
print("--- 行列積の形が合わない ---")
try:
np.zeros((32, 128)) @ np.zeros((256, 64))
except ValueError as e:
print(f"{e}")
print("\n--- reshapeできない ---")
try:
np.arange(12).reshape(3, 5)
except ValueError as e:
print(f"{e}")
print("\n=== 実務でよくある形の操作 ===")
print("--- 画像の平坦化 ---")
image = np.random.randn(28, 28)
flat = image.reshape(-1)
print(f"{image.shape} → {flat.shape}")
print("\n--- バッチ画像の平坦化 ---")
batch_images = np.random.randn(32, 1, 28, 28)
flat_batch = batch_images.reshape(32, -1)
print(f"{batch_images.shape} → {flat_batch.shape}")
print("\n--- マルチヘッドへの分割 ---")
x = np.random.randn(32, 128, 768)
n_heads = 12
head_dim = 768 // n_heads
split = x.reshape(32, 128, n_heads, head_dim)
print(f"{x.shape} → {split.shape} ({n_heads}ヘッド × {head_dim}次元)")
print("\n--- バッチと系列をまとめる ---")
merged = x.reshape(-1, 768)
print(f"{x.shape} → {merged.shape}")
print("\n=== PyTorchのnn.Linearの内部 ===")
try:
import torch
import torch.nn as nn
linear = nn.Linear(768, 3072)
print(f"nn.Linear(768, 3072)")
print(f" weight の形: {linear.weight.shape} (出力次元, 入力次元)")
print(f" 内部では weight.T(形{linear.weight.T.shape})が使われます")
x_in = torch.randn(32, 768)
y_out = linear(x_in)
print(f"\n入力{tuple(x_in.shape)} → 出力{tuple(y_out.shape)}")
manual = x_in @ linear.weight.T + linear.bias
print(f"手動計算と一致するか: {torch.allclose(y_out, manual, atol=1e-6)}")
except ImportError:
print("PyTorchがインストールされていないため、この部分はスキップします。")
初学者がつまずきやすい点
つまずき1:転置とreshapeを同じものだと思う
先に説明したとおり、形は同じでも中身が違います。
判断基準は、要素の位置関係を保ちたいかどうかです。
行と列の役割を入れ替えたいなら、転置です。
単に形を詰め直したいなら、reshapeです。
つまずき2:ブロードキャストの比較が右端からであることを忘れる
(3, 4) と (3,) が足せないのは、直感に反します。
右端から比較するという規則を、明示的に覚えてください。
各行に3要素を足したい場合は、(3,) を (3, 1) にreshapeします。
つまずき3:エラーメッセージを読まずに数値をいじる
エラーメッセージには、二つのテンソルの形が明記されています。
この情報を読めば、何が問題かは特定できます。
読まずに当てずっぽうで修正すると、時間を浪費するだけでなく、別の箇所に問題を先送りすることになります。
演習
演習1:転置とreshapeの違いを確認する
次の行列について、転置した結果と、(3, 2) にreshapeした結果を、それぞれ書き出してください。
演習2:ブロードキャストの成否を判定する
次の組み合わせについて、ブロードキャストが成立するかを判定し、成立する場合は結果の形を答えてください。
(5, 3) と (3,)
(5, 3) と (5,)
(5, 1) と (1, 3)
(2, 3, 4) と (3, 4)
(2, 3, 4) と (2, 4)
演習3:エラーを修正する
次のコードは、エラーになります。原因を特定し、修正してください。
import numpy as np
X = np.random.randn(32, 784)
W = np.random.randn(128, 784)
b = np.random.randn(128)
y = X @ W + b
演習4:形の変換を実装する
import numpy as np
print("=== 課題1:画像バッチを全結合層に通す ===")
images = np.random.randn(64, 3, 32, 32)
print(f"入力画像: {images.shape}")
flattened = images.reshape(64, -1)
print(f"平坦化後: {flattened.shape}")
W = np.random.randn(256, flattened.shape[1]) * 0.01
b = np.zeros(256)
output = flattened @ W.T + b
print(f"全結合層通過後: {output.shape}")
print("\n=== 課題2:マルチヘッドへの分割と復元 ===")
x = np.random.randn(8, 64, 768)
n_heads = 12
head_dim = 768 // n_heads
print(f"元の形: {x.shape}")
split = x.reshape(8, 64, n_heads, head_dim)
print(f"ヘッド分割後: {split.shape}")
transposed = split.transpose(0, 2, 1, 3)
print(f"ヘッドを前に移動: {transposed.shape}")
back = transposed.transpose(0, 2, 1, 3).reshape(8, 64, 768)
print(f"元に戻した形: {back.shape}")
print(f"元と一致するか: {np.allclose(x, back)}")
print("\n=== 課題3:バイアスの正しい加算 ===")
X = np.random.randn(5, 4)
b_correct = np.random.randn(4)
b_wrong = np.random.randn(5)
print(f"X の形: {X.shape}")
print(f"正しいバイアス(列方向)の形: {b_correct.shape}")
result = X + b_correct
print(f"加算結果: {result.shape} 成功")
print(f"\n行ごとに異なる値を足したい場合: {b_wrong.shape}")
try:
X + b_wrong
except ValueError as e:
print(f"そのままではエラー: {e}")
result2 = X + b_wrong.reshape(-1, 1)
print(f"reshape(-1, 1)してから加算: {result2.shape} 成功")
演習の解答例と解説
演習1の解答は、次のとおりです。
転置した結果。
元の1行目である が、転置後の1列目になっています。
reshapeした結果。
要素を横方向に読んで とし、それを2個ずつ詰め直しています。
まったく異なる結果になることが確認できます。
演習2の解答は、次のとおりです。
(5, 3) と (3,) は成立します。右端が3と3で一致します。結果は (5, 3) です。
(5, 3) と (5,) は成立しません。右端が3と5で一致せず、どちらも1ではありません。
(5, 1) と (1, 3) は成立します。右端が1と3で片方が1、次が5と1で片方が1です。結果は (5, 3) です。
(2, 3, 4) と (3, 4) は成立します。右端から4と4、3と3で一致し、残りは片方に存在しないだけです。結果は (2, 3, 4) です。
(2, 3, 4) と (2, 4) は成立しません。右端は4と4で一致しますが、次が3と2で一致せず、どちらも1ではありません。
演習3の解答は、次のとおりです。
エラーの原因は、 の部分です。
Xの形は (32, 784)、Wの形は (128, 784) です。
行列積では、Xの列数784と、Wの行数128が一致していなければなりませんが、一致していません。
修正方法は、Wを転置することです。
y = X @ W.T + b
バイアスbの形は (128,) であり、ブロードキャストにより32行すべてに加算されます。
演習4では、実務でよく使う形の操作が確認できます。
課題1では、(64, 3, 32, 32) という4次元のデータを、(64, 3072) という2次元に平坦化しています。バッチ軸を残し、それ以外をまとめるという操作です。
課題2では、Transformerのマルチヘッド注意で使われる分割と復元を確認しています。転置を二回行うと元に戻ることも確認できます。
課題3では、バイアスの加算において、列方向と行方向で扱いが異なることを確認しています。行ごとに異なる値を足したい場合は、reshapeで軸を明示する必要があります。
講師向けの補足
この章は、理論より実践の章です。演習に多くの時間を割いてください。
特に効果的なのは、受講者に実際にエラーを起こさせることです。わざと形の合わないコードを書かせ、エラーメッセージを読ませ、修正させる。この流れを何度か繰り返すと、対処の型が身につきます。
転置とreshapeの違いは、必ず数値で示してください。形は同じだが中身が違うという事実は、実際に出力を見比べないと実感されません。
ブロードキャストの危険な例、つまり (3, 1) と (1, 3) を足すと (3, 3) になるという例は、必ず紹介してください。エラーにならないバグの怖さを知っておくことが、予防につながります。
また、困ったら shape を表示するという習慣を、繰り返し強調してください。これが、最も実務的な助言です。
理解度を確認する問い
第10章の内容を、次の問いで確認してみてください。
転置とreshapeの違いを、要素の並び方という観点から説明してみてください。
は、どのように書き換えられるでしょうか。
ブロードキャストが成立する条件を、三つ挙げてください。
(3, 4) と (3,) を足そうとするとエラーになります。各行に3個の値を足したい場合、どうすればよいでしょうか。
nn.Linear(768, 3072) の weight の形はどうなっているでしょうか。また、なぜ内部で転置が必要になるのでしょうか。
まとめ
転置は、行列の行と列を入れ替える操作です。(m, n) が (n, m) になります。二回転置すると元に戻り、積の転置は という性質を持ちます。行列積で形が合わないとき、転置によって合わせられることがあります。
reshapeは、要素の総数を変えずに形を変える操作です。要素は最後の軸から順に埋められます。マイナス1を指定すると、残りの次元が自動的に計算されます。
転置とreshapeは、同じ形にできても中身が異なります。転置は要素の位置関係を保ち、reshapeは一列に並べ直してから詰め直します。この違いを理解していないと、エラーにならないまま誤った結果が出ます。
ブロードキャストは、形の異なるテンソルどうしの演算を、自動的に形を合わせて実行する仕組みです。右端の軸から順に比較し、大きさが等しいか、どちらかが1か、どちらかに存在しなければ適合します。便利な機能ですが、意図しない計算が行われる原因にもなります。
エラーに遭遇したときは、メッセージから形を読み取り、意図していた形と比較し、ずれの原因を特定するという手順を踏んでください。当てずっぽうで数値を変えるより、はるかに速く解決できます。
そして最も実務的な助言は、計算の要所で shape を表示することです。これが、形に関するバグを防ぐ、最も確実な方法です。
次の第11章からは、第III部に入ります。ここまでは計算の手順を扱ってきましたが、第11章では、行列を掛けるという操作が、空間の中で何を起こしているのかを扱います。2次元の図を使って、回転、伸縮、射影といった変換を目で見ながら、線形変換の意味を理解していきます。
第11章に進む前に、演習3のエラー修正を、実際にコードを書いて確認しておいてください。エラーメッセージを読んで原因を特定するという経験が、実務での対処力に直結します。
セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。
投稿者プロフィール

- 代表取締役
-
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
海外放浪の末、2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。
学生時代は趣味と実益を兼ねてリゾートバイトにいそしむ。長野県白馬村に始まり、志賀高原でのスキーインストラクター、沖縄石垣島、北海道トマム。高じてオーストラリアのゴールドコーストでツアーガイドなど。現在は野菜作りにはまっている。
最新の投稿
新人エンジニア研修講師2026年8月11日第13章 線形だけでは足りない理由
新人エンジニア研修講師2026年8月11日第12章 次元の拡大と縮小の意味
新人エンジニア研修講師2026年8月11日第11章 2次元で見る線形変換:回転、伸縮、射影
新人エンジニア研修講師2026年8月11日第10章 形を合わせる:転置、reshape、ブロードキャスト
