PostgreSQL作為矢量數(shù)據(jù)庫的入門和擴(kuò)展實(shí)踐
譯文譯者 | 李睿
審校 | 重樓
PostgreSQL擁有豐富的擴(kuò)展和解決方案生態(tài)系統(tǒng),使開發(fā)人員能夠?qū)?shù)據(jù)庫用于通用人工智能應(yīng)用程序。這一指南將引導(dǎo)他們完成使用PostgreSQL作為矢量數(shù)據(jù)庫構(gòu)建生成式人工智能應(yīng)用程序所需的步驟。
首先從Pgvector擴(kuò)展開始,它使Postgres具有特定于矢量數(shù)據(jù)庫的功能。然后,將回顧在PostgreSQL上運(yùn)行的人工智能應(yīng)用程序如何提高性能和可擴(kuò)展性的方法。最后,將使用一個(gè)功能齊全的生成式人工智能應(yīng)用程序,向那些前往舊金山的旅客推薦Airbnb的住宿房源。
Airbnb推薦服務(wù)
示例應(yīng)用程序是一項(xiàng)住宿推薦服務(wù)。想象一下,如果旅客計(jì)劃去舊金山旅游,并希望住在金門大橋附近的一個(gè)靠譜社區(qū)。當(dāng)他們進(jìn)入Airbnb推薦服務(wù),輸入提示,應(yīng)用程序就會推薦三個(gè)最相關(guān)的住宿選擇。
該應(yīng)用程序支持兩種不同的模式:
- OpenAI聊天模式:在這一模式下,Node.js后端利用OpenAI聊天通過API和GPT-4模型根據(jù)用戶的輸入生成住宿推薦。雖然這一模式不是本指南的重點(diǎn),但可以進(jìn)行嘗試。
- Postgres嵌入模式:最初,后端使用OpenAI嵌入API將用戶的提示轉(zhuǎn)換為嵌入(文本數(shù)據(jù)的矢量化表示)。接下來,該應(yīng)用程序在Postgres或YugabyteDB(分布式PostgreSQL)中進(jìn)行相似性搜索,以找到與用戶提示匹配的Airbnb屬性。Postgres利用Pgvector擴(kuò)展在數(shù)據(jù)庫中進(jìn)行相似性搜索。本指南將深入研究這個(gè)特定模式在應(yīng)用程序中的實(shí)現(xiàn)。
先決條件
- 可訪問嵌入模型的OpenAI訂閱。
- 最新的Node.js版本
- 最新版本的Docker
使用Pgvector啟動PostgreSQL
Pgvector擴(kuò)展將向量數(shù)據(jù)庫的所有基本功能添加到Postgres中。它允許存儲和處理具有數(shù)千個(gè)維度的向量,計(jì)算向量化數(shù)據(jù)之間的歐幾里得和余弦距離,并執(zhí)行精確和近似的最近鄰搜索。
1.在Docker中用Pgvector啟動一個(gè)Postgres實(shí)例:
Shell
docker run --name postgresql \
-e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=password \
-p 5432:5432 \
-d ankane/pgvector:latest
2.連接到數(shù)據(jù)庫容器并打開一個(gè)psql會話:
Shell
docker exec -it postgresql psql -h 127.0.0.1 -p 5432 -U postgres
3.啟用Pgvector擴(kuò)展:
SQL
create extension vector;
4.確認(rèn)矢量存在于擴(kuò)展列表中:SQL
1 select * from pg_extension;
oid | extname | extowner | extnamespace | extrelocatable | extversion | extconfig | extcondition
-------+---------+----------+--------------+----------------+------------+-----------+--------------
13561 | plpgsql | 10 | 11 | f | 1.0 | |
16388 | vector | 10 | 2200 | t | 0.5.1 | |
(2 rows)
加載Airbnb數(shù)據(jù)集
該應(yīng)用程序使用了Airbnb的數(shù)據(jù)集,該數(shù)據(jù)集包含舊金山7500多處待租房產(chǎn)。每個(gè)列表提供詳細(xì)的屬性描述,包括房間數(shù)量、設(shè)施類型、位置和其他功能。這些信息非常適合針對用戶提示進(jìn)行相似性搜索。
按照下面的步驟將數(shù)據(jù)集加載到已啟動的Postgres實(shí)例中:
1.克隆應(yīng)用程序存儲庫:
Shell
git clone https://github.com/YugabyteDB-Samples/openai-pgvector-lodging-service.git
2.將Airbnb模式文件復(fù)制到Postgres容器中(將{app_dir}替換為應(yīng)用程序目錄的完整路徑):
Shell
docker cp {app_dir}/sql/airbnb_listings.sql postgresql:/home/airbnb_listings.sql
3.從下面的Google Drive位置下載包含Airbnb數(shù)據(jù)的文件。該文件的大小為174MB,包含了使用OpenAI嵌入模型為每個(gè)Airbnb屬性描述生成的嵌入。
4.將數(shù)據(jù)集復(fù)制到Postgres容器(將{data_file_dir}替換為應(yīng)用程序目錄的完整路徑)。
Shell
docker cp {data_file_dir}/airbnb_listings_with_embeddings.csv postgresql:/home/airbnb_listings_with_embeddings.csv
5.創(chuàng)建Airbnb架構(gòu)并將數(shù)據(jù)加載到數(shù)據(jù)庫中:
Shell
# Create schema
docker exec -it postgresql \
psql -h 127.0.0.1 -p 5432 -U postgres \
-a -q -f /home/airbnb_listings.sql
# Load data
docker exec -it postgresql \
psql -h 127.0.0.1 -p 5432 -U postgres \
-c "\copy airbnb_listing from /home/airbnb_listings_with_embeddings.csv with DELIMITER '^' CSV;"
Airbnb的每個(gè)嵌入都是一個(gè)1536維的浮點(diǎn)數(shù)數(shù)組。這是Airbnb房產(chǎn)描述的數(shù)字/數(shù)學(xué)表示。
Shell
docker exec -it postgresql \
psql -h 127.0.0.1 -p 5432 -U postgres \
-c "\x on" \
-c "select name, description, description_embedding from airbnb_listing limit 1"
# Truncated output
name | Monthly Piravte Room-Shared Bath near Downtown !3
description | In the center of the city in a very vibrant neighborhood. Great access to other parts of the city with all modes of public transportation steps away Like the general theme of San Francisco, our neighborhood is a melting pot of different people with different lifestyles ranging from homeless people to CEO''s
description_embedding | [0.0064848186,-0.0030366974,-0.015895316,-0.015803888,-0.02674906,-0.0083198985,-0.0063770646,0.010318241,-0.011003947,-0.037981577,-0.008783566,-0.0005710134,-0.0028015983,-0.011519859,-0.02011404,-0.02023159,0.03325347,-0.017488765,-0.014902675,-0.006527267,-0.027820067,0.010076611,-0.019069154,-0.03239144,-0.013243919,0.02170749,0.011421901,-0.0044701495,-0.0005861153,-0.0064978795,-0.0006775427,-0.018951604,-0.027689457,-0.00033081227,0.0034317947,0.0098349815,0.0034775084,-0.016835712,-0.0013787586,-0.0041632145,-0.0058219694,-0.020584237,-0.007386032,0.012486378,0.012473317,0.005815439,-0.010990886,-0.015111651,-0.023366245,0.019069154,0.017828353,0.030249426,-0.04315376,-0.01790672,0.0047444315,-0.0053419755,-0.02195565,-0.0057338076,-0.02576948,-0.009769676,-0.016914079,-0.0035232222,...
嵌入是用OpenAI的text- embeddings -ada-002模型生成的。如果需要使用不同的模型,那么:
- 更新{app_dir}/backend/embeddings_generator.js和{app_dir}/backend/postgres_embeddings_service.jsfile中的模型
- 通過node embeddings_generator.js命令啟動生成器來重新生成嵌入。
查找最相關(guān)的Airbnb住宿房源
至此,Postgres已經(jīng)準(zhǔn)備好向用戶推薦最相關(guān)的Airbnb住宿房源。該應(yīng)用程序可以通過比較用戶的提示嵌入與Airbnb描述的嵌入來獲得這些推薦。
首先,啟動Airbnb推薦服務(wù)的一個(gè)實(shí)例:
1.使用OpenAI API密鑰更新{app_dir}/application.properties.ini:
Shell
1 OPENAI_API_KEY=<your key>
2.啟動Node.js后端:
Shell
cd {app_dir}
npm i
cd backend
npm start
3.啟動React前端:
Shell
cd {app_dir}/frontend
npm i
npm start
應(yīng)用程序用戶界面(UI)應(yīng)在默認(rèn)瀏覽器中自動打開。否則,請?jiān)诘刂反蜷_http://localhost:300 。
現(xiàn)在,從應(yīng)用程序用戶界面(UI)中選擇Postgres Embeddings模式,并要求應(yīng)用程序推薦一些與以下提示最相關(guān)的Airbnb住宿房源:
Shell
I'm looking for an apartment near the Golden Gate Bridge with a nice view of the Bay.
該服務(wù)將推薦三種住宿選擇:
在內(nèi)部,應(yīng)用程序執(zhí)行以下步驟來生成推薦(詳細(xì)信息請參見{app_dir}/backend/postgres_embeddings_service.js):
1.應(yīng)用程序使用OpenAI Embeddings模型(text- Embeddings -ada-002)生成用戶提示的矢量化表示:
JavaScript
const embeddingResp = await this.#openai.embeddings.create(
{model: "text-embedding-ada-002",
input: prompt});
2.該應(yīng)用程序使用生成的向量來檢索存儲在Postgres中的最相關(guān)的Airbnb屬性:
JavaScript
const res = await this.#client.query(
"SELECT name, description, price, 1 - (description_embedding <=> $1) as similarity " +
"FROM airbnb_listing WHERE 1 - (description_embedding <=> $1) > $2 ORDER BY description_embedding <=> $1 LIMIT $3",
['[' + embeddingResp.data[0].embedding + ']', matchThreshold, matchCnt]);
相似度計(jì)算為存儲在description_embedding列中的嵌入與用戶提示向量之間的余弦距離。
3.建議的Airbnb屬性以JSON格式返回到React前端:
JavaScript
let places = [];
for (let i = 0; i < res.rows.length; i++) {
const row = res.rows[i];
places.push({
"name": row.name,
"description": row.description,
"price": row.price,
"similarity": row.similarity });
}
return places;
擴(kuò)展方式
目前,Postgres存儲了超過7500處Airbnb住宅房源。通過比較用戶提示和Airbnb描述的嵌入,數(shù)據(jù)庫執(zhí)行精確的最近鄰搜索只需要幾毫秒的時(shí)間。
然而,精確的最近鄰搜索(全表掃描)有其局限性。隨著數(shù)據(jù)集的增長,Postgres在多維向量上執(zhí)行相似性搜索將花費(fèi)更長的時(shí)間。
為了在不斷增長的數(shù)據(jù)量和流量中保持Postgres的性能和可擴(kuò)展性,可以為向量化數(shù)據(jù)使用專門的索引和/或使用分布式版本的Postgres水平擴(kuò)展存儲和計(jì)算資源。
Pgvector擴(kuò)展支持多種索引類型,包括性能最好的HNSW索引(Hierarchical Navigable Small World)。該索引對向量化數(shù)據(jù)執(zhí)行近似最近鄰搜索(ANN),允許數(shù)據(jù)庫即使使用大數(shù)據(jù)量也能保持低且可預(yù)測的延遲。然而,由于搜索是近似的,搜索的召回可能不是100%相關(guān)/準(zhǔn)確的,因?yàn)樗饕槐闅v數(shù)據(jù)的一個(gè)子集。
例如,以下是如何在Postgres中為Airbnb嵌入創(chuàng)建HNSW索引:
SQL
CREATE INDEX ON airbnb_listing
USING hnsw (description_embedding vector_cosine_ops)
WITH (m = 4, ef_construction = 10);
要想更深入地了解HNSW指數(shù)是如何構(gòu)建的,以及如何在Airbnb數(shù)據(jù)上執(zhí)行人工神經(jīng)網(wǎng)絡(luò)搜索,請查看以下視頻:
使用分布式PostgreSQL,當(dāng)單個(gè)數(shù)據(jù)庫服務(wù)器的容量不再足夠時(shí),可以輕松地?cái)U(kuò)展數(shù)據(jù)庫存儲和計(jì)算資源。雖然PostgreSQL最初是為單服務(wù)器部署而設(shè)計(jì)的,但它的生態(tài)系統(tǒng)包含了一些擴(kuò)展和解決方案,使它能夠在分布式配置中運(yùn)行。其中一個(gè)解決方案是YugabyteDB,它是一個(gè)分布式SQL數(shù)據(jù)庫,擴(kuò)展了Postgres在分布式環(huán)境中的功能。
YugabyteDB自2.19.2版本起支持Pgvector擴(kuò)展。它將數(shù)據(jù)和嵌入分布在一組節(jié)點(diǎn)上,促進(jìn)了大規(guī)模的相似性搜索。因此,如果希望Airbnb服務(wù)在Postgres的分布式版本上運(yùn)行:
1.部署一個(gè)多節(jié)點(diǎn)YugabyteDB集群。
2.更新{app_dir}/application.properties.ini文件中的數(shù)據(jù)庫連接設(shè)置:
Properties files
# Configuration for a locally running YugabyteDB instance with defaults.
DATABASE_HOST=localhost
DATABASE_PORT=5433
DATABASE_NAME=yugabyte
DATABASE_USER=yugabyte
DATABASE_PASSWORD=yugabyte
3.從頭加載數(shù)據(jù)(或者使用YugabyteDB Voyager從正在運(yùn)行的Postgres實(shí)例中遷移數(shù)據(jù))并重新啟動應(yīng)用程序。不需要其他代碼級別的更改,因?yàn)閅ugabyteDB與Postgres具有功能和運(yùn)行時(shí)兼容性。
觀看以下的視頻,了解Airbnb推薦服務(wù)如何在分布式Postgres版本上運(yùn)行:
使用Postgres構(gòu)建可擴(kuò)展的人工智能應(yīng)用程序很有趣,人們可以了解更多關(guān)于Postgres作為矢量數(shù)據(jù)庫的知識。
原文標(biāo)題:PostgreSQL as a Vector Database: Getting Started and Scaling,作者:Denis Magda