Những ngày qua ChatGPT có thể nói đã dấy lên một làn sóng thảo luận mạnh mẽ trong xã hội, không chỉ dừng lại trong cộng đồng IT. Người ta nói về nguy cơ robot/ trí tuệ nhân tạo thay thế con người nhiều hơn bao giờ hết. Tuy nhiên mình nghĩ chúng ta nên quan tâm nhiều hơn đến việc làm thế nào để sử dụng ChatGPT (cũng như các mô hình GPT) như một công cụ hỗ trợ để tối ưu hóa năng suất làm việc, cũng như giải quyết các bài toán đang gặp phải.
Vì GPT là một mô hình ngôn ngữ có thế mạnh trong việc sinh văn bản, một hướng áp dụng của nó ta có thể nghĩ đến là sinh thêm dữ liệu training cho bài toán xử lý ngôn ngữ tự nhiên. Mục tiêu là có thể xây dựng được các model NLP hoạt động hiệu quả ngay cả với lượng data sẵn có cực ít.
Trong bài viết này, mình sẽ thử sử dụng mô hình GPT-3 của OpenAI để augment dữ liệu cho bài Sentiment Analysis và xem xét hiệu quả của nó, trước hết là với ngôn ngữ Tiếng Anh.
1. Giới thiệu về mô hình và API của OpenAI
OpenAI cung cấp các mô hình của họ cho chúng ta truy cập dưới dạng API với một mức phí nho nhỏ. Khi tạo tài khoản mới các bạn sẽ được tặng 18 USD để sử dụng thử trong 3 tháng. Ở phần sau mình cũng sẽ review một chút về khía cạnh chi phí này.
Hiện có 4 model chính với quy mô và khả năng khác nhau, đáp ứng được các task NLP đa dạng:
- Davinci: Complex intent, cause and effect, summarization for audience
- Curie: Language translation, complex classification, text sentiment, summarization
- Babbage: Moderate classification, semantic search classification
- Ada: Parsing text, simple classification, address correction, keywords
Nói chung, Davinci là mô hình mạnh nhất, nhưng các mô hình khác cũng có thể thực hiện một số tác vụ nhất định cực kỳ tốt với lợi thế về tốc độ hoặc chi phí đáng kể. Ví dụ, Curie có thể thực hiện nhiều task tương tự như Davinci, nhưng nhanh hơn và chi phí chỉ bằng 1/10. Vì vậy lời khuyên là bạn nên sử dụng Davinci trong khi thử nghiệm vì nó mang lại kết quả tốt nhất. Khi mọi thứ đã hoạt động, hãy thử các mô hình khác để xem liệu bạn có thể nhận được kết quả tương tự với độ trễ thấp hơn hay không. Bạn cũng có thể cải thiện hiệu suất của các mô hình khác bằng cách fine-tune chúng theo một task cụ thể.
Để sử dụng API trên python, chúng ta cài đặt thư viện OpenAI
!pip install --upgrade openai
import openai
Thiết lập API keys của mình tại mục View API keys trong menu Account
Set API key:
openai.api_key = "YOUR_API_KEY"
Với nhiệm vụ sinh dữ liệu này, chúng ta sẽ sử dụng API Completions của OpenAI. Với input là một đoạn prompt (lời nhắc), mô hình sẽ trả về phần còn lại của câu/ đoạn.
completion = openai.Completion.create(model="davinci", prompt="This is a great movie!", max_tokens=100)
completion = openai.Completion.create( model="text-davinci-003", prompt="The following are movie reviews with a negative sentiment. REVIEW:", max_tokens=100, temperature=1.5)
Tham số:
model
: tên của model sẽ sử dụng. Xem thêm tại link hoặc gọi APIprompt
: lời nhắc để "mớm" cho model. Hãy thử các cách đặt câu prompt khác nhau để thu được kết quả ưng ý.max_tokens
: số lượng token tối đa được tạo ra (nhỏ hơn 2048). Sẽ ảnh hưởng đến chi phí nên hãy lựa chọn độ dài phù hợp với nhu cầutemperature
: trong khoảng 0-2 (Defaults to 1), các giá trị cao hơn sẽ làm cho output ngẫu nhiên hơn.top_p
: (Defaults to 1) mô hình chỉ xem xét các token có probability mass thuộc top_p. Dùng thay thế chotemperature
, không dùng cả 2n
: số completion được sinh ra cho mỗi prompt, có ảnh hưởng đến chi phípresence_penalty
,frequency_penalty
: số trong khoảng [-2.0-2.0]. Giá trị dương làm tăng khả năng nói về chủ để mới/ giảm khả năng model lặp lại từ- vv.
Kết quả:
# prompt: "This is a great movie!"
print(completion.choices[0]['text'])
It's got everything in it! Sci-fi, action and even has a fantastic storyline. It keeps you hooked in! Crispin Glover is astonishing in this film! He delivers his is a fantastic performance! And Lea Thompson to me is one of those actresses that's overrated! Could anyone at all explain to me why Lea is way overated!?!? I mean she acts no better then being average! I love Crispin in this film. And is quite funny as well!
I definitely recommend it to anyone interested in learning about the D-Day landings of WWII. People are going to be very affected for days after watching this, actually it may have even been me a bit, but I was usually crying when I was watching it. If my huffings has you feeling "sad", then you must watch this movie, it is heart wrenching and at times funny, while having some serious parts to it. This section is turning out to be a movie just like the 1st part, so these should be good films to watch.
#prompt: "The following are movie reviews with a negative sentiment. REVIEW:"
print(completion.choices[0]['text'])
- "Ridiculous plot and mediocre acting make this one a contest for the worst. Avoid it at all costs!"
- "Insipid performances and a cliched overly long storyline leave much to be desired from this dull abomination of a film!"
- "While this B movie has some interesting ideas, it just didn't work out - leading to cannon fodder with poor script and sloppily edited footage littered and interrupted by annoying advertisers."
"This movie was horrible! I had high hopes but 60 minutes went brilliantly. Terrible plot and really awful acting, comparable only with Sy Pharaoh's filmography." Rate 1/10: 1/10
"The characters were two-dimensional and the plot had more holes than a slice of Swiss cheese. This movie was a complete waste of time and money."
Kết quả nhận được khá hài lòng, dù chưa có thông tin nào khác được feed vào ngoài từ ngữ mô tả sắc thái (tích cực/ tiêu cực), mô hình vẫn có thể sinh ra các review phim đúng yêu cầu, với cách diễn đạt và dùng từ khá đa dạng.
2. Mô tả bài toán
- Bài toán: phân loại nội dung review phim là Positive hay Negative
- Training data gốc: 100 samples lấy từ dataset IMDB Dataset of 50K Movie Reviews, gồm 50 đánh giá tích cực (Positive) + 50 đánh giá tiêu cực (Negative)
- Augmentation data: 1000 samples review sinh từ engine Davinci của mô hình GPT-3 (tỷ lệ Positive - Negative là 5-5)
- Test data: 100 samples lấy từ dataset IMDB Dataset of 50K Movie Reviews, (tỷ lệ Positive - Negative là 5-5)
- Thử nghiệm: Dùng dữ liệu augmentation sinh ra bởi mô hình GPT-3 để đào tạo mô hình phân loại, so sánh kết quả với mô hình train trên data gốc, rút ra kết luận về tính hiệu quả của GPT-3 trong việc tăng cường dữ liệu NLP
3. Thực hiện
Dataset gốc
Tải về và chuẩn bị tập IMDB Dataset of 50K Movie Reviews
!unzip "/content/IMDB Dataset.csv.zip"
imdb_dataset = pd.read_csv("/content/IMDB Dataset.csv")
imdb_dataset['sentiment'] = imdb_dataset['sentiment'].replace('positive', 1)
imdb_dataset['sentiment'] = imdb_dataset['sentiment'].replace('negative', 0)
imdb_dataset = imdb_dataset.sort_values(by=['sentiment'])
Lấy ra 100 sample cho tập train
imdb_dataset_train = pd.concat([imdb_dataset.iloc[:50], imdb_dataset.iloc[-50:]], ignore_index = True, sort = False)
Lấy ra 100 sample cho tập test
imdb_dataset_test = imdb_dataset.iloc[24950:25050]
Sinh augment data
Có 2 hình thức để yêu cầu model sinh dữ liệu cho bài toán của chúng ta.
- Đơn giản là đưa ra yêu cầu về nội dung, sắc thái tích cực/ tiêu cực trong lời nhắc (prompt) cho mô hình (zero-shot data) giống như ví dụ bên trên. Đây cũng là cách mình sử dụng trong bài này.
good_reviews = []
bad_reviews = []
for i in range(0,500): completion = openai.Completion.create(engine="davinci", prompt="This movie was great.",max_tokens=120) good_reviews.append(completion.choices[0]['text']) print('Generating good review number %i'%(i)) completion = openai.Completion.create(engine="davinci", prompt="This movie was terrible.",max_tokens=120) bad_reviews.append(completion.choices[0]['text']) print('Generating bad review number %i'%(i)) display = np.random.choice([0,1],p=[0.7,0.3]) time.sleep(3) if display ==1: display_good = np.random.choice([0,1],p=[0.5,0.5]) if display_good ==1: print('Printing random good review') print(good_reviews[-1]) if display_good ==0: print('Printing random bad review') print(bad_reviews[-1])
- Đưa thêm một hoặc vài sample trong training dataset vào để model học và viết theo (few-shot data)
def gen_data(df_column, sentiment, shots, engine="ada"): data = [] for i in range(len(df_column)): if shots == 1: prompt = f"The following are movie reviews with a {sentiment} sentiment. REVIEW: {df_column[i]} REVIEW:" response = openai.Completion.create(engine=engine, prompt=prompt, max_tokens=75, temperature=.8, top_p=1, n=20, stream=False, stop="REVIEW:") for i in response['choices']: data.append(i['text']) if shots == 2: prompt = f"The following are movie reviews with a {sentiment} sentiment. REVIEW: {df_column[i]} REVIEW: {df_column[i-1]} REVIEW:" response = openai.Completion.create(engine=engine, prompt=prompt, max_tokens=75, temperature=.8, top_p=1, n=20, stream=False, stop="REVIEW:") for i in response['choices']: data.append(i['text']) return data
Mô hình phân loại dùng dữ liệu gốc
Ở đây để tiện so sánh, mình chỉ đơn giản dùng TFIDF để vector hóa review text và mô hình Logistic Regression của sklearn
để phân loại.
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix,plot_confusion_matrix
from sklearn.feature_extraction.text import TfidfVectorizer
vectorizer = TfidfVectorizer (max_features=2500, min_df=7, max_df=0.8)
X1_train = vectorizer.fit_transform(imdb_dataset_train['review']).toarray()
X1_test = vectorizer.transform(imdb_dataset_test['review']).toarray()
y1_train = imdb_dataset_train["sentiment"]
y1_test = imdb_dataset_test["sentiment"]
clf = LogisticRegression(solver='lbfgs')
clf.fit(X1_train, y1_train)
clf.score(X1_test, y1_test)
Kết quả: mô hình đạt độ chính xác 73% khi train trên 100 sample từ dataset gốc.
Mô hình phân loại dùng dữ liệu sinh từ GPT-3
Lấy data từ file đã lưu trước đó
gen_data = pd.read_csv('generated_reviews.csv').drop(columns=['Unnamed: 0'])
gen_data.Sentiment = gen_data.Sentiment.astype(int)
gen_data = gen_data.dropna().reset_index()
vectorizer = TfidfVectorizer (max_features=2500, min_df=7, max_df=0.8)
tokenized_data = vectorizer.fit_transform(gen_data['Reviews']).toarray()
X1_test_1 = vectorizer.transform(imdb_dataset_test['review']).toarray()
labels = np.array(gen_data["Sentiment"]) # Label is already an array of 0 and 1 clf = LogisticRegression(solver='lbfgs')
clf.fit(tokenized_data, labels)
clf.score(X1_test_1, y1_test)
Kết quả: mô hình đạt độ chính xác 82% khi train trên 1000 câu tạo bởi mô hình GPT-3
Như vậy ta thấy việc sinh thêm dữ liệu tổng hợp cho kết quả khá khả quan.
Mô hình phân loại dùng data gốc + data sinh từ GPT-3
Ghép 2 bộ data gốc + augment:
new_dataset = pd.concat([imdb_dataset_train, gen_data], ignore_index = True, sort = False)
new_dataset = new_dataset.drop(['index'], axis = 1)
Vectorization + Classification
vectorizer = TfidfVectorizer (max_features=2500, min_df=7, max_df=0.8)
X2_train = vectorizer.fit_transform(new_dataset['Reviews']).toarray()
X2_test = vectorizer.transform(imdb_dataset_test['Reviews']).toarray()
y2_train = new_dataset["Sentiment"]
y2_test = y1_test clf = LogisticRegression(solver='lbfgs')
clf.fit(X2_train, y2_train)
clf.score(X2_test, y2_test)
Kết quả: mô hình đạt độ chính xác 84% khi train trên tập dữ liệu tổng hợp, tăng 11% so với mô hình ban đầu
Đây cũng là độ chính xác cao nhất đạt được trong 3 thử nghiệm.
Kết luận:
- Như vậy có thể thấy là việc tận dụng mô hình ngôn ngữ lớn như GPT-3 để sinh thêm dữ liệu cho bài toán NLP có đem lại hiệu quả. Nó giúp cải thiện mô hình ban đầu, đồng thời giúp chúng ta xây dựng được một mô hình có độ chính xác cao cho dù dữ liệu đầu vào ít hoặc thậm chí không có.
- Về chi phí: mình đã sinh 1000 câu review, mỗi câu khoảng 100 từ với chi phí khoảng 2 USD ~ 50k, chạy trong vòng 2 tiếng. Cũng khá là kinh tế đấy chứ
- Trong bài viết này mình chỉ sử dụng những setup cơ bản nhất, các bạn có thể thử các kết hợp khác (tinh chỉnh tham số, lựa chọn các engine khác, thêm training data vào prompt, thay đổi cách viết prompt) để tìm ra thiết lập hiệu quả nhất cho bài toán của mình
- Một nghiên cứu khá chi tiết các bạn có thể tham khảo về chủ đề này: https://www.ameliormate.com/gpt3-synthetic-data