Давайте Попробуем Q-Learning, Историю В Трех Частях

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

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

Задача соперников, соответственно, его обогнать.

Очки начисляются за каждое движение агента в симуляции.

Во второй части статьи будут рассмотрены основы алгоритма q-learning и его реализация.

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

Давайте проанализируем влияние этих параметров на результативность его игры.

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

Цель — прикоснуться к самой сути алгоритма, так сказать, потрогать его руками.

Для реализации мы будем использовать только «чистый» Python 3.

Давайте попробуем q-learning, историю в трех частях



Часть 1: Создание среды

Среда, в которой действует агент, представлена двумерным полем размера n. Вот как это выглядит:

Давайте попробуем q-learning, историю в трех частях

Здесь агент представлен одним, враг — двумя.

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

класс среды

  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
   

class W: def __init__(self,n): self.n=n self.P=P(1,1,n) self.ens=[EN(3,3,n),EN(4,4,n),EN(5,5,n)]

Типичный актер: агент или противник.

актерский класс

class un: def __init__(self,x,y): self.x = x self.y = y def getxy(self): return self.x, self.y

Описание агента.

класс агента

class P(un): def __init__(self,x,y,n): self.n=n un.__init__(self,x,y)

Стратегия агента пока пуста.

Стратегия вызова метода агента

def strtg(self): return 0,0

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

Набор разрешенных движений на примере соперника:

Давайте попробуем q-learning, историю в трех частях

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

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

Запишем его возможные действия с проверкой обоснованности смещения.

Метод принимает изменение координат агента и обновляет его положение.

метод перемещения агента

def move(self): dx,dy=self.strtg() a=self.x+dx b=self.y+dy expr=((0<=a<self.n) and (0<=b<self.n)) if expr: self.x=a self.y=b

Описание врага.

класс врага

class EN(un): def __init__(self,x,y,n): self.n=n un.__init__(self,x,y)

Перемещайте врага, проверяя, приемлемо ли движение.

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

метод хода противника

def move(self): expr=False while not expr: a=self.x+random.choice([-1,0,1]) b=self.y+random.choice([-1,0,1]) expr=((0<=a<self.n) and (0<=b<self.n)) if expr: self.x=a self.y=b

Моделирование в один шаг.

Обновите позиции противника и агентов.

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

def step(self): for i in self.ens: i.move() self.P.move()

Визуализация операции моделирования.

метод окружения, рисование поля и положения действующих лиц

def pr(self): print('\n'*100) px,py=self.P.getxy() self.wmap=list([[0 for i in range(self.n)] for j in range(self.n)]) self.wmap[py][px]=1 for i in self.ens: ex,ey=i.getxy() self.wmap[ey][ex]=2 for i in self.wmap: print(i)

Моделирование, основные этапы: 1. Регистрация координат актеров.

2. Проверка того, достигнуты ли условия для завершения моделирования.

3. Начало цикла обновления хода моделирования.

— Рисуем окружающую среду.

— Обновление состояния моделирования.

— Регистрация координат актеров.

— Проверка того, что условия для завершения моделирования были достигнуты.

4. Рисуем окружение.

метод среды, воспроизведение последовательности моделирования

def play(self): px,py=self.P.getxy() bl=True for i in self.ens: ex,ey=i.getxy() bl=bl and (px,py)!=(ex,ey) iter=0 while bl: time.sleep(1) wr.pr() self.step() px,py=self.P.getxy() bl=True for i in self.ens: ex,ey=i.getxy() bl=bl and (px,py)!=(ex,ey) print((px,py),(ex,ey)) print('___') iter=iter+1 print(iter)

Инициализация среды и запуск.

запуск симуляции

if __name__=="__main__": wr=W(7) wr.play() wr.pr()



Часть 2. Обучение агентов

Метод q-обучения основан на введении функции Q, отражающей значение каждого возможного действия агента a для текущего состояния s, в котором в данный момент находится моделирование.

Или кратко:

Давайте попробуем q-learning, историю в трех частях

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

Он также включает оценку того, какое вознаграждение может получить агент в будущем.

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

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

Давайте запишем это как переменную

Давайте попробуем q-learning, историю в трех частях

.

Далее определяем значение максимального ожидаемого вознаграждения на последующих ходах:

Давайте попробуем q-learning, историю в трех частях

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

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

Давайте попробуем q-learning, историю в трех частях

Таким образом, ошибка агента в предсказании значения функции Q для текущего хода запишется следующим образом:

Давайте попробуем q-learning, историю в трех частях

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

Тогда формула итерационного расчета функции Q имеет вид:

Давайте попробуем q-learning, историю в трех частях

Общая формула итерационного расчета функции Q:

Давайте попробуем q-learning, историю в трех частях

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

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

В то же время размер пространства состояний может существенно повлиять на скорость обучения.

агентный метод, признаки, описывающие состояние окружающей среды

def get_features(self,x,y): features=[] for i in self.ens: #800-1400 ex,ey=i.getxy() features.append(ex) features.append(ey) features.append(x) features.append(y) return features

Создание класса модели Q. Переменная гамма — это коэффициент демпфирования, позволяющий нивелировать вклад последующих вознаграждений.

Переменная альфа — это коэффициент скорости обучения модели.

Переменная состояния представляет собой словарь состояний модели.

модель класса Q

class Q: def __init__(self): self.gamma=0.95 self.alpha=0.05 self.state={}

Давайте получим состояние агента на данном ходу.

метод Q модели по запросу получает состояние агента

def get_wp(self,plr): self.plr=plr

Обучаем модель.

Метод Q-модели, одноэтапное обучение модели

def run_model(self,silent=1): self.plr.prev_state=self.plr.curr_state[:-2]+(self.plr.dx,self.plr.dy) self.plr.curr_state=tuple(self.plr.get_features(self.plr.x,self.plr.y))+( self.plr.dx,self.plr.dy) if not silent: print(self.plr.prev_state) print(self.plr.curr_state) r=self.plr.reward if self.plr.prev_state not in self.state: self.state[self.plr.prev_state]=0 nvec=[] for i in self.plr.actions: cstate=self.plr.curr_state[:-2]+(i[0],i[1]) if cstate not in self.state: self.state[cstate]=0 nvec.append(self.state[cstate]) nvec=max(nvec) self.state[self.plr.prev_state]=self.state[self.plr.prev_state]+self.alpha*(-self.state[self.plr.prev_state]+r+self.gamma*nvec)

Способ получения наград. Агент получает вознаграждение за продолжение моделирования.

За столкновение с противником применяется штраф.

метод окружения, определяющий размер вознаграждения

def get_reward(self,end_bool): if end_bool: self.P.reward=1 else: self.P.reward=-1

Агентская стратегия.

Выбирает лучшее значение из словаря состояний модели.

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

Это делается для изучения связанных возможных действий в данном состоянии.

метод агента, выбор следующего хода

def strtg(self): if random.random()<self.eps: act=random.choice(self.actions) else: name1=tuple(self.get_features(self.x,self.y)) best=[(0,0),float('-inf')] for i in self.actions: namea=name1+(i[0],i[1]) if namea not in self.QM.state: self.QM.state[namea]=0 if best[1]<self.QM.state[namea]: best=[i,self.QM.state[namea]] act=best[0] return act

Покажем, что удалось узнать агенту при полной передаче информации о состоянии симуляции (абсолютные координаты агента, абсолютные координаты противника): результат работы алгоритма (гифка ~1МБ)

Давайте попробуем q-learning, историю в трех частях



Часть 3. Варьирование параметров, отражающих состояние среды для агента

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

особенности 1

def get_features(self,x,y): features=[] return features

В результате работы этого алгоритма агент набирает в среднем 40-75 баллов за симуляцию.

Расписание тренировок:

Давайте попробуем q-learning, историю в трех частях

Давайте добавим агент данных.

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

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

Также важно иметь представление о том, насколько близок агент к грани.

особенности 2

def get_features(self,x,y): features=[] for i in self.ens: ex,ey=i.getxy() dx=abs(x-ex) dy=abs(y-ey) l=hypot(dx,dy) features.append(l) to_brdr=min(x,y,self.n-1-x,self.n-1-y) features.append(to_brdr) return features

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

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

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

Расписание тренировок:

Давайте попробуем q-learning, историю в трех частях

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

def get_features(self,x,y): features=[] for i in self.ens: ex,ey=i.getxy() features.append(x-ex) features.append(y-ey) # if near wall x & y. if x==0: features.append(-1) elif x==self.n-1: features.append(1) else: features.append(0) if y==0: features.append(-1) elif y==self.n-1: features.append(1) else: features.append(0) return features

Этот набор переменных сразу существенно поднимает средний балл до 400-800. Расписание тренировок:

Давайте попробуем q-learning, историю в трех частях

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

А именно: абсолютные координаты агента и противника.

особенности 4

def get_features(self,x,y): features=[] for i in self.ens: ex,ey=i.getxy() features.append(ex) features.append(ey) features.append(x) features.append(y) return features

Такой набор параметров позволяет агенту набрать 800–1400 баллов в тестовых симуляциях.

Расписание тренировок:

Давайте попробуем q-learning, историю в трех частях

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

Однако у полного описания окружающей среды есть обратная сторона.

А именно, взрывной рост пространства состояний с увеличением объёма передаваемой агенту информации.

Чтобы обойти эту проблему, были разработаны алгоритмы аппроксимации пространства состояний, такие как алгоритм DQN. Полезные ссылки: habrahabr.ru/post/274597 ai.berkeley.edu/reinforcement.html Полный код программы: 1. Базовая модель среды (из части 1)

import random import time class W: def __init__(self,n): self.n=n self.P=P(1,1,n) self.ens=[EN(3,3,n),EN(4,4,n),EN(5,5,n)] def step(self): for i in self.ens: i.move() self.P.move() def pr(self): print('\n'*100) px,py=self.P.getxy() self.wmap=list([[0 for i in range(self.n)] for j in range(self.n)]) self.wmap[py][px]=1 for i in self.ens: ex,ey=i.getxy() self.wmap[ey][ex]=2 for i in self.wmap: print(i) def play(self): px,py=self.P.getxy() bl=True for i in self.ens: ex,ey=i.getxy() bl=bl and (px,py)!=(ex,ey) iter=0 while bl: time.sleep(1) wr.pr() self.step() px,py=self.P.getxy() bl=True for i in self.ens: ex,ey=i.getxy() bl=bl and (px,py)!=(ex,ey) print((px,py),(ex,ey)) print('___') iter=iter+1 print(iter) class un: def __init__(self,x,y): self.x = x self.y = y def getxy(self): return self.x, self.y class P(un): def __init__(self,x,y,n): self.n=n un.__init__(self,x,y) def strtg(self): return 0,0 def move(self): dx,dy=self.strtg() a=self.x+dx b=self.y+dy expr=((0<=a<self.n) and (0<=b<self.n)) if expr: self.x=a self.y=b class EN(un): def __init__(self,x,y,n): self.n=n un.__init__(self,x,y) def move(self): expr=False while not expr: a=self.x+random.choice([-1,0,1]) b=self.y+random.choice([-1,0,1]) expr=((0<=a<self.n) and (0<=b<self.n)) if expr: self.x=a self.y=b if __name__=="__main__": wr=W(7) wr.play() wr.pr()

2. Модель среды с обучением агентов на 500+1500 симуляциях.



import random import time from math import hypot,pi,cos,sin,sqrt,exp import plot_epoch class Q: def __init__(self): self.gamma=0.95 self.alpha=0.05 self.state={} def get_wp(self,plr): self.plr=plr def run_model(self,silent=1): self.plr.prev_state=self.plr.curr_state[:-2]+(self.plr.dx,self.plr.dy) self.plr.curr_state=tuple(self.plr.get_features(self.plr.x,self.plr.y))+(self.plr.dx,self.plr.dy) if not silent: print(self.plr.prev_state) print(self.plr.curr_state) r=self.plr.reward if self.plr.prev_state not in self.state: self.state[self.plr.prev_state]=0 nvec=[] for i in self.plr.actions: cstate=self.plr.curr_state[:-2]+(i[0],i[1]) if cstate not in self.state: self.state[cstate]=0 nvec.append(self.state[cstate]) nvec=max(nvec) self.state[self.plr.prev_state]=self.state[self.plr.prev_state]+self.alpha*( -self.state[self.plr.prev_state]+r+self.gamma*nvec) class un: def __init__(self,x,y): self.x = x self.y = y self.actions=[(0,0),(-1,-1),(0,-1),(1,-1),(-1,0), (1,0),(-1,1),(0,1),(1,1)] def getxy(self): return self.x, self.y class P(un): def __init__(self,x,y,n,ens,QM,wrld): self.wrld=wrld self.QM=QM self.ens=ens self.n=n self.dx=0 self.dy=0 self.eps=0.95 self.prev_state=tuple(self.get_features(x,y))+(self.dx,self.dy) self.curr_state=tuple(self.get_features(x,y))+(self.dx,self.dy) un.__init__(self,x,y) def get_features(self,x,y): features=[] # for i in self.ens: #80-100 # ex,ey=i.getxy() # dx=abs(x-ex) # dy=abs(y-ey) # l=hypot(dx,dy) # features.append(l) # to_brdr=min(x,y,self.n-1-x,self.n-1-y) # features.append(to_brdr) for i in self.ens: #800-1400 ex,ey=i.getxy() features.append(ex) features.append(ey) features.append(x) features.append(y) # for i in self.ens: #800-1400 # ex,ey=i.getxy() # features.append(x-ex) # features.append(y-ey) # features.append(self.n-1-x) # features.append(self.n-1-y) # for i in self.ens: #400-800 # ex,ey=i.getxy() # features.append(x-ex) # features.append(y-ey) # # if near wall x & y. # if x==0: # features.append(-1) # elif x==self.n-1: # features.append(1) # else: # features.append(0) # if y==0: # features.append(-1) # elif y==self.n-1: # features.append(1) # else: # features.append(0) # features=[] #40-80 return features def strtg(self): if random.random()<self.eps: act=random.choice(self.actions) else: name1=tuple(self.get_features(self.x,self.y)) best=[(0,0),float('-inf')] for i in self.actions: namea=name1+(i[0],i[1]) if namea not in self.QM.state: self.QM.state[namea]=0 if best[1]<self.QM.state[namea]: best=[i,self.QM.state[namea]] act=best[0] return act def move(self): self.dx,self.dy=self.strtg() a=self.x+self.dx b=self.y+self.dy expr=((0<=a<self.n) and (0<=b<self.n)) if expr: self.x=a self.y=b class EN(un): def __init__(self,x,y,n): self.n=n un.__init__(self,x,y) def move(self): expr=False cou=0 while not expr: act=random.choice(self.actions) a=self.x+act[0] b=self.y+act[1] expr=((0<=a<self.n) and (0<=b<self.n)) if expr: self.x=a self.y=b class W: def __init__(self,n,QModel): self.ens=[EN(n-2,n-2,n)]#,EN(n-2,n-1,n),EN(n-1,n-2,n),EN(n-1,n-1,n)] self.P=P(1,1,n,self.ens,QModel,self) self.n=n self.QM=QModel self.QM.get_wp(self.P) def step(self): self.P.move() for i in self.ens: i.move() def pr(self,silent=1): """print map""" #print('\n'*100) px,py=self.P.getxy() self.wmap=list([[0 for i in range(self.n)] for j in range(self.n)]) self.wmap[py][px]=1 for i in self.ens: ex,ey=i.getxy() self.wmap[ey][ex]=2 if not silent: for i in self.wmap: print(i) def is_finished(self): px,py=self.P.getxy() end_bool=True for i in self.ens: ex,ey=i.getxy() end_bool=end_bool and ((px,py)!=(ex,ey)) return end_bool def get_reward(self,end_bool): if end_bool: self.P.reward=1 else: self.P.reward=-1 def play(self,silent=1,silent_run=1): end_bool=self.is_finished() iter=0 while end_bool: self.pr(silent) self.step() end_bool=self.is_finished() self.get_reward(end_bool) if silent_run: self.QM.run_model(silent) if not silent: print('___') time.sleep(0.1) iter=iter+1 return iter QModel=Q() plot=plot_epoch.epoch_graph() for i in range(500): wr=W(5,QModel) wr.P.eps=0.90 iter=wr.play(1) wr.pr(1) plot.plt_virt_game(W,QModel) for i in range(1500): wr=W(5,QModel) #print(len(QModel.state)) wr.P.eps=0.2 iter=wr.play(1) wr.pr(1) plot.plt_virt_game(W,QModel) plot.plot_graph() print('___') for i in range(10): wr=W(5,QModel) wr.P.eps=0.0 iter=wr.play(0) wr.pr(0)

3. Модуль вывода графика (plot_epoch.py).



import matplotlib.pyplot as plt class epoch_graph: def __init__(self): self.it=0 self.iter=[] self.number=[] self.iter_aver=[] def plt_append(self,iter): self.it=self.it+1 self.iter.append(iter) self.number.append(self.it) if len(self.iter)>100: self.iter_aver.append(sum(self.iter[-100:])/100) else: self.iter_aver.append(sum(self.iter)/len(self.iter)) def plt_virt_game(self,W,QModel): wr=W(5,QModel) wr.P.eps=0.0 iter=wr.play(1,0) self.plt_append(iter) def plot_graph(self): plt.plot(self.number,self.iter_aver) plt.xlabel('n_epoch') plt.ylabel('aver. score') plt.show()

Теги: #python3 #обучение с подкреплением #AI #q-learning #python #программирование #Алгоритмы

Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.