日本語CTRLを1から学習する - 12

はじめに

日本語CTRLを学習し始めてから1週間ほどたったので
現状と課題についてまとめる。

学習状況

現状550000Iterほど学習が進んでいる。

元文章の再現

青空文庫データの「庭をつくる人」(室生犀星)の序文入力結果。太字は入力文

元文章の序文

つれづれ草に水は浅いほどよいと書いてある。わたくしは子供のころは大概うしろの川の磧で暮した。河原の中にも流れとは別な清水が湧いていて、そこを掘り捌《さ》いて小さいながれ[#「ながれ」に傍点]をわたくしは毎日作って遊んだものである。

CTRL出力文(各制御コードに対して)
つれづれ草に
青空 つれづれ草に 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花が咲く 花
知識 つれづれ草に つれづれ草に(つれづれぐさに)は、日本のシンガーソングライター・川嶋あいの2枚目のシングル。 表題曲は、テレビアニメ『美少女戦士セーラームーン』のエンディングテーマに起用された。 表題曲は、テレビアニメ『美少女戦士セーラームーン』のエンディングテーマに起用された。 表題曲は、テレビアニメ『美少女戦士セーラームーン』のエンディングテーマに起用された。 表題曲は、テレビアニメ『美少女戦士セーラームーン』のエンディングテーマに起用された。 カップリング曲
つれづれ草に 花が咲くのは 楽しいことじゃない。 でも もう いいの? でも もう いいの? でも いいの? でも いいの? でも いいの? でも いいの? でも いいの? でも いいの? でも いいの? でも いいの? でも いいの? でも いいの? でも いいの?

CTRL出力の最後に同じ文章を繰り返しているのは終了条件を正しく与えていないことが
原因なのでここでは無視する。

この短い入力の場合

  • 制御コード「青空」の場合「草」というキーワードに対して花の関連する単文を返している
  • 制御コード「知識」の場合は入力を曲のタイトルだと解釈してそれに関連する文章を生成している
  • 制御コード「訳」の場合は会話文風に返している

次に序文を伸ばしてみる

つれづれ草に水は浅いほどよいと書いてある。
青空 つれづれ草に水は浅いほどよいと書いてある。 山は、山が深くて、山が深くて、山が深くて、山が深くて、山が深くて、山が深くて、山が深くて、山が深くて、山が深くて、山が深くて、水が深くて、水が深くて、水が深くて、水が深くて、水が深くて、水が深くて、水が深くて、水が深くて、水が
知識 つれづれ草に水は浅いほどよいと書いてある。 花は、花序の基部から出る。花序は、花序の基部から出る。花序は、花序の基部から出る。花序は、花序の基部から出る。花序は、花序の基部から出る。花序は、花序の基部から出る。花序は、花序の基部から出る。 果実は、種子を含まない。 和名
つれづれ草に水は浅いほどよいと書いてある。 でも あの 草は まだ まだ ある。 でも あの 草は まだ ある。 だから あの 草は ある。 あの 草は ある。 あの 草は ある。 あの 草は ある。 あの 草は ある。 あの 草は ある。 あの 草は ある。 あの

少し伸ばした入力に対しては

  • どの制御コードでも短い文の時とは異なる文章を続けている
  • 制御コード「青空」のものは文章が正しく終わっていない

さらに前文を伸ばしてみる

つれづれ草に水は浅いほどよいと書いてある。わたくしは子供のころは大概うしろの川の磧で暮した。
青空 つれづれ草に水は浅いほどよいと書いてある。わたくしは子供のころは大概うしろの川の ⁇ で暮した。川には鮎がたくさんとれた。川には鮎の ⁇ がたくさんとれた。川には鮎の ⁇ がたくさんとれた。川には鮎の ⁇ がたくさんとれた。川には鮎の ⁇ がたくさんとれた。川には鮎の ⁇ がたくさんとれた。川には鮎の ⁇ がたくさんとれた。川には鮎の ⁇ が
知識 つれづれ草に水は浅いほどよいと書いてある。わたくしは子供のころは大概うしろの川の ⁇ で暮した。その ⁇ は、その ⁇ の茎の節から、その ⁇ の茎の節に、その ⁇ の茎の節から、その ⁇ の茎の節から、その ⁇ の茎の節から、その ⁇ の茎の節から、その ⁇ の茎の節から、その ⁇ の茎の節から、その ⁇ の茎の節
つれづれ草に水は浅いほどよいと書いてある。わたくしは子供のころは大概うしろの川の ⁇ で暮した。 ⁇ は ⁇ の茎を刈り取って、その茎を刈り取って、その茎を刈り取って、その茎を刈り取って、その茎を刈り取って、その茎を刈り取って、その茎を刈り取って、その茎を刈り取って、その茎を刈り取って、その茎を刈り取って、その茎を刈り取って、その茎を刈り

"磧"(かわら)という文字は珍しい文字でありSentence Pieceの辞書に入っていないので
??(不明文字)として与えている。

  • 「青空」の場合はそれなりに意味の通る文章を返している。
  • 「知識」「訳」の出力はあまり文章の体をなしていない。

この文章は青空文庫からのデータのものなので「青空」が最も自然な出力となることは妥当である。
ただ「青空」の出力も元の文章とは異なるので学習した知識が混ざってオリジナルの文章を 作り上げていると言える。

次にWikipedia(口入)を序文とする

元文章の序文

口入\n\n口入(くにゅう)は、口出しや干渉、仲介、斡旋などの行為を意味しており、日本においては古代から近代まで長く使われた言葉であるが、中世には特殊な法律用語として用いられた。

口入
青空 口入屋《くちいれや》の亭主は、 「お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お、お
知識 口入屋 口入屋(くちいれや)は、東京都千代田区神田駿河台に本社を置く、主に医薬品・医療機器の卸売りを扱う企業。 主に医薬品・医療機器の卸売りを扱う。 主に医薬品・医療機器の卸売りを扱う。 主に医薬品・医療機器の卸売りを扱う。 主に医薬品・医療機器の卸売りを扱う。 主に医薬品・医療機器の卸売りを扱う。 主に医薬品・医療機器の卸売りを扱う
口入屋が 何で こんな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカな バカ
  • 入力が短すぎるためどの制御コードでも「口入」ではなく「口入屋」に対して言及している
  • 「知識」の出力は一見最もらしい文章だが調べた限り「口入屋」という企業は存在しないのでfake newsを作っている
口入 口入(くにゅう)は、口出しや干渉、仲介、斡旋などの行為を意味しており
青空 口入 口入(くにゅう)は、口出しや干渉、仲介、斡旋などの行為を意味しており、口入の行為は、口入の行為と、口入の行為とに、その区別がある。口入は、口入の行為と、口入の行為とに、その区別がある。口入は、口入の行為と、口入の行為とに、その区別がある。口入は、口入の行為
知識 口入 口入(くにゅう)は、口出しや干渉、仲介、斡旋などの行為を意味しており、口入行為とは区別される。 口入行為は、口入行為の行為が、口入行為の行為が、口入行為の行為が、口入行為の行為が、口入行為の行為が、口入行為の行為が、口入行為の行為が、口入行為の行為が、口入行為の
口入 口入(くにゅう)は、口出しや干渉、仲介、斡旋などの行為を意味しており、口入行為とは、口入行為と、口入行為とを区別する。口入行為とは、口入行為と口入行為とを区別する。口入行為とは、口入行為と口入行為とを区別する。口入行為とは、口入行為と口入行為とを区別する。口入行為とは
  • 入力文を伸ばすと制御コード間の違いが小さくなる
  • 序文にある口入と行為という言葉から口入行為という単語を作りだしている

次にJESCデータを序文とする

元文章の序文

警察は酷い事故だったって 運転手は致命傷だったって

警察は
青空 警察は、その事件を、警察の無能と、無能との、二つの感情から、判断した。 警察は、その事件を、警察の無能と、無能との、二つの感情から、判断した。 警察は、その事件を、無能と無能との、二つの感情から、判断した。 警察は、それを、無能と無能との、二つの感情から判断した。 警察は、
知識 警察は、警察の捜査を妨害する目的で、警察の捜査を妨害する目的で、警察の捜査を妨害する目的で、警察の捜査を妨害する目的で、警察の捜査を妨害する目的で、警察の捜査を妨害する目的で、警察の捜査を妨害する目的で、警察の捜査を妨害する目的で、警察の捜査を妨害する目的で、警察の捜査を妨害する目的で、警察の捜査を妨害する目的で、警察の捜査を妨害する目的で
警察は 警察の 捜査を 妨害する という やり方を 見つけました。 でも 警察は 警察の 捜査を 妨害する という やり方を 見つけました。 警察は 警察の 捜査を 妨害する という やり方を 見つけました。 警察は 警察の 捜査を 妨害する という やり方を 見つけました。 警察は 警察の 捜査を

「警察は」という短い入力だけで「妨害」という言葉が出てくるのは知識が偏っているように感じる

警察は酷い
青空 警察は酷い目に遭った。 警察は、警察の無能を責める。 警察は、警察の無能を責める。 警察は、警察の無能を責める。 警察は、警察の無能を責める。 警察は、警察の無能を責める。 警察は、警察の無能を責める。 警察は、警察の無能を責める。 警察は、警察の
知識 警察は酷い目に遭った。 警察は、警察が「警察が警察を雇う」という、警察のやり方を非難する。 警察は、警察が「警察が警察を雇う」という、警察のやり方を非難する。 警察は、警察が「警察が警察を雇う」という、警察のやり方を非難する。 警察は、警察が「警察が警察を雇う」という、警察のやり方を非難
警察は酷い目に遭った 警察は 警察が 警察を 追い払うのを 阻止したんだ。 警察は 警察が 警察を 追い払うのを 阻止したんだ。 警察は 警察が 追い払うのを 阻止したんだ。 警察は 警察が 追い払うのを 阻止したんだ。 警察は 警察が 追い払うのを 阻止したんだ。 警察は 警察
  • " 警察は酷い目に遭った。"までは制御コード間で共通しているが、それ以降は多少異なっている。
  • 入力文が短いと特定の単語を繰り返しやすい傾向にある(今回は"警察")
警察は酷い事故だって
青空 警察は酷い事故だって、警察がそんな酷い目に遭わせるものかね。」 「そうさ。」とホームズは言った。「警察は、その事故を、その現場に起った出来事と結びつけて考えるべきだった。」 「その事故は、」とホームズが言った。「その事故は、」とホームズが言った。「その事故は、」とホームズが言った。「その事故は、」とホームズが言った。「その事故は
知識 警察は酷い事故だって言った。」と、その発言を引用した。 この事件は、警察が捜査を妨害したとの指摘もある。 この事件は、警察が捜査を妨害したとの指摘もある。 この事件は、警察が捜査を妨害したとの指摘もある。 この事件は、警察が捜査を妨害したとの指摘もある。 この事件は、警察が捜査を妨害したとの指摘もある。 この事件は、警察が
警察は酷い事故だって あるんだろ? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で? 何で
  • 先ほどの文と比べると制御コード間の差がかなり大きくなった
  • 「青空」は会話文の描写、「知識」は会話文の引用、「訳」は会話文そのものとそれぞれの特色が出ている

オリジナル文章作成

適当な文章を与えてみる。

入力文

物理学には愛がある。

物理学には愛がある。
青空 物理学には愛がある。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛である。愛は愛
知識 物理学には愛がある。 物理学では、物理学は、物理学の分野と、物理学の分野とに大別される。物理学は、物理学と物理学の分野に大別される。物理学は、物理学と物理学の分野に大別される。物理学は、物理学と物理学の分野に大別される。物理学は、物理学と物理学の分野に大別される。物理学は、物理学と物理学の分野に大別される。物理学は、物理学と物理学の
物理学には愛がある。 だから 私が あなたを 愛しているなら。 だから あなたを 愛しているなら。 だから あなたを 愛しているなら。 だから あなたを 愛しているなら。 だから あなたを 愛しているなら。 だから あなたを 愛しているなら。 だから あなたを 愛しているなら。 だから あなたを 愛しているなら。 だから あなたを 愛しているなら。
過去というものは未来の積み重ねである
青空 過去というものは未来の積み重ねである。過去を未来に、未来を未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、未来に、
知識 過去というものは未来の積み重ねである。 過去は、過去は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、未来は、
過去というものは未来の積み重ねであると 考えています。 過去を 未来に 変えるということは 未来を 変えるということです。 過去を 変えるということは 未来を 変えるということです。 過去を 変えるということです。 過去を 変えるということです。 過去を 変えるということです。 過去を 変えるということです。 過去を 変えるということです。 過去を 変えるということです。 過去を 変える
既存の概念を覆す帽子
青空 既存の概念を覆す帽子の効果を、その概念の分析によって、我々は、この概念の分析によって、その概念の分析によって、その概念の分析によって、その概念の分析によって、その概念の分析によって、その概念の分析によって、その概念の分析によって、その概念の分析によって、その概念の分析によって、その概念の分析によって、その概念の分析によって、その概念の分析によって、その概念の分析によって、その概念
知識 既存の概念を覆す帽子の「帽子」は、そのデザインやデザインの方向性を、より明確にすることを目的としている。 帽子は、そのデザインやデザインの方向性を、より明確にすることを目的としている。 帽子は、デザインやデザインの方向性を、より明確にすることを目的としている。 帽子は、デザインやデザインの方向性を、より明確にすることを目的としている。 帽子は、デザインやデザインの方向性を、より明確にすることを目的としている。 帽子は
既存の概念を覆す帽子を デザインするのです。 我々は 我々の 新しい概念を 作り変えました。 我々は 新しい概念を 作り変えました。 我々は 新しい概念を 作り変えました。 我々は 新しい概念を 作り変えました。 我々は 新しい概念を 作り変えました。 我々は 新しい概念を作り変えました。 我々は 新しい概念を作り変えました。 我々は 新しい概念を作り変えました。 我々は

どの文でも入力後の最初の一文はそれらしい言葉が続くのだが、
それ以降に長い意味のある文章の生成はできていない。

現状のまとめと改良

現状できていること
  • 入力次第ではある程度意味の通る文章が生成される
  • 制御コードごとに特色のある文章生成ができている
現状できていないこと
  • 長文の生成ができていない
  • 終了判定ができていない

終了判定ができていないのは学習時の前処理で終了コードを教えていないため であるので修正が必要である。
長文が生成できていない理由については現状よく分かっていないが、
考えられる原因としてデータの前処理が良くない可能性がある。

そのため以下の改良を行う。

  1. pad を利用するためにsentence pieceを学習しなおす
  2. 前処理を改良する

前回sentence pieceを学習したときにpad指定を忘れていたので
現在の学習時にpadするときはeosコードを変わりに利用しており、
cross entropyの計算時もeosは無視していた。
次回はeosも予測するようにするためsentence pieceは再学習する。

前処理の改良

現状学習リソースの制限上、学習時の入力トークン数は256が最大となっている。
Wikipedia青空文庫の記事・作品データはこれらよりはるかに長い。(JESCデータは短い)
そのため学習時はWikipedia青空文庫のデータはランダムに切り出して入力としている。
これにより文章の開始としておかしいものまで入力されることで問題の難易度が上昇していると思われる。

これに対処するため元文章から、独立した文章としておかしくない部分を切り出す方法を考える。

青空文庫データ

以前の記事で調べた通り青空文庫データはデータ数自体はそれほど多くないが1つのデータ
が長い文章である。 そのため1つの文章を分割する必要がある。

意味のある文章を生成するためには、できる限り意味が分かれている箇所で分割したいのだが、
データごとに段落、改行などの形式が異なるため正しく分離することは難しい。
そこで文章の中である程度意味のあると思われる部分をおおざっぱに切り出すようにする。

以下のような処理を行う。

  1. 文章から不要部分を削除する。(この記事を参考)
  2. 文章を改行コードで分割し、切れ目の候補行を推定する
  3. 切れ目の候補行から数行を抽出する(5行程度)
  4. 1行が非常に長いものはそのまま利用する

コード - 青空文庫前処理・分割

import re
cmp1 = re.compile('[#.+?]')
cmp2 = re.compile('《.+?》')
line_size = 5
txt_threshold = 100
threshold_text = 500
min_text = 30
max_text = 2000

def preprocess_text(text):
  text = cmp1.sub("", text)
  text = cmp2.sub("", text)
  lines = text.split("\n")

  starts = [0]
  zero_lines = []
  for l in range(len(lines)):
    zero_lines.append(len(lines[l])==0)
    if len(lines[l-1]) == 0 and len(lines[l]) > 0:
      starts.append(l)


  txts = []
  for s in starts:
    lns = zero_lines[s:s+line_size]
    if sum(lns) > 0 and len("".join(lines[s:s+line_size])) < threshold_text:
      continue
    txt = "\n".join(lines[s:s+line_size])
    if len(txt) <= max_text:
      if len(txt) >=min_text:
        txts.append(txt)
    else:
      n_txt = len(txt)
      lines = txt.split("\n")
      n_lines = len(lines)
      txt_per_line = n_txt // n_lines
      i_count = max_text // txt_per_line
      for i in range(i_count+1):
        tx = "\n".join(lines[i*i_count:(i+1)*i_count])
        if len(tx) >= min_text:
          txts.append(tx)
  return txts

Wikipediaデータ

Wikipediaデータは青空文庫のデータほど長くもなく、
また形式もある程度統一されているため簡単な前処理とする。

  1. 文章の最初に存在するタイトル部分の除去
  2. 長い文章を改行で均等に分割する。

コード - Wikipedia前処理・分割

txt_base = 700
new_texts = []
not_title = 0
nan_count = 0
for i, row in tqdm_notebook(wiki_df.iterrows()):
  title = row["title"]
  text = row["text"]
  if isinstance(text, float):
    nan_count += 1
    continue
  if isinstance(title, float):
    not_title += 1
  elif not text.startswith(title):
    not_title += 1
  else:
    txt = text[len(title):].strip()
  n_txt = len(txt)
  n_split = (n_txt + txt_base - 1) // txt_base
  if n_split > 1:
    lines = txt.split("\n")
    n_lines = len(lines)
    nl_size = n_lines //n_split
    for n in range(n_split):
      new_texts.append("\n".join(lines[n*nl_size:(n+1)*nl_size]))
  else:
    new_texts.append(txt)

 データ読み込み時処理

以上の前処理により、学習時のランダム切り出しは行わないこととする。

  1. 文章のトークン長が255よりも短い文章はeosを付与する
  2. eosを付与しても255以下の文章は255となるようにpadを末尾に付与する
  3. 255よりも長い文章は最初の255トークンで文章を切り出す(それ以降の文章は使わない)
  4. 文章の最初に制御コードに対応するトークンを付与する

これにより前回の学習と比べるとデータ1つ1つが単体で文章として成立しやすく
なることを期待する。

まとめと今後

現時点である程度文章を生成する能力は学習できているが、
数日前からの学習経過を見る限りこれ以上の性能向上は期待できないと思う。

現状の学習は60000ステップで止めて前処理を変更した設定で再学習してみる。

参考文献