Нейросети: реализация задачи про грибы на TensorFlow и Python

МЕНЮ


Искусственный интеллект
Поиск
Регистрация на сайте
Помощь проекту

ТЕМЫ


Новости ИИРазработка ИИВнедрение ИИРабота разума и сознаниеМодель мозгаРобототехника, БПЛАТрансгуманизмОбработка текстаТеория эволюцииДополненная реальностьЖелезоКиберугрозыНаучный мирИТ индустрияРазработка ПОТеория информацииМатематикаЦифровая экономика

Авторизация



RSS


RSS новости


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

З Задача

Суть задачи: на основе входных параметров гриба определить его съедобность. Специфика в том, что параметры эти категорийные, а не числовые. Например, параметр «форма шляпки» может иметь значение «плоская» или «выпуклая» или «конусообразная». Набор данных грибов для обучения сети взяты из репозитория машинного обучения. Таким образом, решение задачи можно назвать своеобразным Hello World в области машинного обучения, наряду с задачей про ирисы, где параметры цветка выражены цифровыми значениями.

И Исходники

Скачать все исходники можно из моего репозитория на Github: ссылка. Сделайте это, чтобы посмотреть код в действии. Используйте только исходники, ибо там соблюдены все нужные отступы и кодировка. Ниже весь процесс будет разобран подробно.

П Подготовка

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

И Исходник

from __future__ import absolute_import from __future__ import division from __future__ import print_function  import tensorflow as tf import numpy as np import pandas as pd from sklearn.model_selection import train_test_split  import os  # Функция отвечает за подготовку данных из репозитория. # Результатом работы являются два CSV-файла с подогнанными под TensorFlow данными для обучения и тестирования нейронной сети. В частности, категорийные параметры грибов конвертируются в числовые (0 и 1) def prepare_data(data_file_name): header = ['class', 'cap_shape', 'cap_surface', # Шапка CSV-файла в виде массива, сформирована на основе файла 'agaricus-lepiota.name' из репозитория 'cap_color', 'bruises', 'odor', 'gill_attachment', 'gill_spacing', 'gill_size', 'gill_color', 'stalk_shape', 'stalk_root', 'stalk_surface_above_ring', 'stalk_surface_below_ring', 'stalk_color_above_ring', 'stalk_color_below_ring', 'veil_type', 'veil_color', 'ring_number', 'ring_type', 'spore_print_color', 'population', 'habitat'] df = pd.read_csv(data_file_name, sep=',', names=header)  # Записи с "?" вместо параметра символизируют его отсутствие # выбрасываем эти записи из нашего набора данных df.replace('?', np.nan, inplace=True) df.dropna(inplace=True)  # Съедобность или ядовитость обозначаются в нашем наборе данных # символами 'e' или 'p' соответственно. Необходимо представить эти данные в числовом # виде, поэтому делаем 0 вместо ядовитого, 1 - вместо съедобного значения df['class'].replace('p', 0, inplace=True) df['class'].replace('e', 1, inplace=True)  # Изначально параметры грибов представлены в символьном виде, # то есть в виде слов. TensorFlow может работать только с цифровыми # данными. Библиотека Pandas с помощью функции "get_dummies" # конвертирует наши данные в цифры cols_to_transform = header[1:] df = pd.get_dummies(df, columns=cols_to_transform)  # Теперь надо разделить конвертированные данные # на два набора - один для тренировки (большой) # и один для тестирования нейросети (поменьше) df_train, df_test = train_test_split(df, test_size=0.1)  # Определяем количество строк и столбцов в каждом из наборов данных num_train_entries = df_train.shape[0] num_train_features = df_train.shape[1] - 1  num_test_entries = df_test.shape[0] num_test_features = df_test.shape[1] - 1  # Итоговые наборы записываем во временные csv-файлы, т.к. # необходимо записать количества столбцов и строк в начало шапки # рабочих csv, как того требует TensorFlow df_train.to_csv('train_temp.csv', index=False) df_test.to_csv('test_temp.csv', index=False)  # Пишем количества в тренировочный файл, затем в тестовый open("mushroom_train.csv", "w").write(str(num_train_entries) + "," + str(num_train_features) + "," + open("train_temp.csv").read())  open("mushroom_test.csv", "w").write(str(num_test_entries) + "," + str(num_test_features) + "," + open("test_temp.csv").read())  # Удаляем временные файлы, они больше не нужны os.remove("train_temp.csv") os.remove("test_temp.csv")  # Функция формирует входные данные для тестирования для TensorFlow def get_test_inputs(): x = tf.constant(test_set.data) y = tf.constant(test_set.target)  return x, y  # Функция формирует входные данные для тренировки для TensorFlow def get_train_inputs(): x = tf.constant(training_set.data) y = tf.constant(training_set.target)  return x, y  # Функция возвращает данные двух пробных грибов для # предсказания их съедобности (ожидаемый результат: съедобен, ядовит) # Иными словами, это функция для проверки обученной и протестированной нейросети def new_samples(): return np.array([[0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1]], dtype=np.int)  if __name__ == "__main__": MUSHROOM_DATA_FILE = "agaricus-lepiota.data"  # Подготавливаем данные грибов для TensorFlow, # создав два CSV-файла (тренировка и тест) prepare_data(MUSHROOM_DATA_FILE)  # Загружаем подготовленные данные training_set = tf.contrib.learn.datasets.base.load_csv_with_header( filename='mushroom_train.csv', target_dtype=np.int, features_dtype=np.int, target_column=0)  test_set = tf.contrib.learn.datasets.base.load_csv_with_header( filename='mushroom_test.csv', target_dtype=np.int, features_dtype=np.int, target_column=0)  # Определяем, что все параметры цветов имеют реальные значения (подробнее ниже) feature_columns = [tf.contrib.layers.real_valued_column("", dimension=98)]  # Создаем трехслойную DNN-нейросеть с 10, 20 и 10 нейронами в слое classifier = tf.contrib.learn.DNNClassifier( feature_columns=feature_columns, hidden_units=[10, 20, 10], n_classes=2, model_dir="/tmp/mushroom_model")  # Тренируем нейросеть classifier.fit(input_fn=get_train_inputs, steps=2000)  # Нормализуем нейросеть с помощью тестового набора данных accuracy_score = classifier.evaluate(input_fn=get_test_inputs, steps=1)["accuracy"]  print(" Точность предсказаний: {0:f} ".format(accuracy_score))  # Пробуем запустить нейросеть на двух наших пробных грибах predictions = list(classifier.predict_classes(input_fn=new_samples))  print("Предсказания съедобности пробных грибов: {} " .format(predictions))

З Загружаем и подготавливаем данные из репозитория

Данные для тренировки и тестирования нейросети будем загружать из специально созданного для этого репозитория машинного обучения. Все данные представлены в виде двух файлов: agaricus-lepiota.data и agaricus-lepiota.names. В первом 8124 строки и 22 колонки. Одна строка предоставляет один гриб, каждая колонка — один из 22-х параметров гриба в виде символа-сокращения от целого слова-параметра. Легенда всех символов находится в файле agarius-lepiota.names.

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

from __future__ import absolute_import from __future__ import division from __future__ import print_function  import tensorflow as tf import numpy as np import pandas as pd from sklearn.model_selection import train_test_split  import os

Затем сформируем шапку из параметров гриба для TensorFlow, чтобы библиотека знала, какой столбец в файле данных какому параметру соответствует. Шапка склеивается с файлом данных. Формируем в виде массива, элементы которого берем из файла agaricus-lepiota.names.

header = ['class', 'cap_shape', 'cap_surface',           'cap_color', 'bruises', 'odor', 'gill_attachment',           'gill_spacing', 'gill_size', 'gill_color', 'stalk_shape',           'stalk_root', 'stalk_surface_above_ring',           'stalk_surface_below_ring', 'stalk_color_above_ring',           'stalk_color_below_ring', 'veil_type', 'veil_color',           'ring_number', 'ring_type', 'spore_print_color',           'population', 'habitat'] df = pd.read_csv(data_file_name, sep=',', names=header)

Теперь нужно разобраться с отсутствующими данными. В этом случае в файле agaricus-lepiota.data вместо параметра выставлен символ "?". Есть много методов обработки подобных случаев, мы же будем просто удалять всю строку с хотя бы одним отсутствующим параметром.

df.replace('?', np.nan, inplace=True) df.dropna(inplace=True)

Далее необходимо вручную заменить символьный параметр съедобности на цифровой. То есть «p» и «e» заменить на 0 и 1.

df['class'].replace('p', 0, inplace=True) df['class'].replace('e', 1, inplace=True)

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

cols_to_transform = header[1:] df = pd.get_dummies(df, columns=cols_to_transform)

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

df_train, df_test = train_test_split(df, test_size=0.1)

И последнее. TensorFlow требует, чтобы в начале файлов данных были обозначены количество строк и столбцов файла. Мы вручную извлечем эту информацию из наших тренировочного и калибровочного наборов данных и затем запишем в итоговые CSV-файлы.

# Определяем количество столбцов и строк в каждом наборе num_train_entries = df_train.shape[0] num_train_features = df_train.shape[1] - 1  num_test_entries = df_test.shape[0] num_test_features = df_test.shape[1] - 1  # Пишем наборы во временные CSV df_train.to_csv('train_temp.csv', index=False) df_test.to_csv('test_temp.csv', index=False)  # Пишем полученные выше количества в итоговый CSV, затем туда же пишем временные open("mushroom_train.csv", "w").write(str(num_train_entries) +                                       "," + str(num_train_features) +                                       "," + open("train_temp.csv").read())  open("mushroom_test.csv", "w").write(str(num_test_entries) +                                      "," + str(num_test_features) +                                      "," + open("test_temp.csv").read())

В итоге должны получиться вот такие файлы: тренировочный и калибровочный.

З Забрасываем сформированные данные в TensorFlow

Теперь, когда мы загрузили из репозитория и обработали CSV-файлы с данными грибов, можно отдать их в TensorFlow на обучение. Это делается с помощью функции load_csv_with_header(), предоставляемой самим фрейворком:

training_set = tf.contrib.learn.datasets.base.load_csv_with_header(     filename='mushroom_train.csv',     target_dtype=np.int,     features_dtype=np.int,     target_column=0)  test_set = tf.contrib.learn.datasets.base.load_csv_with_header(     filename='mushroom_test.csv',     target_dtype=np.int,     features_dtype=np.int,     target_column=0)

Функция load_csv_with_header() занимается формированием тренировочного набора данных из тех файлов, что мы собрали выше. Помимо файла с данными в качестве аргумента функция принимает target_dtype, что является типом предсказываемых в итоге данных. В нашем случае необходимо научить нейросеть предсказывать съедобность либо ядовитость гриба, что можно выразить значениями 1 или 0. Таким образом, в нашем случае target_dtype — целое значение (integer). features_dtype — параметр, где задается тип принимаемых на обучение параметров. В нашем случае это также integer (изначально были string, но, как вы помните, мы перегнали их в цифру). В конце задается параметр target_column, что есть индекс колонки с параметром, который нейросети предстоит предсказывать. То есть с параметром съедобности.

С Создаем объект классификатора TensorFlow

То есть объект класса, который занимается непосредственно предсказаниями результата. Иными словами, класса самой нейросети.

feature_columns = [tf.contrib.layers.real_valued_column("", dimension=98)]  classifier = tf.contrib.learn.DNNClassifier(     feature_columns=feature_columns,     hidden_units=[10, 20, 10],     n_classes=2,     model_dir="/tmp/mushroom_model")

Первый параметр — feature_columns. Это параметры грибов. Обратите внимание, что значение параметра создается тут же, чуть выше. Там на вход принимается значение 98 параметра dimension, что значит 98 различных параметров гриба, за исключением съедобности.

hidden_units — количество нейронов в каждом слое нейросети. Правильный подбор количества слоев и нейронов в них — это что-то на уровне искусства в области машинного обучения. Верно определить данные значения можно только спустя опыт. Мы же взяли именно эти цифры просто потому что они указаны в одном из туториалов TensorFlow. И они работают.

n_classes — количество классов для предсказания. У нас их два — съедобный и нет.

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

Т Тренировка

Для простоты работы в будущем, создадим две функции:

def get_test_inputs():   x = tf.constant(test_set.data)   y = tf.constant(test_set.target)    return x, y  def get_train_inputs():   x = tf.constant(training_set.data)   y = tf.constant(training_set.target)    return x, y

Каждая функция выдает свой набор входных данных — для тренировки и для калибровки. x и y — константы TensorFlow, которые необходимы фреймвору для работы. Не вдавайтесь в подробности, просто примите, что эти функции должны быть как посредник между данными и нейросетью.

Тренируем сеть:

classifier.fit(input_fn=get_train_inputs, steps=2000)

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

Далее калибруем натренированную сеть. Это делается с помощью сформированного выше калибровочного набора данных. Результатом работы будет точность будущих предсказаний сети (accuracy_score).

accuracy_score = classifier.evaluate(input_fn=get_test_inputs,                                      steps=1)["accuracy"]  print(" Точность предсказаний: {0:f} ".format(accuracy_score))

О Опробуем в деле

Сейчас нейросеть готова, и можно попробовать предсказать с её помощью съедобность гриба.

def new_samples():     return np.array([[0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,                       1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0,                       0, 0, 1, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0,                       0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0,                       0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0,                       0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1,                       0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,                       1, 0, 1, 0, 0, 0, 0],                      [0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,                       0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1,                       0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0,                       0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0,                       0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,                       0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0,                       0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1,                       0, 0, 0, 0, 0, 0, 1]], dtype=np.int)

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

predictions = list(classifier.predict(input_fn=new_samples)) print("Предсказания съедобности пробных грибов:    {} "       .format(predictions))

Результатом работы должно быть следующее:

Предсказания съедобности пробных грибов:    [0, 1]

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


Источник: m.vk.com

Комментарии: