はじめに
先日発表された Amazon の最新 AI 、 Amazon Nova
早速、 Elixir の Livebook から Bedrock 経由で呼んでみましょう
2024年12月10日現在の情報です
実装したノートブックはこちら
Amazon Nova で出来ること
Amazon Nova には以下の 5 種類が存在します
- Amazon Nova Micro: 安い、速い、そこそこの精度、テキストのみ理解
- Amazon Nova Lite: 中くらいの価格、速度、精度、テキストも画像も動画も理解
- Amazon Nova Pro: お高いけど最高位の速度と精度、テキストも画像も動画も理解
- Amazon Nova Canvas: テキストと画像から画像を生成
- Amazon Nova Reel: テキストと画像から動画を生成
どのモデルも 200 言語以上に対応していて、日本語にも対応しています(特に最適化されている 15 言語にも含まれる)
Amazon Nova の料金
公式の価格表を英語表示にしないと出てきません
リージョンも現在は us-east-1 しか対応していません
モデル | 1,000 入力トークン | 1,000 出力トークン |
---|---|---|
Amazon Nova Micro | $0.000035 | $0.00014 |
Amazon Nova Lite | $0.00006 | $0.00024 |
Amazon Nova Pro | $0.0008 | $0.0032 |
モデル | 画像解像度 | 標準品質 | プレミアム品質 |
---|---|---|---|
Amazon Nova Canvas | 1024 x 1024 以下 | $0.04 | $0.06 |
Amazon Nova Canvas | 2048 x 2048 以下 | $0.06 | $0.08 |
モデル | 動画解像度 | フレームレート | 動画生成 1 秒あたり |
---|---|---|---|
Amazon Nova Reel | 720p | 24 fps | $0.08 |
ちなみに同じ us-east-1 の Claude
モデル | 1,000 入力トークン | 1,000 出力トークン |
---|---|---|
Claude 3.5 Sonnet | $0.003 | $0.015 |
Claude 3.5 Haiku | $0.0008 | $0.004 |
Claude 3 Opus | $0.015 | $0.075 |
Claude 3 Haiku | $0.00025 | $0.00125 |
Claude 3 Sonnet | $0.003 | $0.015 |
OpenAI API
モデル | 1,000 入力トークン | 1,000 出力トークン |
---|---|---|
gpt-4o-mini | $0.00015 | $0.0006 |
gpt-4o | $0.0025 | $0.01 |
o1-mini | $0.003 | $0.012 |
o1-preview | $0.015 | $0.06 |
モデル | 画像解像度 | 標準品質 | プレミアム品質 |
---|---|---|---|
DALL·E 3 | 1024 x 1024 | $0.04 | $0.08 |
DALL·E 3 | 1024×1792、1792×1024 | $0.08 | $0.12 |
Nova が他と比べて低コストなことは間違いなさそう
また、プロンプトキャッシュが利用された場合、さらに安く利用できます
Livebook とは
Elixir のコードをブラウザで実行出来るツールです
例えば以下の画像のように、黒い枠(セル)の中にコードを書いて実行すると、そのすぐ下に結果が表示されます
公式サイトからインストーラーをダウンロードすれば、Windows でも macOS でもすぐに始められます
しかも、Webアプリの実装、デプロイから IoT まで何でもできてしまうので、これ一つあれば全てが賄えます
事前準備
以下の準備を済ませてください
- AWS のアカウント作成
- AWS の認証情報発行(アクセスキーIDとシークレットアクセスキー)
- us-east-1 リージョンでの Amazon Nova モデル利用申請
- Livebook のインストール
セットアップ
Livebook で新しいトートブックを開き、セットアップセル(Notebook dependencies and setup
と書いてある枠)に以下のコードを貼り付けてください
Mix.install([
{:aws, "~> 1.0"},
{:hackney, "~> 1.20"},
{:req, "~> 0.5"},
{:kino, "~> 0.14"}
])
セルの左上 Reconnect and setup
ボタンをクリックすると、処理に必要な依存モジュールがインストールされます
AWS クライアントの準備
AWS 認証情報用の入力エリアを用意します
access_key_id_input = Kino.Input.password("ACCESS_KEY_ID")
secret_access_key_input = Kino.Input.password("SECRET_ACCESS_KEY")
region_input = Kino.Input.text("REGION")
[
access_key_id_input,
secret_access_key_input,
region_input
]
|> Kino.Layout.grid(columns: 3)
表示された入力エリアにアクセスキーIDとシークレットアクセスキー、リージョンを入力してください
Nova を使うため、リージョンは us-east-1
固定です
入力した情報を使って AWS クライアントを作成します
client =
AWS.Client.create(
Kino.Input.read(access_key_id_input),
Kino.Input.read(secret_access_key_input),
Kino.Input.read(region_input)
)
Nova モデル一覧取得
Bedrock の List foundation models API を呼び出し、名前が "Nova" で始まるモデルを抽出します
取得した結果は見やすく表形式にします
models =
client
|> AWS.Bedrock.list_foundation_models()
|> elem(1)
|> Map.get("modelSummaries")
|> Enum.filter(fn model ->
String.starts_with?(model["modelName"], "Nova") and String.ends_with?(model["modelId"], ":0")
end)
keys =
[
"providerName",
"modelName",
"modelId",
"inputModalities",
"outputModalities"
]
Kino.DataTable.new(models, keys: keys)
Nova Micro
まずは最安モデル Nova Micro を使ってみましょう
上記の表を参照し、モデル ID を指定します
model_id = "amazon.nova-micro-v1:0"
テキスト理解
コード生成用のプロンプトを用意します
code_generation_input = """
配列から指定数の要素を選択する組み合わせを全て生成するコードを Elixir で生成してください
コメントを日本語で適切につけてください
"""
以下のコードを実行し、 Bedrock 経由で Nova Micro を呼び出します
results =
client
|> AWS.BedrockRuntime.invoke_model(
model_id,
%{
"accept" => "application/json",
"contentType" => "application/json",
"messages" => [
%{
"role" => "user",
"content" => [
%{
"text" => code_generation_input
}
]
}
]
}
)
|> elem(1)
|> Map.get("output")
実行結果
%{
"message" => %{
"content" => [
%{
"text" => "Elixirで配列から指定数の要素を選択する..."
}
],
"role" => "assistant"
}
}
このままだと読みにくいので、文章をマークダウンとして表示します
results
|> Map.get("message")
|> Map.get("content")
|> hd()
|> Map.get("text")
|> Kino.Markdown.new()
実行結果
ちなみにこのコードは Enum.reverse() |> [result]
の部分でエラーが発生します
Elixir は Nova の情報源に不足しているようです
同じことを今後繰り返すので、テキスト理解の処理を関数化しておきます
invoke_nova_text = fn input, model_id ->
{:ok, res, _} =
client
|> AWS.BedrockRuntime.invoke_model(
model_id,
%{
"accept" => "application/json",
"contentType" => "application/json",
"messages" => [
%{
"role" => "user",
"content" => [%{"text" => input}]
}
]
},
recv_timeout: 60_000
)
res
|> Map.get("output")
|> Map.get("message")
|> Map.get("content")
|> hd()
|> Map.get("text")
end
日本のことをどれくらい理解しているか試してみます
japanese_input = """
大分県について、以下の項目を教えてください
- 有名な戦国武将
- 有名な史跡
- 有名な郷土料理
- ご当地キャラクター
- 特徴的な方言
"""
japanese_input
|> invoke_nova_text.("amazon.nova-micro-v1:0")
|> Kino.Markdown.new()
実行結果
大分県について、以下の項目を詳しくご紹介します。
## 有名な戦国武将
大分県は戦国時代にも重要な役割を果たしました。特に有名な戦国武将としては、以下が挙げられます。
- 立花道雪:立花氏の当主で、戦国武将として有名です。立花山城(現大分県宇佐市)を拠点に、豊前国(現在の福岡県の一部)と豊後国(大分県)で活躍しました。立花道雪は弓の名手としても知られ、その武勇伝は今も語り継がれています。
## 有名な史跡
大分県には多くの歴史的な場所がありますが、特に有名な史跡は以下の通りです。
- 宇佐神宮:日本の八幡神を祀る神社で、国の重要文化財に指定されています。全国の八幡宮の総本宮とされており、古くから多くの参拝客を集めています。
- 別府温泉:世界的に有名な温泉地で、古くから湯治の場として栄えています。特に戦国時代には、大友氏や立花氏などの戦国大名たちが湯治に訪れました。
## 有名な郷土料理
大分県には独特の郷土料理が多くあります。
- からしれんこん:大分県を代表する郷土料理の一つで、辛味大根(からし)とレンコンを味噌ベースの濃厚なタレで煮込んだ料理です。辛味大根は別名「大分みそ」とも呼ばれます。
- 天重:大分県を代表する郷土料理の一つで、揚げた天ぷらを細かく切り、天重汁(みそベースの汁)に浸して食べます。具材は、レンコン、サツマイモ、タケノコ、ニンジン、キクラゲなど多様です。
## ご当地キャラクター
大分県には、以下のご当地キャラクターがあります。
- べっぷー:別府市のご当地キャラクターです。別府温泉のイメージキャラクターとして、温泉街の広報活動やイベントなどで活躍しています。
- おおいたちゃん:大分県の県民性や自然、文化を表現した公式キャラクターです。県庁のイメージキャラクターとして、県内外での広報活動に多用されています。
## 特徴的な方言
大分県は方言が豊富で、特に以下の特徴的な方言が挙げられます。
- 豊後方言(ほうごほうげん):大分県の南部(旧豊後国)で話される方言で、標準語とは異なる独特のアクセントや表現があります。例えば、「食べる」を「たべる」ではなく「たびる」と発音することがあります。
- 豊前方言(ほうぜんほうげん):大分県の北部(旧豊前国)で話される方言で、福岡県の北部方言と似ています。例えば、「うまい」を「めー」と発音することがあります。
これらの項目を通じて、大分県の文化や歴史を知ることができます。
立花道雪は実在するものの、かなりマニアックなところを突いてきました
史跡は正解しているものの、郷土料理以降はデタラメです
「めじろん」「べっぴょん」「ニータン」は流石に知らないか、、、
Nova Lite
中くらいの Nova Lite を使ってみましょう
テキスト理解
まずは Micro と同じテキスト理解をやってみます
code_generation_input
|> invoke_nova_text.("amazon.nova-lite-v1:0")
|> Kino.Markdown.new()
実行結果
Enum.combination
と言う関数が存在しないので、このコードもエラーになります
japanese_input
|> invoke_nova_text.("amazon.nova-lite-v1:0")
|> Kino.Markdown.new()
実行結果
もちろんです!大分県について以下項目を詳しく説明します。
## 有名な戦国武将
- 大友宗麟(おおとも そうりん):大友氏の第19代当主で、大分県における戦国大名として有名です。宗麟はキリスト教(カトリック)を広め、キリシタン大名としても知られています。
## 有名な史跡
- 宇佐神宮:日本三大神宮の一つで、全国に約8,000社ある八幡宮の総本社です。
- 富貴寺:宗麟が建立した寺院で、国の重要文化財に指定されています。
- 耶馬渓:日本で最も美しい渓谷の一つとして知られ、国の特別名勝に指定されています。
- 別府地獄めぐり:温泉で有名な別府には、地獄と呼ばれる8つの温泉があり、それぞれ独特の景観と特徴を持っています。
## 有名な郷土料理
- ふぐ料理:大分県はふぐ料理で有名で、特に冬季にふぐを食べる習慣があります。
- もみじおろし:大分県が産地である馬肉をすり身にしてネギや唐辛子と和えた料理です。
- 豊後牛:大分県産の和牛で、その肉質の良さと柔らかさで知られています。
- 冷や汁:夏の季節限定の郷土料理で、豆腐や野菜を冷やして食べる料理です。
## ご当地キャラクター
- おおいたちゃん:大分県の公式キャラクターです。大分県の特産品や観光地を紹介する役割を担っています。
## 特徴的な方言
大分県の方言は大きく「北部方言」と「南部方言」に分かれます。
- 北部方言(中津・宇佐・豊後高田方言など):アクセントは東京式アクセントに近く、語彙や発音が異なる部分があります。
- 南部方言(大分市・別府・佐伯方言など):アクセントは京阪式アクセントに近く、語彙や発音が異なる部分があります。
例えば、「ありがとう」は北部では「おおきに」や「どうも」と言うことが多いです。南部では「ありがとう」が一般的ですが、方言のニュアンスや表現方法は地域によって異なります。
「もみじおろし」が大分とは聞いたことがないし、「冷や汁」は宮崎ですね
Micro よりは良くなりましたが、キャラクーと方言はデタラメです
画像理解
続いて画像理解用の関数を用意します
invoke_nova_image = fn input_text, image, model_id ->
{:ok, res, _} =
client
|> AWS.BedrockRuntime.invoke_model(
model_id,
%{
"accept" => "application/json",
"contentType" => "application/json",
"messages" => [
%{
"role" => "user",
"content" => [
%{
"image" => %{
"format" => "jpeg",
"source" => %{
"bytes" => Base.encode64(image)
}
}
},
%{
"text" => input_text
}
]
}
]
},
recv_timeout: 60_000
)
res
|> Map.get("output")
|> Map.get("message")
|> Map.get("content")
|> hd()
|> Map.get("text")
end
入力としては以前 Qiita にアップロードした画像を使います
image_url = "https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F1485835%2Fff6c4171-c83b-dc5b-afb3-fd94bc27498b.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=8cf5ad145f3859bb9901ab29b4f8efbf"
画像を WEB からダウンロードします
image =
image_url
|> Req.get!()
|> Map.get(:body)
実行結果
Nova Lite に説明してもらいましょう
"画像に写っているものを詳細に教えてください"
|> invoke_nova_image.(image, "amazon.nova-lite-v1:0")
|> Kino.Markdown.new()
実行結果
画像には、サンタクロースの工房をイメージした、クリスマスをテーマにしたイラストが描かれています。中央に、サンタクロースの手伝いをしている3人のエルフが描かれています。エルフたちは、それぞれが手に持っている用紙を読み、その用紙を箱に入れています。この箱には「PAGE SORTING」と書かれており、数字も表示されています。また、エルフたちは、サンタクロース用の手袋や靴を製造している工房にいるようにも見えます。背景には、クリスマスツリーやキャンディなどの装飾が施されています。また、画面には、エルフたちがいる部屋の天井に吊るされたクリスマスの飾り物がいくつか見られます。
かなり良く理解できているように思えます
「PAGE SORTING」の文字もちゃんと読めていますね
動画理解
続いて動画を理解してもらいます
動画は手元からアップロードします
video_input = Kino.Input.file("VIDEO")
表示されたファイル選択エリアに MP4 ファイルをドラッグ&ドロップします
今回アップロードしたのは以下の動画です
動画を読み込みます
video =
video_input
|> Kino.Input.read()
|> Map.get(:file_ref)
|> Kino.Input.file_path()
|> File.read!()
確認のため、動画を再生してみます
Kino.Video.new(video, :mp4)
動画理解用の関数を用意します
invoke_nova_video = fn input_text, video, model_id ->
{:ok, res, _} =
client
|> AWS.BedrockRuntime.invoke_model(
model_id,
%{
"accept" => "application/json",
"contentType" => "application/json",
"messages" => [
%{
"role" => "user",
"content" => [
%{
"video" => %{
"format" => "mp4",
"source" => %{
"bytes" => Base.encode64(video)
}
}
},
%{
"text" => input_text
}
]
}
]
},
recv_timeout: 600_000
)
res
|> Map.get("output")
|> Map.get("message")
|> Map.get("content")
|> hd()
|> Map.get("text")
end
Nova Lite に説明してもらいます
"動画に写っているものを説明してください"
|> invoke_nova_video.(video, "amazon.nova-lite-v1:0")
|> Kino.Markdown.new()
実行結果
The video shows a person working on a laptop with a colorful ribbon cable connected to a ribbon board. The laptop screen displays a graph, and the person is holding a box with Japanese text on it. The person is seen adjusting the ribbon cable, which has colored wires. The laptop is placed on a table, and there is a keyboard visible.
日本語で質問したのに英語で返ってきました
説明の内容としてはかなり正確に答えています
文書理解
続いて文書理解として、 PDF を読んでもらいます
中央区の ORIZURU WAVE というプロジェクトの PDF を与えてみましょう
pdf_url = "https://www.city.chuo.lg.jp/documents/4167/chirashi.pdf"
WEB から PDF をダウンロードします
pdf =
pdf_url
|> Req.get!()
|> Map.get(:body)
PDF 理解用の関数を用意し、実行します
invoke_nova_doc = fn input_text, doc, model_id ->
{:ok, res, _} =
client
|> AWS.BedrockRuntime.invoke_model(
model_id,
%{
"accept" => "application/json",
"contentType" => "application/json",
"messages" => [
%{
"role" => "user",
"content" => [
%{
"document" => %{
"format" => "pdf",
"name" => "計算書類",
"source" => %{
"bytes" => Base.encode64(doc)
}
}
},
%{
"text" => input_text
}
]
}
]
},
recv_timeout: 600_000
)
res
|> Map.get("output")
|> Map.get("message")
|> Map.get("content")
|> hd()
|> Map.get("text")
end
"何をきっかけとした取り組みですか"
|> invoke_nova_doc.(pdf, "amazon.nova-lite-v1:0")
|> Kino.Markdown.new()
実行結果
東京2020オリンピック・パラリンピック
端的な回答ですが、正解しています
Nova Pro
最高位モデルの Nova Pro を使ってみます
テキスト理解
code_generation_input
|> invoke_nova_text.("amazon.nova-pro-v1:0")
|> Kino.Markdown.new()
実行結果
残念ながら、 Lite と同じく Enum.combination
が存在しないため動きません
japanese_input
|> invoke_nova_text.("amazon.nova-pro-v1:0")
|> Kino.Markdown.new()
大分県に関する情報を以下にまとめます。
## 有名な戦国武将
- 大友宗麟:大友氏の当主で、戦国時代に九州で大きな勢力を持っていました。キリスト教に傾倒し、キリシタン大名としても知られています。
## 有名な史跡
- 宇佐神宮:全国の八幡宮の総本宮とされる由緒正しい神社です。
- 耶馬渓:大分と福岡にまたがる美しい渓谷で、多くの文人墨客に愛された風光明媚な場所です。
- 別府温泉:日本三大秘湯のひとつで、多くの温泉地が点在しています。
## 有名な郷土料理
- とり天:鶏肉の衣揚げで、大分県を代表するB級グルメです。
- だんご汁:麦味噌ベースの汁に Various ingredients such as dumplings, vegetables, and sometimes meat are added.
## ご当地キャラクター
- おおいたさん:大分県の魅力をPRする公式キャラクターで、温泉と湯布院の自然をイメージしたデザインです。
## 特徴的な方言
- 〜とぅ:「〜ところ」の意味で使われる。「こことぅ(ここところ)」など。
- ばってん:「しかし」の意味。九州地方全体でよく使われますが、大分でも使用されます。
以上が大分県に関する主な情報です。
かなりいい感じにはなりましたが、「だんご汁」の説明が途中から英語になっているのと、方言がやはり間違ってますね
「おおいたさん」は初めて聞きました
画像理解
"画像に写っているものを詳細に教えてください"
|> invoke_nova_image.(image, "amazon.nova-pro-v1:0")
|> Kino.Markdown.new()
実行結果
画像には、3人の小さなエルフが描かれている。彼らは、サンタクロースの帽子を被り、緑と赤のストライプの衣装を着ている。エルフたちは、クリスマスツリーや星などのクリスマスの装飾品が飾られた部屋で、机に向かって座っている。机の上には、数冊の本が積み重ねられており、エルフたちはその本を読んでいる。また、彼らは、小さな木製の箱の中に、さらに多くの本を整理している。部屋は、暖かい雰囲気で、窓からは、星や雪が降り注いでいる様子が描かれている。
おおよその理解は合っているものの、「本」ではないですね
動画理解
"動画に写っているものを説明してください"
|> invoke_nova_video.(video, "amazon.nova-pro-v1:0")
|> Kino.Markdown.new()
実行結果
動画には、ラップトップコンピューター、赤、黄、緑、青の4本のワイヤー、それらのワイヤーが接続されている小さな電子機器、そして箱が写っています。
動作は説明していないものの、日本語で返してくれました
文書理解
"何をきっかけとした取り組みですか"
|> invoke_nova_doc.(pdf, "amazon.nova-pro-v1:0")
|> Kino.Markdown.new()
実行結果
東京2020オリンピック・パラリンピック競技大会をきっかけとして、国際平和と文化交流を目的とした取り組みです。
しっかり文章で返してくれました
Nova Canvas
画像生成や画像加工ができる Nova Canvas を使ってみます
画像生成
画像生成用の関数を用意します
generate_image = fn input_text ->
{:ok, res, _} =
client
|> AWS.BedrockRuntime.invoke_model(
"amazon.nova-canvas-v1:0",
%{
"accept" => "application/json",
"contentType" => "application/json",
"taskType" => "TEXT_IMAGE",
"textToImageParams" => %{
"text" => input_text
}
},
recv_timeout: 600_000
)
res
|> Map.get("images")
|> hd()
|> Base.decode64!()
end
あえて、 AI が苦手な指示を出してみます
generate_image.("ラーメンを食べる人")
実行結果
禁断の両手箸!
やはり日本文化にはまだまだ疎いですね
背景削除
画像から背景を削除してみます
remove_image_background = fn image ->
{:ok, res, _} =
client
|> AWS.BedrockRuntime.invoke_model(
"amazon.nova-canvas-v1:0",
%{
"accept" => "application/json",
"contentType" => "application/json",
"taskType" => "BACKGROUND_REMOVAL",
"backgroundRemovalParams" => %{
"image" => Base.encode64(image)
}
},
recv_timeout: 600_000
)
res
|> Map.get("images")
|> hd()
|> Base.decode64!()
end
remove_image_background.(image)
実行結果
いいところまではいっていますが、やはりイラストだと難しいようですね
画像編集
テキストと画像の両方を与えて、画像の編集指示を出すこともできます
edit_image = fn input_text, image ->
{:ok, res, _} =
client
|> AWS.BedrockRuntime.invoke_model(
"amazon.nova-canvas-v1:0",
%{
"accept" => "application/json",
"contentType" => "application/json",
"taskType" => "TEXT_IMAGE",
"textToImageParams" => %{
"conditionImage" => Base.encode64(image),
"text" => input_text
}
},
recv_timeout: 600_000
)
res
|> Map.get("images")
|> hd()
|> Base.decode64!()
end
edit_image.("和風にして", image)
実行結果
着物だったり桜だったり、確かに和風になりました
Nova Reel
Nova Reel で動画を生成してみましょう
動画生成には数分間かかるため、呼び出しを非同期にします
generate_video = fn input_text, s3_uri ->
{:ok, res, _} =
client
|> AWS.BedrockRuntime.start_async_invoke(%{
"modelId" => "amazon.nova-reel-v1:0",
"modelInput" => %{
"taskType" => "TEXT_VIDEO",
"textToVideoParams" => %{
"text" => input_text
},
"videoGenerationConfig" => %{
"durationSeconds" => 6,
"fps" => 24,
"dimension" => "1280x720",
"seed" => 12
}
},
"outputDataConfig" => %{
"s3OutputDataConfig" => %{
"s3Uri" => s3_uri
}
}
})
res
end
テキスト入力で出力先を指定します
s3_uri_input = Kino.Input.text("S3_URI")
s3_uri = Kino.Input.read(s3_uri_input)
generate_video.("クリスマスにサンタがダンスする可愛いアニメーション", s3_uri)
以下のコードを何回か実行し、 Status が "Completed" になるまで待ちます
invokes =
client
|> AWS.BedrockRuntime.list_async_invokes()
|> elem(1)
|> Map.get("asyncInvokeSummaries")
動画生成中の実行結果
[
%{
"clientRequestToken" => "xxx",
"invocationArn" => "arn:aws:bedrock:us-east-1:999:async-invoke/xxx",
"lastModifiedTime" => "2024-12-10T10:15:20Z",
"modelArn" => "arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-reel-v1:0",
"outputDataConfig" => %{
"s3OutputDataConfig" => %{"s3Uri" => "s3://xxx/bedrock/xxx"}
},
"status" => "InProgress",
"submitTime" => "2024-12-10T10:15:03Z"
}
]
動画生成完了後の実行結果
[
%{
"clientRequestToken" => "xxx",
"endTime" => "2024-12-10T10:19:21Z",
"invocationArn" => "arn:aws:bedrock:us-east-1:999:async-invoke/xxx",
"lastModifiedTime" => "2024-12-10T10:19:22Z",
"modelArn" => "arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-reel-v1:0",
"outputDataConfig" => %{
"s3OutputDataConfig" => %{"s3Uri" => "s3://xxx/bedrock/xxx"}
},
"status" => "Completed",
"submitTime" => "2024-12-10T10:15:03Z"
}
]
実行結果から出力先のファイル名を取得します
video_url =
invokes
|> hd()
|> Map.get("outputDataConfig")
|> Map.get("s3OutputDataConfig")
|> Map.get("s3Uri")
|> Kernel.<>("/output.mp4")
URL からバケット名、オブジェクトキーを取得します
bucket = video_url |> String.split("/") |> Enum.at(2)
key = video_url |> String.split("/") |> Enum.slice(3..-1//1) |> Enum.join("/")
動画を S3 からダウンロードします
{:ok, %{"Body" => generated_video}, _} = AWS.S3.get_object(client, bucket, key)
動画を Livebook に表示してみます
Kino.Video.new(generated_video, :mp4)
静止画の動画化
画像とテキストを与えて動画を生成します
animate_image = fn input_text, image, s3_uri ->
{:ok, res, _} =
client
|> AWS.BedrockRuntime.start_async_invoke(%{
"modelId" => "amazon.nova-reel-v1:0",
"modelInput" => %{
"taskType" => "TEXT_VIDEO",
"textToVideoParams" => %{
"text" => input_text,
"images" => [
%{
"format" => "jpeg",
"source" => %{
"bytes" => Base.encode64(image)
}
}
]
},
"videoGenerationConfig" => %{
"durationSeconds" => 6,
"fps" => 24,
"dimension" => "1280x720",
"seed" => 12
}
},
"outputDataConfig" => %{
"s3OutputDataConfig" => %{
"s3Uri" => s3_uri
}
}
})
res
end
動画の元にする画像は以下の条件を満たしている必要があります
- 1280x720 のサイズであること
- アルファチャネルがないこと
該当するようなファイルを用意してアップロードしましょう
start_image_input = Kino.Input.image("IMAGE", format: :jpeg)
start_image =
start_image_input
|> Kino.Input.read()
|> Map.get(:file_ref)
|> Kino.Input.file_path()
|> File.read!()
animate_image.("ダンスさせてください", start_image, s3_uri)
完成するまで待ちます
invokes =
client
|> AWS.BedrockRuntime.list_async_invokes()
|> elem(1)
|> Map.get("asyncInvokeSummaries")
実行結果の動画を元の静止画と並べて確認します
video_url =
invokes
|> hd()
|> Map.get("outputDataConfig")
|> Map.get("s3OutputDataConfig")
|> Map.get("s3Uri")
|> Kernel.<>("/output.mp4")
bucket = video_url |> String.split("/") |> Enum.at(2)
key = video_url |> String.split("/") |> Enum.slice(3..-1//1) |> Enum.join("/")
{:ok, %{"Body" => generated_video}, _} = AWS.S3.get_object(client, bucket, key)
Kino.Layout.grid([
start_image,
Kino.Video.new(generated_video, :mp4)
])
顔だけの画像にダンスさせたのが悪いかもしれませんが、中々怖い動画になりました
おまけ
ChatGPT に同じ質問をしてみた
ChatGPT の 4o に同じテキストの質問をしてみました
ChatGPT の場合、内部的に回答の検証等もしているのでフェアではないです
あくまでも参考程度にしてください
コード生成の回答は完璧です(ちゃんと動きました)
「めじろん」や「りゅうきゅう」も知っているし、方言の回答も完璧でした
インターネット検索はオフにしていたはずではあるんですが、ネットから情報を拾っているっぽい
やはりエージェントを使って情報収集するのが最強ということですかね
「ラーメンを食べる人」は、まあまあですかね
片手がだらんと下がっているのが気になります
Vidu で動画生成してみた
Vidu というサービスに静止画から動画を生成してもらいました
カッコよすぎ、、、
まとめ
Livebook から Amazon Nova のモデルを呼び出し、動画生成など色々なことができました
画像生成と動画生成に関しては、一昔前のクオリティというのが正直な感想ですね、、、
ただ、価格は確かに安いので、用途によっては使えそうです
ファインチューニングして蒸留すれば、もっと実用的なモデルにできそうですね