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

kcgさんのトモダチの日記。 スラドのTwitterでストーリをフォローしよう。

15712099 journal
人工知能

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

日記 by yasuoka

5月24日の日記の手法をもとに、日本語DeBERTa(V2)モデルdeberta-base-japanese-wikipediaも作ってみた。12層・隠れサイズ768・12ヘッド・トークン幅512としたが、青空文庫3億字(元データ2.37億字+異体字増量分0.64億字)にWikipedia 13億字を加えたため、NVIDIA A100-SXM4-40GBで87時間44分(3872034ステップ×32バッチ)もかかってしまった。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-base-japanese-wikipedia --do_train --do_eval --do_predict --max_seq_length 64 --per_device_train_batch_size 16 --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

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

***** eval metrics *****
  epoch                   =        4.0
  eval_accuracy           =     0.6381
  eval_loss               =     1.5437
  eval_runtime            = 0:00:11.53
  eval_samples            =       1119
  eval_samples_per_second =     97.005
  eval_steps_per_second   =     12.136

JCommonSenseQAが0.6381なので、もう少し頑張りたいところだ。うーん、largeモデルにも挑戦すべきかな。

15710069 journal
人工知能

yasuokaの日記: Question Answeringによる国語研長単位係り受け解析モデルのUPOS/LAS/MLAS評価

日記 by yasuoka

6月16日18日19日の日記で公開した国語研長単位係り受け解析用DeBERTaモデルに対し、『Transformersと国語研長単位による日本語係り受け解析モデルの製作』の表4・表5と同様に、UPOS/LAS/MLASで評価してみた。現時点での結果を以下に示す。

  • deberta-base-japanese-aozora-ud-head
    構築時の評価(evaluation) 95.39/88.40/78.17 テスト(predict) 95.03/87.78/77.21
    共通テスト国語第1問【文章Ⅰ】 91.37/78.96/59.55 【文章Ⅱ】 95.76/84.57/69.42
  • deberta-large-japanese-aozora-ud-head
    構築時の評価(evaluation) 95.13/88.99/78.47 テスト(predict) 95.14/87.68/77.26
    共通テスト国語第1問【文章Ⅰ】 90.80/77.17/56.56 【文章Ⅱ】 94.93/82.23/67.34
  • deberta-base-japanese-unidic-ud-head
    構築時の評価(evaluation) 95.34/87.69/74.70 テスト(predict) 94.49/85.91/71.89
    共通テスト国語第1問【文章Ⅰ】 91.31/76.37/50.39 【文章Ⅱ】 96.05/86.72/70.19
  • deberta-large-japanese-unidic-ud-head
    構築時の評価(evaluation) 95.69/88.34/75.72 テスト(predict) 95.52/87.31/74.25
    共通テスト国語第1問【文章Ⅰ】 92.21/77.81/51.93 【文章Ⅱ】 94.76/81.69/62.78

また、私(安岡孝一)の手元にあった日本語BERT/RoBERTaのうち、単文字トークナイザによるモデルもファインチューンして、それぞれ評価してみた。

  • roberta-base-japanese-aozora-ud-head
    構築時の評価(evaluation) 95.26/89.07/78.40 テスト(predict) 94.63/87.24/76.31
    共通テスト国語第1問【文章Ⅰ】 91.68/80.40/60.94 【文章Ⅱ】 94.63/84.75/69.04
  • roberta-large-japanese-aozora-ud-head
    構築時の評価(evaluation) 92.38/82.11/66.82 テスト(predict) 91.89/79.53/64.27
    共通テスト国語第1問【文章Ⅰ】 87.14/67.40/45.08 【文章Ⅱ】 91.50/72.95/53.82
  • bert-base-japanese-wikipedia-ud-head
    構築時の評価(evaluation) 96.79/91.69/83.59 テスト(predict) 96.46/89.58/80.98
    共通テスト国語第1問【文章Ⅰ】 91.30/78.74/58.34 【文章Ⅱ】 94.63/83.61/67.35
  • bert-large-japanese-wikipedia-ud-head
    構築時の評価(evaluation) 96.72/91.35/83.11 テスト(predict) 96.23/89.45/80.60
    共通テスト国語第1問【文章Ⅰ】 90.37/76.16/57.84 【文章Ⅱ】 94.91/85.45/69.62

モデルごとに得手不得手があるらしく、なかなか比較が難しい。ただ、全体の傾向としては、まだesuparによるBiaffine実装には追いついていないようだ。なお、ファインチューン・評価用のGoogle Colaboratoryページを、ここここに示しておいたので、参考にしてほしい。

15705100 journal
人工知能

yasuokaの日記: Question Answeringによる国語研長単位係り受け解析用DeBERTaモデル(BertJapaneseTokenizer版)を公開

日記 by yasuoka

一昨日昨日の日記で書いた係り受け解析手法を、BertJapaneseTokenizerに適用するやり方で、deberta-base-japanese-unidic-ud-headdeberta-large-japanese-unidic-ud-headを試作した。ただ、BertJapaneseTokenizerはコンマの直後のトークナイズが甘く、結果として係り受け解析に失敗してしまう。Google Colaboratoryで試してみよう。

!pip install transformers ufal.chu-liu-edmonds pytokenizations fugashi unidic-lite

from transformers import (AutoTokenizer,AutoModelForQuestionAnswering,
  AutoModelForTokenClassification,AutoConfig,TokenClassificationPipeline)
class TaggerPipeline(TokenClassificationPipeline):
  def __call__(self,text):
    d=super().__call__(text)
    if len(d)>0 and ("start" not in d[0] or d[0]["start"]==None):
      import tokenizations
      v=[x["word"].replace(" ","") for x in d]
      a2b,b2a=tokenizations.get_alignments(v,text)
      for i,t in enumerate(a2b):
        s,e=(0,0) if t==[] else (t[0],t[-1]+1)
        if v[i].startswith(self.tokenizer.unk_token):
          s=([[-1]]+[x for x in a2b[0:i] if x>[]])[-1][-1]+1
        if v[i].endswith(self.tokenizer.unk_token):
          e=([x for x in a2b[i+1:] if x>[]]+[[len(text)]])[0][0]
        d[i]["start"],d[i]["end"]=s,e
    return d
class TransformersSlowUD(object):
  def __init__(self,bert):
    import os
    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=TaggerPipeline(model=d,tokenizer=self.tokenizer,
      aggregation_strategy="simple")
    self.tagger=TaggerPipeline(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,c=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.append([q]+v[0:i]+[[self.tokenizer.mask_token_id]]+v[i+1:]+[[q[-1]]])
    b=[[len(sum(x[0:j+1],[])) for j in range(len(x))] for x in c]
    d=self.model(input_ids=torch.tensor([sum(x,[]) for x in c]),
      token_type_ids=torch.tensor([[0]*x[0]+[1]*(x[-1]-x[0]) for x in b]))
    s,e=d.start_logits.tolist(),d.end_logits.tolist()
    for i in range(n):
      for j in range(n):
        m[i+1,0 if i==j else j+1]=s[i][b[i][j]]+e[i][b[i][j+1]-1]
    h=ufal.chu_liu_edmonds.chu_liu_edmonds(m)[0]
    if [0 for i in h if i==0]!=[0]:
      i=([p for s,e,p in w]+["root"]).index("root")
      j=i+1 if i<n else numpy.nanargmax(m[:,0])
      m[0:j,0]=m[j+1:,0]=numpy.nan
      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):
      p="root" if h[i]==0 else "dep" if p=="root" else p
      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=TransformersSlowUD("KoichiYasuoka/deberta-base-japanese-unidic-ud-head")
doc=nlp("それは,『エスパー魔美』です.")
print(doc)

「それは,『エスパー魔美』です.」という文をdeberta-base-japanese-unidic-ud-headで係り受け解析してみたところ、私(安岡孝一)の手元では以下の結果となった。

# text = それは,『エスパー魔美』です.
1    それ    _    PRON    _    _    3    nsubj    _    SpaceAfter=No
2    は    _    ADP    _    _    1    case    _    SpaceAfter=No
3    ,『エスパー魔美    _    PUNCT    _    _    0    root    _    SpaceAfter=No
4    』    _    PUNCT    _    _    3    punct    _    SpaceAfter=No
5    です    _    AUX    _    _    3    cop    _    SpaceAfter=No
6    .    _    PUNCT    _    _    3    punct    _    SpaceAfter=No

コンマの直後に語境界があるべきところを、そのまま繋いでいってしまって、処理が崩壊している。このあたり、unidic-liteはイマイチなので、他のUniDicに差し替えるべきかしら。

15703479 journal
人工知能

yasuokaの日記: deberta-large-japanese-aozora-ud-headとufal.chu-liu-edmondsによる国語研長単位係り受け解析

日記 by yasuoka

一昨日の日記の手法を拡張して、deberta-large-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,c=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.append([q]+v[0:i]+[[self.tokenizer.mask_token_id]]+v[i+1:]+[[q[-1]]])
    b=[[len(sum(x[0:j+1],[])) for j in range(len(x))] for x in c]
    d=self.model(input_ids=torch.tensor([sum(x,[]) for x in c]),
      token_type_ids=torch.tensor([[0]*x[0]+[1]*(x[-1]-x[0]) for x in b]))
    s,e=d.start_logits.tolist(),d.end_logits.tolist()
    for i in range(n):
      for j in range(n):
        m[i+1,0 if i==j else j+1]=s[i][b[i][j]]+e[i][b[i][j+1]-1]
    m[:,0]=numpy.where(m[:,0]==numpy.nanmax(m[:,0]),0,numpy.nan)
    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):
      p="root" if h[i]==0 else "dep" if p=="root" else p
      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-large-japanese-aozora-ud-head")
doc=nlp("うなぎを浜松に食べに行く")
import deplacy
deplacy.render(doc,Japanese=True)
deplacy.serve(doc,port=None)

「うなぎを浜松に食べに行く」を係り受け解析してみたところ、私(安岡孝一)の手元では以下の結果となった。

うなぎ PROPN ═╗<╗     obj(目的語)
を     ADP   <╝ ║     case(格表示)
浜松   PROPN ═╗ ║<══╗ advmod(連用修飾語)
に     ADP   <╝ ║   ║ case(格表示)
食べ   VERB  ═╗═╝<╗ ║ advcl(連用修飾節)
に     ADP   <╝   ║ ║ case(格表示)
行く   VERB  ═════╝═╝ acl(連体修飾節)

# text = うなぎを浜松に食べに行く
1    うなぎ    _    PROPN    _    _    5    obj    _    SpaceAfter=No
2    を    _    ADP    _    _    1    case    _    SpaceAfter=No
3    浜松    _    PROPN    _    _    7    advmod    _    SpaceAfter=No
4    に    _    ADP    _    _    3    case    _    SpaceAfter=No
5    食べ    _    VERB    _    _    7    advcl    _    SpaceAfter=No
6    に    _    ADP    _    _    5    case    _    SpaceAfter=No
7    行く    _    VERB    _    _    0    acl    _    SpaceAfter=No

SVGで可視化すると、こんな感じ。係り受けがちゃんと交差していて素晴らしいのだが、交差部分のラベルがoblではなく「浜松」⇐advmod=「行く」となっていたり、「うなぎ」の品詞がPROPNになっていたりと、あと一息だ。さて、どうチューニングしていけばいいかな。

15701900 journal
人工知能

yasuokaの日記: deberta-base-japanese-aozora-ud-headとufal.chu-liu-edmondsによる国語研長単位係り受け解析

日記 by yasuoka

昨日の日記で試作した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で可視化すると、こんな感じ。どうやらちゃんと解析できているようだ。ただ、語境界の検知に関しては、かなり手を抜いているので、そのあたりのチューニングは必要だと思う。ふーむ、この方向で進めて行けば、何とかなるかな。

15700763 journal
人工知能

yasuokaの日記: Re: Question Answeringを係り受け解析に応用するには

日記 by yasuoka

一昨日の日記の手法を、もう少しtransformers向けに改造して、国語研長単位向けdeberta-base-japanese-aozora-ud-headを試作してみた。ただ、QuestionAnsweringPipelineにはバグがあるらしく、日本語がうまく通らない。仕方ないので、torch.argmaxを手で叩く方法にしてみた。最新のtransformersをインストールしつつ、Question Answeringによる係り受け解析を試してみよう。

$ pip3 install -U transformers --user
$ python3
>>> import torch
>>> from transformers import AutoTokenizer,AutoModelForQuestionAnswering
>>> tokenizer=AutoTokenizer.from_pretrained("KoichiYasuoka/deberta-base-japanese-aozora-ud-head")
>>> model=AutoModelForQuestionAnswering.from_pretrained("KoichiYasuoka/deberta-base-japanese-aozora-ud-head")
>>> def head(question,context):
...   inputs=tokenizer(question,context,return_tensors="pt",return_offsets_mapping=True)
...   offsets=inputs.pop("offset_mapping").tolist()[0]
...   outputs=model(**inputs)
...   start,end=torch.argmax(outputs.start_logits),torch.argmax(outputs.end_logits)
...   answer=context[offsets[start][0]:offsets[end][-1]]
...   return answer if answer!=tokenizer.mask_token else question
...
>>> print(head("世界中","世界中が刮目している"))
刮目し
>>> print(head("が","世界中が刮目している"))
世界中
>>> print(head("刮目し","世界中が刮目している"))
刮目し
>>> print(head("ている","世界中が刮目している"))
刮目し

なかなかイイ線だ。ただ、単純なtorch.argmaxでは語境界との矛盾が起こる可能性が高く、そのあたりtorch.topkによる確率処理を入れるべきだろう。また、同じ単語が複数あらわれるような文においては、今のところcontext中で[MASK]指定する手法を採用しているが、これの妥当性も考えなければならない。さて、どう進めて行こうかな。

15698641 journal
人工知能

yasuokaの日記: Question Answeringを係り受け解析に応用するには

日記 by yasuoka

Leilei Gan, Yuxian Meng, Kun Kuang, Xiaofei Sun, Chun Fan, Fei Wu, Jiwei Li『Dependency Parsing as MRC-based Span-Span Prediction』(60th Annual Meeting of the ACM (May 2022), Vol.1: Long Papers, pp.2427-2437)を、私(安岡孝一)なりに検討してみた。単語間に区切りの無い言語においては、この論文のtokenやspanをそのまま当てはめるのは難しく、あえて単語(word)と構成鎖(catena)を使うべきではないか、というのが現時点での結論だ。ただ、全ての構成鎖を使うと大変なことになってしまうので、各単語を起点とする最大の構成鎖だけを、とりあえず見てみよう。たとえば「世界中が刮目している」の国語研長単位Universal Dependencies係り受け解析木

世界中 NOUN ═╗<╗ nsubj
が     ADP  <╝ ║ case
刮目し VERB ═╗═╝ root
ている AUX  <╝   aux

に対するQuestion Answeringは、尋ねる単語や構成鎖を[MASK]で表すことにすると、以下のように表現できる(気がする)。

{ "context":"世界中が刮目している",
  "qas":[{ "question":"[MASK]が刮目している",
           "answer":{ "start":4, "end":7, "text":"刮目し", "label":"nsubj" }},
         { "question":"[MASK]刮目している",
           "answer":{ "start":4, "end":7, "text":"刮目し", "label":"nsubj" }},
         { "question":"世界中[MASK]刮目している",
           "answer":{ "start":0, "end":3, "text":"世界中", "label":"case" }},
         { "question":"世界中が[MASK]ている",
           "answer":{ "start":4, "end":7, "text":"刮目し", "label":"root" }},
         { "question":"[MASK]",
           "answer":{ "start":4, "end":7, "text":"刮目し", "label":"root" }},
         { "question":"世界中が刮目し[MASK]",
           "answer":{ "start":4, "end":7, "text":"刮目し", "label":"aux" }}]}

ただ、こういう形でQuestion Answeringを作ったとしても、構成鎖を順に狭めていくのと、隣接確率行列を作ってChu-Liu-Edmondsを解くのでは、あまり結果が変わらない気がする。うーん、このあたり、実際に試してみるしかないかな。

15697261 journal
人工知能

yasuokaの日記: esupar向け国語研長単位係り受け解析用DeBERTaモデル(BertJapaneseTokenizer使用)を公開

日記 by yasuoka

一昨昨日昨日の日記で作成した日本語DeBERTa(V2)モデル(青空文庫元データ2.37億字+異体字増量分0.64億字、BertJapaneseTokenizer使用)を、5月29日の日記と同様の手法でファインチューニングしてみた。とりあえずUPOS/LAS/MLASで評価した結果を以下に示す。

  • deberta-base-japanese-unidic-luw-upos
    構築時の評価(evaluation) 96.92/92.82/84.30 テスト(predict) 96.75/91.71/82.90
    共通テスト『国語』第1問【文章Ⅰ】93.07/84.04/65.47 【文章Ⅱ】96.97/88.60/73.10
  • deberta-large-japanese-unidic-luw-upos
    構築時の評価(evaluation) 96.78/92.50/83.97 テスト(predict) 96.90/91.83/83.47
    共通テスト『国語』第1問【文章Ⅰ】93.92/84.97/67.86 【文章Ⅱ】96.54/86.89/71.48

ざっと見た限り、私(安岡孝一)が以前作ったDeBERTa(V2)モデル群より少し性能がいい。うーむ、DebertaV2TokenizerFastとBertJapaneseTokenizerの差なのかなぁ。

15696147 journal
人工知能

yasuokaの日記: 青空文庫DeBERTaモデルdeberta-large-japanese-unidicリリース

日記 by yasuoka

一昨日の日記の手法を拡張して、トークナイザをBertJapaneseTokenizerに置き換えた日本語DeBERTa(V2)モデルdeberta-large-japanese-unidicも作ってみた。24層・隠れサイズ1024・16ヘッド・トークン幅512とした上で、7772556文3億字(青空文庫元データ2.37億字+異体字増量分0.64億字)をNVIDIA A100-SXM4-40GBで1457355ステップ(32バッチ)学習させたところ、50時間53分かかってしまった。JGLUEが、transformers 4.19.2をサポートしてくれたので、Google Colaboratoty (GPU)上でJCommonSenceQAに挑戦してみよう。

!test -d transformers-4.19.2 || git clone -b v4.19.2 --depth=1 https://github.com/huggingface/transformers transformers-4.19.2
!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.19.2 && patch -p1 ) )
!cd transformers-4.19.2 && pip install .
!pip install -r transformers-4.19.2/examples/pytorch/text-classification/requirements.txt
!pip install fugashi unidic-lite protobuf==3.19.1 tensorboard
!python transformers-4.19.2/examples/pytorch/multiple-choice/run_swag.py --model_name_or_path KoichiYasuoka/deberta-large-japanese-unidic --do_train --do_eval --do_predict --max_seq_length 64 --per_device_train_batch_size 12 --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 False --evaluation_strategy epoch --warmup_ratio 0.1

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

***** eval metrics *****
  epoch                   =        4.0
  eval_accuracy           =     0.4718
  eval_loss               =     2.7386
  eval_runtime            = 0:00:33.67
  eval_samples            =       1119
  eval_samples_per_second =     33.232
  eval_steps_per_second   =      4.158

JCommonSenseQAが0.4718なので、一昨昨日の日記の結果を考え合わせても、青空文庫DeBERTaモデルはJCommonSenseQAに向いてない、ということだろう。他のタスクも試してみたいところだけど、このJGLUEパッチは、transformers本体のsquad.pyやsquad_metrics.pyを勝手にイジっちゃうので注意を要する。examples配下だけだったらいいのだけど、src配下までイジっちゃうのは、まあ何か都合があるのだろう。でも、しばらくはGoogle Colaboratoryで試すしかないかな。

15694822 journal
人工知能

yasuokaの日記: JGLUEのJSQuADをtransformers 4.19.2のDebertaV2TokenizerFastでムリヤリ動かすには

日記 by yasuoka

一昨日の日記で、私(安岡孝一)は以下のように書いた。

でも、JGLUEのタスクのうち、JSQuADはDebertaV2TokenizerFastを受け付けてくれないようだ。

この問題を解決すべく、JSQuADのtrain-v1.0.jsonを眺めていたのだが、妙な点があって悩ましい。たとえばid:a3097844p53q2は、contextが"バラク・オバマ [SEP] 2014年12月17日にオバマとカストロは国交正常化交渉の開始を電撃発表した。数か月以内に大使館を開設し、銀行や通商関係の正常化を話し合うことでも合意した。『』はこの雪解けを「オバマ政権の最大の外交成果」だと位置づけている。"、questionが"アメリカとキューバの国交正常化交渉の開始が発表されたのはいつ?7"となっているのだが、この[SEP]はsep_tokenだとみなしていいのだろうか。あるいは末尾の7は、どう扱うべきなのだろうか。

このあたりがイマイチわからないので、自信がないまま、transformers 4.19.2のrun_qa.py向けに変換してみた。Google Colaboratory (GPU)だと、以下のような感じ。

!test -d transformers-4.19.2 || git clone -b v4.19.2 --depth=1 https://github.com/huggingface/transformers transformers-4.19.2
!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.19.2 && patch -p1 ) )
!cd transformers-4.19.2 && pip install .
!pip install -r transformers-4.19.2/examples/pytorch/text-classification/requirements.txt
!pip install protobuf==3.19.1 tensorboard
import json
for f in ["train-v1.0.json","valid-v1.0.json"]:
  with open("JGLUE/datasets/jsquad-v1.0/"+f,"r",encoding="utf-8") as r:
    j=json.load(r)
  u=[]
  for d in j["data"]:
    for p in d["paragraphs"]:
      for q in p["qas"]:
        u.append({"id":q["id"],"title":d["title"],"context":p["context"],"question":q["question"],"answers":{"text":[x["text"] for x in q["answers"]],"answer_start":[x["answer_start"] for x in q["answers"]]}})
  with open(f,"w",encoding="utf-8") as w:
    json.dump({"data":u},w,ensure_ascii=False,indent=2)
!python transformers-4.19.2/examples/pytorch/question-answering/run_qa.py --model_name_or_path KoichiYasuoka/deberta-base-japanese-aozora --do_train --do_eval --max_seq_length 384 --learning_rate 5e-05 --num_train_epochs 3 --per_device_train_batch_size 16 --per_device_eval_batch_size 16 --output_dir ./output_jsquad2 --overwrite_output_dir --train_file train-v1.0.json --validation_file valid-v1.0.json --save_steps 5000 --warmup_ratio 0.1

このプログラムでdeberta-base-japanese-aozoraをファインチューニングしてみたところ、GPUでも4時間ほど要した上、私の手元では以下の「eval metrics」が出力された。

***** eval metrics *****
  epoch            =     3.0
  eval_exact_match = 74.9887
  eval_f1          = 75.3574
  eval_samples     =    4493

JSQuADが、EM/F1=0.7499/0.7536なので、昨日の日記のBertJapaneseTokenizerより、DebertaV2TokenizerFastの方が性能がいいということになる。ただ、こういう対応の仕方がベンチマークとして正しいのかどうか、私自身よく分からない。うーん、このあたり全部を、ちゃんとJGLUEでサポートしてほしいなあ。

typodupeerror

ソースを見ろ -- ある4桁UID

読み込み中...