Türkçe Kişisel Veri Modelini Sıfırdan Nasıl Eğittim
Türkçe Kişisel Veri Modelini Sıfırdan Nasıl Eğittim
Bir dilekçeyi yapay zekaya verirken içindeki ismi ve kimlik numarasını temizlemek istiyorsan, bunu senin yerine yapacak bir araç arıyorsun demektir. İngilizce için onlarca seçenek var. Türkçe için tek bir tane bile yok. Bir gün içinde bu boşluğu kapatmaya çalıştım, üç kez çuvalladım ve sonunda tarayıcıda çalışan 68 megabaytlık bir model çıkardım. Bu yazı o günün kaydı: neyin işe yaradığını değil, neyin yaramadığını da anlatıyor.
Neden Türkçe için ayrı bir model gerekiyor
Kişisel veri tanıma işini yapan açık modeller var ve iyiler. GLiNER ailesinin kişisel veri sürümü kırk iki veri türünü yedi dilde tanıyor. Avrupa Birliği için hazırlanmış bir başkası yirmi dört resmi AB dilini kapsıyor. Listeye baktığında İngilizce, Fransızca, Almanca, İspanyolca, Portekizce, İtalyanca görüyorsun. Sonrasında Slovence, Yunanca, Felemenkçe eklenmiş.
Türkçe hiçbirinde yok. Türkiye AB üyesi olmadığı için o yirmi dört dillik listede de yer almıyor.
Bu tek başına yeterli bir sebep değil aslında. Asıl sebep şu: Türkçeyi başka bir dil için yapılmış modele ek olarak yamamak çalışmıyor. Sebebi dilin kendi yapısı.
Türkçede ekler isme yapışır. "Ayşe Öztürk" yazmayız, "Ayşe Öztürk'ün dosyası" yazarız. "Konak" demeyiz, "Konak'taki daire" deriz. Bir model ismin nerede bitip ekin nerede başladığını bilmiyorsa, işaretlediği alan eki de içine alır. O alanı karartmaya kalktığında cümle bozulur:
Müvekkilim [KARARTILDI] dosyası incelenmektedir.
Oysa doğrusu şu olmalı:
Müvekkilim [KARARTILDI]'ün dosyası incelenmektedir.
İkinci cümle okunabilir, birinci cümle değil. Fark tek bir ek ama sonuç belgenin kullanılabilir olup olmamasını belirliyor.
Bir de kurumlarda ortaya çıkan kaynaştırma sesi var. "Belediyesi'de" değil "Belediyesi'nde" yazılır, çünkü kelime üçüncü tekil iyelik ekiyle bitiyor ve araya bir n giriyor. Bunu bilmeyen bir model ya eki yanlış keser ya da kurum adını yanlış yerden böler.
Üstelik Türkiye'ye özgü kimlik biçimleri de o modellerde yok. TC kimlik numarasının onuncu ve on birinci hane algoritması, vergi kimlik numarasının kontrol hanesi, Türk IBAN yapısı, plaka biçimi. Bunların Avrupa'da karşılığı olmadığı için Avrupa için yapılmış model bunları aramıyor bile.
Bir üçüncü mesele daha var ve bu en çok gözden kaçanı. Türkçe hukuki ve tıbbi metinlerde kişi çoğu zaman ismiyle değil sıfatıyla anılır. Müvekkilim, davalı, sanık, hastam, refakatçi kızı. Bu ifadelerin hiçbiri isim değil ama hepsi belirli bir kişiyi işaret ediyor ve bu yüzden kişisel veri sayılıyorlar.
Zor olan model değil, veri
Model eğitmek için ekran kartı lazım diye düşünürsün. Aslında o kısım kolay. Kaggle ücretsiz ekran kartı veriyor, küçük bir modeli ince ayar yapmak birkaç dakika sürüyor.
Zor olan veri. Kimsede olmayan, etiketlenmiş veriye sahip olmak. Herkesin bulabildiği veriyle eğitirsen herkesin modelinden farkın olmaz.
Ve kişisel veri tanıma işinde bunun ayrı bir sorunu var: eğitim verisinin içinde gerçek kişisel veri olamaz. Gerçek dilekçelerden veri seti yapamazsın, çünkü o veri setini yayınladığında tam olarak korumaya çalıştığın şeyi ifşa etmiş olursun.
Çözüm sentetik veri. Yani formatı doğru ama tamamen uydurma veri üretmek.
Bir Node.js script yazdım. Yaptığı iş şu: rastgele ama algoritmadan geçen TC kimlik numarası üretiyor, mod 97 kontrolünden geçen IBAN üretiyor, Luhn algoritmasına uyan kart numarası üretiyor. Yaygın Türkçe adları ve soyadları rastgele eşleştiriyor. Gerçek mahalle ve cadde adlarıyla uydurma kapı numaralarını birleştirip adres kuruyor.
Sonra bunları cümle kalıplarının içine yerleştiriyor ve her birinin metin içindeki tam karakter konumunu kaydediyor.
Kritik nokta burada. Ek, işaretlenen alanın dışında kalıyor. "Ayşe Öztürk'ün" ifadesinde işaretlenen alan sadece "Ayşe Öztürk" oluyor, kesme işareti ve ek dışarıda. Bunu on binlerce örnekte tutarlı yaptığında model bu davranışı öğreniyor.
Ek motorunu ayrı yazmak gerekti. Türkçede ek seçimi ünlü uyumuna bağlı: kalın ünlüden sonra kalın ek gelir, ince ünlüden sonra ince. Dört durumlu uyum var, iki durumlu uyum var. Sert sessizle biten kelimede bulunma eki "de" değil "te" oluyor. Sesliyle biten kelimede yönelme ekinden önce y giriyor.
Bunların hepsini koda dökmek yarım gün sürdü ama karşılığı şu: on bin örnekte hiçbir ek yanlış üretilmedi ve hiçbir işaretleme sınırı kaymadı.
Kalıpları da gerçekçi seçmek gerekti. Dava dilekçesi, tebligat, hasta notu, fatura satırı, kira sözleşmesi, iş başvurusu formu, kurumsal yazışma, WhatsApp mesajı. Otuz altı farklı belge kalıbı çıktı.
Ve içine bilerek zorluk koydum. Kimlik numarasının önünde etiket kelimesi geçen kullanımlar, boşluklu ve bitişik yazılmış IBAN'lar, ekli meslek ifadeleri, "Sn." gibi hitaplar, ve klinik notlardaki yaş ve cinsiyet ifadeleri. Sonuncusu özellikle önemli, çünkü onların işaretlenmemesi gerekiyor.
Veri seti on bin örnek olarak çıktı ve açık kaynak yayınlandı. İçinde hiçbir gerçek kişinin verisi yok.
Üç tur ve üç farklı çuvallama
İlk eğitimi GLiNER üzerinde yaptım. GLiNER esnek bir yapı, etiketleri metin olarak alıyor, yani "kişi adı" diye yazdığında ne aradığını anlıyor.
Eğitim öncesi taban modele bir test cümlesi verdim. Bulduğu şey şuydu: iki isim ve bir IBAN. TC kimlik numarasını görmedi, telefonu görmedi, akrabalık ifadesini görmedi. Bu, boşluğun gerçekten var olduğunun kanıtıydı.
Eğitimden sonra aynı cümlede yedi bulgu çıktı. TC kimlik geldi, telefon geldi, "kardeşi" ifadesi geldi. Ekler de doğru yerde kaldı.
Ama gerçek testte, yani eğitim verisinde hiç geçmeyen cümlelerde, üç hata çıktı. Model "TCKN 10000000146" ifadesinde etiket kelimesini de kimlik numarasının içine katmıştı. "eczacıymış" kelimesini eki ile birlikte meslek saymıştı. Ve klinik nottaki "kadın" kelimesini taraf sıfatı sanmıştı.
İkinci turda bunları hedefleyen şablonlar ekledim. Kimlik numaralarını etiket kelimeleriyle birlikte gösterdim, ekli meslek örnekleri koydum, yaş ve cinsiyet ifadelerinin işaretlenmemesi gerektiğini öğreten negatif örnekler ürettim.
Üç hatanın üçü de düzeldi. Ama yeni iki hata çıktı.
Model artık TC kimlik numarasına IBAN diyordu. Ve fatura numarası olan "2026/4471" ifadesini de IBAN sanıyordu.
Sebebini bulmak zor olmadı. Yeni şablonlarda kimlik türlerini yan yana dizmiştim, boru işaretiyle ayırıp arka arkaya sıralamıştım. Model "etiket kelimesi, sonra uzun sayı" kalıbını öğrenmişti ama hangi sayının hangi tür olduğunu ayırt etmeyi bırakmıştı. Sayıları birbirinden ayıran şey uzunluk ve format, ben o ayrımı kendi elimle bozmuştum.
Üçüncü turda o şablonları kaldırdım. Her kimlik türünü kendi doğal cümlesine dağıttım. Sabit telefonu cep telefonuyla aynı cümlede geçen örnekler ekledim, ikisini ayırt etmeyi öğrensin diye. Fatura numarasıyla gerçek IBAN'ın aynı cümlede bulunduğu örnekler koydum.
Üçüncü tur temiz çıktı. Yanlış pozitif yoktu.
Ama iki eksik kaldı. Cümlenin ortasında yalın halde duran sabit telefon numarası bulunamıyordu. Ve "eczacıymış" hala eki ile birlikte geliyordu.
Buraya kadar olan kısım, model eğitiminin gerçekte nasıl yürüdüğünü gösteriyor. Bir yeri düzeltirken başka yer bozuluyor. Ölçmezsen fark etmiyorsun.
Asıl sorun mimarideydi
Bu modeli siteye koymak istedim. Kullanıcı metnini yapıştırsın, tespit tarayıcıda yapılsın, veri hiçbir yere gitmesin.
Model dosyası 1157 megabayttı.
Küçük tabana geçtim, 611 megabayta indi. Hala kimsenin indirmeyeceği bir boyut.
Sıkıştırmayı denedim. Sekiz bit sıkıştırma dosyayı 183 megabayta indirdi ama modeli bozdu, çıktı değerleri orijinalinden 21 birim kaydı. Hassas katmanları hariç tutmayı denedim, daha kötü oldu. On altı bit dönüşümünü denedim, tip uyumsuzluğu verdi. Uyumsuzluğu düzelttim, bir sonraki katmanda aynı hata çıktı.
Beş yöntem denedim, beşi de aynı duvara çarptı.
O noktada araştırmaya gittim ve İsveççe için aynı işi yapmış bir proje buldum. Onların modeli kırk üç megabayttı ve tarayıcıda çalışıyordu. Nasıl yapmışlar diye baktım: GLiNER kullanmamışlar. İsveççe BERT tabanı almışlar ve standart token sınıflandırma yapmışlar.
Ve mimarilerinde bir şey daha vardı: kesin olan şeyleri kural katmanında bırakmışlar, model sadece kuralın bulamadığı isim ve yer adlarını dolduruyormuş.
Bu, benim zaten yaptığım şeydi. Karartma aracı için yazdığım kural tabanlı katman elimde duruyordu ve TC kimlik, IBAN, vergi numarası, kart numarası, telefon, e-posta, plaka için matematiksel doğrulama yapıyordu.
GLiNER'ı bıraktım.
Küçük model büyüğünü geçti
Türkçe için eğitilmiş DistilBERT tabanını aldım. Veriyi BIO formatına çevirdim, yani her kelimeye bir etiket veren standart biçime.
Eğitim iki dakika sürdü. GLiNER'da on dört dakikaydı.
İlk denemede bir hata çıktı: TC kimlik numarası dört parçaya bölünüyordu. Sebebi teknik ve düzeltmesi tek satırdı. Bu mimaride kelimeler alt parçalara bölünüyor ve ben sadece ilk parçaya etiket vermiştim, gerisini görmezden gelmiştim. Her alt parçaya da etiket verince parçalanma bitti.
İkinci eğitimden sonraki sonuç şuydu: küçük model GLiNER'ın üç turunda da yapılamayan şeyi yaptı. Yalın haldeki sabit telefonu buldu. Ve "eczacıymış" sorunu ortadan kalktı.
Yani üçte bir boyuttaki model, daha doğru çıktı.
ONNX'e çevirdim, 270 megabayt. Sıkıştırdım, 68 megabayt. Sıkıştırmanın modeli bozup bozmadığını ölçtüm: doğrulama kümesinde dokuz bin sekiz yüz on dört token üzerinde tahminlerin yüzde 99,36'sı orijinaliyle aynı çıktı.
Bu mimari değişikliğinin dersi şu: bir sorunu çözmek için beş yöntem deniyorsan, muhtemelen yanlış sorunu çözüyorsundur. Ben sıkıştırmayı çözmeye çalışıyordum, oysa mimariyi değiştirmem gerekiyordu.
Sentetik veri neyi öğretemez
Sentetik veriyle çalışmanın bir sınırı var ve bunu baştan bilmek gerekiyor.
Şablonla ürettiğin cümleler gerçek belgelerden daha düzenli oluyor. Gerçek bir dilekçede yazım hatası var, tutarsız boşluk var, kopyala yapıştırdan kalmış bozuk satır sonu var. Taranmış bir belgeden metin çıkardıysan içinde karakter hataları var. Bunların hiçbiri senin ürettiğin veride yok.
Bunun sonucu şu: doğrulama kümesinde ölçtüğün skor gerçeği yansıtmıyor. Doğrulama kümesi de aynı şablonlardan üretildiği için model kalıbı ezberlediyse orada da yüksek not alıyor. İlk turda on altı etiketin on beşinde tam puan çıktı ve bu iyi haber değildi, ezberin işaretiydi.
O yüzden gerçek testi ayrı yaptım. Eğitim verisinde hiç geçmeyen, farklı yazım tarzında dört cümle hazırladım: kısaltma kullanan resmi bir yazı, küçük harfle yazılmış gayriresmi bir mesaj, klinik not biçiminde bir metin, ve boşluklu IBAN içeren bir fatura satırı.
Model bu cümlelerde ezberlemediğini gösterdi. Boşluklu IBAN'ı yakaladı, oysa eğitim verisinde IBAN'lar hep bitişikti. "hocam merhaba" diye başlayan küçük harfli mesajda ismi, akrabalığı, mesleği ve telefonu buldu. "Selin Hanım" hitabını doğru kesti.
Ama şunu da söylemek gerekiyor: bu dört cümle bir ölçüm değil, bir kontrol. Gerçek performansı ancak insanlar kendi belgeleriyle denediğinde öğreneceğiz.
Bir günde ne yapıldı
Sırayla: etiket şeması, veri üreteci, ek motoru, on bin örneklik veri seti, veri setinin yayınlanması, üç tur GLiNER eğitimi ve ölçümü, mimari değişikliği, iki tur yeni eğitim, ONNX dönüşümü, sıkıştırma ve doğrulama, model kartları, site sayfası, model dosyalarının kendi CDN'ine taşınması ve önbellek kuralı.
Bunu tek başına yapılabilir kılan şey yeni bir şey icat etmemek oldu. Taban model hazırdı, eğitim altyapısı ücretsizdi, tarayıcıda çalıştırma kütüphanesi hazırdı, kural katmanı zaten elimde duruyordu. Yaptığım iş bunları doğru sırayla birleştirmek ve aradaki tek eksik parçayı, yani Türkçe eğitim verisini üretmekti.
Eksik parçanın ne olduğunu doğru tespit etmek, işin kendisinden daha önemliydi.
Tarayıcıda çalıştırmanın bedeli
Model şu an neondijital.com üzerindeki Kişisel Veri Bulucu sayfasında çalışıyor. Kullanıcı metnini yapıştırıyor, tespit tarayıcısında yapılıyor, metin hiçbir sunucuya gitmiyor.
Bunun bir bedeli var ve saklamamak lazım: ilk açılışta 68 megabaytlık model kullanıcının tarayıcısına iniyor. İkinci ziyarette önbellekten geliyor, hatta internet olmadan da çalışıyor.
Bir de öğrendiğim bir şey oldu. Modeli başlangıçta Hugging Face'ten çekiyordum. Bazı güvenlik yazılımları bu dış bağlantıyı uyarıyla karşıladı, çünkü şifreli trafiği inceleyemiyorlardı. Sayfa "veri çıkmıyor" derken tarayıcı "bir yere bağlanıyor" uyarısı verirse, anlattığın hikaye çöker.
Model dosyalarını kendi CDN'ime taşıdım. Şimdi hiçbir dış bağlantı yok.
Ne işe yarıyor, ne işe yaramıyor
Sayfadaki araç iki katman çalıştırıyor. Kural katmanı doğrulanabilir kimlikleri matematikle buluyor, model bağlamsal verileri tahminle. Çakışma olursa kural kazanıyor, çünkü matematik tahmini yener.
Bunu neden vurguluyorum: model tek başına yeterli değil. TC kimlik numarasını algoritma kesin bilir, model tahmin eder. Kuralın ulaşabildiği yerde kural kullanılmalı.
Ve tespit etmek, hukuki anlamda anonimleştirmek değil. Bir belgeden ismi ve kimlik numarasını silsen bile geriye "1985 doğumlu, Konak'ta oturan veteriner kadın" kalıyorsa, o belge hala tek bir kişiyi işaret ediyor olabilir. Bu konuyu belgedeki kişisel verileri gizleme yazısında ayrıca ele almıştım.
Bir belgeyi yapay zekaya vermeden önce temizlemenin neden gerekli olabileceği ise ayrı bir tartışma. ChatGPT'ye sözleşme yüklemek KVKK ihlali mi yazısı bu soruyu, yurt dışına veri aktarımı ve yapay zeka araçları yazısı ise verinin sınır dışına çıkması meselesini ele alıyor.
Model ve veri açık kaynak
Üç şey yayında: veri seti, sunucu için büyük model, tarayıcı için küçük model. Hepsi Hugging Face'te, sayfanın altındaki bağlantılardan ulaşılabiliyor.
Veri setini de açık kaynak yaptım çünkü içinde gerçek hiçbir kişinin verisi yok ve bir başkasının bu işi daha iyi yapmasının önünü açmak istiyorum. Türkçe için tek olmak iyi bir şey değil, en iyisi olmak iyi bir şey.
Sık sorulan sorular
Model ne kadar doğru? Kesin bir rakam vermiyorum ve sebebi şu: doğrulama kümesi eğitim verisiyle aynı kalıplardan üretildi, o yüzden orada çıkan skor gerçeği yansıtmaz. Modeli eğitim verisinde hiç geçmeyen cümlelerle test ettim, sonuçlar model kartında duruyor. Kendi belgelerinle denemeni öneririm.
Neden çok dilli yapmadın? Çünkü çok dilli alanda akademik makalesi olan takımlar var ve orada onuncu adam olurdum. Türkçede tek adam oluyorum. Bir Fransız ekibi Türkçe modeli benden iyi yapamaz, ben de İngilizce modeli onlardan iyi yapamam.
Sentetik veriyle eğitilmiş model gerçek belgede çalışır mı? Kısmen. Şablonla üretilen metin gerçek belgelerden daha düzenli. Model gayriresmi mesaj diline ve daha önce görmediği yazım biçimlerine genelleme yaptı, ama OCR gürültüsü ve bozuk düzen içeren belgelerde performansı düşer.
Bunu kendi projemde kullanabilir miyim? Kullanabilirsin. Model Apache lisanslı, veri seti CC-BY lisanslı. Tarayıcıda çalıştırmak için Transformers.js yeterli, sunucuda çalıştıracaksan büyük sürüm daha uygun.
Bu araç KVKK uyumu sağlar mı? Sağlamaz. Teknik bir yardımcıdır. Kişisel verinin belgeden çıkarılması maskeleme yöntemlerinden biridir ve paylaşım öncesi riski azaltır, ama her durumun kendi şartları vardır. Kurumsal bir süreç yürütüyorsan uzman değerlendirmesi al.
---
Kaynaklar ve uyarı
Bu yazı teknik bir çalışma kaydıdır. Kişisel verilerin korunmasına dair değinilen genel çerçeve için KVKK yayınlarına ve mevzuat metinlerine başvurulabilir: kvkk.gov.tr, mevzuat.gov.tr, resmigazete.gov.tr.
Yazıda yer alan bilgiler genel bilgilendirme amaçlıdır, hukuki görüş veya danışmanlık değildir. Yazar avukat değildir. Mevzuat değişebilir; yükümlülüklerini birincil kaynaktan teyit et ve gerektiğinde uzman değerlendirmesi al.
Anlatılan araç ve modeller tahmin yürütür ve yanılabilir. Çıktıyı paylaşmadan önce kendin kontrol et.