Python における 7 つの主要なキーワード抽出アルゴリズムのベンチマーク

Python における 7 つの主要なキーワード抽出アルゴリズムのベンチマーク

私はキーワード抽出タスクのための効率的なアルゴリズムを探していました。 目標は、データ コーパスが急速に増加し、すでに数百万行に達しているため、効率的にキーワードを抽出し、抽出品質と実行時間のバランスをとることができるアルゴリズムを見つけることです。 アルゴリズムに対する私の主な要件の 1 つは、抽出されたキーワード自体が常に意味を持ち、文脈から外れても特定の意味を表現できることです。

[[437010]]

この記事では、2000 件のドキュメントのコーパスを使用して、いくつかのよく知られているキーワード抽出アルゴリズムをテストおよび実験します。

使用されるライブラリのリスト

私は研究のために以下のPythonライブラリを使用しました

前処理段階といくつかのヘルパー関数を支援するNLTK

  • レーキ
  • ヤケ
  • ペケ
  • キーバート
  • スペーシー

Pandas、Matplotlib、その他の一般的なライブラリ

実験手順

ベンチマークは次のように機能します

まず、テキスト データを含むデータセットをインポートします。 次に、各アルゴリズムごとにロジックを抽出する個別の関数を作成します。

algorithm_name(str: text) → [キーワード1, キーワード2, ..., キーワードn]

次に、コーパス全体からキーワードを抽出する機能を作成しました。

extract_keywords_from_corpus(アルゴリズム、コーパス) → {アルゴリズム、コーパスキーワード、経過時間}

次に、Spacy を使用して、キーワードがタスクに適している場合に true または false を返すマッチャー オブジェクトを定義します。

最後に、最終レポートを出力する関数ですべてをまとめます。

データセット

私はインターネットからの小さなテキスト データ セットを使用しています。これはサンプルです

  1. [ '前​​回の質問の続きです。これが結果です!\n'
  2. 「ヨーロッパのミード コンテスト?\nミードについてのフィードバックが欲しいのですが、ヨーロッパから米国にアルコールを輸送するのは違法なので、Mazer Cup に参加するのは私には無理です。(おそらく捕まったり起訴されたりはしないと思いますが、問題の公式記録が残ると、今後の市民権申請に支障が出る可能性があり、そのリスクを冒すつもりはありません)。\n\nヨーロッパのミード コンテストはありますか? または、少なくともミード カテゴリーへのエントリーを受け付け、経験豊富なミード審査員がいる可能性のある大規模なビール コンテストはありますか?' , 'オレンジ ローズマリー ブーチ\n' , 'ついに実現しました。休暇で出かけ、家に帰ったらカビが生えていました。\n' , '金曜日にロンドンでジェラート ショップをオープンするので、ずっとフレーバーの練習をしていました。これが最近の試みの 1 つです!\n' , "常温保存可能なホット ソースを作るためのリソースを持っている人はいませんか? 発酵させてから水に浸すか、圧力缶に入れるか?\n新鮮な唐辛子が何十本もありますホットソースを作るのに使いたいのですが、最終的な目標はレシピをカスタマイズしてアメリカ中の友達に送ることです。缶詰にするのが一番いい方法だと思うのですが、詳しい情報があまり見つかりません。何かアドバイスはありますか?ワインフィルターウォーターフィルター実際的な違いですか?\nどちらも使えるか知りたいです'、'最高のカスタードベースはですか?\nカルバーズフローズンカスタード似た味のレシピを持っている人はいますか? '、'カビ?\n'

ほとんどが食品に関するものです。アルゴリズムをテストするために、2000 件のドキュメントのサンプルを使用します。

一部のアルゴリズムの結果はストップワードと句読点に基づいているため、テキストはまだ前処理されていません。

アルゴリズム

キーワード抽出関数を定義しましょう。

  1. #関数BERTを開始する
  2. bert = キーBERT()
  3. # 1. レーキ
  4. def rake_extractor(テキスト):
  5. 「」 「
  6. Rakeを使用してテキストから上位5つのキーワードを抽出します
  7. 引数: テキスト (str)
  8. 戻り値:キーワードリスト(リスト)
  9. 「」 「
  10. r = レーキ()
  11. r.extract_keywords_from_text(テキスト)
  12. r.get_ranked_phrases()[:5]を返す
  13. # 2. ヤケ
  14. def yake_extractor(テキスト):
  15. 「」 「
  16. YAKEを使用してテキストから上位5つのキーワードを抽出します
  17. 引数: テキスト (str)
  18. 戻り値:キーワードリスト(リスト)
  19. 「」 「
  20. キーワード = yake.KeywordExtractor(lan= "en" 、n=3、windowsSize=3、 top =5).extract_keywords(テキスト)
  21. 結果 = []
  22. キーワードscored_keywordsの場合:
  23. scored_keywordsキーワード:
  24. if isinstance(キーワード, str):
  25. results.append(キーワード)
  26. 結果を返す
  27. # 3. ポジションランク
  28. def position_rank_extractor(テキスト):
  29. 「」 「
  30. PositionRankを使用してテキストから上位5つのキーワードを抽出します
  31. 引数: テキスト (str)
  32. 戻り値:キーワードリスト(リスト)
  33. 「」 「
  34. #グラフ出現する有効品詞定義する
  35. pos = { '名詞' '固有名詞' '形容詞' '副詞' }
  36. 抽出子 = pke.unsupervised.PositionRank()
  37. extractor.load_document(テキスト、言語= 'en' )
  38. extractor.candidate_selection(pos=pos, 最大単語数=5)
  39. # 4.合計を使用して候補に重み付けする 彼らの単語スコアは
  40. #単語位置応じてランダムウォークを使用して計算
  41. #ドキュメント内のノード。グラフでは、ノードは単語(名詞 
  42. #形容詞のみ  
  43. # 3 単語。
  44. extractor.candidate_weighting(ウィンドウ=3、pos=pos)
  45. # 5. 最もスコアの高い5つの候補をキーフレーズとして取得する
  46. キーフレーズ = extractor.get_n_best(n=5)
  47. 結果 = []
  48. キーフレーズ内のscored_keywordsの場合:
  49. scored_keywordsキーワード:
  50. if isinstance(キーワード, str):
  51. results.append(キーワード)
  52. 結果を返す
  53. # 4. シングルランク
  54. def single_rank_extractor(テキスト):
  55. 「」 「
  56. SingleRankを使用してテキストから上位5つのキーワードを抽出します
  57. 引数: テキスト (str)
  58. 戻り値:キーワードリスト(リスト)
  59. 「」 「
  60. pos = { '名詞' '固有名詞' '形容詞' '副詞' }
  61. 抽出器 = pke.unsupervised.SingleRank()
  62. extractor.load_document(テキスト、言語= 'en' )
  63. 抽出子.候補選択(pos=pos)
  64. extractor.candidate_weighting(ウィンドウ=3、pos=pos)
  65. キーフレーズ = extractor.get_n_best(n=5)
  66. 結果 = []
  67. キーフレーズ内のscored_keywordsの場合:
  68. scored_keywordsキーワード:
  69. if isinstance(キーワード, str):
  70. results.append(キーワード)
  71. 結果を返す
  72. # 5. マルチパートランク
  73. def multipartite_rank_extractor(テキスト):
  74. 「」 「
  75. MultipartiteRankを使用してテキストから上位5つのキーワードを抽出します
  76. 引数: テキスト (str)
  77. 戻り値:キーワードリスト(リスト)
  78. 「」 「
  79. 抽出器 = pke.unsupervised.MultipartiteRank()
  80. extractor.load_document(テキスト、言語= 'en' )
  81. pos = { '名詞' '固有名詞' '形容詞' '副詞' }
  82. 抽出子.候補選択(pos=pos)
  83. # 4. 多部グラフを構築しランダムウォークを使用して候補をランク付けする。
  84. # アルファは重み調整メカニズムを制御します  
  85. # しきい値/メソッド パラメータ。
  86. extractor.candidate_weighting(アルファ=1.1、しきい値=0.74、方法= '平均' )
  87. キーフレーズ = extractor.get_n_best(n=5)
  88. 結果 = []
  89. キーフレーズ内のscored_keywordsの場合:
  90. scored_keywordsキーワード:
  91. if isinstance(キーワード, str):
  92. results.append(キーワード)
  93. 結果を返す
  94. # 6. トピックランク
  95. def topic_rank_extractor(テキスト):
  96. 「」 「
  97. TopicRankを使用してテキストから上位5つのキーワードを抽出します
  98. 引数: テキスト (str)
  99. 戻り値:キーワードリスト(リスト)
  100. 「」 「
  101. 抽出子 = pke.unsupervised.TopicRank()
  102. extractor.load_document(テキスト、言語= 'en' )
  103. pos = { '名詞' '固有名詞' '形容詞' '副詞' }
  104. 抽出子.候補選択(pos=pos)
  105. 抽出子.候補の重み付け()
  106. キーフレーズ = extractor.get_n_best(n=5)
  107. 結果 = []
  108. キーフレーズ内のscored_keywordsの場合:
  109. scored_keywordsキーワード:
  110. if isinstance(キーワード, str):
  111. results.append(キーワード)
  112. 結果を返す
  113. # 7. キーバート
  114. def keybert_extractor(テキスト):
  115. 「」 「
  116. KeyBERTを使用してテキストから上位5つのキーワードを抽出します
  117. 引数: テキスト (str)
  118. 戻り値:キーワードリスト(リスト)
  119. 「」 「
  120. キーワード = bert.extract_keywords(テキスト、キーフレーズnグラム範囲=(3, 5)、ストップワード= "英語" 、トップn=5)
  121. 結果 = []
  122. キーワードscored_keywordsの場合:
  123. scored_keywordsキーワード:
  124. if isinstance(キーワード, str):
  125. results.append(キーワード)
  126. 結果を返す

各抽出機能はテキストを入力として受け取り、キーワードのリストを返します。使い方はとても簡単です。

注: 何らかの理由により、関数の外部ですべての抽出オブジェクトを初期化することはできません。これを実行するたびに、TopicRank と MultiPartiteRank でエラーが発生します。パフォーマンスの点では完璧ではありませんが、ベンチマークでは十分な成果が得られます。

pos = {'NOUN', 'PROPN', 'ADJ', 'ADV'} を渡すことで、許容される文法パターンを制限しました。これを Spacy と組み合わせることで、ほぼすべてのキーワードが人間の言語の観点から選択されるようになります。 また、キーワードをより具体的にし、一般的になりすぎないようにするために、キーワードには 3 つの単語を含める必要があります。

コーパス全体からキーワードを抽出する

ここで、いくつかの情報を出力しながら、コーパス全体に単一の抽出器を適用する関数を定義しましょう。

  1. def extract_keywords_from_corpus(抽出器、コーパス):
  2. "" "この関数は、抽出器を使用してドキュメントのリストからキーワードを取得します" ""  
  3. extractor_name = extractor.__name__. replace ( "_extractor" , "" )
  4. logging.info(f "{extractor_name} でキーワード抽出を開始しています" )
  5. コーパス_kws = {}
  6. 開始 =時間.時間()
  7. #logging.info(f "タイマーが開始されました。" ) < -- タイマーの開始を出力する場合は、コメントを解除します 
  8. idxの場合、text in tqdm(enumerate(corpus), desc = "コーパスからキーワードを抽出しています..." ):
  9. corpus_kws[idx] = 抽出子(テキスト)
  10. 終了=時間.時間()
  11. #logging.info(f "タイマーが停止しました。" ) < -- タイマーの終了を出力する場合は、コメントを解除します 
  12. 経過時間 = time .strftime( "%H:%M:%S" , time .gmtime(終了- 開始))
  13. logging.info(f "経過時間: {elapsed}" )
  14.  
  15. 戻り値{ "アルゴリズム" : extractor.__name__,
  16. "コーパス_kws" : コーパス_kws,
  17. "elapsed_time" : 経過時間}

この関数は、受信した抽出データを、タスクの実行にかかった時間などの一連の有用な情報を含む辞書に結合して、後でレポートを生成しやすくします。

構文マッチング関数

この関数は、抽出器によって返されるキーワードが常に(ほぼ?)意味を成すことを保証します。 例えば、

最初の 3 つのキーワードは、単独でも意味を成すものであることがはっきりとわかります。キーワードの意味を理解するためにこれ以上の情報は必要ありませんが、4番目は意味がないので、これをできるだけ避ける必要があります。

Spacy と Matcher オブジェクトは、これを実現するのに役立ちます。 キーワードを受け取り、定義されたパターンが一致する場合に True または False を返す match 関数を定義します。

  1. def match(キーワード):
  2. "" "この関数は、キーワードのリストが特定の POS パターンに一致するかどうかを確認します" ""  
  3. パターン = [
  4. [{ 'POS' : 'PROPN' }, { 'POS' : 'VERB' }, { 'POS' : 'VERB' }],
  5. [{ 'POS' : '名詞' }, { 'POS' : '動詞' }, { 'POS' : '名詞' }],
  6. [{ 'POS' : '動詞' }, { 'POS' : '名詞' }],
  7. [{ 'POS' : 'ADJ' }, { 'POS' : 'ADJ' }, { 'POS' : '名詞' }],
  8. [{ 'POS' : '名詞' }, { 'POS' : '動詞' }],
  9. [{ 'POS' : 'PROPN' }, { 'POS' : 'PROPN' }, { 'POS' : 'PROPN' }],
  10. [{ 'POS' : 'PROPN' }, { 'POS' : 'PROPN' }, { 'POS' : 'NOUN' }],
  11. [{ 'POS' : 'ADJ' }, { 'POS' : '名詞' }],
  12. [{ 'POS' : 'ADJ' }, { 'POS' : '名詞' }, { 'POS' : '名詞' }, { 'POS' : '名詞' }, { 'POS' : '名詞' }],
  13. [{ 'POS' : 'PROPN' }, { 'POS' : 'PROPN' }, { 'POS' : 'PROPN' }, { 'POS' : 'PROPN' }, { 'POS' : 'ADV' }, { 'POS' : 'PROPN' }],
  14. [{ 'POS' : 'PROPN' }, { 'POS' : 'PROPN' }, { 'POS' : 'PROPN' }, { 'POS' : 'VERB' }],
  15. [{ 'POS' : 'PROPN' }, { 'POS' : 'PROPN' }],
  16. [{ 'POS' : '名詞' }, { 'POS' : '名詞' }],
  17. [{ 'POS' : 'ADJ' }, { 'POS' : 'PROPN' }],
  18. [{ 'POS' : 'PROPN' },{ 'POS' : 'ADP' },{ 'POS' : 'PROPN' }],
  19. [{ 'POS' : '属性' }, { 'POS' : '形容詞' }, { 'POS' : '名詞' }],
  20. [{ 'POS' : '属性' }, { 'POS' : '動詞' }, { 'POS' : '名詞' }],
  21. [{ 'POS' : '名詞' }, { 'POS' : 'ADP' }, { 'POS' : '名詞' }],
  22. [{ 'POS' : 'PROPN' }, { 'POS' : '名詞' }, { 'POS' : 'PROPN' }],
  23. [{ '品詞' : '動詞' }, { '品詞' : '副詞' }],
  24. [{ 'POS' : '属性' }, { 'POS' : '名詞' }],
  25. ]
  26. マッチャー = Matcher(nlp.vocab)
  27. matcher. add ( "pos-matcher" , パターン)
  28. #スペースオブジェクトを作成する
  29. doc = nlp(キーワード)
  30. # 一致した項目を反復処理する
  31. マッチ = マッチャー(doc)
  32. # 一致する場合 空でない場合は少なくとも一致が見つかったことを意味します
  33. len(matches) > 0の場合:
  34. 戻る 真実 
  35. 戻る 間違い 

ベンチマーク機能

もうすぐそこに着きます。 これは、スクリプトを起動して結果を収集する前の最後のステップです。

コーパスとデータをシャッフルするためのブール値を受け取るベンチマーク関数を定義します。 各抽出器に対して、

extract_keywords_from_corpus 関数は、この抽出器の結果を含む辞書を返します。 値をリストに保存します。

リスト内の各アルゴリズムについて、計算する。

  • 抽出されたキーワードの平均数
  • 一致するキーワードの平均数
  • 見つかった一致の平均数を操作の実行にかかった時間で割ったスコアを計算します。

すべてのデータを Pandas DataFrame に保存し、.csv としてエクスポートします。

  1. get_sec(time_str)を定義します。
  2. 時間から秒数を取得します。」 「」  
  3. h, m, s = time_str.split( ':' )
  4. 戻る 整数(h) * 3600 +整数(m) * 60 +整数(s)
  5. def benchmark(コーパス、シャッフル= True ):
  6. "" "この関数は、キーワード抽出アルゴリズムのベンチマークを実行します" ""  
  7. logging.info( "ベンチマークを開始しています...\n" )
  8.  
  9. # コーパスをシャッフルする
  10. シャッフルの場合:
  11. ランダムシャッフル(コーパス)
  12. #コーパスからキーワードを抽出
  13. 結果 = []
  14. 抽出器 = [
  15. rake_extractor、
  16. yake_extractor、
  17. トピックランク抽出器、
  18. ポジションランク抽出器、
  19. シングルランク抽出器、
  20. マルチパートランク抽出器、
  21. キーバート抽出器、
  22. ]
  23. 抽出器内の抽出器の場合:
  24. 結果 = extract_keywords_from_corpus(抽出器、コーパス)
  25. 結果.append(結果)
  26. #抽出されたキーワード平均数を計算する
  27. 結果結果として:
  28. len_of_kw_list = []
  29. result[ "corpus_kws" ] .values ​​()内のkwsについて:
  30. len_of_kw_list.append(len(kws))
  31. 結果[ "文書あたりの平均キーワード数" ] = np.mean(len_of_kw_list)
  32. # 一致するキーワード
  33. 結果結果として:
  34. result[ "corpus_kws" ].items()idx、kwsの場合:
  35. マッチ結果 = []
  36. kws内のkwの場合:
  37. match_results.append(match(kw))
  38. 結果[ "corpus_kws" ][idx] = 一致結果
  39. #一致したキーワード平均数を計算
  40. 結果結果として:
  41. 一致するキーワードリストの長さ = []
  42. result[ "corpus_kws" ].items()idx、kwsの場合:
  43. len_of_matching_kws_list.append(len([kw for kw in kws if kw]))
  44. 結果[ "ドキュメントあたりの平均一致キーワード数" ] = np.mean(一致するキーワードリストの長さ)
  45. # 一致するキーワード平均パーセンテージを計算し、小数点第 2 位を四捨五入します
  46. 結果[ "一致したキーワードの平均割合" ] = round(結果[ "文書あたり一致したキーワードの平均割合" ] / 結果[ "文書あたり一致したキーワードの平均割合" ], 2)
  47.  
  48. #一致キーワード平均割合 経過時間
  49. 結果結果として:
  50. 経過秒数 = get_sec(結果[ "経過時間" ]) + 0.1
  51. #経過時間基づいてスコア重み付けする
  52. 結果[ "パフォーマンススコア" ] = round(結果[ "ドキュメントあたりの平均一致キーワード数" ] / 経過秒数、2)
  53.  
  54. # corpus_kwを削除
  55. 結果結果として:
  56. del結果[ "corpus_kws" ]
  57. #結果データフレームを作成する
  58. df = pd.DataFrame(結果)
  59. df.to_csv( "results.csv" インデックス= False )
  60. logging.info( "ベンチマークが終了しました。結果は results.csv に保存されました" )
  61. リターンDF

結果

  1. 結果 = ベンチマーク(テキスト[:2000]、シャッフル= True )

生成されたレポートはこちら

これを視覚化してみましょう:

私たちが定義したスコアリング式によれば(

avg_matched_keywords_per_document/time_elapsed_in_seconds)、Rake は 2 秒で 2000 件のドキュメントを処理し、精度は KeyBERT ほど良くないものの、時間要因で勝者になります。

正確さだけを考えれば、計算は

avg_matched_keywords_per_documentとavg_keywords_per_documentの比率から次の結果が得られます。

精度の観点から見ると、Rake のパフォーマンスもかなり優れています。時間を考慮しなければ、KeyBERT は間違いなく最も正確で意味のあるキーワード抽出アルゴリズムになるでしょう。 Rake は精度では 2 位にランクされましたが、大きく遅れていました。

精度が必要な場合は、KeyBERT が間違いなく第一の選択肢です。速度が必要な場合は、Rake が間違いなく第一の選択肢です。これは、速度が速く、精度も許容範囲内であるためです。

<<:  組み込みアルゴリズム: ビッグデータ可変長ストレージアルゴリズム

>>:  「Nuwa」のAIバージョンが登場!テキストから画像とビデオの生成: 8 つのタスクに 1 つのモデル

ブログ    

推薦する

BluePrismが中国市場に参入し、RPA業界に新たな道を開く

RPA は、その幅広い適用性、無制限のシナリオへの適応性、既存の情報システムを損なわない親和性、AI...

わずか数分で 8 文字のパスワードを解読するにはどうすればよいでしょうか?

翻訳者 |ブガッティレビュー | Chonglouセキュリティの専門家は長い間、オンラインアカウント...

なぜスパムメールがこんなに多いのでしょうか? Redditの男が機械学習の残酷な現実を暴露

近年、AIのトレンドは高まるばかりで、毎年大規模な機械学習カンファレンスが盛んに開催されており、誰も...

Sogouの技術者が在宅勤務中にサーバーを誤操作し、誤って「マグニチュード12の地震警報」を発令した。

2月3日午前11時頃、捜狗入力法の一部のユーザーは、河北省興隆市でマグニチュード12の地震が発生し...

AI陣営を理解するためのチャート: AIを学んで間違った側に立つと自滅につながる可能性がある

AIにはさまざまな手法があります。私たちがよく知っている「5大流派」に加え、この記事の著者はAIのさ...

我が国の人工知能市場の規模は2022年に3705億元に達すると推定されている。

人工知能は、機械を通じて人間の思考と意思決定をシミュレートすることに重点を置いたコンピューターサイエ...

...

...

...

ナレッジグラフの紹介

1.1 ナレッジグラフの開発履歴ナレッジグラフは 1950 年代に始まり、大きく 3 つの開発段階に...

AIと自動化革命に備える10の方法

人工知能と自動化はもはやSFの世界の話ではなく、ビジネスの世界と消費者の世界の両方で非常に現実的かつ...

...

この AI ツールは最近、大騒ぎになっています!試してみますか?

Stable Diffusionをプレイしたことがある人は多いと思います。この製品はmjdjour...

機械学習とディープラーニングの違いは何ですか?

機械学習とディープラーニングのアルゴリズムフローついに人工知能研究僧に入学しました。機械学習とディー...

ポピュラーサイエンス | TensorFlow.js から機械学習について学ぶ

フロントエンド開発者にとって、機械学習を理解するのは難しい場合があります。私は機械学習を勉強し始めて...