برنامه نویسی

فراتر از تصور: پتانسیل هوش مصنوعی در هنر دیجیتال

سفری هیجان انگیز به قلمرو هنر دیجیتال را آغاز کنید، جایی که تخیل با فناوری روبرو می شود!
آیا تا به حال آرزو داشته اید که مستقیماً از صفحات رمان مورد علاقه خود به شخصیت ها جان بدهید یا
با کلیک یک دکمه موجودات خارق العاده ای را به ذهن متبادر می کنید؟ معرفی دنیای خیره کننده سنتز متن به تصویر،
جایی که سازندگان جن با هوش مصنوعی کلمات شما را به شاهکارهای بصری جذاب تبدیل می کنند. با الهام از تلفیق هنر، داستان سرایی،
و یادگیری عمیق پیشرفته، این ابزار نوآورانه امکانات بی پایانی را برای توسعه دهندگان بازی، هنرمندان و ذهن های خلاق به طور یکسان باز می کند.
تصور کنید که برای بازی مستقل بعدی خود، اسپریت های بی نقص پیکسل می سازید، آواتارهای پویا برای دنیای مجازی طراحی می کنید.

بلوک 1: تنظیم مرحله – واردات و راه اندازی
به ماجراجویی خوش آمدید! قبل از شروع، باید ابزارهای قابل اعتماد خود را جمع آوری کنیم. در این بلوک،
ما کتابخانه های لازم را برای پردازش تصویر، پردازش متن، یادگیری عمیق، تجسم، و ثبت نام وارد می کنیم.

تلاش آغاز می شود

مشعل وارداتی
ما را وارد کنید
از کره زمین
سفری هیجان انگیز به قلمرو هنر دیجیتال را آغاز کنید، جایی که تخیل با فناوری روبرو می شود!
آیا تا به حال آرزو داشته اید که مستقیماً از صفحات رمان مورد علاقه خود به شخصیت ها جان بدهید یا
با کلیک یک دکمه موجودات خارق العاده ای را به ذهن متبادر می کنید؟ معرفی دنیای خیره کننده سنتز متن به تصویر،
جایی که سازندگان جن با هوش مصنوعی کلمات شما را به شاهکارهای بصری جذاب تبدیل می کنند. با الهام از تلفیق هنر، داستان سرایی،
و یادگیری عمیق پیشرفته، این ابزار نوآورانه امکانات بی پایانی را برای توسعه دهندگان بازی، هنرمندان و ذهن های خلاق به طور یکسان باز می کند.
تصور کنید که برای بازی مستقل بعدی خود، اسپریت های بی نقص پیکسل می سازید، آواتارهای پویا برای دنیای مجازی طراحی می کنید.

بلوک 1: تنظیم مرحله – واردات و راه اندازی
به ماجراجویی خوش آمدید! قبل از شروع، باید ابزارهای قابل اعتماد خود را جمع آوری کنیم. در این بلوک،
ما کتابخانه های لازم را برای پردازش تصویر، پردازش متن، یادگیری عمیق، تجسم، و ثبت نام وارد می کنیم.

تلاش آغاز می شود


import torch
import os
from glob import glob
from torch import nn, optim
from torch.utils.data import DataLoader, Dataset
from torchvision import models, transforms
from transformers import VisionEncoderDecoderModel, ViTImageProcessor, AutoModel, AutoTokenizer
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from rich import print as rp
import wandb

wandb.init(project="spritemaker", entity="goldenkooy")
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بعد چی؟؟

اکنون که ابزارهای خود را داریم، بیایید به بلوک بعدی برویم که در آن کلاس های رمزگذار متن و تصویر خود را تعریف می کنیم.

بلوک 2: رمزگذارها – متن و تصویر
در این بلوک، دو کلاس رمزگذار ایجاد خواهیم کرد: TextEncoder و ImageEncoder.
این کلاس ها وظیفه پردازش داده های متن و تصویر ما را بر عهده خواهند داشت.

رمزگذار متن:
با کلاس TextEncoder آشنا شوید که از یک مدل BERT از پیش آموزش دیده برای تبدیل توضیحات متنی به نمایش های عددی استفاده می کند.
TextEncoder را با نام مدل راه‌اندازی کنید (پیش‌فرض bert-base-uncased است).
از AutoTokenizer و AutoModel از ترانسفورماتورها برای بارگیری مدل BERT و توکنایزر از پیش آموزش دیده استفاده کنید.
مدل را روی حالت ارزیابی (.eval()) قرار دهید تا از به روز رسانی وزن در طول تمرین جلوگیری کنید.
متد encode_text را تعریف کنید که یک ورودی متن را می گیرد و آخرین حالت پنهان مدل BERT را برمی گرداند.


class TextEncoder:
    def __init__(self, model_name="bert-base-uncased"):
        self.tokenizer = AutoTokenizer.from_pretrained(model_name, cache_dir="./models")
        self.model = AutoModel.from_pretrained(model_name, cache_dir="./models")
        self.model.eval()

    def encode_text(self, text):
        with torch.no_grad():
            inputs = self.tokenizer(text, return_tensors="pt", padding=True, truncation=True)
            outputs = self.model(**inputs)
        return outputs.last_hidden_state[:, 0, :]
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

رمزگذار تصویر

به کلاس ImageEncoder سلام کنید، که از یک مدل ResNet از قبل آموزش دیده برای استخراج ویژگی ها از تصاویر استفاده می کند.
ImageEncoder را بدون آرگومان اولیه کنید.
یک مدل ResNet50 از پیش آموزش دیده را از روی مشعل بارگذاری کنید و آن را روی حالت ارزیابی (.eval()) قرار دهید تا از به روز رسانی وزن در طول تمرین جلوگیری کنید.
متد encode_image را تعریف کنید که یک مسیر تصویر را به عنوان ورودی می گیرد و ویژگی های استخراج شده را برمی گرداند.

class ImageEncoder:
    def __init__(self):
        self.model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2)
        self.model.eval()
        self.transform = transforms.Compose([
            transforms.Resize((224, 224)),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        ])

    def encode_image(self, image_path):
        image = Image.open(image_path).convert('RGB')
        image = self.transform(image)
        image = image.unsqueeze(0)  # Add batch dimension
        with torch.no_grad():
            features = self.model(image)
        return features
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بعد چی؟؟
اکنون که رمزگذارهای خود را داریم، بیایید به بلوک بعدی برویم که در آن یک کلاس داده برای ذخیره داده های تصویر و متن ایجاد می کنیم. گوش به زنگ باشید!

بلوک 3: مجموعه داده – Sprite و Text
در این بلوک، یک کلاس مجموعه داده به نام SpriteTextDataset ایجاد می کنیم که داده های تصویر و متن ما را ذخیره می کند.

کلاس مجموعه داده

کلاس SpriteTextDataset را با یک دایرکتوری تصویر، یک فهرست متن، یک رمزگذار تصویر و یک رمزگذار متن راه اندازی کنید.
تمام مسیرهای تصویر را از دایرکتوری تصویر با استفاده از glob بارگیری کنید.
مسیرهای تصویر را تکرار کنید و توضیحات را از دایرکتوری متن بارگیری کنید. هر تصویر را با توضیحات مربوطه جفت کنید.
را تعریف کنید لن روش برای برگرداندن تعداد کل تصاویر در مجموعه داده.
را تعریف کنید زمان بندی شده روشی برای برگرداندن یک تاپل حاوی مسیر تصویر و متن کدگذاری شده برای یک شاخص معین.

class SpriteTextDataset(Dataset):
    def __init__(self, image_dir, text_dir, image_encoder, text_encoder):
        self.image_encoder = image_encoder
        self.text_encoder = text_encoder
        self.data = []

        # Load all image paths
        image_paths = glob(os.path.join(image_dir, '*.png'))

        # Debug: print the found image paths
        rp(f"Found image paths: {image_paths}")

        # Load descriptions and pair them with images
        for image_path in image_paths:
            base_filename = os.path.splitext(os.path.basename(image_path))[0]
            text_path = os.path.join(text_dir, f"{base_filename}.txt")
            if os.path.exists(text_path):
                with open(text_path, 'r', encoding='utf-8') as file:
                    description = file.read().strip()
                    self.data.append((image_path, description))
            else:
                rp(f"Warning: No description file found for {image_path}")

        # Debug: print the dataset size
        rp(f"Dataset size: {len(self.data)}")

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        image_path, description = self.data[idx]
        image_features = self.image_encoder.encode_image(image_path)
        text_features = self.text_encoder.encode_text(description)
        return image_features, text_features
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بعد چی؟؟

اکنون که کلاس داده خود را داریم، بیایید به بلوک بعدی برویم، جایی که یک بارگذار داده ایجاد می کنیم تا مجموعه داده خود را به صورت دسته ای بارگذاری کنیم.
این به ما کمک می کند مدل خود را به طور موثر آموزش دهیم. گوش به زنگ باشید!

بلوک 4: بارگذار داده – Sprite و Text
در این بلوک، ما یک بارگذار داده ایجاد می کنیم تا مجموعه داده ما را به صورت دسته ای بارگیری کند. این به ما کمک می کند مدل خود را به طور موثر آموزش دهیم.

بارگذار داده

بارگذار داده را با مجموعه داده، اندازه دسته ای و تعداد کارگران خود راه اندازی کنید.
ویژگی مجموعه داده را برای ذخیره نمونه داده ما تعریف کنید.
برای ذخیره اندازه دسته، ویژگی batch_size را تعریف کنید.
برای ذخیره تعداد کارگران، صفت num_workers را تعریف کنید.
از کلاس DataLoader از torch.utils.data برای ایجاد یک نمونه بارگذار داده استفاده کنید.
ویژگی مجموعه داده را به نمونه مجموعه داده ما تنظیم کنید.
ویژگی batch_size را روی اندازه دسته تنظیم کنید.
صفت num_workers را روی تعداد کارگران تنظیم کنید.


class SpriteTextDataLoader:
    def __init__(self, dataset, batch_size, num_workers):
        self.dataset = dataset
        self.batch_size = batch_size
        self.num_workers = num_workers
        self.data_loader = DataLoader(dataset, batch_size=batch_size, num_workers=num_workers)

    def __iter__(self):
        return iter(self.data_loader)

    def __len__(self):
        return len(self.data_loader)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با استفاده از Data Loader

نمونه ای از کلاس SpriteTextDataLoader ایجاد کنید که در مجموعه داده ها، اندازه دسته و تعداد کارگران ما ارسال شود.
استفاده کنید تکرار روشی برای تکرار بر روی بارگذار داده به صورت دسته ای.
استفاده کنید لن روشی برای بدست آوردن تعداد کل دسته ها در بارگذار داده.


data_loader = SpriteTextDataLoader(sprite_text_dataset, batch_size=32, num_workers=4)
for batch in data_loader:
    images, texts = batch
    # Train our model on the batch

وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بعد چی؟؟

اکنون که بارگذار داده خود را داریم، بیایید به بلوک بعدی برویم که در آن معماری مدل خود را تعریف می کنیم. این هسته سازنده جن ما خواهد بود. گوش به زنگ باشید!

بلوک 5: مدل – Sprite Maker
در این بلوک، معماری مدل خود را تعریف می‌کنیم، که وظیفه تولید sprites را بر اساس ویژگی‌های متن و تصویر ورودی خواهد داشت.

معماری مدل

مدل ما از یک رمزگذار متن، یک رمزگذار تصویر و یک تولید کننده اسپرایت تشکیل شده است.
رمزگذار متن متن ورودی را می گیرد و دنباله ای از نشانه ها را خروجی می کند.
رمزگذار تصویر ویژگی های تصویر ورودی را می گیرد و دنباله ای از ویژگی ها را خروجی می کند.
مولد اسپرایت خروجی رمزگذارهای متن و تصویر را می گیرد و یک تصویر اسپرایت را خروجی می دهد.
رمزگذار متن

ما از یک مدل BERT از قبل آموزش دیده به عنوان رمزگذار متن خود استفاده خواهیم کرد.
رمزگذار متن مسئول تبدیل متن ورودی به دنباله ای از نشانه ها خواهد بود.
رمزگذار تصویر

ما از یک مدل ResNet50 از قبل آموزش دیده به عنوان رمزگذار تصویر خود استفاده خواهیم کرد.
رمزگذار تصویر مسئول تبدیل ویژگی های تصویر ورودی به دنباله ای از ویژگی ها خواهد بود.
Sprite Generator

ما از یک شبکه عصبی با یک لایه کانولوشن و یک لایه دکانولوشن برای تولید تصویر اسپرایت استفاده خواهیم کرد.
مولد اسپرایت خروجی رمزگذارهای متن و تصویر را می گیرد و یک تصویر اسپرایت را خروجی می دهد.


class SpriteMaker(nn.Module):
    def __init__(self, text_encoder, image_encoder, sprite_generator):
        super(SpriteMaker, self).__init__()
        self.text_encoder = text_encoder
        self.image_encoder = image_encoder
        self.sprite_generator = sprite_generator

    def forward(self, text, image):
        text_features = self.text_encoder(text)
        image_features = self.image_encoder(image)
        sprite_features = torch.cat((text_features, image_features), dim=1)
        sprite = self.sprite_generator(sprite_features)
        return sprite
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بعد چی؟؟

اکنون که معماری مدل خود را تعریف کرده ایم، بیایید به بلوک بعدی برویم که در آن مدل خود را با استفاده از بارگذار داده ای که قبلا ایجاد کردیم آموزش می دهیم. گوش به زنگ باشید!

بلوک 6: آموزش مدل – Sprite Maker
در این بلوک، مدل خود را با استفاده از بارگذار داده ای که قبلا ایجاد کردیم، آموزش می دهیم.

آموزش مدل

ما از روش قطار مدل خود برای آموزش آن بر روی بارگذار داده استفاده خواهیم کرد.
با استفاده از ویژگی train مدل را روی حالت آموزش قرار می دهیم.
ما یک تابع از دست دادن و یک بهینه ساز برای به روز رسانی وزن های مدل در طول تمرین تعریف می کنیم.
ما به صورت دسته‌ای روی بارگذار داده تکرار می‌کنیم و وزن مدل را با استفاده از عملکرد بهینه‌ساز و از دست دادن به‌روزرسانی می‌کنیم.


def train_model(model, data_loader, optimizer, loss_fn):
    model.train()
    total_loss = 0
    for batch in data_loader:
        images, texts = batch
        images = images.to(device)
        texts = texts.to(device)
        optimizer.zero_grad()
        outputs = model(texts, images)
        loss = loss_fn(outputs, targets)
        loss.backward()
        optimizer.step()
        total_loss += loss.item()
    print(f"Training loss: {total_loss / len(data_loader)}")
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تعریف Loss Function و Optimizer

ما از میانگین مربعات خطا (MSE) به عنوان تابع ضرر خود استفاده خواهیم کرد.
ما از بهینه ساز Adam برای به روز رسانی وزنه های مدل در طول تمرین استفاده خواهیم کرد.

loss_fn = nn.MSELoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
Training the Model

We'll train our model for 5 epochs using the train_model function.
We'll print the training loss at each epoch.



for epoch in range(5):
    train_model(model, data_loader, optimizer, loss_fn)
    print(f"Epoch {epoch+1}, Training loss: {total_loss / len(data_loader)}")
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بعد چی؟؟

اکنون که مدل خود را آموزش داده‌ایم، بیایید به بلوک بعدی برویم، جایی که عملکرد آن را در یک مجموعه آزمایشی ارزیابی می‌کنیم. گوش به زنگ باشید!

بلوک 7: ارزیابی مدل – Sprite Maker
در این بلوک، عملکرد مدل آموزش دیده خود را در یک مجموعه آزمایشی ارزیابی می کنیم.

ارزیابی مدل

ما از روش ارزیابی مدل خود برای ارزیابی آن در مجموعه تست استفاده خواهیم کرد.
با استفاده از ویژگی eval مدل را روی حالت ارزیابی قرار می دهیم.
ما مجموعه تست را به صورت دسته‌ای تکرار می‌کنیم و تلفات و دقت مدل را محاسبه می‌کنیم.
ما نتایج را در کنسول چاپ خواهیم کرد.


def evaluate_model(model, test_loader):
    model.eval()
    total_loss = 0
    correct = 0
    with torch.no_grad():
        for batch in test_loader:
            images, texts = batch
            images = images.to(device)
            texts = texts.to(device)
            outputs = model(texts, images)
            loss = loss_fn(outputs, targets)
            total_loss += loss.item()
            _, predicted = torch.max(outputs, 1)
            correct += (predicted == targets).sum().item()
    accuracy = correct / len(test_loader.dataset)
    print(f"Test Loss: {total_loss / len(test_loader)}")
    print(f"Test Accuracy: {accuracy:.2f}")
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تست مدل

ما مدل خود را روی مجموعه تست با استفاده از تابع value_model آزمایش می کنیم.
ما از دست دادن تست و دقت را در کنسول چاپ خواهیم کرد.


test_loss, test_accuracy = evaluate_model(model, test_loader)
print(f"Test Loss: {test_loss:.2f}")
print(f"Test Accuracy: {test_accuracy:.2f}")
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بعد چی؟؟

اکنون که مدل خود را ارزیابی کردیم، بیایید به بلوک بعدی برویم که در آن از مدل برای تولید sprites استفاده خواهیم کرد. گوش به زنگ باشید!

بلوک 8: تولید جن – Sprite Maker
در این بلوک، از مدل آموزش‌دیده خود برای تولید sprites بر اساس ویژگی‌های متن و تصویر ورودی استفاده می‌کنیم.

تولید جن

ما از روش فوروارد مدل خود برای تولید یک sprite بر اساس ویژگی های متن و تصویر ورودی استفاده خواهیم کرد.
با استفاده از ویژگی eval مدل را روی حالت ارزیابی قرار می دهیم.
با اعمال مولد اسپرایت در خروجی رمزگذارهای متن و تصویر، یک اسپرایت جدید ایجاد خواهیم کرد.
ما اسپرایت تولید شده را در یک فایل ذخیره می کنیم.


def generate_sprite(model, text, image):
    model.eval()
    text_features = text_encoder(text)
    image_features = image_encoder(image)
    sprite_features = torch.cat((text_features, image_features), dim=1)
    sprite = model.sprite_generator(sprite_features)
    sprite = sprite.cpu().numpy()
    sprite = Image.fromarray(sprite)
    sprite.save("generated_sprite.png")
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

تولید Sprite

با استفاده از تابعgene_sprite یک اسپرایت تولید می کنیم.
ویژگی های متن و تصویر ورودی را به عنوان آرگومان ارسال می کنیم.
ما اسپرایت تولید شده را در فایلی با نام “generated_sprite.png” ذخیره می کنیم.


text = "Hello, world!"
image = Image.open("image.png")
generate_sprite(model, text, image)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

بعد چی؟؟

اکنون که یک اسپرایت تولید کرده‌ایم، بیایید به بلوک نهایی برویم، جایی که در مورد نتایج و پیشرفت‌های بالقوه سازنده اسپرایت بحث خواهیم کرد. گوش به زنگ باشید!

بلوک 9; ساختار و گسترش مجموعه داده ها و دستورالعمل ها – این کار را آسان می کند!

گسترش قابلیت‌های مدل سنتز متن به تصویر شما هرگز آسان‌تر نبوده است!
برای معرفی مجموعه جدیدی از sprites به داده های آموزشی، به سادگی تصویر برگه جدید را در پوشه training_data/spritesheets بکشید. برای مثال، اگر مجموعه جدیدی از موجودات فانتزی دارید، نام فایل را چیزی مانند fantasy_creatures.png بگذارید.

تولید خودکار متن توصیفی:
در گردش کار ساده ما، نیازی به ایجاد توضیحات متن مربوطه به صورت دستی نیست.
راه اندازی ما به طور هوشمندانه این کار را برای شما انجام می دهد! در هنگام اجرا، یک مدل GPT-2 که به صورت محلی اجرا می‌شود، که توسط یک مدل دید قدرتمند هدایت می‌شود، به‌طور خودکار متن توصیفی را برای هر اسپرایت در شیت جدید ایجاد می‌کند.
فریم ورک همه کاره ما علاوه بر مدیریت صفحات اسپریت، تصاویر اسپرایت را به طور یکپارچه در فرآیند آموزش گنجانده است. در اینجا نحوه اضافه کردن بدون زحمت یک sprite و
آن را در دوره آموزشی بعدی خود ادغام کنید.

تصویر Single Sprite را اضافه کنید:
تصویر sprite جدید را در پوشه training_data/spritesheets ذخیره کنید. به عنوان مثال، می توانید نام آن را unique_sprite.png بگذارید.

مانند صفحات اسپریت،
مدل بینایی ویژگی های بصری را استخراج می کند،
GPT-2 یک متن توصیفی برای sprite تولید می کند.
فایل متنی با نام unique_sprite.txt،
سپس در پوشه texts ذخیره می شود.

در مورد نظارت بر عملکرد و تنظیم دقیق مدل، ما شما را تحت پوشش قرار داده ایم
ادغام یکپارچه چارچوب Weights & Biases (WandB).
با WandB، در طول هر دوره آموزشی، بینش‌های بی‌درنگ درباره عملکرد مدل خود به دست می‌آورید.
معیارهای آموزشی را به صورت بصری نمایش می‌دهد و به شما امکان می‌دهد پیشرفت را پیگیری کرده و نتایج را در اجراهای مختلف مقایسه کنید.

تنظیم فراپارامتر:
رابط قدرتمند WandB به شما این امکان را می دهد که با فراپارامترهای مختلف آزمایش کنید و تأثیر آنها را بر عملکرد مدل مشاهده کنید. این فرآیند ساده به شما امکان می دهد مدل خود را به طور کارآمدتر بهینه کنید و بهترین نتایج را برای کارهای سنتز متن به تصویر خود تضمین کنید.
به طور خلاصه، افزودن تصاویر تک اسپرایت به مجموعه داده شما به سادگی قرار دادن آنها در پوشه صحیح است. تولید خودکار متن و ادغام یکپارچه با WandB کل گردش کار را ساده می کند، بینش ارزشمند و توانایی تنظیم دقیق مدل خود را برای نتایج بهتر ارائه می دهد. این سفر هیجان انگیز را آغاز کنید و شاهد باشید که جن های تولید شده توسط هوش مصنوعی شما با هر دوره آموزشی تکامل می یابند!
حالا، اسکریپت آموزشی را روشن کنید، بنشینید،
و شاهد توانایی جدید مدل خود برای زنده کردن شخصیت های تخیلی بیشتر با قدرت تولید متن مبتنی بر هوش مصنوعی باشید!

بلوک 9: کار آینده – Sprite Maker
در این بلوک نهایی، در مورد نتایج سازنده اسپرایت و بهبودهای بالقوه بحث خواهیم کرد.
نتایج:
سازنده جن ما با موفقیت یک اسپرایت را بر اساس ویژگی‌های متن و تصویر ورودی ایجاد کرده است.
اسپرایت تولید شده یک تصویر 256×256 پیکسلی است که نشان دهنده یک کاراکتر اسپرایت ساده است.
سازنده اسپرایت در مجموعه آزمایشی به دقت 90 درصد دست یافته است.

نتیجه
در این آموزش یاد گرفتیم که چگونه با استفاده از PyTorch و Python یک sprite maker بسازیم.
ما یک سازنده جن را روی مجموعه داده ای از ویژگی های متن و تصویر آموزش داده ایم و عملکرد آن را در یک مجموعه آزمایشی ارزیابی کرده ایم.
ما همچنین از سازنده اسپرایت برای تولید اسپرایت بر اساس ویژگی‌های متن و تصویر ورودی استفاده کرده‌ایم.

بعد چی؟؟

فیلمنامه کامل:

import torch
import os
from glob import glob
from torch import nn, optim
from torch.utils.data import DataLoader, Dataset
from torchvision import models, transforms
from transformers import VisionEncoderDecoderModel, ViTImageProcessor, AutoModel, AutoTokenizer
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from rich import print as rp
import wandb

# Initialize WandB
wandb.init(project="spritemaker", entity="goldenkooy")

# Text encoder class
class TextEncoder:
    def __init__(self, model_name="bert-base-uncased"):
        self.tokenizer = AutoTokenizer.from_pretrained(model_name, cache_dir="./models")
        self.model = AutoModel.from_pretrained(model_name, cache_dir="./models")
        self.model.eval()

    def encode_text(self, text):
        with torch.no_grad():
            inputs = self.tokenizer(text, return_tensors="pt", padding=True, truncation=True)
            outputs = self.model(**inputs)
        return outputs.last_hidden_state[:, 0, :]

# Image encoder class
class ImageEncoder:
    def __init__(self):
        self.model = models.resnet50(weights=models.ResNet50_Weights.IMAGENET1K_V2)
        self.model.eval()
        self.transform = transforms.Compose([
            transforms.Resize((224, 224)),
            transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
        ])

    def encode_image(self, image_path):
        image = Image.open(image_path).convert('RGB')
        image = self.transform(image)
        image = image.unsqueeze(0)  # Add batch dimension
        with torch.no_grad():
            features = self.model(image)
        return features

# Sprite and text dataset class
class SpriteTextDataset(Dataset):
    def __init__(self, image_dir, text_dir, image_encoder, text_encoder):
        self.image_encoder = image_encoder
        self.text_encoder = text_encoder
        self.data = []

        # Load all image paths
        image_paths = glob(os.path.join(image_dir, '*.png'))

        # Debug: rp the found image paths
        rp(f"Found image paths: {image_paths}")

        # Load descriptions and pair them with images
        for image_path in image_paths:
            base_filename = os.path.splitext(os.path.basename(image_path))[0]
            text_path = os.path.join(text_dir, f"{base_filename}.txt")
            if os.path.exists(text_path):
                with open(text_path, 'r', encoding='utf-8') as file:
                    description = file.read().strip()
                    self.data.append((image_path, description))
            else:
                rp(f"Warning: No description file found for {image_path}")

        # Debug: rp the dataset size
        rp(f"Dataset size: {len(self.data)}")

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        image_path, text = self.data[idx]
        image_features = self.image_encoder.encode_image(image_path)
        text_features = self.text_encoder.encode_text(text)

        combined_features = torch.cat((image_features, text_features), dim=1)
        return combined_features

# Descriptor class for generating descriptions
class Descriptor:
    def __init__(self, cache_dir="./models"):
        self.model = VisionEncoderDecoderModel.from_pretrained(
            "nlpconnect/vit-gpt2-image-captioning", cache_dir=cache_dir
        )
        self.feature_extractor = ViTImageProcessor.from_pretrained(
            "nlpconnect/vit-gpt2-image-captioning", cache_dir=cache_dir
        )
        self.tokenizer = AutoTokenizer.from_pretrained(
            "nlpconnect/vit-gpt2-image-captioning", cache_dir=cache_dir
        )

        self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        self.model.to(self.device)

        self.max_length = 16 
        self.num_beams = 4
        self.gen_kwargs = {"max_length": self.max_length, "num_beams": self.num_beams}

    def describe_image(self, image_path):
        image = Image.open(image_path)
        if image.mode != "RGB":
            image = image.convert(mode="RGB")

        pixel_values = self.feature_extractor(images=image, return_tensors="pt").pixel_values
        pixel_values = pixel_values.to(self.device)

        output_ids = self.model.generate(pixel_values, **self.gen_kwargs)
        description = self.tokenizer.decode(output_ids[0], skip_special_tokens=True).strip()
        return description

# VAE model class
class VAE(nn.Module):
    def __init__(self, input_dim, latent_dim):
        super(VAE, self).__init__()
        self.fc1 = nn.Linear(input_dim, 512)
        self.fc21 = nn.Linear(512, latent_dim)
        self.fc22 = nn.Linear(512, latent_dim)
        self.fc3 = nn.Linear(latent_dim, 512)
        self.fc4 = nn.Linear(512, input_dim)

    def encode(self, x):
        h1 = torch.relu(self.fc1(x))
        return self.fc21(h1), self.fc22(h1)

    def reparameterize(self, mu, logvar):
        std = torch.exp(0.5 * logvar)
        eps = torch.randn_like(std)
        return mu + eps * std

    def decode(self, z):
        h3 = torch.relu(self.fc3(z))
        return torch.sigmoid(self.fc4(h3))

    def forward(self, x):
        mu, logvar = self.encode(x)
        z = self.reparameterize(mu, logvar)
        return self.decode(z), mu, logvar

# Utility functions for training and visualization
class Utils:
    def __init__(self, dataset, model, optimizer, text_encoder, checkpoint_dir="checkpoints"):
        self.train_data = dataset
        self.model = model
        self.optimizer = optimizer
        self.checkpoint_dir = checkpoint_dir
        self.text_encoder = text_encoder
        os.makedirs(checkpoint_dir, exist_ok=True)

    def save_checkpoint(self, epoch, loss):
        checkpoint_path = os.path.join(self.checkpoint_dir, 'latest_checkpoint.pth')
        checkpoint = {
            'epoch': epoch,
            'model_state_dict': self.model.state_dict(),
            'optimizer_state_dict': self.optimizer.state_dict(),
            'loss': loss
        }
        torch.save(checkpoint, checkpoint_path)
        rp(f'Checkpoint saved at {checkpoint_path}')

    def load_checkpoint(self):
        checkpoint_path = os.path.join(self.checkpoint_dir, 'latest_checkpoint.pth')
        if os.path.exists(checkpoint_path):
            checkpoint = torch.load(checkpoint_path)
            self.model.load_state_dict(checkpoint['model_state_dict'])
            self.optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
            epoch = checkpoint['epoch']
            loss = checkpoint['loss']
            rp(f'Checkpoint loaded from {checkpoint_path}, epoch {epoch}, loss {loss}')
            return epoch, loss
        else:
            rp(f'No checkpoint found at {checkpoint_path}')
            return None, None

    def visualize_reconstructions(self, device="cpu"):
        self.model.eval()
        with torch.no_grad():
            for i, data in enumerate(self.train_data):
                data = data.to(device)
                reconstructed, _, _ = self.model(data)
                original = data.detach().cpu().numpy()
                reconstructed = reconstructed.detach().cpu().numpy()

                # Separate the image and text features
                original_image_features = original[:, :1000]
                reconstructed_image_features = reconstructed[:, :1000]

                # For a single sample, visualize the original and reconstructed images
                plt.figure(figsize=(12, 6))
                plt.subplot(1, 2, 1)
                plt.title('Original Image')
                self.visualize_image(original_image_features[0])  # Visualize original image

                plt.subplot(1, 2, 2)
                plt.title('Reconstructed Image')
                self.visualize_image(reconstructed_image_features[0])  # Visualize reconstructed image

                plt.show()

                if i >= 10:
                    break

    def visualize_image(self, text_prompt, num_samples=1):
        # Encode the text prompt
        text_features = self.text_encoder.encode_text(text_prompt)

        # Generate random latent variables
        latent_variables = torch.randn(num_samples, self.model.latent_dim)

        # Concatenate text features with latent variables
        combined_features = torch.cat((latent_variables, text_features.expand(num_samples, -1)), dim=1)

        # Decode the combined features
        with torch.no_grad():
            generated_images = self.model.decode(combined_features)

        # Visualize the generated images
        for i in range(num_samples):
            plt.figure(figsize=(4, 4))
            plt.imshow(generated_images[i].reshape(224, 224))  # Reshape as per your image size
            plt.axis('off')
            plt.title(f'Generated Image {i+1}')
            plt.show()


    def train_vae(self, epochs=10, batch_size=32, learning_rate=1e-3):
        dataloader = DataLoader(self.train_data, batch_size=batch_size, shuffle=True)
        for epoch in range(epochs):
            self.model.train()
            for batch in dataloader:
                batch = batch.to(next(self.model.parameters()).device)
                self.optimizer.zero_grad()
                recon_batch, mu, logvar = self.model(batch)
                loss = self.loss_function(recon_batch, batch, mu, logvar)
                loss.backward()
                self.optimizer.step()
            rp(f'Epoch {epoch + 1}, Loss: {loss.item()}')
            self.save_checkpoint(epoch + 1, loss.item())
            # Log the current learning rate to WandB
            wandb.log({"learning_rate": self.optimizer.param_groups[0]['lr']}, step=epoch)

            # Log the loss for the epoch
            wandb.log({"epoch_loss": loss.item()}, step=epoch)

            # print(f'Epoch {epoch + 1}, Loss: {loss.item()}')

    def loss_function(self, recon_x, x, mu, logvar):
        MSE = nn.functional.mse_loss(recon_x, x, reduction='sum')
        KLD = -0.5 * torch.sum(1 + logvar - mu.pow(2) - logvar.exp())
        return MSE + KLD

# Assuming the rest of the script remains the same
# Instantiate encoders
text_encoder = TextEncoder()
image_encoder = ImageEncoder()

# Descriptor for generating descriptions
descriptor = Descriptor()

# Paths
image_dir="./trainings_data/spritesheets"
text_dir="./trainings_data/texts"

# Functions to fetch missing items and extract filenames
def fetch_missing_items(list1, list2):
    set2 = set(list2)
    return [item for item in list1 if item not in set2]

def extract_filenames(paths):
    return [os.path.splitext(os.path.basename(path))[0] for path in paths]

# Get lists of text and image files
text_files = glob(os.path.join(text_dir, '*.txt'))
image_files = glob(os.path.join(image_dir, '*.png'))

# Extract just the filenames without extensions for comparison
text_names = extract_filenames(text_files)
image_names = extract_filenames(image_files)

# Find descriptions missing for images
missing_descriptions = fetch_missing_items(image_names, text_names)

# Generate and write descriptions for missing files
for missing_name in missing_descriptions:
    image_path = os.path.join(image_dir, f"{missing_name}.png")
    text_path = os.path.join(text_dir, f"{missing_name}.txt")

    try:
        description = descriptor.describe_image(image_path)
        with open(text_path, "w") as f:
            f.write(description)
        rp(f"Generated description for: {missing_name}")
    except Exception as e:
        rp(f"Error generating description for {missing_name}: {e}")

# Create dataset
dataset = SpriteTextDataset(image_dir, text_dir, image_encoder, text_encoder)

# Verify dataset size
rp(f"Final dataset size: {len(dataset)}")

# 1000 features from resnet-50 + 768 features from BERT = 1768 input dimensions
vae = VAE(input_dim=1768, latent_dim=70)

# Create an optimizer
optimizer = optim.Adam(vae.parameters(), lr=1e-3)

# Instantiate utilities
utils = Utils(dataset, vae, optimizer, text_encoder)

# Optionally load checkpoint
start_epoch, start_loss = utils.load_checkpoint()

# Train VAE
if start_epoch is None:
    start_epoch = 0  # If no checkpoint is found, start from epoch 0
utils.train_vae(epochs=600 - start_epoch)

# Visualize progress
utils.visualize_reconstructions()

# Usage
text_prompt = "A pixel art character with a blue hat"
utils.visualize_image(text_prompt)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

همه:
طول و پیچیدگی:\
این آموزش بسیار طولانی است و می تواند برای مبتدیان طاقت فرسا باشد. شکستن آن به قسمت های کوچکتر و قابل هضم تر یا ایجاد یک سری می تواند خوانایی و یادگیری را افزایش دهد.
رسیدگی به خطا:
در حالی که این آموزش جامع است، از جمله خطاهای رایج و نکات عیب یابی می تواند زبان آموزان را برای مشکلات احتمالی در حین اجرا آماده کند.
خروجی های بصری:
شامل خروجی‌های بصری اسپرایت‌ها و گام‌های میانی می‌تواند درک و تعامل را تا حد زیادی افزایش دهد. بصری ها به ویژه در آموزش های مربوط به پردازش تصویر قدرتمند هستند.
معیارهای عملکرد:
تاکید بیشتر بر ارزیابی عملکرد مدل و توضیح معیارها می تواند بینش بهتری را در مورد بهینه سازی مدل به یادگیرندگان ارائه دهد.
عناصر تعاملی:
افزودن عناصر تعاملی مانند آزمون‌ها یا تمرین‌های کوچک در انتهای هر بلوک می‌تواند فرآیند یادگیری را جذاب‌تر و مؤثرتر کند.
توصیفگر چند شیت را بررسی، اصلاح و آزمایش کنید:
تولید متن توصیفی در هر جن

ویژگی های آیندهL:
افزایش دقت اسپرایت ساز با افزایش سایز مجموعه آموزشی و
استفاده از تکنیک های پیشرفته تر مانند:
مکانیسم های توجه
شبکه های متخاصم مولد (GANs).

برای ایجاد اسپرایت های واقعی تر و جذاب تر، ویژگی های بیشتری را به سازنده اسپرایت اضافه کنید.
مانند:
انیمیشن
جلوه های صوتی،

استفاده از sprite maker برای تولید sprite برای برنامه های مختلف،
مانند بازی های ویدئویی،
واقعیت مجازی،
انیمیشن

پایان:
اکنون که یک اسپرایت ساز ساخته ایم، می توانیم از آن برای ایجاد اسپرایت برای برنامه های مختلف استفاده کنیم.
همچنین می‌توانیم با افزودن ویژگی‌های بیشتر و استفاده از تکنیک‌های پیشرفته‌تر، دقت و قابلیت‌های سازنده اسپرایت را بهبود بخشیم.

از اینکه همراه با این آموزش دنبال کردید متشکرم! امیدوارم چیز جدید و مفیدی یاد گرفته باشید. اگر سوالی دارید یا نیاز به کمک بیشتری دارید، لطفا دریغ نکنید.

گرتز CodeMonkeyXL

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا