Прогнозирование временных рядов нейронными сетями.

Прогнозирование временных рядов нейронными сетями.

Постановка задачи: при известных скоростях показа рекламы с 8 до 15 часов, сделать прогноз скорости с 16 до 23 часов.

Архитектура нейронной сети для прогнозирования временных рядов выглядит следующим образом:

3 слоя — входной (8 нейронов — известные скорости масштабированные к 1-це), скрытый слой, выходной слой 7 нейронов — прогнозируемая скорость по часам масштабированные к 1-це.

Максимальная точность прогноза на 7 часов составила 69,1%, при следующих параметрах нейронной сети:

  • трехслойная нейронная сеть, при одном скрытом слое;
  • входной слой – 8 нейронов, скрытый – 16, выходной – 7 нейронов;
  • функция активации скрытого слоя: ELLIOT;
  • функция активации выходного слоя: SIN;
  • алгоритм обучения нейронной сети:TRAIN_BATCH.

gr_forecase

Библиотека для разработки программ с использованием нейронных сетей FANN.

FANN – готовая библиотека для разработки программ, использующих нейронные сети, с открытым исходным кодом, обладает целым рядом достоинств:

  • многослойная нейронная сеть, реализованная на языке C;
  • поддерживает методы обратного распространения ошибки обучения (RPROP, Quickprop, Batch, Incremental);
  • поддерживает эволюционный метод обучения;
  • простота в использовании (позволяет создать, обучить и использовать нейронную сеть всего за три вызова функции);
  • скорость работы до 150 раз быстрее, чем другие свободно распространяемые библиотеки;
  • универсальность (можно настроить множество параметров, как в момент запуска, так и в процессе работы);
  • подробная документация;
  • кроссплатформеность, мультиязычность (библиотека доступна на более чем 20 языках программирования);
  • большой выбор реализованных активаторных функций;
  • простота сохранения и загрузки обученных сетей;
  • поддерживает кэширование данных;
  • открытый исходный код;
  • широко используется;
  • подходит как для десктопных приложений, так и для серверных и скриптовых решений;
  • имеет графический интерфейс;
  • занимает всего 300 кб на жестком диске.

Архитектура нейронных сетей: основные характеристики

 В общем случае, для первичной настройки нейронной сети необходимо задать ряд обязательных параметров:

  • число входов (входных нейронов);
  • число скрытых слоев;
  • число выходов (выходных нейронов);
  • число нейронов в скрытых слоях;
  • алгоритм обучения нейронной сети;
  • параметры алгоритма обучения (коэффициенты и пределы обучения);
  • функции активации для выходного и скрытых слоев.

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

Библиотека FANN позволяет выбрать для работы один из нескольких классических функций активации:

  • линейная функция активации;
  • пороговая функция активации;
  • сигмоидальная функция активации;
  • симметричная сигмоидальная функция активации;
  • Гауссова функция активации;
  • симметричная Гауссова функция активации;
  • функция активации Давида Еллиотта;
  • периодическая синусоидальная функция активации.

Библиотека FANN позволяет выбрать в качестве рабочего один из четырех популярных алгоритмов обучения нейронных сетей:

  • пошаговый алгоритм обратного распространения ошибки — FANN_TRAIN_INCREMENTAL;
  • среднеквадратичный алгоритм обратного распространения ошибки — FANN_TRAIN_BATCH;
  • пакетный алгоритм обучения – FANN_TRAIN_RPROP;
  • ускоренный пакетный алгоритм обучения –FANN_TRAIN_QUICKPROP.

Пример PHP скрипта, прогнозирующий скорость показов рекламы ВКонтакте:

 

<?
set_time_limit(0);

$conn = mysql_connect('', '', '');
mysql_select_db('', $conn);

// ------------------------------------------------------
// Не учитывать праздничные и выходные дни в исследовании
$weekend_f = false;
$stady_end = '09-10-2014';
// ------------------------------------------------------

// Праздничные и выходные дни
static $weekend = array (
  '07-03-2014',
  '08-03-2014',
  '09-03-2014',
  '10-03-2014',
  '15-03-2014',
  '16-03-2014',
  '22-03-2014',
  '23-03-2014',
  '29-03-2014',
  '30-03-2014',
  '05-04-2014',
  '06-04-2014',
  '12-04-2014',
  '13-04-2014'
);

$weekend_sql = ($weekend_f) ? implode("','", $weekend) : 0;

$sql = "SELECT DATE_FORMAT(`time`, '%d-%m-%Y') AS `date`, COUNT(DISTINCT(DATE_FORMAT(`time`, '%d-%m-%Y %H'))) AS `count_h` FROM `vk_stat`
WHERE DATE_FORMAT(`time`, '%d-%m-%Y') NOT IN ('$weekend_sql')
GROUP BY DATE_FORMAT(`time`, '%d-%m-%Y')
HAVING `count_h` = 24
ORDER BY `time`";

$result = mysql_query($sql);
while ($row = mysql_fetch_array($result)){
  $date_list[] = $row['date'];
}

$date_list_sql = implode("','", $date_list);

$sql = "SELECT DATE_FORMAT(`time`, '%d-%m-%Y') AS `date`, DATE_FORMAT(`time`, '%H') AS `h`,
MAX(`sum_spent`) AS `spend`, MAX(`sum_clicks`) AS `click`,
ROUND(AVG(`impressions_sum`)) AS `speed`
FROM `vk_stat` WHERE
DATE_FORMAT(`time`, '%d-%m-%Y') IN ('$date_list_sql')
AND (DATE_FORMAT(`time`, '%H') >= 0 AND DATE_FORMAT(`time`, '%H') < 24)
GROUP BY DATE_FORMAT(`time`, '%H %d-%m-%Y') ORDER BY `time`";

//echo $sql.'<hr />';

$result = mysql_query($sql);
$h = 0;
$in = array();
$out = array();
$i = 0;
$s_t = 0;
$alpha = 0.70;

$num_input = 40;
$num_output = 8;

$click_pre = 0;
$price_pre = 0;

$price_pre_buf = 0;

$clock_stop = 16;

while ($row = mysql_fetch_array($result)){
  if ($i == 0){
    $s_t = $row['speed'];
    $price_pre = $row['spend'];
    $click_pre = $row['click'];

    if (!empty($click_pre)){
      $row['click_price'] = round($price_pre/$click_pre, 2);
      $price_pre_buf = round($price_pre/$click_pre, 2);
    }

    $i++;
  } else {
    $row['speed'] = ceil($s_t + $alpha*($row['speed']-$s_t));
    $price_diff = $row['spend'] - $price_pre;
    $click_diff = $row['click'] - $click_pre;

        if (!empty($click_diff)){
      $row['click_price'] = round($price_diff/$click_diff, 2);
      $price_pre_buf = round($price_diff/$click_diff, 2);
    }
    else {
      $row['click_price'] = $price_pre_buf;
    }

    $s_t = $row['speed'];
    $price_pre = $row['spend'];
    $click_pre = $row['click'];
    if (((int)$row['h'] == 23)) {
      $i = 0;
    }
    else $i++;
  }
  $exchel_data[$row['date']][(int)$row['h']]['s'] = $row['speed'];
  $exchel_data[$row['date']][(int)$row['h']]['p'] = $row['click_price'];

  $data_all['d_'.$row['date']][(int)$row['h']] = $row;
  if (strtotime($row['date']) < strtotime($stady_end)){
    	// Обучающая выборка
    if ((int)$row['h'] < $clock_stop){
      $stady_array['d_'.$row['date']]['in'][] = $row['speed'];
      $stady_array['d_'.$row['date']]['in'][] = $row['click_price']*1000;
      $all[] = $row['speed'];
      $all[] = $row['click_price']*1000;
    } else {
      $stady_array['d_'.$row['date']]['in'][] = $row['click_price']*1000;
      $stady_array['d_'.$row['date']]['out'][] = $row['speed'];
         	$all[] = $row['speed'];
      $all[] = $row['click_price']*1000;
    }
  } else {
    	// Тестовая выборка
    if ((int)$row['h'] < $clock_stop){
      $test_array['d_'.$row['date']]['in'][] = $row['speed'];
      $test_array['d_'.$row['date']]['in'][] = $row['click_price']*1000;
      $all[] = $row['speed'];
      $all[] = $row['click_price']*1000;
    } else {
      $test_array['d_'.$row['date']]['in'][] = $row['click_price']*1000;
      $test_array['d_'.$row['date']]['out'][] = $row['speed'];
         	$all[] = $row['speed'];
      $all[] = $row['click_price']*1000;
    }
  }
}

$max = max($all);
$min = min($all);

$k_size = 1/($max-$min);
foreach ($stady_array as $key=>$data){
  foreach ($data['in'] as $k=>$r){
   		$stady_array[$key]['in'][$k] = ($r-$min)*$k_size;
  }
  foreach ($data['out'] as $k=>$r){
   		$stady_array[$key]['out'][$k] = ($r-$min)*$k_size;
  }
}

foreach ($test_array as $key=>$data){
  foreach ($data['in'] as $k=>$r){
   		$test_array[$key]['in'][$k] = ($r-$min)*$k_size;
  }
  foreach ($data['out'] as $k=>$r){
   		$test_array[$key]['out'][$k] = ($r-$min)*$k_size;
  }
}

$desired_error = 0.0001;
$max_epochs = 300;
$epochs_between_reports = 300;

// Настройка архитектуры нейронной сети
$num_neurons_array =
  array(
    $num_input, // Входной слой
    8,
    16,
    8,
    $num_output // Выходной слой
  );

$ann = fann_create_standard_array(sizeof($num_neurons_array), $num_neurons_array);

fann_set_training_algorithm($ann, FANN_TRAIN_RPROP);
fann_set_activation_function_hidden($ann, FANN_ELLIOT);
fann_set_activation_function_output($ann, FANN_GAUSSIAN);

$size_data_count = sizeof($stady_array);
$tmp_file = "$size_data_count $num_input $num_output\n";
foreach ($stady_array as $data){
  $tmp_file .= implode(' ', $data['in'])."\n";
  $tmp_file .= implode(' ', $data['out'])."\n";
}

file_put_contents(dirname(__FILE__) . '/tmp_data.tmp', $tmp_file);
fann_train_on_file($ann, dirname(__FILE__) . "/tmp_data.tmp", $max_epochs, $epochs_between_reports, $desired_error);
$s = 0;

foreach ($test_array as $key_arr=>$data_item){
//foreach ($stady_array as $key_arr=>$data_item){
  echo '<b>'.$key_arr.'</b><br />';
  echo '<table border="1">';
  echo '<tr><td><b>Час</b></td><td><b>Реальность</b></td><td><b>Прогноз</b></td><td><b>Цена клика</b></td><td><b>MAPE</b></td></tr>';

  $result_arr = fann_run($ann, $data_item['in']);

  $err_abs = 0; $l_mape_sum = 0;
  $price_pre = 0;
  for ($i=0; $i<sizeof($result_arr); $i++){
    	$err = round(abs($data_item['out'][$i]/$k_size - $result_arr[$i]/$k_size), 3);
        $err_abs = round($err/($data_item['out'][$i]/$k_size), 3);

        $real = ceil($data_item['out'][$i]/$k_size);
        $out = ceil($result_arr[$i]/$k_size);
        $l_mape = round(100-$err_abs*100);
    if ($l_mape < 0) $c = '#BB0000';
        if ($l_mape >= 0 && $l_mape < 25) $c = '#FFB3B3';
        if ($l_mape >= 25 && $l_mape < 50) $c = '#FFFFCE';
        if ($l_mape >= 50 && $l_mape < 75) $c = '#FFFF33';
        if ($l_mape >= 75 && $l_mape <= 100) $c = '#17FF17';

    if ($l_mape < 0) $l_mape = 0;

    $price = $data_all[$key_arr][$i+$clock_stop]['click_price'];

    if ($i > 0){
      $price_dif = round($price - $price_pre, 2);
      $price_pre = $price;
    } else {
      $price_dif = 0;
      $price_pre = $price;
    }

        echo '<tr><td>'.($i+$clock_stop).'</td><td>'.$real.'</td><td>'.$out.'</td><td>'.$price.' ('.($price_dif >= 0 ? '+'.$price_dif : $price_dif).')</td><td bgcolor="'.$c.'">'.$l_mape.'</td></tr>';
        $l_mape_sum += $l_mape;
  }

  $mape_step = $l_mape_sum/$i;
  $mape += $mape_step;

    $s++;
  echo '</table>';
  echo 'Точность MAPE = '.round($mape_step,3).'<br /><br />';
}

$mape = round($mape/$s,3);
echo 'Точность ср. MAPE = '.$mape.'<br /><br />';

fann_destroy($ann);


$exchel_data_tmp = $exchel_data;

echo '<table border="1">';
echo '<tr><td>&mdash;</td>';
  for ($i=0; $i<24; $i++){
    echo '<td>'.$i.'</td>';
  }
echo '</tr>';

$i=0;
foreach ($exchel_data as $date_key => $date_arr){
  echo '<tr><td>'.$date_key.'</td>';
  for ($i=0; $i<24; $i++){
    echo '<td>'.$date_arr[$i]['s'].' <br /><small>('.$date_arr[$i]['p'].')</small></td>';
  }
  echo '</tr>';
  $i++;
}

echo '</table>';
?>
Facebook Comments

Леонид Чернядьев

Увлекаюсь программированием, интернет маркетингом, прогнозированием, дизайном и версткой. Принимаю заказы на лидогенерацию. Связь через - https://www.facebook.com/lenid.chernyadyev