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

## 【 実験対象 】 「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

## 今回、行うこと

今回は、この__DALL-E擬似モデル__を対象として、__スターバックス__や__シャネル__、__ベルルッティ（革靴・革鞄・革小物の老舗）__、__ベントレー（高級車メーカー）__といったブランドの固有名詞をどれくらい理解して、画像として描画できるかの実験を行ってみました。

擬似モデルの構造を考えると、この実験の中で、固有名詞の学習範囲の検証を受ける被験者は、DALL-Eのデコーダ部というよりも、「テキストと（そのテキストをキャプション文として持つ）画像のペア」を学習データとして学習済みのCLIPモデルの方かもしれません。

なお、__CLIPモデル__は、web空間上からクロール採取された、画像と、（その）説明文（「キャプション文。英文テキスト）のペアのデータを大量に学んだモデルです。CLIPについては、過去に２本の記事を書いています。よかったら参考にしてみてください。

- [OpenAI CLIPのHuggingFace版をマニュアル通りに動かしてみる](https://qiita.com/electronics_diy721/items/381c1e244d293ca87486)
- [OpenAIのCLIPをTerminalコマンド化した](https://qiita.com/electronics_diy721/items/ee07c3cae6d00f67c461)

また、cedro3さんは、BIG GAN + CLIPでテキスト画像変換するモデルも実装されています。

http://cedro3.com/ai/biggan-clip/

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

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

http://cedro3.com/ai/biggan-clip/


###（ 前段の記事 ）

この記事は、以下の記事の続編です。
作成した__*draw_image_based_upon_text*__メソッドを使って、指示文として与えた英文から画像を生成してみます。

```Python3:使い方
input_text =  "an image of the wind"
draw_image_based_upon_text(input_text, 7000)
```

__（ 前段の記事 ）__

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


#（検証結果） ブランド名の固有名詞の認識力

###*Starbucks*

- スターバックスの店内風景っぽい
- 同店舗のイメージカラーである緑がちゃんと、あしらわれている

```Python3:
input_text = "a photo of starbucks coffee"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/132098279-00c24664-a8ce-4fb7-8650-9545a1367494.png)

__続いて、ロゴマークが学習されているかを検証する__

- 結果は、不正解。但し、描かれた絵の下の部分もじゃもじゃとした部分は、本物のロゴマークの一部が再現されている

```Python3:
input_text = "a logo of starbucks coffee"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/132100663-632d49dc-7965-43dd-896d-758cc79fe445.png)

####（ 実際のロゴマーク ）

ちなみに、実際のロゴマークは次の絵柄である。

![](https://user-images.githubusercontent.com/87643752/132098856-fdc5cac3-4449-49ef-b335-f3b975c2a1eb.png)

今度は、指示文から__*coffee*__という語句を落としてみる。コーヒーの絵柄が描かれたのは、スターバックス社の正式社名に含まれるこの語句に引っ張られた可能性があるからだ。

```Python3:
input_text = "a logo of starbucks"
draw_image_based_upon_text(input_text, 7000)
```

- 得られた結果がこれです。
- 見事、コーヒーの絵柄が消えました。さらに、女性らしきマスコットの顔が浮かび上がりました。

![](https://user-images.githubusercontent.com/87643752/132102442-5cf26318-cb8e-4be6-9674-3a80006ff1d4.png)


__（ 顔の部分を拡大：画面をクローズアップして、スクショ撮影した画像 ）__

![](https://user-images.githubusercontent.com/87643752/132113702-1c7216f4-bac1-4e76-ad27-ec0b4166d469.png)

- 両眼の線（目尻が垂れ下がった笑みを浮かべている目）と、鼻と口に見える（見ようとすれば）。
- スタバのロゴマークに描かれているマスコットの顔とは、まだだいぶ距離があります。
- しかし、波模様の線が複数並行に描かれていて、さらに人の顔が描かれたということは、スタバのロゴは学習済みの可能性が高いのでは？と感じさせます。


今度は__店舗の外観__を見てみます。

```Python3:
input_text = "a photo of the exterior of the starbucks coffee store"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/132104288-98382add-70cd-4099-be1b-70514856c753.png)

店舗のクローズアップ画像を指示した結果が次です。

```Python3:
input_text = "a closeup photo of the exterior of the starbucks coffee store"
draw_image_based_upon_text(input_text, 7000)
```

- 画像の上半分は、指示した通り、店舗の外観が描かれています。
- 画面の下の部分には、駐車中の自動車らしきものが並んで描かれています。

![](https://user-images.githubusercontent.com/87643752/132105316-deb210eb-ae21-4293-b061-d0d9b8dc7088.png)

###今度は、マクドナルド（「McDonald's」）で試してみます

```Python3:
input_text = "a closeup photo of McDonald's store"
draw_image_based_upon_text(input_text, 7000)
```

- 画面は大きく歪んでいますが、黄色の「M」のロゴマークと赤い屋根、白文字で「mcdonald」と表示されました。（「M」が大文字が正解ですが）
- 建物は普通の家に見えますが、学習用データの中に含まれていた（？）であろう海外（米国？）の店舗は、こんな外観の建物が多いのかもしれません。

![](https://user-images.githubusercontent.com/87643752/132112126-8bd71234-1711-4145-8307-b1390e6fdaa5.png)


```Python3:
input_text = "a photo of foods McDonalds"
draw_image_based_upon_text(input_text, 7000)
```

- 「マクドナルドの食事」という指示文を与えて、得られた画像も、それなりにマクドナルドの雰囲気を捉えています。

![](https://user-images.githubusercontent.com/87643752/132113915-fdc0a27c-1124-4a83-9d97-fe4f6771a9a6.png)



 失敗。
- マクドナルドのロゴマークが描けていない。また、ロゴマーク以外もマクドナルドを固有名詞として認識できている様子が見て取れる画像の要素はない

```Python3:
input_text = "mcdonald's hamburger and french fries"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/132032786-8bbde509-dc37-414b-a355-7b0eef0420e5.png)

####（ 実際のロゴマーク ）

__正解は、以下です。__

![](https://user-images.githubusercontent.com/87643752/132091660-28397e9b-b650-40e9-9812-e4aede5c707c.png)

###次に、焼き物「セーブル焼き」の壺を試してみます。

ちなみに、実際のセーブル焼きの壺はこんな感じです。

![](https://user-images.githubusercontent.com/87643752/132112735-ead36e7d-7514-47a4-a869-1dc943f44c19.png)

```Python3:
input_text =  "a photo of sevres vase"
draw_image_based_upon_text(input_text, 7000)
```

- 壺の表面に、さらに別の壺がネストされて描かれている気もしますが（笑）、セーブル焼きの色調と絵柄の雰囲気はうまく捉えられています。

![](https://user-images.githubusercontent.com/87643752/132114042-5a6de492-7040-4847-890a-a3be2a924c12.png)

###次は、ファッション・アイテムの老舗高級ブランド 「Berluti」を試してみます。

###*Berluti*

####【 結果 】
- 柔らかい革の質感の靴と財布と、深みのある独特な色合いは、まさにBerlutiのそれが忠実に描写されている

```Python3:
input_text = "leather shoes of berluti are displayed on a silk cloth"
draw_image_based_upon_text(input_text, 7000)
```

__（ 画像生成：試行１回目 ）__

![](https://user-images.githubusercontent.com/87643752/131976186-43a015c7-91db-48dc-959b-b0363d1cb620.png)

__（ 画像生成：試行２回目 ）__

![](https://user-images.githubusercontent.com/87643752/131981294-43de53b5-54c6-46fe-a66f-d70f4b22718a.png)

__*photo of*と書いてみる__

```Python3:
input_text = "a photo of leather shoes of berluti are displayed on a silk cloth"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/131990379-c290fe00-e1f6-4b07-88b4-3710f38ab544.png)

__今度はBerlutiのお財布__

```Python3:
input_text = "a wallet of berluti is displayed on a silk cloth"
draw_image_based_upon_text(input_text, 7000)
```

- この絵は、Beflutiのお財布らしからぬ部分がある

![](https://user-images.githubusercontent.com/87643752/131991187-960bd9f7-55b6-4779-86d0-a92f1f900ee1.png)


__木箱に入れてみる__

```Python3:
input_text = "a wallet of berluti are displayed inside a wodden box"
draw_image_based_upon_text(input_text, 7000)
```

- Beflutiのお財布らしい絵が見事に描かれている

![](https://user-images.githubusercontent.com/87643752/131987388-ea00ed69-5eba-4734-9f1b-2b497ee5726c.png)


###*Chanel*

まずは、__ロゴマーク__から。

####（ 実際のロゴマーク ）

正解データは以下です。

![](https://user-images.githubusercontent.com/87643752/132091384-0e94926e-6a1c-421e-8c1b-f212fd17e5a5.png)


```Python3:
input_text = "logo of chanel"
draw_image_based_upon_text(input_text, 7000)
```

__Chanelのロゴマークがちゃんと描かれている__
__担し、画面が歪むのが、「DALL-Eのdiscrete VAE + CLIPの組み合わせ」実装の難点である__

![](https://user-images.githubusercontent.com/87643752/132048563-e2a9eea4-f005-4995-844b-3942393c97ac.png)


```Python3:
input_text = "a chanel classic handbag"
draw_image_based_upon_text(input_text, 7000)
```

__Chanelのロゴマークがちゃんと描かれた__

![](https://user-images.githubusercontent.com/87643752/132002166-7f0af720-a274-4027-85ea-4c0425dc9ff0.png)

```Python3:
input_text = "a handbag of chanel"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/132018332-c618a09b-bedd-46d0-947e-87ae90c41d1f.png)


###*Franck Muller*

```Python3:
input_text = "a watch of franck muller"
draw_image_based_upon_text(input_text, 7000)
```
- 微妙なところ。__*Franck Muller*の時計__だと思いこんで見ようとすれば、そう見えなくもないが・・・

![](https://user-images.githubusercontent.com/87643752/132018410-4dd41403-78b0-4a5e-90b7-9e346d733f31.png)

####（ 実際のロゴマーク ）

ちなみに、ロゴマークは以下です。

![](https://user-images.githubusercontent.com/87643752/132091468-898370ef-3cf2-421c-8b78-68a67bce50be.png)

![](https://user-images.githubusercontent.com/87643752/132091491-94d7de8b-46bd-4b5e-88b3-5c6a5fcee165.png)

###iPhone

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

- 本体の上の部分しか描画されていないが、輪郭の形状といい、レンズの形と位置といい、iPhoneスマホである

![](https://user-images.githubusercontent.com/87643752/132081479-9afa0d8f-9e52-48bb-9bdb-c097de8dbf34.png)


![](https://user-images.githubusercontent.com/87643752/132118550-7a1cd4e6-6663-40e9-bec9-07c5d382a583.png)

![](https://user-images.githubusercontent.com/87643752/132118558-e21190bb-a7a2-4b59-b7f9-df6a03ed164f.png)


####アップル社のロゴマークの描画には失敗した

```Python3:
input_text = "logo of apple inc"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/132053008-14e2cd88-424e-47f7-b0cd-9e9ceebd84a9.png)

```Python3:
input_text = "logo of iphone"
draw_image_based_upon_text(input_text, 4000)
```

![](https://user-images.githubusercontent.com/87643752/132080210-f1c817e2-e450-4da1-a6f1-fa54a9f0a34f.png)

```Python3:
input_text = "logo of macbook"
draw_image_based_upon_text(input_text, 4000)
```

![](https://user-images.githubusercontent.com/87643752/132078063-5f02fb9d-7c5d-4931-8f4a-a069529ab825.png)

```Python3:
input_text = "logo of macintosh or mac, macbook"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/132075906-edba51dd-0c9d-48b4-9964-8bb3f6947a2b.png)



__（ 正解データ ）__

![](https://user-images.githubusercontent.com/87643752/132091364-e5378230-30a7-441d-966f-d51ba68dc90d.png)

###*Haagen-Dazs*

- 失敗。
- ハーゲンダッツのロゴマークが描けていない。
- また、ロゴマーク以外もハーゲンダッツを固有名詞として認識できている様子が見て取れる画像の要素はない

```Python3:
input_text = "a haagen-Dazs ice cream"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/132001450-05adfb36-c9ba-4859-9031-db4cd266b09a.png)

__正解は、以下です。__

![](https://user-images.githubusercontent.com/87643752/132091686-e12b7781-d764-4ca9-b27c-36a25dc57af1.png)

###__*Bentley*__

- 車体の正面部分は、Bentleyらしい
- 車体の形もBentleyらしさを感じさせる
- 車体正面のロゴマークはぼやけているが、実際のロゴマークに近い印象を受ける

```Python3:
input_text = "a photo of bentley car"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/132093277-8a6bd889-be37-4bd0-8663-057d8fead8cb.png)

####（ 実際のロゴマーク ）

__Bentleyのロゴ__

![](https://user-images.githubusercontent.com/87643752/132091350-4503b62b-f0b2-44bd-b3e9-2bcbfa5345d9.png)


- 車体の正面部分は、同社の1920年代モデルの自動車の姿かたちがちゃんと描けている
- __*luxury*__と形容詞を与えたからか、植物の皮のような奇妙な模様が車体に塗布されて描かれている

```Python3:
input_text = "Bentley luxury car of 1920's"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/132083738-87bb4625-eca5-4bff-a14d-abccc24bad0e.png)

- __*luxury*__と形容詞を外した。
- 車体の内部をみる視点から描かれている。1920年代当時のクラシック・カーの形が描けてはいる
- __*Bentley*__という文字列が刻まれているが、同社の公式のロゴの形ではない。

```Python3:
input_text = "Bentley car of 1920's"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/132088403-e7022651-8634-4a12-8614-5ae824b8a05c.png)

__*a photo of*__で描き始めてみます。

```Python3:
input_text = "a photo of bentley car of 1920's"
draw_image_based_upon_text(input_text, 7000)
```

- うーん、、、かろうじて雰囲気は伝わってきますが・・・ダメな絵です。

![](https://user-images.githubusercontent.com/87643752/132090947-c8b2d043-dff3-46ff-a8f1-3154d41a158d.png)


- しかし、「Bentleyのロゴの写真」という指示文を出してみると、どうもダメである。

```Python3:
input_text = "logo of bentley"
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/132095364-ed653913-5d35-40bd-a0f4-d32be976c3de.png)

###__*Jaguar*__

```Python3:
input_text = "a photo of Jaguar car"
draw_image_based_upon_text(input_text, 7000)
```
- これは失敗。車のジャガーではなく、『動物の「ジャガー」の姿をした車』のイメージ画像が生成されてしまった

![](https://user-images.githubusercontent.com/87643752/132115194-f09e6f03-a5da-4547-addf-5e822ddda874.png)

指示文を変えてみます。

```Python3:
input_text = "a photo of the car of Jaguar"
draw_image_based_upon_text(input_text, 7000)
```

- 今度は自動車の絵が描かれました。しかし、ジャガーブランドの車とは、見た目が大きく異なります。
- ジャガーはCLIPモデルで未学習の可能性が高いです。

![](https://user-images.githubusercontent.com/87643752/132115847-e8b0cd52-a2f8-4147-9541-013e50334532.png)

####（ 参考 ） ジャガー車のロゴマーク

__ロゴ__

![](https://user-images.githubusercontent.com/87643752/132091754-715620b9-dbe1-4f16-a997-89c393bfe816.png)