安否情報ツイートへのタグ付与

提供:ANPI_NLP

移動: 案内, 検索

安否情報ツイートに固有表現・安否情報タグを人手・半自動で付与する方法や作業の進捗についてのページです。

目次

分担

担当がかぶらないように、行番号の mod(剰余)で分担することにしました。作業を始める前にここに担当部分を書きこんでください。

※注 1行目 = 1 で計算してください(Base 1)


補助ツール

ruby のプログラムだと以下を mod100.rb とした場合

 M = ARGV.shift.to_i
 i = 0
 while line = gets
   i += 1
   puts line if i % 100 == M
 end

以下のコマンドで mod 100 = 2 が取り出せます。

cat tweets.1645c.tsv | ruby mod100.rb 2

Python版を作成しました(江原)。codecs.getreader(sys.stdin)などを使って一回Unicode文字列に変換すると、29123行で1行増えて番号がずれますので、変換せずに以下のように通常の文字列で処理してください。

import sys
linenum = 1
mod_idx = int(sys.argv[1])
for line in sys.stdin:
  if linenum%100 == mod_idx:
    print line.strip()
  linenum = linenum + 1

タグ付けは以下の emacs lisp を定義すると便利です(文責 萩原)。もっと良い方法があればよろしくお願いいたします。 海野さんのタグ削除コード↓をelispに移植して萩原さんのコードに追加しました.下記のものと安否タグ入力関数を含んだEmacsマイナーモードを作成して http://sleepyheads.jp/tmp/anpi-nlp.el に置きました.キーバインドを汚されたくない方はお使いください(文責 数原).

(defun sgml-insert-tag (tagname)
  (interactive)
  (let ( (pold (point)) )
    (goto-char (mark))
    (insert (format "<%s>" tagname))
    (goto-char (+ pold (length (format "<%s>" tagname))))
    (insert (format "</%s>" tagname))
    )
  )

(defun sgml-insert-person-tag () (interactive) (sgml-insert-tag "person"))
(defun sgml-insert-location-tag () (interactive) (sgml-insert-tag "location"))
(defun sgml-insert-organization-tag () (interactive) (sgml-insert-tag "organization"))

(defun sgml-delete-tag ()
  (interactive)
  (save-excursion
    (if (re-search-backward "<[^/>]*>")
        (let ((start-begin (match-beginning 0))
                    (start-end (match-end 0)))
            (if (re-search-forward "</[^>]*>")
                      (progn
                        (delete-region (match-beginning 0) (match-end 0))
                        (delete-region start-begin start-end)))))))

(global-set-key "\C-cp" 'sgml-insert-person-tag)
(global-set-key "\C-cl" 'sgml-insert-location-tag)
(global-set-key "\C-co" 'sgml-insert-organization-tag)
(global-set-key "\C-cd" 'sgml-delete-tag)

xyzzy用の上記と同じスクリプトを書きました(文責 海野)。

(defun sgml-insert-tag (tagname)
  (interactive)
  (let ( (pold (point)) )
    (goto-char (mark))
    (insert (concat "<" tagname ">"))
    (goto-char (+ pold (length (concat "<" tagname ">"))))
    (insert (concat "</" tagname ">"))
    )
  )

(defun sgml-delete-tag ()
  (interactive)
  (save-excursion
    (if (scan-buffer (compile-regexp "<[^/>]*>") :reverse t)
	(let ((start-begin (match-beginning 0))
	      (start-end (match-end 0)))
	  (if (scan-buffer (compile-regexp "</[^>]*>"))
	      (progn
		(delete-region (match-beginning 0) (match-end 0))
		(delete-region start-begin start-end)))))))

(defun sgml-insert-person-tag () (interactive) (sgml-insert-tag "person"))
(defun sgml-insert-location-tag () (interactive) (sgml-insert-tag "location"))
(defun sgml-insert-organization-tag () (interactive) (sgml-insert-tag "organization"))

(global-set-key '(#\C-c #\p) 'sgml-insert-person-tag)
(global-set-key '(#\C-c #\l) 'sgml-insert-location-tag)
(global-set-key '(#\C-c #\o) 'sgml-insert-organization-tag)
(global-set-key '(#\C-c #\d) 'sgml-delete-tag)

秀丸エディタ用マクロ(文責: 岡崎).http://www.chokkan.org/anpi.mac をダウンロードして,どこかに保存.秀丸エディタのメニューの[マクロ(M)]→[マクロ登録(E)]で,ダウンロードしたマクロファイルを読み込む.マクロを呼び出すためのショートカットキーは,[その他(O)]→[キー割り当て(K)]のダイアログで,左側で割り当てるキー(Ctrl+Tなど)を選択し,コマンドで「メニュー/マクロ」を選び,先ほど登録したマクロを割り当てる.使い方は,アノテーションしたい範囲を選択し,「Ctrl+T P」で人名,「Ctrl+T L」で場所名,「Ctrl+T O」で組織名(ただし,「Ctrl+T」はマクロを割り当てたキー).

ブラウザ用UI(Chrome推奨) (文責: 久保) . @fubaさんが右のようなUIを作ってくれました。 http://dl.dropbox.com/u/336399/anpi_annotation.html

昔書いた emacs lisp 用のインクリメンタル grep 検索です (c/migemo でも動きます).タグ付けに役に立つかもしれません(文責: 吉永). http://www.tkl.iis.u-tokyo.ac.jp/~ynaga/tips/elisp_tips.html#igrep

Pythonによる簡易チェッカ(文責 江原)。下記をanpi_check.pyとして保存し、cat タグ付け後のファイル名 | python anpi_check.pyで何も表示されなければ、チェック通過です。<person type="S">のtype="S"などの、タグ内の属性のチェックは行っていないので注意してください。タグの入れ子は考えてません。チェッカをPython 2.4系でも動くようにしました。

import sys,re
NE_tagnames   = ['person', 'location', 'organization','other']
ANPI_tagnames = re.compile('[USILMPOHR]+')
p_tag = re.compile('<([/]?[a-zA-Z]+)[^>]*>')
linenum = 0
for line in sys.stdin:
  linenum = linenum + 1
  lineary = line.split('\t')
  if len(lineary)!=4:
    print linenum, '\tError: too many/few columns\t', line.strip()
    continue
  if not ANPI_tagnames.match(lineary[-1]):
    print linenum, '\tError:ANPI tag\t', line.strip()
  ary = p_tag.split(lineary[2])
  if len(ary)!=1 and not ((len(ary)%4==1) and reduce(lambda x,y:x and y, [('/'+ary[x[0]])==ary[x[1]] and (ary[x[0]] in NE_tagnames) for x in [(4*i+1,4*i+3) for i in range(len(ary)/4)]])):
    print linenum, '\tError:NE tags not match / wrong tag names\t', line.strip()

上記のRuby版を書きました(有賀)。下記をanpi_check.rbとして保存してPython版と同様に使えます。

ne_tagname = ['person','location','organization','other']
anpi_tagnames = "[USILMPOHR]+"
p_tag = "<(\/?[a-zA-Z]+)[^>]*>"

i = 0
while line = gets
  i += 1
  lineary = line.split("\t")
  unless lineary.size == 4
    puts "#{i}\tError: too many/few columns\t#{line.strip}"
    next
  end
  puts "#{i}\tError:ANPI tag\t#{line.strip}" unless lineary[-1] =~ /#{anpi_tagnames}/
  ary = lineary[2].split(/#{p_tag}/)
  puts "#{i}\tError:NE tags not match / wrong tag names\t#{line.strip}"  if ary.size != 1 && !((ary.size%4 == 1 || 0) && Array.new(ary.size/4){|j| [4*j+1,4*j+3]}.map{|x| '/'+ary[x[0]] == ary[x[1]] && ne_tagname.include?(ary[x[0]]) }.inject{|x,y| x && y})

end

また、上記をexerbでWindows用exeファイルにしたものを作りました。 ファイル:Anpi check file.zipをダウンロードして

anpi_check_file.exe XXX.tsv

を実行すると、Python版と同様の結果がでます。

タグ付け結果をブラウザで見るためのスクリプトを書きました(海野) ファイル:Anpi html.zip をダウンロードして、

python anpi_html.py XXXX.tsv

を実行すると、XXXX.tsv.html が作成されて、タグ付けの確認ができます。

指針

人名 (person)・地名 (location)・組織名 (organization) に関する固有表現タグ、および安否情報タグ(U/S/I/L/M/P/O; 詳細は下記)を付与します。 安否情報タグに曖昧性がある場合、人名タグには、可能ならば対応する安否情報タグを属性値(type="I", type="M"など)として割り振る(あくまで余裕のある人のオプションです)。

O 以外の安否情報ツイートに優先的にNEタグを付けてください。あと「東電」とか「蓮舫」とか安否情報に関係の無い NE は基本的に無視してください。

例:(最後のカラムが安否情報タグです)

46900577376010240	123sumi45	親戚を探しています。<location>岩手県奥洲市水沢区</location>に住んでいる<person type="M">角川元久</person>(<person>カドカワモトヒサ</person>50代)と連絡が取れていません。<location>奥洲市水沢区</location>がどうなっているか情報をお願いします。 #anpi	M

名前と地名をみつける

どうやって

固有名詞抽出を適用する。

上記 KyTea の人名・地名モデルで、 (<人名・姓>+)?<人名・名>+ のパターンを使うとけっこう取れます。KyTea は部分アノテートコーパスから分野適用ができるので、FP, FN を見つけたら京大グラムさん @neubig まで (by @mhagiwara)

ツイートに付いているhashが時々へループのメッセージだったり(#j_j_helpme)、場所の名前だったり(#fukushima #ibaraki)必要なものの名前だったりします。 簡単なパタンーでhashの名前とそれが出るtweetsを探しました。下のpython scriptを使うとhashの名前が一つの行で、その下にこのhashが出ている一つ以上のtweet(s)がでます。tweetには最初にtabがあります。 出力から1)ゴミをけすことと2)日本語にマーチングすることが必要ですね。 出力の例は

 #ohfunato (これがhash)
      46891094218252288	dubechoman	<location>岩手県大船渡末崎町</location>の<person>山本</person>えつ子さんと連絡がついたようです。有難うございました。 #save_iwate #ofunato #ohfunato #anpi	L (これがtweet)
 import sys, re
 def get_prev_hash_list(dic_hash, fname):
   key = ""
   for l in open(fname):
     l = l.rstrip()
     if re.match('#', l):
       key = l
     elif re.match('\t', l):
       l = l.lstrip('\t')
       if key not in dic_hash.keys():
   dic_hash[key] = [l]
       else:
   dic_hash[key].append(l)
     else:
       print l
       sys.exit('    Cannot reach here')
 def make_new_hash_list(dic_hash, fn_data):
   prog = re.compile('(#[^ \t#]*)[ \t\r\n]?')
   for t in open(fn_data):
     # For all hashes, convert a sequence of non-alphabet characters into a single underbar
     # And if it is at the end of a hash name, remove it.
     for h in prog.findall(t):
       nor = re.sub('[^#a-zA-Z]+', '_', h)
       nor = nor.rstrip('_')
       if nor not in dic_hash.keys():
   dic_hash[nor] = [t.rstrip()]
       else:
   dic_hash[nor].append(t.rstrip())
 def print_usage(command):
   print "Usage: {0} <tweet file> [previously created hash list]".format(command)
 if __name__ == '__main__':
   fn_data = ""
   dic_hash = dict()
   if len(sys.argv) == 2:
     fn_data = sys.argv[1]
   elif len(sys.argv) == 3:
     fn_data = sys.argv[1]
     get_prev_hash_list(dic_hash, sys.argv[2])
   else:
     print_usage(sys.argv[0])
     sys.exit(1)
   # 1. Make a set of unique hash names
   make_new_hash_list(dic_hash, fn_data)
   for k in dic_hash.keys():
     print k
     for v in dic_hash[k]:
       print '\t' + v
       

@priancho 2011年3月17日 (金) 08:50 (JST)


タグ付け中に見つけた地名など

kyteaで分類できない地名などを記入してください。

ツイートの表現する安否情報についてのタグ(U/S/I/L/M/P/O/H/R)を付与する

安否情報タグは本質的に複数付く可能性があるので、IS... のように可能なものを列挙する。

タグ付けのFAQ

タグ定義を整理中。揺れにくそうなものから書いています。

  • I=私が本人である
  • L=固有名詞(人名/組織名/地域名)が生きているという情報を入手した
  • P=固有名詞(人名/組織名/地域名)が亡くなっているという情報を入手した
  • M=固有名詞(人名/組織名/地域名)の安否情報を探している
  • H=救助要請(誰々が何処何処に取り残されています、等)
  • S=固有名詞以外(「親」などの一般名詞)の安否情報について。もしくは地域の広い意味での情報を探している。
  • R=安否情報・人名リスト等の外部リンクがある(LPRなど、曖昧性が出る場合があるので注意)
  • O=この情報は安否情報ではない
  • U=安否情報かどうか判断がつかない
各ラベルが付けられるツイートの例
ラベル 定義(簡略化。上のリストと齟齬があれば上記を優先してください) 例文
I 私が本人である 〇〇市の××です。無事です。
L 誰かが生きているという情報を入手した 〇〇市の××さんは△△避難所にいらっしゃるようです。無事です。
P 誰かが亡くなっているという情報を入手した 例文P
M 誰かの安否情報を探している 大船渡町XXに住んでいたXXXXさんの安否が確認できずにおります。
H 救助要請 誰々が何処何処に取り残されています
S 特定できない個人の安否情報(探している、判明している)。
もしくは、地域の広い意味での情報を探している
いわき市南部に下宿していた親戚の安否が分からない
○○小学校に避難されている方々に物資は足りていますか?
O このツイートは安否情報ではない 被災者の安否について情報提供できる方はこちらのサイトで!
R 安否情報・人名リスト等の外部リンクがある 〇〇市の安否確認リストですhttp://〜(この場合ラベルはLR)
U 安否情報かどうか判断がつかない 例文U

どうやって

  1. 上記の安否情報関連ツイートコーパスをタグ付けする。(人手でかなりタグ付けできそうなので、タグ付け結果を修正するWeb上のツールを作ろうと思います。@masaoutiyama
  2. anpiレポートから自動的に訓練データをつくる。(どなたかお願いします)
    • anpiレポートから訓練データをつくってみました(by 東工大 笹野 @cacaho
      • 使用したレポートは1〜3408までで、元ツイートが取得でき、タグ付け対象の人名が元ツイートに含まれ、安否に関するタグが付与されているデータのみを使用
      • 「問」が付与されている場合はMラベル、「確」が付与されている場合はLラベルを付与し、同一の元ツイートに関するレポートはマージ
      • "お名前"、"読み"(カタカナに変換した文字列も含む)にマッチした箇所にpersonタグ、"おところ"にマッチした箇所にlocationタグを付与(いずれも後方最長マッチ)
個人用ツール