使用强化学习和q-learning来玩贪吃蛇的Python实现
依赖
Python3.6
Keras
Scikit-image
Tensorflow
背景
我做这个项目的背景当然是要学习更多关于强化学习的知识,但同时也要通过经典的蛇游戏来获得一段记忆之旅。
强化学习
在蛇游戏中,我们控制着一条蛇,它想要吃苹果(奇怪的对吧?!),当它成功的时候,它会得到奖励,而且它的大小也会增加。蛇可以自由地在赛场周围移动,但如果蛇撞到墙壁或它自己,游戏就结束了。为了控制它,我们可以使用四个不同的动作,箭头向上,向下,向左和向右。
那么我们如何教一个神经网络来解决这个游戏呢?如果我们从游戏中得到一张图片然后从那张图片中我们决定采取什么行动,向上,向下,向左或向右。我们前面有一个经典的分类问题。
如果我们有分类问题,我们需要大量的数据来进行训练,因为我们没有任何数据,我们需要自动创建数据。因此,我不玩游戏,而是让一个agent 玩游戏,然后随机的行动,然后游戏会根据动作回报一个奖励。
建议奖励应该介于-1和1之间,因此我选择当蛇吃掉苹果时的奖励为1,当蛇死亡时为-1,否则奖励为0。在奖励的基础上,agent应该学习如何玩这个游戏。
在许多方面,agent学习游戏的方式与动物/人类学习的方式相似。举个例子,如果你想学习一只狗如何坐下,那么告诉它坐下,开始时它不明白我们在说什么,但是在一段时间后它会坐下来,然后得到奖励。下次我们叫狗坐的时候,它会把这些点连起来,坐下来得到奖励。就像在蛇的游戏中,狗狗也能达到一个局部最小值,当我们只显示狗的糖果而不告诉它坐下,它会坐下,当它看到糖果,尽管我们没有说坐。
一个例子是当游戏到达一个局部最小值时,蛇会绕着游戏的中间转,因为它知道苹果有更高的概率出现在那里。为了避免这种情况,如果代理人在短期内做出长期有益而不是短期的行为,我们必须给它一些奖励。
卷积神经网络
卷积神经网络或CNN是一种适合于将输入格式转换为矩阵或张量而不是向量的网络。在我们的例子中,我们使用的输入是80x80x1图像。卷积是应用于图像的滑动滤波器。在gif下面的一个3x3内核过滤器它应用于一个黑白图片。0是黑的,1是白的。筛选器将元素的值相乘,并对其进行汇总。
实践中的工作原理如下所示,其中内核过滤器应用于泰姬陵的图片
过滤器应用的方式也显示在上图中,过滤器从左到右,从上到下地读取。与施加到基体中的过滤器的结果为42:(40 * 0)+(42 * 1)+(46 * 0)+(46 * 0)+(50 * 0)+(55 * 0)+(52 * 0)+(56 * 0)+(58 * 0)= 42。
Q-LEARN
Q-Learning算法如下:
在矩阵R中设置伽玛参数和环境奖励。
将矩阵Q初始化为零。
对于每个episode:
选择一个随机的初始状态。
当目标状态未达到就一直运行。
为当前状态选择所有可能的操作之一。
使用这种可能的操作,转到下一个状态。
根据所有可能的操作获取下一个状态的最大Q值。
计算: Q(state, action) = R(state, action) + Gamma * Max[Q(next state, Action with highest Q-value)]
将下一个状态设置为当前状态。
伽玛参数负责我们想要成为奖励的未来多长时间,其范围介于0和1之间。接近伽马值为零时,代理将考虑立即获得更多回报,如果伽马值接近1,代理人将更多地考虑未来的奖励,因此可以延迟奖励。
state是游戏的当前图片,蛇和苹果在游戏中。actions是agent可以执行的可能操作。
q-value是agent的大脑,它是对过去行为的记忆和对agent的奖励。
代码
我们来看看代码的一些部分。
def build_model():
model = Sequential()
model.add(Convolution2D(16, (8, 8), strides=(4, 4),input_shape=INPUT_SHAPE))
model.add(Activation('relu'))
model.add(Convolution2D(32, (4, 4), strides=(2, 2)))
model.add(Activation('relu'))
model.add(Flatten())
model.add(Dense(256))
model.add(Dense(NB_ACTIONS))
adam=Adam(lr=LEARNING_RATE)
model.compile(loss='mean_squared_error',
optimizer=adam)
print(model.summary())
return model
在这部分代码中,我们建立了CNN:
第3行描述第一个卷积层,16表示卷积输出空间的维数。
(8,8)是内核过滤器的大小。
步幅是指内核如何移动图像,现在它移动了4,4个像素。 - input_shape是我们提供给卷积图层的图片的维度。
Dense(NB_ACTIONS)的层是确保我们从网络中获得4种可能操作的图层。
def stack_image(game_image):
#Make image black and white
x_t = skimage.color.rgb2gray(game_image)
#Resize the image to 80x80 pixels
x_t = skimage.transform.resize(x_t,(80,80))
#Change the intensity of colors, maximizing the intensities.
x_t = skimage.exposure.rescale_intensity(x_t,out_range=(0,255))
# Stacking 2 images for the agent to get understanding of speed
s_t = np.stack((x_t,x_t),axis=2)
# Reshape to make keras like it
s_t = s_t.reshape(1, s_t.shape[0], s_t.shape[1], s_t.shape[2])
return s_t
这部分代码实际上有很好的注释来描述每一个步骤
def train_network(model):
game_state = game.Game() #Starting up a game
game_state.set_start_state()
game_image,score,game_lost = game_state.run(0) #The game is started but no action is performed
s_t = stack_image(game_image)
terminal = False
t = 0
d = []
nb_epoch = 0
while(True):
loss = 0
Q_sa = 0
action_index = 4
r_t = 0
a_t = 'no nothing'
if terminal:
game_state.set_start_state()
if t % NB_FRAMES == 0:
if random.random() <= EPSILON:
action_index = random.randrange(NB_ACTIONS)
a_t = GAME_INPUT[action_index]
else:
action_index = np.argmax(model.predict(s_t))
a_t = GAME_INPUT[action_index]
#run the selected action and observed next state and reward
x_t1_colored, r_t, terminal = game_state.run(a_t)
s_t1 = stack_image(x_t1_colored)
d.append((s_t, a_t, r_t, s_t1))
首先,我开始了游戏,并提供有关初始状态的信息,并从游戏中获取图像。我将损失和Q_sa初始化为0.如果游戏失败了,我会重新启动游戏。
然后agent将做出随机行动或预测行动。这是为了确保我们随时随地进行一些操作,以便agent不会陷入局部最小化。
然后,动作被输入到游戏中,我们接收到一个新的状态的图像,同时也得到状态信息,比如奖励,游戏是否结束
if len(d)==BATCH:
inputs = np.zeros((BATCH, s_t.shape[1], s_t.shape[2], s_t.shape[3]))
targets = np.zeros((BATCH, NB_ACTIONS))
i = 0
for s,a,r,s_pred in d:
inputs[i:i + 1] = s
if r < 0:
targets[i ,a] = r
else:
Q_sa = model.predict(s_pred)
targets[i ,a] = r + GAMMA * np.max(Q_sa)
i+=1
loss += model.train_on_batch(inputs,targets)
d.clear()
#Exploration vs Exploitation
if EPSILON > FINAL_EPSILON:
EPSILON -= EPSILON/500
这里是我们进行Q学习的部分。我们循环当前状态,与该状态相关的行动,奖励以及下一个状态。对于每个状态,我们计算相应的Q值,然后在小批量上训练模型。