ノート/テキストマイニング

>> 前のページは ノート/テキストマイニング/oregano5

2014-01-29 CaboChaによる係り受け解析の利用 〜 文の主節の骨組を取り出す

この技術の文脈

2013年度の卒論で、ニュース記事をツイッタ−の長さに要約できないか、という課題があって、その要約手法の1つとして、文の主節の骨組のみを取り出したい。

イメージとしては、

東邦大学の教育は、創立者のひとり額田晉の著書「自然・生命・人間」に記されているように、
自然に対する畏敬、生命の尊厳の自覚、人間の謙虚な心を原点として、豊かな人間性と
均衡のとれた知識を有する人材の育成を目標としています

から

主語 教育は、
目的語 育成を
目的語 目標と
述語 しています

程度を抜き出すことで、この文の骨格が

(何やらの)教育は、(何やらを)目標と しています

であることがわかるという考え方である。

もうひとつ例を挙げると、

ウィキペディア(英: Wikipedia)は、ウィキメディア財団が運営しているインターネット百科事典である。

に対して

主語 (英:Wikipedia)は、
述語 インターネット百科事典である。

を取り出すことができる。

また別の例として

 29日午前6時50分ごろ、埼玉県川越市藤間の東武東上線の踏切内で、新木場発川越市行き
下り普通列車(10両編成)が軽乗用車に衝突した。軽乗用車に人は乗っておらず、
列車の乗客約300人にけがはなかった。

これに対して

主語 けがは
目的語 約300人に
述語 なかった。

を得る。

見て分かる通り、これが役に立つ結果を生成するかどうかは、元の文の構造による。英文を翻訳したようなウィキペディアの文では非常にうまく行っているが、和文ではなかなか難しいという点を、後で例示する。

係り受け解析CaboChaを使うと、

ウィキペディア(英: Wikipedia)は、ウィキメディア財団が運営しているインターネッ ト百科事典である。
                          ウィキペディア-------D
 (<ARTIFACT>英:Wikipedia</ARTIFACT>)は、-----D
                        ウィキメディア財団が-D |
                                  運営している-D
                  インターネット百科事典である。

* 0 4D 0/0 0.000000
ウィキペディア  名詞,一般,*,*,*,*,*     O
* 1 4D 3/5 0.000000
(      記号,括弧開,*,*,*,*,(,(,(    O
英      名詞,固有名詞,地域,国,*,*,英,エイ,エイ  B-ARTIFACT
:       名詞,サ変接続,*,*,*,*,* I-ARTIFACT
Wikipedia       名詞,固有名詞,組織,*,*,*,*      I-ARTIFACT
)      記号,括弧閉,*,*,*,*,),),)    O
は      助詞,係助詞,*,*,*,*,は,ハ,ワ    O
、      記号,読点,*,*,*,*,、,、,、      O
* 2 3D 1/2 2.041966
ウィキメディア  名詞,一般,*,*,*,*,*     O
財団    名詞,一般,*,*,*,*,財団,ザイダン,ザイダン        O
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ O
* 3 4D 1/3 0.000000
運営    名詞,サ変接続,*,*,*,*,運営,ウンエイ,ウンエイ    O
し      動詞,自立,*,*,サ変・スル,連用形,する,シ,シ      O
て      助詞,接続助詞,*,*,*,*,て,テ,テ  O
いる    動詞,非自立,*,*,一段,基本形,いる,イル,イル      O
* 4 -1D 2/4 0.000000
インターネット  名詞,一般,*,*,*,*,インターネット,インターネット,インターネット  O
百科    名詞,一般,*,*,*,*,百科,ヒャッカ,ヒャッカ        O
事典    名詞,一般,*,*,*,*,事典,ジテン,ジテン    O
で      助動詞,*,*,*,特殊・ダ,連用形,だ,デ,デ   O
ある    助動詞,*,*,*,五段・ラ行アル,基本形,ある,アル,アル       O
。      記号,句点,*,*,*,*,。,。,。      O

のような結果が得られる。

これの中から、句を抽出し、句がリンクしている先(係り受けの係り先)を見る。

係り先が -1 である句は、主節の述語部分であるから、まずこの句を抽出する。

次に、係り先が上記の主節の述語句であるような句を抽出する。

これらの句の性質を見たい。(名詞+助詞)のような形の句の性質は助詞の性質、たとえば格助詞・係助詞によって見ることができる。つまり、(述語句に対する)係助詞であれば主語句になるし、格助詞であればたとえば目的語句になっているはずである。そこで、助詞を取り出してその助詞の性質が係助詞・格助詞であるかどうかを見る。

そのようにして

0 ウィキペディア 4 0 0
1 (英:Wikipedia)は、 4 1 0
2 ウィキメディア財団が 3 0 1
3 運営している 4 0 0
4 インターネット百科事典である。 -1 0 0

の5つの句(句は<句番号, テキスト, リンク先, 係助詞であるか, 格助詞であるか>と記述してある)について分析すると、

プログラムは次のようにして見た。

#!/usr/bin/python
# coding: utf-8
import CaboCha
import sys
import codecs

sys.stdout = codecs.getwriter('utf_8')(sys.stdout)

c = CaboCha.Parser ()

#sentence = "太郎と花子は2003年、奈良先端大を卒業した。"
#sentence = "この本はとても難しいので、読むのをあきらめた。"
sentence = raw_input('>')
print sentence

tree =  c.parse (sentence)
size = tree.size()
print tree.toString(2)    # 適当にフォーマット2で全体を書いてみる(本論とは関係 せず)

print "=================="    # ここから

myid = 0        # 項目のカウンター
ku_list = []
ku = ''
ku_id = 0
ku_link = 0
kakari_joshi = 0
kaku_joshi = 0

for i in range (0, size):
    token = tree.token (i)

    if token.chunk:
        if (ku!=''):
            ku_list.append((ku, ku_id, ku_link, kakari_joshi, kaku_joshi))  #前 の句をリストに追加
        kakari_joshi = 0
        kaku_joshi = 0
        #print ku, ku_id, ku_link
        #print "* ", myid, "link=", token.chunk.link, "headpos=", token.chunk.head_pos,\
       #   "funcpos=", token.chunk.func_pos, "tokensize=", token.chunk.token_size,\
        #   "tokenpos=", token.chunk.token_pos, "score=", token.chunk.score,\
        #   "featurelistsize=", token.chunk.feature_list_size
        ku = token.normalized_surface
        ku_id = myid
        ku_link = token.chunk.link
        myid=myid+1
    else:
        #print "    Not in token.chunk"
        ku = ku + token.normalized_surface
    #print "surface=", token.normalized_surface, "feature=", token.feature, "ne=",  token.ne
    m = (token.feature).split(',')
    if (m[1] == u'係助詞'):
       kakari_joshi = 1
    if (m[1] == u'格助詞'):
       kaku_joshi = 1

    #print "---"

ku_list.append((ku, ku_id, ku_link, kakari_joshi, kaku_joshi))  # 最後にも前の句をリストに追加

print "--------"
for k in ku_list:
    print k[1], k[0], k[2], k[3], k[4]
    if (k[2]==-1):  # link==-1?      # 述語である
        jutsugo_id = ku_id  # この時のidを覚えておく

print "述語句"
for k in ku_list:
    if (k[1]==jutsugo_id):  # jutsugo_idと同じidを持つ句を探す
        print k[1], k[0], k[2], k[3], k[4]
print "--------"
print "述語句に係る句"
for k in ku_list:
    if (k[2]==jutsugo_id):  # jutsugo_idと同じidをリンク先に持つ句を探す
        print k[1], k[0], k[2], k[3], k[4]

print "--------"
print "その中から、主語句と目的語句を選ぶ"
for k in ku_list:
    if (k[2]==jutsugo_id):  # jutsugo_idと同じidをリンク先に持つ句を探す
        #print k[0], k[1], k[2], k[3], k[4]
        if (k[3] == 1):
             print "主語", k[0]
        if (k[4] == 1):
             print "目的語", k[0]
    if (k[1] == jutsugo_id):
         print "述語", k[0]

幅広く試してみる

この原理をいろいろな例文に対して幅広く試してみよう。

日本語の文には主語が無い。その場合には主語が現れない。

これに基づき、各学部それぞれに特色を持った目標を掲げ、施設・設備の充実、優秀な教員の登用などにより、
時代の要請に応じたより高度な教育・研究体制を整備しながら目標達成に努めています。

に対して

目的語 目標達成に
述語 努めています。

を得る。

また、パラレル(並置)の文では、

東邦大学は、2012(平成24)年度の(公財)大学基準協会の大学評価(認証評価)を受審し、
大学基準に適合しているとの認定を受けました。

に対して

主語 東邦大学は、
目的語 認定を
述語 受けました。

のように、前半の部分が欠けてしまう。

次に、述語を含まない例として

認定期間は2020(平成32)年3月31日まで。

だと、CaboChaは

主語 認定期間は
述語 31日まで。

を拾っている。

体言止めの例

1機は機体前部、もう1機は機体後部の左側にある水平安定板を損傷。

に対しては

主語 1機は
主語 1機は
目的語 水平安定板を
述語 損傷。

防衛省に入った連絡によると、29日午前11時24分頃、航空自衛隊松島基地(宮城県)の南東
約45キロの太平洋上で、訓練飛行をしていた空自のT4練習機2機が空中で接触した。

では、主語が無いが

目的語 2機が
目的語 空中で
述語 接触した。

を得る。

かなり長い例

米テキサス州在住の女性が16歳になった誕生日を祝って人生初のスカイダイビングに挑戦したが、
高度約1000メートルの上空でパラシュートに異常が発生し、地面にたたきつけられるという
アクシデントがあった。

目的語 アクシデントが
述語 あった。

短い例

命に別状はないという。

に対しては

目的語 ないと
述語 いう。

となり、ポイントが失われてしまった。

複数の文を並べてみると結構読めるかも知れないということ

Web上にあるニュース記事を入れてみる。改行を取り除いて入れる必要がある。ちょっとだけ改造してみた。

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

import CaboCha
import sys
import codecs
sys.stdout = codecs.getwriter('utf_8')(sys.stdout)

global c
##c = CaboCha.Parser (sys.argv)
c = CaboCha.Parser ()

def treeparse(sentence):
  #sentence = "太郎はこの本を二郎を見た女性に渡した。"
  #sentence = "太郎と花子は2003年、奈良先端大を卒業した。"
  #sentence = "この本はとても難しいので、読むのをあきらめた。"

  tree =  c.parse (sentence)
  size = tree.size()
  #print tree.toString(2)    # 適当にフォーマット2で全体を書いてみる(本論とは関係せず)

  myid = 0        # 項目のカウンター
  ku_list = []
  ku = ''
  ku_id = 0
  ku_link = 0
  kakari_joshi = 0
  kaku_joshi = 0
  shugo = ''
  mokutekigo = ''
  jutsugo = ''

  for i in range (0, size):
    token = tree.token (i)

    if token.chunk:
        if (ku!=''):
            ku_list.append((ku, ku_id, ku_link, kakari_joshi, kaku_joshi))  #前 の句をリストに追加
        kakari_joshi = 0
        kaku_joshi = 0
        #print ku, ku_id, ku_link
        #print "* ", myid, "link=", token.chunk.link, "headpos=", token.chunk.head_pos,\
        #   "funcpos=", token.chunk.func_pos, "tokensize=", token.chunk.token_size,\
        #   "tokenpos=", token.chunk.token_pos, "score=", token.chunk.score,\
        #   "featurelistsize=", token.chunk.feature_list_size
        ku = token.normalized_surface
        ku_id = myid
        ku_link = token.chunk.link
        myid=myid+1
    else:
        #print "    Not in token.chunk"
        ku = ku + token.normalized_surface
    #print "surface=", token.normalized_surface, "feature=", token.feature, "ne=",  token.ne
    m = (token.feature).split(',')
    if (m[1] == u'係助詞'):
       #print "主語", ku, ku_id, ku_link
       kakari_joshi = 1
    if (m[1] == u'格助詞'):
       #print "目的語", ku, ku_id, ku_link
       kaku_joshi = 1

    #print "---"

  ku_list.append((ku, ku_id, ku_link, kakari_joshi, kaku_joshi))  # 最後にも前の句をリストに追加

  # print "EOS"

  for k in ku_list:
    print k[1], k[0], k[2], k[3], k[4]
    if (k[2]==-1):  # link==-1?
        #print "述語", ku, ku_id, ku_link
        jutsugo_id = ku_id  # この時のidを覚えておく

  print "--------"
  print "述語句"
  for k in ku_list:
    if (k[1]==jutsugo_id):  # jutsugo_idと同じidを持つ句を探す
        print k[1], k[0], k[2], k[3], k[4]
  print "--------"
  print "述語句に係る句"
  for k in ku_list:
    if (k[2]==jutsugo_id):  # jutsugo_idと同じidをリンク先に持つ句を探す
        print k[1], k[0], k[2], k[3], k[4]

  print "--------"
  print "その中から、主語句と目的語句を選ぶ"
  for k in ku_list:
    if (k[2]==jutsugo_id):  # jutsugo_idと同じidをリンク先に持つ句を探す
        #print k[0], k[1], k[2], k[3], k[4]
        if (k[3] == 1):
             print "主語", k[0]
             shugo = shugo + ' ' + k[0]
        if (k[4] == 1):
             print "目的語", k[0]
             mokutekigo = mokutekigo + ' ' + k[0]
    if (k[1] == jutsugo_id):
         print "述語", k[0]
         jutsugo = jutsugo + ' ' + k[0]

  return (shugo + ' ' + mokutekigo + ' ' + jutsugo)

# MAIN PROGRAM

output = ''
sentence = raw_input('>')
#print sentence
s = sentence.split(u'。')
for x in s:
  if (x != ''):
    #print '['+x+']'
    u = treeparse(str(x))
    if (output==''):
         output = u
    else:
         output = output + ' /  ' + u

print '==='
print output

入力は、処理が面倒なので改行があってはならないとしてしまった。中で改行を取り除いてもいい。

次の例を試す。

29日午前6時50分ごろ、埼玉県川越市藤間の東武東上線の踏切内で、新木場発川越市行き
下り普通列車(10両編成)が軽乗用車に衝突した。軽乗用車に人は乗っておらず、列車の
乗客約300人にけがはなかった。 県警によると、軽乗用車を運転していた女性会社員(41)
が踏切手前で車を止め、近くの郵便ポストに手紙を入れるため降車。その後、車だけが動き出して
踏切内に進入し、列車と衝突したという。この事故の影響で、東武東上線の志木―小川町間で
4時間半にわたり運転を見合わせた。また、同線が乗り入れている東京メトロ有楽町線と副都心線など、
都内の地下鉄の運行ダイヤにも乱れが出ている。

に対して、

踏切内で、 (10両編成)が 軽乗用車に  衝突した /
けがは  約300人に  なかった /
降車 /                    「女性会社員が」が取れないのがなぜか不明
列車と  衝突したという /
影響で、 志木-小川町間で 4時間半にわ たり 運転を  見合わせた /
運行ダイヤにも  運行ダイヤにも 乱れが  出ている

を得た。これは不完全ながらも意味が分かる。

次の例は、

他国の潜水艦を捜索する作戦に備え、海中や海底の状況を調べる海上自衛隊の無人潜水装置(ROV)1機が
昨年11月、津軽海峡でなくなっていたことが、海自への取材でわかった。海洋観測艦「にちなん」と
ケーブルでつないで遠隔操作中にケーブルが切れたためで、海自は事故調査委員会を設置して
原因究明を進めている。日本近海で中国潜水艦の活動が活発化する中、海洋データの収集に
影響が出る可能性がある。 海自によると、ROVをなくしたのは昨年11月30日午後2時ごろ。
艦艇数隻が9日間にわたって捜索したが、見つからなかった。収集したデータは「にちなん」に
保管されるため、自衛隊以外が回収してもデータの流出はなく、環境への影響もないという。 
価格は約5億円。長さ3メートル、幅と高さは1・7メートル、重さは4トンほどとみられ、
カメラや音波探知機(ソナー)などを備えているとされる。海上幕僚監部広報室は取材に
「水中状況の確認などに使うが、詳細は言えない」としている。

に対して、

ことが、 取材で  わかった /          取り出し方は正しいのだが、内容が不足している
海自は  ためで、 原因究明を  進めている /   これも「ためで」の内容が不足している
可能性が  ある /               これも取り出し方は正しいが、可能性の内容が不足している
なくしたのは   2時ごろ /
見つからなかった /
データは 影響も   ないという /
価格は   約5億円 /
備えていると  される /            これも内容が不足して意味が分からない
海上幕僚監部広報室は  取材に 言えない」と  している

これは、かなり意味が取りづらい。

次の例は、

防衛省に入った連絡によると、29日午前11時24分頃、航空自衛隊松島基地(宮城県)の
南東約45キロの太平洋上で、訓練飛行をしていた空自のT4練習機2機が空中で接触した。
1機は機体前部、もう1機は機体後部の左側にある水平安定板を損傷。2機はこの4〜10分後、
同基地に緊急着陸した。空自は調査官を現地に派遣し、事故原因を調べる。2機は、空自唯一の
曲技飛行専門チーム「ブルーインパルス」に所属している。

に対して

2機が 空中で  接触した /
1機は 1機は  水平安定板を  損傷 /   2文の並列に対応できない
2機は  同基地に  緊急着陸した /
空自は  事故原因を  調べる /
2機は、  「ブルーインパルス」 に  所属している

である。かなり分かるように思う。

次の例は、

[ダラス 28日 ロイター] -米テキサス州在住の女性が16歳になった誕生日を祝って人生初の
スカイダイビングに挑戦したが、高度約1000メートルの上空でパラシュートに異常が発生し、
地面にたたきつけられるというアクシデントがあった。命に別状はないという。九死に一生を得たのは
マッケンジー・ウェシントンさんで、家族が28日に現地テレビ局に語ったところによると、
骨盤や椎骨、肋骨を折るなどして病院に運ばれた。テキサス州ではスカイダイビングができるのは
18歳以上となっているため、ウェシントンさんは16歳でも飛ぶことができるオクラホマ州まで
出向いたという。現地で6時間の訓練を受けたウェシントンさんは、小型機に乗り込んで空に向かい、
先に飛び降りて無事着地した父親が見守る中、生まれて初めてのスカイダイビングに飛び出した。
父親のジョーさんはダラスのテレビ局NBC5に対し、「万が一に備えて娘より後に飛びたかったが、
飛行機と人間の重量の関係で自分が最初、娘が最後になった」と語った。パラシュートは
飛び降りてから約2秒後に出てきたが、ウェシントンさんはらせん状態で落下し、草地の地面に
激突したという。スカイダイビング運営会社は、異常が起きた原因はまだ明らかになっていないとしている。

に対して、

アクシデントが  あった /
別状は  命に  ないという /
病院に  運ばれた /
できるのは ウェシントンさんは   出向いたという /   「できるのは」の「は」の誤認識だろう
ウェシントンさんは、  スカイ ダイビングに  飛び出した /
ジョーさんは  テレビ局NBC5に対し、 なった」と  語った /
地面に  激突したという /
スカイダイビング運営会社は、  明らかになって いないと  している

もう1つの例では

米国で妻への監禁、暴行容疑で逮捕され、保釈されて28日に来日したヤクルトのウラディミール・
バレンティン外野手が29日、都内の球団事務所で会見を行い、あらためて一連の騒動について謝罪した。
「私が今できることは謝罪することだけ。いいプレーをすることで、子どもたちやファンに謝罪の
代わりとしたい」と神妙な面持ちで語った。 30日にチームと一緒に沖縄・浦添へ入り、
春季キャンプに備える。騒動の影響で練習不足も懸念されているが「例年に比べれば体はできていないが、
キャンプが終わるころにはベストになる」と、周囲の不安を一蹴。目標を問われ「優勝すること。
(個人的には)去年と同等、それ以上の活躍をして勝利に貢献したい」と語った。

に対して

ウラディミール・バレンティン外野手が 騒動について  謝罪した /
ことは   謝罪 することだけ /      引用符「 」の扱いが正しくないらしい
したい」と 面持ちで  語った /      同上
春季キャンプに  備える /
なる」と、 不安を  一蹴 /
こと /    貢献したい」と  語った

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2014-01-29 (水) 17:44:14 (1395d)