一文读懂Jina生态的Dataclass

Jina AI一直致力于构建简单、易用、全托管的最佳东西,来协助开发者快速搭建多模态、跨模态运用。而作为工程师,咱们一直在尽力开发新的功用和 API,以满足用户对多模态数据处理的许多场景需求。

Jina 现支撑的 Dataclass 新特性供给了更丰富的默认办法支撑,大大简化了定义类对象的代码量,代码简练清楚。本文我将向你介绍 Dataclass 所带来的便利性,为什么要运用它,以及演示怎么运用它。

作者介绍

Jina AI 机器学习工程师Johannes Messner

Dataclass 是一个数据类,顾名思义,数据类只需求关怀数据,而和详细行为解耦。Dataclass 是对 Document 更高层次的封装,可以更好地表明一个多模态文档。如图所示,你可以利用装饰器 @dataclass,将左边的多模态文档的信息表明为右边代码片段。

一文读懂Jina生态的Dataclass

dataclass-example

总的来说,这个新特性会更好地提高开发者的体会,让开发者近乎于运用自然语言般封装自己的数据,并拓展使其成公共可用的服务。减少了对 DocArray 及其特性的考虑,更多地考虑自己的数据和使命:你可以依据自己的数据来自定义 Dataclass,快速地表明自己的数据,完成自己的使命。 此外最重要的是,DocArray 和 Jina 都支撑了这个新特性。

在这个新特性的开发过程中,为了保证用户的最佳体会,咱们不得不做出一些纤细的设计和改进,以提高其功率、可用性和便携性。因而,让咱们借此机会回顾一下这些决议,咱们做出这些决议的原因,以及咱们以为你会喜欢这个新特性的原因。

过去是怎么做的

Document[1]和DocumentArray[2]一直以来都是极端灵敏的数据结构,基本上可以包容任何类型的数据。但是在过去,咱们运用的是和其他软件相同的方式,供给开发者需求的所有东西,并告知开发者怎么与这些东西交互,怎么让数据去适配这些东西。

举个比方,按照之前的办法,当你想要表明一篇包含多种模态信息的论文,里面包含正文文本、图片、该图片的描绘文本、许多参考文献的超链接、以及一些元数据。在之前,咱们需求这样建模:

fromdocarrayimportDocument
#modellingyourdataasanestedDocument
image=Document(uri="myimage.jpg").load_uri_to_image_tensor()
description=Document(text="thisismyawesomeimage")
references=[
Document(uri="https://arxiv.org/abs/2109.08970"),
Document(uri="https://arxiv.org/abs/1706.03762"),
]
reference_doc=Document(chunks=references)
article=Document(
text="thisisthemaintextofthearticle",
tags={"author":"you","release":"today"},
chunks=[image,description,reference_doc],
)

咱们需求将论文建模为嵌套的 Document,主要数据(如正文和标签)坐落顶层,其他数据放在 chunk(块) 等级,每个块都有别离的 Document 用于保存数据。全体结构如下图所示:

一文读懂Jina生态的Dataclass
Document 全体结构

当你想要拜访刚刚编译的数据,需求输入如下代码:\

author_str=article.tags["author"]
image_tensor=article.chunks[0].tensor
first_reference_uri=article.chunks[2].chunks[0].uri

哦豁突然你不得不考虑块的问题,还有块的块,块的索引,而你想做的仅仅拜访作者、图画和参考文献。明显,这里需求做出改动。

现在:以你的数 据为中心

Jina 经典的 Document、DocumentArray 以及它们供给的 API 依然十分好用,但之前存在的问题是:只要开发者才最了解自己的数据,而像块这样的概念或许无法自然地映射到手头的使命里来。

所以,咱们供给了 Dataclass!

口说无凭,让咱们用 Dataclass 从头建模上文提到的多模态文档:首要定义数据的结构,然后填充数据。

fromdocarrayimportDocument,DocumentArray,dataclass
fromtypingimportList
fromdocarray.typingimportImage,Text,JSON
#首要,咱们定义数据的结构
@dataclass
classArticle:
image:Image
image_description:Text
main_text:Text
metadata:JSON
references:List[Text]
#接着,咱们填充数据进去
article_dataclass=Article(
image="myimage.jpg",
image_description="thisismyawesomeimage",
main_text="thisisthemaintextofthearticle",
metadata={"author":"you","release":"today"},
references=["https://arxiv.org/abs/2109.08970","https://arxiv.org/abs/1706.03762"],
)
article=Document(article_dataclass)

实际上这和人类对国际的看法的方式很相似了,简直等价于用自然语言去描绘这篇多模态文档。换句话说,Dataclase 充当了从实际国际到 DocArray 国际的映射。你简直可以把它看作是一个十分美丽的__init__()办法。

与此同时,咱们也获得了更清晰的 Document 结构。

一文读懂Jina生态的Dataclass
新的 Document 结构

以上这些看起来已经相当不错了,但接下来才是真正酷的部分,也是咱们最新功用所带来的东西。当咱们想在article拜访数据时,咱们可以:

image_doc=article.image#returnsaDocument
image_tensor=article.image.tensor#returnstheimagetensor
author_str=article.metadata.tags["author"]
first_reference_uri=article.references[0].text

这里是在DocumentArray 等级拜访自定义 Dataclass 的语法:

da=DocumentArray([Document(article_dataclass)for_inrange(3)])
image_docs=da["@.[image]"]
image_and_description_docs=da["@.[image,image_description]"]
image_tensors=image_docs[:,"tensor"]

如图所示,即便将 Dataclass 转换为 Document 或 DocumentArray 之后,你依然可以依据你自定义的 Dataclass 来推理数据及嵌套数据。此外,现在”块”已经一去不复返了,你可以直接拜访 Image、reference 等实际数据。

Document Everywhere

在上面的代码里你或许会奇怪, 为什么我需求调用article.image.tensor来获取图画向量,调用article.image不就够了吗?为什么要经过Document这个中间步骤?

有如下 3 个重要因素,使得必须回来一个完好的 Document,而不仅仅存储在 Document 里的数据。

  1. 1.灵敏性:DocArray 是适用于任何类型数据的数据结构,所以灵敏性一直是咱们的首要使命之一。因而,咱们不是回来特定的数据类型,而是回来一个 Document,由于这是最灵敏的数据表明,你可以用它做任何你想做的复杂的使命。
  2. 2.Documents Everywhere:DocArray 和 Jina 中的简直每个操作都将 Document(或 DocumentArray)作为输入,并回来一个作为输出。
  3. 3.更好地融入 J ina 生态:一旦进入到 Jina 生态,将 Document 作为回来类型就变得至关重要,下文我将详细介绍原因。

从本地到云端

一直以来,DocArray 专注于本地和单片机开发者的体会,Jina 将 DocArray 扩展到云端。到目前为止,咱们只讨论了运用 DocArray 进行本地开发,现在让咱们把注意力转移到 Jina 的微服务国际。

首要请定心,运用 Document 的 Dataclass 并不受限于运行环境。它可以在任意 Executor 中完成,不管是在你的电脑上,还是分布全球的 Kubernetes 集群里,又或是在 JCloud。

fromdocarrayimportDocument,dataclass
fromdocarray.typingimportImage,Text
fromjinaimportExecutor,Flow,requests
importnumpyasnp
@dataclass
classArticle:
image:Image
description:Text
article_dataclass=Article(image="myimage.jpg",description="thisisanawesomeimage")
article=Document(article_dataclass)
classImageEncoder(Executor):
@requests(on="/index")
defencode_image(self,docs,*args,**kwargs):
image_tensor=docs[0].image.tensor
docs[0].embedding=np.zeros(image_tensor.shape)#dummyembding
withFlow().add(uses=ImagEncoder)asf:
f.index(inputs=article)

接下来,让咱们看看Dataclass 是怎么在一个真正实践的 demo 中发挥作用的。

实际示例

假设咱们现在有一些十分简单的文章,由图画和描绘组成,咱们想要创建这些文章的 embedding。为此,咱们先对图画和描绘文本别离进行编码,以便将这些向量表明合并成整篇文章的最终 embedding。

fromdocarrayimportDocument,dataclass
fromdocarray.typingimportImage,Text
fromjinaimportExecutor,Flow,requests
importnumpyasnp
@dataclass
classArticle:
image:Image
description:Text
article_dataclass=Article(image="myimage.jpg",description="thisismycoolimage")
article=Document(article_dataclass)
classImageEncoder(Executor):
def__init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.model=lambdat:np.random.rand(
128
)#initializedummyimageembeddingmodel
@requests(on="/encode")
defencode_image(self,docs,**kwargs):
fordindocs:
image=d.image
image.embedding=self.model(image.tensor)
classTextEncoder(Executor):
def__init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.model=lambdat:np.random.rand(
128
)#initializedummytextembeddingmodel
@requests(on="/encode")
defencode_text(self,docs,**kwargs):
fordindocs:
description=d.description
description.embedding=self.model(description.text)
classEmbeddingCombiner(Executor):
def__init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.model=lambdaemb1,emb2:np.concatenate(
[emb1,emb2]
)#initializedummymodeltocombineembeddings
@requests(on="/encode")
defencode_text(self,docs,**kwargs):
fordindocs:
d.embedding=self.model(d.image.embedding,d.description.embedding)
f=(
Flow()
.add(uses=ImageEncoder,name="ImageEncoder")
.add(uses=TextEncoder,name="TextEncoder",needs="gateway")
.add(uses=EmbeddingCombiner,name="Combiner",needs=["ImageEncoder","TextEncoder"])
)
withf:
da=f.post(inputs=article,on="/encode")

如代码所示,图片和文本对应的 embedding 存储到了对应的 Document 中。一致的 Document 的格式标准,使得咱们在寻找和运用相应 Embedding 时可以说是随手拈来,开发起来极端舒适。

通用性

看到这里,期望你和咱们一样为 Documents 交互的新方式而兴奋。或许你会担心:既然这些 Dataclass 是完全个性化的,那我要怎么保证自己上传到Jina Hub[3]的 Executor 能供其他社区用户复用,又怎么保证能运用由其他社区用户同享的 Executor 呢?

无需担心,DocArray 解决这种问题仅仅洒浇水。

尽管现在咱们依然支撑文档级选择器语法 (d.image),但****愈加推荐你运用 DocumentArray 级语法(da['@.[image]'])来坚持最大的互操****作性。

代码胜于雄辩!让咱们重构上面的Executor 代码:

classImageEncoder(Executor):
def__init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.model=lambdat:np.random.rand(
len(t),128
)#initializedummyimageembeddingmodel
@requests(on="/encode")
defencode_image(self,docs,parameters,**kwargs):
path=parameters.get("access_path","@r")
image_docs=docs[path]
embeddings=self.model(image_docs[:,"tensor"])
image_docs.embeddings=embeddings
classTextEncoder(Executor):
def__init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.model=lambdat:np.random.rand(
len(t),128
)#initializedummytextembeddingmodel
@requests(on="/encode")
defencode_text(self,docs,parameters,**kwargs):
path=parameters.get("access_path","@r")
text_docs=docs[path]
embeddings=self.model(text_docs[:,"text"])
text_docs.embeddings=embeddings
classEmbeddingCombiner(Executor):
def__init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
self.model=lambdaemb1,emb2:np.concatenate(
[emb1,emb2],axis=1
)#initializedummymodeltocombineembeddings
@requests(on="/encode")
defcombine(self,docs,parameters,**kwargs):
image_path=parameters.get("image_access_path","@r")
text_path=parameters.get("text_access_path","@r")
image_docs=docs[image_path]
text_docs=docs[text_path]
combined_embeddings=self.model(image_docs.embeddings,text_docs.embeddings)
docs.embeddings=combined_embeddings

重写之后,每个连接到这些 Executor 的客户端都可以供给自己的参数,并将access_path匹配其自定义数据类:

f=(
Flow()
.add(uses=ImageEncoder,name="ImageEncoder")
.add(uses=TextEncoder,name="TextEncoder",needs="gateway")
.add(uses=EmbeddingCombiner,name="Combiner",needs=["ImageEncoder","TextEncoder"])
)
@dataclass
classArticle:
image:Image
description:Text
article_dataclass=Article(image="myimage.jpg",description="thisismycoolimage")
article=Document(article_dataclass)
withf:
da=f.post(
inputs=article,
on="/encode",
parameters={
"ImageEncoder__access_path":"@.[image]",
"TextEncoder__access_path":"@.[description]",
"Combiner__image_access_path":"@.[image]",
"Combiner__text_access_path":"@.[description]",
},
)
print(da[0].embedding.shape)

这样一来,每个用户都可以定义他们自己的 Dataclass, 运用自定义的数据类完成开发使命。而且这些数据类不光在 Executor 是通用的,还可以在整个 Jina 生态系统中重复运用。

下一步等你来探究

一文读懂Jina生态的Dataclass

process

Dataclass 为处理多模态数据的数据科学家和机器学习工程师供给了极好的表达能力,使他们可以以十分直观的方式表明图画、文本、视频、网格、表格数据。本文只介绍了它的部分功用,还有更多强大的功用等你来探究!

  • •自定义数据类型:DocArray 为数据类接口供给了许多常见的类型,如 Text、Image、JSON 等。但你也可以定义和运用你自己的类型,包含定义从数据类到 Document ,以及回来的自定义映射。
  • •嵌套数****据类:一些复杂的领域需求更复杂的建模。例如,一篇文章实际上或许由多个阶段组成,每个阶段包含一个图片、一个描绘和一个正文文本。你可以经过在文章的 Dataclass 中嵌套一个阶段的 Dataclass 的列表来轻松地表明。
  • 子索引:在上面的比方中,咱们运用 EmbeddingCombiner 为每个 Document 生成顶层 embedding,以用于神经查找使命。但是关于某些使命,你或许期望不在顶层进行查找,而是在形式层进行查找。比方你不期望找到全体上相似的文章,而是期望找到那些有相似图片的文章,这就是子索引可以做到的工作。

引证链接

[1]Document:*docarray.jina.ai/fundamental… [2]DocumentArray:*docarray.jina.ai/fundamental… [3]Jina Hub:hub.jina.ai/

参加 J-Tech 沟通社群

官网:Jina.ai

社区:Slack.jina.ai

开源:Github.com/Jina-ai

私信后台参加vx群