概要¶
NumPy¶
NumPyとは,Pythonで利用できる科学計算ライブラリで,行列計算に特化した機能が豊富に収録されています. Pythonの組み込み型には,ListはあってもArray(配列)はありませんでした.NumPyの提供するNDArrayはPythonで使われる標準的な配列型であり,これを含めたNumPyの機能はCやFortranのような高速な言語で実装されているため,NumPyをうまく使いこなすことで「Pythonの手軽さを享受しつつ,チューニングされたC言語製プログラムのような速度で処理する」ことができます.
NumPyが高速な理由¶
NumPyが高速なのは,C/Fortranで実装されているからだけではありません.NumPyでの計算をより高速化するために,BLAS(Basic Linear Algebra Subprograms) と LAPACK(Linear Algebra PACKage) というソフトウェアがリンクされ,利用されています.これがNumPyの処理が高速な理由です.
BLAS(Basic Linear Algebra Subprograms)¶
数値計算で頻出するベクトル・行列の基本演算を、名前と引数の仕様だけ決めた低レベル API の集合である。実装は CPU ベンダや OSS(OpenBLAS、BLIS、Apple Accelerate など)が提供し、最適化されたアセンブリや SIMD で書かれることが多い。
典型的なレベル分けは次のとおりである。
Level 1: ベクトルとベクトル(スカラー倍・内積・ノルム・axpy など)。メモリ参照は O(n) で、演算量も O(n) なので演算密度が低い。
Level 2: 行列とベクトル(例: GEMV)。演算は O(n²)、データも O(n²) 程度なので、サイズによってはメモリ帯域に律速されやすい。
Level 3: 行列と行列(例: GEMM = 一般行列積)。演算 O(n³) に対しデータ O(n²) なので演算密度が高く、キャッシュブロック化と相性が良い。深層学習の線形層や dense LA の心臓部で、高速 GEMM が全体性能を左右することが多い。
要するに、「線形代数の足場となる乗算・加算・コピー・ノルムなどを、共通化された名前で呼べるようにした層」である。
LAPACK(Linear Algebra PACKage)¶
稠密行列向けのより高レベルな数値線形代数ルーチンの集合である。BLAS(主に Level 2/3)を内部で多用し、次のような問題全体の解法を担う。
線形方程式 (Ax = b)(LU、Cholesky、QR などの分解と前進・後退代入)
最小二乗・過剰決定系
固有値・固有ベクトル(対称 / 非対称、一般化固有値)
特異値分解(SVD)
条件数推定、行列の性質(ランク、慣性など)に関わる補助ルーチン
アルゴリズムは安定性(ピボット、スケーリング)やバックワード誤差の解析が進んでおり、「分解 + 簡単な三角系」 に帰着させてから BLAS で速く回す設計が多い。
関係の整理¶
| BLAS | LAPACK | |
|---|---|---|
| 抽象度 | 基本演算(ブロック) | 分解・固有値・SVD などの手続き全体 |
| 典型例 | GEMM, TRSM, AXPY | *getrf, *potrf, *geqrf, *gesvd など |
| 役割 | 速い「積み木」 | その積み木で組んだ「完成品」 |
NumPy の linalg や SciPy の線形代数、多くのフレームワークの CPU 版は、内部で BLAS/LAPACK(またはそれに相当するライブラリ)に依存していることが多い。
NumPyの設定を確認¶
NumPyはPython上でimport numpy as npと書くことでimportでき,np.hogehogeで利用できます.バージョンとコンフィグを確認しましょう.
import numpy as np
print(np.__version__)
print(np.__config__.show())2.4.4
Build Dependencies:
blas:
detection method: system
found: true
include directory: unknown
lib directory: unknown
name: accelerate
openblas configuration: unknown
pc file directory: unknown
version: unknown
lapack:
detection method: system
found: true
include directory: unknown
lib directory: unknown
name: accelerate
openblas configuration: unknown
pc file directory: unknown
version: unknown
Compilers:
c:
commands: cc
linker: ld64
name: clang
version: 15.0.0
c++:
commands: c++
linker: ld64
name: clang
version: 15.0.0
cython:
commands: cython
linker: cython
name: cython
version: 3.2.4
Machine Information:
build:
cpu: aarch64
endian: little
family: aarch64
system: darwin
host:
cpu: aarch64
endian: little
family: aarch64
system: darwin
Python Information:
path: /private/var/folders/bb/km69qtt53nlbxc29ljr4c9d00000gn/T/build-env-8jz__88i/bin/python
version: '3.12'
SIMD Extensions:
baseline:
- NEON
- NEON_FP16
- NEON_VFPV4
- ASIMD
found:
- ASIMDHP
- ASIMDDP
not found:
- ASIMDFHM
None
2.0.2
Build Dependencies:
blas:
detection method: pkgconfig
found: true
include directory: /opt/_internal/cpython-3.12.2/lib/python3.12/site-packages/scipy_openblas64/include
lib directory: /opt/_internal/cpython-3.12.2/lib/python3.12/site-packages/scipy_openblas64/lib
name: scipy-openblas
openblas configuration: OpenBLAS 0.3.27 USE64BITINT DYNAMIC_ARCH NO_AFFINITY
Zen MAX_THREADS=64
pc file directory: /project/.openblas
version: 0.3.27
lapack:
detection method: pkgconfig
found: true
include directory: /opt/_internal/cpython-3.12.2/lib/python3.12/site-packages/scipy_openblas64/include
lib directory: /opt/_internal/cpython-3.12.2/lib/python3.12/site-packages/scipy_openblas64/lib
name: scipy-openblas
openblas configuration: OpenBLAS 0.3.27 USE64BITINT DYNAMIC_ARCH NO_AFFINITY
Zen MAX_THREADS=64
pc file directory: /project/.openblas
version: 0.3.27
Compilers:
c:
commands: cc
linker: ld.bfd
name: gcc
version: 10.2.1
c++:
commands: c++
linker: ld.bfd
name: gcc
version: 10.2.1
cython:
commands: cython
linker: cython
name: cython
version: 3.0.11
Machine Information:
build:
cpu: x86_64
endian: little
family: x86_64
system: linux
host:
cpu: x86_64
endian: little
family: x86_64
system: linux
Python Information:
path: /tmp/build-env-8744k94k/bin/python
version: '3.12'
SIMD Extensions:
baseline:
- SSE
- SSE2
- SSE3
found:
- SSSE3
- SSE41
- POPCNT
- SSE42
- AVX
- F16C
- FMA3
- AVX2
not found:
- AVX512F
- AVX512CD
- AVX512_KNL
- AVX512_KNM
- AVX512_SKX
- AVX512_CLX
- AVX512_CNL
- AVX512_ICL
NoneNumPy・SciPy・scikit-learnの関係¶
NumPy は多次元配列(ndarray)とその周辺演算を提供し,Pythonにおける科学技術計算の土台になるライブラリです.線形代数の基本は numpy.linalg にまとまっていますが,より専門的な数値計算の多くは SciPy に置かれています.
SciPy は独自の「SciPy 配列」型を別に定義するのではなく,NumPy の配列を入出力の共通フォーマットとして用います.疎行列(scipy.sparse),最適化,信号処理,統計,より高機能な線形代数などがここに集約され,多くの処理は内部で(NumPy と同様に)BLAS / LAPACK 相当のライブラリに依存します.
scikit-learn(パッケージ名 scikit-learn,インポート名 sklearn)は,古典的な機械学習アルゴリズムや前処理,モデル選択用のユーティリティを統一的な API で提供します.実装は NumPy 配列(および一部 scipy.sparse)を入力として受け取り,内部で NumPy や SciPy の数値ルーチンを組み合わせて動作します.三者の関係は,「NumPy がデータ表現と基本的な数値演算 → SciPy がより広い科学計算の部品 → scikit-learn がそれらを束ねた学習・予測のインタフェース」という階層と捉えると分かりやすいです.
依存関係として,SciPy は NumPy を前提とし,scikit-learn は NumPy と SciPy を前提とします.実務では,配列操作や特徴量の準備に NumPy,補間や疎行列,一部の距離・統計処理に scipy のサブモジュール,学習器の fit / predict と評価に sklearn のように役割を分けて使うことが多いです.
NumPyと機械学習・深層学習の開発が活発であることの関係¶
NumPy は単なる高速配列ライブラリにとどまらず,Python で機械学習や深層学習の開発が活発になるうえでの共通言語の役割を果たしています.ndarray の dtype やメモリレイアウト(連続配列など)が広く共有されているため,データの読み込み,前処理,可視化,古典的 ML,さらに深層学習フレームワークの境界まで,同じ型のまま受け渡ししやすいことが,ライブラリ間の連携コストを下げ,試作や比較実験を速く回せる土台になっています.
深層学習側では,GPU 上の計算用に PyTorch の Tensor や TensorFlow の tf.Tensor など,フレームワーク固有のテンソル型が中心です.それでも学習済み重みの保存,メトリクスの集計,データセットのオフライン加工などでは NumPy が使われ,多くの API は NumPy 配列との相互変換(例: PyTorch の .numpy() と torch.from_numpy,TensorFlow 2 の .numpy() や tf.convert_to_tensor など)を公式に用意しています.つまり「学習・推論のコアは各フレームワークのテンソル,その前後の科学計算やデータパイプラインは NumPy」という分担が定着しており,エコシステム全体の接続部として NumPy が機能していると言えます.
また,数値コアが C / Fortran 側にありつつ Python から短いコードで呼べるという性質は,研究やプロダクト双方で試行錯誤のサイクルを短くするのに寄与します.チュートリアルや書籍,論文の再現コードでも numpy が前提とされることが多く,初学者から実務者まで同じ表現で議論できることが,人材・知識の流動性を高め,結果として開発コミュニティの活発さを支えている一面もあります.
np.ndarrayの使い方¶
ndarray(N-dimensional array、N次元配列)の使い方を見ていきます.
np.ndarrayの作り方¶
np.array関数にリスト型オブジェクトを渡すと配列が作成できます.
import numpy as np
def object_checker(obj):
print("型:",type(obj))
print("中身:",obj)
if isinstance(obj, np.ndarray):
print("行列のshape:",obj.shape)list1 = [1,2,3]
object_checker(list1)型: <class 'list'>
中身: [1, 2, 3]
arr1 = np.array(list1)
object_checker(arr1)型: <class 'numpy.ndarray'>
中身: [1 2 3]
行列のshape: (3,)
list2 = [[1,2,3], [4,5,6]]
arr2 = np.array(list2)
object_checker(arr2)型: <class 'numpy.ndarray'>
中身: [[1 2 3]
[4 5 6]]
行列のshape: (2, 3)
numpy関数によるnp.ndarrayの初期化¶
numpyはPythonに配列(np.ndarray)を提供します。
このnp.arrayはリストやtupleを元に作るだけでなく、np.zeros、np.onesなどの関数からも作ることができます。
【問題1】 np.zerosを使って要素数10の配列を作ってください。¶
【問題2】 np.onesを使ってshape (10,5)の行列を作ってください。¶
【問題3】 np.arangeを使って0~9までの値を持ったベクトルを作ってください。¶
hint: np.arangeはrangeとほぼ同様の動作をします。
arr3 = np.arange(20)
arr4 = np.reshape(arr3, (10,2)) # arr3.reshape((10,2))でも可
print(
"オリジナル:\n",arr3,
"\nreshape後:\n",arr4
)オリジナル:
[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
reshape後:
[[ 0 1]
[ 2 3]
[ 4 5]
[ 6 7]
[ 8 9]
[10 11]
[12 13]
[14 15]
[16 17]
[18 19]]
【問題4】 ゼロで初期化した要素数30の配列を、5x2x3の三階テンソルに変換してください。¶
行列の転置¶
arr5 = arr4.T
arr5array([[ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18],
[ 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]])np.ndarrayの演算¶
numpy.ndarrayは高速な行列演算が可能です。
また、計算の結果は尤もらしい型になります。
arr1の要素はint32ですが、1.0などのfloat64と演算する事で、計算結果result1がfloat64として出力されます。
加算¶
print(result1 := arr1 +1.0)
# result1 = arr1 + 1.0
# print(result1)
# を省略して書くと print(result1 := arr1 +1.0) になる
print("arr1.dtype:",arr1.dtype)
print("result1.dtype:", result1.dtype)[2. 3. 4.]
arr1.dtype: int64
result1.dtype: float64
減算¶
print(result2 := result1 - 5.0)[-3. -2. -1.]
乗算¶
print(result3 := result2 * 1.5)[-4.5 -3. -1.5]
除算¶
print(result4 := result3 / 3.0)[-1.5 -1. -0.5]
剰余¶
print(result5 := result4 % 2)[0.5 1. 1.5]
【問題】result5の各要素を3乗してみよう.¶
等式¶
print(result6 := result5 ==1)[False True False]
不等式¶
print(result7 := result5 !=1)[ True False True]
print(result8 := result5 >=1)[False True True]
print(result9 := result5 <1)[ True False False]
要素が含まれているか確認する¶
1 in result5True2 in result5False1 not in result5False2 not in result5True【問題5】 以下の1~5までの処理をして答えを返す関数を作成して、実行してください。¶
1~20までの要素を持ったベクトルを用意
ベクトルをreshapeし2x10行列に変換
配列の各要素を20倍
配列の各要素のmod 3をとる
配列の0をTrue、それ以外をFalseに変換
5の答えをreturn
np.ones((10,5)) + np.arange(50).reshape((10,5))array([[ 1., 2., 3., 4., 5.],
[ 6., 7., 8., 9., 10.],
[11., 12., 13., 14., 15.],
[16., 17., 18., 19., 20.],
[21., 22., 23., 24., 25.],
[26., 27., 28., 29., 30.],
[31., 32., 33., 34., 35.],
[36., 37., 38., 39., 40.],
[41., 42., 43., 44., 45.],
[46., 47., 48., 49., 50.]])減算¶
np.array([10,9,8]) - np.ones(3)array([9., 8., 7.])乗算¶
np.array([[10,9,8],[11,91,81]]) * np.array([2,3,4])array([[ 20, 27, 32],
[ 22, 273, 324]])np.array([[10,9,8],[11,91,81]]) * np.array([[11,91,81],[10,9,8]]) array([[110, 819, 648],
[110, 819, 648]])除算¶
np.array([10,9,8]) / np.array([2,3,4])array([5., 3., 2.])剰余¶
np.array([10,9,8]) % np.array([3,4,5])array([1, 1, 3])【問題6】¶
以下の流れで演習用データを作成
10~19の要素を持つベクトルを用意
5x2配列にreshape
これを変数xに代入
15~24の要素を持つベクトルを用意
5x2配列にreshape
これを変数yに代入
この2つの配列に対して
(1) 加算
(2) 減算
(3) 乗算
(4) 除算
(5) 剰余
を計算
# (1)
# (2)
# (3)
# (4)
# (5)
等式と不等式¶
返り値はbool値(真偽値)の配列になります。
等式¶
np.array([5,7,9]) == np.array([3,7,11])array([False, True, False])不等式¶
np.array([5,7,9]) != np.array([3,7,11])array([ True, False, True])np.array([5,7,9]) >= np.array([3,7,11])array([ True, True, False])np.array([5,7,9]) < np.array([3,7,11])array([False, False, True])【問題7】 下の変数x,yについて¶
(1) 2つの変数で等しい養素をTrue, 等しくない養素をFalseにしたベクトルを出力
(2) x以上のyの養素をTrue, 未満をFalseにしたベクトルを出力
x = np.array([1,2,10,20])
y = np.array([5,-1,20,10])# (1)
# (2)
a = np.arange(0,10).reshape((2,5))
b = np.arange(0,15).reshape((5,3))
print("a:")
print(a)
print("b:")
print(b)
np.dot(a,b)a:
[[0 1 2 3 4]
[5 6 7 8 9]]
b:
[[ 0 1 2]
[ 3 4 5]
[ 6 7 8]
[ 9 10 11]
[12 13 14]]
array([[ 90, 100, 110],
[240, 275, 310]])外積 np.outer¶
c = a[0]
d = b[0]
print("c:",c,"d:",d)
np.outer(c,d)c: [0 1 2 3 4] d: [0 1 2]
array([[0, 0, 0],
[0, 1, 2],
[0, 2, 4],
[0, 3, 6],
[0, 4, 8]])np.random.seed(seed=1234)
# 入力データ
X = np.random.random((32, 20))
n_feature = X.shape[1]
n_hidden = 5
W = np.random.random((n_feature, n_hidden))
b = np.random.uniform(n_hidden)配列自体の操作¶
初期化して作成した配列に対して,インデクシング,reshape,転置,値の代入(上書き),スライスなどができます.
インデクシング(要素アクセス)¶
配列の要素にアクセスする方法はlistと同じです.
print(x := np.arange(20))
x[1][ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19]
np.int64(1)Booleanマスク¶
元の配列と同じ形状の配列を使うことで,必要な要素だけを取り出すことができる.
xarray([[ 100, 1000, 2, 3, 4],
[ 5, 1000, 7, 8, 9],
[ 10, 1000, 12, 13, 14],
[1000, 1001, 1002, 1003, 1004]])mask = np.array([[True, False, False, False, False],
[False, True, False, False, False],
[False, False, True, False, False],
[False, False, False, True, False],]
)
x[mask]array([ 100, 1000, 12, 1003])条件式を使えばブールマスクが作れる.
mask = x > 10
maskarray([[ True, True, False, False, False],
[False, True, False, False, False],
[False, True, True, True, True],
[ True, True, True, True, True]])x[mask]array([ 100, 1000, 1000, 1000, 12, 13, 14, 1000, 1001, 1002, 1003,
1004])つまり[]の中に直接条件式を書けばOK
x[x<100]array([ 2, 3, 4, 5, 7, 8, 9, 10, 12, 13, 14])reshape¶
行列の形を変える
print(x := x.reshape((4,5)))
print("形状:", x.shape)[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]]
形状: (4, 5)
print(x2 := x.astype(np.float16))
print("型:", x2.dtype)[[ 0. 1. 2. 3. 4.]
[ 5. 6. 7. 8. 9.]
[10. 11. 12. 13. 14.]
[15. 16. 17. 18. 19.]]
型: float16
【問題9】以下の処理を実装せよ.¶
100から119までの要素を持ったベクトルaを作る
aを2x10行列に変形
aを単精度浮動小数点数にキャスト変換
転置¶
print(x2 := x.T)
print("形状:", x2.shape)[[ 0 5 10 15]
[ 1 6 11 16]
[ 2 7 12 17]
[ 3 8 13 18]
[ 4 9 14 19]]
形状: (5, 4)
値の代入¶
特定要素を置き換え
x2[0,0] = 100
x2array([[100, 5, 10, 15],
[ 1, 6, 11, 16],
[ 2, 7, 12, 17],
[ 3, 8, 13, 18],
[ 4, 9, 14, 19]])配列の1行目を全て上書き (インデックスは0スタートであることに注意)
上書きしたい要素数と同じ要素数の配列を使えば,その通りに上書きできる.
x2[1] = np.arange(100,104)
x2array([[100, 5, 10, 15],
[100, 101, 102, 103],
[ 2, 7, 12, 17],
[ 3, 8, 13, 18],
[ 4, 9, 14, 19]])上書きしたい要素数に関係なく,値を一つだけ使えば,それで全ての要素を上書きできる.
x2[1] = 1000
x2array([[ 100, 5, 10, 15],
[1000, 1000, 1000, 1000],
[ 2, 7, 12, 17],
[ 3, 8, 13, 18],
[ 4, 9, 14, 19]])最後の列だけを上書き
x2[:,-1] = np.arange(1000,1005)
x2array([[ 100, 5, 10, 1000],
[1000, 1000, 1000, 1001],
[ 2, 7, 12, 1002],
[ 3, 8, 13, 1003],
[ 4, 9, 14, 1004]])【問題10】¶
x2の0行3列目の要素を99に上書き
スライス¶
複数の行をスライス
x2[0:2]array([[ 100, 5, 10, 1000],
[1000, 1000, 1000, 1001]])複数の列をスライス
x2[:,1:3]array([[ 5, 10],
[1000, 1000],
[ 7, 12],
[ 8, 13],
[ 9, 14]])【問題11】¶
x2から2~3行目の3列目をスライス