4.1.3 行列:データのバッチ変換

- 行列とは何かを直感的に理解する(1つの表 / 1組の操作)
- 行列積の意味と計算方法を身につける
- 転置、逆行列の直感を理解する
- ニューラルネットワークの各層がなぜ行列積なのかを理解する
- NumPy で行列演算を実装する
まず、とても大事な学習イメージを共有します
Section titled “まず、とても大事な学習イメージを共有します”この節では、行列論を全部学び切ることが目的ではありません。まずは、AI でよく出てくる次の3つの感覚をしっかりつかみましょう。
- 行列は、データをまとめて入れられる
- 行列は、バッチ変換を表せる
- 行列積は、後のモデルで何度も登場する
まずは全体像をつかもう
Section titled “まずは全体像をつかもう”行列をただの「数字の表」として見るだけだと、だんだん抽象的に感じやすくなります。初心者には、次のように見るのがおすすめです。

この節で大事なのは、定義を暗記することではなく、次を理解することです。
- なぜ複数のデータを自然に行列で表すのか
- なぜ行列積で一度に複数のサンプルを処理できるのか
- なぜ深層学習のコードで
X @ Wが何度も出てくるのか
すぐ見返せる用語とコードの前提
Section titled “すぐ見返せる用語とコードの前提”| 用語 | 意味 | ここで重要な理由 |
|---|---|---|
batch | バッチ、一緒に処理する複数のサンプル | 行列はよく1つのバッチを表し、行がサンプル、列が特徴量になります。 |
feature | 特徴量、面積や年齢や距離のような入力列 | 行列の列は通常、異なる特徴量に対応します。 |
shape | NumPy が配列サイズを表す情報 | 行列積は内側の次元が一致するときだけ実行できます。 |
bias / b | バイアス、乗算後に足す学習可能なずれ | X @ W + b により、モデルは回転や拡大縮小だけでなく出力をずらせます。 |
ReLU | Rectified Linear Unit、修正線形ユニット | 負の値を 0 にし、正の値をそのまま残す活性化関数です。非線形性を加えます。 |
determinant | 行列式、正方行列が空間をつぶすかを見る数 | 0 なら、その行列には逆行列がありません。 |
singular matrix | 特異行列、逆行列を持たない行列 | 変換で情報を失うため、元の入力に戻せません。 |
特に断りがない限り、コード片段は import numpy as np が必要です。描画例では import matplotlib.pyplot as plt も必要です。
一、行列とは何か?
Section titled “一、行列とは何か?”見方1:行列は1つの表
初心者向けのたとえ
Section titled “初心者向けのたとえ”ベクトルが「1枚の情報カード」だとすると、 行列はまず次のように理解できます。
- きれいに並んだ情報カードの束
だから機械学習では、
- 1つのサンプルはベクトル
- 複数のサンプルは自然に行列
となります。
Pandas の DataFrame も、本質的には行列に近いものです。
import numpy as np
# 3人の学生の4科目の点数scores = np.array([ [85, 92, 78, 90], # 学生1 [72, 88, 95, 85], # 学生2 [90, 76, 88, 92], # 学生3])print(f"形状: {scores.shape}") # (3, 4) → 3行4列見方2:行列は「変換マシン」
行列にベクトルを入れると、新しいベクトルが出てきます。 これは関数のように、入力 → 変換 → 出力 を行うものです。
flowchart LR A["入力ベクトル<br/>[x, y]"] --> M["行列 M<br/>変換マシン"] M --> B["出力ベクトル<br/>[x', y']"]
style A fill:#e3f2fd,stroke:#1565c0,color:#333 style M fill:#fff3e0,stroke:#e65100,color:#333 style B fill:#e8f5e9,stroke:#2e7d32,color:#333これが線形代数の最も大事な考え方です。行列 = 変換 です。
行列の基本属性
Section titled “行列の基本属性”M = np.array([ [1, 2, 3], [4, 5, 6],])
print(f"形状 (shape): {M.shape}") # (2, 3) → 2行3列print(f"行数: {M.shape[0]}") # 2print(f"列数: {M.shape[1]}") # 3print(f"要素の総数: {M.size}") # 6print(f"データ型: {M.dtype}") # int64print(f"1行目: {M[0]}") # [1 2 3]print(f"1行目3列目: {M[1, 2]}") # 6「1つのサンプル」から「複数のサンプル」へ
Section titled “「1つのサンプル」から「複数のサンプル」へ”すでにベクトルを学んでいるなら、行列は次のように捉えられます。
たくさんのベクトルを、同じ形式で積み重ねたもの。
import numpy as np
# [面積, 築年数, 駅からの距離]house_1 = np.array([88, 5, 1.2])house_2 = np.array([120, 8, 0.5])house_3 = np.array([75, 2, 1.8])
X = np.array([ house_1, house_2, house_3,])
print(X)print("形状:", X.shape) # (3, 3)ここでの意味は次の通りです。
- 各行が1つのサンプル
- 各列が1つの特徴量
これは機械学習や深層学習で最もよく使うデータの並べ方です。
二、行列の基本演算
Section titled “二、行列の基本演算”行列の足し算とスカラー倍
Section titled “行列の足し算とスカラー倍”ベクトルと同じように、対応する位置どうしを足す / 掛ける だけです。
A = np.array([[1, 2], [3, 4]])B = np.array([[5, 6], [7, 8]])
print("足し算:\n", A + B) # [[6, 8], [10, 12]]print("スカラー倍:\n", 3 * A) # [[3, 6], [9, 12]]行列積——最重要の演算
Section titled “行列積——最重要の演算”行列積は、普通の数の掛け算とはまったく違います。ルールはこうです。
結果の各要素 = 左の行列のある行 と 右の行列のある列 の内積
A = np.array([[1, 2], [3, 4]]) # 2×2
B = np.array([[5, 6], [7, 8]]) # 2×2
# 行列積C = A @ B # 推奨の書き方# C = np.dot(A, B) # 同じ意味
print("A @ B =")print(C)# [[19, 22], ← 1*5+2*7=19, 1*6+2*8=22# [43, 50]] ← 3*5+4*7=43, 3*6+4*8=50手で確認してみよう:
- C[0,0] = 1×5 + 2×7 = 5 + 14 = 19
- C[0,1] = 1×6 + 2×8 = 6 + 16 = 22
- C[1,0] = 3×5 + 4×7 = 15 + 28 = 43
- C[1,1] = 3×6 + 4×8 = 18 + 32 = 50
行列積のサイズのルール
Section titled “行列積のサイズのルール”
flowchart LR A["A<br/>m × n"] --> C["C<br/>m × p"] B["B<br/>n × p"] --> C
style A fill:#e3f2fd,stroke:#1565c0,color:#333 style B fill:#fff3e0,stroke:#e65100,color:#333 style C fill:#e8f5e9,stroke:#2e7d32,color:#333大事なルール:左の行列の列数 = 右の行列の行数。 そして、結果の形は (左の行列の行数, 右の行列の列数) になります。
A = np.array([[1, 2, 3], [4, 5, 6]]) # 2×3
B = np.array([[1, 2], [3, 4], [5, 6]]) # 3×2
C = A @ B # 2×2 ✓(3 == 3)print(f"A({A.shape}) @ B({B.shape}) = C({C.shape})")print(C)# [[22, 28],# [49, 64]]A = np.array([[1, 2], [3, 4]])B = np.array([[5, 6], [7, 8]])
print("A @ B =\n", A @ B)print("B @ A =\n", B @ A)print("A@B == B@A?", np.array_equal(A @ B, B @ A)) # False「サンプル行列 × 重み行列」を手計算してみる
Section titled “「サンプル行列 × 重み行列」を手計算してみる”これは初心者がぜひ理解したい部分です。後のニューラルネットワークにそのままつながります。
X = np.array([ [1, 2], [3, 4], [5, 6],]) # 3×2
W = np.array([ [0.1, 1.0], [0.2, 0.5],]) # 2×2
Z = X @ Wprint(Z.round(2))期待される出力:
[[0.5 2. ] [1.1 5. ] [1.7 8. ]]行ごとに見ると、次の意味になります。
- 1行目の出力 = 1つ目のサンプル
[1, 2]に重み行列を適用したもの - 2行目の出力 = 2つ目のサンプル
[3, 4]に重み行列を適用したもの - 3行目の出力 = 3つ目のサンプル
[5, 6]に重み行列を適用したもの
行列積のすごいところは、
1つのサンプルだけでなく、複数のサンプルを一度に計算できること
です。
初心者がまず確認すべき shape の4ステップ
Section titled “初心者がまず確認すべき shape の4ステップ”行列積でよくエラーが出るときは、あわてず次の4つを確認しましょう。
- 左側の行列の形は何か
- 右側の行列の形は何か
- 左側の列数は右側の行数と等しいか
- 期待している出力の形は何か
print("X.shape =", X.shape)print("W.shape =", W.shape)print("Z.shape =", (X @ W).shape)三、行列を「変換」として見る——直感的な可視化
Section titled “三、行列を「変換」として見る——直感的な可視化”行列はベクトルに対して、回転、拡大縮小、せん断 などの変換を行えます。 ここでは、行列で2次元の点を回転させてみます。
import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['Arial Unicode MS']plt.rcParams['axes.unicode_minus'] = False
# 正方形の4頂点 + 始点に戻るsquare = np.array([ [0, 0], [1, 0], [1, 1], [0, 1], [0, 0], # 始点に戻ると閉じた図形として描きやすい]).T # 2×5 に転置して、行列積しやすくする
# 45°回転行列theta = np.radians(45) # 度をラジアンに変換R = np.array([ [np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]])print(f"回転行列:\n{R.round(3)}")
# 回転を適用rotated = R @ square # 行列積!
fig, axes = plt.subplots(1, 2, figsize=(12, 5))
# 変換前axes[0].plot(square[0], square[1], 'b-o', linewidth=2, markersize=8)axes[0].fill(square[0], square[1], alpha=0.2, color='steelblue')axes[0].set_xlim(-1.5, 1.5)axes[0].set_ylim(-0.5, 1.8)axes[0].set_aspect('equal')axes[0].grid(True, alpha=0.3)axes[0].set_title('変換前(元の正方形)')
# 変換後axes[1].plot(square[0], square[1], 'b--', alpha=0.3, linewidth=1)axes[1].plot(rotated[0], rotated[1], 'r-o', linewidth=2, markersize=8)axes[1].fill(rotated[0], rotated[1], alpha=0.2, color='coral')axes[1].set_xlim(-1.5, 1.5)axes[1].set_ylim(-0.5, 1.8)axes[1].set_aspect('equal')axes[1].grid(True, alpha=0.3)axes[1].set_title('変換後(45°回転)')
plt.suptitle('行列変換 = 回転', fontsize=14)plt.tight_layout()plt.show()重要なポイント:2×2 の行列に2次元ベクトルを掛けると、空間変換が1回できます。 この考え方は、任意の次元にも広げられます。
いろいろな変換の例
Section titled “いろいろな変換の例”fig, axes = plt.subplots(1, 4, figsize=(18, 4))
# 元の形triangle = np.array([ [0, 0], [1, 0], [0.5, 1], [0, 0]]).T
transforms = [ (np.eye(2), '元の形(単位行列)'), (np.array([[2, 0], [0, 2]]), '2倍に拡大'), (np.array([[1, 0.5], [0, 1]]), '水平せん断'), (np.array([[-1, 0], [0, 1]]), '左右反転'),]
for ax, (M, title) in zip(axes, transforms): transformed = M @ triangle ax.plot(triangle[0], triangle[1], 'b--', alpha=0.3) ax.fill(triangle[0], triangle[1], alpha=0.1, color='blue') ax.plot(transformed[0], transformed[1], 'r-o', linewidth=2, markersize=6) ax.fill(transformed[0], transformed[1], alpha=0.2, color='coral') ax.set_xlim(-2.5, 2.5) ax.set_ylim(-0.5, 2.5) ax.set_aspect('equal') ax.grid(True, alpha=0.3) ax.set_title(title)
plt.tight_layout()plt.show()四、転置と逆行列
Section titled “四、転置と逆行列”転置(Transpose)
Section titled “転置(Transpose)”転置 = 行と列を入れ替えること。 元の i 行目が i 列目になります。
A = np.array([ [1, 2, 3], [4, 5, 6],])print(f"A の形状: {A.shape}") # (2, 3)print(f"A の転置:\n{A.T}")print(f"転置後の形状: {A.T.shape}") # (3, 2)出力:
A の転置:[[1 4] [2 5] [3 6]]いつ転置を使うの?
- データ処理: 「行がサンプル、列が特徴量」を「行が特徴量、列がサンプル」にしたいとき
- 行列演算: 公式に合わせるために転置が必要なとき
# 単位行列(対角線がすべて1)I = np.eye(3)print("単位行列:\n", I)# [[1. 0. 0.]# [0. 1. 0.]# [0. 0. 1.]]
# 単位行列の性質:A @ I = I @ A = AA = np.array([[1, 2], [3, 4]])print("A @ I == A?", np.allclose(A @ np.eye(2), A)) # True逆行列(Inverse)
Section titled “逆行列(Inverse)”行列 A が「変換」だとすると、逆行列 A⁻¹ は**「元に戻す変換」**です。 A の操作を打ち消します。
A = np.array([[2, 1], [1, 1]])
# 逆行列を計算A_inv = np.linalg.inv(A)print("A の逆行列:\n", A_inv)
# 確認:A @ A_inv = 単位行列print("A @ A_inv =\n", (A @ A_inv).round(10))# [[1. 0.]# [0. 1.]] → 単位行列!直感:A がベクトルを45°回転させたなら、A⁻¹ はそれを元に戻します。 A がベクトルを2倍にしたなら、A⁻¹ は2分の1に戻します。
# 可視化:変換 → 逆変換 = 元に戻るv = np.array([1, 2])
transformed = A @ v # A で変換recovered = A_inv @ transformed # A_inv で元に戻す
print(f"元の値: {v}")print(f"変換後: {transformed}")print(f"元に戻した後: {recovered}") # 元と同じ!五、行列とニューラルネットワーク
Section titled “五、行列とニューラルネットワーク”ニューラルネットワークの本質
Section titled “ニューラルネットワークの本質”この節で最も大事な気づきです。ニューラルネットワークの各層は、本質的には「行列積 + 活性化関数」です。
flowchart LR X["入力 X<br/>n 個の特徴量"] --> MUL["行列積<br/>X @ W + b"] MUL --> ACT["活性化関数<br/>relu / sigmoid"] ACT --> Y["出力 Y<br/>次の層への入力"]
style X fill:#e3f2fd,stroke:#1565c0,color:#333 style MUL fill:#fff3e0,stroke:#e65100,color:#333 style ACT fill:#f3e5f5,stroke:#7b1fa2,color:#333 style Y fill:#e8f5e9,stroke:#2e7d32,color:#3331つのニューロンの式から行列の式へ
Section titled “1つのニューロンの式から行列の式へ”1つのサンプルだけを見ると、ニューラルネットワークの1層は実はこうです。
出力 = 入力ベクトル · 重みベクトル + バイアス
import numpy as np
x = np.array([1.0, 0.5, -0.3])w = np.array([0.2, -0.4, 0.6])b = 0.1
y = x @ w + bprint(round(y, 4))期待される出力:
-0.081つだけでなく、複数のサンプルをまとめて計算すると、自然に次の形になります。
Z = X @ W + b
ここで、
Xはサンプル行列Wは重み行列bはバイアスZは線形出力
です。
コードで1層のニューラルネットワークをまねる
Section titled “コードで1層のニューラルネットワークをまねる”# 1層ニューラルネットワークの順伝播をまねる
# 入力:3サンプル、それぞれ4特徴量X = np.array([ [1.0, 0.5, -0.3, 0.8], [0.2, -0.1, 0.7, 0.3], [0.9, 0.4, 0.1, -0.5],])print(f"入力 X: {X.shape}") # (3, 4)
# 重み行列:4特徴量から2出力へ写すrng = np.random.default_rng(seed=42)W = rng.normal(size=(4, 2)) * 0.5print(f"重み W: {W.shape}") # (4, 2)
# バイアスb = np.zeros(2)
# 順伝播:行列積 + バイアスZ = X @ W + b # (3, 4) @ (4, 2) = (3, 2)print(f"線形出力 Z: {Z.shape}")
# 活性化関数(ReLU:負の値は0、正の値はそのまま)def relu(x): return np.maximum(0, x)
output = relu(Z)print(f"活性化後の出力: {output.shape}") # (3, 2)print(f"\n最終出力:\n{output.round(3)}")seed=42 を使ったときの期待される出力:
入力 X: (3, 4)重み W: (4, 2)線形出力 Z: (3, 2)活性化後の出力: (3, 2)
最終出力:[[0.684 0. ] [0. 0. ] [0.158 0. ]]読み解き:
- 3つのサンプル(3行)、各サンプルに4特徴量(4列)
- 重み行列 W は 4×2 で、4次元特徴量を2次元へ写す
- 行列積で全サンプルを一度に処理できる。これがバッチ計算の強みです
- バイアス
bの形状は(2,)です。NumPy はこれをZの各行に自動で足します。これは broadcasting(ブロードキャスト)と呼ばれます。
多層ネットワーク = 行列の連続積
Section titled “多層ネットワーク = 行列の連続積”# 3層ニューラルネットワークをまねるrng = np.random.default_rng(seed=42)
X = rng.normal(size=(5, 10)) # 5サンプル、10特徴量
# 第1層:10 → 8W1 = rng.normal(size=(10, 8)) * 0.3h1 = relu(X @ W1)print(f"第1層の出力: {h1.shape}") # (5, 8)
# 第2層:8 → 4W2 = rng.normal(size=(8, 4)) * 0.3h2 = relu(h1 @ W2)print(f"第2層の出力: {h2.shape}") # (5, 4)
# 第3層(出力層):4 → 2W3 = rng.normal(size=(4, 2)) * 0.3output = h2 @ W3 # 出力層では通常 ReLU を使わないprint(f"最終出力: {output.shape}") # (5, 2)初心者がよくやる3つの行列ミス
Section titled “初心者がよくやる3つの行列ミス”-
要素ごとの掛け算
A * Bを、行列積だと勘違いする 本当の行列積はA @ Bを使います。 -
形状を見ずにそのまま掛け始める
shapeが合っていないと、必ずエラーになります。 -
「各行が何を表すのか」が分からなくなる 「各行は1つのサンプル」と覚えるだけで、かなり理解しやすくなります。
六、実用例:連立一次方程式を解く
Section titled “六、実用例:連立一次方程式を解く”行列の代表的な応用の1つが、連立一次方程式を解くことです。
2x + y = 5x + 3y = 7行列で書くと、A @ x = b です。
# 係数行列A = np.array([[2, 1], [1, 3]])# 右辺の定数b = np.array([5, 7])
# 方程式を解くx = np.linalg.solve(A, b)print(f"解: x = {x[0]:.2f}, y = {x[1]:.2f}")# 解: x = 1.60, y = 1.80
# 確認print(f"確認: A @ x = {A @ x}") # [5. 7.] ✓七、NumPy の行列操作まとめ
Section titled “七、NumPy の行列操作まとめ”import numpy as np
# ========== 行列を作る ==========A = np.array([[1, 2], [3, 4]])B = np.array([[5, 6], [7, 8]])I = np.eye(2) # 単位行列Z = np.zeros((3, 4)) # 全要素0の行列rng = np.random.default_rng(seed=42)R = rng.normal(size=(3, 4)) # ランダム行列
# ========== 基本演算 ==========print("足し算:\n", A + B)print("スカラー倍:\n", 2 * A)print("要素ごとの掛け算:\n", A * B) # 注意:これは行列積ではありません!
# ========== 行列積 ==========print("行列積:\n", A @ B) # 推奨print("行列積:\n", np.dot(A, B)) # 同じprint("行列積:\n", np.matmul(A, B)) # 同じ
# ========== 転置 ==========print("転置:\n", A.T)
# ========== 逆行列 ==========print("逆行列:\n", np.linalg.inv(A))
# ========== 行列式 ==========print("行列式:", np.linalg.det(A)) # -2.0
# ========== 方程式を解く ==========b = np.array([1, 2])x = np.linalg.solve(A, b)print("方程式の解:", x)ここまで学んだら、次は何を考える?
Section titled “ここまで学んだら、次は何を考える?”行列を学んだあと、次の節へ持っていくとよい問いは次の3つです。
- 行列は、ほとんどのベクトルをどう変えるのか?
- 変換後も向きが変わらない、特別な方向はあるのか?
- その「特別な方向」が、なぜ PCA や次元削減につながるのか?
この3つは、そのまま次の話につながります。
このページを終えたら、この evidence card を残します。
- 数学対象
- ベクトル、行列、固有値、基底、またはベクトル空間の概念
- 数値例
- これを計算するために使う小さな数値または NumPy のスニペット
- 可視化または出力
- 形状、変換後の点、類似度スコア、固有方向、または射影
- AI との関係
- これが embeddings、バッチ、PCA、ニューラル層、または attention のどこに現れるか
- 期待される成果
- 計算と、それを AI の操作に結びつける1文
| 概念 | 直感的な理解 | NumPy 実装 |
|---|---|---|
| 行列 | 1つの表 / 1つの変換 | np.array([[1,2],[3,4]]) |
| 行列積 | 行と列の内積の組み合わせ | A @ B |
| 転置 | 行と列を入れ替える | A.T |
| 単位行列 | 「何もしない」変換 | np.eye(n) |
| 逆行列 | 変換を元に戻す | np.linalg.inv(A) |
| 方程式を解く | Ax = b → x = ? | np.linalg.solve(A, b) |
この節で一番持ち帰ってほしいこと
Section titled “この節で一番持ち帰ってほしいこと”- 行列は「データをまとめる」ためにも、「変換を表す」ためにも使われる
- 行列積は、まず「バッチの内積」として理解するとよい
- だから AI のコードでは
X @ Wが何度も出てくる
手を動かす練習
Section titled “手を動かす練習”練習1:行列積を手で確かめる
Section titled “練習1:行列積を手で確かめる”次が与えられています。
A = np.array([[1, 0, 2], [0, 3, 1]]) # 2×3
B = np.array([[2, 1], [0, 4], [3, 2]]) # 3×2- まず A @ B の結果を手計算する
- そのあと NumPy で確認する
練習2:回転変換
Section titled “練習2:回転変換”回転行列を使って三角形を 90° 回転させ、変換前後を比較する図を描いてみましょう。
ヒント:90° の回転行列は [[0, -1], [1, 0]] です。
練習3:2層ニューラルネットワークをまねる
Section titled “練習3:2層ニューラルネットワークをまねる”2層ネットワークを作り、100個のサンプル(各5特徴量)を入力し、1層目で3つの値、2層目で1つの値を出力するようにしてください。 各層の入力と出力の形状を表示しましょう。
参考実装と解説
- 与えられた行列では、手計算の結果は
A @ B = [[8, 5], [3, 14]]です。NumPy の結果も一致するはずです。 - 90 度回転行列
[[0,-1],[1,0]]は(x,y)を(-y,x)に写します。三角形は大きさと形を保ったまま反時計回りに回転します。 - 2 層ネットワークでは、shape が
(100,5) @ (5,3) -> (100,3)、次に(100,3) @ (3,1) -> (100,1)と流れます。shape の証拠がこの問題の最良の安全確認です。