3
4

動画生成AIは、4次元重みテンソルニューラルネットワークが効くのかな。

Last updated at Posted at 2024-08-17

72ad17ba-1b72-4f7a-a9ff-2688057e6f78.png

ショートストーリー: 動画生成AIと4次元テンソル

東京の夜は、人工の光が溢れる中で、篠田という名のプログラマーが深い思索に耽っていた。彼は、AI分野の最前線で働くエンジニアであり、最近話題になっている動画生成AIのプロジェクトに取り組んでいた。しかし、篠田はその限界に直面していた。

現在の動画生成AIの多くは、単純な2次元の画像フレームを入力し、次のフレームを予測するという方法に依存していた。だが、篠田は気づいていた。このアプローチでは、動画の動きや時間の流れを捉えることができず、生成される映像はどこか不自然であった。

「どうすればいい…」篠田は自分に問いかけた。動画の中の時間的な動き、空間的な変化、それらをすべてニューラルネットワークに学習させるには、もっと複雑な方法が必要だった。

彼はコーディングの手を止め、考えた。入力データが2次元である限り、AIは時間の流れを無視してしまう。画像は単なる静止画であり、動きの連続性を捉えることはできない。しかし、もし入力データそのものを3次元、例えば3x3x3のテンソルとして扱うことができれば…?

「これだ…このアイデアだ…!」篠田は思わず口に出した。

彼のひらめきはこうだった。動画を生成するためには、入力データも時間と空間を含む3次元の情報を持たせるべきだ。そして、そのデータを学習するニューラルネットワークは、さらに複雑な4次元の重み行列を持つべきだということだ。例えば、3x3x3の入力テンソルに対して、3x3x3x3の4次元重みテンソルを用いることで、AIは時間的な動きと空間情報の両方を学習することができる。

篠田は再びキーボードを叩き始めた。彼がコードに組み込んだのは、3x3x3のテンソル形式のデータを使い、それに対して4次元の重みテンソルを掛け算するという計算だった。これにより、AIは画像の時間的な動きや空間的な変化を同時に捉えることができるようになるはずだ。

「これで、画像の時間的な動き、空間情報、それらすべてをニューラルネットワークに取り込むことができる…!」篠田は自信を持って呟いた。

コードが完成し、プログラムを実行する。画面に映し出されたのは、これまでのどのAIにも生成できなかった、自然な流れのある映像だった。静止画ではなく、時間と共に変化する情報がそこにはあった。

篠田は満足げに画面を見つめた。東京の夜はまだ続いていたが、彼の中では新たな一歩を踏み出した感覚があった。この4次元の重みテンソルによる学習が、動画生成AIの未来を切り開く鍵になると信じていた。

「よし、これでいこう。」

篠田は深夜の静けさの中で、次なる挑戦に向けて心を新たにした。

実行結果。

3x3x3 テンソル:
[[[0 7 0]
[6 3 6]
[3 1 3]]

[[1 2 9]
[2 8 9]
[4 1 3]]

[[5 9 8]
[4 6 5]
[9 0 1]]]

3x3x3x3 重みテンソル:
[[[[5 0 3]
[0 0 1]
[0 3 1]]

[[4 6 4]
[2 7 1]
[8 0 2]]

[[7 5 9]
[5 0 6]
[2 2 0]]]

[[[7 7 6]
[0 1 3]
[7 4 3]]

[[1 1 2]
[6 6 7]
[9 8 5]]

[[2 7 4]
[5 9 3]
[3 0 8]]]

[[[4 0 4]
[4 0 7]
[6 7 7]]

[[4 7 3]
[3 0 1]
[6 1 4]]

[[4 1 9]
[7 8 5]
[5 6 6]]]]

出力。掛け算結果 (3x3x3):
[[[ 0. 0. 7.]
[ 78. 57. 39.]
[ 32. 21. 33.]]

[[ 70. 45. 39.]
[131. 122. 105.]
[ 22. 37. 43.]]

[[104. 56. 139.]
[ 64. 33. 38.]
[ 41. 15. 87.]]]

コードの説明:
tensor_3x3x3 は、ランダムな整数で初期化された3x3x3のテンソルです。
tensor_3x3x3x3 は、同様にランダムな整数で初期化された3x3x3x3の4次元テンソルです。
np.matmul を使用して、テンソルの各スライス(3x3x3テンソル)に対して4次元テンソルの各スライス(3x3行列)との掛け算を行い、その結果を3x3x3のテンソルとして保存します。
最後に、結果を出力します。
この方法では、3x3x3テンソルと3x3x3x3テンソルの掛け算結果が3x3x3の形で得られます。

import numpy as np

# 3x3x3 テンソルの定義
tensor_3x3x3 = np.random.randint(0, 10, size=(3, 3, 3))

# 3x3x3x3 の4次元テンソルの定義
tensor_3x3x3x3 = np.random.randint(0, 10, size=(3, 3, 3, 3))

# 3x3x3 テンソルと 3x3x3x3 テンソルの掛け算
# テンソルの各スライス (3x3x3) と4次元テンソルの各スライス (3x3) を掛け算
result = np.zeros((3, 3, 3))
for i in range(tensor_3x3x3.shape[0]):
    for j in range(tensor_3x3x3.shape[1]):
        result[i, j] = np.matmul(tensor_3x3x3[i, j], tensor_3x3x3x3[i, j])

# 3x3x3 テンソルの出力
print("3x3x3 テンソル:")
print(tensor_3x3x3)

# 3x3x3x3 テンソルの出力
print("\n3x3x3x3 テンソル:")
print(tensor_3x3x3x3)

# 掛け算結果 (3x3x3)
print("\n掛け算結果 (3x3x3):")
print(result)

4次元重みテンソルニューラルネットのコード。勾配効果法(勾配降下法)を用いて重み行列を調整し、出力が目標のテンソルに近づくように最適化します。

説明:

初期化:
tensor_3x3x3 はランダムに生成された3×3×3のテンソルです。
weights は学習するべき重み行列で、ランダムに初期化されます。

勾配降下法:
エポックごとに、出力テンソルを計算し、損失(二乗誤差)を計算します。
損失の勾配に基づいて重み行列を更新します。

結果:
生成された出力テンソル final_output を表示します。また、ターゲットテンソル target_tensor と比較します。

このコードは、3×3×3のテンソルに対して、ターゲットテンソルに近づくように4次元重み行列を調整します。

import numpy as np
import matplotlib.pyplot as plt

# 目標テンソル(ターゲットとして使用)と入力テンソルを0〜1に正規化
target_tensor = np.random.randint(0, 10, size=(3, 3, 3)) / 10.0
tensor_3x3x3 = np.random.randint(0, 10, size=(3, 3, 3)) / 10.0

# Xavier 初期化による初期重み行列 (3x3x3x3) の設定
weights = np.random.randn(3, 3, 3, 3) * np.sqrt(2 / (3 * 3))

# 学習率の設定(低めに設定)
learning_rate = 0.001

# 損失関数(二乗誤差)
def compute_loss(output, target):
    return np.sum((output - target) ** 2)

# 勾配降下法
def gradient_descent(weights, tensor_3x3x3, target_tensor, epochs=1000):
    loss_history = []  # ロスの履歴を保存するリスト

    for epoch in range(epochs):
        # フォワードパス: 出力テンソルの計算
        output_tensor = np.zeros((3, 3, 3))
        for i in range(tensor_3x3x3.shape[0]):
            for j in range(tensor_3x3x3.shape[1]):
                output_tensor[i, j] = np.matmul(tensor_3x3x3[i, j], weights[i, j])
        
        # 損失の計算
        loss = compute_loss(output_tensor, target_tensor)
        loss_history.append(loss)  # ロスを保存
        
        # 勾配の計算
        grad = np.zeros_like(weights)
        for i in range(tensor_3x3x3.shape[0]):
            for j in range(tensor_3x3x3.shape[1]):
                grad[i, j] = 2 * (output_tensor[i, j] - target_tensor[i, j]) @ tensor_3x3x3[i, j].T
        
        # 重みの更新
        weights -= learning_rate * grad
        
        # 進捗表示
        if epoch % 100 == 0:
            print(f"Epoch {epoch}, Loss: {loss:.4f}")
    
    return loss_history, output_tensor

# 勾配降下法で重みを調整し、ロス履歴と最終出力テンソルを取得
loss_history, final_output = gradient_descent(weights, tensor_3x3x3, target_tensor)

# 結果表示
print("入力テンソル (3x3x3):")
print(tensor_3x3x3)

print("\nターゲットテンソル (3x3x3):")
print(target_tensor)

print("\n最終出力テンソル (3x3x3):")
print(final_output)

# ロスのプロット
plt.plot(loss_history)
plt.title("Loss over Epochs")
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.grid(True)
plt.show()

Epoch 0, Loss: 16.0042
Epoch 100, Loss: 8.4750
Epoch 200, Loss: 6.5205
Epoch 300, Loss: 5.8329
Epoch 400, Loss: 5.5308
Epoch 500, Loss: 5.3761
Epoch 600, Loss: 5.2865
Epoch 700, Loss: 5.2282
Epoch 800, Loss: 5.1863
Epoch 900, Loss: 5.1537
入力テンソル (3x3x3):
[[[0.8 0.4 0. ]
[0. 0.4 0.8]
[0.4 0. 0.3]]

[[0.2 0.6 0.6]
[0.3 0.6 0.4]
[0.2 0.7 0.7]]

[[0.7 0.3 0.5]
[0.9 0.9 0.4]
[0. 0.1 0.4]]]

ターゲットテンソル (3x3x3):
[[[0.4 0.5 0.8]
[0.7 0.7 0. ]
[0.4 0.8 0.3]]

[[0.4 0.1 0. ]
[0.5 0.7 0. ]
[0.3 0.7 0.9]]

[[0.6 0.1 0.9]
[0.6 0.6 0.5]
[0.8 0.1 0.6]]]

最終出力テンソル (3x3x3):
[[[ 0.26462924 0.64546716 0.87369719]
[ 0.08685159 0.92546592 -0.12074095]
[ 0.22047038 0.26063709 0.31113747]]

[[-0.02417184 0.02001659 0.20883196]
[ 0.32381949 0.82544844 -0.11223038]
[ 0.58783041 0.20271183 1.30094645]]

[[ 0.69386326 0.75495885 0.35070586]
[ 0.82189325 -0.15679363 1.70308904]
[ 0.18894787 0.56085089 0.22217362]]]

image.png

3
4
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
3
4