From 3504ab9f2b079ef8c9717a864ecf92f941850e1d Mon Sep 17 00:00:00 2001 From: atita arora Date: Thu, 23 Mar 2023 14:49:38 +0100 Subject: [PATCH] query time changes with , improved querqy rewriter version , thanks to matthias #151 --- data-encoder/ecommerce/vectors/products.py | 6 +++--- .../ecommerce/vectors/query_vector.py | 15 ++++++++++++++- embeddings/Dockerfile | 2 +- embeddings/app/clip/loadModel.py | 6 ------ embeddings/app/clip/model.py | 9 --------- embeddings/app/{clip => clipL14}/__init__.py | 0 embeddings/app/clipL14/loadModel.py | 10 ++++++++++ embeddings/app/clipL14/model.py | 11 +++++++++++ embeddings/app/{clip => clipL14}/router.py | 2 +- embeddings/app/main.py | 2 +- embeddings/requirements.txt | 1 + ...rqy-embeddings-rewriter-1.0.0-SNAPSHOT.jar | Bin 37878 -> 38622 bytes 12 files changed, 42 insertions(+), 22 deletions(-) delete mode 100644 embeddings/app/clip/loadModel.py delete mode 100644 embeddings/app/clip/model.py rename embeddings/app/{clip => clipL14}/__init__.py (100%) create mode 100644 embeddings/app/clipL14/loadModel.py create mode 100644 embeddings/app/clipL14/model.py rename embeddings/app/{clip => clipL14}/router.py (88%) diff --git a/data-encoder/ecommerce/vectors/products.py b/data-encoder/ecommerce/vectors/products.py index f07c699..9df7afe 100644 --- a/data-encoder/ecommerce/vectors/products.py +++ b/data-encoder/ecommerce/vectors/products.py @@ -13,9 +13,9 @@ # Currently you need to unzip the 4.json.zip file first. -PATH_PRODUCTS_DATASET = "data-encoder/ecommerce/vectors/data/1.json" +PATH_PRODUCTS_DATASET = "data-encoder/ecommerce/vectors/data/test.json" PATH_PRODUCTS_MODEL = "all-MiniLM-L6-v2" -PATH_PRODUCTS_VECTORS_JSON = "data-encoder/ecommerce/vectors/data/products-vectors-1.json" +PATH_PRODUCTS_VECTORS_JSON = "data-encoder/ecommerce/vectors/data/products-vectors-test.json" # Load the CLIP model device = "cuda" if torch.cuda.is_available() else "cpu" @@ -71,7 +71,7 @@ def calculate_product_image_vectors(product): preprocess_image = preprocess(validated_image).unsqueeze(0).to(device) # Encode the image with torch.no_grad(): - image_encoding = model.encode_image(preprocess_image) + image_encoding = model.encode_image(preprocess_image)[0] #print(image_encoding) return image_encoding except Exception: diff --git a/data-encoder/ecommerce/vectors/query_vector.py b/data-encoder/ecommerce/vectors/query_vector.py index 56f4129..b68e317 100644 --- a/data-encoder/ecommerce/vectors/query_vector.py +++ b/data-encoder/ecommerce/vectors/query_vector.py @@ -5,4 +5,17 @@ query_text = "Sony Portable Bluetooth Speaker MBS-100 docking speaker 1.0 channels Black Sony" model = SentenceTransformer(PATH_PRODUCTS_MODEL) text_emb = model.encode(query_text) -print(text_emb) \ No newline at end of file +print(text_emb) + + +#import torch +#import torchvision.transforms as transforms +#import clip + +# Load the CLIP model +#device = "cuda" if torch.cuda.is_available() else "cpu" +#model, preprocess = clip.load('ViT-L/14', device) +#qry_text = "mobilephone" +#qry_encoding = model.encode_text(clip.tokenize(qry_text),normalize_embeddings=True, convert_to_numpy=True) +#print(qry_encoding) +#print (qry_encoding.shape) \ No newline at end of file diff --git a/embeddings/Dockerfile b/embeddings/Dockerfile index 13d5b47..f56783c 100644 --- a/embeddings/Dockerfile +++ b/embeddings/Dockerfile @@ -7,7 +7,7 @@ COPY ./app /code/app RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt # Download models from the internet and store in Docker image -RUN python code/app/clip/loadModel.py +#RUN python code/app/clip/loadModel.py RUN python code/app/minilm/loadModel.py WORKDIR /code/app diff --git a/embeddings/app/clip/loadModel.py b/embeddings/app/clip/loadModel.py deleted file mode 100644 index 8c723bf..0000000 --- a/embeddings/app/clip/loadModel.py +++ /dev/null @@ -1,6 +0,0 @@ -## This script downloads the clip model for embeddings service. - -from sentence_transformers import SentenceTransformer - -model = SentenceTransformer('clip-ViT-L-14') -model.save('/code/app/clip-ViT-L-14.model') diff --git a/embeddings/app/clip/model.py b/embeddings/app/clip/model.py deleted file mode 100644 index bdc0143..0000000 --- a/embeddings/app/clip/model.py +++ /dev/null @@ -1,9 +0,0 @@ -from sentence_transformers import SentenceTransformer - -model = SentenceTransformer('clip-ViT-L-14.model') - - -def get_text_sentence_embedding(text: str, normalize: bool = True): - return model.encode(text, normalize_embeddings=normalize, convert_to_numpy=True) - - diff --git a/embeddings/app/clip/__init__.py b/embeddings/app/clipL14/__init__.py similarity index 100% rename from embeddings/app/clip/__init__.py rename to embeddings/app/clipL14/__init__.py diff --git a/embeddings/app/clipL14/loadModel.py b/embeddings/app/clipL14/loadModel.py new file mode 100644 index 0000000..9a78e51 --- /dev/null +++ b/embeddings/app/clipL14/loadModel.py @@ -0,0 +1,10 @@ +## This script downloads the clip model for embeddings service. + +import torch +import torchvision.transforms as transforms +import clip + +device = "cuda" if torch.cuda.is_available() else "cpu" +model, preprocess = clip.load('ViT-L/14', device) + +#model.save('/code/app/clip-ViT-L-14.model') diff --git a/embeddings/app/clipL14/model.py b/embeddings/app/clipL14/model.py new file mode 100644 index 0000000..f8bcc4f --- /dev/null +++ b/embeddings/app/clipL14/model.py @@ -0,0 +1,11 @@ +import torch +import torchvision.transforms as transforms +import clip + +device = "cuda" if torch.cuda.is_available() else "cpu" +model, preprocess = clip.load('ViT-L/14', device) + +def get_text_sentence_embedding(text: str, normalize: bool = True): + return model.encode_text(clip.tokenize(text))[0] + + diff --git a/embeddings/app/clip/router.py b/embeddings/app/clipL14/router.py similarity index 88% rename from embeddings/app/clip/router.py rename to embeddings/app/clipL14/router.py index 2ae2669..7732240 100644 --- a/embeddings/app/clip/router.py +++ b/embeddings/app/clipL14/router.py @@ -1,6 +1,6 @@ from fastapi import APIRouter -from clip.model import get_text_sentence_embedding +from clipL14.model import get_text_sentence_embedding from embeddings import EmbeddingsTextRequest, OutputFormat diff --git a/embeddings/app/main.py b/embeddings/app/main.py index 22c177a..92dcebe 100644 --- a/embeddings/app/main.py +++ b/embeddings/app/main.py @@ -1,5 +1,5 @@ from fastapi import FastAPI -from clip import router as router_clip +from clipL14 import router as router_clip from minilm import router as router_minilm app = FastAPI() diff --git a/embeddings/requirements.txt b/embeddings/requirements.txt index f5ea080..b873a78 100644 --- a/embeddings/requirements.txt +++ b/embeddings/requirements.txt @@ -3,5 +3,6 @@ fastapi uvicorn[standard] gunicorn pydantic +torch git+https://github.com/openai/CLIP.git diff --git a/solr/lib/querqy-embeddings-rewriter-1.0.0-SNAPSHOT.jar b/solr/lib/querqy-embeddings-rewriter-1.0.0-SNAPSHOT.jar index fd69deb8bed9bd4a6f7816c4039a20e1e9e038f8..5e80ca4ee13474f68487f811972c8b1cf020a899 100644 GIT binary patch delta 9112 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 U48@C;-1EfMutpET3 delta 8536 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 KMKAU@?0*1eQawii