Технологии
Никита Логинов

Вместо программиста — кнопка «Сделать хорошо»: может ли компьютер писать код и не поздно ли уже «идти в айти»

Нейросеть AlphaCode выступила в конкурсах программирования на уровне человека. Но до полной автоматизации по-прежнему далеко — ИИ ещё долго не заменит разработчиков.

Демонстрация работы AlphaCode: слева — словесное описание задачи, справа — итог работы нейросети, изображение DeepMind

В начале февраля компания DeepMind представила нейросеть AlphaCode, которая смогла на равных участвовать в конкурсах по программированию и писать код неплохого качества. Главный научный сотрудник компании Ориол Виньялс заявил, что «сотрудники в восторге от потенциала» AlphaCode — от неё ждут способности помогать писать код и «создавать новые способы производства софта».

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

Что называют автоматизацией программирования

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

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

В 1952 году американская учёная и коммодор флота Грейс Хоппер создала первый компилятор — программу-переводчик с «очеловеченного» машинного кода на обычный. В этом языке ассемблера место многих машинных «слов» заняли более понятные mov (переместить) или add (сложить), что даже визуально сделало код проще. Язык ассемблера (он отличался для каждой модели компьютера) открыл вторую эпоху программирования — которая, впрочем, продлилась недолго.

Язык ассемблера и машинный код, изображение Android Authority

Возможностей ассемблера быстро перестало хватать, так что уже с конца 1950-х годов начали появляться языки третьего поколения: Fortran, ALGOL, COBOL, Basic, Pascal, C и так далее. В них ещё больше машинных инструкций было обёрнуто в понятные слова вроде begin, end, if, else, return. Формировались новые методы программирования, больше похожие на человеческое мышление. С годами языки программирования становились всё ближе к людям и всё дальше уходили от машинного кода.

Автоматизация через фреймворки и «умные» компиляторы

Сейчас программисты используют языки программирования третьего и четвёртого поколений (C++, C#, Python, PHP, Java, JavaScript). Понять код на таких языках даже без подготовки порой не труднее, чем средневековую поэзию, особенно если это Python или PHP. Фреймворки вроде .NET, Qt, Django, Spring и отдельные библиотеки дают миллионы готовых частей программного кода. Отпала надобность писать отдельно для каждой модели компьютера.

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

Программы-компиляторы не просто переводят исходный код в машинный, но и хорошо оптимизируют его. Ещё 20-30 лет назад имело смысл писать особо «тяжёлые» участки программы на языке ассемблера, чтобы выжать максимум скорости. В начале века компиляторы начали генерировать код не хуже былых ассемблерных вставок, а сейчас отдельные программисты уже вряд ли смогут оптимизировать программу так же хорошо, как компиляторы GCC, LLVM/Clang, MSVC или ICC.

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

Метапрограммирование: программы пишут программы

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

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

Программист, например, может написать краткий шаблон, «заготовку» нужной ему программы, а компилятор развернёт его в полноценный работающий код. Это умеют делать многие популярные языки программирования — шаблоны и макросы в том или ином виде есть в C++, Rust, Haskell, Python, Lua и так далее.

Макрос всего в шесть строк добавляет в LISP оператор for, изображение Пола Грэма

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

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

Софт, работающий по принципам метапрограммирования, давно не редкость. Приложения создают отчёты и документацию, веб-фреймворки генерируют HTML-страницы, которые когда-то писали полностью вручную. Браузеры и другие программы используют самомодификацию кода в виде JIT — компиляции исходного кода в машинный прямо во время его выполнения.

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

Блок code недоступен

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

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

Первая попытка создать ИИ: символический подход

В середине 1950-х годов учёные и инженеры, видя успехи компьютеров, пришли к мысли, что искусственный интеллект вполне можно создать в рамках обычной компьютерной программы. Причём не ИИ узкого профиля, а «сильный искусственный интеллект», аналог человеческого. Эта мысль стала доминирующим подходом к созданию ИИ вплоть до середины 1990-х годов.

Явную формулировку идее символического ИИ придали учёные Ньюэлл и Саймон в 1976 году: они выдвинули гипотезу, что интеллект невозможен без вычислений с участием чисел, слов, выражений и прочих символов — а с другой стороны, таких вычислений достаточно для появления или создания интеллекта. Этот тип ИИ, работающий на основе символов, в 1980-х годах получил название «старого доброго искусственного интеллекта» (GOFAI).

Поначалу прогресс в этой области действительно впечатлял. Появились языки LISP и Prolog, хорошо подходящие для программирования ИИ. На них разработчики написали множество экспертных систем, способных самостоятельно рассуждать и делать выводы, а также просто мощных программ для науки, промышленности, здравоохранения, госсектора, финансовых структур. И эти языки используются до сих пор — например, на Prolog написан один из компонентов «ИИ-компьютера» IBM Watson.

В 2011 году IBM Watson даже без доступа к интернету обыграл лучших игроков в аналог «Своей игры», фото IBM

Мощь языка LISP продемонстрировала программа SHRDLU в 1968 году: на транзисторном компьютере PDP-6 с примерно 144 килобайтами памяти она «понимала» текстовые команды на обычном английском языке и давала вразумительные ответы, логически связанные с запросами.

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

Но уже в тех же 1960-х годах символический подход начали жёстко критиковать: например, философ Дрейфус пришёл к выводу, что «потолок» такого ИИ — решение игровых задач, которые не имеют практической ценности. В 1973 году математик Лайтхилл в своём знаковом отчёте повторил этот аргумент в более категоричной форме: он показал, что ни одна система ИИ не добилась ожидаемых успехов, и что символический подход в принципе неспособен привести к созданию искусственного интеллекта.

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

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

Японская LISP-машина LIME, изображение IPSJ

Отчёт Лайтхилла повлёк за собой первую «зиму искусственного интеллекта» — интерес к созданию символического ИИ резко упал, власти и частные компании прекратили финансировать исследования. Ситуацию не спас и успех экспертных систем, под которые даже начали выпускать LISP-машины — компьютеры, аппаратно оптимизированные для выполнения программ на этом языке. Рынок LISP-машин потерпел крах в 1987 году, а к 1995 году стало очевидно: идея символического ИИ несостоятельна, нужен другой подход.

Вторая попытка создать ИИ: на уровень ниже смысла

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

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

Примерная разница между символическим и субсимволическим ИИ

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

Минус такого подхода — в его малой гибкости. Программист может заложить десятки вариантов слов, которые динамически соединяются в разные по форме вопросы с одним смыслом: «Как дела?». Но первое же «Чё как?» вызовет ступор у символического ИИ, потому что такая форма вопроса не выводится логически из заложенных вариантов.

Нейросеть разбивает входные данные (в данном случае — изображение крестика) на «частицы» и прогоняет через несколько слоёв нейронов, изображение Cornell University Library

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

Плюс этого подхода в том, что хорошо обученная нейросеть одинаково воспримет вопросы «Как дела?», «Как поживаешь?», «Чё как?» и так далее. Минус — в его «бессмысленности». Если символический ИИ в процессе работы логически рассуждает, что похоже на мышление, то нейросеть «рефлекторно» выдаёт ответы исключительно на основе статистики, выведенной из прошлого опыта — даже не осмысливая, о чём речь. Похожим образом работают бессознательные области мозга, порождая то, что называют интуицией.

Успех субсимволического ИИ — нейросети начали писать код

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

Примерно так выглядит рабочая станция для работы с нейросетями — «топовый» процессор и несколько мощных видеокарт, фото Bizon Tech

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

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

В 2015 году появился и первый «нейропрограммист»: учёный-информатик Андрей Карпати обучил нейросеть Torch7 на 400 мегабайтах исходного кода Linux, после чего она за ночь создала более 38 тысяч строк нового кода. По словам Карпати, этот код выглядит так, будто его написал программист на языке С — правда, он не работает и даже не компилируется из-за массы ошибок и неточностей.

После этого исследователи представили ещё несколько нейросетей, которые писали или тестировали код, а летом 2021 года случилось, пожалуй, знаковое событие. Microsoft и Open AI запустили GitHub Copilot — ИИ-сервис, который дописывает за программистом участки кода сразу по пять-десять строк. Это «умное автодополнение» сразу стало популярным на практике — к середине осени Copilot генерировал уже 30% нового кода, который программисты всего мира добавляли на GitHub.

Пример работы GitHub Copilot, изображение GitHub

И вот теперь, в начале февраля, компания DeepMind представляет нейросеть AlphaCode, похожую на то, что инженеры и учёные пытались создать 70 лет — она принимает описание задачи на английском языке и выдаёт код, который собирается и работает. Основой «опыта» для AlphaCode послужили 715 гигабайт исходного кода различных проектов на GitHub, а Copilot обучали на нескольких терабайтах созданного кода.

Когда нейросети заменят программистов

Получается, ИИ уже пишет программы — и как помощник программиста, и как сам программист, причём с неплохой результативностью. Copilot понравился половине разработчиков, которые пробовали его в работе, а AlphaCode смог попасть в 54,3% лучших участников конкурсов программирования. Ещё важнее, что нейросети выдают полностью новый код, а не компиляцию кусков кода, который написали люди (хотя изредка у Copilot бывает и такое).

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

Что мешает полностью автоматизировать программирование

  • AlphaCode относительно неплохо работает в «тепличных» условиях конкурсного программирования, где задания подробно и точно описаны, но в реальной разработке софта нет таких условий. Нейросети, как и любые субсимволические ИИ, очень узкоспециализированы, их обучают для одной конкретной задачи. Задача AlphaCode — участвовать в конкурсах по программированию.
  • Лучшие нейросети выдают всего по 10-20 строк работающего и нормально оформленного кода ценой огромного потребления энергии, тогда как типичное приложение или игра состоят из десятков тысяч строк кода. Причём даже такая небольшая производительность нейросети требует огромных массивов уже написанного кода для обучения. Чтобы AlphaCode смогла правильно написать 200 строк хотя бы в одной попытке из трёх — ей понадобится столько кода для обучения, сколько человечество ещё не создало. Сейчас нейросеть выдаёт меньше строк кода, чем содержится текста в её заданиях.
  • Субсимволический ИИ неспособен «размышлять», оперируя логикой, он работает чисто статистически, отталкиваясь от прошлого опыта. Нейросеть не может намеренно создать новый алгоритм или метод программирования, а в работе с новыми языками программирования нейросети совершенно бесполезны — без тысяч или миллионов программ, написанных людьми, этот тип ИИ просто не сможет обучиться языку.
  • Даже долгое и дорогое обучение на уже созданном коде не даёт высокой точности работы «ИИ-программистов». Нейросеть Codex от OpenAI умеет писать на 12 языках программирования, но её код запускается и работает без ошибок только в 37% случаев. Подсказки Copilot просто небезопасны — 40% генерируемого им кода содержит уязвимости. Пробуя нейросети в работе, разработчики обычно приходят к выводу, что до полной автоматизации их профессии ещё далеко.
  • Нейросети работают по принципу «чёрного ящика» — весь процесс проходит в скрытых слоях искусственных нейронов непрозрачно для людей, так что нельзя сказать, почему нейросеть в каждом конкретном случае выдаёт определённый вариант. Такая неподотчётность может помешать полной автоматизации программирования — люди предпочтут держать под контролем хотя бы ключевые моменты разработки софта.
  • Субсимволический ИИ неспособен осознанно придерживаться каких-либо принципов — например, программировать в едином стиле. Порой нейросети выдают очень странный код, который вряд ли написал бы живой разработчик — но тем не менее, он работает. По мере роста объёмов генерируемого кода количество таких участков будет увеличиваться, а это создаст массу проблем с поддержкой софта. Людям нужен человекочитаемый код, а не типично машинный фарш из символов — именно для этого изобретали компиляторы, фреймворки и прочие вещи.

К чему готовиться программистам уже сейчас

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

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

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

#лонгриды #программирование #ии #нейросети #будущее #it