Upload 3 files
Browse files- app.py +68 -30
- models/watermark_faster.py +14 -16
app.py
CHANGED
|
@@ -1,13 +1,18 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
from models.watermark_faster import watermark_model
|
| 3 |
-
import pdb
|
| 4 |
from options import get_parser_main_model
|
| 5 |
|
| 6 |
opts = get_parser_main_model().parse_args()
|
| 7 |
-
model = watermark_model(language=
|
| 8 |
-
def watermark_embed_demo(raw):
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
return watermarked_text
|
| 12 |
|
| 13 |
def watermark_extract(raw):
|
|
@@ -16,42 +21,75 @@ def watermark_extract(raw):
|
|
| 16 |
|
| 17 |
return f"{confidence:.2f}%"
|
| 18 |
|
| 19 |
-
def precise_watermark_detect(raw):
|
| 20 |
-
is_watermark, p_value, n, ones, z_value = model.watermark_detector_precise(raw)
|
| 21 |
confidence = (1 - p_value) * 100
|
| 22 |
|
| 23 |
return f"{confidence:.2f}%"
|
| 24 |
|
| 25 |
-
|
| 26 |
demo = gr.Blocks()
|
| 27 |
with demo:
|
| 28 |
with gr.Column():
|
| 29 |
-
gr.
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
|
|
|
|
| 53 |
|
| 54 |
if __name__ == "__main__":
|
| 55 |
gr.close_all()
|
| 56 |
demo.title = "Watermarking Text Generated by Black-Box Language Models"
|
| 57 |
-
demo.launch()
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
from models.watermark_faster import watermark_model
|
|
|
|
| 3 |
from options import get_parser_main_model
|
| 4 |
|
| 5 |
opts = get_parser_main_model().parse_args()
|
| 6 |
+
model = watermark_model(language=opts.language, mode=opts.mode, tau_word=opts.tau_word, lamda=opts.lamda)
|
|
|
|
| 7 |
|
| 8 |
+
def create_model(language,tau_word):
|
| 9 |
+
global model
|
| 10 |
+
model = watermark_model(language=language, mode=opts.mode, tau_word=tau_word, lamda=opts.lamda)
|
| 11 |
+
# gr.update(visible=True)
|
| 12 |
+
return language,tau_word
|
| 13 |
+
def watermark_embed_demo(raw,tau_word):
|
| 14 |
+
|
| 15 |
+
watermarked_text = model.embed(raw,tau_word)
|
| 16 |
return watermarked_text
|
| 17 |
|
| 18 |
def watermark_extract(raw):
|
|
|
|
| 21 |
|
| 22 |
return f"{confidence:.2f}%"
|
| 23 |
|
| 24 |
+
def precise_watermark_detect(raw,tau_word):
|
| 25 |
+
is_watermark, p_value, n, ones, z_value = model.watermark_detector_precise(raw,tau_word)
|
| 26 |
confidence = (1 - p_value) * 100
|
| 27 |
|
| 28 |
return f"{confidence:.2f}%"
|
| 29 |
|
|
|
|
| 30 |
demo = gr.Blocks()
|
| 31 |
with demo:
|
| 32 |
with gr.Column():
|
| 33 |
+
with gr.Row():
|
| 34 |
+
with gr.Column(scale=9):
|
| 35 |
+
gr.Markdown(
|
| 36 |
+
"""
|
| 37 |
+
# 💦[Watermarking Text Generated by Black-Box Language Models](https://arxiv.org/abs/2305.08883)
|
| 38 |
+
"""
|
| 39 |
+
)
|
| 40 |
+
language = gr.Dropdown(
|
| 41 |
+
label="Language", choices=["English", "Chinese"], value="English"
|
| 42 |
+
)
|
| 43 |
+
tau_word = gr.Number(label="tau_word", value=0.8)
|
| 44 |
+
|
| 45 |
+
# with gr.Column():
|
| 46 |
+
# with gr.Row():
|
| 47 |
+
# gr.Markdown("# Watermarking Text Generated by Black-Box Language Models")
|
| 48 |
+
# with gr.Row(scale=0.25):
|
| 49 |
+
# language = gr.Dropdown(
|
| 50 |
+
# label="Language", choices=["English", "Chinese"], value="English"
|
| 51 |
+
# )
|
| 52 |
+
# tau_word = gr.Number(label="tau_word", value=0.8)#gr.Slider(0, 1, value=0.8, label="tau_word", info="Choose between 0 and 1")
|
| 53 |
+
# model_button = gr.Button("Load Model")
|
| 54 |
+
# inputs = [language,tau_word]
|
| 55 |
+
# model_button.click(fn=create_model, inputs=inputs,outputs=[language,tau_word])
|
| 56 |
+
with gr.Tab("Welcome"):
|
| 57 |
+
gr.Markdown(
|
| 58 |
+
"""
|
| 59 |
+
This space exhibits a watermarking technique that allows third parties to independently inject an authentication watermark into generated text.
|
| 60 |
+
We provide implementations for both English and Chinese text (you can select the respective language in the top right corner).
|
| 61 |
+
Furthermore, you can adjust the value of $\\tau_{word}$ to control the similarity between the original text and the watermarked text.
|
| 62 |
+
We recommend setting $\\tau_{word}$ at 0.8 for English and 0.75 for Chinese.
|
| 63 |
+
Generally, a larger $\\tau_{word}$ increases the similarity between the original and watermarked text, but it also weakens the strength of the watermark.
|
| 64 |
+
More details can be found in our [ArXiv preprint](https://arxiv.org/abs/2305.08883).
|
| 65 |
+
"""
|
| 66 |
+
)
|
| 67 |
+
with gr.Tab("Watermark Injection & Detection"):
|
| 68 |
+
language.change(fn=create_model, inputs=language,outputs=language)
|
| 69 |
+
with gr.Row():
|
| 70 |
+
inputs = gr.TextArea(label="Input text", placeholder="Copy your text here...")
|
| 71 |
+
output = gr.Textbox(label="Watermarked Text",lines=7)
|
| 72 |
+
analysis_button = gr.Button("Inject Watermark")
|
| 73 |
+
inputs_embed = [inputs,tau_word]
|
| 74 |
+
analysis_button.click(fn=watermark_embed_demo, inputs=inputs_embed, outputs=output)
|
| 75 |
+
|
| 76 |
+
inputs_w = gr.TextArea(label="Text to Analyze", placeholder="Copy your watermarked text here...")
|
| 77 |
+
with gr.Row():
|
| 78 |
+
mode = gr.Dropdown(
|
| 79 |
+
label="Detection Mode", choices=["Fast", "Precise"], value="Fast"
|
| 80 |
+
)
|
| 81 |
+
output_detect = gr.Textbox(label="Confidence (the likelihood of the text containing a watermark)")
|
| 82 |
+
detect_button = gr.Button("Detect")
|
| 83 |
|
| 84 |
+
def detect_watermark(inputs_w, mode, tau_word):
|
| 85 |
+
if mode == "Fast":
|
| 86 |
+
return watermark_extract(inputs_w)
|
| 87 |
+
else:
|
| 88 |
+
return precise_watermark_detect(inputs_w,tau_word)
|
| 89 |
|
| 90 |
+
detect_button.click(fn=detect_watermark, inputs=[inputs_w, mode, tau_word], outputs=output_detect)
|
| 91 |
|
| 92 |
if __name__ == "__main__":
|
| 93 |
gr.close_all()
|
| 94 |
demo.title = "Watermarking Text Generated by Black-Box Language Models"
|
| 95 |
+
demo.launch(share = True, server_port=8898)
|
models/watermark_faster.py
CHANGED
|
@@ -8,8 +8,8 @@ import hashlib
|
|
| 8 |
from scipy.stats import norm
|
| 9 |
import gensim
|
| 10 |
import pdb
|
| 11 |
-
|
| 12 |
-
|
| 13 |
from transformers import AutoTokenizer, AutoModelForSequenceClassification
|
| 14 |
|
| 15 |
from transformers import BertForMaskedLM, BertTokenizer, RobertaForSequenceClassification, RobertaTokenizer
|
|
@@ -21,8 +21,6 @@ import paddle
|
|
| 21 |
from jieba import posseg
|
| 22 |
paddle.enable_static()
|
| 23 |
import re
|
| 24 |
-
nltk.download('punkt')
|
| 25 |
-
nltk.download('averaged_perceptron_tagger')
|
| 26 |
def cut_sent(para):
|
| 27 |
para = re.sub('([。!?\?])([^”’])', r'\1\n\2', para)
|
| 28 |
para = re.sub('([。!?\?][”’])([^,。!?\?\n ])', r'\1\n\2', para)
|
|
@@ -70,6 +68,7 @@ class watermark_model:
|
|
| 70 |
self.w2v_model = api.load("glove-wiki-gigaword-100")
|
| 71 |
nltk.download('stopwords')
|
| 72 |
self.stop_words = set(stopwords.words('english'))
|
|
|
|
| 73 |
|
| 74 |
def cut(self,ori_text,text_len):
|
| 75 |
if self.language == 'Chinese':
|
|
@@ -273,8 +272,7 @@ class watermark_model:
|
|
| 273 |
return all_processed_tokens,new_index_space
|
| 274 |
|
| 275 |
|
| 276 |
-
def filter_candidates(self, init_candidates_list, tokens, index_space, input_text):
|
| 277 |
-
|
| 278 |
all_context_word_similarity_scores = self.context_word_sim(init_candidates_list, tokens, index_space, input_text)
|
| 279 |
|
| 280 |
all_sentence_similarity_scores = self.sentence_sim(init_candidates_list, tokens, index_space, input_text)
|
|
@@ -287,7 +285,7 @@ class watermark_model:
|
|
| 287 |
for idx, candidate in enumerate(init_candidates):
|
| 288 |
global_word_similarity_score = self.global_word_sim(tokens[masked_token_index], candidate)
|
| 289 |
word_similarity_score = self.lamda*context_word_similarity_scores[idx]+(1-self.lamda)*global_word_similarity_score
|
| 290 |
-
if word_similarity_score >=
|
| 291 |
filtered_candidates.append((candidate, word_similarity_score))
|
| 292 |
|
| 293 |
if len(filtered_candidates) >= 1:
|
|
@@ -320,7 +318,7 @@ class watermark_model:
|
|
| 320 |
|
| 321 |
return best_candidates, new_index_space
|
| 322 |
|
| 323 |
-
def watermark_embed(self,text):
|
| 324 |
input_text = text
|
| 325 |
# Tokenize the input text
|
| 326 |
tokens = self.tokenizer.tokenize(input_text)
|
|
@@ -344,7 +342,7 @@ class watermark_model:
|
|
| 344 |
init_candidates, new_index_space = self.candidates_gen(tokens,index_space,input_text, 8, 0)
|
| 345 |
if len(new_index_space)==0:
|
| 346 |
return text
|
| 347 |
-
enhanced_candidates, new_index_space = self.filter_candidates(init_candidates,tokens,new_index_space,input_text)
|
| 348 |
|
| 349 |
enhanced_candidates, new_index_space = self.get_candidate_encodings(tokens, enhanced_candidates, new_index_space)
|
| 350 |
|
|
@@ -356,7 +354,7 @@ class watermark_model:
|
|
| 356 |
watermarked_text = re.sub(r'(?<=[\u4e00-\u9fff])\s+(?=[\u4e00-\u9fff,。?!、:])|(?<=[\u4e00-\u9fff,。?!、:])\s+(?=[\u4e00-\u9fff])', '', watermarked_text)
|
| 357 |
return watermarked_text
|
| 358 |
|
| 359 |
-
def embed(self, ori_text):
|
| 360 |
sents = self.sent_tokenize(ori_text)
|
| 361 |
sents = [s for s in sents if s.strip()]
|
| 362 |
num_sents = len(sents)
|
|
@@ -369,9 +367,9 @@ class watermark_model:
|
|
| 369 |
sent_pair = sents[i]
|
| 370 |
# keywords = jieba.analyse.extract_tags(sent_pair, topK=5, withWeight=False)
|
| 371 |
if len(watermarked_text) == 0:
|
| 372 |
-
watermarked_text = self.watermark_embed(sent_pair)
|
| 373 |
else:
|
| 374 |
-
watermarked_text = watermarked_text + self.watermark_embed(sent_pair)
|
| 375 |
if len(self.get_encodings_fast(ori_text)) == 0:
|
| 376 |
# print(ori_text)
|
| 377 |
return ''
|
|
@@ -411,7 +409,7 @@ class watermark_model:
|
|
| 411 |
is_watermark = z >= threshold
|
| 412 |
return is_watermark, p_value, n, ones, z
|
| 413 |
|
| 414 |
-
def get_encodings_precise(self, text):
|
| 415 |
# pdb.set_trace()
|
| 416 |
sents = self.sent_tokenize(text)
|
| 417 |
sents = [s for s in sents if s.strip()]
|
|
@@ -441,7 +439,7 @@ class watermark_model:
|
|
| 441 |
continue
|
| 442 |
|
| 443 |
init_candidates, new_index_space = self.candidates_gen(tokens,index_space,sent_pair, 8, 0)
|
| 444 |
-
enhanced_candidates, new_index_space = self.filter_candidates(init_candidates,tokens,new_index_space,sent_pair)
|
| 445 |
|
| 446 |
# pdb.set_trace()
|
| 447 |
for j,idx in enumerate(new_index_space):
|
|
@@ -451,9 +449,9 @@ class watermark_model:
|
|
| 451 |
return encodings
|
| 452 |
|
| 453 |
|
| 454 |
-
def watermark_detector_precise(self,text,alpha=0.05):
|
| 455 |
p = 0.5
|
| 456 |
-
encodings = self.get_encodings_precise(text)
|
| 457 |
n = len(encodings)
|
| 458 |
ones = sum(encodings)
|
| 459 |
if n == 0:
|
|
|
|
| 8 |
from scipy.stats import norm
|
| 9 |
import gensim
|
| 10 |
import pdb
|
| 11 |
+
from transformers import BertForMaskedLM as WoBertForMaskedLM
|
| 12 |
+
from wobert import WoBertTokenizer
|
| 13 |
from transformers import AutoTokenizer, AutoModelForSequenceClassification
|
| 14 |
|
| 15 |
from transformers import BertForMaskedLM, BertTokenizer, RobertaForSequenceClassification, RobertaTokenizer
|
|
|
|
| 21 |
from jieba import posseg
|
| 22 |
paddle.enable_static()
|
| 23 |
import re
|
|
|
|
|
|
|
| 24 |
def cut_sent(para):
|
| 25 |
para = re.sub('([。!?\?])([^”’])', r'\1\n\2', para)
|
| 26 |
para = re.sub('([。!?\?][”’])([^,。!?\?\n ])', r'\1\n\2', para)
|
|
|
|
| 68 |
self.w2v_model = api.load("glove-wiki-gigaword-100")
|
| 69 |
nltk.download('stopwords')
|
| 70 |
self.stop_words = set(stopwords.words('english'))
|
| 71 |
+
self.nlp = spacy.load('en_core_web_sm')
|
| 72 |
|
| 73 |
def cut(self,ori_text,text_len):
|
| 74 |
if self.language == 'Chinese':
|
|
|
|
| 272 |
return all_processed_tokens,new_index_space
|
| 273 |
|
| 274 |
|
| 275 |
+
def filter_candidates(self, init_candidates_list, tokens, index_space, input_text, tau_word):
|
|
|
|
| 276 |
all_context_word_similarity_scores = self.context_word_sim(init_candidates_list, tokens, index_space, input_text)
|
| 277 |
|
| 278 |
all_sentence_similarity_scores = self.sentence_sim(init_candidates_list, tokens, index_space, input_text)
|
|
|
|
| 285 |
for idx, candidate in enumerate(init_candidates):
|
| 286 |
global_word_similarity_score = self.global_word_sim(tokens[masked_token_index], candidate)
|
| 287 |
word_similarity_score = self.lamda*context_word_similarity_scores[idx]+(1-self.lamda)*global_word_similarity_score
|
| 288 |
+
if word_similarity_score >= tau_word and sentence_similarity_scores[idx] >= self.tau_sent:
|
| 289 |
filtered_candidates.append((candidate, word_similarity_score))
|
| 290 |
|
| 291 |
if len(filtered_candidates) >= 1:
|
|
|
|
| 318 |
|
| 319 |
return best_candidates, new_index_space
|
| 320 |
|
| 321 |
+
def watermark_embed(self,text,tau_word):
|
| 322 |
input_text = text
|
| 323 |
# Tokenize the input text
|
| 324 |
tokens = self.tokenizer.tokenize(input_text)
|
|
|
|
| 342 |
init_candidates, new_index_space = self.candidates_gen(tokens,index_space,input_text, 8, 0)
|
| 343 |
if len(new_index_space)==0:
|
| 344 |
return text
|
| 345 |
+
enhanced_candidates, new_index_space = self.filter_candidates(init_candidates,tokens,new_index_space,input_text,tau_word)
|
| 346 |
|
| 347 |
enhanced_candidates, new_index_space = self.get_candidate_encodings(tokens, enhanced_candidates, new_index_space)
|
| 348 |
|
|
|
|
| 354 |
watermarked_text = re.sub(r'(?<=[\u4e00-\u9fff])\s+(?=[\u4e00-\u9fff,。?!、:])|(?<=[\u4e00-\u9fff,。?!、:])\s+(?=[\u4e00-\u9fff])', '', watermarked_text)
|
| 355 |
return watermarked_text
|
| 356 |
|
| 357 |
+
def embed(self, ori_text, tau_word):
|
| 358 |
sents = self.sent_tokenize(ori_text)
|
| 359 |
sents = [s for s in sents if s.strip()]
|
| 360 |
num_sents = len(sents)
|
|
|
|
| 367 |
sent_pair = sents[i]
|
| 368 |
# keywords = jieba.analyse.extract_tags(sent_pair, topK=5, withWeight=False)
|
| 369 |
if len(watermarked_text) == 0:
|
| 370 |
+
watermarked_text = self.watermark_embed(sent_pair, tau_word)
|
| 371 |
else:
|
| 372 |
+
watermarked_text = watermarked_text + self.watermark_embed(sent_pair, tau_word)
|
| 373 |
if len(self.get_encodings_fast(ori_text)) == 0:
|
| 374 |
# print(ori_text)
|
| 375 |
return ''
|
|
|
|
| 409 |
is_watermark = z >= threshold
|
| 410 |
return is_watermark, p_value, n, ones, z
|
| 411 |
|
| 412 |
+
def get_encodings_precise(self, text, tau_word):
|
| 413 |
# pdb.set_trace()
|
| 414 |
sents = self.sent_tokenize(text)
|
| 415 |
sents = [s for s in sents if s.strip()]
|
|
|
|
| 439 |
continue
|
| 440 |
|
| 441 |
init_candidates, new_index_space = self.candidates_gen(tokens,index_space,sent_pair, 8, 0)
|
| 442 |
+
enhanced_candidates, new_index_space = self.filter_candidates(init_candidates,tokens,new_index_space,sent_pair,tau_word)
|
| 443 |
|
| 444 |
# pdb.set_trace()
|
| 445 |
for j,idx in enumerate(new_index_space):
|
|
|
|
| 449 |
return encodings
|
| 450 |
|
| 451 |
|
| 452 |
+
def watermark_detector_precise(self,text,tau_word,alpha=0.05):
|
| 453 |
p = 0.5
|
| 454 |
+
encodings = self.get_encodings_precise(text,tau_word)
|
| 455 |
n = len(encodings)
|
| 456 |
ones = sum(encodings)
|
| 457 |
if n == 0:
|