BertでECサイトの商品類似度を計算する

## 概要
商品の説明文とタイトルをBertを使ってベクトル化して、商品間の類似性を表現できるかテストする。

## 準備
以下colab環境で実装します。

### アカウント認証

```python
from google.colab import auth
auth.authenticate_user()
```

### tensorflowのバージョンの変更
今回使用するソースコードに合わせてcolabにインストールされているtensorflowのバージョンを変更します。

```python 
#tensorflowを1x系に戻します
!pip uninstall -y tensorflow
!pip install tensorflow==1.15.0
```


### 入力データとBertのソースコードを用意
```python
#必要なファイルを用意する
#こちらの記事を元にjumanppを使用できるようにコードを修正 https://dev.classmethod.jp/articles/bert-text-embedding/
!gsutil cp -r gs://${your_gcs_path}/bert ./
#日本語wikiで学習したモデルを取得 http://nlp.ist.i.kyoto-u.ac.jp/index.php?BERT%E6%97%A5%E6%9C%AC%E8%AA%9EPretrained%E3%83%A2%E3%83%87%E3%83%AB#k1aa6ee3
!gsutil cp -r gs://${your_gcs_path}/Japanese_L-12_H-768_A-12_E-30_BPE ./
#入力データを用意 (今回はamazonの電子書籍のタイトル+商品説明文)
!gsutil cp -r gs://${your_gcs_path}/inputdata ./
```

###JUMAN++をインストール

```python

# 以下を参照してjumanppをインストールする
#https://github.com/ku-nlp/jumanpp
!wget https://github.com/ku-nlp/jumanpp/releases/download/v2.0.0-rc3/jumanpp-2.0.0-rc3.tar.xz
!sudo apt install cmake
!tar xJvf jumanpp-2.0.0-rc3.tar.xz
%cd jumanpp-2.0.0-rc3/
!mkdir bld
%cd bld
!cmake ..
!sudo make install
```

###環境変数を設定

```python
%env MODEL=Japanese_L-12_H-768_A-12_E-30_BPE
%env OUTPUT=output
%env INPUT=inputdata
```

##出力ディレクトリを作成
```python
%cd ../
!mkdir output
```

## Bertで商品をベクトル化

```python
# item.txt : 商品タイトル + 商品説明
!python ./bert/extract_features.py  --input_file=${INPUT}/item.txt  --output_file=${OUTPUT}/output.jsonl  --vocab_file=${MODEL}/vocab.txt  --bert_config_file=${MODEL}/bert_config.json  --init_checkpoint=${MODEL}/bert_model.ckpt  --do_lower_case False  --layers -2
```

## 商品間の距離行列を作成

### Bertの出力結果からベクトル情報を取得
```python
import json
import numpy as np
import pandas as pd
 
# 参照するレイヤーを指定する
TARGET_LAYER = -2
 
# 参照するトークンを指定する
SENTENCE_EMBEDDING_TOKEN = '[CLS]'

file ="./output/output.jsonl"

with open(file, 'r') as f:
    output_jsons = f.readlines()
 
embedding_list = []
for output_json in output_jsons:
    output = json.loads(output_json)
    for feature in output['features']:
        if feature['token'] != SENTENCE_EMBEDDING_TOKEN: continue
        for layer in feature['layers']:
            if layer['index'] != TARGET_LAYER: continue
            embedding_list.append(layer['values'])
 
np.savetxt('output.tsv', embedding_list, delimiter='\t')
```

### 商品タイトル + 商品ベクトルの行列を作成

```python
#商品のベクトル
output = pd.read_csv("output.tsv", sep='\t',header=None)
#商品のタイトル
title = pd.read_csv("inputdata/title.txt", sep='\t',header=None, names=["title"])
#商品タイトルとベクトルを結合
res = pd.concat([title, output], axis=1)
# データを確認
res.head(3)
```
![スクリーンショ2020.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67556/ccef2d57-9304-1944-5b63-bca301794f19.png)

### 商品間の距離を計算
```python
from scipy.spatial import distance
# ベクトルの作成
M = [a for a in res.iloc[:,2:769].values] 
# 総当たりに類似度を計算(こちらを参照https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.cdist.html#scipy.spatial.distance.cdist)
dist_M = distance.cdist(M, M, metric='cosine')
```

### 商品間の距離行列を作成
```python
dist_matrix = pd.DataFrame(dist_M,columns=res['title'],index=res['title'])
```

### ヒートマップで可視化
pythonで日本語の描画が難しそうだったのでスプレッドシートで作成しました。
専門書か漫画かなど大まかなジャンルには別れているようです。
日本語wikiで学習されたモデルを使っていて、商品データでモデルの学習をしていないため、十分にデータを集めて学習を行うことでより精度は上がりそうです。
![2020-04-20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67556/8d2fec28-7769-8ee8-27ee-b5475c3e7748.png)