第1回 pythonで自然言語解析(1)

pythonで自然言語解析してみよう

新聞記事の要約を試みています。要約の文字数が指定されているので文抽出型の要約だけですと指定文字数よりオーバーすることがあります。
これを文生成型の要約を補完的に適用することによって文字数を指定文字数まで調整できるようなものを作りたいというのが最終目標です。
最終目標に向けて前処理をコツコツしてきたいと思います。
まずは、教師データとして、元の文章とお手本の要約した文章があるとして、文単位で比較していき似ているけどちょっと違う文を抽出してみたいと思います。

元の文章
 国際パラリンピック委員会(IPC)は3日、声明を発表し、前日2日に中立選手としての北京パラリンピック(4日開幕)への参加を容認したRPC(ロシアパラリンピック委員会)、ベラルーシの選手について、一転して出場を認めないことを発表した。IPCは声明で「RPCおよびベラルーシの選手のエントリーを拒否することを決定しました」と発表した。パーソンズ会長のコメントとして「圧倒的な数の関係者と連絡をとりオープンに議論した。彼らは私たちが決定を再考しなければ、2022年パラリンピック大会に重大な結果をもたらす可能性が高いと語った。複数のNPC、中には政府、チーム、アスリートからの働きかけを受けていたが、競技に参加しなさそうでもあった」と、撤回に至った経緯を説明した。

要約文
 国際パラリンピック委員会(IPC)は3日、声明を発表し、前日2日に中立選手としての北京パラリンピック(4日開幕)への参加を容認したRPC(ロシアパラリンピック委員会)、ベラルーシの選手について、一転して出場を認めないことを発表した。IPCが「RPCおよびベラルーシの選手のエントリーを拒否することを決定しました」と発表した。

元の文章と要約文を比較してみると、元の文章は全部で4文ありますが、要約文は上の2文を抽出しています。そして2文目の「IPCは声明で」が「IPCが」に修正されています。

これを、修正のあった文だけ抽出し、さらに修正内容も出力してみたいと思います。

この例だと

元の文章
IPCは声明で「RPCおよびベラルーシの選手のエントリーを拒否することを決定しました」と発表した。

要約文
IPCが「RPCおよびベラルーシの選手のエントリーを拒否することを決定しました」と発表した。

修正内容
「は声明で」->「が」

包含関係にあるものはスキップしたい

似ているけどちょっと違うという文をピックアップしたいので要約が元の文章の文の数だけ減らしただけのものはスキップしたいと思います。

例えば、以下の例だと要約文は元の文章の第2文までをそのまま抜粋しただけなので教師データからは除外したいと思います。
プログラムでは、要約文と元の文章が包含関係にあるものはスキップするという処理を入れたいと思います。

元の文章
 国際パラリンピック委員会(IPC)は3日、声明を発表し、前日2日に中立選手としての北京パラリンピック(4日開幕)への参加を容認したRPC(ロシアパラリンピック委員会)、ベラルーシの選手について、一転して出場を認めないことを発表した。IPCは声明で「RPCおよびベラルーシの選手のエントリーを拒否することを決定しました」と発表した。パーソンズ会長のコメントとして「圧倒的な数の関係者と連絡をとりオープンに議論した。彼らは私たちが決定を再考しなければ、2022年パラリンピック大会に重大な結果をもたらす可能性が高いと語った。複数のNPC、中には政府、チーム、アスリートからの働きかけを受けていたが、競技に参加しなさそうでもあった」と、撤回に至った経緯を説明した。

要約文
 国際パラリンピック委員会(IPC)は3日、声明を発表し、前日2日に中立選手としての北京パラリンピック(4日開幕)への参加を容認したRPC(ロシアパラリンピック委員会)、ベラルーシの選手について、一転して出場を認めないことを発表した。IPCは声明で「RPCおよびベラルーシの選手のエントリーを拒否することを決定しました」と発表した。

python で包含関係を調べる良い方法がないかなと調べたところ in 演算子というのを使うのが手っ取り早そうです。

a in b とすると a が bに包含されている場合はTrueをそうでない場合はFalseを返します。

プログラムは以下になります。

summary=" 国際パラリンピック委員会(IPC)は3日、声明を発表し、前日2日に中立選手としての北京パラリンピック(4日開幕)への参加を容認したRPC(ロシアパラリンピック委員会)、ベラルーシの選手について、一転して出場を認めないことを発表した。IPCは声明で「RPCおよびベラルーシの選手のエントリーを拒否することを決定しました」と発表した。
"

original=" 国際パラリンピック委員会(IPC)は3日、声明を発表し、前日2日に中立選手としての北京パラリンピック(4日開幕)への参加を容認したRPC(ロシアパラリンピック委員会)、ベラルーシの選手について、一転して出場を認めないことを発表した。IPCは声明で「RPCおよびベラルーシの選手のエントリーを拒否することを決定しました」と発表した。パーソンズ会長のコメントとして「圧倒的な数の関係者と連絡をとりオープンに議論した。彼らは私たちが決定を再考しなければ、2022年パラリンピック大会に重大な結果をもたらす可能性が高いと語った。複数のNPC、中には政府、チーム、アスリートからの働きかけを受けていたが、競技に参加しなさそうでもあった」と、撤回に至った経緯を説明した。"

print(summary in original)

類似度の高い文を見つける

包含関係にある文章をスキップしたら、あとは文ごとに修正の有無を確認していきたいと思います。
要約文の文と元の文を1文目から比較して内容が同じであれば次の文へ移動し、似ている文は違いを抽出していきます。
似ている文を見つける方法としてdifflib.SequenceMatcher のratioというのが使えそうです。
r = difflib.SequenceMatcher(None, 比較したい文字列1, 比較したい文字列2).ratio()
とすると戻り値として類似度が返却されます。1.0が完全一致で0.95とかだと95%同一という意味です。

import difflib
summary="IPCは声明で「RPCおよびベラルーシの選手のエントリーを拒否することを決定しました」と発表した。"

original="IPCが「RPCおよびベラルーシの選手のエントリーを拒否することを決定しました」と発表した。"
r = difflib.SequenceMatcher(None, summary, original).ratio()
print(r)

0.9230769230769231

差分を出力する

閾値を決めて類似度の高い文が見つかったら差分を出力します。差分の出力もdifflibでできそうです。
Differのcompare メソッドで差分を出力できるみたいです。

diff = difflib.Differ()
diff_array = diff.compare(summary, original)
for df in diff_array :
    print(df)

  I
  P
  C
+ が
- は
- 声
- 明
- で
  「
  R
  P
  C
  お
  よ
  び
  ベ
  ラ
  ル
  ー
  シ
  の
  選
  手
  の
  エ
  ン
  ト
  リ
  ー
  を
  拒
  否
  す
  る
  こ
  と
  を
  決
  定
  し
  ま
  し
  た
  」
  と
  発
  表
  し
  た
  。

この出力結果から「は声明で」->「が」に変換するにはどうすれば良いでしょうか。
結構大変そうなので次回にします。

書籍の紹介

(8)【grep】シェルスクリプトコマンド活用紹介

(8)【grep】シェルスクリプトコマンド活用紹介

第9回 pythonでNQueen(エイトクイーン)対象解除法(2)

第9回 pythonでNQueen(エイトクイーン)対象解除法(2)