안녕하세요. 은공지능 공작소의 파이찬입니다!
정말 오랜만에 포스팅을 올리네요. 9월달은 정말 바쁘게 지낸 것 같습니다.
오늘은 tf-idf vectorizer를 생성할 때 필요한 여러가지 파라미터들에 대해 알아보겠습니다.

min_idf, analyzer, sublinear_tf, ngram_range, max_features
이렇게 5가지 파라미터들에 대해서 알아볼 것입니다.

 

 

 

 

 

 

 

 

 

 

 

 

1. 사전준비

 

 

 

from sklearn.feature_extraction.text import TfidfVectorizer

text = ['I go to my home my home is very large',
        'I went out my home I go to the market',
        'I bought a yellow lemon I go back to home']

tfidf_vectorizer = TfidfVectorizer()
우선 위와같이 데이터를 생성해 줍니다.
저번 시간에 썼던 데이터와 동일한 데이터 입니다.
오늘 사용할 파라미터는 TfidfVectorizer()의 괄호 안에 들어가는 것들입니다.

 

 

 

 

 

tfidf_vectorizer.fit(text) # 벡터라이저가 단어들을 학습합니다.
tfidf_vectorizer.vocabulary_ # 벡터라이저가 학습한 단어사전을 출력합니다.
sorted(tfidf_vectorizer.vocabulary_.items()) # 단어사전을 정렬합니다.
output:
[('back', 0),
 ('bought', 1),
 ('go', 2),
 ('home', 3),
 ('is', 4),
 ('large', 5),
 ('lemon', 6),
 ('market', 7),
 ('my', 8),
 ('out', 9),
 ('the', 10),
 ('to', 11),
 ('very', 12),
 ('went', 13),
 ('yellow', 14)]
먼저 fit 명령어를 통해서 text 변수에 저장된 데이터를 학습시켜줘야 합니다.
이렇게 학습이 끝나면, 단어사전과 idf 벡터 등을 출력해볼 수 있습니다.
단어사전은 .vocabulary_ 명령어를 통해 출력이 가능합니다.

저희는 출력을 이쁘게 하기 위해서 sorted 명령어도 같이 사용해 보았습니다.
sorted 명령어를 통해 살펴보니, 단어들은 알파벳 오름차순으로
인덱스가 부여된다는 사실도 알 수 있었습니다.

 

 

 

 

 

 

 

2. min_df 파라미터 알아보기

 

 

 

min-df는 DF(document-frequency)의 최소 빈도값을 설정해주는 파라미터입니다.
DF는 특정 단어가 나타나는 '문서의 수'를 의미합니다.
단어의 수가 아니라 문서의 수라는 것에 주의해 주세요.

DF 개념에 대해 위의 예시로 다시 설명을 드리자면
단어 'home'의 경우 전체 문서에서 빈도는 4번 이지만,
'home'이라는 단어가 포함된 문서의 수는 3개이기 때문에 DF = 3 입니다.

이러한 DF의 최소값을 설정하여 해당 값보다 작은 DF를 가진 단어들은
단어사전 (vocabulary_)에서 제외하고, 인덱스를 부여하지 않습니다.
이것이 바로 min_df 파라미터 입니다. 코드로 바로 실습해 보겠습니다.

 

 

 

 

 

min_df_vectorizer = TfidfVectorizer(min_df = 2)
min_df_vectorizer.fit(text)
sorted(min_df_vectorizer.vocabulary_.items())
output:
[('go', 0), ('home', 1), ('my', 2), ('to', 3)]
TfidfVectorizer 객체를 생성할 때, 위와 같이 min-df 파라미터를 설정하였습니다.
최소 DF가 2로 설정되었으니, 1인 것들은 모두 탈락하게 됩니다.
(PPT 설명화면에서 회색박스 글자들은 모두 DF가 1인 것들입니다.)

단어사전을 출력해보니 4개의 단어만 남았습니다.
최소 DF를 2 이상 갖는 단어들이 단어사전에 남아있게 되는 것입니다.
이 역시 알파벳 오름차순으로 인덱스를 부여받아 0 ~ 3 의 값이 할당이 되었습니다.

 

 

 

 

 

 

 

3. analyzer 파라미터 알아보기

 

 

analyzer 파라미터는 학습단위를 결정하는 파라미터입니다.
word, char 2가지 옵션 정도를 고려해볼 수 있습니다.

analyzer = 'word'라고 설정시, 학습의 단위를 단어로 설정합니다. (ex - home, go, my ...)
analyzer = 'char'라고 설정시, 학습의 단위를 글자로 설정합니다.(ex - a, b, c, d ...)

 

 

 

 

 

vectorizer = TfidfVectorizer(analyzer = 'word')
vectorizer.fit(text)
sorted(vectorizer.vocabulary_.items())
output:
[('back', 0),
 ('bought', 1),
 ('go', 2),
 ('home', 3),
 ('is', 4),
 ('large', 5),
 ('lemon', 6),
 ('market', 7),
 ('my', 8),
 ('out', 9),
 ('the', 10),
 ('to', 11),
 ('very', 12),
 ('went', 13),
 ('yellow', 14)]
analyzer = 'word'로 파라미터를 주었을 때, 단어 사전의 모습입니다.
기존의 결과와 크게 다르지 않은 모습을 보여줍니다.

 

 

 

 

 

vectorizer = TfidfVectorizer(analyzer = 'char')
vectorizer.fit(text)
sorted(vectorizer.vocabulary_.items())
output:
[(' ', 0),
 ('a', 1),
 ('b', 2),
 ('c', 3),
 ('e', 4),
 ('g', 5),
 ('h', 6),
 ('i', 7),
 ('k', 8),
 ('l', 9),
 ('m', 10),
 ('n', 11),
 ('o', 12),
 ('r', 13),
 ('s', 14),
 ('t', 15),
 ('u', 16),
 ('v', 17),
 ('w', 18),
 ('y', 19)]
analyzer = 'char'로 설정했을 때의 모습입니다.
위의 결과물처럼 각 글자마다 인덱스가 부여된 것을 확인하실 수 있습니다.

그렇다면 위와 같이 char 기반의 analyzer가 실제 모델에서
좋은 성능을 보일지 의문이 남을 수도 있을 것 같습니다.

결론부터 말씀드리면, 상당히 좋은 결과를 내놓을 수 있습니다.
제가 CNN 모델에서 char analyzer로 학습을 시킨 적이 있었는데,
생각보다 괜찮은 결과가 나왔던 것으로 기억합니다.

그러니 모델의 성능을 올리기 위해서,
word 기반 방법만 고집할 것이 아니라 char 기반의 방법도 시도해 볼 만하다고 생각합니다.

 

 

 

 

 

 

 

4. sublinear_tf 파라미터 알아보기

 

 

 

sublinear_tf 파라미터는 TF (Term-Frequency, 단어빈도) 값의
스무딩(smoothing)
여부를 결정하는 파라미터 입니다. (True/False)

TF -> 1 + ln(TF)
위와 같은 수식을 통해 스무딩 처리가 이루어집니다.
이를 이해하기 쉽도록 간단한 예제를 하나 준비해 보았습니다.

 

 

 

 

 

import numpy as np
def sublinear_func(input):
    rst = 1 + np.log(input)
    return rst
    
sublinear_func(100)
sublinear_func(10000)
output:
5.605170185988092
10.210340371976184
sublinear_tf를 구현해보는 간단한 함수를 만들어 보았습니다.
결과를 보시면 100은 5.6 정도로, 10000은 10.2 정도로 값이 확 줄어든 것을 확인하실 수 있습니다.
이처럼 sublinear_tf는 높은 TF값을 완만하게 처리하는 효과를 가지고 있습니다.

TF의 아웃라이어가 너무 심한 데이터의 경우,
이런 서브리니어 파라미터를 True로 바꿔주면 어느정도 효과를 기대할 수 있습니다.
간단히 코드 실습 후 넘어가겠습니다.

 

 

 

 

 

vectorizer = TfidfVectorizer(sublinear_tf = True) # defalut는 False입니다.
vectorizer.fit(text)
a = vectorizer.transform(text).toarray()
a
output:
array([[0.        , 0.        , 0.23427226, 0.39665741, 0.39665741,
        0.39665741, 0.        , 0.        , 0.51076864, 0.        ,
        0.        , 0.23427226, 0.39665741, 0.        , 0.        ],
       [0.        , 0.        , 0.24902824, 0.24902824, 0.        ,
        0.        , 0.        , 0.42164146, 0.3206692 , 0.42164146,
        0.42164146, 0.24902824, 0.        , 0.42164146, 0.        ],
       [0.44514923, 0.44514923, 0.26291231, 0.26291231, 0.        ,
        0.        , 0.44514923, 0.        , 0.        , 0.        ,
        0.        , 0.26291231, 0.        , 0.        , 0.44514923]])
위와 같이 sublinear_tf를 True로 설정하면, 높은 TF값들에 대해서 스무딩 처리를 해줍니다.
하지만 저희 예제 데이터는 너무 작기 때문에, 해당 파라리터의 효과가 별로 없는 것 같네요.
'TF 값에 대해 아웃라이어 처리를 해준다'라는 효과 정도만 기억하고 넘어가시면 되겠습니다.

 

 

 

 

 

 

 

5. ngram_range 파라미터 이해하기

 

 

 

n-gram이라는 것은 단어의 묶음을 말합니다.
이 단어의 묶음을 범위를 설정하는 것이 ngram_range 파라미터입니다.

예를들어, ngram_range = (1, 1)이라면 단어의 묶음을 1개부터 1개까지 설정하라는 의미입니다.
단어사전에는 1개 단어묶음에 인덱스가 할당되겠죠.
이는 기존의 벡터라이저와 별 차이가 없는 결과를 내놓습니다.

만약 ngram_range = (1, 2)라고 한다면, 단어의 묶음을 1개부터 2개까지 설정하라는 뜻입니다.
단어사전에는 1개 단어묶음도 있고, 2개 단어묶음도 존재하게 되겠죠.
가령, 'go home', 'very good'과 같은 2개 짜리 묶음도 인덱스를 받게 되는 것입니다.

 

 

 

 

 

vectorizer = TfidfVectorizer(ngram_range = (1, 1))
vectorizer.fit(text)
sorted(vectorizer.vocabulary_.items())
output:
[('back', 0),
 ('bought', 1),
 ('go', 2),
 ('home', 3),
 ('is', 4),
 ('large', 5),
 ('lemon', 6),
 ('market', 7),
 ('my', 8),
 ('out', 9),
 ('the', 10),
 ('to', 11),
 ('very', 12),
 ('went', 13),
 ('yellow', 14)]
ngram_range를 (1,1)로 설정했을 때의 모습입니다.
단어사전을 살펴보니, 기존의 벡터라이저와 별 차이가 없는 것을 볼 수 있습니다.

 

 

 

 

 

vectorizer = TfidfVectorizer(ngram_range = (1, 2))
vectorizer.fit(text)
sorted(vectorizer.vocabulary_.items())
output:
[('back', 0),
 ('back to', 1),
 ('bought', 2),
 ('bought yellow', 3),
 ('go', 4),
 ('go back', 5),
 ('go to', 6),
 ('home', 7),
 ('home go', 8),
 ...
이번에는 ngram_range를 (1, 2)로 설정해보았습니다.
이는 단어묶음을 1개부터 2개까지로 세팅하겠다는 뜻입니다.
단어사전을 출력해보니, 단어 2개 묶음도 인덱스를 받은 것을 볼 수 있습니다.

사실 이번 포스팅은 이 ngram을 설명할고 하다가,
아예 기본적인 파라미터들을 다 설명해기로 하고 만든 것입니다 ^^

그럼 이 ngram_range를 사용하는 이유는 무엇일까요?
단어가 묶여야 비로소 의미를 가지는 것들이 있기 때문입니다.
예를들어 'very'라는 단어 하나만으로는 의미를 잘 알 수 없지만
'very good', 'very bad' 등과 같이 뒤에 오는 단어가 같이 오면 정확한 의미를 알 수 있습니다.
이렇게 묶여야 의미가 살아나는 녀석들 때문에, ngram_range를 사용한다고 볼 수 있습니다.

 

 

 

 

 

6. max_features 파라미터 알아보기

 

 

 

max_feature는 tf-idf vector의 최대 feature를 설정해주는 파라미터입니다.
해당 파라미터를 이해하려면, feature의 개념에 대해 아셔야 합니다.

머신러닝에서 feature란, 테이블의 컬럼에 해당하는 개념입니다.
또한 행렬의 열에 해당하는 것이기도 합니다.

TF-IDF 벡터는 단어사전의 인덱스만큼 feature를 부여받습니다.
그럼 구체적인 예시를 코드로 한 번 살펴보겠습니다.

 

 

 

 

 

text
output:
['I go to my home my home is very large',
 'I went out my home I go to the market',
 'I bought a yellow lemon I go back to home']
저희의 데이터는 위와 같이 생겼습니다.
모두 15 종류의 단어가 사용되었는데요. 그러므로 feature값은 15가 할당이 됩니다.

 

 

 

 

 

vectorizer = TfidfVectorizer()
a = vectorizer.fit_transform(text).toarray()
a.shape
output:
(3, 15)
벡터의 shape를 출력해보면 (3, 15)가 나옵니다.
이 15라는 값이 feature에 해당하는 것입니다. 이해가 되셨죠?

이제 max_features 파라미터를 통해,
이 feature값을 10으로 제한해보겠습니다.

 

 

 

 

 

vectorizer = TfidfVectorizer(max_features = 10)
a = vectorizer.fit_transform(text).toarray()
a.shape
output:
(3, 10)
feature의 값을 10으로 제한하니, 벡터의 모양이 변했습니다.
(3, 10)으로 shape가 바뀐 것을 확인하실 수 있을 겁니다.

해당 파라미터는 어느 상황에 사용하게 될까요?
자연어처리 데이터를 다루다보면, 엄청 긴 데이터를 만날 수 있습니다.

예를 들어서 다른 데이터의 단어들은 10정도를 가지는데,
어떤 데이터만 단어 종류가 100이 넘어간다고 하면,
이 100에 맞추어 feature의 수가 엄청 늘어나게 됩니다.
그럴 경우, 모델 성능이 저하될 수도 있는 것이죠.

그렇기 때문에 사전에 EDA를 통해서 데이터의 length를 확인하시는 것이 중요합니다.
그렇게 되면 통계적으로 엄청 정확한 기준은 아닐지라도,
대략 해당 파라미터의 스타트 지점을 유추해 볼 수 있을 것입니다.

 

 

 

 

 

 

 

7. 마무리

 

 

 

네 이렇게 오늘은 tf-idf의 중요한 파라미터 5가지에 대해서 알아보았습니다.
도움이 되셨다면 포스팅 하단에 하트 버튼 한 번씩 눌러주시는 것 잊지 마세요~!

지금까지 은공지능 공작소의 파이찬이었습니다.
감사합니다.

 

블로그 이미지

pychan

딥러닝에 관련된 시행착오, 사소하지만 중요한 것들, 가능한 모든 여정을 담았습니다.

댓글을 달아 주세요