Как стать автором
Обновить

Нейропаучок пробует силы! Часть 1

Уровень сложностиПростой
Время на прочтение11 мин
Количество просмотров3.8K

Здравствуй, дорогой читатель. В статье речь пойдет об обработке показаний с датчика с применением как простых алгоритмов, так и нейронной сети. Что проще – решай сам.

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

Первый способ обработки – с помощью Z-фактора, сглаживания гаусса:

import numpy as np

from scipy import stats

import lasio

from scipy import ndimage

las = lasio.read(r"Ваш лас.las")

def changeNan (mass):

       nan_indices = np.isnan(mass);

       non_nan_indices = np.arange(len(mass))[~nan_indices]

       interpolated_values = np.interp(np.arange(len(mass)), non_nan_indices, mass[~nan_indices])

       mass[nan_indices] = interpolated_values[nan_indices]

       return mass

def lasApd (m):

     

       m[m >= 1600] = np.NaN

       m[m < 1] = np.NaN

       k = 1

       m = changeNan(m)

       for i in range(1):      

              z = stats.zscore(m, ddof=0);

              m[abs(z) > k ] = np.NaN

              m = changeNan(m)

       m = ndimage.gaussian_filter(m, np.nanmean(m)/3)

       return m

 

arr = [“набор мнемоник для анализа”]

for i in arr:

       las[i] = lasApd(las[i])

       print(i)

with open(выход.las', mode='w') as f:

    las.write(f, version=2.0) 

  Когда я это написал, узнал о «панде» и что можно сделать проще. Полночи ушло (смена у меня ночная).

             В принципе, мне достаточно такой обработки данных, но я решил узнать, на что способны нейросети. Я не специалист по программированию, и последний раз писал код как человек, которому за это платят,15 лет назад. Забавный факт: когда я брался за Python, я думал, что уже писал на нём в 2006 году, но где-то на 3-ю ночь вспомнил, что тот язык назывался Perl. 

             От нейросети я честно ожидал что-то типа:

1. закинул два файла – обучил,

2. закинул необработанный – получил то, что нужно! 

 Не тут-то было.

Почему TensorFlow? – Случайность. На вторую ночь смотрел видео, где, как мне показалось, в полумистическом откровении пишут и приводят либо одни и те же примеры, либо сразу сложные типа: заполним массив случайными числами так, чтобы получилась картина Микеланджело. В общем, пришлось разбираться, как часто бывало в моей практике программирования, с основ. Общее представление о нейросетях мне дали в 2002 году в универе. Так что не совсем с нуля, а чуть ниже. :)

  Первое, что я захотел сделать, – это научиться складывать:

import tensorflow as tf

import numpy as np

from tensorflow import keras

from tensorflow.keras.layers import Dense

 N = 20

M =2

 y_train = np.random.randint(22, size=(N, M))

x = np.sum(y , axis=1)

#Начал писать статью, так и хочется добавить - тут заполняем данными!

 model = keras.Sequential() #Тип модели, хорошо идет с простыми моделями

model.add(Dense(units = 1, input_shape=(M,), activation = 'linear')) #unit количество данных на выходе, инпут - размерность строки массива (не сразу выкупил разницу, между зеленой матрицой, и массивом 

 model.compile(loss='mean_squared_error') # в данном случае ошибка выше, необходимо больше эпох, что-то написать обязательно

ИЛИ

model.compile(optimizer='rmsprop', loss='mse') #Для математических задач лучше подходит

model.fit( y_train,x, epochs=550, batch_size = 2) #batch_size, хорошая штука, типа шага в цикле

model.save('1plus1_primerno_2.keras')

rr = model.predict(y_train) #узнать Ответ, я так и не удосужился узнать, есть ли другой способ обратится к обученной модели

Ответ: 42

  Разобравшись с простой однослойной нейропаутинкой, приступил сразу к сложному, анализу временных рядов. В процессе создания я для себя понял, что каждый слой – это одно простое действие. На два действия – 2 слоя. Три действия – правильно: 3 слоя. Как обозначить, что это последовательность данных? Конечно, параметром Х. При запросе “нейросеть, убрать ошибку, данные сигнала” в первом же комментарии неизвестному парню советуют: обработай математическими методами.    

import numpy as np

import pandas as pd

from keras.models import Sequential

from keras.layers import Dense

import lasio

lasHandMade = lasio.read(r"идеальные мнемоники сделанные специалистами высшего класса!.las)

las = lasio.read(r"мнемоники до обработки.las")

print(las.curves)

data = {'depth': lasHandMade['DEPTH'],

        'valueHand': lasHandMade['F4KPM'],

        }  

df = pd.DataFrame(data)

print (df)

X = df['depth']

y = df['valueHand']

model = Sequential()

model.add(Dense(10, input_dim=1, activation='relu'))

model.add(Dense(1, activation='linear'))

model.compile(loss='mse', optimizer='adam')

model.fit(X, y, epochs=500, verbose=0)

rr = model.predict(las['мнемоника'] )

print('rr', rr)

las['мнемоника'] = np.squeeze(rr, axis=1)

with open('test_outputN.las', mode='w') as f:

      las.write(f, version=2.0)

Для меня как пользователя важно было узнать все возможные параметры, которые можно передавать в model.fit. Они под катом:

Hidden text

Метод model.fit() в библиотеке Keras принимает ряд параметров для настройки процесса обучения модели. Вот основные параметры метода fit():

  1. x: Входные данные для обучения. Может быть массивом NumPy, списком массивов NumPy (если модель имеет несколько входов), или генератором данных. Этот параметр обязателен.

  2. y: Целевые (выходные) данные для обучения. Также может быть массивом NumPy, списком массивов NumPy (если модель имеет несколько выходов), или генератором данных. Этот параметр необязателен, но может быть необходим, если модель требует целевые значения для обучения (например, при использовании учителя с учителем).

  3. batch_size: Целое число, указывающее размер пакета данных, которые будут переданы в модель для обучения. Этот параметр задает количество образцов, которые будут обработаны перед обновлением весов модели.

  4. epochs: Целое число, указывающее количество эпох обучения (каждая эпоха представляет собой один проход по всем образцам в обучающем наборе данных).

  5. verbose: Управляет тем, выводятся ли информационные сообщения о процессе обучения. Может принимать значения 0 (тишина), 1 (полная информация), 2 (информация о каждой эпохе в одной строке).

  6. validation_data: Кортеж (x_val, y_val) данных, на которых будет оцениваться производительность модели в конце каждой эпохи обучения. Это позволяет отслеживать процесс обучения и определять, происходит ли переобучение.

  7. shuffle: Булево значение, указывающее, следует ли перемешивать обучающие данные перед каждой эпохой. По умолчанию True.

  8. callbacks: Список обратных вызовов Keras, которые будут вызываться во время обучения модели. Обратные вызовы могут выполнять различные действия, такие как сохранение весов модели, раннюю остановку или изменение скорости обучения.

  1. validation_split: Доля данных для использования в качестве набора для валидации. Например, если validation_split=0.1, то последние 10% данных будут использоваться для валидации, а остальные для обучения. Этот параметр может быть удобен, когда у вас нет явно разделенных наборов данных для обучения и валидации.

  2. validation_data: Кортеж (x_val, y_val) данных, на которых будет оцениваться производительность модели в конце каждой эпохи обучения, как уже упомянуто ранее.

  3. initial_epoch: Эпоха, с которой начнется обучение. Может быть полезен, если вам нужно возобновить обучение с определенной точки после его приостановки.

  4. steps_per_epoch: Количество шагов обучения за каждую эпоху. Может быть полезно, если вы хотите обучать модель только на подмножестве данных за каждую эпоху.

  5. validation_steps: Количество шагов валидации в каждой эпохе. По умолчанию используется количество образцов в валидационном наборе данных.

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

  7. sample_weight: 1D массив весов, применяемых к каждому образцу входных данных. Это может быть полезно, когда у вас есть несбалансированные данные или когда вы хотите дать больший вес определенным образцам.

  8. max_queue_size: Максимальный размер очереди данных, используемых при обучении модели. Это может быть полезно при использовании генераторов данных, чтобы ограничить использование оперативной памяти.

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

и model.compile

Hidden text
  1. optimizer: Оптимизатор, который будет использоваться для обновления весов модели в процессе обучения. Например, 'adam', 'sgd', 'rmsprop' и т. д.

  2. loss: Функция потерь, которая будет оптимизироваться в процессе обучения. Для задач регрессии это может быть 'mse' (среднеквадратичная ошибка), 'mae' (средняя абсолютная ошибка) и другие. Для задач классификации это может быть 'categorical_crossentropy', 'binary_crossentropy' и другие.

  3. metrics: Список метрик, которые будут вычисляться в процессе обучения и выводиться во время обучения. Это позволяет отслеживать производительность модели на дополнительных метриках, помимо функции потерь. Например, ['accuracy'] для задач классификации или ['mse', 'mae'] для задач регрессии.

  4. loss_weights: Опционально. Веса, применяемые к разным компонентам функции потерь. Это может быть полезно, когда функция потерь состоит из нескольких компонентов, и вы хотите присвоить разные веса каждому компоненту.

  5. weighted_metrics: Опционально. Список метрик, которые будут взвешены в соответствии с весами образцов и вычислены во время обучения и оценки.

  6. run_eagerly: Опционально. Логическое значение, указывающее, следует ли запускать модель в режиме eager execution. Режим eager execution полезен для отладки и проверки модели, но может снизить производительность при обучении на больших наборах данных.

  7. steps_per_execution: Опционально. Количество шагов градиентного спуска, выполняемых за одну операцию обучения. Это позволяет ускорить процесс обучения на GPU или TPU.

Код без купюр. По моей логике, первый слой должен вычислить ошибку, а второй – хотелось бы – её интерполировать. Эпох (на моём сленге – итераций) ставлю много, ибо мне все равно. Реально их тут надо штук 200. А как хотелось бы – не работает: да, ошибки сглаживает, как и всю кривую, и надо вводить функцию интерполяции. Но я уже понимал, как схитрить, и решил делать, как советуют в мануале, временные ряды. И пошел дальше. 

 import numpy as np

from tensorflow.keras import layers, models

 N = 1000 // количество примеров, лишканул… 

input_shape = (10, 1) // размерность данных для анализа нейросети, иными словами, если вы берете для анализа 1 метр, у вас в него влазит 10 значений, то размерность 10, 1 

data_poor_mnemonik = np.random.uniform(5, 30, size = (N, input_shape[0], input_shape[1]))

data_poor_mnemonik[:, 5] += np.mean(data_poor_mnemonik)*3  # Добавление аномальных точек в сигнал

 data_clear_mnemonik = np.random.uniform(5, 30, size = (N, 10, 1))

            //Модель автокодировщика (обычная) для ... ну как получится

 model = models.Sequential([

    layers.LSTM(64, input_shape=input_shape, return_sequences=True),  

    layers.LSTM(32, return_sequences=True),  

    layers.TimeDistributed(layers.Dense(1))  // взял с сайта по керасу, по сути на каждую единицу шага, в моём случае 1, используется 1 слой. Т.е. умеем что-то, что умеет кодировщик (1 строка), и декодировщик (2 строка)

])

 model.compile(optimizer='adam', loss='mse');

 anomaly_mask = np.zeros_like(data_poor_mnemonik)

anomaly_mask[:, 5] = 1

 model.fit( data_clear_mnemonik,data_poor_mnemonik, sample_weight=(1 - anomaly_mask),  epochs=100, batch_size=32)

 filtM = model.predict(data_poor_mnemonik)

corrM = data_poor_mnemonik * (1 - anomaly_mask) + filtM * anomaly_mask

 import matplotlib.pyplot as plt

N=10

plt.figure(figsize = (8, 6))

plt.plot(data_poor_mnemonik.flatten()[0:100])

plt.plot(corrM.flatten()[0:100])

plt.plot(filtM.flatten()[0:100])

plt.show()

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

Полноценное решение поставленной задачи за грудой кода и понимания, но потихоньку, шаг за шагом…

             Итог обработки на скринах. На первом – исходные данные, на втором – работа алгоритма, на третьем – нейросеть, но с искусственным примером. Математика стабильнее и дает более ясное пространство для манипуляций, выбор критериев отбора и т.п. Математическим методом я написал программу где-то часа за 3, первый раз используя Python и первая нейросеть «2+2» – это еще полчаса. И смутное понимание, того, что делаешь. 4 дня спустя начинаешь понимать первый пример. И через 4 дня всё еще нет реализации полноценного решения чуть более сложной нейросети. Возможно, вам мой опыт пригодится. Большую часть времени ищешь, как пользоваться бестолковым и «пандой».

Оригинал
Оригинал
Математика!
Математика!

   

https://lh7-us.googleusercontent.com/8upBKphXee83DWAwG99n-dDK3rNGQ6Yoyanxw6t_XnjNGV4UUcIMyhkeA8W5rYR2SkWhXAg01gDy-lu0nKUIYfSJE6KW0-LvZBx3EfSup7rOaVGZs_Dp1IFy6_0xUmGzrKaAW0M4qr_DVqjFiulHYkA
Нейронка исправляет! Зеленная кривая, чистая выдача нейронной сети, синяя, частично совпадает с оранжевой как исходной кривой.

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

            Вторая причина – раскрутка маленького приложения, написанного в помощь родному человеку. https://play.google.com/store/apps/details?id=children.notebook Бесплатный проект!

            Если не заминусуют, продолжу.

Upd:

5-й день, еще 2 часа. Писать отдельный пост на эту тему смысла нет, поэтому апдейтом. 

Модель сделана, и делает всё, что заявлялось, но неидеально.

import numpy as np
from tensorflow.keras import layers, models

N = 1000
input_shape = (10, 1)

data_poor_mnemonik = np.random.uniform(5, 30, size = (N, input_shape[0], input_shape[1]))
data_clear_mnemonik = np.copy(data_poor_mnemonik)

data_poor_mnemonik[:, 5] += np.mean(data_poor_mnemonik)*3  # Добавление аномальных точек в сигнал

model = models.Sequential([
    layers.LSTM(64, input_shape=input_shape, return_sequences=True),  
    layers.LSTM(32, return_sequences=True),  
    layers.TimeDistributed(layers.Dense(1))  # взял с сайта по керасу, по сути на каждую единицу шага, в моём случае 1, используется 1 слой.
])

model.compile(optimizer='adam', loss='mse');

anomaly_mask = np.zeros_like(data_poor_mnemonik)
anomaly_mask[:, 5] = 1

model.fit( data_clear_mnemonik,data_poor_mnemonik, sample_weight=(1 - anomaly_mask),  epochs=100, batch_size=32, validation_split=0.2)

data_poor_mnemonik_F_p = np.random.uniform(5, 30, size = (N, input_shape[0], input_shape[1]))
data_poor_mnemonik_F_p[:, 8] += np.mean(data_poor_mnemonik)*3 

filtM = model.predict(data_poor_mnemonik_F_p)
corrM = data_poor_mnemonik_F_p * (1 - anomaly_mask) + filtM * anomaly_mask

import lasio
las = lasio.read(r"Ваш лас файл.las")

arr = np.array(las['Название мнемоники.las'])
arr = arr[:- (arr.size%input_shape[0])]
arr= arr.reshape( int( arr.size / input_shape[0]), input_shape[0], input_shape[1])

rr = model.predict(arr )

rr = np.append(rr.flatten(), np.zeros(las['Название мнемоники'].size%input_shape[0]) )
las['Название мнемоники'] = rr

with open('Ваш лас файл_out.las', mode='w') as f:
    las.write(f, version=2.0) 

Вопросы, которые у меня возникали по ходу, и ответы на них. 

  1. Как задать выходные параметры? 

Нашел два способа:
– задать параметр units = 1… в последнем слое

model.add(Dense(1, activation='linear'))

– задать параметр y_train из первого примера (какие данные для проверки даете, такие и получите).

  1. Сколько слоев, какой размерности, как выбирать? 

Только опыт, эксперимент, пробы и ошибки. Я исходил из правила: 1 (float) значение, размерность скрытого слоя – 16. Про количество слоев писал выше.

Далее будем улучшать, как говорил ранее.

Теги:
Хабы:
Всего голосов 11: ↑7 и ↓4+6
Комментарии8

Публикации

Истории

Работа

Data Scientist
75 вакансий
Python разработчик
114 вакансий

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань