Программирование нейронной сети на языке C
DeepSeek
требует понимания основ линейной алгебры, механики нейросетей и работы с памятью. Ниже приведен пример реализации простой однослойной нейросети (перцептрона) для решения задачи бинарной классификации.
---
### 1. Базовые концепции
- **Нейрон**: Вычисляет взвешенную сумму входов + смещение (bias), применяет функцию активации.
- **Обучение**: Корректировка весов через градиентный спуск (или метод обратного распространения ошибки).
---
### 2. Пример реализации (однослойный перцептрон)
#### Шаг 1: Определение структуры нейрона
```c
typedef struct {
double *weights; // Веса (включая смещение)
int num_inputs; // Количество входов (без учета смещения)
} Perceptron;
```
#### Шаг 2: Инициализация перцептрона
```c
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
// Функция инициализации
void init_perceptron(Perceptron *p, int num_inputs) {
p->num_inputs = num_inputs;
p->weights = (double*)malloc((num_inputs + 1) * sizeof(double)); // +1 для смещения
// Инициализация случайными значениями (-1 до 1)
for (int i = 0; i <= num_inputs; i++) {
p->weights[i] = (double)rand() / RAND_MAX * 2.0 - 1.0;
}
}
```
#### Шаг 3: Функция активации (сигмоида)
```c
double sigmoid(double x) {
return 1.0 / (1.0 + exp(-x));
}
```
#### Шаг 4: Вычисление выхода
```c
double predict(Perceptron *p, double *inputs) {
double sum = p->weights[0]; // Смещение (bias)
for (int i = 0; i < p->num_inputs; i++) {
sum += inputs[i] * p->weights[i + 1];
}
return sigmoid(sum);
}
```
#### Шаг 5: Обучение (метод обратного распространения)
```c
void train(Perceptron *p, double *inputs, double target, double learning_rate) {
double prediction = predict(p, inputs);
double error = target - prediction;
// Обновление весов (включая смещение)
p->weights[0] += error * learning_rate; // Обновление смещения
for (int i = 0; i < p->num_inputs; i++) {
p->weights[i + 1] += error * inputs[i] * learning_rate;
}
}
```
---
### 3. Пример использования
```c
int main() {
srand(time(NULL));
// Создание перцептрона с 2 входами
Perceptron p;
init_perceptron(&p, 2);
// Данные для обучения (логическое И)
double inputs[4][2] = {{0, 0}, {0, 1}, {1, 0}, {1, 1}};
double targets[4] = {0, 0, 0, 1};
// Обучение (1000 эпох)
for (int epoch = 0; epoch < 1000; epoch++) {
for (int i = 0; i < 4; i++) {
train(&p, inputs[i], targets[i], 0.1);
}
}
// Тестирование
for (int i = 0; i < 4; i++) {
printf("Input: [%f, %f] -> Output: %f\n",
inputs[i][0], inputs[i][1], predict(&p, inputs[i]));
}
free(p.weights);
return 0;
}
```
---
### 4. Компиляция и запуск
```bash
gcc perceptron.c -lm -o perceptron && ./perceptron
```
---
### 5. Что можно улучшить?
1. **Многослойные сети**: Добавить скрытые слои и реализовать полноценный backpropagation.
2. **Матричные операции**: Использовать библиотеки вроде BLAS для ускорения вычислений.
3. **Регуляризация**: Добавить L1/L2-регуляризацию для предотвращения переобучения.
4. **Пакетная обработка**: Реализовать обновление весов на мини-батчах.
---
### 6. Ограничения C для нейросетей
- Нет встроенной поддержки GPU.
- Требуется ручное управление памятью.
- Нет библиотек уровня PyTorch/TensorFlow (но можно интегрировать через API).
Для сложных проектов лучше использовать Python, но C подходит для изучения основ и embedded-решений.
///////////////
Реализация многослойной нейронной сети (Multilayer Perceptron, MLP) на C сложнее, но возможна. Приведу пример архитектуры с одним скрытым слоем и backpropagation.
---
### 1. Структуры данных
```c
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <time.h>
// Структура слоя
typedef struct {
double **weights; // Матрица весов [output_size x (input_size + 1)]
int input_size; // Размер входа (без bias)
int output_size; // Размер выхода
} Layer;
// Структура сети
typedef struct {
Layer *layers; // Массив слоев
int num_layers; // Количество слоев
} MLP;
```
---
### 2. Базовые функции
#### Инициализация слоя:
```c
Layer create_layer(int input_size, int output_size) {
Layer l;
l.input_size = input_size;
l.output_size = output_size;
// Выделение памяти: +1 для bias
l.weights = (double**)malloc(output_size * sizeof(double*));
for (int i = 0; i < output_size; i++) {
l.weights[i] = (double*)malloc((input_size + 1) * sizeof(double));
for (int j = 0; j <= input_size; j++) {
l.weights[i][j] = (double)rand() / RAND_MAX * 2.0 - 1.0;
}
}
return l;
}
```
#### Функции активации:
```c
double sigmoid(double x) { return 1.0 / (1.0 + exp(-x)); }
double dsigmoid(double x) { return x * (1.0 - x); } // Производная
```
---
### 3. Прямое распространение (Forward Pass)
```c
double* forward(Layer *layer, double *input) {
double *output = (double*)malloc(layer->output_size * sizeof(double));
for (int i = 0; i < layer->output_size; i++) {
double sum = layer->weights[i][0]; // Bias term
for (int j = 0; j < layer->input_size; j++) {
sum += input[j] * layer->weights[i][j+1];
}
output[i] = sigmoid(sum);
}
return output;
}
```
---
### 4. Обратное распространение (Backpropagation)
#### Вычисление градиентов:
```c
void backward(
Layer *layer,
double *input,
double *output_error,
double learning_rate,
double *gradient_prev
) {
// Вычисляем градиенты для предыдущего слоя
for (int i = 0; i < layer->input_size; i++) {
gradient_prev[i] = 0.0;
for (int j = 0; j < layer->output_size; j++) {
gradient_prev[i] += output_error[j] * layer->weights[j][i+1];
}
gradient_prev[i] *= dsigmoid(input[i]);
}
// Обновляем веса
for (int i = 0; i < layer->output_size; i++) {
layer->weights[i][0] += output_error[i] * learning_rate; // Bias
for (int j = 0; j < layer->input_size; j++) {
layer->weights[i][j+1] +=
learning_rate * output_error[i] * input[j];
}
}
}
```
---
### 5. Пример сети (2-2-1) для XOR
#### Создание сети:
```c
MLP create_mlp() {
MLP net;
net.num_layers = 2;
net.layers = (Layer*)malloc(2 * sizeof(Layer));
// Скрытый слой: 2 входных нейрона, 2 выходных
net.layers[0] = create_layer(2, 2);
// Выходной слой: 2 входных, 1 выходной
net.layers[1] = create_layer(2, 1);
return net;
}
```
#### Обучение:
```c
void train(MLP *net, double *input, double target, double lr) {
// Forward pass
double *h1 = forward(&net->layers[0], input);
double *output = forward(&net->layers[1], h1);
// Ошибка выходного слоя
double output_error = (target - output[0]) * dsigmoid(output[0]);
// Backprop для выходного слоя
double grad1[2];
backward(&net->layers[1], h1, &output_error, lr, grad1);
// Backprop для скрытого слоя
double grad0[2];
backward(&net->layers[0], input, grad1, lr, grad0);
free(h1);
free(output);
}
```
---
### 6. Тестирование
```c
int main() {
srand(time(NULL));
MLP net = create_mlp();
double inputs[4][2] = {{0,0}, {0,1}, {1,0}, {1,1}};
double targets[4] = {0, 1, 1, 0}; // XOR
// Обучение
for (int epoch = 0; epoch < 10000; epoch++) {
for (int i = 0; i < 4; i++) {
train(&net, inputs[i], targets[i], 0.1);
}
}
// Тестирование
for (int i = 0; i < 4; i++) {
double *h1 = forward(&net.layers[0], inputs[i]);
double *output = forward(&net.layers[1], h1);
printf("Input: [%d, %d] -> Output: %.4f\n",
(int)inputs[i][0], (int)inputs[i][1], output[0]);
free(h1);
free(output);
}
// Освобождение памяти
for (int l = 0; l < net.num_layers; l++) {
for (int i = 0; i < net.layers[l].output_size; i++) {
free(net.layers[l].weights[i]);
}
free(net.layers[l].weights);
}
free(net.layers);
return 0;
}
```
---
### 7. Особенности реализации
1. **Архитектура**: Сеть 2-2-1 (2 входа, 2 нейрона в скрытом слое, 1 выход)
2. **Функция активации**: Сигмоида для всех слоев
3. **Обучение**: SGD (стохастический градиентный спуск)
4. **Loss**: MSE (Mean Squared Error)
---
### 8. Что можно улучшить
1. **Мини-батчи**: Реализовать пакетное обновление весов
2. **Регуляризация**: Добавить L2-регуляризацию
3. **Оптимизаторы**: Реализовать Adam или RMSprop
4. **GPU**: Использовать OpenCL/CUDA для ускорения
5. **Сохраниение/загрузка**: Добавить функции для сохранения весов
---
### 9. Ограничения
1. **Производительность**: Чистый C медленнее, чем оптимизированные библиотеки
2. **Отладка**: Нет встроенных инструментов визуализации
3. **Гибкость**: Архитектура жестко задана в коде
Для серьезных проектов лучше использовать готовые фреймворки (TensorFlow, PyTorch), но эта реализация помогает понять основы работы нейросетей "под капотом".
Комментариев нет:
Отправить комментарий