15年!NumPy論文終出爐,還登上了Nature

  機器之心報道web

  編輯:魔王、杜偉、小舟算法

  

NumPy 團隊撰寫了一篇綜述文章,介紹 NumPy 的發展過程、主要特性和數組編程等。這篇文章現已發表在 Nature 上。

  NumPy 是什麼?它是大名鼎鼎的使用 Python 進行科學計算的基礎軟件包,是 Python 生態系統中數據分析、機器學習、科學計算的主力軍,極大簡化了向量與矩陣的操做處理。除了計算外,它還包括了:編程

  功能強大的 N 維數組對象。後端

  精密廣播功能函數。api

  集成 C/C++ 和 Fortran 代碼的工具。數組

  強大的線性代數、傅立葉變換和隨機數功能性能優化

  今日,NumPy 核心開發團隊的論文終於在 Nature 上發表,詳細介紹了使用 NumPy 的數組編程(Array programming)。這篇綜述論文的發表距離 NumPy 誕生已通過去了 15 年。數據結構

  

  NumPy 官方團隊在 Twitter 上簡要歸納了這篇論文的核心內容:架構

  NumPy 爲數組編程提供了簡明易懂、表達力強的高級 API,同時還考慮了維持快速運算的底層機制。框架

  NumPy 提供的數組編程基礎和生態系統中的大量工具結合,造成了適合探索性數據分析的完美交互環境。NumPy 還包括加強與 PyTorch、Dask 和 JAX 等外部庫互操做性的協議。

  基於這些特性,NumPy 爲張量計算提供了標準的 API,成爲 Python 中不一樣數組技術之間的核心協調機制。

  接下來,咱們來看這篇 NumPy 綜述論文的詳細內容。

  論文摘要

  數組編程爲訪問、操縱和計算向量、矩陣和高維數組中的數據提供了功能強大、緊湊且表達力強的語法。NumPy 是 Python 語言的主要數組編程庫,它在物理、化學、天文學、地球科學、生物學、心理學、材料科學、工程學、金融和經濟學等領域的研究分析中都起着相當重要的做用。例如,在天文學中,NumPy 是發現引力波和黑洞首次成像的軟件棧中的重要部分。

  這篇論文回顧了一些基本的數組概念,以及它們如何造成一種簡單而強大的編程範式,使其可以用於組織、探索和分析科學數據。NumPy 是構建科學 Python 生態系統的基礎。它的應用十分廣泛,一些面向特殊需求受衆的項目已經開發出本身的類 NumPy 接口和數組對象。

  因爲其在 Python 生態系統中的核心地位,NumPy 愈來愈多地充當數組計算庫之間的互操做層,而且和其 API 一塊兒提供了靈活的框架,以支持將來十年的科學和工業分析。

  NumPy 的演變史

  在 NumPy 以前,已經出現了兩個 Python 數組包。Numeric 包開發於 20 世紀 90 年代中期,它提供了 Python 中的數組對象和 array-aware 函數。Numeric 是用 C 語言寫的,並連接到線性代數的標準快速實現。其最先的應用之一是美國勞倫斯利弗莫爾國家實驗室的慣性約束核聚變研究。

  爲了處理來自哈勃太空望遠鏡的大型天文圖像,Numeric 被重實現爲 Numarray,它添加了對結構化數組、靈活 indexing、內存映射、字節序變體、更高效的內存使用以及更好的類型轉換規則的支持。

  儘管 Numarray 與 Numeric 高度兼容,但這兩個包之間的差別足以將社區開發者分爲兩類。而 2005 年,NumPy 的出現完美地統一了這兩個包,它將 Numarray 的功能和 Numeric 的 small-array 性能及其豐富的 C API 結合起來。

  現在,15 年過去了,NumPy 幾乎支持全部進行科學和數值計算的 Python 庫(包括 SciPy、Matplotlib、pandas、scikit-learn 和 scikit-image)。NumPy 是一個社區開發的開源庫,它提供了多維 Python 數組對象以及對其進行操做的 array-aware 函數。因爲其固有的簡潔性,事實上 NumPy 數組已經成爲 Python 中數組數據的交換格式。

  NumPy 使用 CPU 對內存內(in-memory)數組進行操做。爲了利用現代的專用存儲和硬件,最近已經擴展出一系列 Python 數組包。與 Numarray–Numeric 之間存在較大差別的狀況不一樣,如今的這些新庫很難在社區開發者中引發分歧,由於它們都是創建在 NumPy 之上的。可是,爲了使社區可以使用新的探索性技術,NumPy 正在過渡爲核心協調機制,該機制規劃了良好定義的數組編程 API,並在合適的時候將其分發給專門的數組實現。

  NumPy 數組

  NumPy 數組是一種可以高效存儲和訪問多維數組的數據結構,支持普遍類型的科學計算。NumPy 數組包括指針和用於解釋存儲數據的元數據,即 data type(數據類型)、shape(形狀)和 strides(步幅),參見下圖 1a。

  

  圖 1:NumPy 數組包括多種基礎數組概念。

  數據類型描述了數組中存儲元素的本質。一個數組只有一個數據類型,數組中的每一個元素在內存中佔用的字節數是同樣的。數據類型包括實數、複數、字符串、timestamp 和指針等。

  數組的形狀決定了每一個軸上的元素數量,軸的數量即爲數組的維數。例如,數字向量可存儲爲形狀爲 N 的一維數組,而彩色視頻是形狀爲 (T, M, N, 3) 的四維數組。

  步幅是解釋計算機內存的必要組件,它能夠線性地存儲元素。步幅描述了在內存中逐行逐列移動時所需的字節數。例如,形狀爲 (4, 3) 的二維浮點數數組,它其中的每一個元素均在內存中佔用 8 個字節數。要想在連續列之間移動,咱們須要在內存中前進 8 個字節數,要想到達下一行,則須要前進 3 × 8 = 24 個字節數。所以該數組的步幅爲 (24, 8)。NumPy 能夠用 C 或 Fortran 的內存順序存儲數組,沿着行或列遍歷。這使得使用這些語言寫的外部庫能夠直接訪問內存中的 NumPy 數組數據。

  用戶使用「indexing」(訪問子數組或單個元素)、「operators」(各類運算符)和「array-aware function」與 NumPy 數組進行交互。它們爲 NumPy 數組編程提供了簡明易懂、表達力強的高級 API,同時還考慮了維持快速運算的底層機制。

  對數組執行 indexing 將返回單個元素、子數組或知足特定條件的元素(參見上圖 1b)。數組甚至還能夠用其餘數組進行 indexing(參加圖 1c)。返回子數組的 indexing 還能夠返回原始數組的「view」,以便在兩個數組之間共享數據。這就爲內存有限的狀況下基於數組數據子集進行運算提供了一種強大的方式。

  爲了補充數組語法,NumPy 還包括對數組執行向量化計算的函數,包括 arithmetic、statistics 和 trigonometry(參見圖 1d)。向量化計算基於整個數組運行而不是其中的單個元素,這對於數組編程而言是必要的。這意味着,在 C 等語言中須要幾十行才能表達的運算在這裏只需一個清晰的 Python 表達式便可實現。這就帶來了簡潔的代碼,並使得用戶沒必要關注分析細節,同時 NumPy 以接近最優的方式循環遍歷數組元素。

  對兩個形狀相同的數組執行向量化計算(如加法)時,接下來會發生什麼是很明確的。而「broadcasting」機制容許 NumPy 處理維度不一樣的數組之間的運算,例如向數組添加一個標量值。broadcasting 還能泛化至更復雜的示例,如縮放數組的每一列或生成座標網格。在 broadcasting 中,單個或兩個數組能夠重疊(沒有從內存中複製任何數據),使得 operands 的形狀匹配(參見圖 1d)。

  其餘 array-aware function(如加、求平均值、求最大值)都是執行逐元素的「reduction」,累積單個數組的一個、多個或全部軸上的結果。例如,將一個 n 維數組與 d 個軸進行累加,獲得維度爲 n d 的數組(參見圖 1f)。

  NumPy 還包含能夠建立、reshaping、concatenating 和 padding 數組,執行數據排序和計數,讀取和寫入文件的 array-aware function。這爲生成僞隨機數提供了大量支持,它還可使用 OpenBLAS 或 Intel MKL 等後端執行加速線性代數。

  總之,內存內的數組表示、緊密貼近數學的語法和多種 array-aware function 共同構成了生產力強、表達力強的數組編程語言。

  科學 Python 生態系統

  Python 是一個開源、通用的解釋型編程語言,很是適合數據清洗、與 web 資源交互和解析文本之類的標準編程任務。添加快速數組操做和線性代數可以讓科學家在一種編程語言中完成全部的工做。

  儘管 NumPy 不是 Python 標準庫的一部分,但它也從與 Python 開發者的良好關係中受益。在過去這些年中,Python 語言已經加入了一些新的功能和特殊的語法,以便 NumPy 具有更加簡潔和易於閱讀的數組表示法。可是,因爲 NumPy 不是 Python 標準庫的一部分,因此它可以規定本身的發佈策略和開發模式。

  從發展史、開發和應用的角度來看,SciPy 和 Matplotlib 與 NumPy 聯繫緊密。SciPy 爲科學計算提供了基礎算法,包括數學、科學和工程程序。Matplotlib 生成可發表品質的圖表和可視化文件。NumPy、SciPy 和 Matplotlib 的結合,再加上 IPython、Jupyter 這類高級交互環境,爲 Python 中的數組編程提供了堅實的基礎。

  如圖 2 所示,科學 Python 生態系統創建在上述基礎之上,它提供了多種普遍應用的專有技術庫,而這又是衆多領域特定項目的基礎。NumPy 是這一 array-aware 庫生態系統的基礎,它設置了文檔標準、提供了數組測試基礎結構,並增長了對 Fortran 等編譯器的構建支持。

  

  圖 2:NumPy 是科學 Python 生態系統的基礎。

  不少研究團隊設計出大型、複雜的科學庫,這些庫爲 Python 生態系統增添了特定於具體應用的功能。例如,由事件視界望遠鏡(Event Horizon Telescope, EHT)合做項目開發的 eht-imaging 庫依賴科學 Python 生態系統的不少低級組件。而 EHT 合做項目利用該庫捕獲了黑洞的首張圖像。

  在 eht-imaging 庫中,NumPy 數組在流程鏈的每一步存儲和操縱數值數據。

  基於數組編程建立的交互式環境及其周邊的工具生態系統(IPython 或 Jupyter 內部)完美適用於探索性數據分析。用戶能夠流暢地檢查、操縱和可視化他們的數據,並快速迭代以改善編程語句。而後,將這些語句拼接入命令式或函數式程序,或包含計算和敘述的 notebook。

  超出探索性研究的科學計算一般在文本編輯器或 Spyder 等集成開發環境(IDE)中完成。這一豐富和高產的環境使 Python 在科學研究界流行開來。

  爲了給探索性研究和快速原型提供補充支持,NumPy 造成了使用通過時間檢驗的軟件工程實踐來提高協做、減小偏差的文化。這種文化不只得到了項目領導者的採納,並且還被傳授給初學者。NumPy 團隊很早就採用分佈式版本控制和代碼審查機制來改善代碼協同,並使用持續測試對 NumPy 的每一個提議更改運行大量自動化測試。

  這種使用最佳實踐來製做可信賴科學軟件的文化已經被基於 NumPy 構建的生態系統所採用。例如,在近期英國皇家天文學會授予 Astropy 的一項獎項中表示:「Astropy 項目爲數百名初級科學家提供了專業水平的軟件開發實踐,包括版本控制使用、單元測試、代碼審查和問題追蹤程序等。這對於現代研究人員而言是一項重要的技能組合,但物理或天文學專業的正規大學教育卻經常忽略這一點。」社區成員經過課程和研討會來彌補正規教育中的這一缺失。

  近來數據科學、機器學習和人工智能的快速發展進一步大幅提高了 Python 的科學使用。Python 的重要應用,如 eht-imaging 庫,現已存在於天然和社會科學的幾乎每一個學科之中。這些工具已經成爲不少領域主要的軟件環境。大學課程、新手培訓營和暑期班一般教授 NumPy 及其生態系統,它們也成爲世界各地社區會議和研討會的焦點。NumPy 和它的 API 已經無處不在了。

  數組激增和互操做性

  NumPy 在 CPU 上提供了內存內、多維和均勻鍵入(即單一指向和跨步的)的數組。NumPy 能夠在嵌入式設備和世界上最大的超級計算機等機器上運行,其性能接近編譯語言。在大多數狀況下,NumPy 解決了絕大部分的數組計算用例。

  可是如今,科學數據集一般超出單個機器的存儲容量,而且能夠在多個機器或雲上存儲。此外,近來深度學習和人工智能應用的加速需求已經促生了專用加速器硬件,包括 GPU、TPU 和 FPGA。目前,因爲 NumPy 具備的內存內數據模型,它沒法直接使用這類存儲和專用硬件。

  然而,GPU、TPU 和 FPGA 的分佈式數據和並行執行可以很好地映射到數組編程範式,因此可用的現代硬件架構與利用它們的計算能力所必需的工具之間存在着差距。

  社區爲彌補這一差距作出的努力使得新的數組實現激增。例如,每一個深度學習框架都建立了本身的數組。PyTorch、TensorFlow、Apache MXNet 和 JAX 數組都有能力以分佈式方式在 CPU 和 GPU 上運行,其中使用惰性計算(lazy evaluation)實現額外性能優化。SciPy 和 PyData/Sparse 都提供有稀疏數組,這些數組一般包含不多的非零值,並只在內存中存儲以提高效率。

  此外,還有一些項目在 NumPy 數組上構建做爲數據容器,並擴展相應功能。Dask 經過這種方式使分佈式數組成爲可能,而標記數組是經過 xarray 實現的。

  這類庫經常模仿 NumPy API,以下降初學者准入門檻,併爲更普遍的社區提供穩定的數組編程接口。這反過來也會阻止一些破壞性分立(disruptive schism),如 Numeric 和 Numarray 之間的差別。

  可是探索使用數組的新方法從本質上講是試驗性的,事實上,Theano 和 Caffe 等一些有前途的庫已經中止了開發。每當用戶決定嘗試一項新技術時,他們必須更改 import 語句,並確保新庫可以實現他們當前使用的全部 NumPy API 部件。

  在理想狀態下,用戶能夠經過 NumPy 函數或語義在專用數組上進行操做,這樣他們能夠編寫一次代碼,而後從 NumPy 數組、GPU 數組、分佈式數組以及其餘數組之間的切換中獲益。爲了支持外部數組對象之間的數組操做,NumPy 增長了一項充當核心協調機制的功能,並提供指定的 API,具體如上圖 2 所示。

  爲了促進這種互操做性,NumPy 提供了容許專用數組傳遞給 NumPy 函數的「協議」,具體以下圖 3 所示。反過來,NumPy 根據須要將操做分派給原始庫。超過 400 個最流行的 NumPy 函數獲得了支持。該協議經過 Dask、CuPy、xarray 和 PyData/Sparse 等普遍使用的庫來實現。

  得益於這些進展,用戶如今可使用 Dask 將本身的計算從單個機器擴展至多個系統。該協議容許用戶經過 Dask 數組中嵌入的 CuPy 數組等,在分佈式多 GPU 系統上大規模地從新部署 NumPy 代碼。

  使用 NumPy 的高級 API,用戶能夠在具備數百萬個核的多系統上利用高度並行化的代碼執行,而且須要的代碼更改最少。

  以下圖 3 所示,NumPy 的 API 和數組協議向生態系統提供了新的數組:

  

  如今,這些數組協議是 NumPy 的主要特徵,它們的重要性預計也會愈來愈大。NumPy 開發者(不少也是這篇文章的做者)迭代地改善和增長協議設計,以改進實用性和簡化應用方式。

  論文最後對 NumPy 的現狀和將來進行了總結和展望:

  在將來十年中,NumPy 開發者將面臨多項挑戰。新設備將出現,現有的專用硬件將面臨摩爾定律的收益遞減,數據科學從業者將愈來愈多,類型也更加普遍。而他們中的大部分將使用 NumPy。

  隨着光片顯微鏡和大型綜合巡天望遠鏡(LSST)等設備和儀器的採用,科學數據的規模將持續擴大。新一代語言、解釋器和編譯器,如 Rust、Julia 和 LLVM,將創造出新的概念和數據結構。

相關文章
相關標籤/搜索