Нейронная сеть для поиска места утечки из трубопровода

от автора

Введение

В данной статье будет рассматриваться задача многомерной интерполяции с помощью нейронной сети. Нейронная сеть построена с помощью среды математического моделирования Engee. Данная задача имеет серьезное практическое применение так как нарушение целостности трубопровода связано с большими экономическими последствиями или даже может привести к техногенной катастрофе (в случае если трубопровод промышленный). Основная проблема обнаружения места утечки что нельзя напрямую зафиксировать факт утечки и определить её можно только алгоритмически измеряя косвенные параметры (давление, скорость потока жидкости или газа в трубопроводе, температура). Для повышения точности измерений данные параметры измеряются на всём протяжении трубопровода. Наиболее простой и распространенный способ. Но у данного способа есть существенный недостаток, он позволяет определить только участок трубопровода и для более точного определения места утечки нужен другой (более точный) алгоритм поиска.

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

Используемые библиотеки

using Fluxusing Statisticsusing Randomusing Plots

Исходные данные для обучения нейронной сети (расстояние, избыточное давление, расход)

data = [    160790.0355;    160600.045;    160530.05;    160440.057;    160400.0625;    120790.035;    120600.0385;    120530.043;    120440.0495;    120400.0535;    100790.029;    100600.036;    100530.04;    100440.0465;    100400.05;    80790.0265;    80600.0278;    80530.0284;    80440.03;    80400.0315;    60440.024;    60400.0248;    40400.0195;]

Подготовка данных

Перед обучением нейронной сети необходимо провести подготовку исходных данных (разделение на вектор-столбцы, нормализация). Для более наглядного представления выходы и входы сети представлены как отдельные переменные. Нормализация требуется для повышения качества работы нейронной сети и приведения входных данных разного типа к одному масштабу а также для предотвращения насыщения для функций активации.

# Разделяем таблицу на входы (X) и выходы (Y)X_raw = Float32.(data[:, 1:2]') # Транспонируем до размера 2 x NY_raw = Float32.(reshape(data[:, 3], 1, :)) # Размер 1 x N# 2. Нормализация данных (критически важно!)# Нейросети плохо работают с данными разного масштаба (сотни у X и сотые доли у Y).stats_x = (mean(X_raw, dims=2), std(X_raw, dims=2) .+ eps(Float32))stats_y = (mean(Y_raw), std(Y_raw) .+ eps(Float32))function normalize(arr, stats)    return @. (arr - stats[1]) / stats[2]endX_norm = normalize(X_raw, stats_x)Y_norm = normalize(Y_raw, stats_y)

Сравнение различных моделей

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

Модель 1 (4 слоя, максимальное число узлов 32)

model = Chain(    Dense(2 => 32, tanh),     Dense(32 => 32, tanh),    Dense(32 => 32, tanh),    Dense(32 => 1))

Модель 2 (8 слоёв, максимальное число узлов 128)

model2 = Chain(    Dense(2 => 128, tanh),     Dense(128 => 128, tanh),    Dense(128 => 128, tanh),    Dense(128 => 128, tanh),    Dense(128 => 64, tanh),    Dense(64 => 32, tanh),    Dense(32 => 16, tanh),    Dense(16 => 1))

Модель 3 (12 слоёв, максимальное число узлов 128)

model3 = Chain(    Dense(2 => 32, tanh),     Dense(32 => 32, tanh),    Dense(32 => 64, tanh),    Dense(64 => 128, tanh),    Dense(128 => 64, tanh),    Dense(64 => 32, tanh),    Dense(32 => 32, tanh),    Dense(32 => 32, tanh),    Dense(32 => 32, tanh),    Dense(32 => 32, tanh),    Dense(32 => 16, tanh),    Dense(16 => 1))

Для каждой из моделей создана функция для расчета погрешностей при работе нейронной сети.

loss(x, y) = Flux.Losses.mse(model(x), y)opt = Flux.setup(Flux.Adam(0.00005), model)loss2(x, y) = Flux.Losses.mse(model2(x), y)opt2 = Flux.setup(Flux.Adam(0.00005), model2)loss3(x, y) = Flux.Losses.mse(model3(x), y)opt3 = Flux.setup(Flux.Adam(0.00005), model3)

Функция для обучения моделей

function train!(epochs,model)    losses = Float32[]    best_model_state = nothing    best_loss = Inf        println("Начало обучения...")    for epoch in 1:epochs        if model == model2        grads2 = Flux.gradient(m -> loss2(X_norm, Y_norm), model2)[1]        Flux.Optimise.update!(opt2, model2, grads2)        current_loss = loss2(X_norm, Y_norm)        elseif model == model3        grads3 = Flux.gradient(m -> loss3(X_norm, Y_norm), model3)[1]        Flux.Optimise.update!(opt3, model3, grads3)        current_loss = loss3(X_norm, Y_norm)        else        grads = Flux.gradient(m -> loss(X_norm, Y_norm), model)[1]        Flux.Optimise.update!(opt, model, grads)        current_loss = loss(X_norm, Y_norm)        end                if current_loss < best_loss            best_loss = current_loss            best_model_state = deepcopy(Flux.state(model))        end        push!(losses, current_loss)        if epoch % 1000 == 0 || epoch == 1            println("Эпоха $epoch | Потери: $(round(current_loss, sigdigits=10))")        end    end        return best_lossend

Обучение моделей

Вызов функции для обучения моделей (вызывается для каждой модели)

train!(10000,model) # обучение модели 1train!(10000,model2) # обучение модели 2train!(10000,model3) # обучение модели 3

Отображение результата обработки

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

# 6. Оценка качества на исходных данных (денормализация)Y_pred_norm = model(X_norm)Y_pred = Y_pred_norm .* stats_y[2] .+ stats_y[1]mae = mean(abs.(Y_pred' .- data[:, 3])) # Средняя абсолютная ошибка в исходных единицахprintln("\nОбучение завершено.")println("Оценка точности на узлах таблицы (MAE): $(round(mae, digits=6))")xg = range(minimum(data[:, 1]), maximum(data[:, 1]), length=50)yg = range(minimum(data[:, 2]), maximum(data[:, 2]), length=50)z_grid = [model(normalize([x, y], stats_x))[1] for y in yg, x in xg]z_grid_denorm = z_grid .* stats_y[2] .+ stats_y[1]#plotlyjs() surface(xg, yg, z_grid_denorm;    title="Определение места утечки",    xlabel="Давление",     zlabel="Расход",    ylabel="Расстояние до места утечки",    legend=false,    alpha=0.8)scatter3d!(data[:, 1], data[:, 2], data[:, 3];     color=:red, markersize=4, label="Исходные узлы")

Денормализация результата и отображение на графике (для второй модели)

# 6. Оценка качества на исходных данных (денормализация)Y_pred_norm = model2(X_norm)Y_pred = Y_pred_norm .* stats_y[2] .+ stats_y[1]mae = mean(abs.(Y_pred' .- data[:, 3])) # Средняя абсолютная ошибка в исходных единицахprintln("\nОбучение завершено.")println("Оценка точности на узлах таблицы (MAE): $(round(mae, digits=6))")xg = range(minimum(data[:, 1]), maximum(data[:, 1]), length=50)yg = range(minimum(data[:, 2]), maximum(data[:, 2]), length=50)z_grid = [model2(normalize([x, y], stats_x))[1] for y in yg, x in xg]z_grid_denorm = z_grid .* stats_y[2] .+ stats_y[1]#plotlyjs() surface(xg, yg, z_grid_denorm;    title="Определение места утечки",    xlabel="Давление",     ylabel="Расход",    zlabel="Расстояние до места утечки",    legend=false,    alpha=0.8)scatter3d!(data[:, 1], data[:, 2], data[:, 3];     color=:red, markersize=4, label="Исходные узлы")

Денормализация результата и отображение на графике (для третьей модели)

# 6. Оценка качества на исходных данных (денормализация)Y_pred_norm = model3(X_norm)Y_pred = Y_pred_norm .* stats_y[2] .+ stats_y[1]mae = mean(abs.(Y_pred' .- data[:, 3])) # Средняя абсолютная ошибка в исходных единицахprintln("\nОбучение завершено.")println("Оценка точности на узлах таблицы (MAE): $(round(mae, digits=6))")xg = range(minimum(data[:, 1]), maximum(data[:, 1]), length=50)yg = range(minimum(data[:, 2]), maximum(data[:, 2]), length=50)z_grid = [model3(normalize([x, y], stats_x))[1] for y in yg, x in xg]z_grid_denorm = z_grid .* stats_y[2] .+ stats_y[1]#plotlyjs() surface(xg, yg, z_grid_denorm;    title="Определение места утечки",    xlabel="Давление",     zlabel="Расход",    ylabel="Расстояние до места утечки",    legend=false,    alpha=0.8)scatter3d!(data[:, 1], data[:, 2], data[:, 3];     color=:red, markersize=4, label="Исходные узлы")

Вывод

Для параметров данного трубопровода наилучший результат показала модель состоящая из четырёх слоёв, она выдала погрешность в узлах 0.007823 что ниже остальных моделей, это связано с ограниченным размером обучающей выборки, из за недостаточно размера сеть из большого числа слоёв окажется недообученной и будет выдавать завышенную погрешность.

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