在 Theano 中模拟布尔掩码

Emulating boolean masks in Theano

我正在将一个 numpy 表达式移植到 theano。给定一个 one-hot 矩阵 Y 的 ground truth classes 和一个 one-hot 矩阵 Y_hat classes。 numpy 代码是:

import numpy as np

y = np.array([1, 0, 1, 2, 2])
y_hat = np.array([2, 0, 1, 1, 0])

Y = np.zeros(shape=(len(y), len(np.unique(y))))
Y_hat = np.zeros_like(Y)

rows = np.arange(len(y))

Y[rows, y] = 1
Y_hat[rows, y_hat] = 1

((Y_hat == Y) & (Y == 1)).sum(axis=0)

最后一个表达式产生 array([1, 1, 0])。我试过使用 theano 的非零值:

from theano import shared
Yt = shared(Y)
Yt_hat = shared(Y_hat)
Yt_hat[Yt.nonzero()].eval()

eval 结果为 array([ 0., 1., 1., 0., 0.]),它是 Yt_hat 行的 0-1 掩码,其中预测是正确的。关于如何使这项工作有什么建议吗?对于不同的做法?谢谢。

以下三个变体演示了如何在 Theano 中重新实现部分 numpy 代码。

请注意 Theano 的 Unique 操作在 GPU 上不支持 运行 并且似乎也不支持梯度。结果版本 3 很多用处不大。版本 2 提供了一个解决方法:在 Theano 之外计算唯一值并将它们传递进来。版本 1 是你的 numpy 代码最后一行的 Theano 实现。

针对您的具体问题:没有必要使用nonzero;在这种情况下,索引在 Theano 中的工作就像在 numpy 中一样。也许您对 yY 感到困惑? (常见的 Python 风格是所有变量和参数名称都坚持小写)。

import numpy as np
import theano
import theano.tensor as tt
import theano.tensor.extra_ops


def numpy_ver(y, y_hat):
    Y = np.zeros(shape=(len(y), len(np.unique(y))), dtype=np.int64)
    Y_hat = np.zeros_like(Y, dtype=np.int64)
    rows = np.arange(len(y), dtype=np.int64)
    Y[rows, y] = 1
    Y_hat[rows, y_hat] = 1
    return ((Y_hat == Y) & (Y == 1)).sum(axis=0), Y, Y_hat


def compile_theano_ver1():
    Y = tt.matrix(dtype='int64')
    Y_hat = tt.matrix(dtype='int64')
    z = (tt.eq(Y_hat, Y) & tt.eq(Y, 1)).sum(axis=0)
    return theano.function([Y, Y_hat], outputs=z)


def compile_theano_ver2():
    y = tt.vector(dtype='int64')
    y_hat = tt.vector(dtype='int64')
    y_uniq = tt.vector(dtype='int64')
    Y = tt.zeros(shape=(y.shape[0], y_uniq.shape[0]), dtype='int64')
    Y_hat = tt.zeros_like(Y, dtype='int64')
    rows = tt.arange(y.shape[0], dtype='int64')
    Y = tt.set_subtensor(Y[rows, y], 1)
    Y_hat = tt.set_subtensor(Y_hat[rows, y_hat], 1)
    z = (tt.eq(Y_hat, Y) & tt.eq(Y, 1)).sum(axis=0)
    return theano.function([y, y_hat, y_uniq], outputs=z)


def compile_theano_ver3():
    y = tt.vector(dtype='int64')
    y_hat = tt.vector(dtype='int64')
    y_uniq = tt.extra_ops.Unique()(y)
    Y = tt.zeros(shape=(y.shape[0], y_uniq.shape[0]), dtype='int64')
    Y_hat = tt.zeros_like(Y, dtype='int64')
    rows = tt.arange(y.shape[0], dtype='int64')
    Y = tt.set_subtensor(Y[rows, y], 1)
    Y_hat = tt.set_subtensor(Y_hat[rows, y_hat], 1)
    z = (tt.eq(Y_hat, Y) & tt.eq(Y, 1)).sum(axis=0)
    return theano.function([y, y_hat], outputs=z)


def main():
    y = np.array([1, 0, 1, 2, 2], dtype=np.int64)
    y_hat = np.array([2, 0, 1, 1, 0], dtype=np.int64)
    y_uniq = np.unique(y)
    result, Y, Y_hat = numpy_ver(y, y_hat)
    print result
    theano_ver1 = compile_theano_ver1()
    print theano_ver1(Y, Y_hat)
    theano_ver2 = compile_theano_ver2()
    print theano_ver2(y, y_hat, y_uniq)
    theano_ver3 = compile_theano_ver3()
    print theano_ver3(y, y_hat)


main()