Использование col2im для обратного распространения CNN - PullRequest
0 голосов
/ 27 февраля 2019

Я работал над реализацией сверточных нейронных сетей.Первоначально я реализовал их со стандартными свертками, которые работали, как ожидалось, и прошел проверку градиента, поэтому я начал работу по оптимизации сверток, используя im2col для прямых проходов и col2im для обратных проходов.Я правильно реализовал im2col, и прямой проход работает, как и ожидалось, однако я заметил, что обратный проход не работает правильно, в основном распространяя градиент в обратном направлении.

Проблема заключается в том, что, как описано в https://stackoverflow.com/a/51717536 и в http://www.programmersought.com/article/16963235/, функция col2im не совсем противоположна im2col, в моем случае это приводит к результатуобратного прохода, чтобы быть полностью неправильным, несмотря на то, что и col2im, и умножение матриц, кажется, работают правильно самостоятельно.

Таким образом, так как я изо всех сил пытаюсь найти много информации в Интернете об этом, как решить эту проблемуПроблема?

Мой код умножения матриц, код col2im и код обратного прохода приведены ниже:

        /// <summary>
    /// O = (A dot B) + C + D
    /// </summary>
    /// <param name="a">NxM dimensional input matrix</param>
    /// <param name="b">MxP dimensional input matrix</param>
    /// <param name="c">Px1 dimensional optional input matrix</param>
    /// <param name="d">Nx1 dimensional optional input matrix</param>
    /// <param name="o">NxP dimensional output matrix</param>
    public static void Mad(Matrix a, Matrix b, Matrix c, Matrix d, Matrix o, bool reset)
    {
        if (a.Columns != b.Rows)
            throw new ArgumentException();

        if (a.Rows != o.Rows)
            throw new ArgumentException();
        if (b.Columns != o.Columns)
            throw new ArgumentException();

        if (c != null && c.Rows != b.Columns)
            throw new ArgumentException();

        if (d != null && d.Rows != a.Rows)
            throw new ArgumentException();

        Parallel.For(0, a.Rows, (i) =>
        //for(int i = 0; i < a.Rows; i++)
        {
            for (int j = 0; j < b.Columns; j++)
            {
                float acc = 0;
                for (int k = 0; k < a.Columns; k++)
                {
                    acc += a.memory[a.Index(i, k)] * b.memory[b.Index(k, j)];
                }

                if (reset)
                    o.memory[o.Index(i, j)] = acc + (c == null ? 0 : c.memory[c.Index(j, 0)]) + (d == null ? 0 : d.memory[d.Index(i, 0)]);
                else
                    o.memory[o.Index(i, j)] += acc + (c == null ? 0 : c.memory[c.Index(j, 0)]) + (d == null ? 0 : d.memory[d.Index(i, 0)]);
            }
        });
    }

public static void Column2Image(int input_sz, int input_cnt, int stride_len, int padding, int filter_sz, int output_sz, Matrix input, Matrix output)
    {
        //Foreach column in input, rearrange it into a block, stripping padding and applying appropriate strides
        int block_sz = filter_sz * filter_sz * input_cnt;
        int len = output_sz * output_sz;


        input.Clear();
        //Using a version based on Caffe for comparison

        //for (int c = 0; c < block_sz; c++)
        Parallel.For(0, block_sz, (c) =>
        {
            int col_off = (c % filter_sz);
            int row_off = (c / filter_sz) % filter_sz;
            int c_im = c / filter_sz / filter_sz;
            for (int row = 0; row < output_sz; row++)
                for (int col = 0; col < output_sz; col++)
                {
                    int row_pad = row * stride_len - padding + row_off;
                    int col_pad = col * stride_len - padding + col_off;
                    if (row_pad >= 0 && row_pad < input_sz && col_pad >= 0 && col_pad < input_sz)
                    {
                        input.memory[input.Index(c_im, row_pad * input_sz + col_pad)] += output.memory[c * output_sz * output_sz + row * output_sz + col];
                    }
                }
        });
    }

    public Matrix[] Propagate(Matrix[] prev_delta)
    {
        //cur_delta = BackwardDelta = Full convolution of prev_delta with 180 rotated filter <- sum from all filters in terms of filterCnt, but spread across inputDepth? 
        //Flatten and transpose the Weights as is, dot the flattened prev_delta
        Matrix.Mad(Weights.Transpose(), prev_delta[0].Reshape(filterCnt, outputSz * outputSz), null, null, BackwardDeltaIC, true);
        Matrix.Column2Image(inputSz, inputDepth, strideLen, paddingSz, filterSz, outputSz, BackwardDelta.Reshape(inputDepth, inputSz * inputSz), BackwardDeltaIC);
        //col2im the result
        return new Matrix[] { BackwardDelta };
    }

Я пытался сравнить реализацию caffe, но я не мог понять, какони справились с этим.Любая информация будет принята с благодарностью!

РЕДАКТИРОВАТЬ: Я должен также упомянуть, что я думаю, что проблема частично связана с наличием ненулевого заполнения, которое, как представляется, также кратко обсуждается https://github.com/pytorch/pytorch/blob/master/aten/src/THNN/generic/Col2Im.c, ноЯ не очень понимаю, как они решают проблему.

...