truth-detector-api / models.py
waliullah123's picture
Upload models.py
5907e4a verified
Raw
History Blame Contribute Delete
5.02 kB
import pandas as pd
import numpy as np
from sklearn.naive_bayes import MultinomialNB, ComplementNB
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC
from sklearn.calibration import CalibratedClassifierCV
from sklearn.ensemble import (
VotingClassifier, RandomForestClassifier, StackingClassifier,
GradientBoostingClassifier, HistGradientBoostingClassifier
)
from sklearn.feature_selection import SelectKBest, f_classif
from sklearn.pipeline import Pipeline
from sklearn.compose import ColumnTransformer
from sklearn.preprocessing import MinMaxScaler
from sklearn.feature_extraction.text import TfidfVectorizer
from dl_model import DeepNewsClassifier # §3.3.2 Deep Learning Module
class FakeNewsModels:
def __init__(self):
# All hand-crafted numeric columns (original + new deception features)
num_cols = [
# Original features
'lexical_density', 'capital_ratio', 'noun_ratio', 'speaker_reliability',
'sentiment_score', 'complexity_score', 'subjectivity_score',
'verb_ratio', 'adj_ratio', 'adv_ratio', 'punctuation_aggression',
'entity_density', 'formal_cadence', 'official_marker',
# Hedging & Certainty
'hedge_ratio', 'certainty_ratio', 'hedge_certainty_diff',
# Emotional
'emotional_intensity', 'sentiment_extremity',
'negativity_score', 'positivity_score',
# Numerical
'number_density', 'has_percentage', 'has_dollar_amount', 'number_count',
# Source / Attribution
'attribution_ratio', 'has_source_citation',
# Readability
'ari_score', 'gunning_fog', 'avg_sentence_length',
# Pronoun & Modality
'first_person_ratio', 'modal_ratio', 'negation_ratio',
# Comparison
'comparative_ratio', 'superlative_ratio',
# Voice
'passive_ratio',
# Urgency / Style
'caps_word_ratio', 'ellipsis_count',
'exclamation_marks', 'question_marks', 'quotes_count',
'total_words', 'avg_word_length',
# Social context history
'false_history_ratio', 'true_history_ratio', 'history_volume',
'is_republican', 'is_democrat',
]
preprocessor = ColumnTransformer(
transformers=[
('text', TfidfVectorizer(
ngram_range=(1, 3),
max_features=12000,
min_df=3,
max_df=0.75,
sublinear_tf=True,
stop_words='english'
), 'statement'),
('meta', MinMaxScaler(), num_cols),
],
remainder='drop'
)
# Feature selection: keep top 5000 features
selector = SelectKBest(f_classif, k=5000)
# Individual classifiers — tuned for better performance and calibrated uncertainty
nb_clf = CalibratedClassifierCV(ComplementNB(alpha=0.1))
lr_clf = LogisticRegression(
C=0.8, max_iter=3000, solver='lbfgs',
class_weight='balanced', l1_ratio=0
)
svm_clf = CalibratedClassifierCV(
LinearSVC(C=0.5, max_iter=5000, class_weight='balanced', dual='auto')
)
rf_clf = CalibratedClassifierCV(
RandomForestClassifier(
n_estimators=600, max_depth=22, min_samples_leaf=3,
class_weight='balanced', random_state=42, n_jobs=-1
)
)
self.models = {
'nb': Pipeline([('pre', preprocessor), ('sel', selector), ('clf', nb_clf)]),
'lr': Pipeline([('pre', preprocessor), ('sel', selector), ('clf', lr_clf)]),
'svm': Pipeline([('pre', preprocessor), ('sel', selector), ('clf', svm_clf)]),
'rf': Pipeline([('pre', preprocessor), ('sel', selector), ('clf', rf_clf)]),
# §3.3.2 Deep Learning Module — BiLSTM-approximating dual encoder
'dl': DeepNewsClassifier(),
}
# STACKING ENSEMBLE with LogisticRegression meta-learner (§3.3)
# Includes Deep Learning module as 5th base estimator
self.models['ensemble'] = StackingClassifier(
estimators=[
('nb', self.models['nb']),
('lr', self.models['lr']),
('svm', self.models['svm']),
('rf', self.models['rf']),
('dl', self.models['dl']), # §3.3.2
],
final_estimator=LogisticRegression(
C=0.5, class_weight='balanced', max_iter=1000
),
stack_method='predict_proba',
cv=5,
n_jobs=-1
)
def get_model(self, name):
return self.models.get(name)
def get_all_models(self):
return self.models