Scikit-learn을 이용한 특징 추출

3 분 소요

해당 포스팅의 내용은 텐서플로2와 머신러닝으로 시작하는 자연어 처리를 보고 개인적으로 정리한 내용입니다.

Scikit-learn은 파이썬용 머신러닝 라이브러리이다. 머신러닝 기술을 활용하는 데 필요한 다양한 기능을 제공하며 특징 추출에 대한 모듈도 제공해준다. 자연어 처리에서 특징 추출이란 텍스트 데이터에서나 단어나 문장들을 어떤 특징 값(벡터)으로 바꿔주는 것을 의미한다. 텍스트 데이터를 다루면서 기본적으로 자주 사용하는 기법이므로 정리해 두고자 한다. 정리할 모듈은 아래와 같다.

  • CountVectorizer
  • TfidVectorizer
  • HashingVectorizer

CountVectorizer

이름에서도 알 수 있듯이 텍스트 데이터에서 횟수를 기준으로 특징을 추출하는 방법이다. 여기서 어떤 단위의 횟수를 셀 것인지는 선택 사항이다. 여기서 말하는 단위는 단어가 될수도 있고, 문자 하나하나가 될 수도 있다. 보통은 텍스트에서 단어를 기준으로 횟수를 츠정하는데, 문장을 입력으로 받아 단어의 횟수를 측정한 뒤 벡터로 만든다.

먼저 scikit-learn 에서 해당 모듈을 불러오자.

from sklearn.feature_extraction.text import CountVectorizer

이번에는 텍스트 데이터를 불러오자. text_data 라는 리스트에 임의대로 문장들을 설정하였다. 그리고 CountVectorizer 객체를 선언한다.

text_data = ['나는 배가 고프다', '내일 점심 뭐먹지', '내일 공부 해야겠다', '점심 먹고 공부 해야지']

count_vectorizer = CountVectorizer()

다음에는 단어를 벡터화하기 위해 참고할 단어사전을 만들어야 한다.

예를 들어 ‘나는 매일 공부를 한다’ 라는 문장을 횟수값으로 이루어진 벡터로 만드는 경우를 생각해보자. 이 때 단어 사전이 아래와 같이 6개의 단어로 구성되어 있다면

"나는", "너가", ""매일", "공부를", "한다.", "좋아한다"

문장의 단어 해당 단어사전에 등장하는 경우 그 횟수로 바꾸고, 등장하지 않는 경우를 0 이라고 한다면 아래의 벡터로 변경 될 것이다.

[1, 0, 1, 1, 1, 0]

즉 첫 번째 단어인 “나는” 한 번 등장하므로 ‘1’이 되고, “너가”의 경우는 등장하지 않으므로 “0”이 되는 것이다.

만약 “나는 매일 매일 공부를 한다”의 문장의 경우는 아래와 같이 변경될 것이다.

[1, 0, 2, 1, 1, 0]

차이점이 보이는가? 사이킷런에서는 자동으로 데이터에 맞는 단어 사전을 만들어준다.

count_vectorizer.fit(text_data)
print(count_vectorizer.vocabulary_)
>> Output
{'나는': 2, '배가': 6, '고프다': 0, '내일': 3, '점심': 7, '뭐먹지': 5, '공부': 1, '해야겠다': 8, '먹고': 4, '해야지': 9}

딕셔너리 형태의 단어 사전이 만들어졌다. 단어 오른쪽의 숫자는 단어 사전의 인덱스이다.

이제 텍스트 데이터를 실제 벡터로 만들어보자. 정의한 텍스트 데이터 중에 하나만 선택해서 벡터로 만든다.

sentence = [text_data[0]] # ['나는 배가 고프다']
print(count_vectorizer.transform(sentence).toarray())
>> Output
[[1 0 1 0 0 0 1 0 0 0]]

“나는 배가 고프다” 라는 문장을 벡터로 만들었다. 단어 사전에서 해당 단어의 인덱스가 각각 ‘2, 6, 0’ 이므로 해당 인덱스에 해당 하는 값이 1로 바뀌게 된다.

이처럼 매우 간단하게 텍스트 데이터에서 특징을 추출할 수 있다. 횟수를 사용해서 벡터를 만들기 때문에 직관적이고 간단해서 여러 상황에서 사용할 수 있다는 장점이 있다.

하지만 단순히 횟수만을 특징으로 잡기 때문에 큰 의미가 없지만 자주 사용되는 단어들, 예를 들면 조사 혹은 지시대명사가 높은 특징 값을 가지기 때문에 유의미하게 사용하기 어려울 수 있다. 이러한 문제점을 해결하기 위해 TF-IDF 방식의 특징 추출을 사용한다.

TfidfVectorizer

TfidfVectorizer는 TF-IDF라는 특정한 값을 사용해서 텍스트 데이터의 특징을 추출한다. 각 값이 의미하는 바를 설명하면 TF(Term Frequency)란 특정 단어가 하나의 데이터 안에서 등장하는 횟수를 의미한다. DF(Document Frequency)는 문자 빈도 값으로, 특정 단어가 여러 데이터에 자주 등장하는지를 알려주는 지표다. IDF(Inverse Document Frequency)는 이 값에 역수를 취해서 구할 수 있으며, 특정 단어가 다른 데이터에 등장하지 않을수록 값이 커짐을 의미한다.

TF-IDF란 이 두 값을 곱해서 사용하므로 어떤 단어가 해당 문서에 자주 등장하지만 다른 문서에는 많이 없는 단어일수록 높은 값을 가지게 된다. 따라서 조사나 지시대명사처럼 자주 등장하는 단어는 TF 값은 크지만 IDF 값이 작아지므로 앞선 CountVectorizer가 가진 문제점을 해결할 수 있다.

역시 사이킷런의 모듈을 불러와 보자.

from sklearn.feature_extraction.text import TfidfVectorizer

이제 특징을 추출할 데이터를 정의하고 해당 객체를 생성한다. 앞과 동일한 데이터를 사용하겠다.

text_data = ['나는 배가 고프다', '내일 점심 뭐먹지', '내일 공부 해야겠다', '점심 먹고 공부 해야지']
tfidf_vectorizer = TfidfVectorizer()

이후 역시 해당 데이터에 맞는 단어 사전을 생성한다.

tfidf_vectorizer.fit(text_data)
print(tfidf_vectorizer.vocabulary_)
>> Output
{'나는': 2, '배가': 6, '고프다': 0, '내일': 3, '점심': 7, '뭐먹지': 5, '공부': 1, '해야겠다': 8, '먹고': 4, '해야지': 9}

그리고 해당 데이터의 한 문장만 객체에 적용해 벡터로 바뀐 값도 출력해보자.

sentence = [text_data[3]] # ['점심 먹고 공부 해야지']
print(tfidf_vectorizer.transform(sentence).toarray())
>> output
[[0.         0.43779123 0.         0.         0.55528266 0.
  0.         0.43779123 0.         0.55528266]]

단어 사전은 동일한데 앞선 CountVectorizer와는 다른 형태의 벡터가 생성된 것을 볼 수 있다.

예시로 사용한 문장은 “점심 먹고 공부 해야지” 인데 여기서 1, 4, 7, 9번째 단어들을 제외한 단어들은 해당 문장에 사용되지 않아 모두 0이 나왔다. 그리고 문장에 나온 단어에 대한 TF-IDF 값을 살펴보자. 우선 1, 7번째 단어인 ‘공부’와 ‘점심’이라는 단어는 0.43 정도의 값을 가지고 4, 9번째 단어인 ‘먹고’, ‘해야지’는 다른 데이터에는 나오지 않기 때문에 0.55로 조금 더 높은 값이 나왔다.

이처럼 특징 추출 방법으로 TF-IDF 값을 사용할 경우 단순 횟수를 이용하는 것보다는 각 단어의 특징을 조금 더 잘 반영할 수 있다. 텐서플로 등 다른 라이브러리를 활용해 딥러닝 모델을 만든다고 해도 이러한 데이터 전처리 작업은 사이킷런을 활용해 할 수 있기 때문에 기억해 두는 것이 좋겠다.

HashingVectorizer

HashingVectorizer는 방법적으로는 CountVectorizer와 동일하지만 해시 함수를 사용하기 때문에 실행 시간을 크게 줄일 수 있다. 따라서 텍스트의 크기가 클수록 HashingVectorizer를 사용하는 것이 효율적이다.

Reference

댓글남기기