От подвала до облака: как обучить нейросеть в домашних условиях

от автора

Всем известно, что обучение нейросетей требует значительных вычислительных ресурсов. Но что делать, если у вас нет мощного оборудования? В этой статье я расскажу, как обучить нейросеть частями, но и объясню ключевые понятия вроде слоев, батчей, и функций активации. Эта статья может быть полезна начинающим разработчикам, кто только погружается в нейронки.

В качестве примера кода я приведу обработку транзакций, однако данные могут быть заменены на любые, где требуется анализ о допустимости или недопустимости результата на основе цепочки данных.

Основные понятия нейросетей

Прежде чем углубляться в процесс, кратко объясним ключевые термины, чтобы статья была понятна даже новичкам.

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<DataSet> loadData() throws IOException {         List<DataSet> dataBatches = new ArrayList<>();         try (BufferedReader br = new BufferedReader(new FileReader(filePath))) {             String line;             List<double[]> features = new ArrayList<>();             List<double[]> 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<DataSet> 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("Обучение завершено.");     } }

Почему этот подход подходит для больших данных?

  1. Батчевый подход: данные загружаются порциями (батчами), что снижает нагрузку на оперативную память. Даже файлы размером в гигабайты можно обработать на обычном ПК.

  2. Простота архитектуры: нейросеть оптимизирована для бинарной классификации, без избыточных слоёв. Это экономит ресурсы.

  3. Масштабируемость: код можно адаптировать для других задач, включая мультиклассовую классификацию или регрессию.

  4. Эффективность: благодаря библиотекам DeepLearning4j и ND4J обучение проходит быстро даже на процессоре.

С помощью этого подхода можно обучать нейронные сети на больших данных, не выходя из дома (понятно, что в разумных лимитах данных). Файл данных, простой код и понятная модель делают процесс удобным и адаптируемым.


ссылка на оригинал статьи https://habr.com/ru/articles/859866/