code window

2024年4月29日星期一

Llamafile: 專為大模型設計的推論框架(一)

Justine Tunney, 一個1984年出生的美女programmer,正在用她開發的程式語言改變大模型生態。

我花了很久時間才消化了她的程式, 的確是個天才!!

大模型的訓練和推論是兩個完全不同的過程。在訓練階段,我們追求更高的準確度和更強的泛化能力,需要大量的計算資源和時間。而在推論階段,我們關注如何在保證準確度的前提下,實現更快的響應速度和更低的計算成本。這需要對推論過程進行針對性的優化。

為了應對這一挑戰,引入了 llama.cpp 。

llama.cpp 是一個專為大模型設計的推論框架,採用一系列先進的優化技術,例如模型壓縮、硬體加速等,目的在提高推論速度和效率。同時,llama.cpp 還具有良好的可擴展性和易用性,能夠輕鬆應對不同場景和需求。

llama.cpp 主要特性:

1. 純C/C++構建,依賴極少。

2. 多平台:Linux、Windows、同時也可部署在 Docker 上。

3. 支持Apple silicon的GPU,通過 METAL 的硬件調用;CPU、GPU、FPGA 等晶片也可支持。

- 在BLAS中支持OpenBLAS/Apple BLAS/ARM Performance Lib/ATLAS/BLIS/Intel MKL/NVHPC/ACML/SCSL/SGIMATH等

- 支持cuBLAS和CLBlast

4. 支持X86 架構的 SSE、AVX、AVX2、AVX512 指令集。

5. 混合精度推論,支持 FP16/FP32,在保持精確度的同時提升推論速度。

6. 量化工具綁定。量化是提升推論速度、降低硬體佔用的一個重要手段。llama.cpp 廣泛支持模型量化至 2bit、3bit、4bit、5bit、6bit、8bit,大大減少模型佔用硬碟及顯存容量,使在單張消費級顯卡上完成大模型推論成為可能。

llama.cpp 的 Metal 構建支持 MAC 裝置以 GPU 執行 LLM 推論過程,7B 版本每秒可以超過 40 個 tokens。

Mark Zuckerberg 曾說 " And there were things like, yeah, llama.cpp ,  reimplemented it more efficiently. So now even when we now run our own version of it, we can do it on way less compute and it is just way more efficient, save a lot of money for everyone who uses this. So that is good. "

LLaMA 這樣的大型模型通常需要高階 GPU 才能流暢運行。Mozilla 支持的 llamafile 專案目的在本地能運行大型模型。llamafile 是Justine在 2023 年 11 月與 Mozilla 合作的一個本地 LLM 項目。使用 Cosmopolitan Libc 將 llama.cpp 包裝為一個單文件跨平台二進制文件,該文件可在 AMD64 和 ARM64 的六種操作系統上運行,同時進行輕微修改為用戶提供最佳的 llama.cpp 體驗。

Justine Tunney為 llamafile 撰寫了 84 個新的矩陣乘法核心,顯著改進了 CPU 的運行性能,讓普通的 CPU 能夠流暢運行大型模型成為可能。 llamafile 在許多情境和硬體上速度比 llama.cpp 快了 1.3 到 5 倍。

5倍!!!!!

在 ARMv8.2+(例如 Raspberry Pi 5)、Intel CPU,以及支援 AVX512 指令集的 AMD Zen 4 CPU 上,運行速度增幅最最明顯。

怎麼辦到的?


llamafile 的功能是把大型語言模型(LLM)的權重轉換成可以執行的程式。

假設你有一個 4GB 的檔案,裡面存著一組 LLM 的權重(通常是 GGUF 格式)。使用 llamafile,你可以把這個 4GB 的檔案轉換成一個程式,在六種不同的作業系統上運行,而且不需要額外安裝其他東西。

這樣就讓 LLM 的分發和運行變得非常容易。也就是說,當模型和權重格式不斷演進的時候,llamafile 為你提供了一個確保權重始終可用並能夠保持一致和可重現性的方法。

這個llamafile是由 Mozilla 創新小組首次發表,由 Cosmopolitan 的創建者 Justine Tunney 開發。通過 MIECO 與 Mozilla,這個計畫資助了她對 Cosmopolitan 3.0 版本的工作。有了 llamafile,Justine 就能夠更直接為 Mozilla 接下來的其他專案做出貢獻。

llamafile 採用 Apache 2.0 許可。對 llama.cpp 本身所做的更改使用的是 MIT 許可,這樣可以更容易將這些更改合併回原項目。沒有 llama.cpp 和 Cosmopolitan,就不會有 llamafile。

Transformer 模型需要執行數十種數學操作才能生成文本,例如 rope、transpose、reshape、softmax、rms_norm 等。所有性能改進都是專注於一個單一操作,即 GGML_OP_MUL_MAT,因為Linux Perf 分析器顯示 llamafile 花了 95% 的時間在這個操作上。

這個矩陣乘法是什麼呢?從使用當今開發人員最喜歡的 Python 語言的 Pythonic 方言來定義世界上最重要的算法開始:

def matmul(A, B):

  assert len(B) == len(A[0])

  return [[sum(A[i][l] * B[l][j]

               for l in range(len(B)))

           for j in range(len(B[0]))]

          for i in range(len(A))]

這只是三個 for 循環和一個乘法-加法操作。

上面的代碼達到了 0.042 gigaflops。大多數 Python 的programmer都夠聰明,知道他們應該將這樣的任務派給像 np.matmul 這樣的庫,它可以達到 29 gigaflops。NumPy 通過使用 FORTRAN 實現了它的速度,FORTRAN過去很長時間一直受到真正的programmer的青睞,如果實際查看 NumPy 使用的 FORTRAN 程式碼,那它其實並不是那麼複雜,顯然可以從中受益。

使用像 C++ 這樣的現代語言來定義子程序,達到了 47 gigaflops。意思是 C++ 比 Python 快了三個數量級。根據摩爾定律,這相當於二十年的進步。

為了在CPU上實現高於47 gigaflops的性能,大多數C++開發人員都知道他們應該使用BLAS庫。開源BLAS中最強大的是由Microsoft、Intel、TI、AMD、HPE、Oracle、Huawei、Facebook、ARM和美國國家科學基金會資助的BLIS。

如果看一下上面給出的C++例子,使用-O3 -ffast-math -march=native編譯,那麼編譯器生成的程式碼應該如下所示:

void SLLMM(int m, int n, int k,

           const float *A, int lda,

           const float *B, int ldb,

           float *C, int ldc) {

#pragma omp parallel for collapse(2) if (m * n * k > 300000)

    for (int i = 0; i < m; ++i)

        for (int j = 0; j < n; ++j) {

            __m256 c = _mm256_setzero_ps();

            for (int l = 0; l < k; l += 😎

                c = _mm256_fmadd_ps(_mm256_loadu_ps(A + lda * i + l),

                                    _mm256_loadu_ps(B + ldb * j + l), c);

            C[ldc * j + i] = hsum(c);

        }

}

希望改進時,llama.cpp通常會像這樣展開最內層的循環:

void SLLMM2(int m, int n, int k,

           const float *A, int lda,

           const float *B, int ldb,

           float *C, int ldc) {

#pragma omp parallel for collapse(2) if (m * n * k > 300000)

    for (int i = 0; i < m; ++i)

        for (int j = 0; j < n; ++j) {

            __m256 c0 = _mm256_setzero_ps();

            __m256 c1 = _mm256_setzero_ps();

            for (int l = 0; l < k; l += 16) {

                c0 = _mm256_fmadd_ps(_mm256_loadu_ps(A + lda * i + l + 0),

                                     _mm256_loadu_ps(B + ldb * j + l + 0), c0);

                c1 = _mm256_fmadd_ps(_mm256_loadu_ps(A + lda * i + l + 😎,

                                     _mm256_loadu_ps(B + ldb * j + l + 😎, c1);

            }

            C[ldc * j + i] = hsum(c0) + hsum(c1);

        }

}

這會稍微提高數值穩定性,但對於性能幫助不大,因為現代的CPU完全能夠自行對未來的循環進行推測性執行。Justine希望展開最外層的循環。這樣做的優勢是能夠在多個浮點運算之間共享a0寄存器加載。效果更明顯,這樣可以善用CPU的自動向量化提高性能。以下是將最外層循環展開的版本:

void SLLMM3(int m, int n, int k,

           const float *A, int lda,

           const float *B, int ldb,

           float *C, int ldc) {

#pragma omp parallel for collapse(2) if (m * n * k > 300000)

    for (int j = 0; j < n; ++j)

        for (int i = 0; i < m; ++i) {

            __m256 c = _mm256_setzero_ps();

            for (int l = 0; l < k; l += 😎

                c = _mm256_fmadd_ps(_mm256_loadu_ps(A + lda * i + l),

                                    _mm256_loadu_ps(B + ldb * j + l), c);

            C[ldc * j + i] = hsum(c);

        }

}

這個版本中,將內層循環的順序與原始的版本進行了調換,將最外層的循環改為遍歷n(行)而不是m(列)。


此外,還可以通過進一步優化矩陣乘法的算法來提高性能。例如,考慮使用分塊矩陣乘法(blocked matrix multiplication)算法,將輸入矩陣劃分為小的子矩陣,減少緩存訪問次數和提高數據重用率。這種優化方式通常會進一步提高矩陣乘法的性能,特別是在大型矩陣上。

基於這些考慮所以Justine為 llamafile 寫了 84 個新的矩陣乘法核心,在讀取提示/圖像時變得更快。與 llama.cpp 相比,使用 F16 和 Q8_0 權重在 CPU 上進行提示評估的時間應該會快 30% 到 500%!

在 ARMv8.2+(例如 RPI 5)、Intel(例如 Alderlake)和 AVX512(例如 Zen 4)跑的時候,這些改進效果最明顯。

核心在符合 L2 快取的矩陣上也會比 MKL 快 2 倍!

Justine等於向由Microsoft,Intel,TI,AMD,HPE,Oracle,Huawei,Facebook,ARM和National Science Foundation贊助的BLIS陣營所支持的BLAS中的MKL(Math Kernel Library)提出了挑戰,

BLAS 的全稱是 Basic Linear Algebra Subprograms。它定義了一組應用程序接口(API)標準,如向量之間的乘法、矩陣之間的乘法等,是數值計算軟件必須具備的核心庫之一。BLAS 也可以被稱為高性能計算、仿真、數據處理、人工智能的基石,它的效率直接關連計算成本。

Intel 的 MKL 庫是針對自家特定的 CPU 平台進行針對性的優化加速的數學核心庫,其中包括了 BLAS 計算庫,還有 LAPACK、DFTs、VML、VSL 等。

Intel MKL 對於自家的每一代處理器都預先做了深度優化,既然是 x86 指令集,AMD、海光處理器理論上都可以拿去作為晶片 SDK 的基礎軟體,許多軟體也整合 MKL 庫。但問題來了:

Intel MKL 庫運行在自家的晶片上性能確實不錯,但在別家晶片上就不一定了(例如 AMD)。這和即使同樣是 x86 指令集的處理器,但處理器架構設計各有差異化有關,MKL 不會傻到幫敵人去針對性優化。因為異構計算的興起,如果軟體或算法希望可以在 ARM、RISC-V,GPU,DSA 架構上運行,靠 MKL 就沒戲了。MKL 要是源碼公開,適配其他晶片平台難度就低許多了,不好意思,MKL 庫不開源。所以 NVIDIA 有了自己的 cuBLAS,當然同樣的道理,cuBLAS 也是不開源的,那這樣你家的 GPU 怎麼辦呢?

這個問題非常普遍,無論是深度學習算法、資料庫、數值計算軟體等,如果你還對性能和穩定性有些追求,就一定會遇到。

在GPU供應短缺的情況下,Llamafile讓您無需昂貴的CUDA核心,只要舊CPU加點內存就足夠了,省下很多成本。Llamafile的原始碼可以在GitHub上找到,使用C++編寫,並且沒有外部依賴,可以在Linux、macOS、Windows、FreeBSD,甚至SerenityOS上編譯。

Justine Tunney已經在努力支援新的資料格式,例如FP16和BF16,以進一步減少內存使用量,甚至已成功在Raspberry Pi上運行了TinyLlama!

Justine很大方的把source code公布在這裡:

https://github.com/....../blob/main/llamafile/sgemm.cpp

沒有留言:

發佈留言

SambaNova SN40L: 利用Dataflow和專家組合(COE)來克服AI記憶牆的大模型

摘要 GPT-4等整體式大型語言模型(LLM)為現代生成AI應用鋪路。然而,大規模訓練、服務及維護整體式LLM仍然極其昂貴和充滿挑戰。現代AI加速器計算能力與記憶體比例的不成比例增長已經造成了記憶體壁障,需要新的方法來部署AI。最近的研究顯示,許多小型專家模型的組合,每個模型參數...