糞糞糞ネット弁慶

読んだ論文についてメモを書きます.趣味の話は http://repose.hatenablog.com

Deep Text Classification Can be Fooled (Preprint) 読んだ

Deep Text Classification Can be Fooled
入力を少し変化させるだけで機械学習によるモデルの推定結果を変えてしまう技術は Adversarial example と呼ばれている.
よく見るのは画像を用いた例だけど,それが文書分類でもできないかと思い探したところ,まずこれがでてきたので読む.

画像一般についてはこちらが詳しい.これを読むまで Deep Learning 特有の問題と思っていたが,実際はそうではないらしい.
はじめてのAdversarial Example

前提

文書 を入力として,その文書のクラス を推定するモデル があるとする.
そこで, に手を加えた によってモデルの出力を任意のクラス に誤分類 させたい.
画像と違うのは,入力が文書であるため,人間の目で見ても不自然でないように変更しなければならないということ.

手法

基本的には Explaining and Harnessing Adversarial Examples (FGSM) で提案されているように,コスト関数の勾配を用いる.
分類モデル Character-level Convolutional Networks for Text Classification で提案されている Character-level CNN.
文書の変更は挿入,修正,削除の3つの方法を適用する.それぞれ説明する.

挿入

各学習データ と目的のクラス および損失関数 に対して,勾配 を計算することで,各次元,つまりは各単語に対して勾配が得られる.
各学習データにおいてこの勾配が大きい単語上位100件を得,それを全学習データで数え上げることで, Hot Training Phrases (HTPs) を得る.
あとは HTP を挿入していくだけで誤分類できる.例えば Company 99.7% と予測していた文書に historic という単語を加えるだけで Building 88.6% と誤分類させることに成功している.
挿入位置も重要で,適当なところに入れると出力が変化しない.元クラスに強く寄与している,つまり が大きい単語の近くに挿入することで適切に誤分類できる.
複数の単語を挿入しなければいけない場合は HTP が含まれた文章を新たに追加する.もしちょうどいいものがなければ,読み手にばれにくい嘘の文章を追加する.

修正

修正といっても削除に近い.
元クラスに寄与している語を を使って探す.これを Hot Sample Phrase (HSP) と呼ぶ.
HSP に対して,元クラスへの損失関数 を増加させつつ,目的のクラスへの損失関数 を減少させるような修正を行なう.
単語の修正とは一体何かというと,

  • ススペルさせる (ミススペコーパスを事前に用意しておく)
  • 人間に見分けがつきにくいような置換を行なう.例えば l と 1 (おそらく置換ルールを用意しておく)

という二種類を行なう.
例えば comedy film property が HSP (Film, 99.9%) だった場合,flim と typo することで Company 99.0% にできる例が示されている.

削除

HSP が高い単語を削除するが,無闇に削除すると人間にばれる.よって形容詞と副詞を削除する.
論文中では seven-part British terevision series という HSP に対して British を削除することで Film 95.5% を 60.5% まで低下させている.

組み合わせる

これら三つを組み合わせることで飛躍的にうまくいく.

実験

例が作れたかどうかだけではなく,人間に読ませる実験も行っている.
変更を加えた文章も人間は適切に分類しており,うまく のみを騙せていることがわかった.
また,「人為的に変更した箇所と思われる部分を指摘せよ」という指示も出したが,精度 5.0%, 再現率 2.0% だったのでかなりばれにくいことがわかった.

シンプルだけどとてもおもしろかった.日本語でやるとすると見た目が似た漢字などが使えるのかもしれない.
日本語の多クラス分類データセットがあればすぐにでも実験できそう.
また, Adversarial example 自体は CNN 固有の問題でないので, GBDT などでも再現できるのではないだろうか.
(追記) 寝て起きて気づいたけれど,「挿入位置にセンシティブである」ということから bag-of-words で表現するモデルでは正しく動作しない気がしてきた.

Neural Collaborative Filtering (WWW 2017) 読んだ & Chainer で実装した

Neural Collaborative Filtering (pdf)

概要

タスクは user と item について評価しているか (1) していないか (0) の情報 (implicit feedback) から未知の user と item の評価を予測する,商品推薦において非常に古典的なもの.
一般的には協調フィルタリングや行列分解を行なうが,この論文では Neural Collaborative Filtering (NCF) を提案している.

手法

人の user と 個の item について,評価/購入しているかしていないかのデータ が与えられているとする.
NCF ではこの を行列分解と多層パーセプトロンの二つを同時に推定することで学習する.

行列分解

入力である の行列を となるように 次元の行列 に分解する.

多層パーセプトロン

それぞれを one-hot encoding したものを連結して多層パーセプトロンで学習.

NCF

行列分解と多層パーセプトロンとを同時に学習する.わかりやすく各処理での次元数を書く.

  • user と item をそれぞれ one-hot encoding したものを連結した M+N 次元のベクトルを作る
  • この入力をもとに M+N -> d_1 -> d_2 -> ... -> d_i 次元まで落とす i 層のニューラルネットを作る
  • 同時に, user と item を それぞれ K 次元に embedding し,要素ごとの積である K 次元のベクトルを作る.これが行列分解に相当する
  • d_i 次元のニューラルネット由来のベクトルと K 次元の行列分解由来のベクトルを連結し d_i + K 次元のベクトルとする
  • 最後に sigmoid に通して予測値 とする

Chainer による実装

はじめてまともに Chainer を書いた.簡単でいい.
とりあえずは二層で,宣言時にユーザ数,アイテム数,行列分解の次元数,ニューラルネットの層の数を指定する.
学習時には user と item ,それらを one-hot encoding したベクトル user_vec/item_vec ,およびラベルを与える.
movielens で実験したところ,素の行列分解よりは精度が高そうな感じがした. ensemble に近い内容だからではないのという気持ちも少しある.

import numpy as np

import chainer
from chainer import functions as F
from chainer import links as L
from chainer import Variable

class NCF(chainer.Chain):
    def __init__(self, n_user, n_item, n_mf_dim, n_dim_1, n_dim_2):
        self.n_user = n_user
        self.n_item = n_item
        self.n_mf_dim = n_mf_dim
        self.n_dim_1 = n_dim_1
        self.n_dim_2 = n_dim_2

        self._layers = {
            'MFQ': L.EmbedID(self.n_user, self.n_mf_dim),
            'MFP': L.EmbedID(self.n_item, self.n_mf_dim),
            'l1': L.Linear(self.n_user + self.n_item, self.n_dim_1),
            'l2': L.Linear(self.n_dim_1, self.n_dim_2),
            'l_out': L.Linear(self.n_dim_2 + self.n_mf_dim, 1)
        }

        super(NCF, self).__init__(**self._layers)

        for param in self.params():
            param.data[...] = np.random.uniform(-0.1, 0.1, param.data.shape)


    def predict(self, u, i, user_vec, item_vec):
        # train neural net
        input_vec = F.concat((user_vec, item_vec), axis = 1)
        h = F.relu(self.l1(input_vec))
        h = F.relu(self.l2(h))

        # matrix factorization
        mf_p_u = self.MFQ(u)
        mf_q_i = self.MFP(i)

        # concat matrix factorization
        h = F.concat((h, mf_p_u * mf_q_i), axis = 1)
        h = self.l_out(h)
        return F.sigmoid(h)


    def __call__(self, u, i, user_vec, item_vec, y):
        pred = self.predict(u, i, user_vec, item_vec)
        loss = F.sigmoid_cross_entropy(pred, y.reshape(len(y), 1))
        chainer.report({'loss': loss}, self)
        return loss

Face-to-BMI: Using Computer Vision to Infer Body Mass Index on Social Media (ICWSM 2017) 読んだ

Face-to-BMI
顔写真から BMI (Body Mass Index) を推定する.

データセットprogresspics - Show us your body transformations をクロールし,皆さんがアップロードしている before / after の写真 4206 枚を集めた. reddit にこんなデータがあるとは思わなかった.

手法は VGG-Net と VGG-Face それぞれで特徴量抽出し,Support Vector Regression で回帰.
実験結果を見るかぎり,女性より男性の方が推定しやすいのは面白い.

golang で BPR な Factorization Machines を書いた

python で実装はしていたけれどもう少し速度が欲しかった.
C++で書こうと思っていたけれど,周りから golang を薦められたので従った.
高速化のためにパラメータ更新は[1106.5730] HOGWILD!: A Lock-Free Approach to Parallelizing Stochastic Gradient Descentにあるような衝突しても構わない形での SGD を使っている.
golang,はじめて書いたけれど型があると気分がいい.go-flymake で書いている最中に何が間違っているかすぐにわかるのもいい.

「声優統計コーパス : 二次配布可能な音素バランス文とその読み上げ音声の構築」を書いた

声優統計コーパス : 二次配布可能な音素バランス文とその読み上げ音声の構築 (pdf, GitHub)
というわけで
プロの女性声優 3 名が 3 パターンの感情表現で読み上げた音声 2 時間分 を「声優統計コーパス」として無料公開します - 糞ネット弁慶
で公開した声優統計コーパスについて,その作り方,特に音素バランス文の選択について書いた.
やっていることは単純だけど細かい話などがあるので,音素バランス文や音声コーパスを作りたい人の手助けになればいいと思う.

今回はカバーすべきダイフォンの最低登場回数しか考慮していないが,分布そのものを近付ける必要があるのかないのか,理想的なダイフォンの分布があるのか,より読み上げに適した文章や単語を選ぶ必要があるのか,といった話はよくわかっていない.
きっと音声処理を行なう研究室や企業にさまざまなノウハウがあるのだと思う.

Field-aware Factorization Machines in a Real-world Online Advertising System (WWW 2017) 読んだ

[1701.04099] Field-aware Factorization Machines in a Real-world Online Advertising System
Criteo で Field-aware Factorization Machines を活用している,という話.

気になった点をメモしておく.

複数マシンでの推定

  • Iterative Parameter Mixing を行っているがマシンが増えるほど収束が遅くなる
  • 学習率を大きくすると収束は早くなるが精度が悪くなる
  • そこで学習率を大きくしつつ AdaGrad を用いて精度良く収束させる

FFM と warm start

warm start

FFM を CTR に用いる際の実運用を考えてみると,時系列に従ってパラメータを再学習する必要がある.また, FFM は油断するとすぐに過学習をしてしまうため, early stopping (予測対象とは別に精度評価用のデータを用意し,そのデータに対する予測精度が悪化した時点で学習を止め,その 1 epoch 前のパラメータを返す) を行う必要がある.
なので,ある年の6月のCTRを予測するために1月から4月までを学習データ (train),5月のデータを early stopping 用のデータ (validation) としてパラメータを学習する.7月の CTR を予測するためには2月から5月までを train , 6月を validation にする,といった具合にデータをずらしていく必要がある.
一般にパラメータ推定は時間がかかるわけだが,よく考えてみると6月予測と7月予測とでは学習データの多くが重複している.なので,6月予測時に推定したパラメータを,7月予測時にパラメータの初期値に用いることで学習時間を削減することができる.これが warm start.

FFM における naive な warm start は失敗する

ロジスティクス回帰であれば大域的最適解を求めることができるのでこれで問題無い.しかし FFM だと案外問題になってくる.
これを検証するために Criteo data set で実験している.
データを(恐らく時系列に従って)90分割し,1から44ブロックを train,45ブロック目を validation, 46ブロック目を test にして推定と予測を行う.続いて,これを 1 ブロック次にずらして推定と予測を行う(2から45ブロックを train,46ブロックを validation,47ブロックをtest).これを最後まで続けてみる.
この時,何も考えずに warm start を行うと,期間が進めば進むほど予測精度は (warm start を用いないベースラインに比べ) どんどん悪化していく.これではよくない.

pre-mature warm-start

そこで提案しているのが pre-mature な warm-start.
手法はシンプルで, validation set に対する予測誤差が悪化したのが t epoch だとする.ここで推定を終え,t - 1 のパラメータを採用するのはこれまでと同様だが,次のパラメータ推定の初期値に採用するパラメータは t - 2 epoch のものを用いる.
これだけで計算時間も約半分に削減できるし (ベースラインで 7 epoch だったのが 2-3 epoch で終わっている),精度もむしろ良くなっている.
更に, pre-mature warm-start を行うことで train のブロック数を少なくしても十分な精度を発揮している.特に上記実験の期間が進めば進むほどその改善度合いは顕著になっている.
もっと極端な例としては train に 1 ブロックだけ用い, warm-start させなくともいいらしい.CTR においてはデータが多すぎると問題になるという話なのか,そもそも FFM があまりに複雑なモデルである,という話なのか,恐らく両者であると思われる.

Trajectory Recovery From Ash: User Privacy Is NOT Preserved in Aggregated Mobility Data (WWW 2017) 読んだ

[1702.06270] Trajectory Recovery From Ash: User Privacy Is NOT Preserved in Aggregated Mobility Data
ユーザのプライバシーなどに考慮し,「誰がいつどこにいたか」という生の位置情報ではなく,「どの領域にいつ何人いたか」という集計情報の公開,共有が行われることが増えてきた.例えば,ある基地局がカバーするエリア内に何人の携帯電話所有者がいるか,といったものである.
この論文では,そのような位置情報の集計データから個別のユーザの移動系列に比較的高い精度で分解が可能な,教師なしの手法を提案している.
タイトルがかっこいい.

手法

今, 人のユーザと 個の位置について,時刻 における各地点の総数 の情報が公開されているとする.
この といったように,各ユーザが時刻 にどこにいるか,という情報を集約したものであると考えることができる.
今,時刻 までの各ユーザ ごとの軌跡 (trajectory) が分解でき, で得られているとする.すると,時刻 の位置情報 を軌跡に紐付けるためには


を解けばいいことがわかる.は軌跡と位置情報を紐付ける行列であるため,あとはコスト行列 が適切に設定できれば を求めることができる.

コスト行列は次の 3 要素から構成される.

  • nighttime
    • 夜は同じ場所に留まりやすい,なので位置の距離をコストにすることで近い位置を紐付けやすくする
  • daytime
    • 昼間は移動するので軌跡の差分を移動速度とし,やはり距離をコストにする
  • accross days
    • 上記2つの要素で特定の日付における軌跡を紐付けることはできたが,1日目と2日目の軌跡をどう紐付けたらいいか
    • 「同じユーザは日をまたいでも似たような行動を取る」という知見にもとづき,情報利得をコストにすることで紐付けを行う

実験はスマートフォンの起動時に得られる位置データと,携帯電話会社が提供したユーザの位置情報の二つを用い,集約した後に復元可能かを検証している.精度は73-91%.

全体を通してシンプルかつ自然な話でとても良かった.