std::vector behavior, move and copy

250 views Asked by At

I was doing a neural net in cpp during free time in order to get some more experience in C++11. However I ran into some problems that a I cannot figure out myself.

struct neuronsLayer
{
    vector<real> ac;

    neuronsLayer(int s)
    {
        std::cout<<"neuronLayer 1"<<std::endl;
        ac = vector<real>(s,0.1f);
    }
    neuronsLayer(const neuronsLayer& nl)
    {
        std::cout<<"neuronLayer 2"<<std::endl;
        ac = vector<real>(nl.ac);
    }
    neuronsLayer(neuronsLayer&& nl)
    {
        std::cout<<"neuronLayer 3"<<std::endl;
        ac = std::move(nl.ac);
    }
    neuronsLayer operator=(const neuronsLayer& nl)
    {
        std::cout<<"neuronLayer 4"<<std::endl;
        return neuronsLayer(nl);
    }
    neuronsLayer(){ std::cout<<"neuronLayer 5"<<std::endl;}
    ~neuronsLayer(){}
};

This is a layer implementation, then :

struct network
{
    vector<neuronsLayer> hiddens;
    vector<neuronsConnection> synaps;
    real alpha;

   //std::initializer_list

    network(vector<int> layers)
    {
        alpha = 1.f;
        hiddens = vector<neuronsLayer>();//+2
        for(int& l : layers)
        {
            hiddens.push_back(neuronsLayer(l));
        }
        synaps = vector<neuronsConnection>();
        for(int i = 0 ; i < layers.size() -1 ; i++)
        {
            synaps.push_back(std::move(neuronsConnection(layers[i],layers[i+1])));
        }
    }

    void forward(vector<real> input)
    {
        hiddens[0].ac = input;
        for (int layer = 0; layer < hiddens.size() -1; ++layer)
        {
            for(int i = 0 ; i < synaps[layer].x ; i++)
            {
                for(int j = 0 ; j < synaps[layer].y ; j++)
                {
                    hiddens[layer+1].ac[i] += hiddens[layer].ac[j] * synaps[layer].w[i + synaps[layer].x * j]; //+ activation +biais
                }
            }
            for(int i = 0 ; i < hiddens[layer].ac.size() ; i ++)
                hiddens[layer+1].ac[i] = 1.f/(1+exp(-hiddens[layer+1].ac[i]));
        }
    }

    void backward(vector<real> expected)
    {
        vector<real> error(expected);
        for(int i = 0 ; i < error.size(); i ++)
        {
            error[i] = expected[i] - hiddens[hiddens.size() -1].ac[i];
        }
        for (int layer = 0; layer < hiddens.size() -1; ++layer)
        {
            for(int i = 0 ; i < synaps[layer].x ; i++)
            {
                for(int j = 0 ; j < synaps[layer].y ; j++)
                {
                    real dw = error[i]*(1+2*exp(-hiddens[0].ac[i])/(1+exp(-hiddens[0].ac[i])));
                    synaps[layer].w[i + synaps[layer].x * j] += dw*alpha;
                }
            }
        }
    }

And the main :

int main(int argc, char** argv)
{
    vector<int> net = {64,2};
    network nn(net);
    vector<float> o = {1,0};
    vector<float> t = {0,1};

    auto rOne = std::bind(std::normal_distribution<float>(6,1), std::default_random_engine{});
    auto rTwo = std::bind(std::normal_distribution<float>(3,1), std::default_random_engine{});

    auto gOne = [&](){
        int x=rOne(),y=rOne();
        //while(x=rOne > 8 or x < 0);
        //while(y=rOne > 8 or y < 0);
        std::vector<real> tbr (64,0);
        tbr[x + y*8] = 1.0;
        return tbr;
    };

    auto gTwo = [&](){
        int x=rTwo(),y=rTwo();
        //while(x=rTwo > 8 or x < 0);
        //while(y=rTwo > 8 or y < 0);
        std::vector<real> tbr (64,0);
        tbr[x + y*8] = 1.0;
        return tbr;
    };

    for(int i = 0 ; i < 5000 ; i++)
    {
        nn.forward(gOne());
        nn.backward(o);
        nn.forward(gTwo());
        nn.backward(t);
    }

I have one major problem and two questions :

1) I receive a SEGFAULT during execution when backward is called, it seems that hiddens[0] is empty. So I might (little understatement) have misunderstood how move is working ?

Program received signal SIGSEGV, Segmentation fault. 
0x0000000000402159 in network::backward (this=0x7fffffffe190, expected=...) at dnn3.cpp:171
171   real dw = error[i]*(1+2*exp(-hiddens[0].ac[i])/(1+exp( hiddens[0].ac[i])));
(gdb) p i
$1 = 0
(gdb) p hiddens[0].ac[i]
$2 = (__gnu_cxx::__alloc_traits<std::allocator<float> >::value_type &) @0x3f0000003f000000: <error reading variable>

2) Before that the output of the program is :

neuronLayer 1
neuronLayer 3
neuronLayer 1
neuronLayer 3
neuronLayer 2

Why is the copy constructor called ? I create only 2 layers, and both of them are generated following the exact same process, and only one of them is using this constructor. And I can't understand why it's needed.

3) Concerning the bound objects rOne and rTwo, are they always returning the same value ? cause when I poked into the gOne output it seems that it gave back twice the same value. Is that normal ?

Thanks in advance, Marc.

EDIT : As asked :

(gdb) p hiddens
 $1 = {<std::_Vector_base<neuronsLayer, std::allocator<neuronsLayer> >> = { _M_impl = {<std::allocator<neuronsLayer>> ={<__gnu_cxx::new_allocator<neuronsLayer>> = {<No data fields>}, <No data fields>},_M_start = 0x60c1a0, _M_finish = 0x60c1d0, _M_end_of_storage = 0x60c1d0}}, <No data fields>}
(gdb) p hiddens[0].ac
$2 = {<std::_Vector_base<float, std::allocator<float> >> = { _M_impl = {<std::allocator<float>> = {<__gnu_cxx::new_allocator<float>> = {<No data fields>}, <No data fields>}, _M_start = 0x3f0000003f000000, _M_finish = 0x3f0000003f000000, _M_end_of_storage = 0x60c2e0}}, <No data fields>}

EDIT 2 :

Breakpoint 1, network::forward (this=0x7fffffffe190, input=...)
(gdb) p hiddens
$1 = {<std::_Vector_base<neuronsLayer, std::allocator<neuronsLayer> >> = {_M_impl = {<std::allocator<neuronsLayer>> = {<__gnu_cxx::new_allocator<neuronsLayer>> = {<No data fields>}, <No data fields>},_M_start = 0x60d1a0, _M_finish = 0x60d1d0, _M_end_of_storage = 0x60d1d0}}, <No data fields>}
(gdb) p hiddens[0]
$2 = (__gnu_cxx::__alloc_traits<std::allocator<neuronsLayer> >::value_type &) @0x60d1a0: { ac = {<std::_Vector_base<float, std::allocator<float> >> = { _M_impl = {<std::allocator<float>> = {<__gnu_cxx::new_allocator<float>> = {<No data fields>}, <No data fields>}, _M_start = 0x60d1e0, _M_finish = 0x60d2e0, _M_end_of_storage = 0x60d2e0}}, <No data fields>}}
(gdb) p hiddens[0].ac
$3 = {<std::_Vector_base<float, std::allocator<float> >> = { _M_impl = {<std::allocator<float>> = {<__gnu_cxx::new_allocator<float>> = {<No data fields>}, <No data fields>}, _M_start = 0x60d1e0, _M_finish = 0x60d2e0, _M_end_of_storage = 0x60d2e0}}, <No data fields>}
(gdb) p hiddens[1]
$4 = (__gnu_cxx::__alloc_traits<std::allocator<neuronsLayer> >::value_type &) @0x60d1b8: { ac = {<std::_Vector_base<float, std::allocator<float> >> = _M_impl = {<std::allocator<float>> = {<__gnu_cxx::new_allocator<float>> = {<No data fields>}, <No data fields>}, _M_start = 0x60d180, _M_finish = 0x60d188, _M_end_of_storage = 0x60d188}}, <No data fields>}}
(gdb) p hiddens[1].ac[0] 
$5 = (__gnu_cxx::__alloc_traits<std::allocator<float> >::value_type &) @0x60d180: 0.100000001
(gdb) p hiddens[0].ac[0]
$6 = (__gnu_cxx::__alloc_traits<std::allocator<float> >::value_type &) @0x60d1e0: 0.100000001
1

There are 1 answers

6
timrau On
neuronsLayer operator=(const neuronsLayer& nl)
{
    std::cout<<"neuronLayer 4"<<std::endl;
    return neuronsLayer(nl);
}

The assignment operator did not do what you want. It indeed copied an temporary neuronsLayer object according to nl passed in, not modifying the content of its caller.

It should be

neuronsLayer& operator=(const neuronsLayer& nl)
{
    std::cout<<"neuronLayer 4"<<std::endl;
    ac = nl.ac;
    return *this;
}

EDIT : As asked :

(gdb) p hiddens
 $1 = {<std::_Vector_base<neuronsLayer, std::allocator<neuronsLayer> >> = { _M_impl = {<std::allocator<neuronsLayer>> ={<__gnu_cxx::new_allocator<neuronsLayer>> = {<No data fields>}, <No data fields>},_M_start = 0x60c1a0, _M_finish = 0x60c1d0, _M_end_of_storage = 0x60c1d0}}, <No data fields>}
(gdb) p hiddens[0].ac
$2 = {<std::_Vector_base<float, std::allocator<float> >> = { _M_impl = {<std::allocator<float>> = {<__gnu_cxx::new_allocator<float>> = {<No data fields>}, <No data fields>}, _M_start = 0x3f0000003f000000, _M_finish = 0x3f0000003f000000, _M_end_of_storage = 0x60c2e0}}, <No data fields>}

Since hiddens[0].ac has _M_start equal to _M_finish, it is empty, and thus fetching its 0th element leads to segmentation fault.