用户:ML查看:15 回复:12 评论:15 创建时间:2019-01-29T22:48:54
前几天看见一个小朋友发帖。说了解一个程序原理最好的办法是看它的源代码。我觉得说的很有道理。看他在结尾想知道GameAi的原理,那么姐姐就花几天时间写了一个。给大家分享一下:
我们就从0来分析一下这个ai是如何做成的:
1.游戏主体:
Flappy bird本身非常好做,上百度搜一个框架直接复制下来就ok。我就是这样的。注意:要熟悉游戏中每个参数的变量是哪个,比如小鸟y坐标,柱子y坐标等。对于搜集数据有用。
2.特征选取:
写到这里,我发现bcm自己也发过一个GameAi的帖子:https://shequ.codemao.cn/community/8912。
其中有一段:
Bcm工作人员选择的特征是小鸟的y坐标值和柱子下的y坐标值。小朋友们千万不要模仿!如果这样的话,小鸟就对柱子离它的远近毫无知觉,比如在这种情况下:
小鸟距离柱子还很远。它就可以缓慢的上升到这个高度再越过柱子。而在这种情况下:
小鸟就必须立刻做出反应,越过柱子。用以上的特征选取方法,最怕就是开局的时候,后面的时候只要不断校准就没事了。但开局的时候这种极端的情况便可能会出现,导致模型失误率上升。
而且我发现如果选用这两种特征。得出的训练数据完全是乱七八糟a,可坑喵姐姐了233。
不过不知道为什么bcm在这种情况下都做出来了,bcm太赞了。毕竟编程业余爱好,是我才学太寡薄了,不过建议大家还是按我选择的特征来。
所以我选的特征是:【小鸟的y坐标-柱子的y坐标,小鸟的x坐标-柱子的x坐标,小鸟的飞行状态(这是一个变量,大于0时小鸟上升,小于时下降。从网上拿到程序后你应该可以找到这个变量。)】
你需要自己先玩flappy bird,设置一个程序不断收集以上的数据并且记录下你玩时的所有行为(跳或不跳)。为训练做准备。Tip:不要收集太多,700组差不多。不然可能会有极端情况影响数据。
3.算法:
拿到的数据长这个样子:
(黄色是跳,紫色是不跳。共660组,别数了)
现在我们要用一个决策算法来分类这两种数据,姐姐不推荐用神经网络。数据基本上是线性可分的。推荐用逻辑回归(这里涉及到一定的大学内容,最起码要对函数有一定理解,初二以下的小朋友可以跳过)
这个算法老吴的课上有讲过:http://open.163.com/movie/2008/1/E/B/M6SGF6VB4_M6SGHM4EB.html(如果你看不懂可以考虑从第一节课开始看,先要大概理解一下导数的概念和链法,不用很深,大概知道就行,可以基本理解梯度上升)。前几节课,不算特别难,主要讲一下这个损失函数:
先不用管那个log。我这么来解释:函数意思就是让模型对正确结论的肯定度提高。这个函数的导数是:
这个导数就不要想着自己推了(其实姐姐也很晕233。前人已经帮我们把导数弄出来了,我们只要都知道梯度上升的原理,把它套进去就ok),不过前几节课中平方误差损失的导数可以自己推导感受一下。
算法部分完全没看懂也没有关系,毕竟姐姐知道社区很多同学都是小学生(当然,如果你有能力,可以试着看看姐姐写的逻辑回归框架源代码,附在文末)所以姐姐给大家送个福利:
我们有sklearn呢!
两行搞定简单喵!这两行代码就让计算机学习了你所收集的数据。然后,你就可以开始进入最后的实地测试环节了!
4.行为分析:
直接将训练的模型放入到游戏中,效果十分差劲。但是姐姐将模型的决策边界调出来,并没有看见什么过拟合的症状,模型将数据拟合的十分完美。继续搜集数据,发现模型就会出过拟合的毛病。
在模型已经无法继续优化,但效果依旧不理想的情况下,该怎么办?这个时候,我们便需要再次回到特征选取的步奏找答案。
经过姐姐研究发现,模型在实际游戏中会不断往上跳,直到喵。我猜测这是因为它对自己离柱子的距离没有足够的警觉而造成的。于是,在数据输入的环节,我将数据柱子x坐标的变量乘上一个很大数,发现它又开始不断不跳。
我似乎找到了问题所在,我不断调整所乘的这个数,小鸟的决策也越来越稳定,精确,最后直至不会撞到柱子。
为什么我要这么做,我不是妄下论断,而是有实际分析数据:
至上而下分别是我对柱子x坐标乘上1.5~1.3时小鸟飞跃同一个柱子时的轨迹。颜色表示了在这个数值下小鸟的生存时间。其中绿线是最好的,取值是1.4。
至此,姐姐已经完成了这个Ai的制造,它能达到的最高分我还不知道,因为它总是可以在我笔记本没电前不喵。
总结:
机器学习的算法现在已经十分成熟,调用起来也很方便。机器学习领域最关键的一点其是程序员对特征的选取以及对数据的分析能力和直觉,掌握了这两点,可以事半功倍!
结尾,附逻辑回归框架:
import numpy as np
import warnings
class logistic_regression():
def __init__(self):
self.data = None
self.label = None
self.weight = []
self.correction = None
def sigmod(self,x,deriv=False):
output = 1 / (1 + np.exp(-x))
if deriv:
return output * (1 - output)
else:
return output
def fit(self,x,y,epoch,lr):
warnings.warn('Data shape:(Data number,Data dimension).Label shape:(Data number,1)')
self.data = x
for i in range(self.data.shape[1]):
self.weight.append(1.1)
self.label = y
self.weight = np.array([self.weight]).T
for i in range(epoch):
self.result = self.data.dot(self.weight)
self.result = 1 / (1 + np.exp(-self.result))
self.loss = self.label - self.result
for a in range(len(self.weight)):
for b in range(len(self.data)):
self.weight[a][0]+=self.loss[b][0]*self.data.T[a][b]*lr
self.c_result = []
self.correct = 0
for i in self.result:
if i[0] >= 0.5:
self.c_result.append(1)
else:
self.c_result.append(0)
for i in range(len(self.c_result)):
if self.c_result[i] == self.label[i]:
self.correct+=1
self.correction = self.correct/len(self.label)
def Correct_Rate(self):
return self.correction
def predict(self,x):
warnings.warn('Train before predict.')
self.predictions = []
self.prediction = x.dot(self.weight)
self.prediction = 1 / (1 + np.exp(-self.prediction))
for i in self.prediction:
if i[0] >= 0.5:
self.predictions.append(1)
else:
self.predictions.append(0)
return self.predictions
我是墨兰,以后如果不会的问题可以向我提问,力所能及范围内的我尽力回答。希望做你们的好姐姐!
姐姐之前在网上看到过很多Ai玩flappy bird的文章,用的不是深层cnn就是深度强化学习,最不济也是遗传算法。很多网友总是喜欢把比较简单的问题搞得很复杂,这样其实不好,简单的问题,就要用简单的方法解决,效率才是最高的!
点赞0
评论
我之前对这个损失函数解释的不是很清楚,现在换一种解释方法:
这个函数就是对比输出的概率和label,将正确的概率累积在一起看,配一张图:
橘色的线是label,蓝线是预测的概率。可以看出,蓝线偏出来了一点,蓝线与橘线完全重合在一起时效果最佳。这个损失函数就是统计偏差出来的蓝线有多少。
点赞0
评论