ããã¯ããªã«ãããããŠæžãããã®ïŒ
åã«ããããªãšã³ããªãŒãæžããŸããã
OpenAI Python APIライブラリーからllama-cpp-pythonで立てたOpenAI API互換のサーバーのチャットモデルへアクセスしてみる - CLOVER🍀
ãã®æã¯ãllama-cpp-pythonã§ç«ãŠãOpenAI APIäºæã®ãµãŒããŒã®ãã£ããã¢ãã«ã®APIã«ã¢ã¯ã»ã¹ããŠã¿ãŸããããä»åã¯åã蟌ã¿ã®
APIã䜿ã£ãŠããã¹ãã®ãã¯ãã«åãããŠã¿ãããšæããŸãã
OpenAIã®åã蟌ã¿
OpenAIã®åã蟌ã¿ïŒEmbeddingsïŒã«é¢ããããã¥ã¡ã³ãã¯ãã¡ãã
Introductionã®æŠå¿µã»ã¯ã·ã§ã³ã§ã¯ã以äžã®ããã«èª¬æãããŠããŸããã
- ã³ã³ãã³ãã®æå³ãä¿æããããšãç®çãšããããŒã¿ã®ãã¯ãã«è¡šçŸ
- é¡äŒŒã®ããŒã¿ã®ãã£ã³ã¯ã«ã¯ãè¿ãEmbeddingsãå«ãŸããåŸåã«ãã
Embeddingsã®ããŒãžã§ãããå°ãèŠãŠã¿ãŸãããã
OpenAIã®ããã¹ãåã蟌ã¿ã¯ãããã¹ãæååã®é¢é£æ§ã枬å®ãããã®ã ããã§ãã
OpenAIâs text embeddings measure the relatedness of text strings.
äžè¬çãªçšéã¯ä»¥äžã®ããã§ãã
- æ€çŽ¢ ⊠ã¯ãšãªæååãšã®é¡äŒŒåºŠã«ãã£ãŠçµæãã©ã³ã¯ä»ãããã
- Search (where results are ranked by relevance to a query string)
- ã¯ã©ã¹ã¿ãªã³ã° ⊠ããã¹ãæååãé¡äŒŒåºŠã«ãã£ãŠã°ã«ãŒãåããã
- Clustering (where text strings are grouped by similarity)
- ã¬ã³ã¡ã³ããŒã·ã§ã³ ⊠é¢é£ããããã¹ããæã€ã¢ã€ãã ããªã³ã¡ã³ããã
- Recommendations (where items with related text strings are recommended)
- ç°åžžæ€åº ⊠é¢é£æ§ã®äœãå€ãå€ãç¹å®ããã
- Anomaly detection (where outliers with little relatedness are identified)
- å€æ§æ§æž¬å® ⊠é¡äŒŒæ§ã®ååžãåæãã
- Diversity measurement (where similarity distributions are analyzed)
- åé¡ âŠ ããã¹ãæååãæãé¡äŒŒããã©ãã«ã§åé¡ãã
- Classification (where text strings are classified by their most similar label)
åã蟌ã¿ã¯ãæµ®åå°æ°ç¹æ°åã®ãã¯ãã«ïŒãªã¹ãïŒã§è¡šçŸãããŸãã2ã€ã®ãã¯ãã«ã¯ãè·é¢ã«ãã£ãŠé¢é£æ§ã枬ãããšãã§ããŸãã
è·é¢ãå°ããå Žåã¯é¢é£æ§ãé«ãããšã瀺ããè·é¢ã倧ããå Žåã¯é¢é£æ§ãäœãããšã瀺ããŸãã
An embedding is a vector (list) of floating point numbers. The distance between two vectors measures their relatedness. Small distances suggest high relatedness and large distances suggest low relatedness.
è·é¢ã®æž¬å®ã«ã¯ãã³ãµã€ã³é¡äŒŒåºŠã䜿çšããããšãæšå¥šãããŠããŸãã
Embeddings / Limitations & risks / Which distance function should I use?
äžæ¹ã§ãã©ã®è·é¢é¢æ°ã䜿ããã¯ããã»ã©éèŠã§ã¯ãªããšãæžãããŠããŸãã
ãŸããOpenAIã«ãããåã蟌ã¿ã®é·ãã¯1ã«æ£èŠåããã次ã®ç¹æ§ãæã€ããã§ãã
- ã³ãµã€ã³é¡äŒŒåºŠã¯ããããç©ã®ã¿ã䜿çšããŠå°ãé«éã«èšç®ã§ãã
- ã³ãµã€ã³é¡äŒŒåºŠãšãŠãŒã¯ãªããè·é¢ã§ãåãã©ã³ãã³ã°ãåŸããã
åã蟌ã¿ã®å©çšã¯ãEmbeddings APIãžã®ã¢ã¯ã»ã¹ã§è¡ããŸãã
åã蟌ã¿ã®äœæã¯ãã¡ãã§ããã
ENDPOINTS / Embeddings / Create embeddings
ãã©ã¡ãŒã¿ãŒãšããŠãå°ãªããšãããã¹ããšã¢ãã«ãå¿ èŠã§ãã
åã蟌ã¿ã®ã¢ãã«ã¯2äžä»£ããã第2äžä»£ïŒã¢ãã«IDã-002
ïŒãšç¬¬1äžä»£ïŒã¢ãã«IDã-001
ïŒããããŸãã
ã¢ãã«ã®äžä»£ | ããŒã¯ãã€ã¶ãŒ | æå€§å ¥åããŒã¯ã³ |
---|---|---|
V2 | cl100k_base | 8191 |
V1 | GPT-2/GPT-3 | 2046 |
第1äžä»£ã®ã¢ãã«ã¯éæšå¥šãšãªã£ãŠããã第2äžä»£ã®ã¢ãã«ã®å©çšãæšå¥šãããŠããŸãã
第2äžä»£ã®ã¢ãã«ã¯text-embedding-ada-002
ã®ã¿ã§ããããŒã¯ãã€ã¶ãŒã¯cl100k_base
ãæ倧å
¥åããŒã¯ã³ã¯8191ã§ãã
åºåã®æ¬¡å
æ°ã¯1536ã§ãã
ããã¹ãçæã¢ãã«ãšåæ§ãåã蟌ã¿ã§ãããŒã¯ã³æ°ãæéã«åæ ãããŸãã
ãŠãŒã¹ã±ãŒã¹ãšãµã³ãã«ã¯ãã¡ãã
ãã¯ãã«åããããŒã¿ãä¿åããããã¯ãã«ããŒã¿ããŒã¹ã®çŽ¹ä»ããããŸãã
Embeddings / Limitations & risks / How can I retrieve K nearest embedding vectors quickly?
æåŸã«ããªã¹ã¯ã«ã€ããŠã
Embeddings / Limitations & risks
OpenAIã®åã蟌ã¿ã¢ãã«ã¯ä¿¡é ŒåºŠãäœãããŸãã¯ç€ŸäŒçãªã¹ã¯ãèµ·ããå¯èœæ§ãããããšãèŠåãããŠããŸãã
ãªãããã®ç·©åçãçšæããæ¹ãããããšã
Our embedding models may be unreliable or pose social risks in certain cases, and may cause harm in the absence of mitigations.
å®éã«èµ·ãã£ãäŸããã¢ãã«ããã€ãŸã§ã®ç¥èãæã£ãŠãããã«ã€ããŠãæžãããŠããŸãã
ãããåã蟌ã¿ãã©ã®ãããªãã®ãã¯ããã£ãŠããã®ã§ãä»åã¯ããã¹ãããŒã¿ãåã蟌ã¿APIã䜿ã£ãŠãã¯ãã«åãããšããã
ãã£ãŠã¿ãããšæããŸãã
OpenAI APIäºæã®ãµãŒããŒãšããŠã¯ãllama-cpp-pythonã䜿ããŸãã
ç°å¢
ä»åã®ç°å¢ã¯ããã¡ãã
$ python3 -V Python 3.10.12
llama-cpp-pythonã®ããŒãžã§ã³ã
$ pip3 freeze | grep llama_cpp_python llama_cpp_python==0.2.20
ã¢ãã«ã¯ãã¡ãã䜿ããŸãã
â»åŸã§æ°ã¥ããŸãããã䜿ãã¢ãã«ã¯ããã ãšãã¡ãªæ°ãããŸããâŠ
TheBloke/Llama-2-7B-Chat-GGUF · Hugging Face
èµ·åã
$ python3 -m llama_cpp.server --model llama-2-7b-chat.Q4_K_M.gguf
ããã¹ãããã¯ãã«åãã
ãŸãã¯ãããã¹ãããã¯ãã«åããŠã¿ãŸãããã
OpenAI APIã®ã©ã€ãã©ãªãŒãã€ã³ã¹ããŒã«ã
$ pip3 install openai
ããŒãžã§ã³ã
$ pip3 freeze | grep openai openai==1.3.7
äœæããããã°ã©ã ã¯ãã¡ãã
to_vector.py
import sys import time from openai import OpenAI text = sys.argv[1] start_time = time.perf_counter() openai = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy-api-key") response = openai.embeddings.create(input=text, model="text-embedding-ada-002") elapsed_time = time.perf_counter() - start_time print(f"raw response = {response}") print() print(f"input text = {text}, to vector = {len(response.data[0].embedding)}") print() print(f"elapsed time = {elapsed_time:.3f} sec")
base_url
ã«ã¯ãllama-cpp-pythonã®OpenAI APIäºæã®ãšã³ããã€ã³ããæå®ãAPIããŒã¯é©åœã§æ§ããŸããã
openai = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy-api-key")
ããšã¯Embeddings Create APIãåŒã³åºããŸããã¢ãã«åã¯text-embedding-ada-002
ãæå®ããŠããŸãããllama-cpp-pythonãµãŒããŒã
察象ãšããå Žåã¯ãªãã§ãæ§ããŸããã
response = openai.embeddings.create(input=text, model="text-embedding-ada-002")
ããšã¯ãã¯ãã«åããçµæã衚瀺ããŸãããã¯ãã«åãã察象ã®ããã¹ãã¯ãã³ãã³ãã©ã€ã³åŒæ°ã§æå®ããããã«ããŠããŸãã
print(f"raw response = {response}") print() print(f"input text = {text}, to vector = {len(response.data[0].embedding)}")
ã§ã¯ãå®è¡ããŠã¿ãŸãããã
$ python3 to_vector.py 'Hello World.'
çµæã
$ python3 to_vector.py 'Hello World.' raw response = CreateEmbeddingResponse(data=[Embedding(embedding=[0.34358564019203186, 0.8082753419876099, 1.666818380355835, -1.0591312646865845, 0.06530340015888214, -0.5284493565559387, -0.6435621380805969, 1.137858271598816, 1.3028210401535034, 0.2946239709854126, -0.5300033092498779, -1.1164494752883911, 0.7548168301582336, 0.40373867750167847, 1.1290810108184814, -0.7566999197006226, 0.267472505569458, 0.7608996629714966, -0.017051421105861664, -1.01883065700531, -0.1597931832075119, -1.3573452234268188, 0.8877636790275574, -1.2786635160446167, -0.24713543057441711, 0.6535821557044983, -0.5414071083068848, ãçç¥ã -2.555652379989624, 0.18512584269046783, 0.42112821340560913, -0.04915745556354523, -0.1607542186975479, -1.6608763933181763, -1.181817650794983, 0.655724287033081, -0.15193837881088257, 0.18946832418441772, -0.06836213171482086, -0.19648043811321259, -1.2785874605178833, 1.3186522722244263, 0.26095831394195557, 0.595634400844574, -0.5786678194999695, -1.9923450946807861, 0.5934603810310364, -0.5940259099006653, 0.1100892424583435, 0.6473436951637268, -0.3595812916755676, 0.5893478393554688, 1.695295810699463], index=0, object='embedding')], model='text-embedding-ada-002', object='list', usage=Usage(prompt_tokens=4, total_tokens=4)) input text = Hello World., to vector = 4096 elapsed time = 0.774 sec
ãã®ããã¹ããªãå®è¡æéã¯1ç§ããã£ãŠããŸããã
ãã¯ãã«åããæã®æ¬¡å
ã¯4096ã§ãããã€ãŸããåã蟌ã¿ã®çµæã®ãªã¹ãã«ã¯4096åã®èŠçŽ ãå«ãŸããŠããŸãã
OpenAIã®ããã¥ã¡ã³ãã§ã¯1536ã ã£ããšæããŸããâŠã
$ python3 to_vector.py 'this is apple.' raw response = CreateEmbeddingResponse(data=[Embedding(embedding=[0.11018591374158859, 0.6475743651390076, 2.998861074447632, -0.586685836315155, 0.09722156822681427, -1.347420334815979, 0.048076026141643524, 2.2477073669433594, 0.57712721824646, 0.28412631154060364, -0.2854914367198944, -1.3638404607772827, 1.5588339567184448, 0.07106658816337585, 1.6731715202331543, -0.9277191758155823, 0.5767037272453308, 1.015160083770752, -0.494839608669281, -0.9214868545532227, -1.3904584646224976, -1.822590708732605, 1.106003761291504, -1.147274374961853, -1.0618056058883667, 0.18969042599201202, -1.3648691177368164, ãçç¥ã -1.3505096435546875, -1.383029580116272, -5.094209671020508, -0.881213366985321, 0.28645381331443787, -0.6735928058624268, 0.49416056275367737, -1.0014870166778564, 1.0167360305786133, -0.4009270668029785, 0.032198164612054825, -0.5705236792564392, 1.8743517398834229, -0.6221891045570374, -1.7606291770935059, 0.4385615885257721, -0.2885802686214447, -0.7018359899520874, 0.23984572291374207, 0.07199232280254364, 1.4006679058074951, 1.113827109336853], index=0, object='embedding')], model='text-embedding-ada-002', object='list', usage=Usage(prompt_tokens=5, total_tokens=5)) input text = this is apple., to vector = 4096 elapsed time = 0.934 sec
ãšãããããããã¹ãããã¯ãã«åããæ¹æ³ã¯ããããŸããã
æ€çŽ¢ããŠã¿ã
æåŸã«æ€çŽ¢ããŠã¿ãŸããããã¡ãªã¿ã«ãããã¯ã¡ãã£ãšããŸããããŸããã§ããâŠã
ããã¥ã¡ã³ãã«èšèŒãããŠããæ€çŽ¢ã®ãµã³ãã«ã¯ããã¡ãã§ãã
from openai.embeddings_utils import get_embedding, cosine_similarity def search_reviews(df, product_description, n=3, pprint=True): embedding = get_embedding(product_description, model='text-embedding-ada-002') df['similarities'] = df.ada_embedding.apply(lambda x: cosine_similarity(x, embedding)) res = df.sort_values('similarities', ascending=False).head(n) return res res = search_reviews(df, 'delicious beans', n=3)
openai.embeddings_utils
ã®cosine_similarity
ã䜿ã£ãŠã³ãµã€ã³é¡äŒŒåºŠãèšç®ããŠããã®ã§ãããå®ã¯çŸåšã®OpenAI APIã®
Pythonã©ã€ãã©ãªãŒã«ã¯ãã®é¢æ°ã¯ãããŸããã
1.0ã«ãªãæã«åé€ãããããã§ãã
ãµã³ãã«ãåããªããšããissueãâŠã
ãã®é¢æ°ã®å®çŸ©ã¯ãã¡ãã§ãnumpyãããã°ç°¡åã«ç§»æ€ã§ããã®ã§ãã®æ¹éã«ããŸããã
https://github.com/openai/openai-python/blob/v0.28.1/openai/embeddings_utils.py#L65-L66
ãšããããã§ãnumpyãã€ã³ã¹ããŒã«ã
$ pip3 install numpy
ããŒãžã§ã³ã
$ pip3 freeze | grep numpy numpy==1.26.2
äœæããããã°ã©ã ã¯ãã¡ãã
search.py
import sys from openai import OpenAI import numpy as np ## https://github.com/openai/openai-python/blob/v0.28.1/openai/embeddings_utils.py#L65-L66 def cosine_similarity(a, b): return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b)) seeds = [ {"name": "Apple", "feature": "With red flesh and thin skin, it has a balanced taste of mild acidity and sweetness."}, {"name": "Banana", "feature": "A yellow fruit with a smooth texture and mild sweetness, known for its high nutritional value."}, {"name": "Grapes", "feature": "Purple in color, these small fruits cluster together with juicy and mildly tangy flavor."}, {"name": "Melon", "feature": "A green fruit with a refreshing texture and aroma, rich in sweetness and water content."}, {"name": "Orange", "feature": "Wrapped in an orange peel, it offers a harmonious blend of refreshing acidity and sweetness, rich in vitamin C."}, {"name": "Strawberry", "feature": "Recognized by its red hue, it carries a distinctive fragrance and a sweet-tart taste, with tiny seeds adding texture."}, {"name": "Pineapple", "feature": "Featuring yellow flesh, it has a sweet-tangy flavor and a unique texture, accompanied by a rich aroma."}, {"name": "Mango", "feature": "An orange fruit with a rich aroma and intense sweetness, offering a smooth and luscious flesh."}, {"name": "Kiwi", "feature": "Green flesh with a balanced combination of acidity and sweetness, enhanced by small black seeds for texture."}, {"name": "Peach", "feature": "Displaying peach-colored flesh, it is juicy and soft with a sweet aroma, complemented by the peach's beautiful appearance."} ] openai = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy-api-key") docs = [] for seed in seeds: feature = seed["feature"] response = openai.embeddings.create(input=feature, model="text-embedding-ada-002") docs.append({"name": seed["name"], "feature": feature, "embedding": response.data[0].embedding}) query = sys.argv[1] response = openai.embeddings.create(input=query, model="text-embedding-ada-002") query_embedding = response.data[0].embedding docs_with_similarity = [{ "name": d["name"], "feature": d["feature"], "embedding": d["embedding"], "similarity": cosine_similarity(d["embedding"], query_embedding) } for d in docs] sorted_docs = sorted(docs_with_similarity, key=lambda d: d["similarity"], reverse=True) print(f"query = {query}") print() print("ranking:") for doc in sorted_docs: print(f" name: {doc['name']}") print(f" feature: {doc['feature']}") print(f" similarity: {doc['similarity']}")
ãã¡ãã¯ã³ãµã€ã³é¡äŒŒåºŠãèšç®ããé¢æ°ã§ãã
## https://github.com/openai/openai-python/blob/v0.28.1/openai/embeddings_utils.py#L65-L66 def cosine_similarity(a, b): return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
以äžã®ãããªæç©ã®ååãšç¹åŸŽã®ããã¥ã¡ã³ãã«å¯ŸããŠ
seeds = [ {"name": "Apple", "feature": "With red flesh and thin skin, it has a balanced taste of mild acidity and sweetness."}, {"name": "Banana", "feature": "A yellow fruit with a smooth texture and mild sweetness, known for its high nutritional value."}, {"name": "Grapes", "feature": "Purple in color, these small fruits cluster together with juicy and mildly tangy flavor."}, {"name": "Melon", "feature": "A green fruit with a refreshing texture and aroma, rich in sweetness and water content."}, {"name": "Orange", "feature": "Wrapped in an orange peel, it offers a harmonious blend of refreshing acidity and sweetness, rich in vitamin C."}, {"name": "Strawberry", "feature": "Recognized by its red hue, it carries a distinctive fragrance and a sweet-tart taste, with tiny seeds adding texture."}, {"name": "Pineapple", "feature": "Featuring yellow flesh, it has a sweet-tangy flavor and a unique texture, accompanied by a rich aroma."}, {"name": "Mango", "feature": "An orange fruit with a rich aroma and intense sweetness, offering a smooth and luscious flesh."}, {"name": "Kiwi", "feature": "Green flesh with a balanced combination of acidity and sweetness, enhanced by small black seeds for texture."}, {"name": "Peach", "feature": "Displaying peach-colored flesh, it is juicy and soft with a sweet aroma, complemented by the peach's beautiful appearance."} ]
ç¹åŸŽã察象ã«ãã¯ãã«åãè¡ããŸãã
docs = [] for seed in seeds: feature = seed["feature"] response = openai.embeddings.create(input=feature, model="text-embedding-ada-002") docs.append({"name": seed["name"], "feature": feature, "embedding": response.data[0].embedding})
æ€çŽ¢æååã¯ãã³ãã³ãã©ã€ã³åŒæ°ã§äžããŠãã¡ãããã¯ãã«åããŸãã
query = sys.argv[1] response = openai.embeddings.create(input=query, model="text-embedding-ada-002") query_embedding = response.data[0].embedding
ãããŠãå ã»ã©ãã¯ãã«åããå€ãå ããããã¥ã¡ã³ããšæ€çŽ¢æååã®ãã¯ãã«ã§ãã³ãµã€ã³é¡äŒŒåºŠããšããŸãã
docs_with_similarity = [{ "name": d["name"], "feature": d["feature"], "embedding": d["embedding"], "similarity": cosine_similarity(d["embedding"], query_embedding) } for d in docs]
çµæãã³ãµã€ã³é¡äŒŒåºŠã®å€ã®éé ã§ãœãŒãã
sorted_docs = sorted(docs_with_similarity, key=lambda d: d["similarity"], reverse=True)
çµæ衚瀺ã
print(f"query = {query}") print() print("ranking:") for doc in sorted_docs: print(f" name: {doc['name']}") print(f" feature: {doc['feature']}") print(f" similarity: {doc['similarity']}")
è©ŠããŠã¿ãŸãã
$ python3 search.py green
ãªãã埮åŠãªçµæã«ãªããŸããâŠã
query = green ranking: name: Kiwi feature: Green flesh with a balanced combination of acidity and sweetness, enhanced by small black seeds for texture. similarity: 0.04215274492006107 name: Banana feature: A yellow fruit with a smooth texture and mild sweetness, known for its high nutritional value. similarity: 0.0344139587395739 name: Melon feature: A green fruit with a refreshing texture and aroma, rich in sweetness and water content. similarity: 0.025705263210870112 name: Peach feature: Displaying peach-colored flesh, it is juicy and soft with a sweet aroma, complemented by the peach's beautiful appearance. similarity: 0.024638075297955937 name: Mango feature: An orange fruit with a rich aroma and intense sweetness, offering a smooth and luscious flesh. similarity: 0.014813710044700355 name: Apple feature: With red flesh and thin skin, it has a balanced taste of mild acidity and sweetness. similarity: 0.014664657619712652 name: Pineapple feature: Featuring yellow flesh, it has a sweet-tangy flavor and a unique texture, accompanied by a rich aroma. similarity: 0.010742138593120263 name: Grapes feature: Purple in color, these small fruits cluster together with juicy and mildly tangy flavor. similarity: 0.008852824997386993 name: Orange feature: Wrapped in an orange peel, it offers a harmonious blend of refreshing acidity and sweetness, rich in vitamin C. similarity: -0.015618559051309908 name: Strawberry feature: Recognized by its red hue, it carries a distinctive fragrance and a sweet-tart taste, with tiny seeds adding texture. similarity: -0.04353728373064396
æãããããªãããå€ããŠããæããããŸãã
ä»ã®åèªã§è©ŠããŠã¿ãããæ¥æ¬èªã§ããã£ãŠã¿ãŸãããããããã埮åŠãªçµæã«ãªããŸããâŠã
â»ãšããããã¢ãã«ã®éžæã誀ã£ãŠããæ°ãããŸã
ãšããããã䜿ãæ¹ã¯ããã£ãã®ã§ä»åã¯ãããŸã§ã«ããŠãããŸãããã
ãããã«
llama-cpp-pythonã§ç«ãŠãOpenAI APIäºæã®ãµãŒããŒã§ãããã¹ãã®ãã¯ãã«åãè©ŠããŠã¿ãŸããã
APIã®äœ¿ãæ¹ãçšèªã®æå³ã¯ãããææ¡ã§ããããªãšæããŸãããçµæãã¡ãã£ãšåŸ®åŠã§ããã
ãŸãã䜿ã£ãŠããã®ã¯OpenAIèªäœã§ã¯ãªãã®ã§ãä»åã¯ãããããã«ããŠãããŸãããã