英文を与えると画像を描いてくれるOpenAI "DALL-E"の再現モデルは、固有名詞をどれだけ学習済みなのか、検証してみた（人名編）

__DALL-E__の実装コードについて簡単に触れさせてください。
__DALL-E__は、Open AIによって開発・発表されましたが、実装コードは一部しか公開されていません（2021年9月5日現在）。

DALL-Eのうち、すでに公開されている（DALL-Eの）画像圧縮器（Discrerte VAE）と、（DALL-Eを開発したOpenAIから別に公開されている）画像とテキスト文の類似度を定量評価してくれる__CLIP__を連結させた__DALL-Eの再現モデル__のコード実装が、サードパーティのプログラマである[@advadnoun氏](https://twitter.com/advadnoun)によって公開されています。

そして、この実装コードにさらに改良の手を加えたコードが、__*cedro3*__さんによって公開されています。

http://cedro3.com/ai/dall-e/

今回は、この__*cedro3*__さんが公開してくれている「DALL-EのDiscrerte VAE」と「CLIP」の連携モデルを動かしてみます。このモデルは、英文指示文を受け取って、その文に描かれた内容を描写した画像を自動生成するとき（＝推論フェーズ）、次のループ処理を指定されたイテレーション（iteration）回数、繰り返します。

####（ 以下のループをひたすら回す ）

・Discrete VAEにランダムパラメータを与えて、 画像を生成
・テキストと、DALL-E Image Decoderから出力された画像をそれぞれ、clipで特徴ベクトル化
・両ベクトル間の類似度をclipで算出（clipで画像とテキストの類似度評価）
・類似度をさらに高めるために、 ランダムパラメータを更新
・新たなランダムパラメータをDiscrete VAEに入力し、 前回よりもテキストに近い画像を生成

##（ 前提 ）

本記事では、次の記事でメソッド化した「DALL-EのDiscrerte VAE」と「CLIP」の連携モデルを動かしました。

https://qiita.com/electronics_diy721/items/ca38096c74cfe1c53e30#%E4%BB%A5%E4%B8%8B%E3%82%92%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E5%8C%96

> 以下をメソッド化
ここから、オリジナルのColaboratory notebookではベタ打ちで実行していたコードを、繰り返し実行しやすいようにメソッドのブロックに包みます。
>
> - メソッド名： __*draw_image_based_upon_text*__
> - 引数： text （取り扱う英文テキスト。str型オブジェクト）

##（ 関連記事 ）

以下の記事では、人名ではなく、シャネルやiPhoneやジャガー（車）といったブランド名の認識力を検証しました。

https://qiita.com/electronics_diy721/items/cb7e5ef2346ab5a9dc69

___

#（ 実験結果 ）

今回、次の著名人の顔を試しました。
その結果、「DALL-EのDiscrerte VAE」と「CLIP」の連携モデルは、これらの人物の顔を「学習済み」であると考えられることがわかりました。

- アルバート・アインシュタイン博士
- スティーブ・ジョブズ
- ビル・ゲイツ
- オバマ元大統領

###アルバート・アインシュタイン博士

```Python3:
input_text = "a photo of albert einstein"
draw_image_based_upon_text(input_text, 4000)
```

- アインシュタイン博士らしい顔が出てきました

![](https://user-images.githubusercontent.com/87643752/132120753-c3d07e17-364d-45c6-a299-fa3c4b441c88.png)

今度は、イテレーション数を7,000に増やしてみます。

```Python3:
input_text = "a photo of albert einstein"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/132123051-798c9882-1a4e-4838-9f87-d2e49c7530ac.png)

指示文から__*a photo of*__を取り除いてみます。


```Python3:
input_text = "albert einstein"
draw_image_based_upon_text(input_text, 7000)
```

もう１回、実行してみます。

![](https://user-images.githubusercontent.com/87643752/132131878-6b5e6a89-78c3-43b8-8f09-daa1e79359cb.png)

###スティーブ・ジョブズ

```Python3:
input_text = "a photo of the face of steve jobs"
draw_image_based_upon_text(input_text, 7000)
```

- 25✖️50=1250iteration目までは良い感じ
- この時点で、「DALL-EのDiscrete VAE + CLIP」モデルは、スティーブ・ジョブズさんの顔を学習済みだと判断できます

![](https://user-images.githubusercontent.com/87643752/132174780-8da53aef-0b80-44a9-bcaf-c2c655ca3bba.png)

- 28✖️50=1400iteration目以降、顔の左下に別の人物の顔が写り込んでしまう

![](https://user-images.githubusercontent.com/87643752/132174961-145751a1-628a-42c2-8794-0e6d0cc4ccc0.png)

- 7,000iteration目

![](https://user-images.githubusercontent.com/87643752/132180524-ee1e80bd-f7b6-499e-963d-c20437e9b81f.png)

```Python3:
input_text = "a photo of steve jobs"
draw_image_based_upon_text(input_text, 7000)
```
- 27✖️50=1350iteration目以降、顔の左側に別の人物の顔が写り込んでしまう

![](https://user-images.githubusercontent.com/87643752/132186306-0916d5e9-c29e-4c1b-9d12-fc48b595aaea.png)

- 7,000iteration目

![](https://user-images.githubusercontent.com/87643752/132194570-110304e4-3de5-4939-a4a0-c8b29e67a6d3.png)

```Python3:
input_text = "steve jobs"
draw_image_based_upon_text(input_text, 7000)
```

- これはNGです。

![](https://user-images.githubusercontent.com/87643752/132168231-be30113e-7855-400c-b7a1-613c489a1700.png)


###ビル・ゲイツ

```Python3:
input_text = "a photo of Bill Gates"
draw_image_based_upon_text(input_text, 7000)
```

- 左右で２人の顔がダブっています
- 左側は、ビルゲイツさんの顔です。右側には髪だけが見えるもう１人の人物がいます。これは若かりし頃のビルゲイツさんの髪に見えなくもないです。
- この時点で、「DALL-EのDiscrete VAE + CLIP」モデルは、ビルゲイツさんの顔を学習済みだと判断できます

![](https://user-images.githubusercontent.com/87643752/132208146-1d8cd23f-5b02-40bb-bf7c-c1583eb84e34.png)

```Python3:
input_text = "a photo of the face of bill gates"
draw_image_based_upon_text(input_text, 7000)
```

- 26✖️50=1300iteration目までは良い感じ

![](https://user-images.githubusercontent.com/87643752/132209914-f298b8e3-2aa9-484f-9eab-4027aef04faf.png)

- 30✖️50=1500iteration目以降、複数の顔が混ざり始めてしまう

![](https://user-images.githubusercontent.com/87643752/132210516-529b1419-e426-46b9-a66f-79f385eb792e.png)

![](https://user-images.githubusercontent.com/87643752/132210402-db26ddbc-806a-4f11-bf9f-f38a59e856fa.png)

- 7000iteration目

![](https://user-images.githubusercontent.com/87643752/132219452-6ae0a570-9b58-42d4-858e-4a90b762296c.png)


###オバマ元大統領


```Python3:
input_text = "barack obama"
draw_image_based_upon_text(input_text, 4000)
```

- オバマ元大統領らしい顔が出てきた
- でも、裏側に別の人（トランプ元大統領？？）らしき顔も描かれている・・・（汗）

![](https://user-images.githubusercontent.com/87643752/132120750-16fb1975-34d8-413e-accf-00328d481ac6.png)

今度は、イテレーション数を7,000に増やしてみます。

```Python3:
input_text = "a photo of barack obama"
draw_image_based_upon_text(input_text, 7000)
```

- 今度も、裏側に別の人（トランプ元大統領？？）らしき顔も描かれている・・・（汗）

![](https://user-images.githubusercontent.com/87643752/132123040-0e1cea53-d9a0-4baf-9ff3-213f548aa03f.png)

もう１回、実行してみます。

- 今度は裏側にいる人も、オバマさんっぽい（？）です。
- しかし、裏側にもう１人の顔が映ってしまう事象は解消されませんでした。

![](https://user-images.githubusercontent.com/87643752/132131852-0384e888-2171-4d96-b265-2b4b6cedfb4f.png)

___

## 【 実験対象 】 「DALL-EのDiscrete VAE + CLIP」の連携モデル

冒頭で触れた__DALL-E__の実装コードについて、詳しく解説します。
__DALL-E__は、Open AIによって開発・発表されましたが、実装コードは一部しか公開されていません（2021年9月5日現在）。

公開されているのは、画像データの次元数を圧縮して、データサイズが小さい画像特徴ベクトルを作り出す部分だけです（この画像次元数を圧縮する部分は、__*Discrete VAE*（離散変分オートエンコーダ）__という手法が採用されています）。

####（ DALL-E実装コードの公開部分 ）

https://github.com/openai/DALL-E

> ###Overview
>
>This is the official PyTorch package for the discrete VAE used for DALL·E. The transformer used to generate the images from the text is not part of this code release.
>
> ###Installation
>Before running the example notebook, you will need to install the package using
>
>```bash:
>pip install DALL-E 
>```

DALL-Eの中で、テキスト文を入力値として受け取り、受け取った文に描かれている場面・情景や概念をうまく表現するイメージ画像を、出力値として出す部分は、未公開の状況です（2021年9月5日現在）。

OpenAIの公式GitHubに掲げられている次の文でそのことが述べられています。

> The transformer used to generate the images from the text is not part of this code release.

実装コードが公開されていないこの部分は、120億個のパラメータを持つTransformerモデルです。Open AIはウェブ空間から2億5千万件の画像とテキストのペアを集めて、このTransformerモデルを学習させたと発表しています。

残念ながら、テキストを画像に変換する肝心の部分が未公開のため、Open AIから公開されている実装コードだけでは、DALL-Eモデルを動かすことはできません。

ところで、Open AIからは、テキストと画像の類似度を数値で評価してくれる__CLIP__モデルも公開されています。__CLIP__に画像ファイルを１枚と、任意個の英単語を引数として渡して実行すると、その画像が任意個ある各英単語に対応する確率を小数（確率値）で返してくれます。全単語の小数（確率値）の総和をとると１になります。

例えば、ドラえもんの画像ファイルと、単語リスト ["dog", "cat", "human", "melon", "ice cream", "car"]を渡して、返り値として[0.200, 0.700, 0.035, 0.025, 0.015, 0.025]が得られたとしましょう。

この例では、画像が"cat"に該当する確率値が"0.700" (=70%）ともっとも高いので、ドラえもんの画像は、指定した単語リストの中では、"cat"である確率が最も高いとCLIPが判断したことになります（"cat"の次に該当確率が高い単語ラベルは、"dog"になる。"dog"の該当確率は0.200 (=20%)です）。

この__CLIP__に、次の２つのベクトルを入力データとして入れることで、テキストと画像が__どれだけ似ているか__の__正解率を得る__ことができます。

- 【 入力１ 】　 ユーザから入力されたテキスト文を特徴ベクトルに変換した文ベクトル
- 【 入力２ 】　 低次元の画像ベクトル。DALL-Eの構成部品としてすでに公開されている画像データの次元圧縮器（*Discrete VAE*）から出力された画像ベクトル

この__CLIPから出力され正解率（類似度）を、誤差逆伝播で最大化する__ことで、__与えられたテキスト文に対応する適切な画像を「生成」する__ことができます。

この「公開済みのDALL-Eの画像圧縮器」と、「（DALL-Eを開発したOpenAIから別に公開されている）__CLIP__」を連結させた__DALL-Eの再現モデル__のコード実装が、サードパーティのプログラマである[@advadnoun氏](https://twitter.com/advadnoun)によって公開されています。そして、この実装コードにさらに改良の手を加えたコードが、__*cedro3*__さんによって公開されています。（このコードをこの記事では、「擬似DALL-Eモデル」と呼ぶことにしたのでした）。

http://cedro3.com/ai/dall-e/

__擬似DALL-Eモデル__は、英文指示文を受け取って、その文に描かれた内容を描写した画像を自動生成するとき（＝推論フェーズ）、次のループ処理を指定されたイテレーション（iteration）回数、繰り返します。

####（ 以下のループをひたすら回す ）

・Discrete VAEにランダムパラメータを与えて、 画像を生成
・テキストと、DALL-E Image Decoderから出力された画像をそれぞれ、clipで特徴ベクトル化
・両ベクトル間の類似度をclipで算出（clipで画像とテキストの類似度評価）
・類似度をさらに高めるために、 ランダムパラメータを更新
・新たなランダムパラメータをDiscrete VAEに入力し、 前回よりもテキストに近い画像を生成

```Python3:GoogleColaboratory
# CLIPのモデル化
! pip install ftfy regex
import clip
model, preprocess = clip.load('ViT-B/32', jit=True)  
model = model.eval()  

# DALL-Eのモデル化
! pip install DALL-E
from dall_e import map_pixels, unmap_pixels, load_model
dec = load_model("https://cdn.openai.com/dall-e/decoder.pkl", 'cuda')

  （ 中略 ）

# テキスト入力
text_input = text
iteration_number = iteration_num

# テキストを特徴ベクトルに変換
token = clip.tokenize(text_input)  
text_v = model.encode_text(token.cuda()).detach().clone()
  
 （ 中略 ）
  
# 学習ループ
for iteration in range(iteration_number):
   # --- 順伝播 ---
   # パラメータから画像を生成
   out = unmap_pixels(torch.sigmoid(dec(latent())[:, :3].float()))
   # 画像をランダム切り出し・回転  
   into = augment(out)
   # 画像を正規化
   into = nom((into))
   # 画像から特徴ベクトルを取得
   image_v = model.encode_image(into)
   # テキストと画像の特徴ベクトルのCOS類似度を計算 
   loss = -torch.cosine_similarity(text_v, image_v).mean() 
```

__実装コードのうち、コアとなる部分は次の２つの部分です__


### 【 コードを読む１ 】
#### パラメータからdiscrete VAE(DALL-E)で画像を生成する部分

- パラメータは最初はランダム値です。
- ループ処理の１周目では、このランダム・パラメータが使われる

>```Python3:
> # パラメータリセット
> latent = Pars().cuda()
>```


```Python3:
# DALL-Eのモデル化
! pip install DALL-E
from dall_e import map_pixels, unmap_pixels, load_model
dec = load_model("https://cdn.openai.com/dall-e/decoder.pkl", 'cuda')

（ 中略 ）

# パラメータリセット
latent = Pars().cuda()
（ 中略 ）
# 学習ループ
for iteration in range(1001):
 
  # --- 順伝播 ---
  # パラメータから画像を生成
  out = unmap_pixels(torch.sigmoid(dec(latent())[:, :3].float()))
```
 
### 【 コードを読む２ 】

#### 生成された画像（の特徴ベクトル）と、テキスト（の特徴ベクトル）の類似度をCLIPで評価し、CLIPによる類似度を最大化する方向に、パラメータを誤差逆伝播修正する部分

```Python3:
# テキスト入力
text_input = 'an armchair in the shape of an avocado'
 
（ 中略 ）

# テキストを特徴ベクトルに変換
token = clip.tokenize(text_input)  
# 学習ループ
for iteration in range(1001):

（ 中略 ）

  # 画像から特徴ベクトルを取得
  image_v = model.encode_image(into)
  # テキストと画像の特徴ベクトルのCOS類似度を計算 
  loss = -torch.cosine_similarity(text_v, image_v).mean()  
 
  # 逆伝播
  optimizer.zero_grad()
  loss.backward()
  optimizer.step() 
```

今回は、この__*cedro3*__さんの実装コードをGoogle Colab+で動かしました。
なお、次のウェブサイトでも、この「DALL-Eのdiscrete VAE + CLIP」の組み合わせが採用されています。

https://axross-recipe.com/recipes/148