ナード戦隊データマン

機械学習と自然言語処理についてのブログ

BERTとfaissを組み合わせる

BERTをエンコーダとして使ったらどうなるか検証するために、文をエンコードし、それをfaissへ入れて類似性検索を行います。

そもそもBERTにおける文ベクトルってあるの?

BERT Vector Space shows issues with unknown words

https://github.com/google-research/bert/issues/164

@jacobdevlin-google (bert開発者)

"I'm not sure what these vectors are, since BERT does not generate meaningful sentence vectors. It seems that this is is doing average pooling over the word tokens to get a sentence vector, but we never suggested that this will generate meaningful sentence representations. And even if they are decent representations when fed into a DNN trained for a downstream task, it doesn't mean that they will be meaningful in terms of cosine distance. (Since cosine distance is a linear space where all dimensions are weighted equally)."

訳:"BERTは意味のある文ベクトルを生成しないので、これらのベクトルが何であるのかについて確証はない。 文ベクトルを取得するためのワードトークンに対する平均プーリングをやっているように見えるが、我々はこの方法が意味のある文表現を生成するとは示唆しない。 そして、たとえ下流のタスクのために訓練されたDNNに供給されたときにそれらがまともな表現であったとしても、それらがコサイン距離の観点から意味があるという意味ではない。 (コサイン距離は、すべての次元が等しく重み付けされている線形空間であるため)。"

Features extracted from layer -1 represent sentence embedding for a sentence?

https://github.com/google-research/bert/issues/71

@jacobdevlin-google (bert開発者) "-1 means the last hidden layer. There are 12 or 24 hidden layers, so -1,-2,-3,-4 means 12,11,10,9 (for BERT-Base.) It's extracted for each token. There is not any "sentence embedding" in BERT (the hidden state of the first token is not a good sentence representation). If you want sentence representation that you don't want to train, your best bet would just to be to average all the final hidden layers of all of the tokens in the sentence (or second-to-last hidden layers, i.e., -2, would be better)."

訳: "-1は最終層を意味します。隠れ層が12か24あるので、-1,-2,-3,-4は12,11,10,9層目です。それぞれのトークンに対して展開されます。BERTには「文エンベディング」のようなものはありません(最初のトークンの隠れ層の状態は良い分表現とは言えません)。もし、文表現を獲得したい場合、最も良い方法となりそうなのは、最終層のすべてのトークンの平均です。(または、最後から2番目の層も良くなるかもしれません。)"

実行

文リストをエンコード

エンコードスクリプトは以下で共有します。 https://github.com/sugiyamath/bert_and_faiss_experiments/blob/master/bert/extract_features.py

なお、このエンコードスクリプトを使うより、bert-as-serviceを使ったほうが高速だと思います。エンコードのやり方は省略します。

faissにぶち込む

1ファイル1文としてエンコードしたjsonファイルを読み込み、最終層の各トークンベクトルの平均をfaissへ格納します。

import os
import json
import numpy as np
from tqdm import tqdm
import faiss

files = list(map(lambda x: os.path.join(".",x), os.scandir("enc_outputs/")))
files[0]
data = []

for file in tqdm(files):
    with open(file) as f:
        tmp = json.load(f)
        tmp = np.mean([d['layers'][0]['values'] for d in tmp['features']], axis=0)
        data.append(tmp)

index = faiss.IndexFlatL2(data[0].shape[0])
index.add(np.array(data, dtype=np.float32))
faiss.write_index(index, "faiss_bert.faiss")

検索する

from gensim.models.doc2vec import Doc2Vec
import numpy as np
import faiss
import sys

if __name__ == "__main__":
    doc_num = int(sys.argv[1])
        
    with open("postlist.txt") as f:
        data = [line.strip() for line in f]

    print("[target doc:", data[doc_num], "]\n")
    index = faiss.read_index("./faiss_bert.faiss")
    vec = index.reconstruct(doc_num)
    D,I = index.search(np.array([vec]), k=30)
    for i in I[0]:
        print(data[i])

[実行方法]

python <スクリプト名> <類似性検索したいドキュメント番号>

実行結果 (1行1投稿)

タグを投稿する形式のSNSを想定します。1投稿に複数タグが投稿できます。

target doc は類似性検索する対象のドキュメントです。このドキュメントに似た投稿を検索します。

python bert_faiss_search.py 20000
[target doc: 極楽鳥 横浜 鶏ラーメン えりな増し 太麺と細麺 ]

極楽鳥 横浜 鶏ラーメン えりな増し 太麺と細麺
煮干し中華 らーめん 麺や城 青森
フェスめし 小籠包 担々麺 餃子 マグロ寿司 麻婆焼鳥
麺屋宗 金色香味塩らぁ麺 仁久屋 松阪牛肉汁餃子 麺や庄の 燻し焼きチャーシューメン 神龍 神龍餃子 小豆島ラーメンHISHIO 醤 丸満 福包み揚げ焼き餃子 世界が麺で満ちる時 名古屋コーチン黄金醤油ラーメン 浜太郎 浜松餃子
つけ麺 らーめん ラーメン 魚介豚骨
武骨屋 武骨屋恵比寿 恵比寿家系ラーメン 恵比寿ラーメン 麺類 中太麺 現場飯 コスパ飯 プチプラ飯 やじー 矢嶋巧 ラーメン 麺スタグラム
麺肆秀膽 塩ラーメン 塩つけ麺 大盛り
京都北白川ラーメン魁力屋 ラーメン 餃子 ランチ 味玉追加 辛子味噌
麺屋又兵衛 名古屋 塩ラーメン チャーシュー 鰹節 らーめん 中華そば ラーメン 麺スタグラム ラーメンインスタグラマー ラーメン好きと繋がりたい ラーメン女子
らーめん ラーメン 豚骨ラーメン 豚骨 麺 ハリガネ チャーシュー もやし 替え玉 ラーメン好き 今年初 麺スタグラム めし ランチ おいしかった
味噌ラーメン 麺 麺スタグラム 拉麺 ラーメン ラーメン部 ramen noodles スープ ルーロー飯 魯肉飯 丼 大阪 コッチネッラ coccinella 美味しい
華丸ラーメン らーめん 焼干しらーめん 醤油ラーメン 青森
つけ麺 ラーメン 麺野郎
うどん 丸亀製麺 牡蠣づくし玉子あんかけ
麺者すぐれ 白つけ麺 三種の肉 穴子天ぷら
カネキッチンヌードル 東長崎 ラーメン らーめん 中華そば 醤油ラーメン らぁ麺 麺活 ラーメンいんすたグラマー ラーメン大好き 麺すたぐらむ ラーメン好き女子
麺スタグラム ラーメン 江古田 ヤマン らはめん 根っからくいしん坊
20190203 品川やきいもテラス 塩やきいも 品達 麺屋翔 塩ラーメン 東京スカイツリー キングダムハーツ 光と闇の塔 星乃珈琲 パンケーキ
京都 クックドゥー 担々麺 ピーマン 稲庭うどん
麺屋三郎 札幌味噌 限定 塩らぅめんリベンジ
関西 京都 祇園 同伴 ディナー 大好物 和食 割烹 ミシュラン2つ星 三玄茶 舞扇 にしむら 鉄板割烹 一道 中華 にしぶち飯店 白椀竹快樓 おばんざい イタリアン オバタリアン ご馳走様でした
つけ麺 ラーメン 麺類好き うまし
めん奏心 特製煮干しそば ラーメン 麺navi東海 めん奏心
簡単 茶碗蒸し 作った ぷるぷる どん兵衛 カップ麺 カップラーメン ラーメン 拉麺 広島県 広島 尾道 尾道ラーメン しんたく 餃子 海鮮巻 料理 ドラえもん お箸 富山 富山県 ディズニー ツムツム お皿
ラーメン らーめん 拉麺 ラーメン屋 とんこつラーメン 豚骨ラーメン とんこつ 麺 ハリガネ 紅生姜 たっぷり めし 麺スタグラム ランチ おいしかった
ラーメン らーめん 拉麺 noodles 塩 ラーメン花月嵐
天下一品 ラーメン こってりネギ多め チャーシュー麺
麺散 表参道 究極の釜揚げうどん バリカタぶっかけスタイル 黒木啓司
純手打ち麺と未来 麺と未来 下北沢 ラーメン ラーメン部 醤油ラーメン 東京ラーメン 東京グルメ
1人飯 女性が入りやすいラーメン屋 恵比寿ラーメン 恵比寿ランチ 神座 ラーメン 中華そば しょうゆラーメン らーめん ラーメン女子 麺スタグラム らーめん部 ラー活 麺活 ramen

いい感じではないでしょうか。

参考