第5章: 係り受け解析
日本語Wikipediaの「人工知能」に関する記事からテキスト部分を抜き出したファイルがai.ja.zipに収録されている. この文章をCaboChaやKNP等のツールを利用して係り受け解析を行い,その結果をai.ja.txt.parsedというファイルに保存せよ.このファイルを読み込み,以下の問に対応するプログラムを実装せよ
ここでは、Google Colaboratory上でCaboChaを使用できる環境を整えます。
以下のサイトを参考にしています。
google colaboratoryでCaboChaを使うための環境構築
CaboChaを動かすには、以下のツールをインストールが必要条件です。
・ CRF++ (0.55以降)
・ MeCab (0.993以降)
・ mecab-ipadic, mecab-jumandic, unidic のいずれか
Mecab等、CaboChaに必要なライブラリをインストール
!apt install -y \
curl \
file \
git \
libmecab-dev \
make \
mecab \
mecab-ipadic-utf8 \
swig \
xz-utils
MeCabのPython用ライブラリをインストール
!pip install mecab-python3
CRF++のインストール
import os
filename_crfpp = 'crfpp.tar.gz'
!wget "https://drive.google.com/uc?export=download&id=0B4y35FiV1wh7QVR6VXJ5dWExSTQ" -O $filename_crfpp
!tar zxvf $filename_crfpp
%cd CRF++-0.58
!./configure
!make
!make install
%cd ..
os.environ['LD_LIBRARY_PATH'] += ':/usr/local/lib'
CaboChaのインストール
FILE_ID = "0B4y35FiV1wh7SDd1Q1dUQkZQaUU"
FILE_NAME = "cabocha.tar.bz2"
!wget --load-cookies /tmp/cookies.txt "https://docs.google.com/uc?export=download&confirm=$(wget --quiet --save-cookies /tmp/cookies.txt --keep-session-cookies --no-check-certificate 'https://docs.google.com/uc?export=download&id=$FILE_ID' -O- | sed -rn 's/.*confirm=([0-9A-Za-z_]+).*/\1\n/p')&id=$FILE_ID" -O $FILE_NAME && rm -rf /tmp/cookies.txt
%cd content
!tar -xvf cabocha.tar.bz2
%cd cabocha-0.69
!./configure --with-mecab-config=`which mecab-config` --with-charset=UTF8
!make
!make check
!make install
%cd ..
!cabocha --version
CaboChaのPython用ライブラリをインストール
%cd cabocha-0.69/python
!python setup.py build_ext
!python setup.py install
!ldconfig
%cd ../..
演習に使用するデータをダウンロードして解凍
!wget https://nlp100.github.io/data/ai.ja.zip
!unzip ai.ja.zip
%ls
ai.ja.txt cabocha-0.69/ CRF++-0.58/ readme.ai.ja.md
ai.ja.zip cabocha.tar.bz2 crfpp.tar.gz sample_data/
40. 係り受け解析結果の読み込み(形態素)
形態素を表すクラスMorphを実装せよ.このクラスは表層形(surface),基本形(base),品詞(pos),品詞細分類1(pos1)をメンバ変数に持つこととする.さらに,係り受け解析の結果(ai.ja.txt.parsed)を読み込み,各文をMorphオブジェクトのリストとして表現し,冒頭の説明文の形態素列を表示せよ.
CaboChaの使い方は、CaboCha & Python3で文節ごとの係り受けデータ取得にわかりやすくまとめられており、参考にさせていただきました。
上記のサイトに、CaboChaでparseした時のデータ構造が下記のように掲載されています。
tree
└ token
└ chunk ←ない場合(NULL)もある
│ └ link ←かかり先ID
│ └ head_pos ←主辞
│ └ func_pos ←機能語
│ └ score ←スコア
└ surface ←形態素部分(元の文字列)
└ feature ←品詞や読みなど形態素の情報部分
└ ne ←??
class Morph: # 形態素クラス
def __init__(self, surface, base, pos, pos1):
# 表層形
self.surface = surface
# 基本形
self.base = base
# 品詞
self.pos = pos
# 品詞細分類
self.pos1 = pos1
def __str__(self):
return "表層形:{} \t基本形:{} \t品詞:{} \t細分類:{}".format(self.surface, self.base, self.pos, self.pos1)
import CaboCha
c = CaboCha.Parser()
with open("ai.ja.txt", mode='r') as f:
lines = f.readlines()
mstMorphList = []
for sentence in lines:
if sentence==None: # 空行はとばす
continue
# 1文ずつ解析
tree = c.parse(sentence)
for i in range(0, tree.size()):
token = tree.token(i)
if len(token.feature) > 0: # feature情報があればfeatureを取得
feature = token.feature.split(',')
if len(feature) > 7: # 長さチェック
mstMorphList.append(Morph(token.surface, feature[6], feature[0], feature[1]))
for morph in mstMorphList:
print(morph)
<出力>
・・・(省略)・・・
表層形:と 基本形:と 品詞:助詞 細分類:格助詞
表層形:答え 基本形:答える 品詞:動詞 細分類:自立
表層形:て 基本形:て 品詞:助詞 細分類:接続助詞
表層形:いる 基本形:いる 品詞:動詞 細分類:非自立
表層形:。 基本形:。 品詞:記号 細分類:句点
41. 係り受け解析結果の読み込み(文節・係り受け)
40に加えて,文節を表すクラスChunkを実装せよ.このクラスは形態素(Morphオブジェクト)のリスト(morphs),係り先文節インデックス番号(dst),係り元文節インデックス番号のリスト(srcs)をメンバ変数に持つこととする.さらに,入力テキストの係り受け解析結果を読み込み,1文をChunkオブジェクトのリストとして表現し,冒頭の説明文の文節の文字列と係り先を表示せよ.本章の残りの問題では,ここで作ったプログラムを活用せよ.
# 40のつづき
class Chunk: # 文節クラス
def __init__(self, morphs, idx, dst, srcs=None):
# 形態素リスト
self.morphs = morphs
# 自身のインデックス番号
self.idx = idx
# 係り先文節インデックス番号
self.dst = dst
# 係り元文節インデックス番号リスト
self.srcs = srcs
def __str__(self):
return "形態素リスト:{}\t係り先文節インデックス番号:{}\t係り元文節インデックス番号リスト:{}".format(morphs, dst, srcs)
class Sentence: # 文クラス
def __init__(self, chunks):
# 文節リスト
self.chunks = chunks
def __str__(self):
ret += (str(chunk) for chunk in self.chunks)
return ret
# 文節追加メソッド
def AddChunk(self, chunk):
if self.chunks==None:
self.chunks = [chunk]
else:
self.chunks.append(chunk)
# 文節リスト取得メソッド
def GetChunks(self):
return self.chunks
import CaboCha
c = CaboCha.Parser()
with open("ai.ja.txt", mode='r') as f:
sentences = f.readlines()
SentenceList=[]
for sentence in sentences: # 1文ずつ解析
tree = c.parse(sentence)
chunkId = -1
text = ""
toChunkId = -1
MorphList=[]
ChunkIdList=[]
dictChunkSource={}
_sentence = Sentence([])
for i in range(0, tree.size()):
token = tree.token(i)
#print(token.feature)
#print(token.chunk)
#print(token.chunk.link)
if token.chunk != None:
if toChunkId!=-1:
# 文に文節を追加
_sentence.AddChunk(Chunk(MorphList, chunkId, toChunkId, toChunkId))
MorphList=[]
toChunkId = token.chunk.link # 係り先文節インデックス番号
chunkId+=1 # 自身のインデックス番号
# 係り元文節インデックス番号リストの作成
if toChunkId in dictChunkSource:
# 係り先文節インデックス番号が辞書に登録済みなら、
# 辞書からリストを取得し、自身のインデックス番号を追加する。
lst = dictChunkSource[toChunkId]
lst.append(chunkId)
else:
# 係り先文節インデックス番号が辞書に未登録なら、
# 自身のインデックス番号を要素に持つリストを作成し、辞書に登録する。
lst = [chunkId]
dictChunkSource[toChunkId] = lst
# feature情報があれば形態素を作成してリストに追加。
if len(token.feature) > 0:
feature = token.feature.split(',')
if len(feature) > 7:
MorphList.append(Morph(token.surface, feature[6], feature[0], feature[1]))
# 文に文節を追加する。
_sentence.AddChunk(Chunk(MorphList, chunkId, toChunkId, toChunkId))
# 文中の各文節の係り元文節インデックス番号リストをセットする
for chunk in _sentence.GetChunks():
if chunk.idx in dictChunkSource:
chunk.srcs = dictChunkSource[chunk.idx]
#print(chunk.srcs)
SentenceList.append(_sentence)
for sentence in SentenceList[5:6]: # ここでは6番目の文を使用
for chunk in sentence.chunks:
if chunk.srcs!=None:
link = []
#print(chunk.srcs)
if type(chunk.srcs)==int:
link = ""
else:
link += (src for src in chunk.srcs) # 係り元文節インデックス番号リスト
print("{} → {} {}".format(str(chunk.idx), str(chunk.dst), link))
out=[]
out += ( morph.surface for morph in chunk.morphs) # 形態素リスト
print(out)
<出力>
0 → 1
['人間', 'の']
1 → 3 [0]
['知的', '能力', 'を']
2 → 3
['コンピュータ', '上', 'で']
3 → 5 [1, 2]
['実現', 'する', '、']
・・・(省略)・・・
42. 係り元と係り先の文節の表示
係り元の文節と係り先の文節のテキストをタブ区切り形式ですべて抽出せよ.ただし,句読点などの記号は出力しないようにせよ.
# 41のつづき
# 形態素の基本形から品詞を取得する関数
def CheckHinshi(strVal):
hinshi = [morph.pos for morph in mstMorphList if morph.base==strVal]
ret=""
if hinshi!=None:
ret = hinshi[0]
return ret;
for sentence in SentenceList[5:6]:
dictMap = {}
for chunk in sentence.chunks:
out=[]
out += ( morph.surface for morph in chunk.morphs if CheckHinshi(morph.base)!="記号")
dictMap[chunk.idx]="".join(out)
#print(join(out))
for chunk in sentence.chunks:
if chunk.srcs!=None:
links = []
#print(chunk.srcs)
if type(chunk.srcs)==int:
link = ""
else:
links += (dictMap[src] for src in chunk.srcs)
link = '\t'.join(links)
dst=""
if chunk.dst in dictMap:
dst = dictMap[chunk.dst]
# 文節 係り先文節 係り元文節1,2,3・・・を出力
print("{}\t{}\t{}".format(dictMap[chunk.idx], dst, link))
<出力>
人間の 知的能力を
知的能力を 実現する 人間の
コンピュータ上で 実現する
実現する 技術ソフトウェアコンピュータシステム 知的能力を コンピュータ上で
様々な 技術ソフトウェアコンピュータシステム
技術ソフトウェアコンピュータシステム ある 実現する 様々な
応用例は ある
自然言語処理 機械翻訳かな漢字変換構文解析等
機械翻訳かな漢字変換構文解析等 専門家の 自然言語処理
専門家の 推論判断を 機械翻訳かな漢字変換構文解析等
推論判断を 模倣する 専門家の
模倣する エキスパートシステム 推論判断を
エキスパートシステム 画像認識等が 模倣する
・・・(省略)・・・
43. 名詞を含む文節が動詞を含む文節に係るものを抽出
名詞を含む文節が,動詞を含む文節に係るとき,これらをタブ区切り形式で抽出せよ.ただし,句読点などの記号は出力しないようにせよ.
# 42のつづき
for sentence in SentenceList[5:6]:
dictMap = {}
for chunk in sentence.chunks:
out=[]
# 文中のすべての文節の記号以外の形態素を辞書に登録
out += ( morph for morph in chunk.morphs if CheckHinshi(morph.base)!="記号")
dictMap[chunk.idx]=out
#print(join(out))
for chunk in sentence.chunks:
target = [ morph for morph in chunk.morphs if CheckHinshi(morph.base)=="名詞"]
if target==None or len(target)==0: # 名詞が含まれてい文節は飛ばす
#print(CheckHinshi(chunk.morphs[0].base))
#print(target)
continue
# 文節中の記号以外の形態素の表層形を連結
fromWord = "".join([morph.surface for morph in dictMap[chunk.idx]])
dst=""
if chunk.dst in dictMap:
morphs = dictMap[chunk.dst]
# 係り先の文節に動詞が含まれていなければ飛ばす
target = [morph for morph in morphs if CheckHinshi(morph.base)=="動詞"]
if target==None or len(target)==0:
continue
# 文節中の記号以外の形態素の表層形を連結
toWord = "".join([morph.surface for morph in morphs])
print("{}\t{}".format(fromWord, toWord))
<出力>
知的能力を 実現する
コンピュータ上で 実現する
技術ソフトウェアコンピュータシステム ある
応用例は ある
推論判断を 模倣する
画像データを 解析して
解析して 検出抽出したりする
パターンを 検出抽出したりする
画像認識等が ある
年に 命名された
ダートマス会議で 命名された
ジョンマッカーシーにより 命名された
命名された 使われている
現在では 使われている
記号処理を 用いた
記述を する
主体と する
意味あいでも 使われている
思考ルーチンも 呼ばれる
ことも ある
###44. 係り受け木の可視化
与えられた文の係り受け木を有向グラフとして可視化せよ.可視化には,Graphviz等を用いるとよい.
graphvizはGoogle Colaboratoryでは標準で用意されているため、
pipコマンドで取得する必要はないです。
graphvizの使い方に関しては、以下のサイトに分かりやすく書かれています。
# 43のつづき
from graphviz import Digraph
# graphvizの初期設定
# 日本語で表示させるので日本語フォントを指定する
dg = Digraph(format='png')
dg.attr('node', shape='box', fontname='MS Gothic')
dg.attr('graph', fontname='MS Gothic')
for sentence in SentenceList[5:6]:
dictMap = {}
for chunk in sentence.chunks:
out=[]
# 文中の文節で、記号以外のすべての形態素を辞書に登録
out += ( morph for morph in chunk.morphs if CheckHinshi(morph.base)!="記号")
dictMap[chunk.idx]=out
# 有向グラフ
#dg.node("".join([morph.surface for morph in out]))
for chunk in sentence.chunks:
target = [ morph for morph in chunk.morphs]
if target==None or len(target)==0:
continue
# 文節中の記号以外の形態素の表層形を連結し、ノードとして登録。
fromWord = "".join([morph.surface for morph in dictMap[chunk.idx]])
dg.node(fromWord)
dst=""
# 係り先文節中の記号以外の形態素の表層形を連結し、エッジ登録。
if chunk.dst in dictMap:
morphs = dictMap[chunk.dst]
target = [morph for morph in morphs]
if target==None or len(target)==0:
continue
toWord = "".join([morph.surface for morph in morphs])
dg.edge(fromWord, toWord)
dg.render('./dgraph', view=True)
<出力>
Google Colaboratoryで、うまく図が出力されない場合、
dg
と入力して実行すると表示されます。
45. 動詞の格パターンの抽出
今回用いている文章をコーパスと見なし,日本語の述語が取りうる格を調査したい. 動詞を述語,動詞に係っている文節の助詞を格と考え,述語と格をタブ区切り形式で出力せよ. ただし,出力は以下の仕様を満たすようにせよ.
動詞を含む文節において,最左の動詞の基本形を述語とする
述語に係る助詞を格とする
述語に係る助詞(文節)が複数あるときは,すべての助詞をスペース区切りで辞書順に並べる
「ジョン・マッカーシーはAIに関する最初の会議で人工知能という用語を作り出した。」という例文を考える. この文は「作り出す」という1つの動詞を含み,「作り出す」に係る文節は「ジョン・マッカーシーは」,「会議で」,「用語を」であると解析された場合は,次のような出力になるはずである.
作り出す で は を
このプログラムの出力をファイルに保存し,以下の事項をUNIXコマンドを用いて確認せよ.
1.コーパス中で頻出する述語と格パターンの組み合わせ
2.「行う」「なる」「与える」という動詞の格パターン(コーパス中で出現頻度の高い順に並べよ)
# 44のつづき
for sentence in SentenceList[5:6]:
dictMap = {}
# 文中のすべての文節中の記号以外の形態素を辞書に登録
for chunk in sentence.chunks:
out=[]
out += ( morph for morph in chunk.morphs if CheckHinshi(morph.base)!="記号")
dictMap[chunk.idx]=out
for chunk in sentence.chunks:
target = [ morph for morph in chunk.morphs if CheckHinshi(morph.base)=="動詞"]
if target==None or len(target)==0:
continue # 文節中に動詞がなければ飛ばす
文節中の記号以外の形態素の表層形を連結
fromWord = "".join([morph.surface for morph in dictMap[chunk.idx]])
toWord=[]
# 係り元文節の助詞を抽出する。
for srcId in chunk.srcs:
if srcId in dictMap:
morphs = dictMap[srcId]
target = [morph.surface for morph in morphs if CheckHinshi(morph.base)=="助詞"]
if target==None or len(target)==0:
continue
toWord += target
# 助詞リストを昇順にソート
toWord = sorted(toWord)
print("{}\t{}".format(fromWord, " ".join(toWord)))
<出力>
実現する で を
模倣する を
解析して を
検出抽出したりする て を
ある が は
命名された で に により
用いた を
する と を
使われている で でも は
呼ばれる も
ある て も
46. 動詞の格フレーム情報の抽出
45のプログラムを改変し,述語と格パターンに続けて項(述語に係っている文節そのもの)をタブ区切り形式で出力せよ.45の仕様に加えて,以下の仕様を満たすようにせよ.
項は述語に係っている文節の単語列とする(末尾の助詞を取り除く必要はない)
述語に係る文節が複数あるときは,助詞と同一の基準・順序でスペース区切りで並べる
「ジョン・マッカーシーはAIに関する最初の会議で人工知能という用語を作り出した。」という例文を考える. この文は「作り出す」という1つの動詞を含み,「作り出す」に係る文節は「ジョン・マッカーシーは」,「会議で」,「用語を」であると解析された場合は,次のような出力になるはずである.
作り出す で は を 会議で ジョンマッカーシーは 用語を
import numpy as np
for sentence in SentenceList[5:6]:
dictMap = {}
for chunk in sentence.chunks:
out=[]
out += ( morph for morph in chunk.morphs if CheckHinshi(morph.base)!="記号")
dictMap[chunk.idx]=out
for chunk in sentence.chunks:
target = [ morph for morph in chunk.morphs if CheckHinshi(morph.base)=="動詞"]
if target==None or len(target)==0:
continue
fromWord = "".join([morph.surface for morph in dictMap[chunk.idx]])
toWord=[]
toWord2=[]
toWord3=[]
strIdx=np.array([])
# 係り元文節の助詞リストと表層形結合リストを作成
for srcId in chunk.srcs:
if srcId in dictMap:
morphs = dictMap[srcId]
target = [morph.surface for morph in morphs if CheckHinshi(morph.base)=="助詞"]
if target==None or len(target)==0:
continue
toWord += target
toWord2.append("".join([morph.surface for morph in morphs]))
#print(toWord)
# 助詞リストをソートした時のインデックスリストを取得
strIdx = np.argsort(toWord)
#print(strIdx)
#print(strIdx.shape)
# 助詞リストをソート
toWord = sorted(toWord)
# 表層形結合リストをソートして新しいリストに追加
for i in strIdx:
if len(toWord2)>i:
toWord3.append(toWord2[i])
print("{}\t{}\t{}".format(fromWord, " ".join(toWord), ' '.join(toWord3)))
<出力>
実現する で を コンピュータ上で 知的能力を
模倣する を 推論判断を
解析して を 画像データを
検出抽出したりする て を 解析して パターンを
ある が は 画像認識等が 応用例は
命名された で に により ダートマス会議で 年に ジョンマッカーシーにより
用いた を 記号処理を
する と を 主体と 記述を
使われている で でも は 現在では 意味あいでも
呼ばれる も 思考ルーチンも
ある て も 使われている ことも
47. 機能動詞構文のマイニング
動詞のヲ格にサ変接続名詞が入っている場合のみに着目したい.46のプログラムを以下の仕様を満たすように改変せよ.
「サ変接続名詞+を(助詞)」で構成される文節が動詞に係る場合のみを対象とする
述語は「サ変接続名詞+を+動詞の基本形」とし,文節中に複数の動詞があるときは,最左の動詞を用いる
述語に係る助詞(文節)が複数あるときは,すべての助詞をスペース区切りで辞書順に並べる
述語に係る文節が複数ある場合は,すべての項をスペース区切りで並べる(助詞の並び順と揃えよ)
例えば「また、自らの経験を元に学習を行う強化学習という手法もある。」という文から,以下の出力が得られるはずである.
学習を行う に を 元に 経験を
# 形態素の基本形が、サ変接続ならTrueを返す関数
def IsSahen(strVal):
hinshi = [morph.pos1 for morph in mstMorphList if morph.base==strVal]
ret=False
if hinshi!=None and len(hinshi)>0:
if hinshi[0]=="サ変接続":
ret = True
return ret;
import numpy as np
for sentence in SentenceList[5:6]:
dictMap = {}
# 文中に含まれるすべての文節の記号以外の形態素を辞書に登録
for chunk in sentence.chunks:
out=[]
out += ( morph for morph in chunk.morphs if CheckHinshi(morph.base)!="記号")
dictMap[chunk.idx]=chunk
for chunk in sentence.chunks:
target1 = [ morph.base for morph in chunk.morphs if IsSahen(morph.base)]
if target1==None or len(target1)==0:
continue # 「サ変接続」が含まれない文節は飛ばす
target2 = [ morph.base for morph in chunk.morphs if morph.base=="を"]
if target2==None or len(target2)==0:
continue # 助詞「を」が含まれなければ飛ばす。
if chunk.dst in dictMap:
chunkD = dictMap[chunk.dst]
target3 = [morph.base for morph in chunkD.morphs if CheckHinshi(morph.base)=="動詞"]
if target3==None or len(target3)==0:
continue # 係り先文節に動詞が含まれなければ飛ばす
fromWord = target1[0] + target2[0] + target3[0]
toWord=[]
toWord2=[]
toWord3=[]
toWord4=[]
strIdx=np.array([])
# 係り元文節の処理
for srcId in chunkD.srcs:
if srcId in dictMap:
morphs = dictMap[srcId].morphs
target = [morph.surface for morph in morphs if CheckHinshi(morph.base)=="助詞"]
if target==None or len(target)==0:
continue # 助詞が含まれなければ飛ばす
toWord += target
toWord2.append("".join([morph.surface for morph in morphs]))
toWord4.append("".join([morph.surface for morph in morphs if IsSahen(morph.base)]))
#print(toWord)
strIdx = np.argsort(toWord)
#print(strIdx)
#print(strIdx.shape)
toWord = sorted(toWord)
for i in strIdx:
if len(toWord2)>i:
toWord3.append(toWord2[i])
print("{}\t{}\t{}".format(fromWord, " ".join(toWord), ' '.join(toWord3)))
<出力>
推論をする を 推論・判断を
処理を用いる を 記号処理を
記述をする と を 主体と 記述を
48. 名詞から根へのパスの抽出
文中のすべての名詞を含む文節に対し,その文節から構文木の根に至るパスを抽出せよ. ただし,構文木上のパスは以下の仕様を満たすものとする.
各文節は(表層形の)形態素列で表現する
パスの開始文節から終了文節に至るまで,各文節の表現を” -> “で連結する
「ジョン・マッカーシーはAIに関する最初の会議で人工知能という用語を作り出した。」という例文を考える. CaboChaを係り受け解析に用いた場合,次のような出力が得られると思われる.
ジョンマッカーシーは -> 作り出した
AIに関する -> 最初の -> 会議で -> 作り出した
最初の -> 会議で -> 作り出した
会議で -> 作り出した
人工知能という -> 用語を -> 作り出した
用語を -> 作り出した
KNPを係り受け解析に用いた場合,次のような出力が得られると思われる.
ジョンマッカーシーは -> 作り出した
AIに -> 関する -> 会議で -> 作り出した
会議で -> 作り出した
人工知能と -> いう -> 用語を -> 作り出した
用語を -> 作り出した
import numpy as np
for sentence in SentenceList[5:6]:
dictMap = {}
# 文中の文節辞書を作成
for chunk in sentence.chunks:
dictMap[chunk.idx]=chunk
ret = ""
for chunk in sentence.chunks:
target1 = [ morph.base for morph in chunk.morphs if CheckHinshi(morph.base)=="名詞"]
if target1==None or len(target1)==0:
continue # 文節中に名詞がなければ飛ばす
# 記号でない形態素の表層形を連結
ret = "".join([morph.surface for morph in chunk.morphs if CheckHinshi(morph.base)!="記号"])
i=0
idx = chunk.dst
# 係り先文節の情報を取得して"->"で連結していく。
# 最大5階層まで
while i<5:
if idx in dictMap:
chunkD = dictMap[idx]
ret += " -> " + "".join([morph.surface for morph in chunkD.morphs if CheckHinshi(morph.base)!="記号"])
i+=1
#print(chunkD.dst)
if chunkD.dst==None or chunkD.dst<0:
break
idx = chunkD.dst
print(ret)
<出力>
人間の -> 知的能力を -> 実現する -> 技術ソフトウェアコンピュータシステム -> ある -> 命名された
知的能力を -> 実現する -> 技術ソフトウェアコンピュータシステム -> ある -> 命名された -> 使われている
コンピュータ上で -> 実現する -> 技術ソフトウェアコンピュータシステム -> ある -> 命名された -> 使われている
実現する -> 技術ソフトウェアコンピュータシステム -> ある -> 命名された -> 使われている -> ある
様々な -> 技術ソフトウェアコンピュータシステム -> ある -> 命名された -> 使われている -> ある
技術ソフトウェアコンピュータシステム -> ある -> 命名された -> 使われている -> ある
応用例は -> ある -> 命名された -> 使われている -> ある
・・・(省略)・・・
49. 名詞間の係り受けパスの抽出
文中のすべての名詞句のペアを結ぶ最短係り受けパスを抽出せよ.ただし,名詞句ペアの文節番号がiとj(i<j)のとき,係り受けパスは以下の仕様を満たすものとする.
問題48と同様に,パスは開始文節から終了文節に至るまでの各文節の表現(表層形の形態素列)を” -> “で連結して表現する
文節iとjに含まれる名詞句はそれぞれ,XとYに置換する
また,係り受けパスの形状は,以下の2通りが考えられる.
文節iから構文木の根に至る経路上に文節jが存在する場合: 文節iから文節jのパスを表示
上記以外で,文節iと文節jから構文木の根に至る経路上で共通の文節kで交わる場合: 文節iから文節kに至る直前のパスと文節jから文節kに至る直前までのパス,文節kの内容を” | “で連結して表示
「ジョン・マッカーシーはAIに関する最初の会議で人工知能という用語を作り出した。」という例文を考える. CaboChaを係り受け解析に用いた場合,次のような出力が得られると思われる.
Xは | Yに関する -> 最初の -> 会議で | 作り出した
Xは | Yの -> 会議で | 作り出した
Xは | Yで | 作り出した
Xは | Yという -> 用語を | 作り出した
Xは | Yを | 作り出した
Xに関する -> Yの
Xに関する -> 最初の -> Yで
Xに関する -> 最初の -> 会議で | Yという -> 用語を | 作り出した
Xに関する -> 最初の -> 会議で | Yを | 作り出した
Xの -> Yで
Xの -> 会議で | Yという -> 用語を | 作り出した
Xの -> 会議で | Yを | 作り出した
Xで | Yという -> 用語を | 作り出した
Xで | Yを | 作り出した
Xという -> Yを
KNPを係り受け解析に用いた場合,次のような出力が得られると思われる.
Xは | Yに -> 関する -> 会議で | 作り出した。
Xは | Yで | 作り出した。
Xは | Yと -> いう -> 用語を | 作り出した。
Xは | Yを | 作り出した。
Xに -> 関する -> Yで
Xに -> 関する -> 会議で | Yと -> いう -> 用語を | 作り出した。
Xに -> 関する -> 会議で | Yを | 作り出した。
Xで | Yと -> いう -> 用語を | 作り出した。
Xで | Yを | 作り出した。
Xと -> いう -> Yを
題意を読み解くのが難しかったため、以下のサイトを参考にさせていただきました。
from itertools import combinations
import re
for sentence in SentenceList[5:6]:
dictMap = {}
# 文中の文節辞書を作成
for chunk in sentence.chunks:
dictMap[chunk.idx]=chunk
MeishiIdx=[]
# 名詞を含む文節のインデックスリスト作成
for i, chunk in enumerate(sentence.chunks):
if "名詞" in [ morph.pos for morph in chunk.morphs]:
MeishiIdx.append(i)
# itertools.combinationsはリスト中の2要素を、重複ないすべての組み合わせにしてくれる
for i, j in combinations(MeishiIdx, 2):
path_i=[]
path_j=[]
# 2つの文節の係り先文節をたどっていき、
# インデックスが一致するまでループしながら文節インデックスをリストに追加
while i!=j:
if i < j:
path_i.append(i)
i = sentence.chunks[i].dst
else:
path_j.append(j)
j = sentence.chunks[j].dst
# 文節XからYが同一経路にある場合
if len(path_j)==0:
retDim = []
# 最初の文節の名詞をXに置換する。XXXのようにXが連続する場合はXに変換する。
retDim.append(re.sub("X+","X", "".join([morph.surface if morph.pos!="名詞" else "X" for morph in sentence.chunks[i].morphs])))
# 係り先の文節を出力リストに追加していく
for k in path_i[1:]:
retDim.append( "".join([morph.surface for morph in sentence.chunks[k].morphs]))
# 対の文節の名詞をYに置換する。YYYのようにYが連続する場合はYに置換する。
retDim.append(re.sub("Y+","Y","".join([morph.surface if morph.pos!="名詞" else "Y" for morph in sentence.chunks[j].morphs])))
print(" -> ".join(retDim))
# XとYが異なる経路にある場合
else:
retDimX = []
# 最初の文節の名詞をXに置換する。XXXのようにXが連続する場合はXに変換する。
retDimX.append( re.sub("X+","X","".join([morph.surface if morph.pos!="名詞" else "X" for morph in sentence.chunks[path_i[0]].morphs])))
# Xの係り先の文節を出力リストに追加していく
for k in path_i[1:]:
retDimX.append( "".join([morph.surface for morph in sentence.chunks[k].morphs]))
ret1 = " -> ".join(retDimX)
retDimY = []
# 対の文節の名詞をYに置換する。YYYのようにYが連続する場合はYに置換する。
retDimY.append(re.sub("Y+","Y","".join([morph.surface if morph.pos!="名詞" else "Y" for morph in sentence.chunks[path_j[0]].morphs])))
# Yの係り先の文節を出力リストに追加していく
for k in path_j[1:]:
retDimY.append( "".join([morph.surface for morph in sentence.chunks[k].morphs]))
ret2 = " -> ".join(retDimY)
# XとYからたどっていった共通の係り先文節があれば、その文節を取得
if sentence.chunks[k].dst!=-1:
ret3 = "".join([morph.surface for morph in sentence.chunks[sentence.chunks[k].dst].morphs])
print(" | ".join([ret1,ret2,ret3]))
<出力>
Xを -> Yを
Xの -> 知的能力を | Yで | 実現する、
Xする、 -> 知的能力を -> Yする、
Xの -> 知的能力を -> 実現する、 | Yな | 技術・ソフトウェア・コンピュータシステム。
X・X・X。 -> 知的能力を -> 実現する、 -> Y・Y・Y。
Xの -> 知的能力を -> 実現する、 -> 技術・ソフトウェア・コンピュータシステム。 | Yは | ある。
Xの -> 知的能力を -> 実現する、 -> 技術・ソフトウェア・コンピュータシステム。 | Y -> (機械翻訳・かな漢字変換・構文解析等)、 -> 専門家の -> 推論・判断を -> 模倣する -> エキスパートシステム、 -> 画像認識等が | ある。
Xの -> 知的能力を -> 実現する、 -> 技術・ソフトウェア・コンピュータシステム。 | (Y・Y・Y)、 -> 専門家の -> 推論・判断を -> 模倣する -> エキスパートシステム、 -> 画像認識等が | ある。
Xの -> 知的能力を -> 実現する、 -> 技術・ソフトウェア・コンピュータシステム。 | Yの -> 推論・判断を -> 模倣する -> エキスパートシステム、 -> 画像認識等が | ある。
Xの -> 知的能力を -> 実現する、 -> 技術・ソフトウェア・コンピュータシステム。 | Y・Yを -> 模倣する -> エキスパートシステム、 -> 画像認識等が | ある。
・・・(省略)・・・