我编写了一个非常简单的游戏,其工作方式如下:
给定一个4x4的正方形区域,玩家可以移动(上,右,下或左)。
在特工从未访问过的广场上获得奖励1。
踩到“死地”可获得-5的奖励,然后游戏将被重置。
在已经访问过的字段上移动会获得-1
进入“胜利场”(恰好有一个)可获得5分奖励,游戏也会被重置。
现在,我希望AI通过Q-Learning学习玩该游戏。
我如何组织输入/要素工程:
网络的输入是形状为1x4的数组,其中arr [0]表示上方的字段(向上移动时),arr [1]表示右侧的字段,arr [2]下方的字段,arr [3]左边的那个。
该数组可以保存的可能值:0、1、2、3
0 =“死场”,所以最坏的情况
1 =该字段位于4x4字段之外(因此您无法进入此处)或该字段已被访问
2 =未访问的字段(所以很好)
3 =“胜利领域”,所以是最好的情况
如您所见,我通过他们的奖励命令他们。
由于游戏以相同的方式进行输入(0 =向上移动,1 =向右移动,2 =向下移动,3 =向左移动),因此AI唯一需要学习的基本上是:选择具有最大值的数组索引。
但是不幸的是,它无法正常工作,即使在30.000集之后,网络也无法学习。
这是我的代码(包括开头的游戏):
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
答案 0 :(得分:0)
我终于找到了解决方案,花了我近一个星期的时间。
关键是要对我的输入内容进行一次热编码。这使我有16个输入神经元,而不是4个,但现在可以使用了。 在1.000集后,我大部分成功集约占91%。
我仍然想知道这样一个事实,即当输入未进行热编码时,它不起作用。我知道ANN会自动使用神经元接受的不同输入之间的更大或更小关系,这可能是一个缺点。 但是由于我以这种方式对输入进行排序,所以如果一个输入大于另一个输入,那也意味着输出应该以相同的方式更大。因此,如果ANN使用该关系,则这里没有缺点,这应该是一个优势。
因此,我认为最好不要对输入进行一次热编码,因为那样一来,我将极大地降低尺寸(4而不是16)。
显然,这个想法没有用。
但是,正如我所说的,现在可以使用16个输入。