trendyol-review-summarizer / scripts /review_summarizer_trendyol_llama.py
enesmanan's picture
fix gradio
b833f77 verified
raw
history blame
12.4 kB
import os
import re
import warnings
from collections import Counter
import matplotlib.pyplot as plt
import nltk
import numpy as np
import pandas as pd
import requests
import seaborn as sns
import torch
from nltk.tokenize import word_tokenize
from nltk.util import ngrams
from transformers import AutoModelForSequenceClassification, AutoTokenizer, pipeline
from wordcloud import WordCloud
warnings.filterwarnings("ignore")
nltk.download("stopwords")
nltk.download("punkt")
class ReviewAnalyzer:
def __init__(self):
self.turkish_stopwords = self.get_turkish_stopwords()
self.setup_sentiment_model()
self.setup_summary_model()
# Lojistik ve satıcı ile ilgili kelimeleri tanımla
self.logistics_seller_words = {
# Kargo ve teslimat ile ilgili
"kargo",
"kargocu",
"paket",
"paketleme",
"teslimat",
"teslim",
"gönderi",
"gönderim",
"ulaştı",
"ulaşım",
"geldi",
"kurye",
"dağıtım",
"hasarlı",
"hasar",
"kutu",
"ambalaj",
"zamanında",
"geç",
"hızlı",
"yavaş",
"günde",
"saatte",
# Satıcı ve mağaza ile ilgili
"satıcı",
"mağaza",
"sipariş",
"trendyol",
"tedarik",
"stok",
"garanti",
"fatura",
"iade",
"geri",
"müşteri",
"hizmet",
"destek",
"iletişim",
"şikayet",
"sorun",
"çözüm",
"hediye",
# Fiyat ve ödeme ile ilgili
"fiyat",
"ücret",
"para",
"bedava",
"ücretsiz",
"indirim",
"kampanya",
"taksit",
"ödeme",
"bütçe",
"hesap",
"kur",
# Zaman ile ilgili teslimat kelimeleri
"bugün",
"yarın",
"dün",
"hafta",
"gün",
"saat",
"süre",
"bekleme",
"gecikme",
"erken",
"geç",
}
def get_turkish_stopwords(self):
"""Genişletilmiş stop words listesini hazırla"""
github_url = "https://raw.githubusercontent.com/sgsinclair/trombone/master/src/main/resources/org/voyanttools/trombone/keywords/stop.tr.turkish-lucene.txt"
stop_words = set()
try:
response = requests.get(github_url)
if response.status_code == 200:
github_stops = set(
word.strip() for word in response.text.split("\n") if word.strip()
)
stop_words.update(github_stops)
except Exception as e:
print(f"GitHub'dan stop words çekilirken hata oluştu: {e}")
stop_words.update(set(nltk.corpus.stopwords.words("turkish")))
additional_stops = {
"bir",
"ve",
"çok",
"bu",
"de",
"da",
"için",
"ile",
"ben",
"sen",
"o",
"biz",
"siz",
"onlar",
"bu",
"şu",
"ama",
"fakat",
"ancak",
"lakin",
"ki",
"dahi",
"mi",
"mı",
"mu",
"mü",
"var",
"yok",
"olan",
"içinde",
"üzerinde",
"bana",
"sana",
"ona",
"bize",
"size",
"onlara",
"evet",
"hayır",
"tamam",
"oldu",
"olmuş",
"olacak",
"etmek",
"yapmak",
"kez",
"kere",
"defa",
"adet",
}
stop_words.update(additional_stops)
print(f"Toplam {len(stop_words)} adet stop words yüklendi.")
return stop_words
def preprocess_text(self, text):
"""Metin ön işleme"""
if isinstance(text, str):
# Küçük harfe çevir
text = text.lower()
# Özel karakterleri temizle
text = re.sub(r"[^\w\s]", "", text)
# Sayıları temizle
text = re.sub(r"\d+", "", text)
# Fazla boşlukları temizle
text = re.sub(r"\s+", " ", text).strip()
# Stop words'leri çıkar
words = text.split()
words = [word for word in words if word not in self.turkish_stopwords]
return " ".join(words)
return ""
def setup_sentiment_model(self):
"""Sentiment analiz modelini hazırla"""
self.device = "cuda" if torch.cuda.is_available() else "cpu"
print(f"Using device for sentiment: {self.device}")
model_name = "savasy/bert-base-turkish-sentiment-cased"
self.sentiment_tokenizer = AutoTokenizer.from_pretrained(model_name)
self.sentiment_model = (
AutoModelForSequenceClassification.from_pretrained(model_name)
.to(self.device)
.to(torch.float32)
)
def setup_summary_model(self):
"""Özet modelini hazırla"""
print("Loading Trendyol-LLM model...")
model_id = "Trendyol/Trendyol-LLM-8b-chat-v2.0"
self.summary_pipe = pipeline(
"text-generation",
model=model_id,
torch_dtype="auto",
device_map="auto",
)
self.terminators = [
self.summary_pipe.tokenizer.eos_token_id,
self.summary_pipe.tokenizer.convert_tokens_to_ids("<|eot_id|>"),
]
self.sampling_params = {
"do_sample": True,
"temperature": 0.3,
"top_k": 50,
"top_p": 0.9,
"repetition_penalty": 1.1,
}
def filter_reviews(self, df):
"""Ürün ile ilgili olmayan yorumları filtrele"""
def is_product_review(text):
if not isinstance(text, str):
return False
return not any(word in text.lower() for word in self.logistics_seller_words)
filtered_df = df[df["Yorum"].apply(is_product_review)].copy()
print(f"\nFiltreleme İstatistikleri:")
print(f"Toplam yorum sayısı: {len(df)}")
print(f"Ürün yorumu sayısı: {len(filtered_df)}")
print(f"Filtrelenen yorum sayısı: {len(df) - len(filtered_df)}")
print(
f"Filtreleme oranı: {((len(df) - len(filtered_df)) / len(df) * 100):.2f}%"
)
return filtered_df
def analyze_sentiment(self, df):
"""Sentiment analizi yap"""
def predict_sentiment(text):
if not isinstance(text, str) or len(text.strip()) == 0:
return {"label": "Nötr", "score": 0.5}
try:
cleaned_text = self.preprocess_text(text)
inputs = self.sentiment_tokenizer(
cleaned_text,
return_tensors="pt",
truncation=True,
max_length=512,
padding=True,
).to(self.device)
with torch.no_grad():
outputs = self.sentiment_model(**inputs)
probs = torch.nn.functional.softmax(outputs.logits, dim=1)
prediction = probs.cpu().numpy()[0]
score = float(prediction[1])
if score > 0.75:
label = "Pozitif"
elif score < 0.25:
label = "Negatif"
elif score > 0.55:
label = "Pozitif"
elif score < 0.45:
label = "Negatif"
else:
label = "Nötr"
return {"label": label, "score": score}
except Exception as e:
print(f"Error in sentiment prediction: {e}")
return {"label": "Nötr", "score": 0.5}
print("\nSentiment analizi yapılıyor...")
results = [predict_sentiment(text) for text in df["Yorum"]]
df["sentiment_score"] = [r["score"] for r in results]
df["sentiment_label"] = [r["label"] for r in results]
df["cleaned_text"] = df["Yorum"].apply(self.preprocess_text)
return df
def get_key_phrases(self, text_series):
"""En önemli anahtar kelimeleri bul"""
text = " ".join(text_series.astype(str))
words = self.preprocess_text(text).split()
word_freq = Counter(words)
# En az 3 kez geçen kelimeleri al
return {
word: count
for word, count in word_freq.items()
if count >= 3 and len(word) > 2
}
def generate_summary(self, df):
"""Yorumların genel özetini oluştur"""
# En önemli yorumları seç
high_rated = df[df["Yıldız Sayısı"] >= 4]
low_rated = df[df["Yıldız Sayısı"] <= 2]
# Önemli kelimeleri bul
positive_phrases = self.get_key_phrases(high_rated["cleaned_text"])
negative_phrases = self.get_key_phrases(low_rated["cleaned_text"])
# En anlamlı yorumları seç
top_positive = (
high_rated.sort_values("sentiment_score", ascending=False)["Yorum"]
.head(3)
.tolist()
)
top_negative = (
low_rated.sort_values("sentiment_score")["Yorum"].head(2).tolist()
)
# En sık kullanılan kelimeler
pos_features = ", ".join(
[f"{word} ({count})" for word, count in list(positive_phrases.items())[:5]]
)
neg_features = ", ".join(
[f"{word} ({count})" for word, count in list(negative_phrases.items())[:5]]
)
summary_prompt = f"""
MacBook Air Kullanıcı Yorumları Analizi:
İSTATİSTİKLER:
- Toplam Yorum: {len(df)}
- Ortalama Puan: {df['Yıldız Sayısı'].mean():.1f}/5
- Pozitif Yorum Oranı: {(len(df[df['sentiment_label'] == 'Pozitif']) / len(df) * 100):.1f}%
SIKÇA KULLANILAN KELİMELER:
Olumlu: {pos_features}
Olumsuz: {neg_features}
ÖRNEK OLUMLU YORUMLAR:
{' '.join([f"• {yorum[:200]}..." for yorum in top_positive])}
ÖRNEK OLUMSUZ YORUMLAR:
{' '.join([f"• {yorum[:200]}..." for yorum in top_negative])}
Lütfen bu veriler ışığında bu ürün için kısa ve öz bir değerlendirme yap.
Özellikle kullanıcıların en çok beğendiği özellikler ve en sık dile getirilen sorunlara odaklan.
Değerlendirmeyi 3 paragrafla sınırla ve somut örnekler kullan.
"""
messages = [
{
"role": "system",
"content": "Sen bir ürün yorumları analiz uzmanısın. Yorumları özetlerken nesnel ve açık ol.",
},
{"role": "user", "content": summary_prompt},
]
outputs = self.summary_pipe(
messages,
max_new_tokens=512,
eos_token_id=self.terminators,
return_full_text=False,
**self.sampling_params,
)
return outputs[0]["generated_text"]
def analyze_reviews(file_path):
df = pd.read_csv(file_path)
analyzer = ReviewAnalyzer()
filtered_df = analyzer.filter_reviews(df)
print("Sentiment analizi başlatılıyor...")
analyzed_df = analyzer.analyze_sentiment(filtered_df)
analyzed_df.to_csv(
"sentiment_analyzed_reviews.csv", index=False, encoding="utf-8-sig"
)
print("Sentiment analizi tamamlandı ve kaydedildi.")
print("\nÜrün özeti oluşturuluyor...")
summary = analyzer.generate_summary(analyzed_df)
with open("urun_ozeti.txt", "w", encoding="utf-8") as f:
f.write(summary)
print("\nÜrün Özeti:")
print("-" * 50)
print(summary)
print("\nÖzet 'urun_ozeti.txt' dosyasına kaydedildi.")
if __name__ == "__main__":
analyze_reviews("data/macbook_product_comments_with_ratings.csv")