データナード

機械学習と自然言語処理についての備忘録 (旧ナード戦隊データマン)

Webクロールで集めたパラレルコーパスをクリーニングする

パラレルコーパスフィルタリング1とは、ノイズのあるパラレルコーパスをクリーニングするタスクです。ParaCrawlプロジェクト2 のようにWebクロールしてパラレルコーパスを生成する手法では、間違った対応関係をもつ文ペアを保持することがあります。あるいは、翻訳モデルの訓練に役立たない様々なノイズを持った文ペアが現れることもあります。このような文ペアを除外するようなタスクです。

概要

パラレルコーパスフィルタリングでは、フィルタリング対象のコーパス、その他のモノリンガルコーパス、その他のパラレルコーパスが使えると仮定します。これらのすべてのデータを利用して、フィルタリング対象の文をフィルタリングするための手法を適用し、元よりも小さなサイズのデータ集合を生成します。

元のデータを使うよりも、フィルタリング後のデータのほうが、特定のテストセットに対してNMTやSMTのBLEUが上がるのであれば、そのテストセットにおいてフィルタリングの効果があったと言えます。そのため、このタスクでは、Mosesやfairseqを固定の方法で使い、フィルタリング効果を測定します。

WMT18では、多くの参加者が以下の手順を使っています3:

  1. 事前フィルタリングルール : 言語検出や文の長さの不一致などを利用した事前フィルタリング。
  2. 文ペアに対するスコアリング関数: 文のペアに対してスコアリングする関数を作成する。
  3. 特徴量関数に対して重みを学習する分類器 : 複数のスコアリング関数や特徴量を機械学習によって重み付けすることで最適化する。

WMT19では、ベースラインとしてZipporah4というツールを使っています。今回はこのzipporahを試しに実行します。

zipporah

zipporahは、Webクロールしたノイズありパラレルコーパスをフィルタリングするためのツールです。

事前準備

事前準備として、クリーンデータ(対応のとれたパラレルコーパス)を用意します。用意したクリーンデータを以下のように分離します。

  • フィルタリング対象データ
  • 訓練用データ (train)
  • 訓練用データ (dev)

フィルタリング対象のデータは、クロールしたパラレルコーパスを使いますが、実験のためにクリーンデータから恣意的に生成します。

データはMeCabでトーカナイズしておきます。

zipporahの入手

git clone https://github.com/hainan-xv/zipporah
cd zipporah
./make.sh

configファイルの用意

configファイルを使って読み込むデータ等を指定します。

# This file has location variables used throughout the system
# Please refer to the paper https://www.cs.jhu.edu/~hxu/zipporah.pdf for details
# for the method.

input_lang=ja
output_lang=en

#==============================================================================
working=/tmp/zipporah/working #結果が保存される場所
ROOT=/root/work/aspec_experiments2/zipporah/zipporah #zipporahの場所
moses=/opt/mosesdecoder #mosesの場所(コンパイル済み)

# model variables
#==============================================================================

clean_stem_good=/tmp/good.ja-en #訓練データ(train)
clean_stem_bad=/tmp/bad.ja-en #フィルタリング対象データ
clean_stem_dev=/tmp/dev.ja-en #訓練データ(dev)

# you probably don't need to change the variables below this point.
#==============================================================================

aligner=fast-align
fast_align_build=/opt/fast_align/build #fast_alignの場所(コンパイル済み)
align_job=1

# Those 2 variables has to do with generating word dictionaries. The dictionary
# computes, for a word-pair {de, en}, p(de | en) and p(en | de), computed by a
# simple counting method.
dict_count_thresh=1  # Only words with counts larger than this number is considered for numerator.
dict_total_count_thresh=1  # Only words with counts larger than this number is considered for denominator.

gridengine=true

#==============================================================================
# features to use
#==============================================================================

bow_constant=0.0001 # This has to do with KL-divergence computation when we compute
                    # scores. A constant is needed to be added to the denominator
                    # to avoid division by 0. Please refer to the paper (section 3.1.1)
                    # https://www.cs.jhu.edu/~hxu/zipporah.pdf for details.
translation_num_jobs=50

ngram_order=5       # n-gram order for language modeling.
word_count=30000    # we take top n most frequent words in the corpus to train
                    # language models, and word_count is the size of the shortlist.
                    # An out-of-shortlist word will be mapped to the least frequent
                    # word in the shortlist for computing probabilities.

# for each number in the string, we will generate parallel sentence pairs picked from the original corpus such that
# their total number of words doesn't exceed the number.
num_words_to_select="500000 1000000 2000000 5000000 10000000 20000000 50000000 100000000 200000000 250000000 350000000 400000000"

#==============================================================================
# Below here is automatic scripts. Do not change anything
#==============================================================================

set -e

if [ "$modeldir" != "" ]; then
  for i in $modeldir/lm.$input_lang $modeldir/lm.$output_lang $modeldir/dict.${input_lang}-${output_lang} $modeldir/dict.${output_lang}-${input_lang}; do
    [ ! -f $i ] && ( echo "model file $i does not exist" >&2 )
  done
fi
lang_pair=${input_lang}-${output_lang}

function check_equal_lines {
  n1=`wc -l $1 | awk '{print $1}'`
  n2=`wc -l $2 | awk '{print $1}'`
  if [ $n1 -ne $n2 ]; then
    echo "Unequal number of lines: $1 and $2" && exit 1
  fi
}

Moses5とfast_align6を/optに配置してコンパイルしてください。

queue.plの変更

zipporahは、GridEngineによってグリッド上でシステムを実行していることを前提としています。しかし、そうではない場合はqueue.plをGridEngineを使わないものに変更する必要があります。

queue.plというファイルがzipporah/scripts内にありますが、これを変更します。

wget https://github.com/kaldi-asr/kaldi/blob/master/egs/wsj/s5/utils/parallel/run.pl
mv run.pl zipporah/scripts
cd scripts
mv queue.pl queue.pl.old
mv run.pl queue.pl

必要ライブラリのインストール

zipporahはpython2を使っています。また、いくつかのライブラリを入れておく必要があります。

apt install python python-pip python-tk
pip2 install numpy matplotlib numpy scikit-learn

実行

実行の際には、zipporah内のrun.shをconfigファイルを指定して実行します。

cd zipporah
./run.sh ../config

結果

すべての実行が正常に完了すると、結果がワーキングディレクトリに指定した場所に保存されます。

$ ls /tmp/zipporah/working/1/step-4/bad
corpus.xz  feats.txt  scores.txt  tr.sum
$ unxz corpus.xz
$ less corpus

利用しているコーパスのライセンスの関係でデータは見せられませんが、以下の形式で保存されています。

ソース言語[tab]ターゲット言語[tab]スコア

スコアは、マイナスは悪い、プラスは良い文ペアです。つまり、マイナスになっているものは除外対象とするべきデータと考えられます。

考察

zipporahのようなツールの性能がどの程度なのかは、以下のような方法で判定することができます:

  1. フィルタリング対象データに対して事前に対応関係のラベルがすべてわかっている場合、classification_reportを出力することで判定する。
  2. フィルタリング対象データに対して事前に対応関係のラベルがない場合、フィルタリング前とフィルタリング後のデータを使って訓練された固定パラメータのNMTやSMTのBLEUスコアによって判定する。

現実的な問題では、対応の取れたクリーンコーパスのデータの量が言語によって異なります。フランス語と英語のペアであれば膨大なデータがありますが、日本語と英語のペアはそれよりも少なく、さらにネパール語と英語のペアは遥かに少ないです。リソースの少ない言語でフィルタリングすることは、リソースのある言語よりも難しいタスクとなります。

スコアリング関数だけがわかっていても、スレッショルドをどうするのかという問題や、複数のスコアリング関数をどう扱うのかという問題があります。zipporahのように、ロジスティック回帰によって複数のスコアリング関数に重み付けを行う仕組みと教師データがあれば、その重みを最適化できます。

また、zipporahの他に、bicleaner7というツールもありますが、こちらはより性能の高いツールです。あるいは、Facebookが開発しているクロスリンガルの文Embedding(LASER)を使う方法もあります8

Parallel Corpus Filteringというタスクの根本にあるのは、「質の高いデータを大量に集める」というところにあると思います。大量のデータを集めるためにWebクロールを利用するbitextor9のようなプロジェクトがあるわけですが、zipporahやbicleanerはその大量のデータからノイズを除去し、質を高めるために使うことができます。

翻訳タスクではデータが重要なので、データの質と量の両方を高める手法の開発が重要です。

参考