Kendi RAG'ınızı nasıl oluşturacağınız ve yerel olarak nasıl çalıştıracağınızla ilgili son paylaşımımdan sonra, bugün yalnızca büyük dil modellerinin konuşma yeteneklerini uygulamakla kalmayıp aynı zamanda dinleme ve konuşma yeteneklerini de ekleyerek bunu bir adım daha ileri götürüyoruz. Fikir çok basit: Bilgisayarınızda çevrimdışı çalışabilen, ikonik Iron Man filmlerinden Jarvis'i veya Friday'i anımsatan bir sesli asistan yaratacağız.
Bu bir giriş dersi olduğundan, bunu Python'da uygulayacağım ve yeni başlayanlar için yeterince basit tutacağım. Son olarak uygulamanın nasıl ölçeklendirileceği konusunda bazı rehberlik sağlayacağım.
Öncelikle sanal bir Python ortamı kurmalısınız. Bunun için pyenv, virtualenv, poet ve benzer amaçlara hizmet eden diğerleri dahil olmak üzere çeşitli seçenekleriniz vardır. Şahsen, kişisel tercihlerimden dolayı bu ders için Şiir'i kullanacağım. Yüklemeniz gereken birkaç önemli kütüphane şunlardır:
Bağımlılıkların ayrıntılı bir listesi için buradaki bağlantıya bakın.
Buradaki en kritik bileşen Ollama'yı kullanacağımız Büyük Dil Modeli (LLM) arka ucudur. Ollama, yüksek lisans eğitimlerini çevrimdışı çalıştırmak ve sunmak için yaygın olarak popüler bir araç olarak tanınmaktadır. Ollama sizin için yeniyse, çevrimdışı RAG ile ilgili önceki makaleme göz atmanızı öneririm: "Kendi RAG'ınızı Oluşturun ve Yerel Olarak Çalıştırın: Langchain + Ollama + Streamlit." Temel olarak Ollama uygulamasını indirmeniz, tercih ettiğiniz modeli çekmeniz ve çalıştırmanız yeterlidir.
Tamam, her şey ayarlandıysa bir sonraki adıma geçelim. Temelde 3 ana bileşenden oluşan uygulamamızın genel mimarisi aşağıdadır:
İş akışı basittir: Konuşmayı kaydedin, metne dönüştürün, LLM kullanarak bir yanıt oluşturun ve Bark'ı kullanarak yanıtı seslendirin.
Whisper, Ollama ve Bark ile sesli asistanın sıra diyagramı.
Uygulama, Bark'ı temel alan bir TextToSpeechService
hazırlanmasıyla başlar; metinden konuşmayı sentezlemek ve daha uzun metin girişlerini aşağıdaki gibi sorunsuz bir şekilde işlemek için yöntemler içerir:
import nltk import torch import warnings import numpy as np from transformers import AutoProcessor, BarkModel warnings.filterwarnings( "ignore", message="torch.nn.utils.weight_norm is deprecated in favor of torch.nn.utils.parametrizations.weight_norm.", ) class TextToSpeechService: def __init__(self, device: str = "cuda" if torch.cuda.is_available() else "cpu"): """ Initializes the TextToSpeechService class. Args: device (str, optional): The device to be used for the model, either "cuda" if a GPU is available or "cpu". Defaults to "cuda" if available, otherwise "cpu". """ self.device = device self.processor = AutoProcessor.from_pretrained("suno/bark-small") self.model = BarkModel.from_pretrained("suno/bark-small") self.model.to(self.device) def synthesize(self, text: str, voice_preset: str = "v2/en_speaker_1"): """ Synthesizes audio from the given text using the specified voice preset. Args: text (str): The input text to be synthesized. voice_preset (str, optional): The voice preset to be used for the synthesis. Defaults to "v2/en_speaker_1". Returns: tuple: A tuple containing the sample rate and the generated audio array. """ inputs = self.processor(text, voice_preset=voice_preset, return_tensors="pt") inputs = {k: v.to(self.device) for k, v in inputs.items()} with torch.no_grad(): audio_array = self.model.generate(**inputs, pad_token_id=10000) audio_array = audio_array.cpu().numpy().squeeze() sample_rate = self.model.generation_config.sample_rate return sample_rate, audio_array def long_form_synthesize(self, text: str, voice_preset: str = "v2/en_speaker_1"): """ Synthesizes audio from the given long-form text using the specified voice preset. Args: text (str): The input text to be synthesized. voice_preset (str, optional): The voice preset to be used for the synthesis. Defaults to "v2/en_speaker_1". Returns: tuple: A tuple containing the sample rate and the generated audio array. """ pieces = [] sentences = nltk.sent_tokenize(text) silence = np.zeros(int(0.25 * self.model.generation_config.sample_rate)) for sent in sentences: sample_rate, audio_array = self.synthesize(sent, voice_preset) pieces += [audio_array, silence.copy()] return self.model.generation_config.sample_rate, np.concatenate(pieces)
__init__
) : Sınıf, model için kullanılacak cihazı belirten isteğe bağlı bir device
parametresi alır (GPU varsa cuda
veya cpu
). suno/bark-small
önceden eğitilmiş modelden Bark modelini ve karşılık gelen işlemciyi yükler. Model yükleyici için suno/bark
belirterek büyük versiyonu da kullanabilirsiniz.
synthesize
) : Bu yöntem, bir text
girişi ve sentez için kullanılacak sesi belirten bir voice_preset
parametresi alır. Diğer voice_preset
değerini buradan kontrol edebilirsiniz. Giriş metnini ve ses ön ayarını hazırlamak için processor
kullanır ve ardından model.generate()
yöntemini kullanarak ses dizisini oluşturur. Oluşturulan ses dizisi bir NumPy dizisine dönüştürülür ve örnekleme hızı, ses dizisiyle birlikte döndürülür.
long_form_synthesize
) : Bu yöntem daha uzun metin girişlerini sentezlemek için kullanılır. İlk önce nltk.sent_tokenize
işlevini kullanarak giriş metnini cümleler halinde tokenize eder. Her cümle için ses dizisini oluşturmak üzere synthesize
yöntemini çağırır. Daha sonra oluşturulan ses dizilerini, her cümlenin arasına kısa bir sessizlik (0,25 saniye) eklenerek birleştirir.
Artık TextToSpeechService
kurulumunu yaptığımıza göre Ollama sunucusunu büyük dil modeli (LLM) hizmetine hazırlamamız gerekiyor. Bunu yapmak için şu adımları uygulamanız gerekir:
ollama pull llama2
.
ollama serve
.
Bu adımları tamamladıktan sonra uygulamanız, kullanıcı girişine yanıtlar oluşturmak için Ollama sunucusunu ve Llama-2 modelini kullanabilecektir.
Daha sonra ana uygulama mantığına geçeceğiz. Öncelikle aşağıdaki bileşenleri başlatmamız gerekiyor:
base.en
) kullanacağız.
ConversationalChain
kullanacağız. Ollama arka ucuyla Llama-2 dil modelini kullanacak şekilde yapılandıracağız. import time import threading import numpy as np import whisper import sounddevice as sd from queue import Queue from rich.console import Console from langchain.memory import ConversationBufferMemory from langchain.chains import ConversationChain from langchain.prompts import PromptTemplate from langchain_community.llms import Ollama from tts import TextToSpeechService console = Console() stt = whisper.load_model("base.en") tts = TextToSpeechService() template = """ You are a helpful and friendly AI assistant. You are polite, respectful, and aim to provide concise responses of less than 20 words. The conversation transcript is as follows: {history} And here is the user's follow-up: {input} Your response: """ PROMPT = PromptTemplate(input_variables=["history", "input"], template=template) chain = ConversationChain( prompt=PROMPT, verbose=False, memory=ConversationBufferMemory(ai_prefix="Assistant:"), llm=Ollama(), )
Şimdi gerekli fonksiyonları tanımlayalım:
record_audio
: Bu işlev, sounddevice.RawInputStream
öğesini kullanarak kullanıcının mikrofonundan ses verilerini yakalamak için ayrı bir iş parçacığında çalışır. Yeni ses verileri mevcut olduğunda geri çağırma işlevi çağrılır ve verileri daha fazla işlenmek üzere bir data_queue
yerleştirir.
transcribe
: Bu işlev, ses verilerini data_queue
metne dönüştürmek için Whisper örneğini kullanır.
get_llm_response
: Bu işlev, geçerli konuşma bağlamını Llama-2 dil modeline (Langchain ConversationalChain
aracılığıyla) besler ve oluşturulan metin yanıtını alır.
play_audio
: Bu işlev, Bark metin-konuşma motoru tarafından oluşturulan ses dalga biçimini alır ve bunu bir ses çalma kitaplığı (örneğin, sounddevice
) kullanarak kullanıcıya oynatır. def record_audio(stop_event, data_queue): """ Captures audio data from the user's microphone and adds it to a queue for further processing. Args: stop_event (threading.Event): An event that, when set, signals the function to stop recording. data_queue (queue.Queue): A queue to which the recorded audio data will be added. Returns: None """ def callback(indata, frames, time, status): if status: console.print(status) data_queue.put(bytes(indata)) with sd.RawInputStream( samplerate=16000, dtype="int16", channels=1, callback=callback ): while not stop_event.is_set(): time.sleep(0.1) def transcribe(audio_np: np.ndarray) -> str: """ Transcribes the given audio data using the Whisper speech recognition model. Args: audio_np (numpy.ndarray): The audio data to be transcribed. Returns: str: The transcribed text. """ result = stt.transcribe(audio_np, fp16=False) # Set fp16=True if using a GPU text = result["text"].strip() return text def get_llm_response(text: str) -> str: """ Generates a response to the given text using the Llama-2 language model. Args: text (str): The input text to be processed. Returns: str: The generated response. """ response = chain.predict(input=text) if response.startswith("Assistant:"): response = response[len("Assistant:") :].strip() return response def play_audio(sample_rate, audio_array): """ Plays the given audio data using the sounddevice library. Args: sample_rate (int): The sample rate of the audio data. audio_array (numpy.ndarray): The audio data to be played. Returns: None """ sd.play(audio_array, sample_rate) sd.wait()
Daha sonra ana uygulama döngüsünü tanımlıyoruz. Ana uygulama döngüsü, kullanıcıya konuşma etkileşimi boyunca aşağıdaki şekilde rehberlik eder:
Kullanıcıdan girişini kaydetmeye başlaması için Enter tuşuna basması istenir.
Kullanıcı Enter'a bastığında, kullanıcının ses girişini yakalamak için ayrı bir iş parçacığında record_audio
işlevi çağrılır.
Kullanıcı kaydı durdurmak için tekrar Enter'a bastığında ses verileri, transcribe
işlevi kullanılarak kopyalanır.
Yazıya aktarılan metin daha sonra Llama-2 dil modelini kullanarak bir yanıt üreten get_llm_response
işlevine iletilir.
Oluşturulan yanıt konsola yazdırılır ve play_audio
işlevi kullanılarak kullanıcıya oynatılır.
if __name__ == "__main__": console.print("[cyan]Assistant started! Press Ctrl+C to exit.") try: while True: console.input( "Press Enter to start recording, then press Enter again to stop." ) data_queue = Queue() # type: ignore[var-annotated] stop_event = threading.Event() recording_thread = threading.Thread( target=record_audio, args=(stop_event, data_queue), ) recording_thread.start() input() stop_event.set() recording_thread.join() audio_data = b"".join(list(data_queue.queue)) audio_np = ( np.frombuffer(audio_data, dtype=np.int16).astype(np.float32) / 32768.0 ) if audio_np.size > 0: with console.status("Transcribing...", spinner="earth"): text = transcribe(audio_np) console.print(f"[yellow]You: {text}") with console.status("Generating response...", spinner="earth"): response = get_llm_response(text) sample_rate, audio_array = tts.long_form_synthesize(response) console.print(f"[cyan]Assistant: {response}") play_audio(sample_rate, audio_array) else: console.print( "[red]No audio recorded. Please ensure your microphone is working." ) except KeyboardInterrupt: console.print("\n[red]Exiting...") console.print("[blue]Session ended.")
Her şeyi bir araya getirdikten sonra uygulamayı yukarıdaki videoda gösterildiği gibi çalıştırabiliriz. Bark modelinin daha küçük versiyonunda bile büyük olması nedeniyle uygulama MacBook'umda oldukça yavaş çalışıyor. Bu nedenle videoyu biraz hızlandırdım. CUDA özellikli bir bilgisayara sahip olanlar için daha hızlı çalışabilir. İşte uygulamamızın temel özellikleri:
Bu uygulamayı üretime hazır durumuna yükseltmeyi hedefleyenler için aşağıdaki geliştirmeler önerilir:
Sonunda basit sesli asistan uygulamamızı tamamladık, kodun tamamına şu adresten ulaşabilirsiniz: https://github.com/vndee/local-talking-llm . Konuşma tanıma, dil modelleme ve metinden konuşmaya teknolojilerinin bu birleşimi, zor gibi görünen ancak aslında bilgisayarınızda çalışabilen bir şeyi nasıl oluşturabileceğimizi gösteriyor. Kodlamanın tadını çıkaralım ve yapay zeka ve programlamayla ilgili en son makaleleri kaçırmamak için bloguma abone olmayı unutmayın.
Burada da yayınlandı