短小精悍的深度学习算法,用来理解深度学习的精髓,涉及深度学习的网络初始化、前向传播、反向传播、权重更新等核心算法功能;有输入层、隐藏层、输出层结构。
* prompt:
* 基于C语言实现的简单深度学习网络(教学用),不使用第三方库,不使用内联函数,要求输出C代码。
* 网络结构:输入层(2) → 隐藏层(4/ReLU) → 输出层(2/Softmax)
* 数据集:异或问题(XOR)
*
* 这段代码实现了一个简单的2-4-2神经网络,
* 使用ReLU作为隐藏层激活函数,Softmax作为输出层激活函数。
* 代码包含网络初始化、前向传播、反向传播和权重更新等核心功能,
* 能够成功学习XOR问题的解决方案。
/**
* prompt:
* 基于C语言实现的简单深度学习网络(教学用),不使用第三方库,不使用内联函数,要求输出C代码。
* 网络结构:输入层(2) → 隐藏层(4/ReLU) → 输出层(2/Softmax)
* 数据集:异或问题(XOR)
*
* 这段代码实现了一个简单的2-4-2神经网络,
* 使用ReLU作为隐藏层激活函数,Softmax作为输出层激活函数。
* 代码包含网络初始化、前向传播、反向传播和权重更新等核心功能,
* 能够成功学习XOR问题的解决方案。
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
// 网络参数定义
#define INPUT_SIZE 2 // 输入层节点数
#define HIDDEN_SIZE 4 // 隐藏层节点数
#define OUTPUT_SIZE 2 // 输出层节点数
#define LEARNING_RATE 0.1 // 学习率
#define EPOCHS 10000 // 训练轮数
// 神经网络结构体
typedef struct {
double weights_ih[INPUT_SIZE][HIDDEN_SIZE]; // 输入层到隐藏层权重
double weights_ho[HIDDEN_SIZE][OUTPUT_SIZE]; // 隐藏层到输出层权重
double bias_h[HIDDEN_SIZE]; // 隐藏层偏置
double bias_o[OUTPUT_SIZE]; // 输出层偏置
} NeuralNetwork;
// 初始化神经网络
void init_network(NeuralNetwork* net) {
srand(time(NULL)); // 设置随机种子
// 初始化输入层到隐藏层的权重(-1到1之间的随机值)
for(int i=0; i<INPUT_SIZE; i++) {
for(int j=0; j<HIDDEN_SIZE; j++) {
net->weights_ih[i][j] = ((double)rand() / RAND_MAX) * 2 - 1;
}
}
// 初始化隐藏层到输出层的权重
for(int i=0; i<HIDDEN_SIZE; i++) {
for(int j=0; j<OUTPUT_SIZE; j++) {
net->weights_ho[i][j] = ((double)rand() / RAND_MAX) * 2 - 1;
}
}
// 初始化偏置
for(int i=0; i<HIDDEN_SIZE; i++) {
net->bias_h[i] = ((double)rand() / RAND_MAX) * 2 - 1;
}
for(int i=0; i<OUTPUT_SIZE; i++) {
net->bias_o[i] = ((double)rand() / RAND_MAX) * 2 - 1;
}
}
// ReLU激活函数
double relu(double x) {
return x > 0 ? x : 0; // 大于0返回x,否则返回0
}
// ReLU导数(用于反向传播)
double relu_derivative(double x) {
return x > 0 ? 1 : 0; // 大于0返回1,否则返回0
}
// Softmax函数(多分类输出)
void softmax(double* input, int size) {
// 数值稳定性处理:减去最大值
double max = input[0];
for(int i=1; i<size; i++) {
if(input[i] > max) {
max = input[i];
}
}
// 计算指数和
double sum = 0.0;
for(int i=0; i<size; i++) {
input[i] = exp(input[i] - max); // 防止数值溢出
sum += input[i];
}
// 归一化
for(int i=0; i<size; i++) {
input[i] /= sum;
}
}
// 前向传播
void forward(NeuralNetwork* net, double* input, double* hidden, double* output) {
// 输入层到隐藏层计算
for(int h=0; h<HIDDEN_SIZE; h++) {
hidden[h] = 0;
// 加权求和
for(int i=0; i<INPUT_SIZE; i++) {
hidden[h] += input[i] * net->weights_ih[i][h];
}
hidden[h] += net->bias_h[h]; // 加上偏置
hidden[h] = relu(hidden[h]); // 应用ReLU激活
}
// 隐藏层到输出层计算
for(int o=0; o<OUTPUT_SIZE; o++) {
output[o] = 0;
for(int h=0; h<HIDDEN_SIZE; h++) {
output[o] += hidden[h] * net->weights_ho[h][o];
}
output[o] += net->bias_o[o]; // 加上偏置
}
// 输出层应用softmax
softmax(output, OUTPUT_SIZE);
}
// 反向传播
void backward(NeuralNetwork* net, double* input, double* hidden, double* output,
double* target, double* d_weights_ih, double* d_weights_ho,
double* d_bias_h, double* d_bias_o) {
// 计算输出层误差(预测值-真实值)
double output_error[OUTPUT_SIZE];
for(int o=0; o<OUTPUT_SIZE; o++) {
output_error[o] = output[o] - target[o];
}
// 计算隐藏层到输出层的梯度(误差*隐藏层输出)
for(int h=0; h<HIDDEN_SIZE; h++) {
for(int o=0; o<OUTPUT_SIZE; o++) {
d_weights_ho[h*OUTPUT_SIZE + o] = output_error[o] * hidden[h];
}
}
// 输出层偏置梯度就是输出误差
for(int o=0; o<OUTPUT_SIZE; o++) {
d_bias_o[o] = output_error[o];
}
// 计算隐藏层误差(反向传播误差)
double hidden_error[HIDDEN_SIZE] = {0};
for(int h=0; h<HIDDEN_SIZE; h++) {
for(int o=0; o<OUTPUT_SIZE; o++) {
hidden_error[h] += output_error[o] * net->weights_ho[h][o];
}
hidden_error[h] *= relu_derivative(hidden[h]); // 乘以激活函数导数
}
// 计算输入层到隐藏层的梯度(隐藏层误差*输入值)
for(int i=0; i<INPUT_SIZE; i++) {
for(int h=0; h<HIDDEN_SIZE; h++) {
d_weights_ih[i*HIDDEN_SIZE + h] = hidden_error[h] * input[i];
}
}
// 隐藏层偏置梯度就是隐藏层误差
for(int h=0; h<HIDDEN_SIZE; h++) {
d_bias_h[h] = hidden_error[h];
}
}
// 更新权重
void update_weights(NeuralNetwork* net,
const double* d_weights_ih,
const double* d_weights_ho,
const double* d_bias_h,
const double* d_bias_o) {
// 更新输入层到隐藏层权重
for(int i=0; i<INPUT_SIZE; i++) {
for(int h=0; h<HIDDEN_SIZE; h++) {
net->weights_ih[i][h] -= LEARNING_RATE * d_weights_ih[i*HIDDEN_SIZE + h];
}
}
// 更新隐藏层到输出层权重
for(int h=0; h<HIDDEN_SIZE; h++) {
for(int o=0; o<OUTPUT_SIZE; o++) {
net->weights_ho[h][o] -= LEARNING_RATE * d_weights_ho[h*OUTPUT_SIZE + o];
}
}
// 更新偏置
for(int h=0; h<HIDDEN_SIZE; h++) {
net->bias_h[h] -= LEARNING_RATE * d_bias_h[h];
}
for(int o=0; o<OUTPUT_SIZE; o++) {
net->bias_o[o] -= LEARNING_RATE * d_bias_o[o];
}
}
// 训练网络
void train(NeuralNetwork* net, double inputs[4][2], int targets[4][2]) {
// 梯度缓存
double d_weights_ih[INPUT_SIZE * HIDDEN_SIZE] = {0};
double d_weights_ho[HIDDEN_SIZE * OUTPUT_SIZE] = {0};
double d_bias_h[HIDDEN_SIZE] = {0};
double d_bias_o[OUTPUT_SIZE] = {0};
for(int epoch=0; epoch<EPOCHS; epoch++) {
for(int sample=0; sample<4; sample++) { // 遍历4个XOR样本
double hidden[HIDDEN_SIZE] = {0};
double output[OUTPUT_SIZE] = {0};
// 前向传播
forward(net, inputs[sample], hidden, output);
// 反向传播计算梯度
backward(net, inputs[sample], hidden, output, targets[sample],
d_weights_ih, d_weights_ho, d_bias_h, d_bias_o);
// 更新权重
update_weights(net, d_weights_ih, d_weights_ho, d_bias_h, d_bias_o);
}
}
}
// 预测函数
void predict(NeuralNetwork* net, double* input) {
double hidden[HIDDEN_SIZE] = {0};
double output[OUTPUT_SIZE] = {0};
forward(net, input, hidden, output);
printf("Input: [%.1f, %.1f] => Output: [%.4f, %.4f] => ",
input[0], input[1], output[0], output[1]);
if(output[0] > output[1]) {
printf("Predicted: 0 (%.2f%%)\n", output[0]*100);
} else {
printf("Predicted: 1 (%.2f%%)\n", output[1]*100);
}
}
int main() {
NeuralNetwork net;
init_network(&net); // 初始化网络
// XOR数据集
double inputs[4][2] = {
{0, 0}, // 0 XOR 0 = 0
{0, 1}, // 0 XOR 1 = 1
{1, 0}, // 1 XOR 0 = 1
{1, 1} // 1 XOR 1 = 0
};
// 目标输出(one-hot编码)
int targets[4][2] = {
{1, 0}, // 0
{0, 1}, // 1
{0, 1}, // 1
{1, 0} // 0
};
// 训练网络
train(&net, inputs, targets);
// 测试网络
printf("XOR Problem Results:\n");
for(int i=0; i<4; i++) {
predict(&net, inputs[i]);
}
return 0;
}