本地大模型LocalAI使用教程指南

banq發表於2024-03-24

 LocalAI是 OpenAI 的開源替代品,它能在本地計算機上執行 LLM。不需要 GPU,消費級硬體就足夠了

如果您可以使用與 OpenAI 相同的 Rest API 在本地執行模型,那不是很棒嗎?嗯,這正是LocalAI必須為您提供的!

 LocalAI 是 OpenAI 的開源替代方案,具有與 OpenAI API 規範相容的 Rest API。除此之外,不需要 GPU,您可以在消費級硬體上執行它。不過,建議使用 GPU,因為它的速度大約快 20 倍。

此處描述了 LocalAI for CPU 的安裝。

1、克隆 LocalAI git 儲存庫。
$ git clone https://github.com/go-skynet/LocalAI

2、導航到儲存庫目錄。
$ cd LocalAI

3、儲存庫包含.env您需要自定義的檔案。

  • 取消註釋THREADS:並將數字調整為您擁有的物理核心數量(在我的例子中為 12);
  • 取消註釋GALLERIES:並將其調整為安裝指南中所述的galleries 。

## Set number of threads.
## Note: 優先選擇物理核心數量。過多的 CPU 會明顯降低效能。
THREADS=12
 
## Specify a different bind address (defaults to <font>":8080")
# ADDRESS=127.0.0.1:8080
 
## Default models context size
# CONTEXT_SIZE=512
#
## Define galleries.
## 要安裝的模型將在 `/models/available` 中顯示
GALLERIES=[{
"name":"model-gallery", "url":"github:go-skynet/model-gallery/index.yaml"}, {"url": "github:go-skynet/model-gallery/huggingface.yaml","name":"huggingface"}]


4、啟動 Docker 容器。
Docker 映象引用的是最新標籤,在撰寫本文時,LocalAI 的 v2.0.0 是最新標籤。你可以導航到映象倉庫,搜尋最新標籤,複製清單雜湊值,然後搜尋複製的清單雜湊值。

$ docker compose up -d --pull always

請耐心等待,這需要一些時間。映象檔案約為 70GB。上一版本 v1.40.0 大約為 14GB。

當容器成功啟動後,你應該可以檢索到可用的模型:

$ curl http://localhost:8080/models/available

安裝模型
首先,您需要安裝一個模型。您可以透過應用程式介面的模型相簿進行安裝,但在撰寫本文時,這仍處於試驗階段。我更喜歡手動新增模型。說明可以在這裡找到,但要知道,隨著時間的推移,說明可能會發生變化,所以不要完全依賴本段的內容。

在 models 目錄下建立 lunademo.yaml 檔案。將執行緒數改為你機器上的物理核心數。

name: lunademo
parameters:
  model: luna-ai-llama2-uncensored.Q5_K_M.gguf
  top_k: 80
  temperature: 0.2
  top_p: 0.7
context_size: 1024
threads: 12
backend: llama
roles:
  assistant: 'ASSISTANT:'
  system: 'SYSTEM:'
  user: 'USER:'
template:
  chat: lunademo-chat
  completion: lunademo-completion


模型指的是包含模型的檔案。從 HuggingFace 將檔案下載到模型目錄。HuggingFace 包含許多您可以使用的開源模型,但在本示例中,您將使用基於 Llama 2 的模型,Llama 2 是由 Meta 建立的人工智慧模型。請注意,在 "模型卡 "中,模型與其用例一起列出。此外,用例中還說明了建議使用的模型。請注意只能使用 GGUF 模型,Llama 2 不再支援 GGML。

還要注意的是,模型的配置檔案中定義了兩個模板。一個是聊天模板,另一個是完成模板。

在模型目錄中建立一個檔案 lunademo-chat.tmpl。該模板來自 HuggingFace 的模型卡(搜尋 Prompt 模板)。

USER: {{.Input}}
 
ASSISTANT:

在模型目錄下建立 lunademo-completion.tmpl 檔案。

Complete the following sentence: {{.Input}}

重啟 Docker 容器以載入模型。

$ docker compose restart

提問
既然模型已經載入,您就可以開始提問了。您可以檢視 OpenAPI 規範,以下是一些示例,以驗證本地模型的響應情況和準確性。

你好嗎?
作為第一個簡單的例子,您可以詢問模型感覺如何。在請求中,您可以提及要使用的模型、資訊,還可以設定溫度。溫度越高,模型就越有創造力。模型回答說它做得很好。

$ curl http:<font>//localhost:8080/v1/chat/completions -H "Content-Type: application/json" -d '{<i>
     
"model": "lunademo",
     
"messages": [{"role": "user", "content": "How are you?"}],
     
"temperature": 0.9 
   }'
{
   
"created":1700993538,
   
"object":"chat.completion",
   
"id":"2fe33052-f4be-4724-8b53-fdade80b49de",
   
"model":"lunademo",
   
"choices":[
      {
         
"index":0,
         
"finish_reason":"stop",
         
"message":{
           
"role":"assistant",
           
"content":"I'm doing well, thank you. How about yourself?"
         }
      }
   ],
   
"usage":{
     
"prompt_tokens":0,
     
"completion_tokens":0,
     
"total_tokens":0
   }
}


整合LangChain4j
上面展示瞭如何透過 LocalAI 執行類似於 OpenAI 的大型語言模型 (LLM)。

使用 OpenAI 的 Rest API 來與 LocalAI 互動。將這些功能整合到 Java 應用程式中可能會很麻煩。

LangChain4j 為您提供了與大模型整合的簡化方法。它基於 Python 庫LangChain。

LangChain4j 示例儲存庫中提供了許多示例。特別是,目錄中的示例other-examples已被用作此部落格的靈感。
本部落格中使用的資源可以在GitHub上找到。

為了將 LangChain4j 與 LocalAI 結合使用,您需要將langchain4j-local-ai依賴項新增到 pom 檔案中。

<dependency>
  <groupId>dev.langchain4j</groupId>
  <artifactId>langchain4j-local-ai</artifactId>
  <version>0.24.0</version>
</dependency>

為了與 LocalAI 整合,您需要建立一個指定以下專案的 ChatLanguageModel:

  • 可訪問 LocalAI 例項的 URL;
  • 您希望在 LocalAI 中使用的模型名稱;
  • 溫度,溫度越高,模型的響應越有創意。

然後,要求模型生成問題的答案並列印出來。

ChatLanguageModel model = LocalAiChatModel.builder()
        .baseUrl(<font>"http://localhost:8080")
        .modelName(
"lunademo")
        .temperature(0.9)
        .build();
 
String answer = model.generate(
"How are you?");
System.out.println(answer);

啟動 LocalAI 並執行上述示例。

響應與預期一致。
I'm doing well, thank you. How about yourself?

LanguageModel 和 ChatLanguageModel 之間的區別
這兩個類在 LangChain4j 中都有,那麼該選哪個呢?

  • 聊天模型是語言模型的一種變體。如果您需要 "文字輸入,文字輸出 "功能,您可以選擇 LanguageModel。
  • 如果還想使用 "聊天資訊 "作為輸入和輸出,則應使用 ChatLanguageModel。

在上面的示例中,你可以只使用 LanguageModel,它的表現也會類似。

嵌入文件
嵌入文件的最簡單方法是閱讀文件,將其分割成塊,然後嵌入這些塊。嵌入意味著將文字轉換為向量(數字)。您要提出的問題也需要嵌入。

向量儲存在一個向量儲存庫中,該儲存庫能找到與您的問題最接近的結果,並用這些結果作出回應。原始碼source code由以下部分組成:

  • 文字需要嵌入。為此需要一個嵌入模型,為簡單起見,您可以使用 AllMiniLmL6V2EmbeddingModel。該模型使用 BERT 模型,這是一種流行的嵌入模型。
  • 嵌入需要儲存在一個嵌入儲存庫中。通常情況下,向量資料庫會被用於此目的,但在這種情況下,你可以使用記憶體中的嵌入儲存。
  • 讀取兩個文件並將其新增到 DocumentSplitter 中。在這裡,您將定義將文件分割成 500 個字元且不重疊的片段。
  • 透過文件分割器,文件被分割成文字段。
  • 嵌入模型用於嵌入文字段。TextSegments 及其嵌入的對應內容都儲存在嵌入儲存區中。
  • 問題也使用相同的模型嵌入。
  •  
  • 要求嵌入儲存查詢與嵌入問題相關的嵌入片段。您可以定義儲存應檢索多少個結果。

在本例中,只要求一個結果:如果找到匹配結果,控制檯將列印以下資訊:
  • The score得分:表示結果與問題匹配程度的數字;
  • The original text原文:片段的文字;
  • The meta data後設資料:將顯示片段來自哪個文件。

private static void askQuestion(String question) {
    EmbeddingModel embeddingModel = new AllMiniLmL6V2EmbeddingModel();
 
    EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
 
    <font>//讀取檔案並將其分割成 500 塊的片段<i>
    Document springsteenDiscography = loadDocument(toPath(
"example-files/Bruce_Springsteen_discography.pdf"));
    Document springsteenSongList = loadDocument(toPath(
"example-files/List_of_songs_recorded_by_Bruce_Springsteen.pdf"));
    ArrayList<Document> documents = new ArrayList<>();
    documents.add(springsteenDiscography);
    documents.add(springsteenSongList);
 
    DocumentSplitter documentSplitter = DocumentSplitters.recursive(500, 0);
    List<TextSegment> documentSegments = documentSplitter.splitAll(documents);
 
   
// 嵌入分段<i>
    Response<List<Embedding>> embeddings = embeddingModel.embedAll(documentSegments);
    embeddingStore.addAll(embeddings.content(), documentSegments);
 
   
//嵌入問題並查詢相關片段<i>
    Embedding queryEmbedding = embeddingModel.embed(question).content();
    List<EmbeddingMatch<TextSegment>> embeddingMatch = embeddingStore.findRelevant(queryEmbedding,1);
    System.out.println(embeddingMatch.get(0).score());
    System.out.println(embeddingMatch.get(0).embedded().text());
    System.out.println(embeddingMatch.get(0).embedded().metadata());
}

問題如下,是檔案中可以找到的一些事實:

public static void main(String args) {
    askQuestion(<font>"on which album was \&#34adam raised a cain\&#34 originally released?");
    askQuestion(
"what is the highest chart position of \&#34Greetings from Asbury Park, N.J.\&#34 in the US?");
    askQuestion(
"what is the highest chart position of the album \&#34tracks\&#34 in canada?");
    askQuestion(
"in which year was \&#34Highway Patrolman\&#34 released?");
    askQuestion(
"who produced \&#34all or nothin' at all?\&#34");
}

嵌入 Markdown 文件
將 PDF 文件轉換成 Markdown 檔案後會有什麼變化?在 Markdown 檔案中識別表格可能比在 PDF 文件中更好,而且它允許你在行級而不是任意的塊大小上分割文件。只有文件中包含問題答案的部分才會被轉換。這意味著唱片目錄中的錄音室專輯和合輯以及錄製的歌曲列表。

分割過程如下

  • 逐行分割文件;
  • 在變數 dataOnly 中讀取表格資料;
  • 在變數 header 中儲存表頭;
  • 為 dataOnly 中的每一行建立一個文字段,並將頁首新增到文字段中。

 source code :

List<Document> documents = loadDocuments(toPath(<font>"markdown-files"));
 
List<TextSegment> segments = new ArrayList<>();
for (Document document : documents) {
    String splittedDocument = document.text().split(
"\n");
    String dataOnly = Arrays.copyOfRange(splittedDocument, 2, splittedDocument.length);
    String header = splittedDocument[0] +
"\n" + splittedDocument[1] + "\n";
 
    for (String splittedLine : dataOnly) {
        segments.add(TextSegment.from(header + splittedLine, document.metadata()));
    }
}

到目前為止得出的結論是

  • 檔案格式以及檔案分割和嵌入的方式對結果有很大影響;
  • 如果問題使用的術語與文件中的資料接近,則會取得更好的結果。
  • 讀取和嵌入文件的方式似乎對結果影響最大。這種方法的優點是可以顯示多個結果。這樣,您就可以確定哪個結果是正確的。
  • 更改您的問題,使其使用文字段中使用的術語,有助於獲得更好的結果。向量儲存的查詢速度非常快。嵌入需要花費一些時間,但只需要做一次。
  • 在不使用 GPU 的情況下,使用 LLM 獲取結果需要更多時間。

向量資料庫
Weaviate是一個向量資料庫,還支援RAG

可以使用 Wea​​viate、LangChain4j 和 LocalAI 實現 RAG,結果是相當驚人的。以正確的方式嵌入文件、過濾結果並將其提供給大模型是一個非常強大的組合,可用於許多用例。

步驟:

  1. 文件嵌入並儲存在 Weaviate 中;
  2. 嵌入問題並使用 Wea​​viate 執行語義搜尋;
  3. Weaviate 返回語義搜尋結果;
  4. 結果被新增到提示中並饋送到 LocalAI,後者使用 LangChain4j 執行 LLM;
  5. LLM返回問題的答案。

GitHub上找到實現原始碼

 

相關文章