Spaces:
Runtime error
Runtime error
| 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") | |