2025. 3. 9. 04:32ㆍData Science Project
데이터 전처리
불용어.txt 다운로드
- 불용어.txt : 불용어를 모아놓은 텍스트 문서
1. 불용어.txt 파일을 읽어 쉼표(,) 기준으로 단어를 분리
# 불용어 로드: 파일에서 쉼표로 구분된 단어들을 읽고 strip()으로 공백 제거
with open('불용어.txt', 'r', encoding='utf-8') as f:
raw_text = f.read()
raw_stopwords = raw_text.split(',')
stopwords = [w.strip() for w in raw_stopwords if w.strip()]
2. 필요에 따른 추가적인 불용어 추가 :
extra_stopwords = ["종합", "포토", "영상", "게시판"]
for word in extra_stopwords:
if word not in stopwords:
stopwords.append(word)
3. 정규식을 통한 전처리
# 전처리 함수 (HTML 태그, 구두점, 공백, 숫자 제거) – KR-WordRank용
def preprocess(text):
text = text.strip()
text = re.compile('<.*?>').sub('', text) # HTML 태그 제거
text = re.compile('[%s]' % re.escape(string.punctuation)).sub(' ', text) # 구두점 제거
text = re.sub(r'\s+', ' ', text) # 연속 공백 제거
text = re.sub(r'\d', ' ', text) # 숫자 제거
return text
4. Komoran 을 활용한 명사추출과 불용어 제거
# 키워드 추출 함수: Komoran 명사 추출 후 불용어 제거 진행 (KR-WordRank용)
def extract_keywords(text):
words = komoran.nouns(text)
words = [w for w in words if len(w) > 1 and w not in stopwords]
return " ".join(words)
모델 선택
형태소 분석에는 다음의 세 형태소 분석기를 이용했다.
- Komoran
- Hannanum
- Okt

Okt 같은 경우는 띄어쓰기가 애매한 경우가 많이 관찰되었다. 예를 들면, '융복합'이라는 단어 또한 '융'과 '복합'으로 나눠버리는 등의 문제점이 발생하였다. 따라서 해당 형태소 분석기는 후보에서 제외하였고, Komoran과 Hannanum 중에 많은 고민을 하다가, Komoran이 전체적으로 높은 성능을 보이는 것 같아 해당 형태소 분석기를 사용하기로 결정하였다.
🗣️ TextRank
TextRank 모델은 PageRank의 알고리즘을 활용한 것으로, 페이지의 개념을 단어의 개념으로 바꾼 알고리즘이다. 즉, 텍스트로 이루어진 글에서 특정 단어가 다른 문장과 얼마만큼의 관계를 맺고 있는지를 계산하는 것이다.
TextRank는 그래프 기반의 랭킹모델로 순위를 매기는 방법이 문단의 추출적 요약에 매우 효과적이라는 생각으로 개발되었다.
📄WordRank
WordRank는 띄어쓰기가 없는 중국어와 일본어에서 그래프 랭킹 알고리즘을 이용해 단어를 추출하기 위해 제안된 방법이다.
WordRank는 일본어와 중국어의 Unsupervised word segmentation을 위해 제안된 방법으로 한국어에 적용할 시 좋은 결과를 낼 수 없다.
📄KR-WordRank
한국어는 띄어쓰기 정보를 이용해야한다. 띄어쓰기 정보를 이용하지 않으면 두 어절의 양끝에 걸친 substring 역시 단어 후보에 포함된다.
KR-WordRank 모델을 사용하여 수집한 뉴스 텍스트 데이터에서 키워드 추출을 진행한다. 뿐만 아니라 이 모델은 다른 단어나 다른 문장과의 관계성을 계산하여 점수화 하는 기능을 제공한다.
각 키워드에 점수를 매겨 해당 키워드를 가지고있는 기사가 그 키워드 점수에 해당하는 랭킹을 가지게 된다.
점수를 내림차순하여 상위 20개의 기사를 가져오도록한다.

키워드는 좀더 문맥을 파악하여 나타내기 위해서, Gemini 모델을 사용하도록 한다.
이렇게 하면, 키워드 점수(중요도) 는 KR-WordRank를 통해 진행하고, 뉴스 요약 및 키워드 추출은 Gemini를 통해 진행한다.

Gemini를 사용하여 각 기사의 title에서 2개의 키워드를 추출하도록 하는 프롬프트
# Gemini API를 호출하여 기사 제목을 요약하는 함수
def get_gemini_summary(text):
gemini_api_key = os.environ.get("GEMINI_API_KEY")
if not gemini_api_key:
return " ".join(text.split()[:2])
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key={gemini_api_key}"
payload = {
"prompt": f"Summarize this news article title : {text}",
"maxOutputTokens": 10
}
headers = {"Content-Type": "application/json"}
try:
response = requests.post(url, json=payload, headers=headers)
if response.status_code == 200:
summary = response.json().get("summary")
return summary if summary else " ".join(text.split()[:2])
else:
return " ".join(text.split()[:2])
except Exception as e:
print("Gemini API error:", e)
return " ".join(text.split()[:2])
Gemini와 KR-WordRank 는 각각 성능이 다르기때문에 전처리 방법도 다르게 하였다.
KR-WordRank 에 하는 전처리 처럼 불용어처리 및 숫자제거와 합성어 분리를 진행하면, Gemini 가 뉴스를 요약하는데 오히려 성능이 저하될수있으므로, 최소한의 전처리만을 진행한다.
Gemini 전처리
# Gemini 전용 전처리 함수: HTML 태그, 구두점, 공백만 제거 (숫자는 그대로 유지)
def preprocess_for_gemini(text):
text = text.strip()
text = re.compile('<.*?>').sub('', text)
text = re.compile('[%s]' % re.escape(string.punctuation)).sub(' ', text)
text = re.sub(r'\s+', ' ', text)
return text
KR-WordRank 에는 모든 전처리를 진행하도록 한다.
# 전처리 함수 (HTML 태그, 구두점, 공백, 숫자 제거) – KR-WordRank용
def preprocess(text):
text = re.compile('<.*?>').sub('', text) # HTML 태그 제거
text = re.compile('[%s]' % re.escape(string.punctuation)).sub(' ', text) # 구두점 제거
text = re.sub(r'\s+', ' ', text) # 연속 공백 제거
text = re.sub(r'\d', ' ', text) # 숫자 제거
return text
# 키워드 추출 함수: Komoran 명사 추출 후 불용어 제거 진행 (KR-WordRank용)
def extract_keywords(text):
words = komoran.nouns(text)
words = [w for w in words if len(w) > 1 and w not in stopwords]
return " ".join(words)
# KR-WordRank용 전처리 함수 (불용어 제거 포함)
def preprocess_text(text):
return extract_keywords(preprocess(text))
참고로 KR-WordRank는 띄어쓰기를 포함한 한국어의 특성을 고안한 알고리즘이기 때문에, 띄어쓰기 전처리는 진행하지 않았다.
모델 적용
Gemini 적용
# Gemini에는 숫자 제거 없이 HTML, 구두점, 공백만 제거된 텍스트 전달
processed_title_for_gemini = preprocess_for_gemini(article_title)
gemini_summary = get_gemini_summary(processed_title_for_gemini)
link = matched_df.iloc[0]["링크"]
result[gemini_summary] = {"score": score, "link": link}
else:
result[keyword] = {"score": score, "link": ""}
return jsonify(result)
KR-WordRank 알고리즘 적용
# KR-WordRank에는 숫자 제거 포함 전처리 진행
docs = [preprocess_text(title) for title in news_df["제목"].tolist()]
docs = [d for d in docs if d.strip()]
if not docs:
return jsonify({"error": "전처리 후 문서가 없습니다."})
wordrank_extractor = KRWordRank(min_count=1, max_length=10, verbose=True)
keywords, word_scores, _ = wordrank_extractor.extract(docs, beta=0.85, max_iter=10)
keywords = {k: v for k, v in keywords.items() if not re.search(r'\d|\[', k)}
sorted_keywords = sorted(keywords.items(), key=lambda x: x[1], reverse=True)[:20]
result = {}
for keyword, score in sorted_keywords:
matched_df = news_df[news_df["제목"].str.contains(keyword, na=False)]
if not matched_df.empty:
article_title = matched_df.iloc[0]["제목"]
'Data Science Project' 카테고리의 다른 글
| 건설공사 사고 예방 및 대응책 생성 : 한솔데코 시즌3 AI 경진대회 (0) | 2025.03.21 |
|---|---|
| [Newspelling] 뉴스 키워드 추출 프로젝트 - 웹페이지 개발 (0) | 2025.03.09 |
| [Newspelling] 뉴스 키워드 추출 프로젝트 - 문제정의 및 데이터 수집 (0) | 2025.03.09 |
| [모델링 프로젝트] 전기차 가격 예측 해커톤 (0) | 2025.01.12 |
| 제 1회 Medical AI MAI 경진대회 (0) | 2024.11.06 |