ディープ・ラーニングで新元号予想してみた: GengoAN — 元号のGAN — 

![UNADJUSTEDNONRAW_thumb_2a5a2.jpg](https://qiita-image-store.s3.amazonaws.com/0/3190/57e2fc05-a813-ec58-df13-8bdbe9088942.jpeg "UNADJUSTEDNONRAW_thumb_2a5a2.jpg")


ついに今日の午前11時半に新元号が発表されますね。

とりあえず PyTorch によるディープ・ラーニング (深層学習) で元号の予想を作ってみました。今までされていない予想方法がないかなと思って、 **視覚的・見た目** を予想することにしました。既にされている人がいたらすみません。

## 方法

機械学習の、深層学習、ディープ・ラーニングの方法の一つ、敵対的生成ネットワーク (GAN) の DCGAN を用いて、元号を **見た目** 視点で予想することにします。 **字面** (**じづら**) というやつです。

テキスト情報・文字情報ではなく、元号の画像を用意して、GANで、それっぽいものをたくさん生成してみるというものです。

コードは aidiary ([@sylvan5](https://twitter.com/sylvan5)) さんの記事 [PyTorch (12) Generative Adversarial Networks (MNIST) - 人工知能に関する断創録](http://aidiary.hatenablog.com/entry/20180304/1520172429) をほとんど写しました。ありがとうございました。主に変更した部分を下で解説しました。


## 入力「元号」画像生成
![sources.png](https://qiita-image-store.s3.amazonaws.com/0/3190/38a0b361-1e65-a28d-b4a6-18ed26619e3a.png "sources.png")


TeX を用いました。

1. rubyでTeXファイルを生成して
2. TeXからPDF (dvipdfmx)
3. PDFからPNG (ImageMagick)

の手順で作りました。rubyからTeXファイルを作るコードは

https://github.com/Nyoho/GengoAN

にあります。

- 元号は少ないので、画像を回転をランダムに100回かけて増やしました。
	- TeXで `\rotatebox{度数法の角度}{文字列}` とすれば回転できます。
- 4文字の元号は省きました。たぶん次も2文字になるでしょうから。(244個)
- 24644 枚のPNGファイルを作成しました。


## PyTorch によるディープ・ラーニングのコード

コードは aidiary (@sylvan5) さんの記事 [PyTorch (12) Generative Adversarial Networks (MNIST) - 人工知能に関する断創録](http://aidiary.hatenablog.com/entry/20180304/1520172429) をほとんど写したのですが、これは MNIST という28×28の画像の集合のためのものなので、サイズを合わせる必要があります。

線形なネットワークの後に、"たたみ込み" 層が2段になっていて、ここで [`ConvTranspose2d` (PyTorch のドキュメントへのリンク)](https://pytorch.org/docs/stable/nn.html#torch.nn.ConvTranspose2d) により、どかんどかんとサイズが大きくなっていきます。`ConvTranspose2d` クラスは中では `F.conv_transpose2d` 関数が使われています。

transposed convolution と言ったり、deconvolution と言ったりします。いわゆる「たたみ込み」の逆のことをするのです。たたみ込みは小さくなりますが、transposed convolution はその反対なので、小さいサイズから大きなサイズが出てきますね。使われるカーネルやストライドやパディングは、全く同じ概念です。それを逆に (行列の言葉で言えば「転置」にして) 使うだけです。


- カーネルのサイズ ($k$とおきます)
- ストライド(ずらしていく幅) ($s$とおきます)
- パディング ($p$とおきます)

によって、この層を通った後の各ユニットのサイズが決まります。
今は kernel サイズ$k$は4、ストライド$s$は2、パディング$p$は1にしているので、最初の入力サイズを 8×8 (縦横8ピクセルずつ) にします。横のサイズを $w$, 縦のサイズを $w$ とおくことにします。

そうすると、まず

```math
\begin{align}
  & (w - 1)\times s - 2 \times p + k \\
= & (8-1)\times 2 - 2 \times 1 + 4 \\
= & 16
\end{align}
```

となり1辺が16ピクセルの出力が出てきて、次に

$$ (16-1)\times 2 - 2 \times 1 + 4 = 32$$

となるので、最後に 32×32の画像ができあがるというわけです。


```python
class Generator(nn.Module):
    
    def __init__(self):
        super(Generator, self).__init__()
        
        self.fc = nn.Sequential(
            nn.Linear(62, 1024),
            nn.BatchNorm1d(1024),
            nn.ReLU(),
            nn.Linear(1024, 128 * 8 * 8),
            nn.BatchNorm1d(128 * 8 * 8),
            nn.ReLU(),
        )
        
        self.deconv = nn.Sequential(
            nn.ConvTranspose2d(128, 64, kernel_size=4, stride=2, padding=1),
            nn.BatchNorm2d(64),
            nn.ReLU(),
            nn.ConvTranspose2d(64, 1, kernel_size=4, stride=2, padding=1),
            nn.Sigmoid(),
        )
        
        initialize_weights(self)

    def forward(self, input):
        x = self.fc(input)
        x = x.view(-1, 128, 8, 8)
        x = self.deconv(x)
        return x
```

この変更を discriminator にも行います。

また、64×64版も同様に、サイズに合わせたネットワークの変更を行います。

## 結果: 32×32

はじめは、縦横32ピクセルずつの画像でやりました。

コードは[Google Colaboratory のノート](https://colab.research.google.com/drive/1_LVpyXZkYbdQjQURhvwoWOEYcWQ_iKjV)をご覧ください。

![32-epoch.png](https://qiita-image-store.s3.amazonaws.com/0/3190/5009b2eb-7086-b7e8-7ce3-af3a3d66a68c.png "32-epoch.png")
![32-epoch2.png](https://qiita-image-store.s3.amazonaws.com/0/3190/69927022-3758-077d-6f84-4875ceecd3c4.png "32-epoch2.png")


epoch 51 ぐらいまでくると結構見えてきますね! でもちょっと見えにくいので解像度を上げてみました。

## 結果: 64×64

見えにくいのでもうちょっと時間をかけて、縦横64ピクセルの画像を作りました。

これも[Google Colaboratory のノート](https://colab.research.google.com/drive/1q5E1utWTntwffGbgTRvTBuv0u8YTrJj6) を置いております。

### epoch 1
![epoch_001.png](https://qiita-image-store.s3.amazonaws.com/0/3190/8e67ab62-788d-f17e-21b8-c9e5bf46a46d.png "epoch_001.png")


### epoch 16

![epoch_016.png](https://qiita-image-store.s3.amazonaws.com/0/3190/a84dfe2b-e2bd-129d-fde8-a80e4a7254d0.png "epoch_016.png")

### epoch 26

![epoch_026.png](https://qiita-image-store.s3.amazonaws.com/0/3190/baae8f3d-cb2c-3f85-26eb-7c581b0ddd81.png "epoch_026.png")

### epoch 41
![epoch_041.png](https://qiita-image-store.s3.amazonaws.com/0/3190/64883569-e77c-ce8e-e533-abe008113eef.png "epoch_041.png")


### epoch 51

![epoch_051.png](https://qiita-image-store.s3.amazonaws.com/0/3190/51263bd1-8212-4f1f-e161-65cbd1f681c3.png "epoch_051.png")


## まとめ

ここから皆様の心の目で、新元号の字面を見いだして下さい。

妻と心の目で見たところ、

底、風、民、顧、天、筆、鳳、亀、実

のような感じの漢字が見えました。

- 天筆 (てんぴつ)
- 鳳民 (ほうみん)
- 風鳳 (ふうほう)
- 天亀 (てんがめ) (店があるww)
- 実底 (じってい)

とかどうですか?!

では。Happy deep learning.

## 追記

当然ながら、外れましたね。
「和」はこれまで19回も出てきたので見えないこともありませんが、「令」は初登場ですので、この生成画像からは見えてきません。(むしろこのネットワークではそれが正常)