ওপেনএআই যখন ChatGPT-এর জন্য উন্নত ভয়েস মোড প্রকাশ করতে দেরি করছে, আমি কীভাবে আমাদের LLM ভয়েস অ্যাপ্লিকেশন তৈরি করেছি এবং এটিকে একটি ইন্টারেক্টিভ বুথে সংহত করেছি তা শেয়ার করতে চাই।
ফেব্রুয়ারির শেষে, বালি বিখ্যাত বার্নিং ম্যান-এর নীতি অনুসারে সাজানো ল্যাম্পু উৎসবের আয়োজন করেছিল। এর ঐতিহ্য অনুযায়ী, অংশগ্রহণকারীরা তাদের নিজস্ব ইনস্টলেশন এবং শিল্প বস্তু তৈরি করে।
ক্যাম্প 19:19- এর আমার বন্ধুরা এবং আমি, ক্যাথলিক স্বীকারোক্তির ধারণা এবং বর্তমান LLM-এর ক্ষমতার দ্বারা অনুপ্রাণিত হয়ে, আমাদের নিজস্ব AI স্বীকারোক্তিমূলক নির্মাণের ধারণা নিয়ে এসেছি, যেখানে যে কেউ কৃত্রিম বুদ্ধিমত্তার সাথে কথা বলতে পারে।
শুরুতে আমরা কীভাবে এটি কল্পনা করেছি তা এখানে:
ধারণাটি পরীক্ষা করতে এবং LLM-এর জন্য একটি প্রম্পট দিয়ে পরীক্ষা শুরু করতে, আমি এক সন্ধ্যায় একটি নিষ্প্রভ বাস্তবায়ন তৈরি করেছি:
এই ডেমোটি বাস্তবায়ন করতে, আমি সম্পূর্ণরূপে OpenAI থেকে ক্লাউড মডেলের উপর নির্ভর করেছি: হুইস্পার , GPT-4 , এবং TTS । চমৎকার লাইব্রেরি speech_recognition- এর জন্য ধন্যবাদ, আমি কোডের মাত্র কয়েক ডজন লাইনে ডেমো তৈরি করেছি।
import os import asyncio from dotenv import load_dotenv from io import BytesIO from openai import AsyncOpenAI from soundfile import SoundFile import sounddevice as sd import speech_recognition as sr load_dotenv() aiclient = AsyncOpenAI( api_key=os.environ.get("OPENAI_API_KEY") ) SYSTEM_PROMPT = """ You are helpfull assistant. """ async def listen_mic(recognizer: sr.Recognizer, microphone: sr.Microphone): audio_data = recognizer.listen(microphone) wav_data = BytesIO(audio_data.get_wav_data()) wav_data.name = "SpeechRecognition_audio.wav" return wav_data async def say(text: str): res = await aiclient.audio.speech.create( model="tts-1", voice="alloy", response_format="opus", input=text ) buffer = BytesIO() for chunk in res.iter_bytes(chunk_size=4096): buffer.write(chunk) buffer.seek(0) with SoundFile(buffer, 'r') as sound_file: data = sound_file.read(dtype='int16') sd.play(data, sound_file.samplerate) sd.wait() async def respond(text: str, history): history.append({"role": "user", "content": text}) completion = await aiclient.chat.completions.create( model="gpt-4", temperature=0.5, messages=history, ) response = completion.choices[0].message.content await say(response) history.append({"role": "assistant", "content": response}) async def main() -> None: m = sr.Microphone() r = sr.Recognizer() messages = [{"role": "system", "content": SYSTEM_PROMPT}] with m as source: r.adjust_for_ambient_noise(source) while True: wav_data = await listen_mic(r, source) transcript = await aiclient.audio.transcriptions.create( model="whisper-1", temperature=0.5, file=wav_data, response_format="verbose_json", ) if transcript.text == '' or transcript.text is None: continue await respond(transcript.text, messages) if __name__ == '__main__': asyncio.run(main())
আমাদের যে সমস্যাগুলি সমাধান করতে হয়েছিল তা এই ডেমোর প্রথম পরীক্ষার পরে স্পষ্ট হয়ে ওঠে:
আমাদের কাছে এই সমস্যাগুলি কীভাবে সমাধান করা যায় তার একটি পছন্দ ছিল: একটি উপযুক্ত প্রকৌশল বা পণ্য সমাধান খোঁজার মাধ্যমে।
আমরা কোড করার আগে, ব্যবহারকারী বুথের সাথে কীভাবে ইন্টারঅ্যাক্ট করবে তা আমাদের সিদ্ধান্ত নিতে হয়েছিল:
বুথে একজন নতুন ব্যবহারকারীকে শনাক্ত করতে, আমরা বেশ কয়েকটি বিকল্প বিবেচনা করেছি: দরজা খোলার সেন্সর, মেঝে ওজন সেন্সর, দূরত্ব সেন্সর, এবং একটি ক্যামেরা + YOLO মডেল। পিছনের পিছনের দূরত্ব সেন্সরটি আমাদের কাছে সবচেয়ে নির্ভরযোগ্য বলে মনে হয়েছিল, কারণ এটি দুর্ঘটনাজনিত ট্রিগারগুলিকে বাদ দেয়, যেমন যখন দরজাটি যথেষ্ট শক্তভাবে বন্ধ থাকে না এবং ওজন সেন্সরের বিপরীতে জটিল ইনস্টলেশনের প্রয়োজন হয় না।
একটি ডায়ালগের শুরু এবং শেষ সনাক্ত করার চ্যালেঞ্জ এড়াতে, আমরা মাইক্রোফোন নিয়ন্ত্রণ করতে একটি বড় লাল বোতাম যুক্ত করার সিদ্ধান্ত নিয়েছি। এই সমাধানটি ব্যবহারকারীকে যেকোন মুহুর্তে AI বাধা দেওয়ার অনুমতি দেয়।
একটি অনুরোধ প্রক্রিয়াকরণে প্রতিক্রিয়া বাস্তবায়ন সম্পর্কে আমাদের অনেক ভিন্ন ধারণা ছিল। আমরা একটি স্ক্রীন সহ একটি বিকল্পের সিদ্ধান্ত নিয়েছি যা দেখায় যে সিস্টেমটি কী করছে: মাইক্রোফোন শোনা, একটি প্রশ্ন প্রক্রিয়া করা বা উত্তর দেওয়া।
আমরা একটি পুরানো ল্যান্ডলাইন ফোনের সাথে একটি বরং স্মার্ট বিকল্প বিবেচনা করেছি। ব্যবহারকারী ফোন তুললে সেশন শুরু হবে, এবং সিস্টেমটি ব্যবহারকারীর কথা শুনবে যতক্ষণ না সে হ্যাং করে। যাইহোক, আমরা সিদ্ধান্ত নিয়েছি যে ব্যবহারকারী ফোন থেকে একটি ভয়েসের পরিবর্তে বুথ দ্বারা "উত্তর" দিলে এটি আরও খাঁটি।
শেষ পর্যন্ত, চূড়ান্ত ব্যবহারকারী প্রবাহ এই মত বেরিয়ে এসেছে:
Arduino দূরত্ব সেন্সর এবং লাল বোতামের অবস্থা পর্যবেক্ষণ করে। এটি HTTP API-এর মাধ্যমে আমাদের ব্যাকএন্ডে সমস্ত পরিবর্তন পাঠায়, যা সিস্টেমকে নির্ধারণ করতে দেয় যে ব্যবহারকারী বুথে প্রবেশ করেছে বা ছেড়ে গেছে এবং মাইক্রোফোন শোনা সক্রিয় করা বা প্রতিক্রিয়া তৈরি করা শুরু করা প্রয়োজন কিনা।
ওয়েব UI হল একটি ব্রাউজারে খোলা একটি ওয়েব পৃষ্ঠা যা ব্যাকএন্ড থেকে ক্রমাগত সিস্টেমের বর্তমান অবস্থা গ্রহণ করে এবং ব্যবহারকারীর কাছে প্রদর্শন করে।
ব্যাকএন্ড মাইক্রোফোন নিয়ন্ত্রণ করে, সমস্ত প্রয়োজনীয় AI মডেলের সাথে ইন্টারঅ্যাক্ট করে এবং LLM প্রতিক্রিয়াগুলিকে ভয়েস করে। এতে অ্যাপের মূল যুক্তি রয়েছে।
কিভাবে Arduino এর জন্য একটি স্কেচ কোড করতে হয়, দূরত্ব সেন্সর এবং বোতামটি সঠিকভাবে সংযুক্ত করুন এবং বুথে এটি একত্রিত করুন একটি পৃথক নিবন্ধের জন্য একটি বিষয়। প্রযুক্তিগত বিবরণে না গিয়ে আমরা কী পেয়েছি তা সংক্ষেপে পর্যালোচনা করি।
আমরা একটি Arduino ব্যবহার করেছি, আরও স্পষ্টভাবে, একটি বিল্ট-ইন Wi-Fi মডিউল সহ মডেল ESP32 । মাইক্রোকন্ট্রোলারটি ল্যাপটপের মতো একই Wi-Fi নেটওয়ার্কের সাথে সংযুক্ত ছিল, যা ব্যাকএন্ডে চলছিল।
আমরা যে হার্ডওয়্যার ব্যবহার করেছি তার সম্পূর্ণ তালিকা:
পাইপলাইনের প্রধান উপাদান হল স্পিচ-টু-টেক্সট (এসটিটি), এলএলএম এবং টেক্সট-টু-স্পিচ (টিটিএস)। প্রতিটি কাজের জন্য, অনেকগুলি বিভিন্ন মডেল স্থানীয়ভাবে এবং ক্লাউডের মাধ্যমে উপলব্ধ।
যেহেতু আমাদের হাতে একটি শক্তিশালী GPU নেই, তাই আমরা মডেলগুলির ক্লাউড-ভিত্তিক সংস্করণগুলি বেছে নেওয়ার সিদ্ধান্ত নিয়েছি। এই পদ্ধতির দুর্বলতা হল একটি ভাল ইন্টারনেট সংযোগের প্রয়োজন। তা সত্ত্বেও, সমস্ত অপ্টিমাইজেশনের পর মিথস্ক্রিয়া গতি গ্রহণযোগ্য ছিল, এমনকি উৎসবে আমাদের মোবাইল ইন্টারনেটের সাথেও।
এখন, আসুন পাইপলাইনের প্রতিটি উপাদান ঘনিষ্ঠভাবে দেখুন।
অনেক আধুনিক ডিভাইস দীর্ঘদিন ধরে স্পিচ রিকগনিশনকে সমর্থন করেছে। উদাহরণস্বরূপ, Apple Speech API iOS এবং macOS এর জন্য উপলব্ধ এবং ওয়েব স্পিচ API ব্রাউজারগুলির জন্য।
দুর্ভাগ্যবশত, তারা হুইস্পার বা ডিপগ্রামের মানের দিক থেকে খুবই নিকৃষ্ট এবং স্বয়ংক্রিয়ভাবে ভাষা সনাক্ত করতে পারে না।
প্রক্রিয়াকরণের সময় কমাতে, ব্যবহারকারীর কথা বলার সাথে সাথে রিয়েল-টাইমে বক্তৃতা চিনতে সর্বোত্তম বিকল্প। সেগুলি কীভাবে বাস্তবায়ন করা যায় তার উদাহরণ সহ এখানে কিছু প্রকল্প রয়েছে:
আমাদের ল্যাপটপের সাথে, এই পদ্ধতি ব্যবহার করে বক্তৃতা স্বীকৃতির গতি বাস্তব সময়ের থেকে অনেক দূরে পরিণত হয়েছে। বেশ কিছু পরীক্ষার পর, আমরা OpenAI থেকে ক্লাউড-ভিত্তিক হুইস্পার মডেলের সিদ্ধান্ত নিয়েছি।
আগের ধাপের স্পিচ টু টেক্সট মডেলের ফলাফল হল ডায়ালগ ইতিহাস সহ আমরা এলএলএম-এ যে পাঠ্য পাঠাই।
একটি LLM নির্বাচন করার সময়, আমরা GPT-3.5 এর সাথে তুলনা করি। GPT-4 এবং Claude. দেখা গেল যে মূল ফ্যাক্টরটি এর কনফিগারেশনের মতো নির্দিষ্ট মডেলটি এত বেশি ছিল না। শেষ পর্যন্ত, আমরা GPT-4-এ বসতি স্থাপন করেছি, যার উত্তর আমরা অন্যদের চেয়ে বেশি পছন্দ করেছি।
এলএলএম মডেলের জন্য প্রম্পটের কাস্টমাইজেশন একটি পৃথক শিল্প ফর্ম হয়ে উঠেছে। আপনার প্রয়োজন অনুসারে কীভাবে আপনার মডেল টিউন করবেন সে সম্পর্কে ইন্টারনেটে অনেক গাইড রয়েছে:
মডেলটিকে আকর্ষক, সংক্ষিপ্তভাবে এবং হাস্যকরভাবে প্রতিক্রিয়া জানাতে আমাদের প্রম্পট এবং তাপমাত্রা সেটিংসের সাথে ব্যাপকভাবে পরীক্ষা করতে হয়েছিল।
আমরা টেক্সট-টু-স্পিচ মডেল ব্যবহার করে এলএলএম থেকে প্রাপ্ত প্রতিক্রিয়ার কথা বলি এবং ব্যবহারকারীর কাছে তা ফিরিয়ে দেই। এই পদক্ষেপটি আমাদের ডেমোতে বিলম্বের প্রাথমিক উত্স ছিল।
এলএলএমগুলি সাড়া দিতে বেশ দীর্ঘ সময় নেয়। যাইহোক, তারা স্ট্রিমিং মোডে প্রতিক্রিয়া তৈরি করতে সমর্থন করে - টোকেন দ্বারা টোকেন। LLM-এর সম্পূর্ণ প্রতিক্রিয়ার জন্য অপেক্ষা না করেই আমরা এই বৈশিষ্ট্যটি ব্যবহার করে অপেক্ষার সময়কে অপ্টিমাইজ করার জন্য পৃথক বাক্যাংশগুলি উচ্চারণ করতে পারি।
LLM থেকে প্রতিক্রিয়ার অবশিষ্ট অংশগুলি প্রক্রিয়াকরণে বিলম্ব লুকানোর জন্য ব্যবহারকারী প্রাথমিক খণ্ডটি শোনার সময় আমরা সময় ব্যবহার করি। এই পদ্ধতির জন্য ধন্যবাদ, প্রতিক্রিয়া বিলম্ব শুধুমাত্র শুরুতে ঘটে এবং ~3 সেকেন্ড।
async generateResponse(history) { const completion = await this.ai.completion(history); const chunks = new DialogChunks(); for await (const chunk of completion) { const delta = chunk.choices[0]?.delta?.content; if (delta) { chunks.push(delta); if (chunks.hasCompleteSentence()) { const sentence = chunks.popSentence(); this.voice.ttsAndPlay(sentence); } } } const sentence = chunks.popSentence(); if (sentence) { this.voice.say(sentence); } return chunks.text; }
এমনকি আমাদের সমস্ত অপ্টিমাইজেশন সহ, একটি 3-4 সেকেন্ড বিলম্ব এখনও তাৎপর্যপূর্ণ। প্রতিক্রিয়া হ্যাং হয়ে গেছে এমন অনুভূতি থেকে ব্যবহারকারীকে বাঁচাতে আমরা প্রতিক্রিয়া সহ UI এর যত্ন নেওয়ার সিদ্ধান্ত নিয়েছি। আমরা বেশ কয়েকটি পন্থা দেখেছি:
আমরা একটি সাধারণ ওয়েব পৃষ্ঠার সাথে শেষ বিকল্পে স্থির হয়েছি যা ব্যাকএন্ড পোল করে এবং বর্তমান অবস্থা অনুযায়ী অ্যানিমেশন দেখায়।
আমাদের AI স্বীকারোক্তি রুম চার দিন ধরে চলেছিল এবং শত শত অংশগ্রহণকারীদের আকৃষ্ট করেছিল। আমরা ওপেনএআই এপিআই-এ প্রায় $50 খরচ করেছি। বিনিময়ে, আমরা যথেষ্ট ইতিবাচক প্রতিক্রিয়া এবং মূল্যবান ইমপ্রেশন পেয়েছি।
এই ছোট পরীক্ষাটি দেখিয়েছে যে সীমিত সংস্থান এবং চ্যালেঞ্জিং বাহ্যিক অবস্থার মধ্যেও একটি এলএলএম-এ একটি স্বজ্ঞাত এবং দক্ষ ভয়েস ইন্টারফেস যুক্ত করা সম্ভব।
যাইহোক, GitHub-এ উপলব্ধ ব্যাকএন্ড উত্সগুলি