Построение нейронных сетей в php используя FANN, пример реализации

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

Поскольку данные для анализа формируются в php и мне этот язык сейчас ближе всего, то искалась библиотека с интерфейсом для php. В связи с этим мне порекомендовали FANN (Fast Artificial Neural Network) — открытое программное обеспечение для построения сетей. У этого решения есть апи для 15 языков, так что почти каждый сможет выбрать что-то для себя.

Пример. Распознавание языка текста на странице

Для примера возьмем задачу легкую, но недалекую от нашей реальности и от серьезных задач. Допустим есть 1000 документов, на 3-х разных языках. Пусть это будут французский, английский и польский. Наша задача научить нейронную сеть распознавать язык документа. Для этого мы используем самый простой частотный механизм. Но тем не менее его результаты неплохи. Его суть в том, что у каждого языка с разной частотой в тексте встречаются одни и те же символы. Мы подготавливаем 3 больших куска текста для каждого из языков (английский, французкий, польский), посчитаем для каждого символа частоты. Эти данные мы передадим в нейронную сеть, с указанием какой набор частот принадлежит каждому из языков. Дальше нейронная сеть все сделает сама.

Начнем с установки FANN

Пример, установки привожу для Ubuntu.

1) Нужно установить пакеты libfann1 и libfann1-dev
apt-get install libfann* 


2) Добавить поддержку fann в php
У меня стоит Apache и установлен модуль php5-dev, поэтому я делаю так

# wget http://pecl.php.net/get/fann
# tar xvfz  fann
# cd fann-0.1.1
# phpize
# ./configure
# make


Если при компиляции появиться ошибки и среди них будет вот такая

fann.c:393: error: ‘zif_fannOO___set’ undeclared (first use in this function)

то следует отредактировать файл php_fann.h и за комментировать строчку 28 #define PHP_FANN_OO 1
После этого заново скомпилировать.

В итоге у нас генерируются модули которые нужно подключить к php
sudo cp -R ./modules/* /usr/lib/php5/20090626+lfs/


И в php.ini дописать
extension=fann.so


Перегрузили апач и проверили что все ок
php -m | grep fann


Решение задачи

Для этого нужно выполнить 2 шага:
1) Обучить сеть (первый листинг)
2) И использовать готовую модель для классификации (второй листинг)

Приведу пример для первого этапа и сразу укажу ссылку на документацию

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

Файл train.php
<?php

/*
 * Задаем параметры сети. 256 - это количество входов, оно должно равняться количеству ваших параметров,
 * По хорошему в этом примере должно быть намного меньне, равно количесству букв в алфавитах.
 * 128 - это количество нейронов в промежуточном слое. Здесь нуужно экспериментальным путем подбирать это число.
 * 3 это количество выходящих сигналов. По скольку у нас 3 языка, то для каждого будет возвращена вероятность
 * 1.0 - connection_rate - его лучше не менять
 * 0.7 - learning_rate - описано здесь хорошо http://www.basegroup.ru/glossary/definitions/learning_rate/
 * */
$ann = fann_create(array(256, 128, 3), 1.0, 0.7);

/*
 * Первый параметр - указатель на нашу сеть, второй - обучающие данные.
 * Мы загружаем 3 порции данных.Каждая порция состоит их входящих показателей и эталонных результирующих.
 * В нем мы сообщаем, что при таких показателях, как мы сейчас передаем, нужно весь вес
 * отдавать на первы нейрон (array(1, 0, 0) // Outputs). при загрузке других типов данный мы смещаем вес на другой нейрон
 * generate_frequencies - просто расчитывает частоты.
 *
 * Последние 3 параметра это
 * - максимальное кол-во итераций
 * - максимальное кол-во ошибок
 * - промежуток между выводами информации 
 * 
 * В файлах en.txt, fr.txt, pl.txt хранится текс размером где-то в 10000 символов для конкретного языка
 * */
fann_train($ann,
    array(
        array(
            generate_frequencies(file_get_contents("en.txt")), // Inputs
            array(1, 0, 0) // Outputs
        ),
        array(
            generate_frequencies(file_get_contents("fr.txt")), // Inputs
            array(0, 1, 0) // Outputs
        ),
        array(
            generate_frequencies(file_get_contents("pl.txt")), // Inputs
            array(0, 0, 1) // Outputs
        ),
    ),
    100000,
    0.00001,
    1000
);

/*
 * Сохранить нашу модель в файл. в дальнейшем ее можно использовать для класификации
 * */
fann_save($ann,"classify.txt");


/*
 * Функция расчета частот
 * */
function generate_frequencies($text){
    // Удалим все кроме букв
    $text = preg_replace("/[^\p{L}]/iu", "", strtolower($text));

    // Найдем параметры для расчета частоты
    $total = strlen($text);
    $data = count_chars($text);

    // Ну и сам расчет
    array_walk($data, function (&$item, $key, $total){
        $item = round($item/$total, 3);
    }, $total);

    return array_values($data);
}

?>


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

Файл run.php
<?php

/*
 * Загружаем модель из файла. Эту модель мы создали на предыдущем шаге.
 * */
$ann = fann_create("classify.txt");


/*
 * Ниже я в нашу сеть передаю 3 текста на разных языках
 * Смотрим результат
 * */

$output = fann_run($ann, generate_frequencies("ANN are slowly adjusted so as to produce the same output as in
            the examples. The hope is that when the ANN is shown a new
            X-ray images containing healthy tissues"));

var_dump($output);

$output = fann_run($ann, generate_frequencies("Voyons, Monsieur, absolument pas, les camions d’aujourd’hui ne se traînent pas, bien au contraire. Il leur arrive même de pousser les voitures. Non, croyez moi, ce qu’il vous faut, c’est un camion !
     - Vous croyez ? Si vous le dites. Est-ce que je pourrais l’avoir en rouge ?
     - Bien entendu cher Monsieur,vos désirs sont des ordres, vous l’aurez dans quinze jours clé en main. Et la maison sera heureuse de vous offrir le porte-clé. Si vous payez comptant. Cela va sans dire, ajouta Monsieur Filou.
     - Ah, si ce "));

var_dump($output);

$output = fann_run($ann, generate_frequencies("tworząc dzieło literackie, pracuje na języku. To właśnie język stanowi tworzywo, dzięki któremu powstaje tekst. Język literacki ( lub inaczej artystyczny) powstaje poprzez wybór odpowiednich środków i przy wykorzystaniu odpowiednich zabiegów technicznych.
            Kompozycja - jest to układ elementów treściowych i formalnych dzieła dokonanych według określonych zasad konstrukcyjnych.
            Kształtowanie tworzywa dzieła literackiego jest procesem skomplikowanym i przebiegającym na wielu poziomach.
            Składa się na nie:"));

var_dump($output);

?>


Результат

Наша модель сгенерировала следующие ответы по текстам.
В первом случае модель решила что ей на вход передали английский язык (98%) и она права

array(3) {
  [0]=>
  float(0.98745632171631)
  [1]=>
  float(0.0094089629128575)
  [2]=>
  float(0)
}


Во втором тексте она приняла решение в пользу французского и снова оказалась права

array(3) {
  [0]=>
  float(0)
  [1]=>
  float(0.99334162473679)
  [2]=>
  float(0)
}


Третий текст она тоже правильно распознала, как польский

array(3) {
  [0]=>
  float(0.015697015449405)
  [1]=>
  float(0)
  [2]=>
  float(1)
}


Некоторые пользователи жалуются что нейронные сети выдают вероятности, а не конкретный ответ. Для тех кто в танке добавлю, что в нашем мире все основано на вероятностях. За правильный ответ нужно считать тот который верен хотя бы на 90%, если меньше то сеть нужно до обучать, что бы улучшить классификацию.

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

Перейти на сайт FANN
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама
Комментарии 19
  • +2
    >У меня стоит Apache и установлен модуль php5-dev, поэтому я делаю так
    а через инсталяху pecl не получится поставить из-за описанной выше ошибки в коде?

    И ещё есть вопрос: не подскажите какие виды нейронных сетей можно обучать непрерывно, а не по выборке?
    • 0
      Сети Кохонена.
      • 0
        спасибо! Похоже самоорганизующиеся сети Кохонена близко к тому, что нужно.

        Правда, не совсем понятно как проверять результат. Задача идентифицировать текстовый спам и «проблемные материалы» (например, не тот раздел) попадающие на ресурс — связи могут быть типа «заглавие содержит или не содержит слова такие-то, такие-то, текст такие слова, у пользователя имя содержит такие-то символы, а подано в таком-то регионе и в такой-то раздел». Вычислить все случаи и закономерности трудно, но при этом глянув на поданный материал я понимаю, что это нужно удалить. Поэтому пришла мысль обучить алгоритм путем постепенно добавления в него плохих (и плюс хороших материалов, если нужно) и смотреть правильно ли он распознает. Ну и по ходу добавлять проблемные материалы, которые появляются в ходе работы.
      • +1
        При попытке установить через pecl другая ошибка была и я не нашел ее решения
        Через pecl его можно установить так

        sudo pecl install channel://pecl.php.net/fann-0.1.1
        


        Вот ошибка кому интересно

        # 
        
        downloading fann-0.1.1.tgz ...
        Starting to download fann-0.1.1.tgz (9,509 bytes)
        .....done: 9,509 bytes
        3 source files, building
        running: phpize
        Cannot find config.m4.
        Make sure that you run '/usr/bin/phpize' in the top level source directory of the module
        
        ERROR: `phpize' failed
        
        
      • –1
        Извиняюсь, что не в тему, но вот конкретно эту самую задачу я бы решал гораздо проще, а именно по присутствию/отсутствию типичной для языка диакритики.

        Если же таковой нет, тогда частотный анализ незаменим.
        • 0
          В русском тексте могут быть, например, английские слова и как тогда? Считать частоты. Поэтому, я думаю, наличие/присутствие символов само по себе не годиться.
        • 0
          У меня проблема и видимо из-за ошибки компиляции, гугл молчит не подскажете в чем дело?

          /usr/sbin/apache2: symbol lookup error: /usr/lib/php5/20090626+lfs/fann.so: undefined symbol: fann_create_array

          Debian 6 php 5.3 apache 2.2
          • 0
            У тебя вроде как с модулем fann.so проблема. Скомпилируй заново модуль, да так что бы без ошибок в процессе.
            • 0
              переустановил сам fann дважды и трижды пересобрал модуль php одно и то-же все собирается без ошибок
              на

              fann_create()

              ругается что не хватает параметров, на

              $ann = fann_create(array(256, 128, 3), 1.0, 0.7);

              падает и пишет в error.log apache ту ошибку
        • –1
          Что-то половина статьи мне напоминает это :)
          • +2
            только тут чище и с примером и нас избавили от большого кол-ва вопросов собрав все в одном месте.
          • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              У меня. Собственно все описанные действие в статье проводились в Ubuntu 10.04. Плюс php 5.3.
              • +1
                А что не получается? Какие ошибки выдает?
                • НЛО прилетело и опубликовало эту надпись здесь
            • 0
              Ранее тоже стояла задача для определения языка.

              Первая версия была очень простая, аналогично Вашей: используя каждые алфавиты, вычислять количество символов принадлежащих к определенному алфавиту, и у кого будет больше, тот и победитель.
              На практике эта система хорошо работала на «до 5» языках, в которых нету пересечений. Пересечений, я имею ввиду пересечений символов в алфавитах. К примеру, если посмотреть на украинский и русский языки. Разногласия в алфавитах очень маленькие. И таких алфавитов очень много (если взять европу, то там вообще почти все такие, которые используют в основном английский алфавит + некоторые свои дополнительные символы). Как пример (очень хорошо помню), это краткие фразы «привет Вася и Петя» определялись как белорусский и русский.

              Далее, когда поняли, что по частоте букв будет не очень то хорошо, пришли к другому решению. А что если загнать все слова с каждого словаря в конечный автомат? В результате, мы имели наборы множества слов для каждого отдельного языка. Точность определения кратких словосочетаний была намного выше да и текстов с большой длинной тоже. Но, есть очень большой минус для такой системы: очень много места занимают все эти словари + обработка.

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

              P.S. Пиарюсь:
              Паралельно со все этой задачей, запили пакет github.com/ZhukV/LanguageDetector
              Проверяли на прогоне текста приложений с Apple iTunes для одного из наших проектов. Точность была очень высока.

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