Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import torch | |
| from torch.nn.functional import softmax | |
| from transformers import AutoTokenizer, AutoModelForSequenceClassification | |
| MODEL_ID = "celalkartoglu/tr-sentiment-bert-win-v1" | |
| LABELS = ["Negative","Notr","Positive"] | |
| MAX_LEN_DEFAULT = 256 | |
| tokenizer = AutoTokenizer.from_pretrained(MODEL_ID) | |
| model = AutoModelForSequenceClassification.from_pretrained(MODEL_ID) | |
| model.eval() | |
| def postprocess_label(probs, min_conf=0.55, close_gap=0.10): | |
| neg, notr, pos = probs | |
| top = probs.argmax() | |
| # düşük güven: Notr | |
| if probs[top] < min_conf: | |
| return "Notr" | |
| # Neg ve Pos birbirine çok yakınsa: Notr | |
| if abs(neg - pos) < close_gap and (notr >= 0.30 or probs[top] < 0.60): | |
| return "Notr" | |
| return LABELS[int(top)] | |
| def infer_one(text, max_len=MAX_LEN_DEFAULT, use_rule=False, min_conf=0.55, close_gap=0.10): | |
| text = (text or "").strip() | |
| if not text: | |
| return "", {"Negative": 0.0, "Notr": 0.0, "Positive": 0.0} | |
| inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=int(max_len)) | |
| logits = model(**inputs).logits | |
| probs = softmax(logits, dim=-1).squeeze().tolist() | |
| scores = {LABELS[i]: float(probs[i]) for i in range(3)} | |
| label = postprocess_label(torch.tensor(probs), min_conf, close_gap) if use_rule else LABELS[int(torch.tensor(probs).argmax())] | |
| return label, scores | |
| def infer_batch(texts, max_len=MAX_LEN_DEFAULT, use_rule=False, min_conf=0.55, close_gap=0.10): | |
| if isinstance(texts, str): | |
| rows = [t.strip() for t in texts.split("\n") if t.strip()] | |
| else: | |
| rows = texts or [] | |
| results = [] | |
| for t in rows: | |
| lbl, scores = infer_one(t, max_len, use_rule, min_conf, close_gap) | |
| results.append([t, lbl, scores["Negative"], scores["Notr"], scores["Positive"]]) | |
| return results | |
| def export_csv(table): | |
| import pandas as pd | |
| df = pd.DataFrame(table, columns=["text","label","Negative","Notr","Positive"]) | |
| return gr.File.update(value=df.to_csv(index=False).encode("utf-8"), visible=True) | |
| theme = gr.themes.Soft(primary_hue="blue", neutral_hue="slate") | |
| CUSTOM_CSS = """ | |
| #title h1 { font-weight: 800; letter-spacing: -0.02em; } | |
| footer {opacity:.7} | |
| """ | |
| with gr.Blocks(theme=theme, css=CUSTOM_CSS, title="Türkçe Duygu Analizi | BERT") as demo: | |
| with gr.Row(): | |
| with gr.Column(scale=7): | |
| gr.Markdown( | |
| "<div id='title'>" | |
| "<h1>🇹🇷 Türkçe Duygu Analizi</h1>" | |
| "<p>Model: <b>celalkartoglu/tr-sentiment-bert-win-v1</b> | Sınıflar: <b>Negative</b>, <b>Notr</b>, <b>Positive</b></p>" | |
| "</div>" | |
| ) | |
| with gr.Column(scale=5, min_width=280): | |
| with gr.Group(): | |
| use_rule = gr.Checkbox(value=False, label="Notr kuralını uygula (düşük güven & Neg-Pos yakınlığı)") | |
| min_conf = gr.Slider(0.30, 0.85, value=0.55, step=0.01, label="Min güven (Notr eşiği)") | |
| close_gap = gr.Slider(0.00, 0.50, value=0.10, step=0.01, label="Neg-Pos yakınlık eşiği") | |
| max_len = gr.Slider(64, 384, value=MAX_LEN_DEFAULT, step=8, label="Maks. token uzunluğu") | |
| with gr.Tabs(): | |
| with gr.Tab("Tek Cümle"): | |
| with gr.Row(): | |
| with gr.Column(scale=7): | |
| txt = gr.Textbox(label="Cümle gir", placeholder="Örn: Film gerçekten çok kötüydü.", lines=3) | |
| with gr.Row(): | |
| btn = gr.Button("Tahmin Et", variant="primary") | |
| clr = gr.Button("Temizle") | |
| with gr.Column(scale=5): | |
| out_label = gr.Label(label="Tahmin") | |
| out_scores = gr.Label(label="Olasılıklar (P)") | |
| gr.Markdown("Örnekler") | |
| gr.Examples( | |
| examples=[ | |
| ["Bu yemek harikaydı, ellerinize sağlık."], | |
| ["Ne çok iyi ne de çok kötüydü."], | |
| ["Film gerçekten çok kötüydü."], | |
| ["Ürün zamanında teslim edildi, paketleme normaldi."], | |
| ["Beklediğim gibi değildi ama kötü de sayılmaz."] | |
| ], | |
| inputs=txt | |
| ) | |
| btn.click( | |
| fn=infer_one, | |
| inputs=[txt, max_len, use_rule, min_conf, close_gap], | |
| outputs=[out_label, out_scores] | |
| ) | |
| clr.click(lambda: ("", {"Negative":0.0,"Notr":0.0,"Positive":0.0}), outputs=[out_label, out_scores]) | |
| with gr.Tab("Çoklu Cümle (satır satır)"): | |
| with gr.Row(): | |
| with gr.Column(scale=7): | |
| multi = gr.Textbox(lines=10, label="Her satıra bir cümle yaz") | |
| btn2 = gr.Button("Toplu Tahmin", variant="primary") | |
| with gr.Column(scale=5): | |
| table = gr.Dataframe(headers=["text","label","Negative","Notr","Positive"], wrap=True, interactive=False, label="Sonuçlar") | |
| with gr.Row(): | |
| to_csv = gr.Button("CSV Olarak İndir") | |
| csv_file = gr.File(label="İndirilebilir CSV", visible=False) | |
| btn2.click( | |
| fn=infer_batch, | |
| inputs=[multi, max_len, use_rule, min_conf, close_gap], | |
| outputs=table | |
| ) | |
| to_csv.click(export_csv, inputs=table, outputs=csv_file) | |
| gr.Markdown("---") | |
| gr.Markdown("💡 İpucu: Nötr cümleler negatif/pozitife kayıyorsa, sağdaki <b>Notr kuralı</b> ayarlarını kullanarak dengeleyebilirsin.") | |
| gr.Markdown("© 2025 • Gradio ile oluşturuldu") | |
| demo.queue().launch() | |