第2章 スカラーとベクトル:数値1個と、数値の並び

こんにちは。ゆうせいです。

第1章では、ディープラーニングにおける線形代数の役割を、大きな地図として確認しました。第2章から、具体的な道具を一つずつ手に取っていきます。最初に扱うのは、スカラーとベクトルです。この二つは、線形代数の最も基本的な要素であり、名前を聞いたことがある方も多いでしょう。ただし、「ベクトルとは向きと大きさを持つ量です」という高校で習った説明だけでは、ディープラーニングでの使われ方が見えてきません。この章では、ベクトルを「数値を並べたもの」として捉え直し、それがモデルの中でどのように現れるのかを確認します。

スカラーとは何か

スカラーとは、数値が1個だけのものです。

3

-0.5

1.618

いずれもスカラーです。日常的に扱う「数」そのものだと考えてください。

なぜ、わざわざ「スカラー」という名前を付けるのでしょうか。理由は、ベクトルや行列と区別するためです。数値が1個なのか、複数並んでいるのかを明確にする必要があるとき、この呼び分けが必要になります。

ディープラーニングに現れるスカラー

スカラーは、次のような場面で登場します。

学習率です。パラメータをどれだけ動かすかを決める値であり、0.001といった1個の数値です。

損失の値です。モデルの予測がどれだけ間違っているかを表す指標であり、最終的には1個の数値にまとめられます。

温度パラメータです。生成される文章の多様性を調整する値であり、これも1個の数値です。

第1章で示した式のうち、\sqrt{d_k} で割るという処理も、スカラーによる割り算です。

ベクトルとは何か

ベクトルとは、数値を1列に並べたものです。

\mathbf{v} = \begin{pmatrix} 3 \ -1 \ 4 \end{pmatrix}

この例では、3個の数値が縦に並んでいます。

横に並べて書くこともあります。

\mathbf{v} = [3, -1, 4]

数学の文脈では、縦に並べた形(列ベクトル)を基本とすることが多く、横に並べた形(行ベクトル)は、その転置として扱われます。転置については、第10章で詳しく説明します。

プログラミングでは、横に並べた形で書くのが一般的です。この違いは、慣れるまで混乱の元になりますが、表している内容は同じです。

記法について

ベクトルを表す記号には、いくつかの流儀があります。

太字の小文字を使う流儀です。\mathbf{v}\mathbf{x} のように書きます。

矢印を付ける流儀です。高校数学では、この書き方が使われます。

この連載では、太字の小文字を使います。ディープラーニングの論文や技術文書では、こちらが標準的だからです。

要素と添え字

ベクトルの中の個々の数値を、要素または成分と呼びます。

各要素を指し示すには、添え字を使います。

\mathbf{v} = [v_1, v_2, v_3]

v_1 は1番目の要素、v_2 は2番目の要素です。

注意点として、数学では1から数え始めるのに対し、プログラミングでは0から数え始めることが一般的です。Pythonでは、v[0] が1番目の要素になります。

この違いは、数式をコードに翻訳する際に、間違いやすい箇所です。数式の v_1 は、コードでは v[0] に対応する、と意識してください。

「次元」という言葉の二つの意味

ここで、初学者が最も混乱する用語を扱います。「次元」という言葉です。

この言葉には、文脈によって二つの異なる意味があります。

意味1:ベクトルの要素数

[3, -1, 4] というベクトルは、要素が3個あります。これを「3次元ベクトル」と呼びます。

768個の数値が並んだベクトルは、768次元ベクトルです。

ディープラーニングの文脈で「768次元の埋め込み」と言えば、この意味です。数値が768個並んでいる、ということです。

意味2:軸の数

一方、プログラムで扱う際には、別の意味の「次元」が登場します。

数値が1列に並んでいるだけなら、軸は1本です。この場合、「1次元の配列」と呼びます。

数値が縦横に並んでいれば、軸は2本です。「2次元の配列」と呼びます。

NumPyやPyTorchで ndim という属性を見ると、この意味での次元数が返ってきます。

混乱の具体例

[3, -1, 4] というベクトルについて、次の二つの言い方が、どちらも正しくなります。

「これは3次元ベクトルです」(要素が3個あるから)

「これは1次元の配列です」(軸が1本だから)

矛盾しているように見えますが、「次元」という同じ言葉が、別のものを指しているだけです。

区別のコツは、次の通りです。数学の文脈で「n次元ベクトル」と言えば、要素数の話です。プログラムの ndim や、テンソルの構造の話であれば、軸の数の話です。

この連載では、混乱を避けるため、要素数を指すときは「要素数」または「次元数」、軸の数を指すときは「軸の数」と、なるべく明示して書きます。

ベクトルの二つの捉え方

ベクトルには、大きく二つの捉え方があります。どちらも正しく、場面によって使い分けます。

捉え方1:矢印としてのベクトル

高校数学で習うのは、こちらです。

2次元ベクトル [3, 2] は、原点から右に3、上に2進んだ点への矢印として図示できます。

この捉え方では、ベクトルは「向きと大きさ」を持つ量です。物理学における力や速度が、この意味でのベクトルです。

この捉え方が有効なのは、2次元や3次元の場合です。図に描けるため、直感的に理解できます。第11章の線形変換では、この捉え方が中心になります。

捉え方2:数値の並びとしてのベクトル

ディープラーニングでは、こちらの捉え方が主になります。

768次元のベクトルを、矢印として想像することはできません。768本の軸を持つ空間を、人間は視覚化できないからです。

そこで、単に「768個の数値が並んだもの」として扱います。向きや大きさという直感は、いったん脇に置きます。

両者の関係

とはいえ、二つの捉え方は無関係ではありません。

2次元や3次元で成り立つ性質の多くは、768次元でもそのまま成り立ちます。内積が類似度を表すという性質も、2次元の図で理解できるものが、そのまま高次元に拡張されます。

したがって、学習の順序としては、2次元や3次元の図で直感を作り、それを高次元に持ち込む、という進め方が効果的です。この連載も、その順序で進めます。

ベクトルの長さ

ベクトルの長さ(ノルム、大きさ)は、次の式で計算されます。

|\mathbf{v}| = \sqrt{v_1^2 + v_2^2 + \cdots + v_n^2}

各要素を2乗して、すべて足して、平方根を取ります。

具体例で確認します。

\mathbf{v} = [3, 4]

|\mathbf{v}| = \sqrt{3^2 + 4^2} = \sqrt{9 + 16} = \sqrt{25} = 5

2次元の場合、これは三平方の定理そのものです。原点から点(3, 4)までの距離が5である、ということです。

3次元以上でも、同じ式がそのまま使えます。要素が増えれば、足す項が増えるだけです。

ディープラーニングでの用途

ベクトルの長さは、次のような場面で使われます。

正則化です。パラメータのベクトルが大きくなりすぎないよう、長さに応じた罰則を損失に加えます。

勾配クリッピングです。勾配ベクトルの長さが一定値を超えた場合に、縮小して学習の暴走を防ぎます。

正規化です。ベクトルを自分自身の長さで割ると、長さが1のベクトルになります。方向だけを取り出したい場合に使われます。

\hat{\mathbf{v}} = \frac{\mathbf{v}}{|\mathbf{v}|}

ディープラーニングに現れるベクトル

ここまでの内容を、実際のモデルに対応づけます。ベクトルは、次のような場面で登場します。

入力データ

表形式のデータでは、1件のサンプルがベクトルになります。

年齢、年収、勤続年数、といった特徴量を並べたものです。

\mathbf{x} = [35, 550, 8]

特徴量が10個あれば、10次元のベクトルです。

単語の埋め込み

言語モデルでは、各単語が数百次元のベクトルとして表現されます。

GPT-2では、1つのトークンが768個の数値からなるベクトルになります。

\mathbf{e} = [0.23, -0.51, 0.08, \ldots, -0.17]

この768個の数値の組み合わせが、その単語の意味を表現しています。

重みとバイアス

ニューロン1個は、入力と同じ数の重みを持ちます。

入力が768次元であれば、重みも768次元のベクトルです。

\mathbf{w} = [w_1, w_2, \ldots, w_{768}]

バイアスは、ニューロン1個につきスカラーですが、層全体で見ればベクトルになります。

出力と確率

分類問題では、各クラスに対する確率が、ベクトルとして出力されます。

3クラス分類であれば、3次元のベクトルです。

\mathbf{p} = [0.7, 0.2, 0.1]

要素の合計が1になっている点が特徴です。

勾配

第14章で詳しく扱いますが、勾配もベクトルです。

各パラメータについて「どちらにどれだけ動かすべきか」を並べたものであり、パラメータの数と同じ要素数を持ちます。

Pythonで確認する

実際に手を動かして確認してみましょう。

import numpy as np

# スカラー
learning_rate = 0.001
print("=== スカラー ===")
print(f"値: {learning_rate}")
print(f"型: {type(learning_rate)}")

# ベクトル
v = np.array([3, -1, 4])
print("\n=== ベクトル ===")
print(f"値: {v}")
print(f"要素数: {len(v)}")
print(f"形(shape): {v.shape}")
print(f"軸の数(ndim): {v.ndim}")

# 要素へのアクセス
print("\n=== 要素へのアクセス ===")
print(f"1番目の要素(数式のv_1): {v[0]}")
print(f"2番目の要素(数式のv_2): {v[1]}")
print(f"最後の要素: {v[-1]}")

# ベクトルの長さ
u = np.array([3, 4])
print("\n=== ベクトルの長さ ===")
print(f"ベクトル: {u}")
print(f"長さ(手計算): {(3**2 + 4**2)**0.5}")
print(f"長さ(NumPy): {np.linalg.norm(u)}")

# 正規化
u_normalized = u / np.linalg.norm(u)
print(f"\n正規化後: {u_normalized}")
print(f"正規化後の長さ: {np.linalg.norm(u_normalized)}")

# 高次元ベクトル
embedding = np.random.randn(768)
print("\n=== 768次元ベクトル(埋め込みの例)===")
print(f"形: {embedding.shape}")
print(f"軸の数: {embedding.ndim}")
print(f"最初の5要素: {embedding[:5].round(4)}")
print(f"長さ: {np.linalg.norm(embedding):.4f}")

このコードを実行すると、「768次元のベクトル」が「軸の数は1」であることが、数値として確認できます。用語の混乱を解消するのに、最も効果的な確認方法です。

初学者がつまずきやすい点

つまずき1:次元という言葉の二重の意味

先に説明したとおりですが、実務でも繰り返し混乱の元になります。

対処法として、コードを書く際は、変数の shape を頻繁に表示する習慣をつけてください。(768,) と表示されれば、軸が1本で要素が768個、という意味です。

shape の表示に慣れると、言葉の混乱に悩まされることが減ります。

つまずき2:縦か横かにこだわりすぎる

数学の教科書では、ベクトルを縦に書くことが多く、横に書く場合は転置記号を付けます。

一方、プログラムでは、1次元配列に縦横の区別がありません。np.array([3, -1, 4]) は、縦とも横とも決まっていない状態です。

計算の際に、必要に応じて解釈されます。この点は、第10章の形を合わせる話で、あらためて扱います。

現時点では、「数学の記法では縦横を区別することがあるが、プログラムでは1次元配列に区別はない」とだけ押さえておいてください。

つまずき3:0から数えることを忘れる

数式の v_1 は、Pythonの v[0] です。

この対応を間違えると、1つずれた要素を参照してしまいます。エラーにならないため、気づきにくい間違いです。

数式をコードに翻訳する際は、添え字の対応を必ず確認する習慣をつけてください。

演習

演習1:ベクトルの長さを手計算する

次の三つのベクトルについて、それぞれ長さを計算してください。

\mathbf{a} = [6, 8]

\mathbf{b} = [1, 2, 2]

\mathbf{c} = [1, 1, 1, 1]

演習2:用語を対応づける

次のそれぞれについて、スカラーかベクトルかを答えてください。ベクトルの場合は、要素数も答えてください。

学習率0.01

3クラス分類の出力確率

GPT-2における1トークンの埋め込み

損失関数の値

10個の特徴量を持つ1件の顧客データ

演習3:正規化の効果を確認する

import numpy as np

# 長さの異なる3つのベクトル
vectors = {
    "a": np.array([3.0, 4.0]),
    "b": np.array([30.0, 40.0]),
    "c": np.array([0.3, 0.4])
}

print(f"{'名前':>4} | {'ベクトル':>18} | {'長さ':>8} | {'正規化後':>18}")
print("-" * 60)

for name, v in vectors.items():
    length = np.linalg.norm(v)
    normalized = v / length
    print(f"{name:>4} | {str(v):>18} | {length:>8.4f} | {str(normalized.round(4)):>18}")

print("\n正規化後のベクトルは、すべて同じ値になっているでしょうか。")
print("なぜそうなるか、考えてみてください。")

演習の解答例と解説

演習1の解答は、次の通りです。

|\mathbf{a}| = \sqrt{6^2 + 8^2} = \sqrt{36 + 64} = \sqrt{100} = 10

|\mathbf{b}| = \sqrt{1^2 + 2^2 + 2^2} = \sqrt{1 + 4 + 4} = \sqrt{9} = 3

|\mathbf{c}| = \sqrt{1^2 + 1^2 + 1^2 + 1^2} = \sqrt{4} = 2

\mathbf{c} の例が示唆的です。すべての要素が1という小さな値であっても、要素数が増えれば長さは大きくなります。要素数が100個であれば、長さは10になります。

高次元のベクトルでは、各要素が小さくても、全体としての長さは大きくなりやすい、という性質があります。この点は、第7章のスケーリングを理解する際の伏線になります。

演習2の解答は、次の通りです。

学習率0.01は、スカラーです。

3クラス分類の出力確率は、3次元のベクトルです。

GPT-2における1トークンの埋め込みは、768次元のベクトルです。

損失関数の値は、スカラーです。

10個の特徴量を持つ1件の顧客データは、10次元のベクトルです。

損失関数の値がスカラーである点は、重要です。モデルの良し悪しを、最終的に1個の数値に集約するからこそ、それを小さくするという明確な目標が立てられます。この点は、第14章の勾配の話につながります。

演習3では、a、b、cの三つのベクトルが、正規化後にすべて同じ値になることが確認できます。

理由は、三つのベクトルが、長さは違うものの、向きが同じだからです。bはaの10倍、cはaの0.1倍であり、いずれも同じ方向を指しています。

正規化は、長さの情報を捨てて、方向の情報だけを取り出す操作である、ということが、この実験から確認できます。

講師向けの補足

この章で最も時間を割くべきは、「次元」という言葉の二重の意味です。

多くの受講者が、後の章でこの点に混乱します。第2章の段階で明確にしておくと、以降の説明がスムーズになります。

効果的な方法は、演習3のコードを拡張し、768次元のベクトルを作って shape と ndim を両方表示させることです。(768,) と 1 という二つの数値が同時に表示される画面を見ると、言葉の使い分けが体で理解されます。

また、ベクトルの二つの捉え方については、「矢印のイメージは2次元と3次元でだけ使い、それ以上では数値の並びとして扱う」という方針を明示してください。768次元の矢印を想像しようとして苦しむ受講者が、毎回一定数出ます。想像しなくてよい、と言い切ることが、かえって助けになります。

理解度を確認する問い

第2章の内容を、次の問いで確認してみてください。

スカラーとベクトルの違いを、自分の言葉で説明してみてください。

「768次元のベクトル」と「1次元の配列」が、同じものを指すことがあるのはなぜでしょうか。

ベクトルを自分自身の長さで割ると、何が起きるでしょうか。その操作は、何を目的として行われるでしょうか。

ディープラーニングにおいて、ベクトルとして表現されるものを、五つ挙げてください。

まとめ

スカラーとは、数値が1個だけのものです。学習率、損失の値、温度パラメータなどが該当します。

ベクトルとは、数値を1列に並べたものです。数学では縦に書くことが多く、プログラムでは横に書くのが一般的ですが、表している内容は同じです。

「次元」という言葉には、二つの意味があります。ベクトルの要素数を指す場合と、配列の軸の数を指す場合です。768次元のベクトルは、軸の数で言えば1次元の配列です。この使い分けは、文脈から判断する必要があります。

ベクトルには、矢印としての捉え方と、数値の並びとしての捉え方があります。2次元や3次元では前者が直感的ですが、768次元のような高次元では、後者として扱います。ただし、低次元で得た直感の多くは、高次元でもそのまま通用します。

ベクトルの長さは、各要素を2乗して足し、平方根を取ることで計算されます。自分自身の長さで割ると、長さ1のベクトルになり、方向の情報だけが残ります。

ディープラーニングでは、入力データ、単語の埋め込み、重み、出力確率、勾配など、あらゆる場面でベクトルが登場します。

次の第3章では、行列とテンソルを扱います。ベクトルを縦横に並べると行列になり、さらに軸を増やすとテンソルになります。ディープラーニングで実際に扱われるデータの形が、この章で明らかになります。

第3章に進む前に、演習3のコードを実行し、正規化の効果を自分の目で確認しておいてください。また、いくつかのベクトルを作って shape と ndim を表示させ、「次元」という言葉の二つの意味を、数値として体感しておくことをお勧めします。

セイ・コンサルティング・グループでは新人エンジニア研修のアシスタント講師を募集しています。

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
海外放浪の末、2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。

学生時代は趣味と実益を兼ねてリゾートバイトにいそしむ。長野県白馬村に始まり、志賀高原でのスキーインストラクター、沖縄石垣島、北海道トマム。高じてオーストラリアのゴールドコーストでツアーガイドなど。現在は野菜作りにはまっている。