Tensorflow - это фреймворк, разработанный Google Brain Team для программирования потоков данных для ряда задач, который также используется для задач глубокого обучения, таких как нейронные сети.

Почему Тензор? Это способ представления данных в глубоком обучении. Тензор - это многомерный массив. Это может быть 1 размер - одно число, 2 - квадрат чисел, 3 - куб чисел или столько, сколько вы можете себе представить.

Размерность тензора - это его ранг.

В Tensorflow у нас есть константы, заполнители и переменные для определения входных данных, меток классов, весов и смещений. Константы, которые не требуют ввода, хранят постоянные значения и выводят их при необходимости; Заполнители, позволяющие вводить данные во время выполнения вычислительного графика. И Переменные, которые изменяют график таким образом, что он может производить новые выходные данные по отношению к тем же входам.

Мы собираемся работать с набором данных Fashion-MNIST от Zalando, чтобы поместить в контекст наше понимание сверточной нейронной сети.

Итак, начнем с импорта необходимых модулей, особенно модуля Tensorflow.

# Import libraries 
import numpy as np
import matplotlib.pyplot as plt 
import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data 
%matplotlib inline 
import os 
os.environ["CUDA_VISIBLE_DEVICES"]="0" #for training on gpu

Затем мы импортируем набор данных, принимая во внимание аргумент one_hot = True, который преобразует категориальные данные в вектор чисел.

data = input_data.read_data_sets('data/fashion',one_hot=True)

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

# Shapes of training set 
print("Training set (images) shape: {shape}".format(shape=data.train.images.shape)) 
print("Training set (labels) shape: {shape}".format(shape=data.train.labels.shape))  
# Shapes of test set 
print("Test set (images) shape
{shape}".format(shape=data.test.images.shape)) 
print("Test set (labels) shape: {shape}".format(shape=data.test.labels.shape))

По результатам мы выяснили, что данные для обучения и тестирования содержат соответственно 55000 и 10000 выборок, каждая из 784-мерных векторов. Вектор - это просто растянутая матрица размером 28 x 28. Он будет преобразован в матрицу размером 28x28x1, чтобы передать ее в модель CNN.

Теперь мы создадим словарь для хранения имен классов с соответствующими метками классов.

# Create dictionary of target classes 
label_dict = {  0: 'T-shirt/top',  1: 'Trouser',  2: 'Pullover',  3: 'Dress',  4: 'Coat',  5: 'Sandal',  6: 'Shirt',  7: 'Sneaker',  8: 'Bag',  9: 'Ankle boot', }

И взгляните на изображения в наборе данных.

plt.figure(figsize=[5,5])  
# Display the first image in training data 
plt.subplot(121) 
curr_img = np.reshape(data.train.images[0], (28,28)) 
curr_lbl = np.argmax(data.train.labels[0,:]) 
plt.imshow(curr_img, cmap='gray') 
plt.title("(Label: " + str(label_dict[curr_lbl]) + ")")  
# Display the first image in testing data 
plt.subplot(122) 
curr_img = np.reshape(data.test.images[0], (28,28)) 
curr_lbl = np.argmax(data.test.labels[0,:]) 
plt.imshow(curr_img, cmap='gray') 
plt.title("(Label: " + str(label_dict[curr_lbl]) + ")")

Масштаб изображений уже изменен между 0 и 1, поэтому нет необходимости изменять их масштаб. Мы можем их визуализировать, а также проверить максимальное и минимальное значение матрицы.

print(data.train.images[0])
print(np.max(data.train.images[0]))
print(np.min(data.train.images[0]))

Измените форму изображений до размера 28 x 28 x 1, чтобы их можно было использовать в качестве входных данных в сеть.

# Reshape training and testing image 
train_X = data.train.images.reshape(-1, 28, 28, 1) 
test_X = data.test.images.reshape(-1,28,28,1)
train_X.shape, test_X.shape

Поместите ярлыки для обучения и тестирования в отдельные переменные и распечатайте их соответствующие формы.

train_y = data.train.labels 
test_y = data.test.labels
train_y.shape, test_y.shape

Для сверточной нейронной сети мы будем использовать три сверточных слоя: первый слой с фильтрами 32–3x3, второй слой с фильтрами 64–3x3 и третий фильтр 128–3x3. Кроме того, будет три слоя max-pooling размером 2x2 каждый.

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

training_iters = 200
learning_rate = 0.001
batch_size = 128

Для сетевых параметров мы сначала определяем количество входов - матрицу 28x28x1, которая является измененным 784-мерным вектором, - а затем количество классов, которое является просто количеством меток классов.

# MNIST data input (img shape: 28*28) 
n_input = 28 
# MNIST total classes (0–9 digits) 
n_classes = 10

Мы определим наши заполнители. Напомним, что они позволяют нам выполнять операции и строить наши вычислительные графы, не загружая в них данные. Итак, у нас будет входной заполнитель x с размером None x 784 и выходной заполнитель с размером None x 10. y, на котором находится метка обучающих изображений, будет иметь размер Нет x 10. Измерение строки - Нет, потому что мы определили размер пакета, который сообщает заполнителям, что они получат это измерение в то время, когда мы будем передавать им данные.

#both placeholders are of type float 
x = tf.placeholder("float", [None, 28,28,1]) 
y = tf.placeholder("float", [None, n_classes])

Хорошо определить функции свертки и максимального объединения, которые мы можем вызывать несколько раз, когда они нам нужны в нашей сети.

Функция свертки conv2d () имеет четыре аргумента: ввод x, веса W, смещение b, и шаги. Последний аргумент установлен в 1, но может иметь любое значение; первый и последний шаг всегда должны быть 1, потому что первый - для номера изображения, а последний - для входного канала. После применения свертки мы добавим смещение и применим функцию активации, выпрямленную линейную единицу (ReLU).

Функция максимального объединения, maxpool2d (), имеет вход x, и размер ядра, k, равным 2, что означает, что Фильтр max-pooling будет квадратной матрицей с размерами 2 x 2, и шаг, с которым фильтр будет перемещаться, также будет 2. Ваш padding будет равен ТАКОМУ. Это гарантирует, что при выполнении операций свертки граничные пиксели изображения не останутся без внимания; он в основном добавляет нули на границах ввода и позволяет фильтру свертки также получать доступ к граничным пикселям. То же самое с операциями max-pooling - добавляются нули. Когда веса и смещения определены, мы заметим, что вход размером 28x28 субдискретизируется до 4x4 после применения трех слоев максимального объединения.

def conv2d(x, W, b, strides=1):     
   # Conv2D wrapper, with bias and relu activation     
   tf.nn.conv2d(x, W, strides=[1, strides, strides, 1],      padding='SAME')     
   x = tf.nn.bias_add(x, b)     
   return tf.nn.relu(x)   
def maxpool2d(x, k=2):     
   return tf.nn.max_pool(x, ksize=[1, k, k, 1], strides=[1, k, k,      1],padding='SAME')
  

После определения оболочек conv2d () и maxpool2d () теперь вы можете определять свои веса и смещения. переменные. Вы создадите два словаря: один для весов, а другой - для смещений. Напомним, что первый слой свертки имеет фильтры 32–3x3, поэтому первый ключ, wc1 в словаре веса, имеет форму аргумента, которая принимает кортеж из 4 значений: первое и второй - размер фильтра, третий - количество каналов во входном изображении, а последний представляет количество сверточных фильтров, которые вы хотите использовать в сверточном слое. Первый ключ в словаре biases, bc1, будет иметь 32 параметра смещения. Точно так же второй ключ wc2 словаря weights имеет параметр формы, который принимает кортеж из 4 значений: первое и второе относятся к размеру фильтра, третье количество каналов из предыдущего ввода. Поскольку мы передали 32 сверточных фильтра на входное изображение, у нас будет 32 канала на выходе из операции первого сверточного слоя. Последнее значение представляет количество фильтров, которые вы хотите включить в фильтр свертки. Обратите внимание, что второй ключ в словаре смещений, bc2, будет иметь 64 параметра.

Повторите то же самое для третьего сверточного слоя. Но постарайтесь понять четвертый ключ, wd1. После применения 3 операций свертки и максимального объединения вы понижаете дискретизацию входного изображения с 28 x 28 x 1 до 4 x 4 x 1, и вам необходимо сгладить этот субдискретизированный вывод, чтобы передать его в качестве ввода на полностью подключенный слой. Вот почему вы должны выполнить операцию умножения, которая является результатом предыдущего слоя или количества каналов, которые выводятся слоем свертки 3. Второй элемент кортежа, который вы передали в shape, имеет количество нейронов, которое вы хотите. в полностью связанном слое. Точно так же в словаре biases четвертый ключ, bd1, имеет 128 параметров.

weights = { 'wc1': tf.get_variable('W0', shape=(3,3,1,32), initializer=tf.contrib.layers.xavier_initializer()),
            'wc2': tf.get_variable('W1', shape=(3,3,32,64), initializer=tf.contrib.layers.xavier_initializer()),      
            'wc3': tf.get_variable('W2', shape=(3,3,64,128), initializer=tf.contrib.layers.xavier_initializer()),      
            'wd1': tf.get_variable('W3', shape=(4*4*128,128), initializer=tf.contrib.layers.xavier_initializer()),      
            'out': tf.get_variable('W6', shape=(128,n_classes), initializer=tf.contrib.layers.xavier_initializer()),  
} 

biases = {  'bc1': tf.get_variable('B0', shape=(32), initializer=tf.contrib.layers.xavier_initializer()),     
            'bc2': tf.get_variable('B1', shape=(64), initializer=tf.contrib.layers.xavier_initializer()),     
            'bc3': tf.get_variable('B2', shape=(128), initializer=tf.contrib.layers.xavier_initializer()),     
            'bd1': tf.get_variable('B3', shape=(128), initializer=tf.contrib.layers.xavier_initializer()),     
            'out': tf.get_variable('B4', shape=(10), initializer=tf.contrib.layers.xavier_initializer()), 
}

Теперь давайте посмотрим на сетевую архитектуру. Функция conv_net () принимает 3 аргумента: входные, x, словари, веса и смещения. . Сначала вы изменяете 784-мерный входной вектор в матрицу 28x28x1, -1 в reshape (), что означает, что первое измерение будет выведено самостоятельно, но остальные фиксированы - 28 x 28 x 1. Затем мы определим conv1, который принимает входные данные как изображение, весит wc1 и смещает bc1. Затем мы применяем max-pooling к выходным данным conv1 и будем продолжать это до conv3. После прохождения всех слоев свертки и максимального объединения мы сгладим выходные данные conv3, а затем соединим сглаженные нейроны conv3 с каждым нейроном следующего слоя. . Затем мы применим функцию активации к выходу полностью подключенного слоя fc1. Наконец, на последнем слое у нас будет 10 нейронов, так как мы должны классифицировать 10 меток. Это означает, что мы соединим все нейроны fc1 в выходном слое с 10 нейронами в последнем слое.

def conv_net(x, weights, biases):        
    # here we call the conv2d function we had defined above and pass the input image x, weights wc1 and bias bc1.     
    conv1 = conv2d(x, weights['wc1'], biases['bc1'])     
    # Max Pooling (down-sampling), this chooses the max value from a 2*2 matrix window and outputs a 14*14 matrix.     
    conv1 = maxpool2d(conv1, k=2)      

    # Convolution Layer     
    # here we call the conv2d function we had defined above and pass the input image x, weights wc2 and bias bc2.     
    conv2 = conv2d(conv1, weights['wc2'], biases['bc2'])     
    # Max Pooling (down-sampling), this chooses the max value from a 2*2 matrix window and outputs a 7*7 matrix.     
    conv2 = maxpool2d(conv2, k=2)      
    
    conv3 = conv2d(conv2, weights['wc3'], biases['bc3'])     
    # Max Pooling (down-sampling), this chooses the max value from a 2*2 matrix window and outputs a 4*4.     
    conv3 = maxpool2d(conv3, k=2)       
    
    # Fully connected layer     
    # Reshape conv2 output to fit fully connected layer input
    fc1 = tf.reshape(conv3, [-1,weights['wd1'].get_shape().as_list()[0]])     
    fc1 = tf.add(tf.matmul(fc1, weights['wd1']), biases['bd1'])
    fc1 = tf.nn.relu(fc1)     
    # Output, class prediction     
    # finally we multiply the fully connected layer with the weights and add a bias term.      
    out = tf.add(tf.matmul(fc1, weights['out']), biases['out']) 
    return out

Затем мы создаем модель и вызываем функцию conv_net (), передавая входные данные x, веса и смещения . Проблема заключается в классификации нескольких классов, мы будем использовать активацию softmax на выходном слое, чтобы дать нам вероятности для каждой метки класса, функция потерь - это кросс-энтропия. Почему кросс-энтропия как функция потерь? Поскольку значение функции кросс-энтропии всегда положительно и стремится к нулю по мере того, как нейрон лучше вычисляет желаемый результат, y, для всех обучающих входных данных, x. Это позволяет избежать проблемы замедления обучения, что означает, что если веса и смещения инициализированы неправильно, даже в этом случае это помогает быстрее восстанавливаться и не сильно мешает фазе обучения. И активация, и функции потерь кросс-энтропии определены в одной строке, здесь, в Tensorflow. Мы передаем два параметра, которые затем представляют собой прогнозируемый результат и метку истинности y. Затем мы возьмем среднее значение reduce_mean () по всем пакетам, чтобы получить единое значение потерь / затрат. Затем мы определяем оптимизатор Adam, популярный алгоритм оптимизации, а затем указываем скорость обучения, явно указав минимальную стоимость, которую мы рассчитали на предыдущем шаге.

pred = conv_net(x, weights, biases)  
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(
logits=pred, labels=y))  
optimizer = tf.train.AdamOptimizer(
learning_rate=learning_rate).minimize(cost)

Ух! Мы уже прошли долгий путь.

Но мы скоро с этим покончим. Поверьте мне.

Теперь протестируем нашу модель. Мы определим еще два узла: правильное_предсказание и точность. Это будет оценивать нашу модель после каждой итерации обучения, что поможет нам отслеживать производительность нашей модели.

#Here you check whether the index of the maximum value of the predicted image is equal to the actual labelled image. and both will be a column vector. 
correct_prediction = tf.equal(tf.argmax(pred, 1), tf.argmax(y, 1)) 
#calculate accuracy across all the given images and average them out.  
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

Имейте в виду, что наши веса и смещения являются переменными и что мы должны инициализировать их, прежде чем мы сможем их использовать. Итак, давайте сделаем это.

# Initializing the variables 
init = tf.global_variables_initializer()

Теперь при обучении и тестировании нашей модели в Tensorflow мы выполним следующие шаги: мы начнем с запуска графа, класса, который выполняет все операции Tensorflow и запускает граф в сеансе. Все операции должны выполняться в пределах отступа; затем мы запускаем сеанс, который выполнит переменные, которые были инициализированы на предыдущем шаге, и оценивает тензор; после этого мы определяем цикл for, который выполняется для количества итераций обучения, которые мы указали в начале. Сразу после этого мы инициируем второй цикл for, который определяет количество пакетов, которые у нас будут в зависимости от выбранного размера пакета, поэтому мы делим общее количество изображений на размер пакета. Затем мы введем изображения на основе размера пакета, который мы передаем в batch_x, и их соответствующих ярлыков в batch_y. Решающий шаг - точно так же, как мы запустили инициализатор после создания графика, теперь мы загрузим заполнители x, и y фактическими данными в словарь и запустим сеанс, передача стоимости и точности, которые мы определили ранее. Возвращает потерю (стоимость) и точность. Мы можем распечатать потери и точность обучения после завершения каждой эпохи, то есть каждой итерации обучения. После завершения каждой итерации обучения мы проверяем только точность, передавая все 10 000 тестовых изображений и меток, чтобы дать нам представление о том, насколько точно наша модель работает во время обучения. Рекомендуется тестировать после того, как наша модель полностью обучена, и проверять ее только тогда, когда она находится в фазе обучения после каждой эпохи.

with tf.Session() as sess:     
     sess.run(init)      
     train_loss = []     
     test_loss = []     
     train_accuracy = []     
     test_accuracy = []     
     summary_writer = tf.summary.FileWriter('./Output', sess.graph) 
     for i in range(training_iters):         
         for batch in range(len(train_X)//batch_size): 
             batch_x = train_X[batch*batch_size:
             min((batch+1)*batch_size,len(train_X))] 
             batch_y = train_y[batch*batch_size:
             min((batch+1)*batch_size,len(train_y))] 
             # Run optimization op (backprop).     
                 # Calculate batch loss and accuracy             
             opt = sess.run(optimizer, feed_dict={x: batch_x,
                                                         y:batch_y})             
             loss, acc = sess.run([cost, accuracy], feed_dict 
                                                  {x:batch_x, 
                                                       y: batch_y})         
         print("Iter " + str(i) + ", Loss= " + \    
                        "{:.6f}".format(loss) + ", Training 
                        Accuracy= " + \                       
                        "{:.5f}".format(acc)) 
         print("Optimization Finished!") 
         
         # Calculate accuracy for all 10000 mnist test images  
         test_acc,valid_loss = sess.run([accuracy,cost], 
         feed_dict={x: test_X,y : test_y}) 
         train_loss.append(loss)        
         test_loss.append(valid_loss)  
         train_accuracy.append(acc) 
         test_accuracy.append(test_acc)         
         print("Testing Accuracy:","{:.5f}".format(test_acc))  
     summary_writer.close()

По результатам обучения и тестирования точность теста выглядит впечатляюще. Не так уж и плохо для начала.

На этом заканчивается наше первое знакомство с миром сверточных нейронных сетей.

Ссылку на код можно найти в моем Github. Мы высоко ценим ваши отзывы об улучшении модели за счет уменьшения переобучения и повышения точности тестирования.