Pustaka manipulasi matriks cepat untuk Elixir diimplementasikan dalam kode asli C dengan CBLAS sgemm() yang sangat optimal digunakan untuk perkalian matriks.
Misalnya, regresi linier tervektorisasi sekitar 13 kali lebih cepat dibandingkan implementasi thread tunggal Oktaf.
Ini juga hemat memori, sehingga Anda dapat bekerja dengan matriks besar, berukuran sekitar miliaran elemen.
Berdasarkan kode matriks dari https://gitlab.com/sdwolfz/experimental/-/tree/master/exlearn
MacBook Pro 2015, Core i7 2,2 GHz, RAM 16 GB
Operasi dilakukan pada matriks 3000×3000 yang diisi dengan bilangan acak.
Anda dapat menjalankan benchmark dari folder /bench
dengan perintah python numpy_bench.py
dan MIX_ENV=bench mix bench
.
benchmark iterations average time
logistic_cost() 1000 1.23 ms/op
np.divide(A, B) 100 15.43 ms/op
np.add(A, B) 100 14.62 ms/op
sigmoid(A) 50 93.28 ms/op
np.dot(A, B) 10 196.57 ms/op
benchmark iterations average time
logistic_cost() 1000 1.23 ms/op (on par)
divide(A, B) 200 7.32 ms/op (~ 2× faster)
add(A, B) 200 7.71 ms/op (~ 2× faster)
sigmoid(A) 50 71.47 ms/op (23% faster)
dot(A, B) 10 213.31 ms/op (8% slower)
Sebenarnya, pembantaian orang yang tidak bersalah.
MacBook Pro 2015, Core i7 2,2 GHz, RAM 16 GB
Perkalian titik dari matriks 500×500
Perpustakaan | Operasi/dtk | Dibandingkan dengan Matrex |
---|---|---|
matriks | 674,70 | |
Matriks | 0,0923 | 7 312,62× lebih lambat |
Numerik | 0,0173 | 38 906.14× lebih lambat |
ExMatrix | 0,0129 | 52 327,40× lebih lambat |
Perkalian titik dari matriks 3×3
Perpustakaan | Operasi/dtk | Dibandingkan dengan Matrex |
---|---|---|
matriks | 3624.36K | |
GrafikMatematika | 1310.16 K | 2,77x lebih lambat |
Matriks | 372,58 K | 9,73x lebih lambat |
Numerik | 89,72 K | 40,40x lebih lambat |
ExMatrix | 35,76 K | 101,35x lebih lambat |
Transposisi matriks 1000x1000
Perpustakaan | Operasi/dtk | Dibandingkan dengan Matrex |
---|---|---|
matriks | 428.69 | |
ExMatrix | 9.39 | 45,64× lebih lambat |
Matriks | 8.54 | 50,17× lebih lambat |
Numerik | 6.83 | 62,80× lebih lambat |
Contoh lengkap perpustakaan Matrex yang berfungsi: Regresi linier pada digit MNIST (buku catatan Jupyter)
Matrex mengimplementasikan protokol Inspect
dan terlihat bagus di konsol Anda:
Ia bahkan dapat menggambar peta panas matriks Anda di konsol! Berikut adalah animasi pelatihan regresi logistik dengan perpustakaan Matrex dan beberapa peta panas matriks:
Paket ini dapat diinstal dengan menambahkan matrex
ke daftar dependensi Anda di mix.exs
:
def deps do
[
{ :matrex , "~> 0.6" }
]
end
Semuanya berjalan dengan baik, berkat kerangka kerja Accelerate. Jika Anda mengalami kesalahan kompilasi
native/src/matrix_dot.c:5:10: fatal error: 'cblas.h' file not found
lalu pastikan alat baris perintah XCode diinstal ( xcode-select --install
). Jika kesalahan masih belum teratasi, untuk MacOS Mojave, jalankan open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg
untuk memulihkan /usr/include dan /usr/lib.
Pada MacOS 10.15 kesalahan ini dapat diatasi dengan
export CPATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/
atau dengan
C_INCLUDE_PATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/Headers mix compile
Anda perlu menginstal perpustakaan ilmiah agar paket ini dapat dikompilasi:
> sudo apt-get install build-essential erlang-dev libatlas-base-dev
Ini pasti akan berfungsi di Windows, tetapi kita memerlukan makefile dan instruksi instalasi. Tolong, berkontribusi.
Dengan bantuan variabel lingkungan MATREX_BLAS
Anda dapat memilih perpustakaan BLAS mana yang akan ditautkan. Ini dapat mengambil nilai blas
(default), atlas
, openblas
atau noblas
.
Opsi terakhir berarti Anda mengkompilasi kode C tanpa ketergantungan eksternal apa pun, jadi, kode tersebut dapat berfungsi di mana saja dengan tempat kompiler C:
$ mix clean
$ MATREX_BLAS=noblas mix compile
Perilaku akses sebagian diterapkan untuk Matrex, sehingga Anda dapat melakukan:
iex > m = Matrex . magic ( 3 )
#Matrex[3×3]
┌ ┐
│ 8.0 1.0 6.0 │
│ 3.0 5.0 7.0 │
│ 4.0 9.0 2.0 │
└ ┘
iex > m [ 2 ] [ 3 ]
7.0
Atau bahkan:
iex > m [ 1 .. 2 ]
#Matrex[2×3]
┌ ┐
│ 8.0 1.0 6.0 │
│ 3.0 5.0 7.0 │
└ ┘
Ada juga beberapa jalan pintas untuk mendapatkan dimensi matriks:
iex > m [ :rows ]
3
iex > m [ :size ]
{ 3 , 3 }
menghitung nilai maksimum seluruh matriks:
iex > m [ :max ]
9.0
atau hanya salah satu barisnya:
iex > m [ 2 ] [ :max ]
7.0
menghitung indeks berbasis satu elemen maksimum untuk seluruh matriks:
iex > m [ :argmax ]
8
dan berturut-turut:
iex > m [ 2 ] [ :argmax ]
3
Modul Matrex.Operators
mendefinisikan ulang operator matematika Kernel
(+, -, *, / <|>) dan mendefinisikan beberapa fungsi praktis, sehingga Anda dapat menulis kode penghitungan dengan cara yang lebih alami.
Ini harus digunakan dengan sangat hati-hati. Kami menyarankan untuk menggunakannya hanya di dalam fungsi tertentu dan hanya untuk meningkatkan keterbacaan, karena penggunaan fungsi modul Matrex
, terutama yang melakukan dua atau lebih operasi dalam satu panggilan, 2-3 kali lebih cepat.
def lr_cost_fun_ops ( % Matrex { } = theta , { % Matrex { } = x , % Matrex { } = y , lambda } = _params )
when is_number ( lambda ) do
# Turn off original operators
import Kernel , except: [ -: 1 , +: 2 , -: 2 , *: 2 , /: 2 , <|>: 2 ]
import Matrex.Operators
import Matrex
m = y [ :rows ]
h = sigmoid ( x * theta )
l = ones ( size ( theta ) ) |> set ( 1 , 1 , 0.0 )
j = ( - t ( y ) * log ( h ) - t ( 1 - y ) * log ( 1 - h ) + lambda / 2 * t ( l ) * pow2 ( theta ) ) / m
grad = ( t ( x ) * ( h - y ) + ( theta <|> l ) * lambda ) / m
{ scalar ( j ) , grad }
end
Fungsi yang sama, dikodekan dengan panggilan metode modul (2,5 kali lebih cepat):
def lr_cost_fun ( % Matrex { } = theta , { % Matrex { } = x , % Matrex { } = y , lambda } = _params )
when is_number ( lambda ) do
m = y [ :rows ]
h = Matrex . dot_and_apply ( x , theta , :sigmoid )
l = Matrex . ones ( theta [ :rows ] , theta [ :cols ] ) |> Matrex . set ( 1 , 1 , 0 )
regularization =
Matrex . dot_tn ( l , Matrex . square ( theta ) )
|> Matrex . scalar ( )
|> Kernel . * ( lambda / ( 2 * m ) )
j =
y
|> Matrex . dot_tn ( Matrex . apply ( h , :log ) , - 1 )
|> Matrex . subtract (
Matrex . dot_tn (
Matrex . subtract ( 1 , y ) ,
Matrex . apply ( Matrex . subtract ( 1 , h ) , :log )
)
)
|> Matrex . scalar ( )
|> ( fn
:nan -> :nan
x -> x / m + regularization
end ) . ( )
grad =
x
|> Matrex . dot_tn ( Matrex . subtract ( h , y ) )
|> Matrex . add ( Matrex . multiply ( theta , l ) , 1.0 , lambda )
|> Matrex . divide ( m )
{ j , grad }
end
Matrex mengimplementasikan Enumerable
, jadi, semua jenis fungsi Enum
dapat diterapkan:
iex > Enum . member? ( m , 2.0 )
true
iex > Enum . count ( m )
9
iex > Enum . sum ( m )
45
Untuk fungsi-fungsi yang ada di Enum
dan Matrex
lebih disukai menggunakan versi Matrex, karena biasanya jauh lebih cepat. Yaitu, untuk matriks 1.000 x 1.000 Matrex.sum/1
dan Matrex.to_list/1
masing-masing 438 dan 41 kali lebih cepat, dibandingkan rekan-rekan Enum
.
Anda dapat menyimpan/memuat matriks dengan format file biner asli (ekstra cepat) dan CSV (lambat, terutama pada matriks besar).
Format Matrex CSV kompatibel dengan keluaran GNU Octave CSV, sehingga Anda dapat menggunakannya untuk bertukar data antara dua sistem.
iex > Matrex . random ( 5 ) |> Matrex . save ( "rand.mtx" )
:ok
iex > Matrex . load ( "rand.mtx" )
#Matrex[5×5]
┌ ┐
│ 0.05624 0.78819 0.29995 0.25654 0.94082 │
│ 0.50225 0.22923 0.31941 0.3329 0.78058 │
│ 0.81769 0.66448 0.97414 0.08146 0.21654 │
│ 0.33411 0.59648 0.24786 0.27596 0.09082 │
│ 0.18673 0.18699 0.79753 0.08101 0.47516 │
└ ┘
iex > Matrex . magic ( 5 ) |> Matrex . divide ( Matrex . eye ( 5 ) ) |> Matrex . save ( "nan.csv" )
:ok
iex > Matrex . load ( "nan.csv" )
#Matrex[5×5]
┌ ┐
│ 16.0 ∞ ∞ ∞ ∞ │
│ ∞ 4.0 ∞ ∞ ∞ │
│ ∞ ∞ 12.0 ∞ ∞ │
│ ∞ ∞ ∞ 25.0 ∞ │
│ ∞ ∞ ∞ ∞ 8.0 │
└ ┘
Nilai khusus float, seperti :nan
dan :inf
hidup dengan baik di dalam matriks, dapat dimuat dari dan disimpan ke file. Namun ketika memasukkannya ke dalam Elixir, mereka dipindahkan ke atom :nan
, :inf
dan :neg_inf
, karena BEAM tidak menerima nilai khusus sebagai float yang valid.
iex > m = Matrex . eye ( 3 )
#Matrex[3×3]
┌ ┐
│ 1.0 0.0 0.0 │
│ 0.0 1.0 0.0 │
│ 0.0 0.0 1.0 │
└ ┘
iex > n = Matrex . divide ( m , Matrex . zeros ( 3 ) )
#Matrex[3×3]
┌ ┐
│ ∞ NaN NaN │
│ NaN ∞ NaN │
│ NaN NaN ∞ │
└ ┘
iex > n [ 1 ] [ 1 ]
:inf
iex > n [ 1 ] [ 2 ]
:nan
iex ( 166 ) > matrex_logo =
... ( 166 ) > "../emnist/emnist-letters-test-images-idx3-ubyte"
.. . ( 166 ) > |> Matrex . load ( :idx )
.. . ( 166 ) > |> Access . get ( 9601 .. 10200 )
.. . ( 166 ) > |> Matrex . list_of_rows ( )
.. . ( 166 ) > |> Enum . reduce ( fn x , sum -> add ( x , sum ) end )
.. . ( 166 ) > |> Matrex . reshape ( 28 , 28 )
.. . ( 166 ) > |> Matrex . transpose ( )
.. . ( 166 ) > |> Matrex . resize ( 2 )
.. . ( 166 ) > |> Matrex . heatmap ( :color24bit )