paint-brush
वर्ड एंबेडिंग्स: बेहतर उत्तरों के लिए आपके चैटबॉट को संदर्भ देने का गुप्त तरीकाद्वारा@tomfernblog
2,991 रीडिंग
2,991 रीडिंग

वर्ड एंबेडिंग्स: बेहतर उत्तरों के लिए आपके चैटबॉट को संदर्भ देने का गुप्त तरीका

द्वारा Tomas Fernandez18m2023/07/26
Read on Terminal Reader

बहुत लंबा; पढ़ने के लिए

वर्ड एम्बेडिंग और चैटजीपीटी का उपयोग करके एक विशेषज्ञ बॉट बनाना सीखें। अपने चैटबॉट की प्रतिक्रियाओं को बढ़ाने के लिए शब्द वैक्टर की शक्ति का लाभ उठाएं।
featured image - वर्ड एंबेडिंग्स: बेहतर उत्तरों के लिए आपके चैटबॉट को संदर्भ देने का गुप्त तरीका
Tomas Fernandez HackerNoon profile picture
0-item
1-item

इसमें कोई संदेह नहीं है कि ओपनएआई का चैटजीपीटी असाधारण रूप से बुद्धिमान है - इसने वकील का बार टेस्ट पास कर लिया है, इसके पास डॉक्टर के समान ज्ञान है , और कुछ परीक्षणों में इसका आईक्यू 155 पर पहुंच गया है । हालाँकि, यह अज्ञानता को स्वीकार करने के बजाय जानकारी गढ़ने की प्रवृत्ति रखता है। यह प्रवृत्ति, इस तथ्य के साथ मिलकर कि इसका ज्ञान 2021 में समाप्त हो जाएगा, जीपीटी एपीआई का उपयोग करके विशेष उत्पादों के निर्माण में चुनौतियां पैदा करता है।


हम इन बाधाओं को कैसे पार कर सकते हैं? हम GPT-3 जैसे मॉडल को नया ज्ञान कैसे प्रदान कर सकते हैं? मेरा लक्ष्य पायथन, ओपनएआई एपीआई और वर्ड एम्बेडिंग का उपयोग करके एक प्रश्न-उत्तर देने वाला बॉट बनाकर इन सवालों का समाधान करना है।

मैं क्या निर्माण करूंगा

मैं एक ऐसा बॉट बनाने का इरादा रखता हूं जो एक प्रॉम्प्ट से निरंतर एकीकरण पाइपलाइन उत्पन्न करता है, जो, जैसा कि आप जानते होंगे, सेमाफोर सीआई/सीडी में वाईएएमएल के साथ तैयार किए जाते हैं


यहां क्रियाशील बॉट का एक उदाहरण दिया गया है:

चल रहे प्रोग्राम का स्क्रीनशॉट. स्क्रीन पर, कमांड निष्पादित होता है: Python query.py "एक CI पाइपलाइन बनाएं जो डॉकर हब पर डॉकर छवि बनाता है और अपलोड करता है", और प्रोग्राम CI पाइपलाइन के अनुरूप YAML प्रिंट करता है जो अनुरोधित कार्रवाई करता है। चल रहे प्रोग्राम का स्क्रीनशॉट. स्क्रीन पर, कमांड 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डी प्रतिनिधित्व। वास्तव में, इन स्थानों के सैकड़ों या हजारों आयाम हो सकते हैं। स्रोत: एआई के मल्टीटूल से मिलें: वेक्टर एंबेडिंग


इन सबके पीछे का वास्तविक गणित इस लेख के दायरे से परे है। हालाँकि, मुख्य बात यह है कि वेक्टर ऑपरेशन हमें गणित का उपयोग करके अर्थ में हेरफेर करने या निर्धारित करने की अनुमति देते हैं । वह वेक्टर लें जो "रानी" शब्द का प्रतिनिधित्व करता है, उसमें से "महिला" वेक्टर घटाएं, और "पुरुष" वेक्टर जोड़ें। परिणाम "राजा" के आसपास एक वेक्टर होना चाहिए। यदि हम "बेटा" जोड़ते हैं, तो हमें "राजकुमार" के करीब पहुंचना चाहिए।

टोकन के साथ तंत्रिका नेटवर्क एम्बेड करना

अब तक, हमने शब्दों को इनपुट और संख्याओं को आउटपुट के रूप में लेते हुए तंत्रिका नेटवर्क को एम्बेड करने पर चर्चा की है। हालाँकि, कई आधुनिक नेटवर्क शब्दों के प्रसंस्करण से टोकन के प्रसंस्करण की ओर बढ़ गए हैं।


टोकन टेक्स्ट की सबसे छोटी इकाई है जिसे मॉडल द्वारा संसाधित किया जा सकता है। टोकन शब्द, वर्ण, विराम चिह्न, प्रतीक या शब्दों के भाग हो सकते हैं।


हम ओपनएआई ऑनलाइन टोकननाइज़र के साथ प्रयोग करके देख सकते हैं कि कैसे शब्दों को टोकन में परिवर्तित किया जाता है, जो टेक्स्ट को टोकन में बदलने और प्रत्येक को एक संख्या के साथ दर्शाने के लिए बाइट-पेयर एन्कोडिंग (बीपीई) का उपयोग करता है:

OpenAI टोकनाइज़र का स्क्रीनशॉट। कुछ पाठ इनपुट किया गया है, और प्रत्येक टोकन को अलग-अलग रंगों द्वारा दर्शाया गया है, जिससे हमें यह देखने की अनुमति मिलती है कि शब्दों को टोकन में कैसे मैप किया जाता है। पाठ पढ़ता है: किसी भी एम्बेडिंग मॉडल के पीछे, एक तंत्रिका नेटवर्क होता है जो इनपुट टेक्स्ट को वैक्टर में परिवर्तित करता है। प्रत्येक प्रकार के एम्बेडिंग मॉडल की अलग-अलग क्षमताएं और गति होती हैं। उदाहरण के लिए, Word2vec शब्द लेता है और 100 से 300 आयामों की सीमा में वैक्टर बनाता है। टोकन और शब्दों के बीच अक्सर 1-टू-1 संबंध होता है। अधिकांश टोकन में शब्द और एक अग्रणी स्थान शामिल होता है। हालाँकि, "एम्बेडिंग" जैसे विशेष मामले हैं, जिसमें दो टोकन होते हैं, "एम्बेड" और "डिंग," या "क्षमताएँ", जिसमें चार टोकन होते हैं। यदि आप "टोकन आईडी" पर क्लिक करते हैं, तो आप प्रत्येक टोकन के मॉडल का संख्यात्मक प्रतिनिधित्व देख सकते हैं।

एंबेडिंग का उपयोग करके एक स्मार्ट बॉट डिजाइन करना

अब जब हमें यह समझ आ गया है कि एम्बेडिंग क्या हैं, तो अगला सवाल यह है: वे एक स्मार्ट बॉट बनाने में हमारी मदद कैसे कर सकते हैं?


सबसे पहले, आइए विचार करें कि जब हम सीधे GPT-3 API का उपयोग करते हैं तो क्या होता है। उपयोगकर्ता एक संकेत जारी करता है, और मॉडल अपनी सर्वोत्तम क्षमता का जवाब देता है।

उपयोगकर्ता और GPT-3 के बीच बातचीत दर्शाने वाला आरेख। उपयोगकर्ता एक संकेत भेजता है, मॉडल प्रतिक्रिया देता है।


हालाँकि, जब हम समीकरण में संदर्भ जोड़ते हैं, तो चीज़ें बदल जाती हैं। उदाहरण के लिए, जब मैंने संदर्भ प्रदान करने के बाद चैटजीपीटी से विश्व कप के विजेता के बारे में पूछा, तो इससे सारा फर्क पड़ गया।


तो, एक स्मार्ट बॉट बनाने की योजना इस प्रकार है:

  1. उपयोगकर्ता के संकेत को अवरोधित करें.
  2. उस प्रॉम्प्ट के लिए एम्बेडिंग की गणना करें, जिससे एक वेक्टर प्राप्त हो।
  3. वेक्टर के पास दस्तावेज़ों के लिए एक डेटाबेस खोजें, क्योंकि उन्हें प्रारंभिक संकेत के लिए शब्दार्थ रूप से प्रासंगिक होना चाहिए।
  4. किसी भी प्रासंगिक संदर्भ के साथ मूल संकेत GPT-3 पर भेजें।
  5. उपयोगकर्ता को GPT-3 की प्रतिक्रिया अग्रेषित करें।

किसी बॉट का अधिक जटिल कार्यान्वयन. उपयोगकर्ता चैटबॉट ऐप को प्रॉम्प्ट भेजता है, जो एक संदर्भ डेटाबेस खोजता है और प्रॉम्प्ट को समृद्ध करने के लिए उसका उपयोग करता है। संकेत GPT-3 को भेजा जाता है, और इसकी प्रतिक्रिया उपयोगकर्ता को भेज दी जाती है।


आइए अधिकांश परियोजनाओं की तरह, डेटाबेस डिज़ाइन करके शुरुआत करें।

एंबेडिंग के साथ एक नॉलेज डेटाबेस बनाना

हमारे संदर्भ डेटाबेस में मूल दस्तावेज़ और उनके संबंधित वैक्टर शामिल होने चाहिए। सिद्धांत रूप में, हम इस कार्य के लिए किसी भी प्रकार के डेटाबेस को नियोजित कर सकते हैं, लेकिन एक वेक्टर डेटाबेस इस कार्य के लिए इष्टतम उपकरण है।


वेक्टर डेटाबेस विशेष डेटाबेस हैं जिन्हें उच्च-आयामी वेक्टर डेटा को संग्रहीत और पुनर्प्राप्त करने के लिए डिज़ाइन किया गया है। खोज के लिए SQL जैसी क्वेरी भाषा का उपयोग करने के बजाय, हम एक वेक्टर प्रदान करते हैं और N निकटतम पड़ोसियों से अनुरोध करते हैं।


वैक्टर उत्पन्न करने के लिए, हम OpenAI से text-embedding-ada-002 का उपयोग करेंगे, क्योंकि यह उनके द्वारा पेश किया जाने वाला सबसे तेज़ और सबसे अधिक लागत प्रभावी मॉडल है। मॉडल इनपुट टेक्स्ट को टोकन में परिवर्तित करता है और उनके संबंधों को जानने के लिए ट्रांसफॉर्मर नामक ध्यान तंत्र का उपयोग करता है। इस तंत्रिका नेटवर्क का आउटपुट पाठ के अर्थ का प्रतिनिधित्व करने वाले वैक्टर हैं।

टोकनाइजेशन प्रक्रिया को दर्शाने वाला आरेख। एक दस्तावेज़ को टोकन किया जाता है और फिर एक एम्बेडिंग न्यूरल नेटवर्क पर भेजा जाता है। नेटवर्क का आउटपुट एक वेक्टर है.


एक संदर्भ डेटाबेस बनाने के लिए, मैं:

  1. सभी स्रोत दस्तावेज़ एकत्र करें.
  2. अप्रासंगिक दस्तावेज़ों को फ़िल्टर करें.
  3. प्रत्येक दस्तावेज़ के लिए एम्बेडिंग की गणना करें।
  4. डेटाबेस में वेक्टर, मूल पाठ और कोई अन्य प्रासंगिक मेटाडेटा संग्रहीत करें।

संदर्भ डेटाबेस में डेटा संग्रहीत करने की प्रक्रिया को दर्शाने वाला आरेख। स्रोत दस्तावेज़ एम्बेडिंग न्यूरल नेटवर्क को भेजा जाता है। डेटाबेस मूल पाठ के साथ वेक्टर को संग्रहीत करता है।

दस्तावेज़ों को वेक्टर में परिवर्तित करना

सबसे पहले, मुझे 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

डेटाबेस का परीक्षण

पाइनकोन डैशबोर्ड को डेटाबेस में वैक्टर दिखाना चाहिए।

पाइनकोन डैशबोर्ड का स्क्रीनशॉट कुल 79 वैक्टर के साथ डेटाबेस दिखा रहा है

हम निम्नलिखित कोड के साथ डेटाबेस को क्वेरी कर सकते हैं, जिसे आप स्क्रिप्ट के रूप में या सीधे पायथन आरईपीएल में चला सकते हैं:

 $ 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 है जो एक डॉकर छवि खींचता है और उसे चलाता है। यह एक अच्छी शुरुआत है क्योंकि यह हमारे "डॉकर कंटेनर्स" खोज स्ट्रिंग के लिए प्रासंगिक है।

बॉट का निर्माण

हमारे पास डेटा है, और हम जानते हैं कि इसकी क्वेरी कैसे करनी है। आइए इसे बॉट में काम पर लगाएं।

प्रॉम्प्ट को संसाधित करने के चरण हैं:

  1. उपयोगकर्ता का संकेत लें.
  2. इसके वेक्टर की गणना करें.
  3. डेटाबेस से प्रासंगिक संदर्भ पुनर्प्राप्त करें।
  4. संदर्भ के साथ उपयोगकर्ता का संकेत GPT-3 पर भेजें।
  5. उपयोगकर्ता को मॉडल की प्रतिक्रिया अग्रेषित करें।

बॉट के लिए डेटा प्रवाह का आरेख. बाईं ओर उपयोगकर्ता संकेत दर्ज करता है, जिसे एम्बेडिंग न्यूरल नेटवर्क द्वारा संसाधित किया जाता है, फिर संदर्भ डेटाबेस में भेजा जाता है। खोज से प्रासंगिक पाठ प्राप्त होता है जिसे GPT-3 मॉडल पर भेजा जाता है। मॉडल का आउटपुट अंतिम उत्तर के रूप में उपयोगकर्ता को भेजा जाता है। हमेशा की तरह, मैं बॉट की मुख्य स्क्रिप्ट, 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}

तीन प्रकार की भूमिकाएँ हैं जो मॉडल की प्रतिक्रिया को प्रभावित करती हैं:

  • उपयोगकर्ता : उपयोगकर्ता के मूल संकेत के लिए.
  • सिस्टम : सहायक के व्यवहार को निर्धारित करने में मदद करता है। हालाँकि इसकी प्रभावशीलता के संबंध में कुछ विवाद है, संदेश सूची के अंत में भेजे जाने पर यह अधिक प्रभावी प्रतीत होता है।
  • सहायक : मॉडल की पिछली प्रतिक्रियाओं का प्रतिनिधित्व करता है। OpenAI API में "मेमोरी" नहीं है; इसके बजाय, हमें बातचीत को बनाए रखने के लिए प्रत्येक इंटरैक्शन के दौरान मॉडल की पिछली प्रतिक्रियाओं को वापस भेजना होगा।


अब आकर्षक भाग के लिए। 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-टोकन सीमा तक पहुंच जाएंगे, जिससे आगे की बातचीत नहीं हो पाएगी।


इसलिए, हमें अनुरोध को टोकन सीमा के भीतर रखने का कोई तरीका खोजना होगा। कुछ रणनीतियाँ अनुसरण करती हैं:

  • पुराने संदेश हटाएँ. हालाँकि यह सबसे सरल समाधान है, यह बातचीत की "मेमोरी" को केवल सबसे हाल के संदेशों तक सीमित करता है।
  • पिछले संदेशों को सारांशित करें. हम पहले के संदेशों को संक्षिप्त करने और उन्हें मूल प्रश्नों और उत्तरों के स्थान पर प्रतिस्थापित करने के लिए "मॉडल से पूछें" का उपयोग कर सकते हैं। यद्यपि यह दृष्टिकोण लागत और प्रश्नों के बीच अंतराल को बढ़ाता है, लेकिन यह पिछले संदेशों को हटाने की तुलना में बेहतर परिणाम दे सकता है।
  • इंटरैक्शन की संख्या पर एक सख्त सीमा निर्धारित करें।
  • GPT-4 API की सामान्य उपलब्धता की प्रतीक्षा करें, जो न केवल अधिक स्मार्ट है बल्कि इसकी टोकन क्षमता दोगुनी है।
  • "gpt-3.5-टर्बो-16k" जैसे नए मॉडल का उपयोग करें जो 16k टोकन तक संभाल सकता है

निष्कर्ष

शब्द एम्बेडिंग और एक अच्छे संदर्भ डेटाबेस के साथ बॉट की प्रतिक्रियाओं को बढ़ाना संभव है। इसे प्राप्त करने के लिए, हमें अच्छी गुणवत्ता वाले दस्तावेज़ीकरण की आवश्यकता है। किसी ऐसे बॉट को विकसित करने में पर्याप्त मात्रा में परीक्षण और त्रुटि शामिल होती है जो विषय वस्तु की समझ रखता है।


मुझे आशा है कि शब्द एम्बेडिंग और बड़े भाषा मॉडल की यह गहन खोज आपकी आवश्यकताओं के अनुरूप एक अधिक शक्तिशाली बॉट बनाने में आपकी सहायता करेगी।


शुभ भवन!


यहाँ भी प्रकाशित किया गया है.