Эта статья представляет собой краткую заметку о реализации алгоритма q-обучения для управления агентом в стохастической среде.
Первая часть статьи будет посвящена созданию среды для проведения симуляций — мини-игр на поле nxn, в которых агент должен как можно дольше держаться подальше от противников, перемещающихся в случайном порядке.
Задача соперников, соответственно, его обогнать.
Очки начисляются за каждое движение агента в симуляции.
Во второй части статьи будут рассмотрены основы алгоритма q-learning и его реализация.
В третьей части мы попробуем изменить параметры, определяющие восприятие агентом окружающей среды.
Давайте проанализируем влияние этих параметров на результативность его игры.
Я специально сместил акцент в сторону использования минимального количества сторонних модулей.
Цель — прикоснуться к самой сути алгоритма, так сказать, потрогать его руками.
Для реализации мы будем использовать только «чистый» Python 3.
Часть 1: Создание среды
Среда, в которой действует агент, представлена двумерным полем размера n. Вот как это выглядит:Здесь агент представлен одним, враг — двумя.
Теперь давайте посмотрим непосредственно на код. Переменные, описывающие окружение: размер поля, агент, список оппонентов.
класс среды
Типичный актер: агент или противник.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
Каждый из участников симуляции может совершать движение в любом направлении, в том числе по диагонали, либо актер также может вообще не двигаться.
Набор разрешенных движений на примере соперника:
Благодаря диагональным движениям у противника всегда есть возможность настигнуть агента, как бы он ни старался уйти.
Это обеспечивает конечное число ходов моделирования для любого уровня обучения агента.
Запишем его возможные действия с проверкой обоснованности смещения.
Метод принимает изменение координат агента и обновляет его положение.
метод перемещения агента 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 при каждом движении агента.
Прежде всего, следует определить размер вознаграждения, которое получит агент в этом ходу.
Давайте запишем это как переменную
.
Далее определяем значение максимального ожидаемого вознаграждения на последующих ходах:
Теперь мы должны решить, что имеет большую ценность для агента: немедленное вознаграждение или будущее.
Эту проблему решает дополнительный коэффициент оценочной составляющей последующих наград. В результате значение функции Q, предсказанное агентом на этом шаге, должно быть максимально близко к значению:
Таким образом, ошибка агента в предсказании значения функции Q для текущего хода запишется следующим образом:
Введем коэффициент, который будет регулировать скорость обучения агента.
Тогда формула итерационного расчета функции Q имеет вид:
Общая формула итерационного расчета функции Q:
Выделим основные признаки, которые составят описание среды с точки зрения агента.
Чем обширнее набор параметров, тем точнее его реакция на изменения окружающей среды.
В то же время размер пространства состояний может существенно повлиять на скорость обучения.
агентный метод, признаки, описывающие состояние окружающей среды 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МБ)
Часть 3. Варьирование параметров, отражающих состояние среды для агента
Для начала рассмотрим ситуацию, когда агент вообще не получает никакой информации об окружающей среде.
особенности 1 def get_features(self,x,y):
features=[]
return features
В результате работы этого алгоритма агент набирает в среднем 40-75 баллов за симуляцию.
Расписание тренировок:
Давайте добавим агент данных.
Прежде всего, вам нужно знать, как далеко находится враг.
Давайте посчитаем это, используя евклидово расстояние.
Также важно иметь представление о том, насколько близок агент к грани.
особенности 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 баллами.
Несмотря на то, что наша реакция на изменения окружающей среды улучшилась, мы по-прежнему теряем львиную долю необходимых нам данных.
Таким образом, агент еще не знает, в каком направлении двигаться, чтобы разорвать дистанцию с противником или отойти от края края.
Расписание тренировок:
Следующий этап – добавить агенту знания, с какой стороны от него находится противник, и находится ли агент на краю поля или нет. особенности 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. Расписание тренировок:
Наконец, мы передаем алгоритму всю доступную информацию об окружающей среде.
А именно: абсолютные координаты агента и противника.
особенности 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. Вращая параметры, отвечающие за восприятие окружающей среды агентом, мы наглядно увидели, как адекватная передача информации и учет всех аспектов при описании среды влияют на эффективность его действий.
Однако у полного описания окружающей среды есть обратная сторона.
А именно, взрывной рост пространства состояний с увеличением объёма передаваемой агенту информации.
Чтобы обойти эту проблему, были разработаны алгоритмы аппроксимации пространства состояний, такие как алгоритм 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 #программирование #Алгоритмы
-
Защита Zimbra Ose От Перебора И Dos-Атак
19 Oct, 24 -
Prodbg Переходит На Rust
19 Oct, 24 -
Железолисп
19 Oct, 24 -
Команда Автоматизации Тестирования Unity 3D
19 Oct, 24