🎨

AIで年賀状を描いてみた 〜CLIPDrawの紹介と応用〜

2021/12/21に公開

この記事は CA22 Advent Calendar 2021 の21日目の記事です。

はじめに

こんにちは、そうそうです!
Twitter: @sou_squared
Instagram: @sousquared

この度、サイバーエージェントに22卒MLエンジニアとして内定をいただきました。弊社では、「AI x クリエイティブ」をテーマに技術力を磨いていきたいと思っています!

この記事では、「AI x クリエイティブ」の分野で最近気になった手法(CLIPDraw)の紹介と、その手法の応用として試してみたことを共有します。

要点

  • 年賀状に使えるような筆ペンスタイルの寅の画像をAIで生成してみた
  • CLIPDrawにより、入力文に対してベジェ曲線を最適化することで生成
  • ベジェ曲線の色を黒に固定
  • いろいろパラメータを試したが、期待した筆ペンスタイルの画像は得られなかった
  • カラーで生成すると良い生成結果が得られた

言語と画像を同時に学習することでAIが生み出す表現の可能性が広がる

経緯

僕は最近、筆ペンやProcreateのペンツールで年賀状の絵を描いてSNSにアップしているのですが、以下のようなシンプルな筆ペンスタイルの絵をAIによって描けないかと考えました。そこで、以下で紹介するCLIPDrawという手法を応用して生成できないかと思い至ったわけです。


2021年の丑
筆ペンで描くようなシンプルな絵をAIで描いてみたい

CLIPDraw

CLIPDraw (Kevin Frans et al., 2021)とは、入力文から絵を描く手法の一つです。OpenAIが提案したCLIPという、テキストと画像を学習したモデルをベースとしていて、入力文に近い絵を生成します。

https://twitter.com/kvfrans/status/1409925269117362181?s=20

僕自身、このツイートを見た時はかなり衝撃を受けました。リアルな画像を生成する手法はたくさん提案されていますが、この手法では曲線を使って画像を生成しており、本当に「AIが絵を描いている」ような感覚になります。

また、筆者のブログでも紹介されていますが、概念的な絵も生成しています。言語と画像を結びつけて学習することで、表現の可能性が拡がっていて面白いです。


CLIPDrawでは概念的な絵も描くことができる

自分でもいくつか実際に試してみました。


"Visiting Kyoto"
着物や寺や山が描かれているように見える


"Coffee addicted"
すごいコーヒーに支配されてそう

いかがでしょうか、入力文をうまく表している絵が生成されているように見えます。

CLIPDrawの生成過程

CLIPDrawは、CLIPに入力して得られる画像とテキストの特徴ベクトルをそれぞれ利用します。生成過程では、ランダムに初期化したベジェ曲線[1]を、入力した文の特徴量に近づける(COS類似度を近づける)ように更新して、徐々に最適な線を描いていきます。


CLIPDrawの生成過程

ライブラリ diffvg

曲線の描写と最適化にはdiffvg[2]というライブラリが使われています。
diffvgは微分可能なラスター変換器です。2Dのベクターグラフをラスター画像に変換し、且つ誤差逆伝播法で微分値を求めることができます。

CLPDrawの実装では、制御点の位置(線の形)、線の幅、色を調整するために使用されています。

pythonパッケージのpydiffvgは、PyTorchで実装されているため[3]、ロスを求めて最適化するステップは、深層学習モデル等の訓練と同じような書き方で実装できます。

制御点の最適化ステップ一部抽出(※実際の実装とは異なります)
# Create a random path
# (実際には複数のランダムなpathを用意する)
path = pydiffvg.Path(num_control_points, points, stroke_width=torch.tensor(1.0), is_closed=False)

# (実際には複数のpointsを加える)
points_vars = []
path.points.requires_grad = True
points_vars.append(path.points)

# Optimizers
points_optim = torch.optim.Adam(points_vars, lr=1.0)

~~ ロスを計算 ~~

# Backpropagate the gradients.
loss.backward()

# Take a gradient descent step.
points_optim.step()

年賀状の絵をCLIPDrawで描いてみよう

冒頭の経緯でも説明した通り、今回の目標は「年賀状に使えるようなスタイルの寅の画像をCLIPDrawで生成すること」です。そこで、以下のようなアプローチでCLIPDrawを修正して、絵を生成してみました。

アプローチ

筆ペンで書いたようなシンプルな絵にするために、線の色を黒に固定しました。(カラーで生成した結果も後に示します。)また、なるべくシンプルな絵に仕上げるために、線の数も64や128などに減らして生成してみました(デフォルトは256)。

  • 線の色を黒に固定
  • 線の数を減らす
  • カラー(オリジナルのCLIPDraw)でも生成

線の色を黒に固定

元の実装では、入力文の特徴量に近づけるために色も変化していきます。今回の実装では、生成する線の色を黒に固定しました。

実際には以下のように、pydiffvg.ShapeGroupの引数であるstroke_colorを黒に固定し、opacity(不透過度)を0.5に固定しました。線を半透明にした理由は、重ね塗ることで表現の幅が広がることを期待したからです。

R, G, B = 0.0, 0.0, 0.0
opacity = 0.5
stroke_color = torch.tensor([R, G, B, opacity]))

操作したパラメータ

操作した主なパラメータは以下の通りです。

- 入力文(prompt)
- ストローク数(num_paths)
- 色:黒に固定する(black) or しない(colorful)

と、ここまでは良かったのですが、ここからはかなり大変でした。。。なぜなら、ここからはひたすらパラメータを変えて生成結果を確認する作業の繰り返しだからです。生成結果を思い通りに制御できないのは、この手法の辛いところだなと思いました。(もし制御方法を提案できれば論文書けそう。うまくいきませんでしたが、補足でスケッチ画像でのファインチューニングした結果も載せています。)

入力文だけでも20以上試しました。

試した入力文の例
- "a drawing of a tiger"
- "a roaring tiger"
- "a tiger prowling through a bamboo grove"
- "2022, the year of the tiger"
etc...

結果

結果から言うと、黒の線だけではなかなかはっきりとした寅の形を生成することはできませんでした。ほとんどが意味を成さない線の集まりになっていました。


"a tiger" 64 paths
輪郭は捉えられている


"a tiger" 128 paths
パスの数を多くするとより不恰好な絵になる


"The tiger prowled through the jungle." 64 paths
ジャングル感は伝わる

なんとなく寅の形を表現できたもの↓


"a tiger prowling through a bamboo grove" 64 paths
竹林の中を寅が彷徨いている

因みに、カラーで生成させた結果の中には、良い感じの絵もありました。やはり表現の探索空間(ここでは色のパターン)が広い方が、最適化アルゴリズムとは相性が良さそうです。


"Year of the Tiger, 2022" 256 paths
数字が2020に見えるが、寅の絵がめでたい感じで良さそう

まとめ

この記事では、CLIPDrawの簡単な紹介と、その応用例として年賀状に使えるような筆ペンスタイルの寅の絵の生成に挑戦した様子を解説しました。

具体的には、CLIPDrawの生成する線の色を黒に固定し、入力文や線の数などの様々なパラメータを操作して絵を生成させました。

結果として、線の色を黒に固定しただけでは、期待したような筆ペンスタイルの寅の絵を描くことはできませんでした。一方、オリジナルのカラーのCLIPDrawでは、正月の雰囲気に合うめでたい寅の絵が生成されたので、とりあえず良しとしましょう(え?)。

最近のモデルは言語と画像を結びつけて学習することで応用の幅が広がっており、CLIPDrawような面白い表現手法も出てきています。これからもAIとクリエイティブに関する分野は更に発展していくと思うので、知識をキャッチアップし、自分の武器にしていきたいです。

最後に宣伝ですが、CAで次世代広告プロダクトの開発に関するインターンシップを応募しています!サーバーサイドとML/DS職で応募しているので、AIを使った新しい広告モデルの開発に興味がある人、とりあえず何かに挑戦してスキルを上げたい人など、どなたでも参加お待ちしています!
(エントリー締切:2022年1月03日(月)23:59)

https://www.cyberagent.co.jp/careers/students/event/detail/id=26886

それではみなさん、良いお年を!!
Have a happy new year!!

補足

よりシンプルな絵を生成させるために、CLIPモデルのファインチューニングも試してみました。

実際には、人間が描いたスケッチ画像のデータセット[4](2万枚)を使ってCLIPモデルをファインチューニングし、学習後のモデルを使ってCLIPDrawの手法で画像を生成させてみました。

しかし結果は、よりクオリティの低い画像を生成するのみでした。


"a drawing of a tiger"
スケッチ画像でファインチューニングをしても、うまく寅の絵は生成されなかった

これに関しては、自分がCLIPを学習させること自体初めてだったので、ノウハウを貯める必要があると思いました。(CLIPの学習はかなりコツが要るようです。そもそも画像数が少ないとは思いますが。)

脚注
  1. 制御点を用いて描く滑らかな曲線。イラレなど多くのグラフィックソフトウェアで利用されている。 ↩︎

  2. Differentiable Rasterizer for Vector Graphics: https://github.com/BachiLi/diffvg ↩︎

  3. TensorFlowの実装もある: https://github.com/BachiLi/diffvg/tree/master/pydiffvg_tensorflow ↩︎

  4. Mathias Eitz, James Hays and Marc Alexa, How Do Humans Sketch Objects?, ACM Trans. Graph. (Proc. SIGGRAPH), 2012 : http://cybertron.cg.tu-berlin.de/eitz/projects/classifysketch/ ↩︎

Discussion