天天看點

使用GGML和LangChain在CPU上運作量化的llama2

使用GGML和LangChain在CPU上運作量化的llama2
來源:DeepHub IMBA本文約3400字,建議閱讀10分鐘本文介紹如何在本地CPU推理上運作量化版本的開源Llama 2。           

Meta AI 釋出了最新一代開源大模型 Llama 2。對比于今年 2 月釋出的 Llama 1,訓練所用的 token 翻了一倍,已經達到了 2 萬億,對于使用大模型最重要的上下文長度限制,Llama 2 也翻了一倍。

在本文,我們将緊跟趨勢介紹如何在本地CPU推理上運作量化版本的開源Llama 2。

使用GGML和LangChain在CPU上運作量化的llama2

量化快速入門

我們首先簡單介紹一下量化的概念:

量化是一種減少用于表示數字或值的比特數的技術。由于量化減少了模型大小,是以它有利于在cpu或嵌入式系統等資源受限的裝置上部署模型。

一種常用的方法是将模型權重從原始的16位浮點值量化為精度較低的8位整數值。

使用GGML和LangChain在CPU上運作量化的llama2

llm已經展示了出色的能力,但是它需要大量的CPU和記憶體,是以我們可以使用量化來壓縮這些模型,以減少記憶體占用并加速計算推理,并且保持模型性能。我們将通過将權重存儲在低精度資料類型中來降低模型參數的精度。

工具和資料

下圖是我們将在這個項目中建構的文檔知識問答應用程式的體系結構。

使用GGML和LangChain在CPU上運作量化的llama2

我們的測試檔案是177頁的曼聯足球俱樂部2022年年報。

為了示範這個項目的量化結果,我們使用一個AMD Ryzen 5 5600X 6核處理器和16GB RAM (DDR4 3600)。

下面是建構這個應用程式時将使用的軟體工具:

1. LangChain

LangChain是一個提供了一組廣泛的內建和資料連接配接器,允許我們連結和編排不同的子產品。可以常見聊天機器人、資料分析和文檔問答等應用。

2. C Transformers

C transformer是一個Python庫,它為使用GGML庫并在C/ c++中實作了Transformers模型。

為了解釋這個事情我們首先要了解GGML:

GGML庫是一個為機器學習設計的張量庫,它的目标是使大型模型能夠在高性能的消費級硬體上運作。這是通過整數量化支援和内置優化算法實作的。

也就是說,llm的GGML版本(二進制格式的量化模型)可以在cpu上高性能地運作。因為我們最終是使用Python的,是以還需要C Transformers庫,它其實就是為GGML模型提供了Python API。

C transformer支援一組標明的開源模型,包括像Llama、GPT4All-J、MPT和Falcon等的流行模型。

使用GGML和LangChain在CPU上運作量化的llama2

3. sentence-transformer

sentence-transformer提供了簡單的方法來計算句子、文本和圖像的嵌入。它能夠計算100多種語言的嵌入。我們将在這個項目中使用開源的all-MiniLM-L6-v2模型。

4. FAISS

Facebook AI相似度搜尋(FAISS)是一個為高效相似度搜尋和密集向量聚類而設計的庫。

給定一組嵌入,我們可以使用FAISS對它們進行索引,然後利用其強大的語義搜尋算法在索引中搜尋最相似的向量。

雖然它不是傳統意義上的成熟的向量存儲(如資料庫管理系統),但它以一種優化的方式處理向量的存儲,以實作有效的最近鄰搜尋。

5. Poetry

Poetry用于設定虛拟環境和處理Python包管理。相比于venv,Poetry使依賴管理更加高效和無縫。這個不是隻做參考,因為conda也可以。

開源LLM

開源LLM領域已經取得了巨大的進步,在HuggingFace的開放LLM排行榜上可以找到模型。為了緊跟時代,我們選擇了最新的開源Llama-2-70B-Chat模型(GGML 8位):

1. Llama 2

它是C Transformers庫支援的開源模型。根據LLM排行榜排名(截至2023年7月),在多個名額中表現最佳。在原來的Llama 模型設定的基準上有了巨大的改進。

2. 模型尺寸:7B

LLM将主要用于總結文檔塊這一相對簡單的任務。是以選擇了7B模型,因為我們在技術上不需要過大的模型(例如65B及以上)來完成這項任務。

3. 微調版:Llama-2-7B-Chat

lama-2- 7b基本模型是為文本補全而建構的,是以它缺乏在文檔問答用例中實作最佳性能所需的微調。而lama-2 - 7b - chat模型是我們的理想候選,因為它是為對話和問答而設計的。該模型被許可(部分)用于商業用途。這是因為經過微調的模型lama-2- chat模型利用了公開可用的指令資料集和超過100萬個人工注釋。

4. 8位量化

考慮到RAM被限制為16GB, 8位GGML版本是合适的,因為它隻需要9.6GB的記憶體而原始的非量化16位模型需要約15gb的記憶體

8位格式也提供了與16位相當的響應品質,而其他更小的量化格式(即4位和5位)是可用的,但它們是以準确性和響應品質為代價的。

建構步驟指導

我們已經了解了各種元件,接下來讓逐漸介紹如何建構文檔問答應用程式。

由于已經有許多教程了,是以我們不會深入到複雜和一般的文檔問答元件的細節(例如,文本分塊,矢量存儲設定)。在本文中,我們将把重點放在開源LLM和CPU推理方面。

1. 資料處理和矢量存儲

這一步的任務是:将文本分割成塊,加載嵌入模型,然後通過FAISS 進行向量的存儲

from langchain.vectorstores import FAISS

 from langchain.text_splitter import RecursiveCharacterTextSplitter

 from langchain.document_loaders import PyPDFLoader, DirectoryLoader

 from langchain.embeddings import HuggingFaceEmbeddings




 # Load PDF file from data path

 loader = DirectoryLoader('data/',

                          glob="*.pdf",

                          loader_cls=PyPDFLoader)

 documents = loader.load()




 # Split text from PDF into chunks

 text_splitter = RecursiveCharacterTextSplitter(chunk_size=500,

                                                chunk_overlap=50)

 texts = text_splitter.split_documents(documents)




 # Load embeddings model

 embeddings = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2',

                                    model_kwargs={'device': 'cpu'})




 # Build and persist FAISS vector store

 vectorstore = FAISS.from_documents(texts, embeddings)

 vectorstore.save_local('vectorstore/db_faiss')
運作上面的Python腳本後,向量存儲将被生成并儲存在名為'vectorstore/db_faiss'的本地目錄中,并為語義搜尋和檢索做好準備。
           

2. 設定提示模闆

我們使用lama-2 - 7b - chat模型,是以需要使用的提示模闆。

一些chat的模闆在這裡不起作用,因為我們的Llama 2模型沒有針對這種會話界面進行專門優化。是以我們需要使用更加直接的模闆,例如:

qa_template = """Use the following pieces of information to answer the user's question.

 If you don't know the answer, just say that you don't know, don't try to make up an answer.

 Context: {context}

 Question: {question}

 Only return the helpful answer below and nothing else.

 Helpful answer:

 """
需要注意的是,相對較小的LLM(如7B),對格式特别敏感。當改變提示模闆的空白和縮進時,可能得到了稍微不同的輸出。
           

3. 下載下傳lama-2 - 7b - chat GGML二進制檔案

由于我們将在本地運作LLM,是以需要下載下傳量化的lama-2 - 7b - chat模型的二進制檔案。

我們可以通過通路TheBloke的Llama-2-7B-Chat GGML頁面來實作,然後下載下傳名為Llama-2-7B-Chat .ggmlv3.q8_0.bin的GGML 8位量化檔案。

使用GGML和LangChain在CPU上運作量化的llama2

下載下傳的是8位量化模型的bin檔案可以儲存在合适的項目子檔案夾中,如/models。

這個頁面還顯示了每種量化格式的更多資訊和詳細資訊:

使用GGML和LangChain在CPU上運作量化的llama2

4. LangChain內建

我們将利用C transformer和LangChain進行內建。也就是說将在LangChain中使用CTransformers LLM包裝器,它為GGML模型提供了一個統一的接口。

from langchain.llms import CTransformers




 # Local CTransformers wrapper for Llama-2-7B-Chat

 llm = CTransformers(model='models/llama-2-7b-chat.ggmlv3.q8_0.bin', # Location of downloaded GGML model

                    model_type='llama', # Model type Llama

                    config={'max_new_tokens': 256,

                            'temperature': 0.01})           

這裡就可以為LLM定義大量配置設定,例如最大令牌、最高k值、溫度和重複懲罰等等,這些參數在我們以前的文章已經介紹過了。

這裡我将溫度設定為0.01而不是0,因為設定成0時,得到了奇怪的響應。

5. 建構并初始化RetrievalQA

準備好提示模闆和C Transformers LLM後,我們還需要編寫了三個函數來建構LangChain RetrievalQA對象,該對象使我們能夠執行文檔問答。

from langchain import PromptTemplate

 from langchain.chains import RetrievalQA

 from langchain.embeddings import HuggingFaceEmbeddings

 from langchain.vectorstores import FAISS




 # Wrap prompt template in a PromptTemplate object

 def set_qa_prompt():

    prompt = PromptTemplate(template=qa_template,

                            input_variables=['context', 'question'])

    return prompt







 # Build RetrievalQA object

 def build_retrieval_qa(llm, prompt, vectordb):

    dbqa = RetrievalQA.from_chain_type(llm=llm,

                                        chain_type='stuff',

                                        retriever=vectordb.as_retriever(search_kwargs={'k':2}),

                                        return_source_documents=True,

                                        chain_type_kwargs={'prompt': prompt})

    return dbqa







 # Instantiate QA object

 def setup_dbqa():

    embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2",

                                        model_kwargs={'device': 'cpu'})

    vectordb = FAISS.load_local('vectorstore/db_faiss', embeddings)

    qa_prompt = set_qa_prompt()

    dbqa = build_retrieval_qa(llm, qa_prompt, vectordb)




    return dbqa
6. 代碼整合
           

最後一步就是是将前面的元件組合到main.py腳本中。使用argparse子產品是因為我們将從指令行将使用者查詢傳遞到應用程式中。

這裡為了評估CPU推理的速度,還使用了timeit子產品。

import argparse

 import timeit




 if __name__ == "__main__":

    parser = argparse.ArgumentParser()

    parser.add_argument('input', type=str)

    args = parser.parse_args()

    start = timeit.default_timer() # Start timer




    # Setup QA object

    dbqa = setup_dbqa()




    # Parse input from argparse into QA object

    response = dbqa({'query': args.input})

    end = timeit.default_timer() # End timer




    # Print document QA response

    print(f'\nAnswer: {response["result"]}')

    print('='*50) # Formatting separator




    # Process source documents for better display

    source_docs = response['source_documents']

    for i, doc in enumerate(source_docs):

        print(f'\nSource Document {i+1}\n')

        print(f'Source Text: {doc.page_content}')

        print(f'Document Name: {doc.metadata["source"]}')

        print(f'Page Number: {doc.metadata["page"]}\n')

        print('='* 50) # Formatting separator




    # Display time taken for CPU inference

    print(f"Time to retrieve response: {end - start}")
           

示例查詢

現在是時候對我們的應用程式進行測試了。我們用以下指令詢問阿迪達斯(曼聯的全球技術贊助商)應支付的最低保證金額:

python main.py "How much is the minimum guarantee payable by adidas?"           

結果如下:

使用GGML和LangChain在CPU上運作量化的llama2

我們成功地獲得了正确響應(即£7.5億),以及語義上與查詢相似的相關文檔塊。

從啟動應用程式并生成響應的總時間為31秒,這是相當不錯的,因為這隻是在AMD Ryzen 5600X(中低檔的消費級CPU)上本地運作它。并且在gpu上運作LLM推理(例如,直接在HuggingFace上運作)也需要兩位數的時間,是以在CPU上量化運作的結果是非常不錯的。

作者:Kenneth Leung

相關資源

本文代碼:https://github.com/kennethleungty/Llama-2-Open-Source-LLM-CPU-Inference

llama2官網:https://ai.meta.com/llama/

曼聯年報:https://ir.manutd.com/~/media/Files/M/Manutd-IR/documents/manu-20f-2022-09-24.pdf

模型下載下傳:https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML