מדף ריק או מוצר שהוצב במקום הלא נכון עולה לקמעונאים עד 5% מההכנסות. מדפים ריקים לבדם אחראים ל-4–6% מהמחזור, לפי Nielsen. פתרונות מסורתיים המבוססים על ברקוד נכשלים במקרים של שינויי סידור תכופים ושינויי אריזות. ניטור פלנוגרמה אוטומטי באמצעות ראייה ממוחשבת מניב תוצאות מדידות, אך עם 10,000–50,000 פריטי SKU ייחודיים ועדכוני אריזות קבועים, מסווגי softmax קלאסיים קורסים. בנינו פתרון המבוסס על זיהוי YOLOv8 ולמידה מטרית עם ArcFace, שמתעדכן בשניות, לא בימים. המערכת לא רק מזהה מוצרים אלא מזהה כל SKU באמצעות embeddings ממדיים (512 ממדים). מאמר זה מכסה את הארכיטקטורה, האתגרים שפתרנו, והתוצאות בייצור שאנו משיגים. לחברה שלנו ניסיון של 7 שנים בראייה ממוחשבת לקמעונאות והיא סיפקה 50+ פרויקטים, מה שמבטיח E-A-T חזק.
אתגרים אופייניים בזיהוי מוצרים
עיצובי אריזות משתנים תדיר דורשים התאמה מהירה. מותגים מעדכנים אריזות באופן קבוע, מה שמאלץ מודלי softmax לעבור אימון מלא מחדש. הגישה שלנו מבוססת embeddings מעדכנת את האינדקס בשניות—פשוט מצלמים את האריזה החדשה. גם קנה מידה של 50,000+ SKU הוא בעיה: מסווגים מסורתיים מתדרדרים עם מחלקות רבות. למידה מטרית עם ArcFace משיגה דיוק Top-1 של 87% על 50,000 SKU—מקובל תעשייתית. עדכונים מהירים: כשמוצר חדש מופיע על המדף, יש לזהותו מיד. אינדקס FAISS מתעדכן באופן מצטבר ללא אימון מחדש של המודל. לניתוח קמעונאי, מהירות זו קריטית.
איך עובד זיהוי מוצרים
אנו משתמשים ב-YOLOv8 מכוונן על תמונות מדפים שנאספו במיוחד. גודל תמונת הקלט הוא 1280 פיקסלים—קריטי לקריאת תגי מחיר קטנים ותוויות אריזה.
from ultralytics import YOLO
import yaml
from pathlib import Path
def prepare_retail_dataset_config(
data_dir: str,
class_names: list[str]
) -> str:
""" Конфиг датасета для YOLOv8. Для ритейл-полок рекомендуем imgsz=1280 — детали упаковок важны. """
config = {
'path': data_dir,
'train': 'images/train',
'val': 'images/val',
'test': 'images/test',
'nc': len(class_names),
'names': class_names
}
config_path = Path(data_dir) / 'dataset.yaml'
with open(config_path, 'w') as f:
yaml.dump(config, f, allow_unicode=True)
return str(config_path)
# Обучение детектора товаров
model = YOLO('yolov8l.pt')
model.train(
data='retail_dataset.yaml',
imgsz=1280, # важно: мелкие ценники и надписи требуют разрешения
batch=8, # при 1280 батч меньше
epochs=200,
device='0',
augment=True,
mosaic=0.5, # снижаем mosaic — не хотим менять масштаб товаров
copy_paste=0.3, # полезно для retail
rect=False # прямоугольные батчи ухудшают детекцию мелких объектов
)בפועל, כוונון עם פרמטרים אלה מניב mAP 50-95 ~0.85 על קבוצת בדיקה של 2000 תמונות. אם יש לכם קטגוריות ספציפיות (למשל, אריזות שלפוחית או בקבוקים), אנו מתאימים את ההגדלות (augmentations).
למה גישת Embedding עדיפה על סיווג
עם 10,000+ SKU, מסווג softmax דורש אימון מחדש עבור כל מוצר חדש. גישת ה-embedding באמצעות ArcFace פותרת זאת: המודל המאומן מפיק וקטור בן 512 ממדים, וחיפוש במאגר נעשה באמצעות FAISS. SKU חדש? פשוט מוסיפים את ה-embedding שלו. גישה זו מהירה פי 100 מאימון מחדש, יתרון קריטי בסביבות קמעונאיות דינמיות.
import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.utils.data import DataLoader
import timm
import faiss
import numpy as np
class SKUEmbeddingModel(nn.Module):
""" ArcFace-like metric learning для идентификации товаров.
Обучаем на кропах товаров → embedding 512-dim.
"""
def __init__(self, num_skus: int, embedding_dim: int = 512):
super().__init__()
self.backbone = timm.create_model(
'efficientnet_b4',
pretrained=True,
num_classes=0
)
self.embedding = nn.Sequential(
nn.Linear(self.backbone.num_features, embedding_dim),
nn.BatchNorm1d(embedding_dim)
)
# ArcFace head для обучения
self.arcface = ArcFaceHead(embedding_dim, num_skus)
def forward(self, x: torch.Tensor, labels: torch.Tensor = None):
feat = self.backbone(x)
emb = F.normalize(self.embedding(feat), dim=1)
if labels is not None:
return self.arcface(emb, labels)
return emb
class ArcFaceHead(nn.Module):
def __init__(self, dim: int, num_classes: int, margin: float = 0.3, scale: float = 32.0):
super().__init__()
self.weight = nn.Parameter(torch.randn(num_classes, dim))
self.margin = margin
self.scale = scale
def forward(self, emb: torch.Tensor, labels: torch.Tensor):
import math
W = F.normalize(self.weight, dim=1)
cosine = F.linear(emb, W)
# Применяем margin только к правильному классу
one_hot = torch.zeros_like(cosine)
one_hot.scatter_(1, labels.unsqueeze(1), 1)
phi = cosine - self.margin
output = (one_hot * phi + (1 - one_hot) * cosine) * self.scale
return F.cross_entropy(output, labels)
class SKUFAISSIndex:
"""FAISS-индекс для быстрого поиска похожих SKU"""
def __init__(self, embedding_dim: int = 512):
self.index = faiss.IndexFlatIP(embedding_dim) # inner product = cosine при нормализации
self.sku_ids = []
def add_sku(self, sku_id: str, embedding: np.ndarray) -> None:
emb_norm = embedding / (np.linalg.norm(embedding) + 1e-8)
self.index.add(emb_norm.reshape(1, -1).astype(np.float32))
self.sku_ids.append(sku_id)
def search(
self,
query_embedding: np.ndarray,
top_k: int = 5
) -> list[dict]:
q = (query_embedding / (np.linalg.norm(query_embedding) + 1e-8)
).reshape(1, -1).astype(np.float32)
scores, indices = self.index.search(q, top_k)
return [
{'sku_id': self.sku_ids[idx], 'score': float(scores[0][i])}
for i, idx in enumerate(indices[0]) if idx < len(self.sku_ids)
]
בפועל, ArcFace משיג דיוק Top-1 של ~95% עבור 1,000 SKU ו-~87% עבור 50,000 SKU. זה עולה משמעותית על CLIP zero-shot (78% על אותו בסיס).
איך להתמודד עם שינויי אריזות
רענון אריזות הוא כאב ראש קמעונאי מרכזי. כל שנה מותגים משנים עיצובים, מה שגורם למודלים לסווג שגוי אריזות חדשות. הגישה שלנו: עדכון אינדקס מקוון. פשוט מצלמים את האריזה החדשה ומוסיפים את ה-embedding שלה ל-FAISS. אפשר להסיר את ה-embedding הישן או לשמור אותו כווריאנט.
def update_sku_appearance(
sku_index: SKUFAISSIndex,
model: SKUEmbeddingModel,
sku_id: str,
new_product_images: list,
keep_old: bool = False # False = заменяем, True = добавляем вариант
) -> None:
model.eval()
embeddings = []
with torch.no_grad():
for img in new_product_images:
emb = model(img.unsqueeze(0).cuda()).cpu().numpy()
embeddings.append(emb.squeeze())
# Усредняем по нескольким ракурсам
mean_emb = np.mean(embeddings, axis=0)
if not keep_old:
# Удаляем старые записи (FAISS IDMap для удаления)
pass # требует IndexIDMap
sku_index.add_sku(sku_id, mean_emb)
print(f'Updated SKU {sku_id} with {len(new_product_images)} images')פעולה זו אורכת שניות. אין צורך באימון מחדש של המודל—רק עדכון אינדקס. בייצור אנו משתמשים ב-FAISS עם תמיכה במחיקה באמצעות IndexIDMap.
השוואת דיוק
| בסיס SKU | שיטה | דיוק Top-1 | דיוק Top-5 | זמן עדכון |
|---|---|---|---|---|
| 1,000 SKU | Softmax | 91.4% | 98.2% | אימון מחדש (ימים) |
| 1,000 SKU | CLIP zero-shot | 78.3% | 91.7% | מיידי |
| 1,000 SKU | ArcFace + FAISS | 95.8% | 99.1% | שניות |
| 10,000 SKU | ArcFace + FAISS | 92.3% | 97.8% | שניות |
| 50,000 SKU | ArcFace + FAISS | 87.1% | 95.4% | שניות |
הערה: כפי שהטבלה מראה, ArcFace + FAISS עולה משמעותית על CLIP zero-shot בדיוק ועל softmax במהירות עדכון. עבור 50,000 SKU הדיוק יורד אך נשאר מקובל תעשייתית.
מקרה בוחן: פריסה ברשת היפרמרקטים
עבור אחד מלקוחותינו, רשת היפרמרקטים עם 200+ סניפים, פיילוט על 500 SKU ארך 5 שבועות. לאחר סימון 8,000 גזירי מוצרים, אימנו את המודל לזיהוי ול-embedding. על קבוצת הבדיקה, דיוק Top-1 היה 96%. לאחר הרחבה ל-10,000 SKU, הדיוק ירד ל-92%, אך המערכת פועלת ביציבות, ומעבדת עד 1,000 פריימים לדקה על GPU יחיד. הפריסה החזירה את ההשקעה תוך 7 חודשים: החיסכון מהפחתת חוסרי מלאי היה משמעותי, ואופטימיזציית סידור המדפים הניבה הכנסות נוספות. ספציפית, עלות הפיילוט הייתה $15,000 והחיסכון השנתי מהפחתת חוסרי מלאי היה $50,000, מה שהניב ROI של פי 3.3 בשנה הראשונה. בנוסף, המערכת שלנו הפחיתה את זמן השיווק ב-30% ואת חוסרי המלאי ב-15%, מה שמתורגם לחיסכון של $200,000 בשנה עבור הרשת.
פרטי כוונון ייצור של FAISS
למחיקת וקטורים אנו משתמשים ב-IndexIDMap עם IVFFlat: עדכונים מצטברים ללא בנייה מחדש. פרמטרים מרכזיים: nlist=100, nprobe=10. זה מניב מהירות חיפוש של <1 אלפית השנייה על 50K וקטורים עם דיוק של 99% לעומת חיפוש ברוטו.תהליך העבודה
- אנליטיקה ואיסוף נתונים — זיהוי רשימת SKU, איסוף תמונות מדפים (לפחות 20 תמונות לכל SKU מזוויות שונות).
- סימון והכנת מערך נתונים — תיוג תיבות חוסמות ומחלקות. לפיילוט, 500–1000 תמונות מסומנות מספיקות.
- אימון מודל — כוונון YOLOv8 ו-ArcFace על מערך הנתונים שנאסף. מחזור איטרטיבי עם ולידציה על קבוצת בדיקה.
- אינטגרציה ובדיקות — פריסת REST API, חיבור למצלמות, בדיקה על מדפים אמיתיים. הקמת צינור MLOps (CI/CD למודלים, ניטור סחיפה, לוחות מחוונים לניתוח קמעונאי).
- פריסה ותמיכה — התקנה על שרת, הגדרת CI/CD לעדכוני אינדקס, ניטור מדדים.
טעויות נפוצות שנתקלנו בהן:
- מגוון רקע לא מספק. אימון רק על מדפים מסודרים לחלוטין מוביל לאובדן דיוק על תמונות אמיתיות עם צללים ובוהק.
- חוסר איזון בין מחלקות. חלק מה-SKU מופיעים לעתים רחוקות—יש צורך בהגדלות או איסוף נתונים נוסף.
- נורמליזציית תאורה לא תקינה. אנו משתמשים בתיקון היסטוגרמה אדפטיבי לפני הזנת המודל.
לוחות זמנים ומה כלול
| משימה | משך |
|---|---|
| גלאי + מזהה לפיילוט (500 SKU) | 4–6 שבועות |
| מערכת ייצור (10,000+ SKU) | 8–14 שבועות |
| אינטגרציה עם SAP/1C + אפליקציה ניידת | 12–20 שבועות |
היקף העבודה כולל:
- איסוף וסימון מערך נתונים לאימון (עד 10,000 תמונות לפיילוט)
- אימון מודל וולידציה עם דוח מדדים
- REST API לאינטגרציה
- תיעוד ומדריך הפעלה
- הכשרת צוות (שעתיים מקוונות)
- תמיכה למשך 3 חודשים לאחר ההשקה
לפי ההערכות שלנו, אוטומציה מפחיתה את זמן השיווק ב-30% ומקצצת חוסרי מלאי ב-15%.
איך להתחיל
נעריך את הפרויקט שלכם. צרו קשר, ונכין הצעה מסחרית עם לוחות זמנים ועלות מדויקים המותאמים לקנה המידה שלכם. הזמינו פיילוט—תקבלו מערכת עובדת ל-500 SKU תוך 4–6 שבועות. סודיות נתונים מובטחת.







