ahans30 commited on
Commit
d389c0e
1 Parent(s): ed4db3a

initial commit

Browse files
.gitignore ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/#use-with-ide
110
+ .pdm.toml
111
+
112
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113
+ __pypackages__/
114
+
115
+ # Celery stuff
116
+ celerybeat-schedule
117
+ celerybeat.pid
118
+
119
+ # SageMath parsed files
120
+ *.sage.py
121
+
122
+ # Environments
123
+ .env
124
+ .venv
125
+ env/
126
+ venv/
127
+ ENV/
128
+ env.bak/
129
+ venv.bak/
130
+
131
+ # Spyder project settings
132
+ .spyderproject
133
+ .spyproject
134
+
135
+ # Rope project settings
136
+ .ropeproject
137
+
138
+ # mkdocs documentation
139
+ /site
140
+
141
+ # mypy
142
+ .mypy_cache/
143
+ .dmypy.json
144
+ dmypy.json
145
+
146
+ # Pyre type checker
147
+ .pyre/
148
+
149
+ # pytype static type analyzer
150
+ .pytype/
151
+
152
+ # Cython debug symbols
153
+ cython_debug/
154
+
155
+ # PyCharm
156
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
159
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
+ #.idea/
161
+
162
+ samples/
163
+
164
+ **.*ipynb
LICENSE.md ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ BSD 3-Clause License
2
+
3
+ Copyright (c) 2023, Abhimanyu Hans, Avi Schwarzschild, Tom Goldstein
4
+
5
+ Redistribution and use in source and binary forms, with or without
6
+ modification, are permitted provided that the following conditions are met:
7
+
8
+ 1. Redistributions of source code must retain the above copyright notice, this
9
+ list of conditions and the following disclaimer.
10
+
11
+ 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ this list of conditions and the following disclaimer in the documentation
13
+ and/or other materials provided with the distribution.
14
+
15
+ 3. Neither the name of the copyright holder nor the names of its
16
+ contributors may be used to endorse or promote products derived from
17
+ this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
README-doc.md ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # <img src="./assets/bino-logo.svg" width=40 style="padding-top: 0px"/> Binoculars: Zero-Shot Detection of LLM-Generated Text [[paper]](https://arxiv.org/abs/2401.12070)
2
+
3
+ <p align="center">
4
+ <img src="assets/binoculars.jpg" width="300" height="300" alt="ool Binoculars with Falcon on Top">
5
+ </p>
6
+
7
+ We introduce Binoculars, a state-of-the-art method for detecting AI-generated text. Binoculars is a
8
+ zero-shot and domain-agnostic (requires no training data) method. It is based on a simple idea: most
9
+ decoder-only, causal language models have a huge overlap in pretraining datasets, for e.g. Common Crawl, Pile, etc.
10
+ More details about the method and results can be found in our paper **Spotting LLMs with Binoculars: Zero-Shot
11
+ Detection of Machine-Generated Text**.
12
+
13
+ ## Getting Started
14
+
15
+ ### Installation
16
+
17
+ To run the implementation of Binoculars, you can clone this repository and install the package using pip. This code was
18
+ developed and tested on Python This code was developed and tested with Python 3.9. To install the package, run the
19
+ following commands:
20
+
21
+ ```bash
22
+ $ git clone https://github.com/ahans30/Binoculars.git
23
+ $ cd Binoculars
24
+ $ pip install -e .
25
+ ```
26
+
27
+ ### Usage
28
+
29
+ Please note, this implementation comes with a fixed global threshold that is used to classify the input as AI-generated
30
+ or not. This threshold is selected using _Falcon-7B_ and _Falcon-7B-Instruct_ models for scoring. If you want to
31
+ use different scoring models, you can pass it as an argument to the `Binoculars` class. Please read the paper for more
32
+ details about the Binoculars work.
33
+
34
+ To detect AI-generated text, please use the following code snippet:
35
+
36
+ ```python
37
+ from binoculars import Binoculars
38
+
39
+ bino = Binoculars()
40
+
41
+ # ChatGPT (GPT-4) output when prompted with “Can you write a few sentences about a capybara that is an astrophysicist?"
42
+ sample_string = '''Dr. Capy Cosmos, a capybara unlike any other, astounded the scientific community with his
43
+ groundbreaking research in astrophysics. With his keen sense of observation and unparalleled ability to interpret
44
+ cosmic data, he uncovered new insights into the mysteries of black holes and the origins of the universe. As he
45
+ peered through telescopes with his large, round eyes, fellow researchers often remarked that it seemed as if the
46
+ stars themselves whispered their secrets directly to him. Dr. Cosmos not only became a beacon of inspiration to
47
+ aspiring scientists but also proved that intellect and innovation can be found in the most unexpected of creatures.'''
48
+
49
+ print(bino.compute_score(sample_string)) # 0.75661373
50
+ print(bino.predict(sample_string)) # 'AI-Generated'
51
+ ```
52
+
53
+ In the above code, user can also pass a `list` of `str` to `compute_score` and `predict` methods to get results for
54
+ the entire batch of samples.
55
+
56
+ ### Demo
57
+
58
+ We have also made a demo available to predict AI-generated text interactively with a simple UI
59
+ using [gradio](https://github.com/gradio-app/gradio). You can run the demo using the following command:
60
+
61
+ ```bash
62
+ $ python app.py
63
+ ```
64
+
65
+ ## Limitations
66
+
67
+ All AI-generated text detectors aim for accuracy, but none are perfect and can have multiple failure modes (e.g.,
68
+ Binoculars is more proficient in detecting English language text compared to other languages). This implementation is
69
+ for academic purposes only and should not be considered as a consumer product. We also strongly caution against using
70
+ Binoculars (or any detector) without human supervision.
71
+
72
+ ## Cite our work
73
+
74
+ If you find this work useful, please cite our paper:
75
+
76
+ ```bibtex
77
+ @misc{hans2024spotting,
78
+ title={Spotting LLMs With Binoculars: Zero-Shot Detection of Machine-Generated Text},
79
+ author={Abhimanyu Hans and Avi Schwarzschild and Valeriia Cherepanova and Hamid Kazemi and Aniruddha Saha and Micah Goldblum and Jonas Geiping and Tom Goldstein},
80
+ year={2024},
81
+ eprint={2401.12070},
82
+ archivePrefix={arXiv},
83
+ primaryClass={cs.CL}
84
+ }
85
+ ```
app.py ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ from demo.demo import app
2
+
3
+ if __name__ == "__main__":
4
+ # Launch the Gradio interface
5
+ app.launch(show_api=False, debug=True, share=True)
assets/bino-logo.svg ADDED
assets/binoculars.jpg ADDED
binoculars/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ from config import huggingface_config
2
+ from .detector import Binoculars
3
+
4
+ __all__ = ["Binoculars"]
binoculars/detector.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Union
2
+
3
+ import numpy as np
4
+ import torch
5
+ import transformers
6
+ from transformers import AutoModelForCausalLM, AutoTokenizer
7
+
8
+ from config import huggingface_config
9
+ from .utils import assert_tokenizer_consistency
10
+ from .metrics import perplexity, entropy
11
+
12
+ torch.set_grad_enabled(False)
13
+
14
+ GLOBAL_BINOCULARS_THRESHOLD = 0.9015310749276843 # selected using Falcon-7B and Falcon-7B-Instruct at bfloat16
15
+ DEVICE_1 = "cuda:0" if torch.cuda.is_available() else "cpu"
16
+ DEVICE_2 = "cuda:1" if torch.cuda.device_count() > 1 else DEVICE_1
17
+
18
+
19
+ class Binoculars(object):
20
+ def __init__(self,
21
+ observer_name_or_path: str = "tiiuae/falcon-7b",
22
+ performer_name_or_path: str = "tiiuae/falcon-7b-instruct",
23
+ use_bfloat16: bool = True,
24
+ max_token_observed: int = 512,
25
+ ) -> None:
26
+ assert_tokenizer_consistency(observer_name_or_path, performer_name_or_path)
27
+
28
+ self.observer_model = AutoModelForCausalLM.from_pretrained(observer_name_or_path,
29
+ device_map={"": DEVICE_1},
30
+ trust_remote_code=True,
31
+ torch_dtype=torch.bfloat16 if use_bfloat16
32
+ else torch.float32,
33
+ token=huggingface_config["TOKEN"]
34
+ )
35
+ self.performer_model = AutoModelForCausalLM.from_pretrained(performer_name_or_path,
36
+ device_map={"": DEVICE_2},
37
+ trust_remote_code=True,
38
+ torch_dtype=torch.bfloat16 if use_bfloat16
39
+ else torch.float32,
40
+ token=huggingface_config["TOKEN"]
41
+ )
42
+
43
+ self.observer_model.eval()
44
+ self.performer_model.eval()
45
+
46
+ self.tokenizer = AutoTokenizer.from_pretrained(observer_name_or_path)
47
+ if not self.tokenizer.pad_token:
48
+ self.tokenizer.pad_token = self.tokenizer.eos_token
49
+
50
+ self.max_token_observed = max_token_observed
51
+
52
+ def _tokenize(self, batch: list[str]) -> transformers.BatchEncoding:
53
+ batch_size = len(batch)
54
+ encodings = self.tokenizer(
55
+ batch,
56
+ return_tensors="pt",
57
+ padding="longest" if batch_size > 1 else False,
58
+ truncation=True,
59
+ max_length=self.max_token_observed,
60
+ return_token_type_ids=False).to(self.observer_model.device)
61
+ return encodings
62
+
63
+ @torch.inference_mode()
64
+ def _get_logits(self, encodings: transformers.BatchEncoding) -> torch.Tensor:
65
+ observer_logits = self.observer_model(**encodings.to(DEVICE_1)).logits
66
+ performer_logits = self.performer_model(**encodings.to(DEVICE_2)).logits
67
+ torch.cuda.synchronize()
68
+ return observer_logits, performer_logits
69
+
70
+ def compute_score(self, input_text: Union[list[str], str]) -> Union[float, list[float]]:
71
+ batch = [input_text] if isinstance(input_text, str) else input_text
72
+ encodings = self._tokenize(batch)
73
+ observer_logits, performer_logits = self._get_logits(encodings)
74
+ ppl = perplexity(encodings, performer_logits)
75
+ x_ppl = entropy(observer_logits.to(DEVICE_1), performer_logits.to(DEVICE_1),
76
+ encodings.to(DEVICE_1), self.tokenizer.pad_token_id)
77
+ binoculars_scores = ppl / x_ppl
78
+ binoculars_scores = binoculars_scores.tolist()
79
+ return binoculars_scores[0] if isinstance(input_text, str) else binoculars_scores
80
+
81
+ def predict(self, input_text: Union[list[str], str]) -> Union[list[str], str]:
82
+ binoculars_scores = np.array(self.compute_score(input_text))
83
+ pred = np.where(binoculars_scores < GLOBAL_BINOCULARS_THRESHOLD, "AI-Generated", "Human-Generated").tolist()
84
+ return pred
binoculars/metrics.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import torch
3
+ import transformers
4
+
5
+ ce_loss_fn = torch.nn.CrossEntropyLoss(reduction="none")
6
+ softmax_fn = torch.nn.Softmax(dim=-1)
7
+
8
+
9
+ def perplexity(encoding: transformers.BatchEncoding,
10
+ logits: torch.Tensor,
11
+ median: bool = False,
12
+ temperature: float = 1.0):
13
+ shifted_logits = logits[..., :-1, :].contiguous() / temperature
14
+ shifted_labels = encoding.input_ids[..., 1:].contiguous()
15
+ shifted_attention_mask = encoding.attention_mask[..., 1:].contiguous()
16
+
17
+ if median:
18
+ ce_nan = (ce_loss_fn(shifted_logits.transpose(1, 2), shifted_labels).
19
+ masked_fill(~shifted_attention_mask.bool(), float("nan")))
20
+ ppl = np.nanmedian(ce_nan.cpu().float().numpy(), 1)
21
+
22
+ else:
23
+ ppl = (ce_loss_fn(shifted_logits.transpose(1, 2), shifted_labels) *
24
+ shifted_attention_mask).sum(1) / shifted_attention_mask.sum(1)
25
+ ppl = ppl.to("cpu").float().numpy()
26
+
27
+ return ppl
28
+
29
+
30
+ def entropy(p_logits: torch.Tensor,
31
+ q_logits: torch.Tensor,
32
+ encoding: transformers.BatchEncoding,
33
+ pad_token_id: int,
34
+ median: bool = False,
35
+ sample_p: bool = False,
36
+ temperature: float = 1.0):
37
+ vocab_size = p_logits.shape[-1]
38
+ total_tokens_available = q_logits.shape[-2]
39
+ p_scores, q_scores = p_logits / temperature, q_logits / temperature
40
+
41
+ p_proba = softmax_fn(p_scores).view(-1, vocab_size)
42
+
43
+ if sample_p:
44
+ p_proba = torch.multinomial(p_proba.view(-1, vocab_size), replacement=True, num_samples=1).view(-1)
45
+
46
+ q_scores = q_scores.view(-1, vocab_size)
47
+
48
+ ce = ce_loss_fn(input=q_scores, target=p_proba).view(-1, total_tokens_available)
49
+ padding_mask = (encoding.input_ids != pad_token_id).type(torch.uint8)
50
+
51
+ if median:
52
+ ce_nan = ce.masked_fill(~padding_mask.bool(), float("nan"))
53
+ agg_ce = np.nanmedian(ce_nan.cpu().float().numpy(), 1)
54
+ else:
55
+ agg_ce = (((ce * padding_mask).sum(1) / padding_mask.sum(1)).to("cpu").float().numpy())
56
+
57
+ return agg_ce
binoculars/utils.py ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ from transformers import AutoTokenizer
2
+
3
+
4
+ def assert_tokenizer_consistency(model_id_1, model_id_2):
5
+ identical_tokenizers = (
6
+ AutoTokenizer.from_pretrained(model_id_1).vocab
7
+ == AutoTokenizer.from_pretrained(model_id_2).vocab
8
+ )
9
+ if not identical_tokenizers:
10
+ raise ValueError(f"Tokenizers are not identical for {model_id_1.name_of_path} and {model_id_2.name_of_path}.")
config.py ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ huggingface_config = {
4
+ # Only required for private models from Huggingface (e.g. LLaMA models)
5
+ "TOKEN": os.environ.get("HF_TOKEN", None)
6
+ }
demo/demo.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __all__ = ["app"]
2
+
3
+ import gradio as gr
4
+ from binoculars import Binoculars
5
+
6
+ BINO = Binoculars()
7
+ TOKENIZER = BINO.tokenizer
8
+ MINIMUM_TOKENS = 64
9
+
10
+
11
+ def count_tokens(text):
12
+ return len(TOKENIZER(text).input_ids)
13
+
14
+
15
+ def run_detector(input_str):
16
+ if count_tokens(input_str) < MINIMUM_TOKENS:
17
+ gr.Warning(f"Too short length. Need minimum {MINIMUM_TOKENS} tokens to run Binoculars.")
18
+ return ""
19
+ return f"{BINO.predict(input_str)}"
20
+
21
+
22
+ # def load_set(progress=gr.Progress()):
23
+ # tokens = [None] * 24
24
+ # for count in progress.tqdm(tokens, desc="Counting Tokens..."):
25
+ # time.sleep(0.01)
26
+ # return ["Loaded"] * 2
27
+
28
+
29
+ css = """
30
+ .green { color: black!important;line-height:1.9em; padding: 0.2em 0.2em; background: #ccffcc; border-radius:0.5rem;}
31
+ .red { color: black!important;line-height:1.9em; padding: 0.2em 0.2em; background: #ffad99; border-radius:0.5rem;}
32
+ .hyperlinks {
33
+ display: flex;
34
+ align-items: center;
35
+ align-content: center;
36
+ padding-top: 12px;
37
+ justify-content: flex-end;
38
+ margin: 0 10px; /* Adjust the margin as needed */
39
+ text-decoration: none;
40
+ color: #000; /* Set the desired text color */
41
+ }
42
+ """
43
+
44
+ capybara_problem = '''Dr. Capy Cosmos, a capybara unlike any other, astounded the scientific community with his groundbreaking research in astrophysics. With his keen sense of observation and unparalleled ability to interpret cosmic data, he uncovered new insights into the mysteries of black holes and the origins of the universe. As he peered through telescopes with his large, round eyes, fellow researchers often remarked that it seemed as if the stars themselves whispered their secrets directly to him. Dr. Cosmos not only became a beacon of inspiration to aspiring scientists but also proved that intellect and innovation can be found in the most unexpected of creatures.'''
45
+
46
+ with gr.Blocks(css=css,
47
+ theme=gr.themes.Default(font=[gr.themes.GoogleFont("Inconsolata"), "Arial", "sans-serif"])) as app:
48
+ with gr.Row():
49
+ with gr.Column(scale=3):
50
+ gr.HTML("<p><h1> binoculars: zero-shot llm-text detector</h1>")
51
+ with gr.Column(scale=1):
52
+ gr.HTML("""
53
+ <p>
54
+ <a href="https://arxiv.org/abs/2401.12070" target="_blank">paper</a>
55
+
56
+ <a href="https://github.com/AHans30/Binoculars" target="_blank">code</a>
57
+
58
+ <a href="mailto:[email protected]" target="_blank">contact</a>
59
+ """, elem_classes="hyperlinks")
60
+ with gr.Row():
61
+ input_box = gr.Textbox(value=capybara_problem, placeholder="Enter text here", lines=8, label="Input Text", )
62
+ with gr.Row():
63
+ clear_button = gr.ClearButton()
64
+ submit_button = gr.Button("Run Binoculars", variant="primary")
65
+ with gr.Row():
66
+ output_text = gr.Textbox(label="Prediction", value="AI-Generated")
67
+
68
+ with gr.Row():
69
+ gr.HTML("<p><p><p>")
70
+ with gr.Row():
71
+ gr.HTML("<p><p><p>")
72
+ with gr.Row():
73
+ gr.HTML("<p><p><p>")
74
+
75
+ with gr.Accordion("Disclaimer", open=False):
76
+ gr.Markdown(
77
+ """
78
+ - `Accuracy` :
79
+ - AI-generated text detectors aim for accuracy, but achieving 100% is challenging.
80
+ - The provided prediction is for demo purposes only and should not be considered a consumer product.
81
+ - Users are advised to exercise discretion, and we assume no liability for any use.
82
+ - `Detection Use Cases` :
83
+ - In this work, our focus is to achieve an ultra-low false positive rate, crucial for sensitive downstream use case (e.g., avoiding false accusations in academic honesty cases).
84
+ - We find optimal application in content moderation, for example in detecting AI-generated reviews on platforms like Amazon, Google, Yelp, etc. This represents one of the most compelling and noteworthy use cases for Binoculars.
85
+ - `Human Supervision Advisory` :
86
+ - Strongly caution against using Binoculars (or any detector) without human supervision.
87
+ - `Performance by Language` :
88
+ - As noted in our paper, Binoculars exhibit superior detection performance in the English language compared to other languages.
89
+ """
90
+ )
91
+
92
+ with gr.Accordion("Cite our work", open=False):
93
+ gr.Markdown(
94
+ """
95
+ ```bibtex
96
+ @misc{hans2024spotting,
97
+ title={Spotting LLMs With Binoculars: Zero-Shot Detection of Machine-Generated Text},
98
+ author={Abhimanyu Hans and Avi Schwarzschild and Valeriia Cherepanova and Hamid Kazemi and Aniruddha Saha and Micah Goldblum and Jonas Geiping and Tom Goldstein},
99
+ year={2024},
100
+ eprint={2401.12070},
101
+ archivePrefix={arXiv},
102
+ primaryClass={cs.CL}
103
+ }
104
+ """
105
+ )
106
+
107
+ # confidence_bar = gr.Label(value={"Confidence": 0})
108
+
109
+ # clear_button.click(lambda x: input_box., )
110
+ submit_button.click(run_detector, inputs=input_box, outputs=output_text)
111
+ clear_button.click(lambda: ("", ""), outputs=[input_box, output_text])
main.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from binoculars import Binoculars
2
+
3
+ bino = Binoculars()
4
+
5
+ # ChatGPT (GPT-4) output when prompted with “Can you write a few sentences about a capybara that is an astrophysicist?"
6
+ sample_string = '''Dr. Capy Cosmos, a capybara unlike any other, astounded the scientific community with his
7
+ groundbreaking research in astrophysics. With his keen sense of observation and unparalleled ability to interpret
8
+ cosmic data, he uncovered new insights into the mysteries of black holes and the origins of the universe. As he
9
+ peered through telescopes with his large, round eyes, fellow researchers often remarked that it seemed as if the
10
+ stars themselves whispered their secrets directly to him. Dr. Cosmos not only became a beacon of inspiration to
11
+ aspiring scientists but also proved that intellect and innovation can be found in the most unexpected of creatures.'''
12
+
13
+ print(bino.compute_score(sample_string)) # 0.75661373
14
+ print(bino.predict(sample_string)) # 'AI-Generated'
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ sentencepiece
2
+ transformers[torch] @ https://github.com/huggingface/transformers/archive/refs/tags/v4.31.0.zip
3
+ numpy
4
+ gradio
5
+ gradio_client
setup.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from setuptools import setup, find_packages
2
+
3
+ setup(
4
+ name='Binoculars',
5
+ version='0.0.10',
6
+ packages=find_packages(),
7
+ url='https://github.com/ahans30/Binoculars',
8
+ license=open("LICENSE.md", "r", encoding="utf-8").read(),
9
+ author='Authors of "Binoculars: Zero-Shot Detection of LLM-Generated Text"',
10
+ author_email='[email protected]',
11
+ description='A language model generated text detector.',
12
+ long_description=open("README.md", "r", encoding="utf-8").read(),
13
+ long_description_content_type="text/markdown",
14
+ install_requires=open("requirements.txt", "r", encoding="utf-8").read().splitlines(),
15
+ )