LoginSignup
3
3

動画生成AIモデルが流行ってますね。 連続する5枚、10枚、15枚の画像を一つの「動画」、あるいは「時間のスナップショット」として捉え、それらを高次元のベクトルに圧縮し、再構成するコード。

Last updated at Posted at 2024-06-19

bc1d5adf-92a3-4889-b336-29dc2c3cffb1.png

タイトル: "タイムライン・リフレクター: 5フレームの冒険"

近未来の2080年、科学者たちの間で新たなプロジェクトが勃興していた。「タイムライン・リフレクター」プロジェクトと名付けられたこの研究は、連続する5枚、10枚、15枚の画像を一つの「動画」、あるいは「時間のスナップショット」として捉え、それらを高次元のベクトルに圧縮し、再構成する技術を開発しようとするものだった。

プロジェクトの主導者である科学者、エレン博士は、この技術を「ビジュアルタイムマシン」と呼び、彼女の目標は、過去の瞬間を再現し、未来の瞬間を予測することだった。

エレン博士は、オートエンコーダというニューラルネットワークモデルを使用して、これらの画像をベクトルに圧縮し、それを元の画像に再構成するプロセスを繰り返し行った。彼女は、トレーニング中に定期的に元の画像と再構成された画像を比較し、その違いを微調整することで、再構成の精度を向上させていった。

ある日、エレン博士は、15枚の画像からなる「動画」を圧縮し、それを再構成した。その結果、彼女は驚愕の事実に遭遇した。再構成された画像は、元の画像と全く同じだった。しかし、驚くべきことに、その画像には、元のシーンには存在しなかった、まるで「未来」から来たと思われる新たな要素が含まれていた。

この発見は、エレン博士とそのチームに新たな可能性を開いた。彼らは、「ビジュアルタイムマシン」が単なる画像の再構成を超えて、未来を予測するツールとして機能することを認識した。

この「タイムライン・リフレクター」プロジェクトは、科学界に衝撃を与え、新たな視点から時間と空間を捉えるための革新的なアプローチを提供した。エレン博士と彼女のチームは、その技術を用いて、過去を振り返り、未来を予測する新たな道を開拓し続けた。

この物語は、AIとディープラーニングの進化が、我々が時間と空間を理解する方法をどのように変えていくかを示す。エレン博士と彼女の「タイムライン・リフレクター」プロジェクトは、科学と技術の進歩が、我々の未来をどのように形成していくかを象徴する物語となった。

前回のあらすじ。

5枚,10枚、15枚の画像を一つのベクトルとしてモデルに入力し、その5枚,10枚、15枚の画像ベクトルを出力するオートエンコーダを構築しています。さらに、出力されたベクトルを元の画像にリシェイプし、トレーニング中に定期的に元の画像と再構成された画像をプロットします。5枚,10枚、15枚の画像を一つの動画とし見なします。

つまりN枚のフレームからなる動画と次のタイムの動画のペアを訓練データとしています。次のタイムの動画を生成するようなモデルになります。

Training with image_set_size=5 and weight_size=500

image.png
image.png
image.png
image.png
image.png

Training with image_set_size=10 and weight_size=500

image.png
image.png
image.png
image.png

Training with image_set_size=15 and weight_size=500

image.png
image.png
image.png
image.png

image.png

このコードは、5枚,10枚、15枚 の画像を一つのベクトルとしてモデルに入力し、その5枚,10枚、15枚分の画像ベクトルを出力するオートエンコーダを構築しています。さらに、出力されたベクトルを元の画像にリシェイプし、トレーニング中に定期的に元の画像と再構成された画像をプロットします。

import numpy as np
import tensorflow as tf
from tensorflow.keras import layers, models, callbacks, initializers
import matplotlib.pyplot as plt

# CIFAR-10データセットの読み込み
(x_train, y_train), (x_test, y_test) = tf.keras.datasets.cifar10.load_data()

# データの前処理(正規化)
x_train = x_train.astype('float32') / 255.0
x_test = x_test.astype('float32') / 255.0

num_samples = 200
epochs = 50
batch_size = 10

# 任意の枚数の画像をランダムに選択する関数
def select_random_images(x, num_images=5):
    indices = np.random.choice(len(x), num_images, replace=False)
    return x[indices]

# カスタムスプライン活性化関数
class SplineActivation(layers.Layer):
    def __init__(self, num_knots=10):
        super(SplineActivation, self).__init__()
        self.num_knots = num_knots
        self.knots = tf.constant(np.linspace(0, 1, num_knots), dtype=tf.float32)
        self.coefficients = self.add_weight(name='coefficients',
                                            shape=(num_knots,),
                                            initializer=initializers.RandomNormal(),
                                            trainable=True)

    def call(self, inputs):
        inputs_flat = tf.reshape(inputs, [-1])
        batch_size = tf.shape(inputs)[0]
        
        spline_values = []
        for i in range(self.num_knots - 1):
            mask = tf.logical_and(inputs_flat >= self.knots[i], inputs_flat < self.knots[i+1])
            spline_value = self.coefficients[i] + (inputs_flat - self.knots[i]) * (self.coefficients[i+1] - self.coefficients[i]) / (self.knots[i+1] - self.knots[i])
            spline_values.append(tf.where(mask, spline_value, 0.0))
        
        spline_output = tf.add_n(spline_values)
        spline_output = tf.reshape(spline_output, tf.shape(inputs))
        return spline_output

# モデルの定義
def create_model(input_shape, weight_size):
    input = layers.Input(shape=input_shape)
    x = layers.Flatten()(input)
    x = SplineActivation()(x)
    encoded = layers.Dense(weight_size, activation='relu')(x)
    
    encoder = models.Model(input, encoded, name='encoder')

    encoded_input = layers.Input(shape=(weight_size,))
    x = layers.Dense(np.prod(input_shape), activation='sigmoid')(encoded_input)
    decoded = layers.Reshape(input_shape)(x)

    decoder = models.Model(encoded_input, decoded, name='decoder')
    
    autoencoder = models.Model(input, decoder(encoder(input)), name='autoencoder')
    return autoencoder

# カスタムコールバックの定義
class PlotReconstructedImages(callbacks.Callback):
    def __init__(self, num_images):
        super().__init__()
        self.num_images = num_images

    def on_epoch_end(self, epoch, logs=None):
        if (epoch + 1) % 5 == 0 or epoch == 0:
            self.plot_images(epoch)

    def plot_images(self, epoch):
        indices = np.random.choice(len(train_data), 1, replace=False)
        sampled_input = train_data[indices]
        sampled_target = target_data[indices]
        decoded_imgs = self.model.predict(sampled_input)
        decoded_imgs = decoded_imgs.reshape(-1, self.num_images, 32, 32, 3)

        # プロットする画像の数
        num_images = self.num_images
        plt.figure(figsize=(20, 4))
        
        for i in range(num_images):
            # 入力画像
            ax = plt.subplot(3, num_images, i + 1)
            plt.imshow(sampled_input[0].reshape(num_images, 32, 32, 3)[i])
            plt.axis("off")
            plt.title("Input")

            # ターゲット画像
            ax = plt.subplot(3, num_images, i + 1 + num_images)
            plt.imshow(sampled_target[0].reshape(num_images, 32, 32, 3)[i])
            plt.axis("off")
            plt.title("Target")

            # 再構成画像
            ax = plt.subplot(3, num_images, i + 1 + 2 * num_images)
            plt.imshow(decoded_imgs[0][i])
            plt.axis("off")
            plt.title("Reconstructed")

        plt.suptitle(f'Epoch {epoch+1}')
        plt.show()

# weight_sizeのリスト
weight_sizes = [500]
image_set_sizes = [5, 10, 15]
losses = {}

# 各weight_sizeとimage_set_sizeでモデルを訓練
for image_set_size in image_set_sizes:
    train_data = np.array([select_random_images(x_train, num_images=image_set_size) for _ in range(num_samples)])
    train_data = train_data.reshape(num_samples, -1)  # image_set_size枚分の画像を一つのベクトルに

    target_data = np.array([select_random_images(x_train, num_images=image_set_size) for _ in range(num_samples)])
    target_data = target_data.reshape(num_samples, -1)  # image_set_size枚分の画像を一つのベクトルに

    for weight_size in weight_sizes:
        print(f"Training with image_set_size={image_set_size} and weight_size={weight_size}")
        autoencoder = create_model((32 * 32 * 3 * image_set_size,), weight_size)
        autoencoder.compile(optimizer='adam', loss='mse')
        
        plot_callback = PlotReconstructedImages(num_images=image_set_size)
        
        history = autoencoder.fit(train_data, target_data,
                                  epochs=epochs,
                                  batch_size=batch_size,
                                  shuffle=True,
                                  verbose=0,  # 損失をプリントしない
                                  callbacks=[plot_callback])
        
        # 訓練ロスを保存
        losses[(image_set_size, weight_size)] = history.history['loss']

# トレーニングロスのプロット
plt.figure(figsize=(15, 10))
for image_set_size in image_set_sizes:
    for weight_size in weight_sizes:
        plt.plot(losses[(image_set_size, weight_size)], label=f'image_set_size={image_set_size}, weight_size={weight_size}')
plt.xlabel('Epoch')
plt.ylabel('Training Loss')
plt.legend()
plt.title('Training Loss for Different Image Set Sizes and Weight Sizes with Spline Activation')
plt.show()

解説

データの準備
CIFAR-10データセットの読み込みと前処理

tf.keras.datasets.cifar10.load_data()を使ってCIFAR-10データセットをロード。
データを0〜1の範囲に正規化 (astype('float32') / 255.0)。
ランダムに5枚の画像を選択する関数

select_random_images(x, num_images=5):与えられたデータから指定された数の画像をランダムに選択。
訓練用データセットの作成

train_data:200個のサンプルを作成し、それぞれ5枚の画像を含む。
モデルの定義
カスタムスプライン活性化関数

SplineActivationクラス:スプライン関数を用いたカスタム活性化関数を実装。
callメソッドで、入力データに対してスプライン補間を行い、活性化値を計算。
オートエンコーダモデルの作成

create_model(input_shape, weight_size):入力画像を圧縮し、再構成するオートエンコーダを定義。
encoder:入力を圧縮する部分。
decoder:圧縮された表現を元の画像に再構成する部分。
autoencoder:encoderとdecoderを繋いだ完全なオートエンコーダ。
カスタムコールバック
画像のプロット
PlotReconstructedImagesクラス:エポック終了時に再構成された画像をプロットするカスタムコールバック。
モデルの訓練とプロット
異なる重みサイズでモデルを訓練

weight_sizesリスト:異なるサイズの潜在変数(圧縮表現)を試す。
各重みサイズについて、モデルを訓練し、訓練損失を記録。
訓練損失のプロット

訓練損失をエポックごとにプロットし、異なる重みサイズの比較を行う。

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