TPTブログ

テックポート株式会社のブログです。 技術情報や製品・サービス情報、 また未経験社員がデータサイエンティストを 目指す奮闘記など、更新していきます。

▲心くじけず言語処理100本ノック==47~49==

f:id:TBT_matsu:20200515145048p:plain
こんにちは!
テービーテックの村松です。

「言語処理100本ノック2020]」
nlp100.github.io
に挑戦中!
途中でくじけないか見守ってください・・・。
そして、皆さんも一緒に挑戦してみましょう!

本日は第5章: 係り受け解析 47~49です!
間違い・コード改善点などありましたら教えていただけると嬉しいです。

第5章: 係り受け解析

日本語Wikipediaの「人工知能」に関する記事からテキスト部分を抜き出したファイル(https://nlp100.github.io/data/ai.ja.zip)を使用して問題に取り組みます。
※6月に5章で使用するデータが変更となりました。更新内容はこちら

下準備はこちら↓↓で行いました。
ds-blog.tbtech.co.jp

47.機能動詞構文のマイニング

「動詞のヲ格にサ変接続名詞が入っている場合のみに着目したい.46のプログラムを以下の仕様を満たすように改変せよ.
〇「サ変接続名詞+を(助詞)」で構成される文節が動詞に係る場合のみを対象とする
〇述語は「サ変接続名詞+を+動詞の基本形」とし,文節中に複数の動詞があるときは,最左の動詞を用いる
〇述語に係る助詞(文節)が複数あるときは,すべての助詞をスペース区切りで辞書順に並べる
〇述語に係る文節が複数ある場合は,すべての項をスペース区切りで並べる(助詞の並び順と揃えよ)」

path = '出力保存先/47.txt'
with open(path, mode='w')as f:
  for s in s_list:
    for chunk in s:
      for m in chunk.morphs:
        if m.pos == '動詞':
          for i, src in enumerate(chunk.srcs):
            if len(s[src].morphs) == 2 and s[src].morphs[0].pos1 == 'サ変接続' and s[src].morphs[1].surface == 'を':
              predicate = ''.join([s[src].morphs[0].surface, s[src].morphs[1].surface, m.surface])
              c = []
              cases = []
              chu = []  
              for sr in chunk.srcs[:i] + chunk.srcs[i + 1:]:
                for mo in s[sr].morphs:
                  if mo.pos == '助詞':
                    c.append(mo.surface)
                if len(c) > 0:
                  cases = cases + c
                  chu.append(' '.join(m.surface for m in s[sr].morphs if m.pos != '記号'))
              if len(cases) > 0:
                cases = sorted(list(set(cases)))
                output = predicate + '\t' + ' '.join(cases) + '\t' + ' '.join(chu) + '\n'
                print(output)
                f.write(output)
##結果(一部抜粋)
記述をする	と	主体 と
注目を集め	が	サポートベクターマシン が
経験を行う	に を	元 に 学習 を
学習を行う	に を	経験 を 元 に
進化を見せ	て において は	活躍 し て いる 特に 敵対 的 生成 ネットワーク は 加え て 生成 技術 において
進化をいる	て において は	活躍 し て いる 特に 敵対 的 生成 ネットワーク は 加え て 生成 技術 において
開発を行っ	は	エイダ・ラブレス は
意味をし	に	データ に
研究を進める	て	費やし て
命令をし	で	機構 で
48.名詞から根へのパスの抽出

「文中のすべての名詞を含む文節に対し,その文節から構文木の根に至るパスを抽出せよ. ただし,構文木上のパスは以下の仕様を満たすものとする。
〇各文節は(表層形の)形態素列で表現する
〇パスの開始文節から終了文節に至るまで,各文節の表現を” -> “で連結する」

s = s_list[5]
for chunk in s:
  n = [m.pos for m in chunk.morphs]
  if '名詞' in n:
    path = [''.join(mo.surface for mo in chunk.morphs if mo.pos != '記号')]
    while chunk.dst != -1:
      path.append(''.join(mo.surface for mo in s[chunk.dst].morphs if mo.pos != '記号'))
      chunk = s[chunk.dst]
  print('->'.join(path))
##結果(一部抜粋)
2006年の->ディープラーニング->登場と->登場により->行った->なった
ディープラーニング->登場と->登場により->行った->なった
深層学習の->登場と->登場により->行った->なった
登場と->登場により->行った->なった
2010年代->以降の->ビッグデータの->登場により->行った->なった
以降の->ビッグデータの->登場により->行った->なった
ビッグデータの->登場により->行った->なった
49.名詞間の係り受けパスの抽出

「文中のすべての名詞句のペアを結ぶ最短係り受けパスを抽出せよ.ただし,名詞句ペアの文節番号がiとj(i “で連結して表現する
〇文節iとjに含まれる名詞句はそれぞれ,XとYに置換する
〇文節iから構文木の根に至る経路上に文節jが存在する場合: 文節iから文節jのパスを表示
〇上記以外で,文節iと文節jから構文木の根に至る経路上で共通の文節kで交わる場合: 文節iから文節kに至る直前のパスと文節jから文節kに至る直前までのパス,文節kの内容を” | “で連結して表示」

from itertools import combinations
import re

s = s_list[5]
nouns = []
for i, chunk in enumerate(s):
  if '名詞' in [morph.pos for morph in chunk.morphs]: 
    nouns.append(i)
for i, j in combinations(nouns, 2): 
  path_i = []
  path_j = []
  while i != j:
    if i < j:
      path_i.append(i)
      i = s[i].dst
    else:
      path_j.append(j)
      j = s[j].dst
  if len(path_j) == 0:
    chunk_X = ''.join([morph.surface if morph.pos != '名詞' else 'X' for morph in s[path_i[0]].morphs])
    chunk_Y = ''.join([morph.surface if morph.pos != '名詞' else 'Y' for morph in s[i].morphs])
    chunk_X = re.sub('X+', 'X', chunk_X)
    chunk_Y = re.sub('Y+', 'Y', chunk_Y)
    path_XtoY = [chunk_X] + [''.join(morph.surface for morph in s[n].morphs) for n in path_i[1:]] + [chunk_Y]
    print(' -> '.join(path_XtoY))
  else:  # 2つ目のケース
    chunk_X = ''.join([morph.surface if morph.pos != '名詞' else 'X' for morph in s[path_i[0]].morphs])
    chunk_Y = ''.join([morph.surface if morph.pos != '名詞' else 'Y' for morph in s[path_j[0]].morphs])
    chunk_k = ''.join([morph.surface for morph in s[i].morphs])
    chunk_X = re.sub('X+', 'X', chunk_X)
    chunk_Y = re.sub('Y+', 'Y', chunk_Y)
    path_X = [chunk_X] + [''.join(morph.surface for morph in s[n].morphs) for n in path_i[1:]]
    path_Y = [chunk_Y] + [''.join(morph.surface for morph in s[n].morphs) for n in path_j[1:]]
    print(' | '.join([' -> '.join(path_X), ' -> '.join(path_Y), chunk_k]))
##結果(一部抜粋)
Xの -> Y
Xの -> ディープラーニング | (Y)の | 登場と
Xの -> ディープラーニング -> Yと
Xの -> ディープラーニング -> 登場と | Y -> 以降の -> ビッグデータの | 登場により、
Xの -> ディープラーニング -> 登場と | Yの -> ビッグデータの | 登場により、
Xの -> ディープラーニング -> 登場と | Yの | 登場により、
Xの -> ディープラーニング -> 登場と -> Yにより、
Xの -> ディープラーニング -> 登場と -> 登場により、 | Yの -> 流行を -> 超えて -> 浸透して | 行った。
Xの -> ディープラーニング -> 登場と -> 登場により、 | Yを -> 超えて -> 浸透して | 行った。
Xの -> ディープラーニング -> 登場と -> 登場により、 | Yに -> 浸透して | 行った。
Xの -> ディープラーニング -> 登場と -> 登場により、 | Yして | 行った。

ここまでご覧いただきありがとうございます。
以上、第5章終了です!