中年engineerの独り言 - crumbjp

LinuxとApacheの憂鬱

SCDVが出来るまで

SCDVとは?

数多ある文章を評価する手法の中で速くて安くて旨いと噂されているもの

趣旨

実験的な実装やその結果は色々手に入るが実際にある程度の規模のサービスに使うとなると、どんな苦労があるのかを紹介しようと思う。 コードレベルの詳細な解説は他所に譲る。

大本

SCDV : Sparse Composite Document Vectors using soft clustering over distributional representations - ACL Anthology

解説サイト

丁寧に解説してくれているサイトが沢山あるので詳しくはそちらを見て欲しい。

手順(詳しくは上記のリンクを参照)

1.日本語コーパスを作る 2.Word2VecとIDFを算出 3.WrodVectorをGMMでトピッククラスターに分類 4.WordTopickVectorを得る 5.実際の記事にWordTopicVectorを適用し、文章ベクトルを得る。

0.ベクトル精度の評価方法

最終的にユーザ行動で判断する。

関連性の高いものを出すべき所で、関連性が高い(とシステムが判断した)ものを出した場合、ユーザーのリアクションに現れる。その数字を見て判断する。

面白いのは高関連度帯と低関連度帯で全く逆の成績(ユーザの反応)を得る場合があった。

大まかに見ると(高関連度帯)非常に上手く行っているモデルでも細かく見ていくと齟齬が多いということで サービスとしては文章ベクトルはあらゆる箇所で応用して使って行きたいのでなるべく細かいところまで精度を求めたい。

1.日本語コーパスをつくる

一般的なWebメディアの記事は、表記の揺れや偏り、口語調、中には非論理的な記事も含まれる場合があるので それを嫌って試しに日本語WikiのみでSCDVまで作ってみたが全く使い物にならなかった。

表記ゆれや、WEB特有の無効なワードに付き合って行く必要がある。

無効ワード

WEBでは文章の意味に関与しないワードが多数あり、これはコーパスを作る段階でも、実際文章を評価する段階でも除外しなければならない。 またascii や半角カナなども全角大文字正規化して扱っている。

ページ構造にまつわるもの

  • タイトル、トピック、コンテンツ
  • 次ページ、前ページ、NEXT
  • トップページ、TOP
  • NEW、新着

WEBメディア語

  • オススメ、おススメ
  • 大人気、話題、必見
  • あなた(に、へ)

EC語

  • サイズ、幅、奥行き、高さ、重量、KG
  • 価格、通常価格、特別価格、本体価格、販売価格
  • YAHOO、楽天市場、AMAZON

イベント系?

  • オリジナル、プレゼント、キャンペーン、期間中
  • 開催日時、TEL

お腹いっぱいすぎる。。。

まだまだ数百あるが根本的に果がない。 先に述べた、高関連度帯ではある程度無視できるが、細かい精度を求めると記事元メディアや記者単位にの適度な頻出度になる単語も多くIDFでは影響を取り切れない。

地道に確定データを作って行けばDeepLearnで検出できるとは思う。

データ量

日本語Wikiと、我々の広告サービスでタグが埋まっているサイトの記事データからコーパスを作るが

wikiが200万記事6GB、サービス側のデータが260万記事14GB程度となる。

サービス用tokenizer

最終的にサービスで使うので、サービス用のtokenizerで処理する。

mecab-ipadic-neologd は必須アイテム。

nodejsからmecabを使う場合、mecabコマンドを叩いて(fork)いてるライブラリばかりで使い物にならないので ネイティブバインディングを自作している。 mecab-gyp - npm

  • 正規化
  • 名詞、動詞のみを抽出。(動詞は含めた方が若干良さそう)
  • 非自立、数、形容動詞語幹、副詞可能などと、上記無効ワードを除外
  • さらに特殊条件(ひらがな1文字など)で色々除外
  • これでも抜けてくる除外したいワードを目検で取る。(なる、ある、ほか、等)

最終的に、10億ワード(900万ユニーク)10GBのコーパスが出来た。

他にも色々込み入ったロジックを実装している事もあり、100並列で1〜2時間かかる。

2.Word2VecとIDFを算出

隣のドキュメントと混ざらないようにwindowサイズ分のパディングを入れておく。

(あまりコレやっている人が居なそうなのはなぜだろう・・・)

gensimで一発。96コアのサーバーで130並列位かけると100%使ってくれる。

これも数時間かかる。

頻出度により約64万単語になった。

3.WrodVectorをGMMでトピッククラスターに分類

sklearnのGaussianMixture で丸一日!!

とにかく時間がかかる。並列化も望めないので悩みのタネ。

しかしここで最終的な文章ベクトルの精度の全てが決まるので何度も実行する事になる。

更にここで視覚的に精度を検証する術がない。

つまり最後まで行ってからココに戻ってくる。。つらい・・・

t-SENによる視覚化

f:id:hiroppon:20210302212900p:plainf:id:hiroppon:20210302213023p:plainf:id:hiroppon:20210302213049p:plainf:id:hiroppon:20210302213117p:plain

評価不能・・・

Word2Vecってワード数が少なくてもこうなるので評価の仕方が本当にわからない。

トピックベクトルまで行っちゃうと今度は色分けする意味が無くなるし困ったものだ。。

4.WordTopickVectorを得る

64万単語、200次元(word-vector)、400クラスターともなると使用メモリーが数十GBを超える。 処理途中も合わせると更に必要になる。単にnumpyで素朴に

(words * clusterWeights) * idf

って訳には行かない。

単語はファイルに落としておいてストリーム処理する。そこまでやるなら並列化は簡単。

単にベクトル演算なので詳細は略。

次元の謎

word2vecは200 〜 300次元、clusterは 50、60 〜 という話だが、この辺はどうにも体感値が違うようだ。

word2vecでは、200次元と300次元の違いが僅かで、cluster は 大きければ大きい程よさそう。 扱ってるコンテンツの幅が広いのが原因だろうか。。

word2vecを400次元以上にするとまた変わるのかもしれないが、試すのはいつになる事やら・・・

5.実際の記事にWordTopicVectorを適用し、文章ベクトルを得る。

ランタイムで記事データを分類するとなると得たベクトルはその最大精度では使えない。 8万次元ものベクトルをユーザアクセスの度に扱うのは重すぎるからだ。

wordTopicVector 自体を1% ~ 3% 程度にスパース化しておく。これで実効1000次元。

これを文章に適用していくと、文章ベクトルが数万次元になってしまうので、それもまた1%程度にスパース化(&正規化)する。

これでドキュメント同士の比較は数百回の掛け算処理になる。

あとドキュメント中に同じ単語が複数回現れる時の扱いでかなり精度が変わるのでここは工夫が必要。

セイコーの腕時計の記事シチズンの腕時計の記事では、各々セイコーシチズンを連呼する(しかもIDFも大きい)。上手く扱わないと文章ベクトル自体がセイコーシチズンになり、記事の共通部である腕時計との距離がどんどん離れて行ってしまう。

Webの記事は表現が節操がないといつも思う

学問的にはともかくサービスとしてはオーダーメイドチューンすべき所だ。

データストア

ここまでデータを圧縮してもランタイムで使うならば毎回DBから引く訳にも行かずメモリーに貯める訳にも行かない。

そもそもメモリーに全ては入らず、一部であってもランタイム用のワーカープロセス1つずつに持つのは非効率(サーバーメモリが潰れる)

じゃあredisに入れる?

f:id:hiroppon:20210303004709p:plain:w300

即死!!

wordTopicVector も文章ベクトルも置き場に困る。 100GBクラスで即応性があり格安で使えるデータストアが必要!!!

これが解決しないなら更に次元を落とすしかない・・・

成果

実際のサービスでの配信での成績の一例

ユーザーは今見ている記事と関係ある記事ほどクリックする確率が高いと仮定すると文章ベクトルの類似度とクリック率に一定の関係が現れるはずだ。 ElasticSearchのmltスコアによる分析と比較して検証する。

よくできました 💮

f:id:hiroppon:20210303110318p:plain:w400

ElasticSearchと同程度のクリック数を切り取りながらもCTRの高い層を抽出出来ている。

これはSCDVによる類似度分析の結果をユーザ行動が支持したといえる。

細かい部分も大丈夫そう💮

f:id:hiroppon:20210303110308p:plain:w400

ElasticSearchのmlt scoreが高いゾーンでもSCDVの分解能が機能しているともいえる。

もっとがんばろう💢

f:id:hiroppon:20210303110313p:plain:w400

ElasticSearch よりも大きくクリックを削っているにも関わらずCTRも低くなってしまっており完敗。

色々見ていくと、ある特定のジャンルを苦手にしているようにも見え、GMMの段階でおかしくしちゃっているトピックがあるんじゃないかと考えている。

まとめ

今回、SCDVによる文章評価を曲がりなりにも本番投入まで持っていき、ある程度の有用性や可能性を見出すことが出来た。

ただ、精度面、性能面での大きな課題もあり、しかもバーターでバランスを取るとなると安定させるためには未だ相当の労力が必要そうだ。

特に重要なポイント

  • Tokenizer 全ての入り口にして、精度に最大寄与する部分。特に細かい部分の精度を求めた時ほどココの撃ち漏らしの影響が大きくなる。
  • 次元 WordVector と ClassSize のバランス。最終的な性能まで含めて考慮する。
  • GMM 時間がかかる上にWordTopicVectorの精度に大きく関わる。単体で評価しにくいのも厄介。

この辺、検討している人がいたら参考にして頂けたら。また情報交換などぜひぜひ。