Что такое КартПол?
CartPole — это задача среды OpenAI, целью которой является проверка вашей способности запрограммировать решение для балансировки шеста, прикрепленного к тележке, как показано ниже. Если шест смещается более чем на 15 градусов от вертикали, вы проигрываете, а если тележка смещается за экран, вы проигрываете. У меня есть только теоретические знания об обучении с подкреплением, поэтому я попытался найти решение без поиска в Google. Вот что я сделал.
Состояние и действие
На каждом временном шаге вы должны слегка толкать тележку влево или вправо. Вы должны выбрать один из этих двух вариантов. Эти параметры известны как «пространство действий». На каждом временном шаге вам предоставляется 4 части информации для принятия решения: положение тележки, скорость тележки, угол полюса, скорость полюса на конце. Эти фрагменты информации в совокупности известны как «состояние».
Установление базовой линии
Официально CartPole считается пройденным, если вы удерживали шест в течение 200 шагов по времени. Я не придерживался этого стандарта, так как пытался решить все это без чьей-либо помощи. Сначала я выполнял случайные действия в 1000 симуляциях столба тележки (каждая симуляция называется эпизодом). Это сгенерировало среднюю симуляцию продолжительностью около 21 временного шага. Однако я не хотел быть просто лучше случайного. Я создал «жестко закодированное» решение, в котором, если столб наклонялся вправо, он двигался вправо, если он наклонялся влево, он двигался влево. Это сгенерировало среднюю продолжительность 40 шагов. Теперь это был мой базовый уровень.
Попытки решения
Первая попытка линейной регрессии
Это очень простое решение, но я подумал, почему бы не попробовать. Вот что я сделал.
- Соберите данные из множества случайных симуляций.
- Используя состояние и действие в качестве входных данных, предскажите, как долго шест остается в поднятом состоянии.
- Запустите model.predict() дважды на каждом временном шаге с подключенными «левым» и «правым» для действий и выберите тот, у которого больше значение оставшегося времени.
Проблема в том, что, поскольку между переменными нет эффекта взаимодействия, одно значение для действия всегда будет генерировать большее прогнозируемое оставшееся время.
Случайный лес второй попытки
Это следующее решение было очень похоже на первое, но я понял, что должен учитывать эффекты взаимодействия между состоянием и действием. Случайный лес сделает именно это, и было проще заменить код для случайного леса, чем добавить полиномиальные и интерактивные эффекты к линейной регрессии.
- Соберите данные из множества случайных симуляций.
- Используя состояние и действие в качестве входных данных, предскажите, как долго шест остается в поднятом состоянии.
- Запустите модель дважды на каждом временном шаге с подключенными «левым» и «правым» для действий и выберите тот, у которого больше значение.
Основная проблема с этим решением заключалась в том, что действие имело настолько низкую важность функции, что было бесполезно предсказывать время, в течение которого оно оставалось активным. Это потому, что он был сильно обучен плохим эпизодам, которые длились недолго, когда случайные действия вообще не помогали. В конце концов, большинство эпизодов были очень плохими примерами. На самом деле, выбор одного и того же действия снова и снова генерирует эпизоды, длящиеся примерно 9 временных шагов, а случайное поддерживает их только немного дольше.
Нейронная сеть третьей попытки
Вместо того, чтобы собирать данные о множестве симуляций, я решил, что буду обучать/переобучать модель в конце каждого эпизода. Я думал, что это позволит получить более качественные данные для обучения, поскольку модель будет улучшаться итеративно, и я буду получать все больше и больше успешных данных для обучения.
- Соберите обучающие данные из одного случайного эпизода.
- Обучите нейронную сеть на случайном эпизоде, чтобы предсказать, как долго шест простоит.
- Запустите еще один эпизод, в котором нейронная сеть предсказывает, как долго она будет работать, время от времени выбирая случайное действие.
- Продолжайте обновлять нейросеть в конце каждого эпизода, в итоге получая случайные действия.
Проблема, с которой я столкнулся при таком подходе, заключается в том, что нейронная сеть начинает с предпочтения одного действия (из-за случайных весов), поэтому она предсказывает одно и то же действие. Затем сеть стала очень точной, предсказывая, что игра закончится за 9 шагов, потому что именно это происходит, когда вы всегда принимаете одно решение. Модель, по сути, сводилась к целенаправленному немедленному завершению игры, чтобы она была очень точной. Я видел, как другие люди успешно реализовывали решение таким образом, но они, несомненно, лучше справляются с оптимизацией компромисса между выполнением случайных действий для обучения и использованием модели, а также проектированием структуры своей нейронной сети.
Четвертая попытка логистической регрессии
Затем я решил переформатировать то, как я думал о проблеме. До этого момента я, по сути, использовал действие в качестве входных данных и дважды запускал model.predict(state, action) с разными значениями для действия. Поэтому вместо этого я решил использовать состояние для предсказания действия. Это преобразовало проблему из проблемы типа регрессии, где я предсказываю непрерывный результат (оставшиеся временные шаги), в проблему бинарной классификации (влево или вправо). Если бы я сделал это со случайными данными для обучения, я бы столкнулся с проблемой, когда ни одно из состояний не коррелирует с действием (поскольку это действия, выбранные случайным образом. Чтобы решить эту проблему, я удалил эпизоды, которые не длились очень долго Остальные эпизоды хоть и генерировались случайным образом, но имели хорошие примеры действий.
- Соберите случайные данные из многих эпизодов.
- Удалите данные, где полюс не оставался стоять в течение по крайней мере 100 временных шагов. Остальные данные показали только хорошие действия, предпринятые в каждом штате.
- Обучить логистическую регрессию X=состояние, Y=действия.
- Запустите новую модель и используйте состояние для прогнозирования действий, а затем выполните эти действия.
Благодаря этому решению модель продержалась около 60 временных шагов. Это превосходит мой жестко запрограммированный базовый уровень на 20 шагов. В этом все еще есть недостатки, сначала мне пришлось сгенерировать много данных, а затем выбросить большую часть из них, так как это были плохие (то есть сортированные) эпизоды. Учитывая, что некоторые люди сделали решения, которые решают эту пару сотен эпизодов, я не очень хорошо справился. Во-вторых, учитывая, что оно длилось всего 60 шагов, чужие решения на 200 тоже не велики.
Увезти
Вот основные вещи, которые я извлек из этого:
- Работать над обучением с подкреплением интересно, и гораздо веселее, чем многие другие проекты машинного обучения, поскольку вы видите влияние своих моделей на что-то «осязаемое» (хотя и цифровое). Что, на мой взгляд, отличает ИИ от МО, так это то, что ИИ способен принимать решения, а МО — нет. ИИ может принимать эти решения (и обычно так и делает) на основе результатов машинного обучения. Обучение с подкреплением кажется наиболее близким к ИИ и будет тем, чем люди считают ИИ в ближайшие годы.
- Простые решения работают! Выучить логистическую регрессию довольно легко, так почему бы не сделать это упражнением на раннем этапе студенческой карьеры? Это, безусловно, более увлекательно, чем титаническая классификация.
- Мне нужно больше узнать о глубоком обучении. Хотя я только что сказал, что «простые решения работают», по мере того, как проблемы, с которыми я сталкиваюсь, усложняются, мне потребуются лучшие методы. Помимо других проблем, у людей есть лучшие решения для CartPole с помощью нейронных сетей, поэтому я определенно должен удвоить усилия, чтобы найти лучшие решения.