Pull to refresh

Comments 59

Можете подсказать хорошие симуляторы нейросетей? Особенно интересуют те, которые умеют использовать GPU для моделирования.
Симуляторов есть великое множество и выбор — это во многом вопрос вкуса. Лично мне очень нравится Netlab by Ian Nabney, на нем сделаны примеры для упомянутой в списке литературы «классической» книжки Кристофера Бишопа. Пакет Netlab — бесплатный. С одной стороны, в этом пакете реализованы мощные оптимизационные алгоритмы, а с другой стороны — метод реализации самих нейронных простой, основан на матрицах. Встроенный в MATLAB пакет Neural Network Toolbox тоже хороший, но более запутанный. Что касается GPU — на мой взгляд, самый сильный пакет это Theano, используйте его. Этот пакет позволяет одни и те же алгоритмы запускать как на CPU, так и на GPU, очень удобно. Еще с GPU умеет работать MATLAB новых версий — просто объявите вашу матрицу как тип gpuArray — и она автоматически будет считаться на видеокарте.

Еще ремарка насчет GPU, если собираетесь им заняться: не советую покупать карты Nvidia серии 6, типа 650GTX и т.д. Несмотря на хорошие параметры (количество процессоров и память), они плохо подходят для вычислительных задач, прироста в мощности может вообще не произойти.
Спасибо! К сожалению, не хватает кармы что-бы плюсануть ваш коммент и статью.
А какие видеокарты хорошо подходят?
Radeon R9 270, к примеру, как?
Библиотеки Theano и MATLAB (через gpuArray) используют технологию NVidia CUDA, видеокарты AMD для них, увы, не подходят вообще. Может быть, уже есть хороший софт для обучения нейронных сетей на основе AMD, но я о таком пока не слышал. Смотрите совместимость с CUDA на сайте NVidia, выбирайте новые карточки — поколения 7x или, в крайнем случае, топовые карточки поколения 5x.
Если планируете использовать Cuda-ConvNet Крижевского, то надо брать GTX 780/Titan. На остальных будет работать сильно медленнее.
Сам не проверял, но он ответил мне именно так.
— прироста в мощности может вообще не произойти

не могли бы обосновать почему это?

у меня перемножние 2 матриц 1000х1000 на GTX680 происходит примерно в 1000 быстрее чем на ЦПУ.
Обосновать не могу, в устройстве видео-карт NVidia не разбираюсь. Но так пишут в группах по глубокому обучению, неоднократно приходилось видеть. Например вот, пишет Крыжевский:

«Note: A Fermi-generation GPU (GTX 4xx, GTX 5xx, or Tesla equivalent) is required to run this code. Older GPUs won't work. Newer (Kepler) GPUs also will work, but as the GTX 680 is a terrible, terrible GPU for non-gaming purposes, I would not recommend that you use it. (It will be slow).»

Взято отсюда: code.google.com/p/cuda-convnet/wiki/Compiling

Сейчас только что попробовал на машинке с GTX650 vs Core 2 Quad (да, процессор уже не новый, Core i7-4* его в пару раз побыстрее будет):

>> A=rand(1000,1000);, B=rand(1000,1000);
>> tic, C=A*B;, toc
Elapsed time is 0.101666 seconds.
>> A_gpu = gpuArray(A);,B_gpu=gpuArray(B);
>> tic, C_gpu=A_gpu*B_gpu;, toc
Elapsed time is 0.001841 seconds.

Перемножение получилось в 10 раз быстрее.

>> tic, D = inv(A);, toc
Elapsed time is 0.142409 seconds.
>> tic, D_gpu = inv(A_gpu);, toc
Elapsed time is 0.229812 seconds.

А взятие обратной матрицы — почти в 2 раза медленнее.
UPD Умножение в 50 раз быстрее — сорри, «клинит» немного под вечер ))
чтобы не быть голословным

    void TestCPUSgemm_1024x1024x1024()
    {
        const size_t m = 1024;
        const size_t k = 1024;
        const size_t n = 1024;

        CPUSgemm( AAA, BBB, CCC, m, k, n );
    }


    void TestGPUSgemm_1024x1024x1024()
    {
        const size_t m = 1024;
        const size_t k = 1024;
        const size_t n = 1024;

        cublas::Sgemm( 1.0f, AAA, BBB, 1.0f, CCC, m, k, n, fnMsg );
    }



Execute: [TestCPUSgemm_1024x1024x1024]: 6.176978
Execute: [TestGPUSgemm_1024x1024x1024]: 0.009492

Intel i7 CPU 960 @ 3.20 via GTX680

да для ЦПУ это один поток, если нагружу все 8 разрыв резко сократится, а если буду использовать MKS то еше быстрее, но проблема в том что один клиент хостится на Amazon, то там да intel, а если Microsoft, то уже АМД. а чинить код под каждый новый — оно мне надо?

Да цпушное перемножение самое тупое, но это простой легко проверяемый пример — в реальности тонны хитровыделанных формул из моделирования.

  void CPUSgemv( const float* A, const float* B, float* C, const int k, const int n )
    {
        for( int x = 0; x < n; x++ )
        {
            C[x] = 0.0f;

            for( int y = 0; y < k; y++ )
            {
                int index = x * k + y;   // fortran style index

                C[x] += ( B[ index ]  * A[ y ] );
            }
        }
    }

    int CPUSgemm( const float* A, const float* B,  float* C, const int m, const int k, const int n )
    {
        for( int r = 0, index = 0; r < m * k; r += k, index += n )
        {
            CPUSgemv( &A[r], B, &C[index], k, n );
        }
        return 0;
    }



да мне не нужны double — используется в основном float, данных и так террабайты
Cпасибо за толковый код, тоже попробую у себя немного по-позже. Удачи!
Да цпушное перемножение самое тупое


У Вас матрицы по 4 Мб (в сумме 12) тогда как кэши у процессора — L1 = 32 Кб, L2 = 256 Кб, L3 = 8 Мб, при этом вычислительная плотность вообще никакая — все упирается в память, по 2 FP операции на 2 чтения и 1 запись. Такой код работает медленно и на GPU. Но Вы его туда и не портировали, а банально взяли уже оптимизированный код — cublas, который тщательно вылизывался целой командой.

В данном примере параллелить на 8 тредов код в таком виде бесполезно — он практически не ускорится. Там надо улучшать локальность операций с памятью, а традиционный метод для этого — блочное перемножение матриц. На Ваших данных это даст выигрыш в скорости примерно на порядок (разница в bandwidth между L1 и обычной памятью). И вот после этого код уже будет параллелиться нормально, что даст еще где-то x5 выигрыш на 8 потоках. Обе оптимизации работают для любых CPU и реализуются для данного примера довольно просто (стандартная студенческая задача в МГУ, если интересно). Но для реального кода заниматься подобными вещами заведомо глупо, поскольку если Вам нужно быстро работать с матрицами 1024x1024, то Вы берете оптимизированные другими библиотеки BLAS, которые есть как с заточкой на конкретное железо (MKL, ACML) так и без оного (ATLAS, uBLAS). У всех этих библиотек, кстати, примерно одинаковый интерфейс (собственно BLAS) что облегчает их взаимозаменяемость. Впрочем особой нужды их заменять, в общем-то, нет: та же MKL прекрасно работает на процессорах AMD. Она будет несколько субоптимальна конечно, но все равно в десятки раз быстрее Вашего самописного варианта.

То есть ровно то о чем я и писал изначально: Вы сравниваете очень плохой самописный CPU-код vs тщательно оптимизированная библиотека на GPU.

Что до fp32, то у него точность при перемножении матриц 1024x1024 никакая. Если надо потыкать пальцем в небо и что-то там приблизительно посчитать на «хороших» (со слабо варьирующимися по порядку величины) матрицах, то это работает. Берете «плохие» матрицы или просто перемножаете хорошие матрицы подольше — вылазит здоровенная погрешность. Но на таких матрицах, кстати, можно и алгоритм быстрого перемножения матриц Штрассена применять или аналоги, он дает асимптотику лучше чем O(n^3) и для n = 1024 это уже должно быть ощутимо.
перемножние 2 матриц 1000х1000 на GTX680 происходит примерно в 1000 быстрее чем на ЦПУ.


У Вас очень плохой CPU-код используется как референс (или уникально слабый CPU) + fp32 а не fp64 потребные для перемножения реальных матриц 1000x1000 :)
Во-первых, умножение двух матриц MATLAB'ом это ни в какой вселенной не плохой CPU-код. Такие операции в MATLAB очень оптимизированы, там используются С-шные либы. Во-вторых, если CPU будет сильный — преимущество GPU будет еще меньше, разве нет? :)
Так я с Вами как раз не спорю, я написал комментарий про «прирост скорости в 1000 раз». Прирост x50 на относительно слабом CPU и мощной видеокарты для хорошо параллелящейся задачи типа перемножения матриц — реалистично. x1000 — нет. Вы привели очень хороший пример. Несколько удивительно только что обращение матрицы работает на GPU хуже.
Извините, это я вас случайно перепутал с предыдущим оппонентом, и продолжил дискуссию, как с ним )))

Хорошего дня!
Раз уж такая тема, то порекомендую мою реализацию CNN для Матлаба github.com/sdemyanov/ConvNet
Там есть версия как на Матлабе, так и на С++, работают идентично.
До GPU руки еще не дошли, но в ближайший месяц наверно сделаю.
Cпасибо большое, рекомендация теме соответствует.
Отличный материал, спасибо! Когда-то занимался немного нейросетями — все очень по делу и правильно написано. Будет очень интересно почитать «практическую» часть.
Хорошая статья. Рекомендации относятся не только к нейронным сетям, но вообще к методам машинного обучения.
Спасибо! Да, это правда — все что здесь написано, работает для разных моделей из арсенала средств машинного обучения: машин опорных векторов, логистической регреcсии, линейных моделей типа Адалина, а также статистических моделей, которые с нейросетями обычно редко ассоциируют: ARMAX, ARMA, AROMA, ARIMA + все это же с приставкой N- (=«нелинейный»). Просто нейросети — очень распространенная парадигма, поэтому я использовал ее как базовый пример для своей заметки.
Вообще здорово, что нейронные сети постепенно возвращаются.
Согласен. :-) В них есть романтика, которой нет в других моделях, основанных на чистой математике.
«Используйте новые алгоритмы обучения: Левенберга-Марквардта, BFGS, Conjugate Gradients и др.»

Я бы не сказал, что это новые алгоритмы. Это просто более умные способы обновления весов, при наличии производных. А сами производные считаются все тем же backpropagation.
Спасибо за комментарий. Вы совершенно правы, но противоречий с моим текстом я не вижу.

Существует два определения backpropagation, в узком и широком смысле. В узком смысле backpropagation это техника для вычисления производных путем пропускания некоторой величины после прямого прохода. Эти производные дальше используются совместно с градиентными оптимизационными алгоритмами 1-го или 2-го порядка для коррекции весов нейросети. В широком, изначальном смысле Вербоса-Румельхарта-Хинтона, backpropagation — это техника получения производных 1-го порядка + простейший оптимизационный алгоритм, градиентный спуск, «два-в-одном». Когда говорят, «обучаю нейронные сети методом backpropagation» — как правило, имеют ввиду эту связку, а не способ получения самих производных по весовым коэффициентам. А под «новыми алгоритмами» я имел ввиду «более новые, чем backpropagation в широком смысле». Сами по себе методы оптимизации BFGS, Conjugate Gradients имеют, конечно, отдельную длинную историю.
Для Conjugate Gradients в русском языке есть прекрасный термин — сопряжённые градиенты.
Мой пост адресуется прежде всего новичкам. Поэтому я дал английский вариант, чтобы им было легче ориентироваться.
Небольшая неточность. Глубокие сети это не те, где много нейронов, а те, где много слоев с нелинейностью.
В основном соглашусь с замечанием.

Действительно, у глубоких нейронных сетей (deep neural networks) скрытых слоев нейронов обычно больше, чем один — например, 4 или 6. Хинтон в своей пионерской статье 2006-го года по DNN пишет, что в неокортексе человека 6 слоев нейронов, если я не ошибаюсь, и это его дополнительно мотивировало. Но много нейронов — это тоже очень важный аспект, характеризующий глубокие нейронные сети. Я видел на хорошей конференции работу, где под «глубокой нейронной сетью» понималась SVM с миллионом свободных параметров, где вообще никаких слоев нету.

Моя личная точка зрения, я здесь абсолютно не претендую на истину в последней инстанции — все же именно большое количество настраиваемых параметров более выпукло характеризует эту научную область, поскольку нейронные сети большим количеством слоев активно встречаются в природе с 1990-х годов, и используются сейчас — просто они по-другому называются. Я имею ввиду рекуррентные нейронные сети (Recurrent Multilayer Perceptron, RMLP, сеть NARX и сеть Эльмана), которые обучаются методом обратного распространения ошибки во времени (Backpropagation Through Time, BPTT). Посмотрите MATLAB — там эти модели сидят давно и плотно в Neural Networks Toolbox, это известные вещи, не экзотика какая-нибудь. Техника такого обучения состоит в том, что рекуррентная нейронная сеть «разворачивается назад во времени» — представляется как многослослойный персептрон с большом количеством слоев, каждый слой соответствует какому-то прошлому такту. По сути получается многослойный персептрон с большим количеством слоев, у Данила Прохорова из Тойоты количество слоев было h=20, который потом обучают. Чем не глубокая сеть?
Рекуррентные сети обычно считаются глубокими, вы правы. Это как раз по тому, что они разворачиваются на много слоев. Но терминология здесь уже устоялась. Deep learning модель — это последовательная композиция (или иерархия) нелинейных преобразований. И не важно, сигмоидальные это функции, или svm, или какие-нибудь sum-product сети.

Нелинейность — здесь ключевое слово, так как последовательность линейных сводится к той же линейной. Вот доказательство — A(Bx + a) + b = ABx + Aa + b = Wx + c :)
Думаю, в главном мы друг друга поняли, не вижу смысла долго спорить дальше. )

Рекуррентные сети являются deep-моделями де-факто, не не считаются такими из-за устоявшейся терминологии. Да, это как в медицине — как привыкли, так и пишут. То, что последовательность линейных слоев сводится к одному линейному слою — поверьте, я это понимаю. ) Кстати, у SVM внутри нету никаких иерархий нелинейных преобразований — это по сути линейная модель, ее способность работать с нелинейными данными происходит из-за того, что она оперирует в пространстве большой размерности, неявно заданном нелинейным ядерным преобразованием.
6 слоев в неокортексе тут совсем не причем. Сам неокортекс разбит на регионы, которые между собой связаны иерархически, параллельно и последовательно. Сами слои составляют регионы и обеспечивают возможность обучаться, возможность локальной ингибиции (внимания), а так же возможность соединиться с другими регионами. Некоторые слои являются связующими с другими регионами, некоторый обеспечивают обучения, некоторые отвечают за входящие сигналы, некоторые за выходящие. Аналогия с искусственными нейронными сетями производится скорее на уровне свзяи между различными регионами неокортекса, нежели между слоями. Если совсем честно, то ученые на 100% не знают что именно делают все 6ть слоев и зачем они нужны в «вычислительном» смысле, но они достаточно близко.
Кстати, интересно было посмотреть сравнение детерминированных (классических нейронных) и вероятностных (DBN) сетей. Какие плюсы, какие минусы, на каких задачах показали себя лучше. Может быть, кто-нибудь встречал такой обзорный материал?
Если найдете такую — пришлите. Тоже с удовольствием прочитал бы. )
А какое конкретно сравнение вы бы хотели увидеть? DBN — это генеративные модели, которые могут использоваться для задачи дискриминации в случае их дополнительной подстройки с использованием размеченных данных. Но в таком случае они становятся обычными глубокими детерминированными нейронными сетями.
Существуют изначальные модели нейронных сетей со стохастическими нейронами — по ним работ достаточно много.
Меня скорее интересуют не принципы работы, а результаты применительно к разным областям и разным условиям. Например, я недавно попробовал использовать RBM для предобучения классификатора выражений лиц по точкам (что-то такое, чтобы у вас было представление), но оказалось, что энергия при обучении практически не уменьшается, а само предобучение только ухудшает классификацию. В то же время, обычный детерменированный перцептрон показал вполне неплохие результаты. И вот интересно, что повлияло на результат — то ли слабый разброс в данных (в нормализованном фрейме ключевые точки на всех снимках находятся близко друг к другу), то ли малый размер входного вектора (~70 входных точек, по 2 координаты в каждой), то ли ещё что-то. Анализировать такие комплексные системы с кучей параметров довольно сложно, поэтому нужно знать их «привычки», чего от них можно ожидать, а чего нет.
Мне кажется, что ваша задача действительно больше подходит для «классических» многослойных персептронов или аналогов, использовать RBM тут ни к чему. Сложность нейросети (= количество настраиваемых параметров), должна быть адекватна сложности задачи. Ваша размерность входных данных, 140, вполне подходит для классических моделей.

Можно еще дополнительно уменьшить размерность данных для облегчения обучения, популярнейший метод — метод главных компонент. Для этого, сложите все ваши векторы (N штук) в одну большую матрицу X с N строк и 140 столбцов, загрузите в MATLAB, и примените к ним функцию pca:

T = pca(X, 'NumComponents', L);

здесь X — ваши данные, L — новая размерность, пусть это будет 20 для вашего случая. Результат процедуры, матрица T — это будет новая матрица понижения размерности размером 140xL. Умножайте каждый «сырой» вектор на нее перед подачей на нейросеть

x_small = x*T;

и вуа-ля.

Что касается RBM, ее идеология — использование на данных большой размерности ~ 1000 и больше, когда препроцессинг не делается вообще, работа идет с сырыми данными. В этом одна из ее главных «вкусностей» — отсутствие предметно-ориентированного препроцессинга, минимум априорной информации о процессе, с которым нужно работать.
Формально Хинтон часто позиционировал RBM как раз в качестве альтернативы PCA и прочим методам снижения размерности.
Входное пространство действительно небольшое и уменьшать его каким-либо методом не самая важная задача в этом случае, потому что обучение и так будет быстрое.
С другой стороны «обучение» стека RBM — это пока сродни искусству. Слишком много мета-параметров и различных модификаций, что ведет к тому, что легко пропустить ту область пространства мета-параметров, которая приведет к улучшению качества.
Согласен с вами.

Предобучение RBM и PCA — суть одно и то же. Но у Хинтона есть дополнительная практическая польза, которая состоит в том, что его метод — онлайновый, «пример-за-примером». Его удобно использовать, если у вас миллионы векторов, тогда как классический PCA требует загружать в память всю матрицу данных целиком.
Спасибо за подробный ответ. Да, как раз PCA и был на предыдущей стадии :) Однако PCA — это линейное преобразование, а специфика задачи подсказывает нелинейные зависимости между точками, поэтому и возникла идея использовать другие, более сложные трансформации. Не то, чтобы я многого ожидал от RBM в такой задаче (это скорее было совмещением приятного с полезным), но результаты оказались уж совсем никакими. Поэтому и хочется знать, в какую сторону более перспективно копать, чтобы не тыкаться наугад, проводя десятки медленных экспериментов.

Кстати, раз уж зашла речь. У меня есть идея использовать RBM в обратную сторону — с её помощью найти наиболее интересные точки/регионы на изображении, и дальше уже работать с полученными точками (интуитивно, вероятностная сеть будет давать максимальный отклик в местах наиболее часто повторяющихся паттернов). Возможно, вы где-то уже видели такие идеи?
Я бы все-таки порекомендовал вам попробовать PCA так, как я написал выше. :)

PCA, конечно, линейное преобразование — но оно таким строго является, если вы используете все компоненты, т.е. в вашем случае когда L=140. В таком случае вы получите матрицу T, которая будет линейно переводить данные из исходного пространства в другое. Если же вы выкинете часть компонент (они сортируются в коде выше в порядке их «значимости»), все сразу становится интереснее, это уже не строго линейное преобразование, а сжатие. =)

Насчет использования RBM в обратную сторону мне трудно тут что-то посоветовать. Пробуйте. =) В Machine Learning разрешено все, что не запрещено в Уголовном Кодексе.
Нет, вы не поняли. PCA я уже пробовал, причём именно так, как вы описали :) Разве что реализацию не из MATLAB-а брал, а из других подручных библиотек. Результат действительно лучше, но всегда хочется ещё выше, быстрее, сильнее. Вообще, если вам интересно, вся эта затея с точками взята из так называемых моделей активного образа (Active Appearance Models). Там PCA используется аж в трёх местах — для преобразования матрицы точек, для преобразования матрицы пикселей внутри внешнего контура точек, а затем ещё и к совместной матрице результатов (опционально). Но все авторы говорят о том, что PCA в данной ситуации неидеален — как для модели точек, так и для модели текстуры под этими точками. Поэтому я перманентно ищу варианты обхода линейности, даже если не для практических целей, то для удовлетворения своих теоретических переживаний :)
Ну, мне уже нечего добавить — могу только пожелать удачи =) Читайте статьи по вашей теме в Google Scholar и посредством sci-hub.org, как это делают другие, может быть, получится найти хорошее нелинейное преобразование.

Удачи!

Да, интересная статья. Салахутдинов пытается реанимировать Mixture Density Networks Бишопа путем их «углубления». Плохо обусловленные задачи, когда одному X соответствует несколько Y — бывает суровая жизненная действительность, обычная регрессия и персептроны на них не работают принципиально, они обучаются среднему нескольких Y.

MDN-сети по способу обучения так же, как и нынешние новые глубокие модели, имеют два этапа: предобучения без учителя, когда с помощью ЕМ-алгоритма находятся центры кластеров и градиентую настройку весов, в чистом виде «fine tuning». Идея красивая, загвоздка в том, что количество гауссианов нужно было задавать а-приори + ЕМ-алгоритм очень жадный до вычислительных ресурсов и не всегда сходится. В итоге, 20 лет назад все затихло — посмотрим, что сейчас скажут специалисты по deep learning и их верные GPU.
А разве нельзя попробовать сделать модификацию, где кол-во гауссиан адаптивно? По аналогии с модификацией EM-алгоритма, где плохо-обусловленные кластеры разбиваются.
Обучайтесь на Train, периодически проверяя качество модели на Test. Для финальной непредвзятой оценки – Validation.

так на то она и кроссвалидация, что бы производить валидацию на Validation, а финальную оценку на Test
Можно одно и то же называть по-разному, суть дела это не меняет. Для меня более привычна система обозначений, когда Train используется для обучения, Test — для периодического контроля в время обучения (то есть, на самом деле тоже неявно используется для обучения!) а Validation — это именно для финальной оценки, не Test. Если вам привычнее называть это по-другому (так тоже обозначают) — это тоже ок.

Кросс-валидация — это прием обучения, когда выборка делится на k равных частей, взятых произвольным образом. Затем на k-1 частях производится обучение, на одной оставшейся — тестирование. И так k раз, конечный результат усредняется. Но как я уже писал, это вещь бывает коварная из-за своего «произвольного» стиля отборка данных в k частей. Валидация должна быть правильная, т.е. максимально приближенная к реальной жизненной ситуации, с которой столкнется система распознавания.
когда выборка делится на k равных частей

это называется k-fold cross-validation, подвид общей теории валидации
Совершенно верно, именно так это и называется.
Благодарю за статью. Недавно на одном ресурсе рекомендовали библиотеку Shark в которой еще до кучи всего по machine learning кроме ANN — что скажете по её поводу, есть ли смысл использовать, или Matlab / Statistica — наше всё?
Посмотрел эту библиотеку — раньше о ней не слышал, но выглядит неплохо, есть базовый «джентельменский набор» алгоритмов + новые методы. Если все, что там задекларировано, не глючит, и вам нравится работать с С — почему бы и нет.

Моя симпатия к MATLAB продиктована тем, что в нем удобно отлаживать такого рода алгоритмы вообще: выводить графики, диаграммы, читать/писать в CSV и Excel файлы и т.д. Промышленные же системы нужно, конечно, реализовывать на С/Java/C# — смотря, что вам нужно. Поэтому, как по мне — лучше всего, когда у одной библиотеки есть MATLAB и С интерфейсы, как у LibSVM. Или как тут один человек сделал в своей либе по сверточным сетям — смотрите в комментариях выше.

Успехов!
Понял, спасибо еще раз за отличную и своевременную статью.
Существует большое количество программных пакетов, в основном на MATLAB и Python, которые позволяют обучать нейронные сети, при этом контролировать процесс обучения и тестирования


octave много кто использует для расчетов нейронных сетей? matlab и STATISTICA то платные…
Octave это заменитель MATLAB — не на 100% совместимый, конечно. Тот же NetLab by Jan Nabney вполне может с ним работать (не пробовал).
Sign up to leave a comment.

Articles

Change theme settings