ニューラルネット
ニューラルネットなんて、古すぎなんで、ネタですらないんですが...。誰かの参考になると良いですね。とりあえず、久しぶりすぎて、忘れまくり。第一関門 XOR 学習ができたので、載っけます。AND, OR は、main 内の set のコメントアウトで調整してください。VC7.1での動作を確認しています。
参考
- ASIN:4563013862 視覚と記憶の情報処理
- 大学時代の教科書
#include "stdafx.h" #include#include #include #include #include #include #include #include using namespace std; class Network { public: static const int inputSize = 2; static const int middleSize = 2; static const int outputSize = 1; static const double sigmoidGain; private: // 添え字の [ ??? + 1 ] はバイアス用 double inToMiddle_[ middleSize ][ inputSize + 1 ]; ///< 入力層から中間層へのリンク double middleToOut_[ outputSize ][ middleSize + 1 ]; ///< 中間層から出力層へのリンク class Skip { const double* array_; int skip_; public: typedef double result_type; Skip( const double* array, int skip ) : array_( array ), skip_( skip ) { } double operator () () { double result = *array_; array_ += skip_; return result; } }; /// [ -0.05, 0.05 ] の乱数発生 static double randWeight() { return 0.1 * rand() / static_cast< double >( RAND_MAX ) - 0.05; } /// 一つの層計算する static double forward( const double* in, const double* weight, int size ) { return 1.0 / ( 1.0 + exp( -sigmoidGain * std::inner_product( in, in + size, weight, weight[ size ] ) ) ); } public: Network() { std::generate( *inToMiddle_, *inToMiddle_ + sizeof ( inToMiddle_ ) / sizeof( double ), &randWeight ); std::generate( *middleToOut_, *middleToOut_ + sizeof ( middleToOut_ ) / sizeof( double ), &randWeight ); } void calc( const double* in, double* middle, double* out ) const { for ( int i = 0; i < middleSize; ++i ) { middle[ i ] = forward( in, inToMiddle_[ i ], inputSize ); BOOST_ASSERT( middle[ i ] >= 0 ); } for ( int i = 0; i < outputSize; ++i ) { out[ i ] = forward( middle, middleToOut_[ i ], middleSize ); BOOST_ASSERT( out[ i ] >= 0 ); } } void calc( const double* in, double* out ) const { double middle[ middleSize ]; calc( in, middle, out ); } void teach( const double* in, const double* expected, double epock ) { double middle[ middleSize ]; double actual[ outputSize ]; calc( in, middle, actual ); // 中間→出力ネットワークを学習 double gamma[ outputSize ]; for ( int i = 0; i < outputSize; ++i ) { gamma[ i ] = epock * sigmoidGain * ( expected[ i ] - actual[ i ] ) * actual[ i ] * ( 1.0 - actual[ i ] ); for ( int j = 0; j < middleSize + 1; ++j ) { middleToOut_[ i ][ j ] += gamma[ i ] * ( ( middleSize == j ) ? 1.0 : middle[ j ] ); } } // 入力→中間ネットワークを学習 for ( int i = 0; i < middleSize; ++i ) { double delta = sigmoidGain * middle[ i ] * ( 1 - middle[ i ] ) * std::inner_product( gamma, gamma + outputSize, boost::make_generator_iterator( Skip( &middleToOut_[ 0 ][ i ], middleSize ) ), 0.0 ); for ( int j = 0; j < inputSize + 1; ++j ) { inToMiddle_[ i ][ j ] += delta * ( ( inputSize == j ) ? 1.0 : in[ j ] ); } } } void print( const double* in, const double* expected, const double threshold ) { double actual[ outputSize ]; calc( in, actual ); for ( int i = 0; i < outputSize; ++i ) { if ( i > 0 ) { cout << ", "; } double boolActual = actual[ i ] > 0.5 ? 1.0 : 0.0; cout << ( ( fabs( boolActual - expected[ i ] ) < threshold ) ? "ok!" : "ng" ); } } }; const double Network::sigmoidGain = 5.0; int main(int argc, const char* argv) { srand( time( 0 ) ); Network n; struct Set { double input[ Network::inputSize ]; double expected[ Network::outputSize ]; }; Set set = { // or // a b t //{ { 1.0, 1.0 }, { 1.0 }, }, //{ { 1.0, 0.0 }, { 1.0 }, }, //{ { 0.0, 1.0 }, { 1.0 }, }, //{ { 0.0, 0.0 }, { 0.0 }, }, // xor // a b t { { 1.0, 1.0 }, { 0.0 }, }, { { 1.0, 0.0 }, { 1.0 }, }, { { 0.0, 1.0 }, { 1.0 }, }, { { 0.0, 0.0 }, { 0.0 }, }, // and // a b t //{ { 1.0, 1.0 }, { 1.0 }, }, //{ { 1.0, 0.0 }, { 0.0 }, }, //{ { 0.0, 1.0 }, { 0.0 }, }, //{ { 0.0, 0.0 }, { 0.0 }, }, }; const int setSize = sizeof ( set ) / sizeof ( Set ); for ( int i = 0; i < 3000; ++i ) { int index[ setSize ]; for ( int j = 0; j < setSize; ++j ) { index[ j ] = j; } std::random_shuffle( index, index + setSize ); for ( int j = 0; j < setSize; ++j ) { int idx = index[ j ]; n.teach( set[ idx ].input, set[ idx ].expected, 0.1 ); } } // 学習結果披露 for ( int j = 0; j < setSize; ++j ) { cout << "( "; n.print( set[ j ].input, set[ j ].expected, 0.01 ); cout << " )"; } cout << endl; return 0; }