はじめに
検索技術の森本と申します。
先日、高速な近傍探索を実現するソフトウエアであるNGT(Neighborhood Graph and Tree)とそのPython用インターフェースを公開しております。それに引き続いてgongtとngtdを公開しました。
アプリケーション開発を想定して開発にGo言語、サーバプロトコルとしてHTTPとgRPCを採用しました。Go APIとしてgongtとHTTP/gRPCサーバ機能を提供するngtdを公開しましたので、近似ベクトル検索エンジンのアプリケーション開発が容易になることを期待しております。
前回のPython用インターフェースの時に使ったgensimを使い、gRPCでPythonから登録して、node.jsから検索する方法をご紹介します。gensimのインストール方法やモデルの入手についてはこちらをご参照ください。
また、今回はngtdのDocker imageを用意したので、是非お使いください。
Dockerを使わない場合や詳細な利用方法は省略しますので、それぞれのリポジトリのREADME.md(gongt)とREADME.md(ngtd)をご参照ください。
Docker imageの取得と確認
Docker Hubで公開していますので、以下のコマンドで取得・確認ができます。
$ sudo docker pull yahoojapan/ngtd:latest
$ sudo docker run --rm yahoojapan/ngtd:latest
NAME:
ngtd - NGT Daemonize
USAGE:
ngtd [global options] command [command options] [arguments...]
VERSION:
0.0.1-first
COMMANDS:
http, H serve ngtd index by http
grpc, g serve ngtd index by grpc
build, b build ngtd index
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--help, -h show help
--version, -v print the version
gRPCサーバの起動
$ sudo docker run -it -v $(pwd)/w2v:/w2v -p 8200:8200 ngtd g -i /w2v/index -d 200 -t bolt -p /w2v/kvs.bdb -P 8200
2018-04-17 08:30:23 [INFO]: NGTD GRPC Server Starting ...
gRPCを用いたインデックス作成
ngtdにはビルドコマンドを用意してありますが、ここではgRPC経由でインデックスを作成します。
gRPCのインストールとPythonインターフェースの生成
pipでライブラリとツールをインストールし、ngtd_pb2.pyとngtd_pb2_grpc.pyを生成します。
$ pip install grpcio grpcio-tools
$ curl -O https://raw.githubusercontent.com/yahoojapan/ngtd/master/proto/ngtd.proto
$ python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ngtd.proto
インデックス作成サンプルプログラム
以下のプログラムを実行すると、インデックスが作成されます。
今回のデータの場合は筆者の実行した環境で1500秒ほどかかりました。
データの挿入時にDBロックを取得しなければならないため、Python版と比較すると1.7倍程度遅くなっています。
# -*- coding: utf-8 -*-
import argparse
from gensim.models.keyedvectors import KeyedVectors
import grpc
import numpy as np
from ngtd_pb2 import CreateIndexRequest, Empty, InsertRequest
import ngtd_pb2_grpc
def generator(model):
# generate insert word2vec data
word_vectors = KeyedVectors.load_word2vec_format(model, binary=True)
for word in word_vectors.index2word:
vector = word_vectors[word]
normalized_vector = vector / np.linalg.norm(vector)
word = word.encode('utf8')
yield InsertRequest(vector=normalized_vector.tolist(), id=word)
def insert(host, port, model):
# insert vectors into ngtd
channel = grpc.insecure_channel('{}:{}'.format(host, port))
stub = ngtd_pb2_grpc.NGTDStub(channel)
for _ in stub.StreamInsert(generator(model)): pass
stub.CreateIndex(CreateIndexRequest(pool_size=8))
stub.SaveIndex(Empty())
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-H', '--host', type=str, help='ngtd hostname', default='localhost')
parser.add_argument('-p', '--port', type=int, help='ngtd port', default=8200)
parser.add_argument('-m', '--model', type=str, help='/path/to/model', default='entity_vector/entity_vector.model.bin')
args = parser.parse_args()
insert(args.host, args.port, args.model)
gRPCを用いた検索
gRPCを採用しており言語を問わず利用できるので、今回はnode.jsを使って検索します。
node.jsのインストールは省略します。
gRPCライブラリのインストール
npmでgrpcをインストールします。node.jsはprotoファイルを直接読み込めるので、生成は省略します。
$ npm install grpc
検索サンプルプログラム
const grpc = require('grpc');
const rl = require('readline').createInterface({
input: process.stdin,
output: process.stdout,
prompt: 'search> '
});
const ngtd = grpc.load('/path/to/ngtd.proto').ngtd;
const label = 'search time';
var client = new ngtd.NGTD(process.argv[2], grpc.credentials.createInsecure());
rl.prompt();
rl.on('line', (line) => {
var query = Buffer.from(line.trim());
console.time(label);
client.SearchByID({id: query, size: 11, epsilon: 0.01}, (err, res) => {
console.timeEnd(label);
if (err) {
console.log('Error: %s', err);
} else {
res.result.forEach((r) => {
console.log('%s\t%f', r.id.toString(), r.distance);
});
}
rl.prompt();
});
}).on('close', () => {
console.log("close.");
});
実行結果
同一サーバー間で約2msで検索できました。通信やデータのシリアライズ処理を含んでいることを考えると、高速に検索できていると言えると思います。
$ node search.js localhost:8200
search> [ヤマハ]
search time: 2.224ms
[ヤマハ] 0
[ローランド] 0.588580846786499
[コルグ] 0.6159689426422119
[河合楽器製作所] 0.6263557076454163
ヤマハ 0.6312673687934875
[電子オルガン] 0.6925672292709351
[イーエスピー] 0.7000870108604431
[フェンダー_(楽器メーカー)] 0.733221709728241
[ギブソン_(楽器メーカー)] 0.7335250377655029
[電子ピアノ] 0.7376455068588257
[シンセサイザー] 0.7419003844261169
search>
おわりに
NGTをサーバ化し、インデックスの構築と検索をgRPCで行ってみましたがいかがでしたでしょうか。gRPC以外にHTTPプロトコルもサポートしていますので、ぜひそちらも試してみてください。機械学習のモデリングはPython製のツール、アプリケーションはnode.jsというように状況に応じて適したツール、言語を選択できるので開発に幅が広がると思います。密ベクトル検索でアプリケーションを構築する際にはぜひご検討いただければと思います。
検索技術 森本
こちらの記事のご感想を聞かせください。
- 学びがある
- わかりやすい
- 新しい視点
ご感想ありがとうございました