Здравствуйте и вновь добро пожаловать на наши занятия по теме «Свёрточные нейронные сети: глубокое обучение на языке Python, часть 3».
Мы продолжим наш пример описанием проблемы и проектом распознавания выражения лица. В этой лекции мы напишем код для свёрточной нейронной сети в TensorFlow. Перед этим мы писали код для Theano и, надо сказать, тут есть много общего.
Начинаем с импорта основных библиотек; кроме них, используем наш старый код из файла ann_tf для скрытых слоёв. Далее, мы знаем, что нам нужно определить convpool-слои и CNN с размерами convpool и скрытых слоёв. В функции main ничего менять не будем.
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from sklearn.utils import shuffle
from util import getImageData, error_rate, init_weight_and_bias, y2indicator
from ann_tf import HiddenLayer
class ConvPoolLayer(object):
def __init__(self, mi, mo, fw=5, fh=5, poolsz=(2, 2)):
class CNN(object):
def __init__(self, convpool_layer_sizes, hidden_layer_sizes):
self.convpool_layer_sizes = convpool_layer_sizes
self.hidden_layer_sizes = hidden_layer_sizes
def main():
X, Y = getImageData()
X = X.transpose((0, 2, 3, 1))
model = CNN(
convpool_layer_sizes=[(20, 5, 5), (20, 5, 5)],
hidden_layer_sizes=[500, 300],
)
model.fit(X, Y)
if __name__ == ‘__main__’:
main()
Теперь по поводу некоторых отличий от Theano. Вставим прежнюю функцию init_filter из TensorFlow. Поскольку фильтры в TensorFlow отличаются порядком определениях от таковых в Theano, убедитесь, что вы вставили нужную функцию.
def init_filter(shape, poolsz):
w = np.random.randn(*shape) / np.sqrt(np.prod(shape[:-1]) + shape[-1]*np.prod(shape[:-2] / np.prod(poolsz)))
return w.astype(np.float32)
Теперь разберёмся до конца с классом ConvPoolLayer. Не забывайте, что порядок следования параметров теперь совершенно другой – сначала ширина и высота фильтра, затем количество входных карт признаков, затем количество исходящих карт признаков.
class ConvPoolLayer(object):
def __init__(self, mi, mo, fw=5, fh=5, poolsz=(2, 2)):
sz = (fw, fh, mi, mo)
W0 = init_filter(sz, poolsz)
self.W = tf.Variable(W0)
b0 = np.zeros(mo, dtype=np.float32)
self.b = tf.Variable(b0)
self.poolsz = poolsz
self.params = [self.W, self.b]
И сразу определим функцию forward для convpool-слоя.
def forward(self, X):
conv_out = tf.nn.conv2d(X, self.W, strides=[1, 1, 1, 1], padding=’SAME’)
conv_out = tf.nn.bias_add(conv_out, self.b)
pool_out = tf.nn.max_pool(conv_out, ksize=[1, 2, 2, 1], strides=[1, 2, 2, 1], padding=’SAME’)
return tf.tanh(pool_out)
Теперь вернёмся к классу CNN. Сам класс мы уже определили, но осталась ещё функция fit со всеми параметрами. Ею и займёмся. Все переменные конвертируем во float32.
def fit(self, X, Y, lr=10e-4, mu=0.99, reg=10e-4, decay=0.99999, eps=10e-3, batch_sz=30, epochs=3, show_fig=True):
lr = np.float32(lr)
mu = np.float32(mu)
reg = np.float32(reg)
decay = np.float32(decay)
eps = np.float32(eps)
K = len(set(Y))
Вновь создадим наш проверочный набор.
X, Y = shuffle(X, Y)
X = X.astype(np.float32)
Y = y2indicator(Y).astype(np.float32)
Xvalid, Yvalid = X[-1000:], Y[-1000:]
X, Y = X[:-1000], Y[:-1000]
Yvalid_flat = np.argmax(Yvalid, axis=1)
Как вы, должно быть, уже хорошо помните, TensorFlow принимает наше X в несколько другой форме, нежели Theano – если в Theano это N, цвет, ширина фильтра, высота фильтра, то в TensorFlow это N, ширина фильтра, высота фильтра, цвет. Проследите, чтобы в функции main это было учтено.
Вернёмся к функции fit. Теперь мы инициируем convpool-слои.
N, d, d, c = X.shape
mi = c
outw = d
outh = d
self.convpool_layers = []
for mo, fw, fh in self.convpool_layer_sizes:
layer = ConvPoolLayer(mi, mo, fw, fh)
self.convpool_layers.append(layer)
outw = outw / 2
outh = outh / 2
mi = mo
И это всё, что касается convpool-слоёв. Переходим теперь к скрытым слоям.
self.hidden_layers = []
M1 = self.convpool_layer_sizes[-1][0]*outw*outh
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 = tf.Variable(W, ‘W_logreg’)
self.b = tf.Variable(b, ‘b_logreg’)
Запишем дополнительные параметры для последующего использования. Всё то же, что и в Theano.
self.params = [self.W, self.b]
for h in self.convpool_layers:
self.params += h.params
for h in self.hidden_layers:
self.params += h.params
Следующий этап – определение функций и переменных TensorFlow.
tfX = tf.placeholder(tf.float32, shape=(None, d, d, c), name=’X’)
tfY = tf.placeholder(tf.float32, shape=(None, K), name=’Y’)
act = self.forward(tfX)
rcost = reg*sum([tf.nn.l2_loss(p) for p in self.params])
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(act, tfY)) + rcost
prediction = self.predict(tfX)
Для функции обучения используем опять же метод адаптивного скользящего среднего градиента (RMSProp).
train_op = tf.train.RMSPropOptimizer(lr, decay=decay, momentum=mu).minimize(cost)
n_batches = N / batch_sz
costs = []
init = tf. initialize_all_variables()
Начинаем сессию и пишем наш основной цикл.
with tf.Session() as session:
session.run(init)
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)]
session.run(train_op, feed_dict={tfX: Xbatch, tfY: Ybatch})
Как обычно, в каждом 20 случае вычисляем значение функции затрат и прогноз нашего проверочного набора.
if j % 20 == 0:
c = session.run(cost, feed_dict={tfX: Xvalid, tfY: Yvalid})
costs.append(c)
p = session.run(prediction, feed_dict={tfX: Xvalid, tfY: Yvalid})
e = error_rate(Yvalid_flat, 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 c in self.convpool_layers:
Z = c.forward(Z)
Z_shape = Z.get_shape().as_list()
Z = tf.reshape(Z, [-1, np.prod(Z_shape[1:])])
for h in self.hidden_layers:
Z = h.forward(Z)
return tf.matmul(Z, self.W) + self.b
Остаётся лишь функция predict. Это совсем просто.
def predict(self, X):
pY = self.forward(X)
return tf.argmax(pY, 1)
Готово. Проверим программу.
Спасибо, что уделили время на изучение данного курса.
Увидимся с вами и продолжим на следующих курсах!