Fake News Detection
텍스트 분류를 활용한 가짜 뉴스 탐지 프로젝트.
프로젝트 개요
| 항목 |
내용 |
| 목표 |
뉴스 기사의 진위 여부 자동 분류 |
| 데이터 |
fake_or_real_news.csv (~30MB) |
| 기법 |
텍스트 분류 (NLP) |
| 언어 |
Python |
| 상태 |
초기 탐색 |
데이터셋
파일 정보
| 항목 |
내용 |
| 파일명 |
fake_or_real_news.csv |
| 크기 |
~30MB |
| 위치 |
iCloud/03_Projects/Python/ |
예상 구조
| id | title | text | label |
|----|-------|------|-------|
| 1 | ... | ... | REAL |
| 2 | ... | ... | FAKE |
분석 접근법
1. 탐색적 데이터 분석 (EDA)
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_csv('fake_or_real_news.csv')
# 기본 정보
print(df.info())
print(df['label'].value_counts())
# 텍스트 길이 분포
df['text_length'] = df['text'].str.len()
df.groupby('label')['text_length'].describe()
2. 전처리 파이프라인
import re
from sklearn.feature_extraction.text import TfidfVectorizer
def preprocess_text(text):
"""텍스트 전처리"""
# 소문자 변환
text = text.lower()
# 특수문자 제거
text = re.sub(r'[^a-zA-Z\s]', '', text)
# 불용어 제거 (선택적)
# text = remove_stopwords(text)
return text
# TF-IDF 벡터화
vectorizer = TfidfVectorizer(
max_features=10000,
ngram_range=(1, 2),
stop_words='english'
)
X = vectorizer.fit_transform(df['text'].apply(preprocess_text))
y = df['label'].map({'REAL': 0, 'FAKE': 1})
3. 모델링 접근법
전통 ML
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.naive_bayes import MultinomialNB
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.2, random_state=42
)
models = {
'Logistic Regression': LogisticRegression(max_iter=1000),
'Naive Bayes': MultinomialNB(),
'Random Forest': RandomForestClassifier(n_estimators=100)
}
for name, model in models.items():
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
print(f"\n{name}:")
print(classification_report(y_test, y_pred))
딥러닝 (BERT)
from transformers import BertTokenizer, BertForSequenceClassification
from transformers import Trainer, TrainingArguments
import torch
# 토크나이저
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
# 데이터셋 준비
class NewsDataset(torch.utils.data.Dataset):
def __init__(self, texts, labels, tokenizer, max_length=512):
self.texts = texts
self.labels = labels
self.tokenizer = tokenizer
self.max_length = max_length
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
encoding = self.tokenizer(
self.texts[idx],
truncation=True,
padding='max_length',
max_length=self.max_length,
return_tensors='pt'
)
return {
'input_ids': encoding['input_ids'].squeeze(),
'attention_mask': encoding['attention_mask'].squeeze(),
'labels': torch.tensor(self.labels[idx])
}
# 모델
model = BertForSequenceClassification.from_pretrained(
'bert-base-uncased', num_labels=2
)
# 학습
training_args = TrainingArguments(
output_dir='./results',
num_train_epochs=3,
per_device_train_batch_size=16,
evaluation_strategy='epoch'
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=eval_dataset
)
trainer.train()
평가 지표
| 지표 |
설명 |
중요도 |
| Accuracy |
전체 정확도 |
기본 |
| Precision (FAKE) |
가짜 뉴스 탐지 정밀도 |
높음 |
| Recall (FAKE) |
가짜 뉴스 탐지율 |
높음 |
| F1-Score |
정밀도/재현율 조화평균 |
핵심 |
| AUC-ROC |
분류 임계값 무관 성능 |
참고 |
오분류 비용
| 오류 유형 |
결과 |
비용 |
| FP (REAL→FAKE) |
진짜 뉴스를 가짜로 |
중간 |
| FN (FAKE→REAL) |
가짜 뉴스를 진짜로 |
높음 |
→ Recall 중시 (가짜 뉴스 놓치지 않기)
피처 분석
중요 피처 예시
# TF-IDF 상위 피처
feature_names = vectorizer.get_feature_names_out()
coefficients = model.coef_[0]
# FAKE 관련 상위 단어
fake_indicators = sorted(
zip(feature_names, coefficients),
key=lambda x: x[1],
reverse=True
)[:20]
# REAL 관련 상위 단어
real_indicators = sorted(
zip(feature_names, coefficients),
key=lambda x: x[1]
)[:20]
예상 패턴
| 구분 |
특징 |
| FAKE |
감정적 어조, 과장 표현, 출처 불분명 |
| REAL |
객관적 어조, 인용 포함, 구체적 수치 |
확장 가능성
1. 멀티모달 분석
- 이미지 검증 (역이미지 검색)
- 소셜 미디어 확산 패턴
2. 실시간 탐지
# API 엔드포인트 예시
from fastapi import FastAPI
app = FastAPI()
@app.post("/detect")
async def detect_fake_news(text: str):
# 전처리
processed = preprocess_text(text)
# 벡터화
vector = vectorizer.transform([processed])
# 예측
prediction = model.predict(vector)[0]
probability = model.predict_proba(vector)[0]
return {
"prediction": "FAKE" if prediction == 1 else "REAL",
"confidence": float(max(probability)),
"probabilities": {
"REAL": float(probability[0]),
"FAKE": float(probability[1])
}
}
3. 설명 가능성
import lime
from lime.lime_text import LimeTextExplainer
explainer = LimeTextExplainer(class_names=['REAL', 'FAKE'])
def predict_proba(texts):
vectors = vectorizer.transform(texts)
return model.predict_proba(vectors)
# 특정 기사 설명
explanation = explainer.explain_instance(
article_text,
predict_proba,
num_features=10
)
explanation.show_in_notebook()
참고 자료
데이터셋
논문
| 논문 |
연도 |
기여 |
| Detecting Fake News on Social Media |
2017 |
초기 ML 접근 |
| BERT for Fake News Detection |
2019 |
Transformer 적용 |
| Multimodal Fake News Detection |
2021 |
이미지+텍스트 |
파일 위치
- 데이터:
~/Library/Mobile Documents/com~apple~CloudDocs/03_Projects/Python/fake_or_real_news.csv
- 코드:
~/Library/Mobile Documents/com~apple~CloudDocs/03_Projects/Python/fakenews.py