メインコンテンツへスキップ

4.1.2 ベクトル:AI の世界の基本単位

ベクトルの内積とコサイン類似度の幾何図

学習の進め方

この章の数学では、定理を「証明」する必要はありません。必要なのは、直感を理解して、コードで実装できることです。各概念には可視化と NumPy コードを付けています。式がすぐに分からなくても大丈夫。図とコードが分かれば十分です。

学習目標

  • ベクトルとは何かを直感的に理解する(方向 + 大きさ)
  • ベクトルの加算、スカラー倍を理解する
  • 内積(Dot Product)の意味を理解する
  • コサイン類似度を身につける——AI で最もよく使う類似度指標
  • NumPy でベクトル操作を実装する

まず、学習のゴールをはっきりさせよう

この節ではコードも使いますが、コードは理解の代わりではありません
コードは、主に次の 2 つを助けてくれます。

  • 抽象的な対象を、目で見える形にする
  • 「自分の直感は合っているか」を確かめる

この節を読んだあと、すぐに問題をスラスラ解けなくても大丈夫です。
むしろ大切なのは、次のことです。

  • 「ある現実の対象」をベクトルとして表せるか
  • 内積とコサイン類似度が、何を比べているのか説明できるか
  • それをおすすめ、検索、RAG などの AI の場面につなげられるか

まずは全体の地図を持とう

この節で一番大事なのは、たくさんの用語を覚えることではなく、まず流れをつかむことです。

ベクトルの AI における意味の地図

この授業は、次のように考えると分かりやすいです。

  • 前半:ひとつの対象をどうベクトルで表すか
  • 後半:ふたつのベクトルがどれくらい似ているかどう比べるか

すぐ見返せる小さな用語集

用語意味初心者が知っておきたい理由
scalarスカラー、つまり 20.5 のような1つの数スカラー倍は「1つの数でベクトル全体を拡大・縮小する」操作です。
dimension次元、つまりベクトルの成分数[90, 85, 92] は数字が3つあるので 3 次元です。
shapeNumPy が配列の構造を表す情報(3,)(1, 3)(3, 1) は数字の数は同じでも、掛け算での振る舞いが違います。
normノルム、つまりベクトルの長さnp.linalg.norm(a) はベクトルの長さや強さを計算します。
NLPNatural Language Processing、自然言語処理テキストベクトルや単語ベクトルは、AI でよく使うベクトル例です。
vector databaseベクトルを保存し、似たベクトルを探すためのデータベース多くの RAG や意味検索システムの検索部分を支えます。

この表は暗記用ではなく、安全ネットとして使いましょう。後のコードで同じ言葉が出たら、ここに戻って、いまの操作とどうつながるか確認してください。

一、ベクトルとは何か?

直感で理解する

ベクトル = 順番のある数字の集まり。

初心者向けのたとえ

初めてベクトルを学ぶとき、「方向 + 大きさ」という説明がまだ少し抽象的に感じることがあります。そんなときは、まず次のように考えてみましょう。

  • ある対象の「情報カード」

たとえば、ある学生がいて、

  • 数学 90
  • 英語 85
  • 物理 92

という情報があるとします。
これらを決まった順番で並べると、コンピュータが扱える情報カードになります。

  • [90, 85, 92]

つまり、ベクトルのいちばん素朴な意味は「幾何学の図形」ではなく、

ある対象を、安定した順番の数字列として表すこと

です。AI の世界では、ベクトルはあらゆるところに出てきます。

AI の場面ベクトル表現次元
ある学生の成績[数学, 英語, 物理] = [90, 85, 92]3 次元
あるピクセルの色[R, G, B] = [255, 128, 0]3 次元
ある単語の意味(単語ベクトル)[0.2, -0.5, 0.8, ...]通常 100〜300 次元
1 枚の画像(平坦化後)[ピクセル1, ピクセル2, ..., ピクセルn]数万〜数百万次元

幾何学的な直感

2 次元空間では、ベクトルは矢印付きの線分として描けます。つまり、方向と**大きさ(長さ)**を持っています。

import numpy as np
import matplotlib.pyplot as plt

plt.rcParams['font.sans-serif'] = ['Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False

# 2つの2次元ベクトルを定義する
a = np.array([3, 2])
b = np.array([1, 4])

# ベクトルを描画する
fig, ax = plt.subplots(figsize=(6, 6))
ax.quiver(0, 0, a[0], a[1], angles='xy', scale_units='xy', scale=1,
color='steelblue', linewidth=2, label=f'a = {a}')
ax.quiver(0, 0, b[0], b[1], angles='xy', scale_units='xy', scale=1,
color='coral', linewidth=2, label=f'b = {b}')

ax.set_xlim(-1, 6)
ax.set_ylim(-1, 6)
ax.set_aspect('equal')
ax.grid(True, alpha=0.3)
ax.axhline(y=0, color='k', linewidth=0.5)
ax.axvline(x=0, color='k', linewidth=0.5)
ax.legend(fontsize=12)
ax.set_title('2次元ベクトルの幾何学的表現')
plt.show()

解釈:ベクトル a = [3, 2] は原点から出発して、右に 3、上に 2 進んだものです。

高次元ベクトルはどう考える?

AI で使うベクトルは、通常は数百次元や数千次元です。図には描けませんが、数学的な操作は同じです。ベクトルは数字の並びであり、どの次元でも同じルールで計算できます。

実際のデータからベクトルへ

初心者がつまずきやすいのは、「ベクトルは数字の列だ」と分かっても、それが現実のデータとどうつながるのか分からないことです。

import numpy as np

student = {
"math": 90,
"english": 85,
"physics": 92,
}

student_vector = np.array([
student["math"],
student["english"],
student["physics"],
])

print("学生のベクトル:", student_vector)
print("ベクトルの形状:", student_vector.shape) # (3,)

本質は次のとおりです。

  • 現実世界では「意味のある項目」がある
  • コンピュータでは「決まった順番の数値配列」にする必要がある

ひとたび対象をベクトルとして表せれば、数式で計算できるようになります。

weights = np.array([0.4, 0.2, 0.4])
score = student_vector @ weights
print("総合点:", score) # 89.8

ここでは、すでにこの先の機械学習の流れにつながっています。

  • データはベクトル
  • ルールもベクトル
  • それらの内積で、1 つのスコアが出る

二、ベクトルの基本演算

ベクトルの加算

2 つのベクトルを足す = 対応する位置の数字を足す ことです。

a = np.array([3, 2])
b = np.array([1, 4])

# ベクトルの加算
c = a + b
print(f"a + b = {c}") # [4, 6]

幾何学的には、b を a の終点に置くと、結果は最初の始点から最後の終点へ向かうベクトルになります。

fig, ax = plt.subplots(figsize=(7, 7))

# a を描く
ax.quiver(0, 0, a[0], a[1], angles='xy', scale_units='xy', scale=1,
color='steelblue', linewidth=2, label=f'a = {a}')
# b を描く(a の終点から開始)
ax.quiver(a[0], a[1], b[0], b[1], angles='xy', scale_units='xy', scale=1,
color='coral', linewidth=2, label=f'b = {b}')
# a + b を描く
ax.quiver(0, 0, c[0], c[1], angles='xy', scale_units='xy', scale=1,
color='green', linewidth=2.5, label=f'a + b = {c}')

ax.set_xlim(-1, 7)
ax.set_ylim(-1, 8)
ax.set_aspect('equal')
ax.grid(True, alpha=0.3)
ax.legend(fontsize=11)
ax.set_title('ベクトルの加算:頭と尾をつなぐ')
plt.show()

スカラー倍

ベクトルに数を掛ける = 各成分にその数を掛ける ことです。

a = np.array([3, 2])

# スカラー倍
print(f"2 * a = {2 * a}") # [6, 4] —— 方向は変わらず、長さは 2 倍
print(f"0.5 * a = {0.5 * a}") # [1.5, 1.0] —— 方向は変わらず、長さは半分
print(f"-1 * a = {-1 * a}") # [-3, -2] —— 方向が反転
fig, ax = plt.subplots(figsize=(8, 6))

vectors = [
(a, 'steelblue', f'a = {a}'),
(2 * a, 'green', f'2a = {2*a}'),
(0.5 * a, 'orange', f'0.5a = {0.5*a}'),
(-1 * a, 'red', f'-a = {-1*a}'),
]

for vec, color, label in vectors:
ax.quiver(0, 0, vec[0], vec[1], angles='xy', scale_units='xy', scale=1,
color=color, linewidth=2, label=label)

ax.set_xlim(-5, 8)
ax.set_ylim(-4, 6)
ax.set_aspect('equal')
ax.grid(True, alpha=0.3)
ax.axhline(y=0, color='k', linewidth=0.5)
ax.axvline(x=0, color='k', linewidth=0.5)
ax.legend(fontsize=11)
ax.set_title('スカラー倍:拡大・縮小と反転')
plt.show()

ベクトルの長さ(ノルム)

ベクトルのノルムと単位ベクトル図解

ベクトルの長さノルム大きさとも呼ぶ)は、三平方の定理で計算できます。

ベクトル a = [a1, a2] の長さ = √(a1² + a2²)

a = np.array([3, 4])

# 方法 1: 自分で計算
length_manual = np.sqrt(a[0]**2 + a[1]**2)
print(f"手計算の長さ: {length_manual}") # 5.0

# 方法 2: NumPy の内蔵関数(おすすめ)
length = np.linalg.norm(a)
print(f"NumPy の長さ: {length}") # 5.0
3-4-5 の三角形

ベクトル [3, 4] の長さはちょうど 5 です。これは有名なピタゴラス数です。データサイエンスでは、ベクトルの長さを求めるときに np.linalg.norm() をよく使います。

単位ベクトル

長さが 1 のベクトルを単位ベクトルといいます。どんなベクトルも、その長さで割ると、同じ向きを持つ単位ベクトルになります。

a = np.array([3, 4])

# 単位化(正規化)
unit_a = a / np.linalg.norm(a)
print(f"単位ベクトル: {unit_a}") # [0.6, 0.8]
print(f"単位ベクトルの長さ: {np.linalg.norm(unit_a)}") # 1.0

なぜ重要なのか? AI では、2 つのベクトルの大きさではなく向きを比べたい場面がよくあります。単位化すると、向きの情報だけを残せます。

まず身につけたい shape 感覚

ベクトル学習の最初の壁は、概念は分かっても、コードを書くと shape で混乱することです。

import numpy as np

a = np.array([1, 2, 3]) # 1次元ベクトル
row = a.reshape(1, 3) # 行ベクトル
col = a.reshape(3, 1) # 列ベクトル

print("a.shape =", a.shape) # (3,)
print("row.shape =", row.shape) # (1, 3)
print("col.shape =", col.shape) # (3, 1)

どれも「3 つの数字」に見えますが、行列計算では意味が違います。

  • (3,) は NumPy の普通の 1 次元配列
  • (1, 3) は「1 行 3 列」を明示
  • (3, 1) は「3 行 1 列」を明示

この先、行列やニューラルネットワークを学ぶときは、公式を暗記するよりも shape の感覚が大事です。


三、内積——ベクトルで最も重要な演算

内積とは?

2 つのベクトルの内積(Dot Product)= 対応する位置を掛けて、最後に足し合わせる ことです。

a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

# 方法 1: 手計算
dot_manual = a[0]*b[0] + a[1]*b[1] + a[2]*b[2]
print(f"手計算: {dot_manual}") # 1*4 + 2*5 + 3*6 = 32

# 方法 2: NumPy(おすすめ)
dot_np = np.dot(a, b)
print(f"NumPy: {dot_np}") # 32

# 方法 3: @ 演算子(Python 3.5+)
dot_at = a @ b
print(f"@ 演算子: {dot_at}") # 32

内積の幾何学的な意味

内積は、2 つのベクトルの向きの関係を表します。

# 同じ向き
a = np.array([1, 0])
b = np.array([1, 1])
print(f"同じ向き: a · b = {np.dot(a, b)}") # 1(正の数)

# 垂直
a = np.array([1, 0])
b = np.array([0, 1])
print(f"垂直: a · b = {np.dot(a, b)}") # 0

# 反対向き
a = np.array([1, 0])
b = np.array([-1, 0])
print(f"反対向き: a · b = {np.dot(a, b)}") # -1(負の数)

なぜ内積は「そろい具合」と考えられるのか?

内積には、次のような重要な見方もあります。

あるベクトルが、別のベクトルの方向にどれだけ投影されるかが大きいほど、内積は大きくなりやすい。

公式は次のように書けます。

a · b = |a| × |b| × cos(theta)

今は導出を覚える必要はありません。まずは次の 3 つだけ押さえましょう。

  1. 2 つのベクトルが同じ向きに近いほど、cos(theta) は 1 に近づく
  2. 2 つのベクトルが垂直に近いほど、cos(theta) は 0 に近づく
  3. 2 つのベクトルが反対向きに近いほど、cos(theta) は -1 に近づく

つまり、内積には次の 2 つが同時に入っています。

  • 長さの情報
  • 向きの情報

可視化で内積を理解する

fig, axes = plt.subplots(1, 3, figsize=(15, 4))

cases = [
([2, 1], [1, 2], '同じ向き(内積 > 0)'),
([2, 0], [0, 2], '垂直(内積 = 0)'),
([2, 1], [-1, -2], '反対向き(内積 < 0)'),
]

for ax, (a, b, title) in zip(axes, cases):
a, b = np.array(a), np.array(b)
dot = np.dot(a, b)

ax.quiver(0, 0, a[0], a[1], angles='xy', scale_units='xy', scale=1,
color='steelblue', width=0.02, label='a')
ax.quiver(0, 0, b[0], b[1], angles='xy', scale_units='xy', scale=1,
color='coral', width=0.02, label='b')

ax.set_xlim(-3, 4)
ax.set_ylim(-3, 4)
ax.set_aspect('equal')
ax.grid(True, alpha=0.3)
ax.axhline(y=0, color='k', linewidth=0.5)
ax.axvline(x=0, color='k', linewidth=0.5)
ax.set_title(f'{title}\na·b = {dot}')
ax.legend()

plt.tight_layout()
plt.show()

四、コサイン類似度——AI で最もよく使う類似度

内積からコサイン類似度へ

内積の大きさは、向きだけでなくベクトルの長さにも影響されます。
もし向きがどれくらい似ているかだけを見たいなら、長さの影響を取り除く必要があります。

コサイン類似度 = 内積 / (ベクトル A の長さ × ベクトル B の長さ)

def cosine_similarity(a, b):
"""2つのベクトルのコサイン類似度を計算する"""
dot_product = np.dot(a, b)
norm_a = np.linalg.norm(a)
norm_b = np.linalg.norm(b)
if norm_a == 0 or norm_b == 0:
raise ValueError("ゼロベクトルには方向がないため、コサイン類似度は定義できません。")
return dot_product / (norm_a * norm_b)

ゼロベクトルを確認する理由は、長さが 0 のベクトルには方向がないからです。コサイン類似度は方向を比べる指標なので、長さ 0 で割ると誤解を招く結果や実行時警告につながります。

コサイン類似度の値の範囲は次の通りです。

意味
1方向が完全に同じ
0完全に無関係(垂直)
-1方向が完全に反対

例:ユーザーの好みの類似度

3 人のユーザーが、5 種類の映画ジャンルに対して好みの強さを付けたとします。

# [アクション, コメディ, 恋愛, SF, ホラー] に対する好みのスコア(1-5)
alice = np.array([5, 3, 4, 5, 1])
bob = np.array([4, 2, 5, 4, 1])
charlie = np.array([1, 5, 2, 1, 5])

# 2人ずつの類似度を計算する
print(f"Alice vs Bob: {cosine_similarity(alice, bob):.4f}")
print(f"Alice vs Charlie: {cosine_similarity(alice, charlie):.4f}")
print(f"Bob vs Charlie: {cosine_similarity(bob, charlie):.4f}")

出力:

Alice vs Bob:     0.9761
Alice vs Charlie: 0.5825
Bob vs Charlie: 0.5600

解釈:Alice と Bob の好みはとても似ています(0.98 は 1 にかなり近い)。Charlie は2人ほど方向がそろっていませんが、完全に反対というわけでもありません。これがレコメンドシステムの基本です。まず好みの方向を比べ、近いユーザーや近いアイテムが好きなものをおすすめします。

AI におけるコサイン類似度の応用

コサイン類似度はこれから何度も使います
  • NLP(11 自然言語処理):2 つの単語ベクトルがどれだけ似ているかを計算する。たとえば「猫」と「犬」はコサイン類似度が高い
  • RAG(8 LLM アプリ開発と RAG):ベクトルデータベースで、関連性の高い文書断片を検索する
  • レコメンドシステム:好みが最も近いユーザーを見つける

つまり、コサイン類似度は AI 学習のあちこちで何度も登場する、とても重要な道具です。

可視化:コサイン類似度が違うベクトル

fig, axes = plt.subplots(1, 4, figsize=(16, 4))

# 異なる類似度を持つベクトルの組
pairs = [
([1, 0], [1, 0.1], '≈ 1.0(ほぼ同じ)'),
([1, 0], [0.7, 0.7], '≈ 0.7(かなり似ている)'),
([1, 0], [0, 1], '= 0(無関係)'),
([1, 0], [-0.9, -0.3], '≈ -0.95(反対)'),
]

for ax, (a, b, desc) in zip(axes, pairs):
a, b = np.array(a), np.array(b)
sim = cosine_similarity(a, b)

ax.quiver(0, 0, a[0], a[1], angles='xy', scale_units='xy', scale=1,
color='steelblue', width=0.02)
ax.quiver(0, 0, b[0], b[1], angles='xy', scale_units='xy', scale=1,
color='coral', width=0.02)

ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-1, 1.5)
ax.set_aspect('equal')
ax.grid(True, alpha=0.3)
ax.set_title(f'cos = {sim:.2f}\n{desc}', fontsize=10)

plt.tight_layout()
plt.show()

もっとも小さな検索例:3 つの候補から最も近いものを探す

次の例は手作りの小さなベクトルですが、考え方はベクトル検索、RAG、意味検索と同じです。

import numpy as np

def cosine_similarity(a, b):
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

query = np.array([0.9, 0.1, 0.8, 0.2])

docs = {
"文書A:機械学習入門": np.array([0.8, 0.2, 0.75, 0.1]),
"文書B:旅行ガイド": np.array([0.1, 0.9, 0.2, 0.8]),
"文書C:深層学習の基礎": np.array([0.85, 0.15, 0.7, 0.25]),
}

scores = []
for name, vec in docs.items():
sim = cosine_similarity(query, vec)
scores.append((name, sim))

scores.sort(key=lambda x: x[1], reverse=True)

for name, sim in scores:
print(f"{name}: {sim:.4f}")

期待される出力:

文書C:深層学習の基礎: 0.9964
文書A:機械学習入門: 0.9922
文書B:旅行ガイド: 0.3333

相似度が最も高いものは、たいていクエリの意味にいちばん近い文書です。


五、NumPy によるベクトル操作のまとめ

この節で学んだ操作を、NumPy で整理しておきましょう。

import numpy as np

# ========== ベクトルの作成 ==========
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])

# ========== 基本演算 ==========
print("加算:", a + b) # [5, 7, 9]
print("減算:", a - b) # [-3, -3, -3]
print("スカラー倍:", 3 * a) # [3, 6, 9]
print("要素ごとの掛け算:", a * b) # [4, 10, 18]

# ========== 内積 ==========
print("内積:", np.dot(a, b)) # 32
print("内積:", a @ b) # 32(同じ意味)

# ========== 長さ(ノルム) ==========
print("長さ:", np.linalg.norm(a)) # 3.742

# ========== 単位化 ==========
unit_a = a / np.linalg.norm(a)
print("単位ベクトル:", unit_a)

# ========== コサイン類似度 ==========
cos_sim = np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
print("コサイン類似度:", cos_sim) # 0.9746

# scikit-learn にも内蔵関数があります
# from sklearn.metrics.pairwise import cosine_similarity

ここまで学んだら、次に持っていくべき問い

ベクトルを学び終えたら、次の 3 つの疑問を持って次へ進むとよいです。

  1. ある対象がベクトルで表せるなら、たくさんの対象はどう表せるのか?
  2. 2 つのベクトルの類似度を比べられるなら、たくさんのベクトルはどう変換するのか?
  3. ニューラルネットワークでは、なぜ毎回 1 つのベクトルだけを処理せず、まとめて処理するのか?

この 3 つの問いは、自然に次の内容へつながります。

次につながる内容
  • 次の節:行列——ベクトルの集まりをまとめて変換する
  • 5 機械学習入門から実践へ:線形回帰では、各サンプルは特徴ベクトルで、モデルは重みベクトルを見つけること
  • 11 自然言語処理(選択トラック):単語ベクトル、文ベクトル、コサイン類似度が何度も登場する
  • 8 LLM アプリ開発と RAG:ベクトルデータベースの中心は、高次元ベクトルの類似検索

まとめ

概念直感的な理解NumPy 実装
ベクトル順番のある数字の集まりnp.array([1, 2, 3])
ベクトルの加算対応する位置を足すa + b
スカラー倍ベクトルを拡大・縮小するk * a
ベクトルの長さ原点から終点までの距離np.linalg.norm(a)
内積2 つのベクトルの向きの関係を測るnp.dot(a, b) または a @ b
コサイン類似度長さを見ずに、向きだけで比べる類似度dot / (norm_a * norm_b)

この節でいちばん持ち帰ってほしいこと

  • ベクトルは、まず「対象を表すもの」であって、矢印の図形だけではない
  • 内積は、まず「そろい具合」として理解するのがよい
  • コサイン類似度は、まず「向きの近さ」として理解するのがよい
  • だからこそ、AI の多くの検索・推薦・マッチングシステムでベクトルが欠かせない

手を動かして練習しよう

練習 1:ベクトル演算

ベクトル a = [2, 3, -1] と b = [1, -2, 4] が与えられたとき、NumPy を使って次を計算してください。

  1. a + b
  2. 3a - 2b
  3. a の長さ
  4. a と b の内積
  5. a と b のコサイン類似度

練習 2:いちばん似ている映画を見つける

5 本の映画の特徴ベクトル(ジャンルの好みを数値化したもの)があるとします。

movies = {
"星际穿越": np.array([5, 1, 3, 5, 2]), # [アクション, コメディ, 感情, SF, ホラー]
"泰囧": np.array([2, 5, 3, 1, 1]),
"战狼2": np.array([5, 1, 2, 2, 1]),
"前任3": np.array([1, 3, 5, 1, 1]),
"异形": np.array([4, 1, 1, 4, 5]),
}

課題:各映画のペアごとにコサイン類似度を計算し、いちばん似ている組と、いちばん似ていない組を見つけてください。

練習 3:ベクトルの加算を可視化する

Matplotlib を使って、次のベクトル加算の過程を矢印付きで描いてみましょう。

  • a = [2, 3]、b = [-1, 2]。a、b、a+b を描く

ヒント:2.1 節のコードを参考にしてください。