📈

文書要約の評価指標QAGSにおいて、2種の回答の類似度をどう測るか

2022/08/30に公開
3

はじめに

こんばんは。今回は、以前も書いたQAGSの話です。文書要約の性能をより的確に評価するための工夫を紹介します。

題目

  • 前置き
  • 提起した問題に対する答え
  • 実験

前置き

QAGS

以前、文書要約モデルの性能の新しい評価指標QAGSについての記事を書きました。

https://zenn.dev/ty_nlp/articles/aaad1aec70d53e

QAGSは、参照文(要約の正解例となる文章)なしで候補文(要約対象の文書を要約した結果の文章)を評価できるのが主な利点です。また、他の評価指標に比べて人間による評価との相関が比較的大きいのも強みです。詳しくは、上の記事をご覧ください。

QAGSの内容の理解を前提として、話を進めます。

問題提起

上の記事で書いた通り、候補文から生成した質問文に対して、2種類の回答を出力します。そして、双方の回答の類似度を測定します。
このときに問題になるのは、どうやってその類似度を測定するかです。というのは、この場面においても、なるべく人間による評価に沿った類似度を算出することが重要だからです。それができないと、候補文の良し悪しの評価がブレやすくなります。

類似度の測定の具体例は、ROUGEやCIDErによって、双方の回答間の単語の重複ぐあいを見ることです。しかしこれらの評価指標は、以前から各所で論じられてきたように、人間による評価との相関が比較的弱いです。
それでは、BERTScoreを用いるのはどうか。ROUGEやCIDErに比べると、人間による評価との相関は大きくなる傾向があります。

この記事では、この問題に対する答えを後述します。

対象の言語

この記事では、文書要約の対象の言語として日本語を前提とします。まずは日本語の場合に興味があったためです。
ただし、この記事に書いた考え方は、他の言語の場合にも応用できると思います。

提起した問題に対する答え

現時点での持論

2種の回答の類似度を測定する際、BERTScoreを改変したものを指標として用いることです。
一例として、2種の回答の類似度を次の式によって測定します。

上の式の計算に必要なのは、BERTScoreの値だけです。これは別途、算出しておきます。
上の式は、グラフにすれば下のようになります。

BERTScoreによる類似度の評価の傾向

ここからは、順を追いながらその理由を説明していきます。

まず、BERTScoreによる類似度の評価の傾向がどのようなものなのか、簡単に検証します。
やや唐突な内容になりますが、以下は検証結果の例です。

No. テキスト1 テキスト2 BERTScore
1 小さな 喫茶店 小さい 。 0.667
2 小さな 喫茶店 道 に ある 交番 0.673
3 小さな 喫茶店 交番 0.686
4 交番 の 隣 0.689
5 小さな 喫茶店 交番 の 隣 に 喫茶店 が ある 。 0.772
6 交番 の 隣 交番 0.781
7 小さな 喫茶店 喫茶店 0.870
8 喫茶店 喫茶店 1.000

各行は、2つのテキストの組み合わせです。これらを、BERTScoreの昇順に並べています。ちなみに、「喫茶店」でなくてもまったく同じテキスト同士ならBERTScoreは1.0です。

これだけのサンプルに限って言えば、No.1~4までの行は、テキスト同士はとても似ているとは言えないですね。人間が評価すると、よくて0.1点(満点を1.0とした場合)だと思います。

それに対して、No.5から7までを見るとどうか。No.6は、「交番」という共通の単語(しかもキーワードっぽい?)があるものの、残念ながら意味は異なります。しかし、No.5,7では、テキスト同士は意味的になかなか似ていると言えます。人間が評するなら、No.6には0.5点くらい、No.5,7には0.8点くらい付けても良さそうなものです。

BERTScoreによる評価のゆがみ

以上の評価を散布図にすると、こうなります。1つ1つの点は、上の表の1つ1つの行に対応しています。

理想なのは、直線的にプロットされることです。一応は直線に近いので、これで及第点と言えなくはないです。
ですが、右にややカーブしたプロットとなっています。せっかくなので、もう少し追求したいです。

この散布図を考察すると、次のことに気づきます。No.5,7のようなテキスト同士が「なかなか似ている場合」でのBERTScoreは、0.82くらいです。それに対し、No.1~4のような「全然似ていない場合」でも、BERTScoreは0.68くらいにもなってしまうことです。
これはつまり、BERTScoreだと、回答同士が全然似ていない場合でも類似度が過大に評価されてしまうということです。あるいは、なかなか似ている場合に対して過小評価されてしまうということです。
「完全一致の場合」と「なかなか似ている場合」のBERTScoreの差は、0.18くらいです。それなら、「なかなか似ている場合」と「全然似ていない場合」のBERTScoreの差は、0.25くらいあっても良さそうな気がします。しかし、それが実際には0.25どころか0.16くらいしかないのです。

BERTScoreに修正をかける

そこで考えた対策は、前述したように、類似度の測定方法を例えばこの式で代替することです。

この測定方法によるスコアを、修正済みBERTScoreと表現することにします。
この式の意図は、次の2つです。これらの意図を単純に反映した結果が、上の式です。

  • BERTScoreが0.85以上なら、満点でも良さそうだと思ったこと
  • BERTScoreが0.65以下なら、0点でも良さそうだと思ったこと

そして、先ほどのデータに対して修正済みBERTScoreを適用すると、次のようになります(左側が修正していないBERTScore、右側が修正済みBERTScore)。

右側のグラフをご覧ください。点が上下に分散しているように見える箇所もありますが、左側のグラフに比べると、直線的になっていることが分かります。つまり、それだけ人間による評価との相関が強くなったということです。

ここまでのまとめ

ということで、前述のようなBERTScoreの修正により、2種の回答の類似度をより人的評価に沿うように測定できる、という話でした。

BERTScoreでも、十分かと言えば十分かもしれません。ですが、多少の実験さえすれば、BERTScoreをどう修正すればよいかがある程度見えてくると思います。それなら、修正のコストに見合う価値はあると思います。

実験

あとは補足として、実験した内容を紹介します。

実験の前提など

文書の一覧

文書の一覧として、次のように設定します(先ほど唐突に出てきた内容は、これらの文章の一部でした)。

文書の種類 内容
要約対象文書 昨日、いつもと違う道を歩いた。すると、交番の隣に小さな喫茶店があるのを発見した。
候補文のパターン1 交番の隣に喫茶店がある。
候補文のパターン2 交番の隣にある喫茶店は小さい。
候補文のパターン3 昨日歩いた道に交番と喫茶店があった。
候補文のパターン4 道にある交番は喫茶店よりも小さい。

1つの要約対象文書に対し、4種類の候補文があります。今回は、いずれで自分で文章を作りました。
上の各候補文の順は、人間から見て妥当性の高い順にしています。
1番目は、理想形に近い候補文です。
2番目は、喫茶店の大きさに言及するような書き方になっており、少し焦点がズレています。
3番目も同様に、焦点がズレています。交番が(も)あったことを強調するかのようですし、「道」にかかる「昨日歩いた」も少し冗長です。
4番目は、要約対象文書にない事柄です。実際、要約対象文書では交番の大きさについては書かれていません。ここまでくると、候補文としての評価は大きく下がってきます。

用いる質問生成モデル

今回は、sonoisaさんがHugging Faceにて配信している質問生成モデルを使用します。トークナイズにもこのモデルを用います。

https://huggingface.co/sonoisa/t5-base-japanese-question-generation

質問生成に用いる答え

質問を生成するには、文章だけでなく、ネタとなる答えが必要です。ここでは、その答えのことをテーマと呼ぶことにします。というのは、「答え」みたいな語句だと、質問応答モデルにより出力される回答と紛らわしいためです。

今回は、候補文から名詞のみを、テーマとして抽出することにします。抽出には、ライブラリのspaCyを用います。

用いる質問応答モデル

こちらの記事にならい、質問応答モデルを構築することにしました。

https://note.com/npaka/n/na8721fdc3e24

また、トークナイザとして東北大学の乾研究室の配信しているモデルを用います。

https://huggingface.co/cl-tohoku/bert-base-japanese-whole-word-masking

BERTScoreの算出に用いるライブラリ

Hugging Faceのライブラリevaluateを使います。出力されるスコアには、適合率、再現率、F1スコアがあります。ひとまず今回は、F1スコアでもってBERTScoreとします。

https://huggingface.co/docs/evaluate/index

文書要約の性能の評価指標

次の3種類とします。

文書要約の性能の評価指標 備考
BERTScore -
QAGS(通常のBERTScore) 2種の回答の類似度を、通常のBERTScoreにより測定する。
QAGS(修正のBERTScore) 2種の回答の類似度を、修正のBERTScoreにより測定する。

1つ目のBERTScoreは、参考までにQAGSとの比較対象として設けています。
いずれの評価指標にもBERTScoreという名称がありますが、2つ目と3つ目はQAGSベースです。これらは、BERTScore自体とは全然別物です。QAGSベースの各評価指標では、あくまでも2種の回答の類似度の測定のためにBERTScoreを用いているだけです。
また、修正のBERTScoreは、BERTScoreを元にした前述の類似度の式の値のことです。

ただし、QAGSベースの評価指標だと、たいてい1つの候補文につき複数のテーマが抽出されます。その場合は、テーマごとに回答間の類似度を測定したあとに、それら類似度の平均値でもってQAGSによる評価値とします。

実験の結果

各候補文に対する評価の結果

今回紹介した工夫により、より人的評価に沿った結果を出すことができました。
ただし、これはごく限られたサンプルを用いての実験結果にすぎないことを補足します。

次の表が、実験から得られた結果です。

パターンNo. 候補文 BERTScore QAGS(通常のBERTScore) QAGS(修正のBERTScore)
1 交番の隣に喫茶店がある。 0.766 0.881 0.870
2 交番の隣にある喫茶店は小さい。 0.765 0.889 0.696
3 昨日歩いた道に交番と喫茶店があった。 0.789 0.779 0.612
4 道にある交番は喫茶店よりも小さい。 0.712 0.744 0.436

前述のように、上のほうにある候補文ほど、人的評価の高い候補文です。下のほうにいくほど悪くなります。ですので、各評価のスコアはこの実態に沿った数値になることが望ましいです。

それでは、各評価のスコアがどうなっているのかを見てみます。
BERTScoreでは、残念ながら実態の割にパターン3の候補文に対するスコアは高くなってしまいました。他の各パターンの候補文に対するスコアからすると、0.74くらいなら良かったところです。
これに比べると、QAGS(通常のBERTScore) のほうが実態に沿った評価ができていますね。ただし、パターン1が最善なのに、スコアで見るとパターン2が大き目になってしまいました。
最後にQAGS(修正のBERTScore) を見ると、QAGS(通常のBERTScore)に見られた難点がないです。だいぶ実態に沿った評価ができていると思いました。

各候補文に対するテーマ抽出、質問生成、質問応答などの結果

また、参考までに、候補文ごとの抽出されたテーマや、テーマごとの各評価の結果を掲載します。
QAGS(通常のBERTScore) に比べてQAGS(修正のBERTScore) は、全然異なる2回答に対して低い評価スコアを出せています。その例は、パターン3のテーマ「交番」や、パターン4のテーマ「喫茶店」、「交番」です。

  • パターン1の候補文

  • パターン2の候補文

  • パターン3の候補文

  • パターン4の候補文

実験の具体的な作業手順

実験の具体的な作業手順を掲載します。この作業手順は一例です。

基本的な前提

  • Google Colabを使います。
  • 後述の質問応答モデルの学習において、GPUを使います。
  • Google Driveの容量100GB分の契約をします(契約料は、ひと月あたり250円程度です)。

大まかな作業手順

大まかに2ステップです。
1ステップ目は、質問応答モデルの学習です。2ステップ目において、より具体的な実験を行います。

1ステップ目:準備(質問応答モデルの学習)

先ほども掲載したこちらの記事の内容に沿って、質問応答モデルの学習を行います。

https://note.com/npaka/n/na8721fdc3e24

上の記事にもある通り、京都大学の黒橋・褚・村脇研究室の配信している運転ドメインQAデータセットを学習に用います。また、このデータセットのダウンロードにあたっては、所定の条件に同意する必要があります。

https://nlp.ist.i.kyoto-u.ac.jp/index.php?Driving domain QA datasets

Colabにおいて、最初にアクセラレータ(Colabのノートブックの「ランタイム」タブ→「ランタイムのタイプを変更」→ハードウェア アクセラレータ)をGPUに変更します。Colab Proなら学習はより安定しますが、使わなくても大丈夫です。

学習にかかった時間は、記憶だと4時間ほどです。
学習中に中間的なモデルが蓄積されていく関係で、数十GBもの容量を使います。エポック数の設定にもよりますが、70GBほどの容量があれば十分です。そのためにも、100GBの容量の契約をしておきます。

2ステップ目:実験

ここからが実験です。
具体的な作業手順としては、次の各コードを順に1つずつ、Colab上のセルにコピー&ペーストして実行すればいいだけです。書き換えの必要な箇所は、質問応答モデルの格納されているパスと、実験上の要約対象文書や各候補文くらいです。
すべてのセルを実行し終えるには、数分ほどかかります。いちど最後まで実行し終えたら、あとは最後のセル内にある要約対象文書と各候補文を任意に再設定し、そのセルのみを再実行すればOKです。

# Googleドライブのマウント
from google.colab import drive
drive.mount('/content/drive')
# 質問応答モデルの格納されているパスを設定する(下記は例)
dir_model_qa = '/content/drive/MyDrive/○○○/×××/QA/DDQA-1.0/transformers/output/'
# ライブラリ類のインストールとインポート

!pip install -q transformers==4.29.2
!pip install -q sentencepiece
!pip install -q fugashi[unidic-lite]
!pip install -q ipadic
!pip install -q ja-ginza
!pip install -q evaluate
!pip install -q bert_score

import numpy as np
import torch
from transformers import T5ForConditionalGeneration, T5Tokenizer, AutoModelForQuestionAnswering, BertJapaneseTokenizer
import spacy
import evaluate
# 関数の定義やモデルの読み込み

# #######################################################################################
#
# ※活用させていただいたOSSやコードなど
#
# ■ Huggingface Transformers 入門 (14) - 日本語の質問応答の学習 (npaka 様)
#
# https://note.com/npaka/n/na8721fdc3e24
#
# ■ 運転ドメインQAデータセット (京都大学 黒橋・褚・村脇研究室 様)
#
# https://nlp.ist.i.kyoto-u.ac.jp/index.php?Driving%20domain%20QA%20datasets
# (ダウンロード条件に同意したうえでダウンロードしています。)
#
# ■ 回答と回答が出てくるパラグラフを与えると質問文を生成するモデル (sonoisa 様)
#
# https://github.com/sonoisa/deep-question-generation
# https://huggingface.co/sonoisa/t5-base-japanese-question-generation
#
# MIT License
#
# Copyright (c) 2021 Isao Sonobe
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#
# ■ トークナイザ (東北大学 乾研究室 様)
#
# https://github.com/cl-tohoku/bert-japanese/tree/v1.0
# https://huggingface.co/cl-tohoku/bert-base-japanese-whole-word-masking
#
# cl-tohoku/bert-japanese is licensed under the Apache License 2.0
#
# A permissive license whose main conditions require preservation of copyright and 
# license notices. Contributors provide an express grant of patent rights. Licensed 
# works, modifications, and larger works may be distributed under different terms 
# and without source code.
#
# #######################################################################################

# ##### 用語 ######
# 要約対象文書:文字通り、要約の対象となる文章
# 候補文      :要約対象文書を要約した結果の文章
# テーマ      :質問を生成するためのネタとなる語句。基本的に、1つの候補文に対し、1つ以上のテーマが生成される。
# 質問文      :候補文とテーマを元にして生成される質問の文章。1つのテーマに対し、質問文を1件のみ生成する。
# 回答        :生成された質問文に対して生成される回答
# #################


# 候補文を総合的に評価する関数の定義(3種類の評価を返す)
def total_evaluate(i_source, i_summary):

  #質問を生成する
  zip_summary_thema_list_gq = qg(i_summary)
  #質問応答に対する評価を返す(順にBERTScore、QAGS(通常のBERTScore)、QAGS(修正のBERTScore)を返す)
  return qa(zip_summary_thema_list_gq, i_source, i_summary)


# 質問を生成する関数の定義
def qg(i_summary):

  # ##### 入力 ######
  # i_summary:候補文
  # #################

  # ##### 出力 ######
  # zip(summary_thema_list, generated_questions):テーマと候補文のセットのリスト、および生成された各質問をまとめたもの
  # #################

  INPUT_MAX_LEN = 128
  OUTPUT_MAX_LEN = 32

  # 候補文とテーマのセットのリストを作成する。
  summary_thema_list = []
  ls_thema = ext_thema(i_summary)
  print('抽出されたテーマの一覧:')
  print(ls_thema)
  for thema in ls_thema:
    summary_thema_list.append((thema, i_summary))

  generated_questions = []

  for thema, summary in summary_thema_list:
    # モデルに入力可能な形式に変換する。
    input = f"answer: {thema} context: {summary}"

    # 候補文とテーマのセットをトークナイズする。
    tokenized_inputs = tokenizer_qg.batch_encode_plus([input], max_length=INPUT_MAX_LEN, truncation=True, 
                                                      padding="longest", return_tensors="pt")
    input_ids = tokenized_inputs['input_ids']
    input_mask = tokenized_inputs['attention_mask']

    # 質問文を生成する。
    tokenized_outputs = model_qg.generate(input_ids=input_ids, attention_mask=input_mask, 
                                          max_length=OUTPUT_MAX_LEN, return_dict_in_generate=True, decoder_start_token_id=0,
                                          temperature=0.0,
                                          num_beams=4,
                                          num_return_sequences=1,
                                          )

    # 生成された質問文のトークン列を文字列に変換する。
    outputs = [tokenizer_qg.decode(ids, skip_special_tokens=True, clean_up_tokenization_spaces=False) 
      for ids in tokenized_outputs.sequences]

    # 質問文をリストに格納する。
    generated_questions.append(outputs)
  
  # 抽出されたテーマがない場合は、空のリストを出力する。
  if summary_thema_list == []:
    return []
  else:
    return zip(summary_thema_list, generated_questions)


# 質問に応答して各評価指標のスコアを出力する関数の定義

def qa(i_zip_summary_thema_gq, i_source, i_summary):

  # ##### 入力 ######
  # i_zip_summary_thema_gq :関数「qg」の出力結果。候補文とテーマのセットのリスト、および生成された各質問をまとめたもの
  # i_source               :要約対象文書
  # i_summary              :候補文
  # #################

  # ##### 出力 ######
  # bs                                            :BERTScoreによる評価スコア
  # np.round(np.average(ls_qags_bs), 3)           :QAGS(通常のBERTScore)による評価スコア(テーマごとのスコアの平均値を取ったもの)
  # np.round(np.average(ls_qags_bs_corrected), 3) :QAGS(修正のBERTScore)による評価スコア(テーマごとのスコアの平均値を取ったもの)
  # #################

  # テーマが1つも抽出されなかった場合は、候補文の評価をしない。
  if i_zip_summary_thema_gq == []:
    return '未測定', '未測定', '未測定'

  # QAGS(通常のBERTScore)の格納先
  ls_qags_bs = []
  # QAGS(修正のBERTScore)の格納先
  ls_qags_bs_corrected = []

  for (thema, summary), questions in i_zip_summary_thema_gq:
    print(f"・テーマ: {thema}")

    #テーマと回答の類似度の評価(BERTScore)
    bs_temp = bertscore.compute(predictions=[i_summary], references=[i_source], lang="ja")
    bs = np.round(bs_temp['f1'][0], 3)

    for question in questions:

      # 推論の実行(要約対象文書を用いた場合)
      answer_source = pred_answer(question, i_source)

      # 推論の実行(候補文を用いた場合)
      answer_summary = pred_answer(question, i_summary)

      # BERTScoreの算出に用いるモデルの設定
      model_type = 'bert-base-multilingual-cased'
      
      # 修正のBERTScoreの算出の際に必要なパラメータ
      bs_corrected_min = 0.65  # BERTScoreがこの値以下なら修正のBERTScoreは0.0
      bs_corrected_max = 0.85  # BERTScoreがこの値以上なら修正のBERTScoreは1.0

      # テーマと回答の類似度の評価(QAGS(通常のBERTScore)、およびQAGS(修正のBERTScore))
      result_bertscore_all = bertscore.compute(predictions=[answer_summary], references=[answer_source], model_type=model_type, lang="ja")
      result_qags_bs = np.round(result_bertscore_all['f1'][0], 3)
      ls_qags_bs.append(result_qags_bs)
      result_qags_bs_corrected = np.round(min(max(result_bertscore_all['f1'][0] - bs_corrected_min, 0.0), bs_corrected_max - bs_corrected_min) / (bs_corrected_max - bs_corrected_min), 3)  #2種の回答間のBERTScoreが0.65以下なら0.0、0.85以上なら1.0となる。
      ls_qags_bs_corrected.append(result_qags_bs_corrected)

      # 結果出力
      print("生成された質問                 :" + question)
      print("回答(要約対象文書を用いた場合): " + answer_source)
      print("回答(候補文を用いた場合)      : " + answer_summary)
      print("スコア(QAGS(通常のBERTScore)):" + str(result_qags_bs))
      print("スコア(QAGS(修正のBERTScore)):" + str(result_qags_bs_corrected) + '\n')
  
  return bs, np.round(np.average(ls_qags_bs), 3), np.round(np.average(ls_qags_bs_corrected), 3)


# 候補文からテーマ(質問を生成するためのネタとなる語句)を抽出する関数の定義
def ext_thema(i_summary):

  doc = nlp(i_summary)
  ls_thema = []
  for tok in doc:
    # 今回は名詞のみを抽出する。
    if tok.pos_ == 'NOUN':
      ls_thema.append(tok.text)
  
  return ls_thema


# 質問文に対する答えを推論する関数(関数「qa」の一部)
def pred_answer(i_question, i_text):

  inputs = tokenizer_qa.encode_plus(i_question, i_text, add_special_tokens=True, return_tensors="pt")
  input_ids = inputs["input_ids"].tolist()[0]
  output = model_qa(**inputs)
  answer_start = torch.argmax(output.start_logits)  
  answer_end = torch.argmax(output.end_logits) + 1 
  
  return tokenizer_qa.convert_tokens_to_string(tokenizer_qa.convert_ids_to_tokens(input_ids[answer_start:answer_end]))


# 'ja_ginza'の読み込み ※Colabの場合、エラーの際はランタイムの再起動により解決する可能性あり。
nlp = spacy.load('ja_ginza')

# 評価指標BERTScoreの読み込み
bertscore = evaluate.load("bertscore")

# 質問生成のモデルとトークナイザの読み込み
model_qg_name_or_path = "sonoisa/t5-base-japanese-question-generation"
model_qg = T5ForConditionalGeneration.from_pretrained(model_qg_name_or_path)
model_qg.eval()

tokenizer_qg = T5Tokenizer.from_pretrained(model_qg_name_or_path, is_fast=True)

# 質問応答のモデルとトークナイザの読み込み
model_qa = AutoModelForQuestionAnswering.from_pretrained(dir_model_qa)
tokenizer_qa = BertJapaneseTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-whole-word-masking') 
# 実験(下記の入力欄に要約対象文書、および候補文の一覧を設定する。そしてこのセルを実行する。)

# #################### 入力 #####################
# 要約対象文書
input_source = "昨日、いつもと違う道を歩いた。すると、交番の隣に小さな喫茶店があるのを発見した。"
# 候補文の一覧
ls_input_summary = ['交番の隣に喫茶店がある。', '交番の隣にある喫茶店は小さい。', '昨日歩いた道に交番と喫茶店があった。', '道にある交番は喫茶店よりも小さい。']
# ###############################################

# 候補文ごとの評価結果の出力
for i, summary in enumerate(ls_input_summary):
  print('★★★候補文:' + summary + '\n')
  score = total_evaluate(input_source, summary)
  print('■総合評価' + '\n')
  print('BERTScore            :' + str(score[0]))
  print('QAGS(通常のBERTScore):' + str(score[1]))
  print('QAGS(修正のBERTScore):' + str(score[2]))
  print('\n' + '\n')

終わりに

サンプルデータの量が少なく、割と簡易な実験になってしまったかもしれません。あまり深追いするときりがないので、いったんここで区切りをつけ、記事としてまとめました。
ただ、限られたデータとは言え、QAGSによる評価が割と人間による評価に沿っているのを確認できて良かったです。加えて、QAGSにおいては修正済みBERTScoreを2種の回答の類似度の測定に用いることで、より人間による評価に沿う形になりました。

最後までお読みいただき、ありがとうございました。

記事の修正履歴

※主な修正内容に絞っており、微細な内容を省略しています。

No. 修正内容
1 「最後にQAGS(通常のBERTScore) を見ると、」を「最後にQAGS(修正のBERTScore) を見ると、」に修正。
2 「!pip install -q scipy==1.7.3」を削除。
3 質問応答モデルの学習におけるColabのProの使用について、使わなくていい旨に修正。
4 実験の2ステップ目において、GPUは不要である旨を追記。
5 「!pip install -q transformers==4.4.2」を「!pip install -q transformers==4.29.2」に修正、およびその旨の追記。
6 spaCyのバージョンについて追記。
7 BERTScoreの計算の仕様の変更。

Discussion

F0XF0X

非常にわかりやすい説明とコードが記載してあって、完全にゼロからGoogle Colabで再現できました!
論文が公開されてからまだあまり時間が経っていないにも関わらず、ここまでの説明ができるのは本当にすごいと思います。ありがとうございます!

1つだけ細かいエラーが出たので、自分の後に見る人のため一応記載しておきます。
「実験」の14行目の
score = total_evaluate(input_source, summary)
のところで、total_evaluateがないというName errorが出ました。

対処法としては
「実験」のまえにevaluateをインストールするだけです。
pip install evaluate
で簡単にできます。

あらためて、素晴らしい説明とコードをありがとうございました!

ty_nlpty_nlp

嬉しいコメントありがとうございます!
この記事を読み、実行までされたそうで、こちらも実験や記事作成をした意義を感じられました。良かったら、他の記事も読んでもらえると嬉しいです。

そのエラーについて確認しました。
ただ、5つに分けたコードのうち上から3番目のコードに「!pip install -q evaluate」を入れてあります。偶然evaluateのインストールに失敗した可能性が考えられますが、実際の要因は分かりかねるところです…。
いずれにしても、今後他の方が実行するときのために情報共有をしていただきありがとうございました。

F0XF0X

ご返信ありがとうございます!
他の記事も見させてもらっています。
非常に参考になります。ありがとうございます!

確かに3個目に「!pip install -q evaluate」がありますね
なぜエラーが起こったかはわかりませんが、同じことが起こった人のために一応消さずに残しておきます。

ありがとうございます