【続々】平成の次の元号を、AIだけで決めさせる物語（＠直前スペシャル）

# 前書き

平成の次の元号をAIで決める物語＆そのテレビ取材編の続き。

前回までで、プログラムは完成していたのだが、
大きな「マッタ」がかかってしまった。
**出した予想が当たらないことが分かってしまった**のだ。

そこで、全結論が変わるほどの変更を行うことになった。
**さらなる「ガチ当て」**をするようにブラッシュアップした。
変更が生じたため再度、本番4/1のテレビ放送前に、その詳細、
VTRの尺に収まらなそうな部分を先行公開することにする。

**前回、多くの方にご声援を頂いた。**
**楽しみと言ってくださる方のためにも、続編をちゃんと書く。**
**応援ありがとうございます！！**

初回の記事と、前回の記事については、以下をご参照。

初回の記事⇒
[平成の次の元号を、AIだけで決めさせる物語](https://qiita.com/youwht/items/0b204c3575c94fc786b8)

前回の記事⇒
[【続】平成の次の元号を、AIだけで決めさせる物語（＠テレビ取材）](https://qiita.com/youwht/items/7b1118ecd91bd2e232bb)

前回までの話を３行で言うと、
AIで元号予想したらテレビ取材を受けることになったよ、
出典も含めてガチで当てがんばるよ、発表直前に放送予定。
VTRに入らない深い内容はQiitaで公開するよ、だ。


# **予想が当たらない事件の経緯**

## 当初予想

前回の記事を書いた時点で、
以下のような全ルールを満たす元号を
「プログラム」によって算出していた。

* 教育漢字２文字（または１つが常用漢字）
* **AIにとって、過去元号の文字に近い＝「いい意味の漢字」である**
* **AIにとって、組み合わせが過去元号の「文字の距離感」に近い**
* 中国または日本の古典に**"出典"**がある（少なくとも、近距離で出現）
* Mecabの辞書に存在していない
* 過去の元号、過去の天皇名と重複しない
* MTSH以外（明治大正昭和平成とイニシャルが重複しない）
* 漢字の画数が一定値以下（過去元号の平均画数等も参考）
* WIKIのタイトルの冒頭２文字と不一致 ≒ 苗字/地名などで俗用されていない
* **【制約事項】作者の漢字に対する好みを入れてはいけない。数値計算のみで算出**

なお、上記ルールにはそれぞれ、一発アウトな除外必須ルールと、
ポイントの加算減算が行われるルールがあり、
最終的には、全ての条件を満たしてポイントの高いものが選定される。

~~ここまででも結構大変なんです。機械が漢字で新語を作るのだから。~~

そして、実は前回書かなかった結論として、
「**仁久**」
「**隆永**」（常用漢字も含む場合）
などを算出していた。

「出典」の文章の長さと候補漢字の個数にもよるが、
長い文章だと、4000個ほど、近距離にある候補漢字のペアを見つけて、
300個ほど（重複もある）、一発除外を潜り抜けたペアを返して、
評価値の高い5個を残すような感じ。

複数の出典に対して上記選定を実施し、
さらにその中で評価値の高い候補がコレ。
かなりの倍率を潜り抜けてきただけあって、
結構**それっぽい**のではないか？
フィルタリングの条件も十分に設定している。

なお、「仁久」は
「万葉集」「礼記」にそれぞれ出典らしき箇所が見つかったのだが、
「万葉集」はそもそも、漢字は「当て字」的に用いられているため、
万葉集を用いて良いかどうかは議論を要する。


あとは、どの古典から第何位までの元号を放送するか、とか、
教育漢字、常用漢字の扱いをどうするか、とか、
「見せ方」をプロデューサ様に決めてもらうだけの状態。
私も、プログラムの大部分をqiitaで公開し、
**放送を待つだけっ・・・！！**
と、思っていた・・・・・。

## 当たらない事件発生

プロデューサ様「大変ですっ！このままでは予想は当たりません！」


**「仁久」＝高島屋の従業員がトイレに行く際の隠語**、
　　　　　**仁久歯科、という歯医者が存在する（知らんがな）**
**「隆永」＝西郷隆盛の、改名する前の名前（めっちゃ知らんがな）**

プロデューサ様「**ガチ当て**なのでそうした語彙も機械的に除外出来ません？」

（*´Д｀）「辞書には無い言葉ですし、フツウに考えて難しくないですか・・・？」


> 「**出資者は無理難題をおっしゃる**」by クワトロ・バジーナ


（ ´Д｀）「AIだけでそれっぽいものを出すだけでも大変なのに・・・」
（ ´Д｀）「うちのAIちゃんは、高島屋の従業員じゃないし・・・」
（ ´Д｀）「うちのAIちゃんは、うんちしないし・・・」





## 決意

とはいえ、私も思うところはあった。
結論が出たあとで、Google先生に聞いて、
仁久＝トイレに行く際の隠語、らしいので微妙だなー、
とは思っていた。
思いながらも、最初に掲げた条件には引っ掛からなかったから
しょーがないのでは？と妥協していたのだ。

まだちょっと私の中で、「ガチ当て」への温度感よりも、
「AIがそれっぽいものを出したこと」への
満足度の方が高かったのかもしれない。

**しかし、ここで妥協してしまっては、当てることは出来ないっ！**

実際の「平成」の時も、"大先生"が元号候補を出すたびに、
担当官から、「すいません、それは俗用されており・・・」的な
やりとりが何度も生じて、相手が"大先生"だけに大変だった、
的な逸話をどこかで読んだ覚えがある。
私は"大先生"ではないが、プロデューサ様も、担当官も、大変だ。

もちろん、出した結果を人の目で精査した後で結論とする、
というのは簡単だが、あくまでもプログラムだけで出したい。

**「人事を尽くして天命を待つ」**
AI（ノートパソコン）も唸りを上げて頑張ってくれましたし、
私も、人事を尽くせるところまではやってみましょう！

> 人類の可能性に限界が無いならば、
> プログラムが出来ることにも限界はない
>　　　　　　　～ 　Char Fuitter (1847～1912 オランダ)　～


# **新・俗用フィルタの実装方針**

元号ほど**高尚なもの**を決めるとなると、
「辞書に載っている」「WIKIに存在」というレベルだけでは、
高島屋のトイレの隠語や西郷隆盛の旧名までは対応できず、
「俗用されているか」の判断として不十分であった。

~~**もはや何と戦っているのか分からない**。~~

では、我々はどのようにして「俗用」を判断するのだろうか？
我々の「感覚」的には（高島屋従業員様を除いて）
「仁久」「隆永」はアリ、である。

今回の判断理由は「**Google先生に聞いた結果**」だ。

では、GoogleのHIT件数を調べて、HITが少なかったら？
Google先生はいろいろ気を利かせてくれるので、
特に２文字レベルでは、単純なHIT件数はアテにならない。

一方、「仁久」を検索すると、
「コトバンク」が一位にヒットして、
高島屋系列のトイレの隠語であることを教えてくれる。

## 俗用フィルタの方針①「タイトルチェック」

そこで、
**検索結果上位２０件のページのタイトルを一覧化して、**
**そのタイトルそれぞれに対して、俗用有無の評価をする**
という方針を考えた。
つまり、「コトバンク」が出てきたらアウト！
他に、「ホットペッパー」とか、「会社」とかが出てきたら、
既に店名や企業名で存在して俗用度が高いことになる。
「Navi」が出てきた場合、だいたい地名として用いられている。
「一般人の人名」＝Facebookなど、は少し迷うところだが、
評価を下げるだけということにしておく。

＃人名らしきもの全て無しにしたら消えすぎかも。
　「平成」も「へなり」という地名はあったし
 　多少の重複は現代においては許容範囲か？ 

## 俗用フィルタの方針②「画像検索」

何度かGoogle検索をかけて気付いたことがある。
俗用されていないような言葉の場合、

**「XX」の画像検索結果、が比較的上位に出現する**。

Google先生から見れば、オマエの調べているものって、
「言葉」として存在しないから「画像」じゃね？
って言ってくるわけだ。

そこで「画像検索結果」が上位に来るものは、
俗用度が低い、ということにした。

画像検索の順位というのは新たな気付きだった。

## 俗用フィルタの方針③「ポイント制」

そもそも、「隆永」はアウトなのだろうか？
西郷隆盛の改名前の名前だから何だというのだ？
どういう基準で官邸がNGにするのかが明確に言えないため、
「何かで一発アウト」にするような形ではなく、
コトバンクは俗用度ポイントが高い、
「画像検索」が上位に来るとポイントが低い、
などといった、「ポイント制」を採用することにする。


# **Google俗用フィルタの実装**

## 検索結果の取得

まず、Google検索結果をリスト化する必要がある。
先人の偉大な知恵を拝借して、
Daiさんのget_search_results_df関数をお借りする。
ご参考： https://note.mu/daikawai/n/n7cb363531396



```python:Google検索結果取得
from bs4 import BeautifulSoup
import requests
import pandas as pd

def get_search_results_df(keyword):
    columns = ['rank','title','url', 'affiliate_url']
    df = pd.DataFrame(columns=columns)
    html_doc = requests.get('https://www.google.co.jp/search?num=20&q=' +keyword).text
    soup = BeautifulSoup(html_doc, 'html.parser') # BeautifulSoupの初期化
    tags = soup.find_all('h3',{'class':'r'})
    rank = 1
    for tag in tags:
        title = tag.text
        url = query_string_remove(tag.select("a")[0].get("href").replace("/url?q=",""))
        affiliate_url = ""
        se = pd.Series([rank, title, url, affiliate_url], columns)
        df = df.append(se, ignore_index=True)
        rank += 1
    return df
 
def query_string_remove(url):
    return url[:url.find('&')]

```

早速使ってみよう。


```python:検索実行
search_results_df = get_search_results_df("仁久")
search_results_df.head(20)

```


| 結果（一部） |
|:-----------|
| 遠方/仁久(エンポウ／ジンキュウ)とは - コトバンク |  
| 仁久 という名前の読み 29件(ひとひさ,まさつね,じんく,とよひさ,男の子 ...|
| あるデパートの従業員トイレを使ってるのですが、トイレでなく「仁久（じ...|
| トイレの隠語   さるのちえ - 楽天ブログ	|
| 仁久  人名漢字辞典 - 読み方検索|
| 「仁久」の書き方・読み方 男の子の名前 - 漢字書き順辞典|
| 「仁久」の男の子の読み方 - パパママいい名前つけてね|
| 「仁久」の時刻表/バス乗換案内/路線図/地図 - NAVITIME|
|  JR松山駅から仁久までの乗換案内 - NAVITIME|
| 川崎仁久 - Wikipedia	 |
| ・・・・・	 |

**はっきり言って、「仁久」はボロボロだった**。
「コトバンク」だけでなく、
「地名」の可能性すら含んでいて、
Wikiの有名人にも含まれていた。

※　実は、前回実装した「WIKIフィルタ」には一部誤りがあり、
WIKIタイトルの「最後の二文字」のフィルタが上手く動作しておらず、
 前回時点では「冒頭二文字」分のフィルタしか動作していなかった。
 そのため、実際は「仁久」はその誤りを訂正すれば除外できたが、
 例として、そのまま採用。
 そもそも、冒頭二文字は、地名や苗字などという目的があったが、
 最後の二文字は、せいぜい名前なので、そこまで除外するのか？
 という迷いも生じていた。今回から正式に採用を決定。
 
 一方で、「俗用されていない」と見える候補では、
 だいたいTOP１０以内で、
 **「XX」の画像検索結果** 
が出現する傾向にあった。
 
## タイトルの評価関数の実装

このようにして取得した、検索結果の
各ページのタイトルを使って、
「俗用度合」を数値化してみる。
この関数の作り方（パラメータ）は諸説あり、
Facebookの人名や、店名などを、どこまで厳しく除外するべきか、は
大変難しいところだ。
複雑な分岐を入れて一週間くらい試行錯誤すれば"完璧な"フィルター
（つまり、人が検索結果を見て判断するのと同レベルのフィルター）
が出来るかもしれない。
しかし、そもそもどういう基準で俗用は却下されるのか、
イマイチ判断がつかないところもあり、
「除外しすぎ」だと何も残らないような状態になってしまう。
あくまでも簡易的な数値化である。

~~もしかしたら実際に選定している人は、~~
~~全戸籍データや、国内外の全会社名/商標データなどを~~
~~準備して臨んでいるのかもしれない。凄いなぁ。~~




```python:タイトルの評価関数
def web_zokuyou_checker(gengou_str, title_list):
    counter = 1
    keyword = gengou_str + " の画像検索結果"
    keyword_eng = "Images for " + gengou_str
    add_val = 1
    for val in title_list:
        if keyword in val or keyword_eng in val:
            #以降の増加が大きく減少。画像検索が上位なほど有利
            add_val = 0.2
        #自分自身が含まれていて、「元号」の話題ではない場合
        #ランクが下がっていく
        if gengou_str in val:
            #元号の話題で触れられている場合は若干の加点
            if "元号" in val :
                counter -= 0.3
                continue
            if "コトバンク" in val :
                counter += 15
                continue
            if "はてなキーワード" in val :
                counter += 15
                continue
            #名前はまだありだが、苗字系はNG
            if "人名漢字辞典" in val or "日本姓氏" in val or "名字" in val or "苗字" in val:
                counter += 3.5
                continue
            if "Facebook" in val :
                counter += 1.5
                continue
            if "NAVITIME" in val or "会社" in val or "地図" in val or "いつもNAVI" in val :
                counter += 2
                continue
            if "老人ホーム" in val or "福祉" in val or "介護" in val :
                counter += 2                
                continue
            if "映画" in val :
                counter += 2                
                continue
            if "Retty" in val or "食べログ" in val or "ぐるなび" in val or "ホットペッパー" in val :
                counter += 2                
                continue
        else:
            #自分が入っていないといっても紛らわしいのでペナルティ
            counter += 0.3
            continue
        counter += add_val
    return counter
```

このような関数を用意することで、
（パラメータの数値や、IF文の条件は適宜変更）
例えば何かの名前に使われていそうな候補に、
「俗用度ポイント」を高く設定することができる。

人間が、Google検索結果を見て、
コレは俗用されてるんじゃね？となぜ思ったのか、
その理由を一つずつ組み込むだけだ。
ただし、組み込みすぎると、「本来はいいもの」も
落とすことになるため、注意を要する。

実際の選定者も、結局は似たことを
（人手で）やっているんだろうなー、
と、思いをはせながらプログラムを書いていく。

## Google俗用フィルタの大問題＝リクエスト制限

あとは、出てきた結果とこの関数を組み合わせて
個々に評価していけば良いのだが、
大きな問題があった。

「Google検索」を連続して実施する場合、
１００回／日くらいの検索で、
**一次的に制限がかけられてしまう模様**。

Google俗用フィルタに渡す前の時点で、
十分に絞り込んだ精鋭を渡す必要がある。

＃この制限が、前述の、フィルタ条件の調整、を
　心行くまで調整/実装できなかった最大の理由である。
 
また、検索する度に、微妙に結果が変わる場合があるが、
それは今回は大きな問題ではない。

> **配られたカードで勝負するっきゃないのさ…**　by スヌーピー

制限の範囲で出来ることをやりきるしかない。
ここまでのアイデアで
古典の検索結果（元号候補）を再評価してみよう！


# **再評価の結果**

## **そして誰もいなくなった**

再評価の結果をヒトコトで言うと、
**候補が足りなくなったｗ**

全部が消えたわけではないものの、
フィルタが強力であったため、
当初出そうとしていた候補の個数を割り込んでしまった。

AIが候補としていた漢字の個数を増やす、
なども考慮したが、もともと元号には７２種類ほどの漢字しか
過去に使われたことが無いため、
（教育漢字に限定するとさらにさらに少ない）
AIが見た「良い意味」であっても、
その近辺の数値より候補の個数を増やすと、
「ゴミ」が増えてくるような印象がある。

今回、フジテレビ殿に用意していただいた「出典」は
過去に出典として使われたことのある漢籍の一部でしかないため、
出典をもっと増やす、のが候補を増やす本筋ではあるが、
既に時間が足りていなかった。

## 対策＆さらなるガチ当て化 ＝ 落選元号

そこで、**「既に出典がある」候補を増やすアイデア**を思い付いた。

**出典＝「落選元号」リスト**を使うという案だ。

実は、
「明治」は室町時代～江戸時代で計10回、
「大正」は鎌倉時代～江戸前期で計4回、
「平成」は幕末に1回、
候補にあがりながらも落選した末に、
やっと元号に選ばれているそうだ。

今回の選定においても、
**過去の「落選元号」が再登場する可能性は十分に高い**。

また、これらの「落選元号」は、
出典が既に用意されているもの、と考えられる。
（こちらから見れば、出典の検索対象として、
 　「落選元号」を追加すれば、過去の人の
  　検索結果を横取りできる、ようなもの）

これらの「落選元号」に対して、
用意したフィルターがどこまで効くのか？
も大変興味がある。

# **「落選元号」へのフィルタ＆評価結果**

もちろん、ただのフィルタでは、
AIが出した感が薄れてしまうため、
過去の落選元号についても、
AIにとって「良い意味」「バランス」であるか
評価を実施し、その最高得点をマークしたものだけ、
最終候補として採用することにする。

落選元号を相手にしたとしても、
あくまで基準は、AIから見た得点であり、
元のやり方で出した候補よりも、より高いものだけを選定し、
落選候補だからといって他候補より優遇はしない。

結果は、大変興味深いものになった。

**「落選元号」５３２個のうち、**
**元々のフィルター条件だけで、**
**なんと３２個まで候補が減ってしまった。**

「常用」「画数」「MTSH」「WIKI」が
それぞれ入り混じっており、
どれかの条件だけで多数落とされているわけではなかった。

さらに、今回実装した**Google俗用フィルタ**によって
（俗用度の点数が一定値以上は除外するという考え方で）
**１７個まで候補が絞られた**

つまり、それっぽい候補が出現したとしても、
今回設定した各種条件だけで、
**５２３個 ⇒ １７個  の割合でフィルタされるということ**。
かなりの厳選率である。

この１７個のうちで、AIの評価値が高い、
**TOP４つが、新たな候補として加わることになった**。

この４つの結果は当日の発表を見てのお楽しみ。
（ガチ当てなので、最終候補は公開できない）

もちろん、元々古典からの出典としていた候補についても、
同様のフィルタはかかっており、
こちらもより**厳選された候補**になっている。

~~ピッタリ当てるのは難易度が高すぎる上に、~~
~~放送時間的に見ると、予想発表後すぐに、~~
~~「残念違いました～」と分かる極悪な仕組みなので、~~
~~過度な期待はしないようにお願いします。~~

## おまけ、こぼれ話

「最終候補」の１７個に残りつつ
AIから見た評価が最も低かった「落選元号」は、以下である。

「応宝」
「用保」
「文功」
「文始」
「協中」 

これらは、「フィルタ」には引っ掛からず、
「俗用」されていないと判断されたものの、
AIから見たら、ちょっと他より元号っぽくないぞ、
とみなされた、ということ。

他に、AIにとっての「距離感」が合わな過ぎ、として
５２３個中３つだけ先に除外されていたものもあった。
（基本はマイナス点だが、ズレが大きすぎる場合一発除外）

「和平」
「永貞」
「能成」

こんなものが以前元号の候補に入っていたんだー、という話と、
これらに対し（他に比べて）低評価を下したAIの判断は、
みなさんにとっていかがであろうか？


# あとがき（いよいよ結果を待つだけ）

実際に元号が発表されれば、
（そして外す可能性の方が高いのだから）
この物語はとんだ笑い話だ。
~~発表される前であっても、最初から笑い話だ~~

だいたい、[書いた人](https://qiita.com/youwht)の他の投稿を見ても、マトモな投稿が無い。

 *  [k8s＝Kubernetesについて本気で調べてみた](https://qiita.com/youwht/items/0f8b6c71a799bc8f1d18)
 *  [どこでもドアを作ってみた物語](https://qiita.com/youwht/items/8ca57766a991371c1cfe)
 *  [しりとり徹底分析！最強キャラ（文字）解説](https://qiita.com/youwht/items/7a4a280e4c49107260ed)
 *  [「赤の他人」の対義語は「白い恋人」 これを自動生成したい物語](https://qiita.com/youwht/items/f21325ff62603e8664e6)


しかし、「元号」にAIとプログラムで挑んだ「遊び」は、
一つの挑戦の物語として、
誰かの心に残り続けてくれるかもしれない。

> 人類の進化は「遊び」からはじまる。
> こんな「遊び」が出来るならば、というアイデアに触発される人がでて、
> 生活にも役に立つような「発明」が生まれるのだ。
> 　　　　　　　　　　～ 　Char Fuitter (1847～1912 オランダ)　～

改元対応で頭を悩ませているエンジニアは多数いらっしゃるだろう。
私も、改元について最も想いを巡らせている人のひとりだと思う。
~~たぶん、そのベクトルはだいぶ違う方向に向いている気がする。~~

次回（があれば）、
「新元号」は、今回のAIの評価でどれくらいの評価値なのか？
Google俗用フィルタの評価値は？
などを確認しながら、予想と現実との乖離を確認したい。
もし、それが記録として残り、未来の次回の改元時に、
**決める側の方に本プログラムの思想が採用**されたら大変面白い。

---

この物語はフィクションです。
登場する人物・団体・名称等は架空であり、
実在のものとは関係ありません。
Char Fuitter （チャー・フイター）は架空の人物です。
※途中で良いことを言っていても騙されないように。
※Google俗用フィルタは、「正解」発表後は、
　「正解」が元はいくつだったのか、確認出来ないかも。