دستیار لباس در فروشگاه هوشمند-از پرس و جو سبک تا مکان قفسه

تا به حال وارد یک فروشگاه پوشاک شده و آرزو کرده ام که یک سیستم هوشمند فقط بتواند بر اساس سبک ، مناسبت خود بپوشد و از کجا آن را در فروشگاه پیدا کنید؟ این ایده ای بود که این پروژه را برانگیخت. من می خواستم بررسی کنم که چگونه چنین سیستم کیوسک ممکن است کار کند – نه تنها توصیه لباس را بر اساس پرس و جوهای کاربر بلکه هدایت مشتری را به قفسه ای که در آن قرار دارد هدایت می کند.
این سند روند و رویکردی را که من هنگام ساخت این پروژه دنبال کردم ، تشریح می کند. این دیدگاه اولیه ، مشکلات کلیدی که من برای حل آن ، فناوری هایی که من انتخاب کردم و اینکه چگونه سیستماتیک سیستم را به طور سیستماتیک برای توسعه تجزیه کردم ، پوشش می دهد. این همچنین شامل تصمیمات معماری ، تکرارهای طراحی و چگونگی قرار گرفتن هر قسمت در راه حل نهایی یکپارچه است. این یک پروژه اکتشافی است! چگونه کارها کار می کنند
بنابراین ، دو سیستم فرعی وجود دارد:
- توصیه های لباس هوشمند – پرس و جوهای کاربر را درک کرده و پیشنهادات لباس مناسب را بر اساس در دسترس بودن فروشگاه بازیابی می کند.
- پیمایش به آن قفسه ای که مورد انتخاب شده را می توان یافت. (این امر به این دلیل است که به جای تماس با API شخص ثالث ، مانند ایجاد نقشه برای طرح فروشگاه و علامت گذاری قفسه ها ، چگونه می توان ناوبری محلی یا مسیریابی را انجام داد.
برای بخش اول ، یعنی جستجوی مواردی که بر اساس نیاز/پرس و جو مشتری جستجو می شود ، من نمی خواهم کاربر برای نمایش یک محصول خاص جستجو کند – این فقط جستجو است!
من می خواهم نرم افزار بفهمد ، چه نوع مناسبت؟ چه نوع لباسی را می خواهد بپوشد ،
Customer: simple and casual for evening birthday party
چیزی شبیه به آن
این یک نام محصول نیست – این یک هدف است. بنابراین ، سیستم ما باید معنی را درک کند و آن را با موارد مربوطه مطابقت دهد. – به دنبال شباهت معنایی هستید.
اما جستجوی رشته عادی چیزی نیست که به ما کمک کند ، ما باید نتایج تطبیق را بر اساس توضیحات پارچه پیدا کنیم یعنی خود داده های متن است. بنابراین ، ما به دنبال چیزی هستیم که زمینه را درک کند و جستجو کند و یا نزدیکترین توضیحات را که توصیف می کند نوع پرس و جو مشتری را توصیف می کند.
بنابراین ، در اینجا ما به دنبال جستجوی هدف یا زمینه نیستیم نه تطبیق کلمه واقعی کلمه (تطبیق رشته) ، یعنی ما باید معنای داده های متن را ضبط کنیم و مطابق آن مطابقت داشته باشیم.
ما می خواهیم نرم افزار با ضبط معنای پرس و جو و سپس جستجوی موارد مشابه با استفاده از توضیحات مورد ، بفهمد مشتری در تلاش است تا بگوید. خوب
بنابراین ، رایانه ها داده های متن را درک نمی کنند ، یعنی برای درک آن داده های متن ، ما باید داده های متن را با استفاده از مدل جاسازی متن به بردارهای عددی تبدیل کنیم (این همان پردازش زبان طبیعی است).
تعبیه متن چیست؟
تعبیه متن بازنمایی عددی متن است که معنای معنایی را ضبط می کند. تعبیه ها به جای اینکه فقط به کلمات کلیدی نگاه کنیم ، زمینه را درک می کنند – بنابراین دو عبارت که به معنای یک چیز یکسان است ، حتی اگر کلمات متفاوت باشند ، تعبیه های مشابهی دارند.
(در اینجا ، از مدل جاسازی متن Gemini از طریق تماس API استفاده می شود)
بنابراین ، ابتدا توضیحی از وسایل لباس را بر اساس سبک ، نفوذ ، مواد آنها ایجاد می کند. این کار می تواند توسط یک مدل یادگیری عمیق دیگر انجام شود که تصاویر پارچه ای را درک می کند و بر اساس مد ایجاد می شود (از هر مدل از پیش آموزش استفاده کنید یا خود را ایجاد کنید) یا فقط ورودی انسان را ایجاد کنید ، این نگرانی من در حال حاضر نیست ، هر یک از راهها کار خواهد کرد.
بنابراین ، من توضیحات خود را در مورد هر مواردی که تأثیر سبک ، مواد ، حس مد ، مناسبت و قومیت را تعریف می کند ، گرفتم. این توضیحات توسط LLM (مدل زبان بزرگ) با ایجاد الگوی توضیحات مورد نیاز ایجاد شده است. LLM که در اینجا استفاده می شود ، جمینی است ، زیرا خدمات API رایگان را ارائه می دهد.
{
"product_name": "Urban Cotton Hoodie",
"price": 1899,
"essential_attributes": "cotton, relaxed fit, pullover hoodie",
"style_influence": "streetwear, Gen Z, casual",
"intended_use": "daily wear, college, winter layering",
"imageUrl":"/img/urban_hoodie.png",
"description": "Relaxed fit cotton pullover hoodie with streetwear Gen Z casual style for daily wear college winter layering. Suitable for all genders.",
"shelfNumber":1
},
انتخاب پایگاه داده
حال ما باید این موارد و توضیحات را به عنوان داده های بردار ذخیره کنیم ، بنابراین باید از پایگاه داده بردار استفاده کنیم.
من انتخاب کرده ام کرومادب برای بردار من db. این امکان را به شما می دهد تا ابرداده (به عنوان مثال ، نام محصول ، قیمت ، URL تصویر) را با هر تعبیه (داخلی) وصل کنید ، و فیلتر پس از جستجوی را ساده تر می کند.
Chromadb دو نوع ذخیره سازی را ارائه می دهد –
- در حافظه -بانک اطلاعاتی بی ثبات است و در حالی که نمونه فعال است ، در حافظه اجرا می شود.
- ذخیره مداوم -می توانید پایگاه داده خود را در ذخیره دائمی ایجاد و ذخیره کنید و از آن در هر نقطه از پروژه های دیگر نیز استفاده کنید-غیر فرار
بنابراین ، جریان مانند ، توضیحات مربوط به موارد در قالب بردار است و جزئیات دیگری مانند نام محصول ، قیمت ، اطلاعات اندازه و هر چیز دیگری را در ابرداده نگه می دارد.
- ایجاد وکتور تعبیه توضیحات
- ذخیره Product_names ، قیمت ، IMG_URL ، هرگونه جزئیات دیگر در ابرداده
- جستجوی بردار را برای توضیحات اعمال کنید
- نتایج تطبیق بازگشت
جستجوی پایگاه داده بردار
جستجوی بردار چگونه کار می کند؟ در اصل ، از کنجکاوی از ناعادلانه استفاده می کند تا دو کلمه/عبارت با چقدر نزدیک یا مشابه باشد. با استفاده از شباهت كسین ، بردار پرس و جو با تمام بردارهای موردی مقایسه می شود.
نزدیکترین مسابقات بازیابی می شوند.
چرا شباهت Cosine؟
شباهت كسین زاویه بین دو بردار (نادیده گرفتن بزرگی) را اندازه گیری می كند ، و آن را برای درک چگونگی “بستن” دو معانی بدون در نظر گرفتن طول ، ایده آل می كند.
این چیزی است که Rag (بازیابی نسل افزوده) معنی دارد. شما یک سند (متن) ارائه می دهید و می خواهید که مدل شما پاسخ را بازیابی کند تا از آن به عنوان پایه دانش استفاده کند و بر اساس آن ذات را اجرا می کند. تطبیق و دریافت اطلاعات از سند ارائه شده. خوب یک چیز بیشتر از این اتفاق می افتد که اطلاعات بازیابی شده عبارت دقیقی است که ممکن است پاسخ مناسب نباشد ، بنابراین پس از آن به مدل LLM با سریع مشخص شده برای بازگشت یک پاسخ تغذیه می شود. اکنون برای همین ، نرم افزار ما باید معنای کلمات را بداند ، به همین دلیل از مدل های LLM استفاده می کند ، آنها از قبل آموزش داده می شوند تا معنای کلمات و عبارات را بدست آورند و در یافتن زمینه و قصد جملات کارآمد باشند. در غیر این صورت می توانید مدل LLM خود را آموزش دهید ، اما این به یک بانک اطلاعاتی و تخصص بسیار بزرگ نیاز دارد تا عملکرد LLM را به سطح تطبیق برساند و این در نهایت روند توسعه شما را تنگ می کند.
بنابراین ، جستجوی شباهت نتایج تطبیق را بدست می آورد ، خوب. اکنون ما فقط باید هر آنچه را که مشتری پرس و جو از آن درخواست می کند ، به Chromadb ما منتقل کنیم تا نتایج مشابهی ارائه دهد!
چرا فقط تعبیه پرس و جو کافی نیست
در سیستم های جستجوی معنایی مبتنی بر بردار ، اثربخشی بازیابی به شدت به تراز معنایی بین پرس و جو کاربر و توضیحات مورد ذخیره شده بستگی دارد. از آنجا که توضیحات محصول با استفاده از یک ساختار سریع ، لحن و واژگان سازگار (از طریق LLM مانند جمینی) ساخته می شود ، نمایش داده های کاربر – که می تواند گاه به گاه ، بدون ساختار یا ناقص باشد – اغلب با همان فضای متنی مطابقت ندارند.
این عدم تطابق منجر به نمرات شباهت زیر حد متوسط در طول مقایسه بردار می شود.
اضافه کردن لایه تحول پرس و جو
برای پرداختن به این موضوع ، ما معرفی می کنیم لایه تحول پرس و جو که بین ورودی کاربر و فرآیند تعبیه قرار دارد. در اینجا ، یک LLM پرس و جو خام را تفسیر می کند ، هدف اصلی را استخراج می کند ، ویژگی های کلیدی متنی (به عنوان مثال ، سبک ، مناسبت ، تناسب) را مشخص می کند و پرس و جو را به شکلی بازسازی می کند که آینه ، دستور زبان و الگوی سریع ساختاری توضیحات مورد اصلی را نشان می دهد. همچنین خطاهای هجی را برطرف کرده و ایموجی ها/نگارشی را حذف می کند. این باعث کاهش تماس های غیر ضروری LLM می شود و زمان پاسخ را سرعت می بخشد.
def refine_query_for_search(user_input):
refined_query_prompt = (
f"Convert the following customer request into a short, search phrase "
f"suitable for retrieving men's fashion products from a vector database. "
f"Keep it under 10 words and do not include explanations.\n\n"
f"User Request: '{user_input}'\n"
f"Search Phrase:"
)
response = client.models.generate_content(
model="gemini-2.0-flash",
contents=refined_query_prompt,
)
refined_query = response.text
return refined_query
این تحول تضمین می کند که پرس و جو نه تنها از نظر معنایی غنی است بلکه از نظر ساختاری سازگار است-شانس مسابقات با وفاداری بالا را در هنگام جستجوی شباهت بردار افزایش می دهد.
در نتیجه ، این سیستم مواردی را که فقط به طور شل و متناسب با هم مرتبط نیستند ، بلکه از لحاظ متنی و معنایی با آنچه کاربر واقعاً به دنبال آن است ، بازیابی می کند.
نتایج نمونه از جستجو در Chromadb
از ابرداده می توان برای استخراج Product_Name ، قیمت و سایر جزئیات استفاده کرد.
اکنون ، جریان ما به این شکل است ،
این یک قسمت از آن را تکمیل می کند.
بله ، ما لیستی از موارد را دریافت خواهیم کرد و سپس می توانیم زمینه های مورد نیاز را برای نمایش آن در صفحه وب یا هر راه دیگری استخراج کنیم. بعداً در مورد آن بحث خواهیم کرد.
ناوبری و مسیری
اکنون که نتایج را بدست آوردیم ، اجازه می دهیم روی مؤلفه ناوبری یا مسیریابی به آن قفسه که در آن می توان مورد را پیدا کرد ، تمرکز کنیم.
بنابراین ، برای ناوبری من به دنبال ردیابی در زمان واقعی نیستم ، ما فقط به مشتری می گوییم که کدام قفسه و کجا باید به دنبالش باشد ، فقط برای ارائه ایده بلکه به روشی خاص.
هیچ ردیابی مکان در زمان واقعی وجود ندارد ، این بدان معنی است که ما به ردیابی GPS احتیاج نداریم. برای یافتن کوتاهترین مسیر بین دو نقطه ثابت از الگوریتم های ساده مسیر استفاده می شود – این ایده اولیه است.
من می توانستم از تصاویر از پیش طراحی شده از طرح فروشگاهی نقشه برداری شده با هر مسیر قفسه از کیوسک استفاده کنم ، اما این مانند هارد کد شده خواهد بود و در هر صورت ، فروشگاه تصمیم می گیرد محل کیوسک را تغییر دهد-این می تواند به هر دلیلی باشد. سپس باید دوباره همه تصاویر را ایجاد کنیم. – چیز خوبی نیست.
بنابراین اجرای یک الگوریتم یافتن مسیر قابل استفاده است ، می توانیم ورودی ها را به عنوان دو نقطه ارائه دهیم تا حتی اگر طرح فروشگاه تغییر کند ، یافتن مسیر تحت تأثیر قرار نمی گیرد. بله ، تغییرات طرح فروشگاه ما را ملزم می کند تا یک نقشه جدید و یک شبکه/ماتریس جدید را دوباره بسازیم و قفسه ها و دیوارها را دوباره نقشه برداری کنیم. اما ما از دردسر مدیریت نقشه برداری سخت از مسیر در تصاویر جلوگیری کردیم. – بهتر از قبل (بهبود یافته)
اکنون برای ردیابی مسیرها ، من از مسیرهای شبکه استفاده می کنم (اساساً یک ماتریس با اندازه NXN است و الگوریتم های مسیریابی را برای یافتن مسیر از (x1 ، y1) تا (x2 ، y2) اعمال می کنم. این شبکه با اندازه NXN تمام طرح فروشگاه را پوشش می دهد و به ترتیب اطلاعاتی مانند منطقه قابل پیاده روی و دیواره ها/قفسه ها را به ترتیب 0 و 1 ها شامل می شود.
از آنجا که انواع مختلفی از موانع وجود ندارد ، 1s ساده برای مناطق غیر قابل پیاده روی و 0s برای مناطق قابل پیاده روی خوب خواهد بود.
ایجاد نقشه و شبکه
حال ، ما باید یک طرح 2D از فروشگاه ایجاد کنیم و یک شبکه NXN را بر روی آن تولید کنیم. سپس با استفاده از مختصات ، 1s و 0 را مشخص می کند (اگر نقشه خیلی بزرگ باشد ، می توانیم از مدل یادگیری عمیق دیگر استفاده کنیم یا می توانیم از مدل یادگیری عمیق دیگری استفاده کنیم یا به عنوان تصویر به عنوان تصویر ایجاد کنیم و اندازه شبکه NXN را ایجاد کنیم و بر اساس شناسایی موانع و علامت گذاری به هماهنگی های قابل پیاده روی و غیر پیاده روی انجام می دهد (با توجه به گروهی از پیکسل ها به عنوان یک اندازه از اندازه سلول ها ، اندازه سلول ها و اندازه سلول)
ما می توانیم ردیف ها و ستون ها را ترفند کنیم تا با طرح فروشگاه مطابقت داشته باشیم.
در زیر یک راه حل برای علامت گذاری مناطق و قفسه های قابل پیاده روی آورده شده است
در اینجا ، قفسه مانند این است
از آنجا که هر قفسه می تواند فقط مختصات در نظر گرفته شود ، بنابراین هر زمان که می خواهیم یک قفسه را برجسته کنیم ، می توانیم مختصات آن را جستجو کنیم (x ، y)shelf_data = {
1 : [[15,9],[17,11]],
2 : [[7,25],[9,28]],
3 : [[11,25],[12,28]],
4 : [[7,25],[9,28]],
5 : [[8,9],[9,11]],
6 : [[8,12],[9,15]],
7 : [[18,3],[19,4]],
8 : [[15,12],[17,15]],
}
مسیر یابی
در اینجا ، ما به دنبال این هستیم که کوتاهترین مسیر را به یک قفسه برسانیم ، زیرا نمی خواهیم مسیری ممکن را نشان دهیم که به طور ناکارآمد در قفسه های دیگر حلقه ها را حلقه کند. بنابراین ، الگوریتم جستجوی وسعت (BFS) برای یافتن کوتاهترین مسیر بین دو نقطه در یک شبکه بدون وزنی اجرا شده است. از آنجا که هزینه هر حرکت یکنواخت است (یعنی حرکت از یک سلول به سلول همسایه همان هزینه را دارد) ، BFS یک انتخاب بهینه و مناسب است.
در ابتدا ، BFS با ذخیره مسیری که تا کنون به همراه هر گره شبکه بازدید شده انجام می شود ، پیاده سازی شد. این بدان معناست که برای هر نقطه کاوش شده ، لیست کامل مراحل از نقطه شروع تا آن گره ذخیره شده و در حافظه حمل می شود.
مسائل مربوط به BFS وانیل
اگرچه به درستی کار کرد ، اما حافظه کارآمد نبود. همانطور که BFS مسیرهای بیشتر و بیشتر را بررسی می کند ، هر قدم به جلو لیست مسیر فعلی را کپی می کند و یک مختصات دیگر را اضافه می کند. این منجر به:
- استفاده از حافظه زیاد ، به خصوص در شبکه های بزرگ.
- ذخیره سازی اضافی بخش های مسیر همپوشانی.
- عملکرد آهسته تر به دلیل کپی کردن لیست در هر تکرار BFS.
بهینه سازی با ردیابی والدین
برای غلبه بر این محدودیت ها ، ما BFS را با اجرای یک رویکرد ردیابی والدین بهینه کردیم.
به جای حمل کل مسیر برای هر گره ، اکنون والدین (گره قبلی) را برای هر سلول بازدید شده در یک آرایه 2D (شبکه) ذخیره می کنیم. پس از رسیدن به مقصد ، ما مسیر را با قدم زدن به سمت عقب از مقصد تا شروع با استفاده از نشانگرهای والدین ذخیره شده بازسازی می کنیم و سپس نتیجه را معکوس می کنیم تا ترتیب صحیح را بدست آوریم.
آن –
- میزان استفاده از حافظه را به میزان قابل توجهی کاهش می دهد.
- با جلوگیری از کپی کردن لیست مکرر ، عملکرد BFS را بهبود می بخشد.
مثال:
0 0 1
0 1 0
0 0 0
جایی که نقطه شروع = (0/0) و نقطه پایان = (2،2)
مسیر از BFS
(0،0) → (1،0) → (2،0) → (2،1) → (2،2)
مسیر بازسازی
PAREND_TRACKER به این شکل خواهد بود – parentTracker[{1,0}] = {0,0};
parentTracker[{2,0}] = {1,0};
parentTracker[{2,1}] = {2,0};
parentTracker[{2,2}] = {2,1};
پله | گره فعلی (curr ) |
گره والدین (parentTracker[curr] ) |
مسیر تاکنون |
---|---|---|---|
1 | (2،2) | (2،1) | [(2,1)] |
2 | (2،1) | (2،0) | [(2,1), (2,0)] |
3 | (2،0) | (1،0) | [(2,1), (2,0), (1,0)] |
4 | (1،0) | (0،0) | [(2,1), (2,0), (1,0), (0,0)] |
پس از معکوس
(0،0) → (1،0) → (2،0) → (2،1)
رمز کلی
vector GridPathFinder::get_shortestDistanceOpti(coordinates st, coordinates en) {
struct node {
int x, y;
};
vector path;
queue q;
vector distX = { 0,0,1,-1,1,-1 };
vector distY = { 1,-1,0,0,1,-1 };
unordered_map parentTracker;
q.push({ st.first, st.second });
visited[st.first][st.second] = true;
while (!q.empty()) {
node tempNode = q.front();
q.pop();
int x = tempNode.x;
int y = tempNode.y;
if (x == en.first && y == en.second) {
break; // found the shortest path
}
for (int i = 0; i < distX.size(); i++) {
int newX = x + distX[i];
int newY = y + distY[i];
if (isValid(newX, newY)) {
visited[newX][newY] = true;
parentTracker[{newX, newY}] = { x,y };
q.push({ newX,newY });
}
}
}
vector finalPath = {};
coordinates curr = en;
// re-constructing the path using ParentTracker
while(curr!=st) {
// running loop for number of elements in tracker
coordinates temp = parentTracker[curr];
finalPath.push_back(temp);
curr = temp;
}
reverse(finalPath.begin(), finalPath.end());
// resetting visited for next pathFinding
this->GridPathFinder::visited = vector>(grid.size(), vector(grid[0].size(), false));
return finalPath;
}
طراحی کلی پشتی
کار
پرس و جو -> “مدرن اما ساده برای کالج”
من زیاد روی طراحی جلوی تمرکز نکردم ….. فقط کار کردم تا منطق اصلی کار را بدست آورم …