如何构建一个神经搜索应用程序

299 阅读6分钟

Jina人工智能概述

在网络上搜索信息并不新鲜。我们每天都在Twitter、LinkedIn、Stack Overflow和Amazon上搜索。我们大家最常见的搜索方式已经有很长一段时间了,就是使用谷歌搜索平台。然而,这不是我们今天要讨论的内容。

我们将讨论神经搜索,以及它与我们所习惯的标准搜索有什么不同。此外,本教程将指导你如何使用Jina AI框架来完成你的ML任务。

前提条件

要跟上进度,你需要熟悉。

  • 机器学习。
  • 深度学习。

什么是神经搜索

在神经搜索之前,开发者必须编写每一条指令来帮助应用程序检索信息。这个过程很耗时,而且会让开发者在试图开发这些应用程序时感到头痛。

而神经搜索的情况则不同。随着神经网络的出现,开发人员编写规则的方式发生了变化。人们可以训练一个神经网络来执行一项任务,而且网络会随着看到的数据越多而变得更好。这与神经搜索的情况相同。简单地说,就是把神经网络引入搜索。

预先训练的神经网络被部署来检索信息。这些网络被训练来检索信息,并在输入大量数据时在信息检索方面变得更好。Jina AI是一个神经搜索框架,使用深度神经网络来执行搜索。

什么是Jina AI

Jina AI是一个开源的、云原生的神经搜索框架。它用于为任何种类的模式建立最先进和可扩展的深度学习搜索应用。例如,视频、图像、源代码、长文本等。该框架允许你将谷歌搜索引擎的 "轻量级 "版本导入你的项目中。

它是由韩晓博士在2020年5月首次推出的。他也是知名开源项目的创建者,如bert-as-a-service和流行的fashion-MNIST数据集。目前,位于德国柏林的开源技术初创公司Jina AI负责维护该框架。

如何安装Jina AI

Jina AI框架很容易使用快速的pip 安装,如下图所示。

!pip install -U jina

如果你想下载Jina的最新版本,请确保包括-U 。另外,别忘了在pip 前面加上感叹号! 。否则,你会得到一个错误。

基本概念

该框架有三个基本概念。

  • 文档
  • 执行者
  • 流程

文档

它是Jina的基本数据类型。一个文档可以是文本、图片、视频或任何你拥有的数据类型。

执行器

它处理数据。在这种情况下,我们的数据来自Document

流程

Flow简化并分配了Executors 。它允许你将DocumentArrayExecutor 连在一起,以带来真正的价值,并从中建立和提供一个应用程序。

  1. 它由pods组成。它们是Jina的 "大脑"。这些pod帮助我们实现特定的任务,如分割、编码和排名。
  2. 语境管理器。
  3. 高层任务的抽象化,即索引或查询。

让我们创建一个示例流程。

from jina import Flow

f = Flow()

我们已经导入了Flow ,并制作了它的一个实例。有了这两行代码,你就准备好了你的流程。然而,我们知道这个流程是一个管理器。现在,这个流程是没有用的,因为它没有管理任何东西。因此,我们需要使用.add() 方法给它添加一些部件。

from jina import Flow

f = Flow().add(uses = 'cnn-encoding').add(uses = 'simple-indexer')

上面的代码告诉flow使用cnn-encoder 。你可以使用任何你想要的编码器。它还告诉flow使用simple-indexer 。这里,我们有一个编码和索引的流程。最后,我们告诉flow如何处理这些,如图所示。

from jina import Flow

f = Flow().add(uses = 'cnn-encoding').add(uses = 'simple-indexer')

with f:
    f.index(docs)

在这里,我们说用我们创建的流程,f ,让我们对一些文档(docs)进行索引。

流程中包含的其他方法有。.start(),.stop(),.block(),.plot(), 和with context manager

实施一个例子来演示如何使用该框架

利用这三个基本概念,让我们实现一个简单的Neural Search 服务来演示如何使用该框架。我们将利用谷歌上的Totally-Looks-Like数据集。该数据集包含了6016个来自野外的图像对,揭示了人类所采用的丰富而多样的标准。

给出一个查询,该框架应该会给我们一些结果作为回报。另外,我们将使用QueryLang 来帮助我们实现这个任务。QueryLang 是Jina的一个基本数据类型。它提供了一个Python接口,允许用户管理和访问Jina并表示查询语言结构。

让我们把必要的依赖性导入我们的代码中。

from docarray import Document, DocumentArray
from jina import Executor, Flow, requests

下面的类执行一些预处理,并通过一个Executor

class PreprocImg(Executor):
    @requests
    async def foo(self, docs: DocumentArray, **kwargs):
        for d in docs:
            (
                d.load_uri_to_image_tensor(200, 200)  # load
                .set_image_tensor_normalization()  # normalize color
                .set_image_tensor_channel_axis(
                    -1, 0
                )  # switch color axis for the PyTorch model later
            )

下面的类执行嵌入并通过一个Executor

class EmbedImg(Executor):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        import torchvision
        self.model = torchvision.models.resnet50(pretrained=True)

    @requests
    async def foo(self, docs: DocumentArray, **kwargs):
        docs.embed(self.model)

下面的类执行匹配并通过Executor

class MatchImg(Executor):
    _da = DocumentArray()

    @requests(on='/index')
    async def index(self, docs: DocumentArray, **kwargs):
        self._da.extend(docs)
        docs.clear()  # clear content to save bandwidth

    @requests(on='/search')
    async def foo(self, docs: DocumentArray, **kwargs):
        docs.match(self._da, limit=9)
        del docs[...][:, ('embedding', 'tensor')]  # save bandwidth as it is not needed

让我们用Flow 来连接所有的Executors 。我们用.add() 方法来把每个Executor 添加到Flow

f = (
    Flow(port_expose=12345)
    .add(uses=PreprocImg)
    .add(uses=EmbedImg, replicas=3)
    .add(uses=MatchImg)
)

我们可以使用.plot() 方法来可视化这个Flow 。我们将图像保存为flow.svg 。你可以按照你的意愿来命名。

f.plot('flow.svg')

下一步涉及下载图像数据集。我们把这个结果保存在变量里面,index_data

index_data = DocumentArray.pull('demo-leftda', show_progress=True)

然后我们用下面的代码对这些图像数据进行索引。

with f:
    f.post(
        '/index',
        index_data,
        show_progress=True,
        request_size=8,
    )
    f.block()

这个过程可能需要一些时间。请耐心等待,因为它正在执行索引。

索引成功后,我们可以使用Python客户端来访问该服务。

from jina import Client

c = Client(port=12345)  # connect to localhost:12345
print(c.post('/search', index_data[0])['@m'])  # '@m' is the matches-selector

最后,我们通过编写以下代码从GRPC接口切换到REST API。

with f:
    ...
    f.protocol = 'http'
    f.block()

.block() 是 中的一个方法。它阻止执行,直到程序被终止。保持 ,以便可以从其他地方(客户端等)使用它,这很有用。Flow Flow

总结

本教程向你展示了如何使用一个简单的例子来构建一个神经搜索应用程序。当然,这只是一个基本的例子,但它包含了所有必要的概念,应该可以让你开始使用该框架。