## Linux 核心專題: LLaMA 推論之效能議題 > 執行人: Lisa304 > [專題解說錄影](https://youtu.be/zCnMppLCh4s) ### Reviewed by `Booker-Chen` 對於 Mistral 1.1B 的實驗中,為什麼調換評估順序之後就可以解決無法產生回應的問題? ### Reviewed by `yinghuaxia` > F16 (FP16) 指的是 16 位 浮點表示。與傳統的 32 位浮點數 (FP32) 相比,它是一種更緊湊的表示方式,允許更快的計算和減少記憶體使用。Q8_0 指的是 8 位元量化權重,模型權重使用 8 位元表示。量化有助於減小模型大小並提高推理速度,同時保持合理的精度。 傳統的浮點數 (FP32) 轉換成 FP16 或 Q8 可以讓計算更快也減少記憶體的使用,同時也保持合理的精度,然而是否在其他地方會有 tradeoff?不然一開始為何不使用這兩種方式呢? ### Reviewed by `aa860630` 在解說影片提到使用 union 來改進浮點數運算的程式碼是有效的,但簡化程式碼後具體得到的好處是什麼呢? ### Reviewed by `yy214123` 下方提到,相較於 llama.cpp,當使用 F16 和 Q8_0 的權重時,可提昇 **30%~500%** 的指令運算時間,想詢問級距這麼大有什麼原因嗎? ### Reviewed by `vestata` 關於你提到的 >Q: 解決了記憶體頻寬問題,不是就不需要進行量化了嗎?這也比較能跟下一句說,本地 LM 不再需要為了速度犧牲知識性能接上,為什麼內文說解決記憶體頻寬問題,會導致量化成為瓶頸呢? 這邊的瓶頸會是指對困惑度的表現嗎? ## 任務說明 閱讀 [LLaMA Now Goes Faster on CPUs](https://justine.lol/matmul/) 並紀錄問題,定位出效能瓶頸 :::danger 聚焦上方任務,不要張貼不直接相關的材料。 老師,那個是你之前給的第一個 TODO,實作 IEEE 754乘法程式碼,還是就不貼了 詳見上方描述。 ::: ## TODO: 閱讀 [LLaMA Now Goes Faster on CPUs](https://justine.lol/matmul/) 並紀錄問題 > 重現相關實驗 ### 閱讀文章 作者為 llamafile 開發了 84 new matrix multiplication kernels,旨在顯著提高在各類型的 CPU 上讀取提示/圖像的速度。在 CPU 上使用 F16 和 Q8_0 的權重時,相較於 llama.cpp,在指令計算時間上將提升 30%~500% 的速度。 :::danger bit 是「位元」,而非「位」,後者是量詞。 務必使用課程規範的詞彙,和閱讀相關說明。 ::: F16 (FP16) 指的是 16 <s>位</s> 浮點表示。與傳統的 32 位浮點數 (FP32) 相比,它是一種更緊湊的表示方式,允許更快的計算和減少記憶體使用。Q8_0 指的是 8 位元量化權重,模型權重使用 8 位元表示。量化有助於減小模型大小並提高推理速度,同時保持合理的精度。 > 模型量化(quantization)是將模型從原本高精度資料格式轉換為低精度格式儲存的一種模型最佳化(model optimization)手段。目前在電腦上訓練模型時使用的資料格式多為 32 位元單精度浮點數(32-bit single-precision floating-point, FP32),訓練完畢後經過模型量化轉換為 8 位元整數(8-bit integer, INT8)格式儲存。 > >量化主要包含兩個步驟: > >1. 選定準備進行量化的實數範圍(FP32),範圍外的數字 clip 到上限或下限; >2. 將實數對應到低位元代表的整數(INT8)。 :::danger 引用權威材料來說明模型量化。 ::: <s> ----[模型量化:轉換為低精度格式提升運算效能](https://medium.com/@lee.kevin.lin/%E6%A8%A1%E5%9E%8B%E9%87%8F%E5%8C%96-quantization-%E8%BD%89%E6%8F%9B%E7%82%BA%E4%BD%8E%E7%B2%BE%E5%BA%A6%E6%A0%BC%E5%BC%8F%E6%8F%90%E5%8D%87%E9%81%8B%E7%AE%97%E6%95%88%E8%83%BD-6af462fba0b1) </s> > The improvements are most dramatic for ARMv8.2+ (e.g. RPI 5), Intel (e.g. Alderlake), and AVX512 (e.g. Zen 4) computers. - Raspberry Pi 5 實作 Armv8.2-A 指令集 - Alderlake 是 Intel 第 12 代處理器的微架構代號 - AVX512(Advanced Vector Extensions 512) 是 Intel 為 x86 架構引入的一組指令集擴展。它允許處理寬向量(512 <s>位</s> ),顯著提高了涉及大規模計算任務的性能,如科學模擬、金融分析和機器學習工作負載。 - Zen 4 是 AMD 的微架構代號,用於其 Ryzen 和 EPYC 處理器。Zen 4 引入了性能、功耗效率的各種改進,並支持新的指令集如 AVX512。 :::danger 注意用語: * (OS) kernel 是「核心」 * (processor) core 是「核」 不要誤用。 ::: 作者的<s>核心</s> 使用 Level 2 Cache 時比 MKL(Math Kernel Library) 快兩倍,如果任務是少於 1000 個 token 的話,能提供可觀的加速。 **Background** :::danger 注意用語: * binary 是「二進位制」,簡稱「二進位」 * file 是「檔案」,而非「文件」(document) ::: llamafile 是作者在 2023.11 在 Mozilla 啟動的本地 LLM 專案。他們使用 Cosmopolitan Libc 來把 llama.cpp 打包成單檔跨平台<s>二進制文件</s> ,可在 [AMD64(x86-64)](https://zh.wikipedia.org/wiki/X86-64) 和 ARM64 的六種作業系統上運行,同時對其進行少量修改。 llamafile 和 Cosmopolitan Libc 都是由 Mozilla 贊助作者的。 <s> Mozilla是一個自由軟體社群,由網景通訊公司的成員於1998年創立。在非正式的場合下,「Mozilla」這個名字常用於不同的事物上。這些事物大都與現已歇業的網景通訊公司及其旗下的應用軟體相關。 名字由來: Mosaic + Godzilla + Killa = Mozilla (哥吉拉殺死當時世界第一的網頁瀏覽器 Mosaic) ---- 維基百科 </s> :::danger 聚焦在我們要解決的 LLaMA 推論議題上面。 ::: **Performance Gains on Enterprise Hardware** >HP Intel® Core™ i9-9900 ($439) w/ 2200 MT/s RAM c. 2020 :::danger via, through 不該翻譯為「通過」,否則無法區分 pass ::: 引入了 `mmap()` 支持,這使得權重加載變得即時,並且<s>內存</s> 佔用減少了一半,但對於評估速度的提升幫助不大。後來作者<s>通過</s> <s>優化內核</s> ,<s>實現</s> 了更大的性能提升,特別是在特定數據類型(如 q8_0 和 f16)上。 :::danger 注意用語! 改進你的漢語表達。 ::: 在這裡,我們看到在 Skylake 平台上(硬體部分),軟體部分 llamafile 的用戶可以期待看到 2 倍的速度提升,而 llama.cpp 用戶則可以期待性能提升 50%,表格中同樣是權重 4 量化格式的時候,根據軟體部分個更改,指令處理速度從 12 上升到 28。 :::danger 不要濫用「優化」,詳見: https://hackmd.io/@sysprog/it-vocabulary ::: 但是這只適用於某些權重。到目前為止,作者只為 q8_0、f16、q4_1、q4_0 和 f32 這些數據類型編寫了<s>優化</s> 的<s>內核</s>。認為 q8_0 和 f16 是非常穩健的選擇,他們在參數量提升的同時,指令處理速度竟然也有提升 205 和 171。如果你有足夠的<s>內存</s> ,可能 f32 也不錯。 | prompt tok/sec | eval tok/sec | model | weights data type | software | | -------------- | ------------ | -------------- | ----------------- | -------------------- | | ==28== | 7 | Mistral 7b | q4_0 | llamafile-0.7 | | 17 | 7 | Mistral 7b | q4_0 | llama.cpp 2024-03-26 | | ==12== | 7 | Mistral 7b | q4_0 | llamafile-0.6.2 | | 32 | 4 | Mistral 7b | q8_0 | llamafile-0.7 | | 23 | 2 | Mistral 7b | f16 | llamafile-0.7 | | ==205== | 26 | TinyLlama 1.1B | q8_0 | llamafile-0.7 | | ==171== | 15 | TinyLlama 1.1B | f16 | llamafile-0.7 | >That's because my new kernels change the rules. They're doing such a good job fixing the memory bandwidth quants always solved, that quantization could become the bigger bottleck.That would be great news for the future of local language models, since it means less need to trade away knowledge for speed. 作者的新方法能夠解決 memory bandwidth quants 的問題,但是在量化上是一個更大的瓶頸。這對於本地語言模型的未來是個好消息,因為這意味著減少了為了速度而犧牲知識的必要性。 memory bandwidth quants <s>內存帶寬</s> 問題指的是處理器從內存中讀取或向內存寫入數據的速度受限,內存帶寬不足可能導致處理器等待數據的時間增加,對於 LLM 頻繁<s>訪問</s> 大量數據時,此問題會很明顯。 :::danger 注意用語: * access 是「存取」,而非「訪問」(visit) * memory 是「記憶體」,而非「內存」 * bandwidth 是「頻寬」,而非「帶寬」 ::: 在語言模型中,量化是一種常見技術,用於減少模型的內存和計算需求。量化通過將浮點數據轉換為較低精度的數據格式(如 8 位或 4 位)來實現。儘管這可以顯著減少模型的內存佔用和計算負擔,但也可能導致模型性能下降,即犧牲了一部分模型的“知識性”,因為量化過程中可能會丟失一些細節信息。 :::danger 注意用語! ::: Q: 解決了內存帶寬問題,不是就不需要進行量化了嗎?這也比較能跟下一句說,本地 LM 不再需要為了速度犧牲知識性能接上,為什麼內文說解決內存帶寬問題,會導致量化成為瓶頸呢? **Performance Gains on Hobbyist Hardware** >$100 Raspberry Pi v5 (ARMv8.2) and v4 (ARMv8.0) Raspberry Pi 是當今商店中最好的個人電腦之一。它們以優惠的價格提供良好的性能,並且功耗極低。 表格是 100 美元的 Raspberry Pi v5 (ARMv8.2) 和 v4 (ARMv8.0) 上的表現: | prompt tok/sec | eval tok/sec | model | weights data type | hardware | software | | -------------- | ------------ | -------------- | ----------------- | -------- | ------------- | | ==62== | 5 | TinyLlama 1.1B | f16 | RPI5 | llamafile-0.7 | | 45 | 9 | TinyLlama 1.1B | q8_0 | RPI5 | llamafile-0.7 | | 10 | 3 | TinyLlama 1.1B | q8_0 | RPI4 | llamafile-0.7 | | ==3== | 2 | TinyLlama 1.1B | f16 | RPI4 | llamafile-0.7 | Raspberry Pi 幾個月前發布了他們的第五代產品,相較於之前的型號速度快得令人難以置信。他們還引入了對 ARMv8.2 dotprod 和 fp16 算術 ISA(指令集架構)的支持,這對於大規模語言模型(LLM)非常有用。僅這兩個特性就讓 llama.cpp 在去年針對 f16 權重達成 10 倍的性能提升。 :::danger 不該使用機器翻譯,用你對文章的認知,重新書寫。 ::: 本週我在此基礎上又實現了額外的 2 倍性能提升,所以總共是提升了二十倍(3 => 62),使用的是原本打算用於 AVX512 (指令擴展集,從 256-bit 擴展到 512-bit)的<s>內核</s> 。強大的<s>數據中心</s> 設備設計的內核能在微小的 Raspberry Pi 上工作,因為兩者的 CPU 都有 32 個向量<s>寄存器</s> (32 vector registers)。 :::danger 注意用語! ::: 新 ARMv8.2 fp16 ISA 可能會==引入更多的誤差==,因為它使得 llamafile 使用 fp16 <s>字</s> (fp16 words),並且我們沒有使用 ==Kahan 求和==來計算點積。fp16(16 位浮點數)因為精度較低,容易在累加大量數據時產生較大的數值誤差。 >Kahan 求和演算法,又稱為補償求和或進位求和演算法,是一個用來降低有限精度浮點數序列累加值誤差的演算法。 > >在電腦程式中,我們需要用有限位數對實數做近似表示,如今的大多數電腦都使用 IEEE-754 規定的浮點數來作為這個近似表示。對於 $\frac{1}{3}$,由於我們無法在有限位數內對它進行精準表示,因此在使用 IEEE-754 表示法時,必須四捨五入一部分數值(truncate)。這種舍入誤差(Rounding off error)是浮點計算的特徵。 > >---- [Kahan summation](https://oi-wiki.org/misc/kahan-summation/) :::danger 使用課程指定的程式碼縮排風格。 ::: ```c void kahan_sum(float *sum, float input[], int n) { float c = 0.0; // 補償誤差 for (int i = 0; i < n; i++) { float y = input[i] - c; // 去掉誤差部分的輸入值 float t = *sum + y; // 可能存在浮點誤差 c = (t - *sum) - y; // 更新補償誤差 *sum = t; // 更新累計和 } } float kahan_dot_product(float *a, float *b, int n) { float sum = 0.0; for (int i = 0; i < n; i++) { a[i] *= b[i]; // 計算每個元素的乘積 } kahan_sum(&sum, a, n); // 對乘積應用 Kahan 求和 return sum; } ``` 因此,Q8_0 (8位整數量化)權重實際上會有略好一些的困惑度,因為它使用的是 dotprod ISA,這使我們==能將有符號的 8 位整數升到 32 位的計算類型==,從而吸收誤差。這意味著在某些情況下,使用 Q8_0 權重可以得到比 fp16 權重更準確的結果。然而,這並不意味著更快的 fp16 權重不能有用。許多這個領域的開發者認為這些差異是微不足道的。 :::danger 你如何驗證上面陳述是否有效? 建立數學模型,探討量化過程中的誤差議題。拿出你的數理素養出來,避免人云亦云。 ::: 例如,假設在 pihole 上設置一個郵件伺服器並讓 TinyLLaMA 過濾垃圾郵件。可以配置 Postfix 來使用 shell 腳本來過濾內容,透過運行 llamafile 命令,詳細的內容請看[原文 hobbyist 段落](https://justine.lol/matmul/#hobbyist)。 **Performance Gains on Gaming Hardware** > Intel® Core™ i9-14900K ($530) w/ 6400 MT/s RAM - 遊戲硬體的高品質對機器學習產業有很大幫助,因為其高效能可以加速模型的訓練和推理。 | prompt tok/sec | eval tok/sec | model | weights data type | hardware | software | | -------------- | ------------ | -------------- | ----------------- | --------- | ------------- | | 406 | 67 | TinyLlama 1.1B | q8_0 | Alderlake | llamafile-0.7 | | 407 | 42 | TinyLlama 1.1B | f16 | Alderlake | llamafile-0.7 | - Alderlake處理器具有強大的性能,尤其在浮點計算方面有顯著提升。 - 性能核心和效率核心的設計使得高性能計算和日常使用可以更好地平衡。 - 編譯速度的提升表明新硬體在處理大型代碼庫時具有極高的效能。 - 通過使用液態金屬和 AI 超頻技術,這款電腦的編譯速度從 35 秒進一步提升至 20 秒,而原本的 HP 需要 64 秒。 - AVX512指令集的未來改進有望進一步提升計算速度和效能。 **Performance Gains on Apple Hardware** >Mac Studio CPU w/ 24-core M2 Ultra ($5000) - 作者選擇使用 Stallman 的編譯器(如 GCC),而不是 Apple 的專有編譯工具(如 Xcode 和 Clang)。這可能帶來一定的劣勢,因為 Apple 的工具更能夠針對其硬體進行最佳化。 | prompt tok/sec | eval tok/sec | model | weights data type | hardware | software | | -------------- | ------------ | -------------- | ----------------- | --------- | ------------- | | 419 | 66 | TinyLlama 1.1B | q8_0 | M2 Ultra | llamafile-0.7 | | 457 | 95 | TinyLlama 1.1B | f16 | M2 Ultra | llamafile-0.7 | - Apple 的 M2 Ultra 處理器使用了一種稱為==垂直整合==的技術,即將 RAM DIMMs 嵌入到 CPU 內部。這樣做的好處是大大降低了延遲,因為 CPU 不再需要進行遠距離的數據傳輸。 - **作業系統的穩定性**對於保持桌面環境的可靠運行至關重要,macOS和Windows都能很好地保護系統不受用戶不當操作的影響。 - Asahi Linux 提供了一種在 Apple Silicon 上運行 Linux 的解決方案,能夠更好地利用 M2 的硬體特性。 **Performance Gains on Professional Hardware** >AMD Ryzen Threadripper PRO 7995WX w/ 96 cores ($10,000) | prompt tok/sec | eval tok/sec | model | weights data type | hardware | software | | -------------- | ------------ | -------------- | ----------------- | --------- | ------------- | | 1268 | 60 | TinyLlama 1.1B | q8_0 | 7995WX | llamafile-0.7 | | 1819 | 52 | TinyLlama 1.1B | f16 | M7995WX | llamafile-0.7 | - AMD Ryzen Threadripper PRO 7995WX:高性能和高價格並存,是目前市場上最強大的CPU之一,特別適合需要大量計算能力的工作負載。 - 這款 CPU 的 384MB L3 快取有助於提高 token 生成速度 - AVX512指令集:更大向量通助於減少舍入誤差,在處理大量數據時有顯著優勢,對於高性能計算至關重要。 - VDPBF16PS 指令能夠將 bfloat16 進行類似於 VNNI 和 ARM dotprod 的點積運算。 - 對於使用 bfloat16 作為權重的模型(如 Mistral 和 TinyLLaMA),有原生的 bfloat16 支持非常有利。 - bfloat16支持:bfloat16(bf16)能表示更多的數字範圍,而不會像 float16(fp16)那樣僅能準確表示 13% 的可能數字。 - RAM和磁碟性能:在選擇硬件時,需要考慮到不同配置的性能差異和可能的問題,以保證系統穩定和高效運行。 :::info 在 Intel 軟體方面都是較新版本的 llamafile-0.7(發布在 03-31) 表現較好,對於 Mac(108) 在 llama.cpp 2024-03-26 表現最好,而 7995WX(93) 在 llamafile-0.6.2 表現得最好,三種硬體的最佳表現不是都發生在最新版本,會是因為什麼原因? ::: --- ### Technical Details **How I Improved Prompt Eval Time on CPUs** >矩陣乘法是 transformer 模型中的基礎運算之一,通常消耗大量的計算資源。作者研究各種實作方式,做性能優化,並探索不同實現中的權衡。 - Python 0.042 GFLOPS => 使用 NumPy 會更好 - NumPy(基於 FORTRAN)的 <s>實現</s> 29 GFLOPS - C 跟 python 的編譯方式差異很大,導致 C++ 的執行速度快許多,47 GFLOPS - C++ 加上 BLAS <s>庫</s>,在多線程模式下可達到約 85 GFLOPS。而其中最強大的是英特爾的數學內核庫 (MKL),其運算速度為 384 gigaflops。 - 提供的 SLLMM4 函數展示了如何利用指令級並行性和高效的記憶體訪問模式來顯著加速矩陣-矩陣乘法。 **How I Got Multiple Threads to Work** >確保矩陣乘法的高效計算,同時與 Llama.cpp 的線程模型兼容,避免了傳統 BLAS 庫帶來的延遲問題。 - 定義了一個 GEMMER 類來實現矩陣乘法,該類負責處理矩陣的不同部分 - 提供了一個外部<s>接口</s> 函數 SLLMMT,可以被 GGML 調用 :::danger 注意用語: * interface 是「介面」,不是「接口」 * library 針對軟體工程時,是「函式庫」,而非語焉不詳的「庫」。作個有文明的人,使用歷久彌新且講究的詞彙。 ::: ### Methodology 在 Linux 上執行以下命令才能可靠地對 llamafile 進行基準測試。 ```shell echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor ``` 作者有像 BLIS 那樣用<s>彙編語言</s> 編寫內核,配置了 Emacs 可以把組合語言直接反編譯成 C++ 程式碼。 --- ## TODO: 定位出 LLaMA 效能瓶頸 > 重現實驗、量化分析 ### 開發環境 ```bash $ gcc --version gcc (Ubuntu 13.2.0-23ubuntu4) 13.2.0 Copyright (C) 2023 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ lscpu 架構: x86_64 CPU 作業模式: 32-bit, 64-bit Address sizes: 46 bits physical, 48 bits virtual Byte Order: Little Endian CPU(s): 48 On-line CPU(s) list: 0-47 供應商識別號: GenuineIntel Model name: Intel(R) Xeon(R) CPU E5-2650 v4 @ 2.20GHz CPU 家族: 6 型號: 79 每核心執行緒數: 2 每通訊端核心數: 12 Socket(s): 2 製程: 1 CPU max MHz: 2900.0000 CPU min MHz: 1200.0000 BogoMIPS: 4390.21 ``` >Intel(R) Xeon(R) CPU E5-2650 v4 @ 2.20GHz | Launch Date | Total Cores | Max Turbo Frequency | Processor Base Frequency | Cache | TDP | | -------- | -------- | -------- | --- | --- | --- | | Q1'16 | 12 | 2.90 GHz | 2.20 GHz | 30 MB Intel® Smart Cache | 105 W | ### 執行 llamafile (針對 x86-64) - llamafile 結合了 llama.cpp 與 Cosmopolitan Libc,讓 LLM 可以在自己的電腦上執行的專案,為檔案更改權限後執行 `./llava-v1.5-7b-q4.llamafile` 的畫面: - 這是一個範例,是為 LLaVA 模型建立的 llamafile![image](https://hackmd.io/_uploads/SkgBAvOrR.png) - 將 llamafile 與外部權重一起使用 - 首先在 Mozilla-Ocho 的 [release page](https://github.com/Mozilla-Ocho/llamafile/releases) 下載沒有任何權重設置的 llamafile。 - 並且在 huggingface 找到想要使用的模型以及權重的網址(會以 .gguf 結尾),在檔案列表的下載圖案上,按右鍵複製鏈結即可。 - 這是模型 [Mistral-7B-v0.1](https://huggingface.co/TheBloke/Mistral-7B-v0.1-GGUF/tree/main) 的網址 - 最後下<s>指令</s> : ``` curl -L -o llamafile.exe https://github.com/Mozilla-Ocho/llamafile/releases/download/0.8/llamafile-0.8 curl -L -o mistral.gguf https://huggingface.co/TheBloke/Mistral-7B-v0.1-GGUF/resolve/main/mistral-7b-v0.1.Q4_0.gguf?download=true ./llamafile.exe -m mistral.gguf ``` - 使用 GPU 的話指令是 `./llamafile.exe -m mistral.gguf -ngl 99` - 從 [GGUF](https://huggingface.co/docs/hub/gguf) 的網頁擷取量化權重的對照表格 | Quantization type | description | | ----------------- | ------------------------------------------------------------------- | | Q4_0 | 4-bit round-to-nearest quantization (q). Each block has 32 weights. | | Q4_K | 4-bit quantization(q). Super-blocks with 8 blocks, each block has 32 weights. | | Q8_0 | 8-bit round-to-nearest quantization (q). Each block has 32 weights. | | F16 (FP16) | 16-bit standard IEEE 754 half-precision floating-point number. | - Q4_0 使用四位元(4-bit)來表示權重,這意味著每個權重值可以用16個不同的數值來表示。 - 採用 group-wise 量化技術,這意味著權重被分成了許多小的組(block),每個組的大小(blocksize)為32。 - 雖然 Q4_0 曾經被使用,但目前社群已經較少使用這種方法,轉而採用更新、更高效的量化技術。 - Q4_K 方法也不如現代量化技術普及,但它比 Q4_0 具有較高的精度。 - 這樣用<s>指令</s> 執行模型,要怎麼評估 prompt/sec? - 使用 Bash 試試看: ```bash #!/bin/bash MODEL_FILE="mistral.gguf" EXECUTABLE="./llamafile.exe" PROMPT="The following is a conversation between a Researcher and their helpful AI assistant Digital Athena which is a large language model trained on the sum of human knowledge.Researcher: Good morning.Digital Athena: How can I help you today? Researcher:'" # 紀錄開始時間 START_TIME=$(date +%s.%N) # 執行模型並計算總的 token 數量 TOTAL_PROMPTS=1 echo "$PROMPT" | $EXECUTABLE -m $MODEL_FILE -e -p $PROMPT # 紀錄結束時間 END_TIME=$(date +%s.%N) # 計算經過的時間(秒) ELAPSED_TIME=$(echo "$END_TIME - $START_TIME" | bc) # 計算每秒的 prompts 數量 PROMPTS_PER_SEC=$(echo "$TOTAL_PROMPTS / $ELAPSED_TIME" | bc -l) echo "Prompt per second: $PROMPTS_PER_SEC" ``` - 但這樣會記錄到模型初始化的時間,所以改成另一個版本,使用 python 去 call 伺服器,伺服器已經開好,就不會計算到這段初始化時間了。 - 閱讀伺服器設定後,呼叫 API 的部分: ```python # LlamaFile 模型 API 伺服器 URL url = "http://127.0.0.1:8080/completion" headers = {"Content-Type": "application/json"} # 定義請求的 payload payload = { "prompt": myprompt, "n_predict": 128 } response = requests.post(url, headers=headers, json=payload) ``` - 計算 token/sec 的部分: ```python generated_tokens = response.json()["content"] eval_tok_sec = len(generated_tokens) / (end_time - start_time) ``` - Evaluation tokens per second: 17.20 - 計算 prompt/sec 的部分: ```python generated_tokens = response.json()["content"] prompt_time = end_time - start_time - len(generated_tokens)/eval_tok_sec prompt_tok_sec = len(prompt2)/ prompt_time ``` - Prompt per second: 102.79 - 後來發現將 n_predict 設為 0,模型不會生成句子,可以直接算 prompt/ token,下面是修改後的程式: ```diff payload = { "prompt": myprompt, - "n_predict": 128 + "n_predict": 0 } generated_tokens = response.json()["content"] - prompt_time = end_time - start_time - len(generated_tokens)/eval_tok_sec + prompt_time = end_time - start_time prompt_tok_sec = len(prompt2)/ prompt_time ``` - 因為原始辦法計算出的 token/sec 只有 19,實在是太低。發現 token 應該不是指有幾個英文字母,嘗試使用 mistral 模型原本的 tokenizer,來仿照模型進行斷詞。 - `conda create -n linuxLLama python=3.8` - `conda activate linuxLLama` - 觀察 [mistral-common](https://github.com/mistralai/mistral-common)後,處理斷詞的函式: ```python def countToken(generated_text): # 使用 Mistral 模型的 tokenizer tokenizer_v1 = MistralTokenizer.v1() tokenized = tokenizer_v1.encode_chat_completion( ChatCompletionRequest( messages = [ UserMessage(content = generated_text)])) return len(tokenized.tokens) ``` - 後來發現 llamafile 有提供斷詞的 API,所以有了第二種斷詞的方法。 ```python urlToken = "http://127.0.0.1:8080/tokenize" text2token = requests.post(urlToken, headers=headers, json={"content": generated_text}) tokens = len(text2token.json()['tokens']) ``` - 在評估的計算方法上也有所更改,先前使用提示長度/秒數,現在更新成提示的 token 數量/秒數。 - 在多次實驗過程中,我發現了一個異常現象:將長度不同的句子輸入 mistral-common 這個 tokenizer 後,其輸出長度均為 137。經過進一步檢查,我注意到這是由於在生成過程中我設定了 n_predict 參數為 128,而 mistral 的 tokenizer 始終輸出 137 的結果未能引起我的注意。隨後,我改用 llamafile 的 API 進行分詞,明顯發現所有句子的長度均為 129,這才讓我意識到這一現像是由 n_predict 參數所引起的。 - 發現有極端值出現的情況,所以在最後算平均時做了去頭去尾的處理: ```python def final(sec_list): sec_list.remove(min(sec_list)) sec_list.remove(max(sec_list)) return sum(sec_list)/len(sec_list) ``` **對於 Mistral 7B 的實驗** - 實驗使用的模型下載來源紀錄: - [TheBloke/Mistral-7B-v0.1-GGUF](https://huggingface.co/TheBloke/Mistral-7B-v0.1-GGUF/tree/main) - [Undi95/Mistral-RP-0.1-7B-GGUF](https://huggingface.co/Undi95/Mistral-RP-0.1-7B-GGUF/tree/main?not-for-all-audiences=true) | weights data type | creater | | ----------------- | ----------------------------- | | q4_0 | TheBloke/Mistral-7B-v0.1-GGUF | | q8_0 | TheBloke/Mistral-7B-v0.1-GGUF | | fp_16 | Undi95/Mistral-RP-0.1-7B-GGUF | **Mistral 7B llamafile 實驗結果:** | prompt (tok/sec) | eval (tok/sec) | model | weights data type | hardware | software | tokenizer | | ---------------- | -------------- | --------------- | ----------------- | --------------------------- | ------------- | -------------- | | 22.03 | 5.99 | Mistral 7B v0.1 | q4_0 | Products formerly Broadwell | llamafile-0.8 | llamafile | | 25.04 | 5.42 | Mistral 7B v0.1 | q8_0 | Products formerly Broadwell | llamafile-0.8 | llamafile | | 13.25 | 2.89 | Mistral 7B v0.1 | fp_16 | Products formerly Broadwell | llamafile-0.8 | llamafile | | 31.85 | 7.10 | Mistral 7B v0.1 | q4_0 | Products formerly Broadwell | llamafile-0.8 | Mistral Common | | 38.20 | 6.77 | Mistral 7B v0.1 | q8_0 | Products formerly Broadwell | llamafile-0.8 | Mistral Common | | 30.75 | 3.02 | Mistral 7B v0.1 | fp_16 | Products formerly Broadwell | llamafile-0.8 | Mistral Common | **實驗觀察** `eval` 會隨著權重增加而下降,跟原本實驗的觀察相符。 關於額外的斷詞實驗,由於 Mistral 模型的斷詞方式會加入許多特殊字元,例如 "hi im Lisa" 會被轉換成 `'</s>▁[INST]▁hi▁im▁Lisa▁[/INST]'`,長度為 11,這使得 `eval` 的結果可能高於使用 LlamaFile 斷詞的情況。 --- **對於 Mistral 1.1B 的實驗** 然而,在發送跟先前 7B 大小模型的相同請求時,發現了模型完全不產生回應的情況。即使嘗試將 n_predict 數值調高,或加入 minkeep 參數,模型仍然無法生成回應。因此,我決定調換評估順序,並在 prompt 中輸入文字。 關於 prompt/tok 的計算方法並沒有更改,但是在 eval/tok 函數上做了一些調整: ```diff generated_text = response.json()["content"] text2token = requests.post(urlToken, headers=headers, json={"content": generated_text}) tokens = len(text2token.json()['tokens']) - eval_tok_sec = tokens / (end_time - start_time) + eval_tok_sec = tokens / (end_time - start_time - prompt_tokens / prompt_tok_sec) ``` - 實驗使用的模型下載來源紀錄: - [afrideva/malaysian-mistral-1.1B-4096-GGUF ](https://huggingface.co/afrideva/malaysian-mistral-1.1B-4096-GGUF/tree/main) | weights data type | creater | | ----------------- | ----------------------------- | | q4_k | afrideva/malaysian-mistral-1.1B-4096-GGUF | | q8_0 | afrideva/malaysian-mistral-1.1B-4096-GGUF | | fp_16 | afrideva/malaysian-mistral-1.1B-4096-GGUF | **Mistral 1.1B llamafile 實驗結果:** | prompt (tok/sec) | eval (tok/sec) | model | weights data type | hardware | software | tokenizer | | ---------------- | -------------- | ----------------- | ----------------- | --------------------------- | ------------- | -------------- | | 100.64 | 35.05 | Mistral 1.1B v0.1 | q4_k | Products formerly Broadwell | llamafile-0.8 | llamafile | | 158.68 | 29.26 | Mistral 1.1B v0.1 | q8_0 | Products formerly Broadwell | llamafile-0.8 | llamafile | | 129.98 | 8.74 | Mistral 1.1B v0.1 | fp_16 | Products formerly Broadwell | llamafile-0.8 | llamafile | | 138.14 | 32.26 | Mistral 1.1B v0.1 | q4_k | Products formerly Broadwell | llamafile-0.8 | Mistral Common | | 185.32 | 24.89 | Mistral 1.1B v0.1 | q8_0 | Products formerly Broadwell | llamafile-0.8 | Mistral Common | | 130.87 | 11.25 | Mistral 1.1B v0.1 | fp_16 | Products formerly Broadwell | llamafile-0.8 | Mistral Common | --- ### 執行 llama.cpp (針對 x86-64) > [llama.cpp github](https://github.com/ggerganov/llama.cpp) - [如何使用 llama.cpp](https://medium.com/@yvontarbajo/%E9%BA%BB%E7%93%9C%E8%B5%B0%E5%85%A5%E9%AD%94%E6%B3%95%E4%B8%96%E7%95%8C-ii-llm-in-house-0b178a62084f) ``` git clone https://github.com/ggerganov/llama.cpp cd llma.cpp make ./llama-server -m ../models/mistral_1b_q4_k.gguf -ngl 99 ``` **評估過程記錄** 我發現 llama.cpp 的伺服器 API 與 llamafile 完全相容,最初計劃直接使用相同的程式碼來進行效能評估。 以下是基於 llama.cpp 伺服器 API 和原本 Mistral 模型所使用的斷詞器的兩種斷詞方法所得到的輸出結果: ``` prompt: Hi, I'm Lisa. API:{'tokens': [28023, 15, 355, 10, 80, 413, 1922, 17]}, length:8 mistral-common:<s>▁[INST]▁Hi,▁I'm▁Lisa.▁[/INST], length:15 ``` **Mistral 1.1B llama.cpp 實驗結果:** > 使用兩種斷詞方法的比較實驗: | prompt (tok/sec) | eval (tok/sec) | model | weights data type | hardware | software | tokenizer | | ---------------- | -------------- | ----------------- | ----------------- | --------------------------- | ------------------- | -------------- | | 169.21 | 33.68 | Mistral 1.1B v0.1 | q4_k | Products formerly Broadwell | llama.cpp 2024-6-19 | llamafile | | 154.77 | 22.68 | Mistral 1.1B v0.1 | q8_0 | Products formerly Broadwell | llama.cpp 2024-6-19 | llamafile | | 132.90 | 13.84 | Mistral 1.1B v0.1 | fp_16 | Products formerly Broadwell | llama.cpp 2024-6-19 | llamafile | | 218.59 | 36.09 | Mistral 1.1B v0.1 | q4_k | Products formerly Broadwell | llama.cpp 2024-6-19 | Mistral Common | | 202.57 | 23.57 | Mistral 1.1B v0.1 | q8_0 | Products formerly Broadwell | llama.cpp 2024-6-19 | Mistral Common | | 214.27 | 14.15 | Mistral 1.1B v0.1 | fp_16 | Products formerly Broadwell | llama.cpp 2024-6-19 | Mistral Common | **Mistral 7B llama.cpp 實驗結果:** > 使用兩種斷詞方法的比較實驗: | prompt (tok/sec) | eval (tok/sec) | model | weights data type | hardware | software | tokenizer | | ---------------- | -------------- | --------------- | ----------------- | --------------------------- | ------------- | -------------- | | 21.38 | 5.66 | Mistral 7B v0.1 | q4_0 | Products formerly Broadwell | llama.cpp 2024-6-19 | llamafile | | 21.51 | 3.78 | Mistral 7B v0.1 | q8_0 | Products formerly Broadwell | llama.cpp 2024-6-19 | llamafile | | 14.80 | 2.19 | Mistral 7B v0.1 | fp_16 | Products formerly Broadwell | llama.cpp 2024-6-19 | llamafile | | 35.61 | 6.03 | Mistral 7B v0.1 | q4_0 | Products formerly Broadwell | llama.cpp 2024-6-19 | Mistral Common | | 35.32 | 4.15 | Mistral 7B v0.1 | q8_0 | Products formerly Broadwell | llama.cpp 2024-6-19 | Mistral Common | | 22.58 | 2.37 | Mistral 7B v0.1 | fp_16 | Products formerly Broadwell | llama.cpp 2024-6-19 | Mistral Common | 關於兩種軟體的實作實驗,通過實驗數據我們可以得出以下結論: 1. 斷詞器影響: 使用 Mistral Common 斷詞器的效能普遍高於 llamafile 斷詞器,尤其是在prompt階段的提升更為顯著。這表明 Mistral Common 斷詞器在處理特定模型和權重時具有更高的效率。 2. 權重類型: 不同的權重類型對效能有明顯影響。輕量化的 q4_0 和 q8_0 權重在 prompt 和 eval 階段的效能均優於 fp_16,這符合量化模型通常能在保持一定精度的同時提高效能的預期。 3. 模型大小: Mistral 1.1B 模型的效能顯著高於 Mistral 7B 模型,這可能是由於模型結構和優化策略的不同所致。 4. 軟體<s>實現</s> : llama.cpp 能夠與 llamafile 完全相容。 ### 兩種軟體對照結果 >先前是不同斷詞方式的對照實驗,再來進入到兩種軟體的對照實驗: **Mistral 7b 兩種軟體對照結果表格** | prompt (tok/sec) | eval (tok/sec) | model | weights data type | hardware | software | tokenizer | | ---------------- | -------------- | --------------- | ----------------- | --------------------------- | ------------------- | --------- | | 22.03 | 5.99 | Mistral 7B v0.1 | q4_0 | Products formerly Broadwell | llamafile-0.8 | llamafile | | 21.38 | 5.66 | Mistral 7B v0.1 | q4_0 | Products formerly Broadwell | llama.cpp 2024-6-19 | llamafile | | 25.04 | 5.42 | Mistral 7B v0.1 | q8_0 | Products formerly Broadwell | llamafile-0.8 | llamafile | | 21.51 | 3.78 | Mistral 7B v0.1 | q8_0 | Products formerly Broadwell | llama.cpp 2024-6-19 | llamafile | | 13.25 | 2.89 | Mistral 7B v0.1 | fp_16 | Products formerly Broadwell | llamafile-0.8 | llamafile | | 14.80 | 2.19 | Mistral 7B v0.1 | fp_16 | Products formerly Broadwell | llama.cpp 2024-6-19 | llamafile | **Mistral 1.1B 兩種軟體對照結果表格** | prompt (tok/sec) | eval (tok/sec) | model | weights data type | hardware | software | tokenizer | | ---------------- | -------------- | ------------------ | ----------------- | --------------------------- | ------------------- | --------- | | 100.64 | 35.05 | Mistral 1.1B v0.1 | q4_k | Products formerly Broadwell | llamafile-0.8 | llamafile | | 169.21 | 33.68 | Mistral 1.1B v0.1 | q4_k | Products formerly Broadwell | llama.cpp 2024-6-19 | llamafile | | 158.68 | 29.26 | Mistral 1.1B v0.1 | q8_0 | Products formerly Broadwell | llamafile-0.8 | llamafile | | 154.77 | 22.68 | Mistral 1.1B v0.1 | q8_0 | Products formerly Broadwell | llama.cpp 2024-6-19 | llamafile | | 143.88 | 21.87 | Mistral 1.1B v0.1 | fp_16 | Products formerly Broadwell | llamafile-0.8 | llamafile | | 132.90 | 13.84 | Mistral 1.1B v0.1 | fp_16 | Products formerly Broadwell | llama.cpp 2024-6-19 | llamafile | 上下測試模型皆為 Mistral,只是上方參數大小為 7B,而下方為 1.1B。 第一個實驗 Mistral 7B v0.1: 1. 在 q4_0 和 q8_0 量化權重下,llamafile 和 llama.cpp 的效能相近,但前者略勝一籌。 2. 在 fp_16 權重下,llamafile 在 prompt 和 eval 兩個階段的效能均高於 llama.cpp。 3. 整體而言,llamafile 在 Mistral 7B v0.1 模型中的效能略高於 llama.cpp。 第二個實驗 Mistral 1.1B v0.1: 1. 在 q4_k 權重下,llama.cpp 的 prompt 效能顯著高於 llamafile,但 eval 效能稍低。 2. 在 q8_0 和 fp_16 權重下,llamafile 在 eval 階段的效能均高於 llama.cpp,但在 prompt 階段則稍低。 3. 整體來看,llamafile 和 llama.cpp 各有優劣,具體效能依權重類型而異。 :::danger 開發紀錄務必依據以下: * 資訊科技詞彙翻譯: https://hackmd.io/@sysprog/it-vocabulary * 詞彙對照表: https://hackmd.io/@l10n-tw/glossaries :::