ニューラルネット

ニューラルネットなんて、古すぎなんで、ネタですらないんですが...。誰かの参考になると良いですね。とりあえず、久しぶりすぎて、忘れまくり。第一関門 XOR 学習ができたので、載っけます。AND, OR は、main 内の set のコメントアウトで調整してください。VC7.1での動作を確認しています。

参考

#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;
}