Нейросетевое сжатие данных

imageВ этой статье я хочу поведать о еще одном классе задач, решаемых нейронными сетями – сжатии данных. Алгоритм, описанный в статье, не претендует на использование в реальных боевых условиях по причине существования более эффективных алгоритмов. Сразу оговорюсь, что речь пойдет только о сжатии без потерь.
Большинство источников в Интернете утверждают, что есть 3 основных популярных архитектур нейронных сетей, решающих задачу сжатия данных.

1. Сеть Кохонена и ее вариации. Часто используется для сжатия изображений с потерей качества, не является алгоритмом сжатия без потерь.

2. Ассоциативная память (Сеть Хопфилда, Двунаправленная ассоциативная память и др.). «Сжатие данных» для этого класса сетей является «фичей», побочным явлением, так как главным их предназначением есть восстановление исходного сигнала/образа из зашумленных/поврежденных входных данных. Чаще всего, на вход этих сетей поступает зашумленный образ той же размерности, потому о сжатии данных речь не идет.

3. Метод «Бутылочного горлышка».

О последнем методе и пойдет речь в статье.

Суть метода


imageАрхитектура сети и алгоритм обучения таковы, что вектор большой размерности требуется передать со входа нейронной сети на ее выходы через относительно небольших размеров канал. Как аналогия – песочные часы, лейка, бутылочное горлышко, что кому ближе. Для реализации сжатия такого рода можно использовать многослойный персептрон следующей архитектуры: количество нейронов во входном и выходном слое одинаково, между ними размещаются несколько скрытых слоев меньшего размера.
Для обучения используется алгоритм обратного распространения ошибки, а в качестве активационной функции – сигмоид (сигмоидная функция, биполярная сигмоидная функция).
Сеть состоит из двух частей – одна часть работает на сжатие данных, другая – на восстановление. Время, затрачиваемое на обе процедуры, примерно одинаково. При практическом использовании полученную сеть разбивают на две. Вывод первой сети передают по каналу связи и подают на вход второй, которая осуществляет декомпрессию.

Что влияет на обучение?


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

Реализация


При реализации использовалась библиотека AForge.NET. Основные опыты я проводил над векторами длиной 10, потом над векторами длиной 20, суть это не изменило, потому увеличивать размерность не стал, но есть еще одна причина. Почему я не экспериментировал с более длинными векторами? Почему бы не генерировать большую обучающую выборку с длинными векторами? Это вызвано ленью тем, что доставляет некоторые проблемы и неудобства. В такой выборке могут быть противоречия. Например, во входном множестве векторов могут существовать два и более идентичных или схожих векторов, желаемый выход на которые будет абсолютно отличаться. Тогда сеть на такой обучающей выборке будет необучаема, а найти «конфликтующие» вектора довольно сложно.

Результаты и выводы


Для меня самым интересным вопросом являлось то, как же все-таки эффективно построить эту сеть? Сколько скрытых слоев она должна иметь? Как быстро проходит обучение? Как быстро проходит сжатие и восстановление?
Эксперименты проводились над двумя основными возможными вариантами построения этой сети.

«Быстрое сжатие»

Сетью с «быстрым сжатием» будем называть сеть, где количество нейронов в скрытых слоях намного меньше (в 3, 5 и т.п. раз), чем во входном/выходном слое, а сеть состоит из 4 слоев – входного, выходного и двух скрытых (например, 10-3-3-10). Подобная топология сети оказалась очень эффективной в некоторых случаях. Во-первых, такая сеть очень быстро обучается. Это связано с тем, что алгоритму обучения необходимо корректировать весовые коэффициенты относительно малого количества связей между соседними слоями. Во-вторых, скорость выдачи результата при функционировании сети тоже очень высока (по той же причине). Обратная сторона – сеть обучаема только на небольшой выборке относительно количества нейронов во входном (и, соответственно, выходном) слое (приемлемое количество векторов примерно равно половине нейронов в первом/последнем слое сети). Также, не стоит перегибать палку и делать слишком сильный перепад между слоями, оптимальный вариант – количество нейронов в скрытом слое должно быть примерно в 3 раза меньше, чем во входном/выходном слое.

«Последовательное сжатие»

Сетью с «последовательным» или «неспешным» сжатием будем называть сеть, где количество нейронов в соседних слоях не сильно отличается (до 25%), а сеть содержит 4 и более скрытых слоев (например, 10-8-5-3-3-5-8-10). Такие сети не оправдали мои ожидания. Даже при малой обучающей выборке и длине вектора они очень долго обучаются. Ну и функционируют, конечно же, тоже медленней. Хотя поговаривают, что в реальных боевых условиях они более стабильны. Но из-за очень долгого время обучения проверить это не удалось.

О эффективности и времени работы

С практической точки зрения интересны два факта – коэффициент сжатия и время сжатия/восстановления. Сеть почти 100%-во обучается и отлично функционирует, сжимая данные в 3 раза. Скорость работы тоже радует. Для архитектуры (10-3-3-10) время выполнения полного цикла (сжатия и восстановления) – 00:00:00.0000050. Для (20-6-6-20) — 00:00:00.0000065. Смело можем делить на 2 и получим 00:00:00.0000025 и 00:00:00.0000033 для сжатия либо восстановления одного вектора.
Испытания проводились на процессоре AMD Athlon II x240 2.80 Ghz.

Пример


В качестве примера — небольшой прокомментированный кусочек кода (на C#), описывающий примитивный пример создания, обучения и функционирования сети. Сеть сжимает вектор в 3 раза, обучаема в 98% случаев. Для функционирования необходимо подключить библиотеку AForge.NET.

using System;
using AForge.Neuro;
using AForge.Neuro.Learning;

namespace FANN
{
  class Program
  {
    static void Main(string[] args)
    {
      // Создаем сеть с сигмодиной активацонной функцией и 4 слоями (с 10,3,3,10 нейронами в каждом слое, соответственно)
      ActivationNetwork net = new ActivationNetwork((IActivationFunction)new SigmoidFunction(), 10,3,3,10);
      // Обучение - алгоритм с обратным распространением ошибки
      BackPropagationLearning trainer = new BackPropagationLearning(net);
      
      // Формируем множество входных векторов
      double[][] input = new double[][] {
          new double[]{1,1,1,1,1,1,1,1,1,1},
          new double[]{0,0,0,0,0,0,0,0,0,0},
          new double[]{1,1,1,1,1,0,0,0,0,0},
          new double[]{0,0,0,0,0,1,1,1,1,1},
        };

      // Формируем множество желаемых выходных векторов
      double[][] output= new double[][] {
          new double[]{1,1,1,1,1,1,1,1,1,1},
          new double[]{0,0,0,0,0,0,0,0,0,0},
          new double[]{1,1,1,1,1,0,0,0,0,0},
          new double[]{0,0,0,0,0,1,1,1,1,1},
        };

      // Переменная, сохраняющая значение ошибки сети на предыдущем шаге
      double prErr = 10000000;
      // Ошибка сети
      double error = 100;
      // Сначала скорость обучения должна быть высока
      trainer.LearningRate = 1;
      // Обучаем сеть пока ошибка сети станет небольшой
      while (error > 0.001)
      {
        // Получаем ошибку сети
        error = trainer.RunEpoch(input, output);
        // Если ошибка сети изменилась на небольшое значения, в сравнении ошибкой предыдущей эпохи
        if (Math.Abs(error - prErr) < 0.000000001)
        {
          // Уменьшаем коэффициент скорости обучения на 2
          trainer.LearningRate /= 2;
          if (trainer.LearningRate < 0.001)
            trainer.LearningRate = 0.001;
        }

        prErr = error;
      }

      // Любуемся полученными результатами
      double[] result;
      result = net.Compute(new double[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 });
      result = net.Compute(new double[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 });
      result = net.Compute(new double[] { 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 });
      result = net.Compute(new double[] { 0, 0, 0, 0, 0, 1, 1, 1, 1, 1 });
    }
  }
}


* This source code was highlighted with Source Code Highlighter.


Литература и примечания


  • Википедия
  • Страница библиотеки — www.aforgenet.com
  • Страница загрузки библиотеки — www.aforgenet.com/framework/downloads.html
  • Из всей библиотеки AForge.NET нужна только ее часть — AForge.Neuro.
  • Библиотека AForge.NET работает с типом double, потому выход сети является дробным, но очень близким к необходимым нам значениям – 0 и 1. Поэтому, для получения полноценного выхода можно прогнать каждое значение массива через «пороговую» функцию, возвращающую 0, если значение меньше 0.5 и 1 в противном случае.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 25
  • +3
    На мой взгляд, именно сжатие с потерями и является хорошим применением нейронных систем. Сжатие без потерь с помощью нейронных сетей не имеет никакой практической выгоды.
    • 0
      Так и есть, эта же мысль выражена в первом абзаце статьи.

      Способность нейронных сетей выявлять некоторые зависимости во входных данных (на что, по большей мере, не способны разработанные точные алгоритмы) нашла широкое применение, в том числе и в сжатии данных, в частности — изображений.

      Алгоритм, описанный в статье сложно применять в реальных боевых условиях из-за ряда недостатков. Но есть и плюсы. Возможно, кто-то после прочтения статьи проанализирует преимущества и недостатки и решит использовать данный подход для решения какой-то своей задачи.
      • 0
        В институте у меня был похожий дипломный проект. Он так и назывался. Сжатие данных с помощью нейронных сетей. Я уже не помню в деталях, но суть исследования была в определении различных зависимостей от количества нейронов в скрытом слое. Коэффициент сжатия, коэффициент потерь.
      • +4
        Есть достаточно известный конкурс на сжатие 100 мб классических английских тестов (Hutter Prize).
        Так вот там уже давно лидируют программы побитового сжатия, внутри которых используется предсказание нейросетью.
      • +1
        Если вы создаете алгоритм сжатия без потери данных, это значит, что вы хотите обучить нейронную сеть на работу без ошибок, т.к. аккуратность работы = 100% Чтобы этого достичь вам нужно провести обучение по всем возможным параметрам входа и затем ждать когда обучение сойдется с коэф. ошибки 0. Если вы хотите сжимать изображение, скажем, 100х100 пикселей, то для этого требуется 2^(100x100) экземпляров для обучения (при условии что изображение ч/б). По-моему, запаритесь обучать :)
        • –1
          Верно говорите, уйдут века. Хотя, нужно будет попробовать и посмотреть на сходимость ошибки.
          Я склоняюсь к мысли, что если и применять подобный алгоритм для сжатия без потерь, то стоит наперед знать какие данные мы будем сжимать и передавать. То есть, грубо говоря, нам нужно постоянно передавать 20 изображений. Обучаем сеть и наслаждаемся результатом. НО, если нужно будет передавать еще одно изображение, то сеть может выдать для него неверный результат. С этого следует, что при добавлении каждого нового вектора сеть стоит переобучать.

          А вообще, если честно, то нейронные сети, которые применяют на практике часто обучают по несколько лет :)
          • 0
            вау, где это вы такие нашли?
            • 0
              Прогнозирование в медицине, погоде, экономической сфере, где имеются терабайты данных за несколько десятков лет и которые могут поступать ежемесячно/еженедельно/ежедневно.

              Может я и ошибаюсь, но такую информацию получил от авторитетного источника — человека, который занимался и видел это на практике. Хотя, он человек весьма пожилой и было это, возможно, давненько, но все же было. А может и до сих пор есть, однозначно отрицать это нельзя.
              • 0
                вы не совсем корректно высказались, модели не перестраиваются заново, но действительно строятся годами — а это большая разница. Если комп.время на расчет модели занимает год — такая модель никому я думаю не нужна.
          • 0
            >и затем ждать когда обучение сойдется с коэф. ошибки 0

            Не нужно этого ждать, т.к. этого не произойдет. Среди всех возможных образов будет масса представителей белого шума — которые (все одновременно) без потерь сжать невозможно в принципе.
            • 0
              chainik выше уже сказал, что самое место нейросети в предсказании веса символа исходя из каких-либо прочих условий (например контекста). При этом кодером может выступать тотже арифметик.
            • +1
              Нейронная сеть такой архитектуры НЕ обеспечивает сжатие без потерь. Это раз.
              Два — нейронная сеть такой архитектуры не факт что вообще дает какое-либо сжатие: требование к точности сигнала в «горлышке» существенно повышается — и если мы кодируем бинарные данные на входе, то при попытке заменить сигналы нейронов в горлышке на бинарные, все сжатие исчезнет.

              Подобная архитектура может быть полезна только в одном случае: если нужно передавать данные определенного типа, с некоторыми внутренними зависимостями, которые лень анализировать серьезно. Тогда примененная «в лоб» нейросеть эти зависимости выловит, и сможет использовать для сжатия (с потерями). Но как только мы попытаемся сжать такой системой данные другого типа — то получим на выходе нечто, весьма слабо напоминающее исходный результат.
              • 0
                Сейчас популярен метод Deep Learning, хотя это является методом с потерями.
                • 0
                  И еще, забыл написать: на входе вы подаете биты (0, 1), а в скрытом слое у вас действительные числа (double), которые занимают больше места чем булевы.
                  • 0
                    Подумал о том же. Немного уточню комментарий Irokez'а:
                    Вы предполагаете, что цепочка такая: 10 бит — 3 бит — 3 бит — 10 бит
                    На самом деле цепочка такая:
                    10 бит — 640 бит — 192 бит — 192 бит — 640 бит — 10 бит

                    пока бутылочное горлышко передает больше бит, чем поступает на вход — система может работать и без ошибок.

                    Без потерь означает, что алгоритм работает строго по одному и тому же правилу. НС можно привести к такому типу если перетренировать, заставив запомнить все комбинации, но тогда её основные плюсы (обучаемость, самостоятельность) пропадут.

                    Polosatyi прав. НС в первую очередь полезен будет для сжатия с потерями.
                  • –2
                    >практической точки зрения интересны два факта – коэффициент сжатия и время сжатия/восстановления.
                    >Сеть почти 100%-во обучается и отлично функционирует, сжимая данные в 3 раза. "

                    Сожмите сжатый файл (zip или mp3) в 3 раза :)
                    • НЛО прилетело и опубликовало эту надпись здесь
                      • 0
                        Можно.
                        Сеть обучается с учителем. В качестве алгоритма обучения — метод обратного распространения ошибки.

                        Обучающая выборка — множество векторов, которые надо сжать. Она состоит из двух массивов векторов. Первый массив — входные вектора, второй — желаемые выходные вектора. Если нам надо просто сжать данные, то эти массивы идентичны (как в примере). (Почему я говорю «просто сжать»? А потому, что подобную архитектуру можно попробовать использовать как альтернативу двунаправленной ассоциативной памяти).

                        После завершения обучения сеть разбивается на две части — кодер и декодер. На вход кодера подаются данные из обучающей выборки (и не только), выход кодера передается по каналу связи и подается на вход декодера. Декодер восстанавливает исходный вектор.
                        • НЛО прилетело и опубликовало эту надпись здесь
                          • 0
                            Посмотрите в сторону FANN — это библиотека, написанная на С, которая имеет интерфейсы на множество языков, в том числе C++, C#, Ruby, Java, Python, Perl, PHP, Lua. Возможно, это то, что Вам нужно.
                            • НЛО прилетело и опубликовало эту надпись здесь
                              • 0
                                Интересно. Дайте мне знать, пожалуйста, когда Ваша библиотека увидит свет.

                                В неочень далеком будущем планирую большой проект с широким использованием нейронных сетей и вопрос производительности стоит на одном из первых мест. Надо бы поискать что-то подходящее, присмотреться и потестировать. Еще есть вариант написать велосипед что-то абсолютно свое, либо распараллелить какую-то из существующих библиотек с открытым исходным кодом.
                                • НЛО прилетело и опубликовало эту надпись здесь
                      • 0
                        прикольно с теоретической точки зрения, спасибо, почитал с интересом. Но в реальных проектах всё же вряд ли применимо.
                        • 0
                          Спасибо. Лично мне было интересно как она проявляет себя на практике, ведь в Интернете ничего по этому поводу нету — повсюду все те же два абзаца.

                          Ну смотря что Вы имеете ввиду под «реальными проектами» :) Если в проектах компании Google, Microsoft, то да, навряд ли они будут использовать. А если в своем проекте/стартапе, где ключевая роль отводится нейронным сетям, то почему бы и нет. Лично меня очень порадовала скорость работы. Возможно, в совокупности еще с какой-то нейронной сетью они будут давать хорошие результаты. В любом случае, удалось пролить немного света на эту область и тут все еще остается не паханное поле :) Нужно пробовать и экспериментировать.

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.