1 мая 2017 года я задал себе вопрос: Могу ли я изучить необходимые компьютерные науки для создания программной части беспилотного автомобиля за один месяц?

22 мая 2017 года, после 26 часов обучения и программирования, я узнал, что ответ был положительным.

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

Сегодня у меня начинается новый месяц и новая задача: Могу ли я изучить необходимые компьютерные науки для создания программной части беспилотного автомобиля за один месяц?

Определение успеха

Чтобы полностью построить беспилотный автомобиль, мне нужно было бы создать 1. Программное обеспечение для беспилотного автомобиля и 2. Аппаратное обеспечение для беспилотного автомобиля. Программное обеспечение будет использовать датчик и входные данные для алгоритмической генерации инструкций по вождению, а оборудование будет выполнять эти инструкции вождения в реальном автомобиле.

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

В частности, я хочу создать программное обеспечение для самостоятельного вождения, которое может делать две вещи:

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

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

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

Моя отправная точка

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

Во-первых, у меня степень Брауна по математике, что очень полезно для этой конкретной области информатики. У меня также есть общий опыт программирования / информатики, который, безусловно, мне поможет.

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

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

В этом месяце я сильно сосредоточен на применении, а не на теории, и на компьютерном зрении, в котором у меня нет опыта.

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

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

Линейный метод обучения

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

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

Я называю это линейным методом обучения.

Использование линейного метода для изучения информатики самоуправляемых автомобилей будет выглядеть примерно так:

  1. Изучите многомерное исчисление
  2. Изучите линейную алгебру
  3. Изучите основы компьютерных наук
  4. Узнайте об общих концепциях машинного обучения
  5. Узнайте о концепциях компьютерного зрения
  6. Узнайте, как программировать на Python (язык кодирования, обычно используемый для машинного обучения)
  7. Узнайте, как использовать TensorFlow (специальная библиотека машинного обучения для Python)
  8. Узнайте, как компьютерное зрение применяется для создания программного обеспечения для беспилотных автомобилей.
  9. Узнайте, как писать код Python и TensorFlow для создания соответствующих программ.
  10. Так далее…

Хотя со временем этот метод может сработать, он неэффективен и, вероятно, неэффективен.

Во-первых, если я начну с изучения многомерного исчисления, как мне узнать, какие части многомерного исчисления относятся к беспилотным автомобилям, а какие - нет? Я не. Так что мне придется все это выучить. То же самое для линейной алгебры, основ информатики и т. Д.

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

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

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

V-метод обучения

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

Вот как работает V-метод обучения:

  1. Я начинаю с конкретного, хорошо задокументированного примера моей конечной цели.
  2. Я пытаюсь понять, как работает этот пример
  3. Все, что мне непонятно в этом примере, я исследую лежащие в основе концепции.
  4. Если я не понимаю лежащих в основе концепций, я исследую основные концепции лежащих в основе концепций, пока не почувствую, что исчерпал этот путь (либо достигнув понимания, либо достигнув точки убывающей отдачи).
  5. В конце концов, я раскладываю по плану достаточно разных путей, чтобы начать видеть закономерности в важных базовых концепциях.
  6. Я изучаю эти соответствующие основные концепции, медленно продвигаясь вверх по цепочке знаний, пока не вернусь на уровень детализации исходного примера.
  7. Наконец, я воспроизвожу пример, основанный на моих новых иерархических знаниях.

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

V-метод намного более эффективен, чем линейный метод, потому что я могу: 1. Изучать в порядке соответствия моей конечной цели, 2. Изучать основополагающие концепции в контексте чего-то осязаемого, и 3. Создавать и организовывать мои знания иерархически взаимосвязаны.

В результате этот метод намного эффективнее, действеннее и увлекательнее.

Итак, вот как я планирую применить V-метод к задаче этого месяца:

  1. Поищите образец кода для самоуправляемой машины с открытым исходным кодом на Github (Github - популярный репозиторий кода, что в основном означает, что я могу найти там множество программных проектов других людей)
  2. Построчно прорабатываю код
  3. Для каждой строчки кода, которую я не понимаю на интуитивном уровне (а это будет большинство из них), начинайте свой спуск через слои базовых концепций.
  4. Выявить закономерности в том, что я постоянно ищу / исследую, и определять наиболее важные фундаментальные концепции
  5. Изучите эти основополагающие концепции
  6. Работайте над слоями базовых концепций, пока я не смогу эффективно объяснить себе каждую строку кода из образца проекта Github.

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

Мой первый шаг - поискать на Github хороший образец проекта…

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

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

В поисках кода

Немного погуглив, я нашел на Github проект, который хорошо соответствовал моим потребностям. Код берет входное изображение дороги и пытается определить, где проходят полосы движения.

Итак, отсюда…

К этому…

Моя цель на сегодня состояла в том, чтобы попытаться воспроизвести этот результат с помощью кода, запущенного на моем собственном компьютере.

Подготовка к настройке

Прежде чем я смог запустить какой-либо код, мне нужно было убедиться, что мой компьютер настроен с соответствующими библиотеками программного обеспечения. В частности, мне нужно было установить библиотеки numpy, matplotlib и OpenCV для Python.

После того, как я сориентировался в Терминале (командная строка на Mac) и нашел некоторые инструкции в Интернете, я столкнулся с моей первой ошибкой ...

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

Я перешел по третьей ссылке и нашел такой ответ:

После выполнения этих нескольких команд (путем копирования и вставки их в Терминал и нажатия «Enter») все, казалось, работало правильно.

Я был официально настроен (по крайней мере, на данный момент).

Запуск кода

Теперь, когда я был настроен, пришло время запустить код. После того, как я снова использовал Google, чтобы пополнить свои ограниченные знания о Терминале, я получил код для запуска, и, похоже, ничего не сломалось.

Получил этот вывод…

Прохладный! Итак, эти числа, по сути, являются математическим представлением двух полос движения.

Все идет нормально. Но где визуальные эффекты?

В проекте Github, который я пытался воспроизвести, код также выводил эти красивые графики ...

А также изображение с красными накладками…

К сожалению, мой код не выводил ни одного из них, и при этом он не сохранял никаких изображений в моем локальном каталоге.

Итак, я снова обратился к Google и поискал «save image python» в надежде выяснить, как сохранить изображение на выходе.

Google любезно посоветовал мне использовать функцию cv2.imwrite (), что я и сделал, и это сработало. И под «проработано» я имею в виду… Мне удалось сохранить изображение фотографии в оттенках серого с линиями полос, визуализированными в белом цвете.

А вот еще ...

И еще один…

Что теперь?

Это хорошее начало.

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

Завтра, поскольку код более или менее работает, я постараюсь построчно пройтись по проекту и начать выяснять, как он на самом деле работает.

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

Конечно, у меня еще нет четкого концептуального понимания, но теперь у меня есть функциональный пример, который я могу использовать в качестве отправной точки.

Вчера я придумал, как определять полосы движения на изображении дороги лицом вперед. Что ж ... Я по крайней мере понял, как запустить код, который мог бы это сделать.

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

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

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

def draw_lane_lines(image):
imshape = image.shape
    
    # Greyscale image
    greyscaled_image = grayscale(image)
    
    # Gaussian Blur
    blurred_grey_image = gaussian_blur(greyscaled_image, 5)
    
    # Canny edge detection
    edges_image = canny(blurred_grey_image, 50, 150)
    
    # Mask edges image
    border = 0
    vertices = np.array([[(0,imshape[0]),(465, 320), (475, 320), 
    (imshape[1],imshape[0])]], dtype=np.int32)
    edges_image_with_mask = region_of_interest(edges_image, 
    vertices)
    
    # Hough lines
    rho = 2 
    theta = np.pi/180 
    threshold = 45    
    min_line_len = 40
    max_line_gap = 100 
    lines_image = hough_lines(edges_image_with_mask, rho, theta,  
    threshold, min_line_len, max_line_gap)
# Convert Hough from single channel to RGB to prep for weighted
    hough_rgb_image = cv2.cvtColor(lines_image, cv2.COLOR_GRAY2BGR)
 
    # Combine lines image with original image
    final_image = weighted_img(hough_rgb_image, image)
    
    return final_image

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

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

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

Таким образом, основываясь на своем сегодняшнем исследовании, я попытаюсь объяснить следующую последовательность событий обработки изображения: Входное изображение → 1. Изображение в градациях серого, 2. Размытие по Гауссу, 3. Обнаружение контуров, 4. Изображение краев маски, 5. Горизонтальные линии → Выходные линии с переулками

Входное изображение

Вот начальное изображение для ввода.

Важно помнить, что изображение - это не что иное, как набор пикселей, расположенных в прямоугольнике. Этот конкретный прямоугольник составляет 960 пикселей на 540 пикселей.

Значение каждого пикселя представляет собой некоторую комбинацию красного, зеленого и синего цветов и представлено тройкой чисел, где каждое число соответствует значению одного из цветов. Значение каждого из цветов может находиться в диапазоне от 0 до 255, где 0 - полное отсутствие цвета, а 255 - 100% -ная интенсивность.

Например, белый цвет представлен как (255, 255, 255), а черный цвет представлен как (0, 0, 0).

Итак, это входное изображение можно описать 960 x 540 = 518 400 тройками чисел в диапазоне от (0, 0, 0) до (255, 255, 255).

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

1. Изображение в оттенках серого.

Первым шагом обработки является преобразование цветного изображения в оттенки серого, эффективно понижая цветовое пространство с трехмерного до одномерного. Гораздо проще (и эффективнее) манипулировать изображением только в одном измерении: это одно измерение - это «темнота» или «интенсивность» пикселя, где 0 представляет черный, 255 представляет белый, а 126 представляет некоторый средний серый цвет. .

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

Например, вот цвет неба на исходной фотографии:

Его можно представить в пространстве RGB (красный, зеленый, синий) как (120, 172, 209).

Если я усредню эти значения вместе, я получу (120 + 172 + 209) / 3 = 167, или этот цвет в пространстве оттенков серого.

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

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

В одном из распространенных методов, называемых колометрическим преобразованием, используется эта взвешенная сумма: 0,2126 красный + 0,7152 зеленый + 0,0722 синий.

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

2. Размытие по Гауссу

Следующим шагом является размытие изображения с помощью Gaussian Blur.

Применяя небольшое размытие, мы можем удалить самую частую информацию (также известную как шум) из изображения, что даст нам «более плавные» цветовые блоки, которые мы сможем анализировать.

Опять же, математика, лежащая в основе размытия по Гауссу, очень проста: размытие просто требует большего количества средних пикселей (этот процесс усреднения представляет собой тип свертки ядра, что является излишне причудливым названием для того, что я ' м собираюсь объяснить).

В основном, чтобы создать размытие, вы должны выполнить следующие шаги:

  1. Выберите пиксель на фотографии и определите его значение.
  2. Найдите значения для локальных соседей выбранного пикселя (мы можем произвольно определить размер этой «локальной области», но обычно она довольно мала)
  3. Возьмите значение исходного пикселя и соседних пикселей и усредните их вместе, используя некоторую систему взвешивания.
  4. Заменить значение исходного пикселя выведенным усредненным значением
  5. Сделайте это для всех пикселей

По сути, этот процесс означает «сделать все пиксели более похожими на пиксели поблизости», что интуитивно звучит как размытие.

Для размытия по Гауссу мы просто используем распределение по Гауссу (т. Е. Колоколообразную кривую) для определения весов на шаге 3 выше. Это означает, что чем ближе пиксель к выбранному пикселю, тем больше его вес.

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

3. Обнаружение хитрых краев

Теперь, когда у нас есть изображение с оттенками серого и размытым по Гауссу, мы попытаемся найти все края на этой фотографии.

Край - это просто область изображения, где происходит внезапный скачок значения.

Например, между серой дорогой и пунктирной белой линией есть четкая граница, поскольку серая дорога может иметь значение примерно 126, белая линия имеет значение, близкое к 255, и постепенного перехода между этими значениями нет. .

Опять же, фильтр Canny Edge Detection использует очень простую математику для поиска ребер:

  1. Выберите пиксель на фото
  2. Определите значение для группы пикселей слева и группы пикселей справа от выбранного пикселя.
  3. Возьмите разницу между этими двумя группами (т.е. вычтите значение одной из других).
  4. Измените значение выбранного пикселя на значение разницы, вычисленное на шаге 3.
  5. Сделайте это для всех пикселей.

Итак, представьте, что мы смотрим только на один пиксель слева и справа от выбранного пикселя, и представьте, что это значения: (Левый пиксель, выбранный пиксель, правый пиксель) = (133, 134, 155). Затем мы вычислили бы разницу между правым и левым пикселями, 155–133 = 22, и установили бы новое значение выбранного пикселя равным 22.

Если выбранный пиксель является краем, разница между левым и правым пикселями будет большим (ближе к 255) и, следовательно, будет отображаться как белый цвет на выводимом изображении. Если выбранный пиксель не является краем, разница будет близка к 0 и будет отображаться черным цветом.

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

Эти различия называются градиентами, и мы можем вычислить общий градиент, по сути, используя теорему Пифагора для сложения отдельных вкладов вертикальных и горизонтальные градиенты. Другими словами, мы можем сказать, что общий градиент² = вертикальный градиент² + горизонтальный градиент².

Так, например, допустим, вертикальный градиент = 22 и горизонтальный градиент = 143, тогда общий градиент = sqrt (22² + 143²) = ~ 145.

Результат выглядит примерно так…

Фильтр Canny Edge Detection теперь выполняет еще один шаг.

Фильтр Canny Edge Detection не просто показывает все края, а пытается идентифицировать важные края.

Для этого мы устанавливаем два порога: высокий порог и низкий порог. Допустим, высокий порог равен 200, а нижний порог - 150.

Для любого общего градиента, значение которого превышает верхний порог 200, этот пиксель автоматически считается краем и преобразуется в чистый белый цвет (255). Для любого общего градиента, значение которого меньше нижнего порога 155, этот пиксель автоматически считается «не краем» и преобразуется в чистый черный цвет (0).

Для любого градиента в диапазоне от 150 до 200 пиксель считается краем только в том случае, если он непосредственно касается другого пикселя, который уже считается краем.

Предполагается, что если эта мягкая кромка соединена с жесткой кромкой, она, вероятно, является частью того же объекта.

После завершения этого процесса для всех пикселей мы получаем изображение, которое выглядит так…

4. Замаскируйте края изображения.

Следующий шаг очень прост: создается маска, которая удаляет все части фотографии, которые, как мы предполагаем, не имеют линий полос.

Получаем это…

Кажется, что это довольно агрессивная и самонадеянная маска, но это то, что сейчас написано в исходном коде. Итак, продолжаем ...

5. Линии Хафа

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

Математика, лежащая в основе преобразования Хафа, немного сложнее, чем все средневзвешенные вычисления, которые мы делали выше, но лишь незначительно.

Вот основная концепция:

Уравнение для прямой имеет вид y = mx + b, где m и b - константы, которые представляют наклон линии и точку пересечения оси y линии соответственно.

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

Затем мы перемещаемся по этому пространству m и b, и для каждой пары (m, b) мы можем определить уравнение для конкретной строки вида y = mx + b. На этом этапе мы хотим протестировать эту строку, поэтому мы находим все пиксели, которые лежат на этой линии на фотографии, и просим их проголосовать, если это хорошее предположение. для полосы движения или нет. Пиксель голосует «да», если он белый (он же часть края), и голос «нет», если он черный.

Пара (m, b), получившая наибольшее количество голосов (или в данном случае две пары, получившие наибольшее количество голосов), определяется как две линии дорожек.

Вот результат преобразования Хафа ...

Я пропускаю ту часть, где вместо использования формулы y = mx + b для представления линии преобразование Хафа использует представление в стиле полярных координат / тригонометрического стиля, в котором используются rho и theta в качестве двух параметров.

Это различие не очень важно (для нашего понимания), поскольку пространство все еще параметризуется в двух измерениях, и логика точно такая же, но это тригонометрическое представление действительно помогает с тем фактом, что мы не можем выразить полностью вертикальное линии с уравнением y = mx + b.

В любом случае, именно поэтому в приведенном выше коде используются rho и theta.

Окончательный результат

И мы закончили.

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

Линия переулка 1

Угловой коэффициент: -0,740605727717; Перехват: 664.075746144

Пункт первый: (475, 311) Пункт два: (960, 599)

Линия переулка 2

Коэф: -0,740605727717; Перехват: 664.075746144

Первая точка: (475, 311) Вторая точка: (0, 664)

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

Вчера я деконструировал фрагмент кода, который определяет полосы движения на изображениях дороги, обращенных вперед.

Нравится…

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

Линия движения 1 = уклон: -0,740605727717; Перехват: 664.075746144

Линия полосы 2 = Coef: -0.740605727717; Перехватчик: 664.075746144.

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

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

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

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

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

Таким образом, мне кажется, что мне следует отвлечься от шага 1, который в основном сосредоточен на распознавании объектов и оцифровке, и вместо этого сосредоточить основное внимание на функции из шага 2, которая отображает входные данные от мир в инструкции по вождению. Я назову эту функцию «Волшебной функцией».

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

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

С завтрашнего дня я начну изучать, как может работать волшебная функция и как я могу ее создать.

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

Сегодня я нашел набор данных размером 223 ГБ, исходный код которого был открыт Udacity (компания, которая делает онлайн-курсы по техническим темам, в том числе беспилотные автомобили), который содержит именно то, что мне нужно:

Udacity взял видео с видеорегистратора, разбил его на отдельные кадры, а затем пометил каждый кадр соответствующим углом поворота, который фактически выполнял человек-водитель.

Например, кадр: 1479425444933328937; угол поворота руля: -0,346924703940749

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

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

Поскольку я только «теоретически» знаю, как это сделать прямо сейчас, я вернулся в Github сегодня, чтобы посмотреть, смогу ли я найти кого-нибудь, кто использует набор данных Udacity таким образом.

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

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

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

Завтра я продолжу играть в эту игру и посмотрю, смогу ли я заставить свой компьютер сотрудничать. А пока я буду надеяться ...

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

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

Приступая к освоению новой территории кодирования, вот общий сюжет: 1. Потратьте два часа, пытаясь каждую возможную перестановку кода / настройки среды, чтобы увидеть, сработает ли какая-то случайная комбинация. 2. Ничего не работает, и вы понимаете, что находитесь дальше. от вашей цели, чем когда вы начали (потому что вы возились с таким большим количеством вещей), 3. Продолжайте попытки еще час, 4. Волшебным образом звезды совпадают, и какая-то необъяснимая комбинация вещей заставляет все работать.

Проблема с этой моделью в том, что ... вам нужно стиснуть зубы и страдать от боли в течение неизвестного количества времени, пока вы магическим образом не достигнете нирваны на Шаге 4. Вы не знаете, когда наступит нирвана, и не верите, что она наступит. когда-нибудь придет, но вы должны продолжать, если не исключено, что это может. Нет постепенного вознаграждения, просто высасывающее душу разочарование, пока оно волшебным образом не сработает.

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

Сегодня я едва держался.

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

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

Так что вместо этого я потратил сегодня около 2,5 часов, утонув в Coding Pit of Despair. Это было жестоко и угрожало уверенности, и я много раз хотел сегодня признать поражение, но смог удержаться.

Каким-то образом я нашел свой путь к концу дня и, похоже, добился ощутимого прогресса. Я не могу сказать наверняка до завтрашнего утра: мой компьютер в настоящее время не работает и, вероятно, продолжит работать всю ночь.

Завтра, надеюсь, проснусь с успешно запущенной программой.

Прошлой ночью, после того как я несколько часов утонул в Кодирующей яме отчаяния, я, наконец, добился некоторого прогресса.

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

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

Как описано в аннотации к статье, разработанная ими система может «отображать необработанные пиксели с одной фронтальной камеры напрямую на команды управления».

Это именно то, что мне нужно!

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

(Обновление: на самом деле это не данные NVIDIA, а набор данных, созданный Салли Ченом. Он собрал данные, прикрепив веб-камеру к лобовому стеклу своей машины и зафиксировав " приводить данные из порта CAN-BUS своего автомобиля).

Небольшое отступление, чтобы прояснить некоторые термины: 1. «Модель» - это функция, которая описывает, как преобразовывать пиксели в инструкции управления, и 2. «Обучать модель» означает итеративное улучшение этой функции с использованием методов машинного обучения. Я объясню этот процесс более подробно в одной из следующих статей.

В любом случае, набор данных NVIDIA включает 25 минут видео с разбивкой по кадрам, где каждый кадр помечен истинным углом поворота (т. Е. Углом поворота, который использовал человек-водитель).

В Терминале я запустил программу для обучения модели, и, несмотря на некоторые предупреждения, она начала работать:

На этом снимке экрана «Шаг» описывает каждый раз, когда часть данных проходит через систему для обучения. «Эпоха» - это просто более широкая категория, содержащая несколько шагов.

Для обучения этой модели я использовал 30 эпох с несколькими десятками шагов на эпоху.

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

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

Таким образом, «обучение модели» - это просто «уменьшение потерь».

Вот график с шагами по оси X и потерями по оси Y. (Прошлой ночью, используя что-то под названием Tensorboard, мой компьютер построил это во время тренировки).

Например, на шаге 126 Убыток составил 5,708.

В то время как на этапе 3241, почти шесть часов спустя, потеря имела значительно лучшее значение 0,1615.

Теперь, когда обучение официально завершено, модель теоретически готова управлять автомобилем, что очень круто.

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

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

У меня есть идея обходного пути, но придется подождать до завтра.

А пока я могу отметить тот факт, что у меня (вероятно) есть достаточно функциональная, уже обученная модель самоуправляемого автомобиля, готовая к использованию. Надеюсь, завтра я смогу понять, как на самом деле его использовать ...

Вчера я придумал, как обучить свой беспилотный автомобиль, но мне было трудно подтвердить, что обучение действительно было эффективным.

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

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

К счастью, несколько дней назад я понял, как сохранять отдельные обработанные кадры на моем локальном компьютере.

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

Затем я объединил отдельные кадры, наложил два видео и нажал кнопку воспроизведения. Вот результат ...

Я очень рад этому!

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

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

Я все еще не совсем уверен, что готов спать на заднем сиденье своего беспилотного автомобиля, но сегодня определенно знаменует собой большой шаг вперед.

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

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

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

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

Вот полный код модели с автоматическим вождением. Это всего лишь 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 и как мало базовых математических знаний необходимо для создания полностью работоспособной модели беспилотного автомобиля.

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

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

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

За последние одиннадцать дней я значительно продвинулся в решении задачи этого месяца, поэтому сегодня я решил сделать перерыв.

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

В частности, я просмотрел кучу видеороликов с YouTube-канала Computerphile, который лично является моим любимым каналом по темам, связанным с информатикой.

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

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

Несмотря на то, что Computerphile менее актуален для задачи этого месяца, у Computerphile есть родственный канал YouTube под названием Numberphile, на котором обсуждаются темы теории чисел и математики в целом.

Вот одно из моих любимых видеороликов Numberphile - в нем есть очень крутой способ приблизительно определить значение числа Пи.

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

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

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

В основном, вот что нужно знать:

  1. В сверточной нейронной сети наверху «стека» находятся сверточные слои, которые изучают, какие операции свертки (т. Е. Манипуляции с изображениями) выделяют лучшие функции для изучения.
  2. Затем есть несколько полных связанных слоев, которые пытаются научиться создавать правильный вывод на основе функций, выделенных сверточными слоями.
  3. Между ними есть несколько математических приемов (например, понижающая дискретизация и выпрямители), которые ускоряют процесс обучения.
  4. Определение формы и параметров слоев обычно выполняется эмпирическим путем (т.е. пробует разные варианты и выбирает конфигурацию, которая дает наилучшие результаты).

Что касается пункта 4, в статье Nvidia, в которой описывается система глубокого обучения, которую я использую, они объясняют, что «сверточные слои были разработаны для выполнения извлечения признаков и были выбраны эмпирически в результате серии экспериментов, в которых менялись конфигурации слоев. . »

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

По сути, пока вы понимаете основные компоненты, создание эффективного беспилотного автомобиля (у Nvidia 98% автономности) - это не что иное, как «угадать и проверить». (Можно возразить, что если мы хотим получить автомобиль с 98% до 100% автономности, мы должны сделать немного больше, чем просто догадываться и проверять, что верно сегодня, но, вероятно, со временем станет менее верным, поскольку мы все больше и больше прикладываем все больше вычислительной мощности к проблеме).

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

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

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

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

Несколько дней назад мне удалось запустить код и заставить свой беспилотный автомобиль успешно ехать по улицам (по крайней мере, виртуально, на основе видеозаписи NVIDIA).

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

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

Первым шагом было отформатировать набор данных Udacity, чтобы он мог обрабатываться моей моделью беспилотного автомобиля.

В отличие от набора данных NVIDIA, который содержит видеоклип, красиво разбитый на последовательно пронумерованные кадры (0.jpg, 1.jpg, 2.jpg, 3.jpg и т. Д.), Набор данных Udacity представляет собой набор изображений с запутанной нумерацией: Между числами есть нечетные промежутки непостоянного размера, и набор данных начинает отсчет с 1479425441182877835.

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

Поэтому моим первым шагом было переименовать все эти файлы в соответствии со схемой нумерации в стиле NVIDIA (т.е. начать с нуля и считать по единицам).

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

Примерно через 12 минут работы я смог написать небольшой скрипт Python для переименования всех файлов Udacity.

import os
def rename(directory):
i = 0
for file_name in os.listdir(directory):
      new_file_name = str(i) + '.jpg'
      old_file_name = file_name
os.rename(old_file_name, new_file_name)
i += 1
PATH = os.path.abspath('/Users/maxdeutsch/Desktop/nvidia/udacity_data')
rename(PATH)

Я запустил сценарий, и через несколько секунд все файлы были переименованы (я рад, что не сделал это вручную).

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

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

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

Вчера я пытался водить свой беспилотный автомобиль по новым дорогам (из набора данных Udacity). Однако машина к этому не была подготовлена ​​и практически неоднократно разбивалась.

Очевидно, что модель, обученная на наборе данных NVIDIA, не подходит для набора данных Udacity.

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

Вот стоп-кадр из набора данных NVIDIA:

А вот кадр из набора данных Udacity:

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

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

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

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

Из этого…

К этому…

После подготовки изображений мне нужно было создать текстовый файл с именем «data.txt» для набора данных Udacity, в котором указан правильный угол поворота рядом с именем соответствующего изображения.

Вот как выглядит файл NVIDIA data.txt:

Тем не менее, данные Udacity были собраны в виде таблицы:

Итак, чтобы создать файл data.txt для набора данных Udacity, мне нужно было выполнить две вещи: 1. Переименовать frame_ids в соответствии со вчерашней схемой нумерации; 2. Узнайте, как преобразовать электронную таблицу в текстовый файл без какого-либо форматирования табличного стиля.

Вместо того, чтобы использовать скрипт Python, я попытался выяснить, как выполнить оба шага в Google Таблицах (вероятно, есть более эффективный способ).

Сначала я набрал небольшую последовательность последовательных чисел, а затем развернул последовательность, чтобы заполнить другие ~ 5000 ячеек.

Затем я использовал встроенные функции TO_TEXT и CONCATENATE, чтобы преобразовать целые числа в строки, а затем объединить эти строки с расширением файла .jpg.

Переходим к шагу 2 - преобразование таблицы в неформатированный текстовый документ.

Я снова использовал CONCATENATE, чтобы объединить имена изображений и углы поворота в одну ячейку (с разделителем в один пробел).

Затем я объединил каждую ячейку с помощью char (10), который представляет собой код символа для разрыва строки. И, наконец, объединил все ячейки в одну ячейку.

Я скопировал содержимое этой единственной ячейки в текстовый документ, и мой файл data.txt был готов к работе.

Странно то, что значения угла поворота Udacity сильно отличаются от значений угла поворота NVIDIA.

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

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

Я изучу это подробнее завтра, а затем начну обучение модели на данных Udacity.

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

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

Обычно вы получаете такую ​​ошибку, когда программа ожидает список вещей, который длиннее, чем фактический список вещей.

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

Однако, просмотрев код, я не смог найти проблему. Все вроде должно работать.

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

Я запустил программу с операторами печати и получил следующий результат:

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

Итак, я открыл файл data.txt и, конечно же… Я случайно скопировал несколько пустых строк в конец файла.

Я удалил эти три пустые строки и перезапустил программу в Терминале.

Это сработало, и модель приступила к обучению.

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

Сегодня, по дороге на работу, я проезжал мимо беспилотного автомобиля Google / Waymo возле железнодорожного вокзала Маунтин-Вью.

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

Вот немного более ясная картина ведущего Uber: похоже, что в настоящее время он управляется людьми, вероятно, в учебных целях. Автомобиль Google ехал сам.

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

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

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

Все началось вчера - я успешно переформатировал данные Udacity и начал обучение модели беспилотного автомобиля.

После того, как модель закончила обучение, я быстро взглянул на график потерь (Loss измеряет «точность» модели - чем меньше потери, тем лучше модель… по большей части).

После 30 эпох обучения потери даже не опустились ниже 1,00, тогда как, когда я обучал модель на данных NVIDIA, потери упали значительно ниже 1,00, вплоть до ~ 0,16.

Я не уверен, почему я ожидал, что произойдет что-то другое - набор данных Udacity, который я использовал, был только 1/8 размера набора данных NVIDIA.

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

Это не большая проблема: я зашел на страницу Udacity Github и загрузил больший набор данных для обучения. По крайней мере, я пытался.

На полпути к загрузке мой компьютер окончательно взбесился.

Оказалось, что локальное хранилище / загрузочный диск моего компьютера был полностью заполнен. Настолько переполнен, что мой компьютер отказывался запускать какие-либо программы. Даже Finder неожиданно вылетал.

Я подключил внешний жесткий диск и начал переносить всю мою документацию Month to Master со своего локального компьютера.

Кстати, мне пришлось сфотографировать экран компьютера с помощью телефона, так как на моем компьютере не хватало места для создания снимков экрана…

Как бы то ни было, за первые шесть месяцев документации M2M было меньше 132 ГБ, 70,8 из которых находились на моем локальном компьютере, поэтому после завершения передачи я смог переместить 70 ГБ локального содержимого в корзину.

Затем, при попытке очистить корзину, мой компьютер завис ...

После нескольких перезапусков компьютера моя корзина, наконец, опустела, и через 30 минут я вернулся к работе.

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

Обучающий набор данных на самом деле был сжат в торрент, поэтому мне нужно было установить BitTorrent, чтобы загрузить набор данных.

После скачивания торрента распаковал файл. Я ожидал увидеть кучу изображений JPEG и файл data.txt, как мы видели раньше, но вместо этого я увидел это ...

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

Итак, мне нужно было выяснить, как извлечь файлы JPEG и CSV из отдельных файлов .bag.

Для работы с файлами .bag необходима библиотека под названием ROS (Robot Operating System), поэтому я попытался ее установить.

Но вот что я нашел на странице установки ROS ...

Другими словами, люди, которые создают ROS, в основном говорят : «Это не сработает. Он потерпит неудачу. Извини ».

И они были правы, это не удалось.

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

В этот момент мне пришлось остановиться на ночлег.

Надеюсь, завтра я смогу продвинуться вперед.

Вчера я серьезно боролся: я пытался конвертировать файлы ROSbag от Udacity в файлы JPEG и CSV, чтобы использовать данные для тренировки своего беспилотного автомобиля, но мне не очень повезло.

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

Сегодня, после множества проб и ошибок, я смог выяснить, как запустить Ubuntu 14.04 и ROS на виртуальной машине с помощью VirtualBox.

После еще большего количества проб и ошибок я понял, как использовать виртуальную машину для извлечения содержимого файлов ROSbag ...

Я ожидал найти набор изображений для одной камеры разумного размера и CSV для соответствующего угла поворота.

Вместо этого набор данных Udacity включает ~ 33000 кадров видео вождения с трех разных углов камеры и все данные для рулевого управления, торможения, газа, GPS и т. Д.

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

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

Вчера я взломал набор данных Udacity…

Итак, сегодня план состоял в том, чтобы переформатировать / подготовить данные, а затем начать обучение модели беспилотного автомобиля.

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

Чтобы подготовить данные, мне нужно было создать файл data.txt, который выглядит примерно так:

По сути, это простой текстовый файл с именем кадра рядом с соответствующим углом поворота, разделенным пробелом.

Это казалось достаточно простым, но возникла проблема:

Когда я открыл файл рулевое управление.csv, ни одна из меток времени в файле не совпадала с метками времени кадров JPEG. Я думал, что, возможно, что-то не замечаю ...

Итак, я просмотрел JPEG и скопировал первую пару номеров кадров.

Затем я индивидуально поискал в CSV эти конкретные номера кадров, но их не было ...

Это было проблемой.

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

К счастью, у меня возникла не очень блестящая идея открыть другие CSV в наборе данных Udacity (просто чтобы посмотреть, что еще там было), и в конце концов я открыл interpolated.csv, который содержит все данные в одном месте И идеально сопоставляет все эти данные с отметками времени на изображениях.

Итак, мне снова повезло.

Я извлек необходимые данные, создал файл data.txt и начал обучение модели.

Скорее всего, это займет всю ночь, так что мы проверим это завтра.

Вчера я начал обучение модели беспилотного автомобиля на основе большого набора данных Udacity.

Значение потерь (показатель, косвенно пропорциональный точности модели) началось с 6,14783.

Много часов спустя модель завершила обучение, уменьшив потери до 0,000377398.

Вот график потери во времени:

Это сокращение потерь весьма поразительно.

Еще более поразительно разница в потерях при использовании 5000 точек данных (как я сделал несколько дней назад), с потерями ~ 1,00 и при использовании 33000 точек данных Udacity, с потерями около 0,000377398.

Другими словами, за счет увеличения размера набора данных в ~ 7 раз потери были уменьшены в ~ 2500 раз. Ясно, что это не линейная зависимость: чем больше данных, тем лучше модель становится смехотворно.

И именно поэтому Google может позволить себе раздавать все / большую часть своих алгоритмов и библиотек машинного обучения через TensorFlow: количество данных является отличительным признаком, а у Google больше всего (из поиска, электронной почты, фотографий, видео на YouTube и т. Д.)

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

Сегодняшний день действительно помог мне оценить ценность данных как конкурентного преимущества.

Говоря о данных… Я достиг важной вехи в создании собственного набора данных: сегодня исполняется 200-й день подряд, когда я написал сообщение в блоге в рамках моего проекта Месяц, чтобы стать мастером.

Мой набор данных ежедневных записей теперь составляет около 85 000 слов. Может быть, когда я закончу со всеми 365 сообщениями, я найду что-нибудь интересное, что можно сделать с этим набором данных ...

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

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

Несколько замечаний:

  • Первоначально я думал, что существует два набора данных Udacity: набор данных для обучения (который я использовал вчера) и данные тестирования (которые я случайно использовал для обучения несколько дней назад). Сегодня я понял, что набор тестовых данных на самом деле является подмножеством обучающего набора, поэтому я решил вместо этого использовать некоторые данные Nvidia для тестирования. Здесь важно то, что модель была обучена на наборе данных Udacity и протестирована на совершенно новом ландшафте из набора данных Nvidia. Другими словами, модель хорошо работает на дорогах за пределами ее обучающего набора (это очень важно, если вам нужен универсальный беспилотный автомобиль).
  • Чтобы правильно смоделировать выходные данные модели Udacity, мне нужно было сделать две вещи: 1. Сопоставить данные Udacity с диапазоном значений, используемым симулятором Nvidia (модель Nvidia использует градусы как единицы измерения, а модель Udacity набор данных варьируется от -1 до 1) и 2. Выполните небольшую предварительную обработку набора для тестирования Nvidia (например, кадрирование) для тестирования модели Udacity.
  • Во время тестирования скрипт визуализировал изображения рулевого колеса, повернутого на основе прогнозируемого угла поворота, а затем я наложил эти визуализации поверх исходных, необрезанных кадров Nvidia для немного более широкого обзора.
  • Примерно через 40 секунд после начала видеозаписи автомобиль полностью останавливается, а затем резко поворачивает направо. Кажется, что машина начинает поворачивать до того, как визуальные эффекты показывают, что она должна ехать направо (в конце концов, машина может ехать прямо), поэтому я не совсем понимаю, как это происходит. В наборе данных Udacity ничего не известно об этом конкретном повороте. Единственное разумное объяснение состоит в том, что модель распознала, что она находится на поворотной полосе, или модель просто более предсказуема, чем человек. В любом случае, это было немного удивительно, но смотреть на это было здорово.

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

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

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

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

В конце концов, моя модель управления была построена следующим образом: 1. Я показывала компьютеру набор изображений, 2. Присваивала числовое значение каждому изображению, 3. Попросила компьютер выяснить, как пиксели изображений соотносятся с числовыми значениями, 4 Использование этого отношения для прогнозирования числового значения, присвоенного другим изображениям.

В случае предсказателя поворота это числовое значение представляет угол поворота . Но с такой же легкостью он мог представлять величину газа или торможения.

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

Итак, вот что я сделал.

Например, в случае throttle я подготовил data.txt с 10 000 изображений, сопоставленных с их соответствующим значением throttle, а затем прогнал этот файл через ту же самую модель машинного обучения.

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

То же самое проделал и с тормозной системой.

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

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

Дело в том, что мне не нужно много работать, чтобы получить троттлинг и торможение бесплатно. Хорошая сделка…

В этом месяце я поставил перед собой задачу создать программную часть беспилотного автомобиля. В частности, я хотел построить две основные вещи: 1. систему рулевого управления автомобиля и 2. систему педалей автомобиля (то есть дроссельную заслонку и тормоз).

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

Чтобы система рулевого управления считалась успешной, я поставил две подцели:

  1. Мне нужно было адаптировать и использовать модель беспилотного автомобиля в наборе данных, для которого она не была специально разработана. Поскольку я использовал модель, основанную на исследовательской работе NVIDIA, применительно к набору данных, предоставленному Udacity, я выполнил требования для этой промежуточной цели.
  2. Во-вторых, мне нужно было обучить модель на одном наборе данных (то есть на множестве дорог), и чтобы она хорошо работала на совершенно другом наборе данных (то есть на множестве новых дорог). Поскольку я обучил модель на наборе данных Udacity, а затем успешно протестировал модель на наборе данных NVIDIA, я также выполнил требования для этой промежуточной цели.

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

Когда все эти элементы собраны, задача этого месяца официально завершена!

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

Теперь вопрос в том ... Что мне нужно сделать / построить, чтобы моя машина действительно выехала на дороги Калифорнии?

Лучший ответ дает Джордж Хотц - пионер самостоятельного движения автомобилей.

18 месяцев назад Bloomberg опубликовал видео о 26-летнем Хотце, который построил в своем гараже полностью работающий (более или менее) беспилотный автомобиль:

В видео Джордж объясняет, что ему нужно было построить только две вещи для создания автомобиля: 1. Система, которая могла бы выводить инструкции по вождению на основе входных данных, и 2. Система, которая могла бы управлять физическими исполнительными механизмами автомобиля (т.е. рулевого колеса, дроссельной заслонки, тормоза) на основе цифровых входов.

В начале этого месяца я назвал №1 «программную часть» и №2 «аппаратную часть», сосредоточив свои усилия на программном обеспечении. Итак, когда №1 завершен, чтобы закончить разработку моей машины, мне нужно будет обратиться к №2.

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

Не то чтобы я слишком много разбираюсь в машинах, но это кажется мне очень доступным (пока у меня есть доступ к Интернету и YouTube). Если бы у меня был еще месяц и, что более важно, машина, это было бы естественным следующим шагом.

По сути, кажется, что беспилотный автомобиль со сквозным управлением - не такой уж и мифический. Джордж Хотц помог показать всем, включая меня, что пространство для беспилотных автомобилей доступно не только для таких компаний, как Google, но и для умеренно случайных любителей.

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

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

Задача этого месяца немного отличается от шести предыдущих Месяца, чтобы освоить. В частности, он не очень хорошо стареет ...

В феврале 2017 года Я сделал сальто. Если бы это был февраль 2020 года, ничего бы не изменилось.

В декабре 2016 года Я научился рисовать реалистичные портреты. Если бы это был декабрь 2036 года, это не имело бы никакого значения.

В этом месяце, в мае 2017 года, Я построил программную часть беспилотного автомобиля. Если бы это был май 2020 года, задача была бы совершенно другой.

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

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

По сути, я хочу сказать, что задача этого месяца не станет проблемой через несколько лет.

Не то чтобы это так уж важно, но интересно подумать об устаревании навыков и о том, как «передний край» быстро превращается в «тупую середину».

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

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

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

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

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

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

Более определенная цель намного предпочтительнее.

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

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

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

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

Несколько дней назад я объявил, что выполнил задание беспилотный автомобиль. В результате получилось вот это видео, на котором мой компьютер управляет автомобилем…

Хотя результатом эксперимента в этом месяце, безусловно, была система, которая может автономно управлять автомобилем, остается вопрос… Что я лично сделал в этом месяце?

В конце концов, моя беспилотная машина изначально была основана на чужом открытом исходном коде (который я слегка адаптировал и обобщил). Этот открытый исходный код был основан на статье, написанной исследовательской группой NVIDIA по беспилотным автомобилям. Модель из исследования была основана на математических методах (обратное распространение и т. Д.), Которые были изобретены вне лаборатории NVIDIA, в основном в исследовательских центрах университетов. И я могу продолжать ...

Эти математические методы основаны на фундаментальных выводах математического анализа, которые были изобретены сотни лет назад, и так далее.

Я также могу взглянуть на это в другом направлении: код, который я запускал, был построен на основе библиотеки машинного обучения, созданной Google, которая была построена на основе языка программирования высокого уровня, созданного другими. Кроме того, чтобы запустить любой из этих кодов, мне нужно было установить на свой компьютер необходимые библиотеки. Установка этих библиотек может быть сложной, поэтому я использовал службы установки, которые были настроены другими людьми, чтобы упростить процесс.

Я все еще могу идти ... но не буду.

Итак, возвращаясь к вопросу: что я лично сделал в этом месяце? Не совсем понятно.

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

С другой стороны, могу сказать, что я взял работу множества других людей и объединил ее, чтобы сделать видео.

Оба верны.

Итак, действительно ли я построил беспилотный автомобиль? Можете ли вы сказать, что такие компании, как OnePlus или Xiaomi, создают смартфоны, даже если программное обеспечение создано Google, а аппаратные компоненты - моим Samsung, Foxconn и другими?

Считается ли «сборка» «построением», а «агрегирование» - «обучением»?

Я бы сказал, что да, но я не думаю, что это имеет значение.

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

Другими словами, часто исключительность мастерства существует только потому, что большинство людей никогда не преследуют «цель» (исходя из предположения, что они не могут).

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

В любом случае, я думаю, что это более интересный результат.

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

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

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

Один из распространенных вопросов, который я слышу в ответ на мою общую комфортность: «Разве ты не предпочел бы контролировать свою жизнь? Даже если в целом статистически безопаснее использовать полностью автономный автомобиль, разве вы не хотите знать, что сделали все, что могли, чтобы выжить? »

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

Вкратце: два дня назад Массачусетский технологический институт опубликовал свой ежегодный исследовательский документ о текущем состоянии интереса потребителей к беспилотным автомобилям, и 48% из 3000 участников исследования заявили, что они никогда не купят автомобиль, который полностью управляет собой.

Более того, в прошлом году 40% молодых людей в возрасте 25–34 лет заявили, что им будет комфортно с автомобилем без водителя, и тем не менее только 20% из той же группы заявили то же самое в этом году.

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

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

Я сейчас нахожусь в гостях у друга в Сиэтле, который беспокоится о записи, которую я написал два дня назад:

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

В результате я часто слышу «Если вам на самом деле не нужно было строить базовую модель беспилотного автомобиля, что на самом деле было проблемой в этом месяце?»

В пятничном посте я согласен с тем, что предполагаемая проблема (создание механизма беспилотного автомобиля) на самом деле не была проблемой, но это не означает, что проблемы вообще не было.

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

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

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

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

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

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

Точно так же программная инженерия сталкивается с двумя основными проблемами: 1. Поиск «органа» (т. Е. Примера компонента, который вы хотите создать), 2. Интеграция этого «органа» в общее тело (т. Е. Обеспечение правильной настройки среды разработки; подключение компонента к другим компонентам и службам, чтобы он должным образом взаимодействовал с остальной частью проекта; изменение компонента, чтобы он вел себя ожидаемым / необходимым образом).

И хотя в этом месяце задача не была сложной из-за предполагаемой сложности заставить машину управлять самим собой, я все же провел много времени в яме отчаяния при программировании, пытаясь 1 . Найти модель беспилотного автомобиля, которую я мог бы успешно запустить, 2. Настроить среду моего компьютера для поддержки модели, 3. Отформатировать данные так, чтобы их могла использовать модель, 4. Выяснить, как вывести что-то интересное из модель, 5. Измените модель для работы с разными наборами данных и для моделирования различного поведения.

В целом предполагаемая проблема не представляла собой проблему, но по-прежнему было много трений.

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

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

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

Как объясняет Райан Холидей: « Препятствие - это путь »

Ваши препятствия не мешают вам преследовать цель, но, по сути, определяют само стремление.

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

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

Но прежде чем я сделаю это, важно отметить, как я провел свое время в этом месяце, и, в частности, как мое время было распределено.

В предыдущие месяцы мое время практики распределялось довольно равномерно - если я тратил 30 часов в течение 30 дней, то обычно я тратил один час в день.

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

Две фазы обучения:

  1. Этап открытия. На этом этапе задача учащегося состоит в том, чтобы изучить поле, найти лучший путь и разработать упражнения в соответствии с этим лучшим путем.
  2. Этап обучения. На этом этапе учащийся уже знает, что ему нужно практиковать, и проводит все свое время сознательно и интенсивно, сосредотачивая свое обучение на этом наилучшем пути.

Все обучение требует этих двух фаз, но эти фазы могут по-разному взаимодействовать в разных типах обучения.

Два типа обучения:

  1. Разъединенное обучение - в разрозненном обучении фаза открытия и фаза обучения могут проводиться как полностью, так и по отдельности. Например, когда я учился запоминать колоду карт, я смог сначала открыть правильную технику, а затем отдельно практиковать эту технику. Раздельное обучение обычно позволяет проводить короткие, но эффективные учебные занятия (после необходимой фазы открытия)
  2. Одновременное обучение - при одновременном обучении фаза открытия и фаза обучения должны выполняться одновременно. Обычно одновременное обучение существует, когда еще нет четко определенного пути (который может быть обнаружен извне), и поэтому его необходимо раскрыть и сформировать во время обучения. Этот процесс обычно требует значительно более длительных сессий, учитывая, что прогресс непредсказуем и неструктурирован.

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

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

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

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

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

Поскольку уже почти конец мая, пора оглянуться назад и посмотреть, сколько времени я потратил на задачу этого месяца.

Большая часть моего времени была распределена на девять более длительных сессий следующей продолжительности: 1,5 часа, 2 часа, 2,5 часа, 1,5 часа, 1 час, 3,5 часа, 2,5 часа, 1,5 часа, 1 час, 3 часа.

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

Итак, в общей сложности я потратил 26 часов на сборку своей беспилотной машины.

Прежде чем вычислить это общее количество, я ожидал около 35–40 часов, поэтому я определенно был немного удивлен этим результатом. Но теперь это имеет смысл: было много дней, когда я чувствовал, что трачу время, но на самом деле я либо позволял своему компьютеру работать самостоятельно, либо писал более подробный, чем обычно, ежедневный пост (то есть фактически не работал над вызов самому себе).

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

Сам «путь» на самом деле не занял много времени.

Сегодня последний день испытания беспилотных автомобилей, и я хочу ответить на один из важных вопросов, которые я получал в течение месяца: «Я думаю, у вас здесь что-то есть… Как вы собираетесь зарабатывать на этом деньги? ”

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

Итак, этот последний пост - мой ответ на это (хотя я не уверен, что отвечу прямо на вопрос) ...

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

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

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

Перед тем, как принять участие в этом месяце, я потратил свое время на овладение навыками, которые имеют очень ограниченную коммерческую ценность, если вообще имеют (например, Кубик Рубика и рисование портретов). Самое классное ... Пока я жив, я смогу наслаждаться этими навыками. Другими словами, мои авансовые инвестиции теоретически приносят бесконечную прибыль.

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

С навыками, которые я приобрел в этом месяце, я могу делать много коммерчески интересных вещей.

Но вот в чем дело… Через 24 месяца (если не раньше) вся эта коммерческая ценность исчезнет. Более того, через 24 месяца этот вызов больше не будет актуальным или интеллектуально интересным: он был интересен только из-за времени, все еще достаточно содержательных технических деталей и нынешнего ажиотажа в отношении беспилотных автомобилей.

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

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

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

Главное, что я хочу сказать, это ... Инвестируете ли вы в настоящее время в себя? Вы пытаетесь узнать что-то новое? И, в частности, пытаетесь ли вы узнать что-то новое только потому, что это весело (а не по финансовым причинам)?

Если нет, возможно, стоит подумать.

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

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

Этот пост является частью моего годичного проекта ускоренного обучения Месяц до мастера.

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

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

Если вам понравилась эта подробная разбивка, дайте мне знать, нажав на сердечко ниже. Это всегда очень ценится.