Elixir 的快速矩陣操作庫以 C 本機程式碼實現,具有用於矩陣乘法的高度最佳化的 CBLAS sgemm()。
例如,向量化線性迴歸比 Octave 單執行緒實現快約 13 倍。
它的記憶體效率也很高,因此您可以處理大型矩陣,大約有十億個元素。
基於 https://gitlab.com/sdwolfz/experimental/-/tree/master/exlearn 的矩陣程式碼
2015 年 MacBook Pro,2.2 GHz 酷睿 i7,16 GB 內存
對填滿隨機數的 3000×3000 矩陣執行操作。
您可以使用python numpy_bench.py
和MIX_ENV=bench mix bench
命令從/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)
確實是屠殺無辜。
2015 年 MacBook Pro,2.2 GHz 酷睿 i7,16 GB 內存
500×500 矩陣的點積
圖書館 | 操作數/秒 | 與Matrex相比 |
---|---|---|
矩陣 | 674.70 | |
矩陣 | 0.0923 | 7 312.62× 較慢 |
努梅克西 | 0.0173 | 38 906.14× 較慢 |
矩陣 | 0.0129 | 52 327.40× 較慢 |
3×3 矩陣的點積
圖書館 | 操作數/秒 | 與Matrex相比 |
---|---|---|
矩陣 | 3624.36K | |
圖數學 | 1310.16K | 慢 2.77 倍 |
矩陣 | 372.58K | 慢 9.73 倍 |
努梅克西 | 89.72K | 慢 40.40 倍 |
矩陣 | 35.76K | 慢 101.35 倍 |
轉置 1000x1000 矩陣
圖書館 | 操作數/秒 | 與Matrex相比 |
---|---|---|
矩陣 | 428.69 | |
矩陣 | 9.39 | 45.64× 慢 |
矩陣 | 8.54 | 慢 50.17 倍 |
努梅克西 | 6.83 | 62.80× 慢 |
Matrex 函式庫的完整範例:MNIST 數字上的線性迴歸(Jupyter 筆記本)
Matrex 實作了Inspect
協議,並且在控制台中看起來不錯:
它甚至可以在控制台中繪製矩陣的熱圖!這是使用 Matrex 函式庫和一些矩陣熱圖進行邏輯迴歸訓練的動畫:
可以透過將matrex
新增至mix.exs
中的依賴項清單來安裝該套件:
def deps do
[
{ :matrex , "~> 0.6" }
]
end
由於 Accelerate 框架,一切都可以開箱即用。如果遇到編譯錯誤
native/src/matrix_dot.c:5:10: fatal error: 'cblas.h' file not found
然後確保安裝了 XCode 命令列工具 ( xcode-select --install
)。如果錯誤仍未解決,對於 MacOS Mojave,請執行open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg
以恢復 /usr/include 和 /usr/lib。
在 MacOS 10.15 上,可以透過以下方式解決此錯誤
export CPATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/
或與
C_INCLUDE_PATH=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Accelerate.framework/Frameworks/vecLib.framework/Headers mix compile
您需要安裝科學庫才能編譯此套件:
> sudo apt-get install build-essential erlang-dev libatlas-base-dev
它肯定可以在 Windows 上運行,但我們需要 makefile 和安裝說明。請貢獻一下。
在MATREX_BLAS
環境變數的幫助下,您可以選擇要連結的 BLAS 庫。它可以採用值blas
(預設值)、 atlas
、 openblas
或noblas
。
最後一個選項意味著您可以在沒有任何外部依賴的情況下編譯 C 程式碼,因此,它應該可以在任何有 C 編譯器的地方工作:
$ mix clean
$ MATREX_BLAS=noblas mix compile
Matrex 部分實現了存取行為,因此您可以執行以下操作:
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
或甚至:
iex > m [ 1 .. 2 ]
#Matrex[2×3]
┌ ┐
│ 8.0 1.0 6.0 │
│ 3.0 5.0 7.0 │
└ ┘
要取得矩陣維數還有幾種快捷方式:
iex > m [ :rows ]
3
iex > m [ :size ]
{ 3 , 3 }
計算整個矩陣的最大值:
iex > m [ :max ]
9.0
或者只是其中一行:
iex > m [ 2 ] [ :max ]
7.0
計算整個矩陣的最大元素的從一開始的索引:
iex > m [ :argmax ]
8
和一行:
iex > m [ 2 ] [ :argmax ]
3
Matrex.Operators
模組重新定義了Kernel
數學運算子(+、-、*、/<|>)並定義了一些方便的函數,因此您可以以更自然的方式編寫計算程式碼。
應非常謹慎地使用它。我們建議僅在特定函數內使用它,並且只是為了提高可讀性,因為使用Matrex
模組函數,尤其是一次呼叫執行兩個或更多操作的函數,速度會快 2-3 倍。
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
相同的函數,使用模組方法呼叫編碼(快 2.5 倍):
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 實作了Enumerable
,因此,各種Enum
函數都適用:
iex > Enum . member? ( m , 2.0 )
true
iex > Enum . count ( m )
9
iex > Enum . sum ( m )
45
對於同時存在於Enum
和Matrex
中的函數,最好使用 Matrex 版本,因為它通常要快得多。即,對於 1 000 x 1 000 矩陣, Matrex.sum/1
和Matrex.to_list/1
分別比其Enum
對應項快 438 倍和 41 倍。
您可以使用本機二進位檔案格式(超快)和 CSV(慢,特別是在大型矩陣上)儲存/載入矩陣。
Matrex CSV 格式與 GNU Octave CSV 輸出相容,因此您可以使用它在兩個系統之間交換資料。
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 │
└ ┘
浮點特殊值,例如:nan
和:inf
很好地存在於矩陣內,可以從檔案載入或儲存到檔案中。但是當它們被放入 Elixir 時,它們會被轉移到:nan
、 :inf
和:neg_inf
原子,因為 BEAM 不接受特殊值作為有效浮點數。
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 )