Ahogrammer

Deep Dive Into NLP, ML and Cloud

「Kerasのto_categoricalの挙動ってちょっと変わってるよね」という話

今日はマニアックな話。

Kerasを使っている人なら、to_categorical関数を使ったことがある人は多いのではないかと思う。to_cateogorical関数をいつ使うかというと、正解クラスをone-hotエンコーディングして出力に与えたいときに使うことが多い。Keras 2.2.0だと以下のように動作する。

>>> from keras.utils.np_utils import to_categorical
>>> to_categorical([[1, 3]], num_classes=4)
array([[[0., 1., 0., 0.],
        [0., 0., 0., 1.]]], dtype=float32)
>>> to_categorical([[1, 3]], num_classes=4).shape
(1, 2, 4)

ところが、系列長が1の入力を渡すと面白い挙動を示す。

>>> to_categorical([[1]], num_classes=4)
array([[0., 1., 0., 0.]], dtype=float32)
>>> to_categorical([[1]], num_classes=4).shape
(1, 4)

なんと、(1, 1, 4)にならない!

では、keras.backendのone_hotはどうなのかというと、想定通りの動きを示す。

>>> import keras.backend as K
>>> K.one_hot([[1]], num_classes=4)
<tf.Tensor 'one_hot:0' shape=(1, 1, 4) dtype=float32>
>>> K.eval(K.one_hot([[1]], num_classes=4))
array([[[0., 1., 0., 0.]]], dtype=float32)

バグなのか?と思ってソースを見たところ、理由はわからないが意図的にやっていることは間違いない。2017/11/15日のcommitで仕様が変わっている。

github.com

以下のIssueでも議論している。

github.com

理由があろうがなかろうが使用者的には困ることがある。そういうときは以下のようにnumpyのexpand_dimsを使うととりあえず解決できる。

>>> y = to_categorical([[1]], num_classes=4)
>>> y.shape
(1, 4)
>>> y = y if len(y.shape) == 3 else np.expand_dims(y, axis=0)
>>> y.shape
(1, 1, 4)