এই ব্লগ পোস্টের জন্য, আমরা আগে যেতে pokrač করার আগে কিছু অনুমান করব, কিন্তু আপনার আদর্শভাবে থাকা উচিত:
- একটি আপস্ট্যাশ অ্যাকাউন্ট যেখানে আপনার একটি রেডিস ইন্সট্যান্স তৈরি আছে
- API টোকেন অ্যাক্সেসসহ একটি Replicate অ্যাকাউন্ট
- আমাদের পছন্দের ফাংশনালিটি বাস্তবায়ন করার জন্য একটি Next.js প্রজেক্ট
- আপনার প্রজেক্ট ডিপ্লয় করার জন্য একটি Vercel অ্যাকাউন্ট
এটি কী?
আপনি কি Replicate-এ উপলব্ধ মডেলগুলো ব্যবহার করে মেশিন লার্নিং দিয়ে ছবি জেনারেট করার সাথে শুরু করতে চেয়েছেন? এই টিউটোরিয়ালে, আমরা Replicate-এর ব্যাপক হোস্টেড মডেলের রেঞ্জ এবং আপস্ট্যাশের রেডিসকে এক্সপ্লোর করব। শুধু এই মডেলগুলো এক্সপ্লোর করব না, বরং একটি সেট আপ করার প্রসেসের মধ্য দিয়ে যাব, এবং আপনি কীভাবে সহজে বাস্তবায়নটি আপডেট করতে পারেন অন্যান্য মডেল ব্যবহার করার জন্যও স্পর্শ করব।
এই টিউটোরিয়ালে, আমরা Microsoft-এর Bringing Old Photos Back to Life মডেলের ব্যবহার কভার করব, যা মূলত একটি পুরনো ছবি নেয়, এটি মডেলের মাধ্যমে চালায়, এবং আপনার ছবির একটি এডিট করা, এবং আশাক্রমে উন্নত সংস্করণ আউটপুট করে।

আর্কিটেকচার কী?
আপনার যদি কিছু React অভিজ্ঞতা থাকে, আপনি কোডবেস পড়ে অ্যাপ আর্কিটেকচার কীভাবে কাজ করে সেটি বুঝতে পারবেন, কিন্তু এটাকে আরও সহজ করার জন্য, বা আপনি যদি একটি ওভারভিউ দেখতে পছন্দ করেন, নিচে একটি প্রদান করা হলো।

শুরু করতে আমার কী লাগবে?
শুরুতে, আপনার অবশ্যই একটি Next.js প্রজেক্ট দরকার। এটি Next.js সেটআপ গাইড ফলো করে করা যায়, অথবা আপনার যদি ইতিমধ্যে একটি সেটআপ থাকে, তাও চলে। এই টিউটোরিয়ালে, আমরা Tailwind CSS ব্যবহার করছি, কিন্তু আপনি অবশ্যই আপনার পছন্দের যেকোনো স্টাইলিং সেটআপ ব্যবহার করতে পারেন।
এখন যেহেতু আমাদের একটি বেসিক Next.js প্রজেক্ট সেটআপ আছে, আমরা আপস্ট্যাশের রেডিস লাইব্রেরি ইনস্টল করতে পারি কমান্ডটি চালিয়ে:
npm install @upstash/redis
এরপর, আমরা আমাদের .env.local ফাইল নিচের কীগুলো দিয়ে পপুলেট করব, যেগুলোর রেডিস টোকেন আপস্ট্যাশ কনসোলে পাওয়া যাবে, Replicate API টোকেন একাউন্টের অধীনে, এবং আপনার সাইট URL যেখানেই আপনি ডিপ্লয় করবেন, এই কেসে এটি Vercel ডিপ্লয়মেন্ট এন্ডপয়েন্ট হবে।
SITE_URL=https://your-project-url.vercel.app
REPLICATE_API_TOKEN=
UPSTASH_REDIS_REST_URL=
UPSTASH_REDIS_REST_TOKEN=
ফ্রন্টএন্ড ফর্ম সেটআপ
শুরুতে, আমাদের একটি ফর্ম দরকার যা ফর্ম হ্যান্ডল করবে, পোলিং এবং সম্পন্ন ছবি প্রদর্শন।
রিস্টোর ইমেজ ফর্ম তৈরি
File: pages/index.tsx
import { MouseEvent, RefObject, useRef, useState } from "react";
import Head from "next/head";
import useInterval from "../hooks/useInterval";
export default function Home() {
const [restoring, setRestoring] = useState<boolean>(false);
const [messageId, setMessageId] = useState<string | null>(null);
const [prediction, setPrediction] = useState<any>({});
const [outputImageUrl, setOutputImageUrl] = useState<string | null>(null);
const imageUrlRef: RefObject<HTMLInputElement> = useRef(null);
const hrRef: RefObject<HTMLInputElement> = useRef(null);
const scratchRef: RefObject<HTMLInputElement> = useRef(null);
useInterval(
async () => {
await fetch(`/api/poll?id=${messageId}`)
.then((res: any) => res.json())
.then((data: any) => {
if (!data.output) {
return;
}
setRestoring(false);
setMessageId(null);
setOutputImageUrl(data.output);
})
.catch((err: any) => console.error(err));
},
messageId ? 1000 : null,
);
async function restoreImage(e: any) {
e.preventDefault();
setRestoring(true);
await fetch("/api/create", {
method: "POST",
body: JSON.stringify({
image_url: imageUrlRef.current?.value,
is_hr: hrRef.current?.value,
has_scratches: scratchRef.current?.value,
}),
headers: {
"Content-Type": "application/json",
},
})
.then((res: Response) => res.json())
.then((data: any) => {
setMessageId(data.data.id);
setPrediction(data.data);
})
.catch((err: Error) => console.error(err));
}
async function cancel(e: MouseEvent<HTMLButtonElement>) {
e.preventDefault();
await fetch("/api/cancel", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
cancel_url: prediction.urls.cancel,
}),
})
.then((res: Response) => res.json())
.then((data: any) => {
setMessageId(null);
setPrediction({});
setRestoring(false);
})
.catch((err: Error) => console.error(err));
}
return (
<>
PhotoRescue
PhotoRescue
Restore your old photos to their former glory.
{outputImageUrl && (
)}
{!outputImageUrl && (
)}
>
);
}
ডিফল্টলি, এই কম্পোনেন্টটি একটি ফর্ম প্রদর্শন করে যা ব্যবহারকারীকে আপনারা যা রিস্টোর করতে চান সেটির ইমেজ URL দিতে দেয়, এবং কিছু অপশন যেন তার সাথে যাবে যেমন ছবিটি হাই রেজোলিউশন কিনা, অথবা ছবিতে স্ক্র্যাচ আছে কিনা যা রিমুভ করতে হবে। একবার ব্যবহারকারী এই ইনফরমেশন পূরণ করে এবং ফর্ম সাবমিট করে, এটি একটি POST রিকোয়েস্ট পাঠায় /api/create-এ ফর্ম ডেটার সাথে।
একবার এই রিকোয়েস্ট API-এ পাঠানো হয়ে যাওয়া এবং প্রিডিকশন ইনফরমেশনসহ একটি রেসপন্স পাওয়া যাওয়ার পর, কম্পোনেন্ট একটি পোলিং স্টেটে প্রবেশ করে যা এক সেকেন্ডে একবার /api/poll-এ একটি GET রিকোয়েস্ট পাঠায় যাতে চেক করা যায় প্রিডিকশন সম্পন্ন হয়েছে কিনা। একবার একটি পোলিং রিকোয়েস্ট একটি সফল রেসপন্স ফেরত দেয়, ইন্ডিকেট করে যে Replicate আমাদের কলব্যাক এন্ডপয়েন্টে একটি রিকোয়েস্ট পাঠিয়েছে, আমরা এখন প্রিডিকশন আউটপুট অ্যাক্সেস করতে পারব।
পোলিং চলাকালীন, ফর্ম একটি বাটন প্রদর্শন করে প্রিডিকশন ক্যানসেল করার অপশনসহ। একবার চাপলে, এটি /api/cancel-এ একটি POST রিকোয়েস্ট পাঠায় প্রিডিকশন ডেটা থেকে পাওয়া cancel_url সহ যা আমরা প্রাথমিক সৃষ্টির সময় পেয়েছিলাম।
পোলিং বাস্তবায়নটি একটি কাস্টম হুক ব্যবহার করে যা hooks/useInterval.ts-এ অবস্থিত যা আমাদের React-এর কম্পোনেন্ট লাইফসাইকেলের সাথে সহজে এবং স্মুথলি কাজ করতে দেয়, এবং যেকোনো React কম্পোনেন্টের ভিতরে কলব্যাকসহ ইন্টারভাল হ্যান্ডেল করার একটি আরও সুবিধাজনক উপায় প্রদান করে। আপনি এখানে এবং এখানে এই হук সম্পর্কে আরও পড়তে পারেন যদি আপনি এর বিষয়ে আরও বিস্তারিত জানতে চান।
import { useEffect, useRef } from "react";
function useInterval(callback: () => void, delay: number | null) {
const savedCallback = useRef(callback);
useEffect(() => {
savedCallback.current = callback;
}, [callback]);
useEffect(() => {
if (!delay && delay !== 0) {
return;
}
const id = setInterval(() => savedCallback.current(), delay);
return () => clearInterval(id);
}, [delay]);
}
export default useInterval;
API সেটআপ
API সেটআপ, কিছু ফাইল থেকে গঠিত, যা আমাদের প্রিডিকশন তৈরি এবং ক্যানসেল করতে দেয়, পোল করে চেক করতে দেয় কখন প্রিডিকশন সম্পন্ন, সেইসাথে কলব্যাক স্পেসিফাই করতে দেয় যা Replicate ব্যবহার করবে প্রিডিকশন তাদের এন্ডে সম্পন্ন হলে।
ইমেজ প্রিডিকশন তৈরি
File: pages/api/create.ts
import type { NextApiRequest, NextApiResponse } from "next";
import fetch, { Response } from "node-fetch";
import redis from "../../lib/redis";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== "POST") {
return res.status(400).json({ message: `Invalid request method: ${req.method}.`, });
}
const { image_url, is_hr, has_scratches }: any = req.body;
await fetch("https://api.replicate.com/v1/predictions", {
method: "POST",
headers: {
Authorization: `Token ${process.env.REPLICATE_API_TOKEN}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
version: "c75db81db6cbd809d93cc3b7e7a088a351a3349c9fa02b6d393e35e0d51ba799",
input: {
image: image_url,
HR: is_hr,
with_scratch: has_scratches,
},
webhook_completed: `${process.env.SITE_URL}/api/callback`,
}),
})
.then((res: Response) => res.json())
.then(async (data: any) => {
await redis.set(data.id, data);
return res.status(202).json({ data: data });
})
.catch((error: Error) => {
return res.status(500).json({ message: error.message });
});
}
আমাদের create API এন্ডপয়েন্টে, আমরা প্রথমে একটি সিম্পল চেক করি নিশ্চিত করার জন্য যে ইনকামিং রিকোয়েস্ট মেথড একটি POST রিকোয়েস্ট, এবং যদি না হয়, আমরা একটি সিম্পল ৪০০ রেসপন্স ফেরত দেব। তারপর আমরা Replicate-এ আমাদের Replicate API টোকেনসহ একটি POST রিকোয়েস্ট পাঠাই। রিকোয়েস্ট বডি দেওয়া মডেল version-র প্যারামিটারগুলো নিয়ে গঠিত যা ইন্ডিকেট করে আমরা কোন মডেলে রিকোয়েস্ট পাঠাচ্ছি (এটি আপনি যে মডেলটি ব্যবহার করতে চান সেটির "API" ট্যাবে পাওয়া যায়)। আমরা ফরম থেকে ডেটা নিয়ে মডেলের সাথে যুক্ত প্যারামিটারগুলিও পাস করি।
একবার রিকোয়েস্ট পাঠানো হয়ে যাওয়ার পর, আমরা ফেরত আসা প্রিডিকশন id ব্যবহার করি রেডিসে এটি সংরক্ষণ করতে, এবং প্রিডিকশন ডেটা ফ্রন্টএন্ডে ফেরত দিয়ে পোলিং-এ ব্যবহার করতে দিয়ে রেডিস আইটেমটি পোল করা হয় যতক্ষণ না এটি একটি সম্পন্ন প্রিডিকশন নিয়ে গঠিত হয়।
কলব্যাক
File: pages/api/callback.ts
import type { NextApiRequest, NextApiResponse } from "next";
import redis from "../../lib/redis";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const { body }: any = req;
try {
await redis.set(body.id, body);
return res.status(200).send(body);
} catch (error) {
return res.status(500).json({ error });
}
}
কলব্যাক এন্ডপয়েন্টটি সেটি যা Replicate একটি POST রিকোয়েস্ট পাঠায় যাতে আমাদের জানতে পারেন যে একটি প্রদত্ত প্রিডিকশনের প্রসেসিং ফিনিশ হয়েছে। আমরা এই রিকোয়েস্ট পাওয়ার সময়, রিকোয়েস্ট বডি থেকে প্রিডিকশন ডেটা রিট্রিভ করি, এবং প্রদত্ত রেডিস আইটেমটি সম্পন্ন প্রিডিকশন ডেটা দিয়ে আপডেট করি।
পোলিং
File: pages/api/poll.ts
import type { NextApiRequest, NextApiResponse } from "next";
import redis from "../../lib/redis";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
const { id }: any = req.query;
try {
const data = await redis.get(id);
if (!data) {
return res
.status(404)
.json({ message: "Data for supplied ID not found" });
}
return res.status(200).json(data);
} catch (error: any) {
return res.status(500).json({ message: error.message });
}
}
আমাদের পোলিং সেটআপের জন্য, আমরা রিকোয়েস্ট থেকে id এক্সট্র্যাক্ট করি এবং তারপর চেষ্টা করি সেই আইডেন্টিফায়ারের অধীনে রেডিসে সংরক্ষিত ডেটা রিট্রিভ করতে, এবং যদি কোনো ডেটা না পাওয়া যায়, আমরা একটি ৪০৪ রেসপন্স ফেরত দেই, কিন্তু যদি ডেটা থাকে, আমরা সেটি ২০০ রেসপন্সের অংশ হিসেবে ফেরত দেই।
ক্যানসেল
File: pages/api/cancel.tsx
import type { NextApiRequest, NextApiResponse } from "next";
import fetch, { Response } from "node-fetch";
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== "POST") {
return res.status(400).json({ message: `Invalid request method: ${req.method}.`, });
}
const { cancel_url }: any = req.body;
await fetch(cancel_url, {
method: "POST",
headers: {
Authorization: `Token ${process.env.REPLICATE_API_TOKEN}`,
"Content-Type": "application/json",
},
})
.then((res: Response) => res.json())
.then((data: any) => {
return res.status(202).json({ data: data });
})
.catch((error: Error) => {
return res.status(500).json({ message: error.message });
});
}
প্রিডিকশন ক্যানসেল করার জন্য API এন্ডপয়েন্টটি যথেষ্ট সিম্পল। আমরা সিম্পলি cancel_url এক্সট্র্যাক্ট করি যা ফ্রন্টএন্ড থেকে পাস করা হয়েছে যা নিজে প্রিডিকশন থেকে আসে যা সংরক্ষণ করা হয়েছিল যখন একটি ক্রিয়েশন রিকোয়েস্ট সাবমিট করা হয়েছিল, এবং আমরা সিম্পলি সেটিতে আমাদের Replicate API টোকেনসহ একটি POST রিকোয়েস্ট পাঠাই।
লিবস
আমাদের লিবসের জন্য, আমরা একটি রেডিস ক্লায়েন্ট তৈরি করব যা ট্র্যাকিং-এ ব্যবহৃত হয়
File: lib/redis.ts
import { Redis } from "@upstash/redis";
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL as string,
token: process.env.UPSTASH_REDIS_REST_TOKEN as string,
});
export default redis;
এই অবজেক্টটি অ্যাপ্লিকেশনে পোলিং চলাকালীন ডেটা সংরক্ষণ এবং রিট্রিভ করতে ব্যবহৃত হবে, যাতে আমরা জানতে পারি কখন Replicate থেকে ওয়েবহুক কমপ্লিশন ঘটেছে।
উপসংহার
Replicate-এ varit মডেল উপলব্ধ যা API ব্যবহার করে ব্যবহার করা যায়। Vercel এবং আপস্ট্যাশের সাথে, মেশিন লার্নিং মডেল ব্যবহার করা এবং ব্যবহারযোগ্য ওয়েব অ্যাপ্লিকেশন ডিপ্লয় করা কখনও 그렇게 সহজ ছিল না।
আপনি যদি সম্পূর্ণ রিপোজিটরি দেখতে চান, আপনি এখানে অ্যাক্সেস করতে পারেন।
আরও ডেভেলপমেন্ট
এটি Replicate-এ একটি αρκি সিম্পল মডেল ব্যবহার করার একটি সিম্পল উদাহরণ মাত্র। সিম্পলি ফর্ম প্যারামিটার এবং API-এ ভার্সন পরিবর্তন করে, আপনি সহজে অন্য মডেলে স্যুইচ করতে পারেন যতক্ষণ না আপনার Replicate API টোকেন লিংকড থাকে, আপনি উপলব্ধ সব মডেল কনসাম করতে পারবেন।
আপনি Replicate-এর সব উপলব্ধ মডেল এক্সপ্লোর করতে পারেন এখানে, এবং একবার আপনি যেটি এক্সপেরিমেন্ট করতে চান সেটি পাওয়া যাবে, আপনি "API" ট্যাবে ক্লিক করে এর ব্যবহার দেখতে পারেন। এখানে আপনি Python, cURL, Cog এবং Docker-এর জন্য বাটনও পাবেন, যা আপনাকে মডেল টেস্ট আউট করতে দেয়, কিন্তু এটি প্যারামিটারগুলো কী কী এবং কীভাবে পাঠানো হয় সেটি জানার জন্যও উপযোগী।