Векторизация кода с операцией в зависимости от предыдущего значения - PullRequest
0 голосов
/ 08 октября 2018

Следующий код моделирует систему, которая может выбирать 3 различных состояния в любое время, и постоянная вероятность перехода между этими состояниями задается матрицей prob_nor.Поэтому каждая точка в trace зависит от предыдущего состояния.

n_states, n_frames = 3, 1000
state_val = np.linspace(0, 1, n_states)

prob = np.random.randint(1, 10, size=(n_states,)*2)
prob[np.diag_indices(n_states)] += 50

prob_nor = prob/prob.sum(1)[:,None] # transition probability matrix, 
                                    # row sum normalized to 1.0

state_idx = range(n_states) # states is a list of integers 0, 1, 2...
current_state = np.random.choice(state_idx)

trace = []      
sigma = 0.1     
for _ in range(n_frames):
    trace.append(np.random.normal(loc=state_val[current_state], scale=sigma))
    current_state = np.random.choice(state_idx, p=prob_nor[current_state, :])

Цикл в приведенном выше коде делает его довольно медленным, особенно когда мне приходится моделировать миллионы точек данных.Есть ли способ векторизовать / ускорить это?

Ответы [ 2 ]

0 голосов
/ 08 октября 2018

Может быть, я что-то упускаю, но я думаю, что вы можете создать current_state s в виде списка, а затем векторизовать оставшиеся шаги:

# Make list of states (slow part)
states = []
current_state = np.random.choice(state_idx)
for _ in range(n_frames):
    states.append(current_state)
    current_state = np.random.choice(state_idx, p=prob_nor[current_state, :])

# Vectorised part
state_vals = state_val[states]   # alternatively np.array(states) / (n_states - 1)
trace = np.random.normal(loc=states, scale=sigma)

Я считаю, что этот метод работает и приведет кумеренное улучшение скорости при использовании некоторой дополнительной памяти (вместо одного создается 3 списка / массива).Решение @ PMende приводит к гораздо большему улучшению скорости.

0 голосов
/ 08 октября 2018

Разгрузите вычисление вероятностей как можно скорее:

possible_paths = np.vstack(
    np.random.choice(state_idx, p=prob_nor[curr_state, :], size=n_frames)
    for curr_state in range(n_states)
)

Тогда вы можете просто выполнить поиск, чтобы следовать вашему пути:

path_trace = [None]*n_frames
for step in range(n_frames):
    path_trace[step] = possible_paths[current_state, step]
    current_state = possible_paths[current_state, step]

Как только вы получите свой путь, выможете вычислить ваш след:

sigma = 0.1
trace = np.random.normal(loc=state_val[path_trace], scale=sigma, size=n_frames)

Сравнение времени:

Чистый питон for цикл

%%timeit
trace_list = []
current_state = np.random.choice(state_idx)
for _ in range(n_frames):
    trace_list.append(np.random.normal(loc=state_val[current_state], scale=sigma))
    current_state = np.random.choice(state_idx, p=prob_nor[current_state, :])

Результаты:

30.1 ms ± 436 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

Векторизованный поиск :

%%timeit
current_state = np.random.choice(state_idx)
path_trace = [None]*n_frames
possible_paths = np.vstack(
    np.random.choice(state_idx, p=prob_nor[curr_state, :], size=n_frames)
    for curr_state in range(n_states)
)
for step in range(n_frames):
    path_trace[step] = possible_paths[current_state, step]
    current_state = possible_paths[current_state, step]
trace = np.random.normal(loc=state_val[path_trace], scale=sigma, size=n_frames)

Результаты:

641 µs ± 6.03 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Ускорение примерно в 50 раз.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...