第11章 2次元で見る線形変換:回転、伸縮、射影
こんにちは。ゆうせいです。
第10章までで、線形代数の計算方法は一通り揃いました。ここからは第III部に入ります。計算の手順ではなく、その計算が何をしているのかという意味を扱います。第11章のテーマは、線形変換です。行列をベクトルに掛けると、ベクトルが別のベクトルに変わります。この変化を、空間の中での動きとして捉え直します。768次元の空間は想像できませんが、2次元であれば紙に描けます。2次元で得た直感の多くは、そのまま高次元に持ち込めます。この章では、図を主役にして、行列が空間に何をしているのかを見ていきます。
線形変換とは何か
定義
行列をベクトルに掛ける操作を、線形変換と呼びます。
入力ベクトル が、行列
によって、別のベクトル
に変換されます。
変換という見方
第8章では、この計算を行と内積を取る手順として学びました。
第11章では、見方を変えます。行列を、空間の点を別の点に移す装置として捉えます。
平面上のすべての点に、この行列を掛けたとしましょう。すべての点が、一斉に別の場所へ移動します。この移動の様子が、その行列の性質を表しています。
線形という言葉の意味
線形変換には、二つの性質があります。
第一に、足し算が保存されます。
先に足してから変換しても、先に変換してから足しても、結果は同じです。
第二に、スカラー倍が保存されます。
先に定数倍してから変換しても、先に変換してから定数倍しても、結果は同じです。
この二つの性質を満たす変換を、線形変換と呼びます。
図形的に言えば、原点は動かず、直線は直線のまま、平行線は平行のまま保たれます。曲がったり折れたりはしません。
この制約が、第13章で扱う線形だけでは足りない理由につながります。
基本ベクトルの行き先を見る
線形変換を理解する最も効率的な方法は、基本ベクトルの行き先を追うことです。
基本ベクトル
2次元における基本ベクトルは、次の二つです。
は右向きの単位ベクトル、
は上向きの単位ベクトルです。
行列の列が、基本ベクトルの行き先である
ここが、この章で最も重要な洞察です。
行列 を基本ベクトルに掛けてみます。
結果を見てください。
の行き先は、行列の1列目です。
の行き先は、行列の2列目です。
つまり、行列の各列は、対応する基本ベクトルがどこへ移るかを表しています。
この見方の威力
行列の数値の羅列を見ても、何をしているのか分かりません。
しかし、1列目が右向きの矢印の行き先、2列目が上向きの矢印の行き先と考えると、行列の意味が一目で分かるようになります。

以降で扱う具体的な変換を、この見方で確認していきます。
代表的な線形変換
変換1:拡大と縮小
は
に、
は
に移ります。
どちらも、向きは変わらず、長さが2倍になっています。
この行列は、空間全体を2倍に拡大します。
すべての点が、原点から2倍の距離に移動します。
こちらは、空間全体を半分に縮小します。

変換2:軸ごとに異なる伸縮
は
に、
は
に移ります。
横方向だけが3倍に伸び、縦方向は変わりません。
正方形が、横長の長方形に変形します。
このように、対角行列は、各軸を独立に伸縮させる変換です。

変換3:回転
これは、原点を中心に、反時計回りに だけ回転させる行列です。
90度回転の場合、 、
ですから、次のようになります。
は
に移ります。右向きが上向きになりました。
は
に移ります。上向きが左向きになりました。
確かに、反時計回りに90度回転しています。
回転では、ベクトルの長さが変わりません。向きだけが変わります。
変換4:せん断
は
のまま変わりません。
は
に移ります。上向きだったものが、右斜め上を向きます。
正方形が、平行四辺形に変形します。
トランプのカードの束を、横にずらしたような変形です。

変換5:射影
は
のまま変わりません。
は
、つまり原点に移ります。
すべての点が、横軸の上に落とされます。縦方向の情報が、完全に失われます。
これを射影と呼びます。影を落とすようなイメージです。
射影の重要な特徴は、情報が失われることです。
も
も、変換後は同じ
になります。変換後の値から、元の値を復元することはできません。
この情報が失われるという性質は、第12章で次元の縮小を扱う際に、重要な意味を持ちます。

変換の合成
複数の変換を続けて行うことを、合成と呼びます。
まず で変換し、その結果を
で変換します。
これは、次のように書き換えられます。
という一つの行列を掛けることと、同じです。
つまり、行列の積は、変換の合成を表しています。
順序が重要な理由
第9章で、 であることを確認しました。
変換として考えると、この理由が明快になります。
90度回転してから横に3倍に伸ばすのと、横に3倍に伸ばしてから90度回転するのでは、結果が違います。
前者では、回転後の座標系で横に伸びます。後者では、伸びた後の図形が回転します。
日常的な操作でも、順序が結果を変える例は多くあります。行列の積も、その一つです。
ニューラルネットワークとの対応
層を重ねることは、変換を合成することに相当します。
入力が、 で変換され、その結果が
で変換されます。
ここで、重要な問題が生じます。
は、一つの行列です。したがって、二層のネットワークは、一層のネットワークと等価になってしまいます。
層を重ねる意味がなくなってしまうのです。
この問題の解決策が、活性化関数です。第13章で詳しく扱います。
高次元への拡張
ここまでは2次元で説明してきました。この直感は、高次元でどこまで通用するのでしょうか。
通用すること
行列の各列が、基本ベクトルの行き先を表すという性質は、何次元でも成り立ちます。
768次元であれば、768本の基本ベクトルがあり、行列の768個の列が、それぞれの行き先を表します。
変換の合成が行列の積であるという性質も、次元によりません。
情報が失われる変換が存在するという性質も、同じです。
通用しないこと、あるいは注意が必要なこと
図に描けません。これが最大の制約です。
また、高次元の空間には、低次元の直感が通用しない性質もあります。
たとえば、高次元では、ランダムに選んだ二つのベクトルは、ほぼ直交します。第6章で扱った内積が、ほぼ0になるということです。
2次元でランダムに二つの矢印を選ぶと、直交することは稀です。しかし、次元が高くなるほど、直交に近い状態が普通になります。
この性質は、高次元の空間が非常に広いことを意味しています。多くの情報を、互いに干渉させずに詰め込めるのです。
第4章で、単語の意味を768個の数値で表現できると説明しました。それが可能なのは、高次元空間のこの性質によります。
Pythonで確認する
import numpy as np
def show_transform(A, name):
"""変換行列が基本ベクトルをどこに移すかを表示"""
e1 = np.array([1, 0])
e2 = np.array([0, 1])
print(f"\n=== {name} ===")
print(f"行列:")
print(A)
print(f"e1 [1,0] → {A @ e1}")
print(f"e2 [0,1] → {A @ e2}")
print(f"(行列の1列目 = {A[:, 0]}, 2列目 = {A[:, 1]})")
print("=== 行列の列は、基本ベクトルの行き先 ===")
show_transform(np.array([[2, 0], [0, 2]]), "2倍に拡大")
show_transform(np.array([[0.5, 0], [0, 0.5]]), "半分に縮小")
show_transform(np.array([[3, 0], [0, 1]]), "横だけ3倍")
theta = np.pi / 2
rot = np.array([[np.cos(theta), -np.sin(theta)],
[np.sin(theta), np.cos(theta)]])
show_transform(rot.round(6), "90度回転")
show_transform(np.array([[1, 1], [0, 1]]), "せん断")
show_transform(np.array([[1, 0], [0, 0]]), "横軸への射影")
print("\n=== 正方形の頂点がどう動くか ===")
square = np.array([
[0, 0],
[1, 0],
[1, 1],
[0, 1]
]).T # 各列が1つの頂点
transforms = {
"変換なし": np.eye(2),
"2倍拡大": np.array([[2, 0], [0, 2]]),
"横3倍": np.array([[3, 0], [0, 1]]),
"90度回転": rot.round(6),
"せん断": np.array([[1, 1], [0, 1]]),
"横軸射影": np.array([[1, 0], [0, 0]])
}
for name, A in transforms.items():
transformed = A @ square
points = [f"({transformed[0,i]:.1f},{transformed[1,i]:.1f})" for i in range(4)]
print(f"{name:>10}: {' '.join(points)}")
print("\n=== 回転は長さを保つ ===")
v = np.array([3.0, 4.0])
print(f"元のベクトル: {v}, 長さ: {np.linalg.norm(v):.4f}")
for deg in [30, 45, 90, 180]:
rad = np.radians(deg)
R = np.array([[np.cos(rad), -np.sin(rad)],
[np.sin(rad), np.cos(rad)]])
rotated = R @ v
print(f"{deg:>3}度回転後: {rotated.round(4)}, 長さ: {np.linalg.norm(rotated):.4f}")
print("\n=== 射影では情報が失われる ===")
P = np.array([[1, 0], [0, 0]])
vectors = [np.array([3, 5]), np.array([3, -2]), np.array([3, 100])]
print("射影行列:")
print(P)
for v in vectors:
print(f"{v} → {P @ v}")
print("縦の値が違っても、射影後は同じ結果になります。")
print("変換後の値から、元の値は復元できません。")
print("\n=== 変換の合成と順序 ===")
A_rot = np.array([[0, -1], [1, 0]]) # 90度回転
A_stretch = np.array([[3, 0], [0, 1]]) # 横3倍
v = np.array([1.0, 0.0])
print(f"元のベクトル: {v}")
print(f"\n回転してから伸ばす: {A_stretch @ (A_rot @ v)}")
print(f"伸ばしてから回転: {A_rot @ (A_stretch @ v)}")
print("\n合成行列で確認:")
print(f"A_stretch @ A_rot =\n{A_stretch @ A_rot}")
print(f"A_rot @ A_stretch =\n{A_rot @ A_stretch}")
print("\n=== 層の合成は1つの行列と等価 ===")
np.random.seed(0)
W1 = np.random.randn(4, 3)
W2 = np.random.randn(2, 4)
x = np.random.randn(3)
step_by_step = W2 @ (W1 @ x)
combined = (W2 @ W1) @ x
print(f"2層を順に通した結果: {step_by_step.round(6)}")
print(f"合成行列を1回掛けた結果: {combined.round(6)}")
print(f"一致するか: {np.allclose(step_by_step, combined)}")
print("\n活性化関数がなければ、層を重ねる意味がありません。")
print("\n=== 高次元ではランダムなベクトルがほぼ直交する ===")
np.random.seed(42)
for dim in [2, 3, 10, 100, 768]:
similarities = []
for _ in range(1000):
a = np.random.randn(dim)
b = np.random.randn(dim)
cos_sim = (a @ b) / (np.linalg.norm(a) * np.linalg.norm(b))
similarities.append(abs(cos_sim))
print(f"{dim:>4}次元: コサイン類似度の絶対値の平均 {np.mean(similarities):.4f}")
print("\n次元が高いほど、ランダムな2本のベクトルは直交に近づきます。")
初学者がつまずきやすい点
つまずき1:行の意味と列の意味を取り違える
計算する際は、行列の行とベクトルの内積を取ります。第8章で学んだとおりです。
一方、変換の意味を読み取る際は、行列の列を見ます。列が、基本ベクトルの行き先です。
計算するときは行、意味を読むときは列。この使い分けを、意識してください。
つまずき2:回転行列を暗記しようとする
この形を丸暗記しようとすると、マイナスの位置を間違えます。
導出方法を覚えるほうが確実です。
を
回転させると、
になります。これが1列目です。
を
回転させると、
になります。これが2列目です。
列は基本ベクトルの行き先という原則から、その場で導けます。
つまずき3:2次元の直感をそのまま高次元に持ち込む
多くの性質は、そのまま通用します。しかし、通用しない性質もあります。
先ほど示した高次元ではランダムなベクトルがほぼ直交するという現象は、その代表例です。
高次元の空間は、直感的に思うよりはるかに広い、と覚えておいてください。
演習
演習1:基本ベクトルの行き先を求める
次の行列について、 と
の行き先をそれぞれ求め、どのような変換かを説明してください。
演習2:変換行列を作る
次の変換を行う行列を、それぞれ書いてください。
縦方向だけを2倍に伸ばす変換
すべての点を原点に移す変換
何もしない変換
180度回転させる変換
演習3:合成の順序を確認する
、
とします。
に対して、
と
をそれぞれ計算し、結果が異なることを確認してください。
演習4:変換を可視化する
import numpy as np
def visualize_transform(A, name):
"""変換の効果を文字で可視化"""
print(f"\n=== {name} ===")
print(f"行列: [{A[0,0]:>5.1f} {A[0,1]:>5.1f}]")
print(f" [{A[1,0]:>5.1f} {A[1,1]:>5.1f}]")
corners = np.array([[0, 1, 1, 0], [0, 0, 1, 1]])
transformed = A @ corners
print("単位正方形の頂点の移動:")
for i in range(4):
before = f"({corners[0,i]:.0f},{corners[1,i]:.0f})"
after = f"({transformed[0,i]:>5.2f},{transformed[1,i]:>5.2f})"
print(f" {before} → {after}")
area = abs(A[0,0]*A[1,1] - A[0,1]*A[1,0])
print(f"面積の倍率: {area:.2f}")
transforms = [
(np.array([[1.0, 0.0], [0.0, 1.0]]), "単位行列(何もしない)"),
(np.array([[2.0, 0.0], [0.0, 2.0]]), "2倍拡大"),
(np.array([[2.0, 0.0], [0.0, 0.5]]), "横2倍・縦0.5倍"),
(np.array([[0.0, -1.0], [1.0, 0.0]]), "90度回転"),
(np.array([[1.0, 0.5], [0.0, 1.0]]), "せん断"),
(np.array([[1.0, 0.0], [0.0, 0.0]]), "横軸への射影"),
]
for A, name in transforms:
visualize_transform(A, name)
print("\n射影では面積が0になります。2次元の図形が1次元に潰れるためです。")
演習の解答例と解説
演習1の解答は、次のとおりです。
一つ目の行列について。
、変わりません。
、上向きが下向きになります。
これは、横軸を鏡として反転させる変換です。上下が反転します。
二つ目の行列について。
、右向きが上向きになります。
、上向きが右向きになります。
これは、縦軸と横軸を入れ替える変換です。斜め45度の直線を鏡として反転させています。
三つ目の行列について。
、原点に潰れます。
、変わりません。
これは、縦軸への射影です。横方向の情報が失われます。
演習2の解答は、次のとおりです。
縦方向だけを2倍に伸ばす変換。
すべての点を原点に移す変換。
何もしない変換。
これが単位行列です。
180度回転させる変換。 、
ですから、次のようになります。
すべてのベクトルが、正反対を向きます。
演習3の解答は、次のとおりです。
を計算します。まず
から。
次に を掛けます。
を計算します。まず
から。
次に を掛けます。
結果は、 と
です。異なります。
前者は横に2倍伸ばしてから回転、後者は回転してから横に2倍伸ばす、という順序です。回転後は縦を向いているため、横に伸ばしても変化しないのです。
演習4では、射影の場合に面積が0になることが確認できます。2次元の図形が1次元の線分に潰れるため、面積を持たなくなります。
講師向けの補足
この章は、図がなければ理解できません。ホワイトボードや資料に、必ず図を用意してください。
最も効果的なのは、単位正方形が各変換でどう変形するかを描くことです。拡大、回転、せん断、射影。それぞれで正方形がどうなるかを、順に描いていきます。
行列の列が基本ベクトルの行き先という原則は、この章の核心です。これを理解すると、行列を見ただけで変換の性質が読み取れるようになります。時間をかけて説明してください。
また、層の合成が一つの行列と等価であるという点は、第13章への重要な伏線です。ここで示しておくと、次の章での活性化関数の必要性が、自然に理解されます。
高次元でランダムなベクトルがほぼ直交するという現象は、驚きをもって受け止められます。演習のコードで実際に確認させると、高次元空間の広さが実感されます。
理解度を確認する問い
第11章の内容を、次の問いで確認してみてください。
行列の各列は、何を表しているでしょうか。
回転行列を、45度の場合について書いてください。導出方法も説明してください。
射影という変換の、最も重要な特徴は何でしょうか。
と
が異なることを、変換の合成という観点から説明してみてください。
活性化関数がない場合、何層重ねても一層と等価になってしまうのは、なぜでしょうか。
まとめ
線形変換とは、行列をベクトルに掛ける操作を、空間の点を別の点に移す変換として捉えたものです。原点は動かず、直線は直線のまま、平行線は平行のまま保たれます。
線形変換を理解する鍵は、行列の各列が、対応する基本ベクトルの行き先を表しているという事実です。1列目は右向きの単位ベクトルの行き先、2列目は上向きの単位ベクトルの行き先です。
代表的な変換として、拡大と縮小、軸ごとに異なる伸縮、回転、せん断、射影があります。回転はベクトルの長さを保ちますが、射影では情報が失われ、変換後の値から元の値を復元できません。
行列の積は、変換の合成を表します。回転してから伸ばすのと、伸ばしてから回転するのでは結果が異なるため、 となります。
ニューラルネットワークで層を重ねることは、変換を合成することに相当します。しかし、線形変換だけを重ねても、合成された一つの行列と等価になってしまいます。層を深くする意味がなくなるという、この問題が、第13章のテーマにつながります。
2次元で得た直感の多くは、高次元でも通用します。ただし、高次元の空間には固有の性質もあり、ランダムに選んだ二つのベクトルがほぼ直交するという現象は、その代表例です。
次の第12章では、次元の拡大と縮小の意味を扱います。768次元を3072次元にする、あるいは3072次元を768次元に戻すという操作が、何をしているのか。この章で学んだ射影の考え方が、そこで活きてきます。
第12章に進む前に、演習4のコードを実行し、各変換で単位正方形がどう変形するかを確認しておいてください。特に、射影で面積が0になる点は、次章の内容につながります。
セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。
投稿者プロフィール

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