nypgd's picture
Update README.md
b6fbca4 verified
|
Raw History Blame Contribute Delete
7.39 kB
metadata
language:
  - tr
tags:
  - named-entity-recognition
  - ner
  - turkish
  - bert
  - deprem
  - disaster
  - token-classification
  - earthquake
license: apache-2.0
base_model: dbmdz/bert-base-turkish-cased
datasets:
  - custom
metrics:
  - f1
  - precision
  - recall
model-index:
  - name: bert-turkish-deprem-ner
    results:
      - task:
          type: token-classification
          name: Named Entity Recognition
        metrics:
          - type: f1
            value: 0.9702
            name: F1 (Stacked 17k)
          - type: precision
            value: 0.968
            name: Precision
          - type: recall
            value: 0.9724
            name: Recall

BERT Türkçe Deprem Tweet NER Modeli

Deprem tweetlerinden kritik bilgileri otomatik olarak çıkarmak için SFT + Pseudo-Labeling (Stacking Ensemble) yöntemiyle fine-tune edilmiş NER modeli.

  • Base model: dbmdz/bert-base-turkish-cased
  • Eğitim: 500 altın standart → LLM pseudo-labeling → 17.000 Stacked veri seti
  • Veri seti: 6 Şubat 2023 Türkiye-Suriye depremi tweet'leri
  • Dil: Türkçe

Desteklenen Etiketler

Etiket Açıklama Örnek
LOC Konum / Adres Hatay Antakya Armutlu Sokak
PER Kişi adı Murat Filazoğlu
PHONE Telefon numarası 0539 218 3976
NEED İhtiyaç / Talep battaniye, jeneratör
ORG Kurum / Organizasyon AFAD, Kızılay
LINK URL bağlantısı https://t.co/...

⚠️ Önemli Not: Ön-İşleme (Tokenization)

Bu model eğitilirken metinler standart BERT tokenizer'ı ile değil, özel olarak NLTK TweetTokenizer kullanılarak kelimelere ayrılmıştır. Bu sayede @kullanici_adi, https://... gibi ifadeler ve telefon numaraları parçalanmadan tek bir bütün olarak ele alınır.

Hugging Face'in standart pipeline fonksiyonu metni agresif bir şekilde parçaladığı için URL'lerde ve kurumsal etiketlerde bölünmeler yaşatabilir. Tam ve doğru performans almak için aşağıdaki özel çıkarım (inference) kodunu kullanmanız tavsiye edilir.

Kullanım

Aşağıdaki kod bloğu, modelin eğitildiği formata uygun olarak metni işler, etiketleri birleştirir ve güven skorlarıyla (score) birlikte döndürür:

import torch
import nltk
from nltk.tokenize import TweetTokenizer
from transformers import AutoTokenizer, AutoModelForTokenClassification

# 1. NLTK TweetTokenizer'ı başlat (Link ve etiketleri korumak için gerekli)
nltk.download('punkt', quiet=True)
tknzr = TweetTokenizer(preserve_case=True, strip_handles=False, reduce_len=False)

# 2. Modeli ve Tokenizer'ı Hugging Face'den yükle
repo_id = "nypgd/bert-turkish-deprem-ner"
tokenizer = AutoTokenizer.from_pretrained(repo_id)
model = AutoModelForTokenClassification.from_pretrained(repo_id)
model.eval()
id2label = model.config.id2label

# 3. Özel Tahmin Fonksiyonu
def extract_entities(text):
    original_tokens = tknzr.tokenize(text)
    if not original_tokens: return []

    inputs = tokenizer(original_tokens, is_split_into_words=True, return_tensors="pt", truncation=True)
    with torch.no_grad():
        logits = model(**inputs).logits

    predictions = torch.argmax(logits, dim=2)[0]
    word_ids = inputs.word_ids()

    token_tags = []
    token_logit_idx = []
    prev_word_idx = None
    
    for sub_pos, (pred_id, word_idx) in enumerate(zip(predictions, word_ids)):
        if word_idx is None:
            continue
        if word_idx != prev_word_idx:
            token_tags.append(id2label[pred_id.item()])
            token_logit_idx.append(sub_pos)
        prev_word_idx = word_idx

    # Entity Birleştirme (Noktalama işaretlerini atlama mantığıyla)
    SKIP_TOKENS = {',', '-', ':', '.', '/', '(', ')'}
    entities = []
    current_ent = None

    for i, (token, tag) in enumerate(zip(original_tokens, token_tags)):
        if tag == 'O' and current_ent and token in SKIP_TOKENS:
            continue
        
        if tag == 'O':
            if current_ent: entities.append(current_ent)
            current_ent = None
            continue

        ent_type = tag[2:]
        score_val = round(torch.softmax(logits[0, token_logit_idx[i]], dim=-1).max().item(), 4)

        if tag.startswith('B-'):
            if current_ent: entities.append(current_ent)
            current_ent = {"entity_group": ent_type, "word": token, "score": score_val, "start": i, "end": i + 1}
        elif tag.startswith('I-') and current_ent and current_ent["entity_group"] == ent_type:
            current_ent["word"] += " " + token
            current_ent["end"] = i + 1
            current_ent["score"] = round(min(current_ent["score"], score_val), 4)
        else:
            if current_ent: entities.append(current_ent)
            current_ent = {"entity_group": ent_type, "word": token, "score": score_val, "start": i, "end": i + 1}

    if current_ent: entities.append(current_ent)
    return entities


# 4. Test Edelim
text = """
@AFADTurkiye ve Ahbap ekipleri, Gaziantep İslahiye Yeni Mahalle Karanfil Sokak No:5 adresinde 7 kişi enkaz altında mahsur kaldı. 
Acil ısıtıcı, çadır ve çocuk maması gerekiyor. Saha sorumlusu Ayşe Yurt iletişim: 0533 123 45 67.
 Konum ve detaylı bilgi için: https://t.co/yardimadresi
"""

results = extract_entities(text)
for ent in results:
    etiket = f"[{ent['entity_group']}]"
    print(f"{etiket:<9} {ent['word']}  (score: {ent['score']:.4f})")

print("[")
for i, ent in enumerate(results):
    virgul = "," if i < len(results) - 1 else ""
    print(f"  {ent}{virgul}")
print("]")


# Formatlı Çıktı
───────────────────────────────────────────────────────────────────────────────────
[ORG]    @AFADTurkiye  (score: 0.9999)
[ORG]    Ahbap  (score: 0.9996)
[LOC]    Gaziantep İslahiye Yeni Mahalle Karanfil Sokak No 5  (score: 0.9999)
[NEED]   ısıtıcı  (score: 0.9999)
[NEED]   çadır  (score: 0.9999)
[NEED]   çocuk maması  (score: 0.9997)
[PER]    Ayşe Yurt  (score: 0.9993)
[PHONE]  0533 123 45 67  (score: 0.9998)
[LINK]   https://t.co/yardimadresi  (score: 0.9999)
───────────────────────────────────────────────────────────────────────────────────

# Ham Çıktı (Tek satırlık liste formatı)
[
  {'entity_group': 'ORG', 'word': '@AFADTurkiye', 'score': 0.9999, 'start': 0, 'end': 1},
  {'entity_group': 'ORG', 'word': 'Ahbap', 'score': 0.9996, 'start': 2, 'end': 3},
  {'entity_group': 'LOC', 'word': 'Gaziantep İslahiye Yeni Mahalle Karanfil Sokak No 5', 'score': 0.9999, 'start': 5, 'end': 14},
  {'entity_group': 'NEED', 'word': 'ısıtıcı', 'score': 0.9999, 'start': 23, 'end': 24},
  {'entity_group': 'NEED', 'word': 'çadır', 'score': 0.9999, 'start': 25, 'end': 26},
  {'entity_group': 'NEED', 'word': 'çocuk maması', 'score': 0.9997, 'start': 27, 'end': 29},
  {'entity_group': 'PER', 'word': 'Ayşe Yurt', 'score': 0.9993, 'start': 33, 'end': 35},
  {'entity_group': 'PHONE', 'word': '0533 123 45 67', 'score': 0.9998, 'start': 37, 'end': 41},
  {'entity_group': 'LINK', 'word': '[https://t.co/yardimadresi](https://t.co/yardimadresi)', 'score': 0.9999, 'start': 48, 'end': 49}
]