자기진화 - 비식별화 마스킹 정책별 정보 손실률(Entropy) 자동 계산 알고리즘 구조
마스킹 정책별 정보 손실률을 자동 계산하려면, 원문과 마스킹 후 텍스트의 분포 차이를 정책 단위로 측정하는 구조가 가장 좋습니다. 텍스트 비식별화 연구에서는 엔트로피, precision, non-uniform entropy, masked language modeling 기반 정보 손실 등을 utility/정보손실 평가에 사용합니다.[pmc.ncbi.nlm.nih]
핵심 아이디어
“마스킹 전에는 정보가 많고, 마스킹 후에는 정보가 줄어드는데, 그 줄어든 양을 엔트로피로 재는 것” 입니다.
엔트로피는 데이터가 "얼마나 예측하기 어려운지", 다시 말해 "얼마나 많은 정보가 들어 있는지 "를 나타내는 값입니다
핵심 아이디어
정책 \(p\)가 적용된 문서 집합에서,
원문 분포 \(P\), 마스킹 후 분포 \(Q_p\)를 만든 뒤
두 분포의 차이를 정보 손실로 계산합니다.
정의
- \(P\): 원문 데이터의 분포
- \(Q_p\): 정책 \(p\)를 적용한 뒤의 분포
- \(H(P)\): 원문이 가진 정보량 (엔트로피)
- \(H(Q_p)\): 마스킹 후 남은 정보량 (엔트로피)
정보 손실 공식
$$InfoLoss(p) = H(Q_p) - H(P)$$
정규화된 정보 손실율
$$InfoLossRate(p) = \frac{H(Q_p) - H(P)}{H(P) + \epsilon}$$
값이 클수록 정보 손실이 크고, 작을수록 유용성이 높다고 해석합니다.

값이 클수록 정보 손실이 크고, 작을수록 유용성이 높다고 해석합니다.[pubmed.ncbi.nlm.nih]
마스킹 전 엔트로피보다 마스킹 후 엔트로피가 얼마나 늘었는지 보면, “정보가 얼마나 사라졌는지”를 볼 수 있습니다 a
가장 단순한 방식은 조건부 엔트로피 증가량을 쓰는 것입니다.[pmc.ncbi.nlm.nih]

값이 크면:
- 더 많이 가려졌거나,
- 구조가 많이 바뀌었거나,
- 의미 손실이 크다는 뜻입니다.
값이 작으면:
- 덜 가려졌거나,
- 원래 문맥을 잘 보존했다는 뜻입니다.pmc.ncbi.nlm.nih+1
예시로 보면
원문:
- “김민준님은 010-1234-5678로 연락하세요.”
마스킹 후:
- “김님은 010-**-5678로 연락하세요.”
여기서:
- 전화번호 같은 직접 식별자는 숨겼고,
- 문장 구조와 연락처라는 문맥은 남았습니다.
즉,
- 개인정보는 줄었고,
- 문서 유용성은 어느 정도 유지됐습니다.
이 경우 엔트로피 관점에서는
식별 가능성은 낮아지고, 불확실성은 높아진 상태라고 보면 됩니다
비식별화는 “많이 지울수록 안전하다”만으로는 부족합니다.
너무 많이 지우면 문서가 쓸모없어지기 때문에,
안전성과 유용성의 균형을 봐야 합니다.pmc.ncbi.nlm.nih+2
그래서 엔트로피 기반 계산은:
- 어떤 정책이 너무 강한지,
- 어떤 정책이 적당한지,
- 어떤 문서 유형에서 유용성이 더 떨어지는지
를 객관적으로 비교하는 데 유용합니다.
임상 데이터 분석 사용 사례인 PMC를 통한 데이터 프라이버시와 유용성 간의 절충 탐구
19가지 비식별 시나리오 모두 재식별 위험을 유의미하게 감소시켰습니다. 그럼에도 불구하고, 비식별화 과정은 기록 억제와 예측 변수의 완전한 마스킹을 초래하여 데이터셋의 유용성을 훼손했습니다. 재식별 감소율과 ARX 효용 점수 사이에만 유의미한 상관관계가 관찰되었습니다.
알고리즘은 다음 논문들을 이론적 근거로 삼으면 됩니다.
- 엔트로피 기반 정보 손실: El Emam et al., Eicher et al.
- 정밀도, 식별 가능성 지표, 그리고 비균일 엔트로피. 각 알고리즘의 성능 속도도 평가 - privacy-utility trade-off 실증: Im et al .pmc.ncbi.nlm.nih
- 텍스트 익명화의 전용 평가 (TAB), Pilán, I., Lison, arxiv
- 정보이론 기반 정량화: Zhang et al
- 논문 인용용 APA 예시
El Emam, K., Dankar, F. K., Vaillancourt, R., Roffey, T., & Lysyk, B. (2009). A globally optimal k-anonymity method for the de-identification of health data. Journal of the American Medical Informatics Association, 16(5), 670–682.
- A globally optimal k-anonymity method for the de-identification of health data - PubMed - Eicher, J., Kuhn, K. A., & Prasser, F. (2017). An experimental comparison of quality models for health data de-identification. Studies in Health Technology and Informatics, 245, 704–708.
- An Experimental Comparison of Quality Models for Health Data De-Identification - PubMed - Pilán, I., Lison, P., Øvrelid, L., Papadopoulou, A., Sánchez, D., & Batet, M. (2022). The Text Anonymization Benchmark (TAB): A dedicated corpus and evaluation framework for text anonymization. Proceedings of the 60th Annual Meeting of the Association for Computational Linguistics.
- [2202.00443] The Text Anonymization Benchmark (TAB): A Dedicated Corpus and Evaluation Framework for Text Anonymization - Im, E., et al. (2024). Exploring the tradeoff between data privacy and utility with a clinical data analysis use case. BMC Medical Informatics and Decision Making.
- Exploring the tradeoff between data privacy and utility with a clinical data analysis use case - PMC - Zhang, Z., Lu, Z., & Tian, Y. (2019/2024). Data privacy quantification and de-identification model based on information theory.
- Data Privacy Quantification and De-identification Model Based on Information Theory | OpenReview
비식별화 계산 구조
정책별 자동 계산은 아래 4단계로 구성하는 것이 좋습니다.
- 문서 단위 토큰화
- 원문과 마스킹문을 동일한 tokenizer로 분해합니다.
- 문서유형별로 분리합니다.
- 개체 단위 치환 매핑
- 어떤 PII가 어떤 정책으로 어떻게 바뀌었는지 기록합니다.
- 예: 전화번호 -> 010-****-5678
- 확률 분포 추정
- 토큰 빈도, 개체 빈도, 길이 분포, 카테고리 분포를 만듭니다.
- 원문 분포 PP, 마스킹 분포 QpQ_p를 추정합니다.
- 엔트로피/손실 계산
- Shannon entropy 또는 non-uniform entropy를 계산합니다.
- 정책별로 평균 정보손실을 구합니다.[pmc.ncbi.nlm.nih]
추천 알고리즘 형태
python
import math
from collections import Counter
def entropy_from_probs(probs):
return -sum(p * math.log2(p) for p in probs if p > 0)
def dist_entropy(tokens):
c = Counter(tokens)
total = sum(c.values())
probs = [v / total for v in c.values()]
return entropy_from_probs(probs)
def info_loss_rate(original_texts, masked_texts):
orig_tokens = []
masked_tokens = []
for o, m in zip(original_texts, masked_texts):
orig_tokens.extend(o.split())
masked_tokens.extend(m.split())
h_orig = dist_entropy(orig_tokens)
h_mask = dist_entropy(masked_tokens)
loss = h_mask - h_orig
rate = loss / (h_orig + 1e-9)
return {
"h_original": h_orig,
"h_masked": h_mask,
"info_loss": loss,
"info_loss_rate": rate
}
정책별 계산 방식
정책마다 같은 방식으로 비교하려면, 아래처럼 그룹별 집계를 해야 합니다.
| 정형 마스킹 | PII 문자열 치환 후 엔트로피 변화 | 안전하지만 문맥 손실 가능 |
| 가명처리 | 원값 대신 일관된 대체값 사용 | 재식별 위험과 유용성의 균형 |
| 범주화 | 값을 구간/등급으로 변환 | 정보 손실은 크지만 구조는 보존 |
| 삭제 | 토큰 제거 | 가장 큰 정보 손실 |
정형 마스킹은 정보 손실이 비교적 낮고, 삭제는 가장 높게 나오는 것이 일반적입니다.[dl.acm]
문서 유형별 보정
엔트로피는 문서마다 길이와 구조가 달라 단순 비교가 어렵기 때문에, 문서유형별 정규화가 필요합니다. 예를 들어 금융 문서는 계좌번호와 금액 패턴의 보존이 중요하고, 의료 문서는 질병명·검사수치 구조가 중요합니다. 그래서 다음 보정을 권장합니다.[pmc.ncbi.nlm.nih]
text
normalized_loss = info_loss_rate * document_type_weight
- 금융: 계좌/금액 필드 가중치 높음.
- 의료: 수치형 변수와 날짜 가중치 높음.
- 고객센터: 문의 유형과 처리 단계 가중치 높음.
RAG DB에 넣을 필드
정책별 엔트로피 결과는 policy_rules에 아래처럼 저장하면 됩니다.
- policy_id
- document_type
- masking_strategy
- h_original
- h_masked
- info_loss
- info_loss_rate
- normalized_loss
- utility_score
- sample_count
- computed_at
운영 기준
권장 기준은 다음처럼 두면 됩니다.
- info_loss_rate가 낮을수록 좋음.
- 그러나 residual_pii_rate도 함께 봐야 함.
- utility_score가 일정 수준 이상이어야 함.
- 문서유형별 기준을 다르게 둬야 함.[upcommons.upc]
결론
즉, 마스킹 정책별 정보 손실률은 원문과 마스킹문 간 엔트로피 차이 + 문서유형 보정 + 정책별 집계로 자동 계산하는 구조가 가장 실용적입니다. 여기에 non-uniform entropy, masked-language-model 기반 손실, downstream task 성능을 함께 붙이면 훨씬 안정적인 유용성 평가가 됩니다.[sciencedirect]
아래는 마스킹 정책별 정보 손실률을 계산해서 policy_rules JSONL로 저장하는 코드입니다. 현재 설계 문서의 RAGManager, DBManager.save_jsonl(), golden_cases / policy_rules 분리 원칙에 맞춰 구성했습니다
import json
import math
import hashlib
from collections import Counter
from datetime import datetime, timezone
from pathlib import Path
OUTPUT_DIR = Path("output")
OUTPUT_DIR.mkdir(exist_ok=True)
def tokenize(text: str):
return text.split()
def entropy(tokens):
if not tokens:
return 0.0
c = Counter(tokens)
total = sum(c.values())
return -sum((v / total) * math.log2(v / total) for v in c.values() if v > 0)
def info_loss_metrics(original_texts, masked_texts):
orig_tokens = []
masked_tokens = []
for o, m in zip(original_texts, masked_texts):
orig_tokens.extend(tokenize(o))
masked_tokens.extend(tokenize(m))
h_original = entropy(orig_tokens)
h_masked = entropy(masked_tokens)
info_loss = h_masked - h_original
info_loss_rate = info_loss / (h_original + 1e-9)
return {
"h_original": round(h_original, 6),
"h_masked": round(h_masked, 6),
"info_loss": round(info_loss, 6),
"info_loss_rate": round(info_loss_rate, 6)
}
def doc_hash(*parts):
s = "||".join(str(p) for p in parts)
return hashlib.sha256(s.encode("utf-8")).hexdigest()
def build_policy_rule_doc(
policy_id,
document_type,
domain,
masking_strategy,
original_texts,
masked_texts,
pii_types=None,
target_utility=0.8,
target_privacy=0.95,
is_active=True
):
metrics = info_loss_metrics(original_texts, masked_texts)
utility_score = max(0.0, 1.0 - max(metrics["info_loss_rate"], 0.0))
doc = {
"doc_id": policy_id,
"doc_type": "policy_rule",
"domain": domain,
"document_type": document_type,
"pii_types": pii_types or [],
"masking_strategy": masking_strategy,
"content": {
"rule_summary": f"{document_type}에서 {masking_strategy} 적용",
"notes": "정책별 정보 손실률 자동 계산 결과 포함"
},
"metadata": {
"quality_score": round(utility_score * 10, 2),
"utility_score": round(utility_score, 6),
"target_utility": target_utility,
"target_privacy": target_privacy,
"privacy_score": round(min(1.0, max(0.0, 1.0 - metrics["info_loss_rate"])), 6),
"info_loss_rate": metrics["info_loss_rate"],
"h_original": metrics["h_original"],
"h_masked": metrics["h_masked"],
"info_loss": metrics["info_loss"],
"sample_count": len(original_texts),
"created_at": datetime.now(timezone.utc).isoformat(),
"is_active": is_active,
"approved": metrics["info_loss_rate"] <= (1.0 - target_utility)
}
}
doc["metadata"]["content_hash"] = doc_hash(policy_id, domain, document_type, masking_strategy, metrics["h_original"], metrics["h_masked"])
return doc
def save_jsonl(doc, filename="policy_rules.jsonl"):
path = OUTPUT_DIR / filename
with path.open("a", encoding="utf-8") as f:
f.write(json.dumps(doc, ensure_ascii=False) + "\n")
return str(path)
# example
original_texts = [
"김민준님은 010-1234-5678로 문의했습니다.",
"계좌번호 110-234-567890으로 이체가 완료되었습니다."
]
masked_texts = [
"김**님은 010-****-5678로 문의했습니다.",
"계좌번호 110-***-******으로 이체가 완료되었습니다."
]
policy_doc = build_policy_rule_doc(
policy_id="POLICY_FIN_001",
document_type="금융문의",
domain="금융",
masking_strategy="정형마스킹",
original_texts=original_texts,
masked_texts=masked_texts,
pii_types=["이름", "전화번호", "계좌번호"]
)
save_jsonl(policy_doc)
동작 방식
- 원문과 마스킹문을 토큰화합니다.
- 각 분포의 엔트로피를 구합니다.
- h_masked - h_original을 정보 손실로 계산합니다.
- 손실률이 낮을수록 유용성이 높다고 판단합니다.
- 결과를 policy_rules.jsonl에 append 저장합니다.04_rag_db_enhancement.md+1
이 코드의 장점
- 정책별로 바로 비교할 수 있습니다.
- 문서 유형별로 저장할 수 있습니다.
- target_utility와 target_privacy를 함께 넣어 과적합을 줄일 수 있습니다.
- content_hash로 중복 정책을 추적할 수 있습니다.01_master_architecture.md+1
다음으로 붙이면 좋은 것
이 코드는 바로 다음 단계로 확장할 수 있습니다.
- RAGManager.add_policy_rule() 연결.
- golden_cases와의 분리 저장.
- 문서 유형별 utility_score 집계.
- base / base_rag / base_rag_lora 비교 로그 저장.
'지식창고 > 논문연구' 카테고리의 다른 글
| 자기진화모델 - 데이터 비식별화 평가 정량적 지표 계산 파이썬 예시 (0) | 2026.07.26 |
|---|---|
| 스스로 진화하는 AI시대 - 자가진화형 LLM 모델이란 ? (0) | 2026.07.13 |
| PRISMA(Preferred Reporting Items for Systematic Reviews and Meta-Analyses) 모델 (0) | 2026.05.30 |
| 연구G3- 연구모형 검증) Gemini 활용 AI 합의 과정 평가 모델 발굴 (0) | 2026.05.24 |
| LLM 기반 비식별화 및 동적 정책 학습 연구(2) (0) | 2026.03.26 |