import streamlit as st import requests import openai import os from datasets import load_dataset from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import SGDClassifier # Incremental Learning에 적합한 모델 from sklearn.metrics import classification_report, accuracy_score import joblib import matplotlib.pyplot as plt import seaborn as sns from pathlib import Path # Streamlit 페이지 설정 st.set_page_config(page_title="정치적 성향 분석 및 반대 관점 생성", page_icon="📰", layout="wide") # OpenAI API 키 설정 openai.api_key = os.getenv("OPENAI_API_KEY") # 허깅페이스 데이터셋 로드 @st.cache_data def load_huggingface_data(): dataset = load_dataset("jacobvs/PoliticalTweets") return dataset # 네이버 뉴스 API를 통해 뉴스 데이터 가져오기 def fetch_naver_news(query, display=15): client_id = "I_8koTJh3R5l4wLurQbG" # 네이버 개발자 센터에서 발급받은 Client ID client_secret = "W5oWYlAgur" # 네이버 개발자 센터에서 발급받은 Client Secret url = "https://openapi.naver.com/v1/search/news.json" headers = { "X-Naver-Client-Id": client_id, "X-Naver-Client-Secret": client_secret, } params = { "query": query, "display": display, # 뉴스 15개 가져오기 "start": 1, "sort": "date", # 최신순으로 정렬 } response = requests.get(url, headers=headers, params=params) if response.status_code == 200: return response.json()['items'] else: st.error("뉴스 데이터를 불러오는 데 실패했습니다.") return [] # 허깅페이스 데이터와 네이버 뉴스 데이터를 결합 def combine_datasets(huggingface_data, naver_data): additional_texts = [item['title'] + ". " + item['description'] for item in naver_data] additional_labels = ["NEUTRAL"] * len(additional_texts) # 기본적으로 중립으로 라벨링 hf_texts = huggingface_data['train']['text'] hf_labels = huggingface_data['train']['party'] return hf_texts + additional_texts, hf_labels + additional_labels # 모델 초기화 def initialize_model(): if os.path.exists("incremental_model.pkl") and os.path.exists("tfidf_vectorizer.pkl"): model = joblib.load("incremental_model.pkl") vectorizer = joblib.load("tfidf_vectorizer.pkl") else: # 초기 모델 및 벡터라이저 설정 model = SGDClassifier(loss='log_loss', max_iter=5, tol=None) # 'log_loss'로 수정 vectorizer = TfidfVectorizer(max_features=1000, stop_words="english") return model, vectorizer # 추가 학습 수행 def incremental_training(texts, labels, model, vectorizer): X = vectorizer.fit_transform(texts) y = [0 if label == "Democrat" else 1 if label == "Republican" else 2 for label in labels] model.partial_fit(X, y, classes=[0, 1, 2]) # Incremental Learning # 모델 및 벡터라이저 저장 joblib.dump(model, "incremental_model.pkl") joblib.dump(vectorizer, "tfidf_vectorizer.pkl") return model, vectorizer # GPT-4를 이용해 반대 관점 기사 생성 def generate_article_gpt4(prompt): try: response = openai.ChatCompletion.create( model="gpt-4", messages=[ {"role": "system", "content": "You are a helpful assistant that generates articles."}, {"role": "user", "content": prompt} ], max_tokens=1024, temperature=0.7 ) return response['choices'][0]['message']['content'] except Exception as e: return f"Error generating text: {e}" # Streamlit 애플리케이션 시작 st.title("📰 정치적 성향 분석 및 반대 관점 생성 도구") st.markdown("네이버 뉴스와 허깅페이스 데이터를 활용하여 뉴스 성향을 분석하고, 반대 관점을 생성합니다.") # 데이터 로드 huggingface_data = load_huggingface_data() query = st.text_input("네이버 뉴스에서 검색할 키워드를 입력하세요", value="정치") # 데이터 결합 및 학습 if st.button("데이터 결합 및 학습"): texts, labels = combine_datasets(huggingface_data, fetch_naver_news(query)) model, vectorizer = initialize_model() model, vectorizer = incremental_training(texts, labels, model, vectorizer) # 성능 평가 X_test = vectorizer.transform(texts) y_test = [0 if label == "Democrat" else 1 if label == "Republican" else 2 for label in labels] y_pred = model.predict(X_test) accuracy = accuracy_score(y_test, y_pred) st.write(f"모델 정확도: {accuracy:.2f}") st.text("분류 리포트:") st.text(classification_report(y_test, y_pred, target_names=["Democrat", "Republican", "NEUTRAL"])) st.success("모델이 새로운 데이터로 추가 학습되었습니다.") # 뉴스 데이터 분석 및 반대 관점 기사 생성 if st.button("뉴스 성향 분석"): model, vectorizer = initialize_model() news_items = fetch_naver_news(query, display=15) # 뉴스 15개 가져오기 if news_items: st.subheader("뉴스 성향 분석 결과") for item in news_items: title = item["title"] description = item["description"] link = item["link"] combined_text = f"{title}. {description}" # 텍스트 벡터화 및 예측 vectorized_text = vectorizer.transform([combined_text]) prediction = model.predict(vectorized_text)[0] sentiment = ["Democrat", "Republican", "NEUTRAL"][prediction] # 반대 관점 생성 opposite_perspective = "보수적" if sentiment == "Democrat" else "진보적" if sentiment == "Republican" else "중립적" prompt = f"다음 기사의 반대 관점으로 기사를 작성하세요:\n\n{combined_text}\n\n반대 관점: {opposite_perspective}" opposite_article = generate_article_gpt4(prompt) st.write(f"**제목:** {title}") st.write(f"**기사 내용:** {description}") st.write(f"**성향:** {sentiment}") st.write(f"**반대 관점 기사:** {opposite_article}") st.write(f"**링크:** [기사 링크]({link})") st.markdown("---")