У меня есть заданный набор данных в матрице y
и я хочу обучать с ним разные ЗВОЛ. ЗВОЛ одномерна (линия), и число нейронов меняется. Сначала я тренирую ЗВОЛ размером N=2
, и N=NMax
, давая в общей сложности NMax-2+1
. Для каждого SOM я хочу сохранить весы после завершения обучения, прежде чем переходить к следующему SOM.
Весь смысл использования PyOpenCL здесь состоит в том, что каждая из внешних петель не зависит от других. А именно, для каждого значения N
скрипт не заботится о том, что происходит, когда N
принимает другие значения. Можно было бы получить тот же результат, что и скрипт NMax-2+1
раз, изменяя значение N
вручную.
Имея это в виду, я надеялся, что смогу выполнить каждую из этих независимых итераций одновременно с использованием графического процессора, так что затраченное время значительно сократится. Увеличение скорости будет меньше 1/(NMax-2+1)
хотя, поскольку каждая итерация дороже, чем предыдущие, как и для больших значений N
, выполняются больше вычислений.
Есть ли способ "перевести" этот код для работы на графическом процессоре? Я никогда раньше не использовал OpenCL, поэтому дайте мне знать, слишком ли это или глупо, так что я могу задать более конкретный вопрос. Код является самодостаточным, поэтому не стесняйтесь попробовать его. Четыре константы, объявленные в начале, могут быть изменены на все, что вам нравится (учитывая, что NMax > 1
и все остальные являются строго положительными).
import numpy as np
import time
m = 3 # Dimension of datapoints
num_points = 2000 # Number of datapoints
iterMax = 150 # Maximum number of iterations
NMax = 3 # Maximum number of neurons
#%%
np.random.seed(0)
y = np.random.rand(num_points,m) # Generate always the same dataset
sigma_0 = 5 # Initial value of width of the neighborhood function
eta_0 = 1 # Initial value of learning rate
w = list(range(NMax - 1))
wClusters = np.zeros((np.size(y,axis = 0),NMax - 1)) # Clusters for each N
t_begin = time.clock() # Start time
for N in range(NMax-1): # Number of neurons for this iteration
w[N] = np.random.uniform(0,1,(N+2,np.size(y,axis=1))) - 0.5 # Initialize weights
iterCount = 1
while iterCount < iterMax:
# Mix up the input patterns
mixInputs = y[np.random.permutation(np.size(y,axis = 0)),:]
# Sigma reduction
sigma = sigma_0 - (sigma_0/(iterMax + 1)) * iterCount
s2 = 2*sigma**2
# Learning rate reduction
eta = eta_0 - (eta_0/(iterMax + 1)) * iterCount
for selectedInput in mixInputs: # Pick up one pattern
# Search winning neuron
aux = np.sum((selectedInput - w[N])**2, axis = -1)
ii = np.argmin(aux) # Neuron 'ii' is the winner
jjs = abs(ii - list(range(N+2)))
dists = np.min(np.vstack([jjs , abs(jjs-(N+2))]), axis = 0)
# Update weights
w[N] = w[N] + eta * np.exp((-dists**2)/s2).T[:,np.newaxis] * (selectedInput - w[N])
print(N+2,iterCount)
iterCount += 1
# Assign each datapoint to its nearest neuron
for kk in range(np.size(y,axis = 0)):
aux = np.sum((y[kk,] - w[N])**2,axis=-1)
ii = np.argmin(aux) # Neuron 'ii' is the winner
wClusters[kk,N] = ii + 1
t_end = time.clock() # End time
#%%
print(t_end - t_begin)
Я пытаюсь дать несколько полный ответ.
Прежде всего:
Можно ли настроить этот код для запуска на графическом процессоре с помощью (py) OpenCL?
Скорее всего, да.
Это можно сделать автоматически?
Нет (афайк).
Большинство вопросов, которые я рассказываю о OpenCL, следующие: "Стоит ли переносить этот кусок кода в OpenCL для ускорения?" Вы заявляете, что ваш внешний цикл независим от результатов других прогонов, что делает код в основном параллелизуемым. В простой реализации каждый рабочий элемент OpenCL выполнял бы тот же код с несколько разными входными параметрами. Не учитывая накладные расходы при передаче данных между хостом и устройством, время работы этого подхода будет равняться времени работы самой медленной итерации. В зависимости от итераций в вашем внешнем цикле это может быть огромным увеличением скорости. Пока номера остаются относительно небольшими, вы можете попробовать использовать модуль multiprocessing
в python для параллелизации этих итераций на CPU вместо графического процессора.
Портирование на графический процессор обычно имеет смысл только в том случае, если параллельно нужно запустить огромное количество процессов (около 1000 или более). Поэтому в вашем случае, если вы действительно хотите увеличить скорость, посмотрите, можете ли вы распараллелить все вычисления внутри цикла. Например, у вас есть 150 итераций и 2000 точек данных. Если бы вы могли как-то распараллелить эти 2000 точек данных, это могло бы обеспечить гораздо больший прирост скорости, что могло бы оправдать работу по переносу всего кода на OpenCL.
TL; DR: сначала попробуйте распараллелить CPU. Если вы обнаружите необходимость запуска более нескольких 100 процессов одновременно, перейдите на GPU.
Обновление: простой код для распараллеливания по процессору с использованием многопроцессорности (без обратного вызова)
import numpy as np
import time
import multiprocessing as mp
m = 3 # Dimension of datapoints
num_points = 2000 # Number of datapoints
iterMax = 150 # Maximum number of iterations
NMax = 10 # Maximum number of neurons
#%%
np.random.seed(0)
y = np.random.rand(num_points,m) # Generate always the same dataset
sigma_0 = 5 # Initial value of width of the neighborhood function
eta_0 = 1 # Initial value of learning rate
w = list(range(NMax - 1))
wClusters = np.zeros((np.size(y,axis = 0),NMax - 1)) # Clusters for each N
def neuron_run(N):
w[N] = np.random.uniform(0,1,(N+2,np.size(y,axis=1))) - 0.5 # Initialize weights
iterCount = 1
while iterCount < iterMax:
# Mix up the input patterns
mixInputs = y[np.random.permutation(np.size(y,axis = 0)),:]
# Sigma reduction
sigma = sigma_0 - (sigma_0/(iterMax + 1)) * iterCount
s2 = 2*sigma**2
# Learning rate reduction
eta = eta_0 - (eta_0/(iterMax + 1)) * iterCount
for selectedInput in mixInputs: # Pick up one pattern
# Search winning neuron
aux = np.sum((selectedInput - w[N])**2, axis = -1)
ii = np.argmin(aux) # Neuron 'ii' is the winner
jjs = abs(ii - list(range(N+2)))
dists = np.min(np.vstack([jjs , abs(jjs-(N+2))]), axis = 0)
# Update weights
w[N] = w[N] + eta * np.exp((-dists**2)/s2).T[:,np.newaxis] * (selectedInput - w[N])
print(N+2,iterCount)
iterCount += 1
# Assign each datapoint to its nearest neuron
for kk in range(np.size(y,axis = 0)):
aux = np.sum((y[kk,] - w[N])**2,axis=-1)
ii = np.argmin(aux) # Neuron 'ii' is the winner
wClusters[kk,N] = ii + 1
t_begin = time.clock() # Start time
#%%
def apply_async():
pool = mp.Pool(processes=NMax)
for N in range(NMax-1):
pool.apply_async(neuron_run, args = (N,))
pool.close()
pool.join()
print "Multiprocessing done!"
if __name__ == '__main__':
apply_async()
t_end = time.clock() # End time
print(t_end - t_begin)
multiprocessing
было даже медленнее, чем не распараллеливание. Имеет ли это смысл?