TL;DR
Bạn có thể tự động giải quyết các tác vụ Xử lý ngôn ngữ tự nhiên điển hình (phân loại, phân tích cảm tính, v.v.) cho dữ liệu văn bản của mình bằng LLM với mức giá rẻ nhất là 10 USD cho mỗi 1 triệu hàng (tùy thuộc vào nhiệm vụ và mô hình), duy trì trong môi trường dbt của bạn. Hướng dẫn, chi tiết và mã bên dưới
Nếu bạn đang sử dụng dbt làm lớp chuyển đổi, bạn có thể gặp tình huống muốn trích xuất thông tin có ý nghĩa từ dữ liệu văn bản phi cấu trúc. Những dữ liệu đó có thể bao gồm đánh giá của khách hàng, tiêu đề, mô tả, nguồn/phương tiện Google Analytics, v.v. Bạn có thể muốn phân loại chúng thành các nhóm hoặc tìm nạp cảm xúc và giọng điệu.
Các giải pháp tiềm năng sẽ là
Khi các mô hình dbt Python đang phát triển, có một giải pháp nữa: bạn có thể giữ các tác vụ Xử lý ngôn ngữ tự nhiên này bên trong môi trường dbt của mình dưới dạng một trong các mô hình dbt.
Nếu điều đó có thể hữu ích cho bạn, hãy xem hướng dẫn từng bước bên dưới về cách sử dụng API OpenAI trong dự án dbt của bạn. Bạn có thể tái tạo mọi thứ từ hướng dẫn này trong môi trường của mình, lấy mã và mẫu dữ liệu từ kho lưu trữ GitHub (xem các liên kết ở cuối).
Nếu bạn đã có dự án và dữ liệu dbt hoặc không muốn tái tạo kết quả, hãy chuyển đến (4) hoặc bỏ qua hoàn toàn phần này. Nếu không, bạn sẽ cần những thứ sau:
Thiết lập dự án dbt . Tài liệu chính thức
Bạn có thể chỉ cần sao chép cái tôi đã chuẩn bị cho hướng dẫn này từ GitHub .
Đừng quên tạo/cập nhật tệp profile.yml của bạn.
Thiết lập cơ sở dữ liệu . Tôi đã sử dụng Bông tuyết. Thật không may, không có phiên bản miễn phí nhưng họ cung cấp bản dùng thử miễn phí 30 ngày .
Hiện tại, các mô hình dbt Python chỉ hoạt động với Snowflake, Databricks và BigQuery (không có PostgreSQL). Vì vậy, hướng dẫn này sẽ phù hợp với bất kỳ trường hợp nào trong số đó, mặc dù một số chi tiết có thể khác nhau.
Chuẩn bị dữ liệu nguồn
Là một tập dữ liệu, tôi đã sử dụng siêu dữ liệu gói R được xuất bản trong kho lưu trữ TidyTuesday.
Tải nó lên cơ sở dữ liệu của bạn.
Cập nhật tệp source.yml
trong dự án dbt để khớp với tên cơ sở dữ liệu và lược đồ của bạn.
Nhận khóa API OpenAI
Làm theo hướng dẫn bắt đầu nhanh từ các tài liệu chính thức .
Không: e nó không miễn phí, nhưng nó trả tiền khi bạn sử dụng. Vì vậy, với tập dữ liệu 10 hàng thử nghiệm, bạn sẽ không bị tính phí nhiều hơn $1 trong quá trình thử nghiệm của mình.
Để cẩn thận hơn, hãy đặt giới hạn chi tiêu.
Thiết lập tích hợp truy cập bên ngoài trong Snowflake
Đầu tiên, nếu bạn đang giải quyết một nhiệm vụ phân loại, bạn cần các danh mục (hay còn gọi là lớp) để sử dụng trong lời nhắc LLM của mình. Về cơ bản, bạn sẽ nói: "Tôi có một danh sách các danh mục này, bạn có thể xác định văn bản này thuộc về danh mục nào không?"
Một số tùy chọn ở đây:
Tạo danh sách các danh mục được xác định trước theo cách thủ công
Nó phù hợp nếu bạn cần các danh mục ổn định và có thể dự đoán được.
Đừng quên thêm "Khác" vào đây để LLM sẽ có những lựa chọn này khi không chắc chắn.
Yêu cầu LLM trong lời nhắc của bạn đề xuất tên danh mục bất cứ khi nào nó sử dụng danh mục "Khác".
Tải danh sách được xác định trước lên lớp thô của cơ sở dữ liệu hoặc dưới dạng CSV trong dự án dbt của bạn (sử dụng dbt seed
).
Cung cấp mẫu dữ liệu của bạn cho LLM và yêu cầu nó đưa ra N danh mục.
Cách tiếp cận tương tự như cách trước, nhưng chúng tôi đang nhận được trợ giúp về danh sách.
Nếu bạn sử dụng GPT, tốt hơn nên sử dụng hạt giống ở đây để có khả năng tái tạo.
Không cần có các danh mục được xác định trước và để LLM thực hiện công việc khi đang di chuyển.
Điều này có thể dẫn đến kết quả ít dự đoán hơn.
Đồng thời, sẽ đủ tốt nếu bạn hài lòng với một mức độ ngẫu nhiên.
Trong trường hợp sử dụng GPT, tốt hơn nên đặt nhiệt độ = 0 để tránh các kết quả khác nhau trong trường hợp bạn cần chạy lại.
Trong bài đăng trên blog này, tôi sẽ chọn tùy chọn thứ 3.
Bây giờ, chúng ta hãy đi vào nội dung chính của bài đăng này và tạo một mô hình dbt sẽ lấy dữ liệu văn bản mới từ bảng ngược dòng, đưa dữ liệu đó vào API OpenAI và lưu danh mục vào bảng.
Như đã đề cập ở trên, tôi sẽ sử dụng tập dữ liệu gói R. R là ngôn ngữ lập trình rất phổ biến trong phân tích dữ liệu. Tập dữ liệu này chứa thông tin về các gói R từ dự án CRAN, chẳng hạn như phiên bản, giấy phép, tác giả, tiêu đề, mô tả, v.v. Chúng tôi quan tâm đến trường title
vì chúng tôi sẽ tạo một danh mục cho mỗi gói dựa trên tiêu đề của nó.
Chuẩn bị nền cho mô hình
Cấu hình dbt có thể được truyền qua phương thức dbt.config(...)
.
Có các đối số bổ sung trong dbt.config, ví dụ: packages
là một yêu cầu về gói.
Mô hình dbt Python có thể tham chiếu các mô hình ngược dòng dbt.ref('...')
hoặc dbt.source('...')
Nó phải trả về DataFrame. Cơ sở dữ liệu của bạn sẽ lưu nó dưới dạng bảng.
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
Kết nối với API OpenAI
Chúng ta cần chuyển secrets
và external_access_integrations
cho dbt.config. Nó sẽ chứa tham chiếu bí mật được lưu trữ trong Tích hợp truy cập bên ngoài Snowflake của bạn.
Lưu ý: tính năng này mới được phát hành vài ngày trước và chỉ có trong phiên bản beta dbt 1.8.0-b3
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'), )
Làm cho mô hình dbt tăng dần và tắt tính năng làm mới hoàn toàn.
dbt run
, có thể vài lần một ngày.materialized='incremental'
, incremental_strategy='append'
, full_refresh = False
, vào dbt.config 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
Thêm logic tăng dần
dbt.this
. Tương tự như các mô hình gia tăng thông thường. 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), :]
Gọi API OpenAI theo đợt
max_tokens
. 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)
Đã đến lúc nói về lời nhắc cho LLM. Đó là những gì tôi nhận được:
Bạn sẽ được cung cấp danh sách tiêu đề gói CRAN R trong ngoặc ```. Các tiêu đề sẽ được phân tách bằng dấu "|" dấu hiệu. Hãy đưa ra một danh mục cho mỗi tiêu đề. Chỉ trả lại tên danh mục được phân tách bằng "|" dấu hiệu.
Mã mô hình dbt cuối cùng
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
Giá API OpenAI được liệt kê ở đây . Họ tính phí theo số lượng token được yêu cầu và trả lại. Mã thông báo là một phiên bản tương quan với một số ký tự trong yêu cầu của bạn. Có các gói nguồn mở để đánh giá một số mã thông báo cho một văn bản nhất định. Ví dụ: Tiktoken . Nếu bạn muốn đánh giá nó theo cách thủ công, nơi cần đến là mã thông báo OpenAI chính thức tại đây .
Trong tập dữ liệu của chúng tôi, có ~ 18 nghìn đầu sách. Đại khái, nó tương đương với 320K mã thông báo đầu vào (180K tiêu đề và 140K lời nhắc hệ thống nếu chúng tôi sử dụng kích thước lô = 5) và 50K mã thông báo đầu ra. Tùy thuộc vào kiểu máy, chi phí cho việc quét toàn bộ sẽ là:
GPT-4 Turbo
: 4,7 USD . Giá cả: đầu vào: $10 / 1 triệu token; đầu ra: $30 / 1 triệu token.GPT-4
: 12,6 USD. Giá cả: đầu vào: $30 / 1 triệu token; đầu ra: $60 / 1 triệu token.GPT-3.5 Turbo
: 0,2 USD. Giá cả: đầu vào: 0,5 USD / 1 triệu token; đầu ra: 1,5 USD / 1 triệu token.Mô hình dbt hoạt động như một cơ duyên. Tôi đã phân loại thành công tất cả các gói 18K mà không có bất kỳ khoảng trống nào. Mô hình này tỏ ra hiệu quả về mặt chi phí và được bảo vệ trước nhiều lần chạy dbt.
Tôi đã xuất bản bảng điều khiển kết quả lên Tableau Public tại đây . Hãy thoải mái chơi với nó, tải xuống dữ liệu và tạo bất cứ thứ gì bạn muốn trên đó.
Một số chi tiết thú vị tôi tìm thấy:
Data Visualization
(1.190 gói, tương đương 6%). Tôi đoán điều này chứng tỏ sự phổ biến của R như một công cụ trực quan hóa, đặc biệt là với các gói như Shiny, Plotly và các gói khác.
Data Import
và Data Processing
. Có vẻ như R bắt đầu được sử dụng nhiều hơn như một công cụ xử lý dữ liệu.
Natural Language Processing
vào năm 2019. Hai năm sau bài báo nổi tiếng "Chú ý là tất cả những gì bạn cần" và nửa năm sau khi phát hành GPT-1 :)Chúng tôi có thể sử dụng một cách tiếp cận khác - nhúng GPT .
Nó rẻ hơn nhiều.
Nhưng nặng về kỹ thuật hơn vì bạn nên tự mình thực hiện phần phân loại (hãy chú ý theo dõi vì tôi sẽ khám phá tùy chọn này trong một trong các bài đăng tiếp theo).
Chắc chắn, việc loại bỏ phần này khỏi dbt và đẩy nó lên các chức năng đám mây hoặc bất kỳ cơ sở hạ tầng nào bạn sử dụng là điều hợp lý. Đồng thời, nếu bạn muốn giữ nó dưới dbt - bài đăng này sẽ giúp bạn hiểu rõ hơn.
Tránh thêm bất kỳ logic nào vào mô hình. Nó sẽ thực hiện một công việc - gọi LLM và lưu kết quả. Điều này sẽ giúp bạn tránh phải chạy lại nó.
Rất có thể bạn đang sử dụng nhiều môi trường trong dự án dbt của mình. Bạn cần lưu ý và tránh chạy đi chạy lại mô hình này trong từng môi trường nhà phát triển trên mỗi Yêu cầu Kéo.
Để thực hiện việc này, bạn có thể kết hợp logic với if dbt.config.get("target_name") == 'dev'
Phản hồi bằng dấu phân cách có thể không ổn định.
Ví dụ: GPT có thể trả về ít phần tử hơn bạn mong đợi và sẽ khó ánh xạ các tiêu đề ban đầu vào danh sách danh mục.
Để khắc phục điều này, hãy thêm response_format={ "type": "json_object" }
vào yêu cầu của bạn để yêu cầu đầu ra JSON. Xem tài liệu chính thức .
Với đầu ra JSON, bạn có thể yêu cầu cung cấp câu trả lời ở định dạng {"title": "category"}, sau đó ánh xạ câu trả lời đó tới các giá trị ban đầu của bạn.
Lưu ý rằng nó sẽ đắt hơn vì nó sẽ tăng kích thước phản hồi.
Thật kỳ lạ, chất lượng phân loại giảm đáng kể khi tôi chuyển sang JSON cho GPT 3.5 Turbo.
Có một giải pháp thay thế trong Snowflake - sử dụng hàm Cortex.complete() . Hãy xem một bài đăng tuyệt vời của Joel Labes trên blog dbt.
Đó là nó! Cho tôi biết bạn nghĩ gì.
Mã đầy đủ trên GitHub: link
Bảng điều khiển công khai của Tableau: liên kết
Tập dữ liệu TidyTuesday R:liên kết