تحتوي مجموعة بيانات Laion-400M على 400 مليون صورة مع تعليقات توضيحية للصور باللغة الإنجليزية. وتوفّر Laion اليوم مجموعة بيانات أكبر من ذلك، لكن العمل معها سيكون مشابهًا.
تتضمن مجموعة البيانات عنوان URL للصورة، وتضمينات لكلٍّ من الصورة والتعليق التوضيحي للصورة، ودرجة تشابه بين الصورة والتعليق التوضيحي للصورة، بالإضافة إلى البيانات الوصفية، مثل عرض الصورة/ارتفاعها، والترخيص، وعلامة NSFW. ويمكننا استخدام مجموعة البيانات لتوضيح البحث التقريبي عن أقرب الجيران في ClickHouse.
تُخزَّن التضمينات والبيانات الوصفية في ملفات منفصلة ضمن البيانات الخام. وتتضمن خطوة إعداد البيانات تنزيل هذه البيانات ودمج الملفات،
ثم تحويلها إلى CSV واستيرادها إلى ClickHouse. يمكنك استخدام البرنامج النصي download.sh التالي لهذا الغرض:
number=${1}
if [[ $number == '' ]]; then
number=1
fi;
wget --tries=100 https://deploy.laion.ai/8f83b608504d46bb81708ec86e912220/embeddings/img_emb/img_emb_${number}.npy # download image embedding
wget --tries=100 https://deploy.laion.ai/8f83b608504d46bb81708ec86e912220/embeddings/text_emb/text_emb_${number}.npy # download text embedding
wget --tries=100 https://deploy.laion.ai/8f83b608504d46bb81708ec86e912220/embeddings/metadata/metadata_${number}.parquet # download metadata
python3 process.py $number # merge files and convert to CSV
تم تعريف النص البرمجي process.py كما يلي:
import pandas as pd
import numpy as np
import os
import sys
str_i = str(sys.argv[1])
npy_file = "img_emb_" + str_i + '.npy'
metadata_file = "metadata_" + str_i + '.parquet'
text_npy = "text_emb_" + str_i + '.npy'
# load all files
im_emb = np.load(npy_file)
text_emb = np.load(text_npy)
data = pd.read_parquet(metadata_file)
# combine files
data = pd.concat([data, pd.DataFrame({"image_embedding" : [*im_emb]}), pd.DataFrame({"text_embedding" : [*text_emb]})], axis=1, copy=False)
# columns to be imported into ClickHouse
data = data[['url', 'caption', 'NSFW', 'similarity', "image_embedding", "text_embedding"]]
# transform np.arrays to lists
data['image_embedding'] = data['image_embedding'].apply(lambda x: x.tolist())
data['text_embedding'] = data['text_embedding'].apply(lambda x: x.tolist())
# this small hack is needed because caption sometimes contains all kind of quotes
data['caption'] = data['caption'].apply(lambda x: x.replace("'", " ").replace('"', " "))
# export data as CSV file
data.to_csv(str_i + '.csv', header=False)
# removed raw data files
os.system(f"rm {npy_file} {metadata_file} {text_npy}")
لبدء مسار إعداد البيانات، نفّذ:
seq 0 409 | xargs -P1 -I{} bash -c './download.sh {}'
مجموعة البيانات مُقسّمة إلى 410 ملفات، ويحتوي كل ملف على حوالي مليون صف. إذا كنت ترغب في العمل على مجموعة فرعية أصغر من البيانات، فما عليك سوى تعديل النطاقات، مثل seq 0 9 | ....
(سكريبت Python أعلاه بطيء جدًا (~2-10 دقائق لكل ملف)، ويستهلك قدرًا كبيرًا من الذاكرة (41 GB لكل ملف)، كما أن ملفات CSV الناتجة كبيرة (10 GB لكل ملف)، لذا توخَّ الحذر. إذا كان لديك ما يكفي من RAM، فزِد قيمة -P1 للحصول على مزيد من التوازي. إذا ظل هذا بطيئًا جدًا، ففكّر في إيجاد آلية أفضل لإدخال البيانات — ربما عبر تحويل ملفات .npy إلى parquet، ثم تنفيذ بقية المعالجة باستخدام ClickHouse.)
لإنشاء جدول في البداية من دون فهارس، شغّل:
CREATE TABLE laion
(
`id` Int64,
`url` String,
`caption` String,
`NSFW` String,
`similarity` Float32,
`image_embedding` Array(Float32),
`text_embedding` Array(Float32)
)
ENGINE = MergeTree
ORDER BY id
لاستيراد ملفات CSV إلى ClickHouse:
INSERT INTO laion FROM INFILE '{path_to_csv_files}/*.csv'
لاحظ أن العمود id هو للتوضيح فقط، ويُعبِّئه البرنامج النصي بقيم غير فريدة.
نفّذ بحث تشابه متجهي بأسلوب الفحص الشامل
لتنفيذ بحث متجهي تقريبي بأسلوب الفحص الشامل، نفّذ:
SELECT url, caption FROM laion ORDER BY cosineDistance(image_embedding, {target:Array(Float32)}) LIMIT 10
target هو مصفوفة تضم 512 عنصرًا، وهو أيضًا معامل للعميل.
وسنستعرض في نهاية المقال طريقة عملية للحصول على مصفوفات كهذه.
أما الآن، فيمكننا استخدام تضمين لصورة عشوائية لمجموعة LEGO بوصفه target.
النتيجة
┌─url───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬─caption──────────────────────────────────────────────────────────────────────────┐
1. │ https://s4.thcdn.com/productimg/600/600/11340490-9914447026352671.jpg │ LEGO Friends: Puppy Treats & Tricks (41304) │
2. │ https://www.avenuedelabrique.com/img/uploads/f20fd44bfa4bd49f2a3a5fad0f0dfed7d53c3d2f.jpg │ Nouveau LEGO Friends 41334 Andrea s Park Performance 2018 │
3. │ http://images.esellerpro.com/2489/I/667/303/3938_box_in.jpg │ 3938 LEGO Andreas Bunny House Girls Friends Heartlake Age 5-12 / 62 Pieces New! │
4. │ http://i.shopmania.org/180x180/7/7f/7f1e1a2ab33cde6af4573a9e0caea61293dfc58d.jpg?u=https%3A%2F%2Fs.s-bol.com%2Fimgbase0%2Fimagebase3%2Fextralarge%2FFC%2F4%2F0%2F9%2F9%2F9200000049789904.jpg │ LEGO Friends Avonturenkamp Boomhuis - 41122 │
5. │ https://s.s-bol.com/imgbase0/imagebase/large/FC/5/5/9/4/1004004011684955.jpg │ LEGO Friends Andrea s Theatershow - 3932 │
6. │ https://www.jucariicucubau.ro/30252-home_default/41445-lego-friends-ambulanta-clinicii-veterinare.jpg │ 41445 - LEGO Friends - Ambulanta clinicii veterinare │
7. │ https://cdn.awsli.com.br/600x1000/91/91201/produto/24833262/234c032725.jpg │ LEGO FRIENDS 41336 EMMA S ART CAFÉ │
8. │ https://media.4rgos.it/s/Argos/6174930_R_SET?$Thumb150$&$Web$ │ more details on LEGO Friends Stephanie s Friendship Cake Set - 41308. │
9. │ https://thumbs4.ebaystatic.com/d/l225/m/mG4k6qAONd10voI8NUUMOjw.jpg │ Lego Friends Gymnast 30400 Polybag 26 pcs │
10. │ http://www.ibrickcity.com/wp-content/gallery/41057/thumbs/thumbs_lego-41057-heartlake-horse-show-friends-3.jpg │ lego-41057-heartlake-horse-show-friends-3 │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────┘
10 rows in set. Elapsed: 4.605 sec. Processed 100.38 million rows, 309.98 GB (21.80 million rows/s., 67.31 GB/s.)
أجرِ بحثًا تقريبيًا عن تشابه المتجهات باستخدام فهرس تشابه المتجهات
لنُعرِّف الآن فهرسَي تشابه متجهات على الجدول.
ALTER TABLE laion ADD INDEX image_index image_embedding TYPE vector_similarity('hnsw', 'cosineDistance', 512, 'bf16', 64, 256)
ALTER TABLE laion ADD INDEX text_index text_embedding TYPE vector_similarity('hnsw', 'cosineDistance', 512, 'bf16', 64, 256)
توضّح الوثائق المعلمات واعتبارات الأداء المتعلقة بإنشاء الفهرس والبحث.
يحدّد تعريف الفهرس أعلاه فهرس HNSW يستخدم “مسافة جيب التمام” بوصفها مقياس المسافة، مع ضبط المعلمة “hnsw_max_connections_per_layer” على 64 والمعلمة “hnsw_candidate_list_size_for_construction” على 256.
يستخدم الفهرس الأعداد العائمة بنصف الدقة (bfloat16) كتكميم لتحسين استخدام الذاكرة.
لبناء الفهرس وتخزينه فعليًا، شغّل هذه العبارات:
ALTER TABLE laion MATERIALIZE INDEX image_index;
ALTER TABLE laion MATERIALIZE INDEX text_index;
قد يستغرق إنشاء الفهرس وحفظه بضع دقائق أو حتى ساعات، وذلك بحسب عدد الصفوف ومعلمات فهرس HNSW.
لإجراء بحث في المتجهات، ما عليك سوى تنفيذ الاستعلام نفسه مرة أخرى:
SELECT url, caption FROM laion ORDER BY cosineDistance(image_embedding, {target:Array(Float32)}) LIMIT 10
النتيجة
┌─url───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┬─caption──────────────────────────────────────────────────────────────────────────┐
1. │ https://s4.thcdn.com/productimg/600/600/11340490-9914447026352671.jpg │ LEGO Friends: Puppy Treats & Tricks (41304) │
2. │ https://www.avenuedelabrique.com/img/uploads/f20fd44bfa4bd49f2a3a5fad0f0dfed7d53c3d2f.jpg │ Nouveau LEGO Friends 41334 Andrea s Park Performance 2018 │
3. │ http://images.esellerpro.com/2489/I/667/303/3938_box_in.jpg │ 3938 LEGO Andreas Bunny House Girls Friends Heartlake Age 5-12 / 62 Pieces New! │
4. │ http://i.shopmania.org/180x180/7/7f/7f1e1a2ab33cde6af4573a9e0caea61293dfc58d.jpg?u=https%3A%2F%2Fs.s-bol.com%2Fimgbase0%2Fimagebase3%2Fextralarge%2FFC%2F4%2F0%2F9%2F9%2F9200000049789904.jpg │ LEGO Friends Avonturenkamp Boomhuis - 41122 │
5. │ https://s.s-bol.com/imgbase0/imagebase/large/FC/5/5/9/4/1004004011684955.jpg │ LEGO Friends Andrea s Theatershow - 3932 │
6. │ https://www.jucariicucubau.ro/30252-home_default/41445-lego-friends-ambulanta-clinicii-veterinare.jpg │ 41445 - LEGO Friends - Ambulanta clinicii veterinare │
7. │ https://cdn.awsli.com.br/600x1000/91/91201/produto/24833262/234c032725.jpg │ LEGO FRIENDS 41336 EMMA S ART CAFÉ │
8. │ https://media.4rgos.it/s/Argos/6174930_R_SET?$Thumb150$&$Web$ │ more details on LEGO Friends Stephanie s Friendship Cake Set - 41308. │
9. │ https://thumbs4.ebaystatic.com/d/l225/m/mG4k6qAONd10voI8NUUMOjw.jpg │ Lego Friends Gymnast 30400 Polybag 26 pcs │
10. │ http://www.ibrickcity.com/wp-content/gallery/41057/thumbs/thumbs_lego-41057-heartlake-horse-show-friends-3.jpg │ lego-41057-heartlake-horse-show-friends-3 │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────┘
10 rows in set. Elapsed: 0.019 sec. Processed 137.27 thousand rows, 24.42 MB (7.38 million rows/s., 1.31 GB/s.)
انخفض زمن استجابة الاستعلام بشكل ملحوظ لأن أقرب الجيران استُرجعوا باستخدام الفهرس المتجهي.
قد يُرجع البحث عن التشابه المتجهي باستخدام فهرس تشابه المتجهات نتائج تختلف اختلافًا طفيفًا عن نتائج الفحص الشامل.
يمكن لفهرس HNSW أن يحقق قيمة استرجاع قريبة من 1 (بنفس دقة الفحص الشامل) مع اختيار معاملات HNSW بعناية وتقييم جودة الفهرس.
إنشاء التضمينات باستخدام UDFs
عادةً ما تكون الحاجة إلى إنشاء تضمينات لصور جديدة أو لتسميات توضيحية جديدة للصور، ثم البحث في البيانات عن أزواج متشابهة من الصور/التسميات التوضيحية للصور. ويمكننا استخدام UDF لإنشاء المتجه target من داخل العميل. ومن المهم استخدام النموذج نفسه لإنشاء البيانات والتضمينات الجديدة الخاصة بعمليات البحث. تستخدم البرامج النصية التالية النموذج ViT-B/32، وهو أيضًا النموذج الذي تستند إليه مجموعة البيانات.
أولاً، احفظ برنامج Python النصي التالي في الدليل user_scripts/ ضمن مسار بيانات ClickHouse، ومنحه صلاحية التنفيذ (chmod +x encode_text.py).
encode_text.py:
#!/usr/bin/python3
#!Note: Change the above python3 executable location if a virtual env is being used.
import clip
import torch
import numpy as np
import sys
if __name__ == '__main__':
device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load("ViT-B/32", device=device)
for text in sys.stdin:
inputs = clip.tokenize(text)
with torch.no_grad():
text_features = model.encode_text(inputs)[0].tolist()
print(text_features)
sys.stdout.flush()
ثم أنشئ encode_text_function.xml في مسار يُشار إليه بواسطة <user_defined_executable_functions_config>/path/to/*_function.xml</user_defined_executable_functions_config> ضمن ملف إعدادات خادم ClickHouse.
<functions>
<function>
<type>executable</type>
<name>encode_text</name>
<return_type>Array(Float32)</return_type>
<argument>
<type>String</type>
<name>text</name>
</argument>
<format>TabSeparated</format>
<command>encode_text.py</command>
<command_read_timeout>1000000</command_read_timeout>
</function>
</functions>
يمكنك الآن ببساطة أن تستخدم:
SELECT encode_text('cat');
سيكون التشغيل الأول بطيئًا لأنه يحمّل النموذج، لكن مرات التشغيل اللاحقة ستكون سريعة. بعد ذلك، يمكننا نسخ الناتج إلى SET param_target=...، ومن ثم كتابة الاستعلامات بسهولة. وبدلًا من ذلك، يمكن استخدام الدالة encode_text() مباشرةً كوسيطة للدالة cosineDistance:
SELECT url
FROM laion
ORDER BY cosineDistance(text_embedding, encode_text('a dog and a cat')) ASC
LIMIT 10
لاحظ أن UDF encode_text() نفسه قد يستغرق بضع ثوانٍ لحساب متجه التضمين وإرجاعه.
يمكن إنشاء تضمين الصور بالطريقة نفسها، ونوفّر نصًا برمجيًا بلغة Python يمكنه إنشاء تضمين لصورة مخزّنة محليًا كملف.
encode_image.py
#!/usr/bin/python3
#!Note: Change the above python3 executable location if a virtual env is being used.
import clip
import torch
import numpy as np
from PIL import Image
import sys
if __name__ == '__main__':
device = "cuda" if torch.cuda.is_available() else "cpu"
model, preprocess = clip.load("ViT-B/32", device=device)
for text in sys.stdin:
image = preprocess(Image.open(text.strip())).unsqueeze(0).to(device)
with torch.no_grad():
image_features = model.encode_image(image)[0].tolist()
print(image_features)
sys.stdout.flush()
encode_image_function.xml
<functions>
<function>
<type>executable_pool</type>
<name>encode_image</name>
<return_type>Array(Float32)</return_type>
<argument>
<type>String</type>
<name>path</name>
</argument>
<format>TabSeparated</format>
<command>encode_image.py</command>
<command_read_timeout>1000000</command_read_timeout>
</function>
</functions>
حمّل صورةً تجريبية للبحث:
# get a random image of a LEGO set
$ wget http://cdn.firstcry.com/brainbees/images/products/thumb/191325a.jpg
ثم شغّل هذا الاستعلام لإنشاء تضمين الصورة أعلاه:
SELECT encode_image('/path/to/your/image');
استعلام البحث الكامل هو :
SELECT
url,
caption
FROM laion
ORDER BY cosineDistance(image_embedding, encode_image('/path/to/your/image')) ASC
LIMIT 10
آخر تعديل في ٢٥ يونيو ٢٠٢٦