Add local image embedding impls

This commit is contained in:
liyaxing
2023-09-26 20:13:55 +08:00
committed by tsukasa
parent 5f346b1bd9
commit 451ab5e0a8
5 changed files with 182 additions and 13 deletions
+1
View File
@@ -25,6 +25,7 @@ class ComputeTaskType(Enum):
VOICE_2_TEXT = "voice_2_text" VOICE_2_TEXT = "voice_2_text"
TEXT_2_VOICE = "text_2_voice" TEXT_2_VOICE = "text_2_voice"
TEXT_EMBEDDING ="text_embedding" TEXT_EMBEDDING ="text_embedding"
IMAGE_EMBEDDING ="image_embedding"
class ComputeTask: class ComputeTask:
+161 -12
View File
@@ -5,6 +5,7 @@ from pydantic import BaseModel
from .compute_task import ComputeTask, ComputeTaskState, ComputeTaskType from .compute_task import ComputeTask, ComputeTaskState, ComputeTaskType
from .queue_compute_node import Queue_ComputeNode from .queue_compute_node import Queue_ComputeNode
from knowledge import ObjectID
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@@ -13,19 +14,20 @@ This is a custom implementation, it should be redesigned.
""" """
class LocalSentenceTransformer_ComputeNode(Queue_ComputeNode): class LocalSentenceTransformer_Text_ComputeNode(Queue_ComputeNode):
# For valid pretrained models, see https://www.sbert.net/docs/pretrained_models.html
def __init__(self, model_name: str = "all-MiniLM-L6-v2"): def __init__(self, model_name: str = "all-MiniLM-L6-v2"):
super().__init__() super().__init__()
self.node_id = "local_sentence_transformer_node" self.node_id = "local_sentence_transformer_text_embedding_node"
self.model_name = model_name self.model_name = model_name
self.model = None self.model = None
def initial(self) -> bool: def initial(self) -> bool:
logger.info( logger.info(
f"LocalSentenceTransformer_ComputeNode init, model_name: {self.model_name}" f"LocalSentenceTransformer_Text_ComputeNode init, model_name: {self.model_name}"
) )
assert self.model_name is not None assert self.model_name is not None
assert self.model is None assert self.model is None
try: try:
@@ -35,9 +37,9 @@ class LocalSentenceTransformer_ComputeNode(Queue_ComputeNode):
except Exception as err: except Exception as err:
logger.error(f"load model {self.model} failed: {err}") logger.error(f"load model {self.model} failed: {err}")
return False return False
return True return True
async def execute_task( async def execute_task(
self, task: ComputeTask self, task: ComputeTask
) -> { ) -> {
@@ -51,14 +53,14 @@ class LocalSentenceTransformer_ComputeNode(Queue_ComputeNode):
}, },
}: }:
try: try:
# logger.debug(f"LocalSentenceTransformer_ComputeNode task: {task}") # logger.debug(f"LocalSentenceTransformer_Text_ComputeNode task: {task}")
if task.task_type == ComputeTaskType.TEXT_EMBEDDING: if task.task_type == ComputeTaskType.TEXT_EMBEDDING:
input = task.params["input"] input = task.params["input"]
logger.debug( logger.debug(
f"LocalSentenceTransformer_ComputeNode task input: {input}" f"LocalSentenceTransformer_Text_ComputeNode task input: {input}"
) )
sentence_embeddings = self.model.encode(input) sentence_embeddings = self.model.encode(input)
# logger.debug(f"LocalSentenceTransformer_ComputeNode task sentence_embeddings: {sentence_embeddings}") # logger.debug(f"LocalSentenceTransformer_Text_ComputeNode task sentence_embeddings: {sentence_embeddings}")
return { return {
"state": ComputeTaskState.DONE, "state": ComputeTaskState.DONE,
"content": sentence_embeddings, "content": sentence_embeddings,
@@ -80,9 +82,7 @@ class LocalSentenceTransformer_ComputeNode(Queue_ComputeNode):
} }
def display(self) -> str: def display(self) -> str:
return ( return f"LocalSentenceTransformer_Text_ComputeNode: {self.node_id}, {self.model_name}"
f"LocalSentenceTransformer_ComputeNode: {self.node_id}, {self.model_name}"
)
def get_capacity(self): def get_capacity(self):
pass pass
@@ -94,3 +94,152 @@ class LocalSentenceTransformer_ComputeNode(Queue_ComputeNode):
def is_local(self) -> bool: def is_local(self) -> bool:
return True return True
from typing import Union
from PIL import Image
import io
class LocalSentenceTransformer_Image_ComputeNode(Queue_ComputeNode):
# For valid pretrained models, see https://www.sbert.net/docs/pretrained_models.html
def __init__(self, model_name: str = "clip-ViT-B-32", multi_model_name: str = "clip-ViT-B-32-multilingual-v1"):
super().__init__()
self.node_id = "local_sentence_transformer_image_embedding_node"
self.model_name = model_name
self.multi_model_name = multi_model_name
self.model = None
self.multi_model = None
def initial(self) -> bool:
logger.info(
f"LocalSentenceTransformer_Image_ComputeNode init, model_name: {self.model_name} {self.multi_model_name}"
)
assert self.model_name is not None
assert self.multi_model_name is not None
assert self.model is None
assert self.multi_model is None
try:
from sentence_transformers import SentenceTransformer
self.model = SentenceTransformer(self.model_name)
self.multi_model = SentenceTransformer(self.multi_model_name)
except Exception as err:
logger.error(f"load model {self.model} failed: {err}")
return False
return True
def _load_image(self, source: Union[ObjectID, bytes] ) -> Optional[Image]:
image_data = None
if isinstance(source, ObjectID):
from knowledge import KnowledgeStore, ImageObject
buf = KnowledgeStore().get_object_store().get_object(source)
if buf is None:
logger.error(f"load image object but not found! {source}")
return None
try:
image_obj= ImageObject.decode(buf)
except Exception as err:
logger.error(f"decode ImageObject from buffer failed: {source}, {err}")
return None
file_size = image_obj.get_file_size()
print(f"got image object: {source.to_base58()}, size: {file_size}")
image_data = KnowledgeStore().get_chunk_reader().read_chunk_list_to_single_bytes(image_obj.get_chunk_list())
elif isinstance(source, bytes):
image_data = source
else:
logger.error(f"unsupport image source type: {type(source)}, {source}")
return None
try:
img = Image.open(io.BytesIO(image_data))
return img
except Exception as err:
logger.error(f"load image from buffer failed: {source}, {err}")
return None
async def execute_task(
self, task: ComputeTask
) -> {
"task_type": str,
"content": str,
"message": str,
"state": ComputeTaskState,
"error": {
"code": int,
"message": str,
},
}:
try:
# logger.debug(f"LocalSentenceTransformer_Text_ComputeNode task: {task}")
if task.task_type == ComputeTaskType.TEXT_EMBEDDING:
input = task.params["input"]
logger.debug(
f"LocalSentenceTransformer_Text_ComputeNode task text input: {input}"
)
sentence_embeddings = self.multi_model.encode(input)
# logger.debug(f"LocalSentenceTransformer_Text_ComputeNode task sentence_embeddings: {sentence_embeddings}")
return {
"state": ComputeTaskState.DONE,
"content": sentence_embeddings,
"message": None,
}
elif task.task_type == ComputeTaskType.IMAGE_EMBEDDING:
input = task.params["input"]
logger.debug(
f"LocalSentenceTransformer_Image_ComputeNode task image input: {input}"
)
img = self._load_image(input)
if img is None:
return {
"state": ComputeTaskState.ERROR,
"error": {"code": -1, "message": "load image failed"},
}
sentence_embeddings = self.model.encode(img, convert_to_tensor=True)
# logger.debug(f"LocalSentenceTransformer_Text_ComputeNode task sentence_embeddings: {sentence_embeddings}")
return {
"state": ComputeTaskState.DONE,
"content": sentence_embeddings,
"message": None,
}
else:
return {
"state": ComputeTaskState.ERROR,
"error": {"code": -1, "message": "unsupport embedding task type"},
}
except Exception as err:
import traceback
logger.error(f"{traceback.format_exc()}, error: {err}")
return {
"state": ComputeTaskState.ERROR,
"error": {"code": -1, "message": "unknown exception: " + str(err)},
}
def display(self) -> str:
return f"LocalSentenceTransformer_Image_ComputeNode: {self.node_id}, {self.model_name}"
def get_capacity(self):
pass
def is_support(self, task: ComputeTask) -> bool:
return (
task.task_type == ComputeTaskType.TEXT_EMBEDDING
or task.task_type == ComputeTaskType.IMAGE_EMBEDDING
)
def is_local(self) -> bool:
return True
+10 -1
View File
@@ -1,6 +1,8 @@
import os import os
import shutil import shutil
from .object import ObjectID from .object import ObjectID
import logging
logger = logging.getLogger(__name__)
class FileBlobStorage: class FileBlobStorage:
@@ -38,16 +40,23 @@ class FileBlobStorage:
def put(self, object_id: ObjectID, contents: bytes): def put(self, object_id: ObjectID, contents: bytes):
full_path = self.get_full_path(object_id) full_path = self.get_full_path(object_id)
if os.path.exists(full_path):
logger.warning(f"will replace object: {object_id}")
self.write_sync(full_path, contents) self.write_sync(full_path, contents)
def get(self, object_id: ObjectID) -> bytes: def get(self, object_id: ObjectID) -> bytes:
full_path = self.get_full_path(object_id) full_path = self.get_full_path(object_id)
if not os.path.exists(full_path):
return None
with open(full_path, "rb") as f: with open(full_path, "rb") as f:
return f.read() return f.read()
def delete(self, object_id: ObjectID): def delete(self, object_id: ObjectID):
full_path = self.get_full_path(object_id) full_path = self.get_full_path(object_id)
os.remove(full_path) if os.path.exists(full_path):
os.remove(full_path)
def exists(self, object_id: ObjectID) -> bool: def exists(self, object_id: ObjectID) -> bool:
full_path = self.get_full_path(object_id) full_path = self.get_full_path(object_id)
+1
View File
@@ -20,6 +20,7 @@ root.addHandler(handler)
def test_st(): def test_st():
image_model = SentenceTransformer('clip-ViT-B-32-multilingual-v1')
model = SentenceTransformer("all-MiniLM-L6-v2") model = SentenceTransformer("all-MiniLM-L6-v2")
# Our sentences we like to encode # Our sentences we like to encode
+9
View File
@@ -74,6 +74,15 @@ class TestVectorSTorage(unittest.TestCase):
image_data = KnowledgeStore().get_chunk_reader().read_chunk_list_to_single_bytes(image_obj.get_chunk_list()) image_data = KnowledgeStore().get_chunk_reader().read_chunk_list_to_single_bytes(image_obj.get_chunk_list())
self.assertEqual(file_size, len(image_data)) self.assertEqual(file_size, len(image_data))
from PIL import Image
import io
image = Image.open(io.BytesIO(image_data))
image.show()
from sentence_transformers import SentenceTransformer
#model = SentenceTransformer('clip-ViT-B-32-multilingual-v1')
model = SentenceTransformer('clip-ViT-B-32')
model.encode(image, convert_to_tensor=True)
def test_relation(self): def test_relation(self):
obj1 = ObjectID.hash_data("12345".encode("utf-8")) obj1 = ObjectID.hash_data("12345".encode("utf-8"))