Данная статья является описанием моего эксперимента передать управление ресурсами JVM нейронной сети, которая будет предсказывать необходимость управления ресурсами на основе текущих данных, таких как загрузка CPU и память.
Отмечу, что не берется в расчет влияние, в текущем моменте времени, дополнительных процессов загрузки ресурсов нашей системы.
Применимость и потенциальные возможности
Предполагается, что нейронная сеть сможет предсказывать пиковые нагрузки, что позволит избежать избыточного расхода ресурсов или, наоборот, их нехватки. На практике это может быть полезно в разных системах, особенно в микросервисной архитектуре, где контейнеры регулярно требуют динамического управления ресурсами. Например Kubernetes динамически изменяет ресурсы на основе данных, которые передает AI-сервис. Это может быть полезно, если в будущем потребуется увеличить автономность системы.
Что мы реализуем
Мы построим систему, которая:
Считывает показатели системы (CPU, память) в реальном времени.
Обучает нейронную сеть на исторических данных для предсказания вероятности перегрузки.
Корректирует параметры JVM, если прогнозируемая нагрузка превышает порог.
Пример данных для обучения
Для простоты в качестве данных для обучения мы будем использовать CSV-файл jvm_usage.csv
, который содержит три столбца:
CPU
— текущая загрузка процессора,Memory
— текущее использование памяти,HighLoad
— метка перегрузки (0 или 1), обозначающая, была ли нагрузка высокой.
Пример содержимого CSV:
0.7,3000,0
0.8,4000,1
0.6,2000,0
1.0,4500,1
Настройка многослойной нейронной сети на Java
Для реализации нейронной сети мы используем библиотеку DeepLearning4j
. Создадим модель с двумя слоями для обработки входных данных о CPU и памяти и одним выходным нейроном, который будет прогнозировать необходимость увеличения ресурсов JVM.
Подключение необходимых зависимостей
В build.gradle
добавьте:
dependencies {
implementation ("org.slf4j:slf4j-simple:1.7.32")
implementation ("org.deeplearning4j:deeplearning4j-core:1.0.0-beta7")
implementation ("org.nd4j:nd4j-native-platform:1.0.0-beta7")
implementation ("org.datavec:datavec-api:1.0.0-beta7")
implementation ("org.datavec:datavec-local:1.0.0-beta7")
}
Код для настройки и обучения модели
import org.deeplearning4j.nn.api.OptimizationAlgorithm;
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.dataset.api.iterator.DataSetIterator;
import org.deeplearning4j.datasets.datavec.RecordReaderDataSetIterator;
import org.nd4j.linalg.learning.config.Nesterovs;
import org.nd4j.linalg.lossfunctions.LossFunctions;
import org.nd4j.linalg.api.ndarray.INDArray;
import org.nd4j.linalg.factory.Nd4j;
import org.datavec.api.records.reader.impl.csv.CSVRecordReader;
import org.datavec.api.split.FileSplit;
import java.io.File
public class JVMAIManager {
private MultiLayerNetwork model;
public void initializeModel() {
MultiLayerConfiguration config = new NeuralNetConfiguration.Builder()
.seed(123)
.optimizationAlgo(OptimizationAlgorithm.STOCHASTIC_GRADIENT_DESCENT)
.updater(new Nesterovs(0.1, 0.9))
.list()
.layer(0, new DenseLayer.Builder().nIn(2).nOut(10)
.activation(Activation.RELU)
.build())
.layer(1, new DenseLayer.Builder().nIn(10).nOut(10)
.activation(Activation.RELU)
.build())
.layer(2, new OutputLayer.Builder(LossFunctions.LossFunction.XENT)
.activation(Activation.SIGMOID)
.nIn(10).nOut(1).build())
.build();
model = new MultiLayerNetwork(config);
model.init();
}
public void trainModel(String datasetPath) throws Exception {
int batchSize = 50;
int labelIndex = 2;
int numClasses = 1;
CSVRecordReader reader = new CSVRecordReader();
reader.initialize(new FileSplit(new File(datasetPath)));
DataSetIterator iterator = new RecordReaderDataSetIterator(reader, batchSize, labelIndex, numClasses);
model.fit(iterator);
}
public double predict(double currentCpu, double currentMemory) {
INDArray input = Nd4j.create(new double[]{currentCpu, currentMemory}, new int[]{1, 2});
INDArray output = model.output(input);
return output.getDouble(0);
}
}
Данный блок кода строит архитектуру нейронной сети, задавая её конфигурацию и порядок слоёв:
Инициализация настроек сети. Мы начинаем с установки базовых параметров сети с помощью
NeuralNetConfiguration.Builder()
. Здесь задаётся seed (случайное начальное значение для генератора), которое помогает воспроизводить результаты, а также выбирается алгоритм оптимизации — стохастический градиентный спуск (SGD). Этот метод регулирует веса сети, минимизируя ошибку на каждом шаге обучения. Дополнительно, для улучшения и стабилизации процесса обучения, мы подключаем обновляющий алгоритмNesterovs
— метод, который «заглядывает» вперёд, избегая лишних колебаний.-
Создание слоёв. Мы добавляем три слоя в сеть:
Первый слой — DenseLayer, или полносвязный слой, принимает на вход два признака (например, значения загрузки процессора и памяти) и выводит 10 нейронов. Он использует функцию активации ReLU (Rectified Linear Unit), которая сохраняет положительные значения и обнуляет отрицательные. Этот слой помогает сети находить сложные зависимости между признаками.
Второй слой — ещё один полносвязный слой с 10 нейронами и функцией активации ReLU. Этот слой обрабатывает и преобразует выходные данные из первого слоя, углубляя сеть для лучшего обучения.
Выходной слой — OutputLayer, служит для получения конечного предсказания. Здесь мы используем функцию активации Sigmoid, которая выводит значение от 0 до 1. Это полезно для задач бинарной классификации, где результат нужно интерпретировать как вероятность принадлежности к одному из двух классов. Мы также указываем функцию потерь
XENT
(кросс-энтропия), которая вычисляет расхождение между предсказанием и истинным ответом, помогая сети улучшать точность.
Инициализация модели. С помощью вызова
MultiLayerNetwork
создаётся нейронная сеть на основе конфигурации, и вызываетсяmodel.init()
, что завершает процесс построения архитектуры сети и подготавливает её для обучения.
Мониторинг и адаптивная настройка JVM
Добавим классJVMAIAutotuner
для мониторинга и динамического управления параметрами JVM:
import java.lang.management.ManagementFactory;
import com.sun.management.OperatingSystemMXBean;
import java.util.logging.Logger;
public class JVMAIAutotuner {
private static final Logger logger = Logger.getLogger(JVMAIAutotuner.class.getName());
private final OperatingSystemMXBean osBean;
private final JVMAIManager aiManager;
public JVMAIAutotuner(JVMAIManager aiManager) {
this.osBean = (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
this.aiManager = aiManager;
}
public void monitorAndAdapt() {
try {
while (true) {
double cpuLoad = osBean.getSystemCpuLoad();
double usedMemory = osBean.getTotalPhysicalMemorySize() - osBean.getFreePhysicalMemorySize();
double prediction = aiManager.predict(cpuLoad, usedMemory);
logger.info(String.format("Прогноз вероятности высокой нагрузки: %.2f", prediction));
if (prediction > 0.8) {
logger.info("Высокая вероятность перегрузки. Увеличиваем ресурсы JVM...");
increaseHeapMemory();
}
Thread.sleep(5000);
}
} catch (Exception e) {
logger.warning("Ошибка в мониторинге и адаптации: " + e.getMessage());
}
}
private void increaseHeapMemory() {
logger.info("Перезапуск JVM с увеличенной памятью...");
// Логика перезапуска или конфигурации контейнера
}
}
Локальный запуск кода
В классе Main
создаем экземпляры JVMAIManager
и JVMAIAutotuner
и запускаем:
public class Main {
public static void main(String[] args) throws Exception {
JVMAIManager aiManager = new JVMAIManager();
aiManager.initializeModel();
aiManager.trainModel("path/to/jvm_usage.csv"); // Укажите путь к CSV-файлу
JVMAIAutotuner autotuner = new JVMAIAutotuner(aiManager);
autotuner.monitorAndAdapt();
}
}
Результаты и возможные доработки
Такой подход позволяет автоматически управлять ресурсами JVM, прогнозируя высокие нагрузки. Вы можете адаптировать этот код для использования в реальных условиях, например, в среде Kubernetes, добавив поддержку динамической настройки контейнеров через API и интеграцию с системой мониторинга, такой как Prometheus.
Комментарии (2)
sshikov
06.11.2024 07:23Мне кажется, определенная проблема данного решения состоит в том, что автор слишком упростил набор параметров JVM. Отсюда и комментарии выше, что не нужна тут никакая нейронка, а достаточно... Ну в общем да, достаточно - если остальные параметры, влияющие на производительность, проигнорировать, оставив скажем один, то нейронка не нужна. А как только мы их пробуем учесть, мы понимаем, что нейронка-то наверное будет в самый раз. И если вы поищете тут статьи про выбор параметров Apache Spark, то как раз это самое и найдете - т.е. нейронку, и обработку результатов запусков, на которых она обучается.
RodionGork
Забавно, только по-моему сама "нейронная сеть" тут не особо нужна. Годится любая, так сказать, "настраиваемая функция" - просто подбираете её параметры по вашему "тренировочному файлу" - хоть популярными методами из вычислительной математики, хоть вообще случайным поиском (да, можно для маркетинга использовать его разновидность под названием "генетический алгоритм") - и вуаля :)
В частности я думаю нечёткую логику очень легко было можно заюзать. Вы ж регулятор создаёте, на этот счёт уже мегатонны статей в теории автоматического управления написаны.
Но конечно с "нейронной сетью" внутри что угодно гораздо лучше продаётся :)