File: dense_layer.hpp

package info (click to toggle)
frugally-deep 0.18.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,036 kB
  • sloc: cpp: 6,680; python: 1,262; makefile: 4; sh: 1
file content (104 lines) | stat: -rw-r--r-- 4,205 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
// Copyright 2016, Tobias Hermann.
// https://github.com/Dobiasd/frugally-deep
// Distributed under the MIT License.
// (See accompanying LICENSE file or at
//  https://opensource.org/licenses/MIT)

#pragma once

#include "fdeep/layers/layer.hpp"
#include "fdeep/tensor.hpp"

#include <fplus/fplus.hpp>

#include <string>
#include <utility>
#include <vector>

namespace fdeep {
namespace internal {

    // Takes a single stack volume (tensor_shape(n)) as input.
    class dense_layer : public layer {
    public:
        static RowMajorMatrixXf generate_params(std::size_t n_in,
            const float_vec& weights, const float_vec& bias)
        {
            assertion(weights.size() % bias.size() == 0, "invalid params");
            return eigen_row_major_mat_from_values(n_in + 1, bias.size(),
                fplus::append(weights, bias));
        }
        dense_layer(const std::string& name, std::size_t units,
            const float_vec& weights,
            const float_vec& bias)
            : layer(name)
            , n_in_(weights.size() / bias.size())
            , n_out_(units)
            , params_(generate_params(n_in_, weights, bias))
        {
            assertion(bias.size() == units, "invalid bias count");
            assertion(weights.size() % units == 0, "invalid weight count");
        }

    protected:
        tensors apply_impl(const tensors& inputs) const override
        {
            assertion(inputs.size() == 1, "invalid number of input tensors");
            auto input = inputs.front();
            // According to the Keras documentation
            // https://keras.io/layers/core/#dense
            // "if the input to the layer has a rank greater than 2,
            // then it is flattened prior to the initial dot product with kernel."
            // But this seems to not be the case.
            // Instead it does this: https://stackoverflow.com/a/43237727/1866775
            // Otherwise the following would need to be done:
            // if (input.shape().get_not_one_dimension_count() > 1)
            // {
            //     input = flatten_tensor(input);
            // }

            const auto feature_arr = input.as_vector();
            const size_t size = feature_arr->size();
            const size_t depth = input.shape().depth_;
            assertion(depth == n_in_ && (size % depth) == 0, "Invalid input value count.");
            std::vector<float_type> result_values((input.shape().volume() / depth) * n_out_);
            const size_t n_of_parts = size / depth;

            Eigen::Map<const RowMajorMatrixXf, Eigen::Unaligned> params(
                params_.data(),
                static_cast<EigenIndex>(params_.rows() - 1),
                static_cast<EigenIndex>(params_.cols()));
            Eigen::Map<const RowMajorMatrixXf, Eigen::Unaligned> bias(
                params_.data() + (params_.rows() - 1) * params_.cols(),
                static_cast<EigenIndex>(1),
                static_cast<EigenIndex>(params_.cols()));

            for (size_t part_id = 0; part_id < n_of_parts; ++part_id) {
                Eigen::Map<const RowMajorMatrixXf, Eigen::Unaligned> m(
                    &(*feature_arr)[part_id * depth],
                    static_cast<EigenIndex>(1),
                    static_cast<EigenIndex>(depth));
                Eigen::Map<RowMajorMatrixXf, Eigen::Unaligned> res_m(
                    &result_values[part_id * n_out_],
                    static_cast<EigenIndex>(1),
                    static_cast<EigenIndex>(n_out_));
                res_m.noalias() = m * params + bias;
            }
            return { tensor(tensor_shape_with_changed_rank(
                                tensor_shape(
                                    input.shape().size_dim_5_,
                                    input.shape().size_dim_4_,
                                    input.shape().height_,
                                    input.shape().width_,
                                    n_out_),
                                input.shape().rank()),
                std::move(result_values)) };
        }

        std::size_t n_in_;
        std::size_t n_out_;
        RowMajorMatrixXf params_;
    };

}
}