かしこい検索システムを作れるOSS「Jina」を使ってみた！

# はじめに

昨今、深層学習技術の発展にはめざましいものがあります。
自然言語の単語や文の意味を捉えた類似検索や質問応答であったり、画像の内容に関する説明文の生成であったりと、ワクワクさせるものが日々登場しています。
それらの技術を使ってよりかしこい検索システムを作ることができれば、これまでうまく活用されていなかったデータの活用促進につながり、より生産性を上げられるのではないかと妄想する毎日を送っていました。
しかし、一からそのような検索システムを構築するのは非常に困難です。
そのため、なかなか一歩を踏み出せずにいたのですが、最近面白いOSSを見つけました。
その名も [Jina](https://github.com/jina-ai/jina) です。
（発音は公式動画等を見る限り「ジーナ」のようです）

今回は、このJinaについて簡単に紹介しつつ、デモを動かしていきたいと思います。

# Jinaって何？

Jinaは、[Han Xiao氏](https://hanxiao.io/)[^1]によって2020年に立ち上げられたベンチャーである[Jina AI](https://jina.ai/)によって開発されているOSSです。
Jinaの特徴として、以下がアピールされています。

* 最新のAIを活用した検索システムを素早く作れる
* テキストだけでなく、画像や音声、動画、ソースコード等の多種多様なデータに対してクロスモーダル/マルチモーダルな検索システムを実現可能
* クラウドネイティブなアーキテクチャを採用し、高いスケーラビリティー等を実現

なかなか魅力的ですね！
とはいえ、やはり使ってみないことにはどんなものなんかいまいちよくわかりません。
幸い、Jinaは様々なデモを公開してくれていますので、実際に手を動かして動作を確認することができます。

# 早速使ってみよう！

Jinaのデモは[こちらのリポジトリ](https://github.com/jina-ai/examples)で公開されています。
READMEに記載の一覧を見るだけでもワクワクさせられますね。
皆様も興味を持った例を早速試したくなるかと思いますが、1つ残念なお知らせです。
現在Jinaの開発は非常にアクティブであるため、大抵の例がそのままでは動かなくなっているのです！
（私もかなり苦しまされました）

なので、この記事では私が試行錯誤の末に動作確認の取れたデモを紹介していきたいと思います。
（今後も順次追加していくかもしれません。PRを送れ？時間があればやります・・・）

## 動作確認環境について

私は以下の環境で動作確認を行っています。

* OS: ubuntu 20.04 (VirtualBox上のVM)
* CPU: 4 vCPU
* RAM: 16GB
* ストレージ: 500GB
* 導入SW(導入方法は各自ググってください)
    * docker: 20.10.1
    * pyenv:  1.2.21-6-g2bf6111f
    * Poetry: 1.1.4

※pyenv/Poetryを使わない場合は、適宜手順を読み替えてください
※GPUが利用できる環境の方が、ストレス無く利用できるかと思います

## デモ一覧

私が少しでも動かそうとしたデモを以下に整理しています。

|デモ名|動作確認|紹介・修正版手順へのリンク|
|-------|-----|--------|
|[My First Jina App](https://github.com/jina-ai/examples/tree/master/my-first-jina-app)|×||
|[Build a NLP Semantic Search System with Transformers](https://github.com/jina-ai/examples/tree/master/southpark-search)|×||
|[Search images from captions and vice-versa](https://github.com/jina-ai/examples/tree/master/cross-modal-search)|△|[Link](#クロスモーダル検索search-images-from-captions-and-vice-versa)|

※動作確認の凡例

* ○：公式手順そのままで動いた
* △：手順を修正したら動いた
* ×：まだ動かせてない


## クロスモーダル検索(Search images from captions and vice-versa)

### どんな検索ができるの？

この例では、テキストを与えてその内容に近い画像を検索したり、逆に画像を与えてその内容に近いテキストを検索したりすることができます。
モデルの学習に使われているのは、[Flicker](https://www.flickr.com/)上の画像に英語でキャプションを付与した[Flickr Image dataset](https://www.kaggle.com/hsankesara/flickr-image-dataset)です。

例えば、「snow dog」というテキストで検索すると、以下のような検索結果となります。
きちんと雪原にいる犬の画像が取得できていますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68336/cde292d0-327f-74e5-47ee-3e9647bdd625.png)

また、以下の画像で検索すると、次のような検索結果となります。
画像の内容を踏まえたテキストが返ってきていますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68336/cfb8aa13-ecb0-f749-97cd-ea73935e4a20.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/68336/18848632-e0cd-d1a2-691f-316e8d6ce73b.png)


### どうやって動かすの？

手順は[こちら](https://github.com/jina-ai/examples/tree/master/cross-modal-search#prerequisites)にありますが、記載されている手順だけでは正しく動きません。
よって、私が動作確認した際の手順を以下に示します。

* 環境設定

```bash
# リポジトリをクローン
git clone https://github.com/jina-ai/examples.git
cd examples/cross-modal-search

# 必要なパッケージをインストール
sudo apt install gfortran libatlas-base-dev libhdf5-dev liblapack-dev libmagic1 pkg-config

# 一部モジュールのビルドにRustが必要なのでインストール
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source $HOME/.cargo/env

# Python3.7 or 3.8 が必要なのでインストール
pyenv install 3.7.9

# pyproject.tomlを生成(対話形式で入力する値は好みでOK)
poetry init
```

* python周りの環境設定

公式手順だけだと不足しているモジュールがあるので、以下の通りpyproject.tomlを編集して必要なモジュールをインストールします。

```toml:pyproject.toml
[tool.poetry]
name = "cross-modal-search"
version = "0.1.0"
description = ""
authors = ["※任意"]
license = "※任意"

[tool.poetry.dependencies]
jina = {version = "0.6.4", extras = ["http", "torch"]}
kaggle = "^1.5.10"
click = "^7.1.2"
docker = "==4.3.0"
Pillow = "^8.0.1"

[tool.poetry.dev-dependencies]

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
```

```bash
# Poetryが指定のPythonを使ってくれない場合は,以下を参考に指定すること
poetry env use /home/vagrant/.pyenv/versions/3.7.9/bin/python

# pyproject.tomlを元にPython仮想環境を設定
poetry install
```

* 各コンテナ用の設定の編集

この例では、画像用とテキスト用の2つのコンテナを利用しています。
ただ、そのままだと動かないところがあるので、`examples/cross-modal-search`内のファイルを以下の通り修正します。


```dockerfile:img_emb/Dockerfile
FROM pytorch/pytorch:latest

RUN apt-get update && apt-get install --no-install-recommends -y git wget
RUN wget http://www.cs.toronto.edu/~faghri/vsepp/runs.tar && tar -xvf runs.tar && rm -rf runs/coco* && rm -rf runs/f30k_vse0/ && \
rm -rf runs/f30k_order*/ && rm -rf runs/f30k_vse++/ && rm -rf runs/f30k_vse++_resnet* && rm -rf runs/f30k_vse++_vggfull_finetune/ && \
rm -rf runs.tar

COPY . ./

RUN git clone https://github.com/JoanFM/vsepp.git -b padded_sequence_sorted && mv vsepp/model.py . && rm -rf vsepp && \
pip install -r requirements.txt && \
python -c "import torchvision.models as models; model = getattr(models, 'vgg19')(pretrained=True).eval()"

# pytestが通らないのでコメントアウト
# 一応それっぽく動いているが、時間があれば原因を調査予定
# RUN pip install pytest && pytest -v -s

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

```

```img_emb/requirements.txt
jina[http]==0.6.4
torch>=1.1.0
torchvision>=0.3.0
```

```txt_emb/requirements.txt
jina[http]==0.6.4
torch>=1.1.0
nltk
pycocotools
```

* データの準備～起動

以降は[公式手順通り](https://github.com/jina-ai/examples/tree/master/cross-modal-search#prepare-the-data)に進めてもらえれば基本OKです。
(一部パス名が間違っていたりフォルダが足りなかったりした気がしますが、メモを失念してしまったので気づいた方はコメントいただければと思います)

ただ、事前にpoetryで構築した仮想環境に切り替えるのを忘れずに。

```bash
poetry shell
```

# 終わりに

今回は公式のデモを動かしてみるだけでしたが、それでも今後の可能性を感じました。
ただ、2020/12/21時点のJinaの最新バージョンは0.8.10で、大きな更新が活発に行われている状況のため、まだまだ手を出しづらい面もあります。
とはいえ、Jinaそのものの品質はなかなか高いように感じるため、個人的に引き続きウォッチしていこうかと思っています。

他の例についても時間を見つけて動かしてみるつもりですが、もし皆様の方で「こうしたら動いた」といった情報があればコメントいただけると幸いです。

本当は私が所有するデータを検索してみたり独自のモデルを使ってみたりしたかったのですが、例を動かすだけでも試行錯誤が必要だったのでそこまではすすめませんでした。
（ただ、トラシューしているうちにJinaへの理解が進んできたのは怪我の功名だったかもしれません。）
デモをいくつか動かせるようになったら、そちらの方にも取り組んでみようかと思います。


[^1]: Han Xiao氏は、[bert-as-service](https://github.com/hanxiao/bert-as-service)の開発者でもあります。また、Tencent AI在籍時には、[GNES](https://github.com/gnes-ai/gnes)という類似のコンセプトのOSSの開発にも携わっていましたが、こちらはすでに開発が停止されてアーカイブされてしまいました。