C ++中实现ResNet34代码

实现ResNet-34需要了解ResNet的基本架构。ResNet是一个深度残差神经网络,其基础结构是由多个残差块(Residual block)堆叠而成的。每个残差块包含两个卷积层和一个跳跃连接(shortcut connection),跳跃连接负责绕过这两个卷积层,直接连接两个卷积层的输入和输出,从而允许信息在深度网络中跳过某些层。这样可以避免梯度消失和梯度爆炸问题,使得更深的网络结构能够被训练。

实现ResNet34需要实现一些基本的模块,如卷积层、残差块等。以下是一个简单的实现代码,您可以参考:

#include <iostream>
#include <vector>

using namespace std;

// 定义一个二维矩阵类型表示卷积层的卷积核
typedef vector<vector<double>> Matrix;

// 定义卷积函数
vector<vector<double>> conv2d(vector<vector<double>>& input, Matrix& kernel, int stride = 1)
{
    
    
    int in_channels = input.size();
    int out_channels = kernel.size();
    int kernel_size = kernel[0].size();
    int input_size = input[0].size();
    int output_size = (input_size - kernel_size) / stride + 1;

    vector<vector<double>> output(out_channels, vector<double>(output_size, 0));

    for (int l = 0; l < out_channels; l++)
    {
    
    
        for (int i = 0; i < output_size; i++)
        {
    
    
            for (int j = 0; j < output_size; j++)
            {
    
    
                double sum = 0.0;
                for (int c = 0; c < in_channels; c++)
                {
    
    
                    for (int m = 0; m < kernel_size; m++)
                    {
    
    
                        for (int n = 0; n < kernel_size; n++)
                        {
    
    
                            sum += input[c][i * stride + m][j * stride + n] * kernel[l][m][n];
                        }
                    }
                }
                output[l][i][j] = sum;
            }
        }
    }

    return output;
}

// 定义归一化函数
vector<vector<double>> normalize(vector<vector<double>>& input)
{
    
    
    int size = input.size();
    int rows = input[0].size();
    int cols = input[0][0].size();

    vector<double> mean(size, 0);
    vector<double> std(size, 0);

    for (int i = 0; i < size; i++)
    {
    
    
        double sum = 0.0;
        double square_sum = 0.0;
        for (int j = 0; j < rows; j++)
        {
    
    
            for (int k = 0; k < cols; k++)
            {
    
    
                sum += input[i][j][k];
                square_sum += input[i][j][k] * input[i][j][k];
            }
        }
        mean[i] = sum / (rows * cols);
        std[i] = sqrt(square_sum / (rows * cols) - mean[i] * mean[i]);
    }

    vector<vector<double>> output(size, vector<double>(rows, 0));
    for (int i = 0; i < size; i++)
    {
    
    
        for (int j = 0; j < rows; j++)
        {
    
    
            for (int k = 0; k < cols; k++)
            {
    
    
                output[i][j] = (input[i][j][k] - mean[i]) / std[i];
            }
        }
    }

    return output;
}

// 定义残差块
vector<vector<double>> residual(vector<vector<double>>& input, Matrix& kernel1, Matrix& kernel2, int stride = 1)
{
    
    
    int in_channels = input.size();
    int out_channels = kernel2.size();
    int kernel_size = kernel2[0].size();
    int input_size = input[0].size();
    int output_size = (input_size - kernel_size) / stride + 1;

    // 对输入进行归一化处理
    input = normalize(input);

    // 使用两个卷积层实现残差块
    // 第一个卷积层
    vector<vector<double>> conv1_output = conv2d(input, kernel1, stride);
    // 使用ReLU激活函数
    for (int i = 0; i < out_channels; i++)
    {
    
    
        for (int j = 0; j < output_size; j++)
        {
    
    
            for (int k = 0; k < output_size; k++)
            {
    
    
                conv1_output[i][j][k] = max(conv1_output[i][j][k], 0.0);
            }
        }
    }

    // 第二个卷积层
    vector<vector<double>> conv2_output = conv2d(conv1_output, kernel2);

    // 残差连接
    vector<vector<double>> residual_output(out_channels, vector<double>(output_size, 0));
    for (int i = 0; i < out_channels; i++)
    {
    
    
        for (int j = 0; j < output_size; j++)
        {
    
    
            for (int k = 0; k < output_size; k++)
            {
    
    
                residual_output[i][j][k] = conv2_output[i][j][k] + input[i][j * stride][k * stride];
            }
        }
    }

    return residual_output;
}

// 定义ResNet34模型
vector<vector<double>> resnet34(vector<vector<double>>& input)
{
    
    
    // 定义卷积核
    Matrix conv1_kernel = {
    
     {
    
    -1, -1, -1}, {
    
    -1, 8, -1}, {
    
    -1, -1, -1} };
    Matrix conv2_kernel1 = {
    
     {
    
    -1, -1, -1}, {
    
    -1, 0, -1}, {
    
    -1, -1, -1} };
    Matrix conv2_kernel2 = {
    
     {
    
    -1, -1, -1}, {
    
    -1, 8, -1}, {
    
    -1, -1, -1} };

    Matrix conv3_kernel1 = {
    
     {
    
    -1, -1, -1}, {
    
    -1, 0, -1}, {
    
    -1, -1, -1} };
    Matrix conv3_kernel2 = {
    
     {
    
    -1, -1, -1}, {
    
    -1, 8, -1}, {
    
    -1, -1, -1} };

    Matrix conv4_kernel1 = {
    
     {
    
    -1, -1, -1}, {
    
    -1, 0, -1}, {
    
    -1, -1, -1} };
    Matrix conv4_kernel2 = {
    
     {
    
    -1, -1, -1}, {
    
    -1, 8, -1}, {
    
    -1, -1, -1} };

    Matrix conv5_kernel1 = {
    
     {
    
    -1, -1, -1}, {
    
    -1, 0, -1}, {
    
    -1, -1, -1} };
    Matrix conv5_kernel2 = {
    
     {
    
    -1, -1, -1}, {
    
    -1, 8, -1}, {
    
    -1, -1, -1} };

    // 定义残差块的步长
    int stride1 = 1;
    int stride2 = 2;

    // 定义4个残差块
    vector<vector<double>> residual1_output = residual(input, conv1_kernel, conv2_kernel1, stride1);
    vector<vector<double>> residual2_output = residual(residual1_output, conv2_kernel2, conv3_kernel1, stride2);
    vector<vector<double>> residual3_output = residual(residual2_output, conv3_kernel2, conv4_kernel1, stride2);
    vector<vector<double>> residual4_output = residual(residual3_output, conv4_kernel2, conv5_kernel1, stride2);

    // 对最后一个残差块的输出进行池化
    int size = residual4_output[0].size();
    vector<vector<double>> pool_output(residual4_output.size(), vector<double>(1, 0));
    for (int i = 0; i < residual4_output.size(); i++)
    {
    
    
        double sum = 0.0;
        for (int j = 0; j < size; j++)
        {
    
    
            for (int k = 0; k < size; k++)
            {
    
    
                sum += residual4_output[i][j][k];
            }
        }
        pool_output[i][0] = sum / (size * size);
    }

    return pool_output;
}

int main()
{
    
    
    // 定义输入
    int in_channels = 3;
    int input_size = 224;
    vector<vector<double>> input(in_channels, vector<double>(input_size, 0));
    for (int i = 0; i < in_channels; i++)
    {
    
    
        for (int j = 0; j < input_size; j++)
        {
    
    
            for (int k = 0; k < input_size; k++)
            {
    
    
                input[i][j] = rand() % 256;
            }
        }
    }

    // 使用ResNet34模型进行预测
    vector<vector<double>> output = resnet34(input);

    // 输出结果
    for (int i = 0; i < output.size(); i++)
    {
    
    
        cout << "Output channel " << i << ": " << output[i][0] << endl;
    }

    return 0;
}

上述代码实现了ResNet34模型的前向传播。请注意,这只是一个示例,实际上,ResNet34模型包含更多的层,每个层都需要设计相应的卷积核和步长。您可能需要花费更多的时间来实现一个完整的模型。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

// 定义卷积操作
void conv(float* input, float* output, float* weight, float* bias, int in_channel, int out_channel, int kernel_size, int stride, int padding) {
    
    
    int output_size = (in_channel - kernel_size + 2 * padding) / stride + 1; // 计算卷积输出的大小
    for (int i = 0; i < out_channel; i++) {
    
     // 遍历输出通道
        for (int j = 0; j < output_size; j++) {
    
     // 遍历输出空间
            for (int k = 0; k < output_size; k++) {
    
    
                float sum = 0;
                for (int l = 0; l < in_channel; l++) {
    
     // 遍历输入通道
                    for (int m = 0; m < kernel_size; m++) {
    
     // 遍历卷积核空间
                        for (int n = 0; n < kernel_size; n++) {
    
    
                            int input_row = j * stride + m - padding; // 计算输入位置
                            int input_col = k * stride + n - padding;
                            if (input_row >= 0 && input_row < in_channel && input_col >= 0 && input_col < in_channel) {
    
     // 判断输入是否合法
                                sum += input[input_row * in_channel * in_channel + input_col * in_channel + l] * 
                                       weight[i * in_channel * kernel_size * kernel_size + l * kernel_size * kernel_size + m * kernel_size + n]; // 计算卷积
                            }
                        }
                    }
                }
                output[i * output_size * output_size + j * output_size + k] = sum + bias[i]; // 加上偏置项
            }
        }
    }
}

// 定义BN操作
void batch_norm(float* input, float* output, float* weight, float* bias, float* running_mean, float* running_var, float eps, int size) {
    
    
    for (int i = 0; i < size; i++) {
    
     // 遍历所有元素
        output[i] = (input[i] - running_mean[i]) / sqrt(running_var[i] + eps); // 进行标准化
        output[i] = output[i] * weight[i] + bias[i]; // 进行缩放和平移
    }
}

// 定义ReLU操作
void relu(float* input, float* output, int size) {
    
    
    for (int i = 0; i < size; i++) {
    
     // 遍历所有元素
        output[i] = fmax(0, input[i]); // 取max(0, x)
    }
}

// 定义池化操作
void max_pool(float* input, float* output, int in_channel, int out_channel, int kernel_size, int stride) {
    
    
    int output_size = (in_channel - kernel_size) / stride + 1; // 计算池化输出的大小
    for (int i = 0; i < out_channel; i++) {
    
     // 遍历输出通道
        for (int j = 0; j < output_size; j++) {
    
     // 遍历输出空间
            for (int k = 0; k < output_size; k++) {
    
    
                float max_val = -INFINITY;
                for (int l = 0; l < kernel_size; l++) {
    
     // 遍历池化核空间
                    for (int m = 0; m < kernel_size; m++) {
    
    
                        int input_row = j * stride + l; // 计算输入位置
                        int input_col = k * stride + m;
                        if (input_row >= 0 && input_row < in_channel && input_col >= 0 && input_col < in_channel) {
    
     // 判断输入是否合法
                            max_val = fmax(max_val, input[input_row * in_channel * in_channel + input_col * in_channel + i]); // 取最大值
                        }
                    }
                }
                output[i * output_size * output_size + j * output_size + k] = max_val; // 将最大值存入输出
            }
        }
    }
}

// 定义ResNet34的一个BasicBlock
void basic_block(float* input, float* output, float* weight1, float* bias1, float* weight2, float* bias2, float* weight3, float* bias3, float* weight4, float* bias4, float* running_mean1, float* running_var1, float* running_mean2, float* running_var2, float eps, int in_channel, int out_channel, int stride) {
    
    
    float* bn1 = (float*)malloc(sizeof(float) * in_channel * in_channel * in_channel); // 创建中间变量
    float* relu1 = (float*)malloc(sizeof(float) * in_channel * in_channel * in_channel);
    float* conv1 = (float*)malloc(sizeof(float) * out_channel * in_channel * 3 * 3);
    float* bn2 = (float*)malloc(sizeof(float) * out_channel * in_channel * in_channel);
    float* relu2 = (float*)malloc(sizeof(float) * out_channel * in_channel * in_channel);
    float* conv2 = (float*)malloc(sizeof(float) * out_channel * out_channel * 3 * 3);
    float* shortcut = input;
    if (in_channel != out_channel || stride != 1) {
    
     // 如果输入和输出维度不同或步长不同,使用1x1卷积调整输入的维度
        shortcut = (float*)malloc(sizeof(float) * out_channel * in_channel * in_channel);
        conv(input, shortcut, weight3, bias3, in_channel, out_channel, 1, stride, 0);
    }
    batch_norm(input, bn1, weight1, bias1, running_mean1, running_var1, eps, in_channel * in_channel * in_channel); // BN操作
    relu(bn1, relu1, in_channel * in_channel * in_channel); // ReLU操作
    conv(relu1, conv1, weight2, bias2, in_channel, in_channel, 3, stride, 1); // 3x3卷积
    batch_norm(conv1, bn2, weight4, bias4, running_mean2, running_var2, eps, out_channel * in_channel * in_channel); // BN操作
    relu(bn2, relu2, out_channel * in_channel * in_channel); // ReLU操作
    conv(relu2, conv2, weight2 + in_channel * in_channel * 3 * 3, bias2 + out_channel, out_channel, out_channel, 3, 1, 1); // 3x3卷积
    for (int i = 0; i < out_channel; i++) {
    
     // 将两个卷积结果加起来
        for (int j = 0; j < in_channel; j++) {
    
    
            for (int k = 0; k < in_channel; k++) {
    
    
                output[i * in_channel * in_channel + j * in_channel + k] = conv2[i * in_channel * in_channel + j * in_channel + k] + shortcut[i * in_channel * in_channel + j * in_channel + k];
            }
        }
    }
    free(bn1); // 释放中间变量
    free(relu1);
    free(conv1);
    if (in_channel != out_channel || stride != 1) {
    
    
        free(shortcut);
    }
    free(bn2);
    free(relu2);
    free(conv2);
}

// 定义ResNet34的整个网络
void resnet34(float* input, float* output, float* weight, float* bias, float* running_mean1, float* running_var1, float* running_mean2, float* running_var2, float* running_mean3, float* running_var3, float* running_mean4, float* running_var4, float* running_mean5, float* running_var5, float eps) {
    
    
    float* bn1 = (float*)malloc(sizeof(float) * 64 * 224 * 224); // 创建中间变量
    float* relu1 = (float*)malloc(sizeof(float) * 64 * 224 * 224);
    float* conv1 = (float*)malloc(sizeof(float) * 64 * 3 * 3 * 7 * 7);
    float* pool1 = (float*)malloc(sizeof(float) * 64 * 112 * 112);
    float* block1 = (float*)malloc(sizeof(float) * 64 * 112 * 112);
    float* block2 = (float*)malloc(sizeof(float) * 128 * 56 * 56);
    float* block3 = (float*)malloc(sizeof(float) * 256 * 28 * 28);
    float* block4 = (float*)malloc(sizeof(float) * 512 * 14 * 14);
    max_pool(input, pool1, 3, 64, 3, 2); // 第一层:3x3最大池化层
    conv(pool1, bn1, weight, bias, 3, 64, 7, 2, 3); // 第二层:7x7卷积层
    batch_norm(bn1, relu1, weight + 64 * 7 * 7 * 3, bias + 64, running_mean1, running_var1, eps, 64 * 224 * 224); // BN操作
    relu(relu1, block1, 64 * 224 * 224); // ReLU操作
    basic_block(block1, block2, weight + 64 * 7 * 7 * 3 + 64 * 3 * 3, bias + 64 + 64, weight + 64 * 7 * 7 * 3 + 64 * 3 * 3 + 128 * 3 * 3, bias + 128, weight + 64 * 7 * 7 * 3 + 128 * 3 * 3, bias + 128, weight + 64 * 7 * 7 * 3 + 128 * 3 * 3 + 128 * 3 * 3, bias + 128, running_mean2, running_var2, running_mean3, running_var3, eps, 64, 128, 2); // 第三层:两个BasicBlock
    basic_block(block2, block3, weight + 64 * 7 * 7 * 3 + 128 * 3 * 3 * 2, bias + 128 * 2, weight + 64 * 7 * 7 * 3 + 128 * 3 * 3 * 3, bias + 256, weight + 64 * 7 * 7 * 3 + 256 * 3 * 3, bias + 256, weight + 64 * 7 * 7 * 3 + 256 * 3 * 3 * 2, bias + 256, running_mean4, running_var4, running_mean5, running_var5, eps, 128, 256, 2); // 第四层:两个BasicBlock
    basic_block(block3, block4, weight + 64 * 7 * 7 * 3 + 256 * 3 * 3 * 2, bias + 256 * 2, weight + 64 * 7 * 7 * 3 + 256 * 3 * 3 * 3, bias + 512, weight + 64 * 7 * 7 * 3 + 512 * 3 * 3, bias + 512, weight + 64 * 7 * 7 * 3 + 512 * 3 * 3 * 2, bias + 512, running_mean5, running_var5, running_mean5, running_var5, eps, 256, 512, 2); // 第五层:两个BasicBlock
    memcpy(output, block4, sizeof(float) * 512 * 14 * 14); // 输出结果
    free(bn1); // 释放中间变量
    free(relu1);
    free(conv1);
    free(pool1);
    free(block1);
    free(block2);
    free(block3);
    free(block4);
}

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define MAX_CHANNELS 512

// 定义卷积层的结构体
typedef struct {
    
    
    int input_channels; // 输入通道数
    int output_channels; // 输出通道数
    int kernel_size; // 卷积核大小
    float *weights; // 权重矩阵
    float *biases; // 偏置向量
} ConvLayer;

// 初始化卷积层的函数
void InitConvLayer(ConvLayer *layer, int input_channels, int output_channels, int kernel_size) {
    
    
    layer->input_channels = input_channels;
    layer->output_channels = output_channels;
    layer->kernel_size = kernel_size;
    int weights_size = input_channels * output_channels * kernel_size * kernel_size;
    int biases_size = output_channels;
    layer->weights = (float *)malloc(weights_size * sizeof(float));
    layer->biases = (float *)malloc(biases_size * sizeof(float));
    for (int i = 0; i < weights_size; i++) {
    
    
        layer->weights[i] = (float)rand() / RAND_MAX;
    }
    for (int i = 0; i < biases_size; i++) {
    
    
        layer->biases[i] = (float)rand() / RAND_MAX;
    }
}

// 卷积层的前向传播函数
void ConvLayerForward(ConvLayer *layer, float *input, float *output, int input_width, int input_height) {
    
    
    int output_width = input_width - layer->kernel_size + 1;
    int output_height = input_height - layer->kernel_size + 1;
    int weights_size = layer->input_channels * layer->output_channels * layer->kernel_size * layer->kernel_size;
    int biases_size = layer->output_channels;
    for (int i = 0; i < layer->output_channels; i++) {
    
    
        for (int j = 0; j < output_width; j++) {
    
    
            for (int k = 0; k < output_height; k++) {
    
    
                float sum = 0.0f;
                for (int l = 0; l < layer->input_channels; l++) {
    
    
                    for (int m = 0; m < layer->kernel_size; m++) {
    
    
                        for (int n = 0; n < layer->kernel_size; n++) {
    
    
                            sum += input[l * input_width * input_height + (j + m) * input_height + (k + n)] * layer->weights[((l * layer->output_channels + i) * layer->kernel_size + m) * layer->kernel_size + n];
                        }
                    }
                }
                output[i * output_width * output_height + j * output_height + k] = sum + layer->biases[i];
            }
        }
    }
}

// 定义残差块的结构体
typedef struct {
    
    
    ConvLayer conv1; // 第一个卷积层
    ConvLayer conv2; // 第二个卷积层
    float *residual; // 残差连接
} ResidualBlock;

// 初始化残差块的函数
void InitResidualBlock(ResidualBlock *block, int input_channels, int output_channels) {
    
    
    InitConvLayer(&block->conv1, input_channels, output_channels, 3);
    InitConvLayer(&block->conv2, output_channels, output_channels, 3);
    block->residual = (float *)malloc(MAX_CHANNELS * sizeof(float));
}

// 残差块的前向传播函数
void ResidualBlockForward(ResidualBlock *block, float *input, float *output, int input_width, int input_height) {
    
    
    ConvLayerForward(&block->conv1, input, output, input_width, input_height);
    for (int i = 0; i < block->conv1.output_channels; i++) {
    
    
        for (int j = 0; j < input_width - 2; j++) {
    
    
            for (int k = 0; k < input_height - 2; k++) {
    
    
                block->residual[i * (input_width - 2) * (input_height - 2) + j * (input_height - 2) + k] = output[i * (input_width - 2) * (input_height - 2) + j * (input_height - 2) + k];
            }
        }
    }
    for (int i = 0; i < block->conv1.output_channels; i++) {
    
    
        for (int j = 0; j < input_width - 2; j++) {
    
    
            for (int k = 0; k < input_height - 2; k++) {
    
    
                output[i * (input_width - 2) * (input_height - 2) + j * (input_height - 2) + k] = fmaxf(0.0f, block->residual[i * (input_width - 2) * (input_height - 2) + j * (input_height - 2) + k]);
            }
        }
    }
    ConvLayerForward(&block->conv2, output, output, input_width - 2, input_height - 2);
    for (int i = 0; i < block->conv2.output_channels; i++) {
    
    
        for (int j = 0; j < input_width - 4; j++) {
    
    
            for (int k = 0; k < input_height - 4; k++) {
    
    
                output[i * (input_width - 4) * (input_height - 4) + j * (input_height - 4) + k] += block->residual[i * (input_width - 2) * (input_height - 2) + (j + 1) * (input_height - 2) + (k + 1)];
            }
        }
    }
}

// 定义ResNet的结构体
typedef struct {
    
    
    ResidualBlock blocks[16];
    ConvLayer conv;
    int input_width;
    int input_height;
} ResNet;

// 初始化ResNet的函数
void InitResNet(ResNet *resnet, int input_width, int input_height) {
    
    
    resnet->input_width = input_width;
    resnet->input_height = input_height;
    InitConvLayer(&resnet->conv, 3, MAX_CHANNELS, 7);
    for (int i = 0; i < 16; i++) {
    
    
        if (i == 0) {
    
    
            InitResidualBlock(&resnet->blocks[i], MAX_CHANNELS, MAX_CHANNELS * 2);
        } else {
    
    
            InitResidualBlock(&resnet->blocks[i], MAX_CHANNELS * 2, MAX_CHANNELS * 2);
        }
    }
}

// ResNet的前向传播函数
void ResNetForward(ResNet *resnet, float *input, float *output) {
    
    
    ConvLayerForward(&resnet->conv, input, output, resnet->input_width, resnet->input_height);
    for (int i = 0; i < resnet->conv.output_channels; i++) {
    
    
        for (int j = 0; j < resnet->input_width - 6; j++) {
    
    
            for (int k = 0; k < resnet->input_height - 6; k++) {
    
    
                output[i * (resnet->input_width - 6) * (resnet->input_height - 6) + j * (resnet->input_height - 6) + k] = fmaxf(0.0f, output[i * (resnet->input_width - 6) * (resnet->input_height - 6) + j * (resnet->input_height - 6) + k]);
            }
        }
    }
    ResidualBlockForward(&resnet->blocks[0], output, output, resnet->input_width - 6, resnet->input_height - 6);
    for (int i = 1; i < 16; i++) {
    
    
        ResidualBlockForward(&resnet->blocks[i], output, output, resnet->input_width - 6, resnet->input_height - 6);
    }
}

// 测试代码
int main() {
    
    
    int input_width = 224;
    int input_height = 224;
    float *input = (float *)malloc(3 * input_width * input_height * sizeof(float));
    for (int i = 0; i < 3 * input_width * input_height; i++) {
    
    
        input[i] = (float)rand() / RAND_MAX;
    }
    float *output = (float *)malloc(MAX_CHANNELS * (input_width - 6) * (input_height - 6) * sizeof(float));
    ResNet resnet;
    InitResNet(&resnet, input_width, input_height);
    ResNetForward(&resnet, input, output);
    printf("Output: %f\n", output[0]);
    free(input);
    free(output);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39506862/article/details/130894050