
多模式数据的数据结构
请注意,您当前查看的读数> docarray> 0.30,这引入了docarray 0.21的一些重大变化。如果您希望继续使用较旧的docarray <= 0.21,请确保通过
pip install docarray==0.21安装它。有关更多信息,请参阅其代码库,文档及其热点分支。
DocArray是一个专业精心设计的Python图书馆,用于多模式数据的表示,传输,存储和检索。为开发多模式AI应用程序而定制,其设计保证了与广泛的Python和机器学习生态系统的无缝集成。截至2022年1月,Docarray按Apache License 2.0公开分发,目前享受LF AI&Data Foundation中沙盒项目的状态。
要从CLI安装Docarray,请运行以下命令:
pip install -U docarray注意要使用docarray <= 0.21,请确保您通过
pip install docarray==0.21安装并查看其代码库和文档及其热点分支。
docarray的新手?根据您的用例和背景,有多种了解docarray的方法:
DocArray以固有地适应机器学习的方式来表示您的数据。
这对于各种情况尤其有益:
熟悉pydantic吗?您会很高兴得知Docarray不仅在Pydantic上构建,而且还保持与之完全兼容!此外,我们有一个专门针对您需求的特定部分!
从本质上讲,docarray以反映python dataclasses的方式促进了数据表示形式,而机器学习是一个不可或缺的组件:
from docarray import BaseDoc
from docarray . typing import TorchTensor , ImageUrl
import torch
# Define your data model
class MyDocument ( BaseDoc ):
description : str
image_url : ImageUrl # could also be VideoUrl, AudioUrl, etc.
image_tensor : TorchTensor [ 1704 , 2272 , 3 ] # you can express tensor shapes!
# Stack multiple documents in a Document Vector
from docarray import DocVec
vec = DocVec [ MyDocument ](
[
MyDocument (
description = "A cat" ,
image_url = "https://example.com/cat.jpg" ,
image_tensor = torch . rand ( 1704 , 2272 , 3 ),
),
]
* 10
)
print ( vec . image_tensor . shape ) # (10, 1704, 2272, 3)让我们仔细研究如何用docarray表示数据:
from docarray import BaseDoc
from docarray . typing import TorchTensor , ImageUrl
from typing import Optional
import torch
# Define your data model
class MyDocument ( BaseDoc ):
description : str
image_url : ImageUrl # could also be VideoUrl, AudioUrl, etc.
image_tensor : Optional [
TorchTensor [ 1704 , 2272 , 3 ]
] = None # could also be NdArray or TensorflowTensor
embedding : Optional [ TorchTensor ] = None因此,您不仅可以定义数据的类型,甚至可以指定张量的形状!
# Create a document
doc = MyDocument (
description = "This is a photo of a mountain" ,
image_url = "https://upload.wikimedia.org/wikipedia/commons/2/2f/Alpamayo.jpg" ,
)
# Load image tensor from URL
doc . image_tensor = doc . image_url . load ()
# Compute embedding with any model of your choice
def clip_image_encoder ( image_tensor : TorchTensor ) -> TorchTensor : # dummy function
return torch . rand ( 512 )
doc . embedding = clip_image_encoder ( doc . image_tensor )
print ( doc . embedding . shape ) # torch.Size([512])当然,您可以将文档构成嵌套结构:
from docarray import BaseDoc
from docarray . documents import ImageDoc , TextDoc
import numpy as np
class MultiModalDocument ( BaseDoc ):
image_doc : ImageDoc
text_doc : TextDoc
doc = MultiModalDocument (
image_doc = ImageDoc ( tensor = np . zeros (( 3 , 224 , 224 ))), text_doc = TextDoc ( text = 'hi!' )
)您很少一次使用一个数据点,尤其是在机器学习应用程序中。这就是为什么您可以轻松收集多个Documents原因:
Documents当构建或与ML系统互动时,通常您需要一次处理多个文档(数据点)。
Docarray为此提供了两个数据结构:
DocVec : Documents向量。文档中的所有张量都堆叠到一个张量中。非常适合ML模型内部处理和使用。DocList : Documents列表。文档中的所有张量均按原样保留。非常适合流式传输,重新排列和调整数据。让我们看看它们,从DocVec开始:
from docarray import DocVec , BaseDoc
from docarray . typing import AnyTensor , ImageUrl
import numpy as np
class Image ( BaseDoc ):
url : ImageUrl
tensor : AnyTensor # this allows torch, numpy, and tensor flow tensors
vec = DocVec [ Image ]( # the DocVec is parametrized by your personal schema!
[
Image (
url = "https://upload.wikimedia.org/wikipedia/commons/2/2f/Alpamayo.jpg" ,
tensor = np . zeros (( 3 , 224 , 224 )),
)
for _ in range ( 100 )
]
)在上面的代码段中, DocVec通过您要与之使用的文档类型进行了参数: DocVec[Image] 。
一开始这看起来可能很奇怪,但是我们有信心您会很快习惯它!此外,它使我们能够做一些很酷的事情,例如大量访问您在文档中定义的字段:
tensor = vec . tensor # gets all the tensors in the DocVec
print ( tensor . shape ) # which are stacked up into a single tensor!
print ( vec . url ) # you can bulk access any other field, too第二个数据结构, DocList ,以类似的方式工作:
from docarray import DocList
dl = DocList [ Image ]( # the DocList is parametrized by your personal schema!
[
Image (
url = "https://upload.wikimedia.org/wikipedia/commons/2/2f/Alpamayo.jpg" ,
tensor = np . zeros (( 3 , 224 , 224 )),
)
for _ in range ( 100 )
]
)您仍然可以批量访问文档的字段:
tensors = dl . tensor # gets all the tensors in the DocList
print ( type ( tensors )) # as a list of tensors
print ( dl . url ) # you can bulk access any other field, too您可以将DocList插入,删除和附加文件:
# append
dl . append (
Image (
url = "https://upload.wikimedia.org/wikipedia/commons/2/2f/Alpamayo.jpg" ,
tensor = np . zeros (( 3 , 224 , 224 )),
)
)
# delete
del dl [ 0 ]
# insert
dl . insert (
0 ,
Image (
url = "https://upload.wikimedia.org/wikipedia/commons/2/2f/Alpamayo.jpg" ,
tensor = np . zeros (( 3 , 224 , 224 )),
),
)您可以在DocVec和DocList之间无缝切换:
vec_2 = dl . to_doc_vec ()
assert isinstance ( vec_2 , DocVec )
dl_2 = vec_2 . to_doc_list ()
assert isinstance ( dl_2 , DocList )DocArray以与机器学习固有兼容的方式促进了数据的传输。
这包括对Protobuf和GRPC的天然支持,以及HTTP以及JSON,JSONSCHEMA,BASE64和BYTES的序列化。
此功能证明对几种情况有益:
您熟悉Fastapi吗?您会很高兴得知DocArray与FastApi保持完整的兼容性!另外,我们为您提供专门的部分!
在数据传输方面,序列化是关键步骤。让我们深入研究docarray如何简化此过程:
from docarray import BaseDoc
from docarray . typing import ImageTorchTensor
import torch
# model your data
class MyDocument ( BaseDoc ):
description : str
image : ImageTorchTensor [ 3 , 224 , 224 ]
# create a Document
doc = MyDocument (
description = "This is a description" ,
image = torch . zeros (( 3 , 224 , 224 )),
)
# serialize it!
proto = doc . to_protobuf ()
bytes_ = doc . to_bytes ()
json = doc . json ()
# deserialize it!
doc_2 = MyDocument . from_protobuf ( proto )
doc_4 = MyDocument . from_bytes ( bytes_ )
doc_5 = MyDocument . parse_raw ( json )当然,序列化并不是您需要的全部。因此,请查看Docarray如何与Jina和Fastapi集成。
建模并可能分发数据后,通常需要将其存储在某个地方。那就是docarray介入的地方!
文档商店提供了一种无缝的方式,顾名思义,存储您的文档。无论是本地还是远程,您都可以通过相同的用户界面进行操作:
文档存储的界面可让您将文档推送到多个数据源,都具有相同的用户界面。
例如,让我们看看如何与磁盘存储一起工作:
from docarray import BaseDoc , DocList
class SimpleDoc ( BaseDoc ):
text : str
docs = DocList [ SimpleDoc ]([ SimpleDoc ( text = f'doc { i } ' ) for i in range ( 8 )])
docs . push ( 'file://simple_docs' )
docs_pull = DocList [ SimpleDoc ]. pull ( 'file://simple_docs' )文档索引可让您在矢量数据库中索引文档,以有效地基于相似性的检索。
这对于:
目前,文档索引支持编织, QDRANT , ELASTICSEARCH , REDIS , MONGO ATLAS和HNSWLIB ,还有更多!
文档索引界面可让您从多个向量数据库中索引和检索文档,所有这些都具有相同的用户界面。
它支持ANN矢量搜索,文本搜索,过滤和混合搜索。
from docarray import DocList , BaseDoc
from docarray . index import HnswDocumentIndex
import numpy as np
from docarray . typing import ImageUrl , ImageTensor , NdArray
class ImageDoc ( BaseDoc ):
url : ImageUrl
tensor : ImageTensor
embedding : NdArray [ 128 ]
# create some data
dl = DocList [ ImageDoc ](
[
ImageDoc (
url = "https://upload.wikimedia.org/wikipedia/commons/2/2f/Alpamayo.jpg" ,
tensor = np . zeros (( 3 , 224 , 224 )),
embedding = np . random . random (( 128 ,)),
)
for _ in range ( 100 )
]
)
# create a Document Index
index = HnswDocumentIndex [ ImageDoc ]( work_dir = '/tmp/test_index' )
# index your data
index . index ( dl )
# find similar Documents
query = dl [ 0 ]
results , scores = index . find ( query , limit = 10 , search_field = 'embedding' )根据您的背景和用例,您可以了解docarray的不同方法。
如果您使用的是0.30.0或更低版本版本,则您将熟悉其Dataclass API。
docarray> = 0.30是那个想法,认真对待。每个文档都是通过Pydantic提供的类似数据级的界面来创建的。
这给出了以下优势:
您可能还熟悉我们用于矢量数据库集成的旧文档商店。现在,它们被称为文档索引并提供以下改进(有关新的API,请参见此处):
目前,文档索引支持编织, QDRANT , ELASTICSEARCH , REDIS , MONGO ATLAS ,确切的邻居搜索和HNSWLIB ,还有更多内容。
如果您来自Pydantic,您可以将docarray文档视为Pydantic模型,而Docarray则是周围的糖果集合。
更具体地说,我们着手使Pydantic适合ML世界- 不是通过替换它,而是通过在其上进行构建!
这意味着您会获得以下好处:
.load()一个用于图像张量的URL,纹理可以加载和令牌文本文档等。这里最明显的优势是对以ML为中心数据的一流支持,例如{Torch, TF, ...}Tensor , Embedding等。
这包括方便的功能,例如验证张量的形状:
from docarray import BaseDoc
from docarray . typing import TorchTensor
import torch
class MyDoc ( BaseDoc ):
tensor : TorchTensor [ 3 , 224 , 224 ]
doc = MyDoc ( tensor = torch . zeros ( 3 , 224 , 224 )) # works
doc = MyDoc ( tensor = torch . zeros ( 224 , 224 , 3 )) # works by reshaping
try :
doc = MyDoc ( tensor = torch . zeros ( 224 )) # fails validation
except Exception as e :
print ( e )
# tensor
# Cannot reshape tensor of shape (224,) to shape (3, 224, 224) (type=value_error)
class Image ( BaseDoc ):
tensor : TorchTensor [ 3 , 'x' , 'x' ]
Image ( tensor = torch . zeros ( 3 , 224 , 224 )) # works
try :
Image (
tensor = torch . zeros ( 3 , 64 , 128 )
) # fails validation because second dimension does not match third
except Exception as e :
print ()
try :
Image (
tensor = torch . zeros ( 4 , 224 , 224 )
) # fails validation because of the first dimension
except Exception as e :
print ( e )
# Tensor shape mismatch. Expected(3, 'x', 'x'), got(4, 224, 224)(type=value_error)
try :
Image (
tensor = torch . zeros ( 3 , 64 )
) # fails validation because it does not have enough dimensions
except Exception as e :
print ( e )
# Tensor shape mismatch. Expected (3, 'x', 'x'), got (3, 64) (type=value_error)如果您来自Pytorch,则可以将docArray视为在模型流过的数据时组织数据的一种方式。
它为您提供了几个优势:
docarray可以直接在ML模型中用于处理和表示多层状塔。这使您可以使用docarray的摘要在nn.Module的内部深入了解数据,并提供了一种兼容兼容的模式,以简化模型培训和模型服务之间的过渡。
要查看其效果,让我们首先观察到三模式ML模型的香草pytorch实现:
import torch
from torch import nn
def encoder ( x ):
return torch . rand ( 512 )
class MyMultiModalModel ( nn . Module ):
def __init__ ( self ):
super (). __init__ ()
self . audio_encoder = encoder ()
self . image_encoder = encoder ()
self . text_encoder = encoder ()
def forward ( self , text_1 , text_2 , image_1 , image_2 , audio_1 , audio_2 ):
embedding_text_1 = self . text_encoder ( text_1 )
embedding_text_2 = self . text_encoder ( text_2 )
embedding_image_1 = self . image_encoder ( image_1 )
embedding_image_2 = self . image_encoder ( image_2 )
embedding_audio_1 = self . image_encoder ( audio_1 )
embedding_audio_2 = self . image_encoder ( audio_2 )
return (
embedding_text_1 ,
embedding_text_2 ,
embedding_image_1 ,
embedding_image_2 ,
embedding_audio_1 ,
embedding_audio_2 ,
)如果您问我们,眼睛不是很容易的。更糟糕的是,如果您需要再添加一种方式,则必须触摸代码库的每个部分,更改forward()返回类型,然后对此进行大量更改。
因此,现在让我们看看docarray的相同代码的样子:
from docarray import DocList , BaseDoc
from docarray . documents import ImageDoc , TextDoc , AudioDoc
from docarray . typing import TorchTensor
from torch import nn
import torch
def encoder ( x ):
return torch . rand ( 512 )
class Podcast ( BaseDoc ):
text : TextDoc
image : ImageDoc
audio : AudioDoc
class PairPodcast ( BaseDoc ):
left : Podcast
right : Podcast
class MyPodcastModel ( nn . Module ):
def __init__ ( self ):
super (). __init__ ()
self . audio_encoder = encoder ()
self . image_encoder = encoder ()
self . text_encoder = encoder ()
def forward_podcast ( self , docs : DocList [ Podcast ]) -> DocList [ Podcast ]:
docs . audio . embedding = self . audio_encoder ( docs . audio . tensor )
docs . text . embedding = self . text_encoder ( docs . text . tensor )
docs . image . embedding = self . image_encoder ( docs . image . tensor )
return docs
def forward ( self , docs : DocList [ PairPodcast ]) -> DocList [ PairPodcast ]:
docs . left = self . forward_podcast ( docs . left )
docs . right = self . forward_podcast ( docs . right )
return docs看起来好多了,不是吗?您立即赢得了代码可读性和可维护性。而且,以相同的价格,您可以将Pytorch模型变成FastApi应用程序,并重复使用文档模式定义(请参见下文)。一切都依靠类型提示以pythonic的方式处理。
像Pytorch方法一样,您也可以使用带有TensorFlow的DocArray处理和表示ML模型中的多模式数据。
首先,要将Docarray与TensorFlow一起使用,我们首先需要按照以下方式安装它:
pip install tensorflow==2.12.0
pip install protobuf==3.19.0
与与Pytorch一起使用DocArray相比,将其与TensorFlow一起使用时有一个主要区别:虽然DocArray的TorchTensor是torch.Tensor的子类。Tensor。张TensorFlowTensor指数不是这样:由于tf.Tensor的某些技术限制, tf.Tensor ,DOCARNARAY的TensorFlowTensor tf.Tensor .tensor属性。
这对您有何影响?每当您想访问张量数据的数据时,可以说是使用它进行操作或将其交给您的ML模型,而不是交出TensorFlowTensor实例,则需要访问其.tensor属性。
这看起来如下:
from typing import Optional
from docarray import DocList , BaseDoc
import tensorflow as tf
class Podcast ( BaseDoc ):
audio_tensor : Optional [ AudioTensorFlowTensor ] = None
embedding : Optional [ AudioTensorFlowTensor ] = None
class MyPodcastModel ( tf . keras . Model ):
def __init__ ( self ):
super (). __init__ ()
self . audio_encoder = AudioEncoder ()
def call ( self , inputs : DocList [ Podcast ]) -> DocList [ Podcast ]:
inputs . audio_tensor . embedding = self . audio_encoder (
inputs . audio_tensor . tensor
) # access audio_tensor's .tensor attribute
return inputs文档是Pydantic模型(有扭曲),因此它们与FastApi完全兼容!
但是,您为什么要使用它们,而不是您已经知道和喜欢的Pydantic模型?好问题!
为了达成协议,让我们向您展示如何轻松地将插槽记录到您的FastApi应用程序中:
import numpy as np
from fastapi import FastAPI
from docarray . base_doc import DocArrayResponse
from docarray import BaseDoc
from docarray . documents import ImageDoc
from docarray . typing import NdArray , ImageTensor
class InputDoc ( BaseDoc ):
img : ImageDoc
text : str
class OutputDoc ( BaseDoc ):
embedding_clip : NdArray
embedding_bert : NdArray
app = FastAPI ()
def model_img ( img : ImageTensor ) -> NdArray :
return np . zeros (( 100 , 1 ))
def model_text ( text : str ) -> NdArray :
return np . zeros (( 100 , 1 ))
@ app . post ( "/embed/" , response_model = OutputDoc , response_class = DocArrayResponse )
async def create_item ( doc : InputDoc ) -> OutputDoc :
doc = OutputDoc (
embedding_clip = model_img ( doc . img . tensor ), embedding_bert = model_text ( doc . text )
)
return doc
input_doc = InputDoc ( text = '' , img = ImageDoc ( tensor = np . random . random (( 3 , 224 , 224 ))))
async with AsyncClient ( app = app , base_url = "http://test" ) as ac :
response = await ac . post ( "/embed/" , data = input_doc . json ())就像香草pydantic模型一样!
吉纳(Jina)采用docarray作为代表和序列化文件的图书馆。
Jina允许提供使用Docarray构建的模型和服务,使您可以充分利用Docarray的序列化Capabilites服务和扩展这些应用程序。
import numpy as np
from jina import Deployment , Executor , requests
from docarray import BaseDoc , DocList
from docarray . documents import ImageDoc
from docarray . typing import NdArray , ImageTensor
class InputDoc ( BaseDoc ):
img : ImageDoc
text : str
class OutputDoc ( BaseDoc ):
embedding_clip : NdArray
embedding_bert : NdArray
def model_img ( img : ImageTensor ) -> NdArray :
return np . zeros (( 100 , 1 ))
def model_text ( text : str ) -> NdArray :
return np . zeros (( 100 , 1 ))
class MyEmbeddingExecutor ( Executor ):
@ requests ( on = '/embed' )
def encode ( self , docs : DocList [ InputDoc ], ** kwargs ) -> DocList [ OutputDoc ]:
ret = DocList [ OutputDoc ]()
for doc in docs :
output = OutputDoc (
embedding_clip = model_img ( doc . img . tensor ),
embedding_bert = model_text ( doc . text ),
)
ret . append ( output )
return ret
with Deployment (
protocols = [ 'grpc' , 'http' ], ports = [ 12345 , 12346 ], uses = MyEmbeddingExecutor
) as dep :
resp = dep . post (
on = '/embed' ,
inputs = DocList [ InputDoc ](
[ InputDoc ( text = '' , img = ImageDoc ( tensor = np . random . random (( 3 , 224 , 224 ))))]
),
return_type = DocList [ OutputDoc ],
)
print ( resp )如果您以通用矢量数据库客户端遇到docarray,则可以最好地将其视为矢量数据库的一种新型ORM 。 Docarray的工作是获取多模式,嵌套和域特异性数据,并将其映射到矢量数据库,将其存储在此处,从而使其可搜索:
from docarray import DocList , BaseDoc
from docarray . index import HnswDocumentIndex
import numpy as np
from docarray . typing import ImageUrl , ImageTensor , NdArray
class ImageDoc ( BaseDoc ):
url : ImageUrl
tensor : ImageTensor
embedding : NdArray [ 128 ]
# create some data
dl = DocList [ ImageDoc ](
[
ImageDoc (
url = "https://upload.wikimedia.org/wikipedia/commons/2/2f/Alpamayo.jpg" ,
tensor = np . zeros (( 3 , 224 , 224 )),
embedding = np . random . random (( 128 ,)),
)
for _ in range ( 100 )
]
)
# create a Document Index
index = HnswDocumentIndex [ ImageDoc ]( work_dir = '/tmp/test_index2' )
# index your data
index . index ( dl )
# find similar Documents
query = dl [ 0 ]
results , scores = index . find ( query , limit = 10 , search_field = 'embedding' )目前,DocArray支持以下矢量数据库:
目前正在进行OpenSearch的集成。
当然,这只是docarray可以做的事情之一,因此我们鼓励您查看其余的读数!
使用DocArray,您可以通过Langchain将外部数据连接到LLM。 DocArray为您提供了建立灵活的文档模式的自由,并从不同的后端中选择文档存储。创建文档索引后,您可以使用DocarRayretRiever将其连接到Langchain应用程序。
安装Langchain通过:
pip install langchain from docarray import BaseDoc , DocList
from docarray . typing import NdArray
from langchain . embeddings . openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings ()
# Define a document schema
class MovieDoc ( BaseDoc ):
title : str
description : str
year : int
embedding : NdArray [ 1536 ]
movies = [
{ "title" : "#1 title" , "description" : "#1 description" , "year" : 1999 },
{ "title" : "#2 title" , "description" : "#2 description" , "year" : 2001 },
]
# Embed `description` and create documents
docs = DocList [ MovieDoc ](
MovieDoc ( embedding = embeddings . embed_query ( movie [ "description" ]), ** movie )
for movie in movies
) from docarray . index import (
InMemoryExactNNIndex ,
HnswDocumentIndex ,
WeaviateDocumentIndex ,
QdrantDocumentIndex ,
ElasticDocIndex ,
RedisDocumentIndex ,
MongoDBAtlasDocumentIndex ,
)
# Select a suitable backend and initialize it with data
db = InMemoryExactNNIndex [ MovieDoc ]( docs ) from langchain . chat_models import ChatOpenAI
from langchain . chains import ConversationalRetrievalChain
from langchain . retrievers import DocArrayRetriever
# Create a retriever
retriever = DocArrayRetriever (
index = db ,
embeddings = embeddings ,
search_field = "embedding" ,
content_field = "description" ,
)
# Use the retriever in your chain
model = ChatOpenAI ()
qa = ConversationalRetrievalChain . from_llm ( model , retriever = retriever )另外,您可以使用内置矢量商店。 Langchain支持两家矢量商店:Docarrayinmemorysearch和Docarrayhnswsearch。两者都是用户友好的,最适合中小型数据集。
Docarray是LF AI Projects,LLC的商标