Spaces:
Running
Running
FactChecker files(final_deploy)
Browse files- .gitattributes +5 -0
- Dockerfile +28 -0
- README.md +12 -12
- app.py +170 -0
- build/asset-manifest.json +13 -0
- build/index.html +1 -0
- build/logo.ico +0 -0
- build/logo.png +0 -0
- build/manifest.json +25 -0
- build/robots.txt +3 -0
- build/static/css/main.b34208b9.css +2 -0
- build/static/css/main.b34208b9.css.map +1 -0
- build/static/js/main.c63a58f2.js +0 -0
- build/static/js/main.c63a58f2.js.LICENSE.txt +49 -0
- build/static/js/main.c63a58f2.js.map +0 -0
- model_training/model_training.ipynb +0 -0
- model_training/visualizations/class_distribution.png +0 -0
- model_training/visualizations/distilbert_confusion_matrix.png +0 -0
- model_training/visualizations/fake_news_wordcloud.png +3 -0
- model_training/visualizations/feature_importance.png +3 -0
- model_training/visualizations/lr_confusion_matrix.png +0 -0
- model_training/visualizations/model_comparison.png +0 -0
- model_training/visualizations/real_news_wordcloud.png +3 -0
- model_training/visualizations/rf_confusion_matrix.png +0 -0
- model_training/visualizations/roc_curves.png +3 -0
- model_training/visualizations/text_length_distribution.png +3 -0
- models/distilbert_model.pt +3 -0
- models/lr_model.pkl +3 -0
- models/rf_model.pkl +3 -0
- models/tfidf_vectorizer.pkl +3 -0
- requirements.txt +10 -0
.gitattributes
CHANGED
@@ -33,3 +33,8 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
+
model_training/visualizations/fake_news_wordcloud.png filter=lfs diff=lfs merge=lfs -text
|
37 |
+
model_training/visualizations/feature_importance.png filter=lfs diff=lfs merge=lfs -text
|
38 |
+
model_training/visualizations/real_news_wordcloud.png filter=lfs diff=lfs merge=lfs -text
|
39 |
+
model_training/visualizations/roc_curves.png filter=lfs diff=lfs merge=lfs -text
|
40 |
+
model_training/visualizations/text_length_distribution.png filter=lfs diff=lfs merge=lfs -text
|
Dockerfile
ADDED
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Hugging Face Docker config.
|
2 |
+
FROM python:3.9
|
3 |
+
|
4 |
+
WORKDIR /app
|
5 |
+
|
6 |
+
# Install dependencies
|
7 |
+
COPY requirements.txt .
|
8 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
9 |
+
|
10 |
+
# Download NLTK resources(location:all users)
|
11 |
+
RUN python -c "import nltk; nltk.download('punkt', download_dir='/usr/local/share/nltk_data'); nltk.download('stopwords', download_dir='/usr/local/share/nltk_data'); nltk.download('wordnet', download_dir='/usr/local/share/nltk_data')"
|
12 |
+
|
13 |
+
# Create a user
|
14 |
+
RUN useradd -m -u 1000 user
|
15 |
+
USER user
|
16 |
+
ENV PATH="/home/user/.local/bin:$PATH"
|
17 |
+
RUN python -c "import nltk; nltk.download('punkt', download_dir='/usr/local/share/nltk_data'); nltk.download('stopwords', download_dir='/usr/local/share/nltk_data'); nltk.download('wordnet', download_dir='/usr/local/share/nltk_data')"
|
18 |
+
RUN python -c "import nltk; nltk.download('punkt_tab', download_dir='/usr/local/share/nltk_data')"
|
19 |
+
ENV NLTK_DATA="/usr/local/share/nltk_data"
|
20 |
+
##
|
21 |
+
|
22 |
+
# Copy application files
|
23 |
+
COPY --chown=user ./app.py .
|
24 |
+
COPY --chown=user ./models ./models
|
25 |
+
COPY --chown=user ./build ./build
|
26 |
+
|
27 |
+
# Command to run Flask app with gunicorn
|
28 |
+
CMD ["gunicorn", "--bind", "0.0.0.0:7860", "app:app"]
|
README.md
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
-
---
|
2 |
-
title: FactChecker
|
3 |
-
emoji:
|
4 |
-
colorFrom:
|
5 |
-
colorTo:
|
6 |
-
sdk: docker
|
7 |
-
pinned: false
|
8 |
-
license: mit
|
9 |
-
short_description: 'FactChecker: Fake News Detector'
|
10 |
-
---
|
11 |
-
|
12 |
-
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
1 |
+
---
|
2 |
+
title: FactChecker
|
3 |
+
emoji: 📚
|
4 |
+
colorFrom: pink
|
5 |
+
colorTo: red
|
6 |
+
sdk: docker
|
7 |
+
pinned: false
|
8 |
+
license: mit
|
9 |
+
short_description: 'FactChecker: Fake News Detector'
|
10 |
+
---
|
11 |
+
|
12 |
+
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
app.py
ADDED
@@ -0,0 +1,170 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from flask import Flask, request, jsonify, send_from_directory
|
2 |
+
import pickle
|
3 |
+
import torch
|
4 |
+
import re
|
5 |
+
import nltk
|
6 |
+
from nltk.corpus import stopwords
|
7 |
+
from nltk.stem import WordNetLemmatizer
|
8 |
+
from transformers import DistilBertTokenizer, DistilBertModel
|
9 |
+
import torch.nn as nn
|
10 |
+
import os
|
11 |
+
import numpy
|
12 |
+
|
13 |
+
# Download NLTK stuff
|
14 |
+
nltk.data.path.append('/usr/local/share/nltk_data')
|
15 |
+
nltk.download('punkt_tab')
|
16 |
+
nltk.download('stopwords')
|
17 |
+
nltk.download('wordnet')
|
18 |
+
nltk.download('punkt')
|
19 |
+
|
20 |
+
app = Flask(__name__, static_folder='build', static_url_path='')
|
21 |
+
|
22 |
+
# Define DistilBERT model class
|
23 |
+
class DistilBERTClassifier(nn.Module):
|
24 |
+
def __init__(self, dropout_rate=0.2):
|
25 |
+
super(DistilBERTClassifier, self).__init__()
|
26 |
+
self.distilbert = DistilBertModel.from_pretrained('distilbert-base-uncased')
|
27 |
+
self.dropout = nn.Dropout(dropout_rate)
|
28 |
+
self.classifier = nn.Linear(768, 2)
|
29 |
+
|
30 |
+
def forward(self, input_ids, attention_mask):
|
31 |
+
outputs = self.distilbert(input_ids=input_ids, attention_mask=attention_mask)
|
32 |
+
pooled_output = outputs.last_hidden_state[:, 0]
|
33 |
+
pooled_output = self.dropout(pooled_output)
|
34 |
+
logits = self.classifier(pooled_output)
|
35 |
+
return logits
|
36 |
+
|
37 |
+
# Clean text function
|
38 |
+
def clean_text(text):
|
39 |
+
text = text.lower()
|
40 |
+
text = re.sub(r'http\S+|www\S+|https\S+', '', text)
|
41 |
+
text = re.sub(r'<.*?>', '', text)
|
42 |
+
text = re.sub(r'[^\w\s]', '', text)
|
43 |
+
text = re.sub(r'\d+', '', text)
|
44 |
+
|
45 |
+
tokens = nltk.word_tokenize(text)
|
46 |
+
|
47 |
+
stop_words = set(stopwords.words('english'))
|
48 |
+
lemmatizer = WordNetLemmatizer()
|
49 |
+
tokens = [lemmatizer.lemmatize(word) for word in tokens if word not in stop_words]
|
50 |
+
|
51 |
+
cleaned_text = ' '.join(tokens)
|
52 |
+
return cleaned_text
|
53 |
+
|
54 |
+
# Load models
|
55 |
+
def load_models():
|
56 |
+
# Set device
|
57 |
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
58 |
+
|
59 |
+
# TF-IDF vectorizer
|
60 |
+
with open('models/tfidf_vectorizer.pkl', 'rb') as f:
|
61 |
+
tfidf_vectorizer = pickle.load(f)
|
62 |
+
|
63 |
+
# Logistic Regression
|
64 |
+
with open('models/lr_model.pkl', 'rb') as f:
|
65 |
+
lr_model = pickle.load(f)
|
66 |
+
|
67 |
+
# random Forest
|
68 |
+
with open('models/rf_model.pkl', 'rb') as f:
|
69 |
+
rf_model = pickle.load(f)
|
70 |
+
|
71 |
+
# load DistilBERT
|
72 |
+
tokenizer = DistilBertTokenizer.from_pretrained('distilbert-base-uncased')
|
73 |
+
distilbert_model = DistilBERTClassifier()
|
74 |
+
distilbert_model.load_state_dict(torch.load('models/distilbert_model.pt', map_location=device))
|
75 |
+
distilbert_model.to(device)
|
76 |
+
distilbert_model.eval()
|
77 |
+
|
78 |
+
return tfidf_vectorizer, lr_model, rf_model, distilbert_model, tokenizer, device
|
79 |
+
|
80 |
+
# Load models at startup
|
81 |
+
tfidf_vectorizer, lr_model, rf_model, distilbert_model, tokenizer, device = load_models()
|
82 |
+
|
83 |
+
@app.route('/')
|
84 |
+
def serve():
|
85 |
+
return send_from_directory(app.static_folder, 'index.html')
|
86 |
+
|
87 |
+
@app.route('/api/analyze', methods=['POST'])
|
88 |
+
def analyze():
|
89 |
+
data = request.get_json()
|
90 |
+
|
91 |
+
if not data or 'text' not in data or 'model' not in data:
|
92 |
+
return jsonify({'error': 'Missing required fields'}), 400
|
93 |
+
news_text = data['text']
|
94 |
+
model_option = data['model']
|
95 |
+
|
96 |
+
if not news_text:
|
97 |
+
return jsonify({'error': 'Text cannot be empty'}), 400
|
98 |
+
|
99 |
+
# Clean text
|
100 |
+
cleaned_text = clean_text(news_text)
|
101 |
+
|
102 |
+
results = {}
|
103 |
+
|
104 |
+
# Using Logistic Regression
|
105 |
+
if model_option in ["lr", "all"]:
|
106 |
+
text_tfidf = tfidf_vectorizer.transform([cleaned_text])
|
107 |
+
lr_pred = lr_model.predict(text_tfidf)[0]
|
108 |
+
lr_prob = lr_model.predict_proba(text_tfidf)[0]
|
109 |
+
results["Logistic Regression"] = {
|
110 |
+
"prediction": "Real" if lr_pred == 1 else "Fake",
|
111 |
+
"fake_prob": float(lr_prob[0]),
|
112 |
+
"real_prob": float(lr_prob[1])
|
113 |
+
}
|
114 |
+
|
115 |
+
# Using Random Forest
|
116 |
+
if model_option in ["rf", "all"]:
|
117 |
+
text_tfidf = tfidf_vectorizer.transform([cleaned_text])
|
118 |
+
rf_pred = rf_model.predict(text_tfidf)[0]
|
119 |
+
rf_prob = rf_model.predict_proba(text_tfidf)[0]
|
120 |
+
results["Random Forest"] = {
|
121 |
+
"prediction": "Real" if rf_pred == 1 else "Fake",
|
122 |
+
"fake_prob": float(rf_prob[0]),
|
123 |
+
"real_prob": float(rf_prob[1])
|
124 |
+
}
|
125 |
+
|
126 |
+
# Using DistilBERT
|
127 |
+
if model_option in ["distilbert", "all"]:
|
128 |
+
encoding = tokenizer(
|
129 |
+
cleaned_text,
|
130 |
+
truncation=True,
|
131 |
+
padding='max_length',
|
132 |
+
max_length=128,
|
133 |
+
return_tensors='pt'
|
134 |
+
)
|
135 |
+
|
136 |
+
with torch.no_grad():
|
137 |
+
input_ids = encoding['input_ids'].to(device)
|
138 |
+
attention_mask = encoding['attention_mask'].to(device)
|
139 |
+
outputs = distilbert_model(input_ids=input_ids, attention_mask=attention_mask)
|
140 |
+
print("Raw model output:", outputs.cpu().numpy())
|
141 |
+
probs = torch.softmax(outputs, dim=1).cpu().numpy()[0]
|
142 |
+
print("After softmax:", probs)
|
143 |
+
|
144 |
+
print(f"Text: {cleaned_text[:50]}...")
|
145 |
+
print(f"Probabilities: Real={probs[0]:.4f}, Fake={probs[1]:.4f}")
|
146 |
+
|
147 |
+
distilbert_pred = 1 if probs[1] > probs[0] else 0
|
148 |
+
results["DistilBERT"] = {
|
149 |
+
"prediction": "Real" if distilbert_pred == 1 else "Fake",
|
150 |
+
"fake_prob": float(probs[0]),
|
151 |
+
"real_prob": float(probs[1])
|
152 |
+
}
|
153 |
+
|
154 |
+
# Calculate overall results for "all models" option
|
155 |
+
if model_option == "all":
|
156 |
+
real_votes = sum(1 for model, result in results.items() if result["prediction"] == "Real")
|
157 |
+
fake_votes = len(results) - real_votes
|
158 |
+
overall_verdict = "Real" if real_votes >= fake_votes else "Fake"
|
159 |
+
results["Overall"] = {
|
160 |
+
"prediction": overall_verdict,
|
161 |
+
"real_votes": real_votes,
|
162 |
+
"fake_votes": fake_votes,
|
163 |
+
"total_models": len(results)
|
164 |
+
}
|
165 |
+
|
166 |
+
return jsonify({'results': results})
|
167 |
+
|
168 |
+
if __name__ == '__main__':
|
169 |
+
port = int(os.environ.get('PORT', 7860))
|
170 |
+
app.run(host='0.0.0.0', port=port)
|
build/asset-manifest.json
ADDED
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"files": {
|
3 |
+
"main.css": "/static/css/main.b34208b9.css",
|
4 |
+
"main.js": "/static/js/main.c63a58f2.js",
|
5 |
+
"index.html": "/index.html",
|
6 |
+
"main.b34208b9.css.map": "/static/css/main.b34208b9.css.map",
|
7 |
+
"main.c63a58f2.js.map": "/static/js/main.c63a58f2.js.map"
|
8 |
+
},
|
9 |
+
"entrypoints": [
|
10 |
+
"static/css/main.b34208b9.css",
|
11 |
+
"static/js/main.c63a58f2.js"
|
12 |
+
]
|
13 |
+
}
|
build/index.html
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="/logo.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="Web site created using create-react-app"/><link rel="apple-touch-icon" href="/logo192.png"/><link rel="manifest" href="/manifest.json"/><title>FactChecker</title><script defer="defer" src="/static/js/main.c63a58f2.js"></script><link href="/static/css/main.b34208b9.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
build/logo.ico
ADDED
|
build/logo.png
ADDED
![]() |
build/manifest.json
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"short_name": "React App",
|
3 |
+
"name": "Create React App Sample",
|
4 |
+
"icons": [
|
5 |
+
{
|
6 |
+
"src": "favicon.ico",
|
7 |
+
"sizes": "64x64 32x32 24x24 16x16",
|
8 |
+
"type": "image/x-icon"
|
9 |
+
},
|
10 |
+
{
|
11 |
+
"src": "logo192.png",
|
12 |
+
"type": "image/png",
|
13 |
+
"sizes": "192x192"
|
14 |
+
},
|
15 |
+
{
|
16 |
+
"src": "logo512.png",
|
17 |
+
"type": "image/png",
|
18 |
+
"sizes": "512x512"
|
19 |
+
}
|
20 |
+
],
|
21 |
+
"start_url": ".",
|
22 |
+
"display": "standalone",
|
23 |
+
"theme_color": "#000000",
|
24 |
+
"background_color": "#ffffff"
|
25 |
+
}
|
build/robots.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
# https://www.robotstxt.org/robotstxt.html
|
2 |
+
User-agent: *
|
3 |
+
Disallow:
|
build/static/css/main.b34208b9.css
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
1 |
+
body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;margin:0}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}*{box-sizing:border-box;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;margin:0;padding:0}body{background-color:#f5f7fa;color:#333}.app{align-items:center;display:flex;justify-content:center;min-height:100vh;padding:20px}.container{max-width:600px;width:100%}.card{background-color:#fff;border-radius:12px;box-shadow:0 4px 20px #0000000d;overflow:hidden}.card-header{align-items:center;border-bottom:1px solid #f0f0f0;display:flex;justify-content:center;padding:24px 20px}h1{color:#333;font-size:18px;font-weight:600}form{padding:20px}textarea{border:1px solid #e8e8e8;border-radius:8px;color:#555;font-size:16px;height:150px;margin-bottom:16px;outline:none;padding:16px;resize:none;transition:border-color .2s;width:100%}textarea:focus{border-color:#4f8df8}textarea::placeholder{color:#aaa}.model-select{margin-bottom:16px}select{-webkit-appearance:none;appearance:none;background-color:#fff;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg width='12' height='8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='m1 1 5 5 5-5' stroke='%23888' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E");background-position:right 16px center;background-repeat:no-repeat;border:1px solid #e8e8e8;border-radius:8px;color:#555;cursor:pointer;font-size:16px;outline:none;padding:12px 16px;width:100%}select:focus{border-color:#4f8df8}button{background-color:#4f8df8;border:none;border-radius:8px;color:#fff;cursor:pointer;font-size:16px;font-weight:600;padding:16px;transition:background-color .2s;width:100%}button:hover{background-color:#3a7fe5}button:disabled{background-color:#a5c4ff;cursor:not-allowed}.error{background-color:#ffefef;border-radius:8px;color:#e74c3c;font-size:14px;margin-top:16px;padding:12px}.results{padding:20px}.results h2{color:#333;font-size:18px;font-weight:600;margin-bottom:20px}.result-item{background-color:#f9f9f9;border-radius:8px;margin-bottom:16px;padding:16px}.result-item.real{border-left:4px solid #2ecc71}.result-item.fake{border-left:4px solid #e74c3c}.result-header{align-items:center;display:flex;justify-content:space-between;margin-bottom:12px}.result-header h3{color:#333;font-size:16px;font-weight:600}.prediction-badge{border-radius:20px;font-size:14px;font-weight:600;padding:6px 12px}.real .prediction-badge{background-color:#e8f7f0;color:#2ecc71}.fake .prediction-badge{background-color:#fdeae8;color:#e74c3c}.confidence{margin-top:12px}.confidence-bar{background-color:#f0f0f0;border-radius:6px;height:12px;margin-bottom:8px;overflow:hidden}.confidence-fill{background-color:#4f8df8;border-radius:6px;height:100%;position:relative}.confidence-fill span{color:#fff;font-size:12px;font-weight:600;position:absolute;right:8px;top:-3px}.confidence p{color:#888;font-size:12px;text-align:right}.back-button{background-color:#e9ecef;color:#495057;margin-top:16px}.back-button:hover{background-color:#dee2e6}.logo-image{height:50px;object-fit:contain;width:50px}.icon{align-items:center;display:flex}
|
2 |
+
/*# sourceMappingURL=main.b34208b9.css.map*/
|
build/static/css/main.b34208b9.css.map
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"version":3,"file":"static/css/main.b34208b9.css","mappings":"AAAA,KAKI,kCAAmC,CACnC,iCAAkC,CAJlC,mIAEY,CAHZ,QAMF,CAEA,KACE,uEAEF,CCZF,EAGE,qBAAsB,CACtB,wHAC8D,CAJ9D,QAAS,CACT,SAIF,CAEA,KACE,wBAAyB,CACzB,UACF,CAEA,KAGE,kBAAmB,CAFnB,YAAa,CACb,sBAAuB,CAEvB,gBAAiB,CACjB,YACF,CAEA,WAEE,eAAgB,CADhB,UAEF,CAEA,MACE,qBAAuB,CACvB,kBAAmB,CACnB,+BAA0C,CAC1C,eACF,CAEA,aAEE,kBAAmB,CAGnB,+BAAgC,CAJhC,YAAa,CAEb,sBAAuB,CACvB,iBAEF,CAMA,GAGE,UAAW,CAFX,cAAe,CACf,eAEF,CAEA,KACE,YACF,CAEA,SAIE,wBAAyB,CACzB,iBAAkB,CAGlB,UAAW,CADX,cAAe,CALf,YAAa,CAOb,kBAAmB,CACnB,YAAa,CAPb,YAAa,CAGb,WAAY,CAKZ,2BAA6B,CAV7B,UAWF,CAEA,eACE,oBACF,CAEA,sBACE,UACF,CAEA,cACE,kBACF,CAEA,OASE,uBAAgB,CAAhB,eAAgB,CAFhB,qBAAuB,CAGvB,qOAA+O,CAE/O,qCAAsC,CADtC,2BAA4B,CAR5B,wBAAyB,CACzB,iBAAkB,CAElB,UAAW,CAOX,cAAe,CARf,cAAe,CAGf,YAAa,CANb,iBAAkB,CADlB,UAaF,CAEA,aACE,oBACF,CAEA,OAKE,wBAAyB,CAFzB,WAAY,CACZ,iBAAkB,CAElB,UAAY,CAGZ,cAAe,CAFf,cAAe,CACf,eAAgB,CANhB,YAAa,CAQb,+BAAiC,CATjC,UAUF,CAEA,aACE,wBACF,CAEA,gBACE,wBAAyB,CACzB,kBACF,CAEA,OAIE,wBAAyB,CADzB,iBAAkB,CAElB,aAAc,CACd,cAAe,CALf,eAAgB,CAChB,YAKF,CAGA,SACE,YACF,CAEA,YAIE,UAAW,CAHX,cAAe,CAEf,eAAgB,CADhB,kBAGF,CAEA,aAGE,wBAAyB,CADzB,iBAAkB,CAElB,kBAAmB,CAHnB,YAIF,CAEA,kBACE,6BACF,CAEA,kBACE,6BACF,CAEA,eAGE,kBAAmB,CAFnB,YAAa,CACb,6BAA8B,CAE9B,kBACF,CAEA,kBAGE,UAAW,CAFX,cAAe,CACf,eAEF,CAEA,kBAEE,kBAAmB,CACnB,cAAe,CACf,eAAgB,CAHhB,gBAIF,CAEA,wBACE,wBAAyB,CACzB,aACF,CAEA,wBACE,wBAAyB,CACzB,aACF,CAEA,YACE,eACF,CAEA,gBAEE,wBAAyB,CACzB,iBAAkB,CAFlB,WAAY,CAIZ,iBAAkB,CADlB,eAEF,CAEA,iBAEE,wBAAyB,CACzB,iBAAkB,CAFlB,WAAY,CAGZ,iBACF,CAEA,sBAME,UAAY,CAFZ,cAAe,CACf,eAAgB,CAJhB,iBAAkB,CAClB,SAAU,CACV,QAIF,CAEA,cAEE,UAAW,CADX,cAAe,CAEf,gBACF,CAEA,aAEE,wBAAyB,CACzB,aAAc,CAFd,eAGF,CAEA,mBACE,wBACF,CAEA,YAEE,WAAY,CACZ,kBAAmB,CAFnB,UAGF,CAEA,MAEE,kBAAmB,CADnB,YAEF","sources":["index.css","App.css"],"sourcesContent":["body {\r\n margin: 0;\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',\r\n 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',\r\n sans-serif;\r\n -webkit-font-smoothing: antialiased;\r\n -moz-osx-font-smoothing: grayscale;\r\n }\r\n \r\n code {\r\n font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',\r\n monospace;\r\n }","* {\r\n margin: 0;\r\n padding: 0;\r\n box-sizing: border-box;\r\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen,\r\n Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\r\n}\r\n\r\nbody {\r\n background-color: #f5f7fa;\r\n color: #333;\r\n}\r\n\r\n.app {\r\n display: flex;\r\n justify-content: center;\r\n align-items: center;\r\n min-height: 100vh;\r\n padding: 20px;\r\n}\r\n\r\n.container {\r\n width: 100%;\r\n max-width: 600px;\r\n}\r\n\r\n.card {\r\n background-color: white;\r\n border-radius: 12px;\r\n box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);\r\n overflow: hidden;\r\n}\r\n\r\n.card-header {\r\n display: flex;\r\n align-items: center;\r\n justify-content: center;\r\n padding: 24px 20px;\r\n border-bottom: 1px solid #f0f0f0;\r\n}\r\n\r\n/* .icon {\r\n margin-right: 12px;\r\n} */\r\n\r\nh1 {\r\n font-size: 18px;\r\n font-weight: 600;\r\n color: #333;\r\n}\r\n\r\nform {\r\n padding: 20px;\r\n}\r\n\r\ntextarea {\r\n width: 100%;\r\n height: 150px;\r\n padding: 16px;\r\n border: 1px solid #e8e8e8;\r\n border-radius: 8px;\r\n resize: none;\r\n font-size: 16px;\r\n color: #555;\r\n margin-bottom: 16px;\r\n outline: none;\r\n transition: border-color 0.2s;\r\n}\r\n\r\ntextarea:focus {\r\n border-color: #4F8DF8;\r\n}\r\n\r\ntextarea::placeholder {\r\n color: #aaa;\r\n}\r\n\r\n.model-select {\r\n margin-bottom: 16px;\r\n}\r\n\r\nselect {\r\n width: 100%;\r\n padding: 12px 16px;\r\n border: 1px solid #e8e8e8;\r\n border-radius: 8px;\r\n font-size: 16px;\r\n color: #555;\r\n background-color: white;\r\n outline: none;\r\n appearance: none;\r\n background-image: url(\"data:image/svg+xml,%3Csvg width='12' height='8' viewBox='0 0 12 8' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L6 6L11 1' stroke='%23888888' stroke-width='2' stroke-linecap='round'/%3E%3C/svg%3E\");\r\n background-repeat: no-repeat;\r\n background-position: right 16px center;\r\n cursor: pointer;\r\n}\r\n\r\nselect:focus {\r\n border-color: #4F8DF8;\r\n}\r\n\r\nbutton {\r\n width: 100%;\r\n padding: 16px;\r\n border: none;\r\n border-radius: 8px;\r\n background-color: #4F8DF8;\r\n color: white;\r\n font-size: 16px;\r\n font-weight: 600;\r\n cursor: pointer;\r\n transition: background-color 0.2s;\r\n}\r\n\r\nbutton:hover {\r\n background-color: #3a7fe5;\r\n}\r\n\r\nbutton:disabled {\r\n background-color: #a5c4ff;\r\n cursor: not-allowed;\r\n}\r\n\r\n.error {\r\n margin-top: 16px;\r\n padding: 12px;\r\n border-radius: 8px;\r\n background-color: #ffefef;\r\n color: #e74c3c;\r\n font-size: 14px;\r\n}\r\n\r\n/* Results styling */\r\n.results {\r\n padding: 20px;\r\n}\r\n\r\n.results h2 {\r\n font-size: 18px;\r\n margin-bottom: 20px;\r\n font-weight: 600;\r\n color: #333;\r\n}\r\n\r\n.result-item {\r\n padding: 16px;\r\n border-radius: 8px;\r\n background-color: #f9f9f9;\r\n margin-bottom: 16px;\r\n}\r\n\r\n.result-item.real {\r\n border-left: 4px solid #2ecc71;\r\n}\r\n\r\n.result-item.fake {\r\n border-left: 4px solid #e74c3c;\r\n}\r\n\r\n.result-header {\r\n display: flex;\r\n justify-content: space-between;\r\n align-items: center;\r\n margin-bottom: 12px;\r\n}\r\n\r\n.result-header h3 {\r\n font-size: 16px;\r\n font-weight: 600;\r\n color: #333;\r\n}\r\n\r\n.prediction-badge {\r\n padding: 6px 12px;\r\n border-radius: 20px;\r\n font-size: 14px;\r\n font-weight: 600;\r\n}\r\n\r\n.real .prediction-badge {\r\n background-color: #e8f7f0;\r\n color: #2ecc71;\r\n}\r\n\r\n.fake .prediction-badge {\r\n background-color: #fdeae8;\r\n color: #e74c3c;\r\n}\r\n\r\n.confidence {\r\n margin-top: 12px;\r\n}\r\n\r\n.confidence-bar {\r\n height: 12px;\r\n background-color: #f0f0f0;\r\n border-radius: 6px;\r\n overflow: hidden;\r\n margin-bottom: 8px;\r\n}\r\n\r\n.confidence-fill {\r\n height: 100%;\r\n background-color: #4F8DF8;\r\n border-radius: 6px;\r\n position: relative;\r\n}\r\n\r\n.confidence-fill span {\r\n position: absolute;\r\n right: 8px;\r\n top: -3px;\r\n font-size: 12px;\r\n font-weight: 600;\r\n color: white;\r\n}\r\n\r\n.confidence p {\r\n font-size: 12px;\r\n color: #888;\r\n text-align: right;\r\n}\r\n\r\n.back-button {\r\n margin-top: 16px;\r\n background-color: #e9ecef;\r\n color: #495057;\r\n}\r\n\r\n.back-button:hover {\r\n background-color: #dee2e6;\r\n}\r\n\r\n.logo-image {\r\n width: 50px; \r\n height: 50px;\r\n object-fit: contain;\r\n}\r\n\r\n.icon {\r\n display: flex;\r\n align-items: center;\r\n}"],"names":[],"sourceRoot":""}
|
build/static/js/main.c63a58f2.js
ADDED
The diff for this file is too large to render.
See raw diff
|
|
build/static/js/main.c63a58f2.js.LICENSE.txt
ADDED
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* @license React
|
3 |
+
* react-dom-client.production.js
|
4 |
+
*
|
5 |
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
6 |
+
*
|
7 |
+
* This source code is licensed under the MIT license found in the
|
8 |
+
* LICENSE file in the root directory of this source tree.
|
9 |
+
*/
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @license React
|
13 |
+
* react-dom.production.js
|
14 |
+
*
|
15 |
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
16 |
+
*
|
17 |
+
* This source code is licensed under the MIT license found in the
|
18 |
+
* LICENSE file in the root directory of this source tree.
|
19 |
+
*/
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @license React
|
23 |
+
* react-jsx-runtime.production.js
|
24 |
+
*
|
25 |
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
26 |
+
*
|
27 |
+
* This source code is licensed under the MIT license found in the
|
28 |
+
* LICENSE file in the root directory of this source tree.
|
29 |
+
*/
|
30 |
+
|
31 |
+
/**
|
32 |
+
* @license React
|
33 |
+
* react.production.js
|
34 |
+
*
|
35 |
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
36 |
+
*
|
37 |
+
* This source code is licensed under the MIT license found in the
|
38 |
+
* LICENSE file in the root directory of this source tree.
|
39 |
+
*/
|
40 |
+
|
41 |
+
/**
|
42 |
+
* @license React
|
43 |
+
* scheduler.production.js
|
44 |
+
*
|
45 |
+
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
46 |
+
*
|
47 |
+
* This source code is licensed under the MIT license found in the
|
48 |
+
* LICENSE file in the root directory of this source tree.
|
49 |
+
*/
|
build/static/js/main.c63a58f2.js.map
ADDED
The diff for this file is too large to render.
See raw diff
|
|
model_training/model_training.ipynb
ADDED
The diff for this file is too large to render.
See raw diff
|
|
model_training/visualizations/class_distribution.png
ADDED
![]() |
model_training/visualizations/distilbert_confusion_matrix.png
ADDED
![]() |
model_training/visualizations/fake_news_wordcloud.png
ADDED
![]() |
Git LFS Details
|
model_training/visualizations/feature_importance.png
ADDED
![]() |
Git LFS Details
|
model_training/visualizations/lr_confusion_matrix.png
ADDED
![]() |
model_training/visualizations/model_comparison.png
ADDED
![]() |
model_training/visualizations/real_news_wordcloud.png
ADDED
![]() |
Git LFS Details
|
model_training/visualizations/rf_confusion_matrix.png
ADDED
![]() |
model_training/visualizations/roc_curves.png
ADDED
![]() |
Git LFS Details
|
model_training/visualizations/text_length_distribution.png
ADDED
![]() |
Git LFS Details
|
models/distilbert_model.pt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:0181662839f9ed56480b01882d344bb0f3ea133fe154a184562e089651dc413f
|
3 |
+
size 265499424
|
models/lr_model.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:abbb63f0866d81b83fbddf21308879f1f4d475f24b337050c0a9e31f9bccbc8b
|
3 |
+
size 80784
|
models/rf_model.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:04577df8c9f96e29bc7614f16f72f733de4e0a41288b4211e1d05a80bfbd29d0
|
3 |
+
size 53206112
|
models/tfidf_vectorizer.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:7eba5171ee839fc50cf4d5eed7bf3e5e1d6a7822cd6cbe00b0e368fa77ad070f
|
3 |
+
size 372373
|
requirements.txt
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
flask==2.0.1
|
2 |
+
werkzeug==2.0.3
|
3 |
+
torch==2.6.0
|
4 |
+
transformers==4.49.0
|
5 |
+
scikit-learn==1.6.1
|
6 |
+
nltk==3.9.1
|
7 |
+
regex==2023.6.3
|
8 |
+
numpy==2.0.2
|
9 |
+
gunicorn==20.1.0
|
10 |
+
matplotlib==3.7.2
|