DALL-EでPhoto-realisticな料理や調度品の画像づくりに挑戦

###ベンチマークの画像

https://data-analytics.fun/2021/02/05/openai-dalle-blog/

このサイトに掲載されている__オリジナル版__のDALL-Eの出力画像の例は、写真と見間違えてしまうほど写実的で高精細な画像です。しかし、よく目を凝らしてみると、写真（スタイルの生成画像）ではなく、2Dグラフィックスの画像であることがわかります。

DALL-Eは、__*photo of*__と指示を与えても、完全な写真スタイルの生成画像を返してくれるわけではないみたいです。この辺りは、（現実には存在しない架空の）写真データを生成するGANモデルとは、一線を隠しているのかもしれません。

![](https://user-images.githubusercontent.com/87643752/131608456-dda76b86-a7fc-4954-a98d-6790dc887053.png)

###今回、挑戦するために用いるのは、「DALL-Eの公開済みの部分実装コード + CLIP」の組み合わせモデル

この記事の最後で詳しく述べますが、__DALL-Eの実装コードは、肝心の部分がまだ非公開のため、OpenAIが論文や公式サイトで公開しているオリジナル版のモデルを、まるっと実装したコードが手に入りません。__

そこで、今回は__サードパーティのプログラマさんが公開してくれている「DALL-Eの公開済みの部分実装コード + CLIP」の組み合わせモデルの実装コードを使います__。詳細は、この記事の最後に書いた説明をご覧ください。

###チャレンジ

ベンチマークと同じ英文 __*"a photo of the food of japan."*__を与えた結果、得られた画像がこれ。
ColabPro+のP100 GPUサーバを使って、イテレーション数 5,000で回しました。

####実験1

```Python3:Google_Colab+
input_text =  "a photo of the food of japan." #japanese foodではなくfood of japanにする (参考） https://data-analytics.fun/2021/02/05/openai-dalle-blog/ food_photo_test4_little_good
draw_image_based_upon_text(input_text, 5000)
```

3つの食べ物の絵は、写真レベルの解像度に近い（まだ絵だとわかりますが）良い画像が出てきました。

残念なのは、土台です。土台の木目がうまくつながっておらず、モザイク調になってしまっっています。

また、水面のようにゆらゆら波打っている部分もあります。食べ物を置く机としては、写実性に欠ける結果となりました。

![](https://user-images.githubusercontent.com/87643752/131593938-20168404-447f-45bd-8e01-9a00f9eb1a6c.png)

####実験2

![](https://user-images.githubusercontent.com/87643752/131704310-9a438f15-73f5-44ba-b135-4d5cda6def61.png)


####実験3

今度は、__*the food of japan*__ではなく、より踏み込んで、__「味噌汁」__と、__料理名を指定__します。
ここから、__イテレーション数を7,000に__増やします。

```Python3:Google_Colab+
input_text =  "a photo of miso-soup."
draw_image_based_upon_text(input_text, 7000)
```

味噌汁は写実的に描けています。写真レベルです。
しかし、左上と左下のお椀の縁の部分が、歪んでしまっています。

机が脈打っているのは、先ほどの画像と同じく、うまく描ききれていません。

![](https://user-images.githubusercontent.com/87643752/131594061-c0a6de03-3981-47a3-9d76-c96726415f37.png)

####実験4

```Python3:Google_Colab+
input_text =  "a photo of sushi."
draw_image_based_upon_text(input_text, 7000)
```

今度は、画質は写真レベルというには、精細さに欠ける粗さが残りますが、全体としてはなかなか写実的に描けています。

![](https://user-images.githubusercontent.com/87643752/131603005-7c9b68ed-f12c-49d8-92c2-a0d354781841.png)

####実験5

```Python3:Google_Colab+
input_text =  "a photo of sushi dishes on Japanese traditional plate." #food_photo_test9_sushi_veri_good
draw_image_based_upon_text(input_text, 7000)
```

これはかなりいい感じです。
しかし、お皿や器が脈打っていて、水面のように光が反射したり、ピントが合わず二重や三重に（ズレながら）重なって描画されています。

イテレーション数をさらに上げることで、この手ブレ画像のような現象が解消されるかは、まだ未確認です。

![](https://user-images.githubusercontent.com/87643752/131612165-96561f83-a776-44db-95f1-38f97b1a7c70.png)

####実験6

```Python3:Google_Colab+
input_text =  "a photo of sukiyaki and japanese sake."
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/131620491-836e53f2-2c1c-442f-a1ed-2ed88b408fbb.png)

####実験7

```Python3:Google_Colab+
input_text =  "a photo of a japanese food of sukiyaki."
draw_image_based_upon_text(input_text, 7000)
```

![](https://user-images.githubusercontent.com/87643752/131635534-a5d27a39-a6ba-472e-9fa9-ac1de876720e.png)

##今回動かしたのは、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() 
```

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

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