coredot.today
기술 탐색으로 돌아가기
신뢰·안전성장

LLM 텍스트 워터마킹

단어를 초록과 빨강으로 나누고 초록 쪽을 살짝 밀어 준다 — 글자 하나 바꾸지 않고 '기계를 거친 글'에 통계 서명을 남기는 방법

직접 해보기 3
종류
오픈소스
라이선스
Apache-2.0
언어
Python
사실 확인
2026.10.24

TRY IT

직접 해보기

OVERVIEW

개요

무엇인가

메릴랜드대 커천바우어(Kirchenbauer)·가이핑(Geiping) 등이 2023년 1월 arXiv에 올리고 ICML 2023에서 발표한 「A Watermark for Large Language Models」의 공식 구현입니다. 언어 모델이 다음 단어를 고르는 순간에 표시를 심고, 나중에 글만 보고 그 표시가 있는지를 p-값으로 판정합니다.

사후 탐지기("이 글이 AI스러운가")는 AI 글과 사람 글이 통계적으로 다르다는 가정에 기대고, 그 여백이 좁아질수록 비원어민의 글을 AI로 오인하는 사고를 냅니다. 워터마크는 질문을 바꿉니다. "우리가 심어 둔 신호가 있는가"를 묻기 때문에, 위양성률을 글쓴이의 문체와 무관하게 계산할 수 있습니다. 2026년 8월 Anthropic이 Claude 텍스트에 적용한 워터마크도 같은 "생성 시점에 심는" 계열입니다(구글 SynthID-Text 방식 기반).

어떻게 동작하나

1. 씨앗
바로 앞 토큰(들)을 해시해 난수 생성기의 씨앗으로 삼는다. 비밀 키가 여기에 섞인다.
2. 분할
그 씨앗으로 어휘 전체를 비율 γ의 초록 목록과 나머지 빨간 목록으로 무작위로 나눈다.
3. 가산점
빨강을 금지하지 않고 초록 토큰의 로짓에 상수 δ를 더한다. 후보가 비슷비슷한 자리(높은 엔트로피)에서만 순위가 뒤집히고, "Barack" 다음의 "Obama"처럼 답이 정해진 자리는 그대로 둔다.
4. 검출
글을 받아 앞 토큰으로 분할을 재현하고 초록 토큰 수를 센다. 모델도 API도 필요 없다.

검출 통계량은 우연이라면 나왔어야 할 초록 개수와의 차이를 흔들림 크기로 나눈 z-점수입니다(T는 채점한 토큰 수, |s|G는 초록 토큰 수).

z=∣s∣G−γTTγ(1−γ)z = \frac{|s|_G - \gamma T}{\sqrt{T\gamma(1-\gamma)}}

논문은 z > 4를 기준으로 삼았고, 이때 위양성률은 약 3×10-5입니다. 논문 그림 1의 예에서는 36개 토큰 중 28개가 초록(γ=0.25라면 우연히 9개)이라 z=7.4, p ≈ 6×10-14가 나왔습니다. 200토큰 생성물 약 500건 실험에서 다항 샘플링(δ=2, γ=0.25)의 검출률은 99.4%, 위양성은 0건이었습니다.

빠른 시작

허깅페이스 generate에 로짓 프로세서 하나를 끼우는 구조입니다. 패키지 배포는 없고, 저장소의 extended_watermark_processor.py를 가져다 씁니다(README 권장 구현). 아래는 README 예제를 한데 이은 것입니다.

python
from transformers import AutoModelForCausalLM, AutoTokenizer, LogitsProcessorList
from extended_watermark_processor import WatermarkLogitsProcessor, WatermarkDetector

tokenizer = AutoTokenizer.from_pretrained("facebook/opt-6.7b")
model = AutoModelForCausalLM.from_pretrained("facebook/opt-6.7b")
vocab = list(tokenizer.get_vocab().values())

# 생성: 초록 목록 비율 0.25, 가산점 2.0 (README 권장 기본값)
processor = WatermarkLogitsProcessor(vocab=vocab, gamma=0.25, delta=2.0, seeding_scheme="selfhash")
inputs = tokenizer("The museum guide explained that", return_tensors="pt").to(model.device)
out = model.generate(**inputs, logits_processor=LogitsProcessorList([processor]))
text = tokenizer.batch_decode(out[:, inputs["input_ids"].shape[-1]:], skip_special_tokens=True)[0]

# 검출: 생성 때와 같은 gamma·seeding_scheme·장치를 써야 한다
detector = WatermarkDetector(vocab=vocab, gamma=0.25, seeding_scheme="selfhash",
                             device=model.device, tokenizer=tokenizer,
                             z_threshold=4.0, normalizers=[], ignore_repeated_ngrams=True)
print(detector.detect(text))  # z-점수, p-값, 초록 비율 등

README가 강조하는 설정 세 가지: 문맥 폭 h는 4 정도(selfhash), 검출 때는 반복 n-그램을 채점에서 빼고(ignore_repeated_ngrams=True), 기본 키 15485863은 실제 배포에서 재사용하지 않습니다. 브라우저 데모는 위 허깅페이스 스페이스에 있습니다.

언제 쓰고, 언제 피하나

상황잘 표시된다거의 표시되지 않는다
글의 종류에세이·블로그·이메일 초안·보고서 서술문코드, 단답형 사실, 인용·원문 복창
작업 형태모델이 처음부터 쓴 긴 글교정·번역·요약처럼 남의 글을 손보는 작업
쓰임대량 합성 콘텐츠 걸러내기, 학습 데이터 정제, 플랫폼 라벨링한 사람의 한 편을 두고 유죄를 다투는 판정
모델 배포API로 제공하는 상용 모델가중치가 공개된 모델 — 샘플링 코드를 고치면 끝난다

한계와 주의

  • '작성함'이 아니라 '처리함'이다. 내 글을 모델이 다듬으면 고친 부분에만 서명이 붙는다. 서명이 없다고 사람이 쓴 것도 아니다(짧은 글, 저엔트로피 글, 다른 모델, 충분한 재작성).
  • 바꿔 쓰기에 약하다. 논문 실험에서 T5로 30%를 갈아 끼우면 AUC가 0.998에서 0.954로, 혼란도는 3배가 됐다. 2023년 3월 반박 논문(Sadasivan 등)의 재귀적 바꿔 쓰기에서는 검출률(위양성 1% 기준)이 99.8%에서 9.7%로 무너졌다.
  • 문맥 폭 h의 줄다리기. h를 키우면 규칙을 알아내기 어려워지지만, 토큰 하나만 바꿔도 뒤따르는 분할이 흔들리는 '공격 증폭'이 커진다.
  • 이모지 어택·동형이의 문자. 단어마다 이모지를 붙이게 한 뒤 지우면 분할이 통째로 바뀐다. 문자 공격은 검사 전 정규화로 막는다.
  • 위조(spoofing). 규칙을 알아낸 공격자는 사람 글을 AI 글처럼 보이게 만들 수도 있다. 비공개 키 방식은 그 대가로 외부 검증이 불가능해진다.
  • 한국어는 임계값을 따로 검증한다. 논문 실험은 영어(OPT, C4)뿐이고, 한국어는 토크나이저마다 같은 문장의 토큰 수가 크게 달라 "몇 토큰이면 충분한가"를 그대로 옮길 수 없다.
  • 이미지 워터마크(SynthID-Image)는 원리가 다르다. 주파수 영역 신호라 역설계·제거 사례가 따로 있다(관련 글 참고).

DEEP DIVE

더 깊이 읽기