Sora dari OpenAI, Difusi Video Stabil dari Stability AI, dan banyak model teks-ke-video lainnya yang telah keluar atau akan muncul di masa depan adalah beberapa tren AI terpopuler di tahun 2024, mengikuti model bahasa besar (LLM). Di blog ini, kami akan membuat model teks-ke-video skala kecil dari awal . Kami akan memasukkan perintah teks, dan model terlatih kami akan menghasilkan video berdasarkan perintah tersebut. Blog ini akan mencakup semuanya mulai dari memahami konsep teoretis hingga mengkodekan keseluruhan arsitektur dan menghasilkan hasil akhir.
Karena saya tidak memiliki GPU yang mewah, saya telah membuat kode arsitektur skala kecil. Berikut perbandingan waktu yang diperlukan untuk melatih model pada prosesor yang berbeda:
Video Pelatihan | zaman | CPU | GPU A10 | GPU T4 |
---|---|---|---|---|
10K | 30 | lebih dari 3 jam | 1 jam | 1 jam 42m |
30K | 30 | lebih dari 6 jam | 1 jam 30 | 2 jam 30 |
100K | 30 | - | 3-4 jam | 5-6 jam |
Menjalankan pada CPU jelas akan membutuhkan waktu lebih lama untuk melatih modelnya. Jika Anda perlu menguji perubahan kode dengan cepat dan melihat hasilnya, CPU bukanlah pilihan terbaik. Saya merekomendasikan penggunaan GPU T4 dari Colab atau Kaggle untuk pelatihan yang lebih efisien dan lebih cepat.
Berikut ini tautan blog yang memandu Anda tentang cara membuat Difusi Stabil dari awal: Mengkodekan Difusi Stabil dari Awal
Kami akan mengikuti pendekatan serupa dengan pembelajaran mesin tradisional atau model pembelajaran mendalam yang melatih kumpulan data dan kemudian menguji data yang tidak terlihat. Dalam konteks teks-ke-video, katakanlah kita memiliki kumpulan data pelatihan 100 ribu video tentang anjing yang mengambil bola dan kucing yang mengejar tikus. Kami akan melatih model kami untuk menghasilkan video kucing yang mengambil bola atau anjing yang mengejar tikus.
Meskipun kumpulan data pelatihan tersebut tersedia dengan mudah di internet, daya komputasi yang dibutuhkan sangat tinggi. Oleh karena itu, kami akan bekerja dengan kumpulan data video objek bergerak yang dihasilkan dari kode Python.
Kami akan menggunakan arsitektur GAN (Generative Adversarial Networks) untuk membuat model kami, bukan model difusi yang digunakan OpenAI Sora. Saya mencoba menggunakan model difusi, namun gagal karena kebutuhan memori, yang berada di luar kapasitas saya. Sebaliknya, GAN lebih mudah dan cepat untuk dilatih dan diuji.
Kami akan menggunakan OOP (Pemrograman Berorientasi Objek), jadi Anda harus memiliki pemahaman dasar tentangnya bersama dengan jaringan saraf. Pengetahuan tentang GAN (Generative Adversarial Networks) tidak wajib, karena kami akan membahas arsitekturnya di sini.
Topik | Link |
---|---|
OOP | Tautan Video |
Teori Jaringan Syaraf Tiruan | Tautan Video |
Arsitektur GAN | Tautan Video |
Dasar-dasar piton | Tautan Video |
Memahami arsitektur GAN penting karena sebagian besar arsitektur kita bergantung padanya. Mari kita jelajahi apa itu, komponen-komponennya, dan banyak lagi.
Generative Adversarial Network (GAN) adalah model pembelajaran mendalam di mana dua jaringan saraf bersaing: yang satu membuat data baru (seperti gambar atau musik) dari kumpulan data tertentu, dan yang lainnya mencoba mengetahui apakah data tersebut asli atau palsu. Proses ini berlanjut hingga data yang dihasilkan tidak dapat dibedakan dengan aslinya.
Hasilkan Gambar : GAN membuat gambar realistis dari perintah teks atau memodifikasi gambar yang ada, seperti meningkatkan resolusi atau menambahkan warna pada foto hitam putih.
Augmentasi Data : Mereka menghasilkan data sintetis untuk melatih model pembelajaran mesin lainnya, seperti membuat data transaksi penipuan untuk sistem deteksi penipuan.
Lengkapi Informasi yang Hilang : GAN dapat mengisi data yang hilang, seperti menghasilkan gambar bawah permukaan dari peta medan untuk aplikasi energi.
Menghasilkan Model 3D : Mereka mengubah gambar 2D menjadi model 3D, berguna dalam bidang seperti perawatan kesehatan untuk membuat gambar organ yang realistis untuk perencanaan bedah.
Ini terdiri dari dua jaringan saraf dalam: generator dan diskriminator . Jaringan-jaringan ini berlatih bersama dalam pengaturan yang berlawanan, di mana salah satu jaringan menghasilkan data baru dan jaringan lainnya mengevaluasi apakah data tersebut asli atau palsu.
Berikut ikhtisar sederhana tentang cara kerja GAN:
Analisis Set Pelatihan : Generator menganalisis set pelatihan untuk mengidentifikasi atribut data, sedangkan diskriminator secara independen menganalisis data yang sama untuk mempelajari atributnya.
Modifikasi Data : Generator menambahkan noise (perubahan acak) ke beberapa atribut data.
Pengiriman Data : Data yang dimodifikasi kemudian diteruskan ke diskriminator.
Perhitungan Probabilitas : Diskriminator menghitung probabilitas bahwa data yang dihasilkan berasal dari kumpulan data asli.
Putaran Umpan Balik : Diskriminator memberikan umpan balik ke generator, memandunya untuk mengurangi gangguan acak di siklus berikutnya.
Pelatihan Adversarial : Generator mencoba memaksimalkan kesalahan diskriminator, sedangkan diskriminator mencoba meminimalkan kesalahannya sendiri. Melalui banyak pengulangan pelatihan, kedua jaringan meningkat dan berkembang.
Keadaan Keseimbangan : Pelatihan berlanjut hingga diskriminator tidak dapat lagi membedakan antara data nyata dan data sintesis, yang menunjukkan bahwa generator telah berhasil belajar menghasilkan data yang realistis. Sampai disini proses pelatihan telah selesai.
gambar dari panduan aws
Mari kita jelaskan model GAN dengan contoh terjemahan gambar-ke-gambar, dengan fokus pada modifikasi wajah manusia.
Gambar Masukan : Gambar masukan berupa gambar nyata wajah manusia.
Modifikasi Atribut : Generator memodifikasi atribut wajah, seperti menambahkan kacamata hitam pada mata.
Gambar yang Dihasilkan : Generator membuat sekumpulan gambar dengan tambahan kacamata hitam.
Tugas Diskriminator : Diskriminator menerima campuran gambar nyata (orang berkacamata hitam) dan gambar yang dihasilkan (wajah yang ditambahkan kacamata hitam).
Evaluasi : Diskriminator mencoba membedakan antara gambar nyata dan gambar yang dihasilkan.
Putaran Umpan Balik : Jika diskriminator mengidentifikasi gambar palsu dengan benar, generator menyesuaikan parameternya untuk menghasilkan gambar yang lebih meyakinkan. Jika generator berhasil mengelabui diskriminator, diskriminator memperbarui parameternya untuk meningkatkan pendeteksiannya.
Melalui proses permusuhan ini, kedua jaringan terus berkembang. Generator menjadi lebih baik dalam menciptakan gambar yang realistis, dan diskriminator menjadi lebih baik dalam mengidentifikasi palsu sampai keseimbangan tercapai, dimana diskriminator tidak dapat lagi membedakan antara gambar nyata dan gambar yang dihasilkan. Sampai di sini, GAN telah berhasil belajar menghasilkan modifikasi yang realistis.
Menginstal pustaka yang diperlukan adalah langkah pertama dalam membangun model teks-ke-video.
pip install -r requirements.txt
Kita akan bekerja dengan berbagai pustaka Python, mari kita impor.
# Operating System module for interacting with the operating system
import os
# Module for generating random numbers
import random
# Module for numerical operations
import numpy as np
# OpenCV library for image processing
import cv2
# Python Imaging Library for image processing
from PIL import Image , ImageDraw , ImageFont
# PyTorch library for deep learning
import torch
# Dataset class for creating custom datasets in PyTorch
from torch . utils . data import Dataset
# Module for image transformations
import torchvision . transforms as transforms
# Neural network module in PyTorch
import torch . nn as nn
# Optimization algorithms in PyTorch
import torch . optim as optim
# Function for padding sequences in PyTorch
from torch . nn . utils . rnn import pad_sequence
# Function for saving images in PyTorch
from torchvision . utils import save_image
# Module for plotting graphs and images
import matplotlib . pyplot as plt
# Module for displaying rich content in IPython environments
from IPython . display import clear_output , display , HTML
# Module for encoding and decoding binary data to text
import base64
Sekarang setelah kita mengimpor semua perpustakaan, langkah selanjutnya adalah menentukan data pelatihan yang akan kita gunakan untuk melatih arsitektur GAN.
Kita perlu memiliki setidaknya 10.000 video sebagai data pelatihan. Mengapa? Ya, karena saya menguji dengan angka yang lebih kecil dan hasilnya sangat buruk, praktis tidak ada yang terlihat. Pertanyaan besar berikutnya adalah: tentang apa video-video ini? Kumpulan data video pelatihan kami terdiri dari lingkaran yang bergerak ke arah berbeda dengan gerakan berbeda. Jadi, mari kita kodekan dan buat 10.000 video untuk melihat tampilannya.
# Create a directory named 'training_dataset'
os . makedirs ( 'training_dataset' , exist_ok = True )
# Define the number of videos to generate for the dataset
num_videos = 10000
# Define the number of frames per video (1 Second Video)
frames_per_video = 10
# Define the size of each image in the dataset
img_size = ( 64 , 64 )
# Define the size of the shapes (Circle)
shape_size = 10
setelah mengatur beberapa parameter dasar selanjutnya kita perlu menentukan teks petunjuk dari kumpulan data pelatihan kita berdasarkan video pelatihan mana yang akan dihasilkan.
# Define text prompts and corresponding movements for circles
prompts_and_movements = [
( "circle moving down" , "circle" , "down" ), # Move circle downward
( "circle moving left" , "circle" , "left" ), # Move circle leftward
( "circle moving right" , "circle" , "right" ), # Move circle rightward
( "circle moving diagonally up-right" , "circle" , "diagonal_up_right" ), # Move circle diagonally up-right
( "circle moving diagonally down-left" , "circle" , "diagonal_down_left" ), # Move circle diagonally down-left
( "circle moving diagonally up-left" , "circle" , "diagonal_up_left" ), # Move circle diagonally up-left
( "circle moving diagonally down-right" , "circle" , "diagonal_down_right" ), # Move circle diagonally down-right
( "circle rotating clockwise" , "circle" , "rotate_clockwise" ), # Rotate circle clockwise
( "circle rotating counter-clockwise" , "circle" , "rotate_counter_clockwise" ), # Rotate circle counter-clockwise
( "circle shrinking" , "circle" , "shrink" ), # Shrink circle
( "circle expanding" , "circle" , "expand" ), # Expand circle
( "circle bouncing vertically" , "circle" , "bounce_vertical" ), # Bounce circle vertically
( "circle bouncing horizontally" , "circle" , "bounce_horizontal" ), # Bounce circle horizontally
( "circle zigzagging vertically" , "circle" , "zigzag_vertical" ), # Zigzag circle vertically
( "circle zigzagging horizontally" , "circle" , "zigzag_horizontal" ), # Zigzag circle horizontally
( "circle moving up-left" , "circle" , "up_left" ), # Move circle up-left
( "circle moving down-right" , "circle" , "down_right" ), # Move circle down-right
( "circle moving down-left" , "circle" , "down_left" ), # Move circle down-left
]
Kami telah menentukan beberapa gerakan lingkaran kami menggunakan petunjuk ini. Sekarang, kita perlu membuat kode beberapa persamaan matematika untuk memindahkan lingkaran itu berdasarkan petunjuknya.
# defining function to create image with moving shape
def create_image_with_moving_shape ( size , frame_num , shape , direction ):
# Create a new RGB image with specified size and white background
img = Image . new ( 'RGB' , size , color = ( 255 , 255 , 255 ))
# Create a drawing context for the image
draw = ImageDraw . Draw ( img )
# Calculate the center coordinates of the image
center_x , center_y = size [ 0 ] // 2 , size [ 1 ] // 2
# Initialize position with center for all movements
position = ( center_x , center_y )
# Define a dictionary mapping directions to their respective position adjustments or image transformations
direction_map = {
# Adjust position downwards based on frame number
"down" : ( 0 , frame_num * 5 % size [ 1 ]),
# Adjust position to the left based on frame number
"left" : ( - frame_num * 5 % size [ 0 ], 0 ),
# Adjust position to the right based on frame number
"right" : ( frame_num * 5 % size [ 0 ], 0 ),
# Adjust position diagonally up and to the right
"diagonal_up_right" : ( frame_num * 5 % size [ 0 ], - frame_num * 5 % size [ 1 ]),
# Adjust position diagonally down and to the left
"diagonal_down_left" : ( - frame_num * 5 % size [ 0 ], frame_num * 5 % size [ 1 ]),
# Adjust position diagonally up and to the left
"diagonal_up_left" : ( - frame_num * 5 % size [ 0 ], - frame_num * 5 % size [ 1 ]),
# Adjust position diagonally down and to the right
"diagonal_down_right" : ( frame_num * 5 % size [ 0 ], frame_num * 5 % size [ 1 ]),
# Rotate the image clockwise based on frame number
"rotate_clockwise" : img . rotate ( frame_num * 10 % 360 , center = ( center_x , center_y ), fillcolor = ( 255 , 255 , 255 )),
# Rotate the image counter-clockwise based on frame number
"rotate_counter_clockwise" : img . rotate ( - frame_num * 10 % 360 , center = ( center_x , center_y ), fillcolor = ( 255 , 255 , 255 )),
# Adjust position for a bouncing effect vertically
"bounce_vertical" : ( 0 , center_y - abs ( frame_num * 5 % size [ 1 ] - center_y )),
# Adjust position for a bouncing effect horizontally
"bounce_horizontal" : ( center_x - abs ( frame_num * 5 % size [ 0 ] - center_x ), 0 ),
# Adjust position for a zigzag effect vertically
"zigzag_vertical" : ( 0 , center_y - frame_num * 5 % size [ 1 ]) if frame_num % 2 == 0 else ( 0 , center_y + frame_num * 5 % size [ 1 ]),
# Adjust position for a zigzag effect horizontally
"zigzag_horizontal" : ( center_x - frame_num * 5 % size [ 0 ], center_y ) if frame_num % 2 == 0 else ( center_x + frame_num * 5 % size [ 0 ], center_y ),
# Adjust position upwards and to the right based on frame number
"up_right" : ( frame_num * 5 % size [ 0 ], - frame_num * 5 % size [ 1 ]),
# Adjust position upwards and to the left based on frame number
"up_left" : ( - frame_num * 5 % size [ 0 ], - frame_num * 5 % size [ 1 ]),
# Adjust position downwards and to the right based on frame number
"down_right" : ( frame_num * 5 % size [ 0 ], frame_num * 5 % size [ 1 ]),
# Adjust position downwards and to the left based on frame number
"down_left" : ( - frame_num * 5 % size [ 0 ], frame_num * 5 % size [ 1 ])
}
# Check if direction is in the direction map
if direction in direction_map :
# Check if the direction maps to a position adjustment
if isinstance ( direction_map [ direction ], tuple ):
# Update position based on the adjustment
position = tuple ( np . add ( position , direction_map [ direction ]))
else : # If the direction maps to an image transformation
# Update the image based on the transformation
img = direction_map [ direction ]
# Return the image as a numpy array
return np . array ( img )
Fungsi di atas digunakan untuk menggerakkan lingkaran kita untuk setiap frame berdasarkan arah yang dipilih. Kita hanya perlu menjalankan loop di atasnya hingga berapa kali video untuk menghasilkan semua video.
# Iterate over the number of videos to generate
for i in range ( num_videos ):
# Randomly choose a prompt and movement from the predefined list
prompt , shape , direction = random . choice ( prompts_and_movements )
# Create a directory for the current video
video_dir = f'training_dataset/video_ { i } '
os . makedirs ( video_dir , exist_ok = True )
# Write the chosen prompt to a text file in the video directory
with open ( f' { video_dir } /prompt.txt' , 'w' ) as f :
f . write ( prompt )
# Generate frames for the current video
for frame_num in range ( frames_per_video ):
# Create an image with a moving shape based on the current frame number, shape, and direction
img = create_image_with_moving_shape ( img_size , frame_num , shape , direction )
# Save the generated image as a PNG file in the video directory
cv2 . imwrite ( f' { video_dir } /frame_ { frame_num } .png' , img )
Setelah Anda menjalankan kode di atas, itu akan menghasilkan seluruh kumpulan data pelatihan kami. Berikut adalah struktur file dataset pelatihan kami.
Setiap folder video pelatihan berisi bingkainya beserta prompt teksnya. Mari kita lihat contoh dataset pelatihan kita.
Dalam dataset pelatihan kami, kami belum menyertakan gerakan lingkaran yang bergerak ke atas lalu ke kanan . Kami akan menggunakan ini sebagai perintah pengujian untuk mengevaluasi model terlatih kami pada data yang tidak terlihat.
Satu hal penting lagi yang perlu diperhatikan adalah bahwa data pelatihan kami berisi banyak sampel di mana objek bergerak menjauh dari tempat kejadian atau muncul sebagian di depan kamera, mirip dengan apa yang kami amati dalam video demo OpenAI Sora.
Alasan memasukkan sampel tersebut ke dalam data pelatihan kami adalah untuk menguji apakah model kami dapat mempertahankan konsistensi saat lingkaran memasuki pemandangan dari paling sudut tanpa merusak bentuknya.
Sekarang setelah data pelatihan kita dibuat, kita perlu mengonversi video pelatihan menjadi tensor, yang merupakan tipe data utama yang digunakan dalam kerangka pembelajaran mendalam seperti PyTorch. Selain itu, melakukan transformasi seperti normalisasi membantu meningkatkan konvergensi dan stabilitas arsitektur pelatihan dengan menskalakan data ke rentang yang lebih kecil.
Kita harus mengkodekan kelas kumpulan data untuk tugas teks-ke-video, yang dapat membaca bingkai video dan perintah teks terkait dari direktori kumpulan data pelatihan, membuatnya tersedia untuk digunakan di PyTorch.
# Define a dataset class inheriting from torch.utils.data.Dataset
class TextToVideoDataset ( Dataset ):
def __init__ ( self , root_dir , transform = None ):
# Initialize the dataset with root directory and optional transform
self . root_dir = root_dir
self . transform = transform
# List all subdirectories in the root directory
self . video_dirs = [ os . path . join ( root_dir , d ) for d in os . listdir ( root_dir ) if os . path . isdir ( os . path . join ( root_dir , d ))]
# Initialize lists to store frame paths and corresponding prompts
self . frame_paths = []
self . prompts = []
# Loop through each video directory
for video_dir in self . video_dirs :
# List all PNG files in the video directory and store their paths
frames = [ os . path . join ( video_dir , f ) for f in os . listdir ( video_dir ) if f . endswith ( '.png' )]
self . frame_paths . extend ( frames )
# Read the prompt text file in the video directory and store its content
with open ( os . path . join ( video_dir , 'prompt.txt' ), 'r' ) as f :
prompt = f . read (). strip ()
# Repeat the prompt for each frame in the video and store in prompts list
self . prompts . extend ([ prompt ] * len ( frames ))
# Return the total number of samples in the dataset
def __len__ ( self ):
return len ( self . frame_paths )
# Retrieve a sample from the dataset given an index
def __getitem__ ( self , idx ):
# Get the path of the frame corresponding to the given index
frame_path = self . frame_paths [ idx ]
# Open the image using PIL (Python Imaging Library)
image = Image . open ( frame_path )
# Get the prompt corresponding to the given index
prompt = self . prompts [ idx ]
# Apply transformation if specified
if self . transform :
image = self . transform ( image )
# Return the transformed image and the prompt
return image , prompt
Sebelum melanjutkan ke pengkodean arsitektur, kita perlu menormalkan data pelatihan kita. Kami akan menggunakan ukuran batch 16 dan mengacak data untuk menghasilkan lebih banyak keacakan.
# Define a set of transformations to be applied to the data
transform = transforms . Compose ([
transforms . ToTensor (), # Convert PIL Image or numpy.ndarray to tensor
transforms . Normalize (( 0.5 ,), ( 0.5 ,)) # Normalize image with mean and standard deviation
])
# Load the dataset using the defined transform
dataset = TextToVideoDataset ( root_dir = 'training_dataset' , transform = transform )
# Create a dataloader to iterate over the dataset
dataloader = torch . utils . data . DataLoader ( dataset , batch_size = 16 , shuffle = True )
Anda mungkin pernah melihat dalam arsitektur transformator di mana titik awalnya adalah mengubah input teks kita menjadi penyematan untuk diproses lebih lanjut dalam perhatian multi-kepala, serupa di sini kita harus mengkodekan lapisan penyematan teks yang menjadi dasar pelatihan arsitektur GAN pada data penyematan kita. dan tensor gambar.
# Define a class for text embedding
class TextEmbedding ( nn . Module ):
# Constructor method with vocab_size and embed_size parameters
def __init__ ( self , vocab_size , embed_size ):
# Call the superclass constructor
super ( TextEmbedding , self ). __init__ ()
# Initialize embedding layer
self . embedding = nn . Embedding ( vocab_size , embed_size )
# Define the forward pass method
def forward ( self , x ):
# Return embedded representation of input
return self . embedding ( x )
Ukuran kosakata akan didasarkan pada data pelatihan kami, yang akan kami hitung nanti. Ukuran penyematannya adalah 10. Jika bekerja dengan kumpulan data yang lebih besar, Anda juga dapat menggunakan model penyematan pilihan Anda sendiri yang tersedia di Hugging Face.
Sekarang kita sudah mengetahui apa yang dilakukan generator di GAN, mari kita kodekan lapisan ini dan pahami isinya.
class Generator ( nn . Module ):
def __init__ ( self , text_embed_size ):
super ( Generator , self ). __init__ ()
# Fully connected layer that takes noise and text embedding as input
self . fc1 = nn . Linear ( 100 + text_embed_size , 256 * 8 * 8 )
# Transposed convolutional layers to upsample the input
self . deconv1 = nn . ConvTranspose2d ( 256 , 128 , 4 , 2 , 1 )
self . deconv2 = nn . ConvTranspose2d ( 128 , 64 , 4 , 2 , 1 )
self . deconv3 = nn . ConvTranspose2d ( 64 , 3 , 4 , 2 , 1 ) # Output has 3 channels for RGB images
# Activation functions
self . relu = nn . ReLU ( True ) # ReLU activation function
self . tanh = nn . Tanh () # Tanh activation function for final output
def forward ( self , noise , text_embed ):
# Concatenate noise and text embedding along the channel dimension
x = torch . cat (( noise , text_embed ), dim = 1 )
# Fully connected layer followed by reshaping to 4D tensor
x = self . fc1 ( x ). view ( - 1 , 256 , 8 , 8 )
# Upsampling through transposed convolution layers with ReLU activation
x = self . relu ( self . deconv1 ( x ))
x = self . relu ( self . deconv2 ( x ))
# Final layer with Tanh activation to ensure output values are between -1 and 1 (for images)
x