minko186's picture
Update app.py
fe12823
raw
history blame
20.9 kB
from utils import cosineSim, googleSearch, getSentences, parallel_scrap, matchingScore, matchingScoreWithTimeout
import gradio as gr
from urllib.request import urlopen, Request
from googleapiclient.discovery import build
import requests
import httpx
import torch
import re
from bs4 import BeautifulSoup
import numpy as np
from transformers import AutoTokenizer, AutoModelForSequenceClassification
import asyncio
from scipy.special import softmax
from evaluate import load
from datetime import date
import nltk
import fitz
from transformers import GPT2LMHeadModel, GPT2TokenizerFast
import nltk, spacy, subprocess, torch
import plotly.graph_objects as go
import torch.nn.functional as F
import nltk
from unidecode import unidecode
nltk.download('punkt')
from writing_analysis import (
normalize,
preprocess_text1,
preprocess_text2,
vocabulary_richness_ttr,
calculate_gunning_fog,
calculate_average_sentence_length,
calculate_average_word_length,
calculate_syntactic_tree_depth,
calculate_perplexity,
)
np.set_printoptions(suppress=True)
def plagiarism_check(
plag_option,
input,
year_from,
month_from,
day_from,
year_to,
month_to,
day_to,
domains_to_skip,
):
api_key = "AIzaSyCLyCCpOPLZWuptuPAPSg8cUIZhdEMVf6g"
api_key = "AIzaSyCS1WQDMl1IMjaXtwSd_2rA195-Yc4psQE"
api_key = "AIzaSyCB61O70B8AC3l5Kk3KMoLb6DN37B7nqIk"
# api_key = "AIzaSyCg1IbevcTAXAPYeYreps6wYWDbU0Kz8tg"
cse_id = "851813e81162b4ed4"
sentences = getSentences(input)
urlCount = {}
ScoreArray = []
urlList = []
date_from = build_date(year_from, month_from, day_from)
date_to = build_date(year_to, month_to, day_to)
sort_date = f"date:r:{date_from}:{date_to}"
# get list of URLS to check
urlCount, ScoreArray = googleSearch(
plag_option,
sentences,
urlCount,
ScoreArray,
urlList,
sort_date,
domains_to_skip,
api_key,
cse_id,
)
print("Number of URLs: ", len(urlCount))
print(urlList)
# Scrape URLs in list
formatted_tokens = []
soups = asyncio.run(parallel_scrap(urlList))
print(len(soups))
print(
"Successful scraping: "
+ str(len([x for x in soups if x is not None]))
+ "out of "
+ str(len(urlList))
)
# Populate matching scores for scrapped pages
for i, soup in enumerate(soups):
print(f"Analyzing {i+1} of {len(soups)} soups........................")
if soup:
page_content = soup.text
for j, sent in enumerate(sentences):
# score = matchingScore(sent, page_content)
score = matchingScoreWithTimeout(sent, page_content)
ScoreArray[i][j] = score
# ScoreArray = asyncio.run(parallel_analyze_2(soups, sentences, ScoreArray))
# print("New Score Array:\n")
# print2D(ScoreArray)
# Gradio formatting section
sentencePlag = [False] * len(sentences)
sentenceToMaxURL = [-1] * len(sentences)
for j in range(len(sentences)):
if j > 0:
maxScore = ScoreArray[sentenceToMaxURL[j - 1]][j]
sentenceToMaxURL[j] = sentenceToMaxURL[j - 1]
else:
maxScore = -1
for i in range(len(ScoreArray)):
margin = (
0.1
if (j > 0 and sentenceToMaxURL[j] == sentenceToMaxURL[j - 1])
else 0
)
if ScoreArray[i][j] - maxScore > margin:
maxScore = ScoreArray[i][j]
sentenceToMaxURL[j] = i
if maxScore > 0.5:
sentencePlag[j] = True
if (
(len(sentences) > 1)
and (sentenceToMaxURL[1] != sentenceToMaxURL[0])
and (
ScoreArray[sentenceToMaxURL[0]][0]
- ScoreArray[sentenceToMaxURL[1]][0]
< 0.1
)
):
sentenceToMaxURL[0] = sentenceToMaxURL[1]
index = np.unique(sentenceToMaxURL)
urlScore = {}
for url in index:
s = [
ScoreArray[url][sen]
for sen in range(len(sentences))
if sentenceToMaxURL[sen] == url
]
urlScore[url] = sum(s) / len(s)
index_descending = sorted(urlScore, key=urlScore.get, reverse=True)
urlMap = {}
for count, i in enumerate(index_descending):
urlMap[i] = count + 1
for i, sent in enumerate(sentences):
formatted_tokens.append(
(sent, "[" + str(urlMap[sentenceToMaxURL[i]]) + "]")
)
formatted_tokens.append(("\n", None))
formatted_tokens.append(("\n", None))
formatted_tokens.append(("\n", None))
print(formatted_tokens)
print(index_descending)
for ind in index_descending:
formatted_tokens.append(
(
urlList[ind] + " --- Matching Score: " + f"{str(round(urlScore[ind] * 100, 2))}%",
"[" + str(urlMap[ind]) + "]",
)
)
formatted_tokens.append(("\n", None))
print(f"Formatted Tokens: {formatted_tokens}")
return formatted_tokens
"""
AI DETECTION SECTION
"""
device = "cuda" if torch.cuda.is_available() else "cpu"
text_bc_model_path = "polygraf-ai/text-detect-bc-v11-4m"
text_bc_tokenizer = AutoTokenizer.from_pretrained(text_bc_model_path)
text_bc_model = AutoModelForSequenceClassification.from_pretrained(text_bc_model_path).to(device)
text_mc_model_path = "polygraf-ai/ai-text-detection-mc-robert-open-ai-detector-v4"
text_mc_tokenizer = AutoTokenizer.from_pretrained(text_mc_model_path)
text_mc_model = AutoModelForSequenceClassification.from_pretrained(text_mc_model_path).to(device)
quillbot_labels = ["Original", "QuillBot"]
quillbot_tokenizer = AutoTokenizer.from_pretrained("FacebookAI/roberta-base")
quillbot_model = AutoModelForSequenceClassification.from_pretrained("polygraf-ai/quillbot-detector-28k").to(device)
def remove_accents(input_str):
text_no_accents = unidecode(input_str)
return text_no_accents
def remove_special_characters(text):
text = remove_accents(text)
pattern = r'[^\w\s\d.,!?\'"()-;]+'
text = re.sub(pattern, '', text)
return text
def remove_special_characters_2(text):
pattern = r'[^a-zA-Z0-9 ]+'
text = re.sub(pattern, '', text)
return text
def update_character_count(text):
return f"{len(text)} characters"
def split_text_allow_complete_sentences_nltk(text, max_length=256, tolerance=30, min_last_segment_length=100, type_det='bc'):
sentences = nltk.sent_tokenize(text)
segments = []
current_segment = []
current_length = 0
if type_det == 'bc':
tokenizer = text_bc_tokenizer
max_length = 333
elif type_det == 'mc':
tokenizer = text_mc_tokenizer
max_length = 256
for sentence in sentences:
tokens = tokenizer.tokenize(sentence)
sentence_length = len(tokens)
if current_length + sentence_length <= max_length + tolerance - 2:
current_segment.append(sentence)
current_length += sentence_length
else:
if current_segment:
encoded_segment = tokenizer.encode(' '.join(current_segment), add_special_tokens=True, max_length=max_length+tolerance, truncation=True)
segments.append((current_segment, len(encoded_segment)))
current_segment = [sentence]
current_length = sentence_length
if current_segment:
encoded_segment = tokenizer.encode(' '.join(current_segment), add_special_tokens=True, max_length=max_length+tolerance, truncation=True)
segments.append((current_segment, len(encoded_segment)))
final_segments = []
for i, (seg, length) in enumerate(segments):
if i == len(segments) - 1:
if length < min_last_segment_length and len(final_segments) > 0:
prev_seg, prev_length = final_segments[-1]
combined_encoded = tokenizer.encode(' '.join(prev_seg + seg), add_special_tokens=True, max_length=max_length+tolerance, truncation=True)
if len(combined_encoded) <= max_length + tolerance:
final_segments[-1] = (prev_seg + seg, len(combined_encoded))
else:
final_segments.append((seg, length))
else:
final_segments.append((seg, length))
else:
final_segments.append((seg, length))
decoded_segments = []
encoded_segments = []
for seg, _ in final_segments:
encoded_segment = tokenizer.encode(' '.join(seg), add_special_tokens=True, max_length=max_length+tolerance, truncation=True)
decoded_segment = tokenizer.decode(encoded_segment)
decoded_segments.append(decoded_segment)
return decoded_segments
def predict_quillbot(text):
with torch.no_grad():
quillbot_model.eval()
tokenized_text = quillbot_tokenizer(text, padding="max_length", truncation=True, max_length=256, return_tensors="pt").to(device)
output = quillbot_model(**tokenized_text)
output_norm = softmax(output.logits.detach().cpu().numpy(), 1)[0]
q_score = {"QuillBot": output_norm[1].item(), "Original": output_norm[0].item()}
return q_score
def predict_bc(model, tokenizer, text):
with torch.no_grad():
model.eval()
tokens = text_bc_tokenizer(
text, padding='max_length', truncation=True, max_length=333, return_tensors="pt"
).to(device)
output = model(**tokens)
output_norm = softmax(output.logits.detach().cpu().numpy(), 1)[0]
print("BC Score: ", output_norm)
return output_norm
def predict_mc(model, tokenizer, text):
with torch.no_grad():
model.eval()
tokens = text_mc_tokenizer(
text, padding='max_length', truncation=True, return_tensors="pt", max_length=256
).to(device)
output = model(**tokens)
output_norm = softmax(output.logits.detach().cpu().numpy(), 1)[0]
print("MC Score: ", output_norm)
return output_norm
def ai_generated_test(ai_option, input):
bc_scores = []
mc_scores = []
samples_len_bc = len(split_text_allow_complete_sentences_nltk(input, type_det = 'bc'))
samples_len_mc = len(split_text_allow_complete_sentences_nltk(input, type_det = 'mc'))
segments_bc = split_text_allow_complete_sentences_nltk(input, type_det = 'bc')
segments_mc = split_text_allow_complete_sentences_nltk(input, type_det = 'bc')
for i in range(samples_len_bc):
cleaned_text_bc = remove_special_characters(segments_bc[i])
bc_score = predict_bc(text_bc_model, text_bc_tokenizer,cleaned_text_bc )
bc_scores.append(bc_score)
for i in range(samples_len_mc):
cleaned_text_mc = remove_special_characters(segments_mc[i])
mc_score = predict_mc(text_mc_model, text_mc_tokenizer, cleaned_text_mc)
mc_scores.append(mc_score)
bc_scores_array = np.array(bc_scores)
mc_scores_array = np.array(mc_scores)
average_bc_scores = np.mean(bc_scores_array, axis=0)
average_mc_scores = np.mean(mc_scores_array, axis=0)
bc_score_list = average_bc_scores.tolist()
mc_score_list = average_mc_scores.tolist()
bc_score = {"AI": bc_score[1].item(), "HUMAN": bc_score[0].item()}
mc_score = {}
label_map = ["OpenAI GPT", "Mistral", "CLAUDE", "Gemini", "LLAMA 2"]
for score, label in zip(mc_score_list, label_map):
mc_score[label.upper()] = score
sum_prob = 1 - bc_score["HUMAN"]
for key, value in mc_score.items():
mc_score[key] = value * sum_prob
if ai_option == "Human vs AI":
mc_score = {}
if sum_prob < 0.01 :
mc_score = {}
return bc_score, mc_score
else:
return bc_score, mc_score
# COMBINED
def main(
ai_option,
plag_option,
input,
# models,
year_from,
month_from,
day_from,
year_to,
month_to,
day_to,
domains_to_skip,
):
formatted_tokens = plagiarism_check(
plag_option,
input,
year_from,
month_from,
day_from,
year_to,
month_to,
day_to,
domains_to_skip,
)
depth_analysis_plot = depth_analysis(input)
bc_score, mc_score = ai_generated_test(ai_option,input)
quilscore = predict_quillbot(input)
return (
bc_score,
mc_score,
formatted_tokens,
depth_analysis_plot,
quilscore
)
def build_date(year, month, day):
return f"{year}{months[month]}{day}"
def len_validator(text):
min_tokens = 200
lengt = len(text_bc_tokenizer.tokenize(text = text, return_tensors="pt"))
if lengt < min_tokens:
return f"Warning! Input length is {lengt}. Please input a text that is greater than {min_tokens} tokens long. Recommended length {min_tokens*2} tokens."
else :
return f"Input length ({lengt}) is satisified."
def extract_text_from_pdf(pdf_path):
doc = fitz.open(pdf_path)
text = ""
for page in doc:
text += page.get_text()
return text
# DEPTH ANALYSIS
print("loading depth analysis")
nltk.download('stopwords')
nltk.download('punkt')
command = ['python3', '-m', 'spacy', 'download', 'en_core_web_sm']
# Execute the command
subprocess.run(command)
nlp = spacy.load("en_core_web_sm")
# for perplexity
model_id = "gpt2"
gpt2_model = GPT2LMHeadModel.from_pretrained(model_id).to(device)
gpt2_tokenizer = GPT2TokenizerFast.from_pretrained(model_id)
def depth_analysis(input_text):
# vocanulary richness
processed_words = preprocess_text1(input_text)
ttr_value = vocabulary_richness_ttr(processed_words)
# readability
gunning_fog = calculate_gunning_fog(input_text)
gunning_fog_norm = normalize(gunning_fog, min_value=0, max_value=20)
# average sentence length and average word length
words, sentences = preprocess_text2(input_text)
average_sentence_length = calculate_average_sentence_length(sentences)
average_word_length = calculate_average_word_length(words)
average_sentence_length_norm = normalize(average_sentence_length, min_value=0, max_value=40)
average_word_length_norm = normalize(average_word_length, min_value=0, max_value=8)
# syntactic_tree_depth
average_tree_depth = calculate_syntactic_tree_depth(nlp, input_text)
average_tree_depth_norm = normalize(average_tree_depth, min_value=0, max_value=10)
# perplexity
perplexity = calculate_perplexity(input_text, gpt2_model, gpt2_tokenizer, device)
perplexity_norm = normalize(perplexity, min_value=0, max_value=30)
features = {
"readability": gunning_fog_norm,
"syntactic tree depth": average_tree_depth_norm,
"vocabulary richness": ttr_value,
"perplexity": perplexity_norm,
"average sentence length": average_sentence_length_norm,
"average word length": average_word_length_norm,
}
print(features)
fig = go.Figure()
fig.add_trace(go.Scatterpolar(
r=list(features.values()),
theta=list(features.keys()),
fill='toself',
name='Radar Plot'
))
fig.update_layout(
polar=dict(
radialaxis=dict(
visible=True,
range=[0, 100],
)),
showlegend=False,
# autosize=False,
# width=600,
# height=600,
margin=dict(
l=10,
r=20,
b=10,
t=10,
# pad=100
),
)
return fig
# START OF GRADIO
title = "Copyright Checker"
months = {
"January": "01",
"February": "02",
"March": "03",
"April": "04",
"May": "05",
"June": "06",
"July": "07",
"August": "08",
"September": "09",
"October": "10",
"November": "11",
"December": "12",
}
with gr.Blocks() as demo:
today = date.today()
# dd/mm/YY
d1 = today.strftime("%d/%B/%Y")
d1 = d1.split("/")
model_list = ["OpenAI GPT", "Mistral", "CLAUDE", "Gemini", "LLAMA2"]
domain_list = ["com", "org", "net", "int", "edu", "gov", "mil"]
gr.Markdown(
"""
# Copyright Checker
"""
)
with gr.Row():
input_text = gr.Textbox(label="Input text", lines=6, placeholder="")
file_input = gr.File(label="Upload PDF")
file_input.change(fn=extract_text_from_pdf, inputs=file_input, outputs=input_text)
char_count = gr.Textbox(label="Minumum Character Limit Check")
input_text.change(fn=len_validator, inputs=input_text, outputs=char_count)
with gr.Row():
with gr.Column():
ai_option = gr.Radio(["Human vs AI", "Human vs AI Source Models"], label="Choose an option please.")
with gr.Column():
plag_option = gr.Radio(["Standard", "Advanced"], label="Choose an option please.")
with gr.Row():
with gr.Column():
only_ai_btn = gr.Button("AI Check")
with gr.Column():
only_plagiarism_btn = gr.Button("Source Check")
with gr.Row():
quillbot_check = gr.Button("Humanized Text Check (Quillbot)")
with gr.Row():
depth_analysis_btn = gr.Button("Detailed Writing Analysis")
with gr.Row():
full_check_btn = gr.Button("Full Check")
gr.Markdown(
"""
## Output
"""
)
# models = gr.Dropdown(
# model_list,
# value=model_list,
# multiselect=True,
# label="Models to test against",
# )
with gr.Row():
with gr.Column():
bcLabel = gr.Label(label="Source")
with gr.Column():
mcLabel = gr.Label(label="Creator")
with gr.Row():
QLabel = gr.Label(label="Humanized")
with gr.Group():
with gr.Row():
month_from = gr.Dropdown(
choices=months,
label="From Month",
value="January",
interactive=True,
)
day_from = gr.Textbox(label="From Day", value="01")
year_from = gr.Textbox(label="From Year", value="2000")
# from_date_button = gr.Button("Submit")
with gr.Row():
month_to = gr.Dropdown(
choices=months,
label="To Month",
value=d1[1],
interactive=True,
)
day_to = gr.Textbox(label="To Day", value=d1[0])
year_to = gr.Textbox(label="To Year", value=d1[2])
# to_date_button = gr.Button("Submit")
with gr.Row():
domains_to_skip = gr.Dropdown(
domain_list,
multiselect=True,
label="Domain To Skip",
)
with gr.Row():
with gr.Column():
sentenceBreakdown = gr.HighlightedText(
label="Source Detection Sentence Breakdown",
combine_adjacent=True,
color_map={
"[1]": "red",
"[2]": "orange",
"[3]": "yellow",
"[4]": "green",
},
)
with gr.Row():
with gr.Column():
writing_analysis_plot = gr.Plot(
label="Writing Analysis Plot"
)
full_check_btn.click(
fn=main,
inputs=[
ai_option,
plag_option,
input_text,
# models,
year_from,
month_from,
day_from,
year_to,
month_to,
day_to,
domains_to_skip,
],
outputs=[
bcLabel,
mcLabel,
sentenceBreakdown,
writing_analysis_plot,
QLabel
],
api_name="main",
)
only_ai_btn.click(
fn=ai_generated_test,
inputs=[ai_option, input_text],
outputs=[
bcLabel,
mcLabel,
],
api_name="ai_check",
)
quillbot_check.click(
fn=predict_quillbot,
inputs=[input_text],
outputs=[QLabel],
api_name="quillbot_check",
)
only_plagiarism_btn.click(
fn=plagiarism_check,
inputs=[
plag_option,
input_text,
year_from,
month_from,
day_from,
year_to,
month_to,
day_to,
domains_to_skip,
],
outputs=[
sentenceBreakdown,
],
api_name="plagiarism_check",
)
depth_analysis_btn.click(
fn=depth_analysis,
inputs=[input_text],
outputs=[writing_analysis_plot],
api_name="depth_analysis",
)
date_from = ""
date_to = ""
demo.launch(share=True, server_name="0.0.0.0", auth=("polygraf-admin", "test@aisd"))