在C语言中实现一个完整的AlexNet神经网络是一个复杂的任务,涉及大量的矩阵运算和卷积操作。这个示例代码将涵盖AlexNet的主要组件,包括卷积层、ReLU激活函数、池化层和全连接层。
由于C语言本身没有像Python的NumPy那样的矩阵库,我们需要自己编写矩阵运算函数。另外,C语言不支持自动微分,因此我们不会实现反向传播和训练部分,只实现前向传播。
代码结构
1.定义数据结构
2.实现矩阵运算函数
3.实现卷积层
4.实现ReLU激活函数
5.实现池化层
6.实现全连接层
7.实现前向传播
示例代码
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define INPUT_SIZE 224
#define CONV1_FILTERS 96
#define CONV2_FILTERS 256
#define CONV3_FILTERS 384
#define CONV4_FILTERS 384
#define CONV5_FILTERS 256
#define FC1_SIZE 4096
#define FC2_SIZE 4096
#define OUTPUT_SIZE 1000
// 卷积层
typedef struct {
int num_filters;
int filter_size;
double ****filters; // 4D array for filters (num_filters x depth x height x width)
} ConvLayer;
// 全连接层
typedef struct {
int input_size;
int output_size;
double **weights; // 2D array for weights (output_size x input_size)
double *biases; // 1D array for biases (output_size)
} FCLayer;
// 初始化卷积层
ConvLayer* init_conv_layer(int num_filters, int depth, int filter_size) {
ConvLayer *layer = malloc(sizeof(ConvLayer));
layer->num_filters = num_filters;
layer->filter_size = filter_size;
layer->filters = malloc(num_filters * sizeof(double***));
for (int i = 0; i < num_filters; i++) {
layer->filters[i] = malloc(depth * sizeof(double**));
for (int j = 0; j < depth; j++) {
layer->filters[i][j] = malloc(filter_size * sizeof(double*));
for (int k = 0; k < filter_size; k++) {
layer->filters[i][j][k] = malloc(filter_size * sizeof(double));
for (int l = 0; l < filter_size; l++) {
layer->filters[i][j][k][l] = (double)rand() / RAND_MAX; // Initialize with random weights
}
}
}
}
return layer;
}
// 初始化全连接层
FCLayer* init_fc_layer(int input_size, int output_size) {
FCLayer *layer = malloc(sizeof(FCLayer));
layer->input_size = input_size;
layer->output_size = output_size;
layer->weights = malloc(output_size * sizeof(double*));
for (int i = 0; i < output_size; i++) {
layer->weights[i] = malloc(input_size * sizeof(double));
for (int j = 0; j < input_size; j++) {
layer->weights[i][j] = (double)rand() / RAND_MAX; // Initialize with random weights
}
}
layer->biases = malloc(output_size * sizeof(double));
for (int i = 0; i < output_size; i++) {
layer->biases[i] = (double)rand() / RAND_MAX; // Initialize with random biases
}
return layer;
}
// ReLU 激活函数
double relu(double x) {
return x > 0 ? x : 0;
}
// 卷积操作
void conv_forward(ConvLayer *layer, double ***input, int input_depth, int input_size, double ***output) {
int output_size = input_size - layer->filter_size + 1;
for (int f = 0; f < layer->num_filters; f++) {
for (int i = 0; i < output_size; i++) {
for (int j = 0; j < output_size; j++) {
double sum = 0.0;
for (int d = 0; d < input_depth; d++) {
for (int x = 0; x < layer->filter_size; x++) {
for (int y = 0; y < layer->filter_size; y++) {
sum += input[d][i + x][j + y] * layer->filters[f][d][x][y];
}
}
}
output[f][i][j] = relu(sum);
}
}
}
}
// 最大池化操作
void max_pooling(double ***input, int input_depth, int input_size, int pool_size, int stride, double ***output) {
int output_size = (input_size - pool_size) / stride + 1;
for (int d = 0; d < input_depth; d++) {
for (int i = 0; i < output_size; i++) {
for (int j = 0; j < output_size; j++) {
double max_val = -INFINITY;
for (int x = 0; x < pool_size; x++) {
for (int y = 0; y < pool_size; y++) {
double val = input[d][i * stride + x][j * stride + y];
if (val > max_val) {
max_val = val;
}
}
}
output[d][i][j] = max_val;
}
}
}
}
// 全连接层前向传播
void fc_forward(FCLayer *layer, double *input, double *output) {
for (int i = 0; i < layer->output_size; i++) {
double sum = 0.0;
for (int j = 0; j < layer->input_size; j++) {
sum += input[j] * layer->weights[i][j];
}
output[i] = relu(sum + layer->biases[i]);
}
}
// 主函数:示例前向传播
int main() {
// 示例输入图像
int input_depth = 3;
double ***input = malloc(input_depth * sizeof(double**));
for (int i = 0; i < input_depth; i++) {
input[i] = malloc(INPUT_SIZE * sizeof(double*));
for (int j = 0; j < INPUT_SIZE; j++) {
input[i][j] = malloc(INPUT_SIZE * sizeof(double));
for (int k = 0; k < INPUT_SIZE; k++) {
input[i][j][k] = (double)rand() / RAND_MAX; // Initialize with random values
}
}
}
// 初始化卷积层和全连接层
ConvLayer *conv1 = init_conv_layer(CONV1_FILTERS, input_depth, 11);
ConvLayer *conv2 = init_conv_layer(CONV2_FILTERS, CONV1_FILTERS, 5);
ConvLayer *conv3 = init_conv_layer(CONV3_FILTERS, CONV2_FILTERS, 3);
ConvLayer *conv4 = init_conv_layer(CONV4_FILTERS, CONV3_FILTERS, 3);
ConvLayer *conv5 = init_conv_layer(CONV5_FILTERS, CONV4_FILTERS, 3);
FCLayer *fc1 = init_fc_layer(6 * 6 * CONV5_FILTERS, FC1_SIZE);
FCLayer *fc2 = init_fc_layer(FC1_SIZE, FC2_SIZE);
FCLayer *fc3 = init_fc_layer(FC2_SIZE, OUTPUT_SIZE);
// 卷积和池化操作
int conv1_output_size = (INPUT_SIZE - 11 + 1);
double ***conv1_output = malloc(CONV1_FILTERS * sizeof(double**));
for (int i = 0; i < CONV1_FILTERS; i++) {
conv1_output[i] = malloc(conv1_output_size * sizeof(double*));
for (int j = 0; j < conv1_output_size; j++) {
conv1_output[i][j] = malloc(conv1_output_size * sizeof(double));
}
}
conv_forward(conv1, input, input_depth, INPUT_SIZE, conv1_output);
// 最大池化操作
int pool1_output_size = conv1_output_size / 2;
double ***pool1_output = malloc(CONV1_FILTERS * sizeof(double**));
for (int i = 0; i < CONV1_FILTERS; i++) {
pool1_output[i] = malloc(pool1_output_size * sizeof(double*));
for (int j = 0; j < pool1_output_size; j++) {
pool1_output[i][j] = malloc(pool1_output_size * sizeof(double));
}
}
max_pooling(conv1_output, CONV1_FILTERS, conv1_output_size, 2, 2, pool1_output);
// 全连接层前向传播
double *fc_input = malloc(6 * 6 * CONV5_FILTERS * sizeof(double));
double *fc1_output = malloc(FC1_SIZE * sizeof(double));
double *fc2_output = malloc(FC2_SIZE * sizeof(double));
double *fc3_output = malloc(OUTPUT_SIZE * sizeof(double));
// 将池化层的输出展平为一维数组
for (int i = 0; i < CONV5_FILTERS; i++) {
for (int j = 0; j < 6; j++) {
for (int k = 0; k < 6; k++) {
fc_input[i * 36 + j * 6 + k] = pool1_output[i][j][k];
}
}
}
// 前向传播通过全连接层
fc_forward(fc1, fc_input, fc1_output);
fc_forward(fc2, fc1_output, fc2_output);
fc_forward(fc3, fc2_output, fc3_output);
// 打印最终输出
for (int i = 0; i < OUTPUT_SIZE; i++) {
printf("%f ", fc3_output[i]);
}
printf("\n");
// 释放内存(此处省略详细的内存释放代码)
return 0;
}
说明
数据结构:
ConvLayer 和 FCLayer 结构体分别表示卷积层和全连接层。
初始化函数:
init_conv_layer 和 init_fc_layer 用于初始化卷积层和全连接层的权重和偏置。
激活函数:
ReLU 激活函数用于非线性变换。
前向传播:
conv_forward 函数实现卷积操作。
max_pooling 函数实现最大池化操作。
fc_forward 函数实现全连接层的前向传播。
主函数:
创建输入数据并初始化网络层。
进行前向传播并打印最终输出。
注意
这个示例代码实现了AlexNet的一部分结构,但并不完整。
实际应用中,卷积神经网络通常会使用专门的深度学习框架(如TensorFlow、PyTorch)来实现和训练。由于C语言的低级操作和内存管理,编写和调试这样的代码需要非常小心,确保内存正确分配和释放。