Эффективное создание списков в Python благодаря динамической переменной

#python #algorithm #design-patterns #refactoring

#python #алгоритм #шаблоны проектирования #рефакторинг

Вопрос:

Я использую Keras для построения LSTM-рекуррентной нейронной сети. Мой код работает хорошо, но не помешал бы серьезный рефакторинг. Я прогнозирую значения временных рядов, и в зависимости от размера окна, который я хочу предсказать, я в конечном итоге пишу код, который кажется слишком специфичным для этого размера окна, т. Е. трудно обслуживать множество разных размеров.

Я разделил свой набор данных на обучающие и тестовые наборы

 print "Dataset length: %d" % len(dataset)
train_size = int(len(dataset) * 0.67)
test_size = len(dataset) - train_size
train, test = dataset[0:train_size,:], dataset[train_size:len(dataset),:]
print "Train length: %d, Test length: %d" % (len(train), len(test))
  

Длина набора данных: 1826 Длина поезда: 1223, длина теста: 603

Затем для обоих train и test мне нужно создать, X который является входом, и Y который является выходом (что я пытаюсь предсказать)

 def create_dataset(dataset, look_back=1, predict_steps=1):

    dataX, dataY = [], []

    for i in range(dataset.shape[0] - look_back - predict_steps):
        dataX.append(dataset[i:(i   look_back), 0])
        dataY.append(dataset[i   look_back:i   look_back   predict_steps, 0])

    return np.array(dataX), np.array(dataY)

look_back = 10
predict_steps = 5
input_dim = look_back   1
trainX, trainY = create_dataset(train, look_back=look_back, predict_steps=predict_steps)
testX, testY = create_dataset(test, look_back=look_back, predict_steps=predict_steps)
print "trainX shape: %s, trainY shape: %s" % (trainX.shape, trainY.shape,)
  

Форма trainX: (1208, 10), форма trainY: (1208, 5)

Если я хочу предсказать на 5 временных шагов вперед, то мои прогнозы, сохраненные в переменной trainY , примут вид [[t 6, t 7, t 8, t 9, t 10], [t 7, t 8, t 9, t 10, t 11]] , т.Е.

 prediction 1    [t 6,   t 7,    t 8,    t 9,    t 10]
prediction 2    [t 7,   t 8,    t 9,    t 10,   t 11]
prediction 3    [t 8,   t 9,    t 10,   t 11,   t 12]
prediction 4    [t 9,   t 10,   t 11,   t 12,   t 13]
prediction 5    [t 10,  t 11,   t 12,   t 13,   t 14]
  

введите описание изображения здесь

Теперь, если я хочу вернуть эти значения в логической последовательности, т.Е. t 6, t 7, t 8,...,t 14
Я использую этот код

 output = trainY
output_plot = np.array([])
output_plot = np.append(output_plot, output[0][0])
output_plot = np.append(output_plot, np.mean([output[0][1], output[1][0]]))
output_plot = np.append(output_plot, np.mean([output[0][2], output[1][1], output[2][0]]))
output_plot = np.append(output_plot, np.mean([output[0][3], output[1][2], output[2][1], output[3][0]]))

for i in range (len(output) - predict_steps   1):
    tmp = np.mean([output[i][4], output[i 1][3], output[i 2][2], output[i 3][1], output[i 4][0]])
    output_plot = np.append(output_plot, tmp)
  

Моя проблема возникает, когда я хочу расширить окно прогнозирования, скажем, до 10 временных шагов. Затем я вручную расширяю предыдущий код следующим образом

 output = trainY
output_plot = np.array([])
output_plot = np.append(output_plot, output[0][0])
output_plot = np.append(output_plot, np.mean([output[0][1], output[1][0]]))
output_plot = np.append(output_plot, np.mean([output[0][2], output[1][1], output[2][0]]))
output_plot = np.append(output_plot, np.mean([output[0][3], output[1][2], output[2][1], output[3][0]]))
output_plot = np.append(output_plot, np.mean([output[0][4], output[1][3], output[2][2], output[3][1], output[4][0]]))
output_plot = np.append(output_plot, np.mean([output[0][5], output[1][4], output[2][3], output[3][2], output[4][1], output[5][0]]))
output_plot = np.append(output_plot, np.mean([output[0][6], output[1][5], output[2][4], output[3][3], output[4][2], output[5][1], output[6][0]]))
output_plot = np.append(output_plot, np.mean([output[0][7], output[1][6], output[2][5], output[3][4], output[4][3], output[5][2], output[6][1], output[7][0]]))
output_plot = np.append(output_plot, np.mean([output[0][8], output[1][7], output[2][6], output[3][5], output[4][4], output[5][3], output[6][2], output[7][1], output[8][0]]))


for i in range (len(output) - predict_steps   1):
    tmp = np.mean([output[i][9], output[i 1][8], output[i 2][7], output[i 3][6], output[i 4][5], output[i 5][4], output[i 6][3], output[i 7][2], output[i 8][1], output[i 9][0]])
    output_plot = np.append(output_plot, tmp)
  

Хотя это работает, это ужасно неэффективно. Как я могу наилучшим образом реорганизовать эти шаги, чтобы сделать код более удобным для более широкого диапазона окон прогнозирования? Кроме того, заголовок моего вопроса не помешало бы немного улучшить, поэтому, пожалуйста, отредактируйте!

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

1. В первой части ваших примеров кода кажется, что вы просто берете среднее значение каждой диагонали над главной антидиагональю квадратного 2D-списка. Но я не понимаю, что пытается сделать ваш цикл. Не len(output) всегда равно predict_steps ? Разве первая строка внутри вашего цикла не вызывала бы IndexError if i что-либо иное, чем 0 ?

2. На самом деле выходные данные не могут иметь никакой длины, в зависимости от набора данных, например, они могут исходить из t 6,...,t 500 , но выводимые данные прогнозируются в Windows с длиной, равной predict_steps . Имеет ли это смысл? Или я обновлю вопрос примером того, что я имею в виду?

3. Но output это все еще квадратная симметричная матрица в форме 2D списка, верно? Можете ли вы показать пример, где, скажем, predict_steps является 3 и len(output) является 5 ? Как output выглядит ваш цикл и на что тогда похож ваш цикл?

4. Я обновил показ примеров из кода, над которым я сейчас работаю, где predict_steps находится 5 . Надеюсь, это поможет прояснить ситуацию

Ответ №1:

(Примечание: я ничего не знаю о вашей проблеме с нейронной сетью, я просто решаю вашу проблему с кодированием / обходом массива)

Использование диагоналей матрицы может быть выполнено с помощью циклов — вам просто нужно настроить параметры цикла в соответствии с соответствующими размерами. Следующий код представляет собой упрощенный макет на чистом Python того, чего вы пытаетесь достичь, исходя из того, что я понимаю о форме ваших данных.

 from pprint import pprint

def create_mock_data(n):
    return [[100   i] for i in range(n)]

def create_dataset(dataset, look_back = 1, predict_steps = 1):
    X, Y = [], []

    for i in range(len(dataset) - look_back - predict_steps):
        X.append([row[0] for row in dataset[i : i look_back]])
        Y.append([row[0] for row in dataset[i look_back : i look_back predict_steps]])

    return X, Y

def antidiagonals(a):
    m, n = len(a), len(a[0])

    for k in range(0, n):
        yield [a[k-i][i] for i in range(k   1)]

    for k in range(n, m):
        yield [a[k-i][i] for i in range(n)]

def pp(label, x):
    print('---', label, '---')
    pprint(x, width = 108)
    print()

def test(n, look_back, predict_steps):
    print('=' * 72)
    print('n =', n)
    print('look_back =', look_back)
    print('predict_steps =', predict_steps)
    print()

    dataset = create_mock_data(n)
    pp('dataset', dataset)

    X, Y = create_dataset(dataset, look_back, predict_steps)
    pp('X', X)
    pp('Y', Y)

    diagonals = list(antidiagonals(Y))
    pp('diagonals of Y', diagonals)

    print()

test(50, look_back = 10, predict_steps = 5)
test(50, look_back = 10, predict_steps = 10)
# test(50, look_back = 15, predict_steps = 10)
  

Обратите внимание, что:

  • Я использовал списки Python вместо массивов numpy, поэтому, пожалуйста, поправьте меня, если я неправильно истолковал вашу индексацию массива, особенно в create_dataset функции.
  • Для простоты я пропустил ту часть, где вы разбиваете исходный набор данных на обучающий и тестовый наборы данных.

Основной код, представляющий интерес, находится в antidiagonals функции. Для каждой строки MxN матрицы это приводит к возрастанию диагонали, начиная с первого элемента этой строки. Первый цикл выдает первые N диагонали, которые все имеют разную длину. Второй цикл выдает следующие M-N диагонали, все из которых имеют N элементы. Когда вы запускаете приведенный выше код, вы можете проверить выходные данные на предмет макетного Y массива и его диагоналей.

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

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

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

1. Это работает превосходно, спасибо. По какой-то причине, когда я реализовал это таким образом, я потерял небольшую степень точности в возвращаемых числах, которую я исправил в antidiagonal циклах с yield np.mean([a[k-i][i] for i in range(k 1)], dtype=np.float64) помощью.