1+ from abc import ABC , abstractmethod
2+ from typing import List , Dict , Any
3+ import re
4+ from sentence_transformers import SentenceTransformer
5+ import numpy as np
6+ from sklearn .metrics .pairwise import cosine_similarity
7+ from transformers import BertModel , BertTokenizer
8+ import torch
9+
10+ class BaseExtractor (ABC ):
11+ def __init__ (self ):
12+ # 다국어 BERT 모델 초기화
13+ self .model = SentenceTransformer ('paraphrase-multilingual-mpnet-base-v2' )
14+ self .bert_model = BertModel .from_pretrained ('klue/bert-base' )
15+ self .tokenizer = BertTokenizer .from_pretrained ('klue/bert-base' )
16+
17+ # 도메인별 전문 용어 사전
18+ self .domain_keywords = {
19+ 'legal' : ['제1조' , '당사자' , '계약기간' , '의무' , '권리' ],
20+ 'medical' : ['투여량' , '부작용' , '진단코드' , '처방' , '증상' ],
21+ 'technical' : ['사양' , '구성' , '설치' , '운영' , '관리' ]
22+ }
23+
24+ @abstractmethod
25+ def preprocess_text (self , text : str ) -> str :
26+ """문서 타입별 전처리 메서드"""
27+ pass
28+
29+ def calculate_sentence_importance (self , sentence : str , position : int , total_sentences : int ) -> float :
30+ """
31+ 문장의 중요도를 계산합니다.
32+
33+ Args:
34+ sentence (str): 분석할 문장
35+ position (int): 문장의 위치
36+ total_sentences (int): 전체 문장 수
37+
38+ Returns:
39+ float: 문장 중요도 점수
40+ """
41+ # TextRank 점수
42+ textrank_score = self ._calculate_textrank_score (sentence )
43+
44+ # BERT 임베딩 기반 의미적 중요도
45+ semantic_score = self ._calculate_semantic_score (sentence )
46+
47+ # 위치 기반 가중치 (문서의 시작과 끝 부분에 더 높은 가중치)
48+ position_weight = self ._calculate_position_weight (position , total_sentences )
49+
50+ # 도메인 특화 점수
51+ domain_score = self ._calculate_domain_score (sentence )
52+
53+ # 종합 점수 계산 (가중치: TextRank 0.3, BERT 0.3, 위치 0.2, 도메인 0.2)
54+ total_score = (textrank_score * 0.3 +
55+ semantic_score * 0.3 +
56+ position_weight * 0.2 +
57+ domain_score * 0.2 )
58+
59+ return total_score
60+
61+ def _calculate_textrank_score (self , sentence : str ) -> float :
62+ """TextRank 알고리즘을 적용한 중요도 점수 계산"""
63+ # 구현은 기존 코드와 동일
64+ pass
65+
66+ def _calculate_semantic_score (self , sentence : str ) -> float :
67+ """BERT 기반 의미적 중요도 점수 계산"""
68+ inputs = self .tokenizer (sentence , return_tensors = "pt" , truncation = True , max_length = 512 )
69+ with torch .no_grad ():
70+ outputs = self .bert_model (** inputs )
71+
72+ # [CLS] 토큰의 임베딩을 문장 표현으로 사용
73+ sentence_embedding = outputs .last_hidden_state [:, 0 , :].numpy ()
74+
75+ # 의미적 중요도 점수 계산 (예: 다른 문장들과의 평균 유사도)
76+ return np .mean (cosine_similarity (sentence_embedding , self .sentence_embeddings ))
77+
78+ def _calculate_position_weight (self , position : int , total : int ) -> float :
79+ """문장 위치 기반 가중치 계산"""
80+ # 문서의 시작과 끝 부분에 더 높은 가중치
81+ normalized_position = position / total
82+ if normalized_position < 0.1 or normalized_position > 0.9 :
83+ return 1.0
84+ elif normalized_position < 0.2 or normalized_position > 0.8 :
85+ return 0.8
86+ else :
87+ return 0.5
88+
89+ def _calculate_domain_score (self , sentence : str ) -> float :
90+ """도메인 특화 점수 계산"""
91+ max_score = 0.0
92+ for domain , keywords in self .domain_keywords .items ():
93+ score = sum (1 for keyword in keywords if keyword in sentence )
94+ max_score = max (max_score , score / len (keywords ))
95+ return max_score
96+
97+ def extract_key_sentences (self , text : str , top_n : int = 5 ) -> List [str ]:
98+ """
99+ 텍스트에서 핵심 문장을 추출합니다.
100+
101+ Args:
102+ text (str): 원본 텍스트
103+ top_n (int): 추출할 문장 수
104+
105+ Returns:
106+ List[str]: 핵심 문장 리스트
107+ """
108+ # 문장 분리
109+ sentences = re .split (r'[.!?]+' , text )
110+ sentences = [s .strip () for s in sentences if len (s .strip ()) > 0 ]
111+
112+ # 문장 임베딩 생성
113+ self .sentence_embeddings = self .model .encode (sentences )
114+
115+ # 각 문장의 중요도 계산
116+ sentence_scores = []
117+ for i , sentence in enumerate (sentences ):
118+ score = self .calculate_sentence_importance (sentence , i , len (sentences ))
119+ sentence_scores .append ((sentence , score ))
120+
121+ # 중요도 순으로 정렬
122+ ranked_sentences = sorted (sentence_scores , key = lambda x : x [1 ], reverse = True )
123+ return [sentence for sentence , _ in ranked_sentences [:top_n ]]
124+
125+ def extract_keywords (self , text : str , top_n : int = 10 ) -> Dict [str , float ]:
126+ """
127+ 텍스트에서 키워드를 추출합니다.
128+
129+ Args:
130+ text (str): 원본 텍스트
131+ top_n (int): 추출할 키워드 수
132+
133+ Returns:
134+ Dict[str, float]: 키워드와 중요도 점수
135+ """
136+ # 단어 분리 및 정규화
137+ words = re .findall (r'\w+' , text .lower ())
138+ word_freq = {}
139+
140+ # 단어 빈도수 계산
141+ for word in words :
142+ if len (word ) > 1 : # 1글자 단어 제외
143+ word_freq [word ] = word_freq .get (word , 0 ) + 1
144+
145+ # TF-IDF 스타일 점수 계산
146+ max_freq = max (word_freq .values ())
147+ keywords = {word : freq / max_freq for word , freq in word_freq .items ()}
148+
149+ # 도메인 특화 키워드 가중치 적용
150+ for word in keywords :
151+ for domain_keywords in self .domain_keywords .values ():
152+ if word in domain_keywords :
153+ keywords [word ] *= 1.5 # 도메인 키워드 가중치 증가
154+
155+ # 상위 키워드 선택
156+ return dict (sorted (keywords .items (), key = lambda x : x [1 ], reverse = True )[:top_n ])
0 commit comments