용도에 맞게 텍스트를 사전에 처리하는 작업이다. 특히 한국어 같은 경우는 구조적으로 축약 / 생략 / 이동 현상이 빈번하고, 불규칙 변형 / 음운 탈락 / 모음 조화 등 문법 구조가 복잡하며 변형의 다양성이 크기에 전처리의 중요성이 매우 크다.
따라서 우리 프로젝트에서도 추후 모델이 좋은 성능을 보일 수 있도록 여러 전처리 방법을 시도하며 최적의 방법을 찾으려 했다.
형태소 분석에는 다음의 세 형태소 분석기를 이용했다.
각 조원이 모델을 하나씩 맡아 전처리를 진행하여 각 모델의 장단점을 파악해왔으며, 성능 비교를 위해 random seed를 설정하여 20개의 동일한 샘플을 뽑아 명사 추출 후의 결과를 비교했다.
해당 세 모델을 비교한 결과, 각 모델의 눈에 띄는 특징은 다음과 같았다.
Okt 같은 경우는 띄어쓰기가 애매한 경우가 많이 관찰되었다. 예를 들면, '융복합'이라는 단어 또한 '융'과 '복합'으로 나눠버리는 등의 문제점이 발생하였다. 따라서 해당 형태소 분석기는 후보에서 제외하였고, Komoran과 Hannanum 중에 많은 고민을 하다가, Komoran이 전체적으로 높은 성능을 보이는 것 같아 해당 형태소 분석기를 사용하기로 결정하였다.
Hannanum은 대체적으로 높은 성능을 보였지만 '못깎아줘'를 명사로 취급한다거나, '인프라'에서 '인프'만 잘라내는 등의 문제점이 관찰되었다.
또한, 조원들과의 논의 과정에서 결정된 것들은 다음과 같았다.
논의 결과를 바탕으로 최종 전처리 코드를 작성하였다.
with open('/content/drive/MyDrive/temp/Newsletter_Board/stopwords.txt', 'r', encoding='cp949') as f:
list_file = f.readlines()
stopwords = list_file[0].split(",")
stopwords.append('데이터')
stopwords.append('빅데이터')
영어와 달리, 한글은 따로 불용어 사전이 존재하지 않기에 불용어 사전을 직접 정의해줘야 한다. 일반적으로 접속사나 조사 등을 불용어로 정의하는데, 해당 프로젝트에서는 명사만을 추출하기에, 기본 불용어는 yoonkt200님의 깃헙을 참고하고, 데이터
와 빅데이터
는 주제 자체이기에 불용어 리스트에 추가해주었다.
# 정규화
def preprocess(text):
text=text.strip()
text=re.compile('<.*?>').sub('', text)
text = re.compile('[%s]' % re.escape(string.punctuation)).sub(' ', text)
text = re.sub('\s+', ' ', text)
text = re.sub(r'\[[0-9]*\]',' ',text)
text=re.sub(r'[^\w\s]', ' ', str(text).strip())
text = re.sub(r'\d',' ',text)
text = re.sub(r'\s+',' ',text)
return text
# 명사/영단어 추출, 한글자 제외, 불용어 제거
def final(text):
n = []
word = komoran.nouns(text)
p = komoran.pos(text)
for pos in p:
if pos[1] in ['SL']:
word.append(pos[0])
for w in word:
if len(w)>1 and w not in stopwords:
n.append(w)
return " ".join(n)
# 최종
def finalpreprocess(text):
return final(preprocess(text))
처음 정규 표현식을 이용할 때는 한글만을 추출해 불필요한 기호를 제거하는 방식으로 코드를 작성하였으나, 이 과정에서 영단어가 제거되는 문제점이 발생해 새로운 정규화 코드를 작성해주었다.
그리고 뉴스 헤드라인에서 명사를 추출하고 추가적으로 영단어를 추출한 뒤, 불용어와 한글자를 제거하는 방향으로 함수를 정의해준 뒤, 앞서 정의한 정규화 함수와 합성함으로써 최종 전처리 함수를 작성하였다.
전처리 과정에서 한글자 단어를 제거한 이유는 보통 한글자 단어 같은 경우는 큰 의미를 가지는 경우가 대부분이며, 조사가 명사로 추출되는 경우도 빈번하기 때문이다.
처음에는 위와 같이 Komoran만을 사용하기로 결정했었지만, 추후 모델링 과정을 거치며 이런 의문이 들었다.
Komoran을 사용하면
공공데이터
와 같은 합성어가공공
과데이터
로 분리되는 문제점이 발생하는데, 이렇게 되면공공데이터
와마이데이터
같은 중요 키워드들이 전처리 과정에서 제외되는건가?
조원들과 여러 방법을 고안한 끝에, 다음과 같이 전처리 과정의 일부를 수정하기로 하였다.
데이터
와 빅데이터
포함 X데이터
라는 키워드가 포함된 뉴스가 실제로 해당 주제와의 관련성이 높기에, 오히려 이 키워드를 적합한 뉴스를 추출하는 지표로 사용하는 것이 좋을 것 같다고 생각)데이터
를 포함한 중요 키워드 리스트를 정의하여 해당 키워드들이 포함된 모든 행을 추출물론 이와 같이 수정한 이유에는 온전히 합성어 분할 문제 뿐만이 아니라, 모델링 과정에서 클러스터링이 제대로 이루어지지 않거나 기사 관련성 보다는 기사의 개수가 우선시되는 등의 문제점 또한 존재했다.
벡터화는 CountVectorizer과 TF-IDF Vectorizer 두가지 방법을 시도해보았다.
vect = CountVectorizer().fit(corpus)
vector = vect.transform(corpus).toarray())
print(vect.vocabulary_)
CountVectorizer는 단어들의 카운트(출현 빈도)로 여러 문서들을 벡터화한다. 즉, 단어 피처에 값을 부여할 때, 각 문서에서 해당 단어가 나타나는 횟수를 부여하는 방법이다.
tfidfv = TfidfVectorizer().fit(corpus)
vector = tfidfv.transform(corpus).toarray()
print(tfidfv.vocabulary_)
순서에 상관 없이 BOW(Bag of word) document에서 중요한 단어를 가중치로 주어 선택하는 방식이다.
해당 단위(문장) 안에서는 많이 등장하지만, 다른 문서들까지 전체에서는 적게 사용될수록 분별력 있는 특징이라는 점을 반영한다.
💡 TF-IDF는 정보 검색과 텍스트 마이닝에서 이용하는 가중치로, 여러 문서로 이루어진 문서군이 있을 때 어떤 단어가 특정 문서 내에서 얼마나 중요한 것인지를 나타내는 통계적 수치이다. 문서의 핵심어를 추출하거나, 검색 엔진에서 검색 결과의 순위를 결정하거나, 문서들 사이의 비슷한 정도를 구하는 등의 용도로 사용할 수 있다.
결론적으로 기존의 DTM(문서 단어 행렬)을 사용하는 것보다 많은 정보를 고려하는 TF-IDF Vectorization 방법을 이용하기로 하였다.