Ускорение операции сокращения в Теано

Редактировать:
Извините, оказалось, что на моем GPU были запущены другие процессы, пока я проводил тестирование, я обновил результаты синхронизации на свободном GPU, и ускорение становится заметным для больших матриц.

Исходное сообщение:

Как написано в этом вопросе, L список матриц, где каждый элемент M это x*n матрица (x переменная, n фиксированный).

Я хочу вычислить сумму M'*M для всех предметов в L (M' это транспонирование M), как это делает следующий код Python.

for M in L:
  res += np.dot(M.T, M)

Ниже приведены некоторые примеры реализаций Numpy и Theano (для исполняемого скрипта, пожалуйста, обратитесь к ответу @DanielRenshaw на предыдущий вопрос).

def numpy_version1(*L):
    n = L[0].shape[1]
    res = np.zeros((n, n), dtype=L[0].dtype)
    for M in L:
        res += np.dot(M.T, M)
    return res

def compile_theano_version1(number_of_matrices, n, dtype):
    L = [tt.matrix() for _ in xrange(number_of_matrices)]
    res = tt.zeros(n, dtype=dtype)
    for M in L:
        res += tt.dot(M.T, M)
    return theano.function(L, res)

def compile_theano_version2(number_of_matrices, n):
    L = theano.typed_list.TypedListType(tt.TensorType(theano.config.floatX, broadcastable=(None, None)))()
    res, _ = theano.reduce(fn=lambda i, tmp: tmp+tt.dot(L[i].T, L[i]),
                           outputs_info=tt.zeros((n, n), dtype=theano.config.floatX),
                           sequences=[theano.tensor.arange(number_of_matrices, dtype='int64')])
    return theano.function([L], res)

Я запускал версии Numpy на CPU и версии Theano на GPU с разными настройками, кажется, что версии Theano всегда пропорционально медленнее, чем версии Numpy (независимо от количества и размера матов).

Но я ожидал, что может произойти некоторая оптимизация с помощью графического процессора, поскольку это простая операция сокращения.

Может ли кто-нибудь помочь мне понять, что происходит под капотом?

Редактировать:
Ниже приведен скрипт (от @DanielRenshaw) для генерации данных, уставшие настройки и результаты.

L = [np.random.standard_normal(size=(x, n)).astype(dtype)
     for x in range(min_x, number_of_matrices + min_x)]

dtype = 'float32'
theano.config.floatX = dtype
iteration_count = 10
min_x = 20

# base case:
# numpy_version1 0.100589990616
# theano_version1 0.243968963623
# theano_version2 0.198153018951
number_of_matrices = 200
n = 100

# increase matrix size:
# numpy_version1 4.90120816231
# theano_version1 0.984472036362
# theano_version2 3.56008815765
number_of_matrices = 200
n = 1000

# increase number of matrices:
# numpy_version1 5.11445093155
# theano_version1 compilation error
# theano_version2 6.54448604584
number_of_matrices = 2000
n = 100

1 ответ

Решение

Проблема, которую вы имеете, не в количестве матриц, а в их размере.

Ваш тестовый пример создает матрицы размера, зависящие от количества матриц, которые у вас есть, таким образом, чем больше у вас матриц, тем больше матрицы, но и тем больше накладные расходы цикла питона (в операциях сокращения), и, таким образом, это делает Труднее обнаружить улучшение скорости.

Я немного изменил генерацию вашей матрицы, чтобы сделать несколько новых тестов:

S = 1000 # Size of the matrices
N = 10 # Number of matrices

L = [np.random.standard_normal(size=(np.random.randint(S//2, S*2), S)).astype(np.float32) for _ in range(N)]

Это генерирует только 10 матриц размера (x, 1000) где x какое-то значение в диапазоне [S//2, S*2] == [500, 2000],

f1 = compile_theano_version1(N, S, np.float32)
f2 = compile_theano_version2(N, S)

  • Теперь некоторые тесты с N = 10 большие матрицы:

За S = 1000, N = 10:

 %timeit numpy_version1(*L)   # 10 loops, best of 3: 131 ms per loop
 %timeit f1(*L)               # 10 loops, best of 3: 37.3 ms per loop
 %timeit f2(L)                # 10 loops, best of 3: 68.7 ms per loop

где теано-функции имеют x4 а также x2 ускорение в ноутбуке с довольно хорошим i7 и приличный NVIDIA 860M (что означает, что вы должны получить некоторые более хорошие ускорения здесь).

За S = 5000, N = 10:

 %timeit numpy_version1(*L)   # 1 loops, best of 3: 4 s per loop
 %timeit f1(*L)               # 1 loops, best of 3: 907 ms per loop
 %timeit f2(L)                # 1 loops, best of 3: 1.77 s per loop

Таким образом, в целом, с этой настройкой, чем больше S Чем больше ускорение Theano преодолевает процессор.


  • Некоторые тесты с N = 100 большие матрицы: theano кажется быстрее

За S = 1000, N = 100:

%timeit numpy_version1(*L)   # 1 loops, best of 3: 1.46 s per loop
%timeit f1(*L)               # 1 loops, best of 3: 408 ms per loop
%timeit f2(L)                # 1 loops, best of 3: 724 s per loop

За S = 2000, N = 100:

%timeit numpy_version1(*L)   # 1 loops, best of 3: 11.3 s per loop
%timeit f1(*L)               # 1 loops, best of 3: 2.72 s per loop
%timeit f2(L)                # 1 loops, best of 3: 4.01 s per loop

  • Тесты с N = 100 маленькие матрицы: NumPy кажется быстрее

За S = 50, N = 100:

%timeit numpy_version1(*L)   # 100 loops, best of 3: 1.17 ms per loop
%timeit f1(*L)               # 100 loops, best of 3: 4.21 ms per loop
%timeit f2(L)                # 100 loops, best of 3: 7.42 ms per loop

Спецификации для испытаний:

  • Процессор: i7 4710HQ
  • Графический процессор: NVIDIA GeForce GTX 860M
  • Numpy: версия 1.10.2, созданная на базе Intel MKT
  • Theano: версия 0.70; floatX = float32; используя GPU
Другие вопросы по тегам