इसमें कोई संदेह नहीं है कि ओपनएआई का चैटजीपीटी असाधारण रूप से बुद्धिमान है - इसने वकील का बार टेस्ट पास कर लिया है, इसके पास डॉक्टर के समान ज्ञान है , और कुछ परीक्षणों में इसका आईक्यू 155 पर पहुंच गया है । हालाँकि, यह अज्ञानता को स्वीकार करने के बजाय जानकारी गढ़ने की प्रवृत्ति रखता है। यह प्रवृत्ति, इस तथ्य के साथ मिलकर कि इसका ज्ञान 2021 में समाप्त हो जाएगा, जीपीटी एपीआई का उपयोग करके विशेष उत्पादों के निर्माण में चुनौतियां पैदा करता है।
हम इन बाधाओं को कैसे पार कर सकते हैं? हम GPT-3 जैसे मॉडल को नया ज्ञान कैसे प्रदान कर सकते हैं? मेरा लक्ष्य पायथन, ओपनएआई एपीआई और वर्ड एम्बेडिंग का उपयोग करके एक प्रश्न-उत्तर देने वाला बॉट बनाकर इन सवालों का समाधान करना है।
मैं एक ऐसा बॉट बनाने का इरादा रखता हूं जो एक प्रॉम्प्ट से निरंतर एकीकरण पाइपलाइन उत्पन्न करता है, जो, जैसा कि आप जानते होंगे, सेमाफोर सीआई/सीडी में वाईएएमएल के साथ तैयार किए जाते हैं ।
यहां क्रियाशील बॉट का एक उदाहरण दिया गया है:
चल रहे प्रोग्राम का स्क्रीनशॉट. स्क्रीन पर, कमांड python query.py "Create a CI pipeline that builds and uploads a Docker image to Docker Hub"
निष्पादित किया जाता है, और प्रोग्राम सीआई पाइपलाइन के अनुरूप YAML प्रिंट करता है जो अनुरोधित कार्रवाई करता है।
डॉक्सजीपीटी , माई आस्कएआई और लाइबेरिया जैसी परियोजनाओं की भावना में, मैं सेमाफोर के बारे में जीपीटी-3 मॉडल को "सिखाने" और पाइपलाइन कॉन्फ़िगरेशन फ़ाइलों को कैसे उत्पन्न किया जाए, इसकी योजना बना रहा हूं। मैं मौजूदा दस्तावेज का लाभ उठाकर इसे हासिल करूंगा।
मैं बॉट निर्माण का पूर्व ज्ञान नहीं मानूंगा और स्वच्छ कोड बनाए रखूंगा ताकि आप इसे अपनी आवश्यकताओं के अनुसार अनुकूलित कर सकें।
इस ट्यूटोरियल का अनुसरण करने के लिए आपको किसी बॉट को कोड करने के अनुभव या तंत्रिका नेटवर्क के ज्ञान की आवश्यकता नहीं है। हालाँकि, आपको आवश्यकता होगी:
चैटजीपीटी, या अधिक सटीक रूप से, जीपीटी-3 और जीपीटी-4, बड़े भाषा मॉडल (एलएलएम) जो उन्हें शक्ति प्रदान करते हैं, को सितंबर 2021 के आसपास कटऑफ तिथि के साथ एक विशाल डेटासेट पर प्रशिक्षित किया गया है।
संक्षेप में, GPT-3 उस तिथि के बाद की घटनाओं के बारे में बहुत कम जानता है। हम इसे एक साधारण संकेत से सत्यापित कर सकते हैं:
चैटजीपीटी को नहीं पता कि 2022 में विश्व कप किसने जीता।
जबकि कुछ ओपनएआई मॉडल फाइन-ट्यूनिंग से गुजर सकते हैं, अधिक उन्नत मॉडल, जैसे कि जिनमें रुचि थी, नहीं हो सकते; हम उनके प्रशिक्षण डेटा को नहीं बढ़ा सकते।
हम GPT-3 के प्रशिक्षण डेटा से परे उससे उत्तर कैसे प्राप्त कर सकते हैं? एक विधि में इसकी पाठ्य समझ क्षमताओं का दोहन शामिल है; प्रासंगिक संदर्भ के साथ संकेत को बढ़ाकर, हम संभवतः सही उत्तर प्राप्त कर सकते हैं।
नीचे दिए गए उदाहरण में, मैं फीफा की आधिकारिक साइट से संदर्भ प्रदान करता हूं, और प्रतिक्रिया काफी भिन्न है:
दिए गए संदर्भ के साथ, चैटजीपीटी सटीक उत्तर दे सकता है।
हम यह निष्कर्ष निकाल सकते हैं कि यदि पर्याप्त प्रासंगिक संदर्भ दिया जाए तो मॉडल किसी भी संकेत का जवाब दे सकता है। सवाल यह है कि मनमाना संकेत दिए जाने पर हम कैसे जान सकते हैं कि क्या प्रासंगिक है? इसे संबोधित करने के लिए, हमें यह पता लगाना होगा कि शब्द एम्बेडिंग क्या हैं।
भाषा मॉडल के संदर्भ में, एम्बेडिंग शब्दों, वाक्यों या संपूर्ण दस्तावेज़ों को वैक्टर या संख्याओं की सूची के रूप में प्रस्तुत करने का एक तरीका है।
एम्बेडिंग की गणना करने के लिए, हमें Word2vec या text-embedding-ada-002 जैसे तंत्रिका नेटवर्क की आवश्यकता होगी। इन नेटवर्कों को बड़ी मात्रा में पाठ पर प्रशिक्षित किया गया है और वे उन आवृत्तियों का विश्लेषण करके शब्दों के बीच संबंध ढूंढ सकते हैं जिनके साथ प्रशिक्षण डेटा में विशिष्ट पैटर्न दिखाई देते हैं।
मान लीजिए कि हमारे पास निम्नलिखित शब्द हैं:
कल्पना कीजिए कि हम प्रत्येक शब्द के लिए वैक्टर की गणना करने के लिए इनमें से एक एम्बेडिंग नेटवर्क का उपयोग करते हैं। उदाहरण के लिए:
शब्द | वेक्टर | प्रसंग |
---|---|---|
बिल्ली | [0.1, 0.2, 0.3, 0.4, 0.5] | जानवर, वस्तुएँ, छोटी चीज़ें |
कुत्ता | [0.6, 0.7, 0.8, 0.9, 1.0] | जानवर, वस्तुएँ, बड़ी चीज़ें |
गेंद | [0.2, 0.4, 0.6, 0.8, 1.0] | वस्तुएँ, खिलौने, छोटी चीज़ें |
घर | [0.3, 0.6, 0.9, 1.2, 1.5] | इमारतें, घर, बड़ी चीज़ें |
एक बार जब हमारे पास प्रत्येक शब्द के लिए वेक्टर होते हैं, तो हम उनका उपयोग पाठ के अर्थ को दर्शाने के लिए कर सकते हैं। उदाहरण के लिए, वाक्य "बिल्ली ने गेंद का पीछा किया" को वेक्टर [0.1, 0.2, 0.3, 0.4, 0.5] + [0.2, 0.4, 0.6, 0.8, 1.0] = [0.3, 0.6, 0.9, 1.2, 1.5] के रूप में दर्शाया जा सकता है। यह वेक्टर एक वाक्य का प्रतिनिधित्व करता है जो एक जानवर द्वारा किसी वस्तु का पीछा करने के बारे में है।
शब्द एम्बेडिंग को बहुआयामी स्थानों के रूप में देखा जा सकता है जहां समान अर्थ वाले शब्द या वाक्य एक साथ करीब होते हैं। हम किसी भी इनपुट टेक्स्ट के समान अर्थ खोजने के लिए वैक्टर के बीच "दूरी" की गणना कर सकते हैं।
वेक्टर रिक्त स्थान के रूप में एम्बेडिंग का 3डी प्रतिनिधित्व। वास्तव में, इन स्थानों के सैकड़ों या हजारों आयाम हो सकते हैं। स्रोत: एआई के मल्टीटूल से मिलें: वेक्टर एंबेडिंग
इन सबके पीछे का वास्तविक गणित इस लेख के दायरे से परे है। हालाँकि, मुख्य बात यह है कि वेक्टर ऑपरेशन हमें गणित का उपयोग करके अर्थ में हेरफेर करने या निर्धारित करने की अनुमति देते हैं । वह वेक्टर लें जो "रानी" शब्द का प्रतिनिधित्व करता है, उसमें से "महिला" वेक्टर घटाएं, और "पुरुष" वेक्टर जोड़ें। परिणाम "राजा" के आसपास एक वेक्टर होना चाहिए। यदि हम "बेटा" जोड़ते हैं, तो हमें "राजकुमार" के करीब पहुंचना चाहिए।
अब तक, हमने शब्दों को इनपुट और संख्याओं को आउटपुट के रूप में लेते हुए तंत्रिका नेटवर्क को एम्बेड करने पर चर्चा की है। हालाँकि, कई आधुनिक नेटवर्क शब्दों के प्रसंस्करण से टोकन के प्रसंस्करण की ओर बढ़ गए हैं।
टोकन टेक्स्ट की सबसे छोटी इकाई है जिसे मॉडल द्वारा संसाधित किया जा सकता है। टोकन शब्द, वर्ण, विराम चिह्न, प्रतीक या शब्दों के भाग हो सकते हैं।
हम ओपनएआई ऑनलाइन टोकननाइज़र के साथ प्रयोग करके देख सकते हैं कि कैसे शब्दों को टोकन में परिवर्तित किया जाता है, जो टेक्स्ट को टोकन में बदलने और प्रत्येक को एक संख्या के साथ दर्शाने के लिए बाइट-पेयर एन्कोडिंग (बीपीई) का उपयोग करता है:
टोकन और शब्दों के बीच अक्सर 1-टू-1 संबंध होता है। अधिकांश टोकन में शब्द और एक अग्रणी स्थान शामिल होता है। हालाँकि, "एम्बेडिंग" जैसे विशेष मामले हैं, जिसमें दो टोकन होते हैं, "एम्बेड" और "डिंग," या "क्षमताएँ", जिसमें चार टोकन होते हैं। यदि आप "टोकन आईडी" पर क्लिक करते हैं, तो आप प्रत्येक टोकन के मॉडल का संख्यात्मक प्रतिनिधित्व देख सकते हैं।
अब जब हमें यह समझ आ गया है कि एम्बेडिंग क्या हैं, तो अगला सवाल यह है: वे एक स्मार्ट बॉट बनाने में हमारी मदद कैसे कर सकते हैं?
सबसे पहले, आइए विचार करें कि जब हम सीधे GPT-3 API का उपयोग करते हैं तो क्या होता है। उपयोगकर्ता एक संकेत जारी करता है, और मॉडल अपनी सर्वोत्तम क्षमता का जवाब देता है।
हालाँकि, जब हम समीकरण में संदर्भ जोड़ते हैं, तो चीज़ें बदल जाती हैं। उदाहरण के लिए, जब मैंने संदर्भ प्रदान करने के बाद चैटजीपीटी से विश्व कप के विजेता के बारे में पूछा, तो इससे सारा फर्क पड़ गया।
तो, एक स्मार्ट बॉट बनाने की योजना इस प्रकार है:
आइए अधिकांश परियोजनाओं की तरह, डेटाबेस डिज़ाइन करके शुरुआत करें।
हमारे संदर्भ डेटाबेस में मूल दस्तावेज़ और उनके संबंधित वैक्टर शामिल होने चाहिए। सिद्धांत रूप में, हम इस कार्य के लिए किसी भी प्रकार के डेटाबेस को नियोजित कर सकते हैं, लेकिन एक वेक्टर डेटाबेस इस कार्य के लिए इष्टतम उपकरण है।
वेक्टर डेटाबेस विशेष डेटाबेस हैं जिन्हें उच्च-आयामी वेक्टर डेटा को संग्रहीत और पुनर्प्राप्त करने के लिए डिज़ाइन किया गया है। खोज के लिए SQL जैसी क्वेरी भाषा का उपयोग करने के बजाय, हम एक वेक्टर प्रदान करते हैं और N निकटतम पड़ोसियों से अनुरोध करते हैं।
वैक्टर उत्पन्न करने के लिए, हम OpenAI से text-embedding-ada-002 का उपयोग करेंगे, क्योंकि यह उनके द्वारा पेश किया जाने वाला सबसे तेज़ और सबसे अधिक लागत प्रभावी मॉडल है। मॉडल इनपुट टेक्स्ट को टोकन में परिवर्तित करता है और उनके संबंधों को जानने के लिए ट्रांसफॉर्मर नामक ध्यान तंत्र का उपयोग करता है। इस तंत्रिका नेटवर्क का आउटपुट पाठ के अर्थ का प्रतिनिधित्व करने वाले वैक्टर हैं।
एक संदर्भ डेटाबेस बनाने के लिए, मैं:
सबसे पहले, मुझे OpenAI API कुंजी के साथ एक पर्यावरण फ़ाइल प्रारंभ करनी होगी। यह फ़ाइल कभी भी संस्करण नियंत्रण के लिए प्रतिबद्ध नहीं होनी चाहिए, क्योंकि एपीआई कुंजी निजी है और आपके खाते से जुड़ी हुई है।
export OPENAI_API_KEY=YOUR_API_KEY
इसके बाद, मैं अपने पायथन एप्लिकेशन के लिए एक वर्चुअलएन्व बनाऊंगा:
$ virtualenv venv $ source venv/bin/activate $ source .env
और OpenAI पैकेज स्थापित करें:
```bash $ pip install openai numpy
आइए स्ट्रिंग "डॉकर कंटेनर" के लिए एम्बेडिंग की गणना करने का प्रयास करें। आप इसे Python REPL पर या Python स्क्रिप्ट के रूप में चला सकते हैं:
$ python >>> import openai >>> embeddings = openai.Embedding.create(input="Docker Containers", engine="text-embedding-ada-002") >>> embeddings JSON: { "data": [ { "embedding": [ -0.00530336843803525, 0.0013223182177171111, ... 1533 more items ..., -0.015645816922187805 ], "index": 0, "object": "embedding" } ], "model": "text-embedding-ada-002-v2", "object": "list", "usage": { "prompt_tokens": 2, "total_tokens": 2 } }
जैसा कि आप देख सकते हैं, OpenAI का मॉडल 1536 आइटम वाली एक embedding
सूची के साथ प्रतिक्रिया करता है - टेक्स्ट-एम्बेडिंग-एडीए-002 के लिए वेक्टर आकार।
जबकि चुनने के लिए कई वेक्टर डेटाबेस इंजन हैं, जैसे क्रोमा जो ओपन-सोर्स है, मैंने पाइनकोन को चुना क्योंकि यह एक फ्री टियर वाला प्रबंधित डेटाबेस है, जो चीजों को सरल बनाता है। उनका स्टार्टर प्लान मेरे लिए आवश्यक सभी डेटा को संभालने में सक्षम है।
अपना पाइनकोन खाता बनाने और अपनी एपीआई कुंजी और पर्यावरण को पुनः प्राप्त करने के बाद, मैं अपनी .env
फ़ाइल में दोनों मान जोड़ता हूं।
अब .env
में मेरे पाइनकोन और ओपनएआई रहस्य शामिल होने चाहिए।
export OPENAI_API_KEY=YOUR_API_KEY # Pinecone secrets export PINECONE_API_KEY=YOUR_API_KEY export PINECONE_ENVIRONMENT=YOUR_PINECONE_DATACENTER
फिर, मैं पायथन के लिए पाइनकोन क्लाइंट स्थापित करता हूं:
$ pip install pinecone-client
मुझे एक डेटाबेस प्रारंभ करने की आवश्यकता है; ये db_create.py
स्क्रिप्ट की सामग्री हैं:
# db_create.py import pinecone import openai import os index_name = "semaphore" embed_model = "text-embedding-ada-002" api_key = os.getenv("PINECONE_API_KEY") env = os.getenv("PINECONE_ENVIRONMENT") pinecone.init(api_key=api_key, environment=env) embedding = openai.Embedding.create( input=[ "Sample document text goes here", "there will be several phrases in each batch" ], engine=embed_model ) if index_name not in pinecone.list_indexes(): print("Creating pinecone index: " + index_name) pinecone.create_index( index_name, dimension=len(embedding['data'][0]['embedding']), metric='cosine', metadata_config={'indexed': ['source', 'id']} )
डेटाबेस बनाने में स्क्रिप्ट को कुछ मिनट लग सकते हैं।
$ python db_create.py
इसके बाद, मैं टिकटोकन पैकेज इंस्टॉल करूंगा। मैं इसका उपयोग यह गणना करने के लिए करूँगा कि स्रोत दस्तावेज़ों में कितने टोकन हैं। यह महत्वपूर्ण है क्योंकि एम्बेडिंग मॉडल केवल 8191 टोकन तक ही संभाल सकता है।
$ pip install tiktoken
पैकेज स्थापित करते समय, आइए एक अच्छी दिखने वाली प्रगति पट्टी बनाने के लिए tqdm
भी स्थापित करें।
$ pip install tqdm
अब मुझे दस्तावेज़ों को डेटाबेस में अपलोड करना होगा। इसके लिए स्क्रिप्ट को index_docs.py
कहा जाएगा। आइए आवश्यक मॉड्यूल आयात करके और कुछ स्थिरांक परिभाषित करके शुरुआत करें:
# index_docs.py # Pinecone db name and upload batch size index_name = 'semaphore' upsert_batch_size = 20 # OpenAI embedding and tokenizer models embed_model = "text-embedding-ada-002" encoding_model = "cl100k_base" max_tokens_model = 8191
इसके बाद, हमें टोकन गिनने के लिए एक फ़ंक्शन की आवश्यकता होगी। OpenAI पेज पर एक टोकन काउंटर उदाहरण है:
import tiktoken def num_tokens_from_string(string: str) -> int: """Returns the number of tokens in a text string.""" encoding = tiktoken.get_encoding(encoding_model) num_tokens = len(encoding.encode(string)) return num_tokens
अंततः, मुझे मूल दस्तावेज़ को प्रयोग करने योग्य उदाहरणों में बदलने के लिए कुछ फ़िल्टरिंग फ़ंक्शंस की आवश्यकता होगी। दस्तावेज़ में अधिकांश उदाहरण कोड बाड़ के बीच हैं, इसलिए मैं प्रत्येक फ़ाइल से सभी YAML कोड निकालूंगा:
import re def extract_yaml(text: str) -> str: """Returns list with all the YAML code blocks found in text.""" matches = [m.group(1) for m in re.finditer("```yaml([\w\W]*?)```", text)] return matches
मेरा कार्य पूरा हो गया है। इसके बाद, यह फ़ाइलों को मेमोरी में लोड करेगा और उदाहरण निकालेगा:
from tqdm import tqdm import sys import os import pathlib repo_path = sys.argv[1] repo_path = os.path.abspath(repo_path) repo = pathlib.Path(repo_path) markdown_files = list(repo.glob("**/*.md")) + list( repo.glob("**/*.mdx") ) print(f"Extracting YAML from Markdown files in {repo_path}") new_data = [] for i in tqdm(range(0, len(markdown_files))): markdown_file = markdown_files[i] with open(markdown_file, "r") as f: relative_path = markdown_file.relative_to(repo_path) text = str(f.read()) if text == '': continue yamls = extract_yaml(text) j = 0 for y in yamls: j = j+1 new_data.append({ "source": str(relative_path), "text": y, "id": f"github.com/semaphore/docs/{relative_path}[{j}]" })
इस बिंदु पर, सभी YAML को new_data
सूची में संग्रहीत किया जाना चाहिए। अंतिम चरण एम्बेडिंग को पाइनकोन में अपलोड करना है।
import pinecone import openai api_key = os.getenv("PINECONE_API_KEY") env = os.getenv("PINECONE_ENVIRONMENT") pinecone.init(api_key=api_key, enviroment=env) index = pinecone.Index(index_name) print(f"Creating embeddings and uploading vectors to database") for i in tqdm(range(0, len(new_data), upsert_batch_size)): i_end = min(len(new_data), i+upsert_batch_size) meta_batch = new_data[i:i_end] ids_batch = [x['id'] for x in meta_batch] texts = [x['text'] for x in meta_batch] embedding = openai.Embedding.create(input=texts, engine=embed_model) embeds = [record['embedding'] for record in embedding['data']] # clean metadata before upserting meta_batch = [{ 'id': x['id'], 'text': x['text'], 'source': x['source'] } for x in meta_batch] to_upsert = list(zip(ids_batch, embeds, meta_batch)) index.upsert(vectors=to_upsert)
संदर्भ के रूप में, आप डेमो रिपॉजिटरी में पूरी Index_docs.py फ़ाइल पा सकते हैं
आइए डेटाबेस सेटअप पूरा करने के लिए इंडेक्स स्क्रिप्ट चलाएँ:
$ git clone https://github.com/semaphoreci/docs.git /tmp/docs $ source .env $ python index_docs.py /tmp/docs
पाइनकोन डैशबोर्ड को डेटाबेस में वैक्टर दिखाना चाहिए।
हम निम्नलिखित कोड के साथ डेटाबेस को क्वेरी कर सकते हैं, जिसे आप स्क्रिप्ट के रूप में या सीधे पायथन आरईपीएल में चला सकते हैं:
$ python >>> import os >>> import pinecone >>> import openai # Compute embeddings for string "Docker Container" >>> embeddings = openai.Embedding.create(input="Docker Containers", engine="text-embedding-ada-002") # Connect to database >>> index_name = "semaphore" >>> api_key = os.getenv("PINECONE_API_KEY") >>> env = os.getenv("PINECONE_ENVIRONMENT") >>> pinecone.init(api_key=api_key, environment=env) >>> index = pinecone.Index(index_name) # Query database >>> matches = index.query(embeddings['data'][0]['embedding'], top_k=1, include_metadata=True) >>> matches['matches'][0] {'id': 'github.com/semaphore/docs/docs/ci-cd-environment/docker-authentication.md[3]', 'metadata': {'id': 'github.com/semaphore/docs/docs/ci-cd-environment/docker-authentication.md[3]', 'source': 'docs/ci-cd-environment/docker-authentication.md', 'text': '\n' '# .semaphore/semaphore.yml\n' 'version: v1.0\n' 'name: Using a Docker image\n' 'agent:\n' ' machine:\n' ' type: e1-standard-2\n' ' os_image: ubuntu1804\n' '\n' 'blocks:\n' ' - name: Run container from Docker Hub\n' ' task:\n' ' jobs:\n' ' - name: Authenticate docker pull\n' ' commands:\n' ' - checkout\n' ' - echo $DOCKERHUB_PASSWORD | docker login ' '--username "$DOCKERHUB_USERNAME" --password-stdin\n' ' - docker pull /\n' ' - docker images\n' ' - docker run /\n' ' secrets:\n' ' - name: docker-hub\n'}, 'score': 0.796259582, 'values': []}
जैसा कि आप देख सकते हैं, पहला मिलान सेमाफोर पाइपलाइन के लिए YAML है जो एक डॉकर छवि खींचता है और उसे चलाता है। यह एक अच्छी शुरुआत है क्योंकि यह हमारे "डॉकर कंटेनर्स" खोज स्ट्रिंग के लिए प्रासंगिक है।
हमारे पास डेटा है, और हम जानते हैं कि इसकी क्वेरी कैसे करनी है। आइए इसे बॉट में काम पर लगाएं।
प्रॉम्प्ट को संसाधित करने के चरण हैं:
हमेशा की तरह, मैं बॉट की मुख्य स्क्रिप्ट, complete.py
में कुछ स्थिरांक को परिभाषित करके शुरुआत करूंगा:
# complete.py # Pinecone database name, number of matched to retrieve # cutoff similarity score, and how much tokens as context index_name = 'semaphore' context_cap_per_query = 30 match_min_score = 0.75 context_tokens_per_query = 3000 # OpenAI LLM model parameters chat_engine_model = "gpt-3.5-turbo" max_tokens_model = 4096 temperature = 0.2 embed_model = "text-embedding-ada-002" encoding_model_messages = "gpt-3.5-turbo-0301" encoding_model_strings = "cl100k_base" import pinecone import os # Connect with Pinecone db and index api_key = os.getenv("PINECONE_API_KEY") env = os.getenv("PINECONE_ENVIRONMENT") pinecone.init(api_key=api_key, environment=env) index = pinecone.Index(index_name)
इसके बाद, मैं टोकन गिनने के लिए फ़ंक्शन जोड़ूंगा जैसा कि OpenAI उदाहरणों में दिखाया गया है। पहला फ़ंक्शन स्ट्रिंग में टोकन की गणना करता है, जबकि दूसरा संदेशों में टोकन की गणना करता है। हम संदेशों को थोड़ा विस्तार से देखेंगे। अभी के लिए, मान लीजिए कि यह एक संरचना है जो बातचीत की स्थिति को स्मृति में रखती है।
import tiktoken def num_tokens_from_string(string: str) -> int: """Returns the number of tokens in a text string.""" encoding = tiktoken.get_encoding(encoding_model_strings) num_tokens = len(encoding.encode(string)) return num_tokens def num_tokens_from_messages(messages): """Returns the number of tokens used by a list of messages. Compatible with model """ try: encoding = tiktoken.encoding_for_model(encoding_model_messages) except KeyError: encoding = tiktoken.get_encoding(encoding_model_strings) num_tokens = 0 for message in messages: num_tokens += 4 # every message follows {role/name}\n{content}\n for key, value in message.items(): num_tokens += len(encoding.encode(value)) if key == "name": # if there's a name, the role is omitted num_tokens += -1 # role is always required and always 1 token num_tokens += 2 # every reply is primed with assistant return num_tokens
निम्नलिखित फ़ंक्शन GPT-3 के लिए एक समृद्ध प्रॉम्प्ट वापस करने के लिए मूल प्रॉम्प्ट और संदर्भ स्ट्रिंग लेता है:
def get_prompt(query: str, context: str) -> str: """Return the prompt with query and context.""" return ( f"Create the continuous integration pipeline YAML code to fullfil the requested task.\n" + f"Below you will find some context that may help. Ignore it if it seems irrelevant.\n\n" + f"Context:\n{context}" + f"\n\nTask: {query}\n\nYAML Code:" )
get_message
फ़ंक्शन एपीआई के साथ संगत प्रारूप में प्रॉम्प्ट को प्रारूपित करता है:
def get_message(role: str, content: str) -> dict: """Generate a message for OpenAI API completion.""" return {"role": role, "content": content}
तीन प्रकार की भूमिकाएँ हैं जो मॉडल की प्रतिक्रिया को प्रभावित करती हैं:
अब आकर्षक भाग के लिए। get_context
फ़ंक्शन संकेत लेता है, डेटाबेस से पूछताछ करता है, और इनमें से एक शर्त पूरी होने तक एक संदर्भ स्ट्रिंग उत्पन्न करता है:
context_tokens_per_query
से अधिक है, वह स्थान जो मैंने संदर्भ के लिए आरक्षित किया था।match_min_score
से नीचे है उन्हें नज़रअंदाज कर दिया जाता है। import openai def get_context(query: str, max_tokens: int) -> list: """Generate message for OpenAI model. Add context until hitting `context_token_limit` limit. Returns prompt string.""" embeddings = openai.Embedding.create( input=[query], engine=embed_model ) # search the database vectors = embeddings['data'][0]['embedding'] embeddings = index.query(vectors, top_k=context_cap_per_query, include_metadata=True) matches = embeddings['matches'] # filter and aggregate context usable_context = "" context_count = 0 for i in range(0, len(matches)): source = matches[i]['metadata']['source'] if matches[i]['score'] < match_min_score: # skip context with low similarity score continue context = matches[i]['metadata']['text'] token_count = num_tokens_from_string(usable_context + '\n---\n' + context) if token_count < context_tokens_per_query: usable_context = usable_context + '\n---\n' + context context_count = context_count + 1 print(f"Found {context_count} contexts for your query") return usable_context
अगला और अंतिम फ़ंक्शन, complete
, OpenAI को एपीआई अनुरोध जारी करता है और मॉडल की प्रतिक्रिया लौटाता है।
def complete(messages): """Query the OpenAI model. Returns the first answer. """ res = openai.ChatCompletion.create( model=chat_engine_model, messages=messages, temperature=temperature ) return res.choices[0].message.content.strip()
बस इतना ही; अब मुझे केवल कमांड लाइन तर्कों से निपटना है और फ़ंक्शन को सही क्रम में कॉल करना है:
import sys query = sys.argv[1] context = get_context(query, context_tokens_per_query) prompt = get_prompt(query, context) # initialize messages list to send to OpenAI API messages = [] messages.append(get_message('user', prompt)) messages.append(get_message('system', 'You are a helpful assistant that writes YAML code for Semaphore continuous integration pipelines and explains them. Return YAML code inside code fences.')) if num_tokens_from_messages(messages) >= max_tokens_model: raise Exception('Model token size limit reached') print("Working on your query... ") answer = complete(messages) print("Answer:\n") print(answer) messages.append(get_message('assistant', answer))
अब स्क्रिप्ट चलाने और यह देखने का समय आ गया है कि इसका प्रदर्शन कैसा रहता है:
$ python complete.py "Create a CI pipeline that builds and uploads a Docker image to Docker Hub"
परिणाम है:
version: v1.0 name: Docker Build and Push agent: machine: type: e1-standard-2 os_image: ubuntu1804 blocks: - name: "Build and Push Docker Image" task: jobs: - name: "Docker Build and Push" commands: - checkout - docker build -t /: . - echo "$DOCKERHUB_PASSWORD" | docker login -u "$DOCKERHUB_USERNAME" --password-stdin - docker push /: promotions: - name: Deploy to production pipeline_file: deploy-production.yml auto_promote: when: "result = 'passed' and branch = 'master'"
यह पहला अच्छा परिणाम है. मॉडल ने हमारे द्वारा प्रदान किए गए संदर्भ उदाहरणों से वाक्यविन्यास का अनुमान लगाया है।
याद रखें कि मैंने एक मामूली लक्ष्य के साथ शुरुआत की थी: YAML पाइपलाइन लिखने के लिए एक सहायक बनाना। अपने वेक्टर डेटाबेस में समृद्ध सामग्री के साथ, मैं सेमाफोर (या किसी भी उत्पाद - दस्तावेज़ों को /tmp
में क्लोन करना याद रखें) के बारे में किसी भी प्रश्न का उत्तर देने के लिए बॉट को सामान्यीकृत कर सकता हूं।
अच्छे उत्तर प्राप्त करने की कुंजी - आश्चर्यजनक रूप से - गुणवत्तापूर्ण संदर्भ है। केवल प्रत्येक दस्तावेज़ को वेक्टर डेटाबेस में अपलोड करने से अच्छे परिणाम मिलने की संभावना नहीं है। संदर्भ डेटाबेस को क्यूरेट किया जाना चाहिए, वर्णनात्मक मेटाडेटा के साथ टैग किया जाना चाहिए और संक्षिप्त होना चाहिए। अन्यथा, हम प्रॉम्प्ट में अप्रासंगिक संदर्भ के साथ टोकन कोटा भरने का जोखिम उठाते हैं।
तो, एक अर्थ में, हमारी जरूरतों को पूरा करने के लिए बॉट को ठीक करने में एक कला - और बहुत सारे परीक्षण और त्रुटि - शामिल है। हम संदर्भ सीमा के साथ प्रयोग कर सकते हैं, निम्न-गुणवत्ता वाली सामग्री को हटा सकते हैं, सारांशित कर सकते हैं और समानता स्कोर को समायोजित करके अप्रासंगिक संदर्भ को फ़िल्टर कर सकते हैं।
आपने देखा होगा कि मेरा बॉट हमें चैटजीपीटी जैसी वास्तविक बातचीत करने में सक्षम नहीं बनाता है। हम एक प्रश्न पूछते हैं और एक उत्तर पाते हैं।
बॉट को पूरी तरह से चैटबॉट में बदलना, सिद्धांत रूप में, बहुत चुनौतीपूर्ण नहीं है। हम प्रत्येक एपीआई अनुरोध के साथ मॉडल पर पिछली प्रतिक्रियाएँ दोबारा भेजकर बातचीत को बनाए रख सकते हैं। पिछले GPT-3 उत्तरों को "सहायक" भूमिका के अंतर्गत वापस भेजा जाता है। उदाहरण के लिए:
messages = [] while True: query = input('Type your prompt:\n') context = get_context(query, context_tokens_per_query) prompt = get_prompt(query, context) messages.append(get_message('user', prompt)) messages.append(get_message('system', 'You are a helpful assistant that writes YAML code for Semaphore continuous integration pipelines and explains them. Return YAML code inside code fences.')) if num_tokens_from_messages(messages) >= max_tokens_model: raise Exception('Model token size limit reached') print("Working on your query... ") answer = complete(messages) print("Answer:\n") print(answer) # remove system message and append model's answer messages.pop() messages.append(get_message('assistant', answer))
दुर्भाग्य से, यह कार्यान्वयन अल्पविकसित है। यह विस्तारित बातचीत का समर्थन नहीं करेगा क्योंकि प्रत्येक बातचीत के साथ टोकन की संख्या बढ़ जाती है। जल्द ही, हम GPT-3 के लिए 4096-टोकन सीमा तक पहुंच जाएंगे, जिससे आगे की बातचीत नहीं हो पाएगी।
इसलिए, हमें अनुरोध को टोकन सीमा के भीतर रखने का कोई तरीका खोजना होगा। कुछ रणनीतियाँ अनुसरण करती हैं:
शब्द एम्बेडिंग और एक अच्छे संदर्भ डेटाबेस के साथ बॉट की प्रतिक्रियाओं को बढ़ाना संभव है। इसे प्राप्त करने के लिए, हमें अच्छी गुणवत्ता वाले दस्तावेज़ीकरण की आवश्यकता है। किसी ऐसे बॉट को विकसित करने में पर्याप्त मात्रा में परीक्षण और त्रुटि शामिल होती है जो विषय वस्तु की समझ रखता है।
मुझे आशा है कि शब्द एम्बेडिंग और बड़े भाषा मॉडल की यह गहन खोज आपकी आवश्यकताओं के अनुरूप एक अधिक शक्तिशाली बॉट बनाने में आपकी सहायता करेगी।
शुभ भवन!
यहाँ भी प्रकाशित किया गया है.