Этот пост является частью 12-месячного проекта ускоренного обучения Month to Master. На май: Моя цель - построить программную часть беспилотного автомобиля.

Теперь, когда у меня есть рабочий код беспилотного автомобиля (см. Вчерашнее видео), в течение следующих нескольких дней я планирую деконструировать код и попытаться понять, как именно он работает.

Сегодня я специально рассмотрю «модель», которую можно рассматривать как основную часть кода кода: модель определяет, как входные изображения преобразуются в инструкции управления.

Сегодня у меня не так много времени, поэтому я не буду подробно описывать, как работает код (поскольку я еще не знаю, и мне все еще нужно провести много исследований). Вместо этого я выскажу несколько гипотез о том, что могут означать строки кода, а затем задокументирую открытые вопросы, которые мне понадобятся для дальнейшего исследования.

Это настроит меня на структурированное изучение материала.

Вот полный код модели с автоматическим вождением. Это всего лишь 50 строк кода плюс комментарии и пробелы (что довольно странно, ведь он управляет машиной и так далее…)

import tensorflow as tf
import scipy
def weight_variable(shape):
  initial = tf.truncated_normal(shape, stddev=0.1)
  return tf.Variable(initial)
def bias_variable(shape):
  initial = tf.constant(0.1, shape=shape)
  return tf.Variable(initial)
def conv2d(x, W, stride):
  return tf.nn.conv2d(x, W, strides=[1, stride, stride, 1], padding='VALID')
x = tf.placeholder(tf.float32, shape=[None, 66, 200, 3])
y_ = tf.placeholder(tf.float32, shape=[None, 1])
x_image = x
#first convolutional layer
W_conv1 = weight_variable([5, 5, 3, 24])
b_conv1 = bias_variable([24])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1, 2) + b_conv1)
#second convolutional layer
W_conv2 = weight_variable([5, 5, 24, 36])
b_conv2 = bias_variable([36])
h_conv2 = tf.nn.relu(conv2d(h_conv1, W_conv2, 2) + b_conv2)
#third convolutional layer
W_conv3 = weight_variable([5, 5, 36, 48])
b_conv3 = bias_variable([48])
h_conv3 = tf.nn.relu(conv2d(h_conv2, W_conv3, 2) + b_conv3)
#fourth convolutional layer
W_conv4 = weight_variable([3, 3, 48, 64])
b_conv4 = bias_variable([64])
h_conv4 = tf.nn.relu(conv2d(h_conv3, W_conv4, 1) + b_conv4)
#fifth convolutional layer
W_conv5 = weight_variable([3, 3, 64, 64])
b_conv5 = bias_variable([64])
h_conv5 = tf.nn.relu(conv2d(h_conv4, W_conv5, 1) + b_conv5)
#FCL 1
W_fc1 = weight_variable([1152, 1164])
b_fc1 = bias_variable([1164])
h_conv5_flat = tf.reshape(h_conv5, [-1, 1152])
h_fc1 = tf.nn.relu(tf.matmul(h_conv5_flat, W_fc1) + b_fc1)
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
#FCL 2
W_fc2 = weight_variable([1164, 100])
b_fc2 = bias_variable([100])
h_fc2 = tf.nn.relu(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
h_fc2_drop = tf.nn.dropout(h_fc2, keep_prob)
#FCL 3
W_fc3 = weight_variable([100, 50])
b_fc3 = bias_variable([50])
h_fc3 = tf.nn.relu(tf.matmul(h_fc2_drop, W_fc3) + b_fc3)
h_fc3_drop = tf.nn.dropout(h_fc3, keep_prob)
#FCL 4
W_fc4 = weight_variable([50, 10])
b_fc4 = bias_variable([10])
h_fc4 = tf.nn.relu(tf.matmul(h_fc3_drop, W_fc4) + b_fc4)
h_fc4_drop = tf.nn.dropout(h_fc4, keep_prob)
#Output
W_fc5 = weight_variable([10, 1])
b_fc5 = bias_variable([1])
y = tf.mul(tf.atan(tf.matmul(h_fc4_drop, W_fc5) + b_fc5), 2)

Построчный комментарий

Теперь я проработаю код по частям и опишу, что, по моему мнению, означает / делает каждый фрагмент.

import tensorflow as tf
import scipy

Первые две строчки просты.

Мы импортируем библиотеку TensorFlow (которую мы будем называть «tf» в другом месте кода) и библиотеку SciPy. TensorFlow - это библиотека Python, написанная Google, которая поможет абстрагироваться от большинства реализаций машинного обучения на уровне земли. SciPy поможет с математикой.

Здесь нет ничего нового.

def weight_variable(shape):
  initial = tf.truncated_normal(shape, stddev=0.1)
  return tf.Variable(initial)
def bias_variable(shape):
  initial = tf.constant(0.1, shape=shape)
  return tf.Variable(initial)

Итак, я думаю, что здесь мы определяем новые объекты, что в основном означает, что мы можем использовать понятие «weight_variable» и «bias_variable» в другом месте нашего кода без необходимости переопределять их когда-либо один раз.

В машинном обучении функция, которую мы пытаемся решить, обычно представлена ​​как Wx + b = y, где нам даны x (список входных изображений) и y (список соответствующих инструкций управления), и мы хотим найти лучший комбинация W и b, чтобы сбалансировать уравнение.

W и b на самом деле не являются отдельными числами, а представляют собой наборы коэффициентов. Эти коллекции многомерны, и размер этих коллекций соответствует количеству узлов в сети машинного обучения. (По крайней мере, так я понимаю сейчас).

Итак, в приведенном выше коде объект weight_variable представляет W, а объект bias_variable представляет b в обобщенном смысле.

Эти объекты принимают входные данные, называемые «форма», которые в основном определяют размерность W и b.

Эти объекты W и b инициируются функцией, называемой «нормальной». Я почти уверен, что это означает, что ... когда изначально создаются наборы W и b, значения отдельных коэффициентов должны быть случайным образом назначены на основе нормального распределения (т. Е. Колоколообразной кривой) со стандартным отклонением 0,1. Стандартное отклонение более или менее определяет насколько случайными должны быть исходные коэффициенты.

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

Что мне еще нужно изучить: мне нужно больше узнать о структуре Wx + b = y, почему она используется, как работает и т. д., но я понимаю основы кода.

def conv2d(x, W, stride):
  return tf.nn.conv2d(x, W, strides=[1, stride, stride, 1], padding='VALID')

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

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

Эта конкретная характеристика определяется «ядром», которое, кажется, определяется с помощью strides = [1, stride, stride, 1] сверху. Хотя я не знаю, что означают шаги и как именно это работает.

Кажется, что у этой функции манипулирования изображением есть три входа: 1. Ядро / шаги (чтобы сказать, как манипулировать изображением); 2. x (то есть само изображение); и 3. W (который, я полагаю, представляет собой набор коэффициентов, которые используются для объединения различных манипуляций с изображениями вместе в некоторой степени).

Мне нужно больше узнать о роли В. во всем этом.

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

Что мне еще нужно изучить: Как именно функция свертки определяется математически и как W играет в этом роль?

x = tf.placeholder(tf.float32, shape=[None, 66, 200, 3])
y_ = tf.placeholder(tf.float32, shape=[None, 1])
x_image = x

Следующие несколько строк кажутся довольно простыми. Еще раз вернемся к уравнению Wx + b = y.

Здесь мы, по сути, определяем заполнители для переменных x и y. Эти заполнители устанавливают размеры переменных (помните: эти переменные представляют собой набор значений, а не просто одно число).

Мы настраиваем x, чтобы ожидать получения изображения определенных размеров, и мы настраиваем y, чтобы ожидать на выходе одно число (то есть угол поворота).

Затем мы переименовываем x в «x_image», чтобы напомнить себе, что x - это изображение, потому что… почему бы и нет.

Здесь нет ничего нового.

#first convolutional layer
W_conv1 = weight_variable([5, 5, 3, 24])
b_conv1 = bias_variable([24])
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1, 2) + b_conv1)

Хорошо, теперь мы находимся на нашем первом сверточном слое.

Мы определяем W_conv1, который является просто конкретным экземпляром переменной веса, которую я объяснил выше (с формой [5, 5, 3, 24]). Я не знаю, как и почему форма была установлена ​​именно таким образом.

Затем мы определяем b_conv1, который является просто конкретным экземпляром bias_variable, который я объяснил выше (с формой [24]). Эти 24, вероятно, должны соответствовать 24 из формы W_conv1, но я не уверен, почему (кроме этого, это поможет заставить работать матричное умножение).

h_conv1 - это промежуточный объект, который применяет функцию свертки к входам x_image и W_conv1, добавляет bconv1 к выходу свертки, а затем обрабатывает все с помощью функции, называемой relu.

Эта штука relu звучит знакомо, но я не могу точно вспомнить, что она делает. Я предполагаю, что это своего рода «скашер» или нормализующая функция, которая в некоторой степени сглаживает все, что бы это ни значило. Я должен это изучить.

Хотя я могу прочитать большую часть кода, я не совсем понимаю, почему «сверточный слой» настроен таким образом.

Что мне еще нужно изучить: что такое сверточный слой, для чего он нужен и как он это делает?

#second convolutional layer
W_conv2 = weight_variable([5, 5, 24, 36])
b_conv2 = bias_variable([36])
h_conv2 = tf.nn.relu(conv2d(h_conv1, W_conv2, 2) + b_conv2)
#third convolutional layer
W_conv3 = weight_variable([5, 5, 36, 48])
b_conv3 = bias_variable([48])
h_conv3 = tf.nn.relu(conv2d(h_conv2, W_conv3, 2) + b_conv3)
#fourth convolutional layer
W_conv4 = weight_variable([3, 3, 48, 64])
b_conv4 = bias_variable([64])
h_conv4 = tf.nn.relu(conv2d(h_conv3, W_conv4, 1) + b_conv4)
#fifth convolutional layer
W_conv5 = weight_variable([3, 3, 64, 64])
b_conv5 = bias_variable([64])
h_conv5 = tf.nn.relu(conv2d(h_conv4, W_conv5, 1) + b_conv5)

У нас есть еще четыре сверточных слоя, которые работают точно так же, как и первый слой, но вместо использования x_image в качестве входных данных они используют выходные данные предыдущего слоя (то есть элемент h_conv).

Я не уверен, как мы решили использовать пять слоев и как и почему формы каждого W_conv разные.

Что мне еще нужно изучить: почему пять слоев и как мы выбираем формы для каждого?

#FCL 1
W_fc1 = weight_variable([1152, 1164])
b_fc1 = bias_variable([1164])
h_conv5_flat = tf.reshape(h_conv5, [-1, 1152])
h_fc1 = tf.nn.relu(tf.matmul(h_conv5_flat, W_fc1) + b_fc1)
keep_prob = tf.placeholder(tf.float32)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
#FCL 2
W_fc2 = weight_variable([1164, 100])
b_fc2 = bias_variable([100])
h_fc2 = tf.nn.relu(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)
h_fc2_drop = tf.nn.dropout(h_fc2, keep_prob)
#FCL 3
W_fc3 = weight_variable([100, 50])
b_fc3 = bias_variable([50])
h_fc3 = tf.nn.relu(tf.matmul(h_fc2_drop, W_fc3) + b_fc3)
h_fc3_drop = tf.nn.dropout(h_fc3, keep_prob)
#FCL 4
W_fc4 = weight_variable([50, 10])
b_fc4 = bias_variable([10])
h_fc4 = tf.nn.relu(tf.matmul(h_fc3_drop, W_fc4) + b_fc4)
h_fc4_drop = tf.nn.dropout(h_fc4, keep_prob)

Далее у нас есть четыре FCL, которые, как мне кажется, расшифровываются как «полностью подключенные уровни».

Настройка этих слоев похожа на этапы свертки, но я не совсем понимаю, что здесь происходит. Я думаю, что это просто ванильные нейронные сети (которые я пишу, чтобы притвориться, что полностью понимаю «обычные нейронные сети»).

В любом случае, мне нужно разобраться в этом подробнее.

Что мне еще нужно изучить: что такое FCL и что происходит на каждом этапе FCL?

#Output
W_fc5 = weight_variable([10, 1])
b_fc5 = bias_variable([1])
y = tf.mul(tf.atan(tf.matmul(h_fc4_drop, W_fc5) + b_fc5), 2)

Наконец, мы берем выходные данные последнего слоя FCL, выполняем сумасшедшие тригонометрические манипуляции и затем выводим y, прогнозируемый угол поворота.

Этот шаг, кажется, просто «заставляет математику работать», но я не уверен.

Что мне еще нужно узнать: как и почему результат рассчитывается таким образом?

Сделанный.

Это заняло больше времени, чем ожидалось - в основном потому, что я смог разобрать больше, чем ожидал.

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

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

Я предполагаю, что это может быть больше искусство, чем наука, но, скорее всего, образованное искусство.

Завтра я начну вникать в свои открытые вопросы.

Прочтите следующий пост. Прочтите предыдущий пост.

Макс Дойч - навязчивый ученик, создатель продукта, подопытный кролик в Месяце до мастера и основатель Openmind.

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