强化学习不适用于这个非常简单的游戏,为什么?Q 学习



我编程了一个非常简单的游戏,它的工作方式如下:

给定一个4x4的正方形场地,玩家可以移动(向上、向右、向下或向左(。

  • 在一个从未去过的广场上,特工会给予奖励1。

  • 踩在"死场地"会得到-5的奖励,然后游戏将被重新设置。

  • 在已经访问过的场地上移动将获得-1 奖励

  • 进入"胜利场"(正好有一个(会得到5分的奖励,游戏也会被重新设定。


现在我想让AI通过Q-Learning学习玩这个游戏。

我如何组织输入/功能工程:

网络的输入是形状为1x4的数组,其中arr[0]表示上面的字段(向上移动时(,arr[1]表示右边的字段,arr[2]表示下面的字段,rr[3]表示左边的字段。

数组可以保存的可能值:0、1、2、3

0="死区",因此最坏的情况是

1=这将在4x4字段之外(所以你不能走到那里(,或者该字段已经被访问过

2=未访问字段(所以这是好事(

3="赢场",因此的最佳情况

正如你所看到的,我是根据他们的报酬来命令他们的。

由于游戏以相同的方式进行输入(0=向上移动,1=向右移动,2=向下移动,3=向左移动(,人工智能基本上只需要学习:选择具有最大值的数组索引。

但不幸的是,它不起作用,网络就是不学习,甚至在30000集之后也不学习。


这是我的代码(包括开头的游戏(:

import numpy as np
import random
Import tensorflow as tf
import matplotlib.pyplot as plt
from time import sleep
episoden = 0
felder = []
schon_besucht = []
playerx = 0
playery = 0
grafik = False
def gib_zustand():
# besonderes feature engineering:
# input besteht nur aus einer richtung, die one-hot-encoded ist; also 4 inputneuronen
# (glut, wand/besucht, unbesucht, sieg)
#
# es ist die richtung, die bewertet werden soll (also 1 outputneuron fuer eine richtung)
# rueckgabe hier: array, shape: 4x4 (s.o.)
global playerx
global playery
# oben 
if playery == 0:
oben = 1
else:
oben = felder[playery-1][playerx]
# rechts
if playerx == 4:
rechts = 1
else:
rechts = felder[playery][playerx+1]
# unten
if playery == 4:
unten = 1
else:
unten = felder[playery+1][playerx]
# links
if playerx == 0:
links = 1
else:
links = felder[playery][playerx-1]
return np.array([oben, rechts, unten, links])
def grafisch():
if grafik:
# encoding:
# glut = G, besucht = b, unbesucht = , sieg = S, Spieler = X
global felder
global playerx
global playery
print('')
for y in range(0,5):
print('|', end='')
for x in range(0,5):
if felder[y][x] == 0:
temp = 'G'
if felder[y][x] == 1:
temp = 'b'
if felder[y][x] == 2:
temp = ' '
if felder[y][x] == 3:
temp = 'S'
if y == playery and x == playerx:
temp = 'X'
print(temp, end='')
print('|', end='')
print('')
def reset():
print('--- RESET ---')
global playery
global playerx
global felder
global schon_besucht
playerx = 1
playery = 3
# anordnung
# glut = 0, wand/besucht = 1, unbesucht = 2, sieg = 3
felder = [[2 for x in range(0,5)] for y in range(0,5)]
# zwei mal glut setzen
gl1 = random.randint(1,3)
gl1_1 = random.randint(2,3) if gl1==3 else (random.randint(1,2) if gl1==1 else random.randint(1,3))
felder[gl1][gl1_1] = 0 # glut
# zweites mal
gl1 = random.randint(1,3)
gl1_1 = random.randint(2,3) if gl1==3 else (random.randint(1,2) if gl1==1 else random.randint(1,3))
felder[gl1][gl1_1] = 0 # glut
# pudding
felder[1][3] = 3
# ruecksetzen
schon_besucht = []
grafisch()
return gib_zustand()
def step(zug):
# 0 = oben, 1 = rechts, 2 = unten, 3 = links
global playerx
global playery
global felder
global schon_besucht
if zug == 0:
if playery != 0:
playery -= 1
if zug == 1:
if playerx != 4:
playerx += 1
if zug == 2:
if playery != 4:
playery += 1
if zug == 3:
if playerx != 0:
playerx -= 1
# belohnung holen
wert = felder[playery][playerx]
if wert==0:
belohnung = -5
if wert==1:
belohnung = -1
if wert==2:
belohnung = 1
if wert==3:
belohnung = 5
# speichern wenn nicht verloren
if belohnung != -5:
schon_besucht.append((playery,playerx))
felder[playery][playerx] = 1
grafisch()
return gib_zustand(), belohnung, belohnung==5, 0 # 0 damits passt
episoden = 0
tf.reset_default_graph()
#These lines establish the feed-forward part of the network used to choose actions
inputs1 = tf.placeholder(shape=[1,4],dtype=tf.float32)
#W1 = tf.Variable(tf.random_uniform([16,8],0,0.01))
W2 = tf.Variable(tf.random_uniform([4,4],0,0.01))
#schicht2 = tf.matmul(inputs1,W1)
Qout = tf.matmul(inputs1,W2)
predict = tf.argmax(Qout,1)
#Below we obtain the loss by taking the sum of squares difference between the target and prediction Q values.
nextQ = tf.placeholder(shape=[1,4],dtype=tf.float32)
loss = tf.reduce_sum(tf.square(nextQ - Qout))
trainer = tf.train.GradientDescentOptimizer(learning_rate=0.1)
updateModel = trainer.minimize(loss)
init = tf.initialize_all_variables()
# Set learning parameters
y = .99
e = 0.1
num_episodes = 10_000
#create lists to contain total rewards and steps per episode
jList = []
rList = []
with tf.Session() as sess:
sess.run(init)
for i in range(num_episodes):             
#Reset environment and get first new observation
s = reset()
rAll = 0
d = False
j = 0
#The Q-Network        
while j < 99:
j+=1
#Choose an action by greedily (with e chance of random action) from the Q-network
a,allQ = sess.run([predict,Qout],feed_dict={inputs1:s.reshape(1,4)}) # berechnet prediction fuer input (input scheint hier one hot encoded zu sein)
if np.random.rand(1) < e:
a[0] = random.randint(0,3)                 
#Get new state and reward from environment
s1,r,d,_ = step(a[0])
#Obtain the Q' values by feeding the new state through our network
Q1 = sess.run(Qout,feed_dict={inputs1:s1.reshape(1,4)})
#Obtain maxQ' and set our target value for chosen action.
maxQ1 = np.max(Q1)

targetQ = allQ
targetQ[0,a[0]] = r + y*maxQ1
#Train our network using target and predicted Q values
_,W1 = sess.run([updateModel,W2],feed_dict={inputs1:s.reshape(1,4),nextQ:targetQ})
rAll += r
s = s1
if r == -5 or r == 5:
if r == 5:
episoden+=1
reset()
#Reduce chance of random action as we train the model.
e = 1./((i/50) + 10)
break
jList.append(j)
#print(rAll)
rList.append(rAll)
print("Percent of succesful episodes: " + str((episoden/num_episodes)*100) + "%")
plt.plot(rList)
plt.plot(jList)

我在一个模拟问题中读到,Q值过高的原因可能是,事实上,代理人有可能在游戏中获得无限的高总奖励。如果特工可以踩到已经访问过的田地,并获得1的奖励,那么这里的情况就是这样。那么,当然,可能的总回报将是无限的。但这里的情况并非如此:玩家这样做会得到糟糕的奖励(-1(。小计算:获胜的场地将获得5分的奖励。具有1的未访问字段。至少有一个死胡同。总共有16个字段。最大可能总奖励:14*1+1*5=19

我终于找到了解决方案,花了将近一周的时间。

关键是让我的输入一个热编码。这使我有16个输入神经元,而不是4个,但现在它起作用了。在1000集之后,我的成功率大多在91%左右。

我仍然在想,当输入不是一个热编码时,它就不起作用了。我知道神经网络会自动使用神经元不同输入之间的较大较小关系,这可能是一个缺点。但由于我是以这种方式对输入进行排序的,因此如果一个输入大于另一个输入,也意味着输出应该以同样的方式更大。因此,如果人工神经网络使用这些关系式,就没有缺点,相反,这应该是一个优点。

因此,我认为最好不要对输入进行热编码,因为这样我可以极大地降低维度(4而不是16(。

显然,这个想法没有奏效。

然而,正如我所说,有了16个输入,它现在就可以工作了。

最新更新