Ana içeriğe geç

Spekülatif Ayrıştırma: Uygulama Örnekleriyle Bir Rehber

Spekülatif ayrıştırmanın ne olduğunu, nasıl çalıştığını, ne zaman kullanılacağını ve Gemma2 modelleriyle nasıl uygulanacağını öğrenin.
· 12 dk. oku

Yapay Zeka ile Keşfedin

LLM’ler son derece güçlüdür, ancak bazen biraz yavaş kalabilirler; hızın kritik olduğu senaryolarda bu ideal değildir. Spekülatif ayrıştırma, kaliteyi bozmadan yanıt üretimini hızlandırarak LLM’leri hızlandırmak için tasarlanmış bir tekniktir.

Özünde, metin üretim sürecinde “ileriden tahmin etme” yoludur; bir sonraki gelebilecek sözcüklere dair öngörüler yaparken yine de LLM’lerden beklediğimiz doğruluğu ve derinliği sağlar.

Bu blogda, spekülatif ayrıştırmanın ne olduğunu, nasıl çalıştığını ve Gemma 2 modelleriyle nasıl uygulanacağını açıklayacağım.

Spekülatif Ayrıştırma Nedir?

Spekülatif ayrıştırma, ön kestirimler üreten daha küçük ve hızlı bir model ekleyerek LLM’leri hızlandırır. Genellikle “taslak” model olarak adlandırılan bu küçük model, ana, daha güçlü LLM’in onaylayabileceği veya iyileştirebileceği bir dizi belirteç oluşturur. Taslak model, üretim sürecini hızlandıran bir ilk geçiş olarak birden fazla belirteç üretir.

Ana LLM’in belirteçleri tek tek üretmesi yerine, taslak model olası adaylar kümesi sağlar ve ana model bunları paralel olarak değerlendirir. Bu yöntem, ilk tahminleri küçük modele devrederek ana LLM üzerindeki hesaplama yükünü azaltır; böylece ana model yalnızca düzeltme veya doğrulamaya odaklanır.

Spekülatif Ayrıştırma veri hattı

Bunu bir yazar ve editör gibi düşünün. Ana LLM, yüksek kaliteli metin üretebilen ancak daha yavaş olan yazardır. Daha küçük ve hızlı “taslak” model ise editör gibi davranır; metnin olası devamlarını hızla üretir. Ana LLM bu önerileri değerlendirir, doğru olanları alır, diğerlerini eler. Bu sayede LLM birden fazla belirteci eşzamanlı işleyerek metin üretimini hızlandırır.

Süreci basit adımlara bölelim:

  1. Taslak üretimi: Daha küçük model (ör. Gemma2-2B-it), giriş istemine dayanarak birden fazla belirteç önerisi üretir. Bu belirteçler spekülatif olarak, yani doğruluklarından emin olunmadan “taslak” belirteçler olarak sunulur.
  2. Paralel doğrulama: Daha büyük model (ör. Gemma2-9B-it) bu belirteçleri paralel olarak doğrular; olasılıklarını modelin öğrendiği dağılıma göre kontrol eder. Uygun görülürlerse nihai çıktıda kullanılır; değilse büyük model tarafından düzeltilir.
  3. ​Nihai çıktı: Belirteçler doğrulandıktan (veya düzeltildikten) sonra kullanıcıya nihai çıktı olarak aktarılır. Tüm bu süreç, geleneksel tek-belirteçli ayrıştırmadan çok daha hızlı gerçekleşir.

Geleneksel Ayrıştırma vs. Spekülatif Ayrıştırma

Geleneksel ayrıştırma belirteçleri teker teker işler ve bu da yüksek gecikmeye yol açar; spekülatif ayrıştırmada ise küçük model belirteçleri toplu üretir, büyük model bunları doğrular. Bu yaklaşım yanıt süresini %30-40 azaltabilir; gecikmeyi 25-30 saniyeden 15-18 saniyeye kadar düşürebilir.

Geleneksel ayrıştırma vs spekülatif ayrıştırma

Ayrıca, spekülatif ayrıştırma bellek kullanımını da optimize eder; belirteç üretiminin çoğunu küçük modele kaydırarak bellek ihtiyacını 26 GB’dan yaklaşık 14 GB’a düşürür ve cihaz üzerinde çıkarımı daha erişilebilir kılar.

Son olarak, büyük model belirteç üretmek yerine yalnızca doğrulama yaptığı için hesaplama talebini %50 azaltır; bu da sınırlı güce sahip mobil cihazlarda daha akıcı performans sağlar ve aşırı ısınmayı önler.

Uygulamalı Örnek: Gemma2 Modelleriyle Spekülatif Ayrıştırma

Gemma2 modellerini kullanarak spekülatif ayrıştırmanın uygulamalı bir örneğini gerçekleştireceğiz. Spekülatif ayrıştırmanın gecikme ve performans açısından standart çıkarımla nasıl karşılaştırıldığını inceleyeceğiz.

Adım 1: Model ve Tokenizer kurulumu

Başlamak için bağımlılıkları içe aktarın ve tohum (seed) ayarlayın.

Ardından, çalıştığınız makinede GPU olup olmadığını kontrol edin. Bu, özellikle Gemma 2-9B-it veya Llama2-13B gibi büyük modeller için gereklidir.

Son olarak, hem küçük hem de büyük modeli tokenizer’larıyla birlikte yükleyin. Burada taslak için Gemma2-2b-it (instruct) modelini, doğrulama için Gemma2-9b-it modelini kullanıyoruz.

Alternatif olarak kullanılabilecek başka modeller de vardır. Örneğin:

  • Gemma 7B (ana) & Gemma 2B (taslak)
  • Mixtral-8x7B (ana) & Mistral 7B (taslak)
  • Pythia 12B (ana) & Pythia 70M (taslak)
  • Llama 2 13B (ana) & TinyLlama 1.1B (taslak)
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, set_seed

# Set Seed
set_seed(42)

# Check if GPU is available
device = "cuda" if torch.cuda.is_available() else "cpu"

# Load the smaller Gemma2 model (draft generation)
small_tokenizer = AutoTokenizer.from_pretrained("google/gemma-2-2b-it", device_map="auto")
small_model = AutoModelForCausalLM.from_pretrained("google/gemma-2-2b-it", device_map="auto", torch_dtype=torch.bfloat16)

# Load the larger Gemma2 model (verification)
big_tokenizer = AutoTokenizer.from_pretrained("google/gemma-2-9b-it", device_map="auto")
big_model = AutoModelForCausalLM.from_pretrained("google/gemma-2-9b-it", device_map="auto", torch_dtype=torch.bfloat16)

Adım 2: Otoregresif (normal) çıkarım 

Önce yalnızca büyük model (Gemma2-9b-it) üzerinde çıkarım yapıp çıktı üretiyoruz. Önce girdi istemini tokenleştirin ve belirteçleri doğru cihaza (varsa GPU) taşıyın. generate yöntemi, modelin çıktısını üretir ve max_new_tokens değerine kadar belirteç üretir. Sonuç, belirteç kimliklerinden yeniden insan tarafından okunabilir metne çevrilir.

def normal_inference(big_model, big_tokenizer, prompt, max_new_tokens=50):
    inputs = big_tokenizer(prompt, return_tensors='pt').to(device)
    outputs = big_model.generate(inputs['input_ids'], max_new_tokens=max_new_tokens)
    return big_tokenizer.decode(outputs[0], skip_special_tokens=True)

Adım 3: Spekülatif ayrıştırma 

Sırada spekülatif ayrıştırma yöntemi var; şu adımları izliyoruz:

  1. Taslak üretimi: Küçük model, verilen istemden metnin bir taslağını üretir.
  2. Doğrulama: Büyük model daha sonra taslağı, taslaktaki her belirteç için log-olasılığı hesaplayarak doğrular.
  3. Log-olasılık hesaplama: Büyük modelin küçük modelin taslağını ne kadar doğru bulduğunu belirlemek için ortalama bir log-olasılık hesaplarız.
def speculative_decoding(small_model, big_model, small_tokenizer, big_tokenizer, prompt, max_new_tokens=50):
    # Step 1: Use the small model to generate the draft
    inputs = small_tokenizer(prompt, return_tensors='pt').to(device)
    small_outputs = small_model.generate(inputs['input_ids'], max_new_tokens=max_new_tokens)
    draft = small_tokenizer.decode(small_outputs[0], skip_special_tokens=True)

    # Step 2: Verify the draft with the big model
    big_inputs = big_tokenizer(draft, return_tensors='pt').to(device)

    # Step 3: Calculate log-likelihood of the draft tokens under the large model
    with torch.no_grad():
        outputs = big_model(big_inputs['input_ids'])
        log_probs = torch.log_softmax(outputs.logits, dim=-1)

    draft_token_ids = big_inputs['input_ids']
    log_likelihood = 0
    for i in range(draft_token_ids.size(1) - 1):
        token_id = draft_token_ids[0, i + 1]
        log_likelihood += log_probs[0, i, token_id].item()

    avg_log_likelihood = log_likelihood / (draft_token_ids.size(1) - 1)

    # Return the draft and its log-likelihood score
    return draft, avg_log_likelihood

Not: Log-olasılık, bir modelin belirli bir belirteç dizisine atadığı olasılığın logaritmasıdır. Burada, modelin, belirteçler dizisinin (üretilen metin) önceki belirteçler verildiğinde geçerli olduğuna ne kadar “güvendiğini” yansıtır.

Adım 4: Gecikmenin ölçülmesi

Her iki tekniği de uyguladıktan sonra, karşılık gelen gecikmeleri ölçebiliriz. Spekülatif ayrıştırmada performansı log-olasılık değerine bakarak değerlendiririz. Negatif aralıkta sıfıra yaklaşan bir log-olasılık değeri, üretilen metnin doğru olduğuna işaret eder.

def measure_latency(small_model, big_model, small_tokenizer, big_tokenizer, prompt, max_new_tokens=50):
    # Measure latency for normal inference (big model only)
    start_time = time.time()
    normal_output = normal_inference(big_model, big_tokenizer, prompt, max_new_tokens)
    normal_inference_latency = time.time() - start_time
    print(f"Normal Inference Output: {normal_output}")
    print(f"Normal Inference Latency: {normal_inference_latency:.4f} seconds")
    print("\n\n")

    # Measure latency for speculative decoding
    start_time = time.time()
    speculative_output, log_likelihood = speculative_decoding(
        small_model, big_model, small_tokenizer, big_tokenizer, prompt, max_new_tokens
    )
    speculative_decoding_latency = time.time() - start_time
    print(f"Speculative Decoding Output: {speculative_output}")
    print(f"Speculative Decoding Latency: {speculative_decoding_latency:.4f} seconds")
    print(f"Log Likelihood (Verification Score): {log_likelihood:.4f}")

    return normal_inference_latency, speculative_decoding_latency

Bu çıktıyı verir:

  • Log-olasılık (doğrulama skoru): -0.5242
  • Normal çıkarım gecikmesi: 17.8899 saniye
  • Spekülatif ayrıştırma gecikmesi: 10.5291 saniye (yaklaşık %70 daha hızlı)

Daha düşük gecikme, metin üretiminde küçük modelin daha az zaman harcaması ve büyük modelin yalnızca üretilen metni doğrulamak için daha az zaman harcaması sayesinde elde edilir.

Beş İstem Üzerinde Spekülatif Ayrıştırmanın Testi

Spekülatif ayrıştırmayı, beş istem kullanarak otoregresif çıkarımla karşılaştıralım:

# List of 5 prompts
prompts = [
    "The future of artificial intelligence is ",
    "Machine learning is transforming the world by ",
    "Natural language processing enables computers to understand ",
    "Generative models like GPT-3 can create ",
    "AI ethics and fairness are important considerations for "
]

# Inference settings
max_new_tokens = 200

# Initialize accumulators for latency, memory, and tokens per second
total_tokens_per_sec_normal = 0
total_tokens_per_sec_speculative = 0
total_normal_latency = 0
total_speculative_latency = 0

# Perform inference on each prompt and accumulate the results
for prompt in prompts:
    normal_latency, speculative_latency, _, _, tokens_per_sec_normal, tokens_per_sec_speculative = measure_latency_and_memory(
        small_model, big_model, small_tokenizer, big_tokenizer, prompt, max_new_tokens
    )
    total_tokens_per_sec_normal += tokens_per_sec_normal
    total_tokens_per_sec_speculative += tokens_per_sec_speculative
    total_normal_latency += normal_latency
    total_speculative_latency += speculative_latency

# Calculate averages
average_tokens_per_sec_normal = total_tokens_per_sec_normal / len(prompts)
average_tokens_per_sec_speculative = total_tokens_per_sec_speculative / len(prompts)
average_normal_latency = total_normal_latency / len(prompts)
average_speculative_latency = total_speculative_latency / len(prompts)

# Output the averages
print(f"Average Normal Inference Latency: {average_normal_latency:.4f} seconds")
print(f"Average Speculative Decoding Latency: {average_speculative_latency:.4f} seconds")
print(f"Average Normal Inference Tokens per second: {average_tokens_per_sec_normal:.2f} tokens/second")
print(f"Average Speculative Decoding Tokens per second: {average_tokens_per_sec_speculative:.2f} tokens/second")
Average Normal Inference Latency: 25.0876 seconds
Average Speculative Decoding Latency: 15.7802 seconds
Average Normal Inference Tokens per second: 7.97 tokens/second
Average Speculative Decoding Tokens per second: 12.68 tokens/second

Bu sonuç, spekülatif ayrıştırmanın daha verimli olduğunu; normal çıkarıma kıyasla saniye başına daha fazla belirteç ürettiğini gösterir. Bu iyileşme, metin üretiminin çoğunu küçük modelin üstlenmesi ve büyük modelin rolünün doğrulamayla sınırlı kalması sayesinde, gecikme ve bellek açısından toplam hesaplama yükünü azaltır. 

Bu bellek gereksinimleriyle, spekülatif ayrıştırma tekniklerini uç cihazlarda kolayca dağıtabilir ve sohbet botları, dil çeviriciler, oyunlar ve daha fazlası gibi cihaz içi uygulamalarımızda hız kazanımları elde edebiliriz.

Kantizasyon ile Optimize Edilmiş Spekülatif Ayrıştırma  

Yukarıdaki yaklaşım verimlidir; ancak cihaz üzerinde çıkarımda gecikme ile bellek optimizasyonu arasında bir ödünleşim vardır. Bunu ele almak için, küçük ve büyük modellere kantizasyon uygulayalım. Küçük model zaten en az yeri kapladığından yalnızca büyük modele kantizasyon uygulamayı deneyerek de deney yapabilirsiniz.

Kantizasyon, Hugging Face transformers kitaplığındaki BitsAndBytesConfig yapılandırması kullanılarak küçük ve büyük modellere uygulanır. Kantizasyon, modelin ağırlıklarını daha kompakt bir biçime dönüştürerek bellek kullanımını önemli ölçüde azaltmamıza ve birçok durumda çıkarım hızını artırmamıza olanak tanır.

Kantizasyonun etkilerini görmek için aşağıdaki kod parçasını yukarıdaki koda ekleyin.

bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,  # Enables 4-bit quantization
    bnb_4bit_quant_type="nf4",  # Specifies the quantization type (nf4)
    bnb_4bit_compute_dtype=torch.bfloat16,  # Uses bfloat16 for computation
    bnb_4bit_use_double_quant=False,  # Disables double quantization to avoid additional overhead
)

# Load the smaller and larger Gemma2 models with quantization applied
small_model = AutoModelForCausalLM.from_pretrained("google/gemma-2-2b-it", device_map="auto", quantization_config=bnb_config)
big_model = AutoModelForCausalLM.from_pretrained("google/gemma-2-9b-it", device_map="auto", quantization_config=bnb_config)

Kantizasyonlu ve kantizasyonsuz spekülatif ayrıştırmanın etkilerini göstermek için çıktılara hızlı bir karşılaştırma:

Kantizasyonla ve kantizasyonsuz karşılaştırmalar

4-bit kantizasyon (ağırlık sıkıştırma)

Yapılandırmada load_in_4bit=True belirtilmiştir; bu da model ağırlıklarının orijinal 32-bit veya 16-bit kayan nokta biçiminden 4-bit tamsayılara kantize edildiği anlamına gelir. Bu, modelin bellek ayak izini azaltır. Kantizasyon, model ağırlıklarını sıkıştırır ve bunları daha verimli depolamamıza ve üzerinde işlem yapmamıza olanak tanır. Somut bellek tasarrufları şunlardır:

  • Hassasiyet 32-bit veya 16-bit kayan noktadan 4-bit tamsayıya düşürüldüğünde, her bir ağırlık artık orijinal alanın 1/4’ü veya 1/8’i kadar yer kaplar; bu da bellek kullanımını önemli ölçüde azaltır.
  • Bu durum bellek kullanımına şu şekilde yansır:
    • Normal çıkarım bellek kullanımı: 26.458 MB
    • Spekülatif ayrıştırma bellek kullanımı: 8.993 MB.

Hesaplama için bfloat16 (Tensor Core’ların verimli kullanımı)

Yapılandırmada bnb_4bit_compute_dtype=torch.bfloat16 ile hesaplamanın bfloat16 (BF16) formatında yapılacağı belirtilir; bu, daha verimli bir kayan nokta biçimidir. BF16, FP16’dan daha geniş dinamik aralığa sahiptir ve FP32’ye kıyasla yarı bellek kullanır; bu da hassasiyet ve performans arasında iyi bir dengedir.

Özellikle NVIDIA GPU’larda (ör. A100) BF16 kullanımı, BF16 işlemleri için optimize edilmiş Tensor Core’ları devreye sokar. Bu da çıkarım sırasında matris çarpmaları ve diğer hesaplamaların hızlanmasını sağlar.

Spekülatif ayrıştırma için iyileşmiş gecikme görüyoruz:

  • Normal çıkarım gecikmesi: 27.65 saniye
  • Spekülatif ayrıştırma gecikmesi: 15.56 saniye

Daha küçük bellek ayak izi, bellek erişimini hızlandırır ve GPU kaynaklarının daha verimli kullanılmasını sağlar; bu da daha hızlı üretime yol açar.

NF4 kantizasyon türü (optimize hassasiyet)

bnb_4bit_quant_type="nf4" seçeneği, sinir ağları için optimize edilmiş Norm-Four Kantizasyonu (NF4) belirtir. NF4 kantizasyonu, ağırlıklar 4 bit ile temsil edilse bile modelin önemli kısımlarının hassasiyetini korumaya yardımcı olur. Bu, basit 4-bit kantizasyona kıyasla performans bozulmasını en aza indirir.

NF4, 4-bit kantizasyonun kompaktlığı ile model tahminlerinin doğruluğu arasında denge kurarak, bellek kullanımını ciddi şekilde azaltırken performansın orijinale yakın kalmasını sağlar.

Çift kantizasyon devre dışı 

Çift kantizasyon (bnb_4bit_use_double_quant=False), 4-bit ağırlıkların üzerine ek bir kantizasyon katmanı daha getirir; bu, bellek kullanımını daha da azaltabilir ancak hesaplama yükü ekler. Bu durumda, çıkarımı yavaşlatmamak için çift kantizasyon devre dışı bırakılmıştır.

Spekülatif Ayrıştırmanın Uygulamaları

Spekülatif ayrıştırmanın potansiyel uygulamaları geniş ve heyecan vericidir. İşte birkaç örnek:

  • Sohbet botları ve sanal asistanlar: Daha hızlı yanıt süreleriyle yapay zekâ ile yapılan konuşmaların daha doğal ve akıcı hissettirilmesi.
  • Gerçek zamanlı çeviri: Spekülatif ayrıştırma, gerçek zamanlı çeviride gecikmeyi azaltır.
  • İçerik üretimi: Spekülatif ayrıştırma, içerik oluşturmayı hızlandırır.
  • Oyun ve etkileşimli uygulamalar: Yapay zekâ destekli karakterlerin veya oyun öğelerinin tepkiselliğini artırarak daha sürükleyici bir deneyim sunmak için, spekülatif ayrıştırma neredeyse gerçek zamanlı yanıtlar elde etmemize yardımcı olabilir.

Spekülatif Ayrıştırmanın Uygulamaları

Spekülatif Ayrıştırmanın Zorlukları

Spekülatif ayrıştırma büyük vaatler sunsa da zorlukları da vardır:

  • Bellek ek yükü: Hem taslak hem ana LLM için birden fazla model durumunu sürdürmek, özellikle doğrulama için daha büyük modeller kullanıldığında, bellek kullanımını artırabilir.
  • Taslak modelin ayarlanması: Doğru taslak modeli seçmek ve parametrelerini ayarlamak, hız ve doğruluk arasında doğru dengeyi kurmak için kritiktir; fazla basit bir model sık doğrulama hatalarına yol açabilir.
  • Uygulama karmaşıklığı: Spekülatif ayrıştırmanın uygulanması, küçük taslak model ile büyük doğrulama modeli arasında dikkatli senkronizasyon ve verimli hata yönetimi gerektirdiğinden geleneksel yöntemlerden daha karmaşıktır.
  • Ayrıştırma stratejileriyle uyumluluk: Spekülatif ayrıştırma hâlihazırda yalnızca açgözlü arama (greedy search) ve örneklemeyi desteklemektedir; bu da kiriş araması (beam search) veya çeşitli örnekleme gibi daha gelişmiş stratejilerde kullanımını sınırlar.
  • Doğrulama ek yükü: Küçük taslak model sık sık doğrulamadan geçemeyen belirteçler üretirse, büyük modelin çıktının bölümlerini yeniden üretmesi gerektiğinden verimlilik kazanımları azalabilir ve hız avantajları ortadan kalkabilir.
  • Toplu işlem desteğinin sınırlı olması: Spekülatif ayrıştırma genellikle toplu girişleri desteklemez; bu da çoklu isteğin paralel işlendiği yüksek verimli sistemlerde etkinliğini azaltabilir.

Sonuç

Spekülatif ayrıştırma, büyük dil modelleriyle etkileşim biçimimizi dönüştürme potansiyeline sahip güçlü bir tekniktir. Üretilen metnin kalitesinden ödün vermeden LLM çıkarımını önemli ölçüde hızlandırabilir. Aşılması gereken zorluklar olsa da spekülatif ayrıştırmanın faydaları tartışmasızdır ve önümüzdeki yıllarda benimsenmesinin artması beklenmektedir; bu da daha hızlı, daha duyarlı ve daha verimli yeni nesil yapay zekâ uygulamalarını mümkün kılacaktır.

Aashi Dutt

Yazar

Aashi Dutt

ML (Üretken Yapay Zekâ) alanında Google Developers Uzmanıyım, Kaggle 3x Expert unvanına sahibim ve 3+ yıllık teknoloji deneyimiyle Women Techmakers Elçisiyim. 2020'de bir sağlık teknolojileri girişiminin kurucu ortağı oldum ve Georgia Tech'te makine öğrenmesi alanında uzmanlaşarak bilgisayar bilimleri yüksek lisansı yapıyorum.

Konular

İlgili