IBM推出的張量計算庫Ocean Tensor Library

 

 

矩陣和張量運算構成了廣泛領域和應用的基礎,並且在許多情況下構成了整體計算複雜性的重要部分。通用gpu能夠加速其中許多操作並使其他操作成爲可能,這導致了這些設備的廣泛適應性。爲了充分利用張量運算的計算能力,需要專門的軟件,目前有幾個包(主要是在深度學習領域)包含了CPU和GPU上的張量運算。然而,支持一般張量運算的獨立框架仍然缺失。在本文中,我們填補了這一空白,並提出了海洋張量庫:一個模塊化的張量支持包,它被設計爲在各種設備類型上需要密集張量操作的應用程序的基礎層。API經過精心設計,功能強大,可擴展,同時易於使用。這個包是開源的。

 

1簡介

在過去十年左右的時間裏,通用GPU已成功應用於醫學成像[14,17]、分子動力學模擬[9]、射電天文學[3]、數據挖掘[4]、圖形處理[15]和許多其他領域[10]。最近,gpu已經廣泛應用於深度學習,並在很大程度上實現了深度學習。與其他領域相比,深層次學習的軟件包可能更多,如Caffe[8]、Torch[7]和Tensorflow[1]。其中一個原因是在深入學習中需要靈活性,以便嘗試不同的網絡架構和數據轉換,以獲得最佳可能的模型。考慮到數據、節點參數和中間結果可以方便地以多維數組或張量的形式表示,這些包已經逐漸向通用計算環境轉變。儘管取得了這些進展,但仍有很大的改進空間:現有的軟件包往往是單一的,需要大量的外部依賴,並且往往缺乏張量佈局靈活性、支持的數據類型或對新設備類型的擴展。最重要的是,一個獨立的張量支持包被設計爲廣泛的其他應用程序的基礎仍然缺失。爲了填補這一空白,解決現有封裝的一些缺點,我們提出了海洋張量包(Ocean Tensor Library),一個模塊化的開源基礎庫用於密集張量操作。

我們在第二節描述了海洋張量包(

簡稱海洋Ocean)的設計和實現,並在第三節強調了它的一些獨特特性。在第四節中,我們將查看現有的包,並將它們與海洋進行對比。我們在第5節中提供了一個使用海洋的示例,並在第6節中進行了總結。

 

2設計和實現

海洋張量(Ocean Tensor Library)包被設計成一個基礎層,用於需要在一個或多個設備類型和實例上進行密集張量操作的應用程序。考慮到廣泛的潛在應用和領域,重要的是將張量運算分組到相干模塊中,而不是通過一個巨大的單片封裝來提供。這樣,用戶可以在需要時安裝功能,這有助於減少依賴項的有效數量。另一個優點是,與外部庫的接口和兼容性被本地化爲獨立的模塊,從而使包更易於管理。海洋中使用的另一個設計原則(本節稍後將討論)是使用定義良好的層。

 

2.1 模塊化

Ocean中的模塊化模塊包括一個接口以及每個受支持設備類型的獨立實現。模塊接口負責與設備無關的參數檢查,包括張量的有效性和張量維度的兼容性。然後,它確定要使用的數據類型和設備,併爲與設備類型關聯的模塊查詢函數查找表。如果可用,則在執行所有必需的類型轉換、廣播以及結果張量和中間張量的分配(例如,當張量在內存中重疊時)之後調用該函數。如果函數不可用,或者尚未加載設備類型的模塊實現,則會引發錯誤。設備級的函數通常只需要檢查對給定數據類型的張量的支持,或者實現張量操作,或者更典型地,調用提供所需操作的低級庫函數。

如果需要,函數可以訪問與每個設備實例關聯的特定於模塊的上下文信息。

模塊接口和設備實現可以單獨加載,但核心模塊接口除外,核心模塊接口包括CPU實現。接口和實現之間的分離使得可以用替代方案來替換模塊實現,例如高度優化或專用的專有版本。使用函數表還可以替換單個函數進行性能比較或調試,或者插入記錄運行時或累積調用統計信息的函數。模塊接口和設備實現之間的分離也使得使用新的設備類型擴展海洋變得容易。

特別是,每個模塊中的模塊和功能可以一次添加和測試一個,從而避免了開始時的巨大開發工作。

核心模塊構成了海洋張量包的基礎。它提供所有基本的張量操作,並實例化和公開可用的設備實例。許多標準功能,例如不同設備類型之間的打印和張量複製,都需要CPU上的張量支持,因此核心模塊接口與CPU實現(pyOcean_CPU)相結合。GPU實現可以通過導入pyOcean_GPU單獨加載。

爲了方便起見,這兩個包裹都是通過海運進口的。允許模塊之間的依賴關係,並且一個模塊的實例化可以導致加載其他模塊。仔細註冊模塊的實例化順序,以確保只有當沒有其他模塊依賴於模塊時,才能最終確定模塊。

 

2.2分層實施

在Ocean Tensor包的實現中,注意保持不同抽象級別之間的乾淨分離,如圖1所示。底層的庫提供獨立於張量表示的低級張量操作。這包括現有的庫,如BLAS和CUBLAS,以及定製開發的堅實基礎庫,它爲CPU和GPU提供基本張量函數。這個級別的庫不是特定於海洋的,可以由需要低級別張量運算的其他應用程序獨立使用。海洋張量API定義了一個統一的張量類型以及各種張量操作,它們被組織爲模塊。如上所述,模塊本身可以分爲兩層,即接口和設備實現。海洋API是用C語言實現的,以最大限度地提高其他語言的可訪問性。圖1中的頂層顯示了可能綁定到Ocean的語言(當前版本只實現對Python的支持)。
但是,API的使用不限於語言綁定。例如,使用張量運算的應用程序,或者支持符號張量計算圖的庫,也可以構建在海洋張量API之上。

 

3功能

Ocean Tensor軟件包[2]爲CPU和GPU提供了一套全面的張量操作。 這些函數可以直接作爲C庫使用,也可以通過易於使用的Python界面使用。 在本節中,我們將探索該軟件包的某些功能,並以基於Python接口的代碼摘錄進行說明。

3.1對象類型

Ocean Tensor Package的用戶界面公開了幾種對象類型,如圖2所示。在對象層次結構的頂部是Tensors和Scalars,它們每個都有給定的數據類型。 張量是存儲爲存儲對象的連續內存塊的視圖。 與每個存儲對象相關聯的是一個流,該流用於調度異步操作以及維護流間依存關係。 流對象本身與特定設備類型(例如CPU或GPU)的設備實例(例如CPU或GPU#0)關聯。

3.1.1設備

設備對象使實例化張量或存儲對象時可以使用的設備規範。 此外,它們還提供給定設備的一般信息,例如支持字節交換數據或所有當前已加載模塊的列表。 根據設備類型,可能會提供其他信息。 例如,在GPU設備上,可以查詢許多屬性,包括多處理器計數或當前可用的空閒內存。 高級功能包括實例化新流,指定設備的中間張量緩衝區的數量及其最大大小。 Ocean維護可用設備的列表,包括ocean.cpu設備以及GPU設備的ocean.gpu列表,可以對它們進行索引以獲得所需的設備實例。

3.1.2存儲

存儲對象封裝了一塊連續的內存,該內存可以動態分配,也可以由外部源提供。 與存儲關聯的數據類型有兩個主要目的:首先,當在不提供類型的情況下從存儲實例化張量時,將其用作默認數據類型; 第二,格式化存儲元件以供顯示。 存儲對象的數據類型可以自由更改,而不會影響使用它的張量。 可以在同一存儲上疊加不同數據類型的張量。 發生這種情況的一個典型示例是查詢複雜的雙精度張量的虛部時,這會導致在double類型的存儲中產生一個額外的張量視圖。 可以共享同一存儲的不同張量類型的數量沒有限制。 張量操作使用存儲流進行同步,以避免競爭情況和數據不一致。

可以將存儲數據標記爲只讀,這可以防止直接或通過張量操作對數據進行任何更新(將存儲標記爲只讀反映在所有派生的張量中)。

​​​​​​​3.1.3張量

海洋張量易於在任何可用設備上實例化。 例如,在設備gpu [0]上創建具有單精度浮點格式的3×4張量只需編寫:tensor = ocean.tensor([3,4],ocean.float,ocean.gpu [ 0])。 省略數據類型或設備時,將使用用戶指定的默認值。 與稍後在第4節中介紹的某些其他程序包相比,沒有當前活動設備的概念,也不需要進行顯式設備更改即可實例化新張量或對其執行操作。 默認情況下,張量遵循列爲主的內存佈局,但支持常規跨度(以字節爲單位),從而允許與大多數Numpy張量兼容。 Numpy [12]的兩個區別是(1)支持Ocean中複雜的半精度數據類型,以及支持Numpy中的其他數據類型(例如字符串和日期時間),以及(2)張量維數的最大值。 目前,Ocean中最大張量維數設置爲8,但是此限制很容易放鬆或消除。 Numpy的硬編碼最大張量爲32。 與Numpy相似,Ocean允許CPU上的張量具有小端字節順序和大端字節順序,並且張量操作可以按任一字節順序進行。 如果需要,可以通過字節交換元素或在標誌和實際字節順序不匹配的情況下,通過簡單地指定適當的字節順序來輕鬆更改字節順序。

3.2 Tensor操作

如第2節中所述,Ocean中的Tensor操作是通過模塊提供的。Core模塊構成了Ocean的基礎,並且包括基本對象類以及設備實例的定義。 作爲最基本的操作,Core模塊支持張量創建。 帶有或不帶有初始化的數據,來自存儲以及嵌套列表,序列和其他張量類型形式的數據的數據。 除了創建張量,Core模塊還提供了廣泛的基本功能集,包括用於形狀和軸操作,索引,複製功能,類型和設備轉換,基本算術運算,三角運算的功能(在所有實數和複數浮點上均受支持) 類型),以及沿一個或多個軸的張量縮減。 (可以在Ocean Tensor Package存儲庫[2]中找到功能的完整列表。)下面,我們重點介紹一些功能。

​​​​​​​3.2.1類型轉換

張量的類型可以看作是數據類型和與張量關聯的設備的組合。 海洋中的張量具有關聯的類型,因此有時可能需要類型轉換。 顯式類型轉換可以使用ocean.cast函數(僅返回所請求類型的張量的副本)和ocean.ensure函數(僅當所請求的類型與輸入的類型不同時才返回類型轉換的張量)使用。 使用數據類型(ocean.float(T))或設備實例(ocean.gpu [1](T))進行類型轉換等效於僅使用數據類型或設備更新來調用ensure函數。

Ocean中的隱式類型轉換用於確保張量操作的輸入參數具有適當的類型和字節順序。 例如考慮張量加法:C = A + B。 爲了避免對所有可能的類型組合實施加法運算,我們需要根據A和B的類型確定C的類型,並相應地對數據類型和設備進行標準化。 解決設備類型的一種方法是強加設備訂購併選擇優先級最高的設備。 這要求對命令進行規範,並且肯定會導致意外結果,當然,當可以從代碼的其他部分更改設備命令時也是如此。 另一種方法是始終從操作員的左側或參數列表中的第一個參數使用設備。

在A + = B中,很明顯B應該被強制到A的設備,因此我們對A + B進行相同的操作。 如果希望使用B的設備(即B.device),則在這種情況下可以寫B + A或使用顯式強制轉換:ocean.ensure(A,B.device)+ B或 只需B.device(A)+ B

對於隱式轉換數據類型,我們遵循Numpy並使用可以保留兩種數據類型的最小可用數據類型。 例如,將有符號和無符號8位整數相加會得到16位有符號整數。 由於沒有標準數據類型可用於四精度浮點數,因此對於64位整數和浮點數會產生異常,這會導致雙精度浮點數。 默認情況下,Ocean上的自動類型轉換默認是打開的,但是如果需要嚴格的類型檢查,則可以由用戶禁用。 關閉時,只要遇到類型不匹配,就會引發異常。

例如,當取負實數的平方根或大小大於1的標量的反餘弦時,基於張量的內容進行類型轉換是理想的。

這樣的運算是否應該導致非數字(NaN)值,返回複數值結果或產生錯誤? Ocean中採用的方法是向此類運算符添加指示計算模式的參數。 在標準模式下,無需對張量元素進行檢查,並且在需要時會生成NaN值。 在警告和錯誤模式下進行檢查,當遇到值超出操作員域的元素時,分別給出警告或錯誤。 最後,在複雜模式下,進行檢查以確定是否結果數據類型應爲實數或複數。 如果需要,可以始終使用顯式類型轉換。

​​​​​​​3.2.2索引

Ocean支持沿一個或多個維度的多種索引模式,這些模式可以組合以對張量進行索引。 基本的一維分度模式是:(1)標量,沿軸分度單個元素; (2)範圍,以索引規則排列的元素集; (3)冒號“:”運算符表示整個尺寸。 除了基本模式外,還可以使用一維或二維索引數組來選擇特定元素,方法是沿一維指定索引,或者沿多個維指定索引元組。 按照Python的慣例,可以使用負索引來指示相對於維度末端的索引。 最後,布爾張量可以用作索引的掩碼,其中非零元素表示要選擇的元素。 索引中省略的維將使用冒號運算符進行隱式索引,並且省略號對象“ ...”可能會出現一次,以指示在該位置應用零個或多個冒號運算符來完成索引。 僅使用基本索引模式(顯式或隱式)對張量進行索引時,該張量的視圖以共享原始存儲的新張量的形式返回。 在所有其他情況下,通過複製索引元素來創建新的張量

索引數組和布爾掩碼需要特殊的預處理:對於索引數組,需要檢查索引的有效性; 對於布爾型蒙版,必須計算所選元素的數量,以確定輸出張量的大小; 並將所選擇的索引轉換爲相對偏移量到被索引張量的數據緩衝區中。 當這樣的索引被重複使用時,爲每次使用應用相同的預處理步驟就浪費了計算工作。 爲了避免這種情況,Ocean引入了索引對象,該索引對象是通過對ocean.index對象建立索引來構造的(在常規函數調用中,不允許將range和冒號參數作爲參數使用)。 一旦創建了索引對象,就可以將其綁定到張量大小以轉換負索引,檢查索引的有效性,確定索引範圍與給定維的重疊,以及將布爾掩碼轉換爲顯式索引。 索引對象可以隨後(或直接)綁定到張量跨度,後者將所有索引數組和布爾掩碼轉換爲張量數據內的相對偏移量。 綁定索引對象和未綁定索引對象都可以以與用於構造它的索引模式完全相同的方式使用。 這樣,如果需要,可以使用索引對象來創建其他索引對象。 當索引對象綁定到大小時,相關的張量尺寸必須匹配,並且跨度也必須匹配。

​​​​​​​3.2.3互操作性

Ocean的Python接口提供了插件模塊,用於定義張量和標量的外部對象類型,以及在這些對象和相應的Ocean類型之間進行轉換。 解析張量操作參數時,會將插件提供的所有擴展對象類型進行比較。 這使它們的使用方式與洋張量和標量基本相同。 例如,我們可以通過導入pyOceanNumpy來聲明Numpy張量和標量類型。 一旦完成,就可以編寫諸如A + np.asarray([4,5,6])的表達式,其中A是一個海洋張量。 可以使用A.convertTo('numpy')轉換爲Numpy,其中'numpy'字符串由插件註冊。 當受外部張量類型支持時,將生成張量的淺表副本,除非用戶另有要求。

​​​​​​​3.2.4解除內存分配

Python中的自動垃圾收集可以延遲張量對象的刪除,並導致設備耗盡可用內存,儘管用戶進行了認真的管理。 爲了強制刪除張量,可以調用dealloc張量函數,該函數維護Python張量對象,但將內容替換爲空張量。 這樣可以釋放任何動態分配的張量數據,同時避免在重新分配後意外使用張量的問題。

 

4 現有的軟件包

我們現在將海洋中的一些功能與其他軟件包中的功能進行比較。由於大多數軟件包都處於活動開發階段,因此我們只討論編寫時可用的功能。

Numpy[12]是用於密集張量操作的事實上的Python包。Numpy是爲cpu上的張量編寫的,不支持任何其他設備類型上的張量。最近的CuPy[11]包爲GPU設備實現了tensors,它的接口與Numpy的接口非常相似,但在其他方面基本上是獨立的。這兩個包都是作爲Python-C API編寫的,並直接擴展Python類,這限制了它們作爲獨立包的使用。此外,這兩個包中的每一個只支持一種設備類型。

ArrayFire[18]是一個支持多種設備類型並作爲具有單獨語言綁定的通用庫編寫的包。這同樣適用於大多數深度學習包。正如在引言中提到的,張量操作是深度學習包的基礎,因此我們考慮一些最流行的軟件:CAFE(8)、PyTrof(6, 13)、TunSoFrase[1 ]和MxNET[5 ]。所有這些包都支持多種設備類型上的張量操作,並將至少一部分可用的張量操作公開給用戶。儘管如此,考慮到對深度學習的關注,這些包不是編寫的,也不是打算用作獨立的張量支持包。特別是,許多包定義了具有高度特定於域的成員函數和變量的張量類。例如,類可以爲每個張量提供梯度信息,或者包括對包含張量的符號計算圖節點的引用。
轉存失敗重新上傳取消轉存失敗重新上傳取消


在表1中,我們列出了一些我們認爲在通用張量包中很重要的性質,因此在海洋中實現。如第3.2.1節中詳細討論的,這些特性之一是支持自動類型鑄造。Numpy、CuPy和ArrayFire都支持此功能,但正在考慮的所有深度學習包中都缺少此功能。

從開發人員的角度來看,有一個統一的張量類型或類是很方便的。除Caffe外的所有包都支持此功能,Caffe提供了一個模板類(其他幾個包在後臺使用模板類,但在API中提供統一類型)。對於tensor包2,支持一組全面的數據類型顯然很重要。包之間的覆蓋範圍有很大的不同,因此我們將重點放在對複雜數據類型的支持上,這需要額外的功能,例如共軛和對張量的實部和虛部的訪問。在所考慮的四個深度學習包中,只有TensorFlow支持複雜的張量類型(基於單精度和雙精度浮點)。Numpy、CuPy和ArrayFire也支持這些類型。Ocean是唯一一個額外提供基於半精度浮點的複雜數據類型的包。

 

張量在內存中的佈局由步長或沿每個維度的連續元素之間的距離給出。 張量跨度的靈活性可實現以下功能:沿尺寸進行廣播,輕鬆操縱軸以及在定期索引的次張量上創建視圖。 此外,它確保與張量和矩陣的各種現有數據類型兼容。 大多數深度學習包以及ArrayFire都遵循連續的行爲主數據順序,並具有隱式跨度,可以根據張量維和給定數據類型的元素大小來推斷隱式跨度。 PyTorch默認情況下也使用此數據順序,但是允許用戶通過將張量步幅指定爲元素大小的非負倍數來覆蓋標準佈局。 Numpy和CuPy支持任意步伐。 這些軟件包中的每一個都與Ocean一起,在可能的情況下實現軸的排序和合並連續的軸,以增加內存局部性並減少遍歷維度的開銷,這兩者都有助於提高跨步數據的張量運算的計算效率。 對於許多操作,例如一元和二進制元素級操作,使用連續張量佈局的包可以將張量展平爲一個維。 其他操作可能需要與上述類似的優化。 ArrayFire將張量維數限制爲四個,並且經常使用顯式嵌套的for循環,並在最內層的循環中使用索引計算來遍歷數據。

提供任意步幅帶來的一些困難是張量可能在內存中自動重疊,並且成對的張量之間的重疊檢測變得不平凡。

爲了獲得一致的計算結果,例如A [[1,2]] = A [[2,1]],對重疊檢測的良好支持至關重要。 Ocean檢查自重疊張量,並在大多數操作中將它們視爲只讀(未正確定義將不同值寫入相同內存地址的語義)。 還包括張量對之間的重疊檢測以及中間張量的分配以解決重疊。 在PyTorch和Numpy中也存在類似的檢查。 TensorFlow中的重疊檢測僅限於張量視圖。

除了Ocean之外,本節中考慮的所有程序包都沒有定義張量類型與張量操作的底層實現之間的明確區分。 結果,除了現有庫(例如BLAS和cuBLAS)已經提供的那些張量操作之外,其他任何張量操作都無法輕鬆轉移以用於其他程序包。

 

5說明性示例

我們現在基於示例QR因式分解來說明Ocean Tensor Package的某些功能(例如,參見[16])。 當然,這僅僅是一個例子。 QR分解應該是軟件包不可或缺的一部分,並計劃在未來的線性代數模塊中提供直接支持。 可以在Ocean Tensor Package存儲庫的文檔中找到核心模塊提供的功能的完整列表以及大量示例[2]。

​​​​​​​

 

6 結論

在本文中,我們介紹了Ocean Tensor軟件包,這是一個通用的張量支持軟件包,用於在不同設備類型上的密集張量。 該軟件包以模塊化的方式組織,將張量操作的連貫集合組合在模塊中。 每個模塊都包含一個與設備無關的接口,該接口公開了可用的功能,以及單獨的設備專用模塊,這些模塊提供了這些設備上這些功能的實現。 在Ocean中,一個有意識的設計決定是提供清晰分離的抽象層。 底層提供了獨立於張量表示的低級張量操作,因此也可以被其他軟件包用作獨立的基礎庫。 Ocean Tensor軟件包的基礎牢固地建立在覈心模塊中,因此未來的工作將主要集中在通過添加新模塊和擴展現有模塊來提高功能性上。

 

Available at https://github.com/ibm/ocean-tensor-package

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章