#pragma once #include #include #include #include #include #include #include #include #include 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 weights; // shape: [output_channels, input_channels, kernel_size, kernel_size] std::vector bias; // shape: [output_channels] // 梯度和动量 std::vector d_weights; // 权重梯度 std::vector d_bias; // 偏置梯度 std::vector weight_momentum; // 动量 std::vector bias_momentum; // 偏置动量 // 中间变量缓存(用于反向传播) std::vector col_buffer; // im2col后的矩阵 std::vector input_cache; // 缓存的输入(用于计算输入梯度) std::vector output_cache; // 缓存的输出(激活前) std::vector activated_cache; // 激活后的输出缓存 std::vector 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 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 forward(const std::vector& input, int batch_size = 1, bool training = true) { // 计算输出大小 int output_size = batch_size * output_channels * output_height * output_width; std::vector output(output_size, 0.0f); std::vector 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 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 backward(const std::vector& grad_output, int batch_size = 1) { // grad_output是激活函数后的梯度,需要先计算激活函数前的梯度 std::vector 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 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 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 d_input(batch_size * input_channels * input_height * input_width, 0.0f); // 计算d_input_col std::vector 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 m_weights(weights.size(), 0.0f); static std::vector v_weights(weights.size(), 0.0f); static std::vector m_bias(bias.size(), 0.0f); static std::vector 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& get_weights() const { return weights; } const std::vector& get_bias() const { return bias; } const std::vector& get_weight_gradients() const { return d_weights; } const std::vector& get_bias_gradients() const { return d_bias; } // 设置参数(用于加载预训练模型) void set_weights(const std::vector& new_weights) { weights = new_weights; } void set_bias(const std::vector& new_bias) { bias = new_bias; } // 设置Leaky ReLU参数 void set_leaky_relu_slope(float slope) { negative_slope = slope; } // 获取输出尺寸 std::tuple get_output_shape() const { return std::make_tuple(output_height, output_width); } // 梯度检查(用于调试) float gradient_check(const std::vector& input, const std::vector& 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 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 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]; } } } } }