Spaces:
Runtime error
Runtime error
Commit
•
a3f0ad9
1
Parent(s):
ac58068
Upload 44 files
Browse files- data/pinecone.ipynb +409 -0
- data/processed/.gitkeep +0 -0
- data/processed/jumia_3650/jumia_3650.json +0 -0
- data/processed/jumia_3650/test.csv +710 -0
- data/processed/jumia_3650/train.csv +0 -0
- image_search_engine/__init__.py +0 -0
- image_search_engine/artifacts/__init__.py +0 -0
- image_search_engine/artifacts/config.json +1 -0
- image_search_engine/artifacts/label_encoder/class_encoder_jumia_3650.pkl +3 -0
- image_search_engine/artifacts/model_staged/index.pkl +3 -0
- image_search_engine/artifacts/model_staged/model.pt +3 -0
- image_search_engine/artifacts/weights/Loss0.6555_epoch3.bin +3 -0
- image_search_engine/data/__init__.py +1 -0
- image_search_engine/data/base_data_module.py +39 -0
- image_search_engine/data/jumia_3650_dataset.py +60 -0
- image_search_engine/data/utils.py +16 -0
- image_search_engine/evaluation/__init__.py +0 -0
- image_search_engine/image_search_engine.egg-info/PKG-INFO +5 -0
- image_search_engine/image_search_engine.egg-info/SOURCES.txt +20 -0
- image_search_engine/image_search_engine.egg-info/dependency_links.txt +1 -0
- image_search_engine/image_search_engine.egg-info/top_level.txt +6 -0
- image_search_engine/metadata/__init__.py +1 -0
- image_search_engine/metadata/jumia_3650.py +19 -0
- image_search_engine/metadata/shared.py +4 -0
- image_search_engine/models/__init__.py +3 -0
- image_search_engine/models/arc_margin_product.py +60 -0
- image_search_engine/models/base.py +200 -0
- image_search_engine/models/efficientnet_ns.py +48 -0
- image_search_engine/models/gem_pooling.py +30 -0
- image_search_engine/product_image_search.py +64 -0
- image_search_engine/tests/__init__.py +0 -0
- image_search_engine/tests/test_img/1.jpg +0 -0
- image_search_engine/tests/test_img/2.jpg +0 -0
- image_search_engine/tests/test_img/3.jpg +0 -0
- image_search_engine/tests/test_img/4.jpg +0 -0
- image_search_engine/tests/test_img/5.jpg +0 -0
- image_search_engine/tests/test_img/6.jpg +0 -0
- image_search_engine/tests/test_img/7.jpg +0 -0
- image_search_engine/tests/test_img/8.jpg +0 -0
- image_search_engine/tests/test_img/Shark2.jpg +0 -0
- image_search_engine/tests/test_img/smart_band.jpg +0 -0
- image_search_engine/tests/test_img/smart_band_2.jpg +0 -0
- image_search_engine/tests/test_img/sony.png +0 -0
- image_search_engine/utils.py +48 -0
data/pinecone.ipynb
ADDED
@@ -0,0 +1,409 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"cells": [
|
3 |
+
{
|
4 |
+
"cell_type": "code",
|
5 |
+
"execution_count": 1,
|
6 |
+
"id": "small-delaware",
|
7 |
+
"metadata": {},
|
8 |
+
"outputs": [
|
9 |
+
{
|
10 |
+
"name": "stdout",
|
11 |
+
"output_type": "stream",
|
12 |
+
"text": [
|
13 |
+
"\u001b[31mERROR: After October 2020 you may experience errors when installing or updating packages. This is because pip will change the way that it resolves dependency conflicts.\n",
|
14 |
+
"\n",
|
15 |
+
"We recommend you use --use-feature=2020-resolver to test your packages with the new resolver before it becomes the default.\n",
|
16 |
+
"\n",
|
17 |
+
"mediapipe 0.8.1 requires opencv-python, which is not installed.\n",
|
18 |
+
"djitellopy 2.4.0 requires opencv-python, which is not installed.\n",
|
19 |
+
"umojahack2023 0.1 requires numpy==1.23.5, but you'll have numpy 1.24.4 which is incompatible.\n",
|
20 |
+
"tensorflow 2.11.0 requires gast<=0.4.0,>=0.2.1, but you'll have gast 0.5.3 which is incompatible.\n",
|
21 |
+
"streamlit 1.3.1 requires click<8.0,>=7.0, but you'll have click 8.1.3 which is incompatible.\n",
|
22 |
+
"pandas-profiling 3.2.0 requires joblib~=1.1.0, but you'll have joblib 1.2.0 which is incompatible.\n",
|
23 |
+
"pandas-profiling 3.2.0 requires markupsafe~=2.1.1, but you'll have markupsafe 2.0.1 which is incompatible.\n",
|
24 |
+
"mediapipe 0.8.1 requires numpy==1.19.3, but you'll have numpy 1.24.4 which is incompatible.\n",
|
25 |
+
"huggingface-hub 0.14.1 requires packaging>=20.9, but you'll have packaging 20.8 which is incompatible.\n",
|
26 |
+
"google-api-core 2.11.0 requires protobuf!=3.20.0,!=3.20.1,!=4.21.0,!=4.21.1,!=4.21.2,!=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev,>=3.19.5, but you'll have protobuf 3.19.3 which is incompatible.\u001b[0m\n"
|
27 |
+
]
|
28 |
+
}
|
29 |
+
],
|
30 |
+
"source": [
|
31 |
+
"!pip install -qU pinecone-client \\\n",
|
32 |
+
" tqdm \\\n",
|
33 |
+
" httpimport \\\n",
|
34 |
+
" requests"
|
35 |
+
]
|
36 |
+
},
|
37 |
+
{
|
38 |
+
"cell_type": "code",
|
39 |
+
"execution_count": 2,
|
40 |
+
"id": "legal-course",
|
41 |
+
"metadata": {},
|
42 |
+
"outputs": [
|
43 |
+
{
|
44 |
+
"name": "stderr",
|
45 |
+
"output_type": "stream",
|
46 |
+
"text": [
|
47 |
+
"/Users/paul/miniconda3/lib/python3.8/site-packages/pinecone/index.py:4: TqdmExperimentalWarning: Using `tqdm.autonotebook.tqdm` in notebook mode. Use `tqdm.tqdm` instead to force console mode (e.g. in jupyter console)\n",
|
48 |
+
" from tqdm.autonotebook import tqdm\n"
|
49 |
+
]
|
50 |
+
}
|
51 |
+
],
|
52 |
+
"source": [
|
53 |
+
"import os\n",
|
54 |
+
"import requests\n",
|
55 |
+
"\n",
|
56 |
+
"import tqdm\n",
|
57 |
+
"import httpimport\n",
|
58 |
+
"import pinecone\n",
|
59 |
+
"import numpy as np\n",
|
60 |
+
"from PIL import Image\n",
|
61 |
+
"\n",
|
62 |
+
"DATA_DIRECTORY = 'tmp'\n",
|
63 |
+
"INDEX_NAME = 'jumia-product-search'\n",
|
64 |
+
"INDEX_DIMENSION = 512\n",
|
65 |
+
"BATCH_SIZE=200"
|
66 |
+
]
|
67 |
+
},
|
68 |
+
{
|
69 |
+
"cell_type": "code",
|
70 |
+
"execution_count": 3,
|
71 |
+
"id": "political-robertson",
|
72 |
+
"metadata": {},
|
73 |
+
"outputs": [],
|
74 |
+
"source": [
|
75 |
+
"PINECONE_API_KEY = \"22e8b95b-18a6-42b4-8824-dc65f08a60e1\"\n",
|
76 |
+
"PINECONE_ENV = \"us-west1-gcp-free\""
|
77 |
+
]
|
78 |
+
},
|
79 |
+
{
|
80 |
+
"cell_type": "code",
|
81 |
+
"execution_count": 4,
|
82 |
+
"id": "contained-financing",
|
83 |
+
"metadata": {},
|
84 |
+
"outputs": [],
|
85 |
+
"source": [
|
86 |
+
"pinecone.init(api_key= PINECONE_API_KEY,\n",
|
87 |
+
" environment=PINECONE_ENV)\n"
|
88 |
+
]
|
89 |
+
},
|
90 |
+
{
|
91 |
+
"cell_type": "code",
|
92 |
+
"execution_count": 7,
|
93 |
+
"id": "biblical-decision",
|
94 |
+
"metadata": {},
|
95 |
+
"outputs": [
|
96 |
+
{
|
97 |
+
"data": {
|
98 |
+
"text/plain": [
|
99 |
+
"{'dimension': 512,\n",
|
100 |
+
" 'index_fullness': 0.0,\n",
|
101 |
+
" 'namespaces': {},\n",
|
102 |
+
" 'total_vector_count': 0}"
|
103 |
+
]
|
104 |
+
},
|
105 |
+
"execution_count": 7,
|
106 |
+
"metadata": {},
|
107 |
+
"output_type": "execute_result"
|
108 |
+
}
|
109 |
+
],
|
110 |
+
"source": [
|
111 |
+
"index_name = 'jumia-product-embeddings'\n",
|
112 |
+
"\n",
|
113 |
+
"if index_name not in pinecone.list_indexes():\n",
|
114 |
+
" # now create the new index\n",
|
115 |
+
" pinecone.create_index(\n",
|
116 |
+
" index_name,\n",
|
117 |
+
" dimension= 512, # 768\n",
|
118 |
+
" metric='cosine',\n",
|
119 |
+
" pod_type='s1',\n",
|
120 |
+
" pods=1\n",
|
121 |
+
" )\n",
|
122 |
+
"\n",
|
123 |
+
"# connect to index\n",
|
124 |
+
"index = pinecone.Index(index_name)\n",
|
125 |
+
"# then check index status\n",
|
126 |
+
"index.describe_index_stats()"
|
127 |
+
]
|
128 |
+
},
|
129 |
+
{
|
130 |
+
"cell_type": "code",
|
131 |
+
"execution_count": 8,
|
132 |
+
"id": "advance-composite",
|
133 |
+
"metadata": {},
|
134 |
+
"outputs": [],
|
135 |
+
"source": [
|
136 |
+
"import joblib\n",
|
137 |
+
"\n",
|
138 |
+
"\n",
|
139 |
+
"EMBED_FILE = \"../image_search_engine/artifacts/embeddings/embed_2023-07-09_15-17-45.pkl\"\n",
|
140 |
+
"\n",
|
141 |
+
"\n",
|
142 |
+
"def load_serialized_object(file_path):\n",
|
143 |
+
" try:\n",
|
144 |
+
" obj = joblib.load(file_path)\n",
|
145 |
+
" return obj\n",
|
146 |
+
" except FileNotFoundError:\n",
|
147 |
+
" print(f\"File not found: {file_path}\")\n",
|
148 |
+
" except Exception as e:\n",
|
149 |
+
" print(f\"Error loading serialized object: {str(e)}\")"
|
150 |
+
]
|
151 |
+
},
|
152 |
+
{
|
153 |
+
"cell_type": "code",
|
154 |
+
"execution_count": 12,
|
155 |
+
"id": "lasting-wyoming",
|
156 |
+
"metadata": {},
|
157 |
+
"outputs": [],
|
158 |
+
"source": [
|
159 |
+
"embeddings = load_serialized_object(EMBED_FILE)"
|
160 |
+
]
|
161 |
+
},
|
162 |
+
{
|
163 |
+
"cell_type": "code",
|
164 |
+
"execution_count": 48,
|
165 |
+
"id": "dramatic-superintendent",
|
166 |
+
"metadata": {},
|
167 |
+
"outputs": [],
|
168 |
+
"source": [
|
169 |
+
"embeddings = [embed.tolist() for embed in embeddings]\n"
|
170 |
+
]
|
171 |
+
},
|
172 |
+
{
|
173 |
+
"cell_type": "code",
|
174 |
+
"execution_count": 10,
|
175 |
+
"id": "formal-million",
|
176 |
+
"metadata": {},
|
177 |
+
"outputs": [
|
178 |
+
{
|
179 |
+
"data": {
|
180 |
+
"text/plain": [
|
181 |
+
"2941"
|
182 |
+
]
|
183 |
+
},
|
184 |
+
"execution_count": 10,
|
185 |
+
"metadata": {},
|
186 |
+
"output_type": "execute_result"
|
187 |
+
}
|
188 |
+
],
|
189 |
+
"source": [
|
190 |
+
"len(embed)"
|
191 |
+
]
|
192 |
+
},
|
193 |
+
{
|
194 |
+
"cell_type": "code",
|
195 |
+
"execution_count": 15,
|
196 |
+
"id": "equipped-conversion",
|
197 |
+
"metadata": {},
|
198 |
+
"outputs": [],
|
199 |
+
"source": [
|
200 |
+
"import json\n",
|
201 |
+
"def load_json_file(file_path):\n",
|
202 |
+
" with open(file_path, \"r\") as file:\n",
|
203 |
+
" data = json.load(file)\n",
|
204 |
+
" return data"
|
205 |
+
]
|
206 |
+
},
|
207 |
+
{
|
208 |
+
"cell_type": "code",
|
209 |
+
"execution_count": 16,
|
210 |
+
"id": "veterinary-greenhouse",
|
211 |
+
"metadata": {},
|
212 |
+
"outputs": [],
|
213 |
+
"source": [
|
214 |
+
"json_data = load_json_file(\"processed/jumia_3650/jumia_3650.json\")"
|
215 |
+
]
|
216 |
+
},
|
217 |
+
{
|
218 |
+
"cell_type": "code",
|
219 |
+
"execution_count": 19,
|
220 |
+
"id": "important-literacy",
|
221 |
+
"metadata": {
|
222 |
+
"collapsed": true,
|
223 |
+
"jupyter": {
|
224 |
+
"outputs_hidden": true
|
225 |
+
}
|
226 |
+
},
|
227 |
+
"outputs": [
|
228 |
+
{
|
229 |
+
"data": {
|
230 |
+
"application/vnd.jupyter.widget-view+json": {
|
231 |
+
"model_id": "8b01efeef77e42268c86219a3dab6a5a",
|
232 |
+
"version_major": 2,
|
233 |
+
"version_minor": 0
|
234 |
+
},
|
235 |
+
"text/plain": [
|
236 |
+
" 0%| | 0/5 [00:00<?, ?it/s]"
|
237 |
+
]
|
238 |
+
},
|
239 |
+
"metadata": {},
|
240 |
+
"output_type": "display_data"
|
241 |
+
},
|
242 |
+
{
|
243 |
+
"name": "stdout",
|
244 |
+
"output_type": "stream",
|
245 |
+
"text": [
|
246 |
+
"['JUMIA3650.0', array([ 0.01932903, -1.791061 , 1.3284612 ], dtype=float32), {'filename': 'product_ear_pods_401_0.jpg', 'product_id': 'product_ear_pods_401', 'product_category': 'ear_pods', 'product_name': 'Power Pod Fingerprint Touch Bluetooth 5.0 Wireless Stereo Hear Phone Pod', 'product_image_url': 'https://ng.jumia.is/unsafe/fit-in/680x680/filters:fill(white)/product/66/4640212/1.jpg?9552', 'product_url': 'https://www.jumia.com.ng/power-pod-fingerprint-touch-bluetooth-5.0-wireless-stereo-hear-phone-pod-212046466.html', 'discounted_price': '₦ 6,500', 'original_price': '₦ 8,500'}]\n",
|
247 |
+
"['JUMIA3650.1', array([ 0.12243042, -1.9603508 , 2.0345879 ], dtype=float32), {'filename': 'product_headset_671_3.jpg', 'product_id': 'product_headset_671', 'product_category': 'headset', 'product_name': 'Solar Charging Bluetooth Earphone Wireless TWS Headset', 'product_image_url': 'https://ng.jumia.is/unsafe/fit-in/680x680/filters:fill(white)/product/43/4238202/1.jpg?0528', 'product_url': 'https://www.jumia.com.ng/generic-solar-charging-bluetooth-earphone-wireless-tws-headset-202832434.html', 'discounted_price': '₦ 8,190', 'original_price': '₦ 13,000'}]\n",
|
248 |
+
"['JUMIA3650.2', array([-0.71607244, -1.5286068 , 1.0747794 ], dtype=float32), {'filename': 'product_ear_pods_406_1.jpg', 'product_id': 'product_ear_pods_406', 'product_category': 'ear_pods', 'product_name': 'Wireless 5.0 Bluetooth Headset 8D Stereo Hear Pod', 'product_image_url': 'https://ng.jumia.is/unsafe/fit-in/680x680/filters:fill(white)/product/82/4189211/1.jpg?5980', 'product_url': 'https://www.jumia.com.ng/generic-wireless-5.0-bluetooth-headset-8d-stereo-hear-pod-112981428.html', 'discounted_price': '₦ 7,900', 'original_price': '₦ 10,000'}]\n",
|
249 |
+
"['JUMIA3650.3', array([-1.2911813 , -0.97381216, 0.1008972 ], dtype=float32), {'filename': 'product_ear_pods_524_0.jpg', 'product_id': 'product_ear_pods_524', 'product_category': 'ear_pods', 'product_name': 'Foam cover for Apple AirPods Max', 'product_image_url': 'https://ng.jumia.is/unsafe/fit-in/680x680/filters:fill(white)/product/19/8334991/1.jpg?4544', 'product_url': 'https://www.jumia.com.ng/generic-foam-cover-for-apple-airpods-max-199433891.html', 'discounted_price': '₦ 12,960', 'original_price': '₦ 15,552'}]\n",
|
250 |
+
"['JUMIA3650.4', array([ 0.7770873, -1.4093312, 0.375482 ], dtype=float32), {'filename': 'product_headset_672_6.jpg', 'product_id': 'product_headset_672', 'product_category': 'headset', 'product_name': 'I12TWS Wireless Bluetooth Earphone Earbuds For IOS/Android', 'product_image_url': 'https://ng.jumia.is/unsafe/fit-in/680x680/filters:fill(white)/product/28/177079/1.jpg?2017', 'product_url': 'https://www.jumia.com.ng/generic-i12tws-wireless-bluetooth-earphone-earbuds-for-iosandroid-97077182.html', 'discounted_price': '₦ 3,300', 'original_price': '₦ 5,704'}]\n"
|
251 |
+
]
|
252 |
+
}
|
253 |
+
],
|
254 |
+
"source": [
|
255 |
+
"from tqdm.auto import tqdm\n",
|
256 |
+
"\n",
|
257 |
+
"batch_size = 300\n",
|
258 |
+
"n_embed = len(embeddings) # number of records to index from each language\n",
|
259 |
+
"\n",
|
260 |
+
"for i in tqdm(range(0, 5)):\n",
|
261 |
+
" embed = embeddings[i]\n",
|
262 |
+
" id_ = f\"JUMIA3650.{i}\"\n",
|
263 |
+
" metadata = json_data[i]\n",
|
264 |
+
" \n",
|
265 |
+
" print([id_, embed[:3], metadata])\n",
|
266 |
+
"# index.upsert(zip(ids, embeds, metadata))"
|
267 |
+
]
|
268 |
+
},
|
269 |
+
{
|
270 |
+
"cell_type": "code",
|
271 |
+
"execution_count": 49,
|
272 |
+
"id": "unnecessary-field",
|
273 |
+
"metadata": {},
|
274 |
+
"outputs": [
|
275 |
+
{
|
276 |
+
"data": {
|
277 |
+
"application/vnd.jupyter.widget-view+json": {
|
278 |
+
"model_id": "a697e31ff7a14a49a8138cd2db6eafc4",
|
279 |
+
"version_major": 2,
|
280 |
+
"version_minor": 0
|
281 |
+
},
|
282 |
+
"text/plain": [
|
283 |
+
" 0%| | 0/10 [00:00<?, ?it/s]"
|
284 |
+
]
|
285 |
+
},
|
286 |
+
"metadata": {},
|
287 |
+
"output_type": "display_data"
|
288 |
+
}
|
289 |
+
],
|
290 |
+
"source": [
|
291 |
+
"batch_size = 300\n",
|
292 |
+
"n_embed = len(embeddings) # number of records to index from each language\n",
|
293 |
+
"\n",
|
294 |
+
"\n",
|
295 |
+
"\n",
|
296 |
+
"upsert = []\n",
|
297 |
+
"for i in tqdm(range(0, n_embed, batch_size)):\n",
|
298 |
+
" ids = [f\"JUMIA_3650.{idx}\" for idx in range(i, i+batch_size)]\n",
|
299 |
+
" batch_embeddings = embeddings[i:i+batch_size]\n",
|
300 |
+
" batch_metadata = json_data[i:i+batch_size]\n",
|
301 |
+
" \n",
|
302 |
+
" index.upsert(zip(ids, batch_embeddings, batch_metadata))"
|
303 |
+
]
|
304 |
+
},
|
305 |
+
{
|
306 |
+
"cell_type": "code",
|
307 |
+
"execution_count": 50,
|
308 |
+
"id": "friendly-mount",
|
309 |
+
"metadata": {},
|
310 |
+
"outputs": [
|
311 |
+
{
|
312 |
+
"data": {
|
313 |
+
"text/plain": [
|
314 |
+
"{'dimension': 512,\n",
|
315 |
+
" 'index_fullness': 0.0,\n",
|
316 |
+
" 'namespaces': {'': {'vector_count': 2941}},\n",
|
317 |
+
" 'total_vector_count': 2941}"
|
318 |
+
]
|
319 |
+
},
|
320 |
+
"execution_count": 50,
|
321 |
+
"metadata": {},
|
322 |
+
"output_type": "execute_result"
|
323 |
+
}
|
324 |
+
],
|
325 |
+
"source": [
|
326 |
+
"index.describe_index_stats()"
|
327 |
+
]
|
328 |
+
},
|
329 |
+
{
|
330 |
+
"cell_type": "code",
|
331 |
+
"execution_count": 41,
|
332 |
+
"id": "lesser-outside",
|
333 |
+
"metadata": {},
|
334 |
+
"outputs": [
|
335 |
+
{
|
336 |
+
"data": {
|
337 |
+
"text/plain": [
|
338 |
+
"300"
|
339 |
+
]
|
340 |
+
},
|
341 |
+
"execution_count": 41,
|
342 |
+
"metadata": {},
|
343 |
+
"output_type": "execute_result"
|
344 |
+
}
|
345 |
+
],
|
346 |
+
"source": [
|
347 |
+
"len(embeddings[i:i+batch_size])"
|
348 |
+
]
|
349 |
+
},
|
350 |
+
{
|
351 |
+
"cell_type": "code",
|
352 |
+
"execution_count": 32,
|
353 |
+
"id": "alpine-production",
|
354 |
+
"metadata": {},
|
355 |
+
"outputs": [
|
356 |
+
{
|
357 |
+
"data": {
|
358 |
+
"text/plain": [
|
359 |
+
"('JUMIA_3650.302',\n",
|
360 |
+
" {'filename': 'product_usb_hub_181_2.jpg',\n",
|
361 |
+
" 'product_id': 'product_usb_hub_181',\n",
|
362 |
+
" 'product_category': 'usb_hub',\n",
|
363 |
+
" 'product_name': '4 Ports USB Hub High Speed Adapter For DESKTOP LAPTOP Black Black',\n",
|
364 |
+
" 'product_image_url': 'https://ng.jumia.is/unsafe/fit-in/680x680/filters:fill(white)/product/42/215709/1.jpg?7924',\n",
|
365 |
+
" 'product_url': 'https://www.jumia.com.ng/generic-4-ports-usb-hub-high-speed-adapter-for-desktop-laptop-black-black-90751224.html',\n",
|
366 |
+
" 'discounted_price': '₦ 2,355',\n",
|
367 |
+
" 'original_price': '₦ 3,533'})"
|
368 |
+
]
|
369 |
+
},
|
370 |
+
"execution_count": 32,
|
371 |
+
"metadata": {},
|
372 |
+
"output_type": "execute_result"
|
373 |
+
}
|
374 |
+
],
|
375 |
+
"source": [
|
376 |
+
"upsert[1][2]"
|
377 |
+
]
|
378 |
+
},
|
379 |
+
{
|
380 |
+
"cell_type": "code",
|
381 |
+
"execution_count": null,
|
382 |
+
"id": "mobile-printer",
|
383 |
+
"metadata": {},
|
384 |
+
"outputs": [],
|
385 |
+
"source": []
|
386 |
+
}
|
387 |
+
],
|
388 |
+
"metadata": {
|
389 |
+
"kernelspec": {
|
390 |
+
"display_name": "Python 3",
|
391 |
+
"language": "python",
|
392 |
+
"name": "python3"
|
393 |
+
},
|
394 |
+
"language_info": {
|
395 |
+
"codemirror_mode": {
|
396 |
+
"name": "ipython",
|
397 |
+
"version": 3
|
398 |
+
},
|
399 |
+
"file_extension": ".py",
|
400 |
+
"mimetype": "text/x-python",
|
401 |
+
"name": "python",
|
402 |
+
"nbconvert_exporter": "python",
|
403 |
+
"pygments_lexer": "ipython3",
|
404 |
+
"version": "3.8.5"
|
405 |
+
}
|
406 |
+
},
|
407 |
+
"nbformat": 4,
|
408 |
+
"nbformat_minor": 5
|
409 |
+
}
|
data/processed/.gitkeep
ADDED
File without changes
|
data/processed/jumia_3650/jumia_3650.json
ADDED
The diff for this file is too large to render.
See raw diff
|
|
data/processed/jumia_3650/test.csv
ADDED
@@ -0,0 +1,710 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
filepath,filename,class
|
2 |
+
index/product_usb_hub_36_2.jpg,product_usb_hub_36_2.jpg,usb_hub
|
3 |
+
index/product_office_chairs_1324_1.jpg,product_office_chairs_1324_1.jpg,office_chairs
|
4 |
+
index/product_mouse_229_5.jpg,product_mouse_229_5.jpg,mouse
|
5 |
+
index/product_ear_pods_552_1.jpg,product_ear_pods_552_1.jpg,ear_pods
|
6 |
+
index/product_usb_hub_16_5.jpg,product_usb_hub_16_5.jpg,usb_hub
|
7 |
+
index/product_mouse_203_2.jpg,product_mouse_203_2.jpg,mouse
|
8 |
+
index/product_backpack_828_1.jpg,product_backpack_828_1.jpg,backpack
|
9 |
+
index/product_ear_pods_449_1.jpg,product_ear_pods_449_1.jpg,ear_pods
|
10 |
+
index/product_backpack_854_6.jpg,product_backpack_854_6.jpg,backpack
|
11 |
+
index/product_wrist_watch_1030_2.jpg,product_wrist_watch_1030_2.jpg,wrist_watch
|
12 |
+
index/product_flash_drive_1411_5.jpg,product_flash_drive_1411_5.jpg,flash_drive
|
13 |
+
index/product_backpack_865_3.jpg,product_backpack_865_3.jpg,backpack
|
14 |
+
index/product_ear_pods_421_5.jpg,product_ear_pods_421_5.jpg,ear_pods
|
15 |
+
index/product_mouse_263_0.jpg,product_mouse_263_0.jpg,mouse
|
16 |
+
index/product_wrist_watch_1049_0.jpg,product_wrist_watch_1049_0.jpg,wrist_watch
|
17 |
+
index/product_usb_hub_24_0.jpg,product_usb_hub_24_0.jpg,usb_hub
|
18 |
+
index/product_wrist_watch_1189_2.jpg,product_wrist_watch_1189_2.jpg,wrist_watch
|
19 |
+
index/product_backpack_885_4.jpg,product_backpack_885_4.jpg,backpack
|
20 |
+
index/product_wrist_watch_1048_1.jpg,product_wrist_watch_1048_1.jpg,wrist_watch
|
21 |
+
index/product_mouse_203_0.jpg,product_mouse_203_0.jpg,mouse
|
22 |
+
index/product_backpack_839_2.jpg,product_backpack_839_2.jpg,backpack
|
23 |
+
index/product_backpack_889_2.jpg,product_backpack_889_2.jpg,backpack
|
24 |
+
index/product_mouse_203_7.jpg,product_mouse_203_7.jpg,mouse
|
25 |
+
index/product_wrist_watch_1026_4.jpg,product_wrist_watch_1026_4.jpg,wrist_watch
|
26 |
+
index/product_wrist_watch_1164_1.jpg,product_wrist_watch_1164_1.jpg,wrist_watch
|
27 |
+
index/product_wrist_watch_1154_0.jpg,product_wrist_watch_1154_0.jpg,wrist_watch
|
28 |
+
index/product_backpack_828_4.jpg,product_backpack_828_4.jpg,backpack
|
29 |
+
index/product_wrist_watch_1124_1.jpg,product_wrist_watch_1124_1.jpg,wrist_watch
|
30 |
+
index/product_headset_774_0.jpg,product_headset_774_0.jpg,headset
|
31 |
+
index/product_mouse_246_0.jpg,product_mouse_246_0.jpg,mouse
|
32 |
+
index/product_wrist_watch_1061_0.jpg,product_wrist_watch_1061_0.jpg,wrist_watch
|
33 |
+
index/product_wrist_watch_1026_3.jpg,product_wrist_watch_1026_3.jpg,wrist_watch
|
34 |
+
index/product_backpack_862_2.jpg,product_backpack_862_2.jpg,backpack
|
35 |
+
index/product_usb_hub_37_0.jpg,product_usb_hub_37_0.jpg,usb_hub
|
36 |
+
index/product_backpack_839_1.jpg,product_backpack_839_1.jpg,backpack
|
37 |
+
index/product_headset_764_4.jpg,product_headset_764_4.jpg,headset
|
38 |
+
index/product_mouse_387_1.jpg,product_mouse_387_1.jpg,mouse
|
39 |
+
index/product_headset_662_1.jpg,product_headset_662_1.jpg,headset
|
40 |
+
index/product_mouse_221_0.jpg,product_mouse_221_0.jpg,mouse
|
41 |
+
index/product_flash_drive_1402_1.jpg,product_flash_drive_1402_1.jpg,flash_drive
|
42 |
+
index/product_backpack_854_3.jpg,product_backpack_854_3.jpg,backpack
|
43 |
+
index/product_ear_pods_593_4.jpg,product_ear_pods_593_4.jpg,ear_pods
|
44 |
+
index/product_backpack_880_5.jpg,product_backpack_880_5.jpg,backpack
|
45 |
+
index/product_ear_pods_445_4.jpg,product_ear_pods_445_4.jpg,ear_pods
|
46 |
+
index/product_flash_drive_1402_4.jpg,product_flash_drive_1402_4.jpg,flash_drive
|
47 |
+
index/product_wrist_watch_1095_0.jpg,product_wrist_watch_1095_0.jpg,wrist_watch
|
48 |
+
index/product_headset_721_1.jpg,product_headset_721_1.jpg,headset
|
49 |
+
index/product_usb_hub_68_2.jpg,product_usb_hub_68_2.jpg,usb_hub
|
50 |
+
index/product_usb_hub_12_1.jpg,product_usb_hub_12_1.jpg,usb_hub
|
51 |
+
index/product_headset_635_7.jpg,product_headset_635_7.jpg,headset
|
52 |
+
index/product_backpack_828_0.jpg,product_backpack_828_0.jpg,backpack
|
53 |
+
index/product_usb_hub_24_2.jpg,product_usb_hub_24_2.jpg,usb_hub
|
54 |
+
index/product_flash_drive_1407_3.jpg,product_flash_drive_1407_3.jpg,flash_drive
|
55 |
+
index/product_usb_hub_11_3.jpg,product_usb_hub_11_3.jpg,usb_hub
|
56 |
+
index/product_office_chairs_1325_1.jpg,product_office_chairs_1325_1.jpg,office_chairs
|
57 |
+
index/product_mouse_207_5.jpg,product_mouse_207_5.jpg,mouse
|
58 |
+
index/product_headset_669_1.jpg,product_headset_669_1.jpg,headset
|
59 |
+
index/product_backpack_802_4.jpg,product_backpack_802_4.jpg,backpack
|
60 |
+
index/product_backpack_885_1.jpg,product_backpack_885_1.jpg,backpack
|
61 |
+
index/product_mouse_374_1.jpg,product_mouse_374_1.jpg,mouse
|
62 |
+
index/product_mouse_246_1.jpg,product_mouse_246_1.jpg,mouse
|
63 |
+
index/product_wrist_watch_1006_2.jpg,product_wrist_watch_1006_2.jpg,wrist_watch
|
64 |
+
index/product_flash_drive_1420_8.jpg,product_flash_drive_1420_8.jpg,flash_drive
|
65 |
+
index/product_backpack_850_2.jpg,product_backpack_850_2.jpg,backpack
|
66 |
+
index/product_wrist_watch_1030_3.jpg,product_wrist_watch_1030_3.jpg,wrist_watch
|
67 |
+
index/product_mouse_203_1.jpg,product_mouse_203_1.jpg,mouse
|
68 |
+
index/product_backpack_889_4.jpg,product_backpack_889_4.jpg,backpack
|
69 |
+
index/product_headset_666_0.jpg,product_headset_666_0.jpg,headset
|
70 |
+
index/product_backpack_885_6.jpg,product_backpack_885_6.jpg,backpack
|
71 |
+
index/product_wrist_watch_1124_4.jpg,product_wrist_watch_1124_4.jpg,wrist_watch
|
72 |
+
index/product_headset_624_0.jpg,product_headset_624_0.jpg,headset
|
73 |
+
index/product_wrist_watch_1198_5.jpg,product_wrist_watch_1198_5.jpg,wrist_watch
|
74 |
+
index/product_backpack_974_1.jpg,product_backpack_974_1.jpg,backpack
|
75 |
+
index/product_usb_hub_136_4.jpg,product_usb_hub_136_4.jpg,usb_hub
|
76 |
+
index/product_ear_pods_591_5.jpg,product_ear_pods_591_5.jpg,ear_pods
|
77 |
+
index/product_usb_hub_176_1.jpg,product_usb_hub_176_1.jpg,usb_hub
|
78 |
+
index/product_usb_hub_11_4.jpg,product_usb_hub_11_4.jpg,usb_hub
|
79 |
+
index/product_ear_pods_567_0.jpg,product_ear_pods_567_0.jpg,ear_pods
|
80 |
+
index/product_mouse_324_5.jpg,product_mouse_324_5.jpg,mouse
|
81 |
+
index/product_office_chairs_1208_0.jpg,product_office_chairs_1208_0.jpg,office_chairs
|
82 |
+
index/product_usb_hub_68_3.jpg,product_usb_hub_68_3.jpg,usb_hub
|
83 |
+
index/product_flash_drive_1411_6.jpg,product_flash_drive_1411_6.jpg,flash_drive
|
84 |
+
index/product_wrist_watch_1189_3.jpg,product_wrist_watch_1189_3.jpg,wrist_watch
|
85 |
+
index/product_headset_787_4.jpg,product_headset_787_4.jpg,headset
|
86 |
+
index/product_backpack_854_1.jpg,product_backpack_854_1.jpg,backpack
|
87 |
+
index/product_headset_668_2.jpg,product_headset_668_2.jpg,headset
|
88 |
+
index/product_office_chairs_1315_0.jpg,product_office_chairs_1315_0.jpg,office_chairs
|
89 |
+
index/product_usb_hub_11_2.jpg,product_usb_hub_11_2.jpg,usb_hub
|
90 |
+
index/product_wrist_watch_1198_3.jpg,product_wrist_watch_1198_3.jpg,wrist_watch
|
91 |
+
index/product_usb_hub_68_5.jpg,product_usb_hub_68_5.jpg,usb_hub
|
92 |
+
index/product_headset_675_7.jpg,product_headset_675_7.jpg,headset
|
93 |
+
index/product_office_chairs_1393_1.jpg,product_office_chairs_1393_1.jpg,office_chairs
|
94 |
+
index/product_office_chairs_1337_0.jpg,product_office_chairs_1337_0.jpg,office_chairs
|
95 |
+
index/product_wrist_watch_1000_3.jpg,product_wrist_watch_1000_3.jpg,wrist_watch
|
96 |
+
index/product_usb_hub_37_1.jpg,product_usb_hub_37_1.jpg,usb_hub
|
97 |
+
index/product_ear_pods_456_2.jpg,product_ear_pods_456_2.jpg,ear_pods
|
98 |
+
index/product_backpack_817_3.jpg,product_backpack_817_3.jpg,backpack
|
99 |
+
index/product_usb_hub_36_0.jpg,product_usb_hub_36_0.jpg,usb_hub
|
100 |
+
index/product_usb_hub_16_2.jpg,product_usb_hub_16_2.jpg,usb_hub
|
101 |
+
index/product_usb_hub_36_3.jpg,product_usb_hub_36_3.jpg,usb_hub
|
102 |
+
index/product_flash_drive_1407_1.jpg,product_flash_drive_1407_1.jpg,flash_drive
|
103 |
+
index/product_wrist_watch_1022_5.jpg,product_wrist_watch_1022_5.jpg,wrist_watch
|
104 |
+
index/product_mouse_263_7.jpg,product_mouse_263_7.jpg,mouse
|
105 |
+
index/product_mouse_349_6.jpg,product_mouse_349_6.jpg,mouse
|
106 |
+
index/product_headset_662_0.jpg,product_headset_662_0.jpg,headset
|
107 |
+
index/product_office_chairs_1350_0.jpg,product_office_chairs_1350_0.jpg,office_chairs
|
108 |
+
index/product_usb_hub_136_2.jpg,product_usb_hub_136_2.jpg,usb_hub
|
109 |
+
index/product_backpack_910_5.jpg,product_backpack_910_5.jpg,backpack
|
110 |
+
index/product_backpack_839_3.jpg,product_backpack_839_3.jpg,backpack
|
111 |
+
index/product_headset_639_0.jpg,product_headset_639_0.jpg,headset
|
112 |
+
index/product_mouse_349_5.jpg,product_mouse_349_5.jpg,mouse
|
113 |
+
index/product_wrist_watch_1189_1.jpg,product_wrist_watch_1189_1.jpg,wrist_watch
|
114 |
+
index/product_flash_drive_1411_4.jpg,product_flash_drive_1411_4.jpg,flash_drive
|
115 |
+
index/product_ear_pods_456_1.jpg,product_ear_pods_456_1.jpg,ear_pods
|
116 |
+
index/product_backpack_854_0.jpg,product_backpack_854_0.jpg,backpack
|
117 |
+
index/product_ear_pods_552_3.jpg,product_ear_pods_552_3.jpg,ear_pods
|
118 |
+
index/product_wrist_watch_1006_4.jpg,product_wrist_watch_1006_4.jpg,wrist_watch
|
119 |
+
index/product_headset_662_7.jpg,product_headset_662_7.jpg,headset
|
120 |
+
index/product_flash_drive_1402_2.jpg,product_flash_drive_1402_2.jpg,flash_drive
|
121 |
+
index/product_usb_hub_16_3.jpg,product_usb_hub_16_3.jpg,usb_hub
|
122 |
+
index/product_wrist_watch_1098_0.jpg,product_wrist_watch_1098_0.jpg,wrist_watch
|
123 |
+
index/product_backpack_889_5.jpg,product_backpack_889_5.jpg,backpack
|
124 |
+
index/product_ear_pods_445_3.jpg,product_ear_pods_445_3.jpg,ear_pods
|
125 |
+
index/product_backpack_850_7.jpg,product_backpack_850_7.jpg,backpack
|
126 |
+
index/product_wrist_watch_1029_0.jpg,product_wrist_watch_1029_0.jpg,wrist_watch
|
127 |
+
index/product_wrist_watch_1122_7.jpg,product_wrist_watch_1122_7.jpg,wrist_watch
|
128 |
+
index/product_wrist_watch_1189_5.jpg,product_wrist_watch_1189_5.jpg,wrist_watch
|
129 |
+
index/product_usb_hub_176_0.jpg,product_usb_hub_176_0.jpg,usb_hub
|
130 |
+
index/product_headset_662_2.jpg,product_headset_662_2.jpg,headset
|
131 |
+
index/product_headset_666_1.jpg,product_headset_666_1.jpg,headset
|
132 |
+
index/product_wrist_watch_1090_0.jpg,product_wrist_watch_1090_0.jpg,wrist_watch
|
133 |
+
index/product_office_chairs_1199_0.jpg,product_office_chairs_1199_0.jpg,office_chairs
|
134 |
+
index/product_ear_pods_591_2.jpg,product_ear_pods_591_2.jpg,ear_pods
|
135 |
+
index/product_backpack_813_0.jpg,product_backpack_813_0.jpg,backpack
|
136 |
+
index/product_backpack_874_0.jpg,product_backpack_874_0.jpg,backpack
|
137 |
+
index/product_ear_pods_440_4.jpg,product_ear_pods_440_4.jpg,ear_pods
|
138 |
+
index/product_headset_681_1.jpg,product_headset_681_1.jpg,headset
|
139 |
+
index/product_ear_pods_585_1.jpg,product_ear_pods_585_1.jpg,ear_pods
|
140 |
+
index/product_wrist_watch_1198_2.jpg,product_wrist_watch_1198_2.jpg,wrist_watch
|
141 |
+
index/product_wrist_watch_1022_6.jpg,product_wrist_watch_1022_6.jpg,wrist_watch
|
142 |
+
index/product_ear_pods_430_3.jpg,product_ear_pods_430_3.jpg,ear_pods
|
143 |
+
index/product_backpack_889_3.jpg,product_backpack_889_3.jpg,backpack
|
144 |
+
index/product_headset_652_3.jpg,product_headset_652_3.jpg,headset
|
145 |
+
index/product_ear_pods_445_2.jpg,product_ear_pods_445_2.jpg,ear_pods
|
146 |
+
index/product_ear_pods_509_2.jpg,product_ear_pods_509_2.jpg,ear_pods
|
147 |
+
index/product_backpack_850_6.jpg,product_backpack_850_6.jpg,backpack
|
148 |
+
index/product_wrist_watch_1026_1.jpg,product_wrist_watch_1026_1.jpg,wrist_watch
|
149 |
+
index/product_ear_pods_488_1.jpg,product_ear_pods_488_1.jpg,ear_pods
|
150 |
+
index/product_headset_635_2.jpg,product_headset_635_2.jpg,headset
|
151 |
+
index/product_flash_drive_1420_7.jpg,product_flash_drive_1420_7.jpg,flash_drive
|
152 |
+
index/product_office_chairs_1296_1.jpg,product_office_chairs_1296_1.jpg,office_chairs
|
153 |
+
index/product_mouse_217_1.jpg,product_mouse_217_1.jpg,mouse
|
154 |
+
index/product_headset_631_1.jpg,product_headset_631_1.jpg,headset
|
155 |
+
index/product_wrist_watch_1030_5.jpg,product_wrist_watch_1030_5.jpg,wrist_watch
|
156 |
+
index/product_ear_pods_521_3.jpg,product_ear_pods_521_3.jpg,ear_pods
|
157 |
+
index/product_headset_631_2.jpg,product_headset_631_2.jpg,headset
|
158 |
+
index/product_flash_drive_1403_6.jpg,product_flash_drive_1403_6.jpg,flash_drive
|
159 |
+
index/product_wrist_watch_1022_1.jpg,product_wrist_watch_1022_1.jpg,wrist_watch
|
160 |
+
index/product_usb_hub_11_5.jpg,product_usb_hub_11_5.jpg,usb_hub
|
161 |
+
index/product_mouse_207_0.jpg,product_mouse_207_0.jpg,mouse
|
162 |
+
index/product_usb_hub_173_0.jpg,product_usb_hub_173_0.jpg,usb_hub
|
163 |
+
index/product_headset_615_8.jpg,product_headset_615_8.jpg,headset
|
164 |
+
index/product_backpack_831_4.jpg,product_backpack_831_4.jpg,backpack
|
165 |
+
index/product_backpack_828_2.jpg,product_backpack_828_2.jpg,backpack
|
166 |
+
index/product_backpack_855_4.jpg,product_backpack_855_4.jpg,backpack
|
167 |
+
index/product_ear_pods_430_0.jpg,product_ear_pods_430_0.jpg,ear_pods
|
168 |
+
index/product_mouse_267_6.jpg,product_mouse_267_6.jpg,mouse
|
169 |
+
index/product_wrist_watch_1175_0.jpg,product_wrist_watch_1175_0.jpg,wrist_watch
|
170 |
+
index/product_ear_pods_535_1.jpg,product_ear_pods_535_1.jpg,ear_pods
|
171 |
+
index/product_headset_635_1.jpg,product_headset_635_1.jpg,headset
|
172 |
+
index/product_backpack_817_7.jpg,product_backpack_817_7.jpg,backpack
|
173 |
+
index/product_wrist_watch_1006_0.jpg,product_wrist_watch_1006_0.jpg,wrist_watch
|
174 |
+
index/product_backpack_910_7.jpg,product_backpack_910_7.jpg,backpack
|
175 |
+
index/product_office_chairs_1296_2.jpg,product_office_chairs_1296_2.jpg,office_chairs
|
176 |
+
index/product_wrist_watch_1043_6.jpg,product_wrist_watch_1043_6.jpg,wrist_watch
|
177 |
+
index/product_ear_pods_434_4.jpg,product_ear_pods_434_4.jpg,ear_pods
|
178 |
+
index/product_flash_drive_1445_4.jpg,product_flash_drive_1445_4.jpg,flash_drive
|
179 |
+
index/product_wrist_watch_1178_1.jpg,product_wrist_watch_1178_1.jpg,wrist_watch
|
180 |
+
index/product_backpack_865_4.jpg,product_backpack_865_4.jpg,backpack
|
181 |
+
index/product_backpack_885_0.jpg,product_backpack_885_0.jpg,backpack
|
182 |
+
index/product_headset_652_0.jpg,product_headset_652_0.jpg,headset
|
183 |
+
index/product_ear_pods_585_0.jpg,product_ear_pods_585_0.jpg,ear_pods
|
184 |
+
index/product_flash_drive_1445_6.jpg,product_flash_drive_1445_6.jpg,flash_drive
|
185 |
+
index/product_headset_675_6.jpg,product_headset_675_6.jpg,headset
|
186 |
+
index/product_ear_pods_430_1.jpg,product_ear_pods_430_1.jpg,ear_pods
|
187 |
+
index/product_usb_hub_186_1.jpg,product_usb_hub_186_1.jpg,usb_hub
|
188 |
+
index/product_office_chairs_1208_3.jpg,product_office_chairs_1208_3.jpg,office_chairs
|
189 |
+
index/product_headset_693_5.jpg,product_headset_693_5.jpg,headset
|
190 |
+
index/product_mouse_374_0.jpg,product_mouse_374_0.jpg,mouse
|
191 |
+
index/product_backpack_812_4.jpg,product_backpack_812_4.jpg,backpack
|
192 |
+
index/product_mouse_203_5.jpg,product_mouse_203_5.jpg,mouse
|
193 |
+
index/product_mouse_246_4.jpg,product_mouse_246_4.jpg,mouse
|
194 |
+
index/product_wrist_watch_1198_6.jpg,product_wrist_watch_1198_6.jpg,wrist_watch
|
195 |
+
index/product_backpack_865_2.jpg,product_backpack_865_2.jpg,backpack
|
196 |
+
index/product_office_chairs_1325_0.jpg,product_office_chairs_1325_0.jpg,office_chairs
|
197 |
+
index/product_mouse_229_4.jpg,product_mouse_229_4.jpg,mouse
|
198 |
+
index/product_backpack_865_1.jpg,product_backpack_865_1.jpg,backpack
|
199 |
+
index/product_backpack_804_0.jpg,product_backpack_804_0.jpg,backpack
|
200 |
+
index/product_wrist_watch_1122_3.jpg,product_wrist_watch_1122_3.jpg,wrist_watch
|
201 |
+
index/product_mouse_221_1.jpg,product_mouse_221_1.jpg,mouse
|
202 |
+
index/product_usb_hub_165_5.jpg,product_usb_hub_165_5.jpg,usb_hub
|
203 |
+
index/product_usb_hub_24_7.jpg,product_usb_hub_24_7.jpg,usb_hub
|
204 |
+
index/product_office_chairs_1225_0.jpg,product_office_chairs_1225_0.jpg,office_chairs
|
205 |
+
index/product_usb_hub_68_4.jpg,product_usb_hub_68_4.jpg,usb_hub
|
206 |
+
index/product_wrist_watch_1122_2.jpg,product_wrist_watch_1122_2.jpg,wrist_watch
|
207 |
+
index/product_ear_pods_576_6.jpg,product_ear_pods_576_6.jpg,ear_pods
|
208 |
+
index/product_usb_hub_168_2.jpg,product_usb_hub_168_2.jpg,usb_hub
|
209 |
+
index/product_ear_pods_492_0.jpg,product_ear_pods_492_0.jpg,ear_pods
|
210 |
+
index/product_usb_hub_68_1.jpg,product_usb_hub_68_1.jpg,usb_hub
|
211 |
+
index/product_office_chairs_1393_0.jpg,product_office_chairs_1393_0.jpg,office_chairs
|
212 |
+
index/product_wrist_watch_1011_1.jpg,product_wrist_watch_1011_1.jpg,wrist_watch
|
213 |
+
index/product_mouse_207_3.jpg,product_mouse_207_3.jpg,mouse
|
214 |
+
index/product_usb_hub_3_0.jpg,product_usb_hub_3_0.jpg,usb_hub
|
215 |
+
index/product_flash_drive_1407_6.jpg,product_flash_drive_1407_6.jpg,flash_drive
|
216 |
+
index/product_mouse_390_0.jpg,product_mouse_390_0.jpg,mouse
|
217 |
+
index/product_mouse_390_2.jpg,product_mouse_390_2.jpg,mouse
|
218 |
+
index/product_usb_hub_185_0.jpg,product_usb_hub_185_0.jpg,usb_hub
|
219 |
+
index/product_backpack_864_5.jpg,product_backpack_864_5.jpg,backpack
|
220 |
+
index/product_wrist_watch_1067_1.jpg,product_wrist_watch_1067_1.jpg,wrist_watch
|
221 |
+
index/product_ear_pods_572_0.jpg,product_ear_pods_572_0.jpg,ear_pods
|
222 |
+
index/product_backpack_817_0.jpg,product_backpack_817_0.jpg,backpack
|
223 |
+
index/product_backpack_802_2.jpg,product_backpack_802_2.jpg,backpack
|
224 |
+
index/product_ear_pods_585_3.jpg,product_ear_pods_585_3.jpg,ear_pods
|
225 |
+
index/product_backpack_813_7.jpg,product_backpack_813_7.jpg,backpack
|
226 |
+
index/product_backpack_910_2.jpg,product_backpack_910_2.jpg,backpack
|
227 |
+
index/product_usb_hub_3_2.jpg,product_usb_hub_3_2.jpg,usb_hub
|
228 |
+
index/product_office_chairs_1208_1.jpg,product_office_chairs_1208_1.jpg,office_chairs
|
229 |
+
index/product_ear_pods_574_3.jpg,product_ear_pods_574_3.jpg,ear_pods
|
230 |
+
index/product_usb_hub_16_4.jpg,product_usb_hub_16_4.jpg,usb_hub
|
231 |
+
index/product_ear_pods_449_2.jpg,product_ear_pods_449_2.jpg,ear_pods
|
232 |
+
index/product_backpack_885_2.jpg,product_backpack_885_2.jpg,backpack
|
233 |
+
index/product_ear_pods_567_5.jpg,product_ear_pods_567_5.jpg,ear_pods
|
234 |
+
index/product_mouse_217_3.jpg,product_mouse_217_3.jpg,mouse
|
235 |
+
index/product_headset_764_0.jpg,product_headset_764_0.jpg,headset
|
236 |
+
index/product_ear_pods_552_2.jpg,product_ear_pods_552_2.jpg,ear_pods
|
237 |
+
index/product_headset_615_6.jpg,product_headset_615_6.jpg,headset
|
238 |
+
index/product_headset_649_0.jpg,product_headset_649_0.jpg,headset
|
239 |
+
index/product_headset_625_1.jpg,product_headset_625_1.jpg,headset
|
240 |
+
index/product_usb_hub_165_1.jpg,product_usb_hub_165_1.jpg,usb_hub
|
241 |
+
index/product_usb_hub_168_0.jpg,product_usb_hub_168_0.jpg,usb_hub
|
242 |
+
index/product_usb_hub_24_6.jpg,product_usb_hub_24_6.jpg,usb_hub
|
243 |
+
index/product_flash_drive_1420_6.jpg,product_flash_drive_1420_6.jpg,flash_drive
|
244 |
+
index/product_backpack_885_3.jpg,product_backpack_885_3.jpg,backpack
|
245 |
+
index/product_flash_drive_1402_0.jpg,product_flash_drive_1402_0.jpg,flash_drive
|
246 |
+
index/product_ear_pods_442_4.jpg,product_ear_pods_442_4.jpg,ear_pods
|
247 |
+
index/product_wrist_watch_1122_5.jpg,product_wrist_watch_1122_5.jpg,wrist_watch
|
248 |
+
index/product_backpack_813_6.jpg,product_backpack_813_6.jpg,backpack
|
249 |
+
index/product_ear_pods_574_0.jpg,product_ear_pods_574_0.jpg,ear_pods
|
250 |
+
index/product_ear_pods_434_3.jpg,product_ear_pods_434_3.jpg,ear_pods
|
251 |
+
index/product_flash_drive_1407_2.jpg,product_flash_drive_1407_2.jpg,flash_drive
|
252 |
+
index/product_wrist_watch_1022_0.jpg,product_wrist_watch_1022_0.jpg,wrist_watch
|
253 |
+
index/product_wrist_watch_1030_1.jpg,product_wrist_watch_1030_1.jpg,wrist_watch
|
254 |
+
index/product_backpack_817_4.jpg,product_backpack_817_4.jpg,backpack
|
255 |
+
index/product_wrist_watch_1006_1.jpg,product_wrist_watch_1006_1.jpg,wrist_watch
|
256 |
+
index/product_headset_641_5.jpg,product_headset_641_5.jpg,headset
|
257 |
+
index/product_wrist_watch_1000_0.jpg,product_wrist_watch_1000_0.jpg,wrist_watch
|
258 |
+
index/product_backpack_864_4.jpg,product_backpack_864_4.jpg,backpack
|
259 |
+
index/product_mouse_267_7.jpg,product_mouse_267_7.jpg,mouse
|
260 |
+
index/product_headset_639_3.jpg,product_headset_639_3.jpg,headset
|
261 |
+
index/product_ear_pods_574_5.jpg,product_ear_pods_574_5.jpg,ear_pods
|
262 |
+
index/product_headset_669_0.jpg,product_headset_669_0.jpg,headset
|
263 |
+
index/product_backpack_974_0.jpg,product_backpack_974_0.jpg,backpack
|
264 |
+
index/product_wrist_watch_1189_6.jpg,product_wrist_watch_1189_6.jpg,wrist_watch
|
265 |
+
index/product_mouse_203_6.jpg,product_mouse_203_6.jpg,mouse
|
266 |
+
index/product_headset_693_4.jpg,product_headset_693_4.jpg,headset
|
267 |
+
index/product_backpack_874_6.jpg,product_backpack_874_6.jpg,backpack
|
268 |
+
index/product_ear_pods_574_4.jpg,product_ear_pods_574_4.jpg,ear_pods
|
269 |
+
index/product_wrist_watch_1107_2.jpg,product_wrist_watch_1107_2.jpg,wrist_watch
|
270 |
+
index/product_ear_pods_488_2.jpg,product_ear_pods_488_2.jpg,ear_pods
|
271 |
+
index/product_headset_787_2.jpg,product_headset_787_2.jpg,headset
|
272 |
+
index/product_ear_pods_412_1.jpg,product_ear_pods_412_1.jpg,ear_pods
|
273 |
+
index/product_ear_pods_576_0.jpg,product_ear_pods_576_0.jpg,ear_pods
|
274 |
+
index/product_flash_drive_1420_2.jpg,product_flash_drive_1420_2.jpg,flash_drive
|
275 |
+
index/product_backpack_874_3.jpg,product_backpack_874_3.jpg,backpack
|
276 |
+
index/product_wrist_watch_1026_0.jpg,product_wrist_watch_1026_0.jpg,wrist_watch
|
277 |
+
index/product_ear_pods_593_5.jpg,product_ear_pods_593_5.jpg,ear_pods
|
278 |
+
index/product_headset_662_3.jpg,product_headset_662_3.jpg,headset
|
279 |
+
index/product_backpack_854_4.jpg,product_backpack_854_4.jpg,backpack
|
280 |
+
index/product_mouse_324_4.jpg,product_mouse_324_4.jpg,mouse
|
281 |
+
index/product_wrist_watch_1011_0.jpg,product_wrist_watch_1011_0.jpg,wrist_watch
|
282 |
+
index/product_headset_625_0.jpg,product_headset_625_0.jpg,headset
|
283 |
+
index/product_ear_pods_585_4.jpg,product_ear_pods_585_4.jpg,ear_pods
|
284 |
+
index/product_ear_pods_445_1.jpg,product_ear_pods_445_1.jpg,ear_pods
|
285 |
+
index/product_office_chairs_1215_0.jpg,product_office_chairs_1215_0.jpg,office_chairs
|
286 |
+
index/product_wrist_watch_1198_0.jpg,product_wrist_watch_1198_0.jpg,wrist_watch
|
287 |
+
index/product_usb_hub_24_4.jpg,product_usb_hub_24_4.jpg,usb_hub
|
288 |
+
index/product_mouse_349_0.jpg,product_mouse_349_0.jpg,mouse
|
289 |
+
index/product_mouse_324_6.jpg,product_mouse_324_6.jpg,mouse
|
290 |
+
index/product_headset_615_0.jpg,product_headset_615_0.jpg,headset
|
291 |
+
index/product_mouse_372_0.jpg,product_mouse_372_0.jpg,mouse
|
292 |
+
index/product_ear_pods_434_1.jpg,product_ear_pods_434_1.jpg,ear_pods
|
293 |
+
index/product_headset_652_2.jpg,product_headset_652_2.jpg,headset
|
294 |
+
index/product_office_chairs_1204_0.jpg,product_office_chairs_1204_0.jpg,office_chairs
|
295 |
+
index/product_mouse_292_0.jpg,product_mouse_292_0.jpg,mouse
|
296 |
+
index/product_wrist_watch_1107_3.jpg,product_wrist_watch_1107_3.jpg,wrist_watch
|
297 |
+
index/product_backpack_812_1.jpg,product_backpack_812_1.jpg,backpack
|
298 |
+
index/product_backpack_850_1.jpg,product_backpack_850_1.jpg,backpack
|
299 |
+
index/product_mouse_349_3.jpg,product_mouse_349_3.jpg,mouse
|
300 |
+
index/product_mouse_349_2.jpg,product_mouse_349_2.jpg,mouse
|
301 |
+
index/product_mouse_212_3.jpg,product_mouse_212_3.jpg,mouse
|
302 |
+
index/product_backpack_880_6.jpg,product_backpack_880_6.jpg,backpack
|
303 |
+
index/product_mouse_267_5.jpg,product_mouse_267_5.jpg,mouse
|
304 |
+
index/product_backpack_804_2.jpg,product_backpack_804_2.jpg,backpack
|
305 |
+
index/product_wrist_watch_1122_6.jpg,product_wrist_watch_1122_6.jpg,wrist_watch
|
306 |
+
index/product_usb_hub_176_7.jpg,product_usb_hub_176_7.jpg,usb_hub
|
307 |
+
index/product_backpack_813_1.jpg,product_backpack_813_1.jpg,backpack
|
308 |
+
index/product_headset_787_0.jpg,product_headset_787_0.jpg,headset
|
309 |
+
index/product_headset_641_2.jpg,product_headset_641_2.jpg,headset
|
310 |
+
index/product_backpack_812_0.jpg,product_backpack_812_0.jpg,backpack
|
311 |
+
index/product_wrist_watch_1178_0.jpg,product_wrist_watch_1178_0.jpg,wrist_watch
|
312 |
+
index/product_ear_pods_442_5.jpg,product_ear_pods_442_5.jpg,ear_pods
|
313 |
+
index/product_ear_pods_445_0.jpg,product_ear_pods_445_0.jpg,ear_pods
|
314 |
+
index/product_usb_hub_161_0.jpg,product_usb_hub_161_0.jpg,usb_hub
|
315 |
+
index/product_ear_pods_434_2.jpg,product_ear_pods_434_2.jpg,ear_pods
|
316 |
+
index/product_wrist_watch_1122_1.jpg,product_wrist_watch_1122_1.jpg,wrist_watch
|
317 |
+
index/product_mouse_246_3.jpg,product_mouse_246_3.jpg,mouse
|
318 |
+
index/product_backpack_885_7.jpg,product_backpack_885_7.jpg,backpack
|
319 |
+
index/product_backpack_874_4.jpg,product_backpack_874_4.jpg,backpack
|
320 |
+
index/product_ear_pods_591_3.jpg,product_ear_pods_591_3.jpg,ear_pods
|
321 |
+
index/product_backpack_812_3.jpg,product_backpack_812_3.jpg,backpack
|
322 |
+
index/product_headset_764_2.jpg,product_headset_764_2.jpg,headset
|
323 |
+
index/product_mouse_324_0.jpg,product_mouse_324_0.jpg,mouse
|
324 |
+
index/product_wrist_watch_1000_2.jpg,product_wrist_watch_1000_2.jpg,wrist_watch
|
325 |
+
index/product_wrist_watch_1006_6.jpg,product_wrist_watch_1006_6.jpg,wrist_watch
|
326 |
+
index/product_ear_pods_412_2.jpg,product_ear_pods_412_2.jpg,ear_pods
|
327 |
+
index/product_ear_pods_593_3.jpg,product_ear_pods_593_3.jpg,ear_pods
|
328 |
+
index/product_flash_drive_1402_3.jpg,product_flash_drive_1402_3.jpg,flash_drive
|
329 |
+
index/product_wrist_watch_1006_5.jpg,product_wrist_watch_1006_5.jpg,wrist_watch
|
330 |
+
index/product_mouse_246_6.jpg,product_mouse_246_6.jpg,mouse
|
331 |
+
index/product_backpack_802_1.jpg,product_backpack_802_1.jpg,backpack
|
332 |
+
index/product_ear_pods_412_0.jpg,product_ear_pods_412_0.jpg,ear_pods
|
333 |
+
index/product_headset_764_1.jpg,product_headset_764_1.jpg,headset
|
334 |
+
index/product_flash_drive_1445_3.jpg,product_flash_drive_1445_3.jpg,flash_drive
|
335 |
+
index/product_ear_pods_412_4.jpg,product_ear_pods_412_4.jpg,ear_pods
|
336 |
+
index/product_mouse_207_6.jpg,product_mouse_207_6.jpg,mouse
|
337 |
+
index/product_backpack_910_0.jpg,product_backpack_910_0.jpg,backpack
|
338 |
+
index/product_usb_hub_11_0.jpg,product_usb_hub_11_0.jpg,usb_hub
|
339 |
+
index/product_backpack_862_3.jpg,product_backpack_862_3.jpg,backpack
|
340 |
+
index/product_headset_625_2.jpg,product_headset_625_2.jpg,headset
|
341 |
+
index/product_backpack_910_6.jpg,product_backpack_910_6.jpg,backpack
|
342 |
+
index/product_headset_652_1.jpg,product_headset_652_1.jpg,headset
|
343 |
+
index/product_backpack_844_0.jpg,product_backpack_844_0.jpg,backpack
|
344 |
+
index/product_office_chairs_1204_1.jpg,product_office_chairs_1204_1.jpg,office_chairs
|
345 |
+
index/product_usb_hub_12_0.jpg,product_usb_hub_12_0.jpg,usb_hub
|
346 |
+
index/product_usb_hub_136_5.jpg,product_usb_hub_136_5.jpg,usb_hub
|
347 |
+
index/product_mouse_246_5.jpg,product_mouse_246_5.jpg,mouse
|
348 |
+
index/product_usb_hub_136_1.jpg,product_usb_hub_136_1.jpg,usb_hub
|
349 |
+
index/product_ear_pods_576_3.jpg,product_ear_pods_576_3.jpg,ear_pods
|
350 |
+
index/product_headset_721_2.jpg,product_headset_721_2.jpg,headset
|
351 |
+
index/product_wrist_watch_1000_1.jpg,product_wrist_watch_1000_1.jpg,wrist_watch
|
352 |
+
index/product_ear_pods_403_0.jpg,product_ear_pods_403_0.jpg,ear_pods
|
353 |
+
index/product_wrist_watch_1022_2.jpg,product_wrist_watch_1022_2.jpg,wrist_watch
|
354 |
+
index/product_usb_hub_24_3.jpg,product_usb_hub_24_3.jpg,usb_hub
|
355 |
+
index/product_headset_721_4.jpg,product_headset_721_4.jpg,headset
|
356 |
+
index/product_ear_pods_567_2.jpg,product_ear_pods_567_2.jpg,ear_pods
|
357 |
+
index/product_flash_drive_1445_1.jpg,product_flash_drive_1445_1.jpg,flash_drive
|
358 |
+
index/product_mouse_238_0.jpg,product_mouse_238_0.jpg,mouse
|
359 |
+
index/product_usb_hub_176_2.jpg,product_usb_hub_176_2.jpg,usb_hub
|
360 |
+
index/product_headset_774_2.jpg,product_headset_774_2.jpg,headset
|
361 |
+
index/product_usb_hub_11_1.jpg,product_usb_hub_11_1.jpg,usb_hub
|
362 |
+
index/product_flash_drive_1445_2.jpg,product_flash_drive_1445_2.jpg,flash_drive
|
363 |
+
index/product_ear_pods_434_0.jpg,product_ear_pods_434_0.jpg,ear_pods
|
364 |
+
index/product_headset_787_6.jpg,product_headset_787_6.jpg,headset
|
365 |
+
index/product_flash_drive_1445_5.jpg,product_flash_drive_1445_5.jpg,flash_drive
|
366 |
+
index/product_ear_pods_593_2.jpg,product_ear_pods_593_2.jpg,ear_pods
|
367 |
+
index/product_headset_721_0.jpg,product_headset_721_0.jpg,headset
|
368 |
+
index/product_backpack_889_1.jpg,product_backpack_889_1.jpg,backpack
|
369 |
+
index/product_ear_pods_576_4.jpg,product_ear_pods_576_4.jpg,ear_pods
|
370 |
+
index/product_mouse_324_3.jpg,product_mouse_324_3.jpg,mouse
|
371 |
+
index/product_flash_drive_1402_5.jpg,product_flash_drive_1402_5.jpg,flash_drive
|
372 |
+
index/product_usb_hub_176_5.jpg,product_usb_hub_176_5.jpg,usb_hub
|
373 |
+
index/product_usb_hub_112_0.jpg,product_usb_hub_112_0.jpg,usb_hub
|
374 |
+
index/product_ear_pods_457_0.jpg,product_ear_pods_457_0.jpg,ear_pods
|
375 |
+
index/product_mouse_267_0.jpg,product_mouse_267_0.jpg,mouse
|
376 |
+
index/product_backpack_854_5.jpg,product_backpack_854_5.jpg,backpack
|
377 |
+
index/product_backpack_835_0.jpg,product_backpack_835_0.jpg,backpack
|
378 |
+
index/product_backpack_874_7.jpg,product_backpack_874_7.jpg,backpack
|
379 |
+
index/product_backpack_855_3.jpg,product_backpack_855_3.jpg,backpack
|
380 |
+
index/product_flash_drive_1420_0.jpg,product_flash_drive_1420_0.jpg,flash_drive
|
381 |
+
index/product_headset_690_0.jpg,product_headset_690_0.jpg,headset
|
382 |
+
index/product_wrist_watch_1020_1.jpg,product_wrist_watch_1020_1.jpg,wrist_watch
|
383 |
+
index/product_wrist_watch_1180_2.jpg,product_wrist_watch_1180_2.jpg,wrist_watch
|
384 |
+
index/product_headset_668_3.jpg,product_headset_668_3.jpg,headset
|
385 |
+
index/product_ear_pods_585_6.jpg,product_ear_pods_585_6.jpg,ear_pods
|
386 |
+
index/product_usb_hub_168_3.jpg,product_usb_hub_168_3.jpg,usb_hub
|
387 |
+
index/product_wrist_watch_1122_4.jpg,product_wrist_watch_1122_4.jpg,wrist_watch
|
388 |
+
index/product_backpack_813_4.jpg,product_backpack_813_4.jpg,backpack
|
389 |
+
index/product_headset_641_3.jpg,product_headset_641_3.jpg,headset
|
390 |
+
index/product_ear_pods_430_2.jpg,product_ear_pods_430_2.jpg,ear_pods
|
391 |
+
index/product_mouse_282_2.jpg,product_mouse_282_2.jpg,mouse
|
392 |
+
index/product_usb_hub_16_1.jpg,product_usb_hub_16_1.jpg,usb_hub
|
393 |
+
index/product_wrist_watch_1061_1.jpg,product_wrist_watch_1061_1.jpg,wrist_watch
|
394 |
+
index/product_ear_pods_509_4.jpg,product_ear_pods_509_4.jpg,ear_pods
|
395 |
+
index/product_wrist_watch_1198_1.jpg,product_wrist_watch_1198_1.jpg,wrist_watch
|
396 |
+
index/product_usb_hub_11_6.jpg,product_usb_hub_11_6.jpg,usb_hub
|
397 |
+
index/product_ear_pods_574_2.jpg,product_ear_pods_574_2.jpg,ear_pods
|
398 |
+
index/product_backpack_813_5.jpg,product_backpack_813_5.jpg,backpack
|
399 |
+
index/product_wrist_watch_1033_0.jpg,product_wrist_watch_1033_0.jpg,wrist_watch
|
400 |
+
index/product_usb_hub_124_1.jpg,product_usb_hub_124_1.jpg,usb_hub
|
401 |
+
index/product_ear_pods_576_1.jpg,product_ear_pods_576_1.jpg,ear_pods
|
402 |
+
index/product_backpack_854_2.jpg,product_backpack_854_2.jpg,backpack
|
403 |
+
index/product_mouse_246_2.jpg,product_mouse_246_2.jpg,mouse
|
404 |
+
index/product_headset_657_1.jpg,product_headset_657_1.jpg,headset
|
405 |
+
index/product_wrist_watch_1061_2.jpg,product_wrist_watch_1061_2.jpg,wrist_watch
|
406 |
+
index/product_wrist_watch_1011_2.jpg,product_wrist_watch_1011_2.jpg,wrist_watch
|
407 |
+
index/product_headset_649_1.jpg,product_headset_649_1.jpg,headset
|
408 |
+
index/product_usb_hub_136_0.jpg,product_usb_hub_136_0.jpg,usb_hub
|
409 |
+
index/product_mouse_217_0.jpg,product_mouse_217_0.jpg,mouse
|
410 |
+
index/product_usb_hub_176_3.jpg,product_usb_hub_176_3.jpg,usb_hub
|
411 |
+
index/product_ear_pods_583_0.jpg,product_ear_pods_583_0.jpg,ear_pods
|
412 |
+
index/product_usb_hub_165_2.jpg,product_usb_hub_165_2.jpg,usb_hub
|
413 |
+
index/product_ear_pods_466_4.jpg,product_ear_pods_466_4.jpg,ear_pods
|
414 |
+
index/product_headset_781_1.jpg,product_headset_781_1.jpg,headset
|
415 |
+
index/product_wrist_watch_1022_4.jpg,product_wrist_watch_1022_4.jpg,wrist_watch
|
416 |
+
index/product_wrist_watch_1098_1.jpg,product_wrist_watch_1098_1.jpg,wrist_watch
|
417 |
+
index/product_flash_drive_1402_6.jpg,product_flash_drive_1402_6.jpg,flash_drive
|
418 |
+
index/product_ear_pods_552_5.jpg,product_ear_pods_552_5.jpg,ear_pods
|
419 |
+
index/product_backpack_889_0.jpg,product_backpack_889_0.jpg,backpack
|
420 |
+
index/product_mouse_372_4.jpg,product_mouse_372_4.jpg,mouse
|
421 |
+
index/product_backpack_831_1.jpg,product_backpack_831_1.jpg,backpack
|
422 |
+
index/product_backpack_874_5.jpg,product_backpack_874_5.jpg,backpack
|
423 |
+
index/product_usb_hub_36_1.jpg,product_usb_hub_36_1.jpg,usb_hub
|
424 |
+
index/product_backpack_850_0.jpg,product_backpack_850_0.jpg,backpack
|
425 |
+
index/product_ear_pods_567_3.jpg,product_ear_pods_567_3.jpg,ear_pods
|
426 |
+
index/product_wrist_watch_1189_4.jpg,product_wrist_watch_1189_4.jpg,wrist_watch
|
427 |
+
index/product_wrist_watch_1168_0.jpg,product_wrist_watch_1168_0.jpg,wrist_watch
|
428 |
+
index/product_wrist_watch_1180_1.jpg,product_wrist_watch_1180_1.jpg,wrist_watch
|
429 |
+
index/product_headset_774_4.jpg,product_headset_774_4.jpg,headset
|
430 |
+
index/product_usb_hub_24_1.jpg,product_usb_hub_24_1.jpg,usb_hub
|
431 |
+
index/product_flash_drive_1403_4.jpg,product_flash_drive_1403_4.jpg,flash_drive
|
432 |
+
index/product_mouse_263_2.jpg,product_mouse_263_2.jpg,mouse
|
433 |
+
index/product_headset_721_3.jpg,product_headset_721_3.jpg,headset
|
434 |
+
index/product_ear_pods_403_1.jpg,product_ear_pods_403_1.jpg,ear_pods
|
435 |
+
index/product_usb_hub_186_0.jpg,product_usb_hub_186_0.jpg,usb_hub
|
436 |
+
index/product_headset_765_0.jpg,product_headset_765_0.jpg,headset
|
437 |
+
index/product_backpack_910_3.jpg,product_backpack_910_3.jpg,backpack
|
438 |
+
index/product_headset_652_4.jpg,product_headset_652_4.jpg,headset
|
439 |
+
index/product_headset_774_3.jpg,product_headset_774_3.jpg,headset
|
440 |
+
index/product_flash_drive_1403_3.jpg,product_flash_drive_1403_3.jpg,flash_drive
|
441 |
+
index/product_backpack_802_3.jpg,product_backpack_802_3.jpg,backpack
|
442 |
+
index/product_headset_639_1.jpg,product_headset_639_1.jpg,headset
|
443 |
+
index/product_mouse_372_5.jpg,product_mouse_372_5.jpg,mouse
|
444 |
+
index/product_wrist_watch_1198_4.jpg,product_wrist_watch_1198_4.jpg,wrist_watch
|
445 |
+
index/product_backpack_839_0.jpg,product_backpack_839_0.jpg,backpack
|
446 |
+
index/product_wrist_watch_1022_3.jpg,product_wrist_watch_1022_3.jpg,wrist_watch
|
447 |
+
index/product_backpack_844_1.jpg,product_backpack_844_1.jpg,backpack
|
448 |
+
index/product_backpack_802_0.jpg,product_backpack_802_0.jpg,backpack
|
449 |
+
index/product_ear_pods_576_2.jpg,product_ear_pods_576_2.jpg,ear_pods
|
450 |
+
index/product_mouse_217_2.jpg,product_mouse_217_2.jpg,mouse
|
451 |
+
index/product_usb_hub_68_0.jpg,product_usb_hub_68_0.jpg,usb_hub
|
452 |
+
index/product_flash_drive_1403_1.jpg,product_flash_drive_1403_1.jpg,flash_drive
|
453 |
+
index/product_ear_pods_488_0.jpg,product_ear_pods_488_0.jpg,ear_pods
|
454 |
+
index/product_backpack_804_5.jpg,product_backpack_804_5.jpg,backpack
|
455 |
+
index/product_backpack_880_4.jpg,product_backpack_880_4.jpg,backpack
|
456 |
+
index/product_flash_drive_1403_2.jpg,product_flash_drive_1403_2.jpg,flash_drive
|
457 |
+
index/product_wrist_watch_1043_0.jpg,product_wrist_watch_1043_0.jpg,wrist_watch
|
458 |
+
index/product_backpack_932_0.jpg,product_backpack_932_0.jpg,backpack
|
459 |
+
index/product_wrist_watch_1006_3.jpg,product_wrist_watch_1006_3.jpg,wrist_watch
|
460 |
+
index/product_headset_662_4.jpg,product_headset_662_4.jpg,headset
|
461 |
+
index/product_mouse_263_1.jpg,product_mouse_263_1.jpg,mouse
|
462 |
+
index/product_usb_hub_168_5.jpg,product_usb_hub_168_5.jpg,usb_hub
|
463 |
+
index/product_office_chairs_1256_0.jpg,product_office_chairs_1256_0.jpg,office_chairs
|
464 |
+
index/product_wrist_watch_1124_3.jpg,product_wrist_watch_1124_3.jpg,wrist_watch
|
465 |
+
index/product_office_chairs_1296_0.jpg,product_office_chairs_1296_0.jpg,office_chairs
|
466 |
+
index/product_headset_641_0.jpg,product_headset_641_0.jpg,headset
|
467 |
+
index/product_wrist_watch_1020_0.jpg,product_wrist_watch_1020_0.jpg,wrist_watch
|
468 |
+
index/product_ear_pods_591_0.jpg,product_ear_pods_591_0.jpg,ear_pods
|
469 |
+
index/product_wrist_watch_1164_0.jpg,product_wrist_watch_1164_0.jpg,wrist_watch
|
470 |
+
index/product_headset_662_6.jpg,product_headset_662_6.jpg,headset
|
471 |
+
index/product_wrist_watch_1043_3.jpg,product_wrist_watch_1043_3.jpg,wrist_watch
|
472 |
+
index/product_ear_pods_509_1.jpg,product_ear_pods_509_1.jpg,ear_pods
|
473 |
+
index/product_wrist_watch_1180_0.jpg,product_wrist_watch_1180_0.jpg,wrist_watch
|
474 |
+
index/product_ear_pods_591_4.jpg,product_ear_pods_591_4.jpg,ear_pods
|
475 |
+
index/product_flash_drive_1403_0.jpg,product_flash_drive_1403_0.jpg,flash_drive
|
476 |
+
index/product_backpack_817_5.jpg,product_backpack_817_5.jpg,backpack
|
477 |
+
index/product_ear_pods_412_3.jpg,product_ear_pods_412_3.jpg,ear_pods
|
478 |
+
index/product_backpack_817_2.jpg,product_backpack_817_2.jpg,backpack
|
479 |
+
index/product_flash_drive_1424_0.jpg,product_flash_drive_1424_0.jpg,flash_drive
|
480 |
+
index/product_flash_drive_1407_4.jpg,product_flash_drive_1407_4.jpg,flash_drive
|
481 |
+
index/product_mouse_372_7.jpg,product_mouse_372_7.jpg,mouse
|
482 |
+
index/product_wrist_watch_1030_6.jpg,product_wrist_watch_1030_6.jpg,wrist_watch
|
483 |
+
index/product_ear_pods_521_0.jpg,product_ear_pods_521_0.jpg,ear_pods
|
484 |
+
index/product_mouse_324_7.jpg,product_mouse_324_7.jpg,mouse
|
485 |
+
index/product_backpack_831_2.jpg,product_backpack_831_2.jpg,backpack
|
486 |
+
index/product_ear_pods_466_5.jpg,product_ear_pods_466_5.jpg,ear_pods
|
487 |
+
index/product_ear_pods_449_0.jpg,product_ear_pods_449_0.jpg,ear_pods
|
488 |
+
index/product_backpack_817_1.jpg,product_backpack_817_1.jpg,backpack
|
489 |
+
index/product_ear_pods_552_6.jpg,product_ear_pods_552_6.jpg,ear_pods
|
490 |
+
index/product_wrist_watch_1026_2.jpg,product_wrist_watch_1026_2.jpg,wrist_watch
|
491 |
+
index/product_wrist_watch_1030_4.jpg,product_wrist_watch_1030_4.jpg,wrist_watch
|
492 |
+
index/product_ear_pods_593_0.jpg,product_ear_pods_593_0.jpg,ear_pods
|
493 |
+
index/product_mouse_207_4.jpg,product_mouse_207_4.jpg,mouse
|
494 |
+
index/product_backpack_855_1.jpg,product_backpack_855_1.jpg,backpack
|
495 |
+
index/product_mouse_212_2.jpg,product_mouse_212_2.jpg,mouse
|
496 |
+
index/product_headset_781_0.jpg,product_headset_781_0.jpg,headset
|
497 |
+
index/product_ear_pods_535_4.jpg,product_ear_pods_535_4.jpg,ear_pods
|
498 |
+
index/product_wrist_watch_1020_2.jpg,product_wrist_watch_1020_2.jpg,wrist_watch
|
499 |
+
index/product_usb_hub_176_4.jpg,product_usb_hub_176_4.jpg,usb_hub
|
500 |
+
index/product_mouse_203_4.jpg,product_mouse_203_4.jpg,mouse
|
501 |
+
index/product_mouse_324_2.jpg,product_mouse_324_2.jpg,mouse
|
502 |
+
index/product_ear_pods_593_1.jpg,product_ear_pods_593_1.jpg,ear_pods
|
503 |
+
index/product_backpack_865_0.jpg,product_backpack_865_0.jpg,backpack
|
504 |
+
index/product_wrist_watch_1048_0.jpg,product_wrist_watch_1048_0.jpg,wrist_watch
|
505 |
+
index/product_wrist_watch_1189_0.jpg,product_wrist_watch_1189_0.jpg,wrist_watch
|
506 |
+
index/product_backpack_839_4.jpg,product_backpack_839_4.jpg,backpack
|
507 |
+
index/product_usb_hub_3_1.jpg,product_usb_hub_3_1.jpg,usb_hub
|
508 |
+
index/product_headset_657_0.jpg,product_headset_657_0.jpg,headset
|
509 |
+
index/product_headset_774_1.jpg,product_headset_774_1.jpg,headset
|
510 |
+
index/product_wrist_watch_1043_5.jpg,product_wrist_watch_1043_5.jpg,wrist_watch
|
511 |
+
index/product_flash_drive_1445_7.jpg,product_flash_drive_1445_7.jpg,flash_drive
|
512 |
+
index/product_backpack_910_1.jpg,product_backpack_910_1.jpg,backpack
|
513 |
+
index/product_flash_drive_1445_0.jpg,product_flash_drive_1445_0.jpg,flash_drive
|
514 |
+
index/product_mouse_228_0.jpg,product_mouse_228_0.jpg,mouse
|
515 |
+
index/product_mouse_263_4.jpg,product_mouse_263_4.jpg,mouse
|
516 |
+
index/product_headset_764_3.jpg,product_headset_764_3.jpg,headset
|
517 |
+
index/product_wrist_watch_1067_0.jpg,product_wrist_watch_1067_0.jpg,wrist_watch
|
518 |
+
index/product_usb_hub_168_1.jpg,product_usb_hub_168_1.jpg,usb_hub
|
519 |
+
index/product_mouse_349_7.jpg,product_mouse_349_7.jpg,mouse
|
520 |
+
index/product_mouse_203_3.jpg,product_mouse_203_3.jpg,mouse
|
521 |
+
index/product_ear_pods_567_1.jpg,product_ear_pods_567_1.jpg,ear_pods
|
522 |
+
index/product_ear_pods_585_2.jpg,product_ear_pods_585_2.jpg,ear_pods
|
523 |
+
index/product_office_chairs_1324_0.jpg,product_office_chairs_1324_0.jpg,office_chairs
|
524 |
+
index/product_flash_drive_1420_1.jpg,product_flash_drive_1420_1.jpg,flash_drive
|
525 |
+
index/product_wrist_watch_1043_1.jpg,product_wrist_watch_1043_1.jpg,wrist_watch
|
526 |
+
index/product_ear_pods_509_0.jpg,product_ear_pods_509_0.jpg,ear_pods
|
527 |
+
index/product_mouse_390_1.jpg,product_mouse_390_1.jpg,mouse
|
528 |
+
index/product_headset_631_0.jpg,product_headset_631_0.jpg,headset
|
529 |
+
index/product_mouse_207_1.jpg,product_mouse_207_1.jpg,mouse
|
530 |
+
index/product_ear_pods_574_6.jpg,product_ear_pods_574_6.jpg,ear_pods
|
531 |
+
index/product_backpack_828_3.jpg,product_backpack_828_3.jpg,backpack
|
532 |
+
index/product_headset_681_0.jpg,product_headset_681_0.jpg,headset
|
533 |
+
index/product_usb_hub_165_0.jpg,product_usb_hub_165_0.jpg,usb_hub
|
534 |
+
index/product_backpack_885_5.jpg,product_backpack_885_5.jpg,backpack
|
535 |
+
index/product_wrist_watch_1189_7.jpg,product_wrist_watch_1189_7.jpg,wrist_watch
|
536 |
+
index/product_headset_625_3.jpg,product_headset_625_3.jpg,headset
|
537 |
+
index/product_wrist_watch_1018_0.jpg,product_wrist_watch_1018_0.jpg,wrist_watch
|
538 |
+
index/product_ear_pods_535_0.jpg,product_ear_pods_535_0.jpg,ear_pods
|
539 |
+
index/product_backpack_812_2.jpg,product_backpack_812_2.jpg,backpack
|
540 |
+
index/product_headset_635_0.jpg,product_headset_635_0.jpg,headset
|
541 |
+
index/product_backpack_855_2.jpg,product_backpack_855_2.jpg,backpack
|
542 |
+
index/product_backpack_831_3.jpg,product_backpack_831_3.jpg,backpack
|
543 |
+
index/product_wrist_watch_1124_2.jpg,product_wrist_watch_1124_2.jpg,wrist_watch
|
544 |
+
index/product_mouse_263_3.jpg,product_mouse_263_3.jpg,mouse
|
545 |
+
index/product_mouse_349_1.jpg,product_mouse_349_1.jpg,mouse
|
546 |
+
index/product_wrist_watch_1030_0.jpg,product_wrist_watch_1030_0.jpg,wrist_watch
|
547 |
+
index/product_backpack_910_4.jpg,product_backpack_910_4.jpg,backpack
|
548 |
+
index/product_backpack_850_3.jpg,product_backpack_850_3.jpg,backpack
|
549 |
+
index/product_ear_pods_574_1.jpg,product_ear_pods_574_1.jpg,ear_pods
|
550 |
+
index/product_headset_721_5.jpg,product_headset_721_5.jpg,headset
|
551 |
+
index/product_mouse_282_3.jpg,product_mouse_282_3.jpg,mouse
|
552 |
+
index/product_usb_hub_36_4.jpg,product_usb_hub_36_4.jpg,usb_hub
|
553 |
+
index/product_backpack_817_6.jpg,product_backpack_817_6.jpg,backpack
|
554 |
+
index/product_backpack_850_4.jpg,product_backpack_850_4.jpg,backpack
|
555 |
+
index/product_mouse_263_6.jpg,product_mouse_263_6.jpg,mouse
|
556 |
+
index/product_ear_pods_535_2.jpg,product_ear_pods_535_2.jpg,ear_pods
|
557 |
+
index/product_backpack_831_5.jpg,product_backpack_831_5.jpg,backpack
|
558 |
+
index/product_ear_pods_591_1.jpg,product_ear_pods_591_1.jpg,ear_pods
|
559 |
+
index/product_usb_hub_124_0.jpg,product_usb_hub_124_0.jpg,usb_hub
|
560 |
+
index/product_usb_hub_168_4.jpg,product_usb_hub_168_4.jpg,usb_hub
|
561 |
+
index/product_usb_hub_16_0.jpg,product_usb_hub_16_0.jpg,usb_hub
|
562 |
+
index/product_wrist_watch_1011_4.jpg,product_wrist_watch_1011_4.jpg,wrist_watch
|
563 |
+
index/product_usb_hub_17_0.jpg,product_usb_hub_17_0.jpg,usb_hub
|
564 |
+
index/product_office_chairs_1362_0.jpg,product_office_chairs_1362_0.jpg,office_chairs
|
565 |
+
index/product_backpack_813_3.jpg,product_backpack_813_3.jpg,backpack
|
566 |
+
index/product_ear_pods_521_2.jpg,product_ear_pods_521_2.jpg,ear_pods
|
567 |
+
index/product_backpack_831_0.jpg,product_backpack_831_0.jpg,backpack
|
568 |
+
index/product_backpack_873_0.jpg,product_backpack_873_0.jpg,backpack
|
569 |
+
index/product_usb_hub_17_1.jpg,product_usb_hub_17_1.jpg,usb_hub
|
570 |
+
index/product_ear_pods_456_0.jpg,product_ear_pods_456_0.jpg,ear_pods
|
571 |
+
index/product_usb_hub_136_3.jpg,product_usb_hub_136_3.jpg,usb_hub
|
572 |
+
index/product_ear_pods_552_0.jpg,product_ear_pods_552_0.jpg,ear_pods
|
573 |
+
index/product_backpack_804_1.jpg,product_backpack_804_1.jpg,backpack
|
574 |
+
index/product_wrist_watch_1006_7.jpg,product_wrist_watch_1006_7.jpg,wrist_watch
|
575 |
+
index/product_wrist_watch_1043_4.jpg,product_wrist_watch_1043_4.jpg,wrist_watch
|
576 |
+
index/product_office_chairs_1204_2.jpg,product_office_chairs_1204_2.jpg,office_chairs
|
577 |
+
index/product_ear_pods_528_0.jpg,product_ear_pods_528_0.jpg,ear_pods
|
578 |
+
index/product_mouse_324_1.jpg,product_mouse_324_1.jpg,mouse
|
579 |
+
index/product_ear_pods_567_4.jpg,product_ear_pods_567_4.jpg,ear_pods
|
580 |
+
index/product_wrist_watch_1029_1.jpg,product_wrist_watch_1029_1.jpg,wrist_watch
|
581 |
+
index/product_mouse_207_2.jpg,product_mouse_207_2.jpg,mouse
|
582 |
+
index/product_flash_drive_1407_0.jpg,product_flash_drive_1407_0.jpg,flash_drive
|
583 |
+
index/product_wrist_watch_1122_0.jpg,product_wrist_watch_1122_0.jpg,wrist_watch
|
584 |
+
index/product_flash_drive_1407_5.jpg,product_flash_drive_1407_5.jpg,flash_drive
|
585 |
+
index/product_flash_drive_1407_7.jpg,product_flash_drive_1407_7.jpg,flash_drive
|
586 |
+
index/product_wrist_watch_1124_0.jpg,product_wrist_watch_1124_0.jpg,wrist_watch
|
587 |
+
index/product_headset_668_0.jpg,product_headset_668_0.jpg,headset
|
588 |
+
index/product_headset_693_3.jpg,product_headset_693_3.jpg,headset
|
589 |
+
index/product_wrist_watch_1107_0.jpg,product_wrist_watch_1107_0.jpg,wrist_watch
|
590 |
+
index/product_mouse_397_2.jpg,product_mouse_397_2.jpg,mouse
|
591 |
+
index/product_ear_pods_442_1.jpg,product_ear_pods_442_1.jpg,ear_pods
|
592 |
+
index/product_backpack_880_3.jpg,product_backpack_880_3.jpg,backpack
|
593 |
+
index/product_ear_pods_440_0.jpg,product_ear_pods_440_0.jpg,ear_pods
|
594 |
+
index/product_usb_hub_123_0.jpg,product_usb_hub_123_0.jpg,usb_hub
|
595 |
+
index/product_mouse_397_1.jpg,product_mouse_397_1.jpg,mouse
|
596 |
+
index/product_backpack_880_7.jpg,product_backpack_880_7.jpg,backpack
|
597 |
+
index/product_ear_pods_421_0.jpg,product_ear_pods_421_0.jpg,ear_pods
|
598 |
+
index/product_headset_736_2.jpg,product_headset_736_2.jpg,headset
|
599 |
+
index/product_usb_hub_186_7.jpg,product_usb_hub_186_7.jpg,usb_hub
|
600 |
+
index/product_usb_hub_38_0.jpg,product_usb_hub_38_0.jpg,usb_hub
|
601 |
+
index/product_backpack_864_3.jpg,product_backpack_864_3.jpg,backpack
|
602 |
+
index/product_usb_hub_186_4.jpg,product_usb_hub_186_4.jpg,usb_hub
|
603 |
+
index/product_backpack_864_1.jpg,product_backpack_864_1.jpg,backpack
|
604 |
+
index/product_headset_629_3.jpg,product_headset_629_3.jpg,headset
|
605 |
+
index/product_mouse_212_5.jpg,product_mouse_212_5.jpg,mouse
|
606 |
+
index/product_ear_pods_442_0.jpg,product_ear_pods_442_0.jpg,ear_pods
|
607 |
+
index/product_mouse_229_3.jpg,product_mouse_229_3.jpg,mouse
|
608 |
+
index/product_wrist_watch_1164_2.jpg,product_wrist_watch_1164_2.jpg,wrist_watch
|
609 |
+
index/product_headset_675_0.jpg,product_headset_675_0.jpg,headset
|
610 |
+
index/product_wrist_watch_1164_5.jpg,product_wrist_watch_1164_5.jpg,wrist_watch
|
611 |
+
index/product_mouse_229_2.jpg,product_mouse_229_2.jpg,mouse
|
612 |
+
index/product_ear_pods_421_2.jpg,product_ear_pods_421_2.jpg,ear_pods
|
613 |
+
index/product_mouse_229_6.jpg,product_mouse_229_6.jpg,mouse
|
614 |
+
index/product_ear_pods_459_2.jpg,product_ear_pods_459_2.jpg,ear_pods
|
615 |
+
index/product_headset_629_0.jpg,product_headset_629_0.jpg,headset
|
616 |
+
index/product_office_chairs_1230_1.jpg,product_office_chairs_1230_1.jpg,office_chairs
|
617 |
+
index/product_ear_pods_586_0.jpg,product_ear_pods_586_0.jpg,ear_pods
|
618 |
+
index/product_wrist_watch_1107_6.jpg,product_wrist_watch_1107_6.jpg,wrist_watch
|
619 |
+
index/product_wrist_watch_1099_0.jpg,product_wrist_watch_1099_0.jpg,wrist_watch
|
620 |
+
index/product_usb_hub_186_2.jpg,product_usb_hub_186_2.jpg,usb_hub
|
621 |
+
index/product_mouse_229_1.jpg,product_mouse_229_1.jpg,mouse
|
622 |
+
index/product_headset_675_1.jpg,product_headset_675_1.jpg,headset
|
623 |
+
index/product_headset_657_2.jpg,product_headset_657_2.jpg,headset
|
624 |
+
index/product_headset_693_0.jpg,product_headset_693_0.jpg,headset
|
625 |
+
index/product_backpack_844_4.jpg,product_backpack_844_4.jpg,backpack
|
626 |
+
index/product_headset_675_4.jpg,product_headset_675_4.jpg,headset
|
627 |
+
index/product_usb_hub_180_2.jpg,product_usb_hub_180_2.jpg,usb_hub
|
628 |
+
index/product_flash_drive_1411_2.jpg,product_flash_drive_1411_2.jpg,flash_drive
|
629 |
+
index/product_ear_pods_442_3.jpg,product_ear_pods_442_3.jpg,ear_pods
|
630 |
+
index/product_ear_pods_403_5.jpg,product_ear_pods_403_5.jpg,ear_pods
|
631 |
+
index/product_backpack_880_1.jpg,product_backpack_880_1.jpg,backpack
|
632 |
+
index/product_headset_629_1.jpg,product_headset_629_1.jpg,headset
|
633 |
+
index/product_mouse_212_6.jpg,product_mouse_212_6.jpg,mouse
|
634 |
+
index/product_ear_pods_466_1.jpg,product_ear_pods_466_1.jpg,ear_pods
|
635 |
+
index/product_usb_hub_186_5.jpg,product_usb_hub_186_5.jpg,usb_hub
|
636 |
+
index/product_flash_drive_1411_3.jpg,product_flash_drive_1411_3.jpg,flash_drive
|
637 |
+
index/product_ear_pods_403_2.jpg,product_ear_pods_403_2.jpg,ear_pods
|
638 |
+
index/product_mouse_397_0.jpg,product_mouse_397_0.jpg,mouse
|
639 |
+
index/product_ear_pods_440_1.jpg,product_ear_pods_440_1.jpg,ear_pods
|
640 |
+
index/product_ear_pods_459_1.jpg,product_ear_pods_459_1.jpg,ear_pods
|
641 |
+
index/product_headset_736_0.jpg,product_headset_736_0.jpg,headset
|
642 |
+
index/product_backpack_864_7.jpg,product_backpack_864_7.jpg,backpack
|
643 |
+
index/product_headset_657_5.jpg,product_headset_657_5.jpg,headset
|
644 |
+
index/product_headset_657_4.jpg,product_headset_657_4.jpg,headset
|
645 |
+
index/product_office_chairs_1393_2.jpg,product_office_chairs_1393_2.jpg,office_chairs
|
646 |
+
index/product_ear_pods_459_4.jpg,product_ear_pods_459_4.jpg,ear_pods
|
647 |
+
index/product_headset_693_2.jpg,product_headset_693_2.jpg,headset
|
648 |
+
index/product_office_chairs_1230_2.jpg,product_office_chairs_1230_2.jpg,office_chairs
|
649 |
+
index/product_usb_hub_38_1.jpg,product_usb_hub_38_1.jpg,usb_hub
|
650 |
+
index/product_mouse_212_0.jpg,product_mouse_212_0.jpg,mouse
|
651 |
+
index/product_headset_668_1.jpg,product_headset_668_1.jpg,headset
|
652 |
+
index/product_ear_pods_459_3.jpg,product_ear_pods_459_3.jpg,ear_pods
|
653 |
+
index/product_usb_hub_142_0.jpg,product_usb_hub_142_0.jpg,usb_hub
|
654 |
+
index/product_usb_hub_180_0.jpg,product_usb_hub_180_0.jpg,usb_hub
|
655 |
+
index/product_office_chairs_1291_0.jpg,product_office_chairs_1291_0.jpg,office_chairs
|
656 |
+
index/product_backpack_864_0.jpg,product_backpack_864_0.jpg,backpack
|
657 |
+
index/product_ear_pods_403_7.jpg,product_ear_pods_403_7.jpg,ear_pods
|
658 |
+
index/product_headset_675_2.jpg,product_headset_675_2.jpg,headset
|
659 |
+
index/product_usb_hub_180_1.jpg,product_usb_hub_180_1.jpg,usb_hub
|
660 |
+
index/product_ear_pods_403_3.jpg,product_ear_pods_403_3.jpg,ear_pods
|
661 |
+
index/product_ear_pods_521_1.jpg,product_ear_pods_521_1.jpg,ear_pods
|
662 |
+
index/product_headset_668_4.jpg,product_headset_668_4.jpg,headset
|
663 |
+
index/product_mouse_212_4.jpg,product_mouse_212_4.jpg,mouse
|
664 |
+
index/product_flash_drive_1411_7.jpg,product_flash_drive_1411_7.jpg,flash_drive
|
665 |
+
index/product_ear_pods_440_2.jpg,product_ear_pods_440_2.jpg,ear_pods
|
666 |
+
index/product_headset_736_1.jpg,product_headset_736_1.jpg,headset
|
667 |
+
index/product_wrist_watch_1018_2.jpg,product_wrist_watch_1018_2.jpg,wrist_watch
|
668 |
+
index/product_backpack_864_6.jpg,product_backpack_864_6.jpg,backpack
|
669 |
+
index/product_backpack_844_6.jpg,product_backpack_844_6.jpg,backpack
|
670 |
+
index/product_headset_736_3.jpg,product_headset_736_3.jpg,headset
|
671 |
+
index/product_backpack_864_2.jpg,product_backpack_864_2.jpg,backpack
|
672 |
+
index/product_usb_hub_142_1.jpg,product_usb_hub_142_1.jpg,usb_hub
|
673 |
+
index/product_mouse_212_7.jpg,product_mouse_212_7.jpg,mouse
|
674 |
+
index/product_wrist_watch_1162_0.jpg,product_wrist_watch_1162_0.jpg,wrist_watch
|
675 |
+
index/product_ear_pods_421_1.jpg,product_ear_pods_421_1.jpg,ear_pods
|
676 |
+
index/product_wrist_watch_1107_4.jpg,product_wrist_watch_1107_4.jpg,wrist_watch
|
677 |
+
index/product_wrist_watch_1107_5.jpg,product_wrist_watch_1107_5.jpg,wrist_watch
|
678 |
+
index/product_mouse_212_1.jpg,product_mouse_212_1.jpg,mouse
|
679 |
+
index/product_backpack_862_4.jpg,product_backpack_862_4.jpg,backpack
|
680 |
+
index/product_headset_629_2.jpg,product_headset_629_2.jpg,headset
|
681 |
+
index/product_flash_drive_1411_0.jpg,product_flash_drive_1411_0.jpg,flash_drive
|
682 |
+
index/product_mouse_229_0.jpg,product_mouse_229_0.jpg,mouse
|
683 |
+
index/product_office_chairs_1253_0.jpg,product_office_chairs_1253_0.jpg,office_chairs
|
684 |
+
index/product_ear_pods_466_2.jpg,product_ear_pods_466_2.jpg,ear_pods
|
685 |
+
index/product_ear_pods_466_3.jpg,product_ear_pods_466_3.jpg,ear_pods
|
686 |
+
index/product_usb_hub_186_6.jpg,product_usb_hub_186_6.jpg,usb_hub
|
687 |
+
index/product_headset_693_1.jpg,product_headset_693_1.jpg,headset
|
688 |
+
index/product_ear_pods_403_4.jpg,product_ear_pods_403_4.jpg,ear_pods
|
689 |
+
index/product_backpack_880_0.jpg,product_backpack_880_0.jpg,backpack
|
690 |
+
index/product_ear_pods_466_0.jpg,product_ear_pods_466_0.jpg,ear_pods
|
691 |
+
index/product_headset_657_3.jpg,product_headset_657_3.jpg,headset
|
692 |
+
index/product_usb_hub_186_3.jpg,product_usb_hub_186_3.jpg,usb_hub
|
693 |
+
index/product_backpack_944_0.jpg,product_backpack_944_0.jpg,backpack
|
694 |
+
index/product_mouse_229_7.jpg,product_mouse_229_7.jpg,mouse
|
695 |
+
index/product_headset_675_3.jpg,product_headset_675_3.jpg,headset
|
696 |
+
index/product_wrist_watch_1164_4.jpg,product_wrist_watch_1164_4.jpg,wrist_watch
|
697 |
+
index/product_office_chairs_1230_0.jpg,product_office_chairs_1230_0.jpg,office_chairs
|
698 |
+
index/product_ear_pods_442_2.jpg,product_ear_pods_442_2.jpg,ear_pods
|
699 |
+
index/product_headset_757_0.jpg,product_headset_757_0.jpg,headset
|
700 |
+
index/product_backpack_880_2.jpg,product_backpack_880_2.jpg,backpack
|
701 |
+
index/product_headset_675_5.jpg,product_headset_675_5.jpg,headset
|
702 |
+
index/product_ear_pods_466_6.jpg,product_ear_pods_466_6.jpg,ear_pods
|
703 |
+
index/product_ear_pods_459_0.jpg,product_ear_pods_459_0.jpg,ear_pods
|
704 |
+
index/product_wrist_watch_1164_3.jpg,product_wrist_watch_1164_3.jpg,wrist_watch
|
705 |
+
index/product_wrist_watch_1164_6.jpg,product_wrist_watch_1164_6.jpg,wrist_watch
|
706 |
+
index/product_backpack_844_7.jpg,product_backpack_844_7.jpg,backpack
|
707 |
+
index/product_backpack_844_5.jpg,product_backpack_844_5.jpg,backpack
|
708 |
+
index/product_wrist_watch_1107_1.jpg,product_wrist_watch_1107_1.jpg,wrist_watch
|
709 |
+
index/product_ear_pods_440_3.jpg,product_ear_pods_440_3.jpg,ear_pods
|
710 |
+
index/product_flash_drive_1411_1.jpg,product_flash_drive_1411_1.jpg,flash_drive
|
data/processed/jumia_3650/train.csv
ADDED
The diff for this file is too large to render.
See raw diff
|
|
image_search_engine/__init__.py
ADDED
File without changes
|
image_search_engine/artifacts/__init__.py
ADDED
File without changes
|
image_search_engine/artifacts/config.json
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
{"seed": 2022, "epochs": 10, "img_size": 224, "model_name": "tf_efficientnet_b0_ns", "num_classes": 8, "train_batch_size": 32, "valid_batch_size": 32, "learning_rate": 0.001, "scheduler": "OneCycleLR", "min_lr": 1e-05, "T_max": 500, "weight_decay": 1e-06, "n_fold": 100, "n_accumulate": 1, "device": "cuda", "test_mode": true, "enable_amp_half_precision": false, "s": 10.0, "m": 0.1, "ls_eps": 0.0, "easy_margin": false}
|
image_search_engine/artifacts/label_encoder/class_encoder_jumia_3650.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:5400e30ac91878cf846e975acb82b7972bf64cccf84d9040e6ef404047fee879
|
3 |
+
size 644
|
image_search_engine/artifacts/model_staged/index.pkl
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:d05416e85b4ffa7eba35f9e453a1ad8153666a6917a56c9028089cf800fb2664
|
3 |
+
size 6023748
|
image_search_engine/artifacts/model_staged/model.pt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:f5369b9fc74deea65f75cb5d219f26a717bec873d590e0f4771b58ae02b73b0e
|
3 |
+
size 18943957
|
image_search_engine/artifacts/weights/Loss0.6555_epoch3.bin
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:12a5ae97a2fb28ea0d6845091646f90509c50013a7b441555aa3f58565621ae4
|
3 |
+
size 18971557
|
image_search_engine/data/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
from .jumia_3650_dataset import Jumia3650Dataset
|
image_search_engine/data/base_data_module.py
ADDED
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import pandas as pd
|
2 |
+
import torch
|
3 |
+
from PIL import Image
|
4 |
+
from sklearn.preprocessing import LabelEncoder
|
5 |
+
from torch.utils.data import DataLoader, Dataset
|
6 |
+
|
7 |
+
from image_search_engine.metadata import jumia_3650
|
8 |
+
|
9 |
+
encoder = LabelEncoder()
|
10 |
+
|
11 |
+
|
12 |
+
class JumiaImageDataset(Dataset):
|
13 |
+
def __init__(self, data_filename, transforms=None):
|
14 |
+
self.df = pd.read_csv(data_filename)
|
15 |
+
self.file_paths = self.df["filepath"].values
|
16 |
+
self.labels = encoder.fit_transform(self.df["class"])
|
17 |
+
self.transforms = transforms
|
18 |
+
|
19 |
+
def __len__(self):
|
20 |
+
return len(self.df)
|
21 |
+
|
22 |
+
def __getitem__(self, index):
|
23 |
+
img_path = jumia_3650.PROCESSED_DATA_DIRNAME / self.file_paths[index]
|
24 |
+
img = Image.open(img_path).convert("RGB")
|
25 |
+
label = self.labels[index]
|
26 |
+
|
27 |
+
if self.transforms:
|
28 |
+
img = self.transforms(img)
|
29 |
+
|
30 |
+
return {"image": img, "label": torch.tensor(label, dtype=torch.long)}
|
31 |
+
|
32 |
+
def create_dataloader(self, batch_size, shuffle=True, num_workers=0):
|
33 |
+
return DataLoader(
|
34 |
+
self,
|
35 |
+
batch_size=batch_size,
|
36 |
+
shuffle=shuffle,
|
37 |
+
num_workers=num_workers,
|
38 |
+
pin_memory=True,
|
39 |
+
)
|
image_search_engine/data/jumia_3650_dataset.py
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pathlib import Path
|
2 |
+
|
3 |
+
import joblib
|
4 |
+
import pandas as pd
|
5 |
+
import torch
|
6 |
+
from PIL import Image
|
7 |
+
from torch.utils.data import DataLoader, Dataset
|
8 |
+
from torchvision import transforms
|
9 |
+
|
10 |
+
from image_search_engine.metadata import jumia_3650
|
11 |
+
|
12 |
+
PACKAGE_DIR = Path(__file__).parent.parent
|
13 |
+
|
14 |
+
# Load the pickled file
|
15 |
+
with open(
|
16 |
+
PACKAGE_DIR / "artifacts/label_encoder/class_encoder_jumia_3650.pkl", "rb"
|
17 |
+
) as file:
|
18 |
+
encoder = joblib.load(file)
|
19 |
+
|
20 |
+
|
21 |
+
class Jumia3650Dataset(Dataset):
|
22 |
+
def __init__(self, data_filename, data_transforms=None, img_size=224):
|
23 |
+
self.df = pd.read_csv(data_filename)
|
24 |
+
self.file_paths = self.df["filepath"].values
|
25 |
+
self.labels = encoder.transform(self.df["class"])
|
26 |
+
self.classes = encoder.classes_
|
27 |
+
self.class_to_idx = {l: i for i, l in enumerate(encoder.classes_)}
|
28 |
+
if transforms is None:
|
29 |
+
self.data_transforms = transforms.Compose(
|
30 |
+
[
|
31 |
+
transforms.ToTensor(),
|
32 |
+
transforms.Resize((img_size, img_size)),
|
33 |
+
transforms.Normalize(
|
34 |
+
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]
|
35 |
+
),
|
36 |
+
]
|
37 |
+
)
|
38 |
+
else:
|
39 |
+
self.data_transforms = data_transforms
|
40 |
+
|
41 |
+
def __len__(self):
|
42 |
+
return len(self.df)
|
43 |
+
|
44 |
+
def __getitem__(self, index):
|
45 |
+
img_path = jumia_3650.PROCESSED_DATA_DIRNAME / self.file_paths[index]
|
46 |
+
img = Image.open(img_path).convert("RGB")
|
47 |
+
label = self.labels[index]
|
48 |
+
|
49 |
+
img = self.data_transforms(img)
|
50 |
+
|
51 |
+
return {"image": img, "label": torch.tensor(label, dtype=torch.long)}
|
52 |
+
|
53 |
+
def create_dataloader(self, batch_size, shuffle=True, num_workers=0):
|
54 |
+
return DataLoader(
|
55 |
+
self,
|
56 |
+
batch_size=batch_size,
|
57 |
+
shuffle=shuffle,
|
58 |
+
num_workers=num_workers,
|
59 |
+
pin_memory=True,
|
60 |
+
)
|
image_search_engine/data/utils.py
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
from pathlib import Path
|
3 |
+
|
4 |
+
package_dir = Path(__file__).resolve().parents[1]
|
5 |
+
|
6 |
+
|
7 |
+
def load_config(
|
8 |
+
file_path=package_dir / "artifacts/config.json",
|
9 |
+
):
|
10 |
+
with open(file_path) as file:
|
11 |
+
data = json.load(file)
|
12 |
+
return data
|
13 |
+
|
14 |
+
|
15 |
+
if __name__ == "__main__":
|
16 |
+
load_config()
|
image_search_engine/evaluation/__init__.py
ADDED
File without changes
|
image_search_engine/image_search_engine.egg-info/PKG-INFO
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Metadata-Version: 2.1
|
2 |
+
Name: image-search-engine
|
3 |
+
Version: 0.1.0
|
4 |
+
Summary: A visual search engine for Jumia that lets users search for products by uploading an image. It uses computer vision to find similar or identical products within the store's inventory, saving users time and providing a more personalized shopping experience.
|
5 |
+
Author: Paul Okewunmi
|
image_search_engine/image_search_engine.egg-info/SOURCES.txt
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
setup.py
|
2 |
+
artifacts/__init__.py
|
3 |
+
data/__init__.py
|
4 |
+
data/base_data_module.py
|
5 |
+
data/jumia_3650_dataset.py
|
6 |
+
data/utils.py
|
7 |
+
evaluation/__init__.py
|
8 |
+
image_search_engine.egg-info/PKG-INFO
|
9 |
+
image_search_engine.egg-info/SOURCES.txt
|
10 |
+
image_search_engine.egg-info/dependency_links.txt
|
11 |
+
image_search_engine.egg-info/top_level.txt
|
12 |
+
metadata/__init__.py
|
13 |
+
metadata/jumia_3650.py
|
14 |
+
metadata/shared.py
|
15 |
+
models/__init__.py
|
16 |
+
models/arc_margin_product.py
|
17 |
+
models/base.py
|
18 |
+
models/efficientnet_ns.py
|
19 |
+
models/gem_pooling.py
|
20 |
+
tests/__init__.py
|
image_search_engine/image_search_engine.egg-info/dependency_links.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
|
image_search_engine/image_search_engine.egg-info/top_level.txt
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
artifacts
|
2 |
+
data
|
3 |
+
evaluation
|
4 |
+
metadata
|
5 |
+
models
|
6 |
+
tests
|
image_search_engine/metadata/__init__.py
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
from . import jumia_3650, shared
|
image_search_engine/metadata/jumia_3650.py
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# from pathlib import Path
|
2 |
+
from image_search_engine.metadata import shared
|
3 |
+
|
4 |
+
RAW_DATA_DIRNAME = shared.DATA_DIRNAME / "raw" / "jumia_3650"
|
5 |
+
METADATA_FILENAME = RAW_DATA_DIRNAME / "metadata.toml"
|
6 |
+
DL_DATA_DIRNAME = shared.DATA_DIRNAME / "downloaded" / "jumia_3650"
|
7 |
+
PROCESSED_DATA_DIRNAME = shared.DATA_DIRNAME / "processed" / "jumia_3650"
|
8 |
+
|
9 |
+
|
10 |
+
CLASS_DICT = {
|
11 |
+
"backpack": 0,
|
12 |
+
"ear_pods": 1,
|
13 |
+
"flash_drive": 2,
|
14 |
+
"headset": 3,
|
15 |
+
"mouse": 4,
|
16 |
+
"office_chairs": 5,
|
17 |
+
"usb_hub": 6,
|
18 |
+
"wrist_watch": 7,
|
19 |
+
}
|
image_search_engine/metadata/shared.py
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from pathlib import Path
|
2 |
+
|
3 |
+
DATA_DIRNAME = Path(__file__).resolve().parents[2] / "data"
|
4 |
+
DOWNLOADED_DATA_DIRNAME = DATA_DIRNAME / "downloaded"
|
image_search_engine/models/__init__.py
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
from .arc_margin_product import ArcMarginProduct
|
2 |
+
from .efficientnet_ns import EfficientNet_b0_ns
|
3 |
+
from .gem_pooling import GeM
|
image_search_engine/models/arc_margin_product.py
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import math
|
2 |
+
|
3 |
+
import torch
|
4 |
+
import torch.nn.functional as F
|
5 |
+
from torch import nn
|
6 |
+
|
7 |
+
ENABLE_HALF_PRECISION = False
|
8 |
+
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
9 |
+
|
10 |
+
|
11 |
+
class ArcMarginProduct(nn.Module):
|
12 |
+
r"""Implement of large margin arc distance: :
|
13 |
+
Args:
|
14 |
+
in_features: size of each input sample
|
15 |
+
out_features: size of each output sample
|
16 |
+
s: norm of input feature
|
17 |
+
m: margin
|
18 |
+
cos(theta + m)
|
19 |
+
"""
|
20 |
+
|
21 |
+
def __init__(
|
22 |
+
self, in_features, out_features, s=10, m=0.10, easy_margin=False, ls_eps=0.0
|
23 |
+
):
|
24 |
+
super(ArcMarginProduct, self).__init__()
|
25 |
+
self.in_features = in_features
|
26 |
+
self.out_features = out_features
|
27 |
+
self.s = s
|
28 |
+
self.m = m
|
29 |
+
self.ls_eps = ls_eps # label smoothing
|
30 |
+
self.weight = nn.Parameter(torch.FloatTensor(out_features, in_features))
|
31 |
+
nn.init.xavier_uniform_(self.weight)
|
32 |
+
|
33 |
+
self.easy_margin = easy_margin
|
34 |
+
self.cos_m = math.cos(m)
|
35 |
+
self.sin_m = math.sin(m)
|
36 |
+
self.th = math.cos(math.pi - m)
|
37 |
+
self.mm = math.sin(math.pi - m) * m
|
38 |
+
|
39 |
+
def forward(self, input, label):
|
40 |
+
# --------------------------- cos(theta) & phi(theta) ---------------------
|
41 |
+
cosine = F.linear(F.normalize(input), F.normalize(self.weight))
|
42 |
+
if ENABLE_HALF_PRECISION == True:
|
43 |
+
cosine = cosine.to(torch.float32)
|
44 |
+
sine = torch.sqrt(1.0 - torch.pow(cosine, 2))
|
45 |
+
phi = cosine * self.cos_m - sine * self.sin_m
|
46 |
+
if self.easy_margin:
|
47 |
+
phi = torch.where(cosine > 0, phi, cosine)
|
48 |
+
else:
|
49 |
+
phi = torch.where(cosine > self.th, phi, cosine - self.mm)
|
50 |
+
# --------------------------- convert label to one-hot ---------------------
|
51 |
+
# one_hot = torch.zeros(cosine.size(), requires_grad=True, device='cuda')
|
52 |
+
one_hot = torch.zeros(cosine.size(), device=DEVICE)
|
53 |
+
one_hot.scatter_(1, label.view(-1, 1).long(), 1)
|
54 |
+
if self.ls_eps > 0:
|
55 |
+
one_hot = (1 - self.ls_eps) * one_hot + self.ls_eps / self.out_features
|
56 |
+
# -------------torch.where(out_i = {x_i if condition_i else y_i) ------------
|
57 |
+
output = (one_hot * phi) + ((1.0 - one_hot) * cosine)
|
58 |
+
output *= self.s
|
59 |
+
|
60 |
+
return output
|
image_search_engine/models/base.py
ADDED
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import copy
|
2 |
+
import gc
|
3 |
+
import time
|
4 |
+
import warnings
|
5 |
+
from collections import defaultdict
|
6 |
+
from pathlib import Path
|
7 |
+
|
8 |
+
# Utils
|
9 |
+
import numpy as np
|
10 |
+
|
11 |
+
# Pytorch Imports
|
12 |
+
import torch
|
13 |
+
import torch.nn as nn
|
14 |
+
from colorama import Back, Fore, Style
|
15 |
+
from PIL import Image
|
16 |
+
|
17 |
+
# Sklearn Imports
|
18 |
+
from torch import nn
|
19 |
+
from torchvision import transforms
|
20 |
+
from tqdm import tqdm
|
21 |
+
|
22 |
+
# import torch.nn as nn
|
23 |
+
|
24 |
+
|
25 |
+
b_ = Fore.BLUE
|
26 |
+
sr_ = Style.RESET_ALL
|
27 |
+
|
28 |
+
warnings.filterwarnings("ignore")
|
29 |
+
|
30 |
+
|
31 |
+
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
32 |
+
|
33 |
+
|
34 |
+
class BaseModel(nn.Module):
|
35 |
+
def __init__(self, predtrained=True) -> None:
|
36 |
+
super(BaseModel, self).__init__()
|
37 |
+
|
38 |
+
def forward(self):
|
39 |
+
pass
|
40 |
+
|
41 |
+
def _criterion(self, outputs, labels):
|
42 |
+
return nn.CrossEntropyLoss()(outputs, labels)
|
43 |
+
|
44 |
+
def _train_one_epoch(self, dataloader, optimizer, scheduler, device, epoch):
|
45 |
+
self.train()
|
46 |
+
|
47 |
+
dataset_size = 0
|
48 |
+
running_loss = 0.0
|
49 |
+
|
50 |
+
bar = tqdm(enumerate(dataloader), total=len(dataloader))
|
51 |
+
for step, data in bar:
|
52 |
+
images = data["image"].to(device, dtype=torch.float)
|
53 |
+
labels = data["label"].to(device, dtype=torch.long)
|
54 |
+
|
55 |
+
batch_size = images.size(0)
|
56 |
+
|
57 |
+
outputs, _ = self(images, labels)
|
58 |
+
loss = self._criterion(outputs, labels)
|
59 |
+
|
60 |
+
loss.backward()
|
61 |
+
|
62 |
+
optimizer.step()
|
63 |
+
optimizer.zero_grad()
|
64 |
+
if scheduler is not None:
|
65 |
+
scheduler.step()
|
66 |
+
|
67 |
+
running_loss += loss.item() * batch_size
|
68 |
+
dataset_size += batch_size
|
69 |
+
|
70 |
+
epoch_loss = running_loss / dataset_size
|
71 |
+
|
72 |
+
bar.set_postfix(
|
73 |
+
Epoch=epoch, Train_Loss=epoch_loss, LR=optimizer.param_groups[0]["lr"]
|
74 |
+
)
|
75 |
+
gc.collect()
|
76 |
+
|
77 |
+
return epoch_loss
|
78 |
+
|
79 |
+
@torch.inference_mode()
|
80 |
+
def _valid_one_epoch(self, dataloader, optimizer, device, epoch):
|
81 |
+
self.eval()
|
82 |
+
|
83 |
+
dataset_size = 0
|
84 |
+
running_loss = 0.0
|
85 |
+
|
86 |
+
bar = tqdm(enumerate(dataloader), total=len(dataloader))
|
87 |
+
for step, data in bar:
|
88 |
+
images = data["image"].to(device, dtype=torch.float)
|
89 |
+
labels = data["label"].to(device, dtype=torch.long)
|
90 |
+
|
91 |
+
batch_size = images.size(0)
|
92 |
+
|
93 |
+
outputs, _ = self(images, labels)
|
94 |
+
loss = self._criterion(outputs, labels)
|
95 |
+
|
96 |
+
running_loss += loss.item() * batch_size
|
97 |
+
dataset_size += batch_size
|
98 |
+
|
99 |
+
epoch_loss = running_loss / dataset_size
|
100 |
+
|
101 |
+
bar.set_postfix(
|
102 |
+
Epoch=epoch, Valid_Loss=epoch_loss, LR=optimizer.param_groups[0]["lr"]
|
103 |
+
)
|
104 |
+
|
105 |
+
gc.collect()
|
106 |
+
|
107 |
+
return epoch_loss
|
108 |
+
|
109 |
+
def run_training(
|
110 |
+
self,
|
111 |
+
train_loader,
|
112 |
+
valid_loader,
|
113 |
+
optimizer,
|
114 |
+
scheduler,
|
115 |
+
device,
|
116 |
+
num_epochs,
|
117 |
+
weights_dir,
|
118 |
+
):
|
119 |
+
# To automatically log gradients
|
120 |
+
|
121 |
+
if torch.cuda.is_available():
|
122 |
+
print("[INFO] Using GPU: {}\n".format(torch.cuda.get_device_name()))
|
123 |
+
|
124 |
+
start = time.time()
|
125 |
+
best_model_wts = copy.deepcopy(self.state_dict())
|
126 |
+
best_epoch_loss = np.inf
|
127 |
+
history = defaultdict(list)
|
128 |
+
|
129 |
+
for epoch in range(1, num_epochs + 1):
|
130 |
+
gc.collect()
|
131 |
+
|
132 |
+
# dataloader, optimizer, scheduler, criterion, device, epoch
|
133 |
+
train_epoch_loss = self._train_one_epoch(
|
134 |
+
optimizer=optimizer,
|
135 |
+
scheduler=scheduler,
|
136 |
+
dataloader=train_loader,
|
137 |
+
device=device,
|
138 |
+
epoch=epoch,
|
139 |
+
)
|
140 |
+
val_epoch_loss = self._valid_one_epoch(
|
141 |
+
valid_loader, optimizer, device=device, epoch=epoch
|
142 |
+
)
|
143 |
+
|
144 |
+
history["Train Loss"].append(train_epoch_loss)
|
145 |
+
history["Valid Loss"].append(val_epoch_loss)
|
146 |
+
|
147 |
+
# deep copy the model
|
148 |
+
if val_epoch_loss <= best_epoch_loss:
|
149 |
+
print(
|
150 |
+
f"{b_}Validation Loss Improved ({best_epoch_loss} ---> {val_epoch_loss})"
|
151 |
+
)
|
152 |
+
best_epoch_loss = val_epoch_loss
|
153 |
+
best_model_wts = copy.deepcopy(self.state_dict())
|
154 |
+
PATH = "Loss{:.4f}_epoch{:.0f}.bin".format(best_epoch_loss, epoch)
|
155 |
+
torch.save(self.state_dict(), weights_dir / PATH)
|
156 |
+
# Save a model file from the current directory
|
157 |
+
print(f"Model Saved{sr_}")
|
158 |
+
|
159 |
+
print()
|
160 |
+
|
161 |
+
end = time.time()
|
162 |
+
time_elapsed = end - start
|
163 |
+
print(
|
164 |
+
"Training complete in {:.0f}h {:.0f}m {:.0f}s".format(
|
165 |
+
time_elapsed // 3600,
|
166 |
+
(time_elapsed % 3600) // 60,
|
167 |
+
(time_elapsed % 3600) % 60,
|
168 |
+
)
|
169 |
+
)
|
170 |
+
print("Best Loss: {:.4f}".format(best_epoch_loss))
|
171 |
+
|
172 |
+
# load best model weights
|
173 |
+
self.load_state_dict(best_model_wts)
|
174 |
+
|
175 |
+
return history
|
176 |
+
|
177 |
+
def generate_embeddings(self, image, data_transforms=None):
|
178 |
+
if data_transforms is None:
|
179 |
+
data_transforms = transforms.Compose(
|
180 |
+
[
|
181 |
+
transforms.ToTensor(),
|
182 |
+
transforms.Resize((224, 224)),
|
183 |
+
transforms.Normalize(
|
184 |
+
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]
|
185 |
+
),
|
186 |
+
]
|
187 |
+
)
|
188 |
+
|
189 |
+
self.eval()
|
190 |
+
|
191 |
+
image = image.convert("RGB")
|
192 |
+
image = data_transforms(image)
|
193 |
+
image = image.unsqueeze(0) # Add batch dimension
|
194 |
+
image = image.to(DEVICE)
|
195 |
+
|
196 |
+
# Generate embedding
|
197 |
+
with torch.no_grad():
|
198 |
+
embedding = self(image)
|
199 |
+
|
200 |
+
return embedding.squeeze().cpu().numpy().tolist()
|
image_search_engine/models/efficientnet_ns.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import timm
|
2 |
+
import torch
|
3 |
+
import torch.nn as nn
|
4 |
+
|
5 |
+
from image_search_engine.models.arc_margin_product import ArcMarginProduct
|
6 |
+
from image_search_engine.models.base import BaseModel
|
7 |
+
from image_search_engine.models.gem_pooling import GeM
|
8 |
+
from image_search_engine.utils import PACKAGE_DIR
|
9 |
+
|
10 |
+
CLASSES = 8
|
11 |
+
SCALE = 10
|
12 |
+
MARGIN = 0.1
|
13 |
+
EMBEDING_SIZE = 512
|
14 |
+
|
15 |
+
WEIGHTS_DIR = PACKAGE_DIR / "artifacts/weights"
|
16 |
+
DEVICE = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
17 |
+
|
18 |
+
|
19 |
+
class EfficientNet_b0_ns(BaseModel):
|
20 |
+
def __init__(self, pretrained=True, load_weights=False):
|
21 |
+
super(EfficientNet_b0_ns, self).__init__(predtrained=pretrained)
|
22 |
+
self.model = timm.create_model("tf_efficientnet_b0_ns", pretrained=pretrained)
|
23 |
+
in_features = self.model.classifier.in_features
|
24 |
+
self.model.classifier = nn.Identity()
|
25 |
+
self.model.global_pool = nn.Identity()
|
26 |
+
self.pooling = GeM()
|
27 |
+
self.drop = nn.Dropout(p=0.2, inplace=False)
|
28 |
+
self.fc = nn.Linear(in_features, EMBEDING_SIZE)
|
29 |
+
self.arc = ArcMarginProduct(
|
30 |
+
EMBEDING_SIZE,
|
31 |
+
CLASSES,
|
32 |
+
)
|
33 |
+
if load_weights:
|
34 |
+
self.load_state_dict(
|
35 |
+
torch.load(WEIGHTS_DIR / "Loss0.6555_epoch3.bin", map_location=DEVICE)
|
36 |
+
)
|
37 |
+
|
38 |
+
def forward(self, images, labels=None):
|
39 |
+
features = self.model(images)
|
40 |
+
pooled_features = self.pooling(features).flatten(1)
|
41 |
+
pooled_drop = self.drop(pooled_features)
|
42 |
+
emb = self.fc(pooled_drop)
|
43 |
+
|
44 |
+
if labels is not None:
|
45 |
+
output = self.arc(emb, labels)
|
46 |
+
return output, emb
|
47 |
+
else:
|
48 |
+
return emb
|
image_search_engine/models/gem_pooling.py
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import torch
|
2 |
+
import torch.nn as nn
|
3 |
+
import torch.nn.functional as F
|
4 |
+
|
5 |
+
|
6 |
+
class GeM(nn.Module):
|
7 |
+
def __init__(self, p=3, eps=1e-6):
|
8 |
+
super(GeM, self).__init__()
|
9 |
+
self.p = nn.Parameter(torch.ones(1) * p)
|
10 |
+
self.eps = eps
|
11 |
+
|
12 |
+
def forward(self, x):
|
13 |
+
return self.gem(x, p=self.p, eps=self.eps)
|
14 |
+
|
15 |
+
def gem(self, x, p=3, eps=1e-6):
|
16 |
+
return F.avg_pool2d(x.clamp(min=eps).pow(p), (x.size(-2), x.size(-1))).pow(
|
17 |
+
1.0 / p
|
18 |
+
)
|
19 |
+
|
20 |
+
def __repr__(self):
|
21 |
+
return (
|
22 |
+
self.__class__.__name__
|
23 |
+
+ "("
|
24 |
+
+ "p="
|
25 |
+
+ "{:.4f}".format(self.p.data.tolist()[0])
|
26 |
+
+ ", "
|
27 |
+
+ "eps="
|
28 |
+
+ str(self.eps)
|
29 |
+
+ ")"
|
30 |
+
)
|
image_search_engine/product_image_search.py
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
import torch
|
3 |
+
import os
|
4 |
+
|
5 |
+
from image_search_engine import utils
|
6 |
+
from image_search_engine.models import EfficientNet_b0_ns
|
7 |
+
from typing import Union
|
8 |
+
from pathlib import Path
|
9 |
+
from PIL import Image
|
10 |
+
from dotenv import load_dotenv
|
11 |
+
import pinecone
|
12 |
+
|
13 |
+
MODEL_FILE = "model.pt"
|
14 |
+
INDEX_FILE = "index.pkl"
|
15 |
+
|
16 |
+
PROJECT_DIR = utils.PACKAGE_DIR.parent
|
17 |
+
INDEX_NAME = "jumia-product-embeddings"
|
18 |
+
|
19 |
+
PINECONE_API_KEY = os.environ.get("PINECONE_API_KEY")
|
20 |
+
PINECONE_ENV = os.environ.get("PINECONE_ENV")
|
21 |
+
|
22 |
+
|
23 |
+
def load_pinecone_existing_index():
|
24 |
+
pinecone.init(api_key=PINECONE_API_KEY, environment=PINECONE_ENV)
|
25 |
+
index = pinecone.Index(INDEX_NAME)
|
26 |
+
return index
|
27 |
+
|
28 |
+
|
29 |
+
index = load_pinecone_existing_index()
|
30 |
+
|
31 |
+
|
32 |
+
class JumiaProductSearch:
|
33 |
+
def __init__(self, model_path=None):
|
34 |
+
if model_path is None:
|
35 |
+
model_path = utils.STAGED_MODEL_DIR / MODEL_FILE
|
36 |
+
self.model = EfficientNet_b0_ns()
|
37 |
+
self.model.load_state_dict(torch.load(model_path))
|
38 |
+
self.index = utils.load_serialized_object(utils.STAGED_MODEL_DIR / INDEX_FILE)
|
39 |
+
|
40 |
+
def _encode(self, image: Union[str, Path, Image.Image]):
|
41 |
+
image_pil = image
|
42 |
+
if not isinstance(image, Image.Image):
|
43 |
+
image_pil = utils.read_image_pil(image)
|
44 |
+
|
45 |
+
query_embedding = self.model.generate_embeddings(image_pil)
|
46 |
+
|
47 |
+
return query_embedding
|
48 |
+
|
49 |
+
def search(self, image, k):
|
50 |
+
xq = self._encode(image)
|
51 |
+
result = index.query(xq, top_k=k, include_metadata=True)
|
52 |
+
return result
|
53 |
+
|
54 |
+
def search_nn(self, image):
|
55 |
+
query_embedding = self.encode(image)
|
56 |
+
distances, idxs = self.index.kneighbors(query_embedding, return_distance=True)
|
57 |
+
return idxs
|
58 |
+
|
59 |
+
|
60 |
+
if __name__ == "__main__":
|
61 |
+
search = JumiaProductSearch()
|
62 |
+
test_img = utils.PACKAGE_DIR / "tests/test_img/1.jpg"
|
63 |
+
idx = search.search(test_img)
|
64 |
+
print(idx)
|
image_search_engine/tests/__init__.py
ADDED
File without changes
|
image_search_engine/tests/test_img/1.jpg
ADDED
![]() |
image_search_engine/tests/test_img/2.jpg
ADDED
![]() |
image_search_engine/tests/test_img/3.jpg
ADDED
![]() |
image_search_engine/tests/test_img/4.jpg
ADDED
![]() |
image_search_engine/tests/test_img/5.jpg
ADDED
![]() |
image_search_engine/tests/test_img/6.jpg
ADDED
![]() |
image_search_engine/tests/test_img/7.jpg
ADDED
![]() |
image_search_engine/tests/test_img/8.jpg
ADDED
![]() |
image_search_engine/tests/test_img/Shark2.jpg
ADDED
![]() |
image_search_engine/tests/test_img/smart_band.jpg
ADDED
![]() |
image_search_engine/tests/test_img/smart_band_2.jpg
ADDED
![]() |
image_search_engine/tests/test_img/sony.png
ADDED
![]() |
image_search_engine/utils.py
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
from pathlib import Path
|
3 |
+
import smart_open
|
4 |
+
import joblib
|
5 |
+
from typing import Union
|
6 |
+
from PIL import Image
|
7 |
+
|
8 |
+
PACKAGE_DIR = Path(__file__).parent
|
9 |
+
STAGED_MODEL_DIR = PACKAGE_DIR / "artifacts/model_staged"
|
10 |
+
|
11 |
+
|
12 |
+
def read_image_pil(image_uri: Union[Path, str]) -> Image:
|
13 |
+
with smart_open.open(image_uri, "rb") as image_file:
|
14 |
+
return read_image_pil_file(image_file)
|
15 |
+
|
16 |
+
|
17 |
+
def read_image_pil_file(image_file) -> Image:
|
18 |
+
with Image.open(image_file) as image:
|
19 |
+
image = image.convert(mode=image.mode)
|
20 |
+
return image
|
21 |
+
|
22 |
+
|
23 |
+
def load_serialized_object(file_path):
|
24 |
+
try:
|
25 |
+
obj = joblib.load(file_path)
|
26 |
+
return obj
|
27 |
+
except FileNotFoundError:
|
28 |
+
print(f"File not found: {file_path}")
|
29 |
+
except Exception as e:
|
30 |
+
print(f"Error loading serialized object: {str(e)}")
|
31 |
+
|
32 |
+
|
33 |
+
def load_config(
|
34 |
+
file_path=PACKAGE_DIR / "artifacts/config.json",
|
35 |
+
):
|
36 |
+
with open(file_path) as file:
|
37 |
+
data = json.load(file)
|
38 |
+
return data
|
39 |
+
|
40 |
+
|
41 |
+
def load_json_file(file_path):
|
42 |
+
with open(file_path, "r") as file:
|
43 |
+
data = json.load(file)
|
44 |
+
return data
|
45 |
+
|
46 |
+
|
47 |
+
if __name__ == "__main__":
|
48 |
+
load_config()
|