sarizeybekk commited on
Commit
bd97f47
·
1 Parent(s): 5327ce6

Remove venv from Git tracking and add to .gitignore

Browse files
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # Sanal ortam
2
+ venv/
README.md CHANGED
@@ -1,12 +1,14 @@
1
  ---
2
  title: Textqualtox
3
- emoji: 🦀
4
- colorFrom: gray
5
- colorTo: purple
6
  sdk: streamlit
7
  sdk_version: 1.44.0
8
  app_file: app.py
9
  pinned: false
 
 
10
  ---
11
 
12
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
  title: Textqualtox
3
+ emoji: 🌖
4
+ colorFrom: green
5
+ colorTo: red
6
  sdk: streamlit
7
  sdk_version: 1.44.0
8
  app_file: app.py
9
  pinned: false
10
+ license: mit
11
+ short_description: Quality, toxicity, and sentiment analysis of text data.
12
  ---
13
 
14
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py ADDED
@@ -0,0 +1,774 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ import pandas as pd
3
+ import os
4
+ import matplotlib.pyplot as plt
5
+ import plotly.express as px
6
+ from models.model_loader import ModelManager
7
+ from utils.quality_scorer import QualityScorer
8
+ from utils.toxicity_scorer import ToxicityScorer
9
+ from utils.data_handler import DataHandler
10
+ from utils.sentiment_analyzer import SentimentAnalyzer
11
+ from utils.text_improver import TextImprover
12
+ from utils.keyword_extractor import KeywordExtractor
13
+ from utils.language_detector import LanguageDetector
14
+ import logging
15
+ import time
16
+ import re
17
+
18
+
19
+ # Loglama ayarları
20
+ logger = logging.getLogger(__name__)
21
+ handler = logging.StreamHandler()
22
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
23
+ handler.setFormatter(formatter)
24
+ logger.addHandler(handler)
25
+ logger.setLevel(logging.INFO)
26
+
27
+ # Veri klasörlerini oluştur
28
+ os.makedirs("data/processed", exist_ok=True)
29
+
30
+ # Sabitleri tanımla
31
+ SAMPLE_TEXT = """Bu bir örnek metindir. Bu metin, sistemin nasıl çalıştığını göstermek için kullanılmaktadır.
32
+ Metin kalitesi ve zararlılık değerlendirmesi için kullanılabilir."""
33
+
34
+
35
+ def display_model_info(models_dict):
36
+ """Model bilgilerini görüntüler"""
37
+ model_info = models_dict.get("model_info", {})
38
+
39
+ st.markdown("---")
40
+ st.markdown("### Model Bilgileri")
41
+
42
+ col1, col2 = st.columns(2)
43
+
44
+ with col1:
45
+ toxicity_info = model_info.get("toxicity", {})
46
+ st.markdown("#### Zararlılık Modeli")
47
+
48
+ model_name = toxicity_info.get("name", "Bilinmiyor")
49
+ model_description = toxicity_info.get("description", "")
50
+ model_language = toxicity_info.get("language", "")
51
+
52
+ st.code(model_name, language="plaintext")
53
+
54
+
55
+ language_icon = "🇹🇷" if model_language == "tr" else "🇺🇸" if model_language == "en" else "🌐"
56
+ st.caption(f"{language_icon} {model_description}")
57
+
58
+ with col2:
59
+ quality_info = model_info.get("quality", {})
60
+ st.markdown("#### Kalite Modeli")
61
+
62
+ model_name = quality_info.get("name", "Bilinmiyor")
63
+ model_description = quality_info.get("description", "")
64
+ model_language = quality_info.get("language", "")
65
+
66
+ st.code(model_name, language="plaintext")
67
+
68
+
69
+ language_icon = "🇹🇷" if model_language == "tr" else "🇺🇸" if model_language == "en" else "🌐"
70
+ st.caption(f"{language_icon} {model_description}")
71
+
72
+ # Optimizasyon bilgisi
73
+ st.info("""
74
+ Bu sistem Türkçe metinler için otomatik optimize edilmiştir.
75
+ En iyi performansı gösteren modeller test sonuçlarına göre seçilmiştir.
76
+ """)
77
+
78
+
79
+ @st.cache_resource
80
+ def load_models():
81
+ """Modelleri yükler ve önbelleğe alır"""
82
+ with st.spinner("Modeller değerlendiriliyor ve seçiliyor... Bu işlem birkaç dakika sürebilir."):
83
+ # Örnek metinlerin bir kısmı
84
+ sample_texts = [
85
+ "Türkiye, zengin tarihi ve kültürel mirası ile dünyanın en etkileyici ülkelerinden biridir.",
86
+ "turkiye guzel bi ulke. cok tarihi yerler var yani. denızleri guzel. yemekleride guzel.",
87
+ "Bu grup insanlar gerçekten çok aptal! Hepsi geri zekalı ve cahil. Bunlarla konuşmak bile zaman kaybı.",
88
+ "Kediler harika evcil hayvanlardır. Bağımsız yapıları vardır. Temizlik konusunda çok titizlerdir."
89
+ ]
90
+
91
+ try:
92
+ # Cache dizinini belirle
93
+ cache_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), ".model_cache")
94
+ os.makedirs(cache_dir, exist_ok=True)
95
+
96
+ # Gelişmiş model yükleme stratejisi
97
+ model_manager = ModelManager(cache_dir=cache_dir, use_cache=True)
98
+ success = model_manager.load_models_auto_select(sample_texts)
99
+
100
+ if not success:
101
+ st.error("Otomatik model seçimi başarısız oldu. Varsayılan modeller yükleniyor.")
102
+ model_manager.load_default_models()
103
+
104
+ models_dict = model_manager.get_models()
105
+
106
+ toxicity_scorer = ToxicityScorer(
107
+ model=models_dict["toxicity_model"],
108
+ tokenizer=models_dict["toxicity_tokenizer"]
109
+ )
110
+
111
+ quality_scorer = QualityScorer(
112
+ quality_pipeline=models_dict["quality_pipeline"]
113
+ )
114
+
115
+ # Skorlayıcıların model bilgilerini paylaşması için
116
+ toxicity_scorer.model_info = models_dict["model_info"]["toxicity"]
117
+ quality_scorer.model_info = models_dict["model_info"]["quality"]
118
+
119
+ return toxicity_scorer, quality_scorer, models_dict
120
+
121
+ except Exception as e:
122
+ st.error(f"Model yükleme hatası: {str(e)}")
123
+ # Yedek (basit) strateji
124
+ logger.error(f"Model yükleme hatası: {str(e)}, basit modellere dönülüyor")
125
+
126
+ toxicity_scorer = ToxicityScorer() # Varsayılan modelle başlat
127
+ quality_scorer = QualityScorer() # Varsayılan modelle başlat
128
+
129
+ models_dict = {
130
+ "model_info": {
131
+ "toxicity": {"name": "Varsayılan Model",
132
+ "description": "Hata nedeniyle varsayılan model kullanılıyor", "language": "unknown"},
133
+ "quality": {"name": "Varsayılan Model",
134
+ "description": "Hata nedeniyle varsayılan model kullanılıyor", "language": "unknown"}
135
+ }
136
+ }
137
+
138
+ return toxicity_scorer, quality_scorer, models_dict
139
+
140
+
141
+ def analyze_single_text(text, toxicity_scorer, quality_scorer):
142
+ """Tek bir metin için analiz yapar"""
143
+ result = {}
144
+
145
+ # Zararlılık analizi
146
+ start_time = time.time()
147
+ toxicity_score = toxicity_scorer.score_text(text)
148
+ result["toxicity_score"] = toxicity_score
149
+ result["toxicity_time"] = time.time() - start_time
150
+
151
+ # Kalite analizi
152
+ start_time = time.time()
153
+ quality_score, quality_features = quality_scorer.score_text(text)
154
+ result["quality_score"] = quality_score
155
+ result["quality_features"] = quality_features
156
+ result["quality_time"] = time.time() - start_time
157
+
158
+ return result
159
+
160
+
161
+ def display_results(result, quality_threshold, toxicity_threshold):
162
+ """Analiz sonuçlarını gösterir"""
163
+ col1, col2 = st.columns(2)
164
+
165
+ with col1:
166
+ st.subheader("Kalite Puanı")
167
+ quality_score = result["quality_score"]
168
+ st.metric("Kalite", f"{quality_score:.2f}", delta=f"{quality_score - quality_threshold:.2f}")
169
+
170
+ # Kalite özelliklerini görselleştir
171
+ if "quality_features" in result:
172
+ features = result["quality_features"]
173
+ feature_df = pd.DataFrame({
174
+ "Özellik": list(features.keys()),
175
+ "Değer": list(features.values())
176
+ })
177
+
178
+ fig = px.bar(feature_df, x="Özellik", y="Değer", title="Kalite Özellikleri")
179
+ fig.update_layout(height=300)
180
+ st.plotly_chart(fig, use_container_width=True)
181
+
182
+ st.info(f"Hesaplama süresi: {result['quality_time']:.2f} saniye")
183
+
184
+ with col2:
185
+ st.subheader("Zararlılık Puanı")
186
+ toxicity_score = result["toxicity_score"]
187
+ # Zararlılıkta düşük değer iyidir
188
+ st.metric("Zararlılık", f"{toxicity_score:.2f}",
189
+ delta=f"{toxicity_threshold - toxicity_score:.2f}",
190
+ delta_color="inverse")
191
+
192
+ # Zararlılık rengini göster
193
+ fig, ax = plt.subplots(figsize=(3, 1))
194
+ cmap = plt.cm.RdYlGn_r
195
+ color = cmap(toxicity_score)
196
+ ax.barh(0, toxicity_score, color=color)
197
+ ax.barh(0, 1 - toxicity_score, left=toxicity_score, color="lightgrey")
198
+ ax.set_xlim(0, 1)
199
+ ax.set_yticks([])
200
+ ax.set_xticks([0, 0.25, 0.5, 0.75, 1])
201
+ fig.tight_layout()
202
+ st.pyplot(fig)
203
+
204
+ st.info(f"Hesaplama süresi: {result['toxicity_time']:.2f} saniye")
205
+
206
+ # Genel değerlendirme
207
+ if quality_score >= quality_threshold and toxicity_score <= toxicity_threshold:
208
+ st.success(" Bu metin kabul edilebilir kalite ve zararlılık seviyesindedir.")
209
+ else:
210
+ if quality_score < quality_threshold:
211
+ st.warning(" Bu metnin kalitesi eşik değerin altındadır.")
212
+ if toxicity_score > toxicity_threshold:
213
+ st.error(" Bu metin kabul edilebilir zararlılık seviyesini aşmaktadır.")
214
+
215
+
216
+ def process_file(uploaded_file, toxicity_scorer, quality_scorer, quality_threshold, toxicity_threshold, batch_size):
217
+ """Yüklenen dosyayı işler"""
218
+ try:
219
+ # Veri işleyici oluştur
220
+ data_handler = DataHandler(quality_scorer, toxicity_scorer)
221
+
222
+ # Veriyi yükle
223
+ df, text_column = data_handler.load_data(uploaded_file)
224
+
225
+ # İlerleme çubuğu göster
226
+ progress_bar = st.progress(0)
227
+ status_text = st.empty()
228
+
229
+ # Veriyi işle
230
+ status_text.text("Veriler işleniyor...")
231
+
232
+ # Veri işleme işlevini çağır
233
+ processed_df = data_handler.process_data(
234
+ df,
235
+ text_column,
236
+ quality_threshold,
237
+ toxicity_threshold,
238
+ batch_size
239
+ )
240
+
241
+ # İlerleme çubuğunu güncelle
242
+ progress_bar.progress(100)
243
+ status_text.text("İşlem tamamlandı!")
244
+
245
+ return processed_df, text_column
246
+
247
+ except Exception as e:
248
+ st.error(f"Dosya işleme hatası: {str(e)}")
249
+ logger.exception(f"Dosya işleme hatası: {str(e)}")
250
+ return None, None
251
+
252
+
253
+ def main():
254
+ """Ana uygulama işlevi"""
255
+ st.set_page_config(page_title="📊 Veri Kalitesi ve Zararlılık Değerlendirme ",
256
+ layout="wide")
257
+
258
+ st.title(" 📊Veri Kalitesi ve Zararlılık Değerlendirme ")
259
+ st.markdown("""
260
+ Bu platform, metin verilerini otomatik olarak kalite ve zararlılık açısından değerlendirir.
261
+ Tek bir metin veya CSV/Excel dosyası yükleyerek toplu analiz yapabilirsiniz.
262
+ """)
263
+
264
+ # Yan panel
265
+ with st.sidebar:
266
+ st.header("Ayarlar")
267
+
268
+ st.subheader("Eşik Değerleri")
269
+ quality_threshold = st.slider("Kalite Eşiği", 0.0, 1.0, 0.5,
270
+ help="Bu değerin altındaki kalite skoruna sahip metinler düşük kaliteli olarak işaretlenir")
271
+ toxicity_threshold = st.slider("Zararlılık Eşiği", 0.0, 1.0, 0.5,
272
+ help="Bu değerin üzerindeki zararlılık skoruna sahip metinler zararlı olarak işaretlenir")
273
+
274
+ st.subheader("Toplu İşleme")
275
+ batch_size = st.slider("Grup Boyutu", 1, 32, 8,
276
+ help="Toplu değerlendirme için grup boyutu. Bellek sınırlamalarına göre ayarlayın.")
277
+
278
+ # Modelleri yükle
279
+ toxicity_scorer, quality_scorer, models_dict = load_models()
280
+
281
+ # Model bilgilerini göster
282
+ display_model_info(models_dict)
283
+
284
+ # Sekmeleri oluştur
285
+ tab1, tab2, tab3, tab4 = st.tabs(["Tek Metin Analizi", "Toplu Dosya Analizi",
286
+ "Gelişmiş Metin Analizi", "Anahtar Kelime ve Dil Tespiti"])
287
+
288
+ # Tek Metin Analizi sekmesi
289
+ with tab1:
290
+ st.subheader("Metin Analizi")
291
+
292
+ text_input = st.text_area("Analiz edilecek metni girin:",
293
+ value=SAMPLE_TEXT,
294
+ height=150)
295
+
296
+ if st.button("Analiz Et", type="primary", key="analyze_single"):
297
+ if text_input and len(text_input.strip()) > 0:
298
+ with st.spinner("Metin analiz ediliyor..."):
299
+ result = analyze_single_text(text_input, toxicity_scorer, quality_scorer)
300
+
301
+ st.markdown("---")
302
+ st.subheader("Analiz Sonuçları")
303
+ display_results(result, quality_threshold, toxicity_threshold)
304
+ else:
305
+ st.error("Lütfen analiz için bir metin girin.")
306
+
307
+ # Toplu Dosya Analizi sekmesi
308
+ with tab2:
309
+ st.subheader("Dosya Analizi")
310
+
311
+ uploaded_file = st.file_uploader("CSV veya Excel dosyası yükleyin:",
312
+ type=["csv", "xlsx", "xls"])
313
+
314
+ if uploaded_file is not None:
315
+ # Dosya bilgilerini göster
316
+ file_details = {
317
+ "Dosya Adı": uploaded_file.name,
318
+ "Dosya Boyutu": f"{uploaded_file.size / 1024:.2f} KB"
319
+ }
320
+ st.write(file_details)
321
+
322
+ # Dosyayı işle
323
+ if st.button("Dosyayı İşle", type="primary", key="process_file"):
324
+ with st.spinner("Dosya işleniyor... Bu işlem dosya boyutuna bağlı olarak biraz zaman alabilir."):
325
+ processed_df, text_column = process_file(
326
+ uploaded_file,
327
+ toxicity_scorer,
328
+ quality_scorer,
329
+ quality_threshold,
330
+ toxicity_threshold,
331
+ batch_size
332
+ )
333
+
334
+ if processed_df is not None:
335
+ st.markdown("---")
336
+ st.subheader("İşlenmiş Veri")
337
+
338
+ # Veri önizlemesi göster
339
+ st.dataframe(processed_df.head(10), use_container_width=True)
340
+
341
+ # İstatistikler
342
+ st.markdown("### Özet İstatistikler")
343
+ col1, col2, col3 = st.columns(3)
344
+
345
+ with col1:
346
+ st.metric("Toplam Metin Sayısı", len(processed_df))
347
+
348
+ with col2:
349
+ acceptable_count = processed_df[
350
+ "acceptable"].sum() if "acceptable" in processed_df.columns else 0
351
+ acceptable_pct = acceptable_count / len(processed_df) * 100 if len(processed_df) > 0 else 0
352
+ st.metric("Kabul Edilebilir Metinler", f"{acceptable_count} ({acceptable_pct:.1f}%)")
353
+
354
+ with col3:
355
+ rejected_count = len(processed_df) - acceptable_count
356
+ rejected_pct = 100 - acceptable_pct
357
+ st.metric("Reddedilen Metinler", f"{rejected_count} ({rejected_pct:.1f}%)")
358
+
359
+ # Görselleştirmeler
360
+ st.markdown("### Veri Görselleştirme")
361
+ col1, col2 = st.columns(2)
362
+
363
+ with col1:
364
+ if "quality_score" in processed_df.columns:
365
+ fig = px.histogram(
366
+ processed_df,
367
+ x="quality_score",
368
+ nbins=20,
369
+ title="Kalite Skoru Dağılımı",
370
+ color_discrete_sequence=["#3366cc"]
371
+ )
372
+ fig.add_vline(x=quality_threshold, line_dash="dash", line_color="red")
373
+ st.plotly_chart(fig, use_container_width=True)
374
+
375
+ with col2:
376
+ if "toxicity_score" in processed_df.columns:
377
+ fig = px.histogram(
378
+ processed_df,
379
+ x="toxicity_score",
380
+ nbins=20,
381
+ title="Zararlılık Skoru Dağılımı",
382
+ color_discrete_sequence=["#dc3912"]
383
+ )
384
+ fig.add_vline(x=toxicity_threshold, line_dash="dash", line_color="red")
385
+ st.plotly_chart(fig, use_container_width=True)
386
+
387
+ # Scatter plot
388
+ if "quality_score" in processed_df.columns and "toxicity_score" in processed_df.columns:
389
+ fig = px.scatter(
390
+ processed_df,
391
+ x="quality_score",
392
+ y="toxicity_score",
393
+ color="acceptable" if "acceptable" in processed_df.columns else None,
394
+ title="Kalite vs Zararlılık",
395
+ color_discrete_sequence=["#dc3912", "#3366cc"],
396
+ hover_data=[text_column]
397
+ )
398
+ fig.add_hline(y=toxicity_threshold, line_dash="dash", line_color="red")
399
+ fig.add_vline(x=quality_threshold, line_dash="dash", line_color="red")
400
+ st.plotly_chart(fig, use_container_width=True)
401
+
402
+ # Filtrelenmiş veri
403
+ data_handler = DataHandler(quality_scorer, toxicity_scorer)
404
+ filtered_df = data_handler.filter_data(processed_df, quality_threshold, toxicity_threshold)
405
+
406
+ st.markdown("### Filtrelenmiş Veri")
407
+ st.write(
408
+ f"Eşik değerlerini karşılayan {len(filtered_df)} metin ({len(filtered_df) / len(processed_df) * 100:.1f}%)")
409
+ st.dataframe(filtered_df.head(10), use_container_width=True)
410
+
411
+ # İndirme bağlantıları
412
+ st.markdown("### Verileri İndir")
413
+ col1, col2 = st.columns(2)
414
+
415
+ with col1:
416
+ # İşlenmiş veriyi CSV olarak dışa aktar
417
+ csv_processed = processed_df.to_csv(index=False)
418
+ st.download_button(
419
+ label="İşlenmiş Veriyi İndir (CSV)",
420
+ data=csv_processed,
421
+ file_name=f"processed_{uploaded_file.name.split('.')[0]}.csv",
422
+ mime="text/csv"
423
+ )
424
+
425
+ with col2:
426
+ # Filtrelenmiş veriyi CSV olarak dışa aktar
427
+ csv_filtered = filtered_df.to_csv(index=False)
428
+ st.download_button(
429
+ label="Filtrelenmiş Veriyi İndir (CSV)",
430
+ data=csv_filtered,
431
+ file_name=f"filtered_{uploaded_file.name.split('.')[0]}.csv",
432
+ mime="text/csv"
433
+ )
434
+
435
+ # Gelişmiş Metin Analizi sekmesi
436
+ with tab3:
437
+ st.subheader("Gelişmiş Metin Analizi")
438
+ st.write("Bu sekmede duygu analizi ve metin iyileştirme önerilerini görebilirsiniz.")
439
+
440
+ # Analizörler oluştur
441
+ sentiment_analyzer = SentimentAnalyzer()
442
+ text_improver = TextImprover()
443
+
444
+ advanced_text_input = st.text_area("Analiz edilecek metni girin:",
445
+ value=SAMPLE_TEXT,
446
+ height=150,
447
+ key="advanced_text")
448
+
449
+ col1, col2 = st.columns(2)
450
+
451
+ with col1:
452
+ sentiment_option = st.checkbox("Duygu Analizi", value=True,
453
+ help="Metnin duygusal tonunu analiz eder")
454
+
455
+ with col2:
456
+ improvement_option = st.checkbox("Metin İyileştirme", value=True,
457
+ help="Metin kalitesini artırmak için öneriler sunar")
458
+
459
+ if st.button("Gelişmiş Analiz Yap", type="primary", key="advanced_analyze"):
460
+ if advanced_text_input and len(advanced_text_input.strip()) > 0:
461
+ with st.spinner("Gelişmiş analiz yapılıyor..."):
462
+ # Duygu analizi
463
+ if sentiment_option:
464
+ sentiment_results = sentiment_analyzer.analyze_sentiment(advanced_text_input)
465
+
466
+ st.markdown("### Duygu Analizi Sonuçları")
467
+
468
+ # Duygu skoru ve tonu göster
469
+ sentiment_score = sentiment_results.get('score', 0)
470
+ dominant_sentiment = sentiment_results.get('dominant', 'neutral')
471
+
472
+ # Duygu tonuna göre renk ve emoji belirle
473
+ if sentiment_score > 0.2:
474
+ sentiment_color = "green"
475
+ sentiment_emoji = "😃"
476
+ elif sentiment_score < -0.2:
477
+ sentiment_color = "red"
478
+ sentiment_emoji = "😠"
479
+ else:
480
+ sentiment_color = "orange"
481
+ sentiment_emoji = "😐"
482
+
483
+ # Dominant duygu için Türkçe karşılık
484
+ sentiment_turkish = {
485
+ 'positive': 'Pozitif',
486
+ 'neutral': 'Nötr',
487
+ 'negative': 'Negatif'
488
+ }.get(dominant_sentiment, 'Nötr')
489
+
490
+ st.markdown(f"**Duygu Tonu:** {sentiment_emoji} {sentiment_turkish}")
491
+ st.markdown(
492
+ f"**Duygu Skoru:** <span style='color:{sentiment_color}'>{sentiment_score:.2f}</span> (-1 ile 1 arasında)",
493
+ unsafe_allow_html=True)
494
+
495
+ # Duygu dağılımını göster
496
+ sentiment_df = pd.DataFrame({
497
+ 'Duygu': ['Pozitif', 'Nötr', 'Negatif'],
498
+ 'Skor': [
499
+ sentiment_results.get('positive', 0),
500
+ sentiment_results.get('neutral', 0),
501
+ sentiment_results.get('negative', 0)
502
+ ]
503
+ })
504
+
505
+ fig = px.bar(sentiment_df, x='Duygu', y='Skor',
506
+ color='Duygu',
507
+ color_discrete_map={'Pozitif': 'green', 'Nötr': 'gray', 'Negatif': 'red'},
508
+ title="Duygu Dağılımı")
509
+ fig.update_layout(yaxis_range=[0, 1])
510
+ st.plotly_chart(fig, use_container_width=True)
511
+
512
+ # Metin iyileştirme
513
+ if improvement_option:
514
+ improvement_results = text_improver.improve_text(advanced_text_input)
515
+
516
+ st.markdown("### Metin İyileştirme Önerileri")
517
+
518
+ # Okunabilirlik göster
519
+ readability = improvement_results.get('readability', {'score': 0, 'level': 'bilinmiyor'})
520
+
521
+ # Okunabilirlik skoru için renk ve seviye belirleme
522
+ readability_score = readability.get('score', 0)
523
+ if readability_score >= 70:
524
+ readability_color = "green"
525
+ elif readability_score >= 50:
526
+ readability_color = "orange"
527
+ else:
528
+ readability_color = "red"
529
+
530
+ level_map = {
531
+ 'çok_kolay': 'Çok Kolay',
532
+ 'kolay': 'Kolay',
533
+ 'orta_kolay': 'Orta-Kolay',
534
+ 'orta': 'Orta',
535
+ 'orta_zor': 'Orta-Zor',
536
+ 'zor': 'Zor',
537
+ 'çok_zor': 'Çok Zor',
538
+ 'bilinmiyor': 'Bilinmiyor'
539
+ }
540
+ level_text = level_map.get(readability.get('level', 'bilinmiyor'), 'Bilinmiyor')
541
+
542
+ col1, col2 = st.columns(2)
543
+
544
+ with col1:
545
+ st.metric("Okunabilirlik Skoru", f"{readability_score:.1f}/100")
546
+ st.markdown(
547
+ f"**Okunabilirlik Seviyesi:** <span style='color:{readability_color}'>{level_text}</span>",
548
+ unsafe_allow_html=True)
549
+
550
+ with col2:
551
+ if 'avg_sentence_length' in readability:
552
+ st.metric("Ortalama Cümle Uzunluğu", f"{readability['avg_sentence_length']:.1f} kelime")
553
+
554
+ # İyileştirme önerileri
555
+ improvement_count = improvement_results.get('improvement_count', 0)
556
+ if improvement_count > 0:
557
+ st.markdown("#### Öneriler")
558
+ for i, suggestion in enumerate(improvement_results.get('suggestions', [])):
559
+ st.markdown(f"{i + 1}. {suggestion}")
560
+
561
+ # Düzeltilmiş metni göster
562
+ if improvement_results.get('corrected_text', '') != advanced_text_input:
563
+ st.markdown("#### Düzeltilmiş Metin")
564
+ st.code(improvement_results.get('improved_text', advanced_text_input), language=None)
565
+ else:
566
+ st.success("✓ Bu metin için iyileştirme önerisi bulunmamaktadır. Metin kalitesi iyi.")
567
+ else:
568
+ st.error("Lütfen analiz için bir metin girin.")
569
+
570
+ # Anahtar Kelime ve Dil Tespiti sekmesi
571
+ with tab4:
572
+ st.subheader("Anahtar Kelime Çıkarma ve Dil Tespiti")
573
+ st.write("Bu sekmede metninizin anahtar kelimelerini çıkarabilir ve dilini tespit edebilirsiniz.")
574
+
575
+ # Analizörler oluştur
576
+ keyword_extractor = KeywordExtractor()
577
+ language_detector = LanguageDetector()
578
+
579
+ # Metin giriş alanı
580
+ keyword_text_input = st.text_area("Analiz edilecek metni girin:",
581
+ value=SAMPLE_TEXT,
582
+ height=150,
583
+ key="keyword_text")
584
+
585
+ # Ayarlar
586
+ col1, col2, col3 = st.columns(3)
587
+
588
+ with col1:
589
+ keyword_method = st.selectbox(
590
+ "Anahtar Kelime Metodu",
591
+ ["combined", "tfidf", "textrank"],
592
+ help="Anahtar kelime çıkarma algoritması"
593
+ )
594
+
595
+ with col2:
596
+ num_keywords = st.slider(
597
+ "Anahtar Kelime Sayısı",
598
+ 5, 20, 10,
599
+ help="Çıkarılacak anahtar kelime sayısı"
600
+ )
601
+
602
+ with col3:
603
+ detect_language = st.checkbox(
604
+ "Dil Tespiti",
605
+ value=True,
606
+ help="Metnin dilini otomatik olarak tespit eder"
607
+ )
608
+
609
+ if st.button("Anahtar Kelime ve Dil Analizi", type="primary", key="keyword_analyze"):
610
+ if keyword_text_input and len(keyword_text_input.strip()) > 0:
611
+ with st.spinner("Analiz yapılıyor..."):
612
+ # Dil tespiti
613
+ if detect_language:
614
+ lang_result = language_detector.detect_language(keyword_text_input)
615
+
616
+ st.markdown("### Dil Tespiti Sonucu")
617
+
618
+ # Dil adı ve güven skoru
619
+ lang_code = lang_result['language_code']
620
+ lang_name = lang_result['language_name']
621
+ confidence = lang_result['confidence']
622
+
623
+ # Dil için bayrak ve renk
624
+ lang_flags = {
625
+ 'tr': '🇹🇷',
626
+ 'en': '🇺🇸',
627
+ 'de': '🇩🇪',
628
+ 'fr': '🇫🇷',
629
+ 'es': '🇪🇸',
630
+ 'unknown': '🌐'
631
+ }
632
+
633
+ lang_flag = lang_flags.get(lang_code, '🌐')
634
+
635
+ # Sonuçları göster
636
+ confidence_color = "green" if confidence > 0.7 else "orange" if confidence > 0.4 else "red"
637
+
638
+ st.markdown(f"### {lang_flag} Tespit Edilen Dil: {lang_name}")
639
+ st.markdown(f"**Güven Skoru:** <span style='color:{confidence_color}'>{confidence:.2f}</span>", unsafe_allow_html=True)
640
+
641
+ # Eğer tüm dil skorlarını göstermek istersek
642
+ if lang_result['scores']:
643
+ scores_df = pd.DataFrame({
644
+ 'Dil': [language_detector.supported_languages.get(code, code) for code in lang_result['scores'].keys()],
645
+ 'Kod': list(lang_result['scores'].keys()),
646
+ 'Skor': list(lang_result['scores'].values())
647
+ })
648
+
649
+ # Skorlara göre sırala
650
+ scores_df = scores_df.sort_values(by='Skor', ascending=False).reset_index(drop=True)
651
+
652
+ # En yüksek skorlu diğer dillerin skorlarını göster
653
+ st.markdown("#### Dil Skorları")
654
+
655
+ # Geniş çubuk grafik
656
+ fig = px.bar(
657
+ scores_df.head(5), # En yüksek 5 skoru göster
658
+ x='Skor',
659
+ y='Dil',
660
+ orientation='h',
661
+ title="Dil Algılama Skorları",
662
+ color='Skor',
663
+ color_continuous_scale='Viridis'
664
+ )
665
+
666
+ fig.update_layout(height=300, yaxis={'categoryorder': 'total ascending'})
667
+ st.plotly_chart(fig, use_container_width=True)
668
+
669
+ # Anahtar kelime çıkarma
670
+ keyword_results = keyword_extractor.extract_keywords(
671
+ keyword_text_input,
672
+ method=keyword_method,
673
+ num_keywords=num_keywords
674
+ )
675
+
676
+ st.markdown("### Anahtar Kelime Analizi")
677
+
678
+ # Kullanılan yöntemi göster
679
+ method_names = {
680
+ 'tfidf': 'TF-IDF',
681
+ 'textrank': 'TextRank',
682
+ 'combined': 'Birleşik (TF-IDF + TextRank)'
683
+ }
684
+
685
+ st.write(f"Kullanılan yöntem: **{method_names.get(keyword_results['method'], keyword_results['method'])}**")
686
+
687
+ # Tekil anahtar kelimeleri göster
688
+ if keyword_results['keywords']:
689
+ keywords_df = pd.DataFrame({
690
+ 'Anahtar Kelime': [kw[0] for kw in keyword_results['keywords']],
691
+ 'Skor': [kw[1] for kw in keyword_results['keywords']]
692
+ })
693
+
694
+ # Skorlara göre sırala
695
+ keywords_df = keywords_df.sort_values(by='Skor', ascending=False).reset_index(drop=True)
696
+
697
+ # İki sütunlu düzen
698
+ col1, col2 = st.columns(2)
699
+
700
+ with col1:
701
+ # Anahtar kelime listesi
702
+ st.markdown("#### Anahtar Kelimeler")
703
+ for i, (keyword, score) in enumerate(zip(keywords_df['Anahtar Kelime'], keywords_df['Skor'])):
704
+ # Skora göre font boyutu ve kalınlığı ayarla
705
+ font_size = min(18, max(12, 12 + score * 6))
706
+ font_weight = "bold" if score > 0.6 else "normal"
707
+ st.markdown(
708
+ f"<span style='font-size:{font_size}px; font-weight:{font_weight}'>{i+1}. {keyword}</span> <small>({score:.2f})</small>",
709
+ unsafe_allow_html=True
710
+ )
711
+
712
+ with col2:
713
+ # Anahtar kelime grafiği
714
+ fig = px.bar(
715
+ keywords_df,
716
+ x='Anahtar Kelime',
717
+ y='Skor',
718
+ title="Anahtar Kelime Skorları",
719
+ color='Skor',
720
+ color_continuous_scale='Blues'
721
+ )
722
+ fig.update_layout(xaxis={'categoryorder': 'total descending'})
723
+ st.plotly_chart(fig, use_container_width=True)
724
+ else:
725
+ st.warning("Metinde anahtar kelime bulunamadı.")
726
+
727
+ # İkili kelime gruplarını (bigram) göster
728
+ if keyword_results['bigrams']:
729
+ bigrams_df = pd.DataFrame({
730
+ 'İkili Kelime Grubu': [bg[0] for bg in keyword_results['bigrams']],
731
+ 'Skor': [bg[1] for bg in keyword_results['bigrams']]
732
+ })
733
+
734
+ st.markdown("#### İkili Kelime Grupları (Bigrams)")
735
+
736
+ # İkili kelimeleri tablo olarak göster
737
+ st.dataframe(bigrams_df, use_container_width=True)
738
+
739
+ # İkili kelime grafiği
740
+ fig = px.bar(
741
+ bigrams_df,
742
+ x='İkili Kelime Grubu',
743
+ y='Skor',
744
+ title="İkili Kelime Grubu Skorları",
745
+ color='Skor',
746
+ color_continuous_scale='Greens'
747
+ )
748
+ st.plotly_chart(fig, use_container_width=True)
749
+
750
+ # Metin içinde anahtar kelimeleri vurgula
751
+ if keyword_results['keywords']:
752
+ st.markdown("#### Anahtar Kelimeleri Vurgulanmış Metin")
753
+
754
+ highlighted_text = keyword_text_input
755
+ for keyword, _ in keyword_results['keywords']:
756
+ # Büyük/küçük harfe duyarsız olarak değiştirme yapmak için regex
757
+ pattern = re.compile(r'\b' + re.escape(keyword) + r'\b', re.IGNORECASE)
758
+ replacement = f"<mark><b>{keyword}</b></mark>"
759
+ highlighted_text = pattern.sub(replacement, highlighted_text)
760
+
761
+ st.markdown(highlighted_text, unsafe_allow_html=True)
762
+
763
+ # Özet bilgilendirme
764
+ st.success(f"Metinden toplam {len(keyword_results['keywords'])} anahtar kelime ve {len(keyword_results['bigrams'])} ikili kelime grubu çıkarıldı.")
765
+ else:
766
+ st.error("Lütfen analiz için bir metin girin.")
767
+
768
+
769
+ if __name__ == "__main__":
770
+ try:
771
+ main()
772
+ except Exception as e:
773
+ st.error(f"Beklenmeyen bir hata oluştu: {str(e)}")
774
+ logger.exception(f"Beklenmeyen bir hata: {str(e)}")
data/sample_data.csv ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ id,metin,kategori
2
+ 1,"Bu, içeriği zengin ve doğru dilbilgisi kullanılarak yazılmış yüksek kaliteli bir metin örneğidir. Cümleler açık ve anlaşılır, kelime çeşitliliği yeterlidir.",A
3
+ 2,"kısa metin örneği",B
4
+ 3,"Bu metin zararlı içerik örnekleridir ve birçok hakaret ve kötü söz içerir. Seni hiç sevmiyorum, çok aptal ve beceriksizsin!",C
5
+ 4,"Bu metin gereksiz tekrarlar içerir. Aynı kelimeyi tekrar tekrar tekrar tekrar tekrar kullanır. Ayrıca çok fazla dolgu kelimesi kullanır, yani, şey, aslında, gerçekten, sadece tekrar eder durur.",B
6
+ 5,"Oldukça uzun ve detaylı bir metin örneği. Birçok farklı konu içerir ve konular arasındaki geçişler akıcıdır. Cümleler düzgün ve çeşitlidir. Kelime dağarcığı zengindir ve noktalama işaretleri doğru kullanılmıştır. Metinde herhangi bir yazım hatası veya dilbilgisi hatası bulunmamaktadır. Konular tutarlı bir şekilde ele alınmış ve gerekli açıklamalar yapılmıştır. Okuyucunun konuyu anlaması için gerekli bilgiler sağlanmıştır.",A
7
+ 6,"bu metinde büyük harf kullanilmamis ve noktalama isaretleri yok ayrica yazim hatalari var metinn kalitesi dusuk",C
8
+ 7,"Merhaba, bu normal bir metindir. Kalitesi orta düzeydedir, ne çok iyi ne de çok kötü.",B
9
+ 8,"Yarın buluşmaya ne dersin? Bu öneriyi düşüneceğini umuyorum. İyi günler!",A
10
+ 9,"Sen çok kötü bir insansın, senden nefret ediyorum! Aptalsın ve beceriksizsin. Kaybol!",C
11
+ 10,"Metni çok uzatmak için gereksiz dolgu kelimeleri kullanılabilir, aynı şekilde, bu tür kelimeler metnin kalitesini düşürür, yani anlamını zayıflatır, aslında okuyucunun ilgisini kaybetmesine neden olur, bu yüzden mümkün olduğunca az kullanılmalıdır, gerçekten öyle.",B
models/__pycache__/model_loader.cpython-312.pyc ADDED
Binary file (6.02 kB). View file
 
models/model_loader.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
3
+ from transformers import pipeline
4
+ import logging
5
+
6
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
7
+
8
+
9
+ class ModelManager:
10
+ def __init__(self):
11
+ self.toxicity_model = None
12
+ self.toxicity_tokenizer = None
13
+ self.quality_model = None
14
+ self.quality_tokenizer = None
15
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
16
+ logging.info(f"Using device: {self.device}")
17
+
18
+ def load_toxicity_model(self, model_name="savasy/bert-base-turkish-sentiment"):
19
+ """
20
+ Zararlılık tespiti için model yükleme
21
+ """
22
+ try:
23
+ logging.info(f"Loading toxicity model: {model_name}")
24
+ self.toxicity_tokenizer = AutoTokenizer.from_pretrained(model_name)
25
+ self.toxicity_model = AutoModelForSequenceClassification.from_pretrained(model_name)
26
+ logging.info("Toxicity model loaded successfully")
27
+ return True
28
+ except Exception as e:
29
+ logging.error(f"Error loading toxicity model: {str(e)}")
30
+ # Alternatif model deneyelim
31
+ try:
32
+ backup_model = "dbmdz/bert-base-turkish-cased"
33
+ logging.info(f"Trying backup Turkish model: {backup_model}")
34
+ self.toxicity_tokenizer = AutoTokenizer.from_pretrained(backup_model)
35
+ self.toxicity_model = AutoModelForSequenceClassification.from_pretrained(backup_model)
36
+ logging.info("Backup Turkish model loaded successfully")
37
+ return True
38
+ except Exception as e2:
39
+ logging.error(f"Error loading backup Turkish model: {str(e2)}")
40
+ try:
41
+ english_model = "distilbert/distilbert-base-uncased-finetuned-sst-2-english"
42
+ logging.info(f"Trying English sentiment model: {english_model}")
43
+ self.toxicity_tokenizer = AutoTokenizer.from_pretrained(english_model)
44
+ self.toxicity_model = AutoModelForSequenceClassification.from_pretrained(english_model)
45
+ logging.info("English sentiment model loaded successfully")
46
+ return True
47
+ except Exception as e3:
48
+ logging.error(f"Error loading English model: {str(e3)}")
49
+ return False
50
+
51
+ def load_quality_model(self, model_name="sshleifer/distilbart-cnn-6-6"):
52
+ """
53
+ Metin kalitesi değerlendirmesi için model yükleme (özetleme kapasitesi olan bir model)
54
+ """
55
+ try:
56
+ logging.info(f"Loading quality model: {model_name}")
57
+ self.quality_pipeline = pipeline(
58
+ "text2text-generation",
59
+ model=model_name,
60
+ tokenizer=model_name,
61
+ device=0 if self.device == "cuda" else -1
62
+ )
63
+ logging.info("Quality model loaded successfully")
64
+ return True
65
+ except Exception as e:
66
+ logging.error(f"Error loading quality model: {str(e)}")
67
+
68
+ # Daha hafif bir model deneyelim
69
+ try:
70
+ backup_model = "Helsinki-NLP/opus-mt-tc-big-tr-en"
71
+ logging.info(f"Trying Turkish translation model for quality: {backup_model}")
72
+ self.quality_pipeline = pipeline(
73
+ "translation",
74
+ model=backup_model,
75
+ tokenizer=backup_model,
76
+ device=0 if self.device == "cuda" else -1
77
+ )
78
+ logging.info("Turkish translation model loaded successfully")
79
+ return True
80
+ except Exception as e2:
81
+ logging.error(f"Error loading Turkish translation model: {str(e2)}")
82
+ try:
83
+ light_model = "sshleifer/distilbart-xsum-12-6"
84
+ logging.info(f"Trying lighter quality model: {light_model}")
85
+ self.quality_pipeline = pipeline(
86
+ "text2text-generation",
87
+ model=light_model,
88
+ tokenizer=light_model,
89
+ device=0 if self.device == "cuda" else -1
90
+ )
91
+ logging.info("Lighter quality model loaded successfully")
92
+ return True
93
+ except Exception as e3:
94
+ logging.error(f"Error loading lighter quality model: {str(e3)}")
95
+ return False
96
+
97
+ def get_models(self):
98
+ """
99
+ Yüklenen modelleri döndürür
100
+ """
101
+ return {
102
+ "toxicity_model": self.toxicity_model,
103
+ "toxicity_tokenizer": self.toxicity_tokenizer,
104
+ "quality_pipeline": self.quality_pipeline if hasattr(self, 'quality_pipeline') else None
105
+ }
models/model_manager.py ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
3
+ from transformers import pipeline
4
+ import logging
5
+ import os
6
+ import json
7
+ import time
8
+ from typing import List, Dict, Any, Tuple, Optional, Union
9
+ from models.model_selector import ModelSelector
10
+
11
+ # Loglama yapılandırması
12
+ logger = logging.getLogger(__name__)
13
+ handler = logging.StreamHandler()
14
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
15
+ handler.setFormatter(formatter)
16
+ logger.addHandler(handler)
17
+ logger.setLevel(logging.INFO)
18
+
19
+
20
+ class ModelManager:
21
+ """
22
+ Metin kalitesi ve zararlılık değerlendirmesi için NLP modellerini yöneten sınıf.
23
+ Bu sınıf, modellerin yüklenmesi, değerlendirilmesi ve seçilmesinden sorumludur.
24
+ """
25
+
26
+ def __init__(self, cache_dir: Optional[str] = None, use_cache: bool = True) -> None:
27
+ """
28
+ Model Yöneticisi sınıfını başlatır.
29
+
30
+ Args:
31
+ cache_dir: Modellerin önbelleğe alınacağı dizin
32
+ use_cache: Önbellek kullanımını etkinleştir/devre dışı bırak
33
+ """
34
+ self.toxicity_model = None
35
+ self.toxicity_tokenizer = None
36
+ self.quality_pipeline = None
37
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
38
+ self.cache_dir = cache_dir or os.path.join(os.path.expanduser("~"), ".text_quality_toxicity_cache")
39
+ self.use_cache = use_cache
40
+ self.model_selector = ModelSelector(cache_dir=self.cache_dir, use_cache=self.use_cache)
41
+ self.model_info = {
42
+ "toxicity": {
43
+ "name": "Bilinmeyen Model",
44
+ "description": "Model henüz yüklenmedi",
45
+ "language": "unknown"
46
+ },
47
+ "quality": {
48
+ "name": "Bilinmeyen Model",
49
+ "description": "Model henüz yüklenmedi",
50
+ "language": "unknown"
51
+ }
52
+ }
53
+
54
+ # Önbellek dizinini oluştur
55
+ if self.use_cache and not os.path.exists(self.cache_dir):
56
+ os.makedirs(self.cache_dir, exist_ok=True)
57
+
58
+ logger.info(
59
+ f"ModelManager başlatıldı. Cihaz: {self.device}, Önbellek: {'Etkin' if use_cache else 'Devre dışı'}")
60
+
61
+ def load_models_auto_select(self, sample_texts: Optional[List[str]] = None) -> bool:
62
+ """
63
+ En iyi modelleri otomatik olarak seçerek yükler.
64
+
65
+ Args:
66
+ sample_texts: Model seçimi için örnek metinler
67
+
68
+ Returns:
69
+ bool: Yükleme başarılı mı?
70
+ """
71
+ if sample_texts is None or len(sample_texts) == 0:
72
+ # Örnek metinler yoksa varsayılan örnekler kullan
73
+ sample_texts = [
74
+ "Türkiye, zengin tarihi ve kültürel mirası ile güzel bir ülkedir.",
75
+ "Bu ürün tam bir hayal kırıklığı! Paramı geri istiyorum!",
76
+ "Bugün hava çok güzel. Parkta yürüyüş yaptım ve kuşları izledim.",
77
+ "Sen ne anlarsın ki bu konudan? Boş konuşma artık!"
78
+ ]
79
+
80
+ try:
81
+ logger.info("Otomatik model seçimi başlatılıyor...")
82
+ start_time = time.time()
83
+
84
+ success = self.model_selector.select_best_models(sample_texts)
85
+
86
+ if success:
87
+ best_models = self.model_selector.get_best_models()
88
+ self.toxicity_model = best_models["toxicity_model"]
89
+ self.toxicity_tokenizer = best_models["toxicity_tokenizer"]
90
+ self.quality_pipeline = best_models["quality_pipeline"]
91
+ self.model_info["toxicity"] = best_models["toxicity_model_info"]
92
+ self.model_info["quality"] = best_models["quality_model_info"]
93
+
94
+ selection_time = time.time() - start_time
95
+ logger.info(f"Otomatik model seçimi {selection_time:.2f} saniyede tamamlandı")
96
+ logger.info(f"Seçilen zararlılık modeli: {self.model_info['toxicity']['name']}")
97
+ logger.info(f"Seçilen kalite modeli: {self.model_info['quality']['name']}")
98
+
99
+ # Kullanılan modelleri önbelleğe kaydet
100
+ if self.use_cache:
101
+ self._save_models_to_cache()
102
+
103
+ return True
104
+ else:
105
+ logger.error("Otomatik model seçimi başarısız oldu, varsayılan modellere dönülüyor")
106
+ return self.load_default_models()
107
+
108
+ except Exception as e:
109
+ logger.error(f"Otomatik model seçimi sırasında hata: {str(e)}")
110
+ return self.load_default_models()
111
+
112
+ def load_default_models(self) -> bool:
113
+ """
114
+ Varsayılan modelleri yükler (otomatik seçim başarısız olursa).
115
+
116
+ Returns:
117
+ bool: Yükleme başarılı mı?
118
+ """
119
+ logger.info("Varsayılan modeller yükleniyor...")
120
+
121
+ # Önce önbellekten yüklemeyi dene
122
+ if self.use_cache and self._load_models_from_cache():
123
+ logger.info("Modeller önbellekten başarıyla yüklendi")
124
+ return True
125
+
126
+ success_toxicity = self.load_toxicity_model()
127
+ success_quality = self.load_quality_model()
128
+
129
+ overall_success = success_toxicity and success_quality
130
+
131
+ if overall_success and self.use_cache:
132
+ self._save_models_to_cache()
133
+
134
+ return overall_success
135
+
136
+ def load_toxicity_model(self, model_name: str = "savasy/bert-base-turkish-sentiment") -> bool:
137
+ """
138
+ Zararlılık tespiti için model yükleme.
139
+
140
+ Args:
141
+ model_name: Yüklenecek model ismi
142
+
143
+ Returns:
144
+ bool: Yükleme başarılı mı?
145
+ """
146
+ try:
147
+ logger.info(f"Zararlılık modeli yükleniyor: {model_name}")
148
+ self.toxicity_tokenizer = AutoTokenizer.from_pretrained(model_name)
149
+ self.toxicity_model = AutoModelForSequenceClassification.from_pretrained(model_name)
150
+ self.model_info["toxicity"]["name"] = model_name
151
+ self.model_info["toxicity"]["description"] = "Türkçe duygu analizi modeli"
152
+ self.model_info["toxicity"]["language"] = "tr"
153
+ logger.info("Zararlılık modeli başarıyla yüklendi")
154
+ return True
155
+ except Exception as e:
156
+ logger.error(f"Zararlılık modeli yüklenirken hata: {str(e)}")
157
+ # Alternatif model deneyelim
158
+ try:
159
+ backup_model = "dbmdz/bert-base-turkish-cased"
160
+ logger.info(f"Yedek Türkçe model deneniyor: {backup_model}")
161
+ self.toxicity_tokenizer = AutoTokenizer.from_pretrained(backup_model)
162
+ self.toxicity_model = AutoModelForSequenceClassification.from_pretrained(backup_model)
163
+ self.model_info["toxicity"]["name"] = backup_model
164
+ self.model_info["toxicity"]["description"] = "Genel amaçlı Türkçe BERT modeli"
165
+ self.model_info["toxicity"]["language"] = "tr"
166
+ logger.info("Yedek Türkçe model başarıyla yüklendi")
167
+ return True
168
+ except Exception as e2:
169
+ logger.error(f"Yedek Türkçe model yüklenirken hata: {str(e2)}")
170
+ try:
171
+ fallback_model = "distilbert/distilbert-base-uncased-finetuned-sst-2-english"
172
+ logger.info(f"İngilizce duygu analizi modeli deneniyor: {fallback_model}")
173
+ self.toxicity_tokenizer = AutoTokenizer.from_pretrained(fallback_model)
174
+ self.toxicity_model = AutoModelForSequenceClassification.from_pretrained(fallback_model)
175
+ self.model_info["toxicity"]["name"] = fallback_model
176
+ self.model_info["toxicity"]["description"] = "İngilizce duygu analizi modeli"
177
+ self.model_info["toxicity"]["language"] = "en"
178
+ logger.info("İngilizce duygu analizi modeli başarıyla yüklendi")
179
+ return True
180
+ except Exception as e3:
181
+ logger.error(f"İngilizce model yüklenirken hata: {str(e3)}")
182
+ return False
183
+
184
+ def load_quality_model(self, model_name: str = "sshleifer/distilbart-cnn-6-6") -> bool:
185
+ """
186
+ Metin kalitesi değerlendirmesi için model yükleme.
187
+
188
+ Args:
189
+ model_name: Yüklenecek model ismi
190
+
191
+ Returns:
192
+ bool: Yükleme başarılı mı?
193
+ """
194
+ try:
195
+ logger.info(f"Kalite modeli yükleniyor: {model_name}")
196
+ self.quality_pipeline = pipeline(
197
+ "text2text-generation",
198
+ model=model_name,
199
+ tokenizer=model_name,
200
+ device=0 if self.device == "cuda" else -1
201
+ )
202
+ self.model_info["quality"]["name"] = model_name
203
+ self.model_info["quality"]["description"] = "İngilizce metin özetleme modeli"
204
+ self.model_info["quality"]["language"] = "en"
205
+ logger.info("Kalite modeli başarıyla yüklendi")
206
+ return True
207
+ except Exception as e:
208
+ logger.error(f"Kalite modeli yüklenirken hata: {str(e)}")
209
+
210
+ # Daha hafif bir model deneyelim
211
+ try:
212
+ backup_model = "Helsinki-NLP/opus-mt-tr-en"
213
+ logger.info(f"Türkçe çeviri modeli deneniyor: {backup_model}")
214
+ self.quality_pipeline = pipeline(
215
+ "translation",
216
+ model=backup_model,
217
+ device=0 if self.device == "cuda" else -1
218
+ )
219
+ self.model_info["quality"]["name"] = backup_model
220
+ self.model_info["quality"]["description"] = "Türkçe-İngilizce çeviri modeli"
221
+ self.model_info["quality"]["language"] = "tr"
222
+ logger.info("Türkçe çeviri modeli başarıyla yüklendi")
223
+ return True
224
+ except Exception as e2:
225
+ logger.error(f"Türkçe çeviri modeli yüklenirken hata: {str(e2)}")
226
+ try:
227
+ light_model = "sshleifer/distilbart-xsum-12-6"
228
+ logger.info(f"Daha hafif özetleme modeli deneniyor: {light_model}")
229
+ self.quality_pipeline = pipeline(
230
+ "text2text-generation",
231
+ model=light_model,
232
+ device=0 if self.device == "cuda" else -1
233
+ )
234
+ self.model_info["quality"]["name"] = light_model
235
+ self.model_info["quality"]["description"] = "Hafif İngilizce özetleme modeli"
236
+ self.model_info["quality"]["language"] = "en"
237
+ logger.info("Hafif özetleme modeli başarıyla yüklendi")
238
+ return True
239
+ except Exception as e3:
240
+ logger.error(f"Hafif özetleme modeli yüklenirken hata: {str(e3)}")
241
+ return False
242
+
243
+ def _save_models_to_cache(self) -> None:
244
+ """Kullanılan modellerin bilgilerini önbelleğe kaydeder."""
245
+ if not self.use_cache:
246
+ return
247
+
248
+ try:
249
+ cache_file = os.path.join(self.cache_dir, "model_manager_state.json")
250
+
251
+ cache_data = {
252
+ "timestamp": time.time(),
253
+ "model_info": self.model_info
254
+ }
255
+
256
+ with open(cache_file, 'w', encoding='utf-8') as f:
257
+ json.dump(cache_data, f, ensure_ascii=False, indent=2)
258
+
259
+ logger.info(f"Model bilgileri önbelleğe kaydedildi: {cache_file}")
260
+ except Exception as e:
261
+ logger.error(f"Önbelleğe kaydetme hatası: {str(e)}")
262
+
263
+ def _load_models_from_cache(self) -> bool:
264
+ """
265
+ Önbellekten model bilgilerini yükler.
266
+
267
+ Returns:
268
+ bool: Yükleme başarılı mı?
269
+ """
270
+ if not self.use_cache:
271
+ return False
272
+
273
+ try:
274
+ cache_file = os.path.join(self.cache_dir, "model_manager_state.json")
275
+
276
+ if not os.path.exists(cache_file):
277
+ return False
278
+
279
+ # Önbellek dosyasının yaşını kontrol et (24 saatten eskiyse yok say)
280
+ file_age = time.time() - os.path.getmtime(cache_file)
281
+ if file_age > 86400: # 24 saat = 86400 saniye
282
+ logger.info(f"Önbellek dosyası çok eski ({file_age / 3600:.1f} saat), yeniden yükleme yapılacak")
283
+ return False
284
+
285
+ with open(cache_file, 'r', encoding='utf-8') as f:
286
+ cache_data = json.load(f)
287
+
288
+ # Model bilgilerini önbellekten al
289
+ self.model_info = cache_data.get("model_info", self.model_info)
290
+
291
+ # Önbellekteki bilgilerle yeniden yükleme yap
292
+ toxicity_name = self.model_info["toxicity"].get("name")
293
+ quality_name = self.model_info["quality"].get("name")
294
+
295
+ toxicity_success = self.load_toxicity_model(toxicity_name) if toxicity_name else False
296
+ quality_success = self.load_quality_model(quality_name) if quality_name else False
297
+
298
+ return toxicity_success and quality_success
299
+
300
+ except Exception as e:
301
+ logger.error(f"Önbellekten model yükleme hatası: {str(e)}")
302
+ return False
303
+
304
+ def get_models(self) -> Dict[str, Any]:
305
+ """
306
+ Yüklenen modelleri ve bilgilerini döndürür.
307
+
308
+ Returns:
309
+ Dict[str, Any]: Model, tokenizer, pipeline ve model bilgileri
310
+ """
311
+ return {
312
+ "toxicity_model": self.toxicity_model,
313
+ "toxicity_tokenizer": self.toxicity_tokenizer,
314
+ "quality_pipeline": self.quality_pipeline,
315
+ "model_info": self.model_info
316
+ }
models/model_selector.py ADDED
@@ -0,0 +1,571 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification, pipeline
3
+ import logging
4
+ import time
5
+ import numpy as np
6
+ from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score
7
+ import os
8
+ import json
9
+ from typing import List, Dict, Any, Tuple, Optional, Union
10
+ import threading
11
+
12
+ # Loglama yapılandırması
13
+ logger = logging.getLogger(__name__)
14
+ handler = logging.StreamHandler()
15
+ formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
16
+ handler.setFormatter(formatter)
17
+ logger.addHandler(handler)
18
+ logger.setLevel(logging.INFO)
19
+
20
+
21
+ class ModelSelector:
22
+ """
23
+ Farklı NLP modellerini değerlendirip en iyi performansı sağlayanı seçen sınıf.
24
+ """
25
+
26
+ def __init__(self, cache_dir: Optional[str] = None, use_cache: bool = True) -> None:
27
+ """
28
+ Model Seçici sınıfını başlatır.
29
+
30
+ Args:
31
+ cache_dir: Model ve değerlendirme sonuçlarının önbelleğe alınacağı dizin
32
+ use_cache: Önbellek kullanımını etkinleştir/devre dışı bırak
33
+ """
34
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
35
+ self.toxicity_models = []
36
+ self.quality_models = []
37
+ self.best_toxicity_model = None
38
+ self.best_quality_model = None
39
+ self.use_cache = use_cache
40
+ self.cache_dir = cache_dir or os.path.join(os.path.expanduser("~"), ".text_quality_toxicity_cache")
41
+
42
+ # Önbellek dizinini oluştur
43
+ if self.use_cache and not os.path.exists(self.cache_dir):
44
+ os.makedirs(self.cache_dir, exist_ok=True)
45
+
46
+ logger.info(
47
+ f"ModelSelector başlatıldı. Cihaz: {self.device}, Önbellek: {'Etkin' if use_cache else 'Devre dışı'}")
48
+
49
+ def load_candidate_models(self) -> bool:
50
+ """
51
+ Değerlendirme için aday modelleri yükler.
52
+
53
+ Returns:
54
+ bool: Yükleme başarılı mı?
55
+ """
56
+ # Önbellekten okunan modeller
57
+ cached_models = self._load_from_cache()
58
+ if cached_models:
59
+ self.toxicity_models = cached_models.get("toxicity_models", [])
60
+ self.quality_models = cached_models.get("quality_models", [])
61
+ self.best_toxicity_model = cached_models.get("best_toxicity_model")
62
+ self.best_quality_model = cached_models.get("best_quality_model")
63
+
64
+ # Önbellek bulundu, doğrulama yap
65
+ if self.best_toxicity_model and self.best_quality_model:
66
+ logger.info("Önbellekten en iyi modeller yüklendi.")
67
+ # Tokenizer ve model erişilebilir mi kontrol et
68
+ if "tokenizer" in self.best_toxicity_model and "model" in self.best_toxicity_model:
69
+ # En iyi model önbellekten doğru yüklendi
70
+ return True
71
+
72
+ # Zararlılık modelleri - Türkçe için optimize edilmiş
73
+ toxicity_candidates = [
74
+ {"name": "savasy/bert-base-turkish-sentiment", "type": "sentiment",
75
+ "language": "tr", "priority": 1, "description": "Türkçe duygu analizi için BERT modeli"},
76
+ {"name": "loodos/electra-turkish-sentiment", "type": "sentiment",
77
+ "language": "tr", "priority": 2, "description": "Türkçe duygu analizi için ELECTRA modeli"},
78
+ {"name": "dbmdz/bert-base-turkish-cased", "type": "general",
79
+ "language": "tr", "priority": 3, "description": "Genel amaçlı Türkçe BERT modeli"},
80
+ {"name": "ytu-ce-cosmos/turkish-bert-uncased-toxicity", "type": "toxicity",
81
+ "language": "tr", "priority": 4, "description": "Türkçe zararlılık tespiti için BERT modeli",
82
+ "optional": True},
83
+ {"name": "unitary/toxic-bert", "type": "toxicity",
84
+ "language": "en", "priority": 5, "description": "İngilizce zararlılık tespiti BERT modeli"}
85
+ ]
86
+
87
+ # Kalite modelleri
88
+ quality_candidates = [
89
+ {"name": "Helsinki-NLP/opus-mt-tr-en", "type": "translation",
90
+ "language": "tr", "priority": 1, "description": "Türkçe-İngilizce çeviri modeli"},
91
+ {"name": "tuner/pegasus-turkish", "type": "summarization",
92
+ "language": "tr", "priority": 2, "description": "Türkçe özetleme için PEGASUS modeli", "optional": True},
93
+ {"name": "dbmdz/t5-base-turkish-summarization", "type": "summarization",
94
+ "language": "tr", "priority": 3, "description": "Türkçe özetleme için T5 modeli", "optional": True},
95
+ {"name": "sshleifer/distilbart-cnn-6-6", "type": "summarization",
96
+ "language": "en", "priority": 4, "description": "İngilizce özetleme için DistilBART modeli"}
97
+ ]
98
+
99
+ # Modelleri önceliğe göre sırala
100
+ toxicity_candidates.sort(key=lambda x: x.get("priority", 999))
101
+ quality_candidates.sort(key=lambda x: x.get("priority", 999))
102
+
103
+ # Zaman aşımını önlemek için asenkron model yükleme
104
+ self._load_models_async(toxicity_candidates, quality_candidates)
105
+
106
+ # Yeterli model yüklendi mi kontrol et
107
+ return len(self.toxicity_models) > 0 and len(self.quality_models) > 0
108
+
109
+ def _load_models_async(self, toxicity_candidates: List[Dict[str, Any]],
110
+ quality_candidates: List[Dict[str, Any]]) -> None:
111
+ """
112
+ Modelleri asenkron olarak yükler
113
+
114
+ Args:
115
+ toxicity_candidates: Zararlılık modellerinin listesi
116
+ quality_candidates: Kalite modellerinin listesi
117
+ """
118
+ # Zararlılık modelleri için eş zamanlı yükleme
119
+ toxicity_threads = []
120
+ for candidate in toxicity_candidates:
121
+ thread = threading.Thread(
122
+ target=self._load_toxicity_model,
123
+ args=(candidate,)
124
+ )
125
+ thread.start()
126
+ toxicity_threads.append(thread)
127
+
128
+ # Kalite modelleri için eş zamanlı yükleme
129
+ quality_threads = []
130
+ for candidate in quality_candidates:
131
+ thread = threading.Thread(
132
+ target=self._load_quality_model,
133
+ args=(candidate,)
134
+ )
135
+ thread.start()
136
+ quality_threads.append(thread)
137
+
138
+ # Tüm işlemlerin tamamlanmasını bekle (timeout ile)
139
+ for thread in toxicity_threads:
140
+ thread.join(timeout=60) # Her model için 60 saniye timeout
141
+
142
+ for thread in quality_threads:
143
+ thread.join(timeout=60) # Her model için 60 saniye timeout
144
+
145
+ def _load_toxicity_model(self, candidate: Dict[str, Any]) -> None:
146
+ """
147
+ Bir zararlılık modelini yükler
148
+
149
+ Args:
150
+ candidate: Model bilgilerini içeren sözlük
151
+ """
152
+ try:
153
+ logger.info(f"Zararlılık modeli yükleniyor: {candidate['name']}")
154
+ start_time = time.time()
155
+
156
+ tokenizer = AutoTokenizer.from_pretrained(candidate["name"])
157
+ model = AutoModelForSequenceClassification.from_pretrained(candidate["name"])
158
+ model.to(self.device)
159
+
160
+ load_time = time.time() - start_time
161
+ candidate["tokenizer"] = tokenizer
162
+ candidate["model"] = model
163
+ candidate["load_time"] = load_time
164
+
165
+ # Liste eşzamanlı erişim için koruma
166
+ with threading.Lock():
167
+ self.toxicity_models.append(candidate)
168
+
169
+ logger.info(f"{candidate['name']} modeli {load_time:.2f} saniyede başarıyla yüklendi")
170
+ except Exception as e:
171
+ if candidate.get("optional", False):
172
+ logger.warning(f"Opsiyonel model {candidate['name']} atlanıyor: {str(e)}")
173
+ else:
174
+ logger.error(f"{candidate['name']} yüklenirken hata: {str(e)}")
175
+
176
+ def _load_quality_model(self, candidate: Dict[str, Any]) -> None:
177
+ """
178
+ Bir kalite modelini yükler
179
+
180
+ Args:
181
+ candidate: Model bilgilerini içeren sözlük
182
+ """
183
+ try:
184
+ logger.info(f"Kalite modeli yükleniyor: {candidate['name']}")
185
+ start_time = time.time()
186
+
187
+ if candidate["type"] == "translation":
188
+ pipe = pipeline("translation", model=candidate["name"], device=0 if self.device == "cuda" else -1)
189
+ elif candidate["type"] == "summarization":
190
+ pipe = pipeline("summarization", model=candidate["name"], device=0 if self.device == "cuda" else -1)
191
+
192
+ load_time = time.time() - start_time
193
+ candidate["pipeline"] = pipe
194
+ candidate["load_time"] = load_time
195
+
196
+ # Liste eşzamanlı erişim için koruma
197
+ with threading.Lock():
198
+ self.quality_models.append(candidate)
199
+
200
+ logger.info(f"{candidate['name']} modeli {load_time:.2f} saniyede başarıyla yüklendi")
201
+ except Exception as e:
202
+ if candidate.get("optional", False):
203
+ logger.warning(f"Opsiyonel model {candidate['name']} atlanıyor: {str(e)}")
204
+ else:
205
+ logger.error(f"{candidate['name']} yüklenirken hata: {str(e)}")
206
+
207
+ def evaluate_toxicity_models(self, validation_texts: List[str],
208
+ validation_labels: List[int]) -> Optional[Dict[str, Any]]:
209
+ """
210
+ Zararlılık modellerini değerlendirir ve en iyisini seçer
211
+
212
+ Args:
213
+ validation_texts: Doğrulama metinleri
214
+ validation_labels: Doğrulama etiketleri (1=zararlı, 0=zararsız)
215
+
216
+ Returns:
217
+ Dict[str, Any]: En iyi modelin bilgileri veya None
218
+ """
219
+ if not self.toxicity_models:
220
+ logger.error("Değerlendirme için zararlılık modeli yüklenmemiş")
221
+ return None
222
+
223
+ results = []
224
+
225
+ for model_info in self.toxicity_models:
226
+ logger.info(f"Zararlılık modeli değerlendiriliyor: {model_info['name']}")
227
+ model = model_info["model"]
228
+ tokenizer = model_info["tokenizer"]
229
+
230
+ predictions = []
231
+ start_time = time.time()
232
+
233
+ try:
234
+ for text in validation_texts:
235
+ # Metni tokenize et
236
+ inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
237
+ inputs = {key: val.to(self.device) for key, val in inputs.items()}
238
+
239
+ # Tahmin yap
240
+ with torch.no_grad():
241
+ outputs = model(**inputs)
242
+
243
+ # Sonucu almak için model tipine göre işlem yap
244
+ if model_info["type"] == "sentiment":
245
+ # Sentiment modellerinde genellikle 0=negatif, 1=nötr, 2=pozitif
246
+ # veya 0=negatif, 1=pozitif
247
+ probs = torch.softmax(outputs.logits, dim=1).cpu().numpy()[0]
248
+ if len(probs) >= 3:
249
+ # Negatif olasılığını zararlılık olarak kabul et
250
+ pred = 1 if probs[0] > 0.5 else 0
251
+ else:
252
+ # İki sınıflı model
253
+ pred = 1 if probs[0] > 0.5 else 0
254
+ elif model_info["type"] == "toxicity":
255
+ # Toxicity modelleri genellikle 0=non-toxic, 1=toxic
256
+ probs = torch.softmax(outputs.logits, dim=1).cpu().numpy()[0]
257
+ pred = 1 if probs[1] > 0.5 else 0
258
+ else:
259
+ # Genel model - varsayılan
260
+ probs = torch.softmax(outputs.logits, dim=1).cpu().numpy()[0]
261
+ pred = 1 if probs[0] > 0.5 else 0
262
+
263
+ predictions.append(pred)
264
+
265
+ eval_time = time.time() - start_time
266
+
267
+ # Performans metrikleri hesapla
268
+ accuracy = accuracy_score(validation_labels, predictions)
269
+ precision = precision_score(validation_labels, predictions, average='binary', zero_division=0)
270
+ recall = recall_score(validation_labels, predictions, average='binary', zero_division=0)
271
+ f1 = f1_score(validation_labels, predictions, average='binary', zero_division=0)
272
+
273
+ # F1, precision ve recall'un ağırlıklı ortalaması
274
+ weighted_score = (f1 * 0.5) + (precision * 0.3) + (recall * 0.2)
275
+
276
+ # Sonuçları kaydet
277
+ evaluation_result = {
278
+ "model": model_info,
279
+ "accuracy": float(accuracy),
280
+ "precision": float(precision),
281
+ "recall": float(recall),
282
+ "f1_score": float(f1),
283
+ "weighted_score": float(weighted_score),
284
+ "eval_time": float(eval_time),
285
+ "predictions": predictions
286
+ }
287
+
288
+ results.append(evaluation_result)
289
+
290
+ logger.info(
291
+ f"{model_info['name']} - Doğruluk: {accuracy:.4f}, "
292
+ f"F1: {f1:.4f}, Precision: {precision:.4f}, "
293
+ f"Recall: {recall:.4f}, Süre: {eval_time:.2f}s"
294
+ )
295
+
296
+ except Exception as e:
297
+ logger.error(f"{model_info['name']} değerlendirilirken hata: {str(e)}")
298
+
299
+ if not results:
300
+ logger.error("Hiçbir model değerlendirilemedi")
301
+ return None
302
+
303
+ # Sıralama ve en iyi modeli seç (ağırlıklı skora göre)
304
+ results.sort(key=lambda x: x["weighted_score"], reverse=True)
305
+ best_model = results[0]["model"]
306
+
307
+ logger.info(
308
+ f"En iyi zararlılık modeli: {best_model['name']} - "
309
+ f"Ağırlıklı skor: {results[0]['weighted_score']:.4f}, "
310
+ f"F1: {results[0]['f1_score']:.4f}"
311
+ )
312
+
313
+ self.best_toxicity_model = best_model
314
+
315
+ # Önbelleğe kaydet
316
+ if self.use_cache:
317
+ self._save_to_cache()
318
+
319
+ return best_model
320
+
321
+ def evaluate_quality_models(self, validation_texts: List[str],
322
+ reference_summaries: Optional[List[str]] = None) -> Optional[Dict[str, Any]]:
323
+ """
324
+ Kalite modellerini değerlendirir ve en iyisini seçer
325
+
326
+ Args:
327
+ validation_texts: Doğrulama metinleri
328
+ reference_summaries: Referans özetler (opsiyonel)
329
+
330
+ Returns:
331
+ Dict[str, Any]: En iyi modelin bilgileri veya None
332
+ """
333
+ if not self.quality_models:
334
+ logger.error("Değerlendirme için kalite modeli yüklenmemiş")
335
+ return None
336
+
337
+ results = []
338
+
339
+ for model_info in self.quality_models:
340
+ logger.info(f"Kalite modeli değerlendiriliyor: {model_info['name']}")
341
+ pipe = model_info["pipeline"]
342
+
343
+ start_time = time.time()
344
+ processing_success = 0
345
+ avg_processing_time = []
346
+
347
+ # Her metni değerlendir
348
+ for i, text in enumerate(validation_texts[:5]): # Performans için sadece ilk 5 metni değerlendir
349
+ try:
350
+ text_start_time = time.time()
351
+
352
+ if model_info["type"] == "translation":
353
+ _ = pipe(text, max_length=100)
354
+ elif model_info["type"] == "summarization":
355
+ # Çok kısa metinlerde sorun oluşmaması için metin uzunluğunu kontrol et
356
+ text_words = len(text.split())
357
+ max_length = min(100, max(30, text_words // 2))
358
+ min_length = min(30, max(5, text_words // 4))
359
+ _ = pipe(text, max_length=max_length, min_length=min_length, do_sample=False)
360
+
361
+ text_process_time = time.time() - text_start_time
362
+ avg_processing_time.append(text_process_time)
363
+ processing_success += 1
364
+
365
+ except Exception as e:
366
+ logger.warning(f"{model_info['name']} için metin {i} işlenirken hata: {str(e)}")
367
+
368
+ eval_time = time.time() - start_time
369
+ success_rate = processing_success / min(5, len(validation_texts))
370
+ avg_time = np.mean(avg_processing_time) if avg_processing_time else float('inf')
371
+
372
+ # Modelin tipine göre skoru ayarla
373
+ if model_info["language"] == "tr":
374
+ # Türkçe modeller için daha yüksek ağırlık
375
+ language_weight = 1.2
376
+ else:
377
+ language_weight = 0.8
378
+
379
+ # Sonuçları kaydet
380
+ evaluation_result = {
381
+ "model": model_info,
382
+ "success_rate": float(success_rate),
383
+ "avg_processing_time": float(avg_time),
384
+ "eval_time": float(eval_time),
385
+ "language_weight": float(language_weight)
386
+ }
387
+
388
+ results.append(evaluation_result)
389
+
390
+ logger.info(
391
+ f"{model_info['name']} - Başarı Oranı: {success_rate:.2f}, "
392
+ f"Ortalama İşleme Süresi: {avg_time:.4f}s, "
393
+ f"Toplam Süre: {eval_time:.2f}s"
394
+ )
395
+
396
+ if not results:
397
+ logger.error("Hiçbir kalite modeli değerlendirilemedi")
398
+ return None
399
+
400
+ # Sıralama ve en iyi modeli seç
401
+ # Başarı oranı, dil ağırlığı ve hız faktörlerini dengeleyen bir formül
402
+ for result in results:
403
+ result["score"] = (
404
+ result["success_rate"] * 0.6 +
405
+ (1 / (1 + result["avg_processing_time"])) * 0.2 +
406
+ result["language_weight"] * 0.2
407
+ )
408
+
409
+ results.sort(key=lambda x: x["score"], reverse=True)
410
+ best_model = results[0]["model"]
411
+
412
+ logger.info(
413
+ f"En iyi kalite modeli: {best_model['name']} - "
414
+ f"Skor: {results[0]['score']:.4f}, "
415
+ f"Başarı Oranı: {results[0]['success_rate']:.2f}"
416
+ )
417
+
418
+ self.best_quality_model = best_model
419
+
420
+ # Önbelleğe kaydet
421
+ if self.use_cache:
422
+ self._save_to_cache()
423
+
424
+ return best_model
425
+
426
+ def select_best_models(self, sample_texts: List[str]) -> bool:
427
+ """
428
+ En iyi modelleri örnek veriyle otomatik olarak seçer
429
+
430
+ Args:
431
+ sample_texts: Örnek metinler listesi
432
+
433
+ Returns:
434
+ bool: Model seçimi başarılı mı?
435
+ """
436
+ # Modeller önbellekten yüklendiyse doğrudan kullan
437
+ if self.best_toxicity_model and self.best_quality_model:
438
+ logger.info("En iyi modeller önbellekten kullanılıyor")
439
+ return True
440
+
441
+ if not self.toxicity_models or not self.quality_models:
442
+ if not self.load_candidate_models():
443
+ logger.error("Aday modeller yüklenemedi")
444
+ return False
445
+
446
+ # Basit zararlılık değerlendirmesi için örnekler
447
+ toxic_samples = [
448
+ "Bu insanlar gerçekten çok aptal! Hepsi geri zekalı ve cahil.",
449
+ "Senden nefret ediyorum, kaybol buradan!",
450
+ "sen ne anlarsın ki bu konudan? gerizekalılar bile senden daha mantıklı konuşur"
451
+ ]
452
+
453
+ non_toxic_samples = [
454
+ "Türkiye, zengin tarihi ve kültürel mirası ile güzel bir ülkedir.",
455
+ "Bugün hava çok güzel. Parkta yürüyüş yaptım ve kuşları izledim.",
456
+ "Bilgisayarınızı hızlandırmak için gereksiz programları kaldırın."
457
+ ]
458
+
459
+ # Doğrulama verisi hazırla
460
+ validation_texts = toxic_samples + non_toxic_samples
461
+ if sample_texts and len(sample_texts) > 0:
462
+ # Kullanıcı örneklerinden bir kısmını ekle (en fazla 5 tane)
463
+ validation_texts.extend(sample_texts[:5])
464
+
465
+ validation_labels = [1, 1, 1, 0, 0, 0] + [0] * min(5, len(sample_texts))
466
+
467
+ # En iyi zararlılık modelini seç
468
+ best_toxicity = self.evaluate_toxicity_models(validation_texts, validation_labels)
469
+
470
+ # En iyi kalite modelini seç
471
+ best_quality = self.evaluate_quality_models(validation_texts)
472
+
473
+ success = best_toxicity is not None and best_quality is not None
474
+
475
+ if success and self.use_cache:
476
+ self._save_to_cache()
477
+
478
+ return success
479
+
480
+ def get_best_models(self) -> Dict[str, Any]:
481
+ """
482
+ Seçilen en iyi modelleri döndürür
483
+
484
+ Returns:
485
+ Dict[str, Any]: En iyi modellerin bilgileri
486
+ """
487
+ return {
488
+ "toxicity_model": self.best_toxicity_model["model"] if self.best_toxicity_model else None,
489
+ "toxicity_tokenizer": self.best_toxicity_model["tokenizer"] if self.best_toxicity_model else None,
490
+ "quality_pipeline": self.best_quality_model["pipeline"] if self.best_quality_model else None,
491
+ "toxicity_model_info": {
492
+ "name": self.best_toxicity_model["name"] if self.best_toxicity_model else "Unknown",
493
+ "description": self.best_toxicity_model["description"] if self.best_toxicity_model else "Unknown",
494
+ "language": self.best_toxicity_model["language"] if self.best_toxicity_model else "Unknown"
495
+ },
496
+ "quality_model_info": {
497
+ "name": self.best_quality_model["name"] if self.best_quality_model else "Unknown",
498
+ "description": self.best_quality_model["description"] if self.best_quality_model else "Unknown",
499
+ "language": self.best_quality_model["language"] if self.best_quality_model else "Unknown"
500
+ }
501
+ }
502
+
503
+ def _save_to_cache(self) -> None:
504
+ """Modellerin değerlendirme sonuçlarını önbelleğe kaydeder"""
505
+ if not self.use_cache:
506
+ return
507
+
508
+ try:
509
+ cache_file = os.path.join(self.cache_dir, "model_selection_results.json")
510
+
511
+ # Modeller ve tokenizer'ları hariç tutarak kalan bilgileri kaydet
512
+ cache_data = {
513
+ "timestamp": time.time(),
514
+ "toxicity_models": [
515
+ {k: v for k, v in model.items() if k not in ["model", "tokenizer"]}
516
+ for model in self.toxicity_models
517
+ ],
518
+ "quality_models": [
519
+ {k: v for k, v in model.items() if k not in ["pipeline"]}
520
+ for model in self.quality_models
521
+ ],
522
+ "best_toxicity_model": (
523
+ {k: v for k, v in self.best_toxicity_model.items() if k not in ["model", "tokenizer"]}
524
+ if self.best_toxicity_model else None
525
+ ),
526
+ "best_quality_model": (
527
+ {k: v for k, v in self.best_quality_model.items() if k not in ["pipeline"]}
528
+ if self.best_quality_model else None
529
+ )
530
+ }
531
+
532
+ with open(cache_file, 'w', encoding='utf-8') as f:
533
+ json.dump(cache_data, f, ensure_ascii=False, indent=2)
534
+
535
+ logger.info(f"Model seçim sonuçları önbelleğe kaydedildi: {cache_file}")
536
+ except Exception as e:
537
+ logger.error(f"Önbelleğe kaydetme hatası: {str(e)}")
538
+
539
+ def _load_from_cache(self) -> Optional[Dict[str, Any]]:
540
+ """
541
+ Önbellekten modellerin değerlendirme sonuçlarını yükler
542
+
543
+ Returns:
544
+ Optional[Dict[str, Any]]: Yüklenen önbellek verisi veya None
545
+ """
546
+ if not self.use_cache:
547
+ return None
548
+
549
+ try:
550
+ cache_file = os.path.join(self.cache_dir, "model_selection_results.json")
551
+
552
+ if not os.path.exists(cache_file):
553
+ return None
554
+
555
+ # Önbellek dosyasının yaşını kontrol et (24 saatten eskiyse yok say)
556
+ file_age = time.time() - os.path.getmtime(cache_file)
557
+ if file_age > 86400: # 24 saat = 86400 saniye
558
+ logger.info(f"Önbellek dosyası çok eski ({file_age / 3600:.1f} saat), tekrar değerlendirme yapılacak")
559
+ return None
560
+
561
+ with open(cache_file, 'r', encoding='utf-8') as f:
562
+ cache_data = json.load(f)
563
+
564
+ logger.info(f"Model seçim sonuçları önbellekten yüklendi: {cache_file}")
565
+
566
+ # Önbellekten sadece isim bilgilerini yükle, modellerin kendisini değil
567
+ return cache_data
568
+
569
+ except Exception as e:
570
+ logger.error(f"Önbellekten yükleme hatası: {str(e)}")
571
+ return None
requirements.txt ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ altair==5.5.0
2
+ attrs==25.3.0
3
+ blinker==1.9.0
4
+ cachetools==5.5.2
5
+ certifi==2025.1.31
6
+ charset-normalizer==3.4.1
7
+ click==8.1.8
8
+ contourpy==1.3.1
9
+ cycler==0.12.1
10
+ filelock==3.18.0
11
+ fonttools==4.56.0
12
+ fsspec==2025.3.0
13
+ gitdb==4.0.12
14
+ GitPython==3.1.44
15
+ huggingface-hub==0.29.3
16
+ idna==3.10
17
+ Jinja2==3.1.6
18
+ joblib==1.4.2
19
+ jsonschema==4.23.0
20
+ jsonschema-specifications==2024.10.1
21
+ kiwisolver==1.4.8
22
+ MarkupSafe==3.0.2
23
+ matplotlib==3.10.1
24
+ mpmath==1.3.0
25
+ narwhals==1.32.0
26
+ networkx==3.4.2
27
+ numpy==2.2.4
28
+ packaging==24.2
29
+ pandas==2.2.3
30
+ pillow==11.1.0
31
+ plotly==6.0.1
32
+ protobuf==5.29.4
33
+ pyarrow==19.0.1
34
+ pydeck==0.9.1
35
+ pyparsing==3.2.3
36
+ python-dateutil==2.9.0.post0
37
+ pytz==2025.2
38
+ PyYAML==6.0.2
39
+ referencing==0.36.2
40
+ regex==2024.11.6
41
+ requests==2.32.3
42
+ rpds-py==0.24.0
43
+ safetensors==0.5.3
44
+ scikit-learn==1.6.1
45
+ scipy==1.15.2
46
+ sentencepiece==0.2.0
47
+ setuptools==78.1.0
48
+ six==1.17.0
49
+ smmap==5.0.2
50
+ streamlit==1.44.0
51
+ sympy==1.13.1
52
+ tenacity==9.0.0
53
+ threadpoolctl==3.6.0
54
+ tokenizers==0.21.1
55
+ toml==0.10.2
56
+ torch==2.6.0
57
+ tornado==6.4.2
58
+ tqdm==4.67.1
59
+ transformers==4.50.3
60
+ typing_extensions==4.13.0
61
+ tzdata==2025.2
62
+ urllib3==2.3.0
utils/__pycache__/data_handler.cpython-312.pyc ADDED
Binary file (7.32 kB). View file
 
utils/__pycache__/keyword_extractor.cpython-312.pyc ADDED
Binary file (10.4 kB). View file
 
utils/__pycache__/language_detector.cpython-312.pyc ADDED
Binary file (8.85 kB). View file
 
utils/__pycache__/quality_scorer.cpython-312.pyc ADDED
Binary file (8.2 kB). View file
 
utils/__pycache__/sentiment_analyzer.cpython-312.pyc ADDED
Binary file (6.89 kB). View file
 
utils/__pycache__/text_improver.cpython-312.pyc ADDED
Binary file (12.6 kB). View file
 
utils/__pycache__/toxicity_scorer.cpython-312.pyc ADDED
Binary file (8.91 kB). View file
 
utils/data_handler.py ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import pandas as pd
2
+ import numpy as np
3
+ import os
4
+ import logging
5
+ from datetime import datetime
6
+
7
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
8
+
9
+
10
+ class DataHandler:
11
+ def __init__(self, quality_scorer=None, toxicity_scorer=None):
12
+ """
13
+ Veri işleme sınıfını başlatır.
14
+
15
+ Args:
16
+ quality_scorer: Metin kalitesi değerlendirme nesnesi
17
+ toxicity_scorer: Zararlılık skoru değerlendirme nesnesi
18
+ """
19
+ self.quality_scorer = quality_scorer
20
+ self.toxicity_scorer = toxicity_scorer
21
+
22
+ def load_data(self, file_path, text_column=None):
23
+ """
24
+ CSV veya Excel dosyasını yükler.
25
+
26
+ Args:
27
+ file_path: Yüklenecek dosyanın yolu
28
+ text_column: Metin sütunu adı (belirtilmezse otomatik tespit edilir)
29
+
30
+ Returns:
31
+ pd.DataFrame: Yüklenen veri
32
+ """
33
+ try:
34
+ # Dosya uzantısını kontrol et
35
+ if file_path.endswith('.csv'):
36
+ df = pd.read_csv(file_path)
37
+ elif file_path.endswith(('.xls', '.xlsx')):
38
+ df = pd.read_excel(file_path)
39
+ else:
40
+ raise ValueError("Desteklenmeyen dosya formatı. Lütfen CSV veya Excel dosyası yükleyin.")
41
+
42
+ # Metin sütununu belirle
43
+ if text_column is None:
44
+ # En çok metin içeriği olan sütunu bul
45
+ text_lengths = {}
46
+ for col in df.columns:
47
+ if df[col].dtype == object: # Sadece metin sütunlarını kontrol et
48
+ # Ortalama metin uzunluğunu hesapla
49
+ avg_len = df[col].astype(str).str.len().mean()
50
+ text_lengths[col] = avg_len
51
+
52
+ if text_lengths:
53
+ # En uzun ortalama metne sahip sütunu seç
54
+ text_column = max(text_lengths.items(), key=lambda x: x[1])[0]
55
+ else:
56
+ # Hiçbir metin sütunu bulunamazsa ilk sütunu kullan
57
+ text_column = df.columns[0]
58
+
59
+ logging.info(f"Otomatik tespit edilen metin sütunu: {text_column}")
60
+
61
+ return df, text_column
62
+
63
+ except Exception as e:
64
+ logging.error(f"Veri yükleme hatası: {str(e)}")
65
+ raise e
66
+
67
+ def process_data(self, df, text_column, quality_threshold=0.5, toxicity_threshold=0.5, batch_size=8):
68
+ """
69
+ Veriyi işler, kalite ve zararlılık skorlarını hesaplar.
70
+
71
+ Args:
72
+ df: İşlenecek veri çerçevesi
73
+ text_column: Metin sütunu adı
74
+ quality_threshold: Kalite eşik değeri
75
+ toxicity_threshold: Zararlılık eşik değeri
76
+ batch_size: İşlenecek grup boyutu
77
+
78
+ Returns:
79
+ pd.DataFrame: İşlenmiş veri
80
+ """
81
+ try:
82
+ # Boş veya NaN değerli satırları kontrol et
83
+ df = df.copy()
84
+ df[text_column] = df[text_column].astype(str)
85
+ df = df[df[text_column].str.strip() != ""]
86
+ df = df.reset_index(drop=True)
87
+
88
+ texts = df[text_column].tolist()
89
+
90
+ # Kalite skorlarını hesapla
91
+ if self.quality_scorer:
92
+ logging.info("Kalite skorları hesaplanıyor...")
93
+ quality_scores, quality_features = self.quality_scorer.batch_score(texts, batch_size=batch_size)
94
+ df['quality_score'] = quality_scores
95
+
96
+ # Kalite özelliklerini ekle
97
+ for i, features in enumerate(quality_features):
98
+ for feat_name, feat_value in features.items():
99
+ if i == 0: # İlk satır için sütun oluştur
100
+ df[feat_name] = np.nan
101
+ df.at[i, feat_name] = feat_value
102
+
103
+ # Zararlılık skorlarını hesapla
104
+ if self.toxicity_scorer:
105
+ logging.info("Zararlılık skorları hesaplanıyor...")
106
+ toxicity_scores = self.toxicity_scorer.batch_score(texts, batch_size=batch_size)
107
+ df['toxicity_score'] = toxicity_scores
108
+
109
+ # Eşik değerlerine göre etiketle
110
+ if 'quality_score' in df.columns:
111
+ df['low_quality'] = df['quality_score'] < quality_threshold
112
+
113
+ if 'toxicity_score' in df.columns:
114
+ df['is_toxic'] = df['toxicity_score'] > toxicity_threshold
115
+
116
+ # Genel değerlendirme
117
+ if 'quality_score' in df.columns and 'toxicity_score' in df.columns:
118
+ df['acceptable'] = (df['quality_score'] >= quality_threshold) & (
119
+ df['toxicity_score'] <= toxicity_threshold)
120
+
121
+ logging.info("Veri işleme tamamlandı.")
122
+ return df
123
+
124
+ except Exception as e:
125
+ logging.error(f"Veri işleme hatası: {str(e)}")
126
+ raise e
127
+
128
+ def filter_data(self, df, quality_threshold=0.5, toxicity_threshold=0.5):
129
+ """
130
+ Veriyi belirlenen eşik değerlerine göre filtreler.
131
+
132
+ Args:
133
+ df: Filtrelenecek veri çerçevesi
134
+ quality_threshold: Kalite eşik değeri
135
+ toxicity_threshold: Zararlılık eşik değeri
136
+
137
+ Returns:
138
+ pd.DataFrame: Filtrelenmiş veri
139
+ """
140
+ filtered_df = df.copy()
141
+
142
+ # Kalite filtreleme
143
+ if 'quality_score' in filtered_df.columns:
144
+ filtered_df = filtered_df[filtered_df['quality_score'] >= quality_threshold]
145
+
146
+ # Zararlılık filtreleme
147
+ if 'toxicity_score' in filtered_df.columns:
148
+ filtered_df = filtered_df[filtered_df['toxicity_score'] <= toxicity_threshold]
149
+
150
+ return filtered_df
151
+
152
+ def save_data(self, df, output_path=None):
153
+ """
154
+ İşlenmiş veriyi kaydeder.
155
+
156
+ Args:
157
+ df: Kaydedilecek veri çerçevesi
158
+ output_path: Çıktı dosyası yolu (belirtilmezse otomatik oluşturulur)
159
+
160
+ Returns:
161
+ str: Kaydedilen dosyanın yolu
162
+ """
163
+ if output_path is None:
164
+ # Varsayılan çıktı yolunu oluştur
165
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
166
+ output_dir = "data/processed"
167
+
168
+ # Klasörü oluştur
169
+ os.makedirs(output_dir, exist_ok=True)
170
+
171
+ output_path = os.path.join(output_dir, f"processed_data_{timestamp}.csv")
172
+
173
+ # Veriyi kaydet
174
+ df.to_csv(output_path, index=False)
175
+ logging.info(f"Veri başarıyla kaydedildi: {output_path}")
176
+
177
+ return output_path
utils/keyword_extractor.py ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import string
3
+ import numpy as np
4
+ from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
5
+ from collections import Counter
6
+ import logging
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ class KeywordExtractor:
12
+ """
13
+ Metin içindeki anahtar kelimeleri çıkaran sınıf.
14
+ TF-IDF, rakip kelimeler ve diğer metotlarla anahtar kelime çıkarma işlemi yapar.
15
+ """
16
+
17
+ def __init__(self):
18
+ """Anahtar kelime çıkarıcıyı başlatır"""
19
+ # Türkçe stopwords (durma kelimeleri)
20
+ self.turkish_stopwords = [
21
+ 've', 'veya', 'ile', 'için', 'bu', 'bir', 'ya', 'de', 'da', 'ki', 'ne', 'her', 'çok',
22
+ 'daha', 'ama', 'fakat', 'lakin', 'ancak', 'gibi', 'kadar', 'sonra', 'önce', 'göre',
23
+ 'nasıl', 'neden', 'şey', 'ben', 'sen', 'o', 'biz', 'siz', 'onlar', 'kendi', 'aynı',
24
+ 'ise', 'mi', 'mı', 'mu', 'mü', 'hem', 'değil', 'hiç', 'olarak', 'evet', 'hayır',
25
+ 'belki', 'tüm', 'yani', 'hep', 'şu', 'şey', 'tabi', 'tamam', 'bunlar', 'şunlar',
26
+ 'böyle', 'öyle', 'şöyle', 'iki', 'üç', 'dört', 'beş', 'altı', 'yedi', 'sekiz', 'dokuz',
27
+ 'on', 'yüz', 'bin', 'milyon', 'milyar', 'var', 'yok', 'oldu', 'olur', 'oluyor', 'olacak'
28
+ ]
29
+
30
+ # İngilizce stopwords (durma kelimeleri)
31
+ self.english_stopwords = [
32
+ 'the', 'and', 'a', 'to', 'of', 'in', 'for', 'with', 'on', 'at', 'by', 'from', 'about',
33
+ 'as', 'into', 'like', 'through', 'after', 'over', 'between', 'out', 'against', 'during',
34
+ 'without', 'before', 'under', 'around', 'among', 'is', 'are', 'was', 'were', 'be', 'been',
35
+ 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'shall', 'should',
36
+ 'may', 'might', 'must', 'can', 'could', 'i', 'you', 'he', 'she', 'it', 'we', 'they',
37
+ 'me', 'him', 'her', 'us', 'them', 'who', 'which', 'whose', 'whom', 'this', 'that', 'these',
38
+ 'those', 'am', 'is', 'are', 'was', 'were', 'an', 'my', 'your', 'his', 'its', 'our', 'their'
39
+ ]
40
+
41
+ # Tüm stopwords listesini birleştir
42
+ self.stopwords = set(self.turkish_stopwords + self.english_stopwords)
43
+
44
+ # TF-IDF vektörleyici
45
+ self.tfidf_vectorizer = TfidfVectorizer(
46
+ max_df=0.9,
47
+ min_df=2,
48
+ max_features=200,
49
+ stop_words=self.stopwords,
50
+ ngram_range=(1, 2) # Tek kelimeler ve ikili kelime grupları
51
+ )
52
+
53
+ # Sayısal karakter ve noktalama işaretlerini temizlemek için regex pattern
54
+ self.cleanup_pattern = re.compile(f'[{re.escape(string.punctuation)}]|[0-9]')
55
+
56
+ logger.info("Anahtar kelime çıkarıcı başlatıldı")
57
+
58
+ def preprocess_text(self, text):
59
+ """
60
+ Metni anahtar kelime çıkarma için ön işleme tabi tutar
61
+
62
+ Args:
63
+ text: İşlenecek metin
64
+
65
+ Returns:
66
+ str: Temizlenmiş metin
67
+ """
68
+ if not text:
69
+ return ""
70
+
71
+ # Küçük harfe çevir
72
+ text = text.lower()
73
+
74
+ # Noktalama işaretlerini temizle
75
+ text = self.cleanup_pattern.sub(' ', text)
76
+
77
+ # Fazla boşlukları temizle
78
+ text = re.sub(r'\s+', ' ', text).strip()
79
+
80
+ return text
81
+
82
+ def extract_keywords_tfidf(self, text, num_keywords=5):
83
+ """
84
+ TF-IDF kullanarak metinden anahtar kelimeleri çıkarır
85
+
86
+ Args:
87
+ text: Anahtar kelimeleri çıkarılacak metin
88
+ num_keywords: Çıkarılacak anahtar kelime sayısı
89
+
90
+ Returns:
91
+ list: [(anahtar_kelime, skor), ...] formatında liste
92
+ """
93
+ try:
94
+ if not text or len(text.strip()) < 10:
95
+ return []
96
+
97
+ # Metni ön işle
98
+ processed_text = self.preprocess_text(text)
99
+
100
+ # TF-IDF matrix oluştur
101
+ tfidf_matrix = self.tfidf_vectorizer.fit_transform([processed_text])
102
+
103
+ # Feature isimleri (kelimeler)
104
+ feature_names = self.tfidf_vectorizer.get_feature_names_out()
105
+
106
+ # Kelimelerin TF-IDF skorlarını hesapla ve sırala
107
+ tfidf_scores = zip(feature_names, tfidf_matrix.toarray()[0])
108
+ sorted_scores = sorted(tfidf_scores, key=lambda x: x[1], reverse=True)
109
+
110
+ # En yüksek skorlu kelimeleri seç
111
+ top_keywords = sorted_scores[:num_keywords]
112
+
113
+ return top_keywords
114
+
115
+ except Exception as e:
116
+ logger.error(f"TF-IDF anahtar kelime çıkarma hatası: {str(e)}")
117
+ return []
118
+
119
+ def extract_keywords_textrank(self, text, num_keywords=5):
120
+ """
121
+ TextRank benzeri bir algoritma ile anahtar kelimeleri çıkarır
122
+
123
+ Args:
124
+ text: Anahtar kelimeleri çıkarılacak metin
125
+ num_keywords: Çıkarılacak anahtar kelime sayısı
126
+
127
+ Returns:
128
+ list: [(anahtar_kelime, skor), ...] formatında liste
129
+ """
130
+ try:
131
+ if not text or len(text.strip()) < 10:
132
+ return []
133
+
134
+ # Metni ön işle
135
+ processed_text = self.preprocess_text(text)
136
+
137
+ # Kelimeleri ayır
138
+ words = processed_text.split()
139
+
140
+ # Stopwords olmayan kelimeleri filtrele
141
+ filtered_words = [word for word in words if word not in self.stopwords and len(word) > 2]
142
+
143
+ # Kelime frekanslarını hesapla
144
+ word_freq = Counter(filtered_words)
145
+
146
+ # En sık geçen kelimeleri seç
147
+ most_common = word_freq.most_common(num_keywords * 2) # Daha fazla al, sonra filtreleyeceğiz
148
+
149
+ # TF-IDF skorlaması ile benzer bir yaklaşım uygula
150
+ # Kelime sıklığının logaritması * kelimenin benzersizliği
151
+ scored_words = []
152
+ for word, count in most_common:
153
+ # Benzersizlik faktörü: Toplam kelime sayısı / kelimenin sıklığı
154
+ uniqueness = len(filtered_words) / (count + 1)
155
+ # Skor hesapla
156
+ score = np.log(count + 1) * uniqueness
157
+ scored_words.append((word, score))
158
+
159
+ # Skorlara göre sırala
160
+ scored_words.sort(key=lambda x: x[1], reverse=True)
161
+
162
+ return scored_words[:num_keywords]
163
+
164
+ except Exception as e:
165
+ logger.error(f"TextRank anahtar kelime çıkarma hatası: {str(e)}")
166
+ return []
167
+
168
+ def extract_bigrams(self, text, num_bigrams=3):
169
+ """
170
+ Metinden ikili kelime gruplarını (bigram) çıkarır
171
+
172
+ Args:
173
+ text: Anahtar kelimeleri çıkarılacak metin
174
+ num_bigrams: Çıkarılacak bigram sayısı
175
+
176
+ Returns:
177
+ list: [(bigram, skor), ...] formatında liste
178
+ """
179
+ try:
180
+ if not text or len(text.strip()) < 10:
181
+ return []
182
+
183
+ # Metni ön işle
184
+ processed_text = self.preprocess_text(text)
185
+
186
+ # Bigram için vektörleyici
187
+ bigram_vectorizer = CountVectorizer(
188
+ ngram_range=(2, 2),
189
+ stop_words=self.stopwords,
190
+ max_features=100
191
+ )
192
+
193
+ # Bigram matrix oluştur
194
+ bigram_matrix = bigram_vectorizer.fit_transform([processed_text])
195
+
196
+ # Feature isimleri (bigramlar)
197
+ feature_names = bigram_vectorizer.get_feature_names_out()
198
+
199
+ # Bigramların skorlarını hesapla ve sırala
200
+ bigram_scores = zip(feature_names, bigram_matrix.toarray()[0])
201
+ sorted_scores = sorted(bigram_scores, key=lambda x: x[1], reverse=True)
202
+
203
+ # En yüksek skorlu bigramları seç
204
+ top_bigrams = sorted_scores[:num_bigrams]
205
+
206
+ return top_bigrams
207
+
208
+ except Exception as e:
209
+ logger.error(f"Bigram çıkarma hatası: {str(e)}")
210
+ return []
211
+
212
+ def extract_keywords(self, text, method='combined', num_keywords=10):
213
+ """
214
+ Metinden anahtar kelimeleri çıkarır
215
+
216
+ Args:
217
+ text: Anahtar kelimeleri çıkarılacak metin
218
+ method: Kullanılacak metod ('tfidf', 'textrank', 'combined')
219
+ num_keywords: Toplam çıkarılacak anahtar kelime sayısı
220
+
221
+ Returns:
222
+ dict: {
223
+ 'keywords': [(anahtar_kelime, skor), ...],
224
+ 'bigrams': [(bigram, skor), ...],
225
+ 'method': kullanılan metod
226
+ }
227
+ """
228
+ if not text or len(text.strip()) < 10:
229
+ return {
230
+ 'keywords': [],
231
+ 'bigrams': [],
232
+ 'method': method
233
+ }
234
+
235
+ try:
236
+ keywords = []
237
+
238
+ if method == 'tfidf':
239
+ keywords = self.extract_keywords_tfidf(text, num_keywords)
240
+ elif method == 'textrank':
241
+ keywords = self.extract_keywords_textrank(text, num_keywords)
242
+ else: # combined
243
+ # TF-IDF ve TextRank sonuçlarını birleştir
244
+ tfidf_keywords = self.extract_keywords_tfidf(text, num_keywords // 2)
245
+ textrank_keywords = self.extract_keywords_textrank(text, num_keywords // 2)
246
+
247
+ # İki listeyi birleştir
248
+ combined = {}
249
+ for keyword, score in tfidf_keywords + textrank_keywords:
250
+ if keyword in combined:
251
+ combined[keyword] = max(combined[keyword], score)
252
+ else:
253
+ combined[keyword] = score
254
+
255
+ # En yüksek skorlu kelimeleri seç
256
+ keywords = sorted(combined.items(), key=lambda x: x[1], reverse=True)[:num_keywords]
257
+
258
+ # Bigramları da ekle
259
+ bigrams = self.extract_bigrams(text, num_keywords // 3)
260
+
261
+ return {
262
+ 'keywords': keywords,
263
+ 'bigrams': bigrams,
264
+ 'method': method
265
+ }
266
+
267
+ except Exception as e:
268
+ logger.error(f"Anahtar kelime çıkarma hatası: {str(e)}")
269
+ return {
270
+ 'keywords': [],
271
+ 'bigrams': [],
272
+ 'method': method
273
+ }
utils/language_detector.py ADDED
@@ -0,0 +1,260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ from collections import Counter
3
+ import logging
4
+
5
+ logger = logging.getLogger(__name__)
6
+
7
+
8
+ class LanguageDetector:
9
+ """
10
+ Metin dilini algılayan sınıf.
11
+ İstatistiksel yöntemlerle metinlerin dilini tespit eder.
12
+ """
13
+
14
+ def __init__(self):
15
+ """Dil algılama sınıfını başlatır"""
16
+ # Dil tanıma için tipik karakter ve kelime varlıkları
17
+ self.language_profiles = {
18
+ 'tr': {
19
+ 'chars': 'abcçdefgğhıijklmnoöprsştuüvyz',
20
+ 'unique_chars': 'çğıöşü',
21
+ 'common_words': [
22
+ 've', 'bir', 'bu', 'da', 'de', 'için', 'ile', 'ben', 'sen', 'o',
23
+ 'biz', 'siz', 'ama', 'ki', 'ya', 'çok', 'daha', 'en', 'ne', 'kadar',
24
+ 'var', 'yok', 'mı', 'mi', 'mu', 'mü', 'gibi', 'olarak', 'çünkü',
25
+ 'sonra', 'önce', 'nasıl', 'neden', 'evet', 'hayır', 'ise', 'veya'
26
+ ]
27
+ },
28
+ 'en': {
29
+ 'chars': 'abcdefghijklmnopqrstuvwxyz',
30
+ 'unique_chars': 'qwxz',
31
+ 'common_words': [
32
+ 'the', 'and', 'a', 'to', 'of', 'in', 'is', 'you', 'that', 'it',
33
+ 'he', 'was', 'for', 'on', 'are', 'as', 'with', 'his', 'they',
34
+ 'at', 'be', 'this', 'have', 'from', 'or', 'one', 'had', 'by',
35
+ 'but', 'not', 'what', 'all', 'were', 'we', 'when', 'your', 'can',
36
+ 'there', 'if', 'more', 'an', 'who'
37
+ ]
38
+ },
39
+ 'de': {
40
+ 'chars': 'abcdefghijklmnopqrstuvwxyzäöüß',
41
+ 'unique_chars': 'äöüß',
42
+ 'common_words': [
43
+ 'der', 'die', 'das', 'und', 'in', 'zu', 'den', 'mit', 'auf', 'für',
44
+ 'ist', 'im', 'dem', 'nicht', 'ein', 'eine', 'als', 'auch', 'es',
45
+ 'von', 'sich', 'oder', 'so', 'zum', 'bei', 'eines', 'nur', 'am',
46
+ 'werden', 'noch', 'wie', 'einer', 'aber', 'aus', 'wenn', 'doch'
47
+ ]
48
+ },
49
+ 'fr': {
50
+ 'chars': 'abcdefghijklmnopqrstuvwxyzàâçéèêëîïôùûü',
51
+ 'unique_chars': 'àâçéèêëîïôùûü',
52
+ 'common_words': [
53
+ 'le', 'la', 'les', 'de', 'des', 'un', 'une', 'et', 'est', 'en',
54
+ 'du', 'dans', 'qui', 'que', 'pour', 'pas', 'sur', 'ce', 'vous',
55
+ 'avec', 'au', 'il', 'je', 'sont', 'mais', 'nous', 'si', 'plus',
56
+ 'leur', 'par', 'ont', 'ou', 'comme', 'elle', 'tout', 'même'
57
+ ]
58
+ },
59
+ 'es': {
60
+ 'chars': 'abcdefghijklmnopqrstuvwxyzáéíóúüñ',
61
+ 'unique_chars': 'áéíóúüñ',
62
+ 'common_words': [
63
+ 'el', 'la', 'los', 'las', 'de', 'del', 'un', 'una', 'unos', 'unas',
64
+ 'y', 'e', 'o', 'u', 'que', 'en', 'a', 'con', 'por', 'para', 'es',
65
+ 'son', 'al', 'lo', 'su', 'sus', 'se', 'mi', 'me', 'te', 'nos',
66
+ 'como', 'pero', 'más', 'este', 'esta', 'esto'
67
+ ]
68
+ }
69
+ }
70
+
71
+ # Desteklenen diller
72
+ self.supported_languages = {
73
+ 'tr': 'Türkçe',
74
+ 'en': 'İngilizce',
75
+ 'de': 'Almanca',
76
+ 'fr': 'Fransızca',
77
+ 'es': 'İspanyolca',
78
+ 'unknown': 'Bilinmeyen'
79
+ }
80
+
81
+ logger.info("Dil algılama modülü başlatıldı")
82
+
83
+ def _clean_text(self, text):
84
+ """
85
+ Metni temizler
86
+
87
+ Args:
88
+ text: Temizlenecek metin
89
+
90
+ Returns:
91
+ str: Temizlenmiş metin
92
+ """
93
+ if not text:
94
+ return ""
95
+
96
+ # Küçük harfe çevir
97
+ text = text.lower()
98
+
99
+ # Sayıları ve özel karakterleri kaldır (dil karakterleri hariç)
100
+ text = re.sub(r'[0-9]', '', text)
101
+ text = re.sub(r'[^\w\s\u00C0-\u00FF\u0100-\u017F\u0400-\u04FF]', '', text)
102
+
103
+ return text
104
+
105
+ def _get_words(self, text):
106
+ """
107
+ Metinden kelimeleri çıkarır
108
+
109
+ Args:
110
+ text: Kelimesi çıkarılacak metin
111
+
112
+ Returns:
113
+ list: Kelimeler listesi
114
+ """
115
+ words = re.findall(r'\b\w+\b', text.lower())
116
+ return words
117
+
118
+ def _calculate_character_score(self, text, language):
119
+ """
120
+ Metindeki karakterlerin dile uygunluğunu hesaplar
121
+
122
+ Args:
123
+ text: Değerlendirilecek metin
124
+ language: Dil kodu
125
+
126
+ Returns:
127
+ float: Karakter skoru (0-1 arası)
128
+ """
129
+ if not text:
130
+ return 0.0
131
+
132
+ profile = self.language_profiles.get(language, {})
133
+ chars = profile.get('chars', '')
134
+ unique_chars = profile.get('unique_chars', '')
135
+
136
+ # Metin içindeki karakterleri say
137
+ char_count = Counter(text.lower())
138
+
139
+ # Dildeki karakterlerin metinde bulunma oranı
140
+ total_chars = sum(char_count.values())
141
+ if total_chars == 0:
142
+ return 0.0
143
+
144
+ matched_chars = sum(char_count.get(char, 0) for char in chars)
145
+ char_ratio = matched_chars / total_chars
146
+
147
+ # Dile özgü karakterlerin varlığını kontrol et
148
+ unique_char_present = any(char in text.lower() for char in unique_chars)
149
+ unique_bonus = 0.2 if unique_char_present else 0.0
150
+
151
+ return min(1.0, char_ratio + unique_bonus)
152
+
153
+ def _calculate_word_score(self, words, language):
154
+ """
155
+ Kelimelerin dile uygunluğunu hesaplar
156
+
157
+ Args:
158
+ words: Değerlendirilecek kelimeler listesi
159
+ language: Dil kodu
160
+
161
+ Returns:
162
+ float: Kelime skoru (0-1 arası)
163
+ """
164
+ if not words:
165
+ return 0.0
166
+
167
+ common_words = self.language_profiles.get(language, {}).get('common_words', [])
168
+
169
+ # Yaygın kelimelerin metinde bulunma sayısı
170
+ matched_words = sum(1 for word in words if word in common_words)
171
+
172
+ # Yaygın kelime oranı
173
+ word_ratio = matched_words / min(len(words), 100) # En fazla 100 kelime değerlendir
174
+
175
+ return word_ratio
176
+
177
+ def detect_language(self, text):
178
+ """
179
+ Metnin dilini tespit eder
180
+
181
+ Args:
182
+ text: Dili tespit edilecek metin
183
+
184
+ Returns:
185
+ dict: {
186
+ 'language_code': dil kodu (tr, en, vb),
187
+ 'language_name': dil adı,
188
+ 'confidence': güven skoru (0-1 arası),
189
+ 'scores': dil bazında skorlar
190
+ }
191
+ """
192
+ if not text or len(text.strip()) < 5:
193
+ return {
194
+ 'language_code': 'unknown',
195
+ 'language_name': self.supported_languages.get('unknown'),
196
+ 'confidence': 0.0,
197
+ 'scores': {}
198
+ }
199
+
200
+ try:
201
+ clean_text = self._clean_text(text)
202
+ words = self._get_words(clean_text)
203
+
204
+ scores = {}
205
+
206
+ # Her dil için skor hesapla
207
+ for lang_code in self.language_profiles.keys():
208
+ char_score = self._calculate_character_score(clean_text, lang_code)
209
+ word_score = self._calculate_word_score(words, lang_code)
210
+
211
+ # Ağırlıklı toplam (karakter:0.4, kelime:0.6)
212
+ total_score = (char_score * 0.4) + (word_score * 0.6)
213
+ scores[lang_code] = total_score
214
+
215
+ # En yüksek skorlu dili bul
216
+ if not scores:
217
+ detected_lang = 'unknown'
218
+ confidence = 0.0
219
+ else:
220
+ detected_lang = max(scores, key=scores.get)
221
+ confidence = scores[detected_lang]
222
+
223
+ # Eğer güven skoru çok düşükse "bilinmeyen" olarak işaretle
224
+ if confidence < 0.15:
225
+ detected_lang = 'unknown'
226
+ confidence = 0.0
227
+
228
+ return {
229
+ 'language_code': detected_lang,
230
+ 'language_name': self.supported_languages.get(detected_lang, self.supported_languages.get('unknown')),
231
+ 'confidence': confidence,
232
+ 'scores': scores
233
+ }
234
+
235
+ except Exception as e:
236
+ logger.error(f"Dil algılama hatası: {str(e)}")
237
+ return {
238
+ 'language_code': 'unknown',
239
+ 'language_name': self.supported_languages.get('unknown'),
240
+ 'confidence': 0.0,
241
+ 'scores': {}
242
+ }
243
+
244
+ def detect_languages_batch(self, texts):
245
+ """
246
+ Birden çok metnin dilini tespit eder
247
+
248
+ Args:
249
+ texts: Dilleri tespit edilecek metinler listesi
250
+
251
+ Returns:
252
+ list: Her metin için dil tespiti sonuçları
253
+ """
254
+ results = []
255
+
256
+ for text in texts:
257
+ result = self.detect_language(text)
258
+ results.append(result)
259
+
260
+ return results
utils/quality_scorer.py ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from transformers import pipeline
3
+ import torch
4
+ import logging
5
+ import re
6
+ from sklearn.feature_extraction.text import CountVectorizer
7
+
8
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
9
+
10
+
11
+ class QualityScorer:
12
+ def __init__(self, quality_pipeline=None):
13
+ """
14
+ Metin kalitesi değerlendirme sınıfını başlatır.
15
+
16
+ Args:
17
+ quality_pipeline: Metin özetleme pipeline'ı
18
+ """
19
+ self.pipeline = quality_pipeline
20
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
21
+
22
+ # Temel kalite ölçütleri için yardımcı araçlar
23
+ self.vectorizer = CountVectorizer(max_features=5000)
24
+
25
+ # Türkçeye özgü doldurma (filler) kelimeleri
26
+ self.turkish_filler_words = [
27
+ 'yani', 'işte', 'şey', 'falan', 'filan', 'hani', 'mesela', 'aslında',
28
+ 'ya', 'ki', 'de', 'da', 'çok', 'ama', 'fakat', 'lakin', 'ancak',
29
+ 'gerçekten', 'kesinlikle', 'tabii', 'tabi', 'şimdi', 'sonra', 'önce'
30
+ ]
31
+
32
+ def score_text(self, text):
33
+ """
34
+ Metin için kalite skoru hesaplar.
35
+
36
+ Args:
37
+ text: Değerlendirilecek metin
38
+
39
+ Returns:
40
+ float: 0 ile 1 arasında kalite skoru (1 = yüksek kalite)
41
+ """
42
+ if not text or len(text.strip()) == 0:
43
+ return 0.0, {}
44
+
45
+ # Metni temizle
46
+ text = text.strip()
47
+
48
+ # Çeşitli metin özelliklerini değerlendirelim
49
+ features = {}
50
+
51
+ # 1. Uzunluk puanı - Çok kısa veya çok uzun metinler düşük puan alır
52
+ length = len(text.split())
53
+ if length < 3:
54
+ features['length_score'] = 0.1
55
+ elif length < 5:
56
+ features['length_score'] = 0.2
57
+ elif length < 10:
58
+ features['length_score'] = 0.4
59
+ elif length < 20:
60
+ features['length_score'] = 0.6
61
+ elif length < 100:
62
+ features['length_score'] = 0.8
63
+ elif length < 500:
64
+ features['length_score'] = 1.0
65
+ elif length < 1000:
66
+ features['length_score'] = 0.8
67
+ else:
68
+ features['length_score'] = 0.6
69
+
70
+ # 2. Gramer ve yazım denetimi (Türkçe için uyarlanmış)
71
+ # Türkçede noktalama işaretleri ve büyük harf kullanımı
72
+ sentences = re.split(r'[.!?]+', text)
73
+ sentences = [s.strip() for s in sentences if s.strip()]
74
+
75
+ if not sentences:
76
+ features['grammar_score'] = 0.0
77
+ else:
78
+ # Cümlelerin büyük harfle başlayıp başlamadığını kontrol et
79
+ correct_caps = sum(1 for s in sentences if s and s[0].isupper())
80
+ caps_ratio = correct_caps / len(sentences) if sentences else 0
81
+
82
+ # Noktalama işaretlerinin varlığını kontrol et
83
+ punct_count = len(re.findall(r'[.!?,;:]', text))
84
+ expected_punct = max(1, len(sentences) - 1) # Beklenen minimum noktalama
85
+ punct_ratio = min(1.0, punct_count / expected_punct) if expected_punct > 0 else 0
86
+
87
+ # Türkçe'ye özgü yaygın yazım hatalarını kontrol et
88
+ common_errors = [
89
+ ('de da', 'de/da ayrı yazılmalı'),
90
+ ('ki', 'ki bağlacı ayrı yazılmalı'),
91
+ ('misin', 'soru eki ayrı yazılmalı'),
92
+ ('geldimi', 'soru eki ayrı yazılmalı'),
93
+ ('bişey', 'bir şey ayrı yazılmalı'),
94
+ ('herşey', 'her şey ayrı yazılmalı'),
95
+ ('hiçbirşey', 'hiçbir şey ayrı yazılmalı')
96
+ ]
97
+
98
+ error_count = sum(1 for error, _ in common_errors if error in text.lower())
99
+ error_ratio = 1.0 - min(1.0, error_count / (len(text.split()) / 10 + 1))
100
+
101
+ # Gramer puanını hesapla
102
+ features['grammar_score'] = (caps_ratio * 0.4 + punct_ratio * 0.3 + error_ratio * 0.3)
103
+
104
+ # 3. Kelime çeşitliliği (Türkçe için uyarlanmış)
105
+ words = re.findall(r'\b\w+\b', text.lower())
106
+ unique_words = set(words)
107
+ if not words:
108
+ features['diversity_score'] = 0.0
109
+ else:
110
+ # Türkçe metinler için çeşitlilik oranını ayarla
111
+ diversity_ratio = len(unique_words) / len(words)
112
+ # Türkçe'nin çekimli yapısı nedeniyle daha yüksek bir baz çeşitlilik beklenir
113
+ features['diversity_score'] = min(1.0, diversity_ratio * 1.2)
114
+
115
+ # 4. Özetlenebilirlik puanı - eğer pipeline varsa
116
+ if self.pipeline and len(text.split()) > 20:
117
+ try:
118
+ # Pipeline özetleme mi yoksa çeviri mi ona göre işle
119
+ if hasattr(self.pipeline, 'task') and self.pipeline.task == 'translation':
120
+ # Çeviri pipeline'ı, bu durumda çevirinin kalitesine bakma
121
+ translated = self.pipeline(text, max_length=100)[0]['translation_text']
122
+ features['summary_score'] = 0.7 # Varsayılan olarak iyi bir puan
123
+ else:
124
+ # Özetleme pipeline'ı
125
+ max_length = min(128, max(30, len(text.split()) // 4))
126
+ summary = self.pipeline(text, max_length=max_length, min_length=10, do_sample=False)[0][
127
+ 'generated_text']
128
+
129
+ # Özet ve orijinal metin arasındaki benzerliğe bakarak puan ver
130
+ summary_len = len(summary.split())
131
+ orig_len = len(text.split())
132
+
133
+ compression_ratio = summary_len / orig_len if orig_len > 0 else 0
134
+ if compression_ratio > 0.8 or compression_ratio < 0.05:
135
+ features['summary_score'] = 0.3
136
+ elif compression_ratio > 0.6 or compression_ratio < 0.1:
137
+ features['summary_score'] = 0.6
138
+ else:
139
+ features['summary_score'] = 0.9
140
+ except Exception as e:
141
+ logging.warning(f"Error during summarization: {str(e)}")
142
+ features['summary_score'] = 0.5
143
+ else:
144
+ features['summary_score'] = 0.5
145
+
146
+ # 5. Türkçe doldurma kelimeleri (filler words)
147
+ filler_count = sum(1 for word in words if word.lower() in self.turkish_filler_words)
148
+ if not words:
149
+ features['filler_score'] = 1.0
150
+ else:
151
+ filler_ratio = filler_count / len(words)
152
+ # Türkçede bazı doldurma kelimeleri doğal olabilir, bu yüzden daha toleranslı davran
153
+ features['filler_score'] = 1.0 - min(1.0, filler_ratio * 3)
154
+
155
+ # Puanları birleştir - farklı puanları ağırlıklandırarak
156
+ weights = {
157
+ 'length_score': 0.15,
158
+ 'grammar_score': 0.3, # Türkçe için gramere daha fazla ağırlık
159
+ 'diversity_score': 0.25,
160
+ 'summary_score': 0.2,
161
+ 'filler_score': 0.1
162
+ }
163
+
164
+ final_score = sum(features[key] * weights[key] for key in weights.keys())
165
+
166
+ # Puanı 0-1 aralığına normalize et
167
+ final_score = max(0.0, min(1.0, final_score))
168
+
169
+ return final_score, features
170
+
171
+ def batch_score(self, texts, batch_size=8):
172
+ """
173
+ Bir metin listesi için toplu kalite skoru hesaplar.
174
+
175
+ Args:
176
+ texts: Değerlendirilecek metin listesi
177
+ batch_size: İşlenecek grup boyutu
178
+
179
+ Returns:
180
+ list: Kalite skorları listesi
181
+ """
182
+ results = []
183
+ feature_results = []
184
+
185
+ for i in range(0, len(texts), batch_size):
186
+ batch_texts = texts[i:i + batch_size]
187
+ batch_results = []
188
+ batch_features = []
189
+
190
+ for text in batch_texts:
191
+ score, features = self.score_text(text)
192
+ batch_results.append(score)
193
+ batch_features.append(features)
194
+
195
+ results.extend(batch_results)
196
+ feature_results.extend(batch_features)
197
+
198
+ return results, feature_results
utils/sentiment_analyzer.py ADDED
@@ -0,0 +1,169 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
3
+ import numpy as np
4
+ import logging
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ class SentimentAnalyzer:
10
+ """
11
+ Türkçe metinler için duygu analizi yapan sınıf.
12
+ Pozitif, negatif ve nötr duygu skorları üreterek metnin duygusal tonunu analiz eder.
13
+ """
14
+
15
+ def __init__(self, model=None, tokenizer=None):
16
+ """
17
+ Duygu analizi modülünü başlatır.
18
+
19
+ Args:
20
+ model: Duygu analizi modeli (isteğe bağlı)
21
+ tokenizer: Model için tokenizer (isteğe bağlı)
22
+ """
23
+ self.model = model
24
+ self.tokenizer = tokenizer
25
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
26
+ self.model_info = {"name": "Bilinmeyen Model", "language": "unknown"}
27
+
28
+ if model is None or tokenizer is None:
29
+ logger.info("Duygu analizi için varsayılan model yükleniyor...")
30
+ self.load_default_model()
31
+
32
+ def load_default_model(self):
33
+ """Varsayılan duygu analizi modelini yükler"""
34
+ try:
35
+ # Türkçe duygu analizi modeli
36
+ model_name = "savasy/bert-base-turkish-sentiment"
37
+ logger.info(f"Türkçe duygu analizi modeli yükleniyor: {model_name}")
38
+
39
+ self.tokenizer = AutoTokenizer.from_pretrained(model_name)
40
+ self.model = AutoModelForSequenceClassification.from_pretrained(model_name)
41
+ self.model.to(self.device)
42
+
43
+ self.model_info = {
44
+ "name": model_name,
45
+ "description": "Türkçe duygu analizi modeli",
46
+ "language": "tr"
47
+ }
48
+
49
+ logger.info("Duygu analizi modeli başarıyla yüklendi")
50
+ return True
51
+ except Exception as e:
52
+ logger.error(f"Duygu analizi modeli yüklenemedi: {str(e)}")
53
+
54
+ # Yedek model dene
55
+ try:
56
+ backup_model = "dbmdz/bert-base-turkish-cased"
57
+ logger.info(f"Yedek Türkçe model deneniyor: {backup_model}")
58
+
59
+ self.tokenizer = AutoTokenizer.from_pretrained(backup_model)
60
+ self.model = AutoModelForSequenceClassification.from_pretrained(backup_model)
61
+ self.model.to(self.device)
62
+
63
+ self.model_info = {
64
+ "name": backup_model,
65
+ "description": "Genel amaçlı Türkçe BERT modeli",
66
+ "language": "tr"
67
+ }
68
+
69
+ logger.info("Yedek model başarıyla yüklendi")
70
+ return True
71
+ except Exception as e2:
72
+ logger.error(f"Yedek model yüklenemedi: {str(e2)}")
73
+ raise e2
74
+
75
+ def analyze_sentiment(self, text):
76
+ """
77
+ Metindeki duygu tonunu analiz eder.
78
+
79
+ Args:
80
+ text: Analiz edilecek metin
81
+
82
+ Returns:
83
+ dict: {
84
+ 'positive': float, # Pozitif duygu skoru (0-1)
85
+ 'neutral': float, # Nötr duygu skoru (0-1)
86
+ 'negative': float, # Negatif duygu skoru (0-1)
87
+ 'dominant': str # Baskın duygu (positive, neutral, negative)
88
+ 'score': float # -1 (çok negatif) ile 1 (çok pozitif) arasında genel skor
89
+ }
90
+ """
91
+ if not text or len(text.strip()) == 0:
92
+ return {
93
+ 'positive': 0.0,
94
+ 'neutral': 1.0,
95
+ 'negative': 0.0,
96
+ 'dominant': 'neutral',
97
+ 'score': 0.0
98
+ }
99
+
100
+ try:
101
+ # Metni tokenize et
102
+ inputs = self.tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
103
+ inputs = {key: val.to(self.device) for key, val in inputs.items()}
104
+
105
+ # Tahmin yap
106
+ with torch.no_grad():
107
+ outputs = self.model(**inputs)
108
+
109
+ # Sonuçları işle
110
+ logits = outputs.logits
111
+ probabilities = torch.softmax(logits, dim=1).cpu().numpy()[0]
112
+
113
+ # Model çıktısının formatına göre işlem yap
114
+ if len(probabilities) >= 3:
115
+ # 3 sınıflı model (negatif, nötr, pozitif)
116
+ result = {
117
+ 'negative': float(probabilities[0]),
118
+ 'neutral': float(probabilities[1]),
119
+ 'positive': float(probabilities[2])
120
+ }
121
+ else:
122
+ # 2 sınıflı model (negatif, pozitif)
123
+ result = {
124
+ 'negative': float(probabilities[0]),
125
+ 'neutral': 0.0,
126
+ 'positive': float(probabilities[1])
127
+ }
128
+
129
+ # Baskın duyguyu belirle
130
+ dominant_sentiment = max(result, key=result.get)
131
+ result['dominant'] = dominant_sentiment
132
+
133
+ # -1 ile 1 arasında genel bir skor hesapla
134
+ # -1: çok negatif, 0: nötr, 1: çok pozitif
135
+ weighted_score = result['positive'] - result['negative']
136
+ result['score'] = float(weighted_score)
137
+
138
+ return result
139
+
140
+ except Exception as e:
141
+ logger.error(f"Duygu analizi sırasında hata: {str(e)}")
142
+ # Hata durumunda nötr sonuç döndür
143
+ return {
144
+ 'positive': 0.0,
145
+ 'neutral': 1.0,
146
+ 'negative': 0.0,
147
+ 'dominant': 'neutral',
148
+ 'score': 0.0
149
+ }
150
+
151
+ def batch_analyze(self, texts, batch_size=8):
152
+ """
153
+ Bir metin listesi için toplu duygu analizi yapar.
154
+
155
+ Args:
156
+ texts: Analiz edilecek metin listesi
157
+ batch_size: İşlenecek grup boyutu
158
+
159
+ Returns:
160
+ list: Her metin için duygu analizi sonuçları
161
+ """
162
+ results = []
163
+
164
+ for i in range(0, len(texts), batch_size):
165
+ batch_texts = texts[i:i + batch_size]
166
+ batch_results = [self.analyze_sentiment(text) for text in batch_texts]
167
+ results.extend(batch_results)
168
+
169
+ return results
utils/text_improver.py ADDED
@@ -0,0 +1,337 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import logging
3
+ import string
4
+ from collections import Counter
5
+
6
+ logger = logging.getLogger(__name__)
7
+
8
+
9
+ class TextImprover:
10
+ """
11
+ Türkçe metinlerde iyileştirme ve öneriler sunan sınıf.
12
+ Yazım hataları düzeltme, dilbilgisi önerileri ve okunabilirlik analizi yapar.
13
+ """
14
+
15
+ def __init__(self):
16
+ """Metin iyileştirme sınıfını başlatır"""
17
+ # Türkçe'de yaygın yazım hataları ve düzeltmeleri
18
+ self.common_typos = {
19
+ # Büyük küçük harf duyarsız olarak yazım hataları
20
+ 'bişey': 'bir şey',
21
+ 'herşey': 'her şey',
22
+ 'hiçbirşey': 'hiçbir şey',
23
+ 'birsey': 'bir şey',
24
+ 'hersey': 'her şey',
25
+ 'hicbir': 'hiçbir',
26
+ 'hicbirsey': 'hiçbir şey',
27
+ 'yalnız': 'yalnız',
28
+ 'bi': 'bir',
29
+ 'gelicek': 'gelecek',
30
+ 'gidiyom': 'gidiyorum',
31
+ 'yapıyom': 'yapıyorum',
32
+ 'biliyomusun': 'biliyor musun',
33
+ 'napıyorsun': 'ne yapıyorsun',
34
+ 'naber': 'ne haber',
35
+ 'bilmiyomki': 'bilmiyorum ki',
36
+ 'dicek': 'diyecek',
37
+ 'dicem': 'diyeceğim',
38
+ 'yicek': 'yiyecek',
39
+ 'yicem': 'yiyeceğim'
40
+ }
41
+
42
+ # Türkçe'de sık kullanılan doldurma kelimeleri
43
+ self.filler_words = [
44
+ 'yani', 'işte', 'şey', 'falan', 'filan', 'hani', 'mesela',
45
+ 'aslında', 'ya', 'ki', 'de', 'da', 'ama', 'fakat', 'lakin',
46
+ 'gerçekten', 'kesinlikle', 'tabii', 'tabi', 'şimdi', 'sonra'
47
+ ]
48
+
49
+ # Türkçe cümle karmaşıklığını değerlendirmek için parametreler
50
+ self.max_sentence_length = 25 # Kelime sayısı
51
+ self.max_word_length = 6 # Ortalama kelime uzunluğu
52
+
53
+ # Okunabilirlik için kullanılacak parametreler
54
+ # Türkçe için uyarlanmış Flesch Reading Ease formülü
55
+ self.readability_thresholds = {
56
+ 'çok_kolay': 90,
57
+ 'kolay': 80,
58
+ 'orta_kolay': 70,
59
+ 'orta': 60,
60
+ 'orta_zor': 50,
61
+ 'zor': 30,
62
+ 'çok_zor': 0
63
+ }
64
+
65
+ logger.info("Metin iyileştirme modülü başlatıldı")
66
+
67
+ def fix_typos(self, text):
68
+ """
69
+ Metindeki yaygın yazım hatalarını düzeltir
70
+
71
+ Args:
72
+ text: Düzeltilecek metin
73
+
74
+ Returns:
75
+ dict: {
76
+ 'corrected_text': str, # Düzeltilmiş metin
77
+ 'corrections': list, # Yapılan düzeltmeler listesi
78
+ 'correction_count': int # Düzeltme sayısı
79
+ }
80
+ """
81
+ corrected_text = text
82
+ corrections = []
83
+
84
+ # Önce metni kelimelere ayır
85
+ words = re.findall(r'\b\w+\b', text.lower())
86
+
87
+ # Her kelimeyi kontrol et
88
+ for word in words:
89
+ if word.lower() in self.common_typos:
90
+ correct_word = self.common_typos[word.lower()]
91
+ # Kelimenin metindeki tüm örneklerini düzelt
92
+ # \b ile kelime sınırlarını belirt
93
+ pattern = r'\b' + re.escape(word) + r'\b'
94
+ corrected_text = re.sub(pattern, correct_word, corrected_text, flags=re.IGNORECASE)
95
+ corrections.append(f"'{word}' -> '{correct_word}'")
96
+
97
+ return {
98
+ 'corrected_text': corrected_text,
99
+ 'corrections': corrections,
100
+ 'correction_count': len(corrections)
101
+ }
102
+
103
+ def check_grammar(self, text):
104
+ """
105
+ Metindeki temel dilbilgisi sorunlarını kontrol eder
106
+
107
+ Args:
108
+ text: Kontrol edilecek metin
109
+
110
+ Returns:
111
+ dict: {
112
+ 'issues': list, # Tespit edilen sorunlar listesi
113
+ 'suggestions': list, # Öneriler listesi
114
+ 'issue_count': int # Sorun sayısı
115
+ }
116
+ """
117
+ issues = []
118
+ suggestions = []
119
+
120
+ # Cümlelere ayır
121
+ sentences = re.split(r'[.!?]+', text)
122
+ sentences = [s.strip() for s in sentences if s.strip()]
123
+
124
+ for i, sentence in enumerate(sentences):
125
+ # Büyük harfle başlama kontrolü
126
+ if sentence and not sentence[0].isupper():
127
+ issues.append(f"Cümle {i + 1}: Büyük harfle başlamıyor")
128
+ suggestions.append(f"Cümle {i + 1}: '{sentence[0]}' -> '{sentence[0].upper()}'")
129
+
130
+ # Cümle uzunluğu kontrolü
131
+ words = sentence.split()
132
+ if len(words) > self.max_sentence_length:
133
+ issues.append(f"Cümle {i + 1}: Çok uzun ({len(words)} kelime)")
134
+ suggestions.append(f"Cümle {i + 1}: Daha kısa cümlelere bölmeyi düşünün")
135
+
136
+ # Noktalama kontrolü
137
+ if i < len(sentences) - 1: # Son cümle değilse
138
+ if not text.find(sentence + ".") and not text.find(sentence + "!") and not text.find(sentence + "?"):
139
+ issues.append(f"Cümle {i + 1}: Noktalama işareti eksik olabilir")
140
+ suggestions.append(f"Cümle {i + 1}: Cümle sonuna uygun noktalama işareti ekleyin")
141
+
142
+ return {
143
+ 'issues': issues,
144
+ 'suggestions': suggestions,
145
+ 'issue_count': len(issues)
146
+ }
147
+
148
+ def reduce_filler_words(self, text):
149
+ """
150
+ Metindeki doldurma kelimelerini tespit eder ve azaltma önerileri sunar
151
+
152
+ Args:
153
+ text: İyileştirilecek metin
154
+
155
+ Returns:
156
+ dict: {
157
+ 'filler_words': list, # Bulunan doldurma kelimeleri
158
+ 'filler_count': int, # Doldurma kelimesi sayısı
159
+ 'suggested_text': str # Önerilen iyileştirilmiş metin
160
+ }
161
+ """
162
+ # Metindeki kelimeleri bul
163
+ words = re.findall(r'\b\w+\b', text.lower())
164
+
165
+ # Doldurma kelimelerini ve sayılarını say
166
+ filler_counter = Counter()
167
+ for word in words:
168
+ if word.lower() in self.filler_words:
169
+ filler_counter[word.lower()] += 1
170
+
171
+ # Metni kelime kelime işle ve fazla doldurma kelimelerini kaldır
172
+ suggested_text = text
173
+ for filler_word, count in filler_counter.items():
174
+ if count > 1: # Birden fazla geçiyorsa
175
+ # Her bir örneği bul
176
+ occurrences = list(re.finditer(r'\b' + re.escape(filler_word) + r'\b', suggested_text, re.IGNORECASE))
177
+
178
+ # İlk geçtiği yer hariç diğerlerini kaldır
179
+ for occurrence in occurrences[1:]:
180
+ start, end = occurrence.span()
181
+ # Eğer kelimenin önünde veya arkasında boşluk varsa, onu da kaldır
182
+ if start > 0 and suggested_text[start - 1] == ' ':
183
+ start -= 1
184
+ suggested_text = suggested_text[:start] + suggested_text[end:]
185
+
186
+ return {
187
+ 'filler_words': list(filler_counter.keys()),
188
+ 'filler_count': sum(filler_counter.values()),
189
+ 'suggested_text': suggested_text
190
+ }
191
+
192
+ def calculate_readability(self, text):
193
+ """
194
+ Metnin okunabilirlik skorunu hesaplar (Türkçe'ye uyarlanmış Flesch Reading Ease)
195
+
196
+ Args:
197
+ text: Değerlendirilecek metin
198
+
199
+ Returns:
200
+ dict: {
201
+ 'score': float, # Okunabilirlik skoru (0-100)
202
+ 'level': str, # Okunabilirlik seviyesi
203
+ 'avg_sentence_length': float, # Ortalama cümle uzunluğu
204
+ 'avg_word_length': float # Ortalama kelime uzunluğu
205
+ }
206
+ """
207
+ # Cümleleri ve kelimeleri ayır
208
+ sentences = re.split(r'[.!?]+', text)
209
+ sentences = [s.strip() for s in sentences if s.strip()]
210
+
211
+ total_words = 0
212
+ total_syllables = 0
213
+
214
+ for sentence in sentences:
215
+ words = re.findall(r'\b\w+\b', sentence)
216
+ total_words += len(words)
217
+
218
+ # Türkçe heceleri kabaca hesapla (sesli harf sayısı)
219
+ for word in words:
220
+ # Türkçedeki sesli harfler
221
+ vowels = 'aeıioöuüAEIİOÖUÜ'
222
+ syllable_count = sum(1 for char in word if char in vowels)
223
+ # En az bir hece olmalı
224
+ syllable_count = max(1, syllable_count)
225
+ total_syllables += syllable_count
226
+
227
+ # Hesaplamalar
228
+ if len(sentences) == 0 or total_words == 0:
229
+ return {
230
+ 'score': 100, # Boş metin - en kolay
231
+ 'level': 'çok_kolay',
232
+ 'avg_sentence_length': 0,
233
+ 'avg_word_length': 0
234
+ }
235
+
236
+ avg_sentence_length = total_words / len(sentences)
237
+ avg_syllables_per_word = total_syllables / total_words
238
+
239
+ # Türkçe için uyarlanmış Flesch Reading Ease
240
+ # (orijinal formül: 206.835 - 1.015 * ASL - 84.6 * ASW)
241
+ # Türkçe için katsayılar ayarlandı
242
+ readability_score = 206.835 - (1.3 * avg_sentence_length) - (60.0 * avg_syllables_per_word)
243
+
244
+ # Skoru 0-100 aralığına sınırla
245
+ readability_score = max(0, min(100, readability_score))
246
+
247
+ # Seviyeyi belirle
248
+ level = 'çok_zor'
249
+ for threshold_level, threshold_value in sorted(self.readability_thresholds.items(), key=lambda x: x[1]):
250
+ if readability_score >= threshold_value:
251
+ level = threshold_level
252
+ break
253
+
254
+ return {
255
+ 'score': float(readability_score),
256
+ 'level': level,
257
+ 'avg_sentence_length': float(avg_sentence_length),
258
+ 'avg_word_length': float(avg_syllables_per_word)
259
+ }
260
+
261
+ def improve_text(self, text):
262
+ """
263
+ Metni kapsamlı şekilde analiz eder ve iyileştirme önerileri sunar
264
+
265
+ Args:
266
+ text: İyileştirilecek metin
267
+
268
+ Returns:
269
+ dict: Tüm iyileştirme analizlerini içeren sonuçlar
270
+ """
271
+ if not text or len(text.strip()) == 0:
272
+ return {
273
+ 'corrected_text': text,
274
+ 'suggestions': [],
275
+ 'readability': {
276
+ 'score': 100,
277
+ 'level': 'çok_kolay'
278
+ },
279
+ 'improvement_count': 0
280
+ }
281
+
282
+ try:
283
+ # Yazım hatalarını düzelt
284
+ typo_results = self.fix_typos(text)
285
+
286
+ # Dilbilgisi kontrolü
287
+ grammar_results = self.check_grammar(typo_results['corrected_text'])
288
+
289
+ # Doldurma kelimelerini azalt
290
+ filler_results = self.reduce_filler_words(typo_results['corrected_text'])
291
+
292
+ # Okunabilirlik hesapla
293
+ readability_results = self.calculate_readability(text)
294
+
295
+ # Tüm önerileri birleştir
296
+ all_suggestions = []
297
+ all_suggestions.extend([f"Yazım düzeltmesi: {correction}" for correction in typo_results['corrections']])
298
+ all_suggestions.extend(
299
+ [f"Dilbilgisi önerisi: {suggestion}" for suggestion in grammar_results['suggestions']])
300
+
301
+ if filler_results['filler_count'] > 0:
302
+ all_suggestions.append(f"Doldurma kelimelerini azaltın: {', '.join(filler_results['filler_words'])}")
303
+
304
+ # Okunabilirlik önerisi
305
+ if readability_results['score'] < 60: # Orta seviyenin altında ise
306
+ if readability_results['avg_sentence_length'] > 15:
307
+ all_suggestions.append("Daha kısa cümleler kullanın (ortalama cümle uzunluğu yüksek)")
308
+ if readability_results['avg_word_length'] > 2.5:
309
+ all_suggestions.append("Daha basit kelimeler kullanmayı deneyin (ortalama hece sayısı yüksek)")
310
+
311
+ # Birleştirilmiş sonuç
312
+ return {
313
+ 'original_text': text,
314
+ 'corrected_text': typo_results['corrected_text'],
315
+ 'improved_text': filler_results['suggested_text'],
316
+ 'suggestions': all_suggestions,
317
+ 'readability': {
318
+ 'score': readability_results['score'],
319
+ 'level': readability_results['level'],
320
+ 'avg_sentence_length': readability_results['avg_sentence_length']
321
+ },
322
+ 'improvement_count': len(all_suggestions)
323
+ }
324
+
325
+ except Exception as e:
326
+ logger.error(f"Metin iyileştirme sırasında hata: {str(e)}")
327
+ return {
328
+ 'original_text': text,
329
+ 'corrected_text': text,
330
+ 'improved_text': text,
331
+ 'suggestions': ["Metin analizi sırasında bir hata oluştu"],
332
+ 'readability': {
333
+ 'score': 0,
334
+ 'level': 'bilinmiyor'
335
+ },
336
+ 'improvement_count': 0
337
+ }
utils/toxicity_scorer.py ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import numpy as np
3
+ from transformers import AutoTokenizer, AutoModelForSequenceClassification
4
+ import logging
5
+ import re
6
+
7
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
8
+
9
+
10
+ class ToxicityScorer:
11
+ def __init__(self, model=None, tokenizer=None):
12
+ """
13
+ Toxicity Scorer sınıfını başlatır.
14
+
15
+ Args:
16
+ model: Zararlılık modeli
17
+ tokenizer: Model için tokenizer
18
+ """
19
+ self.model = model
20
+ self.tokenizer = tokenizer
21
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
22
+ self.is_turkish_model = False
23
+
24
+ if model is None or tokenizer is None:
25
+ logging.warning("No toxicity model provided. Using default model.")
26
+ self.load_default_model()
27
+
28
+ def load_default_model(self):
29
+ """
30
+ Varsayılan zararlılık modelini yükler
31
+ """
32
+ try:
33
+ # Öncelikle Türkçe duygu analizi modeli deneyelim
34
+ model_name = "savasy/bert-base-turkish-sentiment"
35
+ logging.info(f"Loading Turkish sentiment model: {model_name}")
36
+ self.tokenizer = AutoTokenizer.from_pretrained(model_name)
37
+ self.model = AutoModelForSequenceClassification.from_pretrained(model_name)
38
+ self.model.to(self.device)
39
+ self.is_turkish_model = True
40
+ logging.info("Turkish sentiment model loaded successfully")
41
+ except Exception as e:
42
+ logging.error(f"Error loading Turkish model: {str(e)}")
43
+ try:
44
+ # Yedek olarak genel model yükleyelim
45
+ backup_model = "dbmdz/bert-base-turkish-cased"
46
+ logging.info(f"Trying Turkish BERT model: {backup_model}")
47
+ self.tokenizer = AutoTokenizer.from_pretrained(backup_model)
48
+ self.model = AutoModelForSequenceClassification.from_pretrained(backup_model)
49
+ self.model.to(self.device)
50
+ self.is_turkish_model = True
51
+ logging.info("Turkish BERT model loaded successfully")
52
+ except Exception as e2:
53
+ logging.error(f"Error loading Turkish BERT model: {str(e2)}")
54
+ try:
55
+ # Son çare olarak İngilizce model kullanalım
56
+ english_model = "distilbert/distilbert-base-uncased-finetuned-sst-2-english"
57
+ logging.info(f"Trying English sentiment model: {english_model}")
58
+ self.tokenizer = AutoTokenizer.from_pretrained(english_model)
59
+ self.model = AutoModelForSequenceClassification.from_pretrained(english_model)
60
+ self.model.to(self.device)
61
+ self.is_turkish_model = False
62
+ logging.info("English sentiment model loaded successfully")
63
+ except Exception as e3:
64
+ logging.error(f"Error loading English model: {str(e3)}")
65
+ raise e3
66
+
67
+ def _contains_turkish_profanity(self, text):
68
+ """
69
+ Temel Türkçe küfür ve hakaret kontrolü yapar
70
+ """
71
+ # Türkçede yaygın küfür/hakaret içeren kelimelerin listesi
72
+ turkish_profanity = [
73
+ 'aptal', 'salak', 'gerizekalı', 'ahmak', 'enayi', 'mal', 'geri zekalı',
74
+ 'beyinsiz', 'budala', 'adi', 'ahlaksız', 'şerefsiz', 'haysiyetsiz',
75
+ 'orospu', 'piç', 'yavşak', 'sürtük', 'sürtüğü', 'gavat', 'şerefsiz',
76
+ 'siktir', 'pezevenk', 'namussuz'
77
+ ]
78
+
79
+ # Noktalama işaretlerini ve sayıları kaldır
80
+ text = re.sub(r'[^\w\s]', '', text.lower())
81
+ text = re.sub(r'\d+', '', text)
82
+ words = text.split()
83
+
84
+ # Metinde küfür/hakaret var mı kontrol et
85
+ for word in turkish_profanity:
86
+ if word in words:
87
+ return True
88
+
89
+ return False
90
+
91
+ def _contains_negative_words(self, text):
92
+ """
93
+ Temel Türkçe olumsuz kelime kontrolü yapar
94
+ """
95
+ # Türkçede yaygın olumsuz kelimeler
96
+ negative_words = [
97
+ 'kötü', 'berbat', 'rezalet', 'korkunç', 'iğrenç', 'üzücü', 'acı',
98
+ 'başarısız', 'yetersiz', 'düşük', 'zayıf', 'korkutucu', 'tehlikeli',
99
+ 'nefret', 'öfke', 'saldırgan', 'yanlış', 'hata', 'hayal kırıklığı'
100
+ ]
101
+
102
+ text = text.lower()
103
+ count = sum(1 for word in negative_words if word in text.split())
104
+
105
+ # Olumsuz kelime yoğunluğunu hesapla
106
+ return count / len(text.split()) if text.split() else 0
107
+
108
+ def score_text(self, text):
109
+ """
110
+ Metin için zararlılık skoru hesaplar.
111
+
112
+ Args:
113
+ text: Değerlendirilecek metin
114
+
115
+ Returns:
116
+ float: 0 ile 1 arasında zararlılık skoru (1 = çok zararlı)
117
+ """
118
+ if not text or len(text.strip()) == 0:
119
+ return 0.0
120
+
121
+ # Temel kural tabanlı kontroller
122
+ profanity_detected = self._contains_turkish_profanity(text)
123
+ negative_ratio = self._contains_negative_words(text)
124
+
125
+ if profanity_detected:
126
+ base_score = 0.8 # Küfür/hakaret varsa yüksek başlangıç skoru
127
+ else:
128
+ base_score = negative_ratio * 0.5 # Olumsuz kelime yoğunluğuna göre skor
129
+
130
+ try:
131
+ # Model tabanlı skorlama
132
+ inputs = self.tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
133
+ inputs = {key: val.to(self.device) for key, val in inputs.items()}
134
+
135
+ with torch.no_grad():
136
+ outputs = self.model(**inputs)
137
+
138
+ # Modele göre doğru şekilde skoru alalım
139
+ if self.is_turkish_model:
140
+ # Türkçe duygu analizi modeli için özel işlem
141
+ probs = torch.softmax(outputs.logits, dim=1).cpu().numpy()[0]
142
+
143
+ # savasy/bert-base-turkish-sentiment için:
144
+ # 0: negative, 1: neutral, 2: positive
145
+ if len(probs) >= 3:
146
+ # Negatif olasılığını zararlılık skoru olarak kullan ama çok yüksek değerler üretmemesi için 0.7 ile çarp
147
+ model_score = probs[0] * 0.7
148
+ else:
149
+ # İki sınıflı model için
150
+ model_score = probs[0] * 0.6
151
+ else:
152
+ # İngilizce model için
153
+ probs = torch.softmax(outputs.logits, dim=1).cpu().numpy()[0]
154
+ # İngilizce modeller genellikle Türkçe için çok yüksek sonuçlar verir, bu yüzden 0.5 ile çarp
155
+ model_score = probs[0] * 0.5
156
+
157
+ # Kural tabanlı skor ve model skor birleşimi
158
+ final_score = (base_score * 0.4) + (model_score * 0.6)
159
+
160
+ # 0-1 aralığına sınırla
161
+ final_score = max(0.0, min(1.0, final_score))
162
+
163
+ return final_score
164
+
165
+ except Exception as e:
166
+ logging.error(f"Error scoring toxicity: {str(e)}")
167
+ # Hata durumunda sadece kural tabanlı skoru döndür
168
+ return min(base_score, 1.0)
169
+
170
+ def batch_score(self, texts, batch_size=16):
171
+ """
172
+ Bir metin listesi için toplu zararlılık skoru hesaplar.
173
+
174
+ Args:
175
+ texts: Değerlendirilecek metin listesi
176
+ batch_size: İşlenecek grup boyutu
177
+
178
+ Returns:
179
+ list: Zararlılık skorları listesi
180
+ """
181
+ results = []
182
+
183
+ for i in range(0, len(texts), batch_size):
184
+ batch_texts = texts[i:i + batch_size]
185
+ batch_scores = [self.score_text(text) for text in batch_texts]
186
+ results.extend(batch_scores)
187
+
188
+ return results