開始用 RAG 處理資料後,發現抓出的內容似乎有點落差,而影響查詢結果最重要的就是 Embedding,原本想說 Open AI 的 embedding 模型價格也不算便宜,應該還不至於太差吧,直到看到 ihower 大大的 繁體中文 Embedding 評測,才發現不是貴的就最好
拿 Spring AI Embedding 預設的 text-embedding-ada-002
模型來說,繁體中文的準確率竟然排到 24 名,而 text-embedding-3-small
價格便宜了 5 倍,準確率竟還高了一名,至於最貴的text-embedding-3-large
排名也只到 13,雖然命中率勉強破 9 成,不過跟第一名的 voyage-multilingual-2
差了將近 7%
另外這數據還有個頗重要的觀念,dimensions 不是越高就越好,拿第四名的微軟 multilingual-e5-small
來看,其 dimensions 竟然只有 384
dimensions 越高運算就越花時間,dimensions 少的話運算速度跟儲存空間都能有效降低,multilingual-e5-small
這模型真的非常優秀,唯一的缺點就是一次只能處理 512 個 Tokens,另外這是開源模型,只能在自己機器或是雲端伺服器部屬(可以參考 Azure 的雲端方案 https://learn.microsoft.com/zh-tw/azure/postgresql/flexible-server/azure-local-ai)
至於付費的部分若要便宜首推 Open AI 的 text-embedding-3-small
,我們之前寫的程式只需增加一個模型的參數,對原本就使用 Open AI 的人來說可以做到最小的變動
spring:
ai:
openai:
api-key: ${OPENAI_KEY}
chat:
options:
model: gpt-4o-mini
embedding:
options:
model: text-embedding-3-small
不過凱文大叔主要想提升 RAG 的準確率,所以直接挑第一名的 voyage-multilingual-2
進行測試,官網(https://www.voyageai.com/) 註冊後即可建立 API Key
可以看到上方一些免費試用的限制,不過 5千萬的 Tokens 測試也綽綽有餘了,凱文大叔將這次鐵人賽 30 天的文章進行 embedding 後也才12萬多的 tokens
由於 Spring AI 沒提供 Voyage 的模型串接,凱文大叔就直接用 Open AI 的設定試看看
spring:
ai:
openai:
api-key: ${OPENAI_KEY}
chat:
options:
model: gpt-4o-mini
embedding:
base-url: https://api.voyageai.com/v1/embeddings
api-key: ${VOYAGE_KEY}
options:
model: voyage-multilingual-2
dimensions: 1024
程式的部分使用之前測試 Embedding 的 Controller
@RestController
@RequiredArgsConstructor
public class EmbeddingController {
private final EmbeddingModel embeddingModel;
@GetMapping("/embedding")
public List<Double> embed(String message) {
return embeddingModel.embed(message);
}
}
輸入網址測試看看 http://localhost:8080/embedding?message=這是voyageai的Embedding測試
很不幸的直接報錯,來看看原始碼吧,希望不用自己寫個 API
public <T> ResponseEntity<EmbeddingList<Embedding>> embeddings(EmbeddingRequest<T> embeddingRequest) {
Assert.notNull(embeddingRequest, "The request body can not be null.");
Assert.notNull(embeddingRequest.input(), "The input can not be null.");
Assert.isTrue(embeddingRequest.input() instanceof String || embeddingRequest.input() instanceof List,
"The input must be either a String, or a List of Strings or List of List of integers.");
if (embeddingRequest.input() instanceof List list) {
Assert.isTrue(!CollectionUtils.isEmpty(list), "The input list can not be empty.");
Assert.isTrue(list.size() <= 2048, "The list must be 2048 dimensions or less");
Assert.isTrue(list.get(0) instanceof String || list.get(0) instanceof Integer
|| list.get(0) instanceof List,
"The input must be either a String, or a List of Strings or list of list of integers.");
}
return this.restClient.post()
.uri("/v1/embeddings")
.body(embeddingRequest)
.retrieve()
.toEntity(new ParameterizedTypeReference<>() {
});
}
看最後 return 的部分.就是使用 RestClient 來呼叫,uri 的部分竟然直接加上 "/v1/embeddings"
難怪會失敗,這樣把 base-url 的 /v1/embeddings 拿掉即可,好在後面字串都一樣,不用再多寫一個
其實要自己寫也不難,程式幾乎一樣,只差在 URL 而已
base-url: https://api.voyageai.com
再試一次,還是報錯…
400 - {"detail":"The request body is not valid JSON, or some arguments were not specified properly. In particular, Argument 'dimensions' is not supported by our API"}
不過錯誤不太一樣,看起來是不需要傳送 dimensions 參數,拿掉再測試看看
總算成功了,等後續凱文大叔將 ETL 跟查詢部分改成 Voyage 的 Embedding 模型,再來看看近似搜尋會不會比較準
雖然目前 Open AI 還是市佔率第一,不過不同的模型都有自己的特色,未來開發 AI 應用應該會拿不同的模型組合應用,才能展現各個模型的優勢
今天學到了甚麼?
程式碼下載: https://github.com/kevintsai1202/SpringBoot-AI-Day32.git
凱文大叔使用 Java 開發程式超過 20 年,對於 Java 生態非常熟悉,曾使用反射機制開發 ETL 框架,對 Spring 背後的原理非常清楚,目前以 Spring Boot 作為後端開發框架,前端使用 React 搭配 Ant Design
下班之餘在 Amazing Talker 擔任程式語言講師,並獲得學員的一致好評
最近剛成立一個粉絲專頁-凱文大叔教你寫程式 歡迎大家多追蹤,我會不定期分享實用的知識以及程式開發技巧
想討論 Spring 的 Java 開發人員可以加入 FB 討論區 Spring Boot Developer Taiwan
我是凱文大叔,歡迎一起加入學習程式的行列