Word2Vecを使って類似Webサイトを発見する

# はじめに
Word2Vecを使って、VALUESの保有するWeb行動ログデータから類似のWebサイトを発見する
というサービスを作ってみました。

前回の投稿では、類似検索キーワードを取り扱いました。
[Word2Vecを使って類似検索キーワードを発見する](https://qiita.com/zak_y/items/707a3bddaf2db3d98235)

Word2Vecはもともと単語をベクトル化するための手法ですが、その魅力に触れるなかで
その手法は特に言葉でなくとも、前後の配置に意味のあるものであればなんでも応用できるのでは？
と気付きました。
今回はそれをWebサイトの閲覧行動に適用し、一般ユーザーの行動から類似Webサイトを見てみよう！
というお話です。

# Word2Vecのモデル作成

## コーパスの用意

元データとして、VALUESのWeb行動ログデータからユーザー一人ひとりの閲覧サイトを時系列に沿って抽出します。
連続する同じサイトは1つに集約し、また1時間以上Web行動があいた場合は別行動とするなど、
いくつか調整をした上で最終的には下記のようなデータを用意します。

```text:site.txt
www.facebook.com weather.yahoo.co.jp map.yahoo.co.jp rurubu.travel www.ana.co.jp travel.rakuten.co.jp www.nhk.or.jp
twitter.com finance.yahoo.co.jp twitter.com jp.wsj.com
www.persol-career.co.jp miidas.jp
shopping.yahoo.co.jp zozo.jp www.mercari.com
：
```
※1行がユーザーひとりの一連のWeb行動にあたります。

スペースで区切られたサイト名の羅列が、まるで文章のように見えませんか？

## モデルの作成
gensimというPythonのライブラリを使用します。

```python:train.py
from gensim.models import word2vec
import logging

logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)

sentences = word2vec.LineSentence('site.txt')
model = word2vec.Word2Vec(sentences,
                          sg=1,
                          size=200,
                          min_count=100,
                          sample=0)
model.save('site.model')
```

## 類似Webサイトの発見
作成したモデルを使って、次のようなコードで類似Webサイトを確認できます。

```python:most_similar.py
from gensim.models import word2vec

model = word2vec.Word2Vec.load('site.model')

results = model.most_similar(positive=['qiita.com'], topn=10)

for result in results:
	print(result[0] + "\t" + "%.3f" % result[0])
```
※上記例では「qiita.com」の類似Webサイトを出力しています。

結果は、下記のようになりました。

```text:
$python3 most_similar.py
teratail.com	0.892
*****	0.883
ja.stackoverflow.com	0.860
*****	0.859
www.lifewithpython.com	0.855
dqn.sakusakutto.jp	0.853
*****	0.848
lealog.hateblo.jp	0.844
*****	0.842
www.task-notes.com	0.842
```
※入力した「qiita.com」と類似度が高いWebサイトが出力されています。
※（トップページにアクセスできないサイトは念のため*で伏せています）

# サービスの起動
作成したモデルのファイルサイズが大きい場合、1リクエストごとにモデルファイルを読み込んでいると
オーバーヘッドが大きくなります。
今回、Pythonの比較的簡易なWebフレームワークであるBottleを使ってWebサービス化し、
モデルは起動時に読み込むことで高速にレスポンスを返すことにしました。
サンプルのコードは下記の通りです。

```python:site_daemon.py
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from bottledaemon import daemon_run
from bottle import route, HTTPResponse

@route("/site/")
@route("/site/<target_site>")
def site(target_site=None):
    out = {'success': True, 'errcode': 0, 'errmessage': '', 'result': {'similar_list': []}}
    tartget_list = target_site.split(' ')

    results = model.most_similar(positive=tartget_list, topn=100)
    similar_list = []
    for result in results:
        record = {'site': result[0], 'score': '%.3f' % result[1]}
        similar_list.append(record)
    out['result']['similar_list'] = similar_list

    jsonstr = json.dumps(out, ensure_ascii=False)
    response = HTTPResponse(status=200, body=jsonstr)
    response.set_header("Content-Type", "application/json")
    return response

if __name__ == "__main__":
    import sys
    if sys.argv[1] == 'start':
        import json
        from gensim.models import word2vec
        model = word2vec.Word2Vec.load('site.model')

    daemon_run(host='0.0.0.0', port=5000)
```

# 何に使える？
マーケティングにおける3C分析などで、Competitor(競合)を調べたいってことよくありますよね。
でも、クライアントにヒアリングしても競合がどこだかよくわかっていない...ってケースも稀ではありません。
そんな時、類似Webサイトでさくっと調べてみて
「一般消費者のWeb行動から分析してみたところ、類似Webサイトとしてこれらのサイトが見つかりました。
御社の競合（または参考）になるサイトではありませんか？」
と会話することで、お、わかってるな、感を出すことができます。

# おわりに
今回、Word2Vecの仕組みを利用して言葉ではないものの類似度を出してみました。
この手法自体、わりと多方面に活用できそうなヒントが得られた予感がします。
レコメンド系とも相性がよさそうなので、またどこかの機会で試したみたいと思います。