Masalah Ketepatan Positif Palsu Presidio: Mengurangkan Positif Palsu tanpa Meninggalkan PII
Presidio dirancang dengan bias tinggi terhadap peringatan — lebih baik menandai 1,000 positif palsu daripada meninggalkan 1 PII yang sebenarnya.
Hasil: Skor keyakinan tinggi pada entiti umum:
"The meeting is at 2 PM" → Email address? No, that's a time.
"Call John at 555-1212" → PHONE_NUMBER score 0.85 (mungkin palsu, mungkin sebenarnya)
"Project deadline Q3 2024" → DATE? ADDRESS? Entity yang salah
"Error code 404-505-600" → PHONE_NUMBER? SSN? (Tidak, kode ralat)
"Budget $1,000,000 for headcount" → AMOUNT (betul), tapi juga CREDIT_CARD (palsu!)
Jenis Positif Palsu Presidio
1. Format Positif Palsu
Numerik biasa yang cocok corak tetapi bukan PII:
Format yang cocok: 555-1234 (format telefon)
Konteks: "Server error code 555-1234"
Presidio mengesan: PHONE_NUMBER score 0.7
Kepercayaan: Palsu — kode ralat, bukan nombor telefon
2. Konteks Positif Palsu
Nomor yang sebenarnya cocok tetapi bukan PII dalam konteks tertentu:
"GDPR Perkara 32 menyatakan organisasi harus melindungi data pribadi"
Presidio mengesan: GDPR sebagai mungkin entity (jika ada dalam recognizer)
Konteks: Tutorial kepatuhan, bukan data nyata
3. Positif Palsu Keselarian
Karena banyak pengecam berjalan, beberapa menghasilkan skor rendah yang mencumuli:
"Invoice #50286-X"
Presidio percaya kemungkinan:
- CREDIT_CARD: 0.3
- ACCOUNT_NUMBER: 0.2
- Skor gabungan (threshold tidak jelas): ditandai sebagai PII
Strategi Mengurangkan Positif Palsu Tanpa Mengorbankan Recall
Strategi 1: Peningkatan Skor Keyakinan Threshold
Lama: Bendera apa-apa dengan skor > 0.5
results = analyzer.analyze(
text=text,
language="en",
entities=["PHONE_NUMBER", ...],
score_threshold=0.5 # Terlalu rendah
)
Baharu: Naikkan threshold ke 0.75 atau 0.8
results = analyzer.analyze(
text=text,
language="en",
entities=["PHONE_NUMBER", ...],
score_threshold=0.75 # Kurang positif palsu
)
Kesan: Mengurangkan positif palsu tetapi mungkin meninggalkan PII yang sebenarnya (skor rendah).
Strategi 2: Whitelist Konteks
Untuk jenis dokumen yang diketahui mengandungi corak palsu, tentukan whitelist:
SAFE_CONTEXTS = {
"error_logs": [
r"Error code \d{3}-\d{3}-\d{3}", # Jangan bendera kode ralat sebagai SSN
r"Exit code \d+" // Jangan bendera sebagai ID
],
"configuration_files": [
r"Port \d{4}", // Jangan bendera sebagai ID
r"IP \d+\.\d+\.\d+\.\d+" // Jangan bendera sebagai ADDRESS
],
"regulatory_documents": [
r"Article \d+", // Jangan bendera nomor artikel sebagai entiti
r"(GDPR|HIPAA|CCPA) § \d+" // Jangan bendera sebagai ID
]
}
def filter_results_by_context(results, text, context_type):
safe_patterns = SAFE_CONTEXTS.get(context_type, [])
filtered = []
for result in results:
is_safe = False
for pattern in safe_patterns:
if re.search(pattern, text[result.start:result.end]):
is_safe = True
break
if not is_safe:
filtered.append(result)
return filtered
Strategi 3: Pengecam Negatif (Antonyms)
Daftarkan pengecam yang menghilangkan positif palsu:
class FalsePositiveRemover(EntityRecognizer):
"""Mengenal pasti corak yang BUKAN PII meskipun serupa dengan PII"""
def __init__(self):
self.false_positive_patterns = [
r"Exit code \d+",
r"Error \d{3}",
r"Port \d{4}",
r"(Recital|Article) \d+",
r"Q[1-4] \d{4}" // Quarters, bukan tarikh
]
def analyze(self, text, language="en"):
# Kembalikan daftar rentang yang ADALAH positif palsu
return ...
def remove_false_positives(results, text):
fp_remover = FalsePositiveRemover()
false_ranges = fp_remover.analyze(text)
# Saring hasil yang bersentuhan dengan julat positif palsu
filtered = [
r for r in results
if not any(fp.start <= r.start < fp.end for fp in false_ranges)
]
return filtered
Strategi 4: Konteks Negatif
Turunkan skor atau keluarkan entiti jika perkataan negatif muncul di dekat mereka:
NEGATIVE_CONTEXTS = {
"PHONE_NUMBER": [
r"error code",
r"product code",
r"serial number",
r"port",
r"model \d+"
],
"DATE_OF_BIRTH": [
r"before|after|since", // Tempat & tarikh sejarah
r"event date", // Acara, bukan DOB individu
r"published|created|updated" // Tarikh dokumen
]
}
def apply_negative_context(result, text):
surrounding_text = text[max(0, result.start-50):result.end+50].lower()
negative_words = NEGATIVE_CONTEXTS.get(result.entity_type, [])
for neg_pattern in negative_words:
if re.search(neg_pattern, surrounding_text):
result.score *= 0.5 # Turunkan skor sebanyak 50%
return result
Menguji Keseimbangan Presisi-Recall
Gunakan dataset pelabelan untuk mengukur keseimbangan:
from sklearn.metrics import precision_recall_fscore_support
# Dataset: dokumen berlabel dengan PII yang benar
test_set = [
{"text": "...", "entities": [{"type": "PERSON", "start": 10, "end": 20}, ...]},
...
]
predicted = analyzer.analyze(text)
# Hitung metrik
tp = sum(1 for p in predicted if p.entity_type in expected)
fp = sum(1 for p in predicted if p.entity_type not in expected) // Positif palsu
fn = sum(1 for e in expected if e.entity_type not in predicted) // Negatif palsu
precision = tp / (tp + fp) if tp + fp > 0 else 0 // Seberapa banyak positif yang betul?
recall = tp / (tp + fn) if tp + fn > 0 else 0 // Berapa banyak PII yang kami tangkap?
f1 = 2 * (precision * recall) / (precision + recall)
print(f"Precision: {precision:.2f} (kurang false positives)")
print(f"Recall: {recall:.2f} (tangkap PII lebih)")
print(f"F1: {f1:.2f} (keseimbangan)")
Nilai Benchmark Realistis
Taruhan ketat GDPR: Presisi 70–80%, Recall 95%+ (lebih suka meninggalkan positif palsu daripada PII yang terlepas) Keseimbangan: Presisi 85–90%, Recall 90%–92% Ketat presisi (contohnya e-discovery): Presisi 95%+, Recall 80%–85% (kurang positif palsu, tetapi mungkin PII terlepas)
Kesimpulan: Mengurangkan positif palsu memerlukan gabungan peningkatan threshold, whitelist, dan pengenal negatif. Ujian terhadap dataset berlabel untuk memastikan anda tidak mengorbankan recall dalam mencapai presisi.