Здравствуйте и вновь добро пожаловать на наши занятия – «Обработка данных: практическое глубокое обучение в Theano и TensorFlow»
Мы продолжаем рассматривать наш пример с распознаванием выражения лица.
Если вы не хотите писать код самостоятельно, а сразу просмотреть его окончательную версию, зайдите в репозитарий Github и найдите файл ann_theano.py.
Как обычно, нам понадобятся библиотеки NumPy, Matplotlib и Theano. Из файла util.py мы импортируем функции getData, getBinaryData – сугубо чтобы проверить функцию на небольшом наборе данных, y2indicator, error_rate, relu – только для того случая, если у вас старая версия Theano, в которой не встроена эта функция, и init_weight_and_bias. Из sklearn.utils нам понадобится функция shuffle, чтобы перемешивать данные и не использовать каждый раз один и тот же их набор.
import numpy as np
import theano
import theano.tensor as T
import matplotlib.pyplot as plt
from util import getData, getBinaryData, y2indicator, error_rate, relu, init_weight_and_bias
from sklearn.utils import shuffle
Функция main совершенно тривиальна. Мы создадим искусственную нейронную сеть (ИНС) с 2 000 узлов в первом скрытом слое и с 1 000 узлов – во втором. Мы также создадим функцию fit, в качестве аргументов принимающую наши X и Y.
def main()
X, Y = getData()
model = ANN([2000, 1000])
model.fit(X, Y, show_fig=True)
if __name__ == ‘__main__’:
main()
Теперь создадим для скрытого слоя соответствующий класс, чтобы впоследствии было легче добавлять любое количество скрытых слоёв. При этом создадим общие переменные Theano self.W и self.b, так что каждый скрытый слой будет иметь собственные значения весовых коэффициентов и свободных членов. Функция init_weight_and_bias создаст W размерностью M1xM2 и b размерностью M2. Кроме того, определим функцию forward – в данном случае она будет использовать функцию relu.
class HiddenLayer(object):
def __init__(self, M1, M2, an_id):
self.id = an_id
self.M1 = M1
self.M2 = M2
W, b = init_weight_and_bias(M1, M2)
self.W = theano.shared(W, ‘W_%s’ % self.id)
self.b = theano.shared(b, ‘b_%s’ % self.id)
self.params = [self.W, self.b]
def forward(self, X):
return relu(X.dot(self.W) + self.b)
Это всё, что касается скрытого слоя.
Теперь мы можем определить класс, посвящённый нейронной сети. В нём определим функцию fit, имеющую множество параметров, впрочем, нам уже хорошо знакомых – коэффициент обучения, импульс, скорость затухания, количество эпох и размер пакета.
class ANN(object):
def __init__(self, hidden_layer_sizes):
self.hidden_layer_sizes = hidden_layer_sizes
def fit(self, X, Y, learning_rate=10e-7, mu=0.99, decay=0.999, reg=10e-12, epochs=400, batch_sz=100, show_fig=False):
learning_rate = np.float32(learning_rate)
Теперь определим валидационный набор данных. На случай, если вы захотите запустить код с помощью графического процессора, переведём наши данные в float32. В качестве валидационного набора оставим последние 1 000 наблюдений (примеров).
# make a validation set
X, Y = shuffle(X, Y)
X = X.astype(np.float32)
Y = Y.astype(np. float32)
Xvalid, Yvalid = X[-1000:], Y[-1000:]
X, Y = X[:-1000], Y[:-1000]
Следующий этап – инициация скрытых слоёв.
# initialize hidden layers
N, D = X.shape
K = len(set(Y))
self.hidden_layers = []
M1 = D
count = 0
for M2 in self.hidden_layer_sizes:
h = HiddenLayer(M1, M2, count)
self.hidden_layers.append(h)
M1 = M2
count += 1
W, b = init_weight_and_bias(M1, K)
self.W = theano.shared(W, ‘W_logreg’)
self.b = theano.shared(b, ‘b_logreg’)
Далее – сбор всех параметров, необходимых для градиентного спуска.
self.params = [self.W, self.b]
for h in self.hidden_layers:
self.params += h.params
Для увеличения скорости градиентного спуска нам нужен импульс.
# for momentum
dparams = [theano.shared(np.zeros(p.get_value().shape)) for p in self.params]
Теперь – код для метода адаптивного скользящего среднего градиента (RMSprop).
# for rmsprop
cache = [theano.shared(np.zeros(p.get_value().shape, dtype=np.float32)) for p in self.params]
Далее определяем наши функции и переменные Theano.
# set up theano functions and variables
thX = T.fmatrix(‘X’)
thY = T.ivector(‘Y’)
pY = self.th_forward(thX)
Сразу же определяем функцию затрат с учётом регуляризации.
rcost = reg*T.sum([(p*p).sum() for p in self.params])
cost = -T.mean(T.log(pY[T.arange(thY.shape[0]), thY])) + rcost
prediction = self.th_predict(thX)
cost_predict_op = theano.function(
inputs=[thX, thY],
outputs=[cost, prediction]
)
Теперь определяем выражения для обновления весовых коэффициентов.
updates = [
(c, decay*c + (1-decay)*T.grad(cost, p)) for p, c in zip(self.params, cache)
] + [
(p, p + mu*dp – learning_rate*T.grad(cost, p)/T.sqrt(c + 10e-10)) for p, c, dp in zip(self.params, cache, dparams)
] + [
(dp, mu*dp – learning_rate*T.grad(cost, p)/T.sqrt(c + 10e-10)) for p, c, dp in zip(self.params, cache, dparams)
]
Теперь мы можем определить функцию для обучений нейронной сети.
train_op = theano.function(
inputs=[thX, thY],
updates=updates
)
Следующий этап – реализация пакетного градиентного спуска. Значение функции затрат будем выводить на экран через каждых 20 циклов.
n_batches = N / batch_sz
costs = []
for i in xrange(epochs):
X, Y = shuffle(X, Y)
for j in xrange(n_batches):
Xbatch = X[j*batch_sz:(j*batch_sz+batch_sz)]
Ybatch = Y[j*batch_sz:(j*batch_sz+batch_sz)]
train_op(Xbatch, Ybatch)
if j % 20 == 0:
c, p = cost_predict_op(Xvalid, Yvalid)
costs.append(c)
e = error_rate(Yvalid, p)
print(“i:”, i, “j:”, j, “nb:”, n_batches, “cost:”, c, “error rate:”, e)
И после всего этого – рисуем график.
if show_fig:
plt.plot(costs)
plt.show()
Нам осталось определить лишь функции forward и predict.
def forward(self, X):
Z = X
for h in self.hidden_layers:
Z = h.forward(Z)
return T.nnet.softmax(Z.dot(self.W) + self.b)
def predict(self, X):
pY = self.th_forward(X)
return T.argmax(pY, axis=1)
Всё, можем запускать: