LoginSignup
1203
380

More than 5 years have passed since last update.

【続】平成の次の元号を、AIだけで決めさせる物語(@テレビ取材)

Posted at

前書き

以前Qiitaの記事で、平成の次の元号をAIで予測したことがあり、
それについて、テレビ取材(フジテレビ)を受けることになった。

前回作った元号予測AIを強化して、
四書五経や、日本の古典なども出典も視野に入れて、
AIによる元号ガチ予測を実施して欲しいとのこと。
4月1日、新元号が発表される当日、発表直前に放映する予定だそうだ。

つまり、新元号の予想はXXです!と言った瞬間に、
残念違いましたーー!と分かってしまう、がっかり感

事前にウワサ等で予想されているものは採用されない、
という話もあるために、ガチで当てるためには直前放映しかない
という恐るべき戦い
(どう考えてもピッタリ当てるのは難易度が高すぎる)

まだ前回の記事をご覧になっていない方は、以下を参照されたし。

前回の記事⇒ 平成の次の元号を、AIだけで決めさせる物語

本記事投稿理由

本当は、4月1日の放送に期待してください、
以上終わり、なのだが、

①テレビ上の放送時間は恐らく5分程度と短く、
 一般人向けの放送であるため、
 プログラム的な面白さはたぶんほぼ含まれない。

②4月1日の放送&発表後に、試行錯誤した内容や、
 プログラム自体をアップしても良いが、
 4月1日の発表後に記事にしても、
 既に正解が出ているので全く面白くない。

③むしろ、発表後に投稿するならば、
 「実際に公開された元号が、AIから見てどうだったのか?」という
 新元号の評価の物語の方が
 外した予想の物語よりも、オモシロイだろう。

⇒苦肉の策として、
 放送前に、予想AI&プログラムの物語の大部分の趣旨は公開してみる
 Qiitaの読者諸兄には前回の物語の続きを心待ちにしている
 おかしな人もいるかもしれないので、ちゃんと続きを書く。
 番組宣伝的にもなっていいかもしれない。たぶん。

 事前に予想されてしまうと却下という話のため、
 最後の「結論」的な部分は
 公開できない部分があるが、そこはご了承を願いたい。
 まあ、「予想」は、発表後に書いても無意味なので、
 放送時間では入りきらない&詳細すぎる部分を、
 さきにここに書いておくよ、ってこと。
 あくまで、プログラムを使った
 現在進行形の「物語」としてお楽しみください。

前回の予測の概要と、この記事で目指す内容

前回の記事の内容は、
良い意味を持つ漢字と、
その漢字のバランスの取れた組み合わせ
機械的な計算のみで見つけることができるか?
という試みであった。

前回の記事⇒ 平成の次の元号を、AIだけで決めさせる物語

元号の多くは、四書五経などの漢籍に出典を持つが、
それは定められたルールではない

元号法に関連して定められた、
1979年の元号選定手続きの要領によれば、
あくまでも以下の6つがルールである。

 1. 国民の理想としてふさわしいようなよい意味を持つものであること。
 2. 漢字2字であること。
 3. 書きやすいこと。
 4. 読みやすいこと。
 5. これまでに元号又はおくり名として用いられたものでないこと。
 6. 俗用されているものでないこと。

よって、四書五経などの漢籍を参照するという、
いわば「定石」を学習せずに、
「1」の「国民の理想としてふさわしいようなよい意味」を、
日本語の通常の文章(wikipediaのテキストデータ)から
機械学習によって導き出すことができるか?という点が
前回挑戦したテーマである。

例えば、囲碁や将棋のAIを作る場合を考えてみると、
そのルールだけから作るのか、
プロ棋士同士の棋譜を見て学ぶのか、二通りの方法があるが、
プロ棋士の棋譜(漢籍そのもの及び、学者の選定基準)を
一切入れずにどこまで出来るのか、ということ。

元号を「当てる」ために作っているのとはちょっと違う。

※実際は、四書五経などの古典データの入手が面倒という点と、
 そのどの書をお手本に選ぶかなどの、人間判断が入ってしまうことを
 嫌ったという実務的な理由もあって、「ルールのみ」の取り組みとした。

今回のフジテレビ殿の依頼内容について、私と利害が一致した理由は、
じゃあコイツに「出典」の候補になるデータを喰わせたらどうなるの?
もっと頑張れば(当てにいけば)どうなるの?
という興味の探究である。

フジテレビ殿としても、Qiita記事そのままではなく、さらなる強化版を放送したい。
私としても、出典こみ&様々な要素を強化したバージョンを
元号発表前までの旬な間に作ってみたいし、当ててみたい
ということで、出典の入手をフジテレビ殿にお願いし、
前回のプログラムのブラッシュアップを行うことになった。
(取材の申し込みをいただいたのは良いキッカケとなった)

実際に出た結論は4月1日までのお楽しみとして、
試した内容や、プログラムを強化した内容、
その試行錯誤の歩みの一部を、以下に記す。
(※本記事投稿時点ではほぼ完成したものの、最終結論はもうちょい)

ちゃんと記録するとまたまた長くなってしまう。
同じ時代を生きる人々へ、歴史的瞬間に共に立ち会う物語として、
未来への戦いに挑んだ記録を残す日誌として、扱って欲しい

本投稿の内容

前回記事を見ていただいた上で、その強化版&続編です。
こういう検討をしたけどダメだった、の部分は
放送時間的に全く入らないと思われるため、そのへんの記録目的も含めます。

  • ガチで、AIだけに漢字のセレクトを任せ、新元号に相応しい言葉を計算します。
  • 使えるINPUT情報は、以下のようにしました。
    • Wikipediaのテキストデータ
      • (ただの大量テキストとして扱う)
    • 既に使われた元号の一覧(=これをお手本として学習)
    • 教育用漢字一覧(小学校で覚える漢字一覧=1006字)(=候補一覧)
      • 読みやすい=小学校レベルの漢字、と仮定。
      • (※常用漢字の場合は1945字。データを入れ替えればこちらでも可)
    • 別な意味の除外リストの作成のための情報
      • Mecabの辞書(これは前回も利用していた)
      • 過去の天皇名の一覧(New)
      • Wikipediaのタイトル項目の一覧(New)
    • 漢字読み方API(New)
    • 四書五経、古事記、などの中国&日本の古典データ
      • フジテレビ殿に入手していただいたデータ、詳細はヒミツ(New)
      • 漢籍だけでなく、日本古典の可能性もありえるらしい
      • 一部学者先生によるオススメ古典も入れるかもしれない
  • 製作者が調整出来ることは「数値の設定だけ」で、主観で判断はいけない、とします。
    • ★ここは前回と同様。正直一番厳しい制約
    • 例えば、「苦」の字は意味が悪いから使われないよね、といって除外するのは禁止
    • 何かの基準で、上位N個に絞るとか、何かの得点値が10以上、などと数値を切るのはOK
    • つまり、候補の約1000字に対して、「全ての文字を平等に扱う」ことになる。
      • ※固定ルールを予め実装するのはアリとする。「平」が再度になることは無い、漢数字は誤解が生じるため使わない、など
      • 数値やテキストの変更だけに依存し、誰がやっても同じ結果が出ること、と言い換えても良い。
  • おおまかな内容
    • 大量の日本語コーパス(wikipedia)or 漢籍&古典の学習結果をもとに、
    • お手本データ=過去元号、と似たような文字 = 「良い文字」を見つける
    • 画数なども踏まえて、元号で採用されるであろう漢字の候補を絞る
    • 漢籍&古典の出典がある組み合わせを探索する
    • MTSHチェック(明治大正昭和平成とイニシャルが重複しない)や、
    • その他の意味を持つかどうか、などの複合的な要因で、フィルタリング
    • 出てきた文字の組み合わせの「バランス」と「良字具合」を、AIに評価させる
    • 出典ごとに、評価値が高かったものを提示する
  • コードの実行環境は全て、Windows10 + Python3 +JupyterNotebook を前提。

元号予想の挑戦方法の方針

最初に、大きな進め方として2案ある。

前回は、「良い意味の文字」を見つけるために、
日本語コーパスに対する機械学習を行い、
char2vecの技術を用いて、
漢字同士の意味をベクトル化した。

それを踏まえて、以下の2方針をそれぞれ検討&実行した

方針案① 学習のINPUTに漢籍古典を使おう案
このchar2vecの機械学習の元データとして、漢籍や古典を使う
(最初から漢籍・古典をベースとする)という案

方針案② 出典を見つけにいこう案
現代日本人にとっての漢字の意味付けはWikipediaテキストデータで
既に出来ていると考えて、前回できなかった「出典」を探しに行く
(漢籍や古典を検索対象として使う)という案

方針案① 学習のINPUTに漢籍古典を使おう案

まず、「漢籍」=四書五経など、
と日本の古典は、それぞれ分けて考える。
そもそも言語が違う点と、「漢籍」のデータは
中国語の漢字で作られており、日本語の漢字とコードが一致しないため。

最終的には、両方とも試した。

まず、対象とするutf-8形式のテキストデータを
同じフォルダに集め、それらを結合したり、
余計な文字を排除したりしたデータを作って、
学習のINPUTファイルとする。

ファイルを集める
%%time
import codecs
import glob
import re
def open_ch_file(filepath):
    input_text = ""
    with codecs.open(filepath,"r", "utf-8") as f:
        input_text += f.read()
        #改行を小さな空白に変換
        input_text = input_text.replace('\r','')
        input_text = input_text.replace('\n',' ')
        # 数値を除去
        input_text = re.sub(r'[0-9]+', "", input_text)
    return input_text

folder_file_path = "XXXXX/XXXXX/*.txt"
file_list = glob.glob(folder_file_path)
input_text = ""
for filepath in file_list:
    input_text += open_ch_file(filepath)

#1文字ずつに区切る
chars = [c for c in input_text if c != u' ']

with codecs.open('KANSEKI.txt',"w", "utf-8") as f:
    f.write(u' '.join(chars))

集めたファイルに対して、word2vecの学習を実施する。
この部分のコードは、パラメータの違い以外は前回と同様。


%%time
import logging
from gensim.models import word2vec

logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

sentences = word2vec.Text8Corpus('KANSEKI.txt')
model = word2vec.Word2Vec(sentences, size=40, window=12, min_count=3, hs=0, negative=5, iter=30)

model.save("mychar2vec_XXXXXX_model")

前回との大きな違いは、使用するパラメータ。
ひらがななどのデータが混ざっていないため、
windowサイズは小さめに設定してもいいかもしれない。
データの総量が少ないため、
次元数(size)やmin_countは小さめに。
繰り返し実行回数(iter)は大きめに設定するなど変更して、
何度か試している。
Wikipediaほどのボリュームがないため、
何度か試してもすぐに終わる点は楽。

出来たモデルの性能を以下のように試してみる。

お試し入力
out = model.most_similar(positive = [u'書'], topn=10)
print(out)
out = model.most_similar(positive = [u'右'], topn=10)
print(out)
out = model.most_similar(positive = [u'天'], topn=10)
print(out)
結果
[('記', 0.6210867762565613), ('諱', 0.5740101337432861), ('讀', 0.5511521697044373), ('古', 0.510854184627533), ('著', 0.5030744075775146), ('臧', 0.47281569242477417), ('蘇', 0.4715611934661865), ('告', 0.46267572045326233), ('論', 0.45687738060951233), ('噩', 0.4497215151786804)]
[('左', 0.6416146159172058), ('纛', 0.6342562437057495), ('殳', 0.6199294924736023), ('拒', 0.5840848684310913), ('夾', 0.5733482241630554), ('旆', 0.5700335502624512), ('綦', 0.5488435626029968), ('衽', 0.5380955934524536), ('把', 0.5014756917953491), ('肩', 0.47802940011024475)]
[('陛', 0.8151514530181885), ('祇', 0.5380247235298157), ('垓', 0.5238890647888184), ('上', 0.4873164892196655), ('歿', 0.4687088131904602), ('宜', 0.4571455717086792), ('祗', 0.447026789188385), ('佑', 0.43108344078063965), ('綱', 0.4226115643978119), ('足', 0.4220472574234009)]

「書」に対して「記」
「右」に対して「左」
が出てくるため、多少上手くいっているところはあるが、
2位以降が良く分からない。
wikipediaで作ったものとは全くレベルが違う。

試しに200種類にクラスタリングした結果を見ると、
「東西南北」が同じクラスタに入っていたり、
「父母兄弟」が同じクラスタに入っていたりと、
良い部分もいくつか見られたものの、
予想で使えるレベルにはなっていなかった。

「出典になりそうな良い古典」だけをINPUTにした場合、
やはり、単純にデータボリュームが小さいために、
様々な漢字を網羅した学習モデルを作るのには無理があった。
上記の例は、漢籍の出力例だ。日本の古典でも同様かそれ以下であった。

方針案①の結論:
「学習用データ」として「古典」を扱うことはそのデータ量的に難しい、と判明した。

方針案② 出典を見つけにいこう案

案②は、結論から言えばある程度上手くいった。
前回と同様の方法で、採用されるであろう漢字候補を出し、
その漢字が古典上で使われている位置を検索、
「出典」と言えるほど近しい場所で使われているペアを見つけて
抽出するという考え方。

また、「漢字候補を出す場所」
「出てきた元号をチェック(評価)する場所」、
それぞれで、前回に比べて様々なパワーアップを実装している。

以降、追加した点や、ポイントを、実装含めてご紹介する。

漢字候補の抽出

いきなり、この物語の最大の核心的な関数を提示する。

元号に使われている漢字=「良い意味」を持つ、と仮定して、
常用漢字リストor教育漢字リストの中から、
その「良い意味」に近い文字を見つけたいという趣旨。

例えば、元号で3回以上使われている、以下の文字列を「お手本」と考える。

  • 永元天治応和長正文安延暦徳寛保承仁嘉康建慶久平弘貞享宝禄明大亀寿万化養観喜中政

お手本との距離(コサイン類似度)を計算する関数と、
全漢字に対して、その距離を求めて、近い字の上位を返す関数を、
それぞれ以下のように実装した。

#与えられた漢字と「お手本」との距離感を算出する。
#与えられた漢字と近い上位20%のお手本時の距離の平均。
def get_otehon_ave_ruijido(char, otehon_str):
    jyoui_kosuu = round(len(otehon_str)/5)
    distance = 0
    cnt = 0
    distance_list = []
    for stridx in range(0, len(otehon_str)):
        distance = JPmodel.similarity(char, otehon_str[stridx])
        distance_list.append(distance)

    distance_list.sort(reverse=True)
    distance_list=distance_list[0:jyoui_kosuu]
    ave = sum(distance_list)/len(distance_list)
    return ave

#お手本を入力すると、それと似た漢字の一覧を返す関数
#(どれくらい似ている文字を返すのか指定する)
def get_Gengou_Kouho_Kanji(otehon_str, target_str, min_ruijido):
    char_val_list=[]
    for char in target_str:
        if char in NG_STR:
            continue
        #元の文字と、お手本として指定したリストとの平均値をリスト化
        char_val_list.append([char, get_otehon_ave_ruijido(char, otehon_str)] )
    #ソート
    char_val_list = sorted(char_val_list, key=lambda x:x[1], reverse=True)
    gengou_kouho_kanji_str = ""
    for char_val in char_val_list:
        if char_val[1] >= min_ruijido:
            gengou_kouho_kanji_str += char_val[0]
    return gengou_kouho_kanji_str

前回は、それぞれの「お手本」に似ている文字を選んでから
各文字を「評価」するという段取りにしていた。
今回は、「評価関数」を先に作っておき、
常用漢字リストや教育漢字リストなど、
任意の対象に対して、その全漢字を「評価」出来る仕組みとした。

結果、「教育漢字」を対象にすると、ポイントの高い順に、
以下の結果が得られた。
ここでは、TOP50位までを表示している。

  • 永徳忠仁孝久清元康天安正喜幸松竹坂田氏宝明晴臣宮守豊城宗孫延老蔵郷皇養誠倉家里戸納長志尊太恩文寺賀敬

上記の結果出来た候補漢字を、
過去の元号の採用回数とともに出力してみよう!

候補漢字に過去採用回数を付けて出力する
for char in GENGOU_KOUHO_KANJI_STR:
    if char in OTEHON_STR:
        print(char ,":は",otehon_val_dict[char] ,"回過去使用(お手本リストに含む)")
    elif char in ALL_OTEHON:
        print(char ,":は",otehon_val_dict[char] ,"回過去使用(お手本リストに含まれない=AIが発見)")
    else:
        print(char , ": はどちらにも含まれていない、AIが見つけた新字です★")
出力結果
  29 回過去使用お手本リストに含む
  16 回過去使用お手本リストに含む
 : はどちらにも含まれていないAIが見つけた新字です
  13 回過去使用お手本リストに含む
 : はどちらにも含まれていないAIが見つけた新字です
  9 回過去使用お手本リストに含む
 : はどちらにも含まれていないAIが見つけた新字です
  28 回過去使用お手本リストに含む
  10 回過去使用お手本リストに含む
  23 回過去使用お手本リストに含む
  17 回過去使用お手本リストに含む
  19 回過去使用お手本リストに含む
  3 回過去使用お手本リストに含む
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
  7 回過去使用お手本リストに含む
  7 回過去使用お手本リストに含む
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
  16 回過去使用お手本リストに含む
  1 回過去使用お手本リストに含まれないAIが発見
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
  3 回過去使用お手本リストに含む
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
  19 回過去使用お手本リストに含む
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
  19 回過去使用お手本リストに含む
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です
 : はどちらにも含まれていないAIが見つけた新字です

前回の候補文字に近い。
上位50位 = 全体(教育用漢字約1000個)の5%の、
「過去元号(=お手本)と意味が似ている」とAIが捉えられた結果がコレ。

前回優勝者の「孝」をはじめとし、
「幸」「晴」「豊」「賀」なんてのも、
実はいままで元号に使われていなかった、のは少し驚き。
っぽい文字(?)が出ている気はする。

なお、INPUTを「常用漢字」に拡大する場合、TOP50には
「隆」「賢」「澄」「江」「雅」などが、新字として入ってきていた。

人間の感覚としては、ちょっと元号としてはどうかなー、
という字もあるのだが、AIの計算結果では、
これらの字が元号の文字に近いとみなされたのね、
というように見ると、それはそれで面白い。
何となく、「宮」「城」「郷」「家」「里」「寺」など、
場所系の言葉が近しいと思われたのだろうか。
「天」もある意味では良い場所を示しているし、
「建」などの字もお手本にあるから?

フィルタリング①画数

出てきた文字に対して、「書きやすい」の条件を満たすために、
画数でフィルタリングをかけることにする。

IPAが試験提供している以下のAPIを用いて、
予め常用漢字に対して、全部の画数を取得しておいた。

MJ文字情報取得API
https://mojikiban.ipa.go.jp/search/help/api

特定の漢字リストを使ってAPIデータを取得して保存しておく
import time
import pickle
target_list_dict = {}
for char in target_list:
    #スリープは必須
    time.sleep(3)
    api_result = get_char_data(char)
    if api_result['status'] == "success":
        print(api_result['results'][0]['読み'])
        target_list_dict[char] = get_char_data(char)
    else:
        print("API-ERR")
        #print(api_result)

print("API-Finish")
with open('jyouyou_kanji.dic', mode='wb') as f:
    pickle.dump(target_list_dict, f)

print("pickle-Finish")

何度も同じ漢字についてAPIを投げるのは、
API提供者の情報処理推進機構殿に申し訳ないので、
今回使う分はあらかじめ上記のように一回だけ取得して、
pickleで保存しておくことによって、
あとはローカルで使えるようにしておいた。

使い方
import pickle
import pprint
with open('jyouyou_kanji.dic', mode='rb') as f:
    KANJI_INFO_DIC = pickle.load(f)
pprint.pprint(KANJI_INFO_DIC["和"])
print(KANJI_INFO_DIC["皇"]['results'][0]['総画数'])
結果
{'count': 1,
 'find': True,
 'results': [{'IPAmj明朝フォント実装': {'フォントバージョン': '005.01', '実装したUCS': 'U+548C'},
              'JISX0213': {'包摂区分': '0', '水準': '1', '面区点位置': '1-47-34'},
              'MJ文字図形': {'MJ文字図形バージョン': '1.0',
                         'uri': 'http://mojikiban.ipa.go.jp/MJ008199.png'},
              'MJ文字図形名': 'MJ008199',
              'UCS': {'対応するUCS': 'U+548C', '対応カテゴリー': 'A'},
              '住基ネット統一文字コード': 'J+548C',
              '入管外字コード': '',
              '入管正字コード': '548C',
              '大字源': 1162,
              '大漢和': '3490',
              '大漢語林': 1374,
              '戸籍統一文字番号': '040260',
              '新大字典': 1886,
              '日本語漢字辞典': 1397,
              '漢字施策': {'人名用漢字': True, '常用漢字': True},
              '登記統一文字番号': '00040260',
              '総画数': 8,
              '読み': {'訓読み': ['やわらぐ', 'やわらげる', 'なごむ', 'なごやか', 'あえる'],
                     '音読み': ['ワ', 'オ', 'カ']},
              '部首内画数': [{'内画数': 5, '部首': 30}]}],
 'status': 'success'}
9

このように、特定の漢字に関する詳細データ、画数などが
いつでも取得できるようになった。
元号予想だけでなく、かなり便利なデータを作れた!!

この関数を使って、さきほどの要領で出した候補文字に対して、
一定の画数以下である、というフィルターをかける。

では、その「一定の画数」とはいくつなのか?
過去元号の画数を調べる。
(※画数取得関数は上の例ですぐ作れるのでコードは省略)

過去元号に使われている漢字の画数をグラフ化
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt

gengoulist = ["大化","白雉",,,"省略",,,"昭和","平成"]

kakusuulist=[]
for idx in range(0, len(gengoulist)):
    kakusuu = get_Gengou_kakusuu(gengoulist[idx][0])
    kakusuulist.append(kakusuu)
    kakusuu = get_Gengou_kakusuu(gengoulist[idx][1])
    kakusuulist.append(kakusuu)

# 折れ線グラフを出力
left = np.array(range(0, len(kakusuulist)))
height = np.array(kakusuulist)

plt.plot(left, height)

from statistics import mean, median,variance,stdev

m = mean(kakusuulist)
median = median(kakusuulist)
variance = variance(kakusuulist)
stdev = stdev(kakusuulist)
print('平均: {0:.2f}'.format(m))
print('中央値: {0:.2f}'.format(median))
print('分散: {0:.2f}'.format(variance))
print('標準偏差: {0:.2f}'.format(stdev))
結果
平均: 7.83
中央値: 8.00
分散: 12.90
標準偏差: 3.59

kakusuu.png

ということで、
平均はおよそ8画と判明した。
右側(近代側)にいくにつれて、画数が減っているかなー、
ということを少し期待してグラフ化してみたが、
あまり変わっていないように見える。
画数などを本格的に気にしていたのは
恐らく本当に最近だけなのでしょうね。

この結果を元に数字を決めて、画数でのフィルタリング処理を実装した。
(結果はあえて省略)

なお、ついでに、「音読み」での読み方を元に、
MTSH除外(明治大正昭和平成と同じアルファベットにならないように)
という簡易的なチェックも実装できた。

フィルタリング②他で使われている言葉を除外

漢籍や古典とのマッチング結果後で良いのだが、
他で使われている言葉は使えないので除外する必要がある。

実際に試行錯誤を進めていくなかで、
「延安」という組み合わせが上位候補として出現した。
しかし、「延安」は中国の都市名として存在している。
「延安市」という項目がwikipediaに存在する。

前回は、mecabの辞書による除外のみを実装していたが、
mecabの辞書はそこまで万能には使えない。
(いろいろ使いにくい点がある)

そこで、wikipediaの全タイトル項目を取得し、
その最初の2文字&最後の2文字、については、
既に別な何かが存在するor誤解が生じやすいのでNG、
というフィルタリングを考えた。

上記で言えば「延安」はコレでフィルタ出来るし、
有名人の苗字や、有名人の名前、の大部分もフィルタ出来る。
例:田村正和⇒「田村」と「正和」が除外リストに登録される。

wikiのタイトルリストから、除外リストを生成する
import codecs
#wiki_title/jawiki-latest-all-titles-in-ns0
def makeWikiTitleList(filepath):
    WIKI_TITLE_LIST_MAE = []
    WIKI_TITLE_LIST_ATO = []
    infile = codecs.open(filepath,"r", "utf-8")
    for line in infile:
        if len(line) > 1:
            #m前の二文字
            WIKI_TITLE_LIST_MAE.append(line[0:2])
            #m最後の二文字
            WIKI_TITLE_LIST_ATO.append(line[-2:])

    infile.close()
    #重複削除
    WIKI_TITLE_LIST_MAE = list(set(WIKI_TITLE_LIST_MAE))
    WIKI_TITLE_LIST_ATO = list(set(WIKI_TITLE_LIST_ATO))

    return WIKI_TITLE_LIST_MAE, WIKI_TITLE_LIST_ATO

「2文字」ちょうどのタイトルだけ除外するわけではなく、
前後2文字除外、というルールであるため、
実はこれは結構強力なフィルターで、人名地名をはじめ、
誤解が生じやすい系、他の意味に捉えられそう系、をかなりはじく。

漢籍や古典とのマッチング

他にも、過去天皇名などのフィルタや、
評価準備を進めたが、既に超長いので、
細かいものは省略して、いよいよ漢籍や古典とのマッチングに入る。

「与えられた一定距離内に、候補とする漢字がある状態」
を、入力文章内から全探索するコードである。

ちょっと汚いコードになってしまった。
出典がどこか分かるように、近隣部分を合わせて表示したり、
お手本(元号の漢字)と該当の候補漢字との類似度を表示したり、
などのインフォメーション系を追加しているのでごちゃごちゃしている。
(実際はさらにもうちょっと情報を追加)

一定距離内に居る候補文字を、入力テキスト中から検索する関数
#テキストリストを入れると、近接して候補文字を使用しているところと、その場所を返す関数
def get_Gengou_Kouho_kinsetu(OTEHON_STR, GENGOU_KOUHO_KANJI_STR, target_str, max_kyori):
    kouho_resultlist =[]
    kaisi_no = 0
    target_str_len = len(target_str)
    while kaisi_no < target_str_len :
        kaisi_char = target_str[kaisi_no]
        if kaisi_char in GENGOU_KOUHO_KANJI_STR:
            kaisi_to_end = 1
            #全体が入っている&上限文字数を超えない、という両条件
            while kaisi_no + kaisi_to_end < target_str_len and kaisi_to_end <= max_kyori:
                end_no = kaisi_no + kaisi_to_end
                end_char = target_str[end_no]
                if end_char in GENGOU_KOUHO_KANJI_STR:
                    #両方とも、候補となる文字列に入っている状態
                    distance = JPmodel.similarity(kaisi_char, end_char)
                    inyou_kaisi_no = kaisi_no-5
                    if inyou_kaisi_no < 0:
                        inyou_kaisi_no = 0
                    inyou_end_no = end_no+1+5
                    if inyou_end_no > target_str_len-1:
                        inyou_end_no = target_str_len-1

                    kouho_resultlist.append([kaisi_char+end_char,
                                            kaisi_to_end,
                                            target_str[inyou_kaisi_no : inyou_end_no],
                                            get_otehon_ave_ruijido(kaisi_char, OTEHON_STR),
                                            get_otehon_ave_ruijido(end_char, OTEHON_STR),
                                            distance
                                            ])
                #検索対象をずらす
                kaisi_to_end  += 1
        else:
            pass
        kaisi_no +=1
    return kouho_resultlist

この関数を用いて、フジテレビ殿に用意していただいた
各種漢籍や、古典に対して、検索をしていくと、
AIの見つけた良い漢字の2文字の組み合わせ」で、
漢籍、古典内に出典と言える場所がありそうなペア
が見つかるというわけだ。

実際に、候補をいくつか見つけることができた。

ここで、ちょっと面白い点は、日本の古典を対象にした場合、
もともと、「XX天皇」という表記が含まれることが多すぎて、
過去の天皇名や、その一部の文字を使った結果ばかりが
出てきてしまうという傾向があった。
そうした名称の一部を使うことは、「出典」ではないため、
日本の古典を扱う場合には、「XX天皇」及び「XX」の部分は、
予め全て消去したテキストデータを用いたことを報告しておく。

最後に、そうして見つけた候補に対して
元号としてどの程度相応しいのか数値評価」を行う。

ベースは、各文字がどこまで「お手本」に近いか、と
前回同様の、漢字2文字間の距離、の判定だ。
さきほどのwikiタイトルフィルタなどを入れておき、
引っ掛かったものは0点=除外するというチェックや、
MTSH除外などのチェックも実施する。
(前回は、評価以前に絞り込みでかなり候補が減ってしまったから、
 消去法的なところもあったため、今回は、
 候補を多めにとって、フィルター条件はきつくしながらも、
 最後は相応しい度合の数値、の上位を出したい)

この後の結果も書きたいところではあるが、
まだ最終段階は確認中であることと、
そもそも、結果まで書いてしまうと
官邸側に回避されてしまうこともあり得て、
ガチ当てにならないので、
一旦、残念ながらここまでの記載とする。

続く
~ to be continued ~

あとがき、所感

★この記事への反応(いいね)が多ければ、後日必ず続きを書きます。
※元号当ては、前回も今回も長かったように、書くのが大変なんですよね。
 もし、続きが知りたい人が多ければ、頑張って書きます。
 少なければ、あとはテレビ見てください、だけでいいかな。
 気になった方は忘れないうちに「いいね」しておいてくださいませ。

■所感:
今回は単純なchar2vecによるAI学習だけでなく、
「計算だけ」での的中確率を高められるように
既存用語との重複チェックや、画数判定、
そして特に出典箇所の検索など、
実際の元号判定で行われていそうな内容
できるだけ全てプログラムで完結するように、実装していった。
かなり泥臭い部分を作りこんだ、とも言える。

ここまで作ってくると、もはやAIによる予想というより、
本当に元号を選んでいる側の人たちに、
これらのプログラムを差し上げて、
「候補の発見」や「チェック」を楽にするような仕組みとして
使っていただくほうが良いような気もしてきた。
選定作業の効率がアップすることは間違いない気がする。
今からでもホンモノの人から申し入れがあればいつでも差し上げる。

あとは、発音的なチェックが、基準が難しく出来ていないというくらいだ。
(言いやすい=どういう基準で?、とか、
 音だけで別な意味に聞こえる=どこまでマイナー単語を含めるべき?、とか)

今回、出典に検索をかけたとしても、
自分自身で気に入った文字や、予想や、組み合わせの好みを、
直接は使えないことは、かなり難しく感じた。
あくまで「数値」や「プログラム上の調整」だけで、
全てを決めていくことはかなり難しい。
(パラメータのチューニングは行っているわけで、完全に私見がゼロかと言われると、
 多少は入ってしまうのかもしれないが、極力自身の感性や勝手な予想は除外、の意味)

世の中に多く出回っている「元号予想」は、
その人自身の考えた理想、日本への期待、祈り、カン、が込められたものだ。
そのため、横から見ると実際は結論ありきであり、
予想プロセスは不透明」だが「予想者自身にとっては正しい予想」となる。
一方、今回のAIでの予想は、
予想のプロセスは透明」だが「予想者自身にとっては正しくない予想」で真逆だ。

とはいえ、一度演算してみると、
前回の結果「孝天」といい、
今回の結果「(ヒミツ)」といい、
しばらく眺めていると愛着が湧いてくるのが不思議である。
これが良い漢字の持つエネルギーなのだろうか?

恐らく、4月1日の発表に対しても、多くの人が、
最初は疑問や違和感を持ちつつも、
しばらく眺めているうちに愛着が湧いてくるのだろうな
と思し、そうなって欲しい。
みなが愛着を持つ良い時代になって欲しい。

以上。
次回へ続く(?) かどうかは反応次第

1203
380
24

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1203
380