質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.52%

  • Python 3.x

    6350questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • 自然言語処理

    118questions

    自然言語処理は、日常的に使用される自然言語をコンピューターに処理させる技術やソフトウェアの総称です。

GemsimのTfidfModelの返り値について

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 111

YuheiFujioka

score 10

ある文章についてgensimのTfidfModelを用いたところ、
(単語id,tfidf値)のリストを得ることが出来たのですが、一部単語id(単語'theのid)は含まれていませんでした。
リファレンスも確認しましたが、原因が分からず困っています。よろしくお願いします。
リファレンス : https://radimrehurek.com/gensim/models/tfidfmodel.html

#解析対象の文章
corpus = [
    "The elephant sneezed at the sight of potatoes.",
    "Bats can see via echolocation. See the bat sight sneeze!",
    "Wondering, she opened the door to the studio.",
]


####import libraries
import nltk
nltk.download('punkt')
import string
import numpy as np
import gensim
import pandas as pd


####単語をtoken化する関数1
def tokenize(texts):

    stem = nltk.stem.SnowballStemmer('english')
    stem_list = []

    for text in texts:
        text = text.lower()

        for token in nltk.word_tokenize(text):
            if  token in string.punctuation:
                continue
            stem_list.append(stem.stem(token))
    return stem_list


####単語をtoken化する関数2
def tokenize2(text):

    stem = nltk.stem.SnowballStemmer('english')
    stem_list = []

    for token in nltk.word_tokenize(text):
        if  token in string.punctuation:
            continue

        stem_list.append(stem.stem(token))

    return stem_list


###tf-idfを計算する関数
def tf_idf_gensim(corpus):

    dictionary = [tokenize2(doc) for doc in corpus]

    print('dictionary : ',dictionary)

    lexicon = gensim.corpora.Dictionary(dictionary)
    id2token = lexicon.token2id
    token2id = {v:k for k,v in lexicon.token2id.items()}
    print(" token2id : ",token2id)

    corpus_ = [lexicon.doc2bow(tokenize2(doc)) for doc in corpus]
    print('token : id in corpus_[0]',[(token2id[a],b) for a, b in corpus_[0]])

    tfidf = gensim.models.TfidfModel(corpus= corpus_ ,dictionary=lexicon,normalize=True)

    return [(token2id[token_id],tfidf_value)for token_id, tfidf_value in tfidf[corpus_[0]]]


print(tf_idf_gensim(corpus))

------出力結果------------------------------
dictionary :  [['the', 'eleph', 'sneez', 'at', 'the', 'sight', 'of', 'potato'], ['bat', 'can', 'see', 'via', 'echoloc', 'see', 'the', 'bat', 'sight', 'sneez'], ['wonder', 'she', 'open', 'the', 'door', 'to', 'the', 'studio']]

token2id :  {0: 'at', 1: 'eleph', 2: 'of', 3: 'potato', 4: 'sight', 5: 'sneez', 6: 'the', 7: 'bat', 8: 'can', 9: 'echoloc', 10: 'see', 11: 'via', 12: 'door', 13: 'open', 14: 'she', 15: 'studio', 16: 'to', 17: 'wonder'}

tfidfに代入前

(token : id ) in corpus_[0]: [('at', 1), ('eleph', 1), ('of', 1), ('potato', 1), ('sight', 1), ('sneez', 1), ('the', 2)]

tfidfに代入後->theがなくなっている

[('at', 0.4837965208957426), ('eleph', 0.4837965208957426), ('of', 0.4837965208957426), ('potato', 0.4837965208957426), ('sight', 0.17855490118826325), ('sneez', 0.17855490118826325)]

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • hayataka2049

    2018/06/20 15:52 編集

    質問文の編集画面を開き、コードの部分を選択して<code>を押し、「ここに言語を入力」を「python」に書き換えてください。コピペしてそのまま実行できる形で掲載していただくのが理想です

    キャンセル

  • YuheiFujioka

    2018/06/20 15:57

    ご指摘ありがとうございます。勉強になりました。

    キャンセル

回答 1

checkベストアンサー

+1

tfidf[corpus_[0]]

これはけっきょく

tfidf.__getitem__(corpus_[0])

に変換されるのですが(これはpythonの仕様。[]で値を参照しているときは内部的には__getitem__を呼んでいる)、gensim.models.TfidfModel.__getitem__の仕様を調べると

__getitem__(bow, eps=1e-12)
Get tf-idf representation of the input vector and/or corpus.

bow : {list of (int, int), iterable of iterable of (int, int)}
Input document or copus in BoW format.
eps : float
Threshold value, will remove all position that have tfidf-value less than eps.
Returns:    
vector (list of (int, float)) – TfIdf vector, if bow is document OR
TransformedCorpus – TfIdf corpus, if bow is corpus.

gensim: models.tfidfmodel – TF-IDF model

となっており、epsが怪しいのでeps=-1で実行するようにしてみました(こうすると絶対にスレッショルドにかからない。負の値を指定して問題ないことは実装を見て確認しています)。

    return [(token2id[token_id],tfidf_value)for token_id, tfidf_value in tfidf.__getitem__(corpus_[0], eps=-1)]

するとこのような結果が得られました。

[('at', 0.4837965208957426), ('eleph', 0.4837965208957426), ('of', 0.4837965208957426), ('potato', 0.4837965208957426), ('sight', 0.17855490118826325), ('sneez', 0.17855490118826325), ('the', 0.0)]

0.0かぁ。とすると、不幸にもlogの中身が1になったのかなぁ。と思って、色々見に行くと

gensim: models.tfidfmodel – TF-IDF model #gensim.models.tfidfmodel.TfidfModel
(数式の画像とオプションのwglobalを見る)

gensim: models.tfidfmodel – TF-IDF model #gensim.models.tfidfmodel.df2idf
(デフォルトのwglobalに使われている関数)

とりあえずadd=0.0がデフォルトなのはよくないので、
参考(一番最後の行):
idf(inverse documet frequency)について

こうしました。

    corpus_ = [lexicon.doc2bow(tokenize2(doc)) for doc in corpus]
    print('token : id in corpus_[0]',[(token2id[a],b) for a, b in corpus_[0]])

    def wglobal(docfreq, totaldocs):
        return gensim.models.tfidfmodel.df2idf(docfreq, totaldocs, add=1.0)

    tfidf = gensim.models.TfidfModel(corpus= corpus_ ,dictionary=lexicon, normalize=True, wglobal=wglobal)

    return [(token2id[token_id],tfidf_value)for token_id, tfidf_value in tfidf[corpus_[0]]]

事なきを得ました。

[('at', 0.4323167186448522), ('eleph', 0.4323167186448522), ('of', 0.4323167186448522), ('potato', 0.4323167186448522), ('sight', 0.26507378242266566), ('sneez', 0.26507378242266566), ('the', 0.3344858724443731)]

疲れた、というかgensimのデフォルト設定がひどい・・・(全文書に出てくるような単語が消えても誰も困らないのかもしれないけど・・・)

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/20 17:10

    ありがとうございます!
    wgglobalが発散しないうように、addの値をデフォルトから変更してあげる必要があったのですね。wgglobalの部分も目を通していましたが、読み込みが足りてませんでした。

    キャンセル

  • 2018/06/20 17:17

    発散とはちょっと違いますね。別にinfに行く訳ではないので(log(1)=0というだけ。theは全文書に出現するのでlogの中身が3/3に・・・)

    キャンセル

  • 2018/06/20 17:31 編集

    ありがとうございます。ご指摘頂いた内容を理解しました。
    __getitem__(bow, eps=1e-12)のepsを変更するか、wgklobalのaddを変更するようにします。

    どちらを変更するかで、tf-idfの値の考え方(※)が変わるので、適宜使い分けたいと思います。

    ※文書間における単語のレアリティを重視する場合は、epsを変更。
     単語の出現頻度を重視する場合は、addを変更。

    キャンセル

  • 2018/06/20 17:34

    epsを変更したところで、どの文書でもtheのtfidf値は0になるだけなので、要らない気がします・・・
    あまりオリジナルのidf値を損ないたくなければ、小さなの値をaddに渡すという手もあります。全文書に出現する単語は重み0.1にするとか。もちろん他の単語のidfがどれくらいになるかを見て相対的に決めないといけないので、けっこう難しいのですが

    キャンセル

  • 2018/06/20 17:41

    たとえば100文書あってうち99文書に出現する単語のidfはadd=0として0.014くらいになるはずなので、100文書中100文書に出現する単語のidfはこれより小さければよしとします。0.01とかにしておけば大勢に影響はなく、今回のような問題も避けられるはずです

    キャンセル

  • 2018/06/20 17:46

    ありがとうございます。文書の数から、addの値を決めるようにします。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.52%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • Python 3.x

    6350questions

    Python 3はPythonプログラミング言語の最新バージョンであり、2008年12月3日にリリースされました。

  • 自然言語処理

    118questions

    自然言語処理は、日常的に使用される自然言語をコンピューターに処理させる技術やソフトウェアの総称です。