В предыдущих материалах этого цикла мы рассматривали методы предварительной обработки данных при помощи СУБД. Это может быть полезно при очень больших объемах обрабатываемой информации. В этой статье я продолжу описывать инструменты для интеллектуальной обработки больших объёмов данных, остановившись на использовании Python и Theano.
Рассмотрим задачи, которые мы будем решать, и какими инструментами для этого воспользуемся. Читать предыдущие части [1,2,3] желательно, но не обязательно. Исходные данные, которые были получены в результате выполнения предыдущих частей цикла статей можно взять отсюда[Исходные данные].
Задачи
Если среди данных есть скрытые взаимосвязи, корреляция между признаками, структурные связи, то в таком случае возникает задача уменьшения размерности входных данных. Эта проблема особенно ярко себя проявляет в ситуации, когда обрабатывается большое число разреженных данных.
Для сравнения методов уменьшения размерности данных мы собираемся рассмотреть метод нейросетевого сжатия (Autoencoder) и метод главных компонент (PCA).
Классический путь решения этой задачи — метод анализа главных компонент (PCA). В википедии неплохо описано [4], там же есть ссылка [5] на сайт с примерами в Excel, и подробным и довольно понятным описанием. Вот еще ссылки на статьи на Хабре[6,7].
Как правило, результаты уменьшения размерности входных данных, в первую очередь сравнивают именно с результатами применения этого метода.
В статье [8] описано что такое автоенкодер и как он работает, потому основной материал будет посвящен именно реализации и применению этого подхода.
Бангио и Йошуа написали хороший и внятный обзор применения нейросетевого сжатия, если вдруг кому-нибудь нужна библиография и ссылки на работы в этом направлении, чтобы сравнивать свои результаты с существующими, в работе [9], раздел 4.6, стр. 48 — 50.
В качестве инструментария, я решил применять Python и Theano[10]. Этот пакет я обнаружил после прослушивания курсов [курсера нейронные сети], изучая ссылки предоставленные преподавателем J. Hinton. При помощи этого пакета реализованы нейронные сети «глубокого обучения» (deep learning neural networks). Такого рода сети, как было описано в лекциях и статье [11, 12] легли в основу системы для распознавания голоса, используемой Google в Android.
Этот подход к построению нейронных сетей по мнению некоторых ученых является достаточно перспективным, и уже показывает неплохие результаты. Потому мне стало интересно применить его для решения задач на Kaggle.
Также, на сайте deeplearning.net достаточно много примеров построения систем машинного обучения с использованием именно этого пакета.
Инструменты
В документации разработчиков, дано такое определение:
Theano — это библиотека Python и оптимизирующий компилятор, которые позволяют определять, оптимизировать и вычислять математические выражения эффективно используя многомерные массивы
Возможности библиотеки:
- тесная интеграция с NumPy;
- прозрачное использование GPU;
- эффективное дифференцирование переменных;
- быстрая и стабильная оптимизация;
- динамическая генерация кода на C;
- расширенные возможности юнит-тестирования и самопроверок;
Theano используется в высокоинтенсивных вычислительных научных исследований с 2007 года.
По сути, программирование под Theano не является программированием в полном смысле этого слова, так как пишется программа на Python, которая создает выражение для Theano.
С другой стороны, это является программированием, так как мы объявляем переменные, создаем выражение которое говорит что делать с этими переменнымии компилируем эти выражения в функции, которые используются при вычислении.
Если кратко, то вот список того, что именно может делать Theano в отличии от NumPy.
- оптимизация скорости выполнения: Theano может использовать g++ или nvcc для того чтобы откомпилировать части части вашего выражения в инструкции GPU или CPU, которые выполняются намного быстрее чистого Python;
- диференциицирование переменных: Theano может автоматически строить выражения для вычисления градиента;
- стабильность оптимизации: Theano может распознать некоторые численно неточно вычисляемые выражения и рассчитать их используя более надежные алгоритмы
Наиболее близкий к Theano пакет это sympy. Пакет sympy можно сравнить с Mathematica, в то время как NumPy более похож на пакет MATLAB. Theano является гибридом, разработчики которого попытались взять лучшие стороны обоих этих пакетов.
Скорее всего, в продолжение цикла, будет написана отдельная статью о том как настроить theano, подключить использование CUDA GPU к тренировке нейронных сетей и решать проблемы, возникающие при установке.
А пока, на моей операционной системе Linux Slackware 13.37 установлен python-2.6, setuptools-0.6c9-py2.6, g++, python-dev, NumPy, SciPy, BLAS. Подробный мануал по установке для распространеных ОС на английском языке: [12].
Прежде чем мы начнем снижать размерность данных, попытаемся реализовать простой пример использования Theano для написания функции, которая позволит нам решить нашу задачу о разбиении группы на две части.
В качестве метода для разбиения данных на два класса воспользуемся логистической регрессией[13].
Код примера основывается на [14] с некоторыми изменениями:
import numpy
import theano
import theano.tensor as T
import csv as csv
#Открываем и читаем csv файл
csv_file_object = csv.reader(open('./titanik_train_final.csv', 'rb'), delimiter='t')
data=[] #Создаем переменную-массив
for row in csv_file_object: # Каждую строку
data.append(row) # заносим в массив data
data = numpy.array(data) # Превращаем обычный массив в тип numpy.array
data = data.astype('float') # Конвертируем в тип float
Y = data[:,1] # Все строки первого столбца заносим в Y
X = data[:,2:] # Все строки со второго столбца и до последнего заносим в X
#Аналогично для тестовой выборки, только без Y
csv_file_object = csv.reader(open('./titanik_test_final.csv', 'rb'), delimiter='t')
data_test=[]
for row in csv_file_object:
data_test.append(row)
Tx = numpy.array(data_test)
Tx = Tx.astype('float')
Tx = Tx[:,1:]
#Создаем объект для получения случайных чисел
rng = numpy.random
#Количество строк
N = 891
#Количество столбцов
feats = 56
#Количество тренировочных шагов
training_steps = 10000
# Декларируем символьные переменные Theano
x = T.matrix("x")
y = T.vector("y")
w = theano.shared(rng.randn(feats), name="w")
b = theano.shared(0., name="b")
# Создаем «выражение» Theano
p_1 = 1 / (1 + T.exp(-T.dot(x, w) - b)) # Вероятность того, что результат равен 1
prediction = p_1 > 0.5 # Порог для прогнозирования
xent = -y * T.log(p_1) - (1-y) * T.log(1-p_1) # Функция ошибки для перекрестной энтропии
cost = xent.mean() + 0.01 * (w ** 2).sum() # Стоимость минимизации
gw,gb = T.grad(cost, [w, b]) # Рассчитываем градиент стоимости
# Компилируем «выражение» Theano
train = theano.function(
inputs=[x,y],
outputs=[prediction, xent],
updates=((w, w - 0.1 * gw), (b, b - 0.1 * gb)))
predict = theano.function(inputs=[x], outputs=prediction)
# Тренировка модели
for i in range(training_steps):
pred, err = train(X, Y)
#Прогнозирование
P = predict(Tx)
#Сохраняем в файл
numpy.savetxt('./autoencoder.csv',P,'%i')
Наиболее интересными в этом коде являются строки:
# Создаем «выражение» Theano
p_1 = 1 / (1 + T.exp(-T.dot(x, w) - b)) # Вероятность того, что результат равен 1
prediction = p_1 > 0.5 # Порог для прогнозирования
xent = -y * T.log(p_1) - (1-y) * T.log(1-p_1) # Функция ошибки для перекрестной энтропии
cost = xent.mean() + 0.01 * (w ** 2).sum() # Стоимость минимизации
gw,gb = T.grad(cost, [w, b]) # Рассчитываем градиент стоимости
# Компилируем «выражение» Theano
train = theano.function(
inputs=[x,y],
outputs=[prediction, xent],
updates=((w, w - 0.1 * gw), (b, b - 0.1 * gb)))
predict = theano.function(inputs=[x], outputs=prediction)
Здесь происходит следующее(разбираем с конца !): У нас есть два «выражения» train и predict. Они «компилируются»(это не только компиляция, но в начале изучения лучше будет упростить) при помощи theano.function в вид, который может быть потом вызван и исполнен.
Входными параметрами у нас для прогнозирования является x, а выходным — выражение prediction, которое равно p_1 > 0,5 — т. е. порог, который возвращает значение да/нет, т. е. значения 0 или 1. В свою очередь, выражение p_1 содержит сведения о том, что именно нужно делать с переменной х, а именно:
p_1 = 1 / (1 + T.exp(-T.dot(x, w) - b))
где x, w, b — переменные, при этом w и b мы определяем при помощи выражения train.
Для train входными данными будут x, y, а выходными prediction и xent. При этом мы будем обновлять(искать оптимальные значения) для w и b, обновляя их по формулам
w-0.1*gw, b-0.1*gb
где gw и gb — градиенты, связанные с ошибкой xent через выражение cost.
А ошибка, вычисляемая по формуле, получается из вот такого выражения:
xent = -y * T.log(p_1) - (1-y) * T.log(1-p_1)
И когда мы «компилируем» выражения predict и train, Theano берет все необходимые выражения, создает для них код на С для CPU/GPU и исполняет их соответствующим образом. Это дает заметный прирост в производительности, при этом не лишая нас удобств использования среды Python.
Результатом исолнения указанного кода будет качество прогноза, равное 0.765555 про правилам оценивания Kaggle для соревнования Titanik.
В следующих статьях цикла мы попытаемся уменьшить размерность задачи используя разные алгоритмы, а результаты сравним.
Автор: shadoof