Содержание страницы
Основы Theano. Переменные, функции, оптимизация выражений
Здравствуйте и вновь добро пожаловать на занятия по теме «Обработка данных: практическое глубокое обучение в Theano и TensorFlow».
В этой лекции мы рассмотрим основы библиотеки Theano. Мы рассмотрим Theano достаточно подробно, чтобы вы без особых проблем смогли создать собственную нейронную сеть, а в следующей лекции мы построим нейронную сеть с использованием Theano и проверим её на наших данных. Если вы хотите получить используемый в этой лекции код, перейдите по адресу, найдите папку ann_class2 и в ней файл theano1.py.
Итак, запустим файл в Iphyton, чтобы вы могли видеть, что будет происходить. Дело в том, что множество новых идей и вещей будут работать не так, как можно было бы ожидать в обычном Phyton. Я не буду показывать, как установить Theano, поскольку это весьма несложно – просто зайдите на сайт и следуйте инструкциям. До сих пор я ни разу не слышал, чтобы у кого-то при этом возникали проблемы.
После этого первое, о чём мы поговорим, – это переменные Theano. Поскольку мы настолько часто будем использовать тензоры Theano, то будем обозначать библиотеку Theano через большое Т:
import theano.tensor as T
Мы можем создать скалярную переменную, использовав имя в качестве первого параметра:
c = T.scalar(‘c’)
То же самое мы можем сделать для вектора:
v = T.vector(‘v’)
И для матрицы:
A = T.matrix(‘A’)
В Theano есть и тензоры, размерности которых достаточно равняться трём. Вы можете столкнуться с ними, если будете работать с неплоскими изображениями вроде тех, которыми мы уже пользовались. В таком случае у нас будет вектор длиной в 784 элемента, который должен быть переформатирован в размерность 28х28, чтобы иметь возможность просматривать изображения. Если вы хотите сохранить изображения в виде квадратов и у вас есть N изображений, то получится размерность Nx28x28, что и является трёхмерным тензором. Если же у вас есть три цветовых канала, например красный, зелёный и синий, то получится тензор с размерностью Nx3x28x28, являющийся четырёхмерным тензором. В этом курсе мы не будем работать с тензорами, поэтому это всё, что я хотел о них рассказать.
Обратите внимание, что все созданные мною переменные не имеют текущих значений. Все созданные переменные являются лишь символами, но мы можем проводить над ними алгебраические операции. В качестве примера давайте проведём операцию скалярного произведения между A и v:
w = A.dot(v)
На самом деле никакой операции умножения мы не провели, поскольку она невозможна ввиду того, что ни A, ни v до сих пор не имеют никаких значений. Как же установить значения для A и v и найти результат w? Вот тут-то и вступают в игру функции Theano. Импортируем высокоуровневый модуль Theano, используя который, мы можем создавать функции Theano:
import theano
Каждое создание функции определяет входные и исходящие величины:
matrix_times_vector = theano.function(inputs=[A,v], outputs=w)
Импортируем теперь библиотеку Numpy, чтобы создать действительные массивы и вызвать функцию:
import numpy as np
A_val = np.array([[1,2], [3,4]])
v_val = np.array([5,6])
А вот так мы вызываем функцию и получаем ожидаемый результат:
w_val = matrix_times_vector(A_val, v_val)
print w_val
Пока что ничего особенного, не так ли?
Одним из больших преимуществ Theano является то обстоятельство, что эта библиотека связывает все переменные в граф, который мы можем использовать для вычисления градиента, применяя цепное правило, обсуждавшееся в первом курсе.
В Theano обычные переменные являются необновляемыми. Для создания обновляемой переменной нам необходимо создать то, что называется общей переменной (shared variable). Этим и займёмся.
x = theano.shared(20.0, ‘x’)
Здесь первым параметром является первоначальное значение переменной, а вторым – её имя.
Давайте также создадим простенькую функцию затрат, решение для которой мы можем найти самостоятельно и о которой известно, что она имеет глобальный минимум:
cost = x*x + x +1
Теперь давайте объясним Theano, как мы хотим обновить значение x, предложив выражение для обновления:
x_update = x – 0.3*T.grad(cost, x)
Что замечательно в Theano, так это то, что она автоматически вычисляет градиент. Функция grad имеет два параметра: первый – это функция, чей градиент мы хотим найти, а второй – переменная, относительно которой мы ищем градиент. При этом в качестве второго параметра вы можете подставить несколько переменных в виде списка.
Теперь давайте создадим простенькую обучающую функцию, похожую на предыдущую, но с тем исключением, что добавим в неё новый аргумент updates. Аргумент updates содержит список кортежей; каждый из кортежей включает два элемента: первый – обновляемая общая переменная, а второй – используемое для обновления выражение.
train = theano.function(inputs=□, outputs=cost, updates=[(x, x_update)])
Таким образом, в функции нет входных величин, и позже вы поймёте, почему.
Итак, мы создали функцию для обучения, но на самом деле ещё не вызвали.
Обратите внимание, что x является не входной величиной, а величиной, которая будет обновляться. В дальнейших примерах входящими величинами будут данные и метки. Таким образом, параметр inputs использует данные и метки, а параметр updates использует параметры модели с их обновлениями.
Теперь нам необходимо написать цикл для вызова обучающей функции:
for i in xrange(25):
cost_val = train()
print cost_val
Всё сошлось довольно быстро. Обратите внимание, что мы пришли к ожидаемому значению функции затрат и можем вывести на экран оптимальное значение x, использовав функцию get_value:
x.get_value()
Это значение и ожидалось.
Построение нейронной сети в Theano
Теперь настало время создать нейронную сеть в Theano, используя основы, которые мы изучили в предыдущей лекции. Если вы не хотите писать код самостоятельно, а лишь просмотреть его, перейдите по адресy и найдите папку ann_class2. Соответствующий файл называется theano2.py.
Итак, первым делом мы импортируем все обычные библиотеки, включая на этот раз и Theano, а также Matplotlib, чтобы иметь возможность построить график логарифма функции правдоподобия. Кроме того, из файла util.py импортируем функции
get_normalized_data и y2indicator.
import numpy as np
import theano
import theano.tensor as T
import matplotlib.pyplot as plt
from util import get_normalized_data, y2indicator
Далее мы определим функцию error_rate, возвращающую лишь среднее значение случаев, когда p ≠ t.
def error_rate(p, t):
return np.mean(p != t)
Новые версии библиотеки Theano имеют встроенную функцию relu, но на случай, если у вас таких версий нет, мы определим её сами.
def relu(a):
return a * (a > 0)
Обе эти функции используют значения «истина» и «ложь» и преобразуют их в числа.
Первым этапом, как обычно, идёт получение данных. Мы используем все те же параметры, которые установили в предыдущих примерах, поэтому на самом деле их можно просто скопировать из предыдущего файла.
def main():
X, Y = get_normalized_data()
max_iter = 20
print_period = 10
lr = 0.00004
reg = 0.01
Xtrain = X[:-1000,]
Ytrain = Y[:-1000]
Xtest = X[-1000:,]
Ytest = Y[-1000:]
Ytrain_ind = y2indicator(Ytrain)
Ytest_ind = y2indicator(Ytest)
N, D = Xtrain.shape
batch_sz = 500
n_batches = N // batch_sz
M = 300
K = 10
W1_init = np.random.randn(D, M) / 28
b1_init = np.zeros(M)
W2_init = np.random.randn(M, K) / np.sqrt(M)
b2_init = np.zeros(K)
Следующий этап – создание переменных и выражений Theano, причём все значения весовых коэффициентов и свободных членов будут общими переменными.
thX = T.matrix(‘X’)
thT = T.matrix(‘T’)
W1 = theano.shared(W1_init, ‘W1’)
b1 = theano.shared(b1_init, ‘b1’)
W2 = theano.shared(W2_init, ‘W2’)
b2 = theano.shared(b2_init, ‘b2’)
Вызовем также функции relu и софтмакс – Theano имеет встроенную функцию софтмакс.
thZ = relu( thX.dot(W1) + b1 )
thY = T.nnet.softmax( thZ.dot(W2) + b2 )
Кроме того, определим в Theano нашу функцию затрат, включая и выражение для регуляризации. Мы можем так записать, поскольку Theano автоматически выполняет дифференцирование и нам нет нужды проводить его вручную.
cost = -(thT * T.log(thY)).sum() + reg*((W1*W1).sum() + (b1*b1).sum() + (W2*W2).sum() + (b2*b2).sum())
Нам также понадобится переменная prediction, чтобы вычислить коэффициент ошибок.
prediction = T.argmax(thY, axis=1)
Далее запишем выражения для обновления значений весовых коэффициентов и свободных членов.
update_W1 = W1 – lr*T.grad(cost, W1)
update_b1 = b1 – lr*T.grad(cost, b1)
update_W2 = W2 – lr*T.grad(cost, W2)
update_b2 = b2 – lr*T.grad(cost, b2)
Теперь создадим функцию train, являющуюся функцией Theano. Обратите внимание, что эта функция не имеет исходящих величин.
train = theano.function(
inputs=[thX, thT],
updates=[(W1, update_W1), (b1, update_b1), (W2, update_W2), (b2, update_b2)],
)
И заодно создадим функцию get_prediction, ведь мы всегда так делаем для проверочного набора данных.
get_prediction = theano.function(
inputs=[thX, thT],
outputs=[cost, prediction],
)
Далее используем цикл из предыдущего примера, но с использованием написанных нами функций Theano, и выведём всё на экран.
LL = []
for i in xrange(max_iter):
for j in xrange(n_batches):
Xbatch = Xtrain[j*batch_sz:(j*batch_sz + batch_sz),]
Ybatch = Ytrain_ind[j*batch_sz:(j*batch_sz + batch_sz),]
train(Xbatch, Ybatch)
if j % print_period == 0:
cost_val, prediction_val = get_prediction(Xtest, Ytest_ind)
err = error_rate(prediction_val, Ytest)
print(“Cost / err at iteration i=%d, j=%d: %.3f / %.3f” % (i, j, cost_val, err))
LL.append(cost_val)
plt.plot(LL)
plt.show()
if __name__ == ‘__main__’:
main()
Итак, запустим программу. Кажется, всё работает, как ожидалось.