ERRANTを使用するための下準備

博論関係のメモ。
GitHubERRANTというプログラムがある。

正式名称は、

ERRor ANnotation Toolkit: Automatically extract and classify grammatical errors in parallel original and corrected sentences.

ということで、例えば、学習者の作文とそれを添削した母語話者の添削文の2つを持っていれば、自動で文法的なエラーを検出し分類してくれるというもの。つまり、学習者の作文だけでなく、その学習者の作文に対して母語話者によって添削文が付与されている学習者コーパスなどがあれば、エラータグ等つけることなく、このプログラムを走らせれば文法的なエラーを検出できるという優れもの。

これは、一度使ってみようということで、まずは下準備が必要。
まずは、学習者コーパス。母語話者の添削文が付与されている学習者コーパスといえば、NICER: the Nagoya Interlanguage Corpus of English Rebornということで、今回はNICERを使ってみる。

NICERは、CHAT形式で以下のように、学習者の文と母語話者による添削文がパラレルに収録されている。

*JPN513:	education of teachers
%NTV:	Education of Teachers
%COM:	
%par:
*JPN513:	Let's think about education of teachers in Japan.
%NTV:	Let's think about the education of teachers in Japan.
%COM:	
%par:
*JPN513:	Firstly, in Japan how do people become teachers?
%NTV:	First, in Japan, how do people become teachers?
%COM:	The "-ly" ending after numbers is old-fashioned. Just say "First", "Second", etc.
*JPN513:	I know two ways.
%NTV:	OK
%COM:	
*JPN513:	One is to enter school for growing teachers.
%NTV:	One is to enter a teacher's college.
%COM:	
*JPN513:	The other is to study education besides the major.
%NTV:	The other is to study education in addition to another major.
%COM:	
*JPN513:	Near here Aichi education college are there.
%NTV:	NG
%COM:	

後略

ERRANTのreadmeを参照すると、学習者の作文と母語話者の作文は別々のファイルに保存されていなければならないらしい。しかし、NICERの収録形式では、学習者の作文の直後にそれに対応する母語話者の作文が付与されている。このままではERRANTを使用することができないので、学習者の作文は学習者用のテキストファイルに保存し、母語話者の作文は母語話者用のテキストファイルに保存することにした。もちろん手作業でやっていると(できないこともないが。。)莫大な時間がかかってしまうので、Pythonでプログラムを作成してサクッと処理することにした。

学習者の作文は、

*JPN513:	Let's think about education of teachers in Japan.
*JPN513:	Firstly, in Japan how do people become teachers?

といったように、文の先頭に学習者のIDが付与されているので、これを見つけ出して、抽出すれば良い。
母語話者の作文も同様に、

%NTV:	Let's think about the education of teachers in Japan.
%NTV:	First, in Japan, how do people become teachers?

といったように、「%NTV」という記号が先頭に付与されているので、これを見つけ出して抽出すれば良い。

案外簡単そうに思える。実際これだけなら簡単。抜き出したそれぞれの作文は、別々のファイルに保存したいので、学習者の作文を抽出したファイルは、末尾に「_ori」、母語話者の作文を抽出したファイルは、「_cor」を末尾につけることにした。
実際に、Pythonでプログラムを書いてみた。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys,re,glob

argvs = sys.argv
argc = len(argvs)

file_lists = sorted(glob.glob(sys.argv[1] + '*.txt'))

for filename in file_lists:
	fn = filename.strip("test/"".txt")
	fw = open(str(fn) + "_ori.txt", 'w')
	fw2 = open(str(fn) + "_cor.txt", 'w')
	with open(filename, 'r') as fn:
		files = fn.read()
		for i in files:
			text = re.findall('(\*.+)', files, re.IGNORECASE)
			text_ori = "\n".join(text)
			text2 = re.findall('(%NTV.+)', files, re.IGNORECASE)
			text_cor = "\n".join(text2)
		fw.write(str(text_ori))
		fw2.write(str(text_cor))
	fw.close()
	fw2.close()

学習者の作文と母語話者作文の抽出は、以下のような感じ。

学習者
*JPN513:	education of teachers
*JPN513:	Let's think about education of teachers in Japan.
*JPN513:	Firstly, in Japan how do people become teachers?
*JPN513:	I know two ways.
*JPN513:	One is to enter school for growing teachers.
*JPN513:	The other is to study education besides the major.
*JPN513:	Near here Aichi education college are there.
母語話者添削文
%NTV:	Education of Teachers
%NTV:	Let's think about the education of teachers in Japan.
%NTV:	First, in Japan, how do people become teachers?
%NTV:	OK
%NTV:	One is to enter a teacher's college.
%NTV:	The other is to study education in addition to another major.
%NTV:	NG

これでやりたいことはできた、、かのように見えるが、問題がある。
話者記号の「*JPN513」の部分と「%NTV」の部分が要らない。これだけなら、プログラムを少し修正すれば対応可能(正規表現のグループ化を話者記号の部分だけ「?:」を使って無効にする)だが、文法的に問題のない作文には、母語話者が「OK」と書いている。これが厄介。
ERRANTでは、母語話者の添削文と学習者の文を比較してエラーを検出するので、学習者が文法的に問題のない文を書いているのであれば、文法的なエラーがないということを認識させるために、母語話者の作文も「OK」ではなく、学習者の作文と全く同じでなければならない。

というわけで、母語話者が添削文を付与する箇所に「OK」と書いていれば、その箇所に学習者の文をそのまま挿入するという処理を付け加えることにした。さらに、上で書いたプログラムでは、カレントディレクトリに結果のファイルが保存されるので、カレントディレクトリではなくて、新しいディレクトリを作成して、そこに結果のファイルを保存することにした。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import sys,os,re,glob

argvs = sys.argv
argc = len(argvs)

os.makedirs(sys.argv[1], exist_ok = True)

for fn in sorted(glob.glob(sys.argv[2] + '*.txt')):
	fn2 = fn.strip(sys.argv[2] + "txt")
	fn3 = fn2.strip("\.")
	fw_ori = open(sys.argv[1] + fn3 + "_ori.txt", 'w')
	fw_cor = open(sys.argv[1] + fn3 + "_cor.txt", 'w')
	with open(fn, 'r') as fn4:
		files = fn4.readlines()
	for i in files:
		original = re.findall(r'(?:\*JAN\d\d\d\d:\t)(.+\n)', i, re.IGNORECASE)
		if len(original) != 0:
			result_ori = original[0]
			fw_ori.write(result_ori)
		correct = re.findall(r'(?:%NTV:\t)(.+\n)', i, re.IGNORECASE)
		if len(correct) != 0:
			cor_rep = [j.replace('OK\s?\n', result_ori) for j in correct]
			fw_cor.write(cor_rep[0])
	fw_ori.close()
	fw_cor.close()

使い方は、MacならTerminal上で、WindowsならCygwin上などで

python3 プログラム名 保存先ディレクトリ名 抽出対象ディレクトリ名

なので、上のプログラムをextraction.pyという名前で保存するとし、保存先のディレクトリ名を「result」、NICERの保存場所が「NICER」とすると、

python3 extraction.py result/ NICER/

これで走るはず。

学習者の作文と母語話者作文の抽出は、以下のような感じ。

学習者
education of teachers
Let's think about education of teachers in Japan.
Firstly, in Japan how do people become teachers?
I know two ways.
One is to enter school for growing teachers.
The other is to study education besides the major.
Near here Aichi education college are there.
母語話者添削文
Education of Teachers
Let's think about the education of teachers in Japan.
First, in Japan, how do people become teachers?
I know two ways.
One is to enter a teacher's college.
The other is to study education in addition to another major.
NG

最初に書いたプログラムだと、「I know two ways」の部分が、母語話者の添削文だと「OK」だったのが、新しいプログラムでは、しっかりと学習者が書いた正しい文に書き換わっている。
これで下準備は完了したので、後はERRANTを使っていろいろやってみることにする。

添削文の「NG」については、これはもうどうしようもないのでとりあえずそのままにしておこう。。