乐趣区

关于前端:所见即搜3分钟教你搭建一个服装搜索系统

摘要:用 MindSpore+Jina,基于 Fashion-MNIST Dataset 搭建的服装搜寻零碎。

引言

各位算法萌新们,是不是常常训练了模型却不晓得如何部署和利用?或者只会调参但不会前端后端所以没法向老板们解释这个模型能够做啥?如果有一种非常简单的形式,让你在 3 分钟内就能建设一个以深度学习为撑持的搜寻零碎,并能在前端展现进去 show 给各位老板们看?想不想尝试呢?本文来自 MindSpore 社区技术治理委员会(TSC)的成员肖涵博士——Jina 的创始人,用 MindSpore+Jina,基于 Fashion-MNIST Dataset 搭建的服装搜寻零碎。

[本文目录]

  • 如何用 Jina①步搞定?
  • Jina 的 hello-world 是如何运行的?
  • 如何应用 MindSpore+Jina 来搭建搜寻零碎?
  • 创立一个 MindSpore Executor
  • 批改 MindSpore 的 Encoder 和网络代码
  • 写一个单元测试
  • 筹备 Dockerfile
  • 最初一步:终于能够 Build 了!
  • 来看 MindSpore 的成品吧!
  • 总结

喜爱逛淘宝或者各大海淘网站的各位程序员(的女朋友们),你们在浏览服装的时候,是不是会常常看见模特们身上的衣服,全!都!想!要!然而,不晓得从哪儿买,货号是什么?就算从各大穿搭博主那儿晓得货号了,也懒得一一去搜寻。当初,齐全不须要这么麻烦,只有你花 3 分钟建设这个服装搜寻零碎,当你的女朋友再看到模特身上的衣服,就能够搜寻出最类似的衣服,是不是很赞!

图 1 Shop the look

在做之前,先理解一下咱们明天须要应用的两个框架:MindSpore 和 Jina

  • MindSpore 是 2020 年 3 月 28 日华为开源的深度学习框架,它能原生反对自家的昇腾芯片,极大的晋升了运行性能!
  • Jina 是一个由最先进的 AI 和深度学习驱动的云端神经搜寻框架,能够在多个平台和架构上实现任何类型的大规模索引和查问。无论你搜寻图片、视频片段还是音频片段,Jina 都能解决。

这里应用的数据集是 Fashion-MNIST dataset。它蕴含 70,000 张图片,其中 60,000 张为训练集,10,000 张为测试集。每张图片都是 28×28 的灰度图像,一共 10 个类别。上面咱们正式开始吧!

如何用 Jina①步搞定?

首先你须要一台电脑,确认一下环境是否 ok:

  • Mac OS or Linux
  • Python 3.7, 3.8
  • Docker

而后执行以下一行命令即可:

pip install jina && jina hello-world

或者间接用 docker:

docker run -v "$(pwd)/j:/j" jinaai/jina hello-world --workdir /j && open j/hello-world.html  # replace "open" with "xdg-open" on Linux

当初开始运行程序, 就能够看到运行后果了:

图 3 Jina hello-world 运行后果

是不是很神奇?那么 Jina 是如何实现的呢?能够先花 1 分钟工夫理解 Jina 的十个根本组件,在本文中最重要的三个信息别离是:

  • YAML config:让用户能够自定义的形容对象的属性。
  • Executor:代表了 Jina 中的算法单元。譬如把图像编码成向量、对后果进行排序等算法等都能够用 Executor 来表述。咱们能够用 Crafter 来把制作 / 宰割和转化要搜寻的内容,而后用 Encoder 来将制作好的搜寻对象示意为向量,再用 Indexer 保留和检索搜寻的向量和键值信息,最初用 Ranker 来对搜寻出的后果排序。
  • Flow:示意一个高阶的工作, 譬如咱们所说的索引(index)、搜寻(search)、训练(train),都属于一个 flow。

Jina 的 hello-world 是如何运行的?

想晓得 hello-world 运行的细节嘛?其实在很简略,在 hello-world 里,咱们应用 YAML 文件来形容 index 和 search 的 flow,能够导入 YAML 文件,并通过.plot() 命令来可视化:

from pkg_resources import resource_filename
from jina.flow import Flow

f = Flow.load_config(resource_filename('jina', '/'.join(('resources', 'helloworld.flow.index.yml')))).plot()

图 4 hello-world YAML 文件流程图

YAML 文件里的信息是如何示意成图的呢?上面能够看看直观的比照:

图 5 YAML 文件信息

其实,这个 flow 中蕴含了两步(在 Jina 中也能够叫两个 Pod):第一步它将数据通过并行的形式喂给 encoder,输入向量和 meta 信息分片存储在索引器中。查问 flow 也是以同样的形式运行,只不过在参数上有些小变动。

既然原理这么简略,如果咱们本人训练的模型,是不是也能够替换呢?上面咱们来手把手教大家如何只用 4 步,就能够用 MindSpore+Jina 来搭建服装搜寻零碎。

如何应用 MindSpore+Jina 来搭建搜寻零碎?

创立一个 MindSpore Executor

MindSpore 的 ModelZoo 里有很多深度学习模型,本文应用的是最经典的 CV 网络:LeNet。咱们能够通过 jina hub 来创立一个新的 MindSpore Executor,本文应用的 Jina Hub 版本是 v0.7 的,能够输出以下命令装置:

pip install "jina[hub]"

装置好后,如果你想创立一个新的 executor,能够间接输出:

jina hub new

执行这个命令后会弹出一下领导命令,依照上面的要求输出即可,有些设置间接用默认的就行,间接按 Enter 键就能够啦。

比拟重要的是这几个命令:

所有命令输出实现后,你会看到 MindSporeLeNet 这个文件夹曾经创立胜利了。而后下载 MindSpore 的 LeNet 代码库和 Fashion MNIST 的训练数据,依照上面的形式把它们放到 MindSporeLeNet 模块下即可:

图 7 MindSporeLeNet 代码构造

批改 MindSpore 的 Encoder 和网络结构代码

  • 1. 批改__init__.py

这是原始的__init__.py 代码,有一个根底类 BaseEncoder , 咱们要扭转一下 encode 的形式, 把它变成 BaseMindsporeEncoder。

from jina.executors.encoders import BaseEncoder

class MindsporeLeNet(BaseEncoder):
    """:class:`MindsporeLeNet` What does this executor do?."""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # your customized __init__ below
        raise NotImplementedError

    def encode(self, data, *args, **kwargs):
        raise NotImplementedError

BaseMindsporeEncoder 是 Jina 中的抽象类,它在__init__构造函数中会导入 MindSpore 模型的 checkpoint。此外,它还能通过 self.model 提供 MindSpore 模型的属性接口。上面这张表显示了 MindSporeLeNet 通过构造函数继承的类。

图 8 MindSporeLeNet 中继承的类

批改完当前如下所示:

from jina.executors.encoders.frameworks import BaseMindsporeEncoder

class MindsporeLeNet(BaseMindsporeEncoder):
    """:class:`MindsporeLeNet` Encoding image into vectors using mindspore."""

    def encode(self, data, *args, **kwargs):
        # do something with `self.model`
        raise NotImplementedError

    def get_cell(self):
        raise NotImplementedError
  • 2. 执行 encode() 办法。

给定一堆 batch size 为 B 的图像数据(用的 numpy 的 ndarray 来示意,shape 为 [B, H, W]),encode() 把图像数据转换成向量的 embeddings(shape 为[B, D])。通过 self.model 导入 MindSpore LeNet 模型后,咱们能够通过 self.model(Tensor(data)).asnumpy() 来进行转换即可。

留神 :self.model 的输出 shape 很容易出错。原始的 LeNet 模型的输出是三通道的图片,shape 是 32×32,所以输出必须是[B, 3, 32, 32]。然而 Fashion-MNIST 是灰度图片,单通道,图像的 shape 是 28×28,所以咱们要么调整图片的尺寸,要么给图片补零。这里咱们就用简略的补零操作了。最终的 encode() 函数如下所示:

def encode(self, data, *args, **kwargs):
    # LeNet only accepts BCHW format where H=W=32
    # hence we need to do some simple padding
    data = numpy.pad(data.reshape([-1, 1, 28, 28]),
                  [(0, 0), (0, 0), (0, 4), (0, 4)]).astype('float32')
    return self.model(Tensor(data)).asnumpy()
  • 3. 执行 get_cell()办法。

在 MindSpore 中,咱们通常把神经网络中的层叫做『cell』,它能够是一个独自的神经网络层(譬如 conv2d, relu, batch_norm)。为了失去向量的 embedding,咱们只须要从 LeNet 中移除 classification head 即可(譬如最初一个 softmax 层)。这个很好实现,只须要从原始的 LeNet5 类中继承,而后重写 construct() 函数即可。

def get_cell(self):
    from .lenet.src.lenet import LeNet5

    class LeNet5Embed(LeNet5):
        def construct(self, x):
            x = self.conv1(x)
            x = self.relu(x)
            x = self.max_pool2d(x)
            x = self.conv2(x)
            x = self.relu(x)
            x = self.max_pool2d(x)
            x = self.flatten(x)
            x = self.fc1(x)
            x = self.relu(x)
            x = self.fc2(x)
            x = self.relu(x)
            return x

    return LeNet5Embed()

写一个单元测试

当你在创立一个 Jina executor 的时候,肯定不要忘了写单元测试,如果在 executor 里没有单元测试的话,是无奈通过 Jina Hub API 来创立的哦~

在这个样例中曾经生成了一个测试模板,你能够在 tests 文件夹外面找到 test_mindsporelenet.py 文件。先查看下 MindSpore 是否运行,如果能够运行的话,看看输入的 shape 是否是咱们所心愿的。

import numpy as np

from .. import MindsporeLeNet


def test_mindsporelenet():
    """here is my test code

    https://docs.pytest.org/en/stable/getting-started.html#create-your-first-test
    """mln = MindsporeLeNet(model_path='lenet/ckpt/checkpoint_lenet-1_1875.ckpt')
    tmp = np.random.random([4, 28 * 28])

    # The sixth layer is a fully connected layer (F6) with 84 units.
    # it is the last layer before the output
    assert mln.encode(tmp).shape == (4, 84)

筹备 Dockerfile

python 层面的筹备工作曾经实现了,上面咱们筹备 Docker image。咱们能够基于已有的 Dockerfile 来创立,只须要加一行运行 train.py 代码来生成 checkpoint 文件的代码即可。

FROM mindspore/mindspore-cpu:1.0.0

# setup the workspace
COPY . /workspace
WORKDIR /workspace

# install the third-party requirements
RUN pip install --user -r requirements.txt

+ RUN cd lenet && 
+    python train.py --data_path data/fashion/ --ckpt_path ckpt --device_target="CPU" && 
+    cd -

# for testing the image
RUN pip install --user pytest && pytest -s

ENTRYPOINT ["jina", "pod", "--uses", "config.yml"]

这一行应用了 MindSpore LeNet 代码库里的 train.py 来生成训练的 checkpoint。咱们在测试和部署的时候会用到这个 checkpoint。在 config.yml 文件中,须要把 checkpoint 的文件地址放在 model_path 这个参数里。requests.on 定义了 MindSporeLeNet 在 index 和 search 的 request 下应该如何执行。如果下面这些内容不了解也没关系,其实都是从 helloworld.encoder.yml 这个文件里复制和改变的。

!MindsporeLeNet
with:
  model_path: lenet/ckpt/checkpoint_lenet-1_1875.ckpt
metas:
  py_modules: 
    - __init__.py
    # - You can put more dependencies here
requests:
  on:
    [IndexRequest, SearchRequest]:
      - !Blob2PngURI {}
      - !EncodeDriver {}
      - !ExcludeQL
        with:
          fields:
            - buffer
            - chunks

最初一步:终于能够 Build 了!

终于能够把 MindSporeLeNet build 成 Docker 镜像了!!执行以下命令:

jina hub build MindsporeLeNet/ --pull --test-uses
  • –pull:当你的图片数据集不在本地时,这个命令会通知 Hub builder 来下载数据集
  • –test-uses:减少一个额定的测试来查看创立的镜像是否能够通过 Jina Flow API 试运行胜利。

当初终端曾经开始打印日志了,如果工夫太久的话,能够在 MindsporeLeNet/lenet/src/config.py 中将 epoch_size 调小。

最初胜利的信息:

HubIO@51772[I]:Successfully built cfa38dcfc1f9
HubIO@51772[I]:Successfully tagged jinahub/pod.encoder.mindsporelenet:0.0.1
HubIO@51772[I]:building MindsporeLeNet/ takes 57 seconds (57.86s)
HubIO@51772[S]: built jinahub/pod.encoder.mindsporelenet:0.0.1 (sha256:cfa38dcfc1) uncompressed size: 1.1 GB

当初你能够通过上面的命令将它作为一个 Pod 来应用了:

jina pod --uses jinahub/pod.encoder.mindsporelenet:0.0.1

比照 jina pod –uses abc.yml, 咱们会发现 jinahub/pod.encoder.mindsporelenet:0.0.1 的日志信息的结尾处有一个 docker 容器。这些 log 日志是从 Docker 的 container 传输到 host 端的,上面形容了两者具体的差别。

图 9 差别比照

当然,你也能够上传这个镜像到 Docker 仓库里:

jina hub build MindsporeLeNet/ --pull --test-uses --repository YOUR_NAMESPACE --push

来看 MindSpore 的成品吧!

最初,间接在 index 和 search 的 flow 中来应用新创建的 MindSpore Executor 吧,很简略,只须要替换 pods.encode.uses 这行代码就行:

图 10 index 与 query 的 YAML 文件差别

jina hello-world 的参数能够自定义,只有指定咱们刚刚编写好的 index 和 query 的 YAML 文件,输出以下命令即可:

jina hello-world --uses-index helloworld.flow.index.yml --uses-query helloworld.flow.query.yml

哈哈,实现了!几分钟之内你就能够看到结尾动图显示的查问后果了!

图 11 最终输入后果

总结

本文中应用了 MindSpore+Jina 来独特搭建一个服装搜寻零碎,代码非常简单,其实只有学会批改 encode 的代码,依据须要构建网络层,而后打包成 docker 的 image,批改 YAML 文件就能够用 Jina 来实现最终的展现成果了,这样大家只有能够依据本人的需要,批改大量的代码,即可自行搭建一个基于 MindSpore 的搜寻零碎,是不是非常简单呢~ 感兴趣的同学能够间接点击以下链接,就能够间接运行:https://gitee.com/mindspore/c…

本文分享自华为云社区《3 分钟教你用 MindSpore 和 Jina 搭建一个服装搜寻零碎!》,原文作者:chengxiaoli。

点击关注,第一工夫理解华为云陈腐技术~

退出移动版