遗漏的部分

This commit is contained in:
qing committed 2026-08-02 21:21:03 +08:00
1 parent 84fb370da8
commit 0493383ee8
63 files changed
+28237 -4

No files matched your search

+141
View File
@@ -0,0 +1,141 @@
#pragma once
#include <iostream>
#include <vector>
#include <functional>
#include "../Dashun.h"
// 卷积神经网络构建辅助类(整合卷积、池化、展平层)
class CNNBuilder {
private:
std::vector<std::function<std::vector<float>(const std::vector<float>&, int, bool)>> forward_layers;
std::vector<std::function<std::vector<float>(const std::vector<float>&, int)>> backward_layers;
std::vector<std::tuple<std::string, int, int, int>> layer_shapes;
public:
// 添加卷积层
void add_conv_layer(ConvLayer& conv, int in_channels, int height, int width) {
conv.set_input_shape(height, width);
auto [out_height, out_width] = conv.get_output_shape();
forward_layers.push_back([&conv](const std::vector<float>& input, int batch, bool train) {
return conv.forward(input, batch, train);
});
backward_layers.push_back([&conv](const std::vector<float>& grad, int batch) {
auto a = conv.backward(grad, batch);
conv.update_parameters(0.3);
return a;
});
layer_shapes.push_back({"Conv", in_channels, out_height, out_width});
}
// 添加池化层
void add_pool_layer(PoolingLayer& pool, int in_channels, int height, int width) {
pool.set_input_shape(in_channels, height, width);
auto [out_channels, out_height, out_width] = pool.get_output_shape();
forward_layers.push_back([&pool](const std::vector<float>& input, int batch, bool train) {
return pool.forward(input, batch, train);
});
backward_layers.push_back([&pool](const std::vector<float>& grad, int batch) {
return pool.backward(grad, batch);
});
layer_shapes.push_back({"Pool", out_channels, out_height, out_width});
}
// 添加展平层
void add_flatten_layer(FlattenLayer& flatten, int in_channels, int height, int width) {
flatten.set_input_shape(in_channels, height, width);
int out_size = flatten.get_output_size();
forward_layers.push_back([&flatten](const std::vector<float>& input, int batch, bool train) {
return flatten.forward(input, batch, train);
});
backward_layers.push_back([&flatten](const std::vector<float>& grad, int batch) {
return flatten.backward(grad, batch);
});
layer_shapes.push_back({"Flatten", out_size, 1, 1});
}
// 前向传播(整个网络)
std::vector<float> forward(const std::vector<float>& input, int batch_size = 1, bool training = true) {
std::vector<float> current = input;
for (size_t i = 0; i < forward_layers.size(); i++) {
current = forward_layers[i](current, batch_size, training);
if (current.empty()) {
std::cerr << "Error in layer " << i << " ("
<< std::get<0>(layer_shapes[i]) << ")" << std::endl;
return std::vector<float>();
}
}
return current;
}
// 反向传播(整个网络)
std::vector<float> backward(const std::vector<float>& grad_output, int batch_size = 1) {
std::vector<float> current_grad = grad_output;
for (int i = backward_layers.size() - 1; i >= 0; i--) {
current_grad = backward_layers[i](current_grad, batch_size);
if (current_grad.empty()) {
std::cerr << "Error in backward pass of layer " << i << " ("
<< std::get<0>(layer_shapes[i]) << ")" << std::endl;
return std::vector<float>();
}
}
return current_grad;
}
// 打印网络结构
void print_architecture() const {
std::cout << "CNN Architecture:" << std::endl;
std::cout << "=================" << std::endl;
for (size_t i = 0; i < layer_shapes.size(); i++) {
auto [layer_type, channels, height, width] = layer_shapes[i];
if (layer_type == "Flatten") {
std::cout << "Layer " << i << ": " << layer_type
<< " -> Output size: " << channels << std::endl;
} else {
std::cout << "Layer " << i << ": " << layer_type
<< " -> Output shape: " << channels
<< "x" << height << "x" << width << std::endl;
}
}
}
};
/*
// 构建简单CNN
ConvLayer conv1(3, 16, 3, 1, 1);
PoolingLayer pool1(2, 2, 2);
ConvLayer conv2(16, 32, 3, 1, 1);
PoolingLayer pool2(2, 2, 2);
FlattenLayer flatten;
// 使用CNN构建器
CNNBuilder cnn;
cnn.add_conv_layer(conv1, 3, 32, 32);
cnn.add_pool_layer(pool1, 16, 32, 32);
cnn.add_conv_layer(conv2, 16, 16, 16);
cnn.add_pool_layer(pool2, 32, 16, 16);
cnn.add_flatten_layer(flatten, 32, 8, 8);
// 执行前向传播
auto output = cnn.forward(input_data, batch_size, true);
*/
+562
View File
@@ -0,0 +1,562 @@
#pragma once
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <chrono>
#include <random>
#include <numeric>
#include <functional>
class ConvLayer {
private:
// 参数
int input_channels;
int output_channels;
int kernel_size;
int stride;
int padding;
int input_height;
int input_width;
int output_height;
int output_width;
// Leaky ReLU参数
float negative_slope; // Leaky ReLU的负斜率,通常为0.01
// 权重和偏置
std::vector<float> weights; // shape: [output_channels, input_channels, kernel_size, kernel_size]
std::vector<float> bias; // shape: [output_channels]
// 梯度和动量
std::vector<float> d_weights; // 权重梯度
std::vector<float> d_bias; // 偏置梯度
std::vector<float> weight_momentum; // 动量
std::vector<float> bias_momentum; // 偏置动量
// 中间变量缓存(用于反向传播)
std::vector<float> col_buffer; // im2col后的矩阵
std::vector<float> input_cache; // 缓存的输入(用于计算输入梯度)
std::vector<float> output_cache; // 缓存的输出(激活前)
std::vector<float> activated_cache; // 激活后的输出缓存
std::vector<float> col_cache; // 缓存的col矩阵(用于权重梯度计算)
// 辅助函数
void im2col(const float* input, int batch_size);
void col2im(const float* col, float* im, int batch_size);
void sgemm(int M, int N, int K,
const float* A, const float* B, float* C,
bool transA = false, bool transB = false);
void sgemm_add(int M, int N, int K,
const float* A, const float* B, float* C,
float alpha = 1.0f, float beta = 1.0f,
bool transA = false, bool transB = false);
// Leaky ReLU激活函数及其导数
inline float leaky_relu(float x) const {
return x > 0 ? x : negative_slope * x;
}
inline float leaky_relu_derivative(float x) const {
return x > 0 ? 1.0f : negative_slope;
}
// 应用激活函数到整个张量
void apply_leaky_relu(float* data, int size) {
for (int i = 0; i < size; i++) {
data[i] = leaky_relu(data[i]);
}
}
// 计算激活函数的梯度
void compute_activation_gradient(const float* pre_activation, float* gradient, int size) {
for (int i = 0; i < size; i++) {
gradient[i] *= leaky_relu_derivative(pre_activation[i]);
}
}
public:
// 构造函数 - 添加negative_slope参数
ConvLayer(int in_channels, int out_channels,
int kernel_size, int stride = 1, int padding = 0,
float negative_slope = 0.01f)
: input_channels(in_channels),
output_channels(out_channels),
kernel_size(kernel_size),
stride(stride),
padding(padding),
negative_slope(negative_slope) {
// 初始化权重和偏置
int weight_size = out_channels * in_channels * kernel_size * kernel_size;
weights.resize(weight_size);
d_weights.resize(weight_size, 0.0f);
weight_momentum.resize(weight_size, 0.0f);
bias.resize(out_channels);
d_bias.resize(out_channels, 0.0f);
bias_momentum.resize(out_channels, 0.0f);
// He初始化(适合ReLU激活函数)
float std_dev = sqrt(2.0f / (in_channels * kernel_size * kernel_size));
std::default_random_engine generator;
std::normal_distribution<float> distribution(0.0f, std_dev);
for (int i = 0; i < weight_size; i++) {
weights[i] = distribution(generator);
}
for (int i = 0; i < out_channels; i++) {
bias[i] = 0.1f; // 小的正偏置
}
}
// 设置输入尺寸(计算输出尺寸)
void set_input_shape(int height, int width) {
input_height = height;
input_width = width;
output_height = (height + 2 * padding - kernel_size) / stride + 1;
output_width = (width + 2 * padding - kernel_size) / stride + 1;
// 预分配缓冲区
int col_height = input_channels * kernel_size * kernel_size;
int col_width = output_height * output_width;
col_buffer.resize(col_height * col_width);
// 清空缓存
input_cache.clear();
output_cache.clear();
activated_cache.clear();
col_cache.clear();
}
// 前向传播
std::vector<float> forward(const std::vector<float>& input, int batch_size = 1, bool training = true) {
// 计算输出大小
int output_size = batch_size * output_channels * output_height * output_width;
std::vector<float> output(output_size, 0.0f);
std::vector<float> pre_activation(output_size, 0.0f); // 激活前的输出
// 如果训练模式,缓存输入
if (training) {
input_cache = input;
}
// 对batch中的每个样本进行处理
for (int b = 0; b < batch_size; b++) {
const float* input_ptr = input.data() + b * input_channels * input_height * input_width;
// 1. im2col转换
im2col(input_ptr, 1);
// 2. 矩阵乘法:weights * col_buffer
int M = output_channels; // 输出通道数
int N = output_height * output_width; // 输出特征图大小
int K = input_channels * kernel_size * kernel_size; // 卷积核展平大小
// 如果训练模式,缓存col矩阵
if (training && b == 0) {
col_cache = col_buffer;
}
// 临时存储矩阵乘法结果(激活前)
std::vector<float> gemm_result(M * N, 0.0f);
sgemm(M, N, K, weights.data(), col_buffer.data(), gemm_result.data());
// 3. 加上偏置并存储到pre_activation
float* pre_activation_ptr = pre_activation.data() + b * output_channels * output_height * output_width;
float* output_ptr = output.data() + b * output_channels * output_height * output_width;
for (int oc = 0; oc < output_channels; oc++) {
float bias_val = bias[oc];
for (int i = 0; i < output_height * output_width; i++) {
float value = gemm_result[oc * N + i] + bias_val;
pre_activation_ptr[oc * output_height * output_width + i] = value;
// 4. 应用Leaky ReLU激活函数
output_ptr[oc * output_height * output_width + i] = leaky_relu(value);
}
}
}
// 如果训练模式,缓存激活前和激活后的输出
if (training) {
output_cache = pre_activation; // 缓存激活前的输出
activated_cache = output; // 缓存激活后的输出
}
return output; // 返回激活后的输出
}
// 反向传播
std::vector<float> backward(const std::vector<float>& grad_output, int batch_size = 1) {
// grad_output是激活函数后的梯度,需要先计算激活函数前的梯度
std::vector<float> grad_pre_activation(grad_output);
// 计算激活函数的梯度:grad_pre_activation = grad_output * leaky_relu_derivative(pre_activation)
for (int b = 0; b < batch_size; b++) {
const float* pre_activation_ptr = output_cache.data() + b * output_channels * output_height * output_width;
float* grad_ptr = grad_pre_activation.data() + b * output_channels * output_height * output_width;
for (int i = 0; i < output_channels * output_height * output_width; i++) {
grad_ptr[i] *= leaky_relu_derivative(pre_activation_ptr[i]);
}
}
// 1. 计算偏置梯度: d_bias = sum(grad_pre_activation, axis=(0,2,3))
std::fill(d_bias.begin(), d_bias.end(), 0.0f);
for (int b = 0; b < batch_size; b++) {
const float* grad_ptr = grad_pre_activation.data() + b * output_channels * output_height * output_width;
for (int oc = 0; oc < output_channels; oc++) {
float sum = 0.0f;
for (int i = 0; i < output_height * output_width; i++) {
sum += grad_ptr[oc * output_height * output_width + i];
}
d_bias[oc] += sum;
}
}
// 2. 计算权重梯度: d_weights = grad_pre_activation_col * col_cache^T
std::fill(d_weights.begin(), d_weights.end(), 0.0f);
int M = output_channels; // 输出通道数
int N = input_channels * kernel_size * kernel_size; // 卷积核展平大小
int K = output_height * output_width; // 输出特征图大小
// 将grad_pre_activation reshape为矩阵 [output_channels, batch_size * output_height * output_width]
std::vector<float> grad_pre_activation_mat(M * K * batch_size, 0.0f);
for (int b = 0; b < batch_size; b++) {
const float* grad_ptr = grad_pre_activation.data() + b * output_channels * output_height * output_width;
for (int oc = 0; oc < output_channels; oc++) {
for (int i = 0; i < output_height * output_width; i++) {
grad_pre_activation_mat[oc * (K * batch_size) + b * K + i] =
grad_ptr[oc * output_height * output_width + i];
}
}
}
// 扩展col_cache以匹配batch大小
std::vector<float> col_cache_batched(N * K * batch_size, 0.0f);
for (int b = 0; b < batch_size; b++) {
std::copy(col_cache.begin(), col_cache.end(),
col_cache_batched.begin() + b * N * K);
}
// 矩阵乘法: d_weights = grad_pre_activation_mat * col_cache_batched^T
// grad_pre_activation_mat: [M, K*batch_size], col_cache_batched^T: [K*batch_size, N]
sgemm_add(M, N, K * batch_size,
grad_pre_activation_mat.data(), col_cache_batched.data(), d_weights.data(),
1.0f, 0.0f, false, true);
// 3. 计算输入梯度: d_input_col = weights^T * grad_pre_activation_mat
// 然后通过col2im将d_input_col转换为d_input
std::vector<float> d_input(batch_size * input_channels * input_height * input_width, 0.0f);
// 计算d_input_col
std::vector<float> d_input_col(N * K * batch_size, 0.0f);
sgemm(N, K * batch_size, M,
weights.data(), grad_pre_activation_mat.data(), d_input_col.data(),
true, false);
// 将d_input_col通过col2im转换回输入形状
for (int b = 0; b < batch_size; b++) {
float* d_input_ptr = d_input.data() + b * input_channels * input_height * input_width;
col2im(d_input_col.data() + b * N * K, d_input_ptr, 1);
}
return d_input;
}
// 更新参数(使用SGD with momentum)
void update_parameters(float learning_rate = 0.01f, float momentum = 0.9f) {
// 更新权重
for (size_t i = 0; i < weights.size(); i++) {
weight_momentum[i] = momentum * weight_momentum[i] - learning_rate * d_weights[i];
weights[i] += weight_momentum[i];
}
// 更新偏置
for (size_t i = 0; i < bias.size(); i++) {
bias_momentum[i] = momentum * bias_momentum[i] - learning_rate * d_bias[i];
bias[i] += bias_momentum[i];
}
// 重置梯度(可选)
// std::fill(d_weights.begin(), d_weights.end(), 0.0f);
// std::fill(d_bias.begin(), d_bias.end(), 0.0f);
}
// Adam优化器更新参数
void update_parameters_adam(float learning_rate = 0.001f,
float beta1 = 0.9f, float beta2 = 0.999f,
float epsilon = 1e-8f, int t = 1) {
static std::vector<float> m_weights(weights.size(), 0.0f);
static std::vector<float> v_weights(weights.size(), 0.0f);
static std::vector<float> m_bias(bias.size(), 0.0f);
static std::vector<float> v_bias(bias.size(), 0.0f);
// 更新权重的一阶矩和二阶矩估计
for (size_t i = 0; i < weights.size(); i++) {
m_weights[i] = beta1 * m_weights[i] + (1 - beta1) * d_weights[i];
v_weights[i] = beta2 * v_weights[i] + (1 - beta2) * d_weights[i] * d_weights[i];
// 偏差校正
float m_hat = m_weights[i] / (1 - powf(beta1, t));
float v_hat = v_weights[i] / (1 - powf(beta2, t));
// 更新权重
weights[i] -= learning_rate * m_hat / (sqrtf(v_hat) + epsilon);
}
// 更新偏置的一阶矩和二阶矩估计
for (size_t i = 0; i < bias.size(); i++) {
m_bias[i] = beta1 * m_bias[i] + (1 - beta1) * d_bias[i];
v_bias[i] = beta2 * v_bias[i] + (1 - beta2) * d_bias[i] * d_bias[i];
// 偏差校正
float m_hat = m_bias[i] / (1 - powf(beta1, t));
float v_hat = v_bias[i] / (1 - powf(beta2, t));
// 更新偏置
bias[i] -= learning_rate * m_hat / (sqrtf(v_hat) + epsilon);
}
}
// 获取参数
const std::vector<float>& get_weights() const { return weights; }
const std::vector<float>& get_bias() const { return bias; }
const std::vector<float>& get_weight_gradients() const { return d_weights; }
const std::vector<float>& get_bias_gradients() const { return d_bias; }
// 设置参数(用于加载预训练模型)
void set_weights(const std::vector<float>& new_weights) { weights = new_weights; }
void set_bias(const std::vector<float>& new_bias) { bias = new_bias; }
// 设置Leaky ReLU参数
void set_leaky_relu_slope(float slope) { negative_slope = slope; }
// 获取输出尺寸
std::tuple<int, int> get_output_shape() const {
return std::make_tuple(output_height, output_width);
}
// 梯度检查(用于调试)
float gradient_check(const std::vector<float>& input,
const std::vector<float>& grad_output,
int weight_idx, float epsilon = 1e-5f) {
// 保存原始权重
float original_weight = weights[weight_idx];
// 计算f(x + epsilon)
weights[weight_idx] = original_weight + epsilon;
auto output_plus = forward(input, 1, false);
// 计算f(x - epsilon)
weights[weight_idx] = original_weight - epsilon;
auto output_minus = forward(input, 1, false);
// 恢复原始权重
weights[weight_idx] = original_weight;
// 计算数值梯度
float numerical_grad = 0.0f;
for (size_t i = 0; i < output_plus.size(); i++) {
numerical_grad += grad_output[i] * (output_plus[i] - output_minus[i]) / (2 * epsilon);
}
// 返回梯度差异
return fabs(numerical_grad - d_weights[weight_idx]);
}
};
// im2col实现 - 将图像转换为矩阵
void ConvLayer::im2col(const float* input, int batch_size) {
int col_height = input_channels * kernel_size * kernel_size;
int col_width = output_height * output_width;
int input_h_padded = input_height + 2 * padding;
int input_w_padded = input_width + 2 * padding;
// 创建填充后的输入(如果需要)
std::vector<float> padded_input;
const float* input_data = input;
if (padding > 0) {
padded_input.resize(input_channels * input_h_padded * input_w_padded, 0.0f);
for (int c = 0; c < input_channels; c++) {
for (int h = 0; h < input_height; h++) {
for (int w = 0; w < input_width; w++) {
int padded_idx = c * input_h_padded * input_w_padded +
(h + padding) * input_h_padded + (w + padding);
int orig_idx = c * input_height * input_width + h * input_width + w;
padded_input[padded_idx] = input[orig_idx];
}
}
}
input_data = padded_input.data();
}
// 执行im2col
int col_idx = 0;
for (int c = 0; c < input_channels; c++) {
for (int kh = 0; kh < kernel_size; kh++) {
for (int kw = 0; kw < kernel_size; kw++) {
for (int h = 0; h < output_height; h++) {
for (int w = 0; w < output_width; w++) {
int input_h = h * stride + kh;
int input_w = w * stride + kw;
int input_idx = c * input_h_padded * input_w_padded +
input_h * input_w_padded + input_w;
col_buffer[col_idx++] = input_data[input_idx];
}
}
}
}
}
}
// col2im实现 - 将矩阵转换回图像(用于梯度传播)
void ConvLayer::col2im(const float* col, float* im, int batch_size) {
int col_height = input_channels * kernel_size * kernel_size;
int col_width = output_height * output_width;
int input_h_padded = input_height + 2 * padding;
int input_w_padded = input_width + 2 * padding;
// 创建填充后的图像缓冲区
std::vector<float> padded_im(input_channels * input_h_padded * input_w_padded, 0.0f);
// 执行col2im
int col_idx = 0;
for (int c = 0; c < input_channels; c++) {
for (int kh = 0; kh < kernel_size; kh++) {
for (int kw = 0; kw < kernel_size; kw++) {
for (int h = 0; h < output_height; h++) {
for (int w = 0; w < output_width; w++) {
int input_h = h * stride + kh;
int input_w = w * stride + kw;
int input_idx = c * input_h_padded * input_w_padded +
input_h * input_w_padded + input_w;
padded_im[input_idx] += col[col_idx++];
}
}
}
}
}
// 去除padding,得到原始输入梯度
if (padding > 0) {
for (int c = 0; c < input_channels; c++) {
for (int h = 0; h < input_height; h++) {
for (int w = 0; w < input_width; w++) {
int padded_idx = c * input_h_padded * input_w_padded +
(h + padding) * input_w_padded + (w + padding);
int orig_idx = c * input_height * input_width + h * input_width + w;
im[orig_idx] = padded_im[padded_idx];
}
}
}
} else {
std::copy(padded_im.begin(), padded_im.end(), im);
}
}
// 优化的矩阵乘法
void ConvLayer::sgemm(int M, int N, int K,
const float* A, const float* B, float* C,
bool transA, bool transB) {
// 初始化输出为0
std::memset(C, 0, M * N * sizeof(float));
// 调用带参数的版本
sgemm_add(M, N, K, A, B, C, 1.0f, 0.0f, transA, transB);
}
// 带参数的矩阵乘法(支持转置)
void ConvLayer::sgemm_add(int M, int N, int K,
const float* A, const float* B, float* C,
float alpha, float beta,
bool transA, bool transB) {
// 如果beta为0,清空C
if (beta == 0.0f) {
std::memset(C, 0, M * N * sizeof(float));
}
// 优化:使用循环展开和缓存友好的访问模式
const int BLOCK_SIZE = 4;
// 处理A转置的情况
if (transA && !transB) {
// C = A^T * B
for (int i = 0; i < M; i++) {
for (int k = 0; k < K; k++) {
float a = A[k * M + i] * alpha;
if (a == 0.0f) continue;
const float* B_ptr = B + k * N;
float* C_ptr = C + i * N;
int j = 0;
for (; j <= N - BLOCK_SIZE; j += BLOCK_SIZE) {
C_ptr[j] += a * B_ptr[j];
C_ptr[j + 1] += a * B_ptr[j + 1];
C_ptr[j + 2] += a * B_ptr[j + 2];
C_ptr[j + 3] += a * B_ptr[j + 3];
}
for (; j < N; j++) {
C_ptr[j] += a * B_ptr[j];
}
}
}
} else if (!transA && transB) {
// C = A * B^T
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
float sum = 0.0f;
for (int k = 0; k < K; k++) {
sum += A[i * K + k] * B[j * K + k];
}
C[i * N + j] = beta * C[i * N + j] + alpha * sum;
}
}
} else if (transA && transB) {
// C = A^T * B^T = (B * A)^T
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
float sum = 0.0f;
for (int k = 0; k < K; k++) {
sum += A[k * M + i] * B[j * K + k];
}
C[i * N + j] = beta * C[i * N + j] + alpha * sum;
}
}
} else {
// C = A * B
for (int i = 0; i < M; i++) {
for (int k = 0; k < K; k++) {
float a = A[i * K + k] * alpha;
if (a == 0.0f) continue;
const float* B_ptr = B + k * N;
float* C_ptr = C + i * N;
int j = 0;
for (; j <= N - BLOCK_SIZE; j += BLOCK_SIZE) {
C_ptr[j] += a * B_ptr[j];
C_ptr[j + 1] += a * B_ptr[j + 1];
C_ptr[j + 2] += a * B_ptr[j + 2];
C_ptr[j + 3] += a * B_ptr[j + 3];
}
for (; j < N; j++) {
C_ptr[j] += a * B_ptr[j];
}
}
}
}
}
+366
View File
@@ -0,0 +1,366 @@
#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();
}
+66
View File
@@ -0,0 +1,66 @@
#pragma once
#include <cmath>
#include <iostream>
/* FIXME: 设置为每层可调节的 */
/* FIXME: 现在这个“接口”并未提供抽象函数 */
constexpr float LEAKY_RELU_ALPHA = 0.01;
using f_t = float(*)(float); /* FIXME: 在qing::Thread附近已经定义了f_t标识符 */
/* 神经网络层接口 */
class ILayer {
public:
/* 激活函数枚举类 */
enum class ActivationFunc {
None, ReLU, Leaky_ReLU, Sigmoid, Tanh
};
/* 获取类型名 */
static const char* get_f_type(ActivationFunc& af);
/* 通过类型名获取实体 */
static ActivationFunc parse_ff_type(const std::string& name);
/* 向输出流存储模型 */
virtual void save(std::ostream& out) = 0;
/* 从输入流加载模型
* FIXME: 这个方法我做成了工厂模式
*/
//virtual void load(std::istream& in) = 0;
protected:
/* 获取函数对象 - 获取激活函数 */
static f_t getf(ActivationFunc type);
/* 获取函数对象 - 求激活函数导数 */
static f_t getfdot(ActivationFunc type);
/* ReLU 激活函数 */
static float relu(float x);
/* ReLU 导数 */
static float relu_derivative(float x);
/*
* leaky ReLU 激活函数
*
* NOTE: alpha太小或者选择使用ReLU函数的情况下,会导致梯度消失
* NOTE: Leaky ReLU 不会使梯度完全消失,但也会使它变得很小
*/
static float leaky_relu(float x);
/* leaky ReLU 导数 */
static float leaky_relu_derivative(float x);
/* Sigmoid 激活函数 */
static float sigmoid(float x);
/* Sigmoid 导数 */
static float sigmoid_derivative(float x);
/* Tanh 激活函数 */
static float tanh(float x);
/* Tanh 导数 */
static float tanh_derivative(float x);
};
+3
View File
@@ -0,0 +1,3 @@
#pragma once
#include "../Dashun.h"
class ILayerConvolutional: public ILayer, public ILearnable {};
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include "nn/ILayer.hpp"
#include "nn/ILearnable.hpp"
class ILayerFullyConnectted: public ILayer, public ILearnable {
public:
/* 正向传播,返回神经网络输出 */
virtual std::vector<float> forward(const std::vector<float>& inputs) = 0;
/* 反向传播,返回上一层的误差,方便堆叠 */
virtual std::vector<float> backward(const std::vector<float>& error) = 0;
};
+5
View File
@@ -0,0 +1,5 @@
#pragma once
class ILearnable {
public:
virtual void update(float discount) = 0;
};
+523
View File
@@ -0,0 +1,523 @@
#pragma once
#define cimg_use_png /* -lpng */
#define cimg_use_jpeg /* -ljpeg */
#define cimg_display 0 // 禁用显示窗口,用于服务器环境
#include "CImg.h"
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <numeric>
#include <cmath>
#include <memory>
#include <sys/stat.h> // 用于检查文件是否存在
#include <dirent.h> // 用于目录遍历
class ImageLoader {
private:
// 图像参数
int target_channels; // 目标通道数(如RGB=3, 灰度=1)
int target_height; // 目标高度
int target_width; // 目标宽度
bool normalize_to_01; // 是否归一化到[0,1]
bool normalize_mean_std; // 是否使用均值标准差归一化
float mean_val; // 均值(归一化用)
float std_val; // 标准差(归一化用)
bool auto_mean_std; // 是否自动计算均值标准差
// 预计算的均值和标准差(针对每个通道)
std::vector<float> channel_means;
std::vector<float> channel_stds;
// 用于调试的verbose标志
bool verbose;
// 辅助函数:检查文件是否存在(POSIX兼容)
bool file_exists(const std::string& filename) {
struct stat buffer;
return (stat(filename.c_str(), &buffer) == 0);
}
// 辅助函数:检查是否为普通文件
bool is_regular_file(const std::string& path) {
struct stat path_stat;
if (stat(path.c_str(), &path_stat) != 0) {
return false;
}
return S_ISREG(path_stat.st_mode);
}
// 辅助函数:获取文件扩展名
std::string get_file_extension(const std::string& filename) {
size_t dot_pos = filename.find_last_of(".");
if (dot_pos == std::string::npos) {
return "";
}
return filename.substr(dot_pos);
}
// 辅助函数:转换为小写
std::string to_lower(const std::string& str) {
std::string result = str;
std::transform(result.begin(), result.end(), result.begin(), ::tolower);
return result;
}
public:
// 构造函数
ImageLoader(int channels = 3, int height = 224, int width = 224,
bool norm_01 = true, bool norm_mean_std = false,
float mean = 0.0f, float std = 1.0f, bool auto_compute = false)
: target_channels(channels),
target_height(height),
target_width(width),
normalize_to_01(norm_01),
normalize_mean_std(norm_mean_std),
mean_val(mean),
std_val(std),
auto_mean_std(auto_compute),
verbose(false) {
// 初始化通道统计量
channel_means.resize(target_channels, 0.0f);
channel_stds.resize(target_channels, 1.0f);
}
// 从文件加载单张图片
std::vector<float> load_image(const std::string& filepath, bool verbose = false) {
this->verbose = verbose;
try {
// 检查文件是否存在
if (!file_exists(filepath)) {
throw std::runtime_error("File not found: " + filepath);
}
// 加载图像
cimg_library::CImg<unsigned char> img(filepath.c_str());
if (verbose) {
std::cout << "Loaded image: " << filepath << std::endl;
std::cout << "Original size: " << img.width() << "x" << img.height()
<< "x" << img.spectrum() << std::endl;
}
// 转换为目标格式并预处理
return preprocess_image(img, verbose);
} catch (const std::exception& e) {
std::cerr << "Error loading image " << filepath << ": " << e.what() << std::endl;
return {};
}
}
// 从CImg对象加载图片
std::vector<float> load_from_cimg(const cimg_library::CImg<unsigned char>& input_img, bool verbose = false) {
this->verbose = verbose;
try {
// 创建图像的副本(因为预处理函数可能会修改图像)
cimg_library::CImg<unsigned char> img = input_img;
if (verbose) {
std::cout << "Loading from CImg object" << std::endl;
std::cout << "Original size: " << img.width() << "x" << img.height()
<< "x" << img.spectrum() << std::endl;
}
// 检查图像是否为空
if (img.is_empty()) {
throw std::runtime_error("Input CImg object is empty");
}
// 转换为目标格式并预处理
return preprocess_image(img, verbose);
} catch (const std::exception& e) {
std::cerr << "Error loading image from CImg object: " << e.what() << std::endl;
return {};
}
}
// 从CImg对象加载图片(移动语义版本)
std::vector<float> load_from_cimg(cimg_library::CImg<unsigned char>&& input_img, bool verbose = false) {
this->verbose = verbose;
try {
// 直接使用移动过来的图像对象
cimg_library::CImg<unsigned char> img = std::move(input_img);
if (verbose) {
std::cout << "Loading from CImg object (move semantics)" << std::endl;
std::cout << "Original size: " << img.width() << "x" << img.height()
<< "x" << img.spectrum() << std::endl;
}
// 检查图像是否为空
if (img.is_empty()) {
throw std::runtime_error("Input CImg object is empty");
}
// 转换为目标格式并预处理
return preprocess_image(img, verbose);
} catch (const std::exception& e) {
std::cerr << "Error loading image from CImg object: " << e.what() << std::endl;
return {};
}
}
// 批量加载图片
std::vector<float> load_batch(const std::vector<std::string>& filepaths, bool verbose = false) {
this->verbose = verbose;
std::vector<float> batch_data;
int loaded_count = 0;
for (const auto& filepath : filepaths) {
auto img_data = load_image(filepath, verbose);
if (!img_data.empty()) {
if (batch_data.empty()) {
batch_data = img_data;
} else {
// 追加到batch数据中
batch_data.insert(batch_data.end(), img_data.begin(), img_data.end());
}
loaded_count++;
}
}
if (verbose) {
std::cout << "Successfully loaded " << loaded_count << " images out of "
<< filepaths.size() << std::endl;
}
return batch_data;
}
// 从文件夹加载所有图片
std::vector<float> load_from_directory(const std::string& dir_path,
const std::vector<std::string>& extensions = {".jpg", ".jpeg", ".png", ".bmp"},
bool verbose = false) {
this->verbose = verbose;
std::vector<std::string> image_files;
try {
// 打开目录
DIR* dir = opendir(dir_path.c_str());
if (!dir) {
throw std::runtime_error("Cannot open directory: " + dir_path);
}
// 读取目录项
struct dirent* entry;
while ((entry = readdir(dir)) != nullptr) {
std::string filename = entry->d_name;
// 跳过"."和".."
if (filename == "." || filename == "..") {
continue;
}
// 构建完整路径
std::string full_path = dir_path;
if (dir_path.back() != '/') {
full_path += "/";
}
full_path += filename;
// 检查是否为普通文件
if (is_regular_file(full_path)) {
// 检查文件扩展名
std::string ext = get_file_extension(filename);
std::string ext_lower = to_lower(ext);
// 检查是否在允许的扩展名列表中
bool valid_extension = false;
for (const auto& allowed_ext : extensions) {
if (ext_lower == to_lower(allowed_ext)) {
valid_extension = true;
break;
}
}
if (valid_extension) {
image_files.push_back(full_path);
}
}
}
closedir(dir);
if (verbose) {
std::cout << "Found " << image_files.size() << " images in directory: "
<< dir_path << std::endl;
}
return load_batch(image_files, verbose);
} catch (const std::exception& e) {
std::cerr << "Error reading directory " << dir_path << ": " << e.what() << std::endl;
return {};
}
}
// 计算数据集统计量(用于归一化)
void compute_dataset_statistics(const std::vector<std::string>& filepaths,
int max_images = 1000) {
std::cout << "Computing dataset statistics..." << std::endl;
int num_images = std::min(static_cast<int>(filepaths.size()), max_images);
std::vector<std::vector<float>> channel_sums(target_channels, std::vector<float>());
// 采样计算
for (int i = 0; i < num_images; i++) {
auto img_data = load_image(filepaths[i], false);
if (!img_data.empty()) {
// 计算每个通道的像素值
int pixels_per_channel = target_height * target_width;
for (int c = 0; c < target_channels; c++) {
const float* channel_start = img_data.data() + c * pixels_per_channel;
channel_sums[c].insert(channel_sums[c].end(),
channel_start,
channel_start + pixels_per_channel);
}
}
if (i % 100 == 0) {
std::cout << "Processed " << i << " images..." << std::endl;
}
}
// 计算均值和标准差
for (int c = 0; c < target_channels; c++) {
if (!channel_sums[c].empty()) {
// 计算均值
float sum = std::accumulate(channel_sums[c].begin(), channel_sums[c].end(), 0.0f);
channel_means[c] = sum / channel_sums[c].size();
// 计算标准差
float variance = 0.0f;
for (float val : channel_sums[c]) {
variance += (val - channel_means[c]) * (val - channel_means[c]);
}
variance /= channel_sums[c].size();
channel_stds[c] = std::sqrt(variance);
std::cout << "Channel " << c << ": mean = " << channel_means[c]
<< ", std = " << channel_stds[c] << std::endl;
}
}
std::cout << "Statistics computation completed." << std::endl;
}
// 获取批次数(根据总像素数计算)
int get_batch_size(const std::vector<float>& data) const {
int single_image_size = target_channels * target_height * target_width;
if (single_image_size == 0) return 0;
return data.size() / single_image_size;
}
// 设置归一化参数
void set_normalization_params(const std::vector<float>& means,
const std::vector<float>& stds) {
if (means.size() == target_channels && stds.size() == target_channels) {
channel_means = means;
channel_stds = stds;
} else {
std::cerr << "Error: Means and stds must have size " << target_channels << std::endl;
}
}
// 获取归一化参数
std::pair<std::vector<float>, std::vector<float>> get_normalization_params() const {
return {channel_means, channel_stds};
}
// 显示图像信息(用于调试)
void display_image_info(const std::vector<float>& image_data, int channels_to_show = 3) const {
int single_image_size = target_channels * target_height * target_width;
std::cout << "Image data info:" << std::endl;
std::cout << " Dimensions: " << target_channels << " x " << target_height
<< " x " << target_width << std::endl;
std::cout << " Total values: " << image_data.size() << std::endl;
if (!image_data.empty()) {
// 计算统计信息
float min_val = *std::min_element(image_data.begin(), image_data.end());
float max_val = *std::max_element(image_data.begin(), image_data.end());
float sum_val = std::accumulate(image_data.begin(), image_data.end(), 0.0f);
float mean_val = sum_val / image_data.size();
std::cout << " Min value: " << min_val << std::endl;
std::cout << " Max value: " << max_val << std::endl;
std::cout << " Mean value: " << mean_val << std::endl;
// 显示每个通道的前几个像素
int pixels_per_channel = target_height * target_width;
for (int c = 0; c < std::min(target_channels, channels_to_show); c++) {
std::cout << " Channel " << c << " (first 5 pixels): ";
for (int i = 0; i < std::min(5, pixels_per_channel); i++) {
std::cout << image_data[c * pixels_per_channel + i] << " ";
}
std::cout << std::endl;
}
}
}
// 获取目标尺寸(公开访问器)
int get_target_height() const { return target_height; }
int get_target_width() const { return target_width; }
int get_target_channels() const { return target_channels; }
// 设置verbose模式
void set_verbose(bool v) { verbose = v; }
private:
// 图像预处理主函数
std::vector<float> preprocess_image(cimg_library::CImg<unsigned char>& img, bool verbose = false) {
// 1. 调整通道数
adjust_channels(img);
// 2. 调整尺寸
resize_image(img);
// 3. 转换为浮点数
std::vector<float> float_data = convert_to_float(img);
// 4. 重新排列维度为 [channels, height, width]
std::vector<float> reordered_data = reorder_dimensions(float_data, img);
// 5. 归一化
if (normalize_to_01) {
normalize_0_1(reordered_data);
}
if (normalize_mean_std) {
normalize_mean_std_channels(reordered_data);
}
if (verbose) {
std::cout << "Processed image size: " << target_channels << "x"
<< target_height << "x" << target_width << std::endl;
}
return reordered_data;
}
// 调整通道数
void adjust_channels(cimg_library::CImg<unsigned char>& img) {
int current_channels = img.spectrum();
if (current_channels == 1 && target_channels == 3) {
// 灰度转RGB:复制单通道到三个通道
cimg_library::CImg<unsigned char> rgb_img(img.width(), img.height(), img.depth(), 3);
cimg_forXYZ(img, x, y, z) {
rgb_img(x, y, z, 0) = img(x, y, z, 0); // R
rgb_img(x, y, z, 1) = img(x, y, z, 0); // G
rgb_img(x, y, z, 2) = img(x, y, z, 0); // B
}
img = rgb_img;
} else if (current_channels == 3 && target_channels == 1) {
// RGB转灰度:使用标准公式
img = img.get_channel(0) * 0.299f +
img.get_channel(1) * 0.587f +
img.get_channel(2) * 0.114f;
} else if (current_channels == 4 && target_channels == 3) {
// RGBA转RGB:移除alpha通道
img.channels(0, 2);
} else if (current_channels > target_channels) {
// 保留前target_channels个通道
img.channels(0, target_channels - 1);
} else if (current_channels < target_channels) {
// 重复最后一个通道来补足
int channels_to_add = target_channels - current_channels;
for (int i = 0; i < channels_to_add; i++) {
img.append(img.get_channel(current_channels - 1), 'c');
}
}
}
// 调整图像尺寸
void resize_image(cimg_library::CImg<unsigned char>& img) {
if (img.width() != target_width || img.height() != target_height) {
if (verbose) {
std::cout << "Resizing from " << img.width() << "x" << img.height()
<< " to " << target_width << "x" << target_height << std::endl;
}
// 使用CImg的resize函数
img.resize(target_width, target_height, -100, -100, 3); // 3 = 三次样条插值
}
}
// 转换为浮点数
std::vector<float> convert_to_float(const cimg_library::CImg<unsigned char>& img) {
std::vector<float> float_data;
float_data.reserve(img.width() * img.height() * img.spectrum());
cimg_forC(img, c) {
cimg_forXY(img, x, y) {
float_data.push_back(static_cast<float>(img(x, y, 0, c)));
}
}
return float_data;
}
// 重新排列维度为 [channels, height, width]
std::vector<float> reorder_dimensions(const std::vector<float>& data,
const cimg_library::CImg<unsigned char>& img) {
int channels = img.spectrum();
int height = img.height();
int width = img.width();
std::vector<float> reordered(channels * height * width);
// CImg默认布局: [width, height, depth, spectrum]
// 我们需要: [channels, height, width]
for (int c = 0; c < channels; c++) {
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
// CImg索引: (x, y, z, c) = (w, h, 0, c)
int src_idx = c * (height * width) + h * width + w;
int dst_idx = c * (height * width) + h * width + w;
reordered[dst_idx] = data[src_idx];
}
}
}
return reordered;
}
// 归一化到[0,1]
void normalize_0_1(std::vector<float>& data) {
for (auto& val : data) {
val /= 255.0f;
}
}
// 使用均值标准差归一化(逐通道)
void normalize_mean_std_channels(std::vector<float>& data) {
int pixels_per_channel = target_height * target_width;
for (int c = 0; c < target_channels; c++) {
float mean = auto_mean_std ? channel_means[c] : mean_val;
float std = auto_mean_std ? channel_stds[c] : std_val;
for (int i = 0; i < pixels_per_channel; i++) {
int idx = c * pixels_per_channel + i;
data[idx] = (data[idx] - mean) / std;
}
}
}
};
+64
View File
@@ -0,0 +1,64 @@
/*
___col___
/ \
+---------+ \
| + + + + | | row - 第一维度
| + + + + | |
+---------+ /
*/
#pragma once
#include <cmath>
#include <vector>
#include <iostream>
#include <stdexcept>
#include "cn/中文化.hpp"
命名空间 qing {
模板 <类型名 T>
/* 矩阵类 - 二维向量 (FIXME: 1.增加函数广播方法。2.增加+=操作符等)。3. 增加初始化方法*/
类 Matrx {
公开的:
Matrx();
Matrx(常量 长型 row, 常量 长型 col); /* 该构造函数用于从头开始构造 */
Matrx(常量 长型 row, 常量 长型 col, 常量 std::vector<T>& src); /* 该构造函数用于通过已有的vector进行构造 */
const T& get(long row, long col) const; /* 访问器 - 访问矩阵元素 */
T& witch(long row, long col); /* 访问器 - 访问矩阵元素 */
const std::vector<T>& get_data() const; /* 访问器 - 用于获取原始数据 */
const long get_col() const; /* 获取列数 */
const long get_row() const; /* 获取行数 */
bool has_same_shape(const Matrx<T>& other); /* 两个矩阵是否具有相同形状 */
bool has_dot_shape(const Matrx<T>& other); /* 具有矩阵乘法的形状(标准:A的列数与B的行数相等) */
void assert_same_shape(const Matrx<T>& other); /* 断言两个矩阵具有相同形状 */
void assert_dot_shape(const Matrx<T>& other); /* 断言两个矩阵具有点积前置形状 */
Matrx<T> operator+(const Matrx<T>& other); /* 加法操作符重定义 - 矩阵加法 */
Matrx<T> operator+(T& item); /* 矩阵加法广播 */
Matrx<T> operator-(const Matrx<T>& other); /* 操作符重载 - 矩阵减法 */
Matrx<T> operator-(T& item); /* 操作符重载 - 矩阵减法广播 */
const Matrx<T> operator*(const Matrx<T>& other); /* 操作符重载 - 矩阵乘法 */
Matrx<T> operator*(T& item); /* 操作符重载 - 矩阵乘法广播 */
Matrx<T> operator/(T& item); /* 操作符重载 - 矩阵除法广播 */
/* 储存矩阵 */
void save(std::ostream& out) {
out << row << " " << col; /* 储存形状 */
for (const auto& item: arr) { /* 储存数据 */
out << " ";
out << item;
}
}
/* FIXME: 不写load()方法的原因是使用工厂模式较为方便 */
/* FIXME: 增加矩阵转置操作 */
/* FIXME: 添加广播以使得矩阵与向量相乘 */
/* FIXME: 增加元素对应乘积 */
private:
long row, col;
std::vector<T> arr; /* 存储矩阵的张量 */
};
}
+199
View File
@@ -0,0 +1,199 @@
/*
* MatrixCuda.cuh - CUDA加速的矩阵运算库
*
* 基于现有的Matrx类设计,提供GPU加速的矩阵运算
* 支持float和double类型的矩阵运算
*
* 注意:使用CUDA需要安装NVIDIA CUDA Toolkit
*/
#pragma once
#ifdef __CUDACC__
#define CUDA_HOST_DEVICE __host__ __device__
#define CUDA_HOST __host__
#define CUDA_DEVICE __device__
#else
#define CUDA_HOST_DEVICE
#define CUDA_HOST
#define CUDA_DEVICE
#endif
#include <cuda_runtime.h>
#include <device_launch_parameters.h>
#include <iostream>
#include <vector>
#include <stdexcept>
#include <cstring>
namespace qing {
// 前向声明
template <typename T>
class MatrixCuda;
// CUDA内核函数声明
namespace cuda_kernels {
// 矩阵加法内核
template <typename T>
__global__ void matrix_add_kernel(const T* A, const T* B, T* C,
long rows, long cols, long total_elements);
// 矩阵减法内核
template <typename T>
__global__ void matrix_sub_kernel(const T* A, const T* B, T* C,
long rows, long cols, long total_elements);
// 矩阵标量乘法内核
template <typename T>
__global__ void matrix_scalar_mul_kernel(const T* A, T scalar, T* C,
long total_elements);
// 矩阵标量加法内核
template <typename T>
__global__ void matrix_scalar_add_kernel(const T* A, T scalar, T* C,
long total_elements);
// 矩阵转置内核
template <typename T>
__global__ void matrix_transpose_kernel(const T* A, T* C,
long rows, long cols);
// 矩阵元素对应乘积内核
template <typename T>
__global__ void matrix_hadamard_kernel(const T* A, const T* B, T* C,
long total_elements);
// 矩阵复制内核
template <typename T>
__global__ void matrix_copy_kernel(const T* src, T* dst, long total_elements);
} // namespace cuda_kernels
/**
* @brief CUDA加速的矩阵类
*
* 这个类提供了GPU加速的矩阵运算,包括:
* - 矩阵加法、减法
* - 标量运算
* - 矩阵转置
* - 元素对应乘积
* - 矩阵乘法(使用cuBLAS)
*/
template <typename T>
class MatrixCuda {
public:
// 构造函数和析构函数
CUDA_HOST MatrixCuda();
CUDA_HOST MatrixCuda(long rows, long cols);
CUDA_HOST MatrixCuda(long rows, long cols, const std::vector<T>& data);
CUDA_HOST MatrixCuda(const MatrixCuda<T>& other);
CUDA_HOST MatrixCuda(MatrixCuda<T>&& other) noexcept;
CUDA_HOST ~MatrixCuda();
// 赋值运算符
CUDA_HOST MatrixCuda<T>& operator=(const MatrixCuda<T>& other);
CUDA_HOST MatrixCuda<T>& operator=(MatrixCuda<T>&& other) noexcept;
// 内存管理
CUDA_HOST void allocate_device_memory();
CUDA_HOST void free_device_memory();
CUDA_HOST void copy_to_device();
CUDA_HOST void copy_from_device();
CUDA_HOST void sync_device_to_host();
CUDA_HOST void sync_host_to_device();
// 访问器
CUDA_HOST_DEVICE long get_rows() const { return rows_; }
CUDA_HOST_DEVICE long get_cols() const { return cols_; }
CUDA_HOST_DEVICE long get_total_elements() const { return rows_ * cols_; }
CUDA_HOST const std::vector<T>& get_host_data() const { return host_data_; }
CUDA_HOST T* get_device_data() const { return device_data_; }
CUDA_HOST T get_element(long row, long col) const;
CUDA_HOST void set_element(long row, long col, T value);
// 形状检查
CUDA_HOST bool has_same_shape(const MatrixCuda<T>& other) const;
CUDA_HOST bool has_dot_shape(const MatrixCuda<T>& other) const;
CUDA_HOST void assert_same_shape(const MatrixCuda<T>& other) const;
CUDA_HOST void assert_dot_shape(const MatrixCuda<T>& other) const;
// 矩阵运算(GPU加速)
CUDA_HOST MatrixCuda<T> add(const MatrixCuda<T>& other) const;
CUDA_HOST MatrixCuda<T> subtract(const MatrixCuda<T>& other) const;
CUDA_HOST MatrixCuda<T> multiply_scalar(T scalar) const;
CUDA_HOST MatrixCuda<T> add_scalar(T scalar) const;
CUDA_HOST MatrixCuda<T> transpose() const;
CUDA_HOST MatrixCuda<T> hadamard_product(const MatrixCuda<T>& other) const;
// 运算符重载
CUDA_HOST MatrixCuda<T> operator+(const MatrixCuda<T>& other) const;
CUDA_HOST MatrixCuda<T> operator+(T scalar) const;
CUDA_HOST MatrixCuda<T> operator-(const MatrixCuda<T>& other) const;
CUDA_HOST MatrixCuda<T> operator-(T scalar) const;
CUDA_HOST MatrixCuda<T> operator*(T scalar) const;
CUDA_HOST MatrixCuda<T> operator/(T scalar) const;
// 复合赋值运算符
CUDA_HOST MatrixCuda<T>& operator+=(const MatrixCuda<T>& other);
CUDA_HOST MatrixCuda<T>& operator+=(T scalar);
CUDA_HOST MatrixCuda<T>& operator-=(const MatrixCuda<T>& other);
CUDA_HOST MatrixCuda<T>& operator-=(T scalar);
CUDA_HOST MatrixCuda<T>& operator*=(T scalar);
CUDA_HOST MatrixCuda<T>& operator/=(T scalar);
// 矩阵乘法(使用cuBLAS)
CUDA_HOST MatrixCuda<T> matmul(const MatrixCuda<T>& other) const;
CUDA_HOST MatrixCuda<T> operator*(const MatrixCuda<T>& other) const;
// 工具函数
CUDA_HOST void print(const std::string& name = "") const;
CUDA_HOST void fill(T value);
CUDA_HOST void fill_random(T min = 0, T max = 1);
CUDA_HOST bool is_valid() const;
// 静态工厂方法
CUDA_HOST static MatrixCuda<T> zeros(long rows, long cols);
CUDA_HOST static MatrixCuda<T> ones(long rows, long cols);
CUDA_HOST static MatrixCuda<T> identity(long size);
CUDA_HOST static MatrixCuda<T> random(long rows, long cols, T min = 0, T max = 1);
// 从现有Matrx类转换
CUDA_HOST static MatrixCuda<T> from_matrx(const class Matrx<T>& mat);
CUDA_HOST class Matrx<T> to_matrx() const;
private:
long rows_;
long cols_;
std::vector<T> host_data_; // 主机内存数据
T* device_data_; // 设备内存数据
bool device_allocated_; // 设备内存是否已分配
bool host_modified_; // 主机数据是否已修改
bool device_modified_; // 设备数据是否已修改
// 私有辅助函数
CUDA_HOST void check_index(long row, long col) const;
CUDA_HOST void initialize_from_vector(const std::vector<T>& data);
// CUDA错误检查
CUDA_HOST static void check_cuda_error(cudaError_t error, const char* file, int line);
// 计算线程块和网格大小
CUDA_HOST static dim3 get_block_size(long total_elements);
CUDA_HOST static dim3 get_grid_size(long total_elements, dim3 block_size);
};
// CUDA错误检查宏
#define CHECK_CUDA_ERROR(err) (MatrixCuda<float>::check_cuda_error(err, __FILE__, __LINE__))
// 显式实例化声明
extern template class MatrixCuda<float>;
extern template class MatrixCuda<double>;
} // namespace qing
// 包含内联实现
#include "MatrixCuda.inl.cuh"
+739
View File
@@ -0,0 +1,739 @@
/*
* MatrixCuda.inl.cuh - MatrixCuda类的内联实现
* 包含CUDA内核函数和模板成员函数的实现
*/
#ifndef MATRIX_CUDA_INL_CUH
#define MATRIX_CUDA_INL_CUH
#include "MatrixCuda.cuh"
#include <random>
#include <chrono>
#include <algorithm>
namespace qing {
// ============================================================================
// CUDA内核函数实现
// ============================================================================
namespace cuda_kernels {
// 矩阵加法内核
template <typename T>
__global__ void matrix_add_kernel(const T* A, const T* B, T* C,
long rows, long cols, long total_elements) {
long idx = blockIdx.x * blockDim.x + threadIdx.x;
long stride = blockDim.x * gridDim.x;
for (long i = idx; i < total_elements; i += stride) {
C[i] = A[i] + B[i];
}
}
// 矩阵减法内核
template <typename T>
__global__ void matrix_sub_kernel(const T* A, const T* B, T* C,
long rows, long cols, long total_elements) {
long idx = blockIdx.x * blockDim.x + threadIdx.x;
long stride = blockDim.x * gridDim.x;
for (long i = idx; i < total_elements; i += stride) {
C[i] = A[i] - B[i];
}
}
// 矩阵标量乘法内核
template <typename T>
__global__ void matrix_scalar_mul_kernel(const T* A, T scalar, T* C,
long total_elements) {
long idx = blockIdx.x * blockDim.x + threadIdx.x;
long stride = blockDim.x * gridDim.x;
for (long i = idx; i < total_elements; i += stride) {
C[i] = A[i] * scalar;
}
}
// 矩阵标量加法内核
template <typename T>
__global__ void matrix_scalar_add_kernel(const T* A, T scalar, T* C,
long total_elements) {
long idx = blockIdx.x * blockDim.x + threadIdx.x;
long stride = blockDim.x * gridDim.x;
for (long i = idx; i < total_elements; i += stride) {
C[i] = A[i] + scalar;
}
}
// 矩阵转置内核
template <typename T>
__global__ void matrix_transpose_kernel(const T* A, T* C,
long rows, long cols) {
long row = blockIdx.y * blockDim.y + threadIdx.y;
long col = blockIdx.x * blockDim.x + threadIdx.x;
if (row < rows && col < cols) {
// A[row, col] -> C[col, row]
C[col * rows + row] = A[row * cols + col];
}
}
// 矩阵元素对应乘积内核
template <typename T>
__global__ void matrix_hadamard_kernel(const T* A, const T* B, T* C,
long total_elements) {
long idx = blockIdx.x * blockDim.x + threadIdx.x;
long stride = blockDim.x * gridDim.x;
for (long i = idx; i < total_elements; i += stride) {
C[i] = A[i] * B[i];
}
}
// 矩阵复制内核
template <typename T>
__global__ void matrix_copy_kernel(const T* src, T* dst, long total_elements) {
long idx = blockIdx.x * blockDim.x + threadIdx.x;
long stride = blockDim.x * gridDim.x;
for (long i = idx; i < total_elements; i += stride) {
dst[i] = src[i];
}
}
} // namespace cuda_kernels
// ============================================================================
// MatrixCuda类成员函数实现
// ============================================================================
// 构造函数和析构函数
template <typename T>
CUDA_HOST MatrixCuda<T>::MatrixCuda()
: rows_(0), cols_(0), device_data_(nullptr),
device_allocated_(false), host_modified_(false), device_modified_(false) {}
template <typename T>
CUDA_HOST MatrixCuda<T>::MatrixCuda(long rows, long cols)
: rows_(rows), cols_(cols), device_data_(nullptr),
device_allocated_(false), host_modified_(false), device_modified_(false) {
if (rows <= 0 || cols <= 0) {
throw std::invalid_argument("Matrix dimensions must be positive");
}
host_data_.resize(rows * cols, T(0));
}
template <typename T>
CUDA_HOST MatrixCuda<T>::MatrixCuda(long rows, long cols, const std::vector<T>& data)
: rows_(rows), cols_(cols), device_data_(nullptr),
device_allocated_(false), host_modified_(false), device_modified_(false) {
initialize_from_vector(data);
}
template <typename T>
CUDA_HOST MatrixCuda<T>::MatrixCuda(const MatrixCuda<T>& other)
: rows_(other.rows_), cols_(other.cols_),
host_data_(other.host_data_), device_data_(nullptr),
device_allocated_(false), host_modified_(other.host_modified_),
device_modified_(false) {
if (other.device_allocated_) {
allocate_device_memory();
copy_to_device();
}
}
template <typename T>
CUDA_HOST MatrixCuda<T>::MatrixCuda(MatrixCuda<T>&& other) noexcept
: rows_(other.rows_), cols_(other.cols_),
host_data_(std::move(other.host_data_)),
device_data_(other.device_data_),
device_allocated_(other.device_allocated_),
host_modified_(other.host_modified_),
device_modified_(other.device_modified_) {
other.rows_ = 0;
other.cols_ = 0;
other.device_data_ = nullptr;
other.device_allocated_ = false;
other.host_modified_ = false;
other.device_modified_ = false;
}
template <typename T>
CUDA_HOST MatrixCuda<T>::~MatrixCuda() {
free_device_memory();
}
// 赋值运算符
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator=(const MatrixCuda<T>& other) {
if (this != &other) {
free_device_memory();
rows_ = other.rows_;
cols_ = other.cols_;
host_data_ = other.host_data_;
host_modified_ = other.host_modified_;
device_modified_ = false;
if (other.device_allocated_) {
allocate_device_memory();
copy_to_device();
}
}
return *this;
}
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator=(MatrixCuda<T>&& other) noexcept {
if (this != &other) {
free_device_memory();
rows_ = other.rows_;
cols_ = other.cols_;
host_data_ = std::move(other.host_data_);
device_data_ = other.device_data_;
device_allocated_ = other.device_allocated_;
host_modified_ = other.host_modified_;
device_modified_ = other.device_modified_;
other.rows_ = 0;
other.cols_ = 0;
other.device_data_ = nullptr;
other.device_allocated_ = false;
other.host_modified_ = false;
other.device_modified_ = false;
}
return *this;
}
// 内存管理函数
template <typename T>
CUDA_HOST void MatrixCuda<T>::allocate_device_memory() {
if (device_allocated_ || get_total_elements() == 0) {
return;
}
cudaError_t err = cudaMalloc(&device_data_, get_total_elements() * sizeof(T));
CHECK_CUDA_ERROR(err);
device_allocated_ = true;
device_modified_ = false;
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::free_device_memory() {
if (device_allocated_ && device_data_ != nullptr) {
cudaError_t err = cudaFree(device_data_);
CHECK_CUDA_ERROR(err);
device_data_ = nullptr;
device_allocated_ = false;
device_modified_ = false;
}
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::copy_to_device() {
if (!device_allocated_) {
allocate_device_memory();
}
if (host_modified_ && get_total_elements() > 0) {
cudaError_t err = cudaMemcpy(device_data_, host_data_.data(),
get_total_elements() * sizeof(T),
cudaMemcpyHostToDevice);
CHECK_CUDA_ERROR(err);
host_modified_ = false;
device_modified_ = true;
}
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::copy_from_device() {
if (device_allocated_ && device_modified_ && get_total_elements() > 0) {
cudaError_t err = cudaMemcpy(host_data_.data(), device_data_,
get_total_elements() * sizeof(T),
cudaMemcpyDeviceToHost);
CHECK_CUDA_ERROR(err);
device_modified_ = false;
host_modified_ = true;
}
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::sync_device_to_host() {
copy_from_device();
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::sync_host_to_device() {
copy_to_device();
}
// 访问器函数
template <typename T>
CUDA_HOST T MatrixCuda<T>::get_element(long row, long col) const {
check_index(row, col);
return host_data_[row * cols_ + col];
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::set_element(long row, long col, T value) {
check_index(row, col);
host_data_[row * cols_ + col] = value;
host_modified_ = true;
}
// 形状检查函数
template <typename T>
CUDA_HOST bool MatrixCuda<T>::has_same_shape(const MatrixCuda<T>& other) const {
return rows_ == other.rows_ && cols_ == other.cols_;
}
template <typename T>
CUDA_HOST bool MatrixCuda<T>::has_dot_shape(const MatrixCuda<T>& other) const {
return cols_ == other.rows_;
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::assert_same_shape(const MatrixCuda<T>& other) const {
if (!has_same_shape(other)) {
throw std::invalid_argument("Matrices must have the same shape");
}
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::assert_dot_shape(const MatrixCuda<T>& other) const {
if (!has_dot_shape(other)) {
throw std::invalid_argument("Matrices cannot be multiplied: cols of first != rows of second");
}
}
// 矩阵运算函数
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::add(const MatrixCuda<T>& other) const {
assert_same_shape(other);
MatrixCuda<T> result(rows_, cols_);
// 确保数据在设备上
sync_host_to_device();
other.sync_host_to_device();
result.allocate_device_memory();
// 设置CUDA内核参数
long total_elements = get_total_elements();
dim3 block_size = get_block_size(total_elements);
dim3 grid_size = get_grid_size(total_elements, block_size);
// 启动内核
cuda_kernels::matrix_add_kernel<T><<<grid_size, block_size>>>(
device_data_, other.device_data_, result.device_data_,
rows_, cols_, total_elements
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::subtract(const MatrixCuda<T>& other) const {
assert_same_shape(other);
MatrixCuda<T> result(rows_, cols_);
// 确保数据在设备上
sync_host_to_device();
other.sync_host_to_device();
result.allocate_device_memory();
// 设置CUDA内核参数
long total_elements = get_total_elements();
dim3 block_size = get_block_size(total_elements);
dim3 grid_size = get_grid_size(total_elements, block_size);
// 启动内核
cuda_kernels::matrix_sub_kernel<T><<<grid_size, block_size>>>(
device_data_, other.device_data_, result.device_data_,
rows_, cols_, total_elements
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::multiply_scalar(T scalar) const {
MatrixCuda<T> result(rows_, cols_);
// 确保数据在设备上
sync_host_to_device();
result.allocate_device_memory();
// 设置CUDA内核参数
long total_elements = get_total_elements();
dim3 block_size = get_block_size(total_elements);
dim3 grid_size = get_grid_size(total_elements, block_size);
// 启动内核
cuda_kernels::matrix_scalar_mul_kernel<T><<<grid_size, block_size>>>(
device_data_, scalar, result.device_data_, total_elements
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::add_scalar(T scalar) const {
MatrixCuda<T> result(rows_, cols_);
// 确保数据在设备上
sync_host_to_device();
result.allocate_device_memory();
// 设置CUDA内核参数
long total_elements = get_total_elements();
dim3 block_size = get_block_size(total_elements);
dim3 grid_size = get_grid_size(total_elements, block_size);
// 启动内核
cuda_kernels::matrix_scalar_add_kernel<T><<<grid_size, block_size>>>(
device_data_, scalar, result.device_data_, total_elements
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::transpose() const {
MatrixCuda<T> result(cols_, rows_);
// 确保数据在设备上
sync_host_to_device();
result.allocate_device_memory();
// 设置CUDA内核参数(2D网格用于转置)
dim3 block_size(16, 16);
dim3 grid_size((cols_ + block_size.x - 1) / block_size.x,
(rows_ + block_size.y - 1) / block_size.y);
// 启动转置内核
cuda_kernels::matrix_transpose_kernel<T><<<grid_size, block_size>>>(
device_data_, result.device_data_, rows_, cols_
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::hadamard_product(const MatrixCuda<T>& other) const {
assert_same_shape(other);
MatrixCuda<T> result(rows_, cols_);
// 确保数据在设备上
sync_host_to_device();
other.sync_host_to_device();
result.allocate_device_memory();
// 设置CUDA内核参数
long total_elements = get_total_elements();
dim3 block_size = get_block_size(total_elements);
dim3 grid_size = get_grid_size(total_elements, block_size);
// 启动内核
cuda_kernels::matrix_hadamard_kernel<T><<<grid_size, block_size>>>(
device_data_, other.device_data_, result.device_data_, total_elements
);
CHECK_CUDA_ERROR(cudaGetLastError());
CHECK_CUDA_ERROR(cudaDeviceSynchronize());
result.device_modified_ = true;
result.sync_device_to_host();
return result;
}
// 运算符重载
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator+(const MatrixCuda<T>& other) const {
return add(other);
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator+(T scalar) const {
return add_scalar(scalar);
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator-(const MatrixCuda<T>& other) const {
return subtract(other);
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator-(T scalar) const {
return add_scalar(-scalar);
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator*(T scalar) const {
return multiply_scalar(scalar);
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator/(T scalar) const {
if (scalar == T(0)) {
throw std::invalid_argument("Division by zero");
}
return multiply_scalar(T(1) / scalar);
}
// 复合赋值运算符
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator+=(const MatrixCuda<T>& other) {
*this = *this + other;
return *this;
}
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator+=(T scalar) {
*this = *this + scalar;
return *this;
}
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator-=(const MatrixCuda<T>& other) {
*this = *this - other;
return *this;
}
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator-=(T scalar) {
*this = *this - scalar;
return *this;
}
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator*=(T scalar) {
*this = *this * scalar;
return *this;
}
template <typename T>
CUDA_HOST MatrixCuda<T>& MatrixCuda<T>::operator/=(T scalar) {
*this = *this / scalar;
return *this;
}
// 矩阵乘法(基础实现,使用朴素算法)
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::matmul(const MatrixCuda<T>& other) const {
assert_dot_shape(other);
MatrixCuda<T> result(rows_, other.cols_);
// 同步数据到主机(使用朴素算法在主机上计算)
sync_device_to_host();
other.sync_device_to_host();
// 朴素矩阵乘法(O(n³))
for (long i = 0; i < rows_; ++i) {
for (long j = 0; j < other.cols_; ++j) {
T sum = T(0);
for (long k = 0; k < cols_; ++k) {
sum += get_element(i, k) * other.get_element(k, j);
}
result.set_element(i, j, sum);
}
}
// 标记结果已修改
result.host_modified_ = true;
result.sync_host_to_device();
return result;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::operator*(const MatrixCuda<T>& other) const {
return matmul(other);
}
// 工具函数
template <typename T>
CUDA_HOST void MatrixCuda<T>::print(const std::string& name) const {
sync_device_to_host();
if (!name.empty()) {
std::cout << name << " (" << rows_ << "x" << cols_ << "):" << std::endl;
} else {
std::cout << "Matrix (" << rows_ << "x" << cols_ << "):" << std::endl;
}
for (long i = 0; i < rows_; ++i) {
std::cout << " [";
for (long j = 0; j < cols_; ++j) {
std::cout << get_element(i, j);
if (j < cols_ - 1) std::cout << ", ";
}
std::cout << "]" << std::endl;
}
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::fill(T value) {
std::fill(host_data_.begin(), host_data_.end(), value);
host_modified_ = true;
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::fill_random(T min, T max) {
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_real_distribution<T> dist(min, max);
for (auto& val : host_data_) {
val = dist(gen);
}
host_modified_ = true;
}
template <typename T>
CUDA_HOST bool MatrixCuda<T>::is_valid() const {
return rows_ > 0 && cols_ > 0 && !host_data_.empty();
}
// 静态工厂方法
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::zeros(long rows, long cols) {
MatrixCuda<T> mat(rows, cols);
mat.fill(T(0));
return mat;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::ones(long rows, long cols) {
MatrixCuda<T> mat(rows, cols);
mat.fill(T(1));
return mat;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::identity(long size) {
MatrixCuda<T> mat(size, size);
mat.fill(T(0));
for (long i = 0; i < size; ++i) {
mat.set_element(i, i, T(1));
}
return mat;
}
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::random(long rows, long cols, T min, T max) {
MatrixCuda<T> mat(rows, cols);
mat.fill_random(min, max);
return mat;
}
// 从现有Matrx类转换
template <typename T>
CUDA_HOST MatrixCuda<T> MatrixCuda<T>::from_matrx(const class Matrx<T>& mat) {
// 注意:这里假设Matrx类有get_row(), get_col(), get_data()方法
long rows = mat.get_row();
long cols = mat.get_col();
const std::vector<T>& data = mat.get_data();
return MatrixCuda<T>(rows, cols, data);
}
template <typename T>
CUDA_HOST class Matrx<T> MatrixCuda<T>::to_matrx() const {
sync_device_to_host();
// 注意:这里假设Matrx类有相应的构造函数
// 实际实现可能需要根据Matrx类的具体接口调整
return class Matrx<T>(rows_, cols_, host_data_);
}
// 私有辅助函数
template <typename T>
CUDA_HOST void MatrixCuda<T>::check_index(long row, long col) const {
if (row < 0 || row >= rows_ || col < 0 || col >= cols_) {
throw std::out_of_range("Matrix index out of range");
}
}
template <typename T>
CUDA_HOST void MatrixCuda<T>::initialize_from_vector(const std::vector<T>& data) {
if (data.size() != static_cast<size_t>(rows_ * cols_)) {
throw std::invalid_argument("Data size does not match matrix dimensions");
}
host_data_ = data;
host_modified_ = true;
}
// CUDA错误检查
template <typename T>
CUDA_HOST void MatrixCuda<T>::check_cuda_error(cudaError_t error, const char* file, int line) {
if (error != cudaSuccess) {
std::cerr << "CUDA error at " << file << ":" << line << ": "
<< cudaGetErrorString(error) << std::endl;
throw std::runtime_error("CUDA error occurred");
}
}
// 计算线程块和网格大小
template <typename T>
CUDA_HOST dim3 MatrixCuda<T>::get_block_size(long total_elements) {
// 使用256个线程的块(CUDA的典型值)
const long max_threads_per_block = 256;
long threads = std::min(total_elements, max_threads_per_block);
return dim3(threads, 1, 1);
}
template <typename T>
CUDA_HOST dim3 MatrixCuda<T>::get_grid_size(long total_elements, dim3 block_size) {
// 计算需要的块数
long blocks = (total_elements + block_size.x - 1) / block_size.x;
// 限制最大块数(避免过多块)
const long max_blocks = 65535;
blocks = std::min(blocks, max_blocks);
return dim3(blocks, 1, 1);
}
// 显式实例化
template class MatrixCuda<float>;
template class MatrixCuda<double>;
} // namespace qing
#endif // MATRIX_CUDA_INL_CUH
+67
View File
@@ -0,0 +1,67 @@
#pragma once
#include <iostream>
#include <stdexcept>
#include "nn/NeuralNetwork.hpp"
namespace qing {
class NNBuilder { /* 神经网络构建 */
public:
void add(NeuralNetwork &layer);
/* 获取变换层的形状
* FIXME: 这个函数有必要吗 */
std::vector<std::pair<long, long>> get_shape() const;
/* 向标准输出打印形状 */
void print_shape() const;
/* 向输出流储存模型 */
void save(std::ostream& out) {
for (auto& layer: nn) {
layer.save(out);
}
}
/* 从输入流加载模型 */
void load(std::istream& in) {
nn.clear();
while(true) {
try{
auto layer = NeuralNetwork::Load_in_Factory(in);
add(layer);
}
catch (std::runtime_error& e) {
break;
}
}
}
/*
* 将一层非线性层分裂为两层,相当于插入一层神经元
*/
//void fork(int index, long num, float rate) {
/*
* 神经网络生长,增加第layer层,每层增加num个神经元
*
* layer: 增加的层数
* num: 增加的神经元数量
*
* FIXME: 神经网络生长时应当冻结其它层(除了最后一层?)(learning_rate == 0)
*/
//void grow(int layer, int num) {
/* 前向反馈 */
std::vector<float> forward(std::vector<float>& r);
/* 反向传播 */
std::vector<float> backward(std::vector<float>& errs, float discount = 1.0);
/* 计算误差 */
std::vector<float> cal_err(std::vector<float>& r, std::vector<float>& t);
private:
std::vector<NeuralNetwork> nn;
};
}
+149
View File
@@ -0,0 +1,149 @@
#pragma once
#include <random>
#include "nn/Matrix.hh"
#include "nn/ILayerFullyConnectted.hpp"
namespace qing {
/* 人工神经网络 - 全连接层 */
class NeuralNetwork: public ILayerFullyConnectted {
public:
/* 构造函数 使用基本参数构造
* FIXME: 只能输入已经存在的向量 */
NeuralNetwork(
const long inputno, const long outputno,
const std::vector<float> &weights,
const std::vector<float> &bias,
const float learning_rate,
const ActivationFunc func_type
);
/* 获取激活函数枚举类型 */
const ActivationFunc& get_f() const {
return f_type;
}
/* 获取学习率 */
const float& get_lr () const {
return learning_rate;
}
/*
* 获取输入层大小
*
* FIXME: 优化掉这两个访问器,通过直接返回矩阵,获取层的形状
*/
long get_inputno() const;
/* 获取输出层大小 */
long get_outputno() const;
/* 获取权重矩阵 */
Matrx<float>& get_weights();
/* 获取偏置矩阵 */
Matrx<float>& get_bias();
/* 正态分布Xavier初始化 */
static void xavierNormalInit(std::vector<float> &weights, long fan_in, long fan_out);
/* 均匀分布Xavier初始化函数 */
static void xavierUniformInit(std::vector<float> &weights, int fan_in, int fan_out);
/* 工厂模式 通过形状构建全连接层 */
static NeuralNetwork Create_in_Factory(const long inputno, const long outputno, float learning_rate, ActivationFunc func_type);
/* 工厂模式 通过输入流读形状构建 */
static NeuralNetwork Load_in_Factory(std::istream& in) {
std::string f_name;
if (!(in >> f_name))
throw std::runtime_error("read out of lines");
float learning_rate;
in >> learning_rate;
/*------------------*/
size_t row, col;
in >> row; /* FIXME: 如果是负数会怎样 */
in >> col;
auto weis = load_vec(in, row * col);
size_t rowb, colb;
in >> rowb >> colb; /* FIXME: 如果这里读错了,也要抛出异常 */
auto bias = load_vec(in, col);
auto layer = NeuralNetwork(
row, col, weis, bias, learning_rate, ILayer::parse_ff_type(f_name));
/*------------------*/
return layer;
}
static std::vector<float> load_vec(std::istream& in, size_t length) {
std::vector<float> data;
for (size_t i = 0; i < length; ++i) {
float item;
in >> item;
data.push_back(item);
}
return data;
}
/* 前向反馈 */
std::vector<float> forward(const std::vector<float>& x) override;
/* 反向传播 */
std::vector<float> backward(const std::vector<float>& errors) override;
/*
* 更新权重
*
* 可以输入一个折扣系数
*/
void update(float discount = 1.0);
/*
* 神经网络层长大
*
* 重新初始化权重矩阵,使用均匀分布初始化
*
* num: 增加的神经元数量
* flag: true为增加输入层,false为增加输出层
*/
//void grow(int num, bool flag) {
/* 输出神经网络到输出流 */
void save(std::ostream& out) override {
out << get_f_type(f_type) << " ";
out << learning_rate << " ";
weights.save(out); out << " ";
bias.save(out); out << "\n";
}
/* 从输入流加载模型
* FIXME: 只能从工厂模式中构造 */
//void load(std::istream& in) override
private:
/* 神经网络层规格 */
long inputno, outputno;
/* 权重与偏置矩阵 */
Matrx<float> weights; /* (outputs, inputs) */
Matrx<float> bias; /* (1, outputs) */
/* 学习率 */
float learning_rate;
/* 激活函数 */
ActivationFunc f_type;
/* 存储在类内部的上一次的输入,输出,和激活前输出 */
Matrx<float> prev_inputs, prev_raw_inputs, prev_outputs;
/* 存储本次学习的权重梯度以及偏置梯度 */
Matrx<float> weights_grad, bias_grad;
};
}
+502
View File
@@ -0,0 +1,502 @@
#pragma once
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <limits>
#include <functional>
class PoolingLayer {
private:
// 池化类型枚举
enum class PoolType {
MAX,
AVG
};
// 参数
int pool_height;
int pool_width;
int stride;
int padding;
PoolType pool_type;
// 输入输出尺寸
int input_channels;
int input_height;
int input_width;
int output_height;
int output_width;
// 中间变量缓存(用于反向传播)
std::vector<int> max_idx_cache; // 最大池化时缓存最大值位置
std::vector<float> input_cache; // 缓存的输入
std::vector<float> output_cache; // 缓存的输出
// 辅助函数
//void max_pool_forward(const float* input, float* output, int batch_size);
//void avg_pool_forward(const float* input, float* output, int batch_size);
//void max_pool_backward(const float* grad_output, float* grad_input, int batch_size);
//void avg_pool_backward(const float* grad_output, float* grad_input, int batch_size);
public:
// 构造函数
PoolingLayer(int pool_size, int stride = 2, int padding = 0, bool max_pool = true)
: pool_height(pool_size),
pool_width(pool_size),
stride(stride),
padding(padding),
pool_type(max_pool ? PoolType::MAX : PoolType::AVG) {}
// 带不同高度和宽度的构造函数
PoolingLayer(int pool_height, int pool_width, int stride = 2, int padding = 0, bool max_pool = true)
: pool_height(pool_height),
pool_width(pool_width),
stride(stride),
padding(padding),
pool_type(max_pool ? PoolType::MAX : PoolType::AVG) {}
// 设置输入尺寸
void set_input_shape(int channels, int height, int width) {
input_channels = channels;
input_height = height;
input_width = width;
// 计算输出尺寸
output_height = (height + 2 * padding - pool_height) / stride + 1;
output_width = (width + 2 * padding - pool_width) / stride + 1;
// 清空缓存
max_idx_cache.clear();
input_cache.clear();
output_cache.clear();
}
// 前向传播
std::vector<float> forward(const std::vector<float>& input, int batch_size = 1, bool training = true) {
// 计算输出大小
int output_size = batch_size * input_channels * output_height * output_width;
std::vector<float> output(output_size, 0.0f);
// 如果训练模式,缓存输入
if (training) {
input_cache = input;
}
// 根据池化类型选择前向传播函数
if (pool_type == PoolType::MAX) {
max_pool_forward(input.data(), output.data(), batch_size);
} else {
avg_pool_forward(input.data(), output.data(), batch_size);
}
// 如果训练模式,缓存输出
if (training) {
output_cache = output;
}
return output;
}
// 反向传播
std::vector<float> backward(const std::vector<float>& grad_output, int batch_size = 1) {
// 创建输入梯度
std::vector<float> grad_input(batch_size * input_channels * input_height * input_width, 0.0f);
// 根据池化类型选择反向传播函数
if (pool_type == PoolType::MAX) {
max_pool_backward(grad_output.data(), grad_input.data(), batch_size);
} else {
avg_pool_backward(grad_output.data(), grad_input.data(), batch_size);
}
return grad_input;
}
// 获取输出尺寸
std::tuple<int, int, int> get_output_shape() const {
return std::make_tuple(input_channels, output_height, output_width);
}
// 获取参数信息
void print_info() const {
std::cout << "Pooling Layer Info:" << std::endl;
std::cout << " Type: " << (pool_type == PoolType::MAX ? "Max Pooling" : "Average Pooling") << std::endl;
std::cout << " Pool Size: " << pool_height << "x" << pool_width << std::endl;
std::cout << " Stride: " << stride << std::endl;
std::cout << " Padding: " << padding << std::endl;
std::cout << " Input Shape: " << input_channels << "x" << input_height << "x" << input_width << std::endl;
std::cout << " Output Shape: " << input_channels << "x" << output_height << "x" << output_width << std::endl;
}
private:
// 最大池化前向传播实现
void max_pool_forward(const float* input, float* output, int batch_size) {
// 计算填充后的尺寸
int padded_height = input_height + 2 * padding;
int padded_width = input_width + 2 * padding;
// 清空最大值位置缓存
max_idx_cache.clear();
max_idx_cache.resize(batch_size * input_channels * output_height * output_width, -1);
// 对每个批次、每个通道进行处理
for (int b = 0; b < batch_size; b++) {
for (int c = 0; c < input_channels; c++) {
// 当前通道的输入和输出指针
const float* input_ptr = input + b * input_channels * input_height * input_width +
c * input_height * input_width;
float* output_ptr = output + b * input_channels * output_height * output_width +
c * output_height * output_width;
int* max_idx_ptr = max_idx_cache.data() + b * input_channels * output_height * output_width +
c * output_height * output_width;
// 对每个输出位置进行计算
for (int oh = 0; oh < output_height; oh++) {
for (int ow = 0; ow < output_width; ow++) {
// 计算池化窗口的起始位置
int start_h = oh * stride - padding;
int start_w = ow * stride - padding;
// 计算池化窗口的结束位置
int end_h = std::min(start_h + pool_height, padded_height);
int end_w = std::min(start_w + pool_width, padded_width);
// 调整起始位置,确保不超出边界
start_h = std::max(start_h, 0);
start_w = std::max(start_w, 0);
// 在池化窗口内寻找最大值
float max_val = -std::numeric_limits<float>::max();
int max_idx = -1;
for (int ph = start_h; ph < end_h; ph++) {
for (int pw = start_w; pw < end_w; pw++) {
// 计算原始输入中的索引(考虑填充)
int orig_ph = ph - padding;
int orig_pw = pw - padding;
// 检查是否在有效范围内
if (orig_ph >= 0 && orig_ph < input_height &&
orig_pw >= 0 && orig_pw < input_width) {
int idx = orig_ph * input_width + orig_pw;
float val = input_ptr[idx];
if (val > max_val) {
max_val = val;
max_idx = idx;
}
}
}
}
// 存储输出和最大值位置
output_ptr[oh * output_width + ow] = max_val;
max_idx_ptr[oh * output_width + ow] = max_idx;
}
}
}
}
}
// 平均池化前向传播实现
void avg_pool_forward(const float* input, float* output, int batch_size) {
// 计算填充后的尺寸
int padded_height = input_height + 2 * padding;
int padded_width = input_width + 2 * padding;
// 对每个批次、每个通道进行处理
for (int b = 0; b < batch_size; b++) {
for (int c = 0; c < input_channels; c++) {
// 当前通道的输入和输出指针
const float* input_ptr = input + b * input_channels * input_height * input_width +
c * input_height * input_width;
float* output_ptr = output + b * input_channels * output_height * output_width +
c * output_height * output_width;
// 对每个输出位置进行计算
for (int oh = 0; oh < output_height; oh++) {
for (int ow = 0; ow < output_width; ow++) {
// 计算池化窗口的起始位置
int start_h = oh * stride - padding;
int start_w = ow * stride - padding;
// 计算池化窗口的结束位置
int end_h = std::min(start_h + pool_height, padded_height);
int end_w = std::min(start_w + pool_width, padded_width);
// 调整起始位置,确保不超出边界
start_h = std::max(start_h, 0);
start_w = std::max(start_w, 0);
// 在池化窗口内计算平均值
float sum = 0.0f;
int count = 0;
for (int ph = start_h; ph < end_h; ph++) {
for (int pw = start_w; pw < end_w; pw++) {
// 计算原始输入中的索引(考虑填充)
int orig_ph = ph - padding;
int orig_pw = pw - padding;
// 检查是否在有效范围内
if (orig_ph >= 0 && orig_ph < input_height &&
orig_pw >= 0 && orig_pw < input_width) {
int idx = orig_ph * input_width + orig_pw;
sum += input_ptr[idx];
count++;
}
}
}
// 计算平均值
output_ptr[oh * output_width + ow] = (count > 0) ? sum / count : 0.0f;
}
}
}
}
}
// 最大池化反向传播实现
void max_pool_backward(const float* grad_output, float* grad_input, int batch_size) {
// 初始化输入梯度为0
std::memset(grad_input, 0, batch_size * input_channels * input_height * input_width * sizeof(float));
// 对每个批次、每个通道进行处理
for (int b = 0; b < batch_size; b++) {
for (int c = 0; c < input_channels; c++) {
// 当前通道的梯度指针
const float* grad_output_ptr = grad_output + b * input_channels * output_height * output_width +
c * output_height * output_width;
float* grad_input_ptr = grad_input + b * input_channels * input_height * input_width +
c * input_height * input_width;
const int* max_idx_ptr = max_idx_cache.data() + b * input_channels * output_height * output_width +
c * output_height * output_width;
// 对每个输出位置进行反向传播
for (int oh = 0; oh < output_height; oh++) {
for (int ow = 0; ow < output_width; ow++) {
// 获取最大值位置索引
int max_idx = max_idx_ptr[oh * output_width + ow];
// 如果最大值位置有效,将梯度传递到该位置
if (max_idx >= 0) {
grad_input_ptr[max_idx] += grad_output_ptr[oh * output_width + ow];
}
}
}
}
}
}
// 平均池化反向传播实现
void avg_pool_backward(const float* grad_output, float* grad_input, int batch_size) {
// 初始化输入梯度为0
std::memset(grad_input, 0, batch_size * input_channels * input_height * input_width * sizeof(float));
// 计算填充后的尺寸
int padded_height = input_height + 2 * padding;
int padded_width = input_width + 2 * padding;
// 对每个批次、每个通道进行处理
for (int b = 0; b < batch_size; b++) {
for (int c = 0; c < input_channels; c++) {
// 当前通道的梯度指针
const float* grad_output_ptr = grad_output + b * input_channels * output_height * output_width +
c * output_height * output_width;
float* grad_input_ptr = grad_input + b * input_channels * input_height * input_width +
c * input_height * input_width;
// 对每个输出位置进行反向传播
for (int oh = 0; oh < output_height; oh++) {
for (int ow = 0; ow < output_width; ow++) {
// 计算池化窗口的起始位置
int start_h = oh * stride - padding;
int start_w = ow * stride - padding;
// 计算池化窗口的结束位置
int end_h = std::min(start_h + pool_height, padded_height);
int end_w = std::min(start_w + pool_width, padded_width);
// 调整起始位置,确保不超出边界
start_h = std::max(start_h, 0);
start_w = std::max(start_w, 0);
// 计算有效池化窗口的大小
int count = 0;
for (int ph = start_h; ph < end_h; ph++) {
for (int pw = start_w; pw < end_w; pw++) {
// 计算原始输入中的索引(考虑填充)
int orig_ph = ph - padding;
int orig_pw = pw - padding;
// 检查是否在有效范围内
if (orig_ph >= 0 && orig_ph < input_height &&
orig_pw >= 0 && orig_pw < input_width) {
count++;
}
}
}
// 如果池化窗口内有有效元素,计算平均梯度
if (count > 0) {
float avg_grad = grad_output_ptr[oh * output_width + ow] / count;
// 将平均梯度分配到池化窗口内的每个位置
for (int ph = start_h; ph < end_h; ph++) {
for (int pw = start_w; pw < end_w; pw++) {
// 计算原始输入中的索引(考虑填充)
int orig_ph = ph - padding;
int orig_pw = pw - padding;
// 检查是否在有效范围内
if (orig_ph >= 0 && orig_ph < input_height &&
orig_pw >= 0 && orig_pw < input_width) {
int idx = orig_ph * input_width + orig_pw;
grad_input_ptr[idx] += avg_grad;
}
}
}
}
}
}
}
}
}
};
// 使用示例函数
inline void pooling_layer_example() {
std::cout << "=== Pooling Layer Example ===" << std::endl;
// 创建最大池化层
PoolingLayer max_pool(2, 2, 0, true); // 2x2 最大池化,步长2,无填充
max_pool.set_input_shape(3, 4, 4); // 3个通道,4x4输入
// 创建输入数据
std::vector<float> input(3 * 4 * 4);
for (int i = 0; i < 3 * 4 * 4; i++) {
input[i] = static_cast<float>(i) / 10.0f;
}
// 前向传播
std::cout << "Forward pass..." << std::endl;
auto output = max_pool.forward(input, 1, true);
// 获取输出形状
auto [channels, height, width] = max_pool.get_output_shape();
std::cout << "Output shape: " << channels << "x" << height << "x" << width << std::endl;
// 打印输出
std::cout << "Output values (first 10): ";
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 = max_pool.backward(grad_output, 1);
std::cout << "Gradient input size: " << grad_input.size() << std::endl;
std::cout << "Gradient input values (first 10): ";
for (int i = 0; i < std::min(10, static_cast<int>(grad_input.size())); i++) {
std::cout << grad_input[i] << " ";
}
std::cout << std::endl;
// 打印层信息
max_pool.print_info();
}
// 全局平均池化层(常用于CNN分类网络的最后)
class GlobalAveragePooling {
private:
int input_channels;
int input_height;
int input_width;
std::vector<float> input_cache;
public:
// 设置输入尺寸
void set_input_shape(int channels, int height, int width) {
input_channels = channels;
input_height = height;
input_width = width;
}
// 前向传播
std::vector<float> forward(const std::vector<float>& input, int batch_size = 1, bool training = true) {
// 缓存输入(如果需要)
if (training) {
input_cache = input;
}
// 计算输出大小:每个通道一个值
int output_size = batch_size * input_channels;
std::vector<float> output(output_size, 0.0f);
// 对每个批次、每个通道计算平均值
int spatial_size = input_height * input_width;
for (int b = 0; b < batch_size; b++) {
for (int c = 0; c < input_channels; c++) {
const float* input_ptr = input.data() + b * input_channels * spatial_size + c * spatial_size;
float sum = 0.0f;
// 计算该通道所有空间位置的平均值
for (int i = 0; i < spatial_size; i++) {
sum += input_ptr[i];
}
output[b * input_channels + c] = sum / spatial_size;
}
}
return output;
}
// 反向传播
std::vector<float> backward(const std::vector<float>& grad_output, int batch_size = 1) {
// 创建输入梯度
int spatial_size = input_height * input_width;
std::vector<float> grad_input(batch_size * input_channels * spatial_size, 0.0f);
// 对每个批次、每个通道分配梯度
for (int b = 0; b < batch_size; b++) {
for (int c = 0; c < input_channels; c++) {
float grad = grad_output[b * input_channels + c] / spatial_size;
float* grad_input_ptr = grad_input.data() + b * input_channels * spatial_size + c * spatial_size;
// 将梯度均匀分配到所有空间位置
for (int i = 0; i < spatial_size; i++) {
grad_input_ptr[i] = grad;
}
}
}
return grad_input;
}
// 获取输出尺寸
std::tuple<int, int> get_output_shape() const {
return std::make_tuple(input_channels, 1);
}
};
/*
// 创建池化层
PoolingLayer pool(2, 2, 0, true); // 2x2最大池化,步长2,无填充
pool.set_input_shape(64, 28, 28); // 64通道,28x28输入
// 前向传播
auto output = pool.forward(input, batch_size, true);
// 反向传播
auto grad_input = pool.backward(grad_output, batch_size);
*/