Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sent_to_word_contexts_matrix 내 dynamic_weight 관련 문제 #137

Open
smbslt3 opened this issue Oct 2, 2021 · 6 comments
Open

sent_to_word_contexts_matrix 내 dynamic_weight 관련 문제 #137

smbslt3 opened this issue Oct 2, 2021 · 6 comments

Comments

@smbslt3
Copy link

smbslt3 commented Oct 2, 2021

from soynlp.vectorizer import sent_to_word_contexts_matrix

x, idx2vocab = sent_to_word_contexts_matrix(
    corpus,
    windows=3,
    min_tf=10,
    tokenizer=tokenizer, # (default) lambda x:x.split(),
    dynamic_weight=False,
    verbose=True
)

위 코드와 https://lovit.github.io/nlp/representation/2018/09/05/glove/ 게시물을 참고하여 Glove를 학습시키는데,
dynamic_weight를 False로 하는 경우에

glove.fit(x.tocoo() 에서
ValueError: Buffer dtype mismatch, expected 'double' but got 'long' 라는 에러가 발생합니다.

동일한 데이터/코드 상태에서 dynamic_weight=True인 경우, glove-python 자체 Corpus()를 통해 학습시키는 경우
문제 없이 학습 및 동작합니다.

@mino-park7
Copy link

안녕하세요.

dynamic_weight=True인 경우 word_context 계산 시 나눗셈이 있어서, sent_to_word_contexts_matrix가 만들어질 때 자동으로 np.double로 캐스팅이 되는 것 같습니다.
dynamic_weight=False면, 나눗셈이 없어, np.int로 dtype이 결정되어 ValueError가 생기는 듯 합니다.

일단 사용자분의 코드에서 glove.fit(x.astype(np.double).tocoo()) 로 사용하시면 에러 없이 사용가능 할 듯 합니다.

mino-park7 added a commit to mino-park7/soynlp that referenced this issue Oct 31, 2021
sent_to_word_contexts_matrix 에서 dynamic_weight=False시
contexts_matrix가 long type이 되어 GloVe에 fit 시 에러 발생가능한 부분 수정
mino-park7 added a commit to mino-park7/soynlp that referenced this issue Oct 31, 2021
fix lovit#137 contexts matrix dtype error
@smbslt3
Copy link
Author

smbslt3 commented Nov 6, 2021

안녕하세요
말씀해주신대로 코드를 수정하니 우선 코드가 실행은 됩니다.

그런데 제가 코퍼스의 크기에 따라 모델의 정확도가 어떻게 변하는지를 연구중인데,
soynlp를 통해 glove를 구현하는 방식(https://lovit.github.io/nlp/representation/2018/09/05/glove/) 을 사용한 경우
glove-python을 사용한 방법과 다른 결과가 나오네요

저는 이게 dynamic_weight의 문제일거라 생각해서 이를 False로 놓고 구현해봤는데,
dynamic_weight=False일 때의 결과가 glove-python의 결과보다는 dynamic_weight=True의 결과와 유사합니다.
좀 더 구체적으로 말하자면, 코퍼스의 크기가 일정 수준 이상으로 커졌을 때, soynlp를 이용한 구현에서는 오히려 정확도가 감소합니다. glove-python으로는 감소 없이 단조증가 하구요.

glove-python 구현체의 문제가 없다는 전제 하에서, soynlp를 이용한 co-occrence matrix 구현에 문제가 있는 것 같은데,
이에 대한 조언을 받을 수 있을까요?

안녕하세요.

dynamic_weight=True인 경우 word_context 계산 시 나눗셈이 있어서, sent_to_word_contexts_matrix가 만들어질 때 자동으로 np.double로 캐스팅이 되는 것 같습니다. dynamic_weight=False면, 나눗셈이 없어, np.int로 dtype이 결정되어 ValueError가 생기는 듯 합니다.

일단 사용자분의 코드에서 glove.fit(x.astype(np.double).tocoo()) 로 사용하시면 에러 없이 사용가능 할 듯 합니다.

@mino-park7
Copy link

mino-park7 commented Nov 6, 2021

안녕하세요.

@smbslt3 님, 혹시 실험 진행하신 코드를 올려주실 수 있을까요?

co-occurrence matrix 구현상의 문제점은 보이지 않습니다.

dynamic weight의 경우에도, 단어간 거리에 따른 가중치 차이를 주기 위한 방법의 하나일 뿐, 구현이 잘못된 것이 아닙니다.

corpus의 크기가 증가할 때 무조건 성능이 높아지는 것은 아닙니다. 일단 성능 측정을 결국 어떤 task로 하셨는지도 중요할 것 같고, soynlp의 co-occurrence matrix 구하는 코드를 보자면 tokenizer가 그냥 띄어쓰기를 가지고 split하는 것이기 때문에, 전처리 되지 않은 문서를 가지고 계산시 corpus가 커질수록 성능이 떨어질 가능성이 있습니다.

종합적인 판단을 위해서는 전체적인 실험세팅을 보아야 가능할 것 같습니다.

@smbslt3
Copy link
Author

smbslt3 commented Nov 8, 2021

안녕하세요

코드가 매우 길어 일부를 축약하여 colab 링크로 대체합니다 (데이터의 문제로 실행은 안되실겁니다)
코드 내부에서 build_emd_model함수를 보시면 됩니다
그 외의 코드는 거의 대부분 공유하고 있으며, train corpus를 만들 때 glove-python에는 mean_term-frequency를 정하는 옵션이 없어서
iterator(MyGloVeIter) 내부에서 수작업으로 제외처리를 했습니다

dynamic = True https://drive.google.com/file/d/1qUS5UBgcICJQT0EItcmTqf8vnicn-eft/view?usp=sharing
dynamic = False https://drive.google.com/file/d/1Jg8sZtGePKoQFuWHJ3aeZxhv6QnhJ_3L/view?usp=sharing
GloVe Banilla https://drive.google.com/file/d/1BnEEDY4wN5mRi8LhiJGQGQ3t4dCdzoiG/view?usp=sharing

세 개의 노트북에서 대부분의 코드(코퍼스 전처리, 시각화 등)는 공유하고 있습니다.
GloVe 외에도 word2vec과 fasttext 모델도 위 코드를 공유중인데, soynlp를 사용한 경우 외에는 예상 밖의 결과가 나오지는 않습니다.

soynlp를 사용해 구현한 경우(dynamic = True/False), 둘 다 코퍼스의 크기가 1N을 초과하면서 성능 증가치가 감소합니다.
1회 분석이라 감소 후 다시 증가하는 것으로 보이나, 사실 감소 이후 구간의 표준편차가 매우 커 정확도(F1-score)가 들쑥날숙 합니다.
그에 비해 glove-python을 사용한 코드는 시각화 결과와 마찬가지로 단조증가하며, 다른 종류의 corpus를 사용한 경우, epochs, window size, x_max 값을 바꾼 다양한 변화에도 모두 단조 증가하는 모습을 보였습니다.

저는 이 두 차이가 dynamic weight 때문이라 생각해서, 이를 False로 놓으면 구현체와 상관 없이 glove-python처럼 단조증가 하는 모습을 보일거라 예상했는데, 오히려 구현체에 따라 결과가 다르게 나왔습니다.

이미 tokenizing을 완료한 list를 입력으로 사용했기에, sent_to_word_contexts_matrixtokenizer값을
기본에서 입력값을 그대로 사용하는 것으로 변환했는데, 이 부분이 문제가 될까요?

@mino-park7
Copy link

안녕하세요

코드가 매우 길어 일부를 축약하여 colab 링크로 대체합니다 (데이터의 문제로 실행은 안되실겁니다) 코드 내부에서 build_emd_model함수를 보시면 됩니다 그 외의 코드는 거의 대부분 공유하고 있으며, train corpus를 만들 때 glove-python에는 mean_term-frequency를 정하는 옵션이 없어서 iterator(MyGloVeIter) 내부에서 수작업으로 제외처리를 했습니다

dynamic = True https://drive.google.com/file/d/1qUS5UBgcICJQT0EItcmTqf8vnicn-eft/view?usp=sharing dynamic = False https://drive.google.com/file/d/1Jg8sZtGePKoQFuWHJ3aeZxhv6QnhJ_3L/view?usp=sharing GloVe Banilla https://drive.google.com/file/d/1BnEEDY4wN5mRi8LhiJGQGQ3t4dCdzoiG/view?usp=sharing

세 개의 노트북에서 대부분의 코드(코퍼스 전처리, 시각화 등)는 공유하고 있습니다. GloVe 외에도 word2vec과 fasttext 모델도 위 코드를 공유중인데, soynlp를 사용한 경우 외에는 예상 밖의 결과가 나오지는 않습니다.

soynlp를 사용해 구현한 경우(dynamic = True/False), 둘 다 코퍼스의 크기가 1N을 초과하면서 성능 증가치가 감소합니다. 1회 분석이라 감소 후 다시 증가하는 것으로 보이나, 사실 감소 이후 구간의 표준편차가 매우 커 정확도(F1-score)가 들쑥날숙 합니다. 그에 비해 glove-python을 사용한 코드는 시각화 결과와 마찬가지로 단조증가하며, 다른 종류의 corpus를 사용한 경우, epochs, window size, x_max 값을 바꾼 다양한 변화에도 모두 단조 증가하는 모습을 보였습니다.

저는 이 두 차이가 dynamic weight 때문이라 생각해서, 이를 False로 놓으면 구현체와 상관 없이 glove-python처럼 단조증가 하는 모습을 보일거라 예상했는데, 오히려 구현체에 따라 결과가 다르게 나왔습니다.

이미 tokenizing을 완료한 list를 입력으로 사용했기에, sent_to_word_contexts_matrixtokenizer값을 기본에서 입력값을 그대로 사용하는 것으로 변환했는데, 이 부분이 문제가 될까요?

안녕하세요.

코드를 확인해보았는데, 실제 데이터가 어떻게 생겼는지 잘 모르겠어서 분석에 어려움이 있습니다.

일단 glove-python에서 제공하는 corpus.fit()도 co-occurrence matrix를 생성해 주는 함수이고, 이의 내부 구현은 dynamic = True와 같습니다. glove Corpus
다만, soynlp의 co-occurence를 구하는 함수는 sentence의 list를 받아 sentence를 word로 바꾸어 계산을 합니다.
즉, [sent1, sent2, ...] 가 있으면

for sent in sentences:
    for words in sent:
       XXX

와 같이 계산하므로 따로 tokenizer 부분이 있고, glove-python는 그냥 corpus 전체를 words로 바꾼 후 한번의 for문을 돌립니다.

이 두 방법의 차이를 논의해보자면, 길이 10이 되지않는 sentence 들이 많을 것이고, 특히 문장 양 끝에 있는 단어는 window에 잡히는 단어가 적을 것입니다. 그에 따른 차이가 성능에 영향을 미쳤을 수 있을 것입니다.

만약 space tokenizer를 사용하지 않고 mecab과 같은 pos tagger를 사용하신다면, 의미를 가지는 명사, 형용사 위주의 단어만을 word dict에 넣어서 쓰시고, windows를 줄이는 것을 추천 드립니다.

@smbslt3
Copy link
Author

smbslt3 commented Feb 25, 2022

안녕하세요 피드백이 늦었습니다.
말씀하신 부분 참고하여 다른 방식으로 구현을 해봤습니다.

  1. glove 라이브러리의 Corpus 기능 사용 (banilla glove)
  2. soynlp 사용, list of sentence + tokenizer 사용
  3. soynlp 사용, list of list of tokens + tokenizer 미사용 (tokenizer = lambda x: x)
  4. soynlp 사용, 전체 corpus를 하나의 string으로 묶어 입력, tokenizer 사용

위 네 경우를 테스트 한 결과, 2=3=4>1 의 결과가 나옵니다.
2=3은 이론적으로 같아야 하기 때문에(문장 입력 후 soynlp가 tokenizing / tokenizing이 완료된 데이터를 입력) 문제가 없는데
4번은 1번의 케이스(전체 corpus를 하나로 처리해 window 양 끝의 token이 임의의 neighbor에 의해 학습되도록 처리)를 모사한 구현인데
1번과 유사하지 않고 2,3번과 거의 동일한 결과가 나왔습니다.

추정컨데, glove 라이브러리에서 window는 특정 단어에서 window+1 크기 만큼 뒤의 단어까지 학습하는, 즉, 그 자체가 window의 범위로 보이는데 (window=10일 때, 실제 window = 10+1)

            window_stop = int_min(i + window_size + 1, wordslen)

soynlp의 경우, window 파라메터로 넣어주는 값이 학습하는 단어를 포함한 양 옆의 범위가 아니라 2k+1로 표현 되는 윈도우 범위 수식의 k에 해당하는 것 같습니다(window=10일 때, 실제 window = 20+1)

            # left_contexts
            for w in range(windows):
                j = i - (w + 1)
                if j < 0 or not (words[j] in vocab2idx):
                    continue
                word2contexts[word][words[j]] += weight[w]

            # right_contexts
            for w in range(windows):
                j = i + w + 1
                if j >= n or not (words[j] in vocab2idx):
                    continue
                word2contexts[word][words[j]] += weight[w]

즉, glove는 window=10을 넣었을 때, 학습하는 단어 양 옆 5개까지 학습하고
soynlp는 window=10을 넣었을 때, 학습하는 단어 양 옆 10개까지 학습하는 것 같습니다.
동일 window 세팅에서 학습 시간이 2배 이상 걸리는 것도 이 부분이 의심이 갑니다(soynlp를 사용할 때, window를 절반으로 줄이면 학습 시간이 glove banilla의 1.4배 정도로 줄어듭니다)

테스트 해본 결과, soynlp에서 window를 절반으로 줄였을 때, glove banilla와 완벽하게 같아지진 않지만 window를 그대로 썼을 때보다는 glove banilla의 결과에 가까운 결과가 나왔습니다
이 부분 혹시 확인해주실 수 있나요?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants