TL;DR
Metin verileriniz için tipik Doğal Dil İşleme görevlerini (sınıflandırma, duyarlılık analizi vb.) LLM kullanarak 1 milyon satır başına 10 ABD doları kadar ucuza (göreve ve modele bağlıdır) otomatik olarak çözebilir ve dbt ortamınızda kalabilirsiniz. Talimatlar, ayrıntılar ve kod aşağıdadır
Dönüştürme katmanınız olarak dbt kullanıyorsanız, yapılandırılmamış metin verilerinden anlamlı bilgiler çıkarmak istediğiniz bir durumla karşılaşabilirsiniz. Bu tür veriler müşteri incelemelerini, başlıkları, açıklamaları, Google Analytics kaynaklarını/araçlarını vb. içerebilir. Bunları gruplar halinde kategorilere ayırmak veya duyguları ve tonları almak isteyebilirsiniz.
Potansiyel çözümler şunlar olabilir:
- Makine öğrenimi modellerini dbt akışının dışına uygulayın (veya bir LLM'yi arayın)
- CASE WHEN ifadelerini kullanarak dbt modelleri içindeki basit kategorizasyonları tanımlayın
- Kategorileri önceden tanımlayın ve bunları ham veritabanı katmanınıza yükleyin veya dbt çekirdek işlevinden yararlanın
Python dbt modelleri geliştikçe bir çözüm daha var: Bu Doğal Dil İşleme görevlerini dbt modellerinden biri olarak dbt ortamınızda tutabilirsiniz.
Bu sizin için yararlı olabilirse dbt projenizde OpenAI API'yi nasıl kullanacağınıza ilişkin adım adım kılavuza bakın. GitHub deposundaki kod ve veri örneğini alarak bu kılavuzdaki her şeyi ortamınızda çoğaltabilirsiniz (sondaki bağlantılara bakın).
Ortamı Ayarlayın
Zaten bir dbt projeniz ve verileriniz varsa veya sonuçları yeniden oluşturmak istemiyorsanız (4)'e geçin veya bu bölümü tamamen atlayın. Aksi takdirde aşağıdakilere ihtiyacınız olacaktır:
Dbt projesini ayarlayın . Resmi belgeler
Bu kılavuz için hazırladığımı GitHub'dan kolayca kopyalayabilirsiniz.
Profiles.yml dosyanızı oluşturmayı/güncellemeyi unutmayın.
Veritabanını ayarlayın . Snowflake'i kullandım. Ne yazık ki ücretsiz bir sürümü yok ancak 30 günlük ücretsiz deneme süresi sunuyorlar.
Şu anda dbt Python modelleri yalnızca Snowflake, Databricks ve BigQuery (PostgreSQL yok) ile çalışmaktadır. Bu nedenle, bazı ayrıntılar farklılık gösterse de, bu eğitim bunlardan herhangi biri için işe yarayacaktır.
Kaynak verilerini hazırlayın
Veri kümesi olarak TidyTuesday deposunda yayınlanan bir R paketi meta verilerini kullandım.
Veritabanınıza yükleyin.
Veritabanınız ve şema adlarınızla eşleşecek şekilde dbt projesindeki
source.yml
dosyasını güncelleyin.
OpenAI API anahtarını edinin
Resmi belgelerdeki hızlı başlangıç talimatlarını izleyin.
Not: Ücretsiz değildir ancak kullandıkça öde yöntemidir. Dolayısıyla, 10 satırlık test veri kümesiyle, denemeleriniz sırasında sizden 1 ABD dolarından fazla ücret alınmaz.
Daha dikkatli olmak için bir harcama limiti belirleyin.
Snowflake'te Harici Erişim Entegrasyonunu Ayarlama
- Bu yalnızca Snowflake kullanıyorsanız geçerlidir.
- Bu yapılmazsa dbt Python modelleri internetteki hiçbir API'ye (OpenAI API dahil) erişemez.
- Resmi talimatları izleyin.
- OpenAI API anahtarını bu entegrasyonda saklayın.
Bir Kategori Listesi Oluşturun
İlk olarak, eğer bir sınıflandırma görevini çözüyorsanız, LLM isteminizde kullanmak için kategorilere (diğer adıyla sınıflara) ihtiyacınız vardır. Temel olarak şöyle diyeceksiniz: "Bu kategorilerin bir listesi var, bu metnin hangisine ait olduğunu tanımlayabilir misiniz?"
Buradaki bazı seçenekler:
Önceden tanımlanmış kategorilerin bir listesini manuel olarak oluşturun
Sabit ve öngörülebilir kategorilere ihtiyacınız varsa uygundur.
Buraya "Diğerleri" eklemeyi unutmayın, böylece Yüksek Lisans belirsiz olduğunda bu seçeneklere sahip olacaktır.
İsteminizde LLM'den "Diğerleri" kategorisini kullandığında bir kategori adı önermesini isteyin.
Önceden tanımlanmış bir listeyi veritabanının ham katmanına veya dbt projenize CSV olarak yükleyin (
dbt seed
kullanarak).
Verilerinizin bir örneğini LLM'ye gönderin ve ondan N kategori bulmasını isteyin.
Önceki yaklaşımla aynı, ancak liste konusunda yardım alıyoruz.
GPT kullanıyorsanız, tekrarlanabilirlik açısından burada tohum kullanmak daha iyidir.
Önceden tanımlanmış kategoriler olmadan gidin ve LLM'nin işi hareket halindeyken yapmasına izin verin.
Bu daha az öngörülebilir sonuçlara yol açabilir.
Aynı zamanda, eğer bir rastlantısallık payına sahipseniz bu da yeterince iyidir.
GPT kullanım durumunda, yeniden çalıştırmanız gerekmesi durumunda farklı sonuçlardan kaçınmak için sıcaklık = 0 değerini ayarlamak daha iyidir.
Bu blog yazısında 3. seçeneği kullanacağım.
OpenAI API'sini Çağırmak için bir dbt Python Modeli oluşturun
Şimdi bu yazının ana kısmına geçelim ve yukarı akış tablosundan yeni metin verilerini alacak, bunu OpenAI API'sine besleyecek ve kategoriyi tabloya kaydedecek bir dbt modeli oluşturalım.
Yukarıda bahsettiğim gibi R paketleri veri kümesini kullanacağım. R, veri analizinde oldukça popüler bir programlama dilidir. Bu veri kümesi, CRAN projesindeki R paketleri hakkında sürüm, lisans, yazar, başlık, açıklama vb. bilgileri içerir. Her paket için başlığına göre bir kategori oluşturacağımız için title
alanıyla ilgileniyoruz.
Modelin tabanını hazırlayın
dbt config,
dbt.config(...)
yöntemi aracılığıyla aktarılabilir.
dbt.config dosyasında ek argümanlar vardır; örneğin,
packages
bir paket gereksinimidir.
dbt Python modeli, yukarı akış modellerine referans verebilir
dbt.ref('...')
veyadbt.source('...')
Bir DataFrame döndürmesi gerekir. Veritabanınız onu tablo olarak kaydedecektir.
import os import openai import pandas as pd COL_TO_CATEGORIZE = 'title' def model(dbt, session): import _snowflake dbt.config( packages=['pandas', 'openai'], ) df = dbt.ref('package').to_pandas() df.drop_duplicates(subset=[COL_TO_CATEGORIZE], inplace=True) return df
OpenAI API'sine bağlanın
secrets
veexternal_access_integrations
dbt.config'e aktarmamız gerekiyor. Snowflake Harici Erişim Entegrasyonunuzda saklanan gizli referansı içerecektir.
Not: Bu özellik yalnızca birkaç gün önce yayımlandı ve yalnızca beta dbt sürüm 1.8.0-b3'te mevcut.
dbt.config( packages=['pandas', 'openai'], secrets={'openai_key': 'openai_key', 'openai_org': 'openai_org'}, external_access_integrations=['openai_external_access_integration'], ) client = openai.OpenAI( api_key=_snowflake.get_generic_secret_string('openai_key'), organization=_snowflake.get_generic_secret_string('openai_org'), )
Dbt modelini artımlı yapın ve tam yenilemeleri kapatın.
- Bu bölüm OpenAI API maliyetlerini düşük tutmak için gereklidir.
- Aynı metni birden çok kez kategorize etmesini önleyecektir.
- Aksi takdirde, günde birkaç kez olabilen
dbt run
her çalıştırdığınızda OpenAI'ye tüm verileri gönderirsiniz. - Dbt.config dosyasına
materialized='incremental'
,incremental_strategy='append'
,full_refresh = False
ekliyoruz. - Artık tam tarama yalnızca ilk dbt çalıştırması için olacak ve daha sonraki çalıştırmalar için (artımlı veya tam yenileme fark etmez) yalnızca deltayı kategorilere ayıracaktır.
- Daha dikkatli olmak istiyorsanız, benzersiz girişlerin sayısını azaltmak için verilerinizi biraz ön işleme tabi tutabilirsiniz, ancak LLM'ler doğal dille daha iyi çalıştığı için çok fazla ön işleme yapmaktan kaçının.
dbt.config( materialized='incremental', incremental_strategy='append', full_refresh = False, packages=['pandas', 'openai'], secrets={'openai_key': 'openai_key', 'openai_org': 'openai_org'}, external_access_integrations=['openai_external_access_integration'], ) if dbt.is_incremental: pass
Artımlılık mantığı ekleyin
- Artımlı çalıştırmada (kurulumumuz nedeniyle, ilki dışındaki tüm çalıştırmalarda anlamına gelir), zaten kategorize edilmiş tüm başlıkları kaldırmamız gerekir.
- Bunu sadece
dbt.this
kullanarak yapabiliriz. Normal artımlı modellere benzer.
if dbt.is_incremental: categorized_query = f''' SELECT DISTINCT "{ COL_TO_CATEGORIZE }" AS primary_key FROM { dbt.this } WHERE "category" IS NOT NULL ''' categorized = [row.PRIMARY_KEY for row in session.sql(categorized_query).collect()] df = df.loc[~df[COL_TO_CATEGORIZE].isin(categorized), :]
Toplu olarak OpenAI API'yi çağırın
- Maliyetleri azaltmak için verileri OpenAI API'ye toplu olarak göndermek daha iyidir.
- Sistem istemi, sınıflandırmamız gereken metinden 5 kat daha büyük olabilir. Her oyun için ayrı ayrı sistem istemi gönderirsek, tekrarlanan şeyler için çok daha yüksek token kullanımına yol açacaktır.
- Ancak partinin büyük olmaması gerekiyor. Büyük partilerde GPT daha az istikrarlı sonuçlar üretmeye başlar. Deneylerime göre parti büyüklüğü = 5 yeterince iyi çalışıyor.
- Ayrıca yanıtın ilgili boyutu aşmamasını sağlamak için
max_tokens
kısıtlamasını ekledim.
BATCH_SIZE = 5 n_rows = df.shape[0] categories = [None for idx in range(n_rows)] for idx in range(0, n_rows, BATCH_SIZE): df_sliced = df.iloc[idx:idx+BATCH_SIZE, :] user_prompt = f'```{ "|".join(df_sliced[COL_TO_CATEGORIZE].to_list()) }```' chat_completion = client.chat.completions.create( messages=[ {'role': 'system', 'content': SYSTEM_PROMPT}, {'role': 'user', 'content': user_prompt} ], model='gpt-3.5-turbo', temperature=0, max_tokens=10*BATCH_SIZE + 2*BATCH_SIZE, ) gpt_response = chat_completion.choices[0].message.content gpt_response = [category.strip() for category in gpt_response.split('|')] categories[idx:idx + len(gpt_response)] = gpt_response df['category'] = categories df.dropna(subset=['category'], inplace=True)
LLM için bir istem hakkında konuşmanın zamanı geldi. Elimde şu var:
Size ``` parantez içinde CRAN R paket başlıklarının bir listesi verilecektir. Başlıklar "|" ile ayrılacaktır imza. Her başlık için bir kategori bulun. Yalnızca "|" ile ayrılmış kategori adlarını döndür imza.
- Talimatı doğrudan noktaya tutun.
- SQL enjeksiyonlarından kaçınmak için ``` tekniğini kullanın.
- Sonuç formatı konusunda net olun. Benim durumumda "|" istedim hem giriş hem de çıkışlar için ayırıcı olarak
Son dbt model kodu
import os import openai import pandas as pd SYSTEM_PROMPT = '''You will be provided a list of CRAN R package titles in ``` brackets. Titles will be separated by "|" sign. Come up with a category for each title. Return only category names separated by "|" sign. ''' COL_TO_CATEGORIZE = 'title' BATCH_SIZE = 5 def model(dbt, session): import _snowflake dbt.config( materialized='incremental', incremental_strategy='append', full_refresh = False, packages=['pandas', 'openai'], secrets={'openai_key': 'openai_key', 'openai_org': 'openai_org'}, external_access_integrations=['openai_external_access_integration'], ) client = openai.OpenAI( api_key=_snowflake.get_generic_secret_string('openai_key'), organization=_snowflake.get_generic_secret_string('openai_org'), ) df = dbt.ref('package').to_pandas() df.drop_duplicates(subset=[COL_TO_CATEGORIZE], inplace=True) if dbt.is_incremental: categorized_query = f''' SELECT DISTINCT "{ COL_TO_CATEGORIZE }" AS primary_key FROM { dbt.this } WHERE "category" IS NOT NULL ''' categorized = [row.PRIMARY_KEY for row in session.sql(categorized_query).collect()] df = df.loc[~df[COL_TO_CATEGORIZE].isin(categorized), :] n_rows = df.shape[0] categories = [None for idx in range(n_rows)] for idx in range(0, n_rows, BATCH_SIZE): df_sliced = df.iloc[idx:idx+BATCH_SIZE, :] user_prompt = f'```{ "|".join(df_sliced[COL_TO_CATEGORIZE].to_list()) }```' chat_completion = client.chat.completions.create( messages=[ {'role': 'system', 'content': SYSTEM_PROMPT}, {'role': 'user', 'content': user_prompt} ], model='gpt-3.5-turbo', temperature=0, max_tokens=10*BATCH_SIZE + 2*BATCH_SIZE, ) gpt_response = chat_completion.choices[0].message.content gpt_response = [category.strip() for category in gpt_response.split('|')] categories[idx:idx + len(gpt_response)] = gpt_response df['category'] = categories df.dropna(subset=['category'], inplace=True) return df
Maliyet Tahminleri
OpenAI API Fiyatlandırması burada listelenmiştir. Talep edilen ve iade edilen jeton sayısına göre ücret alırlar. Belirteç, isteğinizdeki bir dizi karakterle ilişkilendirilen bir örnektir. Belirli bir metin için bir dizi belirteci değerlendirmek için açık kaynak paketleri vardır. Örneğin Tiktoken . Manuel olarak değerlendirmek isterseniz gidilecek yer burada resmi bir OpenAI tokenizerdır.
Veri setimizde ~18K başlık bulunmaktadır. Kabaca, 320.000 giriş tokenına (parti boyutu = 5 kullanırsak 180.000 başlık ve 140.000 sistem istemi) ve 50.000 çıkış tokenına eşittir. Modele bağlı olarak tam taramanın maliyeti şu şekilde olacaktır:
-
GPT-4 Turbo
: 4,7 dolar . Fiyatlandırma: girdi: 10$ / 1 milyon token; çıktı: 30 $ / 1 milyon jeton. -
GPT-4
: 12,6 dolar. Fiyatlandırma: girdi: 30 ABD doları / 1 milyon token; çıktı: 60 $ / 1 milyon jeton. -
GPT-3.5 Turbo
: 0,2 ABD doları. Fiyatlandırma: girdi: 0,5 ABD Doları / 1 milyon token; çıktı: 1,5 ABD doları / 1 milyon jeton.
Sonuçlar
Dbt modeli bir cazibe gibi çalıştı. 18K paketlerin tamamını boşluk bırakmadan başarıyla kategorize ettim. Modelin uygun maliyetli olduğu ve birden fazla dbt çalıştırılmasına karşı korunduğu kanıtlandı.
Sonuç kontrol panelini burada Tableau Public'te yayınladım. Onunla oynamaktan, verileri indirmekten ve bunun üzerinde dilediğinizi oluşturmaktan çekinmeyin.
Bulduğum bazı ilginç ayrıntılar:
- İlk 1 kategori
Data Visualization
(1.190 paket veya %6). Sanırım bu, özellikle Shiny, Plotly ve diğerleri gibi paketlerde R'nin bir görselleştirme aracı olarak popülerliğini kanıtlıyor.
- 2023'te en çok büyüyen iki kategori
Data Import
veData Processing
oldu. Görünüşe göre R daha çok bir veri işleme aracı olarak kullanılmaya başlandı.
- İlk 30 kategori arasında yıldan yıla en büyük büyüme 2019'da
Natural Language Processing
gerçekleşti. Ünlü "Attention Is All You Need" makalesinden iki yıl sonra ve GPT-1'in yayınlanmasından altı ay sonra :)
Diğer Fikirler
Alternatif bir yaklaşım kullanabiliriz: GPT yerleştirmeleri .
Çok daha ucuz.
Ancak daha çok mühendislik ağırlıklı çünkü sınıflandırma kısmını kendi başınıza yapmalısınız (bu seçeneği sonraki yazılardan birinde inceleyeceğim için bizi takip etmeye devam edin).
Elbette bu kısmı dbt'den çıkarıp bulut işlevlerine veya kullandığınız altyapıya aktarmak mantıklı olacaktır. Aynı zamanda, eğer onu dbt altında tutmak istiyorsanız - bu yazı tam size göre.
Modele herhangi bir mantık eklemekten kaçının. Tek bir iş yapmalı: LLM'yi arayın ve sonucu kaydedin. Bu, tekrar çalıştırmaktan uzak durmanıza yardımcı olacaktır.
Dbt projenizde birçok ortamı kullanma olasılığınız yüksektir. Dikkatli olmanız ve bu modeli her geliştirici ortamında her Çekme İsteğinde tekrar tekrar çalıştırmaktan kaçınmanız gerekir.
Bunu yapmak için, mantığı
if dbt.config.get("target_name") == 'dev'
ile birleştirebilirsiniz.
Sınırlayıcıyla verilen yanıt kararsız olabilir.
Örneğin, GPT beklediğinizden daha az öğe döndürebilir ve ilk başlıkları kategori listesiyle eşleştirmek zor olacaktır.
Bunun üstesinden gelmek için JSON çıktısını gerektirecek şekilde isteğinize
response_format={ "type": "json_object" }
ekleyin. Resmi belgelere bakın.JSON çıktısıyla, {"title": "category"} biçiminde bir yanıt sağlamanızı isteyebilir ve ardından bunu başlangıç değerlerinizle eşleştirebilirsiniz.
Yanıt boyutunu artıracağından daha pahalı olacağını unutmayın.
Garip bir şekilde, GPT 3.5 Turbo için JSON'a geçtiğimde sınıflandırma kalitesi önemli ölçüde düştü.
Snowflake'te cortex.complete() işlevini kullanan bir alternatif var. Dbt blogunda Joel Labes'in harika gönderisine göz atın.
Bu kadar! Ne düşündüğü söyle.
Bağlantılar
GitHub'daki kodun tamamı: bağlantı
Tableau Genel kontrol paneli: bağlantı
TidyTuesday R veri kümesi:bağlantı