パスワードを忘れた? アカウント作成

yasuokaさんのトモダチの日記みんなの日記も見てね。 Idle.srad.jpは、あなたの人生において完全な時間の浪費です。見るなよ、見るなよ。

15749766 journal
中国

yasuokaの日記: Bellman-FordはEvaHan 2022 Bakeoffの夢を見るか 2

日記 by yasuoka

一昨昨日の日記を眺めていて、「B-」「I-」ラベルにおける矛盾解消がちょっと雑なのが気になった。そこで、Bellman-Fordをlogits向けに変形して、挟み込んでみることにした。Google Colaboratory (GPU)だと、こんな感じ。

pretrained_model="SIKU-BERT/sikuroberta"
!pip install transformers
import os
url="https://github.com/CIRCSE/LT4HALA"
d=os.path.basename(url)
!test -d $d || git clone --depth=1 $url
!cp $d/2022/data_and_doc/EvaHan*.txt $d/2022/data_and_doc/*EvaHan*.py .
!sed '1s/^.//' EvaHan_testa_raw.txt | tr -d '\015' > testa.txt
!sed '1s/^.//' EvaHan_testb_raw.txt | tr -d '\015' > testb.txt
!test -f zuozhuan_train_utf8.txt || unzip $d/2022/data_and_doc/zuozhuan_train_utf8.zip
!sed '1s/^.//' zuozhuan_train_utf8.txt | tr -d '\015' | nawk '{{gsub(/。\/w/,"。/w\n");print}}' | egrep -v '^ *$' > train.txt
class EvaHanDataset(object):
  def __init__(self,file,tokenizer):
    self.ids,self.pos=[],[]
    label,cls,sep=set(),tokenizer.cls_token_id,tokenizer.sep_token_id
    with open(file,"r",encoding="utf-8") as r:
      for t in r:
        w,p=[k.split("/") for k in t.split()],[]
        v=tokenizer([k[0] for k in w],add_special_tokens=False)["input_ids"]
        for x,y in zip(v,w):
          if len(y)==1:
            y.append("w")
          if len(x)==1:
            p.append(y[1])
          elif len(x)>1:
            p.extend(["B-"+y[1]]+["I-"+y[1]]*(len(x)-1))
        self.ids.append([cls]+sum(v,[])+[sep])
        self.pos.append(["w"]+p+["w"])
        label=set(sum([self.pos[-1],list(label)],[]))
    self.label2id={l:i for i,l in enumerate(sorted(label))}
  __len__=lambda self:len(self.ids)
  __getitem__=lambda self,i:{"input_ids":self.ids[i],"labels":[self.label2id[t] for t in self.pos[i]]}
import torch,numpy
from transformers import AutoTokenizer,AutoConfig,AutoModel,AutoModelForTokenClassification,DataCollatorForTokenClassification,TrainingArguments,Trainer
tkz=AutoTokenizer.from_pretrained(pretrained_model)
mdl=AutoModel.from_pretrained(pretrained_model)
dir="."+pretrained_model.replace("/",".")
mdl.save_pretrained(dir+"/tmp-model")
trainDS=EvaHanDataset("train.txt",tkz)
cfg=AutoConfig.from_pretrained(dir+"/tmp-model",num_labels=len(trainDS.label2id),label2id=trainDS.label2id,id2label={i:l for l,i in trainDS.label2id.items()})
arg=TrainingArguments(per_device_train_batch_size=32,output_dir="/tmp",overwrite_output_dir=True,save_total_limit=2,save_strategy="epoch")
trn=Trainer(model=AutoModelForTokenClassification.from_pretrained(dir+"/tmp-model",config=cfg),args=arg,train_dataset=trainDS,data_collator=DataCollatorForTokenClassification(tkz))
trn.train()
trn.save_model(dir+"/evahan2022-model")
tkz.save_pretrained(dir+"/evahan2022-model")
mdl=AutoModelForTokenClassification.from_pretrained(dir+"/evahan2022-model")
idl=mdl.config.id2label
mtx=numpy.full((len(idl),len(idl)),numpy.nan)
d=numpy.array([numpy.nan if idl[i].startswith("I-") else 0 for i in range(len(idl))])
for i in range(len(idl)):
  if idl[i].startswith("B-"):
    mtx[i,mdl.config.label2id["I-"+idl[i][2:]]]=0
  else:
    mtx[i]=d
    if idl[i].startswith("I-"):
      mtx[i,i]=0
for f in ["testa","testb"]:
  with open(f+".txt","r",encoding="utf-8") as r:
    u,e=[],[]
    for s in r:
      t=s.strip().split("。")
      w=[j+"。" if i<len(t)-1 else j for i,j in enumerate(t) if j!=""]
      if len(w)==0:
        e[-1]=e[-1]+"\n"
      else:
        u.extend(w)
        e.extend([" "]*(len(w)-1)+["\n"])
  with open(f+dir+".txt","w",encoding="utf-8") as w:
    with torch.no_grad():
      for s,z in zip(u,e):
        v=tkz(s,return_offsets_mapping=True)
        m=mdl(torch.tensor([v["input_ids"]])).logits[0].numpy()
        for i in range(m.shape[0]-1,0,-1):
          m[i-1]+=numpy.nanmax(m[i]+mtx,axis=1)
        p=[numpy.nanargmax(m[0])]
        for i in range(1,m.shape[0])):
          p.append(numpy.nanargmax(m[i]+mtx[p[-1]]))
        d=[[idl[q],s[t[0]:t[1]]] for q,t in zip(p,v["offset_mapping"]) if t[0]<t[1]]
        for i in range(len(d)-1,0,-1):
          if d[i][0].startswith("I-"):
            if d[i-1][0].startswith("B-"):
              e=d.pop(i)
              d[i-1]=[d[i-1][0][2:],d[i-1][1]+e[1]]
            elif d[i-1][0].startswith("I-"):
              e=d.pop(i)
              d[i-1][1]=d[i-1][1]+e[1]
        for i in range(len(d)):
          if d[i][0].startswith("B-") or d[i][0].startswith("I-"):
            d[i][0]=d[i][0][2:]
        print(" ".join(t[1]+"/"+t[0] for t in d),file=w,end=z)
!python eval_EvaHan_2022_FINAL.py testa{dir}.txt EvaHan_testa_gold.txt
!python eval_EvaHan_2022_FINAL.py testb{dir}.txt EvaHan_testb_gold.txt

私(安岡孝一)の手元では、15分ほどで以下の結果が得られた。

The result of testa.SIKU-BERT.sikuroberta.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 95.7918 | 96.6976 | 96.2426 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 91.5660 | 92.4318 | 91.9969 |
+-----------------+---------+---------+---------+

The result of testb.SIKU-BERT.sikuroberta.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 94.4153 | 91.3848 | 92.8753 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 88.6004 | 85.7565 | 87.1552 |
+-----------------+---------+---------+---------+

一昨昨日に較べると、「Pos tagging」における解析精度が上がっている。ちなみに1行目を「pretrained_model="KoichiYasuoka/bert-ancient-chinese-base-upos"」に変えたところ、私の手元では以下の結果になった。

The result of testa.KoichiYasuoka.bert-ancient-chinese-base-upos.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 95.9862 | 96.7403 | 96.3617 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 91.9335 | 92.6558 | 92.2933 |
+-----------------+---------+---------+---------+

The result of testb.KoichiYasuoka.bert-ancient-chinese-base-upos.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 94.8630 | 91.8269 | 93.3202 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 88.9929 | 86.1447 | 87.5457 |
+-----------------+---------+---------+---------+

あるいは1行目を「pretrained_model="Jihuai/bert-ancient-chinese"」に変えたところ、私の手元では以下の結果になった。

The result of testa.Jihuai.bert-ancient-chinese.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 96.1192 | 96.8504 | 96.4835 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 92.0233 | 92.7233 | 92.3720 |
+-----------------+---------+---------+---------+

The result of testb.Jihuai.bert-ancient-chinese.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 94.6381 | 91.4387 | 93.0109 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 88.5360 | 85.5429 | 87.0137 |
+-----------------+---------+---------+---------+

ざっと見た限りだと、Bellman-Fordが「Pos tagging」を改善するのは間違いなさそうだ。ただし、それでも、復旦大学のチームに今一歩で及んでいない。やっぱり世界の壁は厚いなあ。

15747693 journal
中国

yasuokaの日記: 様々な事前学習モデルで戦うEvaHan 2022 Bakeoff

日記 by yasuoka

一昨日昨日の日記で挑戦したEvaHan 2022 Bakeoffだが、事前学習モデルをroberta-classical-chinese-base-uposに置き換えるべく、さらにプログラムを書き直してみた。Google Colaboratory (GPU)だと、こんな感じ。

pretrained_model="KoichiYasuoka/roberta-classical-chinese-base-upos"
!pip install transformers
import os
url="https://github.com/CIRCSE/LT4HALA"
d=os.path.basename(url)
!test -d $d || git clone --depth=1 $url
!cp $d/2022/data_and_doc/EvaHan*.txt $d/2022/data_and_doc/*EvaHan*.py .
!sed '1s/^.//' EvaHan_testa_raw.txt | tr -d '\015' > testa.txt
!sed '1s/^.//' EvaHan_testb_raw.txt | tr -d '\015' > testb.txt
!test -f zuozhuan_train_utf8.txt || unzip $d/2022/data_and_doc/zuozhuan_train_utf8.zip
!sed '1s/^.//' zuozhuan_train_utf8.txt | tr -d '\015' | nawk '{{gsub(/。\/w/,"。/w\n");print}}' | egrep -v '^ *$' > train.txt
class EvaHanDataset(object):
  def __init__(self,file,tokenizer):
    self.ids,self.pos=[],[]
    label,cls,sep=set(),tokenizer.cls_token_id,tokenizer.sep_token_id
    with open(file,"r",encoding="utf-8") as r:
      for t in r:
        w,p=[k.split("/") for k in t.split()],[]
        v=tokenizer([k[0] for k in w],add_special_tokens=False)["input_ids"]
        for x,y in zip(v,w):
          if len(y)==1:
            y.append("w")
          if len(x)==1:
            p.append(y[1])
          elif len(x)>1:
            p.extend(["B-"+y[1]]+["I-"+y[1]]*(len(x)-1))
        self.ids.append([cls]+sum(v,[])+[sep])
        self.pos.append(["w"]+p+["w"])
        label=set(sum([self.pos[-1],list(label)],[]))
    self.label2id={l:i for i,l in enumerate(sorted(label))}
  __len__=lambda self:len(self.ids)
  __getitem__=lambda self,i:{"input_ids":self.ids[i],"labels":[self.label2id[t] for t in self.pos[i]]}
from transformers import AutoTokenizer,AutoConfig,AutoModel,AutoModelForTokenClassification,DataCollatorForTokenClassification,TrainingArguments,Trainer,pipeline
tkz=AutoTokenizer.from_pretrained(pretrained_model)
mdl=AutoModel.from_pretrained(pretrained_model)
dir="."+pretrained_model.replace("/",".")
mdl.save_pretrained(dir+"/tmp-model")
trainDS=EvaHanDataset("train.txt",tkz)
cfg=AutoConfig.from_pretrained(dir+"/tmp-model",num_labels=len(trainDS.label2id),label2id=trainDS.label2id,id2label={i:l for l,i in trainDS.label2id.items()})
arg=TrainingArguments(per_device_train_batch_size=32,output_dir="/tmp",overwrite_output_dir=True,save_total_limit=2,save_strategy="epoch")
trn=Trainer(model=AutoModelForTokenClassification.from_pretrained(dir+"/tmp-model",config=cfg),args=arg,train_dataset=trainDS,data_collator=DataCollatorForTokenClassification(tkz))
trn.train()
trn.save_model(dir+"/evahan2022-model")
tkz.save_pretrained(dir+"/evahan2022-model")
tagger=pipeline(task="ner",model=dir+"/evahan2022-model",device=0)
for f in ["testa","testb"]:
  with open(f+".txt","r",encoding="utf-8") as r:
    u,e=[],[]
    for s in r:
      t=s.split("。")
      w=[j+"。" if i<len(t)-1 else j for i,j in enumerate(t) if j!=""]
      if len(w)==0:
        e[-1]=e[-1]+"\n"
      else:
        u.extend(w)
        e.extend([" "]*(len(w)-1)+["\n"])
  with open(f+dir+".txt","w",encoding="utf-8") as w:
    for s,v,z in zip(u,tagger(u),e):
      d=[[t["entity"],s[t["start"]:t["end"]]] for t in v]
      for i in range(len(d)-1,0,-1):
        if d[i][0].startswith("I-"):
          if d[i-1][0].startswith("B-"):
            e=d.pop(i)
            d[i-1]=[d[i-1][0][2:],d[i-1][1]+e[1]]
          elif d[i-1][0].startswith("I-"):
            e=d.pop(i)
            d[i-1][1]=d[i-1][1]+e[1]
      for i in range(len(d)):
        if d[i][0].startswith("B-") or d[i][0].startswith("I-"):
          d[i][0]=d[i][0][2:]
      print(" ".join(t[1]+"/"+t[0] for t in d),file=w,end=z)
!python eval_EvaHan_2022_FINAL.py testa{dir}.txt EvaHan_testa_gold.txt
!python eval_EvaHan_2022_FINAL.py testb{dir}.txt EvaHan_testb_gold.txt

私(安岡孝一)の手元では、10分ほどで以下の結果が得られた。

The result of testa.KoichiYasuoka.roberta-classical-chinese-base-upos.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 95.6372 | 96.7829 | 96.2066 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 91.4782 | 92.5740 | 92.0228 |
+-----------------+---------+---------+---------+

The result of testb.KoichiYasuoka.roberta-classical-chinese-base-upos.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 94.6948 | 91.7414 | 93.1947 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 88.1912 | 85.4407 | 86.7942 |
+-----------------+---------+---------+---------+

昨日のSikuRoBERTaに較べると、TestAは少し良くなっているものの、TestBは負けている。ちなみに1行目を「pretrained_model="KoichiYasuoka/roberta-classical-chinese-base-char"」に変えたところ、私の手元では以下の結果になった。

The result of testa.KoichiYasuoka.roberta-classical-chinese-base-char.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 95.6555 | 96.6620 | 96.1562 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 91.5679 | 92.5314 | 92.0471 |
+-----------------+---------+---------+---------+

The result of testb.KoichiYasuoka.roberta-classical-chinese-base-char.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 94.5461 | 91.3866 | 92.9395 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 88.3888 | 85.4351 | 86.8869 |
+-----------------+---------+---------+---------+

あるいは1行目を「pretrained_model="KoichiYasuoka/bert-ancient-chinese-base-upos"」に変えたところ、私の手元では以下の結果になった。

The result of testa.KoichiYasuoka.bert-ancient-chinese-base-upos.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 95.8582 | 96.9998 | 96.4256 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 91.6673 | 92.7589 | 92.2098 |
+-----------------+---------+---------+---------+

The result of testb.KoichiYasuoka.bert-ancient-chinese-base-upos.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 94.9103 | 92.2411 | 93.5567 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 88.5648 | 86.0741 | 87.3017 |
+-----------------+---------+---------+---------+

1行目を「pretrained_model="Jihuai/bert-ancient-chinese"」に変えたところ、私の手元では以下の結果になった。

The result of testa.Jihuai.bert-ancient-chinese.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 96.0153 | 97.0495 | 96.5297 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 91.7915 | 92.7802 | 92.2832 |
+-----------------+---------+---------+---------+

The result of testb.Jihuai.bert-ancient-chinese.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 94.9311 | 92.1185 | 93.5037 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 88.4839 | 85.8624 | 87.1534 |
+-----------------+---------+---------+---------+

1行目を「pretrained_model="ethanyt/guwenbert-base"」に変えたところ、私の手元では以下の結果になった。

The result of testa.ethanyt.guwenbert-base.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 91.8585 | 94.1737 | 93.0017 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 83.9147 | 86.0296 | 84.9590 |
+-----------------+---------+---------+---------+

The result of testb.ethanyt.guwenbert-base.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 90.2871 | 88.8548 | 89.5652 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 80.1835 | 78.9115 | 79.5424 |
+-----------------+---------+---------+---------+

1行目を「pretrained_model="uer/gpt2-chinese-ancient"」に変えたところ、私の手元では以下の結果になった。

The result of testa.uer.gpt2-chinese-ancient.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 87.7262 | 93.0433 | 90.3066 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 80.3559 | 85.2263 | 82.7195 |
+-----------------+---------+---------+---------+

The result of testb.uer.gpt2-chinese-ancient.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 88.7446 | 91.5074 | 90.1048 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 77.3378 | 79.7455 | 78.5232 |
+-----------------+---------+---------+---------+

ざっと見くらべたところでは、TestAに関してはbert-ancient-chineseが、TestBに関してはbert-ancient-chinese-uposが、それぞれいい値になっている。TestAは『春秋左氏伝』、TestBは『史記』の一部らしいのだけど、さて、こういう結果、どう解釈すればいいのかな。

15746834 journal
中国

yasuokaの日記: TransformersのTokenClassificationでEvaHan 2022 Bakeoff

日記 by yasuoka

遅ればせながら昨日の日記で挑戦したEvaHan 2022 Bakeoffだが、esuparの学習モジュールからUniversal Dependenciesガラミのアレコレを外して、TransformersのTrainerを使う形で書き直してみた。Google Colaboratory (GPU)だと、こんな感じ。

!pip install transformers
import os
url="https://github.com/CIRCSE/LT4HALA"
d=os.path.basename(url)
!test -d $d || git clone --depth=1 $url
!cp $d/2022/data_and_doc/EvaHan*.txt $d/2022/data_and_doc/*EvaHan*.py .
!sed '1s/^.//' EvaHan_testa_raw.txt | tr -d '\015' > testa.txt
!sed '1s/^.//' EvaHan_testb_raw.txt | tr -d '\015' > testb.txt
!test -f zuozhuan_train_utf8.txt || unzip $d/2022/data_and_doc/zuozhuan_train_utf8.zip
!sed '1s/^.//' zuozhuan_train_utf8.txt | tr -d '\015' | nawk '{{gsub(/。\/w/,"。/w\n");print}}' | egrep -v '^ *$' > train.txt
class EvaHanDataset(object):
  def __init__(self,file,tokenizer):
    self.ids,self.pos=[],[]
    label,cls,sep=set(),tokenizer.cls_token_id,tokenizer.sep_token_id
    with open(file,"r",encoding="utf-8") as r:
      for t in r:
        w,p=[k.split("/") for k in t.split()],[]
        v=tokenizer([k[0] for k in w],add_special_tokens=False)["input_ids"]
        for x,y in zip(v,w):
          if len(y)==1:
            y.append("w")
          if len(x)==1:
            p.append(y[1])
          elif len(x)>1:
            p.extend(["B-"+y[1]]+["I-"+y[1]]*(len(x)-1))
        self.ids.append([cls]+sum(v,[])+[sep])
        self.pos.append(["w"]+p+["w"])
        label=set(sum([self.pos[-1],list(label)],[]))
    self.label2id={l:i for i,l in enumerate(sorted(label))}
  __len__=lambda self:len(self.ids)
  __getitem__=lambda self,i:{"input_ids":self.ids[i],"labels":[self.label2id[t] for t in self.pos[i]]}
from transformers import AutoTokenizer,AutoConfig,AutoModelForTokenClassification,DataCollatorForTokenClassification,TrainingArguments,Trainer,pipeline
brt="SIKU-BERT/sikuroberta"
tkz=AutoTokenizer.from_pretrained(brt)
trainDS=EvaHanDataset("train.txt",tkz)
cfg=AutoConfig.from_pretrained(brt,num_labels=len(trainDS.label2id),label2id=trainDS.label2id,id2label={i:l for l,i in trainDS.label2id.items()})
arg=TrainingArguments(per_device_train_batch_size=32,output_dir="/tmp",overwrite_output_dir=True,save_total_limit=2,save_strategy="epoch")
trn=Trainer(model=AutoModelForTokenClassification.from_pretrained(brt,config=cfg),args=arg,train_dataset=trainDS,data_collator=DataCollatorForTokenClassification(tkz))
trn.train()
trn.save_model("roberta-han")
tkz.save_pretrained("roberta-han")
tagger=pipeline(task="ner",model="roberta-han",device=0)
for f in ["testa","testb"]:
  with open(f+".txt","r",encoding="utf-8") as r:
    with open(f+"_close.txt","w",encoding="utf-8") as w:
      for s in r:
        d=[]
        if s.strip()!="":
          t=s.split("。")
          u=[j+"。" if i<len(t)-1 else j for i,j in enumerate(t) if j!=""]
          v=tagger(u)
          for j,k in zip(u,v):
            d+=[[t["entity"],j[t["start"]:t["end"]]] for t in k]
        for i in range(len(d)-1,0,-1):
          if d[i][0].startswith("I-"):
            if d[i-1][0].startswith("B-"):
              e=d.pop(i)
              d[i-1]=[d[i-1][0][2:],d[i-1][1]+e[1]]
            elif d[i-1][0].startswith("I-"):
              e=d.pop(i)
              d[i-1][1]=d[i-1][1]+e[1]
        for i in range(len(d)):
          if d[i][0].startswith("B-") or d[i][0].startswith("I-"):
            d[i][0]=d[i][0][2:]
        print(" ".join(t[1]+"/"+t[0] for t in d),file=w)
!python eval_EvaHan_2022_FINAL.py testa_close.txt EvaHan_testa_gold.txt
!python eval_EvaHan_2022_FINAL.py testb_close.txt EvaHan_testb_gold.txt

ただし、EvaHan向けに品詞「w」を特別扱いしている。私(安岡孝一)の手元では、10分ほどで以下の結果が得られた。

The result of testa_close.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 95.3232 | 96.7260 | 96.0195 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 90.9441 | 92.2825 | 91.6084 |
+-----------------+---------+---------+---------+

The result of testb_close.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 94.6465 | 92.1817 | 93.3978 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 88.2383 | 85.9404 | 87.0742 |
+-----------------+---------+---------+---------+

昨日の結果に較べて、TestAは少し悪くなっているものの、TestBは少し良くなっている。タマタマそういう結果なのだが、これなら「closed modality」だと信じてもらえるだろうか。

15746190 journal
中国

yasuokaの日記: EvaHan 2022 Bakeoffに対するesuparの挑戦

日記 by yasuoka

EvaHan 2022 Bakeoffに、遅ればせながらesuparで挑戦してみた。まずはSikuRoBERTaを使う「closed modality」クラスで、Google Colaboratory (GPU)用に書いてみた。

!pip install esupar
import os
url="https://github.com/CIRCSE/LT4HALA"
d=os.path.basename(url)
!test -d $d || git clone --depth=1 $url
!cp $d/2022/data_and_doc/EvaHan*.txt $d/2022/data_and_doc/*EvaHan*.py .
!sed '1s/^.//' EvaHan_testa_raw.txt | tr -d '\015' > testa.txt
!sed '1s/^.//' EvaHan_testb_raw.txt | tr -d '\015' > testb.txt
!test -f zuozhuan_train_utf8.txt || unzip $d/2022/data_and_doc/zuozhuan_train_utf8.zip
!sed '1s/^.//' zuozhuan_train_utf8.txt | tr -d '\015' | nawk '{{gsub(/。\/w/,"。/w\n");print}}' > train.txt
s='NF>0{OFS="\t";printf("# text = ");for(i=1;i<=NF;i++){split($i,a,"/");printf("%s",a[1])}print"";for(i=1;i<=NF;i++){split($i,a,"/");print i,a[1],"_",a[2],"_","_","_","_","_","SpaceAfter=No"}print""}'
!nawk '{s}' train.txt > train.pos
!python -m esupar.train SIKU-BERT/sikuroberta roberta-han 32 /tmp train.pos
from transformers import pipeline
tagger=pipeline(task="ner",model="roberta-han",device=0)
for f in ["testa","testb"]:
  with open(f+".txt","r",encoding="utf-8") as r:
    with open(f+"_close.txt","w",encoding="utf-8") as w:
      for s in r:
        d=[]
        if s.strip()!="":
          t=s.split("。")
          u=[j+"。" if i<len(t)-1 else j for i,j in enumerate(t) if j!=""]
          v=tagger(u)
          for j,k in zip(u,v):
            d+=[[t["entity"],j[t["start"]:t["end"]]] for t in k]
        for i in range(len(d)-1,0,-1):
          if d[i][0].startswith("I-"):
            if d[i-1][0].startswith("B-"):
              e=d.pop(i)
              d[i-1]=[d[i-1][0][2:],d[i-1][1]+e[1]]
            elif d[i-1][0].startswith("I-"):
              e=d.pop(i)
              d[i-1][1]=d[i-1][1]+e[1]
        for i in range(len(d)):
          if d[i][0].startswith("B-") or d[i][0].startswith("I-"):
            d[i][0]=d[i][0][2:]
        print(" ".join(t[1]+"/"+t[0] for t in d),file=w)
!python eval_EvaHan_2022_FINAL.py testa_close.txt EvaHan_testa_gold.txt
!python eval_EvaHan_2022_FINAL.py testb_close.txt EvaHan_testb_gold.txt

ただ、esuparはUniversal Dependencies向けの細かいチューニングがおこなわれている上に、学習モジュールがブラックボックスなので「closed modality」と言っても、信じてもらえなさそうな気がする。とりあえず私(安岡孝一)の手元では、15分ほどで以下の結果が得られた。

The result of testa_close.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 95.3882 | 96.6869 | 96.0332 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 91.0675 | 92.3074 | 91.6833 |
+-----------------+---------+---------+---------+

The result of testb_close.txt is:
+-----------------+---------+---------+---------+
|       Task      |    P    |    R    |    F1   |
+-----------------+---------+---------+---------+
|Word segmentation| 94.5596 | 92.0145 | 93.2697 |
+-----------------+---------+---------+---------+
|   Pos tagging   | 88.0331 | 85.6636 | 86.8322 |
+-----------------+---------+---------+---------+

残念ながら、復旦大学のチームに今一歩で及んでいない。しかも、4ヶ月前のesuparだと、差はもっと拡がると思う。なかなか世界の壁は厚いなあ。

15740281 journal
日本

yasuokaの日記: 「象は鼻が長い」の主語は「象」なのか「鼻」なのか 2

日記 by yasuoka

昨日の日記の読者から、「私は腹が立つた」は「象は鼻が長い」と同じ構造なのか、という趣旨の質問をもらった。昨日と同様に、nsubj:outerで書いてみよう。

象   NOUN ═╗<══╗ nsubj:outer(主語[外側])
は   ADP  <╝   ║ case(格表示)
鼻   NOUN ═╗<╗ ║ nsubj(主語)
が   ADP  <╝ ║ ║ case(格表示)
長い ADJ  ═══╝═╝ root(親)

確かに、「象は鼻が長い」なら「鼻が長い」をPredicate Clauseとみなすのも、アリだと思う。ただ、もし本当にPredicate Cluaseであるならば、Universal Dependenciesの新しいルールによって倒置文「鼻が象は長い」は、以下のように表すことになる。

鼻   NOUN ═╗<╗   nsubj(主語)
が   ADP  <╝ ║   case(格表示)
象   NOUN ═╗ ║<╗ nsubj:outer(主語[外側])
は   ADP  <╝ ║ ║ case(格表示)
長い ADJ  ═══╝═╝ root(親)

うーん、これだと私(安岡孝一)個人としては、nsubj:outerというタグの名前そのものに、かなり不自然な印象を受けるのだが、まあ、タグだから仕方ないのかな。

15739750 journal
日本

yasuokaの日記: Re:「私は腹が立つた」の主語は「私」なのか「腹」なのか 2

日記 by yasuoka

2021年8月6日の日記に書いた「私は腹が立つた」だが、Universal Dependenciesに新たに導入されたnsubj:outerを使うべきだろうか、と思い悩み始めた。deplacy風に書くと、こんな感じ。

私   PRON ═╗<══╗ nsubj:outer(主語[外側])
は   ADP  <╝   ║ case(格表示)
腹   NOUN ═╗<╗ ║ nsubj(主語)
が   ADP  <╝ ║ ║ case(格表示)
立つ VERB ═╗═╝═╝ root(親)
た   AUX  <╝     aux(動詞補助成分)

ただ、この文の「腹が立つた」が、いわゆるPredicate Clauseにあたるかどうか、私(安岡孝一)には自信がない。やっぱり、これじゃマズイかなぁ。

15730446 journal
テレビ

yasuokaの日記: 『ちむどんどん』第67話での人名用漢字ネタ

日記 by yasuoka

『新しい常用漢字と人名用漢字』(三省堂、2011年3月)の読者から、本日付けのasagei MUSE『【ちむどんどん】記事への投書にも誤りが!新聞社が舞台とは思えない時代考証ミスとは?』を読んでみてほしい、との御連絡をいただいた。

「愛が目にした3通目の投書には『鎌田侑由子』という差出人の名前が書き添えてありました。他の投書にも名前の書かれているものが多く、制作側は多くの仮名を考えたのでしょう。しかしここに問題があったのです。というのも侑由子の『侑』は、1981年10月1日から人名用漢字として使えるようになった漢字。しかし物語は1978年(昭和53年)なのですから、侑由子さんから投書が来ることは有り得ないのです」

えっと、私(安岡孝一)自身は、昨日の『ちむどんどん』第67話を見ていないので、迂闊なことは書けないのだが、1978年のタイミングだと沖縄出身の人だったりしないのだろうか。あるいは、そもそも1947年以前の生まれだったり(たとえば乾侑美子とか)、投書がペンネームだったりしないのだろうか。うーん、これらの可能性を全て排除できる画像だったとすると、ぜひ見てみないとダメかな。

15722990 journal
人工知能

yasuokaの日記: 日本語DeBERTaモデルdeberta-large-japanese-wikipediaリリース

日記 by yasuoka

6月25日の日記の手法をもとに、日本語DeBERTa(V2)モデルdeberta-large-japanese-wikipediaも作ってみた。24層・隠れサイズ1024・16ヘッド・トークン幅512としたが、青空文庫3億字(元データ2.37億字+異体字増量分0.64億字)にWikipedia 13億字を加えたため、NVIDIA A100-SXM4-40GBで254時間51分(7744065ステップ×16バッチ)もかかってしまった。Google Colaboratoty (GPU)上でJCommonSenceQAに挑戦してみよう。

!test -d transformers-4.20.1 || git clone -b v4.20.1 --depth=1 https://github.com/huggingface/transformers transformers-4.20.1
!test -d JGLUE || ( git clone --depth=1 https://github.com/yahoojapan/JGLUE && cat JGLUE/fine-tuning/patch/transformers-4.9.2_jglue-1.0.0.patch | ( cd transformers-4.20.1 && patch -p1 ) )
!cd transformers-4.20.1 && pip install .
!pip install -r transformers-4.20.1/examples/pytorch/text-classification/requirements.txt
!pip install protobuf==3.19.1 tensorboard
!python transformers-4.20.1/examples/pytorch/multiple-choice/run_swag.py --model_name_or_path KoichiYasuoka/deberta-large-japanese-wikipedia --do_train --do_eval --do_predict --max_seq_length 64 --per_device_train_batch_size 8 --learning_rate 5e-05 --num_train_epochs 4 --output_dir ./output_jcommonsenseqa --overwrite_output_dir --train_file JGLUE/datasets/jcommonsenseqa-v1.0/train-v1.0.json --validation_file JGLUE/datasets/jcommonsenseqa-v1.0/valid-v1.0.json --test_file JGLUE/datasets/jcommonsenseqa-v1.0/valid-v1.0.json --use_fast_tokenizer True --evaluation_strategy epoch --warmup_ratio 0.1

ファインチューニングに70分ほどかかったが、私(安岡孝一)の手元では以下の「eval metrics」が出力された。

***** eval metrics *****
  epoch                   =        4.0
  eval_accuracy           =     0.5996
  eval_loss               =     3.4278
  eval_runtime            = 0:00:36.81
  eval_samples            =       1119
  eval_samples_per_second =     30.393
  eval_steps_per_second   =      3.802

JCommonSenseQAが0.5996となっていて、悲しいことにdeberta-base-japanese-wikipediaより低い。largeモデルを頑張って作ったのに、baseモデルより低いというのは、どこか作り方を間違ってるのかしら。

15721128 journal
人工知能

yasuokaの日記: ckiplab/bert-base-han-chineseは「孟子[MASK]梁惠王」の[MASK]に何を埋めてくるのか

日記 by yasuoka

台湾の中央研究院がckiplab/bert-base-han-chineseを公開した。上古漢語標記語料庫・中古漢語語料庫・近代漢語語料庫・現代漢語語料庫の4つで鍛えた言語モデルらしい。とりあえず、Google Colaboratoryで動かしてみよう。

!pip install transformers
import torch
from transformers import BertTokenizer,BertForMaskedLM
tokenizer=BertTokenizer.from_pretrained("ckiplab/bert-base-han-chinese")
model=BertForMaskedLM.from_pretrained("ckiplab/bert-base-han-chinese")
tokens=tokenizer.tokenize("孟子[MASK]梁惠王")
mask=tokens.index("[MASK]")
ids=torch.tensor([tokenizer.convert_tokens_to_ids(tokens)])
with torch.no_grad():
  outputs=model(ids)
  pred=outputs[0][0,mask].topk(5)
for i,t in enumerate(tokenizer.convert_ids_to_tokens(pred.indices)):
  tokens[mask]=t
  print(i+1,tokens)

AutoTokenizerがうまく動かないのでBertTokenizerに頼ったのだが、「孟子[MASK]梁惠王」という例文に対し、私(安岡孝一)の手元では以下の結果が得られた。

1 ['孟', '子', '為', '梁', '惠', '王']
2 ['孟', '子', '曰', '梁', '惠', '王']
3 ['孟', '子', '言', '梁', '惠', '王']
4 ['孟', '子', '之', '梁', '惠', '王']
5 ['孟', '子', '以', '梁', '惠', '王']

GuwenBERTRoBERTa-Classical-Chineseと較べると、イマイチうまく穴埋めできてない。どうなってるんだろうと思いつつ、ckiplab/bert-base-han-chineseのvocab.txtを眺めてみたところ、12153行目に「しにはとんとんワークケートを」が紛れ込んでいる。AnchiBERTSikuBERTでもそうだったが、どうして中国語モデルのvocab.txtに、ワークゲートの求人情報が入ってるんだろ。

15719504 journal
人工知能

yasuokaの日記: 古典中国語(漢文)Universal DependenciesはCombinatory Categorial Grammarに変換できるのか

日記 by yasuoka

Tu-Anh Tran・Yusuke Miyao『Development of a Multilingual CCG Treebank via Universal Dependencies Conversion』(LREC 2022, pp.5220-5233)を読んでいて、古典中国語UD(Universal Dependencies)を本当にCCG(Combinatory Categorial Grammar)に変換できたのか、というところが気になった。というのも、この論文は、私(安岡孝一)のグループが製作中のUD_Classical_Chinese-Kyotoを扱っているが、その中身について全く言及が無いのだ。

『古典中国語(漢文)Universal Dependenciesとその応用』(情報処理学会論文誌, Vol.63, No.2 (2022年2月), pp.355-363)でも書いたが、UDを古典中国語に適用する場合、補語が節であるようなコピュラ文(約1.6%)が、記法上うまく書けない。たとえば「是民受之也」という文は、SuPar-Kanbun+deplacyだと、以下のように解析されてしまう。

>>> import suparkanbun
>>> nlp=suparkanbun.load()
>>> doc=nlp("是民受之也")
>>> import deplacy
>>> deplacy.render(doc)
是 PRON <════╗   nsubj
民 NOUN <══╗ ║   nsubj
受 VERB ═╗═╝═╝═╗ ROOT
之 PRON <╝     ║ obj
也 PART <══════╝ discourse:sp

「是X也」というコピュラ文の補語Xが「民受之」なのだが、UDはそれをうまく書けず、「受」からnsubjが2本出てしまう。このあたり、実はUDそのものの限界(の一つ)なのだが、さて、これをCCGにうまく変換できたのか、私個人としては非常に気になるところだ。

ただ、以前『Universal Dependenciesの拡張にもとづく古典中国語(漢文)の直接構成鎖解析の試み』(情報処理学会研究報告, Vol.2019-CH-120『人文科学とコンピュータ』, No.1 (2019年5月11日), pp.1-8)でも報告したとおり、この手の変換は、単語間あるいは構成鎖(catena)間に、何がしかの全順序関係(実は半順序関係でも何とかなる)を持ち込めば、かなりの部分がうまく行く。なので、『Development of a Multilingual CCG Treebank via Universal Dependencies Conversion』でも、何がしかの順序関係を持ち込んだのかと思ったのだが、どうもそうでもなさそうだ。うーん、本当にうまくいったのかしら。

typodupeerror

アレゲは一日にしてならず -- アレゲ見習い

読み込み中...