File size: 13,359 Bytes
f21a673
 
 
10f3d6f
 
f21a673
f842376
f21a673
 
 
 
 
f842376
f21a673
 
 
 
 
282a0cf
7df1b03
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4364c1e
 
7df1b03
4364c1e
 
 
 
 
 
 
7df1b03
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f21a673
7df1b03
f21a673
7df1b03
 
 
 
 
 
 
f21a673
7df1b03
 
 
 
 
 
f21a673
7df1b03
 
f21a673
7df1b03
 
 
 
 
 
 
 
 
 
 
 
 
 
f21a673
7df1b03
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f21a673
7df1b03
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f21a673
7df1b03
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f21a673
7df1b03
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f21a673
7df1b03
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f21a673
7df1b03
f21a673
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
import gradio as gr
from transformers import DebertaTokenizer, DebertaForSequenceClassification
from transformers import pipeline
import json


save_path_abstract = './fine-tuned-deberta'
model_abstract = DebertaForSequenceClassification.from_pretrained(save_path_abstract)
tokenizer_abstract = DebertaTokenizer.from_pretrained(save_path_abstract)

classifier_abstract = pipeline('text-classification', model=model_abstract, tokenizer=tokenizer_abstract)

save_path_essay = './fine-tuned-deberta'
model_essay = DebertaForSequenceClassification.from_pretrained(save_path_essay)
tokenizer_essay = DebertaTokenizer.from_pretrained(save_path_essay)

classifier_essay = pipeline('text-classification', model=model_essay, tokenizer=tokenizer_essay)

demo_essays = json.load(open('samples.json'))
index = None 


################# HELPER FUNCTIONS (DETECTION TAB) ####################

def process_result_detection_tab(text): 
    '''
    Classify the text into one of the four categories by averaging the soft predictions of the two models.

    Args: 
        text: str: the text to be classified
    Returns:
        dict: a dictionary with the following keys: 
            'Machine Generated': float: the probability that the text is machine generated
            'Human Written': float: the probability that the text is human written
            'Machine Written, Machine Humanized': float: the probability that the text is machine written and machine humanized
            'Human Written, Machine Polished': float: the probability that the text is human written and machine polished
    '''
    mapping = {'llm': 'Machine Generated', 'human':'Human Written', 'machine-humanized': 'Machine Written, Machine Humanized', 'machine-polished': 'Human Written, Machine Polished'} 

    # Initialize scores for all classes
    final_results = {label: 0.0 for label in mapping.values()}

    # Add scores from classifier_abstract
    if result['label'] in mapping:
        final_results[mapping[result['label']]] += 0.5 * result['score']

    # Add scores from classifier_essay
    if result_r['label'] in mapping:
        final_results[mapping[result_r['label']]] += 0.5 * result_r['score']

    print(final_results)
    return final_results

def update_detection_tab(name, uploaded_file, radio_input):
    ''' 
    Callback function to update the result of the classification based on the input text or uploaded file.
    Args: 
        name: str: the input text from the Textbox
        uploaded_file: file: the uploaded file from the file input
    Returns:
        dict: the result of the classification including labels and scores 
    ''' 

    if name == '' and uploaded_file is None:
        return "" 
    if uploaded_file is not None:
        return f"Work in progress"
    else:
        return process_result_detection_tab(name)
   
def active_button_detection_tab(input_text, file_input):
    '''
    Callback function to activate the 'Check Origin' button when the input text or file input
    is not empty. For text input, the button can be clickde only when the word count is between 
    50 and 500.

    Args:
        input_text: str: the input text from the textbox
        file_input: file: the uploaded file from the file input
    Returns:
        gr.Button: The 'Check Origin' button with the appropriate interactivity.
    '''

    if (input_text == "" and file_input is None) or (file_input is None and not (50 <= len(input_text.split()) <= 500)):
        return gr.Button("Check Origin", variant="primary", interactive=False) 
    
    return gr.Button("Check Origin", variant="primary", interactive=True)

def clear_detection_tab():
    '''
    Callback function to clear the input text and file input in the 'Try it!' tab. 
    The interactivity of the 'Check Origin' button is set to False to prevent user click when the Textbox is empty.

    Args:
        None
    Returns:
        str: An empty string to clear the Textbox.
        None: None to clear the file input.
        gr.Button: The 'Check Origin' button with no interactivity.
    '''

    return "", None,  gr.Button("Check Origin", variant="primary", interactive=False)

def count_words_detection_tab(text): 
    ''' 
    Callback function called when the input text is changed to update the word count. 
    Args:
        text: str: the input text from the Textbox
    Returns:
        str: the word count of the input text for the Markdown widget
    ''' 
    return (f'{len(text.split())}/500 words (Minimum 50 words)')


################# HELPER FUNCTIONS (CHALLENGE TAB) ####################

def clear_challenge_tab(): 
    ''' 
    Callback function to clear the text and result in the 'Challenge Yourself' tab.
    The interactivity of the buttons is set to False to prevent user click when the Textbox is empty.

    Args: 
        None
    Returns:
        gr.Button: The 'Machine-Generated' button with no interactivity.
        gr.Button: The 'Human-Written' button with no interactivity.
        gr.Button: The 'Machine-Humanized' button with no interactivity.
        gr.Button: The 'Machine-Polished' button with no interactivity.
        str: An empty string to clear the Textbox.
    '''

    mg = gr.Button("Machine-Generated", variant="secondary", interactive=False)
    hw = gr.Button("Human-Written", variant="secondary", interactive=False)
    mh = gr.Button("Machine-Humanized", variant="secondary", interactive=False)
    mp = gr.Button("Machine-Polished", variant="secondary", interactive=False)
    
    return mg, hw, mh, mp, ''

def generate_text_challenge_tab(): 
    ''' 
    Callback function to randomly sample an essay from the dataset and set the interactivity of the buttons to True.
    Args: 
        None
    Returns:
        str: A sample text from the dataset
        gr.Button: The 'Machine-Generated' button with interactivity.
        gr.Button: The 'Human-Written' button with interactivity.
        gr.Button: The 'Machine-Humanized' button with interactivity.
        gr.Button: The 'Machine-Polished' button with interactivity.
        str: An empty string to clear the Result.
    '''

    global index # to access the index of the sample text for the show_result function
    mg = gr.Button("Machine-Generated", variant="secondary", interactive=True)
    hw = gr.Button("Human-Written", variant="secondary", interactive=True)
    mh = gr.Button("Machine-Humanized", variant="secondary", interactive=True)
    mp = gr.Button("Machine-Polished", variant="secondary", interactive=True)
    index = random.choice(range(80))
    essay = demo_essays[index][0]
    return essay, mg, hw, mh, mp, '' 

def correct_label_challenge_tab(): 
    '''
    Function to return the correct label of the sample text based on the index (global variable).
    Args:
        None
    Returns:
        str: The correct label of the sample text
    '''

    if 0 <= index < 20 : 
        return 'Human-Written'
    elif 20 <= index < 40:
        return 'Machine-Generated'
    elif 40 <= index < 60:
        return 'Machine-Polished'
    elif 60 <= index < 80:
        return 'Machine-Humanized'
        
def show_result_challenge_tab(button): 
    '''
    Callback function to show the result of the classification based on the button clicked by the user.
    The correct label of the sample text is displayed in the primary variant. 
    The chosen label by the user is displayed in the stop variant if it is incorrect.

    Args:
        button: str: the label of the button clicked by the user
    Returns:
        str: the outcome of the classification
        gr.Button: The 'Machine-Generated' button with the appropriate variant.
        gr.Button: The 'Human-Written' button with the appropriate variant.
        gr.Button: The 'Machine-Humanized' button with the appropriate variant.
        gr.Button: The 'Machine-Polished' button with the appropriate variant.
    ''' 
 
    correct_btn = correct_label_challenge_tab() 
    mg = gr.Button("Machine-Generated", variant="secondary")
    hw = gr.Button("Human-Written", variant="secondary")
    mh = gr.Button("Machine-Humanized", variant="secondary")
    mp = gr.Button("Machine-Polished", variant="secondary")
    
    if button == 'Machine-Generated':
        mg = gr.Button("Machine-Generated", variant="stop")
    elif button == 'Human-Written':
        hw = gr.Button("Human-Written", variant="stop")
    elif button == 'Machine-Humanized':
        mh = gr.Button("Machine-Humanized", variant="stop")
    elif button == 'Machine-Polished':
        mp = gr.Button("Machine-Polished", variant="stop")

    if correct_btn == 'Machine-Generated':
        mg = gr.Button("Machine-Generated", variant="primary")
    elif correct_btn == 'Human-Written':
        hw = gr.Button("Human-Written", variant="primary")
    elif correct_btn == 'Machine-Humanized':
        mh = gr.Button("Machine-Humanized", variant="primary")  
    elif correct_btn == 'Machine-Polished':
        mp = gr.Button("Machine-Polished", variant="primary")

    outcome = ''
    if button == correct_btn:
        outcome = 'Correct'
    else:
        outcome = 'Incorrect' 

    return outcome, mg, hw, mh, mp


############################## GRADIO UI ##############################

with gr.Blocks() as demo:

    gr.Markdown("""<h1><centre>Machine Generated Text (MGT) Detection</center></h1>""")
    with gr.Tab('Try it!'): 
        
        with gr.Row():
            radio_button = gr.Dropdown(['Student Essay', 'Scientific Abstract'], label = 'Text Type', info = 'We have specialized models that work on domain-specific text.', value='Student Essay')
        
        with gr.Row():

            input_text = gr.Textbox(placeholder="Paste your text here...", label="Text", lines=10, max_lines=15)
            file_input = gr.File(label="Upload File", file_types=[".txt", ".pdf"])

        with gr.Row(): 
            wc = gr.Markdown("0/500 words (Minimum 50 words)")
        with gr.Row():
            check_button = gr.Button("Check Origin", variant="primary", interactive=False)
            clear_button = gr.ClearButton([input_text, file_input], variant="stop")
        
        out = gr.Label(label='Result')
        clear_button.add(out)
        
        check_button.click(fn=update_detection_tab, inputs=[input_text, file_input, radio_button], outputs=out)

        input_text.change(count_words_detection_tab, input_text, wc, show_progress=False)
        input_text.input(
            active_button_detection_tab,
            [input_text, file_input],
            [check_button],
        )

        file_input.upload(
            active_button_detection_tab, 
            [input_text, file_input],
            [check_button],
        ) 

        clear_button.click(
            clear_detection_tab,
            inputs=[],
            outputs=[input_text, file_input, check_button],
        )
    

        # Adding JavaScript to simulate file input click
        gr.Markdown(
            """
            <script>
            document.addEventListener("DOMContentLoaded", function() {
                const uploadButton = Array.from(document.getElementsByTagName('button')).find(el => el.innerText === "Upload File");
                if (uploadButton) {
                    uploadButton.onclick = function() {
                        document.querySelector('input[type="file"]').click();
                    };
                }
            });
            </script>
            """
        )

    with gr.Tab('Challenge Yourself!'): 
        gr.Markdown(
            """
            <style>
            .gr-button-secondary {
                width: 100px;
                height: 30px;
                padding: 5px;
            }
            .gr-row {
                display: flex;
                align-items: center;
                gap: 10px;
            }
            .gr-block {
                padding: 20px;
            }
            .gr-markdown p {
                font-size: 16px;
            }
            </style>
            <span style='font-family: Arial, sans-serif; font-size: 20px;'>Was this text written by <strong>human</strong> or <strong>AI</strong>?</span>
            <p style='font-family: Arial, sans-serif;'>Try detecting one of our sample texts:</p>
            """
        )

        
        with gr.Row(): 
            generate = gr.Button("Generate Sample Text", variant="primary")
            clear = gr.ClearButton([], variant="stop")

        with gr.Row(): 
            text = gr.Textbox(value="", label="Text", lines=20, interactive=False)
        
        with gr.Row():

            mg = gr.Button("Machine-Generated", variant="secondary", interactive=False)
            hw = gr.Button("Human-Written", variant="secondary", interactive=False)
            mh = gr.Button("Machine-Humanized", variant="secondary", interactive=False)    
            mp = gr.Button("Machine-Polished", variant="secondary", interactive=False)

        with gr.Row(): 
            result = gr.Label(label="Result", value="") 

        clear.add([result, text])
        generate.click(generate_text_challenge_tab, [], [text, mg, hw, mh, mp, result])
        for button in [mg, hw, mh, mp]: 
            button.click(show_result_challenge_tab, [button], [result, mg, hw, mh, mp])

        clear.click(clear_challenge_tab, [], [mg, hw, mh, mp, result])
        

demo.launch(share=False)