#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define INPUT_SIZE 2
#define HIDDEN_SIZE 3
#define OUTPUT_SIZE 1
/* 定义神经元结构体 */
typedef struct _neuron {
double* weights; // 权重数组
double bias; // 偏置值
double output; // 输出
} neuron;
/* 定义隐藏层和输出层结构体 */
typedef struct _layer {
int size; // 层数
neuron* neurons; // 神经元数组
} layer;
/* 定义神经网络结构体 */
typedef struct _network {
layer input_layer; // 输入层
layer hidden_layer; // 隐藏层
layer output_layer; // 输出层
} network;
/* 初始化神经元 */
void init_neuron(neuron* n, int size) {
n->weights = (double*)malloc(sizeof(double) * size);
for (int i = 0; i < size; i++) {
n->weights[i] = ((double)rand() / RAND_MAX) * 2 - 1;
}
n->bias = ((double)rand() / RAND_MAX) * 2 - 1;
}
/* 初始化层 */
void init_layer(layer* l, int size, int input_size) {
l->size = size;
l->neurons = (neuron*)malloc(sizeof(neuron) * size);
for (int i = 0; i < size; i++) {
init_neuron(&l->neurons[i], input_size);
}
}
/* 初始化神经网络 */
void init_network(network* net) {
init_layer(&net->input_layer, INPUT_SIZE, 0);
init_layer(&net->hidden_layer, HIDDEN_SIZE, INPUT_SIZE);
init_layer(&net->output_layer, OUTPUT_SIZE, HIDDEN_SIZE);
}
/* 计算神经元输出值 */
double activate(neuron* n, double* inputs) {
double sum = 0;
for (int i = 0; i < INPUT_SIZE; i++) {
sum += inputs[i] * n->weights[i];
}
sum += n->bias;
return 1.0 / (1.0 + exp(-sum));
}
/* 前向传播 */
void forward(network* net, double* inputs) {
/* 输入层计算 */
for (int i = 0; i < INPUT_SIZE; i++) {
net->input_layer.neurons[i].output = inputs[i];
}
/* 隐藏层计算 */
for (int i = 0; i < HIDDEN_SIZE; i++) {
neuron* n = &net->hidden_layer.neurons[i];
n->output = activate(n, inputs);
}
/* 输出层计算 */
for (int i = 0; i < OUTPUT_SIZE; i++) {
neuron* n = &net->output_layer.neurons[i];
n->output = activate(n, [n->output_layer.neurons[j].output for j in range(HIDDEN_SIZE)]);
}
}
/* 计算误差 */
double error(double* target_output, double* output) {
double err = 0;
for (int i = 0; i < OUTPUT_SIZE; i++) {
err += pow(target_output[i] - output[i], 2);
}
return err;
}
/* 反向传播 */
void backward(network* net, double* target_output, double learning_rate) {
/* 计算输出层误差 */
for (int i = 0; i < OUTPUT_SIZE; i++) {
neuron* n = &net->output_layer.neurons[i];
double output = n->output;
double error = -(target_output[i] - output) * output * (1 - output);
n->bias -= learning_rate * error;
for (int j = 0; j < HIDDEN_SIZE; j++) {
n->weights[j] -= learning_rate * error * net->hidden_layer.neurons[j].output;
}
}
/* 计算隐藏层误差 */
for (int i = 0; i < HIDDEN_SIZE; i++) {
neuron* n = &net->hidden_layer.neurons[i];
double output = n->output;
double error = 0;
for (int j = 0; j < OUTPUT_SIZE; j++) {
neuron* o = &net->output_layer.neurons[j];
error += o->weights[i] * (target_output[j] - o->output) * o->output * (1 - o->output);
}
n->bias -= learning_rate * error;
for (int j = 0; j < INPUT_SIZE; j++) {
n->weights[j] -= learning_rate * error * net->input_layer.neurons[j].output;
}
}
}
/* 训练神经网络 */
void train(network* net, double** training_inputs, double** training_outputs, int epochs, double learning_rate) {
for (int epoch = 0; epoch < epochs; epoch++) {
double total_error = 0;
for (int i = 0; i < NUM_EXAMPLES; i++) {
forward(net, training_inputs[i]);
total_error += error(training_outputs[i], [net->output_layer.neurons[j].output for j in range(OUTPUT_SIZE)]);
backward(net, training_outputs[i], learning_rate);
}
printf("Epoch %d: error = %f\n", epoch, total_error);
}
}
/* 测试神经网络 */
void test(network* net, double** test_inputs) {
for (int i = 0; i < NUM_TEST_EXAMPLES; i++) {
double* inputs = test_inputs[i];
forward(net, inputs);
printf("[");
for (int j = 0; j < INPUT_SIZE; j++) {
printf("%f ", inputs[j]);
}
printf("] -> [");
for (int j = 0; j < OUTPUT_SIZE; j++) {
printf("%f ", net->output_layer.neurons[j].output);
}
printf("]\n");
}
}
int main(int argc, char** argv) {
/* 初始化随机数种子 */
srand((unsigned int)time(NULL));
/* 定义训练集和测试集 */
double* training_inputs[NUM_EXAMPLES] = {
(double[]){0, 0},
(double[]){0, 1},
(double[]){1, 0},
(double[]){1, 1}
};
double* training_outputs[NUM_EXAMPLES] = {
(double[]){0},
(double[]){1},
(double[]){1},
(double[]){0}
};
double* test_inputs[NUM_TEST_EXAMPLES] = {
(double[]){0, 0},
(double[]){0, 1},
(double[]){1, 0},
(double[]){1, 1}
};
/* 初始化神经网络 */
network net;
init_network(&net);
/* 训练神经网络 */
train(&net, training_inputs, training_outputs, NUM_EPOCHS, LEARNING_RATE);
/* 测试神经网络 */
test(&net, test_inputs);
return 0;
}
解释说明
这是一个简单的实现,仅仅展示了神经网络框架的基本结构和算法。
该程序实现了一个简单的多层感知机(Multilayer Perceptron, MLP)神经网络,并使用该神经网络来解决XOR逻辑运算问题。以下是程序的主要流程:
定义常量:程序中定义了输入层、隐藏层和输出层的大小,以及学习率、迭代轮数、训练集和测试集的大小等常量。
定义结构体:程序中定义了三个结构体:神经元结构体(neuron)、层结构体(layer)和神经网络结构体(network),用于存储神经网络的各种参数信息。
初始化神经网络:程序中通过调用init_network函数初始化神经网络。在初始化过程中,会为每个神经元随机生成一组权重并将其存储在神经元结构体中。
前向传播:程序中通过调用forward函数实现前向传播算法。在前向传播中,程序会依次计算输入层、隐藏层和输出层的神经元的输出值。
计算误差:程序中定义了error函数用于计算预测结果与实际结果之间的误差。
反向传播:程序中通过调用backward函数实现反向传播算法。在反向传播中,程序首先计算输出层的误差,然后将误差信号向后传递到隐藏层和输入层,最后使用梯度下降算法更新权重和偏置值。
训练神经网络:程序中通过调用train函数对神经网络进行训练。训练过程中,程序会多次迭代计算误差并调整权重和偏置值,以使神经网络的性能逐步提高。
测试神经网络:程序中通过调用test函数对已经训练好的神经网络进行测试,并输出测试结果。
具体来说,该神经网络是一个三层结构的MLP,其中包含1个输入层、1个隐藏层和1个输出层。输入层大小为2,隐藏层大小为3,输出层大小为1。在训练阶段,程序使用反向传播算法对神经网络进行学习,然后使用前向传播算法进行预测,最后输出测试结果。
测试方法
定义测试集:在主函数中定义一个包含多组输入数据的测试集,每组输入数据都是一个大小为2的double类型数组
double* test_inputs[NUM_TEST_EXAMPLES] = {
(double[]){0, 0},
(double[]){0, 1},
(double[]){1, 0},
(double[]){1, 1}
};
调用test函数:在主函数中调用test函数,对测试集中的每一组输入数据进行测试,并输出测试结果。
/* 测试神经网络 */
test(&net, test_inputs);
在test函数内部,程序会依次计算每一组输入数据的输出结果,并将结果输出到控制台上。具体来说,程序会按照如下格式输出每组输入数据和其对应的输出结果:
[IN] -> [OUT]
其中,IN表示输入数据,OUT表示神经网络的输出结果。对于本例中的神经网络,输出结果只有一个值。
最后,通过观察输出结果,可以评估该神经网络在XOR逻辑运算问题上的性能表现
编译方法
gcc -o program program.c -lm
这里使用了-lm参数,表示需要链接数学库libm。因为本程序使用了数学函数,如exp和log等。
运行可执行文件,开始训练和测试神经网络。
./program
注意,本程序中使用了随机数生成函数,运行结果可能会略有不同。如果多次运行结果不一致,可以尝试增加训练轮数或调整学习率等参数来改善程序的性能。