
多模式數據的數據結構
請注意,您當前查看的讀數> 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的商標