Slide 1

Slide 1 text

Slackの絵文字サジェストを機械 学習でリバースエンジニアリング

Slide 2

Slide 2 text

機械学習って、人間のつけたものだったり、どっ かのデータベースにあったり、 “ラベル”を学習す るというイメージを持つことが多いけれど、 どこか に実装があるアルゴリズム そのものを学習するこ とって... もちろんできらぁ def function(query, target): …… リバースエンジニアリング 人間の模倣 法則の学習 港区

Slide 3

Slide 3 text

minorと打つと、badminton_racquet_and_shuttlecockがサ ジェストされる slackの絵文字アルゴリズムって意外と不思議な挙 動をする

Slide 4

Slide 4 text

slackの絵文字サジェストのアルゴリズムを学習してみよう

Slide 5

Slide 5 text

問題設計

Slide 6

Slide 6 text

学習データ作成 length = 3 candidate_characters = string.ascii_lowercase + string.digits + ' ' for query_characters in itertools.combinations_with_replacement(candidate_characters, length): query = ''.join(query_characters) editor = browser.find_element(By.CLASS_NAME, 'ql-editor') editor.send_keys(query) l = browser.find_element_by_id('chat_input_tab_ui') candidates = l.find_elements_by_tag_name('li') result = [candidate.text for candidate in candidates] f.write(json.dumps(dict(key=query, result=result))+'\n') 3文字までの英数字の組み合わせの全通り seleniumで直接入力し、サジェスト結果をそのまま保存 {'aaa': ['kaaba'], 'aab': ['kaaba', 'tanabata_tree'], 'aac': ['abacus', 'parachute'], 'aad': ['green_salad'], 'aae': [], 'aaf': ['falafel'], 'aag': [],

Slide 7

Slide 7 text

アルゴリズムの一致度の評価はどうやる? query -> answerが1:1ではないので、precision@k, recall@kではなく、平均順位を評価指標とする {'aaa': ['kaaba'], 'aab': ['kaaba', 'tanabata_tree'], 'aac': ['abacus', 'parachute'], 'aad': ['green_salad'], 'aae': [], 'aaf': ['falafel'], 'aag': [], mean(1,2,3,5) = 2.75 ● 最小値は2.5なので、0.25分間違えてる ○ oboに対してtoolboxが不正解なの なんで...謎アルゴリズム これを評価セットのkey全てで順位を取る (最小値)

Slide 8

Slide 8 text

一旦それっぽいアルゴリズムを考えてみる 理論最小値: 7.70 def distance(query, target): if not set(query).issubset(target): return 99999 else: return target.find(query[0]) 文字列がサブセットになっているか 59.65 マッチ部の最小長さ+出現位置 def distance2(query, target): if not set(query).issubset(target): return 9999999999 else: pattern = '.*?'.join(query) matches = re.findall(pattern, target) if not matches: return 999999999 else: return min(len(found) for found in matches)*100+target.find(query[0]) 17.29 def distance4(query, target): if not set(query).issubset(target): return 9999999999 else: pattern = '.*?'.join(query) matches = re.findall(pattern, target) if not matches: return 999999999 else: return min(len(found) for found in matches)*1000+len(target)*10+target.find( matches[0]) マッチ部の最小長さ+文字列長+出現 位置 14.94

Slide 9

Slide 9 text

識別器編

Slide 10

Slide 10 text

学習できる形に落とす やりたいこと: actualがTrueのものを上位に集める “obo/snowboarder” “obo/horse” 9.998831e-01 7.690672e-12 なるべく1に近いスコア な るべく0に近いスコア になるように学習 モデル (LSTM, Transformer) ● key/queryと、区切り文字を適当にきめて文字列で入れることで二変数・三変数関数を雑に表現できるの はNLPのおもしろいところ ○ もちろん別々にエンコードして明確に 2変数にしてもいいですが ● 今回は1/0を当てる問題にしているが、ランキング学習をするという手もある ○ snowboarderはhorseより順位が上である、というのを学習させる ○ 特に上位の並びを当てるのはこちらの方がよくなるはず

Slide 11

Slide 11 text

結果: SoTA達成、勝利 [ヒューリスティック] マッチ部の最小長さ+文字列長+出現位置 14.94 (+7.24) [学習] LSTM, 2layer, 100D 10.49 (+2.79) 理論最小値 7.70

Slide 12

Slide 12 text

どういうものを間違える? modelワースト “7”のモデル予測値 学習の方が大外ししない 意味的に似てるもの”8”を捉え ちゃってる!なんで!!

Slide 13

Slide 13 text

さらなる高みへ: 速度改善 keyが変わるごとに、全てのcandidateと の組み合わせを実行しなきゃ行けない、 手元のMacだと1891絵文字の探索で平 均1秒、線形なので会社の全絵文字 10000だと5,6秒かかりそう “obo/snowboarder” “obo/horse” 9.998831e-01 7.690672e-12 モデル (LSTM, Transformer) for key in tqdm(keys_test): for candidate in more_itertools.chunked(candidates, n=100): xs = key + '/' + candidate pred = torch.nn.Sigmoid()(model(xs)).to('cpu').numpy()

Slide 14

Slide 14 text

さらなる高みへ: 速度改善 “obo” “horse” 9.998831e-01 7.690672e-12 QueryEncoder モデル (LSTM, Transformer) TargetEncoder モデル (LSTM, Transformer) “snowboarder” 内積 なるべく1に近いスコア な るべく0に近いスコア になるように学習 target_feature = model.model2(candidates).to('cpu').numpy() #事前計算 query_feature = model.model1(keys_test).to('cpu').numpy() score=query_feature.dot(target_feature.T) ● targetは事前にエンコードしておけるので、検索時にはクエリのエンコー ドと行列積1回: 平均0.018s ● エンコード以外はベクトルの内積だけなので、例えば ESなど汎用的な 検索エンジンを利用可能 for文が消える

Slide 15

Slide 15 text

“obo” “horse” 9.998831e-01 7.690672e-12 QueryEncoder モデル (LSTM, Transformer) TargetEncoder モデル (LSTM, Transformer) “snowboarder” 内積

Slide 16

Slide 16 text

結果 平均順位 時間 [ヒューリスティック] マッチ部の最小長さ+文字列長+ 出現位置 14.94 (+7.24) 0.008s [学習] TwinModel 46.01(+) 0.018s [学習] LSTM, 2layer, 100D 10.49 (+2.79) 1s 理論最小値 7.70

Slide 17

Slide 17 text

どうして難しいのか? obo/snowboarder o.*?b.*?o match: owbo obo o.*?b.*?o ???? snowboarder 難1: 内積で表現できる形 式にエンコード 難2: どんなクエリにも対 応可能なエンコード (例えば)1層目でクエリを 組み立てて2層目でマッチ すればOK

Slide 18

Slide 18 text

まとめ ● 機械学習でリバースエンジニアリングはできる ● 頑張れば速度も実用レベルまで詰められる ● 今回は速度と精度がトレードオフだったけど、もう少し頑張れば両取りもできるかな ○ もう少し学習は進められそう ○ Twinモデルで上位100個に絞ってから結合モデルで for loopというのも定石