Результат функции Memoize на основе выбранных параметров - PullRequest
4 голосов
/ 11 апреля 2020

У меня есть функция со следующей сигнатурой:

def spectrogram(signal: numpy.ndarray, sampling_frequency=16000, win_len=512, hop=256, win_type='hanning')

Функция ожидает массив numpy на входе (сигнал), плюс ряд других параметров и выводит массив numpy. Он рассчитывает спектрограмму для данного аудиофайла с целью получения определенных характеристик acousti c. Для каждого файла я собираюсь вызывать эту функцию несколько раз, часто с одинаковыми параметрами, но не всегда. Для некоторых функций я могу изменить hop или win_type. Я думал кешировать результаты, чтобы не выполнять одни и те же вычисления более одного раза. Результаты действительны для каждого файла. Файлы будут обрабатываться параллельно с joblib.

Я думал запоминать результаты на основе имени файла (это не тот параметр, который у меня обычно был бы для функции) и полей sampling_frequency, win_len, hop и win_type (т.е. НЕ signal - это может быть большой массив, и гораздо эффективнее смотреть на имя файла, которое уникально).

Как можно Я лучше всего запомнил результаты? Все решения, которые я видел, кэшируют результаты на основе предоставленного ввода; в моем случае я бы хотел сделать заметки на основе выбранных полей. Я на Python 3.6.

Ответы [ 2 ]

1 голос
/ 13 апреля 2020

Я решил опубликовать свой собственный ответ, основанный на ответе @Ethan (+1 голос), добавив два элемента:

  1. Ограничение кэша. Это было одной из моих предпосылок, поэтому я не мог принять ответ Итана. Последний неограничен и быстро истощит мою память.

  2. Я чувствую себя более элегантно; он использует декоратор и модуль, предназначенный для кэширования (и отлавливания некоторых угловых случаев). Он более пригоден для повторного использования и поэтому более дружественен для других.

from cachetools.keys import hashkey
from cachetools import cached, LRUCache

def mykey(signal, *args, **kwargs):
    key = hashkey(*args, **kwargs)
    return key

@cached(LRUCache(maxsize=6), key=mykey)
def spectrogram(signal: numpy.ndarray, filename, sampling_frequency=16000, win_len=512, hop=256, win_type='hanning')

По сути, я просто игнорирую signal и вместо этого принимаю filename в качестве дополнительного параметра для кэширования. При определенных обстоятельствах даже filename не понадобится. Если для каждого файла создается отдельный процесс, эта защита не требуется, так как кеш не может быть разделен между процессами.

Bonus

Я также решил попробовать Memory из joblib и это также показало хорошие результаты. Вот фрагмент:

from joblib import Memory

memory = Memory('cachedir', verbose=0, bytes_limit=100000)

@memory.cache
def spectrogram(signal: numpy.ndarray, sampling_frequency=16000, win_len=512, hop=256, win_type='hanning')

Он работал в среднем на 25% хуже, чем первое решение:

  • записывает на диск
  • вычисляет га sh завершено numpy.ndarray

Учитывая вышесказанное, это все равно отличный результат.

1 голос
/ 11 апреля 2020

Вот моя реализация, мне пришлось смоделировать некоторые данные, так что, если она не делает то, что вам нужно, я с удовольствием подправлю ее.

def memo(hashTable, fileName, signal: np.ndarray, sampling_frequency=16000, win_len=512, hop=256, win_type='hanning'):  
    new_hash = hash(fileName + str(sampling_frequency) + str(win_len) + str(hop) + win_type)
    if new_hash in hashTable.keys():
        return hashTable[new_hash]
    else:
        answer = spectrogram(signal, sampling_frequency, win_len, hop, win_type)
        hashTable[new_hash] = answer
        return answer


def spectrogram(signal: np.ndarray, sampling_frequency=16000, win_len=512, hop=256, win_type='hanning'):
    makeArrayUnique = hop - 256
    return np.arange(makeArrayUnique, 24 + makeArrayUnique).reshape(2,12)

def testHash():
    hashTable = {}
    dummySignal = np.zeros(10)
    print('First call', memo(hashTable, 'file1', signal=dummySignal))
    print('Second Call', memo(hashTable, 'file1', signal=dummySignal, hop=260))
    print('First call again', memo(hashTable, 'file1', signal=dummySignal))

    print('Hash Table', hashTable)

Вывод, показывающий 3 вызова, но только две записи в таблице ha sh:

>>> testHash()
First call [[ 0  1  2  3  4  5  6  7  8  9 10 11]
 [12 13 14 15 16 17 18 19 20 21 22 23]]
Second Call [[ 4  5  6  7  8  9 10 11 12 13 14 15]
 [16 17 18 19 20 21 22 23 24 25 26 27]]
First call again [[ 0  1  2  3  4  5  6  7  8  9 10 11]
 [12 13 14 15 16 17 18 19 20 21 22 23]]
Hash Table {-4316472197502448580: array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11],
       [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]]), 6772234510013844540: array([[ 4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15],
       [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]])}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...