安否情報ツイートへのタグ付与
提供:ANPI_NLP
安否情報ツイートに固有表現・安否情報タグを人手・半自動で付与する方法や作業の進捗についてのページです。
目次 |
分担
担当がかぶらないように、行番号の mod(剰余)で分担することにしました。作業を始める前にここに担当部分を書きこんでください。
※注 1行目 = 1 で計算してください(Base 1)
- mod 100 = 0:楽天技研 萩原 @mhagiwara
- mod 100 = 1:東工大 橋本 @taiichi84
- mod 100 = 2:NICT 内山 @masaoutiyama
- mod 100 = 3:京大 グラム @neubig
- mod 100 = 4:未来大 藤田 @akf
- mod 100 = 5:NII 松林 @Yucchiiro
- mod 100 = 6:楽天技研 山田 @violairline
- mod 100 = 7: 東大 江原 @niam
- mod 100 = 8: 琉大 當間 @naltoma
- mod 100 = 9: 東大 北川 @k_kitagawa
- mod 100 = 10: 東大 岡崎 @chokkanorg
- mod 100 = 11: 東大 松原 @whym
- mod 100 = 12: 広島市立大 難波、石野、矢舖 @presri, @ayayan1028, @takajin3
- mod 100 = 13: 大阪市立大 井上 @akinoue
- mod 100 = 14: ミクシィ 木村 @kimuras
- mod 100 = 15: 東大 清水 @nobuyukishimizu
- mod 100 = 16:楽天技研 山田 @violairline
- mod 100 = 17: 金山 @kanayama_h
- mod 100 = 18: 海野 @unnonouno
- mod 100 = 19: 東大 宇佐美 @yusmi
- mod 100 = 20: 西山 @chopstickexe
- mod 100 = 21: 東大 清水2 @kakenman
- mod 100 = 22: 長岡技科大 井手上 @jewel_x12
- mod 100 = 23: 有賀 @chezou
- mod 100 = 24: 渡辺 @TeaTown
- mod 100 = 25: 東大 花元 @mogella
- mod 100 = 26:未来大 藤田 @akf
- mod 100 = 27:ヤフー 町永 @machy
- mod 100 = 28:PFI 徳永 @tkng
- mod 100 = 29:豊橋技科大 山本 @y_yammt
- mod 100 = 30:早大 相川 @awakia
- mod 100 = 31: 東大 猿渡 @saru
- mod 100 = 32:豊橋技科大 小林 @gun_smith
- mod 100 = 33:坪井 @yuutat
- mod 100 = 34:豊橋技科大 坂地 @tetsuwaka
- mod 100 = 35:黒田 @ymku
- mod 100 = 36:東大 佐藤 @issei_sato
- mod 100 = 37:風間 @jun1kaz
- mod 100 = 38 : 東大 古田 @furushchev
- mod 100 = 39 : 岩倉
- mod 100 = 40 : 東工大 久保 @beatinaniwa
- mod 100 = 41 : 平尾
- mod 100 = 42: 長岡技科大 井手上 @jewel_x12
- mod 100 = 43: 小林(の)
- mod 100 = 44: NAIST 水本 @tomo_wb
- mod 100 = 45: 数原 @sleepy_yoshi
- mod 100 = 46: 東大 三輪 @mmiwa
- mod 100 = 47: 琉大 与儀 @yogisuzu
- mod 100 = 48: 京大 金丸 @kana0355
- mod 100 = 49: 東工大 横野 @hikaruy
- mod 100 = 50: 東工大 飯田
- mod 100 = 51: 東大 横井 @hoshi_kei
- mod 100 = 52: NAIST 小嵜 @smly
- mod 100 = 53: 東大 岩澤 @iwsw_kett
- mod 100 = 54: NAIST 吉田 @syou6162
- mod 100 = 55: NAIST 田尻 @pavlocat
- mod 100 = 56: 東大 大岩 @kisa12012
- mod 100 = 57: SUNY/京大 淺尾 @asaokitan
- mod 100 = 58: SUNY/京大 中川 @nakagawanatsuko
- mod 100 = 59: NAIST 大木 @o_bon
- mod 100 = 60: 筑波大 角田 @lpm11
- mod 100 = 61: 東工大 飯田
- mod 100 = 62: 荻野(紫) @sogido
- mod 100 = 63: NAIST/東北大 水野 @jmizuno
- mod 100 = 64: NAIST 浅原
- mod 100 = 65: 東大 亀田 @cm3
補助ツール
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 |
どうやって
- 上記の安否情報関連ツイートコーパスをタグ付けする。(人手でかなりタグ付けできそうなので、タグ付け結果を修正するWeb上のツールを作ろうと思います。@masaoutiyama )
- anpiレポートから自動的に訓練データをつくる。(どなたかお願いします)
- anpiレポートから訓練データをつくってみました(by 東工大 笹野 @cacaho)
- 使用したレポートは1〜3408までで、元ツイートが取得でき、タグ付け対象の人名が元ツイートに含まれ、安否に関するタグが付与されているデータのみを使用
- 「問」が付与されている場合はMラベル、「確」が付与されている場合はLラベルを付与し、同一の元ツイートに関するレポートはマージ
- "お名前"、"読み"(カタカナに変換した文字列も含む)にマッチした箇所にpersonタグ、"おところ"にマッチした箇所にlocationタグを付与(いずれも後方最長マッチ)
- anpiレポートから訓練データをつくってみました(by 東工大 笹野 @cacaho)