52
53

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

僕がAI Text-to-Artジェネレータを1週間で作った方法

Last updated at Posted at 2021-10-24

本記事は、20歳のAIエンジニア/フルスタックデベロッパFathy Rashad氏による「How I built an AI Text-to-Art Generator」(2021年10月2日公開)の和訳を、著者の許可を得て掲載しているものです。

#僕がAI Text-to-Artジェネレータを1週間で作った方法

Text2Art.comの作成方法について、順を追って詳しく説明します。

Text2Art 生成アートのギャラリー(著者による画像)

##概要

これは、僕がどうやって1週間でText2Art.comを作成したかを説明する記事です。Text2Artは、VQGAN+CLIPベースのAIアートジェネレータです。テキストを入力するだけで、ピクセルアート、線画、水彩画・油彩画など、あらゆる種類のアートを生成できます。この記事では、VQGAN+CLIPの実験から、Gradioを使用したシンプルなUIの構築、モデル提供のためのFastAPIへの移行、そして最後に、Firebaseを使用したキューシステムまで、僕の思考プロセスを追います。興味のある部分までスキップしてください。

text2art.comでお試しいただけます。ここにソースコードがあります。
(リポジトリにぜひ星を付けてください)

IMAGE ALT TEXT HERE

Text2Artデモ(更新情報:現ユーザー数1.5K超)

##目次

  • はじめに
  • 仕組み
  • CodeVQGAN+CLIPを使用したコードによるアート生成
  • Gradioを使用したUI構築
  • FastAPIを使用したML提供
  • Firebaseを使用したキューシステム

##はじめに

少し前に、ジェネレーティブアートとNFTが世界を席巻しました。これを可能にしたのは、OpenAIがtext-to-image生成を大きく進歩させたことです。今年の初めにOpenAIは、優れた機能を有する、強力なtext-to-imageジェネレータDALL-Eを発表しました。以下は「キリンとドラゴンのキメラ/ドラゴンを模したキリン/ドラゴンでできたキリンのプロの高品質イラスト」というテキストプロンプトでDALL-Eが生成した画像です。DALL-Eがいかに優れているか分かります。

「キリンとドラゴンのキメラ/ドラゴンを模したキリン/ドラゴンでできたキリンのプロの高品質イラスト」テキストプロンプトによるDALL-E生成画像(OpenAI MITライセンス画像)

残念ながらDALL-Eは一般公開されていません。しかし幸いなことに、DALL-Eの魔法の背後にあるモデルであるCLIPが代わりに公開されました。CLIP(Contrastive Image-Language Pretraining:対照言語画像事前学習)は、テキストと画像を組み合わせたマルチモーダルネットワークです。つまり、画像とキャプションの一致度をスコア化できます。これは、入力テキストと完全一致する画像を生成するようにジェネレータを誘導するのにとても役立ちます。DALL-Eでは、CLIPを使用して、生成された画像をランク付けし、スコアが最も高い画像(テキストプロンプトに最も近い画像)を出力します。

画像とキャプションをスコア化するCLIPの例(著者による画像)

DALL-Eの発表から数ヶ月後、VQGAN(Vector Quantized GAN:ベクトル量子化GAN)という新しい変形画像ジェネレータが公開されました。VQGANとCLIPを組み合わせると、DALL-Eに近い品質が得られます。学習済みVQGANモデルが公開されてから、沢山の素晴らしいアートがコミュニティで生成されました。

街の港の夜景と沢山の船の絵、戦争中の難民の絵(著者による画像)

この結果にとても驚き、友人と共有したいと思いました。しかし、アート生成のコードに没頭してくれる人はあまりいないので、コードを見ることなくプロンプトを入力するだけで、誰でもすぐに欲しい画像を生成できるウェブサイト、Text2Art.comを作成することにしました。

##仕組み

VQGAN+CLIPはどのように動作するのでしょうか。簡単に言うと、ジェネレータが画像を生成し、CLIPがその画像との一致度を測定します。次に、ジェネレータはCLIPモデルからのフィードバックを使用して、より「正確な」画像を生成します。CLIPスコアが十分に高くなり、生成された画像がテキストと一致するまで、何度もイテレートします。

VQGANモデルが画像を生成し、CLIPがその処理を導く。ジェネレータがより「正確な」画像を生成するまで、何度もイテレートする(出典:LJ MirandaによるVQGAN図解

VQGANやCLIPの内部動作については、この記事の焦点ではないため、ここでは触れません。VQGAN、CLIP、DALL-Eについてさらに説明が必要な場合は、以下の素晴らしい資料を参照してください。

###X+CLIP

VQGAN+CLIPは、画像ジェネレータとCLIPの組み合わせでできることの一例に過ぎません。VQGANはどんな種類のジェネレータとも置き換えられ、ジェネレータによってはとてもうまく機能します。X+CLIPには、StyleCLIP(StyleGAN+CLIP)、CLIPDraw(ベクターアートジェネレータ使用)、BigGAN+CLIPなど、多くのバリエーションがあります。画像の代わりに音声を使用するAudioCLIPもあります。

StyleCLIPを使用した画像編集(出典:StyleCLIP Paper

##VQGAN+CLIPを使用したコードによるアート生成

VQGAN+CLIPを使用したアート生成を数行のシンプルなコードで実現した、dribnetのclipitリポジトリのコードを使用しています(更新情報:clipitpixrayに移行されました)。

VQGAN+CLIPはかなりのGPUメモリを必要とするため、Google Colabで実行することをお勧めします。ここにColabノートブックがあります。これに沿って進めてください。

まず、Colabで実行する場合は、ランタイムタイプをGPUに変更してください。

ColabのランタイムタイプをGPUに変更する手順(著者による画像)

次に、まずコードベースと依存関係を設定する必要があります。

from IPython.utils import io
with io.capture_output() as captured:
    !git clone https://github.com/openai/CLIP
    # !pip install taming-transformers
    !git clone https://github.com/CompVis/taming-transformers.git
    !rm -Rf clipit
    !git clone https://github.com/mfrashad/clipit.git
    !pip install ftfy regex tqdm omegaconf pytorch-lightning
    !pip install kornia
    !pip install imageio-ffmpeg
    !pip install einops
    !pip install torch-optimizer
    !pip install easydict
    !pip install braceexpand
    !pip install git+https://github.com/pvigier/perlin-numpy

    # ClipDraw deps
    !pip install svgwrite
    !pip install svgpathtools
    !pip install cssutils
    !pip install numba
    !pip install torch-tools
    !pip install visdom

    !pip install gradio

    !git clone https://github.com/BachiLi/diffvg
    %cd diffvg
    # !ls
    !git submodule update --init --recursive
    !python setup.py install
    %cd ..

    !mkdir -p steps
    !mkdir -p models

(注意:「!」はGoogle Colabの特殊コマンドで、Pythonではなくbashでコマンドを実行することを意味する)

ライブラリをインストールしたら、clipitをインポートし、以下の数行のコードを実行するだけで、VQGAN+CLIPを使用したアート生成ができます。テキストプロンプトを好きなものに変更するだけです。さらに、イテレーション回数、幅、高さ、ジェネレータモデル、動画生成の有無など、clipitにオプションも指定できます。使用可能なオプションの詳細については、ソースコードを確認してください。

import sys
sys.path.append("clipit")
import clipit

# To reset settings to default
clipit.reset_settings()

# You can use "|" to separate multiple prompts
prompts = "underwater city"

# You can trade off speed for quality: draft, normal, better, best
quality = "normal"

# Aspect ratio: widescreen, square
aspect = "widescreen"

# Add settings
clipit.add_settings(prompts=prompts, quality=quality, aspect=aspect)

# Apply these settings and run
settings = clipit.apply_settings()
clipit.do_init(settings)
cliptit.do_run(settings)

clipit.py

VQGAN+CLIPを使用してアートを生成するコード

コードを実行すると、画像が生成されます。イテレーションする度に、画像はテキストプロンプトに近付きます。

「水中都市」画像生成における、イテレーションによる結果改善(著者による画像)

###イテレーション回数の増加

イテレーション回数を増やしたい場合は、iterationsオプションを使用して、必要なだけ設定します。例えば、500回イテレーションしたい場合は、以下のように設定します。

clipit.add_settings(iterations=500)

###動画の生成

イテレーションする度に画像を生成する必要があるため、その画像を保存して、AIの画像生成方法のアニメーションを作成できます。設定の適用前にmake_video=Trueを追加するだけです。

clipit.add_settings(make_video=True)

以下の動画が生成されます。

生成された「水中都市」GIF(著者による画像)

###画像サイズのカスタマイズ

size=(width, height)オプションを追加して、画像を変更することもできます。例えば、ここでは解像度800x200のバナー画像を生成します。解像度が高いほど、多くのGPUメモリが必要になることに注意してください。

clipit.add_settings(size=(800, 200))

「空想の王国 #artstation」プロンプトで800x200の画像を生成(著者による画像)

###ピクセルアートの生成

clipitには、ピクセルアートを生成するオプションもあります。CLIPDrawのレンダラーをシーンの裏で使用し、パレットの色の制限やピクセル化など、ピクセルアートのスタイルを強制します。ピクセルアートオプションを使用するには、use_pixeldraw=Trueオプションを有効にするだけです。

clipit.add_settings(use_pixeldraw=True)

左「鎧の騎士 #pixelart」と右「中国の空想のゲームの世界 #pixelart」プロンプトで生成された画像(著者による画像)

###VQGAN+CLIP キーワード修飾子

CLIPにはバイアスがあるため、プロンプトに特定のキーワードを追加すると、生成画像に特定の効果が生じる場合があります。例えば、テキストプロンプトに「unreal engine」を追加すると、リアルなスタイルやHDスタイルが生成される傾向があります。また、「deviantart」「artstation」「flickr」など特定のサイト名を追加すると、通常、結果がより美しくなります。最高のアートを生成する「artstation」というキーワードが気に入っています。

キーワードの比較(kingdomakrillicによる画像

さらに、キーワードを使用してアートスタイルを調整することもできます。例えば、「鉛筆スケッチ」「ローポリゴン」などのキーワードや、「Thomas Kinkade」「James Gurney」などのアーティスト名です。

アートスタイルのキーワード比較(kingdomakrillicによる画像

さまざまなキーワードの効果の詳細については、kingdomakrillicによる包括的な実験結果を確認してください。同じ4つのテーマで200以上のキーワードを試しています。

##Gradioを使用したUI構築

最初の予定では、MLモデルのデプロイにGradioを使用するつもりでした。Gradioは、MLデモを数行のコードだけで簡単に作成できるPythonライブラリです。Gradioなら10分未満でデモを作成できます。さらに、ColabでGradioを実行すると、Gradioドメインを使用して共有可能なリンクが生成されます。このリンクをすぐに友人や一般の人と共有し、デモを試してもらうことができます。Gradioにはまだ制約がいくつかありますが、単一機能のデモをしたい場合には、最適なライブラリだと思います。

Gradio UI(著者による画像)

以下は、Text2ArtアプリのシンプルなUIを構築するコードです。コードを見れば一目瞭然だと思いますが、さらに説明が必要な場合は、Gradioドキュメントを参照してください。

import gradio as gr
import torch
import clipit

# Define the main function
def generate(prompt, quality, style, aspect):
    torch.cuda.empty_cache()
    clipit.reset_settings()

    use_pixeldraw = (style == 'pixel art')
    use_clipdraw = (style == 'painting')
    clipit.add_settings(prompts=prompt,
        aspect=aspect,
        quality=quality,
        use_pixeldraw=use_pixeldraw,
        use_clipdraw=use_clipdraw,
        make_video=True)

    settings = clipit.apply_settings()
    clipit.do_init(settings)
    clipit.do_run(settings)

    return 'output.png', 'output.mp4'

# Create the UI
prompt = gr.inputs.Textbox(default="Underwater city", label="Text Prompt")
quality = gr.inputs.Radio(choices=['draft', 'normal', 'better'], label="Quality")
style = gr.inputs.Radio(choices=['image', 'painting','pixel art'], label="Type")
aspect = gr.inputs.Radio(choices=['square', 'widescreen','portrait'], label="Size")

# Launch the demo
iface = gr.Interface(generate, inputs=[prompt, quality, style, aspect], outputs=['image', 'video'], enable_queue=True, live=False)
iface.launch(debug=True)

text2art_gradio.py

Gradio UIを構築するコード

これをGoogle Colabやローカルで実行すると、共有可能なリンクが生成され、デモを公開できます。NgrokなどのSSHトンネリングを使用しなくても自分のデモを共有できるので、とても便利です。さらにGradioは、月額わずか7ドルでデモを永久にホストできるホスティングサービスも提供しています。

Gradioデモの共有可能なリンク(著者による画像)

ただしGradioは、単一機能のデモにだけ適しています。ギャラリー、ログイン、カスタムCSSなどの機能を追加してカスタムサイトを作成することは、かなり制限されているか、全くできません。

手っ取り早い解決策としては、GradioのUIとは別にデモサイトを作成することです。次に、iframe要素を使用して、そのサイトにGradio
UIを埋め込みます。最初にこの方法を試してみましたが、重要な欠点に気が付きました。それは、MLアプリ自体と対話する必要がある部分をパーソナライズできないことです。例えば、入力検証やカスタムプログレスバーなどはiframeでは使用できません。そこで、代わりにAPIを構築することにしました。

##FastAPIを使用したMLモデル提供

APIを迅速に構築するために、FlaskではなくFastAPIを使用しています。主な理由は、FastAPIの方が記述が早い(コードが少ない)こと、(Swagger UIを使用して)ドキュメントを自動生成するため、基本的なUIでAPIをテストできることです。さらに、FastAPIは非同期関数をサポートしており、Flaskより高速だと言われています。

URLに/docs/を追加して、Swagger UIにアクセスする(著者による画像)

Swagger UIでAPIをテストする(著者による画像)

以下は、FastAPIサーバーとしてMLの機能を提供するコードです。

import clipit
import torch
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi import FastAPI, File, UploadFile, Form, BackgroundTasks
from fastapi.responses import FileResponse

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=['*'],
    allow_credentials=True,
    allow_methods=['*'],
    allow_headers=['*'],
)

@app.get('/')
async def root():
    return {'hello': 'world'}

@app.post("/generate")
async def generate(
        seed: int = Form(None),
        iterations: int = Form(None),
        prompts: str = Form("Underwater City"),
        quality: str = Form("draft"),
        aspect: str = Form("square"),
        scale: float = Form(2.5),
        style: str = Form('image'),
        make_video: bool = Form(False),
    ):
    torch.cuda.empty_cache()
    clipit.reset_settings()

    use_pixeldraw = (style == 'Pixel Art')
    use_clipdraw = (style == 'Painting')
    clipit.add_settings(prompts=prompts,
        seed=seed,
        iterations=iterations,
        aspect=aspect,
        quality=quality,
        scale=scale,
        use_pixeldraw=use_pixeldraw,
        use_clipdraw=use_clipdraw,
        make_video=make_video)

    settings = clipit.apply_settings()
    clipit.do_init(settings)
    clipit.do_run(settings)

    return FileResponse('output.png', media_type="image/png")

text2art_fastapi_1.py

APIサーバーのコード

サーバーを定義したら、uvicornを使用して実行できます。さらに、Google ColabはColabインターフェース経由のサーバーアクセスしか許可していないので、FastAPIサーバーを公開するにはNgrokを使用する必要があります。

import nest_asyncio
from pyngrok import ngrok
import uvicorn

ngrok_tunnel = ngrok.connect(8000)
print('Public URL:', ngrok_tunnel.public_url)
print('Doc URL:', ngrok_tunnel.public_url+'/docs')
nest_asyncio.apply()
uvicorn.run(app, port=8000)

fastapi_ngrok.py

サーバーを実行して公開するコード

サーバーを実行したら、(生成されたngrok URLに/docsを追加して)Swagger UIにアクセスし、APIをテストすることができます。

FastAPI Swagger UIを使用して「水中城」を生成する(著者による画像)

APIテスト中、品質やイテレーションにもよりますが、推論に約3-20分かかることがあると分かりました。3分という時間は、HTTPリクエストとしては既にとても長いとみなされていて、ユーザーはサイト上でそれだけ長く待つことを望まないでしょう。そこで、推論時間が長いため、推論をバックグラウンドタスクとして設定し、結果が出たらユーザーにメールで通知する方が、タスクに適しているかもしれないと判断しました。

方針が決まったので、まずメール送信機能を記述します。最初はSendGridメールAPIを使用していましたが、無料使用枠(1日100通)を使い切ったため、Mailgun APIに切り替えました。これはGitHub Student Developer Packに含まれていて、学生でも月20,000通のメール送信が可能です。

以下は、Mailgun APIを使用して画像を添付したメールを送信するコードです。

import requests
def email_results_mailgun(email, prompt):
    return requests.post("https://api.mailgun.net/v3/text2art.com/messages",
        auth=("api", "YOUR_MAILGUN_API_KEY"),
        files=[("attachment",("output.png", open("output.png", "rb").read() )),
            ("attachment", ("output.mp4", open("output.mp4", "rb").read() ))],
        data={"from": "Text2Art <YOUR_EMAIL>",
            "to": email,
            "subject": "Your Artwork is ready!",
            "text": f'Your generated arts using the prompt "{prompt}".',
            "html": f'Your generated arts using the prompt
<strong>"{prompt}"</strong>.'})

send_email_mailgun.py

Mailgun APIを使用してメールを送信するコード

次に、FastAPIでサーバーコードをバックグラウンドタスクに変更し、バックグラウンドで結果をメール送信します。

#@title API Functions
import clipit
import torch
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi import FastAPI, File, UploadFile, Form, BackgroundTasks
from fastapi.responses import FileResponse

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=['*'],
    allow_credentials=True,
    allow_methods=['*'],
    allow_headers=['*'],
)

# define function to be run as background tasks
def generate(email, settings):
    clipit.do_init(settings)
    clipit.do_run(settings)

    prompt = " | ".join(settings.prompts)

    email_results_mailgun(email, prompt)

@app.get('/')
async def root():
    return {'hello': 'world'}

@app.post("/generate")
async def add_task(
        email: str,
        background_tasks: BackgroundTasks,
        seed: int = Form(None),
        iterations: int = Form(None),
        prompts: str = Form("Underwater City"),
        quality: str = Form("draft"),
        aspect: str = Form("square"),
        scale: float = Form(2.5),
        style: str = Form('image'),
        make_video: bool = Form(False),
    ):
    torch.cuda.empty_cache()
    clipit.reset_settings()

    use_pixeldraw = (style == 'Pixel Art')
    use_clipdraw = (style == 'Painting')
    clipit.add_settings(prompts=prompts,
        seed=seed,
        iterations=iterations,
        aspect=aspect,
        quality=quality,
        scale=scale,
        use_pixeldraw=use_pixeldraw,
        use_clipdraw=use_clipdraw,
        make_video=make_video)

    settings = clipit.apply_settings()

    # Run function as background task
    background_tasks.add_task(generate, email, settings)

    return {"message": "Task is processed in the background"}

fastapi_background.py

このコードを使用すると、サーバーは、生成処理の終了を待って画像を返信するのではなく、リクエストに迅速に「タスクはバックグラウンドで処理中です」というメッセージを返信します。

処理が終了すると、サーバーはユーザーにメールで結果を送信します。

ユーザーにメールで送信する画像・動画の結果(著者による画像)

すべてうまく機能しているように見えたので、フロントエンドを構築し、サイトを友人と共有しました。しかし、複数ユーザーでテストすると、同時実行の問題があることが分かりました。

最初のタスクの処理中に、2番目のユーザーがサーバーにリクエストすると、なぜか2番目のタスクが並列プロセスやキューを作成せずに現在のプロセスを終了してしまいます。原因は分かりませんでした。もしかしたらclipitコードでグローバル変数を使用していることが原因かもしれません。代わりにメッセージキューシステムを実装する必要があると分かったので、デバッグにはあまり時間をかけませんでした。

メッセージキューシステムについてGoogle検索したところ、ほとんどがRabbitMQやRedisを勧めていました。しかし、RabbitMQやRedisはsudo権限が必要なようで、Google Colabにインストールできるか分かりませんでした。最終的には、プロジェクトをできるだけ早く終わらせたかったこと、Firebaseが一番使い慣れていることから、代わりにGoogle Firebaseをキューシステムとして使用することにしました。

簡単に言えば、ユーザーがフロントエンドでアートを生成しようとすると、キューというコレクションにタスク(プロンプト、画像タイプ、サイズなど)を記述したエントリが追加されます。並行して、Google Colabでスクリプトを実行します。このスクリプトは、キューコレクションの新しいエントリを継続的にリッスンし、タスクを1つずつ処理します。

import torch
import clipit
import time
from datetime import datetime
import firebase_admin
from firebase_admin import credentials, firestore, storage

if not firebase_admin._apps:
    cred = credentials.Certificate("YOUR_CREDENTIAL_FILE")
    firebase_admin.initialize_app(cred, {
        'storageBucket': 'YOUR_BUCKET_URL'
    })

db = firestore.client()
bucket = storage.bucket()

def generate(doc_id, prompt, quality, style, aspect, email):
    torch.cuda.empty_cache()
    clipit.reset_settings()

    use_pixeldraw = (style == 'pixel art')
    use_clipdraw = (style == 'painting')
    clipit.add_settings(prompts=prompt,
        seed=seed,
        aspect=aspect,
        quality=quality,
        use_pixeldraw=use_pixeldraw,
        use_clipdraw=use_clipdraw,
        make_video=True)

    settings = clipit.apply_settings()
    clipit.do_init(settings)
    clipit.do_run(settings)

    data = {
        "seed": seed,
        "prompt": prompt,
        "quality": quality,
        "aspect": aspect,
        "type": style,
        "user": email,
        "created_at": datetime.now()
    }
    db.collection('generated_images').document(doc_id).set(data)
    email_results_mailgun(email, prompt)

transaction = db.transaction()

@firestore.transactional
def claim_task(transaction, queue_objects_ref):
    # query firestore
    queue_objects = queue_objects_ref.stream(transaction=transaction)

    # pull the document from the iterable
    next_item = None
    for doc in queue_objects:
        next_item = doc

    # if queue is empty return status code of 2
    if not next_item:
        return {"status": 2}

    # get information from the document
    next_item_data = next_item.to_dict()
    next_item_data["status"] = 0
    next_item_data['id'] = next_item.id

    # delete the document and return the information
    transaction.delete(next_item.reference)
    return next_item_data

# initialize query
queue_objects_ref = (
    db.collection("queue")
    .order_by("created_at", direction="ASCENDING")
    .limit(1)
)

transaction_attempts = 0
while True:
    try:
        # apply transaction
        next_item_data = claim_task(transaction, queue_objects_ref)
        if next_item_data['status'] == 0:
            generate(next_item_data['id'],
                next_item_data['prompt'],
                next_item_data['quality'],
                next_item_data['type'],
                next_item_data['aspect'],
                next_item_data['email'])
            print(f"Generated {next_item_data['prompt']} for {next_item_data['email']}")

    except Exception as e:
        print(f"Could not apply transaction. Error: {e}")
        time.sleep(5)
        transaction_attempts += 1
        if transaction_attempts > 20:
            db.collection("errors").add({
                "exception": f"Could not apply transaction. Error: {e}",
                "time": str(datetime.now())
            })
            exit()

firebase_worker.py

タスクを処理し、キューを継続的にリッスンするバックエンドコード

フロントエンドでは、キューに新しいタスクを追加するだけです。ただし、フロントエンドで適切なFirebase設定を行っていることを確認してください。

db.collection("queue").add({
    prompt: prompt,
    email: email,
    quality: quality,
    type: type,
    aspect: aspect,
    created_at: firebase.firestore.FieldValue.serverTimestamp(),
})

完成です!これで、ユーザーがフロントエンドでアートを生成しようとすると、キューに新しいタスクが追加され、Colabサーバーのワーカースクリプトは、キューのタスクを1つずつ処理します。

コードの詳細については、GitHubリポジトリをご覧ください。
(リポジトリにぜひ星を付けてください)

フロントエンドでキューに新しいタスクを追加する(著者による画像)

Firebaseのキューの内容(著者による画像)

##おわりに

この記事が気に入ったら、他の記事もぜひチェックしてください!

AIで自分をディズニーキャラクターとしてアニメ化する
デジタルアートの未来を覗く
towardsdatascience.com

StyleGAN2でアニメキャラクターを生成する
クールなアニメ顔の補間を生成する方法を学ぶ
towardsdatascience.com

Linkedinでもお気軽にご連絡ください。

Muhammad Fathy Rashad テクニカルライター Medium | LinkedIn
16歳で大学の最年少学生として学位を取得し、2K超インストールされたモバイルゲーム2つを発表…
https://www.linkedin.com/in/mfathyrashad/

##参照

[1] https://openai.com/blog/dall-e/
[2] https://openai.com/blog/clip/
[3] https://ljvmiranda921.github.io/notebook/2021/08/08/clip-vqgan/
[4] https://github.com/orpatashnik/StyleCLIP
[5] https://towardsdatascience.com/understanding-flask-vs-fastapi-web-framework-fe12bb58ee75

##翻訳協力

この記事は以下の方々のご協力により公開する事ができました。改めて感謝致します。

Original Author: Fathy Rashad (Linkedin, https://www.mfrashad.com/)
Original Article: How I built an AI Text-to-Art Generator
Thank you for letting us share your knowledge!

選定担当: @gracen
翻訳担当: @gracen
監査担当: -
公開担当: @gracen

##ご意見・ご感想をお待ちしております
今回の記事はいかがでしたか?
・こういう記事が読みたい
・こういうところが良かった
・こうした方が良いのではないか
などなど、率直なご意見を募集しております。
頂いたお声は、今後の記事の質向上に役立たせて頂きますので、お気軽に
コメント欄にてご投稿ください。Twitterでもご意見を受け付けております。
皆様のメッセージをお待ちしております。

52
53
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
52
53

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?