Download app.py from apak/MISC_NER: direct link, hf CLI and curl.
- Browser
- Download file 19.3 kB
-
https://huggingface.co/spaces/apak/MISC_NER/resolve/main/app.py
- Command line
-
hf download hf://spaces/apak/MISC_NER/app.py
-
curl -L -o app.py https://huggingface.co/spaces/apak/MISC_NER/resolve/main/app.py
19.3 kB
| import torch | |
| import wikipedia | |
| from transformers import pipeline | |
| import gradio as gr | |
| import os | |
| import re | |
| # import bitsandbytes # Lokal çalıştırmada gereklidir | |
| # --- Global Log Listesini Tanımla --- | |
| _initial_logs = [] | |
| # --- 1. İlk Tarama Modeli (Hızlı NER) --- | |
| _initial_logs.append("1. Standart NER Modeli yükleniyor...") | |
| device_id = 0 if torch.cuda.is_available() else -1 | |
| ner_pipe = pipeline( | |
| "ner", | |
| model="xlm-roberta-large-finetuned-conll03-english", | |
| aggregation_strategy="simple", | |
| device=device_id | |
| ) | |
| _initial_logs.append("✅ Standart NER Modeli Hazır.") | |
| # --- 2. Akıl Yürütme Modeli (LLM - RAG Karar Verici) --- | |
| _initial_logs.append("2. LLM (Karar Verici) yükleniyor...") | |
| model_id = "Qwen/Qwen2.5-1.5B-Instruct" | |
| llm_model_kwargs = {} | |
| IS_LLM_ENABLED = False | |
| # Cihaz ayarlarını ve optimizasyonları yapma | |
| if torch.cuda.is_available(): | |
| llm_model_kwargs["torch_dtype"] = torch.bfloat16 | |
| llm_model_kwargs["load_in_4bit"] = True | |
| llm_device_map = "auto" | |
| _initial_logs.append("CUDA/GPU desteği bulundu, model **bfloat16 & 4-bit kuantizasyon** ile yüklenecek.") | |
| else: | |
| _initial_logs.append("CUDA desteği bulunamadı, model CPU üzerinde float32 ile yüklenecek.") | |
| llm_device_map = "cpu" | |
| # Hata Düzeltme: 'trust_remote_code' buradan KALDIRILDI. | |
| # llm_model_kwargs["trust_remote_code"] = True # ❌ Bu satır HATAYA neden oluyordu! | |
| try: | |
| gen_pipe = pipeline( | |
| "text-generation", | |
| model=model_id, | |
| model_kwargs=llm_model_kwargs, | |
| device_map=llm_device_map, | |
| trust_remote_code=True # ✅ Buraya taşındı, artık tekil değer olarak iletiliyor. | |
| ) | |
| IS_LLM_ENABLED = True | |
| _initial_logs.append("✅ LLM (Karar Verici) Başarıyla Yüklendi.") | |
| except Exception as e: | |
| _initial_logs.append(f"❌ LLM yüklenirken kritik hata: {e}") | |
| gen_pipe = None | |
| IS_LLM_ENABLED = False | |
| _initial_logs.append("⚠️ LLM devre dışı bırakıldı. Uygulamayı sadece Standart NER yapacaktır.") | |
| _initial_logs.append("✅ Modeller Hazır!") | |
| # --- Geri kalan tüm kod (Wikipedia Fonksiyonu, LLM Rafine Etme, Pipeline, Etiketler, Gradio Arayüzü) önceki versiyonunuzdaki gibi aynı kalır. --- | |
| # Not: Tam kodun çalışması için bu noktadan sonraki kısımları aynen eklemelisiniz. | |
| # ... (Wikipedia Fonksiyonu) | |
| wikipedia.set_lang("tr") | |
| def get_wiki_summary(term): | |
| """Wikipedia'dan bir terim için özet bilgi çekme.""" | |
| try: | |
| page = wikipedia.page(term, auto_suggest=True, redirect=True) | |
| return wikipedia.summary(page.title, sentences=3, auto_suggest=False) | |
| except (wikipedia.exceptions.PageError, | |
| wikipedia.exceptions.RedirectError, | |
| wikipedia.exceptions.DisambiguationError, | |
| Exception): | |
| return None | |
| # --- LLM ile Etiket Güncelleme Fonksiyonu --- | |
| def refine_label_with_llm(entity_text, wiki_context, custom_label_definitions): | |
| """LLM kullanarak MISC etiketini özel etiketlerden biriyle değiştirme.""" | |
| if not IS_LLM_ENABLED or gen_pipe is None: | |
| return "MISC", "LLM devre dışı olduğu için güncellenemedi." | |
| refinable_labels = {k: v for k, v in custom_label_definitions.items() if k not in ["PER", "LOC", "ORG", "MISC"]} | |
| label_definitions_str = "\n".join( | |
| [f"- {k}: {v}" for k, v in refinable_labels.items()] | |
| ) | |
| few_shot_examples = """ÖRNEK 1 (NORP İÇİN): | |
| VARLIK: Türk | |
| BAĞLAM: Türkler, Türkiye Cumhuriyeti'nde yaşayan ve Türkçe konuşan büyük bir etnik gruptur. (NORP tanımı ile eşleşir.) | |
| GÖZLEM: 'Türk' varlığı bir etnik grubu veya milliyeti ifade ediyor. Bağlamda 'etnik grup' ve 'Türkiye Cumhuriyeti' geçiyor. | |
| AKIL YÜRÜTME: Etnik veya dini grupları tanımlayan etiket 'NORP'dur. 'Türk' varlığı bu tanıma uymaktadır. | |
| CEVAP: NORP | |
| ÖRNEK 2 (LANGUAGE İÇİN): | |
| VARLIK: Türkçe | |
| BAĞLAM: Türkçenin ilk yazılı örnekleri Orhun Yazıtları'dır. Türk dilleri koluna ait bir dildir. Dünya çapında en çok konuşulan 20. dildir. (LANGUAGE tanımı ile eşleşir.) | |
| GÖZLEM: 'Türkçe', bir iletişim aracı olan dili ifade ediyor. Bağlamda 'Türk dilleri koluna ait bir dildir' ifadesi geçiyor. | |
| AKIL YÜRÜTME: İnsan dillerinin adlarını tanımlayan etiket 'LANGUAGE'dir. Varlık tanımıyla tam olarak eşleşmektedir. | |
| CEVAP: LANGUAGE | |
| ÖRNEK 3 (BOOK İÇİN): | |
| VARLIK: Sinekli Bakkal | |
| BAĞLAM: Halide Edib Adıvar'ın 1935'te yayınlanan ve popüler kültürde önemli yer tutan romanıdır. Eser, II. Dünya Savaşı öncesi İstanbul'u anlatır. (BOOK tanımı ile eşleşir.) | |
| GÖZLEM: 'Sinekli Bakkal' yazar adı ve yayın tarihi ile birlikte bir 'roman' olarak anılıyor. Bağlamda 'romanıdır' ifadesi geçiyor. | |
| AKIL YÜRÜTME: Yazılı veya basılı eserleri, romanları ve yayınları tanımlayan etiket 'BOOK'tur. Bu tanıma uymaktadır. | |
| CEVAP: BOOK | |
| ÖRNEK 4 (TITLE İÇİN): | |
| VARLIK: General | |
| BAĞLAM: Birçok orduda yüksek rütbeli bir subay unvanıdır. Türkiye'de en yüksek rütbelerden biridir. (TITLE tanımı ile eşleşir.) | |
| GÖZLEM: 'General' bir rütbe, unvan veya pozisyon belirtiyor. Bağlamda 'yüksek rütbeli bir subay unvanı' ifadesi geçiyor. | |
| AKIL YÜRÜTME: Kişinin unvanını, rütbesini veya pozisyonunu tanımlayan etiket 'TITLE'dır. 'General' bu tanıma uymaktadır. | |
| CEVAP: TITLE | |
| """ | |
| prompt = f"""Sen uzman bir veri sınıflandırma sistemisin. | |
| Görevin, aşağıdaki varlığı ve bağlamı analiz ederek, **YALNIZCA** ETİKET TANIMLARI'ndan birini (veya uymuyorsa MISC'i) seçmektir. | |
| AKIL YÜRÜTME ZİNCİRİNİ (Chain of Thought) takip ederek adım adım karar ver: | |
| 1. GÖZLEM: Varlık ve Wiki Bağlamı arasındaki anahtar eşleşmeleri (isim, tarih, unvan, tür, yayıncı vb.) listele. | |
| 2. AKIL YÜRÜTME: Bu gözlemlerin, **ETİKET TANIMLARI**'ndan hangisine **EN YAKIN** olduğunu gerekçesiyle açıkla. | |
| 3. KARAR: Yalnızca uygun olan ETİKET'i (veya MISC'i) ÇIKTI FORMATI'na uygun olarak belirt. **MISC'i sadece hiçbir tanıma uymuyorsa kullan.** | |
| {few_shot_examples} | |
| --- | |
| ETİKET TANIMLARI:{label_definitions_str} | |
| --- | |
| GÖREV VARLIĞI: | |
| VARLIK: {entity_text} | |
| BAĞLAM (Wikipedia): {wiki_context} | |
| ÇIKTI FORMATI: | |
| GÖZLEM: [Tespit edilen anahtar özellikler] | |
| AKIL YÜRÜTME: [Gözlemlerin hangi tanıma uyduğu ve neden] | |
| CEVAP: [YALNIZCA SEÇİLEN ETİKETİ BÜYÜK HARFLERLE YAZ. ÖRNEK: BOOK veya LANGUAGE. SAKIN CEVAP KISMINA BAŞKA BİR ŞEY EKLEME.] | |
| """ | |
| messages = [{"role": "user", "content": prompt}] | |
| try: | |
| outputs = gen_pipe( | |
| messages, | |
| max_new_tokens=150, | |
| do_sample=False, | |
| temperature=0.1 | |
| ) | |
| full_output = outputs[0]["generated_text"][-1]["content"].strip() | |
| except Exception as e: | |
| return "MISC", f"LLM çalışma zamanı hatası: {e}" | |
| final_label = "MISC" | |
| cog_output = "" | |
| valid_labels = list(custom_label_definitions.keys()) | |
| try: | |
| match_cevap = re.search(r'CEVAP:\s*([A-Z_]+)', full_output, re.IGNORECASE) | |
| if match_cevap: | |
| raw_label = match_cevap.group(1).strip().upper() | |
| if raw_label in valid_labels: | |
| final_label = raw_label | |
| match_reasoning = re.search(r'AKIL YÜRÜTME:\s*(.*)', full_output, re.IGNORECASE | re.DOTALL) | |
| if match_reasoning: | |
| cog_output = match_reasoning.group(1).strip().replace('\n', ' ') | |
| except Exception: | |
| pass | |
| return final_label, cog_output | |
| # --- NER Pipeline Fonksiyonu (RAG KISITLAMASI İLE) --- | |
| def advanced_ner_pipeline(text, target_labels, progress=gr.Progress()): | |
| """ | |
| Standart NER modelini çalıştırır. RAG/LLM süreci SADECE MISC etiketleri için çalışır. | |
| """ | |
| log_messages = list(_initial_logs) | |
| try: | |
| initial_results = ner_pipe(text) | |
| except Exception as e: | |
| log_messages.append(f"❌ Hata: Temel NER modelinde sorun oluştu: {e}") | |
| yield log_messages, [] | |
| return | |
| final_results = [] | |
| total_entities = len(initial_results) | |
| log_messages.append(f"🕵️♀️ Toplam {total_entities} varlık inceleniyor...") | |
| yield log_messages, None | |
| STANDARD_LABELS = ["PER", "LOC", "ORG"] | |
| for i, entity in enumerate(initial_results): | |
| word = entity['word'] | |
| label = entity['entity_group'] | |
| score = entity['score'] | |
| current_progress = (i + 1) / total_entities | |
| progress(current_progress, desc=f"İşleniyor: {word}") | |
| result_obj = { | |
| "entity": word, | |
| "initial_label": label, | |
| "initial_score": score, | |
| "final_label": label, | |
| "source": "Model", | |
| "reasoning": "" | |
| } | |
| # RAG KARAR NOKTASI: YALNIZCA MISC VE LLM AÇIKSA RAG YAP | |
| if label == "MISC" and IS_LLM_ENABLED: | |
| standardized_word = word.title() | |
| log_messages.append(f" ⚠️ MISC tespit edildi: '{word}' (Skor: {score:.2f}). Wikipedia'ya soruluyor...") | |
| yield log_messages, None | |
| wiki_context = get_wiki_summary(standardized_word) | |
| if wiki_context: | |
| log_messages.append(f" 📄 Wiki Bağlamı bulundu: {wiki_context[:50]}...") | |
| yield log_messages, None | |
| new_label, reasoning = refine_label_with_llm(standardized_word, wiki_context, target_labels) | |
| result_obj["final_label"] = new_label | |
| result_obj["source"] = "RAG+LLM (CoT)" | |
| result_obj["reasoning"] = reasoning | |
| log_messages.append(f" 🔄 Etiket Güncellendi: MISC -> {new_label}") | |
| if reasoning: | |
| log_messages.append(f" 💡 Akıl Yürütme: {reasoning[:70]}...") | |
| yield log_messages, None | |
| else: | |
| log_messages.append(" ❌ Wiki'de bilgi bulunamadı, MISC olarak kalıyor.") | |
| yield log_messages, None | |
| # Standart etiketler için loglama (RAG atlanır) | |
| elif label in STANDARD_LABELS: | |
| log_messages.append(f" ➡️ {word} etiket: {label} (Standart etiket, RAG atlandı.) (Skor: {score:.2f})") | |
| yield log_messages, None | |
| # Diğer standart etiketler (MISC ve standart 3'lü dışında) | |
| else: | |
| log_messages.append(f" ✅ {word} için etiket: {label} (Skor: {score:.2f})") | |
| yield log_messages, None | |
| final_results.append(result_obj) | |
| log_messages.append("✅ İşlem tamamlandı! Nihai sonuçlar tabloda.") | |
| yield log_messages, final_results | |
| # --- Özel Etiket Tanımları --- | |
| custom_label_definitions = { | |
| # Standart CoNLL-2003 etiketleri | |
| "PER": "Kişi adları, takma adlar, ünlüler.", | |
| "ORG": "Şirketler, kurumlar, hükümet kuruluşları.", | |
| "LOC": "Coğrafi yerler, siyasi bölgeler, binalar.", | |
| # MISC içinden rafine edilecek ince taneli etiketler | |
| "DATE": "Mutlak veya göreceli tarih ifadeleri (yıl, ay, gün).", | |
| "TIME": "Günün saati, zaman aralığı.", | |
| "MONEY": "Parasal değerler, para birimleri.", | |
| "QUANTITY": "Ağırlık, uzunluk, hacim gibi ölçü birimleri ve sayısal değerler.", | |
| "PERCENT": "Yüzdelik ifadeler.", | |
| "NORP": "Milliyetler, etnik, dini veya politik gruplar.", | |
| "LAW": "Resmi kanun, yasa, yönetmelik veya hukuki belge adı.", | |
| "EVENT": "Savaşlar, festivaller, spor turnuvaları, doğal afetler veya kurumsal etkinlikler.", | |
| "BOOK": "Yazılı veya basılı bir eser, yayınlanmış roman, ders kitabı.", | |
| "MOVIE": "Sinema filmi, dizi, belgesel gibi görsel-işitsel yapıt.", | |
| "SONG": "Müzik eseri, şarkı, beste veya albüm adı.", | |
| "ART": "Resim, heykel, spesifik mimari eser adı (Örn: Mona Lisa).", | |
| "AWARD": "Ödül, madalya veya nişan adı (Örn: Nobel, Oscar).", | |
| "PRODUCT": "Ticari olarak satılan somut bir eşya, model, cihaz veya marka serisi (Örn: iPhone 15 Pro, Mercedes C200).", | |
| "SOFTWARE": "Bilgisayar programları, mobil uygulamalar, yapay zeka sistemleri, işletim sistemleri.", | |
| "ORG_SUB": "Şirket birimleri, üniversite fakülteleri, dernek şubeleri gibi büyük bir kurumun alt birimleri.", | |
| "LANGUAGE": "Dillerin adı (Örn: İngilizce, Arapça).", | |
| "TITLE": "Kişinin unvanı, rütbesini veya pozisyonunu (Örn: Profesör, General, Başkan).", | |
| "CYBER": "URL, E-posta adresi, IP adresi, hashtag veya kullanıcı adı.", | |
| # MISC: Kalan her şey | |
| "MISC": "Diğer adlandırılmış varlıklar (LLM tarafından rafine edilemeyen veya uymayanlar)." | |
| } | |
| # --- Gradio Arayüzü Fonksiyonu --- | |
| def process_ner_request(text, progress=gr.Progress()): | |
| """ | |
| Gradio arayüzünden çağrılan ana fonksiyondur. | |
| """ | |
| all_logs = [] | |
| final_results = None | |
| for logs_step, results_step in advanced_ner_pipeline(text, custom_label_definitions, progress=progress): | |
| all_logs = logs_step | |
| if results_step is not None: | |
| final_results = results_step | |
| log_output_html = "<div style='max-height: 200px; overflow-y: scroll; border: 1px solid #eee; padding: 10px; margin-bottom: 10px; background-color: #f9f9f9; border-radius: 8px; font-family: monospace; font-size: 12px;'>" | |
| for log in all_logs: | |
| color = 'blue' if '✅' in log else ('orange' if '⚠️' in log else ('red' if '❌' in log else ('gray' if '➡️' in log else 'black'))) | |
| log_output_html += f"<p style='margin: 0; color: {color};'>{log.replace(' ', ' ')}</p>" | |
| log_output_html += "</div>" | |
| current_results_html = "" | |
| if final_results: | |
| color_map = { | |
| "PER": "background-color: #f8c291;", | |
| "ORG": "background-color: #b3c99f;", | |
| "LOC": "background-color: #a2c4c9;", | |
| "MISC": "background-color: #fef08a; font-weight: bold;", | |
| "DATE": "background-color: #e5ccff;", | |
| "TIME": "background-color: #d1d5db;", | |
| "MONEY": "background-color: #fcd34d;", | |
| "QUANTITY": "background-color: #bfdbfe;", | |
| "PERCENT": "background-color: #99f6e4;", | |
| "NORP": "background-color: #fbcfe8;", | |
| "LAW": "background-color: #f0abfc;", | |
| "EVENT": "background-color: #a7f3d0;", | |
| "BOOK": "background-color: #ffedd5;", | |
| "MOVIE": "background-color: #c7d2fe;", | |
| "SONG": "background-color: #e9d5ff;", | |
| "ART": "background-color: #bae6fd;", | |
| "AWARD": "background-color: #fee2e2;", | |
| "PRODUCT": "background-color: #ffc999;", | |
| "SOFTWARE": "background-color: #d1fae5;", | |
| "ORG_SUB": "background-color: #ccfbf1;", | |
| "LANGUAGE": "background-color: #fef9c3;", | |
| "TITLE": "background-color: #fecaca;", | |
| "CYBER": "background-color: #dbeafe;" | |
| } | |
| current_results_html = """ | |
| <style> | |
| .ner-table { width:100%; border-collapse: collapse; font-family: Arial, sans-serif; border-radius: 8px; overflow: hidden; box-shadow: 0 4px 6px rgba(0,0,0,0.1); } | |
| .ner-table th, .ner-table td { padding: 12px 15px; text-align: left; border-bottom: 1px solid #ddd; } | |
| .ner-table th { background-color: #3b82f6; color: white; font-weight: bold; text-transform: uppercase; } | |
| .ner-table tr:nth-child(even) { background-color: #f3f4f6; } | |
| .ner-table tr:hover { background-color: #e5e7eb; } | |
| .tooltip-text { visibility: hidden; background-color: #333; color: #fff; text-align: center; border-radius: 6px; padding: 5px 10px; position: absolute; z-index: 1; bottom: 125%; left: 50%; transform: translateX(-50%); opacity: 0; transition: opacity 0.3s; width: 300px; font-size: 11px; line-height: 1.4; } | |
| .tooltip-container:hover .tooltip-text { visibility: visible; opacity: 1; } | |
| .tooltip-container { position: relative; display: inline-block; } | |
| </style> | |
| <table class='ner-table'>\n""" | |
| current_results_html += " <tr>\n" | |
| current_results_html += " <th>VARLIK</th>\n" | |
| current_results_html += " <th>İLK ETİKET</th>\n" | |
| current_results_html += " <th>İLK TAHMİN SKORU</th>\n" | |
| current_results_html += " <th>RAG SONRASI ETİKETİ (GEREKÇE)</th>\n" | |
| current_results_html += " <th>KAYNAK</th>\n" | |
| current_results_html += " </tr>\n" | |
| for item in final_results: | |
| final_label_style = color_map.get(item['final_label'], "") | |
| reasoning_tooltip = "" | |
| if item.get("reasoning"): | |
| reasoning_tooltip = f""" | |
| <div class='tooltip-container'> | |
| {item['final_label']} | |
| <span class='tooltip-text'> | |
| CoT Akıl Yürütme: {item['reasoning']} | |
| </span> | |
| </div> | |
| """ | |
| else: | |
| reasoning_tooltip = item['final_label'] | |
| score_formatted = f"{item['initial_score'] * 100:.2f}%" | |
| score_color = "" | |
| if item['initial_score'] > 0.95: score_color = "color: #10b981;" | |
| elif item['initial_score'] > 0.8: score_color = "color: #f59e0b;" | |
| else: score_color = "color: #ef4444;" | |
| current_results_html += " <tr>\n" | |
| current_results_html += f" <td>{item['entity']}</td>\n" | |
| current_results_html += f" <td>{item['initial_label']}</td>\n" | |
| current_results_html += f" <td style='{score_color} font-weight: bold;'>{score_formatted}</td>\n" | |
| current_results_html += f" <td style='{final_label_style}'>{reasoning_tooltip}</td>\n" | |
| current_results_html += f" <td>{item['source']}</td>\n" | |
| current_results_html += " </tr>\n" | |
| current_results_html += "</table>" | |
| yield log_output_html, current_results_html | |
| # --- Gradio Arayüzünü Tanımla ve Başlat --- | |
| iface = gr.Interface( | |
| fn=process_ner_request, | |
| inputs=gr.Textbox(lines=5, placeholder="Metin giriniz...", label="Giriş Metni"), | |
| outputs=[gr.HTML(label="İşlem Logları"), gr.HTML(label="Güncellenmiş NER Sonuçları")], | |
| title="NER-Project v1", | |
| description="1to20forMISC", | |
| examples=[ | |
| ["Milli Eğitim Bakanlığı'na bağlı Lise Birimleri, 2024 Türkiye Kupası etkinliğine katılacak ve %15 indirim uygulayacak."], | |
| ["General Vural, Türkçe dilini kullanan Türk askerlerini, https://example.com üzerinden uyardı. 'Hürriyet Kasidesi' eserini okudu."], | |
| ["Windows 11 işletim sistemi, 1000 TL karşılığında satışa sunulmuştur. 'Cumhurbaşkanı' unvanına sahip kişi, Kanun maddesini değiştirdi."], | |
| ], | |
| analytics_enabled=False | |
| ) | |
| if __name__ == "__main__": | |
| port = int(os.environ.get('PORT', 7860)) | |
| iface.launch(share=False, server_port=port) |