Anwenden des VGG16-Vorschulmodells auf Empfehlungen basierend auf Produktbildern

Heute möchte ich Ihnen über meine Erfahrungen mit der Verwendung eines neuronalen Netzwerks berichten, um ähnliche Produkte für ein Online-Shop-Empfehlungssystem zu finden. Ich werde hauptsächlich über technische Dinge sprechen. Ich habe beschlossen, diesen Artikel über Habré zu schreiben, weil ich zu Beginn dieses Projekts eine geeignete Lösung für Habré gefunden habe, die sich jedoch als veraltet herausstellte und geändert werden musste. Deshalb habe ich beschlossen, das Material für diejenigen zu aktualisieren, die eine ähnliche Lösung benötigen.






Unabhängig davon möchte ich sagen, dass dies meine erste Erfahrung bei der Erstellung eines mehr oder weniger seriösen Projekts im Bereich Data Science ist. Wenn also einer der erfahreneren Kollegen sieht, was noch verbessert werden kann, freue ich mich nur über den Rat .





Ich beginne mit einem kleinen Hintergrund darüber, warum die gewählte Logik des Online-Shops gewählt wurde - nämlich einer Empfehlung, die auf ähnlichen Produkten basiert (und nicht auf Methoden der kollaborativen Filterung zum Beispiel). Tatsache ist, dass dieses Empfehlungssystem für einen Online-Shop entwickelt wurde, der Uhren verkauft, und daher bis zu 90% der Benutzer, die auf die Website kommen, nicht zurückkehren. Im Allgemeinen bestand die Aufgabe darin, die Anzahl der Seitenaufrufe von Benutzern zu erhöhen, die durch Werbung auf die Seiten bestimmter Produkte gelangen. Diese Benutzer haben eine Seite angezeigt und die Website verlassen, wenn das Produkt nicht zu ihnen passt.





Ich muss sagen, dass ich in diesem Projekt nicht die Möglichkeit hatte, mich in das Backend eines Online-Shops zu integrieren - eine klassische Geschichte für kleine und mittlere Online-Shops. Es war notwendig, sich nur auf das System zu verlassen, das ich neben der Seite machen werde. Aus diesem Grund habe ich mich als visuelle Lösung auf der Website selbst entschieden, ein Popup-JS-Widget zu erstellen. Eine Zeile fügt dem HTML-Code js hinzu, versteht den Seitentitel, zu dem der Benutzer gekommen ist, und übergibt ihn an das Backend des Dienstes. Wenn das Backend ein Produkt in seiner Datenbank mit vorinstallierten Produkten findet, sucht es erneut in der vorbereiteten Produktdatenbank nach Empfehlungen und gibt sie an js zurück. Js zeigt sie dann im Widget an. Um die Auswirkungen auf die Ladegeschwindigkeit zu verringern, erstellt js einen Iframe, in dem die gesamte Arbeit mit der Anzeige des Widgets erledigt wird. Unter anderem,Außerdem können Sie das Problem mit dem Schnittpunkt der CSS-Klassen des Widgets und der Site beseitigen.





, Data Science. , . , , - .





.





( , A/B-) - ; , , - .





. .





:





!pip install theano

%matplotlib inline
from keras.models import Sequential
from keras.layers.core import Flatten, Dense, Dropout
from keras.layers.convolutional import Convolution2D, MaxPooling2D, ZeroPadding2D
from keras.optimizers import SGD
import cv2, numpy as np
import os
import h5py
from matplotlib import pyplot as plt

from keras.applications import vgg16
from keras.applications import Xception
from keras.preprocessing.image import load_img,img_to_array
from keras.models import Model
from keras.applications.imagenet_utils import preprocess_input

from PIL import Image
import os
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
import pandas as pd

import theano
theano.config.openmp = True
      
      



( , ):





import re
def sorted_alphanumeric(data):
    convert = lambda text: int(text) if text.isdigit() else text.lower()
    alphanum_key = lambda key: [ convert(c) for c in re.split('([0-9]+)', key) ] 
    return sorted(data, key=alphanum_key)

dirlist = sorted_alphanumeric(os.listdir('images'))

r1 = []
r2 = []
for i,x in enumerate(dirlist):
    if x.endswith(".jpg"):
        r1.append((int(x[:-4]),i))
        r2.append((i,int(x[:-4])))

extid_to_intid_dict = dict(r1)
intid_to_extid_dict = dict(r2)
      
      



:





imgs_path = "images/"
imgs_model_width, imgs_model_height = 224, 224

nb_closest_images = 3 #    (  )
      
      



( ):





vgg_model = vgg16.VGG16(weights='imagenet')
      
      



( 1000 ImageNet - . 4096- , ).





— , .





:





feat_extractor = Model(inputs=vgg_model.input, outputs=vgg_model.get_layer("fc2").output)
      
      



, CNN . , :





feat_extractor.summary()
      
      



( , xml -, , , ; , ):





files = [imgs_path + x for x in os.listdir(imgs_path) if "jpg" in x]

print("number of images:",len(files))
      
      



:





import re

def atof(text):
    try:
        retval = float(text)
    except ValueError:
        retval = text
    return retval

def natural_keys(text):
    '''
    alist.sort(key=natural_keys) sorts in human order
    http://nedbatchelder.com/blog/200712/human_sorting.html
    (See Toothy's implementation in the comments)
    float regex comes from https://stackoverflow.com/a/12643073/190597
    '''
    return [ atof(c) for c in re.split(r'[+-]?([0-9]+(?:[.][0-9]*)?|[.][0-9]+)', text) ]

files.sort(key=natural_keys)
      
      



PIL :





original = load_img(files[1], target_size=(imgs_model_width, imgs_model_height))
plt.imshow(original)
plt.show()
print("image loaded successfully!")
      
      



PIL numpy array:

PIL - width, height, channel

Numpy - height, width, channel





numpy_image = img_to_array(original) #    
      
      



batch format.

expand_dims





, - batchsize, height, width, channels. , 0.





image_batch = np.expand_dims(numpy_image, axis=0) #   - (2-dims)
print('image batch size', image_batch.shape)
      
      



VGG:





processed_image = preprocess_input(image_batch.copy()) #    
      
      



( ):





img_features = feat_extractor.predict(processed_image)
      
      



:





print("features successfully extracted!")
print("number of image features:",img_features.size)
img_features
      
      



, — .





importedImages = []

for f in files:
    filename = f
    original = load_img(filename, target_size=(224, 224))
    numpy_image = img_to_array(original)
    image_batch = np.expand_dims(numpy_image, axis=0)
    
    importedImages.append(image_batch)
    
images = np.vstack(importedImages)

processed_imgs = preprocess_input(images.copy())
      
      



:





imgs_features = feat_extractor.predict(processed_imgs)

print("features successfully extracted!")
imgs_features.shape
      
      



:





cosSimilarities = cosine_similarity(imgs_features)
      
      



pandas dataframe:





columns_name = re.findall(r'[0-9]+', str(files))

cos_similarities_df = pd.DataFrame(cosSimilarities, columns=files, index=files)
cos_similarities_df.head()
      
      



. 6000 SKU. 6000 * 6000. float 0 1 8 , . , 430 ( 130 ). . , - GitHub, . GitHub 100 ( ). , - . :) - - - . :





cos_similarities_df_2.round(2) # cos_similarities_df_2 -     ,   
      
      



, . float. pandas float float16 - .





int:





cos_similarities_df_2.apply(lambda x: x * 100)

cos_similarities_df_2.apply(lambda x: x.astype(np.uint8))
      
      



31 . .





h5:





cos_similarities_df_2.to_hdf('storage/cos_similarities.h5', 'data')
      
      



40 . , -, GitHub, -, :)





, , :





import re

# function to retrieve the most similar products for a given one

def retrieve_most_similar_products(given_img):

    print("-----------------------------------------------------------------------")
    print("original product:")
    original = load_img(given_img, target_size=(imgs_model_width, imgs_model_height))
    original_img = int(re.findall(r'[0-9]+', given_img)[0])
    print((df_items_2.iloc[[original_img]]['name'].iat[0], df_items_2.iloc[[original_img]]['pricer_uah'].iat[0], df_items_2.iloc[[original_img]]['url'].iat[0]))
   
    plt.imshow(original)
    plt.show()

    print("-----------------------------------------------------------------------")
    print("most similar products:")

    closest_imgs = cos_similarities_df[given_img].sort_values(ascending=False)[1:nb_closest_images+1].index
    closest_imgs_scores = cos_similarities_df[given_img].sort_values(ascending=False)[1:nb_closest_images+1]

    for i in range(0,len(closest_imgs)):
        original = load_img(closest_imgs[i], target_size=(imgs_model_width, imgs_model_height))
        item = int(re.findall(r'[0-9]+', closest_imgs[i])[0])
        print(item)
        print((df_items_2.iloc[[item]]['name'].iat[0], df_items_2.iloc[[item]]['pricer_uah'].iat[0], df_items_2.iloc[[item]]['url'].iat[0]))
        plt.imshow(original)
        plt.show()
        print("similarity score : ",closest_imgs_scores[i])

kbr = '' #    
find_rec = int(df_items_2.index[df_items_2['name'] == kbr].tolist()[0]) # df_items_2    ,     
print(find_rec)

retrieve_most_similar_products(files[find_rec])
      
      



:)





.





, - :





, :





import os

if not os.path.exists('storage'):
    os.makedirs('storage')

if not os.path.exists('images'):
    os.makedirs('images')
      
      



, xml - .





, , :





# importing required modules
import urllib.request

image_counter = 0

error_list = []

#        
def image_from_df(row):
    global image_counter
    
    item_id = image_counter
    
    filename = f'images/{item_id}.jpg'
    image_url = f'{row.image}'

    try:
      conn = urllib.request.urlopen(image_url)
       
    except urllib.error.HTTPError as e:

      # Return code error (e.g. 404, 501, ...)
      error_list.append(item_id)

    except urllib.error.URLError as e:

      # Not an HTTP-specific error (e.g. connection refused)
      
      print('URLError: {}'.format(e.reason))


    else:

      # 200
      urllib.request.urlretrieve(image_url, filename)
      image_counter += 1
      
      



xml, :





df_items_2.apply(lambda row: image_from_df(row), axis=1)
      
      



, . . . , xml , . , , , , , .





for i in error_list:

  df_items_2.drop(df_items_2.index[i], inplace = True)
  df_items_2.reset_index(drop=True, inplace = True) 

print(f'   : {error_list}')
print(len(error_list))
      
      



, . , - ! )





, - )





P.S. , VGG - VGG19. , .





P.S.S , : , Senior JavaScript Developer ( js CORS-); , Senior Python Developer Senior Engineer ( Docker CI/CD pipeline); SkillFactory, SkillFactory Accelerator ( , Data Science ); (, A/B- ); (Ein weiterer Mentor, der beim Verständnis der NLP-Probleme und insbesondere der Arbeit der Tycoons beim Erstellen von Chat-Bots geholfen hat (ein weiteres Projekt, an dem ich als Teil des Beschleunigers gearbeitet habe und über das ich möglicherweise etwas später sprechen werde); dies ist Emil Maggeramov (Mentor, der im Allgemeinen meine Fortschritte beim Beschleuniger für die Erstellung dieses Projekts überwachte); dies sind die Klassenkameraden Valery Kuryshev und Georgy Bregman (die regelmäßig einmal pro Woche angerufen werden und die während der Woche gesammelten Erfahrungen teilen).








All Articles