1. C++ / Говнокод #13190

    +18

    1. 01
    2. 02
    3. 03
    4. 04
    5. 05
    6. 06
    7. 07
    8. 08
    9. 09
    10. 10
    11. 11
    12. 12
    13. 13
    14. 14
    15. 15
    16. 16
    17. 17
    18. 18
    19. 19
    20. 20
    21. 21
    22. 22
    23. 23
    24. 24
    25. 25
    26. 26
    27. 27
    28. 28
    29. 29
    30. 30
    31. 31
    32. 32
    33. 33
    34. 34
    35. 35
    36. 36
    37. 37
    38. 38
    39. 39
    40. 40
    41. 41
    42. 42
    43. 43
    44. 44
    45. 45
    46. 46
    47. 47
    48. 48
    49. 49
    50. 50
    51. 51
    52. 52
    53. 53
    54. 54
    55. 55
    56. 56
    57. 57
    58. 58
    59. 59
    60. 60
    61. 61
    62. 62
    63. 63
    64. 64
    65. 65
    66. 66
    67. 67
    68. 68
    std::tuple<std::vector<long double> , std::vector<std::vector<long double> > , std::vector<long double> >
    inline
    training(const std::size_t hidden_neurons,
             const long double hlr,
             const std::size_t epochs,
             const std::vector< long double > train_ou,
             const std::vector< std::vector< long double > >& train_in,
             volatile bool * reset)
    {
    
    
        auto train_inp = train_in;
        auto train_out = train_ou;
    //    std::cerr << "hidden_neurons: " << hidden_neurons << std::endl;
    //    std::cerr << "hlr: " << hlr << std::endl;
    //    std::cerr << "epochs: " << epochs << std::endl;
    //    std::cerr << "train_inp: " << train_inp << std::endl;
    //    std::cerr << "train_out: " << train_out << std::endl;
    
        const auto mu_inp = mean( train_inp );
        const auto sigma_inp = stand( train_inp );
        train_inp = ( train_inp - mu_inp[ 0 ] ) / sigma_inp[ 0 ];
        const auto mu_out = mean( train_out );
        const auto sigma_out = stand( train_out );
        train_out = ( train_out - mu_out ) / sigma_out;
        const auto patterns = size( train_inp ).first;
    
        std::cout << "patterns: " << patterns << std::endl;
        auto bias = ones( patterns );
        train_inp = merge( train_inp, bias );
        const auto inputs = size( train_inp ).second;
    
        std::vector< long double > err( epochs );
    
        auto weight_input_hidden = ( randn( inputs, hidden_neurons) - 0.5l ) / 10.0l;
        auto weight_hidden_output = ( randn( hidden_neurons ) - 0.5l ) / 10.0l;
    
        for( std::size_t i = 0; i < epochs; ++i ) {
            if ( *reset ) {
                break;
            }
            const auto alr = hlr;
            const auto blr = alr / 10.0;
            for( std::size_t j = 0; j < patterns; ++j ){
                const auto patnum = ( static_cast<std::size_t>( round( randd() * patterns + 0.5 ) ) - 1 ) % patterns;
                const auto this_pat = train_inp[ patnum ];
                const auto act = train_out[ patnum ];
                const auto hval = feval( []( const long double & v ){ return std::tanh( v ); }, this_pat * weight_input_hidden );
                const auto pred = hval * weight_hidden_output;
                const auto error = pred - act;
                const auto delta_HO = hval * error * blr;
                weight_hidden_output = weight_hidden_output - delta_HO;
                const auto m1 = weight_hidden_output * alr * error;
                const auto m2 = 1.0l - (hval^2);
                const auto m3 = dot_operator( m1, m2, std::multiplies< long double >());
                const auto m4 = vec_to_vecvec( m3 );
                const auto delta_IH = m4 * this_pat;
                weight_input_hidden = weight_input_hidden - trans( delta_IH );
            }
            const auto p1 = feval( []( const long double& v ){ return std::tanh( v ); }, train_inp * weight_input_hidden );
            const auto pred = weight_hidden_output * trans( p1 );
            const auto error = pred - train_out;
            const auto error_sq = error ^ 2;
            err[ i ] = std::sqrt( std::accumulate( error_sq.cbegin(), error_sq.cend(), 0.0, std::plus<long double> () ) );
            std::cerr << "err[ i ]: " << err[ i ] << ' ' << i <<  std::endl;
        }
        return std::move(std::make_tuple(weight_hidden_output, weight_input_hidden, err));
    }

    Велосипедостроение

    Запостил: Abbath, 19 Июня 2013

    Комментарии (31) RSS

    • Автор очень не любил классы и структуры?
      Ответить
    • >vec_to_vecvec
      ехал vec через vec
      Ответить
    • > const auto this_pat = train_inp[ patnum ];
      Автор не умеет пользоваться auto либо std::vector< std::vector. Лишнее копирование.
      const auto& this_pat = train_inp[ patnum ];

      Не удивлюсь, если ещё где-то подобные косяки есть. Кто-нибудь видит?
      Ответить
      • Вот в этом "const auto&" вся суть крестов. Как в дзен-коане.
        Ответить
      • да везде
        особенно внутри цикла

        когда надо обучить здоровую нейросетку (и да еще и заюзать генетический алгоритм), такая еботня небось будет считаться не сутки, а месяц
        Ответить
        • > да везде
          > особенно внутри цикла
          Это где например? Больше не вижу.
          Ответить
          • edit: перепишу ответ
            Ответить
            • input_hidden, hidden_output - массивы, первый - двумерный
              this_pat - массив - типа очередная строка из входящих данных ("pattern") - копия
              hval - массив (но тут я могу ошибаться)
              интересно вообще как тут работает train_inp * weight_input_hidden - переопределен где то operator *?
              pred - тоже массив (см. hval)
              и т.д.

              но да, я тут еще раз посмотрел - может и не факт, что сильно оптимальней можно сделать, я слишком анскиллед в ручном кодировании нейросетей
              auto вообще усложняет понимание кода
              Ответить
              • Даже если там везде массивы (худший случай), то везде сработает перемещение (rvalue), так что все верно. Лучше ты врядли перепишешь.
                Ответить
                • ну как минимум это
                  weight_input_hidden = weight_input_hidden - trans( delta_IH );
                  разве нельзя оптимальней переписать?
                  Ответить
                  • weight_input_hidden -= trans( delta_IH );

                    По идеи будет быстрее, но это же нужно лишние операторы перегружать, пространство имен какое-то загрязнять. Так что человеку видимо было лень (мне наверное тоже ради одной строки, если не тормозит), а о boost::operators он не знал (хотя если делать через последний, то я уж не помню получится ли оптимальнее).

                    На самом деле тут все можно переписать через шаблонные выражения (особенно то с учетом auto). Каждая операция накапливает конструирует объект нового типа т_операция<т_источник_данных__или__т_пре дшествующая_операция, ...> и в конце после накопления дерева выражения все это исполняется за раз без лишних обращений к разным страницам. Это будет кешфрендли. Для нейронов с учетом их кол-ва это было особенно бы полезно.
                    Ответить
              • >>переопределен где то operator *

                Не он один
                https://github.com/Abbath/ECOHT-ANN_2/blob/master/Utils.hpp
                Ответить
                • template < typename T >
                  inline constexpr
                  std::size_t
                  dimension ( const std::vector<T> & vector )
                  {
                      return vector.size();
                  }
                  разве размер вектора может быть известен во время компиляции в общем случае?

                  Ну и текст коммитов, конечно, шедеврален.
                  Ответить
                • 1) если функция шаблонная, она уже inline
                  2) не вижу особого смысла городить return std::move( - это и так и так будет подлежать RVO
                  3) const double и другие базовые типы - на мой взгляд лишнее
                  4) рандомайзер можно было бы взять и получше - вон их сколько boost_random.reference.generators

                  а в остальном нехилый труд вложен
                  Ответить
                  • Это все было написано за один день и ночь, как можно увидеть из истории коммитов. Дедлайн был сегодня в 12.00 по Москве. Изначально это был алгоритм на матлабе. По мере запиливания под плюсы был сделан велосипед в виде микроматлаба.

                    std::move ставились в угаре на всякий случай.

                    конст там везде, где можно.

                    Вывод этого рандомайзера похож на вывод матлабовского. Подошло.

                    Авторы 5 раз писали backpropagation. Первые 4 раза получалась классификация, на 5 таки вышла аппроксимация двухмерной функции.
                    Ответить
                  • Поясните кстати про инлайн.
                    Если объявления в заголовочном файле, а код отдельно, где ставить inline?
                    Ответить
                    • самая важная - рядом с телом, но если будет рядом с телом - значит надо и в заголовочном (см. последний абзац)
                      но это в любом случае плохо делить на две части, объясню почему:

                      такое может прокатить, только если никакой другой юнит трансляции не использует эту функцию - только один единственный (там же, где и будет тело описано)
                      но раз так - накой его выносить в заголовочный файл, пусть объявляется и описывается прямо в .cpp

                      не исключаю, что можно в двух юнитах трансляции записать два разных тела инлайновой функции - т.о. будет неплохой способ выстрелить себе в ногу, т.к. скорее всего компилятор в итоге просто возьмет любую из двух
                      так же не берусь предсказать, что будет, если в одном юните эта функция всё же будет инлайновая (linkonce), а во втором - честно экспортируемая
                      Ответить
        • Там один слой всего то.
          Ответить
      • Ссылка была частью оптимизации, но потом оно перестало работать и мы откатились.
        Ответить
    • std::vector<long double> > inline - попахивает CSS'ятиной
      Ответить
    • Какой багор )))
      Ответить

    Добавить комментарий