| #pragma once |
| #include "layer.hpp" |
| #include "../core/backend.hpp" |
| #include <string> |
|
|
| namespace newnet { |
|
|
| class Dense : public Layer { |
| public: |
| Tensor weights; |
| Tensor bias; |
| std::string activation; |
| |
| |
| Tensor cached_input; |
| Tensor cached_preact; |
| Tensor cached_output; |
| |
| Dense(int in_features, int out_features, std::string activation_ = "none") |
| : activation(activation_) { |
| |
| weights = Tensor::xavier(in_features, out_features); |
| weights.init_grad(); |
| |
| |
| bias = Tensor({1, out_features}, 0.0f); |
| bias.requires_grad = true; |
| bias.init_grad(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| Tensor forward(const Tensor& input) override { |
| int batch = input.rows(); |
| int in_f = input.cols(); |
| int out_f = weights.cols(); |
| |
| assert(in_f == weights.rows() && "Input features must match weight rows"); |
| |
| |
| cached_input = input; |
| |
| |
| Tensor preact({batch, out_f}); |
| backend::matmul( |
| input.data.data(), weights.data.data(), preact.data.data(), |
| batch, out_f, in_f |
| ); |
| |
| |
| for (int i = 0; i < batch; i++) { |
| backend::add( |
| &preact.data[i * out_f], bias.data.data(), |
| &preact.data[i * out_f], out_f |
| ); |
| } |
| |
| cached_preact = preact; |
| |
| |
| Tensor output({batch, out_f}); |
| if (activation == "relu") { |
| backend::relu_forward(preact.data.data(), output.data.data(), batch * out_f); |
| } else if (activation == "sigmoid") { |
| backend::sigmoid_forward(preact.data.data(), output.data.data(), batch * out_f); |
| } else { |
| output = preact; |
| } |
| |
| cached_output = output; |
| return output; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| Tensor backward(const Tensor& grad_output) override { |
| int batch = cached_input.rows(); |
| int in_f = cached_input.cols(); |
| int out_f = weights.cols(); |
| |
| |
| Tensor grad_preact({batch, out_f}); |
| if (activation == "relu") { |
| |
| backend::relu_backward( |
| grad_output.data.data(), cached_preact.data.data(), |
| grad_preact.data.data(), batch * out_f |
| ); |
| } else if (activation == "sigmoid") { |
| |
| backend::sigmoid_backward( |
| grad_output.data.data(), cached_output.data.data(), |
| grad_preact.data.data(), batch * out_f |
| ); |
| } else { |
| grad_preact = grad_output; |
| } |
| |
| |
| |
| |
| |
| |
| Tensor input_T({in_f, batch}); |
| backend::transpose( |
| cached_input.data.data(), input_T.data.data(), |
| batch, in_f |
| ); |
| |
| std::vector<float> gw(in_f * out_f, 0.0f); |
| backend::matmul( |
| input_T.data.data(), grad_preact.data.data(), gw.data(), |
| in_f, out_f, batch |
| ); |
| |
| for (int i = 0; i < in_f * out_f; i++) { |
| weights.grad[i] += gw[i]; |
| } |
| |
| |
| |
| std::vector<float> gb(out_f, 0.0f); |
| backend::sum_columns(grad_preact.data.data(), gb.data(), batch, out_f); |
| for (int i = 0; i < out_f; i++) { |
| bias.grad[i] += gb[i]; |
| } |
| |
| |
| |
| |
| |
| Tensor weights_T({out_f, in_f}); |
| backend::transpose( |
| weights.data.data(), weights_T.data.data(), |
| in_f, out_f |
| ); |
| |
| Tensor grad_input({batch, in_f}); |
| backend::matmul( |
| grad_preact.data.data(), weights_T.data.data(), grad_input.data.data(), |
| batch, in_f, out_f |
| ); |
| |
| return grad_input; |
| } |
| |
| std::vector<Tensor*> parameters() override { |
| return {&weights, &bias}; |
| } |
| }; |
|
|
| } |
|
|