42 ポイント 投稿者 xguru 2025-02-03 | 5件のコメント | WhatsAppで共有
  • 数時間かけて自分専用のAI画像モデルを訓練し、自分で撮ったような写真を作れるか試してみたプロジェクト
    • 例: 「スーパーマン」の仮装をした自分の写真を生成
  • 試した理由: 面白そうだったこと、子どもたちと一緒に遊ぶのに良さそうだったこと、カスタムモデルや高度なAIの部分をもう少し学べること
  • 12〜18か月前ならこの作業はかなり複雑だったが、今ではとても簡単になっている
  • 2時間以内にモデルを作って欲しい画像を得られ、要点は適切なツールを素早く見極めることだった

モデル/訓練パターンの選択

  • 必要な要素
    • ベースモデル(base model)
    • 訓練/ファインチューニング手法
    • 訓練データセット(自分の写真を数枚など)
  • 多くのAIはStable Diffusionを勧めているが、Pieter Levelsが使っていたFluxモデルのほうが性能が良いとのことで、Fluxを選択
    • 完全に最新のSOTAモデルではないが、十分に優秀
  • 訓練手法にはLoRA(Low-Rank Adaptation)を使用
    • モデル全体を再学習するのではなく、特定の「マジックワード」に結び付いた部分だけを訓練する
    • 例: 「czue」のような珍しい単語をモデルに学習させ、プロンプトでその単語を使うと、そのデータセットの特徴が反映される

訓練セットの作成

  • 学習対象の写真を複数枚(10〜15枚ほど)用意する必要がある
    • 表情、背景、照明、角度などに多様性があるほど良い
    • 1枚の写真には1人だけ写っているのが望ましい
  • 訓練時にはテキスト説明が必要で、ここにマジックワードを含める必要がある
    • 例: "a photo of czue on the beach, wearing a blue shirt"
  • ただし最近のツールは自動で画像キャプションを生成してくれるため、自分で説明を入力しなくてもよい

モデルの訓練

  • 最初はローカルで訓練するつもりだったが、GPUとRAMが足りず難しかった
  • GPUクラウドサーバー上で直接コードを動かすこともできるが、最終的にはReplicateを使った
    • GPUレンタルサービスで、すでに用意されたレシピをそのまま使える
  • 今回はostris/flux-dev-lora-trainerレシピを使用
    • Replicateアカウントを作成し、請求情報を設定する必要がある
  • 主なパラメータ
    • input_images: 訓練用写真(zip)
    • trigger_word: マジックワード 例) "czue"
    • hf_repo_id, hf_token: Hugging Faceのリポジトリ/トークン
    • autocaption_prefix: 自動生成キャプションの先頭に付ける文言 (例: "A photo of czue,")

Hugging Faceにモデルを保存

  • Hugging Faceはモデルを保存・共有するためのプラットフォーム
  • Replicateでも学習済みモデルをどこかに保存してくれるが、Hugging Faceに上げると他のツールと連携しやすい
  • アカウントとモデルを作成した後、hf_repo_idを渡す
  • 学習が終わると、"lora.safetensors"という大きなファイル(約180MB)がHugging Faceにアップロードされる

モデルで画像を生成

  • 学習が終わったら、モデルにテキストを入力して画像を作る推論(inference)を行う
  • ローカルで直接試すこともできるが、ここでもReplicateを使った
    • lucataco/flux-dev-lorahf_loraフィールドを設定するだけでよい
      • publicなHugging FaceリポジトリID、またはReplicateにアップロードされた学習済みモデルのリンク
  • 例: "a photo of czue surfing" と入力すると、次のようにサーフィンをしている自分の画像が得られる

プログラムからモデルを実行

  • さまざまなプロンプトを試し、結果を自動保存したいなら、API呼び出し方式が便利
  • 以下のPythonスクリプトを例として作成した (全コードはGithubにある)
    # /// script  
    # requires-python = ">=3.12"  
    # dependencies = [  
    #     "replicate",  
    # ]  
    # ///  
    import argparse  
    import os  
    import re  
    import replicate  
    import uuid  
    
    DEFAULT_MODEL = "czue/me-v1"  
    DEFAULT_COUNT = 1  
    
    def get_input(prompt, model=DEFAULT_MODEL, count=DEFAULT_COUNT):  
        return {  
            "prompt": prompt,  
            "hf_lora": model,  
            "num_outputs": count  
        }  
    
    def main():  
        parser = argparse.ArgumentParser()  
        parser.add_argument("prompt", help="Prompt for the photo")  
        parser.add_argument("--model", default=DEFAULT_MODEL,  
                          help="Model to use (default: %(default)s)")  
        parser.add_argument("--count", default=DEFAULT_COUNT,  
                          help="Number of photos to generate (default: %(default)s)", type=int)  
        args = parser.parse_args()  
    
        input = get_input(args.prompt, args.model, args.count)  
        output = replicate.run(  
            "lucataco/flux-dev-lora:091495765fa5ef2725a175a57b276ec30dc9d39c22d30410f2ede68a3eab66b3",  
            input=input  
        )  
    
        output_dir = "output"  
        os.makedirs(output_dir, exist_ok=True)  
    
        prompt_slug = "-".join(args.prompt.split(" ")[-3:])  
        prompt_slug = re.sub(r'[^a-zA-Z0-9\-]', '', prompt_slug).lower()  
    
        for index, item in enumerate(output):  
            file_id = uuid.uuid4().hex[:5]  
            output_path = os.path.join(output_dir, f"{prompt_slug}-{file_id}.webp")  
            with open(output_path, "wb") as file:  
                file.write(item.read())  
                print(f"Saved photo {output_path}")  
    
    if __name__ == "__main__":  
        main()  
    
  • 使用例
    uv run main.py "a photo of czue, a 40 year old man, writing a blog post" \  
     --model="czue/me-v1" \  
     --count=4  
    

結果

  • モデルの性能にはばらつきがある
    • 人物の特徴をかなり似せて捉えることもあるが、時には別人を生成してしまうこともある
    • 特定の年齢や性別などを追加でプロンプトに明示すると、より正確になる
  • たとえば、"a photo of czue, a 40 year old man, writing a blog post" は比較的一貫した画像を生成した
  • 一方で、"a photo of czue writing a blog post" は結果のばらつきがかなり大きかった
  • 別の人物を一緒に入れると、顔が混ざるなどの問題が起きる
    • バラク・オバマと一緒にいる写真を作ろうとしたところ、自分の顔の一部がオバマ側に反映され、その逆も起きた
  • それでも十分に面白く有用で、子どもたちと一緒にいろいろ試すことができた

コスト

  • 無料ではないが、そこまで高くもない
  • 自分と子どもたちを合わせて3つのモデルを学習したが、それぞれ約$2.50だった
  • 画像生成は1枚あたり約$0.03なので、30枚作っても$1ほど
  • 実験全体で使った金額は$10未満で、思ったより負担が小さく満足だった
  • AIモデルの訓練と画像生成に興味があるなら、思ったより簡単に試せるので挑戦してみる価値はある

5件のコメント

 
cladio 2025-02-04

面白半分でやってみましたが、本当に簡単ですね。
(https://www.youtube.com/watch?v=sNpQ9ULDMoo)
あれこれ作ってみて、しばらく笑っていました……

 
botplaysdice 2025-02-04

結局、私たちは死ぬ前に、自分のように訓練されたモデルをネットワークにアップロードして死のうとするのではないでしょうか? 生存本能のように……。それは『私』ではありませんが。

 
sollscherr 2025-02-03

技術を見て、ある小説を思い出したので紹介します。イ・ユリ作家の短編集『ビスッパンウル・ポン』に収録されている「クロノス」という短編なのですが、人間を、つまり自分のデータを保存して学習するAIを扱った内容です。あのコメントの猫のように。認知症になった母親が、症状がさらに悪化する前にそれを使います。そして子どもたちは葛藤します。慰められもするし、罪悪感も覚えます。この技術や、またあの猫の話に関心のある方なら、ぜひ一度読んでみてください。

 
xguru 2025-02-03

Hacker Newsのあるコメントが目を引きます

  • 愛していた亡き猫のためにこうしてみた。結果には満足したけれど、ある瞬間ふと、自分がやっていることにぞっとした
    • 大きなビジネスになりそう。私はおそらく何十万通ものメール、テキスト、チャットなどを送ってきたはずで、愛する人のコミュニケーションのコーパスを学習させて、その人が亡くなった後も「その人」とチャットできるようにするのは十分に可能だ
    • 父が亡くなった後、父の声でこれをやって、LLMを支えるアシスタントと会話できる機能を設定し、父の声や話し方で返答するようにした。とても奇妙な形で対処し、悲しんでいた時期で、結局、自分がしていることは本当におかしいと感じた
    • これは ブラック・ミラーの「Be Right Back」エピソード に似ている

情報提供的なコメントも一つ

  • Fluxの場合、テキストエンコーダーにははるかに多くの機能があり、より意味があり包括的な文章でプロンプトできる
    • したがって、Stable Diffusionで見られた従来のカンマ区切りの簡潔なフレーズは減らしてよい
  • また、学習画像にも同じ作業を行う必要がある。モデルに「私」として記憶してほしくないすべてのもの(していること、着ている服、一緒にいる人、アクセサリーなど)にキャプションを付けるとよい
 
humblebee 2025-02-03

「十分に発達した科学技術は魔法と見分けがつかない - Arthur C. Clarke」

ほんの2年前まではSF映画でしか見られないようなことでしたが、私たちは本当に魔法が現実になる瞬間をリアルタイムで目撃していますね 😳