מערכת זיהוי מוצרים על מדף מבוססת בינה מלאכותית

מדף ריק או מוצר שלא במקומו פירושו הכנסות אבודות שקשה לעקוב אחריהן ידנית. אנחנו מפתחים מערכות זיהוי מוצרים מבוססות AI באמצעות ראייה ממוחשבת, המנטרות אוטומטית פלנוגרמות ומזהות כל SKU. הצוות שלנו מספק פרויקטים מסוג מפתח-לצרכן, מביקורת ועד הטמעה ותמיכה שוטפת, ומבטיח פתרון אמין שמתאים לשינויים במבחר.

תחומי פיתוח AI

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית ווב עבור FEEDME
    פיתוח אפליקציית ווב עבור FEEDME
    1365
  • פיתוח חנות מקוונת לחברת FURNORO
    פיתוח חנות מקוונת לחברת FURNORO
    1330
  • עיצוב לוגו לחברת B2B Advance
    עיצוב לוגו לחברת B2B Advance
    776
  • פיתוח אפליקציית ווב עבור Enviok
    פיתוח אפליקציית ווב עבור Enviok
    1077
  • פיתוח לוגו חברת AIDER
    פיתוח לוגו חברת AIDER
    1022
  • פיתוח CRM עבור Chasseurs
    פיתוח CRM עבור Chasseurs
    1113

מדף ריק או מוצר שהוצב במקום הלא נכון עולה לקמעונאים עד 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% לעומת חיפוש ברוטו.

תהליך העבודה

  1. אנליטיקה ואיסוף נתונים — זיהוי רשימת SKU, איסוף תמונות מדפים (לפחות 20 תמונות לכל SKU מזוויות שונות).
  2. סימון והכנת מערך נתונים — תיוג תיבות חוסמות ומחלקות. לפיילוט, 500–1000 תמונות מסומנות מספיקות.
  3. אימון מודל — כוונון YOLOv8 ו-ArcFace על מערך הנתונים שנאסף. מחזור איטרטיבי עם ולידציה על קבוצת בדיקה.
  4. אינטגרציה ובדיקות — פריסת REST API, חיבור למצלמות, בדיקה על מדפים אמיתיים. הקמת צינור MLOps (CI/CD למודלים, ניטור סחיפה, לוחות מחוונים לניתוח קמעונאי).
  5. פריסה ותמיכה — התקנה על שרת, הגדרת 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 שבועות. סודיות נתונים מובטחת.