Numpy очень медленный при выполнении цикла

#python #loops #numpy

#питон #петли #numpy #python #циклы

Вопрос:

Я разрабатываю модель рынка труда на основе агентов на python / numpy. Модель фокусируется на процессе сопоставления работников и фирм, которые характеризуются l-мерными битовыми строками. Рабочие и фирмы с близко совпадающими битовыми строками сопоставляются друг с другом.

На этом этапе модель запускается должным образом и выдает правильный вывод. Однако это происходит чрезвычайно медленно. На 20 итераций уходит около 77 секунд. (Я запускаю модель на Macbook Pro с процессором i5 и 8 ГБ оперативной памяти). Для сравнения, я изначально написал модель на R, где 20 итераций занимают примерно 0,5 секунды. Это кажется действительно странным, поскольку из всего, что я прочитал, python должен быть быстрее, чем R для выполнения цикла и других функций программирования.

Я потратил много времени, пытаясь оптимизировать код и изучая проблемы с numpy. Кроме того, я попытался запустить модель в Sage, но не заметил никакой разницы.

Я прилагаю ключевые сегменты кода ниже. Пожалуйста, дайте мне знать, если есть проблемы с кодом или если есть другие проблемы с numpy, которые я, возможно, пропустил.

Спасибо,

Daniel Scheer

Код:

 from __future__ import division
from numpy import*
import numpy as np
import time
import math as math

NUM_WORKERS         = 1000
NUM_FIRMS           = 65
ITERATIONS          = 20
HIRING_THRESHOLD    = 0.4
INTERVIEW_THRESHOLD = 0.2
RANDOM_SEED         = 1
SKILLSET_LENGTH     = 50
CONS_RETURN         = 1
INC_RETURN          = 1
RETURN_COEFF        = 1.8
PRODUCTIVITY_FACTOR = 0.001

#"corr" function computes closeness between worker i and firm j
def corr(x,y):
    return 1-(np.sum(np.abs(x-y))/SKILLSET_LENGTH)

#"skill_evolve" function randomly changes a segment of the firm's skill demand bit string
def skill_evolve(start,end,start1,q,j,firms):
    random.seed(q*j)
    return around(random.uniform(0,1,(end-start1)))

#"production" function computes firm output
def production(prod):
    return (CONS_RETURN*prod) math.pow(INC_RETURN*prod,RETURN_COEFF)

#"hire_unemp" function loops though unemployed workers and matches them with firms
def hire_unemp(j):
    for i in xrange(NUM_WORKERS):
        correlation = corr(workers[(applicants[i,0]-1),9:(9 SKILLSET_LENGTH 1)],firms[j,4:(4 SKILLSET_LENGTH 1)])
        if (workers[(applicants[i,0]-1),3] == 0 and correlation > HIRING_THRESHOLD and production(correlation*PRODUCTIVITY_FACTOR) >= (production((firms[j,2] (correlation*PRODUCTIVITY_FACTOR))/(firms[j,1] 1)))):
            worker_row = (applicants[i,0]-1)
            workers[worker_row,3] = firms[j,0]
            workers[worker_row,4] = correlation
            workers[worker_row,5] = (workers[worker_row,4] workers[worker_row,1])*PRODUCTIVITY_FACTOR
            firms[j,1] = firms[j,1] 1
            firms[j,2] = firms[j,2] workers[worker_row,5]
            firms[j,3] = production(firms[j,2])
            workers[worker_row,7] = firms[j,3]/firms[j,1]
            #print "iteration",q,"loop unemp","worker",workers[worker_row,0]
            break

#"hire_unemp" function loops though employed workers and matches them with firms 
def hire_emp(j):
    for i in xrange(NUM_WORKERS):
        correlation = corr(workers[(applicants[i,0]-1),9:(9 SKILLSET_LENGTH 1)],firms[j,4:(4 SKILLSET_LENGTH 1)])
        if (workers[(applicants[i,0]-1),3] > 0 and correlation > HIRING_THRESHOLD and (production((firms[j,2] (correlation*PRODUCTIVITY_FACTOR))/(firms[j,1] 1) > workers[(applicants[i,0]-1),7]))):
            worker_row = (applicants[i,0]-1)
            otherfirm_row = (workers[worker_row,3]-1)
            #print q,firms[otherfirm_row,0],firms[otherfirm_row,1],"before"
            firms[otherfirm_row,1] = firms[otherfirm_row,1]-1
            #print q,firms[otherfirm_row,0],firms[otherfirm_row,1],"after"
            firms[otherfirm_row,2] = array([max(array([0], float),firms[otherfirm_row,2]-workers[worker_row,5])],float)
            firms[otherfirm_row,3] = production(firms[otherfirm_row,2])
            workers[worker_row,3] = firms[j,0]
            workers[worker_row,4] = correlation
            workers[worker_row,5] = (workers[worker_row,4] workers[worker_row,1])*PRODUCTIVITY_FACTOR
            firms[j,1] = firms[j,1] 1
            firms[j,2] = firms[j,2] workers[worker_row,5]
            firms[j,3] = CONS_RETURN*firms[j,2] math.pow(INC_RETURN*firms[j,2],RETURN_COEFF)
            workers[worker_row,7] = firms[j,3]/firms[j,1]
            #print "iteration",q,"loop emp","worker",workers[worker_row,0]
            break

workers = zeros((NUM_WORKERS,9 SKILLSET_LENGTH))
workers[:,0] = arange(1,NUM_WORKERS 1)
random.seed(RANDOM_SEED*1)
workers[:,1] = random.uniform(0,1,NUM_WORKERS)
workers[:,2] = 5
workers[:,3] = 0
workers[:,4] = 0
random.seed(RANDOM_SEED*2)
workers[:, 9:(9 SKILLSET_LENGTH)] = around(random.uniform(0,1,(NUM_WORKERS,SKILLSET_LENGTH)))

random.seed(RANDOM_SEED*3)
firms = zeros((NUM_FIRMS, 4))
firms[:,0] = arange(1,NUM_FIRMS 1)
firms = hstack((firms,around(random.uniform(0,1,(NUM_FIRMS,SKILLSET_LENGTH)))))

start_full = time.time()

for q in arange(ITERATIONS):
    random.seed(q)
    ordering = random.uniform(0,1,NUM_WORKERS).reshape(-1,1)
    applicants = hstack((workers, ordering))
    applicants = applicants[applicants[:,(size(applicants,axis=1)-1)].argsort(),]

    #Hire workers from unemployment
    start_time = time.time()
    map(hire_unemp, xrange(NUM_FIRMS))
    print "Iteration unemp -: %2.5f seconds" % (q, time.time() - start_time)

    #Hire workers from employment
    start_time = time.time()
    map(hire_emp, xrange(NUM_FIRMS))
    print "Iteration emp -: %2.5f seconds" % (q, time.time() - start_time)
  

Комментарии:

1. Вы упускаете суть numpy. Вы буквально перевели свой код с R. В нескольких случаях он использует numpy неоптимальным образом. Не присваивайте значения определенному элементу массива numpy внутри цикла, если вы можете избежать этого (а вы почти всегда можете).

2. Другими словами, по возможности избегайте циклов, работая со всем массивом сразу. Это слишком много кода для исправления, но если у вас есть более конкретная подзадача, был бы рад помочь. Что, по вашему мнению, является узким местом в этом коде?

3. Попробуйте RPy, если вы хотите использовать R-код внутри Python. В противном случае попробуйте Cython, если вы хотите писать циклически и компилировать их в вызываемые функции Python. Я могу вам посочувствовать. Мне не нравится философия оптимизации NumPy / Matlab, требующая от программиста думать в векторизованных операциях. Многие научные алгоритмы более удобочитаемы и понятны в циклах, не говоря уже о большей расширяемости. По этой причине я думаю, что Cython стоит дополнительных усилий. Тогда вы сможете использовать удобные функции NumPy для вашего более быстрого основного кода.

4. Вы можете ускорить это на порядки, векторизовав свой код. В качестве примера того, что я имею в виду, посмотрите на первые два примера на technicaldiscovery.blogspot.com/2011/06 /… . Обучение векторизации ваших программ будет хорошей инвестицией вашего времени. Я бы начал с чтения о широковещательной передаче и необычной индексации.