概要
- NumPy は配列計算に便利だが、複雑な操作になると扱いが難しくなる問題を抱えていることを論じる内容。
- ブロードキャスト や インデクシング の不透明さが、コードの可読性や予測性を損なう要因として指摘されている。
- np.einsum のような例外的に明快な機能もあるが、全体的には直感的な設計になっていないと批判。
- 関数の挙動 や多次元配列の扱いが一貫していないことが、学習コストやバグの温床になっている。
- 理想的な配列言語 への要望と、NumPyの設計上の「原罪」について考察されている。
NumPyへの愛憎とその課題
NumPyの魅力と限界
- NumPy はPythonで配列計算を行うためのソフトウェアであり、 PyTorch などの機械学習ライブラリにも大きな影響を与えていることを確認。
- 2次元までの配列計算は直感的かつ簡単に記述できるが、 高次元配列 や複雑な操作になると急激に分かりづらくなることを実感。
- 例えば、 複数の5×5行列 (100個)と 複数の長さ5ベクトル (100個)を同時に解く場合、ループを使えば明快だが、NumPy流の「ベクトル化」ではどの記法が正しいか分からなくなる現象を指摘。
- np.linalg.solve のような関数の使い方が直感的でなく、ドキュメントを読んでも正確な方法が分かりづらいことを問題視。
配列操作の複雑さ
- 3次元以上の配列同士の演算では、 次元合わせ(dimension alignment) や ブロードキャスト のために、Noneやreshape、transposeを多用する必要があり、可読性が著しく低下することを批判。
- 例:A(K×L×M)、B(L×N)、C(K×M)の場合にDkn = 1/(LM) × ∑lm Aklm Bln Ckmを計算するには、複雑な次元操作やtensordot、einsumなどを駆使する必要があることを確認。
- np.einsum は例外的に明快で強力だが、独自のインデックス記法によるドメイン固有言語を導入しているため、NumPyの標準的な設計とは異なることを強調。
ブロードキャストの功罪
- ブロードキャスト は一見便利だが、次元合わせのための冗長な記述や、演算の意味がコードから直感的に読み取れない問題を引き起こすことを指摘。
- A*Bのような単純な記述でも、内部でどのブロードキャスト規則が適用されているかを常に意識する必要があり、可読性・予測性が損なわれることを強調。
インデクシングの罠
- NumPyの高度な インデクシング は挙動が複雑で、配列の形状(shape)が直感的に予測できないことを多くの例で示す。
- 実際にAIモデルに形状を答えさせても、正答率は低く、学習コストが高いことを証明。
- シンプルなインデクシングを心掛けても、どこで複雑な挙動が紛れ込むか分からず、コードの安全性・可読性が低下することを指摘。
NumPy関数設計の一貫性欠如
- np.linalg.solve などの関数設計が一貫しておらず、axesパラメータやバリエーション関数、独自の内部規則(Conventions)などが混在していることを批判。
- どの関数がどの次元に対して動作するか、ユーザーが都度調べて合わせる必要があることを問題視。
- 新たな関数を自作した場合、NumPyが持つ「Einstein記法」的な柔軟性を持たせることができず、コードの再利用性や拡張性が低いことを指摘。
理想的な配列言語への提案
明快さと直感性への要求
- 理想的な配列言語とは、「やりたいことが 明らか に書けて、コードを読んだ時に 何をしているか明白 であること」が重要であると提案。
- NumPyは「インデックス」を隠し、 ブロードキャスト に頼った設計が根本的な問題(原罪)であると考察。
まとめと提案
- NumPyは配列計算の標準として普及したが、 高次元配列 や 複雑な操作 になると、設計の一貫性や直感性が失われることを再確認。
- np.einsum のようなインデックスベースの記法が、より明快で強力な設計思想のヒントになることを示唆。
- 配列言語の今後の発展には、「明快さ」「直感性」「一貫性」を重視した設計が不可欠であると提案。