Русский
Русский
English
Статистика
Реклама

Машинное обучение. нейросети python

Как с помощью хакатона столкнуть математиков и MLщиков, и кто тогда победит

28.11.2020 12:19:52 | Автор: admin

Введение



Эта статья о том, как мы совместно с роснефтёвой дочкой Самаранефтехимпроект и Казанским Федеральным Университетом в сентябре 2020 года провели Хакатон трёх городов, на котором предложили студентам решить классическую задачу сейсмической корреляции отражающих горизонтов. С такими задачами постоянно сталкиваются специалисты по сейсморазведке по всему миру. Для участников задачу решили преподнести как задачу поиска оптимального пути, чтобы не отпугнуть студентов страшными словами. В статье расскажем подробнее про задачу и разберём интересные решения участников. Это будет увлекательно для специалистов как по прикладному математическому моделированию, так и машинному обучению и анализу данных.


Организационная часть


Интересные подробности организации онлайн-хакатона в трёх городах мы рассказали в статье на vc.ru Нефтянка и хакатон. Марафон это не только бег.
Упомянем лишь, что для онлайн-формата мы выбрали сервис Discord и оставим ссылку на правила хакатона (ссылка на площадке Boosters).

Постановка задачи


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

image

Вертикальные сечения сейсмического куба и распознанные сейсмические отражающие горизонты

На практике горизонты выделяются послойно на сейсмических разрезах сейсмического куба как вручную, с помощью расстановки (специалисты по сейсморазведке говорят пикирования) большого числа реперных точек, так и с помощью автоматизированных и полуавтоматизированных процедур поиска. Безусловно, качественное решение задачи интерпретации сейсмических горизонтов с помощью программного обеспечения крайне востребовано и позволяет существенно снизить временные затраты специалистов по сейсморазведке.
В то же время, изучение источников (Least-squares horizons with local slopes and multi-grid correlations, Waveform Embedding: automatic horizon picking with unsupervised deep learning) показывает, что разработанные алгоритмы и решения основаны на небольшом числе математических подходов, поэтому мы решили попробовать привлечь студентов с их ещё не затуманенным научными изысканиями сознанием и предложить им данную задачу в форме задачи поиска оптимального пути на сложной поверхности.
В итоге задача была сформулирована так: построение пути движения на сложной поверхности, проходящего через заданные точки и удовлетворяющего условиям минимума некоторого функционала, зависящего от длины пути и его углов (градиентов).

image
Пример части исходного сейсмического разреза для построения горизонта. Зеленая линия заранее известная часть, красная искомая.

Участникам соревнования предстояло за 12 часов найти решение, позволяющее продолжить путь по оптимальной траектории на скрытой части датасета. На валидацию решения предоставлялось 20 попыток, побеждал участник с минимальным значением метрики.

Подробное описание данных
Ниже подробное описание данных, которые были доступны участнику:
image

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

По-простому, участнику надо было продолжить hor_2 в области L1. Область L2 представляла собой перпендикулярный срез L1. Мы её добавили для генерации участниками дополнительных знаний. Но, к сожалению, она не пригодилась участникам.


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

$F(y ,z)=i=0Nyi pred-yi etalon2+zi,yi pred-zi,yi etalon2$

где:
N размерность искомого горизонта;
yi pred координаты горизонта, полученного с помощью алгоритма, i0,N;
yi etalon координаты эталонного горизонта;
zi,yi значения карты поверхности в точке с координатамиi,yi;
yi =yiheight, где height максимально возможное значение координаты y карты поверхности;
zi,yi=zi,yimax(z), где max(z) наибольшее значение карты поверхности.

Реализация метрики в Python
image


Какие методы применяли команды



Задача подбиралась изначально такой, которую можно было бы решить несколькими способами: прямым и обратным (классическими математическими методами и методами машинного обучения соответственно).
С точки зрения машинного обучения задачу можно решать двумя методами:
1) Построение регрессии
Используя известные пары точек (xi,yi), можно построить отображение f:(xi)yi путём минимизации функции потерь L. (xi) признаковое описание i-й точки.
image
Функцией потерь может быть как исходная функция ошибки из постановки задачи, так и более простая функция, например, среднеквадратическое отклонение построенного и исходного путей:1Ni=1N(yi yi)^2.
Для построения отображения f можно воспользоваться множеством популярных методов машинного обучения: начиная с полиномиальной регрессии, проходя через случайный лес и глубокими нейронными сетями.
image
2) Семантическая сегментация
image
Пример семантической сегментации

Исходную задачу можно решать как задачу компьютерного зрения. Точки (x, y) рассматривать как пикселы изображения, где всё изображение это весь датасет, а яркость пиксела (x, y) значение z(x, y). Для построения пути нужно каждому пикселю присвоить один из классов 0 или 1. Часть изображения, находящаяся ниже пути или включающая его, относится к классу 0, а оставшаяся к классу 1. Бытовое решение для такой задачи полносвёрточная нейросеть U-Net, на вход получающая кусок (патч) исходного изображения и выдающая массив того же размера, состоящий из нулей и единиц, обозначающих классы соответствующих пикселов.
Кроме методов глубокого обучения, для сегментации изображений можно также использовать методы классического компьютерного зрения и обработки изображений, например, Flood fill. Это и сделал один из участников, тем самым предобработав изображение для дальнейшего применения алгоритмов поиска кратчайшего пути.
С точки зрения классических математических методов предложенная задача является классической задачей оптимизации, и мы наблюдали попытки её решения следующими группами методов:
1. Методы, использующие принцип локального экстремума;
Суть данного подхода заключается в поиске локальных экстремумов значений поверхности в пределах заданного окна поиска. Далее выбирается то значение координаты y среди найденных экстремумов, которое наименьшим образом отличается от y, найденного на предыдущем шаге.
2. Методы, использующие принцип глобального экстремума;
В рамках данного подхода при определении координаты yi ищется глобальный экстремум среди усреднённых значений поверхности карты в пределах заданного окна поиска.
3. Методы, основанные на минимизации заданной эвристики.
Данным подходом воспользовалось несколько команд, суть данного подхода заключается создании и минимизации предложенного командами функционала.

Итоги и кто победил


Для начала разберём решения участников.
Методы машинного обучения:
Одним из решений была авторегрессионная свёрточная нейросеть, выдающая вещественное число значение пути yi для i-го шага. На вход нейросети подавались патчи 32x32 пиксела исходного изображения. В качестве функции для извлечения признаков использовалась предобученная свёрточная нейросеть ResNet34. Полученное этой нейросетью признаковое представление объединялось со значениями данного пути с предыдущих 32 шагов. Для прогнозирования дальше 32 шагов в качестве предыдущих значений горизонта использовались предыдущие прогнозы нейросети. Нейросеть обучалась модификацией стохастического градиентного спуска Adam с экспоненциальным уменьшением шага оптимизатора по мере обучения. Для обучения минимизировалось среднее абсолютное отклонение (эксперименты со среднеквадратическим отклонением дали хуже результат). Во избежание переобучения использовался Дропаут, то есть случайное обнуление части нейронов. Для обучения нейросети потребовалось около 10 минут, 20 полных проходов по всему датасету и 720 шагов оптимизатора.
image
Решение, полученное с помощью свёрточной нейросети. Красная линия реальный путь, синяя полученный участником.

Прогноз нейросети занимает около 1 минуты на CPU AMD Threadripper 2950x и GPU Nvidia GTX 1080 Ti.
Результат нейросети (метрика) 5.71 на публичной турнирной таблице. Также были проделаны эксперименты с заменой свёрточной нейросети на рекуррентную, но её результат был хуже. В итоге в качестве финального решения были использованы классические методы вычислительной математики.
Кроме законченных решений, участники также поделились своими идеями, которые не успели реализовать из-за жёстких временных рамок соревнования и вычислительной сложности их задумок. Некоторые из них пытались применить нейронные сети, но, потратив большую часть времени, переходили к более простым и эффективным алгоритмам или даже к грубому перебору и правилам, что в итоге дало лучший результат и привело к призовым местам.
Также ряд интересных решений основан на знаниях из других дисциплин: например, классическое компьютерное зрение и обработка изображений, теория графов, анализ временных рядов. Одна из команд даже поставила задачу в терминах обучения с подкреплением, про которое вы могли слышать, и придумала решение, но, к сожалению, не успела его реализовать.
Классические математические методы:
image
Одно из решений, полученное методом локального экстремума. Красная линия реальный путь, синяя полученная участником.

Для данного метода в качестве экстремума использовался локальный максимум. Синим цветом отмечен построенный участниками путь, красным искомый горизонт. Подробное описание представлено ниже.
yi+1=minj-yi,i0,N-1,j,
=m|z(i,m)>z(i,m-1)z(i,m)>z(i,m+1),mm1,m2,
m1=max(1,yi-sizey),
m2=min(height-1,yi+sizey),
где:
height максимально возможное значение координаты y карты поверхности;
sizey размер окна поиска.
Метод реализован на языке Python. Время работы составило порядка 0.103 секунд, F(y, z) = 1.57, sizey= 100.
Вывод: метод достаточно прост для реализации, время работы не превышает 0.1 секунды.
image
Одно из решений, полученное глобальным экстремумом. Красная линия реальный путь, синяя полученный участником.

Перейдём к следующей группе. Как и ранее, в данном методе максимум использовался в качестве экстремума.
yi=argmax1sizex j=0sizex-1z(i+j,m),i0,N,mm1,m2,
m1=max(1,yi-sizey),
m2=min(height-1,yi+sizey),
где:
height максимально возможное значение координаты y карты поверхности;
sizex,sizey размер окна поиска.
Метод реализован на языке Python. Время работы составило порядка 0.19 секунд, F(y, z) = 1.97, sizex= 9, sizey= 21.
Вывод: метод достаточно прост для реализации, время работы не превышает 0.2 секунд.
image
Одно из решений, полученное эвристикой. Красная линия реальный путь, синяя полученный участником.

Рассмотрим последнюю группу методов. Как уже говорилось ранее, очередная координата yi+1 ищется по минимуму функционала в пределах заданного окна поиска.
Ниже представлен один из функционалов, предложенных командами. С математической точки он выглядит следующим образом:
yi+1=min(z(i,j)-z(i,yi))2max2(z)+(j-yi)2height2,i0,N-1,j,
=m|z(i,m)>z(i,m-1)z(i,m)>z(i,m+1),mm1,m2,
m1=max(1,yi-sizey),
m2=min(height-1,yi+sizey),
где:
height максимально возможное значение координаты y карты поверхности;
коэффициент, отвечающий за влияние ошибки по y на значение функционала;
sizey размер окна поиска;
max(z) наибольшее значение карты поверхности.
Метод был реализован на языке Python. Время работы составило порядка 0.12 секунд, F(y, z) = 1.58, sizey= 50, = 15000.7.
Вывод: время работы метода не превышает 0.15 секунд.
Методы всех трех групп показали достаточно близкие результаты на заданном наборе данных. Наименьшее значение метрики (1.57) было достигнуто методом, основанным на поиске локальных экстремумов значений поверхности в пределах заданного окна поиска.

Заключительная часть


К сожалению, к концу хакатона почти все новаторы перешли на темную сторону переквалифицировались и стали консервативно настроенными, то есть стали отправлять решения на классических алгоритмах и победили консерваторы.
Мы хотели объединить участников из двух областей: вычислительной математики и машинного обучения. Одни привыкли работать с неструктурированными данными неизвестной природы, другие изучать физические процессы и строить на их основе математические модели. Чтобы увеличить разнообразие идей и решений, мы кратко рассказали, как были получены данные. Это одна из причин, по которой решение на основе простых численных методов дало лучшие результаты. Второй причиной стало то, что для студенческого хакатона мы подготовили не очень сложные данные небольшого объёма, поэтому современные трудоёмкие методы машинного обучения проигрывают более простым альтернативам.
Мы считаем, что это отличный урок, который поможет участникам правильно ставить задачи и выбирать оптимальные методы для их решения. Важно помнить, что сначала стоит попробовать простое решение, так называемый бейзлайн, возможно именно он позволит достичь цели в короткие сроки.
Участниками Хакатона были предложены авторские алгоритмы нахождения оптимального пути в массиве больших данных применительно к задаче автоматической сейсмической кинематической интерпретации, которая в настоящее время решается в рамках разработки корпоративного программного обеспечения в области геологии и сейсмики. Наиболее конкурентные реализации алгоритмов найдут своё применение при разработке и реализации программных модулей данных программных комплексов.
Будем рады вас видеть на финале марафона ИТ-соревнований, который пройдёт 28 ноября онлайн. В программе: награждение победителей соревнований, презентация первой версии мобильного приложения для экспресс-оценки качества пропанта. Также в рамках мероприятия будут организованы панельные дискуссии на актуальные темы Управление данными и DS проектами и Компьютерное зрение. Интересными кейсами поделятся представители Head of Data Science Alfa, CDO Мегафон, Huawei, Head of CV X5 и др. Не пропустите всё самое интересное (Марафон ИТ-соревнований 2020 Роснефть).
Подробнее..

Как прикрутить нейросеть к сайту по быстрому

05.03.2021 16:08:13 | Автор: admin

В данном материале предлагается, приложив небольшие усилия, соединить python 3.7+flask+tensorflow 2.0+keras+небольшие вкрапления js и вывести на web-страницу определенный интерактив. Пользователь, рисуя на холсте, будет отправлять на распознавание цифры, а ранее обученная модель, использующая архитектуру CNN, будет распознавать полученный рисунок и выводить результат. Модель обучена на известном наборе рукописных цифр MNIST, поэтому и распознавать будет только цифры от 0 до 9 включительно. В качестве системы, на которой все это будет крутиться, используется windows 7.


Небольшое вступление

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

Так вышло и в этот раз. Читая Hands-On Python Deep Learning for the Web авторства Anubhav Singh, Sayak Paul, сначала все шло хорошо. Однако, после первой главы праздник закончился. Самое неприятное было то, что заявленные требования в requirements в целом соблюдались.

Масло в огонь подлили и сами разработчики пакетов tensorflow и keras. Один пакет работает только с определенным другим и, либо даунгрейд одного из них либо бубен шамана.
Но и это еще не все. Оказывается, что некоторые пакеты еще и зависимы от архитектуры используемого железа!

Так, за неимением алтернативы железа, устанавливался tensorflow 2.0 на платформу с Celeron j1900 и, как оказалось, там нет инструкции AVX2:


И вариант через pip install tensorflow не работал.
Но не все так грустно при наличии желания и интернета!

Вариант с tensorflow 2.0 удалось реализовать через wheel github.com/fo40225/tensorflow-windows-wheel/tree/master/2.0.0/py37/CPU/sse2 и установку x86: vc_redist.x86.exe, x64: vc_redist.x64.exe (http://personeltest.ru/aways/support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads).

Keras был установлен с минимальной версией, с которой он стал совместим с tensorflow Keras==2.3.0.

Поэтому
pip install tensorflow-2.0.0-cp37-cp37m-win_amd64.whl
и
pip install keras==2.3.0


Основное приложение.


Рассмотрим код основной программы.
flask_app.py
#code work with scipy==1.6.1, tensorflow @ file:///D:/python64/tensorflow-2.0.0-cp37-cp37m-win_amd64.whl,#Keras==2.3.0from flask import Flask, render_template, requestimport imageio#https://imageio.readthedocs.io/en/stable/examples.html#from scipy.misc import imread, imresize#from matplotlib.pyplot import imreadimport numpy as npimport tensorflow as tffrom tensorflow.keras.models import model_from_jsonfrom skimage import transform,iojson_file = open('model.json','r')model_json = json_file.read()json_file.close()model = model_from_json(model_json)model.load_weights("weights.h5")model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])#graph = tf.get_default_graph()graph = tf.compat.v1.get_default_graph()app = Flask(__name__)@app.route('/')def index():    return render_template("index.html")import reimport base64def convertImage(imgData1):    imgstr = re.search(r'base64,(.*)', str(imgData1)).group(1)    with open('output.png', 'wb') as output:        output.write(base64.b64decode(imgstr))@app.route('/predict/', methods=['GET', 'POST'])def predict():    global model, graph        imgData = request.get_data()    convertImage(imgData)    #print(imgData)       #x = imread('output.png', mode='L')    #x.shape    #(280, 280)    x = imageio.imread('output.png',pilmode='L')    #x = imresize(x, (28, 28))    #x = x.resize(x, (28, 28))    x = transform.resize(x, (28,28), mode='symmetric', preserve_range=True)    #(28, 28)    #type(x)    #<class 'numpy.ndarray'>    x = x.reshape(1, 28, 28, 1)    #(1, 28, 28, 1)     x = tf.cast(x, tf.float32)        # perform the prediction    out = model.predict(x)            #print(np.argmax(out, axis=1))    # convert the response to a string    response = np.argmax(out, axis=1)    return str(response[0])if __name__ == "__main__":    # run the app locally on the given port    app.run(host='0.0.0.0', port=80)# optional if we want to run in debugging mode    app.run(debug=True)



Подгрузили пакеты:
from flask import Flask, render_template, requestimport imageio#https://imageio.readthedocs.io/en/stable/examples.html#from scipy.misc import imread, imresize#from matplotlib.pyplot import imreadimport numpy as npimport tensorflow as tffrom tensorflow.keras.models import model_from_jsonfrom skimage import transform,io

Как выяснилось imread, imresize устарели еще со времен scipy==1.0. Непонятно, как у автора все работало, учитывая, что книга относительно нова (2019). С современной scipy==1.6.1 книжный вариант кода не работал.

Загружаем с диска, компилируем модель нейросети:
json_file = open('model.json','r')model_json = json_file.read()json_file.close()model = model_from_json(model_json)model.load_weights("weights.h5")model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])#graph = tf.get_default_graph()graph = tf.compat.v1.get_default_graph()

Здесь произведена замена на tf.compat.v1.get_default_graph() в виду несовместимости.

Далее часть, относящаяся к серверу на flask. Прорисовка шаблона страницы:
@app.route('/')def index():    return render_template("index.html")


Часть, преобразующая картинку в числовой массив:
import reimport base64def convertImage(imgData1):    imgstr = re.search(r'base64,(.*)', str(imgData1)).group(1)    with open('output.png', 'wb') as output:        output.write(base64.b64decode(imgstr))


Основная функция предсказания:
def predict():    global model, graph        imgData = request.get_data()    convertImage(imgData)    #print(imgData)       #x = imread('output.png', mode='L')    #x.shape    #(280, 280)    x = imageio.imread('output.png',pilmode='L')    #x = imresize(x, (28, 28))    #x = x.resize(x, (28, 28))    x = transform.resize(x, (28,28), mode='symmetric', preserve_range=True)    #(28, 28)    #type(x)    #<class 'numpy.ndarray'>    x = x.reshape(1, 28, 28, 1)    #(1, 28, 28, 1)     x = tf.cast(x, tf.float32)        # perform the prediction    out = model.predict(x)            #print(np.argmax(out, axis=1))    # convert the response to a string    response = np.argmax(out, axis=1)    return str(response[0])

Закоментированы строки, которые были заменены на рабочие, а также оставлены выводы отдельных строк для наглядности.

Как все работает.


После запуска командой python flask_app.py запускается локальный flask-сервер, который выводит index.html с вкраплением js.
Пользователь рисует на холсте цифру, нажимает predict. Картинка улетает на сервер, где сохраняется и преобразуется в цифровой массив. Далее в бой вступает CNN, распознающая цифру и возвращающая ответ в виде цифры.
Сеть не всегда дает верный ответ, т.к. обучалась всего на 10 эпохах. Это можно наблюдать, если нарисовать спорную цифру, которая может трактоваться по-разному.
*Можно покрутить слайдер, увеличивая или уменьшая толщину начертания цифры для целей распознавания.

Второй вариант программы через API,curl

.
Поользователь загружает на сервер свое изображение с цифрой для распознавания и нажимает отправить:


Заменим index.js на следующий
index.js:
$("form").submit(function(evt){evt.preventDefault();var formData = new FormData($(this)[0]);$.ajax({url: '/predict/',type: 'POST',data: formData,async: false,cache: false,contentType: false,enctype: 'multipart/form-data',processData: false,success: function (response) {$('#result').empty().append(response);}});return false;});



Шаблон страницы также изменится:
index.html
<!DOCTYPE html><html lang="en"><head><title>MNIST CNN</title></head><body><h1>MNIST Handwritten Digits Prediction</h1><form><input type="file" name="img"></input><input type="submit"></input></form><hr><h3>Prediction: <span id="result"></span></h3><scriptsrc='https://code.jquery.com/jquery-3.6.0.min.js'></script><script src="{{ url_for('static',filename='index.js') }}"></script></body></html>



Немного изменится и основная программа:
flask_app2.py
#code work with scipy==1.6.1, tensorflow @ file:///D:/python64/tensorflow-2.0.0-cp37-cp37m-win_amd64.whl,#Keras==2.3.0from flask import Flask, render_template, requestimport imageio#https://imageio.readthedocs.io/en/stable/examples.html#from scipy.misc import imread, imresize#from matplotlib.pyplot import imreadimport numpy as npimport tensorflow as tffrom tensorflow.keras.models import model_from_jsonfrom skimage import transform,iojson_file = open('model.json','r')model_json = json_file.read()json_file.close()model = model_from_json(model_json)model.load_weights("weights.h5")model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=['accuracy'])#graph = tf.get_default_graph()graph = tf.compat.v1.get_default_graph()app = Flask(__name__)@app.route('/')def index():    return render_template("index.html")import reimport base64def convertImage(imgData1):    imgstr = re.search(r'base64,(.*)', str(imgData1)).group(1)    with open('output.png', 'wb') as output:        output.write(base64.b64decode(imgstr))@app.route('/predict/', methods=['POST'])def predict():    global model, graph        imgData = request.get_data()    try:        stringToImage(imgData)    except:        f = request.files['img']        f.save('image.png')           #x = imread('output.png', mode='L')    #x.shape    #(280, 280)    x = imageio.imread('image.png',pilmode='L')    #x = imresize(x, (28, 28))    #x = x.resize(x, (28, 28))    x = transform.resize(x, (28,28), mode='symmetric', preserve_range=True)    #(28, 28)    #type(x)    #<class 'numpy.ndarray'>    x = x.reshape(1, 28, 28, 1)    #(1, 28, 28, 1)     x = tf.cast(x, tf.float32)        # perform the prediction    out = model.predict(x)            #print(np.argmax(out, axis=1))    # convert the response to a string    response = np.argmax(out, axis=1)    return str(response[0])if __name__ == "__main__":    # run the app locally on the given port    app.run(host='0.0.0.0', port=80)# optional if we want to run in debugging mode    app.run(debug=True)



Запускается все похоже python flask_app2.py

Вариант с curl (для windows)

.
Скачиваем curl

В командной строке windows отправляем команду:
curl -X POST -F img=@1.png http://localhost/predict/

где 1.png картинка с цифрой (или она же с путем к ней).
В ответ прилетит распознанная цифра.

Файлы для скачивания скачать.
Подробнее..

Перевод Как проверить подлинность банкнот с помощью нейросети

31.03.2021 16:16:13 | Автор: admin

Разработка прогнозной модели нейронной сети для нового набора данных может оказаться сложной задачей.

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

Этот процесс можно использовать для разработки эффективных моделей нейронных сетей для задач классификации и регрессионного прогнозного моделирования.

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


После прохождения этого урока вы будете знать:

  • как загрузить и обобщить набор данных о банкноте и использовать результаты, чтобы предложить подготовку данных и конфигурации модели для использования;

  • как изучить динамику обучения простых моделей MLP в наборе данных;

  • как разработать надежные оценки производительности модели, настроить производительность модели и сделать прогнозы на основе новых данных.

Давайте начнём.

Набор данных классификации банкнот

Первый шаг определить и изучить набор данных.

Мы будем работать со стандартным набором данных двоичной классификации Банкнота.

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

Набор данных содержит 1372 строки с 5 числовыми переменными. Это проблема классификации с двумя классами (двоичная классификация).

Ниже представлен список пяти переменных в наборе данных:

  • дисперсия Вейвлет-преобразованного изображения (непрерывная);

  • асимметрия Вейвлет-преобразованного изображения (непрерывная);

  • эксцесс Вейвлет-преобразованного изображения (непрерывный);

  • энтропия изображения (непрерывная);

  • класс (целое число).

Ниже приведён образец первых 5 строк набора данных.

3.6216,8.6661,-2.8073,-0.44699,04.5459,8.1674,-2.4586,-1.4621,03.866,-2.6383,1.9242,0.10645,03.4566,9.5228,-4.0112,-3.5944,00.32924,-4.4552,4.5718,-0.9888,04.3684,9.6718,-3.9606,-3.1625,0...

Вы можете посмотреть весь набор данных здесь:

Мы можем загрузить набор данных как DataFrame pandas прямо из URL-адреса.

Например:

# load the banknote dataset and summarize the shapefrom pandas import read_csv# define the location of the dataseturl = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/banknote_authentication.csv'# load the datasetdf = read_csv(url, header=None)# summarize shapeprint(df.shape)

При выполнении кода набор данных загружается непосредственно из URL-адреса и выводится форма набора данных.

В этом случае мы можем подтвердить, что набор данных имеет 5 переменных (4 входных и одна для вывода) и что набор содержит 1372 строки.

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

Это также предполагает, что использование перекрёстной проверки в k-кратном размере было бы хорошей идеей, потому что она даст более надёжную оценку производительности модели, чем разделение на обучение/тест, и потому что одна модель будет соответствовать секундам, а не часам или дням обучения.

(1372, 5)

Затем мы можем узнать больше о наборе данных, просмотрев сводную статистику и график данных.

# show summary statistics and plots of the banknote datasetfrom pandas import read_csvfrom matplotlib import pyplot# define the location of the dataseturl = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/banknote_authentication.csv'# load the datasetdf = read_csv(url, header=None)# show summary statisticsprint(df.describe())# plot histogramsdf.hist()pyplot.show()

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

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

                 0            1            2            3            4count  1372.000000  1372.000000  1372.000000  1372.000000  1372.000000mean      0.433735     1.922353     1.397627    -1.191657     0.444606std       2.842763     5.869047     4.310030     2.101013     0.497103min      -7.042100   -13.773100    -5.286100    -8.548200     0.00000025%      -1.773000    -1.708200    -1.574975    -2.413450     0.00000050%       0.496180     2.319650     0.616630    -0.586650     0.00000075%       2.821475     6.814625     3.179250     0.394810     1.000000max       6.824800    12.951600    17.927400     2.449500     1.000000

Затем для каждой переменной создаётся гистограмма.

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

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

Теперь, когда мы знакомы с набором данных, давайте посмотрим, как можно разработать модель нейронной сети.

Динамика обучения нейронной сети

Мы разработаем модель многослойного персептрона (MLP) для набора данных, используя TensorFlow.

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

С учётом того, что набор данных невелик, небольшой размер пакета, вероятно, является хорошей идеей, например, 16 или 32 строки. Использование Адамовской версии стохастического градиентного спуска хорошая идея для начала работы, так как она автоматически адаптирует скорость обучения и хорошо работает на большинстве наборов данных.

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

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

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

...# ensure all data are floating point valuesX = X.astype('float32')# encode strings to integery = LabelEncoder().fit_transform(y)

Затем мы можем разделить набор данных на переменные входа и выхода, а затем на наборы для обучения и тестирования 67/33.

...# split into input and output columnsX, y = df.values[:, :-1], df.values[:, -1]# split into train and test datasetsX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)

Мы можем определить минимальную модель MLP. В этом случае мы будем использовать один скрытый слой с 10 узлами и один выходной слой (выбранный произвольно). Мы будем использовать функцию активации ReLU в скрытом слое и инициализацию веса he_normal, поскольку их совместное использование является хорошей практикой.

Результатом этой модели является активация сигмоида для двоичной классификации, и мы минимизируем двоичную кросс-энтропийную потерю.

...# determine the number of input featuresn_features = X.shape[1]# define modelmodel = Sequential()model.add(Dense(10, activation='relu', kernel_initializer='he_normal', input_shape=(n_features,)))model.add(Dense(1, activation='sigmoid'))# compile the modelmodel.compile(optimizer='adam', loss='binary_crossentropy')

Мы подгоним модель для 50 тренировочных эпох (выбранных произвольно) с размером пакета 32, потому что это небольшой набор данных.

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

...# fit the modelhistory = model.fit(X_train, y_train, epochs=50, batch_size=32, verbose=0, validation_data=(X_test,y_test))

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

...# predict test setyhat = model.predict_classes(X_test)# evaluate predictionsscore = accuracy_score(y_test, yhat)print('Accuracy: %.3f' % score)

Наконец, мы построим кривые обучения кросс-энтропийных потерь для наборов обучения и тестовых наборов во время обучения.

...# plot learning curvespyplot.title('Learning Curves')pyplot.xlabel('Epoch')pyplot.ylabel('Cross Entropy')pyplot.plot(history.history['loss'], label='train')pyplot.plot(history.history['val_loss'], label='val')pyplot.legend()pyplot.show()

Если связать всё это вместе, полный пример оценки нашего первого MLP в наборе данных банкнот приведён ниже.

# fit a simple mlp model on the banknote and review learning curvesfrom pandas import read_csvfrom sklearn.model_selection import train_test_splitfrom sklearn.preprocessing import LabelEncoderfrom sklearn.metrics import accuracy_scorefrom tensorflow.keras import Sequentialfrom tensorflow.keras.layers import Densefrom matplotlib import pyplot# load the datasetpath = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/banknote_authentication.csv'df = read_csv(path, header=None)# split into input and output columnsX, y = df.values[:, :-1], df.values[:, -1]# ensure all data are floating point valuesX = X.astype('float32')# encode strings to integery = LabelEncoder().fit_transform(y)# split into train and test datasetsX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33)# determine the number of input featuresn_features = X.shape[1]# define modelmodel = Sequential()model.add(Dense(10, activation='relu', kernel_initializer='he_normal', input_shape=(n_features,)))model.add(Dense(1, activation='sigmoid'))# compile the modelmodel.compile(optimizer='adam', loss='binary_crossentropy')# fit the modelhistory = model.fit(X_train, y_train, epochs=50, batch_size=32, verbose=0, validation_data=(X_test,y_test))# predict test setyhat = model.predict_classes(X_test)# evaluate predictionsscore = accuracy_score(y_test, yhat)print('Accuracy: %.3f' % score)# plot learning curvespyplot.title('Learning Curves')pyplot.xlabel('Epoch')pyplot.ylabel('Cross Entropy')pyplot.plot(history.history['loss'], label='train')pyplot.plot(history.history['val_loss'], label='val')pyplot.legend()pyplot.show()

Запуск примера сначала соответствует модели в наборе обучающих данных, а затем сообщает о точности классификации в наборе тестовых данных.

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

В этом случае мы видим, что модель достигла идеальной точности в 100 % процентов. Это может означать, что задача прогнозирования проста и/или что нейронные сети хорошо подходят для этой задачи.

Accuracy: 1.000

#

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

С первой попытки у нас всё получилось на удивление хорошо.

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

Оценка робастной модели

K-кратная процедура перекрёстной проверки может обеспечить более надёжную оценку производительности MLP, хотя она может быть очень медленной.

Это связано с тем, что необходимо подобрать и оценить k моделей. Это не проблема, если размер набора данных невелик, например наш набор данных.

Мы можем использовать класс StratifiedKFold и вручную пронумеровать каждую, подогнать модель, оценить её, а затем сообщить среднее значение оценок в конце процедуры.

...# prepare cross validationkfold = KFold(10)# enumerate splitsscores = list()for train_ix, test_ix in kfold.split(X, y):# fit and evaluate the model.........# summarize all scoresprint('Mean Accuracy: %.3f (%.3f)' % (mean(scores), std(scores)))

Мы можем использовать эту структуру для разработки надёжной оценки производительности модели MLP с нашей базовой конфигурацией и даже с рядом различных подготовок данных, архитектур моделей и конфигураций обучения.

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

Если мы снова внесём в модель большие изменения, рекомендуется вернуться назад и убедиться, что модель сходится надлежащим образом.

Полный пример этой структуры для оценки базовой модели MLP из предыдущего раздела приведён ниже.

# k-fold cross-validation of base model for the banknote datasetfrom numpy import meanfrom numpy import stdfrom pandas import read_csvfrom sklearn.model_selection import StratifiedKFoldfrom sklearn.preprocessing import LabelEncoderfrom sklearn.metrics import accuracy_scorefrom tensorflow.keras import Sequentialfrom tensorflow.keras.layers import Densefrom matplotlib import pyplot# load the datasetpath = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/banknote_authentication.csv'df = read_csv(path, header=None)# split into input and output columnsX, y = df.values[:, :-1], df.values[:, -1]# ensure all data are floating point valuesX = X.astype('float32')# encode strings to integery = LabelEncoder().fit_transform(y)# prepare cross validationkfold = StratifiedKFold(10)# enumerate splitsscores = list()for train_ix, test_ix in kfold.split(X, y):# split dataX_train, X_test, y_train, y_test = X[train_ix], X[test_ix], y[train_ix], y[test_ix]# determine the number of input featuresn_features = X.shape[1]# define modelmodel = Sequential()model.add(Dense(10, activation='relu', kernel_initializer='he_normal', input_shape=(n_features,)))model.add(Dense(1, activation='sigmoid'))# compile the modelmodel.compile(optimizer='adam', loss='binary_crossentropy')# fit the modelmodel.fit(X_train, y_train, epochs=50, batch_size=32, verbose=0)# predict test setyhat = model.predict_classes(X_test)# evaluate predictionsscore = accuracy_score(y_test, yhat)print('>%.3f' % score)scores.append(score)# summarize all scoresprint('Mean Accuracy: %.3f (%.3f)' % (mean(scores), std(scores)))

Выполнение примера выводит результат о производительности модели на каждой итерации процедуры оценки и выводит среднее значение и стандартное отклонение точности классификации в конце выполнения.

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

В этом случае мы видим, что модель MLP достигла средней точности около 99,9 %.

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

Это удивительно (для меня), потому что я ожидал, что потребуется некоторое масштабирование данных и, возможно, преобразование мощности.

>1.000>1.000>1.000>1.000>0.993>1.000>1.000>1.000>1.000>1.000Mean Accuracy: 0.999 (0.002)

Затем давайте посмотрим, как мы можем подогнать окончательную модель и использовать её для прогнозов.

Окончательная модель и прогнозы

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

В этом случае мы будем использовать модель с отсевом и небольшим размером в качестве нашей окончательной модели.

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

...# split into input and output columnsX, y = df.values[:, :-1], df.values[:, -1]# ensure all data are floating point valuesX = X.astype('float32')# encode strings to integerle = LabelEncoder()y = le.fit_transform(y)# determine the number of input featuresn_features = X.shape[1]# define modelmodel = Sequential()model.add(Dense(10, activation='relu', kernel_initializer='he_normal', input_shape=(n_features,)))model.add(Dense(1, activation='sigmoid'))# compile the modelmodel.compile(optimizer='adam', loss='binary_crossentropy')

Затем мы можем использовать эту модель для прогнозирования новых данных.

Во-первых, мы можем определить ряд новых данных.

...# define a row of new datarow = [3.6216,8.6661,-2.8073,-0.44699]

Примечание: я взял эту строку из первой строки набора данных, и ожидаемая метка "0".

Затем мы можем сделать прогноз.

...# make predictionyhat = model.predict_classes([row])

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

...# invert transform to get label for classyhat = le.inverse_transform(yhat)

И в этом случае мы просто выведем прогноз.

...# report predictionprint('Predicted: %s' % (yhat[0]))

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

# fit a final model and make predictions on new data for the banknote datasetfrom pandas import read_csvfrom sklearn.preprocessing import LabelEncoderfrom sklearn.metrics import accuracy_scorefrom tensorflow.keras import Sequentialfrom tensorflow.keras.layers import Densefrom tensorflow.keras.layers import Dropout# load the datasetpath = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/banknote_authentication.csv'df = read_csv(path, header=None)# split into input and output columnsX, y = df.values[:, :-1], df.values[:, -1]# ensure all data are floating point valuesX = X.astype('float32')# encode strings to integerle = LabelEncoder()y = le.fit_transform(y)# determine the number of input featuresn_features = X.shape[1]# define modelmodel = Sequential()model.add(Dense(10, activation='relu', kernel_initializer='he_normal', input_shape=(n_features,)))model.add(Dense(1, activation='sigmoid'))# compile the modelmodel.compile(optimizer='adam', loss='binary_crossentropy')# fit the modelmodel.fit(X, y, epochs=50, batch_size=32, verbose=0)# define a row of new datarow = [3.6216,8.6661,-2.8073,-0.44699]# make predictionyhat = model.predict_classes([row])# invert transform to get label for classyhat = le.inverse_transform(yhat)# report predictionprint('Predicted: %s' % (yhat[0]))

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

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

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

Predicted: 0.0

Дальнейшее чтение

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

Вывод

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

В частности, вы узнали:

  • как загрузить и обобщить набор данных о банкноте и использовать результаты, чтобы предложить подготовку данных и конфигурации модели для использования;

  • как изучить динамику обучения простых моделей MLP в наборе данных;

  • как разработать надёжные оценки производительности модели, настроить производительность модели и сделать прогнозы на основе новых данных.

Узнайте, как прокачаться в других специальностях или освоить их с нуля:

Другие профессии и курсы
Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru