От подвала до облака: как обучить нейросеть в домашних условиях
Всем известно, что обучение нейросетей требует значительных вычислительных ресурсов. Но что делать, если у вас нет мощного оборудования? В этой статье я расскажу, как обучить нейросеть частями, но и объясню ключевые понятия вроде слоев, батчей, и функций активации.
В качестве примера кода я приведу обработку транзакций, однако данные могут быть заменены на любые, где требуется анализ о допустимости или недопустимости результата на основе цепочки данных.
Основные понятия нейросетей
Прежде чем углубляться в процесс, кратко объясним ключевые термины, чтобы статья была понятна даже новичкам.
1. Слои нейросети
Нейросеть состоит из слоев, где каждый слой выполняет математическую обработку данных:
Входной слой принимает данные (например, текст или изображение).
Скрытые слои обрабатывают данные, выявляя паттерны и взаимосвязи.
Выходной слой выдает результат, например, вероятность принадлежности к какому-либо классу.
В простом многослойном перцептроне (MLP) каждый узел (нейрон) в слое соединен с узлами следующего слоя. Вот пример:
Входные данные: 100 чисел (например, векторы слов).
Первый скрытый слой: 64 нейрона. Каждый из них вычисляет взвешенную сумму входов и применяет функцию активации (например, ReLU).
Выходной слой: 2 нейрона (например, для предсказания «положительный» или «отрицательный»).
2. Батчи
Когда мы обучаем нейросеть, данные разбиваются на части — батчи. Вместо того чтобы подавать всю обучающую выборку сразу, модель обрабатывает один батч за раз.
Пример: если у нас есть 1000 текстов и размер батча — 32, то модель обработает данные в 32 итерациях (по 32 текста за итерацию).
Зачем нужны батчи?
Экономия памяти: мы не храним весь набор данных в оперативной памяти.
Ускорение обучения: обработка маленьких порций данных позволяет быстрее корректировать параметры модели.
3. Функции активации
Функции активации определяют, как выход одного нейрона преобразуется для следующего слоя. Популярные функции:
ReLU (Rectified Linear Unit): пропускает положительные значения, обнуляя отрицательные.
Softmax: преобразует выходной слой в вероятности (сумма всех значений = 1).
Как я реализовал обучение
Создадим файл transactions.csv, содержащий данные о транзакциях. Каждая строка — это одна транзакция, включающая 10 входных параметров и метку класса (0
или 1
). Вот пример содержимого файла:
0.5,0.1,0.3,0.2,0.9,0.7,0.4,0.6,0.8,0.3,1 0.6,0.2,0.4,0.1,0.8,0.5,0.3,0.5,0.7,0.2,0 0.7,0.3,0.5,0.4,0.7,0.6,0.2,0.4,0.9,0.1,1
Каждая строка состоит из 10 чисел (фичей), за которыми следует метка класса:
0
— нормальная транзакция,1
— подозрительная транзакция.
Реализация кода
1. Загрузка данных из файла
Напишем класс DataLoader, который будет загружать данные из файла и делить их на батчи. Это позволяет работать с большими файлами, обрабатывая данные частями:
import org.nd4j.linalg.dataset.DataSet;
import org.nd4j.linalg.factory.Nd4j;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class DataLoader {
private final String filePath;
private final int batchSize;
public DataLoader(String filePath, int batchSize) {
this.filePath = filePath;
this.batchSize = batchSize;
}
public List loadData() throws IOException {
List dataBatches = new ArrayList<>();
try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {
String line;
List features = new ArrayList<>();
List labels = new ArrayList<>();
while ((line = br.readLine()) != null) {
String[] parts = line.split(",");
double[] input = new double[parts.length - 1];
for (int i = 0; i < parts.length - 1; i++) {
input[i] = Double.parseDouble(parts[i]);
}
features.add(input);
double[] label = new double[2];
label[Integer.parseInt(parts[parts.length - 1])] = 1.0;
labels.add(label);
if (features.size() == batchSize) {
dataBatches.add(new DataSet(Nd4j.create(features.toArray(new double[0][])), Nd4j.create(labels.toArray(new double[0][]))));
features.clear();
labels.clear();
}
}
}
return dataBatches;
}
}
2. Создание модели нейронной сети
Для классификации данных создадим простую нейросеть с двумя скрытыми слоями. Она будет обучаться определять подозрительные транзакции:
import org.deeplearning4j.nn.conf.MultiLayerConfiguration;
import org.deeplearning4j.nn.conf.NeuralNetConfiguration;
import org.deeplearning4j.nn.conf.layers.DenseLayer;
import org.deeplearning4j.nn.conf.layers.OutputLayer;
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
import org.nd4j.linalg.activations.Activation;
import org.nd4j.linalg.lossfunctions.LossFunctions;
public class ModelBuilder {
public static MultiLayerNetwork buildModel(int inputSize, int outputSize) {
MultiLayerConfiguration config = new NeuralNetConfiguration.Builder()
.learningRate(0.01)
.list()
.layer(new DenseLayer.Builder()
.nIn(inputSize)
.nOut(64)
.activation(Activation.RELU)
.build())
.layer(new DenseLayer.Builder()
.nIn(64)
.nOut(32)
.activation(Activation.RELU)
.build())
.layer(new OutputLayer.Builder(LossFunctions.LossFunction.NEGATIVELOGLIKELIHOOD)
.nIn(32)
.nOut(outputSize)
.activation(Activation.SOFTMAX)
.build())
.build();
MultiLayerNetwork model = new MultiLayerNetwork(config);
model.init();
return model;
}
}
3. Обучение модели
Теперь объединим загрузку данных и модель для обучения:
import org.deeplearning4j.nn.multilayer.MultiLayerNetwork;
import org.nd4j.linalg.dataset.DataSet;
import java.io.IOException;
import java.util.List;
public class Main {
public static void main(String[] args) throws IOException {
String filePath = "transactions.csv"; //путь до файла
int batchSize = 50;
int numFeatures = 10; // Количество входных признаков
int numClasses = 2; // Бинарная классификация
DataLoader dataLoader = new DataLoader(filePath, batchSize);
List trainingData = dataLoader.loadData();
MultiLayerNetwork model = ModelBuilder.buildModel(numFeatures, numClasses);
int numEpochs = 10;
for (int epoch = 0; epoch < numEpochs; epoch++) {
for (DataSet batch : trainingData) {
model.fit(batch);
}
System.out.println("Эпоха " + (epoch + 1) + " завершена.");
}
System.out.println("Обучение завершено.");
}
}
Почему этот подход подходит для больших данных?
Батчевый подход: данные загружаются порциями (батчами), что снижает нагрузку на оперативную память. Даже файлы размером в гигабайты можно обработать на обычном ПК.
Простота архитектуры: нейросеть оптимизирована для бинарной классификации, без избыточных слоёв. Это экономит ресурсы.
Масштабируемость: код можно адаптировать для других задач, включая мультиклассовую классификацию или регрессию.
Эффективность: благодаря библиотекам DeepLearning4j и ND4J обучение проходит быстро даже на процессоре.
С помощью этого подхода можно обучать нейронные сети на больших данных, не выходя из дома (понятно, что в разумных лимитах данных). Файл данных, простой код и понятная модель делают процесс удобным и адаптируемым.