Spaces:
Sleeping
Sleeping
File size: 8,613 Bytes
4816e22 d674f2f 4816e22 d674f2f 4816e22 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | import os
import logging
import re
from datasets import load_dataset
# Configure logging
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
# Try to import Zemberek, fall back to regex-based analysis if unavailable
try:
from zemberek import TurkishMorphology
ZEMBEREK_AVAILABLE = True
except ImportError:
ZEMBEREK_AVAILABLE = False
logging.warning("Zemberek not available, using regex-based morphological analysis")
class Preprocessor:
def __init__(self, engine="zemberek"):
"""
engine: "zemberek" (default) or "nuve"
"""
self.engine = engine
self.morphology = None
self.nuve = None
if self.engine == "zemberek":
if ZEMBEREK_AVAILABLE:
logging.info("Initializing Zemberek Morphology...")
try:
self.morphology = TurkishMorphology.create_with_defaults()
logging.info("Zemberek initialized.")
except Exception as e:
logging.warning(f"Zemberek initialization failed: {e}, using fallback")
else:
logging.info("Using regex-based Turkish morphological analysis")
elif self.engine == "nuve":
from src.nuve_bridge import NuveBridge
logging.info("Initializing Nuve Bridge...")
self.nuve = NuveBridge()
logging.info("Nuve Bridge initialized.")
elif self.engine == "hybrid":
# Initialize BOTH
if ZEMBEREK_AVAILABLE:
logging.info("Initializing Zemberek (Hybrid)...")
try:
self.morphology = TurkishMorphology.create_with_defaults()
except Exception as e:
logging.warning(f"Zemberek init failed: {e}")
else:
logging.warning("Zemberek not available for hybrid mode.")
from src.nuve_bridge import NuveBridge
logging.info("Initializing Nuve Bridge (Hybrid)...")
self.nuve = NuveBridge()
def load_wikiann(self, split="train", limit=None):
"""
Loads the WikiANN (tr) dataset.
"""
logging.info(f"Loading WikiANN (tr) split: {split}")
try:
dataset = load_dataset("wikiann", "tr", split=split)
if limit:
dataset = dataset.select(range(limit))
return dataset
except Exception as e:
logging.error(f"Failed to load dataset: {e}")
return None
def load_wikiner(self, split="train", limit=None):
"""
Loads the turkish-nlp-suite/turkish-wikiNER dataset.
"""
logging.info(f"Loading Turkish WikiNER split: {split}")
try:
dataset = load_dataset("turkish-nlp-suite/turkish-wikiNER", split=split)
if limit:
dataset = dataset.select(range(limit))
return dataset
except Exception as e:
logging.error(f"Failed to load dataset: {e}")
return None
def _regex_analyze(self, word):
"""
Regex-based Turkish morphological analysis fallback.
Strips common Turkish suffixes to find approximate lemma.
"""
# Common Turkish suffixes (order matters - longer first)
suffixes = [
"'tan", "'ten", "'dan", "'den", # Ablative
"'ta", "'te", "'da", "'de", # Locative
"'nın", "'nin", "'nun", "'nün", # Genitive
"'ın", "'in", "'un", "'ün", # Genitive without buffer
"'ya", "'ye", "'a", "'e", # Dative
"'yı", "'yi", "'ı", "'i", "'u", "'ü", # Accusative
"lar", "ler", # Plural
"dır", "dir", "dur", "dür", # Copula
"mış", "miş", "muş", "müş", # Past participle
"yor", "iyor", "uyor", "üyor", # Present continuous
]
lemma = word
for suffix in suffixes:
if lemma.lower().endswith(suffix):
lemma = lemma[:-len(suffix)]
break
# Determine POS based on patterns
if word[0].isupper():
pos = "Noun" # Proper noun
elif word.endswith(("mak", "mek")):
pos = "Verb"
elif word.endswith(("lı", "li", "lu", "lü", "sız", "siz")):
pos = "Adj"
else:
pos = "Noun"
return lemma if lemma else word, pos
def analyze_word(self, word):
"""
Analyzes a word using Zemberek, Nuve, or regex fallback.
Returns: lemma, pos, morph_info (dict)
"""
if self.engine == "zemberek" and self.morphology:
try:
results = self.morphology.analyze(word)
if results.analysis_results:
best = results.analysis_results[0]
lemma = best.get_stem()
pos = best.item.primary_pos.value if hasattr(best.item, 'primary_pos') else "UNK"
# Extract rich morph info
morphemes = []
for md in best.morpheme_data_list:
m_id = md.morpheme.id_
morphemes.append({
"Id": m_id,
"Surface": md.surface,
"HasChange": len(md.surface) > 0 and md.surface != md.morpheme.id_, # Simple heuristic
"Type": "Root" if md == best.morpheme_data_list[0] else "Suffix",
"Labels": [] # Zemberek doesn't expose labels the same way as Nuve
})
return lemma, pos, morphemes
else:
return word, "UNK", []
except Exception:
l, p = self._regex_analyze(word)
return l, p, []
elif self.engine == "nuve" and self.nuve:
analysis = self.nuve.analyze(word)
return analysis['lemma'], "UNK", analysis.get('morphemes', [])
else:
l, p = self._regex_analyze(word)
return l, p, {}
def process_sentence(self, tokens):
"""
Analyzes a list of tokens.
Returns a list of dicts: [{'word': w, 'lemma': l, 'pos': p, 'morph': m}, ...]
"""
if self.engine == "nuve" and self.nuve:
# Batch process tokens for speed
nuve_results = self.nuve.analyze_batch(tokens)
processed = []
for token in tokens:
res = nuve_results.get(token, {'lemma': token, 'morphemes': []})
processed.append({
'word': token,
'lemma': res['lemma'],
'pos': "UNK",
'morph': res.get('morphemes', [])
})
return processed
if self.engine == "hybrid":
# 1. Get Nuve Results (Batch)
nuve_results = {}
if self.nuve:
nuve_results = self.nuve.analyze_batch(tokens)
processed = []
for token in tokens:
# 2. Get Zemberek Results (Word-by-word)
z_lemma, z_pos, z_morph = self.analyze_word(token) # analyze_word uses self.morphology if set
# 3. Get Nuve Result
n_res = nuve_results.get(token, {'lemma': token, 'morphemes': []})
processed.append({
'word': token,
'lemma': n_res['lemma'], # Default to Nuve lemma as primary
'pos': z_pos, # Default to Zemberek POS as primary
'morph': n_res.get('morphemes', []), # Default morph
# Store Both explicitly
'nuve_lemma': n_res['lemma'],
'nuve_morph': n_res.get('morphemes', []),
'zemberek_lemma': z_lemma,
'zemberek_pos': z_pos,
'zemberek_morph': z_morph
})
return processed
processed = []
for token in tokens:
lemma, pos, morph = self.analyze_word(token)
processed.append({
'word': token,
'lemma': lemma,
'pos': pos,
'morph': morph
})
return processed
if __name__ == "__main__":
p = Preprocessor()
ds = p.load_wikiann(limit=10)
if ds:
print("Example 0:", ds[0])
print("Analysis:", p.process_sentence(ds[0]['tokens']))
|