| #ifndef NEUROFLOW_NETWORKS_HPP |
| #define NEUROFLOW_NETWORKS_HPP |
|
|
| |
| |
| |
| |
| |
| |
| |
|
|
| #include <cmath> |
| #include <memory> |
| #include <random> |
| #include <thread> |
| #include <variant> |
| #include <vector> |
| #include "tensor.hpp" |
|
|
| #ifdef USE_CUDA |
| #include "cuda_kernels.hpp" |
| #endif |
|
|
| namespace neuroflow { |
|
|
| |
| |
| |
| |
| class Linear { |
| public: |
| Tensor weight; |
| Tensor bias; |
| Tensor weight_scale; |
| bool quantized; |
| |
| Linear(size_t in_features, size_t out_features, bool use_bias = true, bool quant = false) |
| : quantized(quant) { |
| if (quant) { |
| weight = Tensor({out_features, in_features}, QuantType::INT8); |
| weight_scale = Tensor({out_features}, QuantType::FP32); |
| } else { |
| weight = Tensor({out_features, in_features}, QuantType::FP32); |
| |
| float* w = weight.as_fp32(); |
| float scale = std::sqrt(2.0f / (in_features + out_features)); |
| size_t n = weight.numel(); |
| std::mt19937 init_rng(std::hash<std::thread::id>{}(std::this_thread::get_id()) + in_features * 31 + out_features); |
| std::uniform_real_distribution<float> dist(-scale, scale); |
| for (size_t i = 0; i < n; ++i) { |
| w[i] = dist(init_rng); |
| } |
| } |
| |
| if (use_bias) { |
| bias = Tensor({out_features}, QuantType::FP32); |
| memset(bias.data_.get(), 0, bias.data_size_); |
| } |
| } |
| |
| Tensor forward(const Tensor& input) { |
| Tensor output({input.shape_[0], weight.shape_[0]}, QuantType::FP32); |
| |
| if (quantized) { |
| |
| |
| TensorOps::gemm(input, weight, output, false, true); |
| } else { |
| |
| |
| TensorOps::gemm(input, weight, output, false, true); |
| } |
| |
| |
| if (bias.data_) { |
| #ifdef USE_CUDA |
| if (CudaContext::instance().is_available() && output.is_on_gpu()) { |
| bias.to_gpu(); |
| int rows = static_cast<int>(output.shape_[0]); |
| int cols = static_cast<int>(output.shape_[1]); |
| launch_bias_add(output.as_gpu_fp32(), bias.as_gpu_fp32(), rows, cols, |
| CudaContext::instance().stream()); |
| output.gpu_dirty_ = true; |
| } else |
| #endif |
| { |
| float* out = output.as_fp32(); |
| float* b = bias.as_fp32(); |
| for (size_t i = 0; i < output.shape_[0]; ++i) { |
| for (size_t j = 0; j < output.shape_[1]; ++j) { |
| out[i * output.shape_[1] + j] += b[j]; |
| } |
| } |
| } |
| } |
| |
| return output; |
| } |
| |
| |
| void quantize() { |
| if (quantized) return; |
| |
| Tensor new_weight({weight.shape_[0], weight.shape_[1]}, QuantType::INT8); |
| Tensor scale({weight.shape_[0]}, QuantType::FP32); |
| |
| TensorOps::quantize_int8(weight, new_weight, scale); |
| |
| weight = new_weight; |
| weight_scale = scale; |
| quantized = true; |
| } |
| }; |
|
|
| |
| |
| |
| class LayerNorm { |
| public: |
| Tensor weight; |
| Tensor bias; |
| float eps; |
| |
| LayerNorm(size_t dim, float epsilon = 1e-5f) : eps(epsilon) { |
| weight = Tensor({dim}, QuantType::FP32); |
| bias = Tensor({dim}, QuantType::FP32); |
| |
| float* w = weight.as_fp32(); |
| float* b = bias.as_fp32(); |
| for (size_t i = 0; i < dim; ++i) { |
| w[i] = 1.0f; |
| b[i] = 0.0f; |
| } |
| } |
| |
| Tensor forward(const Tensor& input) { |
| Tensor output = input.clone(); |
| TensorOps::layer_norm(output, weight, bias, eps); |
| return output; |
| } |
| }; |
|
|
| |
| |
| |
| class GELU { |
| public: |
| Tensor forward(const Tensor& input) { |
| Tensor output = input.clone(); |
| TensorOps::gelu(output); |
| return output; |
| } |
| }; |
|
|
| |
| |
| |
| class Dropout { |
| public: |
| float rate; |
| bool training; |
| |
| Dropout(float r = 0.1f) : rate(r), training(false) {} |
| |
| Tensor forward(const Tensor& input) { |
| Tensor output = input.clone(); |
| TensorOps::dropout(output, rate, training); |
| return output; |
| } |
| |
| void set_training(bool t) { training = t; } |
| }; |
|
|
| |
| |
| |
| class Sequential { |
| public: |
| using LayerVariant = std::variant< |
| std::shared_ptr<Linear>, |
| std::shared_ptr<LayerNorm>, |
| std::shared_ptr<GELU>, |
| std::shared_ptr<Dropout> |
| >; |
| |
| std::vector<LayerVariant> layers; |
| |
| template<typename T> |
| void add(std::shared_ptr<T> layer) { |
| layers.push_back(layer); |
| } |
| |
| template<typename T> |
| std::shared_ptr<T> get(size_t idx) { |
| return std::get<std::shared_ptr<T>>(layers[idx]); |
| } |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| class ExecutiveControlNetwork { |
| public: |
| |
| std::vector<std::shared_ptr<Linear>> dlpfc_linear; |
| std::vector<std::shared_ptr<LayerNorm>> dlpfc_norm; |
| std::vector<std::shared_ptr<GELU>> dlpfc_gelu; |
| std::vector<std::shared_ptr<Dropout>> dlpfc_drop; |
| |
| |
| std::shared_ptr<Linear> ofc1, ofc2; |
| |
| |
| std::shared_ptr<Linear> vmpfc1, vmpfc2; |
| |
| size_t num_layers; |
| size_t hidden_dim; |
| |
| ExecutiveControlNetwork(size_t input_dim, size_t hidden_dim, size_t output_dim, size_t layers = 2) |
| : num_layers(layers), hidden_dim(hidden_dim) { |
| |
| |
| size_t prev_dim = input_dim; |
| for (size_t i = 0; i < layers; ++i) { |
| dlpfc_linear.push_back(std::make_shared<Linear>(prev_dim, hidden_dim)); |
| dlpfc_norm.push_back(std::make_shared<LayerNorm>(hidden_dim)); |
| dlpfc_gelu.push_back(std::make_shared<GELU>()); |
| dlpfc_drop.push_back(std::make_shared<Dropout>(0.1f)); |
| prev_dim = hidden_dim; |
| } |
| |
| |
| size_t half = hidden_dim / 2; |
| ofc1 = std::make_shared<Linear>(hidden_dim, half); |
| ofc2 = std::make_shared<Linear>(half, 1); |
| |
| |
| vmpfc1 = std::make_shared<Linear>(hidden_dim, half); |
| vmpfc2 = std::make_shared<Linear>(half, output_dim); |
| } |
| |
| struct Output { |
| Tensor decision; |
| Tensor value; |
| std::vector<Tensor> hidden_states; |
| }; |
| |
| Output forward(const Tensor& x) { |
| Output out; |
| Tensor h = x; |
| |
| |
| for (size_t i = 0; i < num_layers; ++i) { |
| h = dlpfc_linear[i]->forward(h); |
| h = dlpfc_norm[i]->forward(h); |
| h = dlpfc_gelu[i]->forward(h); |
| h = dlpfc_drop[i]->forward(h); |
| out.hidden_states.push_back(h.clone()); |
| } |
| |
| |
| Tensor v = ofc1->forward(h); |
| TensorOps::gelu(v); |
| out.value = ofc2->forward(v); |
| |
| |
| Tensor d = vmpfc1->forward(h); |
| TensorOps::gelu(d); |
| out.decision = vmpfc2->forward(d); |
| |
| return out; |
| } |
| |
| void set_training(bool t) { |
| for (auto& drop : dlpfc_drop) drop->set_training(t); |
| } |
| |
| |
| void quantize() { |
| for (auto& l : dlpfc_linear) l->quantize(); |
| ofc1->quantize(); |
| ofc2->quantize(); |
| vmpfc1->quantize(); |
| vmpfc2->quantize(); |
| } |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| class DefaultModeNetwork { |
| public: |
| size_t memory_dim; |
| size_t latent_dim; |
| size_t num_associations; |
| |
| |
| std::shared_ptr<Linear> mem_encoder1, mem_encoder2; |
| |
| |
| std::vector<std::pair<std::shared_ptr<Linear>, std::shared_ptr<Linear>>> association_heads; |
| |
| |
| std::shared_ptr<Linear> future_proj1; |
| std::shared_ptr<LayerNorm> future_norm; |
| std::shared_ptr<GELU> future_gelu; |
| |
| DefaultModeNetwork(size_t memory_dim, size_t latent_dim, size_t num_assoc = 8) |
| : memory_dim(memory_dim), latent_dim(latent_dim), num_associations(num_assoc) { |
| |
| |
| mem_encoder1 = std::make_shared<Linear>(memory_dim, latent_dim * 2); |
| mem_encoder2 = std::make_shared<Linear>(latent_dim * 2, latent_dim); |
| |
| |
| for (size_t i = 0; i < num_assoc; ++i) { |
| auto head1 = std::make_shared<Linear>(latent_dim, latent_dim); |
| auto head2 = std::make_shared<Linear>(latent_dim, latent_dim); |
| association_heads.push_back({head1, head2}); |
| } |
| |
| |
| future_proj1 = std::make_shared<Linear>(latent_dim * num_assoc, latent_dim * 2); |
| future_norm = std::make_shared<LayerNorm>(latent_dim * 2); |
| future_gelu = std::make_shared<GELU>(); |
| } |
| |
| struct Output { |
| Tensor vision; |
| std::vector<Tensor> associations; |
| Tensor latent; |
| }; |
| |
| Output forward(const Tensor& memory_input) { |
| Output out; |
| |
| |
| Tensor h = mem_encoder1->forward(memory_input); |
| TensorOps::gelu(h); |
| out.latent = mem_encoder2->forward(h); |
| |
| |
| for (auto& head : association_heads) { |
| Tensor assoc = head.first->forward(out.latent); |
| TensorOps::gelu(assoc); |
| assoc = head.second->forward(assoc); |
| out.associations.push_back(assoc); |
| } |
| |
| |
| out.vision = TensorOps::concat(out.associations, 1); |
| out.vision = future_proj1->forward(out.vision); |
| out.vision = future_norm->forward(out.vision); |
| out.vision = future_gelu->forward(out.vision); |
| |
| return out; |
| } |
| |
| void quantize() { |
| mem_encoder1->quantize(); |
| mem_encoder2->quantize(); |
| future_proj1->quantize(); |
| for (auto& [h1, h2] : association_heads) { |
| h1->quantize(); |
| h2->quantize(); |
| } |
| } |
| }; |
|
|
| |
| |
| |
| |
| |
| |
| class SalienceNetwork { |
| public: |
| |
| std::shared_ptr<Linear> saliency1, saliency2, saliency3; |
| |
| |
| std::shared_ptr<Linear> gate1, gate2; |
| |
| |
| std::shared_ptr<Linear> anomaly1, anomaly2; |
| |
| SalienceNetwork(size_t input_dim, size_t hidden_dim) { |
| |
| saliency1 = std::make_shared<Linear>(input_dim, hidden_dim); |
| saliency2 = std::make_shared<Linear>(hidden_dim, hidden_dim / 2); |
| saliency3 = std::make_shared<Linear>(hidden_dim / 2, 1); |
| |
| |
| gate1 = std::make_shared<Linear>(input_dim, hidden_dim); |
| gate2 = std::make_shared<Linear>(hidden_dim, 2); |
| |
| |
| anomaly1 = std::make_shared<Linear>(input_dim, hidden_dim); |
| anomaly2 = std::make_shared<Linear>(hidden_dim, 1); |
| } |
| |
| struct Output { |
| Tensor saliency; |
| Tensor gates; |
| Tensor anomaly; |
| }; |
| |
| Output forward(const Tensor& x, const Tensor* baseline = nullptr) { |
| Output out; |
| |
| |
| Tensor h = saliency1->forward(x); |
| TensorOps::gelu(h); |
| h = saliency2->forward(h); |
| TensorOps::gelu(h); |
| out.saliency = saliency3->forward(h); |
| |
| float* s = out.saliency.as_fp32(); |
| for (size_t i = 0; i < out.saliency.numel(); ++i) { |
| s[i] = 1.0f / (1.0f + std::exp(-s[i])); |
| } |
| |
| |
| h = gate1->forward(x); |
| TensorOps::gelu(h); |
| out.gates = gate2->forward(h); |
| TensorOps::softmax(out.gates); |
| |
| |
| if (baseline) { |
| Tensor diff = x.clone(); |
|
|
| float* d = diff.as_fp32(); |
| const float* b = baseline->as_fp32(); |
| for (size_t i = 0; i < diff.numel(); ++i) d[i] -= b[i]; |
| |
| h = anomaly1->forward(diff); |
| TensorOps::gelu(h); |
| out.anomaly = anomaly2->forward(h); |
| } else { |
| out.anomaly = Tensor({x.shape_[0], 1}, QuantType::FP32); |
| } |
| |
| return out; |
| } |
| |
| void quantize() { |
| saliency1->quantize(); |
| saliency2->quantize(); |
| saliency3->quantize(); |
| gate1->quantize(); |
| gate2->quantize(); |
| anomaly1->quantize(); |
| anomaly2->quantize(); |
| } |
| }; |
|
|
| } |
|
|
| #endif |