Як працює семантичний пошук зображень на пристрої?
Уявіть: користувач вводить «собака на пляжі», і застосунок миттєво виводить всі фото собаки на пляжі — без тегів, без ручного сортування. Це семантичний пошук зображень на основі CLIP (Contrastive Language-Image Pre-training) від OpenAI. Ми реалізуємо його повністю на пристрої: зображення та тексти переводяться в 512-вимірні вектори, а косинусна схожість між ними визначає релевантність. Жодної відправки даних на сервер — приватність користувача зберігається. Наш досвід з MobileCLIP-S0 та векторними БД дозволяє розгорнути таке рішення за 1–4 тижні.
Оцініть можливості для вашого застосунку — напишіть нам, обговоримо деталі. Економія порівняно з хмарним пошуком досягає 80% за рахунок відсутності плати за інференс. On-device CLIP пошук у 5 разів швидший за хмарний завдяки локальному інференсу. Окупність інвестицій в on-device AI зазвичай становить 6–12 місяців. Отримайте консультацію — зв'яжіться з нами.
Архітектура та переваги on-device AI
Пайплайн складається з двох незалежних етапів:
- Індексація (один раз для всієї галереї, потім інкрементально): для кожного фото обчислюємо CLIP Image Embedding (512-вимірний вектор) та зберігаємо в локальну векторну БД.
- Пошук (при кожному запиті користувача): запит користувача перетворюється на CLIP Text Embedding (той самий 512-вимірний вектор), виконується ANN-пошук найближчих векторів у базі, повертаються фото за спаданням косинусної схожості.
| Порівняння | On-device | Cloud |
|---|---|---|
| Латентність | 15-20 ms | 200-500 ms |
| Вартість за 10k запитів | безкоштовно | суттєві витрати |
| Приватність | Дані на пристрої | Дані на сервері |
On-device AI вигідніший за хмарний: відсутність затримки мережі, нульова вартість інференсу, повна приватність користувача. Економія до 80% порівняно з хмарними рішеннями, швидка окупність.
Інтеграція CLIP через CoreML
Apple не включила CLIP у стандартний Vision framework, але Apple ML Research випустила ml-mobileclip — дистильовану версію спеціально для мобільних пристроїв. MobileCLIP-S0: 18 MB, 3–5 ms на інференс зображення на iPhone 14.
MobileCLIP — офіційний репозиторій з кодом.
import CoreML
class MobileCLIPEmbedder {
private let imageEncoder: MobileCLIPImageEncoder
private let textEncoder: MobileCLIPTextEncoder
func embedImage(_ cgImage: CGImage) throws -> [Float] {
let resized = resize(cgImage, to: CGSize(width: 256, height: 256))
let input = MobileCLIPImageInput(image: MLMultiArray(from: resized))
let output = try imageEncoder.prediction(input: input)
return l2Normalize(output.embedding.toFloatArray())
}
func embedText(_ query: String) throws -> [Float] {
let tokens = tokenize(query) // BPE tokenizer
let input = MobileCLIPTextInput(tokens: MLMultiArray(from: tokens))
let output = try textEncoder.prediction(input: input)
return l2Normalize(output.embedding.toFloatArray())
}
}
Tokenizer для CLIP — BPE (Byte Pair Encoding). Swift-реалізація є в репозиторії apple/ml-mobileclip. На Android: ONNX Runtime з MobileCLIP — менш зручно, але працює.
Як інтегрувати CLIP в iOS застосунок?
Покрокова інструкція для впровадження семантичного пошуку:
- Підготувати модель. Завантажте MobileCLIP-S0 з репозиторію Apple ML. Конвертуйте в CoreML за допомогою
coremltools. - Інтегрувати енкодери. Створіть класи для обробки зображень і тексту, як у прикладі вище. Переконайтеся, що нормалізація та токенізація відповідають.
- Налаштувати індексацію. Використовуйте
BGProcessingTaskдля фонової індексації галереї. Зберігайте ембеддінги у векторну БД (наприклад, sqlite-vss). - Реалізувати пошук. При отриманні запиту обчислюйте ембеддінг тексту та виконуйте ANN-пошук. Повертайте відсортовані результати.
Приклад фонової індексації:
class GalleryIndexer {
private var lastIndexedDate: Date {
get { UserDefaults.standard.object(forKey: "lastIndexedDate") as? Date ?? .distantPast }
set { UserDefaults.standard.set(newValue, forKey: "lastIndexedDate") }
}
func indexNewPhotos() async {
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(format: "creationDate > %@", lastIndexedDate as CVarArg)
let newPhotos = PHAsset.fetchAssets(with: .image, options: fetchOptions)
newPhotos.enumerateObjects { [weak self] asset, _, _ in
guard let self else { return }
if let embedding = self.computeEmbedding(for: asset) {
self.vectorDB.insert(assetId: asset.localIdentifier, embedding: embedding)
}
}
lastIndexedDate = Date()
}
}
Пошук виконується за ~20 ms: text embedding (5 ms) + ANN search (15 ms). Результати миттєві.
func search(query: String, topK: Int = 30) async throws -> [PHAsset] {
let textEmbedding = try mobileCLIP.embedText(query)
let results = vectorDB.search(vector: textEmbedding, limit: topK)
let fetchOptions = PHFetchOptions()
fetchOptions.predicate = NSPredicate(
format: "localIdentifier IN %@",
results.map { $0.assetId }
)
let assets = PHAsset.fetchAssets(with: fetchOptions)
let idToScore = Dictionary(uniqueKeysWithValues: results.map { ($0.assetId, $0.score) })
return assets.objects(at: IndexSet(0..<assets.count))
.sorted { idToScore[$0.localIdentifier, default: 0] > idToScore[$1.localIdentifier, default: 0] }
}
Як ми обираємо векторну БД на пристрої?
Для пошуку серед 50 000 векторів потрібен ANN-індекс. Розглянемо три варіанти з конкретними характеристиками:
| Технологія | Продуктивність | Складність інтеграції | Обсяг бази |
|---|---|---|---|
SQLite + sqlite-vss |
15-20 ms на пошук | Середня (SQL-розширення) | 10k-50k |
| FAISS (C++ через JNI/Swift) | 5-10 ms на пошук | Висока (збірка під платформу) | 50k-500k |
| Flat L2 через Accelerate | 15 ms на 10k векторів | Низька (стандартна бібліотека) | до 10k |
SQLite з розширенням sqlite-vss — додає віртуальні таблиці для векторного пошуку. Компактний, працює в embedded режимі:
CREATE VIRTUAL TABLE photo_embeddings USING vss0(embedding(512));
INSERT INTO photo_embeddings(rowid, embedding) VALUES (42, json('[0.1, -0.3, ...]'));
SELECT rowid, distance FROM photo_embeddings WHERE vss_search(embedding, json('[0.2, -0.1, ...]')) LIMIT 20;
Простий flat L2/cosine через Accelerate — для галерей до 10k фото цілком достатньо без спеціалізованого індексу:
func cosineSimilarity(_ a: [Float], _ b: [Float]) -> Float {
var dotProduct: Float = 0
vDSP_dotpr(a, 1, b, 1, &dotProduct, vDSP_Length(a.count))
return dotProduct // Після L2-нормалізації = косинусна схожість
}
Перебір 10000 512-вимірних векторів на iPhone 14 через vDSP_dotpr — ~15 ms. Для галерей до 20k прийнятно.
Багатомовний пошук
CLIP навчений переважно на англійській. Для російського запиту «собака на пляжі» — якість гірша, ніж для «dog on beach». Рішення: переклад запиту через простий словник частих слів або Google Translate API перед embeddings. На практиці достатньо перекласти 100–200 частих запитів без API.
Що входить в роботу і скільки це займе?
| Задача | Терміни |
|---|---|
| Базовий CLIP-пошук з flat index для галерей до 10k | 1–1.5 тижні |
| Масштабована реалізація з ANN-індексом, інкрементальним оновленням, багатомовністю та візуальним пошуком за референс-фото | 3–4 тижні |
Вартість розраховується індивідуально в залежності від складності інтеграції та цільових пристроїв. Отримайте консультацію — зв'яжіться з нами для точної оцінки.
Чому варто довірити це завдання нам?
Ми спеціалізуємося на мобільному ML більше 6 років і реалізували 15+ проєктів з on-device AI. Гарантуємо дотримання App Store Review Guidelines (розділи 4.2 та 5.1) та безпеку користувацьких даних. Надаємо повну документацію та підтримку після впровадження.







