Amazon ElasticsearchServiceのK-NNを触ってみる

# はじめに
AWSよりAmazon ElasticSearchServiceでk-NN検索ができるという発表があったのでちょっとだけ試してみました。
https://aws.amazon.com/jp/about-aws/whats-new/2020/03/build-k-nearest-neighbor-similarity-search-engine-with-amazon-elasticsearch/
なお、opendistro版にはすでに実装されていた機能だそうです。(ちなみに私、ElasticSearchは勉強中なのであまり詳しくないです)

# 環境構築
ElasticSearch 7.1以降に対応ということです。手元に6.8までしかなかったので、7.1を新たに作りました。ドメイン構築時にk-NN専用の設定は必要ないので、細かいところは割愛します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/a077e0aa-7bee-5f81-e8fb-51db2d545cab.png)

# 枠を作る
kibanaのDevToolsに移動して、インデックスとマッピングを同時に作ります。
新しく knn-vector というデータ型ができています。
インデックスは "index.knn" : true というパラメータを指定して作る必要があります。
マッピングは2次元ベクトルと、4次元ベクトルを作ります。

```
PUT my-index
{
  "settings": {
    "index.knn": true
  },
  "mappings": {
    "properties": {
      "my_vector1": {
        "type": "knn_vector",
        "dimension": 2
      },
      "my_vector2": {
        "type": "knn_vector",
        "dimension": 4
      }
    }
  }
}
```

結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/b44de67b-497b-73ed-0607-a90f032e8680.png)

## インデックスの確認
AWSマネジメントコンソールのElasticSearchに移動して、マッピングがちゃんとknn_vectorとして作成されていることを確認します。(クエリで確認してもいいのですがこっちのほうが楽なので)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/2a2cd782-56ba-2937-dc52-0b1f211607e6.png)


# データを入れる
2次元と4次元のデータをそれぞれ入れます。

```
POST _bulk
{ "index": { "_index": "my-index", "_id": "1" } }
{ "my_vector1": [1.5, 2.5], "price": 12.2 }
{ "index": { "_index": "my-index", "_id": "2" } }
{ "my_vector1": [2.5, 3.5], "price": 7.1 }
{ "index": { "_index": "my-index", "_id": "3" } }
{ "my_vector1": [3.5, 4.5], "price": 12.9 }
{ "index": { "_index": "my-index", "_id": "4" } }
{ "my_vector1": [5.5, 6.5], "price": 1.2 }
{ "index": { "_index": "my-index", "_id": "5" } }
{ "my_vector1": [4.5, 5.5], "price": 3.7 }
{ "index": { "_index": "my-index", "_id": "6" } }
{ "my_vector2": [1.5, 5.5, 4.5, 6.4], "price": 10.3 }
{ "index": { "_index": "my-index", "_id": "7" } }
{ "my_vector2": [2.5, 3.5, 5.6, 6.7], "price": 5.5 }
{ "index": { "_index": "my-index", "_id": "8" } }
{ "my_vector2": [4.5, 5.5, 6.7, 3.7], "price": 4.4 }
{ "index": { "_index": "my-index", "_id": "9" } }
{ "my_vector2": [1.5, 5.5, 4.5, 6.4], "price": 8.9 }
```

結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/59106985-2d89-e938-791d-4a35e59d8e6e.png)

# 検索する
4次元データに対して、特定のポイントを指定します。
近しいデータを2個探したいのでkを2に設定しています。
無事、2個のデータが返ってきました。

```
GET my-index/_search
{
  "size": 2,
  "query": {
    "knn": {
      "my_vector2": {
        "vector": [2, 3, 5, 6],
        "k": 2
      }
    }
  }
}
```

結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/0566898d-2df7-c75c-679a-889b7cc2712f.png)

# kibanaのVisualizeについて
kibanaで簡単に可視化できたらいいなと思ったんですが、単純にインデックスパターンを作成しようとしたところ、my_vectorがknn-vector型として認識されずに、unknownになってしまったので無理そうです。
裏でt-SNEなんかを使って2次元表示してくれると面白そうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/538058/01002495-7b1e-6aa7-8970-ea5813591f7e.png)

# 感想
そもそもベクトル化されたデータが必要です。文章であれば事前にSageMakerなどでword2vecをかけるというのも手ですけど、であればJupyterNotebookでエンコーディングから可視化までやってしまえばいいという気もします。
分析インターフェースとしてElasticSearchに慣れている人むけによさげなんでしょうか。
なにぶん私はElasticSearchにさほど慣れてないので、、よい使い方があればどなたか教えてくださいなー。