পাইটর্চে ফাস্ট নিউরাল স্টাইল ট্রান্সফার ও Core ML এ কনভার্ট

এই টিউটোরিয়ালে আমরা টোটাল তিনটা কাজ করবঃ

  • পাইটর্চে ফাস্ট নিউরাল স্টাইল ট্রান্সফার মডেল ট্রেইন করব
  • সেভ করা মডেল coreML এ কনভার্ট করব
  • ঐ CoreML মডেল ব্যবহার করে একটা iOS অ্যাপ তৈরি করব

পাইটর্চ ফাস্ট নিউরাল স্টাইল ট্রান্সফার

ফাস্ট নিউরাল স্টাইল ২০১৬ সাইলের একটা পাবলিকেশন। যা ব্যবহার করে রিয়েলটাইম যে কোন ছবি বা ভিডিওতে অন্য আরেকটা ইমেজের স্টাইল এপ্লাই করা যায়। নিউরাল নেটওয়ার্ক কিভাবে কাজ করে, এসব বুঝার জন্য ক্লাসিক উদাহরন। আমরা পাইটর্চের অফিশিয়াল উদারনটাই ফলো করব।

পাইটর্চের অনেক গুলো উদারণ নিয়ে example রিপোজিটরি রয়েছে। যেখান থেকে চাইলে পুরা রিপোজিটোরি ডাউনলোড করে নিতে পারেন। আমরা কাজ করব fast_neural_style উদাহরণে। এর পূর্বে কম্পিউটারে পাইটর্চ ইন্সটল করে নিতে হবে। অনেক ভাবেই করা যায়। আমি মিনিকন্ডা ব্যবহার করে ইন্সটল করব।

তার আগে দেখে নেই আমরা কি করতে যাচ্ছি। একটা আর্ট বা স্টাইল ইমেজ দিয়ে মডেল ট্রেইন করার পর ঐ মডেল ব্যবহার করে আমরা যে কোন ইমেজের উপর নির্দিষ্ট আর্ট এপ্লাই করতে পারব। নিচের উদাহরণটি দেখিঃ

স্টাইল ইমেজ

ইনপুট ইমেজ

আউটপুট

এনভারনমেন্ট সেটআপ

পাইথন প্যাকেজ ম্যানেজার conda ইন্সট

কম্পিউটারে মিনিকন্ডা ইন্সটল না থাকলে প্রথমে ইন্সটল করে নিব। উইন্ডোজ, ম্যাক, লিনাক্স সব অপারেটিং সিস্টেমের জন্যই পাওয়া যায়। মিনিকন্ডা হচ্ছে এনাকন্ডার মিনি ভার্সন। এনাকন্ডাতে অনেক বেশি প্যাকেজ থাকে। যেগুলোর বেশির ভাগই অদরকারি, তাই মিনি ভার্সন আমি প্রেফার করি। এরপর conda ব্যবহার করে দরকারি প্যাকেজ গুলো ইন্সটল করে নিতে পারব।

পাইটর্চ ইন্সটল

পাইটর্চ ইন্সটলের পূর্বে একটা ভার্চুয়াল এনভারনমেন্ট তৈরি করে নিতে হবে। যে এনভারনমেন্টে আমরা এই প্রজেক্টের জন্য দরকারি প্যাকেজ গুলো ইন্সটল করে নিতে পারব। তার জন্যঃ

conda create -n env_name python=3.12

এখানে env_name এর যায়গায় যে কোন নাম দিতে পারব। যেমন আমি দিলাম torch। এরপর এই এনভারনমেন্ট এক্টিভেট করবঃ

conda activate env_name 

আমি যেহেতু এনভারনমেন্টের নাম দিয়েছি torch, তাই আমি কমান্ড লিখব conda activate torch

GPU ভার্সন

এই এনভারনমেন্টে পাইটর্চ রিলেটেড প্যাকেজ গুলো ইন্সটল করব। যাদের GPU রয়েছে, তারা লিখবঃ

conda install pytorch torchvision torchaudio pytorch-cuda=12.1 -c pytorch -c nvidia

আমাদের CUDA সাপোর্টেড GPU লাগবে। যদি থাকে, এবং GPU ড্রাইভার ইন্সটল করা থাকে, তাহলে নিচের কমান্ড রান করলে GPU এর ইনফরমেশন দিবেঃ

nvidia-smi 

CUDA ড্রাইভার ইন্সটল করা আছে কিনা, তা ভেরিফিকেশন করতে পারেন এই কমান্ড রান করেঃ

nvcc --version

যদি না থাকে, আপনার সিস্টেম অনুযায়ি CUDA টুলকিট এবং NVIDIA cuDNN ডাউনলোড করে ইন্সটল করে নিতে হবে।

সব ঠিক মত ইন্সটল হয়েছে কিনা, তা নিচের পাইথন স্ক্রিপ্ট রান করে দেখে নিতে পারেনঃ

import torch
print("CUDA Available: ", torch.cuda.is_available())

যেমন gpu-test.py ফাইলে সেভ করে এরপর কমান্ডলাইন / টার্মিনালে গিয়ে python gpu-test.py কমান্ড রান করব। যদি true রিটার্ণ করে, এর মানে সব সেটআপ করা হয়েছে।

CPU ভার্সন

যাদের GPU নেই, তারা CPU ভার্সন ইন্সটল করে কাজ করতে পারবেন।

conda install pytorch torchvision torchaudio cpuonly -c pytorch

মডেল ট্রেইনিং ডেটা

আমরা যানি যেকোন মডেল ট্রেইন করার জন্য প্রচুর ডেটা লাগে। ডেটা উপরের উদারহণের জন্য  COCO 2014 Training images dataset ডেটাসেট ব্যবহার করেছে। এখানে ৮০ হাজার ইমেজ রয়েছে। প্রায় ১৩ জিবি ইমেজ।

এই ইমেজ গুলো ডাউনলোড করে fast_neural_style ফোল্ডারের ভেতর রাখব। ইমেজ গুলো রাখতে ইমেজ আমি রেখেছি train2014 এর ভেতর class1 নামে। এখানে একাধিক ফোল্ডার করে ইমেজ রাখা যাবে। তবে আমি একটা ফোল্ডারেই সব গুলো রেখেছি। কম্পিউটার পাওয়ারফুল না হলে একটা ফোল্ডারে সব ইমেজ রাখলে কম্পিউটার হ্যাংগ করতে পারে।

মডেল ট্রেইনিং

মডেল ট্রেইনিং এর জন্য নিচের মত করে কমাড লিখব।

python neural_style/neural_style.py train --dataset </path/to/train-dataset> --style-image </path/to/style/image> --save-model-dir </path/to/save-model/folder> --epochs 2 --cuda 1

মাল্টিলাইন করেও কমান্ড লেখা যাবে। যেমন আমি এভাবে লিখেছিঃ

 python neural_style/neural_style.py train \
--dataset ./train2014 \
--style-image ./images/style-images/candy.jpg \
--epochs 2 \
--batch-size 4 \
--image-size 256 \
--content-weight 1e5 \
--style-weight 1e10 \
--lr 1e-3 \
--save-model-dir ./models
--cuda 1

CPU ব্যবহার করে ট্রেইন করলে –cuda 1 প্যারামিটার দেওয়ার দরকার নেই। পারফেক্ট রেজাল্টের জন্য --content-weight এবং--style-weight প্যারামিটার গুলোর ভ্যালু পরিবর্তন করে দেখা যেতে পারে।

উপরের উদাহরনে images/style-images ফোল্ডারে কিছু আর্ট দেওয়া আছে। সেখান থেকে আমি candy.jpg ব্যবহার করেছি। এখানে নিজের মত করে যে কোন আর্ট স্টাইল ইমেজ হিসেবে দেওয়া যাবে।

মডেল ট্রেইন কমপ্লিট হলে model ফোল্ডারে মডেল সেভ হবে।

মডেল টেস্টিং

মডেল ট্রেইনিং শেষে আমরা যে কোন ইমেজে স্টাইল এপ্লাই করতে পারবঃ

python neural_style/neural_style.py eval --content-image </path/to/content/image> --model </path/to/saved/model> --output-image </path/to/output/image> --cuda 0

কমান্ড সহজে লেখার জন্য মডেল রিনেইম করে result.model লিখেছি। এরপর নিচের মত করে কমান্ড লিখেছিঃ

python neural_style/neural_style.py eval \
--content-image images/content-images/amber.jpg \
--model models/result.model \
--output-image output.jpg \
--cuda 0

সব কিছু সুন্দর মত করতে পারলে কনগ্র্যাটস! আর্টিকেলের প্রথম ধাপ শেষ হয়েছে।

মডেলকে Core ML মডেলে কনভার্ট

প্রথমে coremltools প্যাকেজ ইন্সটল করে নিতে হবে। সম্ভবত coremltools পাইটর্চের GPU ভার্সন সাপোর্ট করে না। আমি অনেক বার চেষ্টা করেছি। অনেক এরর পেয়েছি। তো এর জন্য আলাদা করে আরেকটা এনভারনমেন্ট তৈরি করে নিয়েছি coreML নামে। আপনি যে কোন নাম দিতে পারেন।

conda create -n coreML python=3.10

এরপর এই এনভারনমেন্ট এক্টিভেট করেছিঃ

conda activate coreML

এর মধ্যে পাইটর্চ, কোরএমএল টুল ইন্সটল করে নিয়েছিঃ

pip install torch
pip install torchvision
pip install -U coremltools

এরপর নিচের মত করে কনভার্টার স্ক্রিপ্ট লিখেছিঃ

import torch
import coremltools as ct
import sys
import os

# Add the 'neural_style' directory to the system path
sys.path.append(os.path.join(os.path.dirname(__file__), 'neural_style'))



# Now import TransformerNet
from transformer_net import TransformerNet

# Load the saved model state_dict
model = TransformerNet()

# Modify the state_dict to remove running_mean and running_var keys
state_dict = torch.load('models/result.model')

# Filter out the running_mean and running_var keys
for key in list(state_dict.keys()):
    if "running_mean" in key or "running_var" in key:
        del state_dict[key]

# Load the filtered state_dict into the model
model.load_state_dict(state_dict)
model.eval()  # Set the model to evaluation mode

#  input shape for tracing (adjust the size as per your input dimensions)
input_shape = torch.randn(1, 3, 1000, 1000)

# Trace the model using torch.jit.trace
traced_model = torch.jit.trace(model, input_shape)

# Convert to Core ML using coremltools
coreml_model = ct.convert(
    traced_model,
    inputs=[ct.ImageType(name="input", shape=input_shape.shape)],  # Specify input as an image
    outputs=[ct.ImageType(name="output",  scale=1, bias=[0, 0, 0])]  # Specify output as an image
)

# Save the Core ML model
coreml_model.save('coreml-output/result.mlpackage')

print("Model successfully converted and saved as 'coreml-output/result.mlpackage'")

এই ফাইলটা fast_neural_style ফোল্ডারের ভেতর রেখেছি। যেমন converter.py

এরপর নিচের মত করে রান করবঃ

python  converter.py

coreml-output ফোল্ডারে এই Core ML প্যাকেজ এক্সপোর্ট হবে। যা আমরা অ্যাপল ফ্লাটফর্ম যেমন macOS, iOS ইত্যাদিতে ব্যবহার করতে পারব।

কভার্ট করা মডেল ব্যবহার করে iOS অ্যাপ তৈরি

Xcode এ একটা SwiftUI প্রজেক্ট তৈরি করে নিব। এরপর কনভার্ট করা mlpackage প্রজেক্টে কপি করব। এই মডেল ব্যবহার করে একটা ইমেজকে আর্টে পরিণত করতে নিচের মত করে একটা ফাংশন লিখব। সিমিলার আরেকটা আর্টিকেলে স্টেপ গুলো সম্পর্কে বিস্তারিত লেখা রয়েছে CoreML মডেল ও এর ব্যবহার – ইমেজ ক্লাসিফিকেশন

    // Function to Make Art from the image using Core ML
    func makeArt(_ image: UIImage) {
        do {
            let modelConfiguration = MLModelConfiguration()
            // Load yoru model
            let model = try VNCoreMLModel(for: Candy(configuration: modelConfiguration).model)
            
            let request = VNCoreMLRequest(model: model) { request, error in
                if let results = request.results as? [VNPixelBufferObservation],
                   let pixelBuffer = results.first?.pixelBuffer {
                    
                    // Convert the pixel buffer to CIImage
                    let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
                    let context = CIContext()
                    
                    // Create CGImage
                    if let cgImage = context.createCGImage(ciImage, from: ciImage.extent) {
                        // Convert CGImage to UIImage
                        self.output = UIImage(cgImage: cgImage)
                        // Use the processed UIImage (uiImage)
                    }
                } else {
                    print("No results or invalid format")
                }
            }
            
            // Load the image from the Assets
            if  let cgImage = image.cgImage {
                let handler = VNImageRequestHandler(cgImage: cgImage, options: [:])
                try handler.perform([request])
            }
        } catch {
            print("Error: \(error.localizedDescription)")
        }
    }

যেমন Candy স্টাইল এপ্লাই করে মডেল তৈরি করলে নিচের মত করে আউটপুট পাবোঃ

সম্পূর্ণ ContentView.swift, যেখানে ইমেজ চুজ পিকার এবং সেভ করার ফাংশনালিটি যোগ করেছিঃ

import SwiftUI
import CoreML
import Vision
import UIKit

struct ContentView: View {
    @State private var image: UIImage? = nil
    @State private var output: UIImage? = nil
    @State private var isImagePickerPresented: Bool = false
    
    
    
    var body: some View {
        VStack {
            ScrollView{
                if let image = image {
                    Image(uiImage: image)
                        .resizable()
                        .scaledToFit()
                        .frame(height: 300)
                        .padding()
                    
                    HStack{
                        Button("Make Art") {
                            makeArt(image)
                        }
                    }
                }
                
                if let output = output{
                    Image(uiImage: output)
                        .resizable()
                        .scaledToFit()
                        .frame(height: 300)
                        .padding()
                    
                    Button("Save"){
                        UIImageWriteToSavedPhotosAlbum(output, nil, nil, nil)
                    }
                }
                
                Button("Pick an Image") {
                    isImagePickerPresented = true
                }
            }
            .padding()
        }
        .sheet(isPresented: $isImagePickerPresented, content: {
            ImagePicker(selectedImage: $image, sourceType: .photoLibrary)
        })
    }
    
    // Function to Make Art from the image using Core ML
    func makeArt(_ image: UIImage) {
        do {
            let modelConfiguration = MLModelConfiguration()
            // Load yoru model
            let model = try VNCoreMLModel(for: Candy(configuration: modelConfiguration).model)
            
            let request = VNCoreMLRequest(model: model) { request, error in
                if let results = request.results as? [VNPixelBufferObservation],
                   let pixelBuffer = results.first?.pixelBuffer {
                    
                    // Convert the pixel buffer to CIImage
                    let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
                    let context = CIContext()
                    
                    // Create CGImage
                    if let cgImage = context.createCGImage(ciImage, from: ciImage.extent) {
                        // Convert CGImage to UIImage
                        self.output = UIImage(cgImage: cgImage)
                        // Use the processed UIImage (uiImage)
                    }
                } else {
                    print("No results or invalid format")
                }
            }
            
            // Load the image from the Assets
            if  let cgImage = image.cgImage {
                let handler = VNImageRequestHandler(cgImage: cgImage, options: [:])
                try handler.perform([request])
            }
        } catch {
            print("Error: \(error.localizedDescription)")
        }
    }
    
    // ImagePicker helper to allow the user to pick an image
    struct ImagePicker: UIViewControllerRepresentable {
        @Binding var selectedImage: UIImage?
        @Environment(\.presentationMode) var presentationMode
        var sourceType: UIImagePickerController.SourceType = .photoLibrary
        
        class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
            let parent: ImagePicker
            
            init(parent: ImagePicker) {
                self.parent = parent
            }
            
            func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
                if let image = info[.originalImage] as? UIImage {
                    parent.selectedImage = image
                }
                parent.presentationMode.wrappedValue.dismiss()
            }
            
            func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
                parent.presentationMode.wrappedValue.dismiss()
            }
        }
        
        func makeCoordinator() -> Coordinator {
            Coordinator(parent: self)
        }
        
        func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
            let picker = UIImagePickerController()
            picker.delegate = context.coordinator
            picker.sourceType = sourceType
            return picker
        }
        
        func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {}
    }   
}
#Preview {
    ContentView()
}

উপরের কোডে ফটো সেভ করার কোড লেখা রয়েছে। এর জন্য Info.plist ফটো লাইব্রেরি ইউজেজ ডেসক্রিপশন যোগ করতে হবে। না হলে অ্যাপ ক্র্যাশ করবে।

Info.plist এ Privacy – Photo Library Usage Description কী এর জন্য “We need access to your photo library to classify images.” ভ্যালু সেট করব।

আমি বাংলাদেশী রিক্সা আর্ট ব্যবহার করে মডেল ট্রেইন করেছি। ঐটার আউটপুটঃ

রিক্সা আর্ট দিয়ে মডেল ট্রেইন করার পর

গিটহাবে iOS এবং পাইথন কোড আপলোড করে দিয়েছি।

Leave a Reply