Как мы учимся у TensorFlow и мечтаем о будущем кремниевой долины?

На прошлой неделе, когда я просматривал Medium, я увидел интересную статью о машинном обучении, в которой говорилось об AutoGraph, инструменте для преобразования обычного кода Python в код Graph, который TensorFlow использует для выполнения.



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

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

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

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

Почему у нас вообще были компьютерные программы, понятные человеку?

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

Возвышение фон Неймана

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

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

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

Фактически, все современные компьютеры построены на архитектуре фон Неймана.

Сборка All Hail

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

В старших классах я купил калькулятор TI-84 для своего курса алгебры с отличием. Однажды я обнаружил, что могу писать программы на калькуляторе. Инструкции по программированию от TI были следующие.

Знакомство с TI-Basic на вашем TI-84 Plus CE

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

Что такое программа?

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

Программа - это набор из одной или нескольких командных инструкций. Используя комбинацию операторов if, goto / label и функций ввода-вывода, таких как обнаружение нажатия клавиш или печать текст, я создал свою первую игру для TI-84: Tank Warfare. Это была простая игра, в которой случайным образом появляются вражеские цели, а цель игры заключалась в том, чтобы управлять танком с помощью клавиш со стрелками и стрелять по целям. После этого я даже написал в Facebook, жалуясь, что потратил 4 часа, пытаясь понять, как написать программу.

Если подумать, среда программирования на TI-84 была довольно примитивной. В основных строительных блоках не было ничего необычного, и мне потребовалось много времени, чтобы привыкнуть к окружающей среде и изменить образ мышления. Калькулятор начинает выполнение с первой строки программы. Единственный способ обойти инструкции - использовать goto и label. Обычно я помещаю метку в начало функционального блока и могу использовать goto, чтобы направить поток управления ЦП для перехода к любым меткам.

Если мы распечатаем программу на бумаге, вы заметите, что она написана в том же плоском формате, что и эти личностные тесты - если вы выберете A, перейдите к вопросу 3, иначе перейдите к вопросу 2; Если вы выбрали B, переходите к вопросу 4 и т. Д.

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

Рождение языков программирования

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

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

Ранние языки программирования обычно имеют прямое сопоставление один-к-одному с ассемблером. Для более поздних языков обычно требуется более сложный компилятор для компиляции кода, удобочитаемого человеком, в код ассемблера, читаемый ЦП. У языков сценариев, таких как Python, есть свой интерпретатор, который интерпретирует каждую строку кода во время выполнения и соответствующим образом настраивает соответствующие инструкции ЦП. Технологии развиваются экспоненциально, и теперь мы живем в мире, в котором мы генерируем повторяющийся шаблонный код и заполняем оставшуюся логику для создания новых программ.

Статус кво

Приближаясь к 2018 году, теперь у нас есть разработчики полного стека, пишущие веб-приложения React-Typescript и серверные микросервисы Golang. Веб-приложения обычно компилируются в файл пакета Javascript для интерпретации клиентским браузером. Микросервисы компилируются в исполняемые приложения, которые работают на AWS или в любых докеризированных контейнерных средах. TL; DR заключается в том, что люди больше не обращают внимания на то, как программы выполняются на уровне ЦП. Они обращают внимание на код и на то, как облегчить себе жизнь.

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

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

Альтернативная вселенная: слой графа и метапрограммирование

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

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

Промежуточный слой графа

Так же, как TensorFlow работает с графом вычислений, что, если в альтернативной вселенной мы создадим промежуточный слой графа между языками программирования и инструкциями по сборке? Этот уровень в основном содержит чисто вычислительные и платформенно-независимые графы, которые можно интерпретировать и оптимизировать в инструкции ЦП, зависящие от платформы. Затем мы представляем инструмент, похожий на AutoGraph, который компилирует обычный код в вычислительные графы.

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

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

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

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

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

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

Мета-программирование и будущее DevOps

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

Фактически, мы уже делали нечто подобное. В современных технологических компаниях DevOps обычно упрощает жизнь разработчикам, создавая инструменты и сценарии для автоматизации многих повторяющихся процессов. На моем текущем рабочем месте у нас есть инструменты генерации кода, поддерживаемые DevOps, которые генерируют мосты SQL из типов Golang, поэтому нам не нужно вручную определять их каждый раз, когда мы создаем новую таблицу. Мы также используем протоколы Google Protocol Buffers, которые автоматически создают безопасные сообщения для разных платформ и языков.

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

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

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