摘要:用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张为测试集。每张图片都是28x28的灰度图像,一共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_filenamefrom jina.flow import Flowf = 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 BaseEncoderclass 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 BaseMindsporeEncoderclass 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是32x32,所以输出必须是[B, 3, 32, 32]。然而Fashion-MNIST是灰度图片,单通道,图像的shape是28x28,所以咱们要么调整图片的尺寸,要么给图片补零。这里咱们就用简略的补零操作了。最终的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 npfrom .. import MindsporeLeNetdef 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 workspaceCOPY . /workspaceWORKDIR /workspace# install the third-party requirementsRUN 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 imageRUN pip install --user pytest && pytest -sENTRYPOINT ["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这个文件里复制和改变的。
!MindsporeLeNetwith: model_path: lenet/ckpt/checkpoint_lenet-1_1875.ckptmetas: py_modules: - __init__.py # - You can put more dependencies hererequests: 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 cfa38dcfc1f9HubIO@51772[I]:Successfully tagged jinahub/pod.encoder.mindsporelenet:0.0.1HubIO@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 。
点击关注,第一工夫理解华为云陈腐技术~