yasuokaの日記: deberta-base-japanese-aozora-ud-headとufal.chu-liu-edmondsによる国語研長単位係り受け解析
昨日の日記で試作したdeberta-base-japanese-aozora-ud-headに対し、ufal.chu-liu-edmondsを使って係り受け解析木を解くプログラムを書いてみた。ちょっと長くなってしまったのだが、Google Colaboratoryで動かしてみよう。
!pip install transformers ufal.chu-liu-edmonds deplacy
class TransformersUD(object):
def __init__(self,bert):
import os
from transformers import (AutoTokenizer,AutoModelForQuestionAnswering,
AutoModelForTokenClassification,AutoConfig,TokenClassificationPipeline)
self.tokenizer=AutoTokenizer.from_pretrained(bert)
self.model=AutoModelForQuestionAnswering.from_pretrained(bert)
x=AutoModelForTokenClassification.from_pretrained
if os.path.isdir(bert):
d,t=x(os.path.join(bert,"deprel")),x(os.path.join(bert,"tagger"))
else:
from transformers.file_utils import hf_bucket_url
c=AutoConfig.from_pretrained(hf_bucket_url(bert,"deprel/config.json"))
d=x(hf_bucket_url(bert,"deprel/pytorch_model.bin"),config=c)
s=AutoConfig.from_pretrained(hf_bucket_url(bert,"tagger/config.json"))
t=x(hf_bucket_url(bert,"tagger/pytorch_model.bin"),config=s)
self.deprel=TokenClassificationPipeline(model=d,tokenizer=self.tokenizer,
aggregation_strategy="simple")
self.tagger=TokenClassificationPipeline(model=t,tokenizer=self.tokenizer)
def __call__(self,text):
import numpy,torch,ufal.chu_liu_edmonds
w=[(t["start"],t["end"],t["entity_group"]) for t in self.deprel(text)]
z,n={t["start"]:t["entity"].split("|") for t in self.tagger(text)},len(w)
r,m=[text[s:e] for s,e,p in w],numpy.full((n+1,n+1),numpy.nan)
v=self.tokenizer(r,add_special_tokens=False)["input_ids"]
for i,t in enumerate(v):
q=[self.tokenizer.cls_token_id]+t+[self.tokenizer.sep_token_id]
c=[q]+v[0:i]+[[self.tokenizer.mask_token_id]]+v[i+1:]+[[q[-1]]]
b=[len(sum(c[0:j+1],[])) for j in range(len(c))]
d=self.model(input_ids=torch.tensor([sum(c,[])]),
token_type_ids=torch.tensor([[0]*b[0]+[1]*(b[-1]-b[0])]))
s,e=d.start_logits.tolist()[0],d.end_logits.tolist()[0]
for j in range(n):
m[i+1,0 if i==j else j+1]=s[b[j]]+e[b[j+1]-1]
h=ufal.chu_liu_edmonds.chu_liu_edmonds(m)[0]
u="# text = "+text.replace("\n"," ")+"\n"
for i,(s,e,p) in enumerate(w,1):
u+="\t".join([str(i),r[i-1],"_",z[s][0][2:],"_","|".join(z[s][1:]),
str(h[i]),p,"_","_" if i<n and w[i][0]<e else "SpaceAfter=No"])+"\n"
return u+"\n"
nlp=TransformersUD("KoichiYasuoka/deberta-base-japanese-aozora-ud-head")
doc=nlp("全学年にわたって小学校の国語の教科書に挿し絵が用いられている")
import deplacy
deplacy.render(doc,Japanese=True)
deplacy.serve(doc,port=None)
「全学年にわたって小学校の国語の教科書に挿し絵が用いられている」を解析してみたところ、私(安岡孝一)の手元では以下の結果になった。
全学年 NOUN ═╗<══════╗ obl(斜格補語)
にわたって ADP <╝ ║ case(格表示)
小学校 NOUN ═╗<╗ ║ nmod(体言による連体修飾語)
の ADP <╝ ║ ║ case(格表示)
国語 NOUN ═╗═╝<╗ ║ nmod(体言による連体修飾語)
の ADP <╝ ║ ║ case(格表示)
教科書 NOUN ═╗═══╝<╗ ║ obl(斜格補語)
に ADP <╝ ║ ║ case(格表示)
挿し絵 NOUN ═╗<══╗ ║ ║ nsubj(主語)
が ADP <╝ ║ ║ ║ case(格表示)
用い VERB ═╗═╗═╝═╝═╝ root(親)
られ AUX <╝ ║ aux(動詞補助成分)
ている AUX <══╝ aux(動詞補助成分)
# text = 全学年にわたって小学校の国語の教科書に挿し絵が用いられている
1 全学年 _ NOUN _ _ 11 obl _ SpaceAfter=No
2 にわたって _ ADP _ _ 1 case _ SpaceAfter=No
3 小学校 _ NOUN _ _ 5 nmod _ SpaceAfter=No
4 の _ ADP _ _ 3 case _ SpaceAfter=No
5 国語 _ NOUN _ _ 7 nmod _ SpaceAfter=No
6 の _ ADP _ _ 5 case _ SpaceAfter=No
7 教科書 _ NOUN _ _ 11 obl _ SpaceAfter=No
8 に _ ADP _ _ 7 case _ SpaceAfter=No
9 挿し絵 _ NOUN _ _ 11 nsubj _ SpaceAfter=No
10 が _ ADP _ _ 9 case _ SpaceAfter=No
11 用い _ VERB _ _ 0 root _ SpaceAfter=No
12 られ _ AUX _ _ 11 aux _ SpaceAfter=No
13 ている _ AUX _ _ 11 aux _ SpaceAfter=No
SVGで可視化すると、こんな感じ。どうやらちゃんと解析できているようだ。ただ、語境界の検知に関しては、かなり手を抜いているので、そのあたりのチューニングは必要だと思う。ふーむ、この方向で進めて行けば、何とかなるかな。
deberta-base-japanese-aozora-ud-headとufal.chu-liu-edmondsによる国語研長単位係り受け解析 More ログイン