Files
blackbird/lci/include/nn/FlattenLayer.hpp
2026-08-02 21:21:03 +08:00

367 lines
13 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#pragma once
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <tuple>
#include "ConvLayer.hpp"
class FlattenLayer {
private:
// 输入输出尺寸
int batch_size;
int input_channels;
int input_height;
int input_width;
int output_size;
// 缓存输入形状(用于反向传播)
std::vector<int> input_shape_cache;
std::vector<float> input_cache; // 缓存输入数据(可选)
// 辅助函数
//void flatten_forward(const float* input, float* output);
//void flatten_backward(const float* grad_output, float* grad_input);
public:
// 构造函数
FlattenLayer() : batch_size(0), input_channels(0),
input_height(0), input_width(0), output_size(0) {}
// 设置输入尺寸(自动计算输出尺寸)
void set_input_shape(int channels, int height, int width, int batch = 1) {
input_channels = channels;
input_height = height;
input_width = width;
batch_size = batch;
// 计算输出大小
output_size = channels * height * width;
// 缓存输入形状
input_shape_cache = {channels, height, width, batch};
}
// 设置输入尺寸(从卷积/池化层获取)
template<typename... Args>
void set_input_shape(std::tuple<Args...> shape_tuple) {
set_input_shape(std::get<0>(shape_tuple),
std::get<1>(shape_tuple),
std::get<2>(shape_tuple));
}
// 前向传播
std::vector<float> forward(const std::vector<float>& input, int batch_size = 1, bool training = true) {
// 如果之前没有设置输入尺寸,自动推断
if (input_channels == 0 && input_height == 0 && input_width == 0) {
// 这里假设输入是完整的batch数据,需要外部调用set_input_shape
// 或者根据输入大小推断(但不知道高度和宽度)
std::cerr << "Warning: Input shape not set for FlattenLayer. "
<< "Please call set_input_shape() first." << std::endl;
return input; // 安全返回,但可能不正确
}
// 检查输入大小是否匹配
int expected_input_size = batch_size * input_channels * input_height * input_width;
if (input.size() != static_cast<size_t>(expected_input_size)) {
std::cerr << "Error: Input size mismatch in FlattenLayer. "
<< "Expected: " << expected_input_size
<< ", Got: " << input.size() << std::endl;
return std::vector<float>();
}
// 如果训练模式,缓存输入形状
if (training) {
this->batch_size = batch_size;
input_shape_cache = {input_channels, input_height, input_width, batch_size};
input_cache = input; // 可选:缓存输入数据
}
// 计算输出大小
int output_total_size = batch_size * output_size;
std::vector<float> output(output_total_size, 0.0f);
// 执行展平操作
flatten_forward(input.data(), output.data());
return output;
}
// 反向传播
std::vector<float> backward(const std::vector<float>& grad_output, int batch_size = 1) {
// 检查梯度大小是否匹配
int expected_grad_size = batch_size * output_size;
if (grad_output.size() != static_cast<size_t>(expected_grad_size)) {
std::cerr << "Error: Gradient size mismatch in FlattenLayer. "
<< "Expected: " << expected_grad_size
<< ", Got: " << grad_output.size() << std::endl;
return std::vector<float>();
}
// 恢复输入形状
if (input_shape_cache.size() >= 4) {
input_channels = input_shape_cache[0];
input_height = input_shape_cache[1];
input_width = input_shape_cache[2];
this->batch_size = input_shape_cache[3];
}
// 计算输入梯度大小
int input_total_size = this->batch_size * input_channels * input_height * input_width;
std::vector<float> grad_input(input_total_size, 0.0f);
// 执行反向展平操作
flatten_backward(grad_output.data(), grad_input.data());
return grad_input;
}
// 获取输出尺寸
int get_output_size() const {
return output_size;
}
// 获取输出尺寸(返回元组)
std::tuple<int> get_output_shape() const {
return std::make_tuple(output_size);
}
// 获取输入尺寸
std::tuple<int, int, int> get_input_shape() const {
return std::make_tuple(input_channels, input_height, input_width);
}
// 获取完整输入尺寸(包含批次)
std::tuple<int, int, int, int> get_full_input_shape() const {
return std::make_tuple(input_channels, input_height, input_width, batch_size);
}
// 打印层信息
void print_info() const {
std::cout << "Flatten Layer Info:" << std::endl;
std::cout << " Input Shape: " << input_channels << "x"
<< input_height << "x" << input_width;
if (batch_size > 0) {
std::cout << " (batch: " << batch_size << ")";
}
std::cout << std::endl;
std::cout << " Output Size: " << output_size;
if (batch_size > 0) {
std::cout << " (total: " << batch_size * output_size << ")";
}
std::cout << std::endl;
}
// 重置层状态
void reset() {
input_shape_cache.clear();
input_cache.clear();
batch_size = 0;
input_channels = 0;
input_height = 0;
input_width = 0;
output_size = 0;
}
// 检查是否已初始化
bool is_initialized() const {
return input_channels > 0 && input_height > 0 && input_width > 0;
}
private:
// 展平前向传播实现
void flatten_forward(const float* input, float* output) {
// 简单地将多维数据复制到一维数组
int elements_per_sample = input_channels * input_height * input_width;
for (int b = 0; b < batch_size; b++) {
const float* input_ptr = input + b * elements_per_sample;
float* output_ptr = output + b * output_size;
// 直接内存复制
std::copy(input_ptr, input_ptr + elements_per_sample, output_ptr);
// 或者使用循环(等效)
/*
for (int i = 0; i < elements_per_sample; i++) {
output_ptr[i] = input_ptr[i];
}
*/
}
}
// 展平反向传播实现
void flatten_backward(const float* grad_output, float* grad_input) {
// 反向传播只是将梯度重新整形为输入形状
int elements_per_sample = input_channels * input_height * input_width;
for (int b = 0; b < batch_size; b++) {
const float* grad_output_ptr = grad_output + b * output_size;
float* grad_input_ptr = grad_input + b * elements_per_sample;
// 直接内存复制
std::copy(grad_output_ptr, grad_output_ptr + elements_per_sample, grad_input_ptr);
// 或者使用循环(等效)
/*
for (int i = 0; i < elements_per_sample; i++) {
grad_input_ptr[i] = grad_output_ptr[i];
}
*/
}
}
};
// 动态展平层(自动推断输入形状)
class DynamicFlattenLayer {
private:
int batch_size;
int input_channels;
int input_height;
int input_width;
int output_size;
public:
DynamicFlattenLayer() : batch_size(0), input_channels(0),
input_height(0), input_width(0), output_size(0) {}
// 前向传播(自动推断形状)
std::vector<float> forward(const std::vector<float>& input,
std::tuple<int, int, int> input_shape,
int batch_size = 1,
bool training = true) {
// 从元组获取输入形状
input_channels = std::get<0>(input_shape);
input_height = std::get<1>(input_shape);
input_width = std::get<2>(input_shape);
this->batch_size = batch_size;
// 验证输入大小
int expected_size = batch_size * input_channels * input_height * input_width;
if (input.size() != static_cast<size_t>(expected_size)) {
std::cerr << "Error: Input size mismatch in DynamicFlattenLayer. "
<< "Expected: " << expected_size
<< ", Got: " << input.size()
<< " (shape: " << input_channels << "x"
<< input_height << "x" << input_width
<< ", batch: " << batch_size << ")" << std::endl;
return std::vector<float>();
}
// 计算输出大小
output_size = input_channels * input_height * input_width;
int total_output_size = batch_size * output_size;
std::vector<float> output(total_output_size);
// 执行展平
const float* input_ptr = input.data();
float* output_ptr = output.data();
int elements_per_sample = output_size;
for (int b = 0; b < batch_size; b++) {
std::copy(input_ptr, input_ptr + elements_per_sample, output_ptr);
input_ptr += elements_per_sample;
output_ptr += elements_per_sample;
}
return output;
}
// 反向传播
std::vector<float> backward(const std::vector<float>& grad_output, int batch_size = 1) {
// 检查是否已初始化
if (output_size == 0) {
std::cerr << "Error: DynamicFlattenLayer not initialized. "
<< "Call forward() first." << std::endl;
return std::vector<float>();
}
// 验证梯度大小
int expected_grad_size = batch_size * output_size;
if (grad_output.size() != static_cast<size_t>(expected_grad_size)) {
std::cerr << "Error: Gradient size mismatch in DynamicFlattenLayer. "
<< "Expected: " << expected_grad_size
<< ", Got: " << grad_output.size() << std::endl;
return std::vector<float>();
}
// 恢复输入形状
int total_input_size = batch_size * input_channels * input_height * input_width;
std::vector<float> grad_input(total_input_size);
// 执行反向展平
const float* grad_output_ptr = grad_output.data();
float* grad_input_ptr = grad_input.data();
int elements_per_sample = output_size;
for (int b = 0; b < batch_size; b++) {
std::copy(grad_output_ptr, grad_output_ptr + elements_per_sample, grad_input_ptr);
grad_output_ptr += elements_per_sample;
grad_input_ptr += elements_per_sample;
}
return grad_input;
}
// 获取输出尺寸
int get_output_size() const { return output_size; }
// 获取输入形状
std::tuple<int, int, int> get_input_shape() const {
return std::make_tuple(input_channels, input_height, input_width);
}
};
// 使用示例函数
inline void flatten_layer_example() {
std::cout << "=== Flatten Layer Example ===" << std::endl;
// 创建展平层
FlattenLayer flatten;
// 设置输入形状(例如从池化层获取的输出)
// 假设池化层输出:64通道,7x7特征图
flatten.set_input_shape(64, 7, 7);
// 创建模拟输入数据(batch_size=2)
int batch_size = 2;
std::vector<float> input(2 * 64 * 7 * 7);
for (size_t i = 0; i < input.size(); i++) {
input[i] = static_cast<float>(i) / 100.0f;
}
// 前向传播
std::cout << "Forward pass..." << std::endl;
auto output = flatten.forward(input, batch_size, true);
std::cout << "Input size: " << input.size() << std::endl;
std::cout << "Output size: " << output.size() << std::endl;
std::cout << "Expected output size per sample: " << flatten.get_output_size() << std::endl;
// 打印部分结果
std::cout << "First 10 output values: ";
for (int i = 0; i < std::min(10, static_cast<int>(output.size())); i++) {
std::cout << output[i] << " ";
}
std::cout << std::endl;
// 创建模拟梯度(全为1)
std::vector<float> grad_output(output.size(), 1.0f);
// 反向传播
std::cout << "Backward pass..." << std::endl;
auto grad_input = flatten.backward(grad_output, batch_size);
std::cout << "Gradient input size: " << grad_input.size() << std::endl;
std::cout << "Expected gradient input size: " << 2 * 64 * 7 * 7 << std::endl;
// 验证形状正确性
auto [channels, height, width] = flatten.get_input_shape();
std::cout << "Recovered input shape: " << channels
<< "x" << height << "x" << width << std::endl;
// 打印层信息
flatten.print_info();
}