trendyol-review-summarizer / scripts /review_summarizer.py
enesmanan's picture
Upload 7 files
66f5d71 verified
raw
history blame
11 kB
import pandas as pd
import numpy as np
from transformers import (
AutoTokenizer,
AutoModelForSequenceClassification
)
import torch
import os
import requests
from collections import Counter
import warnings
from nltk.tokenize import word_tokenize
import nltk
import re
import google.generativeai as genai
from dotenv import load_dotenv
import logging
warnings.filterwarnings('ignore')
# NLTK indirmelerini try-except bloğuna alalım
try:
nltk.download('stopwords', quiet=True)
nltk.download('punkt', quiet=True)
except:
print("NLTK dosyaları indirilemedi, devam ediliyor...")
logger = logging.getLogger(__name__)
class ReviewAnalyzer:
def __init__(self):
try:
# Load environment variables
load_dotenv()
# Configure Gemini API
api_key = os.getenv('GOOGLE_API_KEY')
if not api_key:
raise ValueError("GOOGLE_API_KEY bulunamadı")
genai.configure(api_key=api_key)
self.model = genai.GenerativeModel('gemini-pro')
# Sentiment model kurulumu
self.setup_sentiment_model()
# Stop words yükleme
self.turkish_stopwords = self.get_turkish_stopwords()
logger.info("ReviewAnalyzer başarıyla başlatıldı")
except Exception as e:
logger.error(f"ReviewAnalyzer başlatılırken hata: {str(e)}")
raise
# 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"""
try:
self.device = "cuda" if torch.cuda.is_available() else "cpu"
logger.info(f"Using device for sentiment: {self.device}")
model_name = "savasy/bert-base-turkish-sentiment-cased"
logger.info(f"Tokenizer yükleniyor: {model_name}")
self.sentiment_tokenizer = AutoTokenizer.from_pretrained(model_name)
logger.info(f"Model yükleniyor: {model_name}")
self.sentiment_model = (
AutoModelForSequenceClassification.from_pretrained(model_name)
.to(self.device)
.to(torch.float32)
)
logger.info("Sentiment model başarıyla yüklendi")
except Exception as e:
logger.error(f"Sentiment model kurulumunda hata: {str(e)}", exc_info=True)
raise
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ı özetle"""
# Yorumları ve yıldızları birleştir
reviews_with_ratings = [
f"Yıldız: {row['Yıldız Sayısı']}, Yorum: {row['Yorum']}"
for _, row in df.iterrows()
]
# Prompt hazırla
prompt = f"""
Aşağıdaki ürün yorumlarını analiz edip özet çıkar:
{reviews_with_ratings[:50]} # İlk 50 yorumu al (API limiti için)
Lütfen şu başlıklar altında özetle:
1. Genel Değerlendirme
2. Olumlu Yönler
3. Olumsuz Yönler
4. Öneriler
Önemli: Yanıtını Türkçe olarak ver ve madde madde listele.
"""
try:
response = self.model.generate_content(prompt)
summary = response.text
except Exception as e:
summary = f"Özet oluşturulurken hata oluştu: {str(e)}"
return summary
def analyze_reviews(self, df):
"""Tüm yorumları analiz et"""
try:
# Yorumları filtrele
filtered_df = self.filter_reviews(df)
# Sentiment analizi yap
analyzed_df = self.analyze_sentiment(filtered_df)
return analyzed_df
except Exception as e:
print(f"Analiz sırasında hata oluştu: {str(e)}")
return pd.DataFrame()
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')