텍스트 워터마킹
텍스트 워터마킹 (Text Watermarking)
텍스트 워터마킹은 생성된 토큰 선택에 통계적 신호를 박아 넣는 기법이에요. 생성을 아는 검출기(detector)는 모델 가중치에 접근하지 않고도 이 신호를 검사할 수 있어요.
설계와 범위는 RFC #53916에서 다뤄져요.
설정 (Configuration)
엔진 시작 시 알고리즘과 비밀 키를 구성해서 워터마킹을 활성화해요:
vllm serve MODEL \
--watermark-config '{"algorithm":"gumbel","key":42}'
--watermark-config를 생략하면 워터마킹은 비활성화돼요. Gumbel-max(gumbel)가 활성화된 WatermarkConfig 안에서의 기본 알고리즘이에요. 워터마킹이 구성되면 요청에 대해 기본적으로 활성화돼요.
요청은 엔진 레벨 알고리즘이나 키를 바꾸지 않고도 옵트아웃(opt out)할 수 있어요:
from vllm import SamplingParams
sampling_params = SamplingParams(watermarking=False)
OpenAI 호환 API도 같은 watermarking: false 요청 필드를 받아요. 워터마킹이 필수인 배포는 이 필드를 신뢰할 수 있는 호출자로 제한하거나, 인그레스 경계에서 값 검증·제거를 해서 신뢰할 수 없는 클라이언트가 옵트아웃할 수 없게 해야 해요.
context_width는 각 워터마크 결정을 시드하는 이전 출력 토큰 수를 제어하고 기본값은 4예요. 값이 클수록 삽입·삭제·치환 시 더 많은 이후 컨텍스트가 바뀌므로 워터마크가 편집에 덜 견고해져요. 16을 넘는 값도 허용되지만 경고를 냅니다.
아키텍처 (Architecture)
WatermarkConfig가 알고리즘과 PRF를 선택해요. Model Runner V2가 해당 Watermarker를 만들고, GPUWatermarkSampler가 온도, min-p, top-k, top-p가 적용된 뒤 최종 확률적 토큰 선택에서 그 워터마커를 호출해요. 워터마커는 토큰을 직접 고르거나 로짓을 변환해 vLLM의 랜덤 샘플러에 위임할 수 있어요.
검출은 생성과 분리돼 있어요. vLLM은 참조 알고리즘용 검출기 프리미티브를 제공해요. WatermarkDetector는 토큰 ID를 소비하므로, 호출자는 생성과 일치하는 토크나이저와 워터마크 프로파일을 쓸 책임을 유지해요.
알고리즘 (Algorithms)
Gumbel-max
Gumbel-max는 키, 이전 생성 토큰 컨텍스트, 그리고 각 후보 토큰에서 결정적 의사난수 값을 도출한 뒤, 그 결과 Gumbel 노이즈를 범주형 샘플링에 사용해요. Aaronson의 원래 발표를 참고하세요.
Gumbel-max는 확률적 샘플링을 요구해요. Greedy 요청(temperature=0)은 워터마킹을 우회하고 워커당 한 번 경고를 발생시켜요.
단일 키 Gumbel-max 워터마킹은 퇴화 생성(degenerate generations)을 늘릴 수 있어요. 특히 컨텍스트가 반복될 때 같은 키의 랜덤 벡터를 재사용하면, 최대 토큰 한도까지 이어지는 반복 루프를 강화할 수 있어요.
SynthID-Text
SynthID-Text는 계획 중이지만 현재는 구현돼 있지 않아요.
의사난수 함수 (Pseudorandom functions)
워터마크 PRF는 비밀 키, 토큰 컨텍스트, 후보 토큰을 재현 가능한 랜덤 값으로 바꿔요. 생성과 검출은 장치와 릴리스를 걸쳐 동일한 값을 만들어야 해요. 값은 또한 샘플링 알고리즘과 검출기 통계에 충분히 균일하고 독립적이어야 해요. PRF 선택은 고급 호환성·성능 설정이라 대부분의 사용자는 기본값을 유지하는 게 좋아요.
워터마킹 생성은 현재 philox PRF를 지원해요:
philox는 Random123 논문의 counter-based Philox4x32-10 생성기에 기반해요. 병렬적이고 가속기에서 벡터화되며 CPU 전송을 피하지만, 암호학적 PRF가 아니고 키 복구나 위조 저항을 제공하지 않아요. vLLM은 입력 매핑을 버전화하고 호환성 벡터를 제공해서 생성과 검출이 상호 운용 가능하게 해요.
검출 (Detection)
검출기 프리미티브는 토큰 ID로 동작하고 모델 가중치를 요구하지 않아요:
from transformers import AutoTokenizer
from vllm.v1.watermarking import GumbelWatermarkDetector
tokenizer = AutoTokenizer.from_pretrained(MODEL)
token_ids = tokenizer.encode(text, add_special_tokens=False)
result = GumbelWatermarkDetector(key=42, prf="philox").detect(token_ids)
print(result.p_value, result.is_watermarked)
토크나이저, 알고리즘, PRF, 키, 컨텍스트 너비는 생생과 일치해야 해요. Gumbel-max 검출은 기본적으로 반복 컨텍스트를 한 번만 채점해서 동일한 PRF 랜덤 벡터가 독립적 증거로 취급되지 않게 해요. 검출기의 보정이 상관 점수용으로 조정되지 않았다면 deduplicate_contexts=True를 유지해요.
보고되는 p-value는 채점된 PRF 입력이 독립적이라는 가정 아래 보정돼요. 배포는 고정 키 하나를 쓰므로 문서 전반에서 반복되는 구조가 같은 PRF 값을 재사용해, 문서 내에서 컨텍스트가 중복 제거되더라도 실현된 오탐(false-positive)률이 키에 따라 달라질 수 있어요. 배포된 키로 대표적인 비워터마킹 트래픽에서 오탐률을 측정한 뒤에 is_watermarked를 결정에 사용하세요.
최소 HTTP 검출기는 examples/basic/online_serving/watermark_detection_server.py에 있어요:
python examples/basic/online_serving/watermark_detection_server.py \
--tokenizer MODEL --key 42 --prf philox
curl http://localhost:8000/detect \
-H 'Content-Type: application/json' \
-d '{"text":"Text to inspect"}'
점수와 p-value는 토큰별 워터마크 신호에 대한 정보를 노출해요. 반복된 쿼리는 이 정보로 워터마크 출력을 흉내 내는 텍스트를 만들거나, 워터마크가 더 이상 검출되지 않게 워터마크 텍스트를 수정하는 데 쓸 수 있어요.
제한 사항 (Limitations)
- 워터마킹은 현재 Model Runner V2에서만 사용할 수 있어요.
- Gumbel-max는 스펙큘레이티브 디코딩과 함께 구성할 수 없어요.
- 빔 서치는 모델 로그 확률에서 후보를 확장하므로 Gumbel-max 워터마킹을 적용하지 않아요.
- vLLM 샘플러를 커스텀 샘플러로 교체하는 모델은 구성된 워터마킹을 사용할 수 없어요.
더 알아보기
- vLLM 공식 문서: Text watermarking