namespace SemanticSearch.Models
{
using System;
using System.Collections.Generic;
using System.Linq;
/// <summary>
/// Word piece tokenizer based on bert-base-uncased model in transformers.
/// See tools/tokenizer.py for examples in python.
/// </summary>
public class BertTokenizer
{
private readonly List<string> vocabulary;
public BertTokenizer(List<string> vocabulary)
{
this.vocabulary = vocabulary;
}
/// <summary>
/// Tokenize a set of strings.
/// </summary>
/// <param name="texts">List of strings.</param>
/// <returns>List of tokens.</returns>
public List<(string Token, int VocabularyIndex)> Tokenize(IEnumerable<string> texts)
{
// [CLS] Words of sentence [SEP] Words of next sentence [SEP]
IEnumerable<string> tokens = new[]
{
DefaultTokens.Classification
};
foreach (var text in texts)
{
tokens = tokens.Concat(this.TokenizeSentence(text));
tokens = tokens.Concat(new[] { DefaultTokens.Separation });
}
return tokens
.SelectMany(this.TokenizeSubwords)
.ToList();
}
/**
* Some words in the vocabulary are too big and will be broken up in to subwords
* Example "Embeddings"
* [‘em’, ‘##bed’, ‘##ding’, ‘##s’]
* https://mccormickml.com/2019/05/14/BERT-word-embeddings-tutorial/
* https://developpaper.com/bert-visual-learning-of-the-strongest-nlp-model/
* https://medium.com/@_init_/why-bert-has-3-embedding-layers-and-their-implementation-details-9c261108e28a
*/
private IEnumerable<(string Token, int VocabularyIndex)>
TokenizeSubwords(string word)
{
if (this.vocabulary.Contains(word))
{
return new (string, int)[]
{
(word, this.vocabulary.IndexOf(word))
};
}
var tokens = new List<(string, int)>();
var remaining = word;
while (!string.IsNullOrEmpty(remaining) && remaining.Length > 2)
{
var prefix = this.vocabulary.Where(remaining.StartsWith)
.OrderByDescending(o => o.Length)
.FirstOrDefault();
if (prefix == null)
{
tokens.Add((DefaultTokens.Unknown,
this.vocabulary.IndexOf(DefaultTokens.Unknown)));
return tokens;
}
var replaced = remaining.Replace(prefix, "##");
if (replaced.Length == remaining.Length)
{
break;
}
remaining = replaced;
tokens.Add((prefix, this.vocabulary.IndexOf(prefix)));
}
if (!string.IsNullOrWhiteSpace(word) && !tokens.Any())
{
tokens.Add((DefaultTokens.Unknown,
this.vocabulary.IndexOf(DefaultTokens.Unknown)));
}
return tokens;
}
private IEnumerable<string> TokenizeSentence(string text)
{
// remove spaces and split the , . : ; etc..
return text.Split(
new[] { " ", " ", "\r\n" },
StringSplitOptions.None)
.SelectMany(o =>
o.SplitAndKeep(".,;:\\/?!#$%()=+-*\"'–_`<>&^@{}[]|~'"
.ToArray()))
.Select(o => o.ToLower());
}
public class DefaultTokens
{
public const string Padding = "";
public const string Unknown = "[UNK]";
public const string Classification = "[CLS]";
public const string Separation = "[SEP]";
public const string Mask = "[MASK]";
}
}
}
PUT my_index_demo_vector
{
"mappings": {
"properties": {
"title": {
"type": "text"
},
"embedding": {
"type": "dense_vector",
"dims": 16
}
}
}
}
POST my_index_demo_vector/_doc/1
{
"title": "Polisa ubezpieczeniowa potężnego Franka",
"embedding": [0.123, 0.456, 0.789, 0.234, 0.567, 0.890,
0.345, 0.678, 0.901, 0.123, 0.456, 0.789, 0.234, 0.567, 0.890, 0.345]
}
POST my_index_demo_vector/_search
{
"query": {
"script_score": {
"query": {
"match": {
"title": "polisa ubezpieczeniowa"
}
},
"script": {
"source": "cosineSimilarity(params.queryVector, 'embedding') + 1.0",
"params": {
"queryVector": [0.123, 0.456, 0.789, 0.234, 0.567, 0.890, 0.345,
0.678, 0.901, 0.123, 0.456, 0.789, 0.234, 0.567, 0.890, 0.345]
}
}
}
}
}
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 2,
"hits": [
{
"_index": "my_index_demo_vector",
"_id": "1",
"_score": 2,
"_source": {
"title": "Polisa ubezpieczeniowa potężnego Franka",
"embedding": [
0.123,
0.456,
0.789,....
]
}
}
]
}
}
from elasticsearch import Elasticsearch
es = Elasticsearch(
"http://localhost:9201"
)
es.ping()
import pandas as pd
df = pd.read_csv("products_catalog.csv").loc[:499]
df.head()
df.fillna("None", inplace=True)
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-mpnet-base-v2')
df["DescriptionVector"] = df["Description"].apply(lambda x: model.encode(x))
from indexMapping import indexMapping
es.indices.create(index="all_products_demo", mappings=indexMapping)
indexMapping = {
"properties":{
"ProductID":{
"type":"long"
},
"ProductName":{
"type":"text"
},
"ProductBrand":{
"type":"text"
},
"Gender":{
"type":"text"
},
"Price (INR)":{
"type":"long"
},
"NumImages":{
"type":"long"
},
"Description":{
"type":"text"
},
"PrimaryColor":{
"type":"text"
},
"DescriptionVector":{
"type":"dense_vector",
"dims": 768,
"index":True,
"similarity": "l2_norm"
}
}
}
record_list = df.to_dict("records")
for record in record_list:
try:
es.index(index="all_products_demo", document=record,
id=record["ProductID"])
except Exception as e:
print(e)
es.count(index="all_products")
input_keyword = "Blue Shoes"
vector_of_input_keyword = model.encode(input_keyword)
query = {
"field" : "DescriptionVector",
"query_vector" : vector_of_input_keyword,
"k" : 2,
"num_candidates" : 500,
}
#res = es.knn_search(index="all_products_demo", knn=query , source=["ProductName","Description"])
#ElasticsearchWarning: The kNN search API has been replaced by the `knn` option in the search API.
res2 = es.search(index="all_products_demo",knn=query, source=["ProductName","Description"])
res2["hits"]["hits"]
True 32
Name: count, dtype: int64
ObjectApiResponse({'acknowledged': True, 'shards_acknowledged': True, 'index': 'all_products_demo'})
ObjectApiResponse({'count': 468, '_shards': {'total': 1, 'successful': 1, 'skipped': 0, 'failed': 0}})
[{'_index': 'all_products_demo',
'_id': '10018013',
'_score': 0.61429423,
'_source': {'ProductName': 'Puma Men Blue Sneakers',
'Description': 'A pair of round-toe blue sneakers, has regular styling, lace-up detailTextile upperCushioned footbedTextured and patterned outsoleWarranty: 3 monthsWarranty provided by brand/manufacturer'}},
{'_index': 'all_products_demo',
'_id': '10018075',
'_score': 0.61429423,
'_source': {'ProductName': 'Puma Men Blue Sneakers',
'Description': 'A pair of round-toe blue sneakers, has regular styling, lace-up detailTextile upperCushioned footbedTextured and patterned outsoleWarranty: 3 monthsWarranty provided by brand/manufacturer'}}]
import streamlit as st
from elasticsearch import Elasticsearch
from sentence_transformers import SentenceTransformer
indexName = "all_products_demo"
try:
es = Elasticsearch(
"http://localhost:9201"
)
except ConnectionError as e:
print("Connection Error:", e)
if es.ping():
print("Succesfully connected to ElasticSearch!!")
else:
print("Oops!! Can not connect to Elasticsearch!")
def search(input_keyword):
model = SentenceTransformer('all-mpnet-base-v2')
vector_of_input_keyword = model.encode(input_keyword)
query = {
"field": "DescriptionVector",
"query_vector": vector_of_input_keyword,
"k": 10,
"num_candidates": 500
}
res = es.knn_search(index="all_products_demo"
, knn=query
, source=["ProductName","Description"]
)
results = res["hits"]["hits"]
return results
def main():
st.title("Search Fashion Products")
# Input: User enters search query
search_query = st.text_input("Enter your search query")
# Button: User triggers the search
if st.button("Search"):
if search_query:
# Perform the search and get results
results = search(search_query)
# Display search results
st.subheader("Search Results")
for result in results:
with st.container():
if '_source' in result:
try:
st.header(f"{result['_source']['ProductName']}")
except Exception as e:
print(e)
try:
st.write(f"Description: {result['_source']['Description']}")
except Exception as e:
print(e)
st.divider()
if __name__ == "__main__":
main()
from openai import OpenAI
client = OpenAI(api_key="sk-I3DkQxDvx000jEei3Bcfvsdsdsfdfcdsfasd3IoDOC")
def get_embedding(text, model="text-embedding-ada-002"):
text = text.replace("\n", " ")
return client.embeddings.create(input = [text], model=model).data[0].embedding
input_keyword = "brown Shoes for men under 1500"
vector_of_input_keyword = get_embedding(input_keyword)
my_prompt = f"""I have data in elastic search of all clothing products with their description,
color, price and the gender they belongs to.
genders are {gender_list}
colors are {color_list}
price can be anything from 0 to 100k
based on user's search query. give me json output as follows
{{
"color": "it should be what users want. give Not-Mentioned if user did not explicitly
mentioned the color in query. If the color mentioned by user is not present in above color list, give Not-Found",
"gender": "gender should be from above list only. if not specified give Not-Mentioned."
"max_price":
"min_price":
}}
users query : {input_keyword}
"""
response = client.chat.completions.create(
model="gpt-3.5-turbo-1106",
response_format={ "type": "json_object" },
messages=[
{"role": "system",
"content":
"You are a helpful assistant designed to output only in JSON format.No other text or explaination."},
{"role": "user", "content": my_prompt}
]
)
response.choices[0].message.content
filter_map = json.loads(response.choices[0].message.content)
# {'color': 'Brown', 'gender': 'Men', 'max_price': 1500, 'min_price': 0}
q1 = {
"knn": {
"field": "NameDescriptionVector",
"query_vector": vector_of_input_keyword,
"k": 10,
"num_candidates": 10000
},
"_source": ["ProductName","Description","PrimaryColor","Price (INR)","ProductBrand","Gender"]
}
filter_query = {
"bool": {
"must": [
{
"match": {
"PrimaryColor": {
"query": filter_map["color"],
"fuzzy_transpositions": "false",
"fuzziness": 0
}
}
},
{
"range": {
"Price (INR)": {
"gte": filter_map["min_price"],
"lte": filter_map["max_price"]
}
}
}
]
}
}
res = es.knn_search(index="my_products_demo2",
body=q1,
request_timeout=5000,
filter=filter_query)
res["hits"]["hits"]