周六被小伙伴拖去游泳,美名其曰:锻炼身体。其实某人就是去泡澡的,哈哈。说正题吧,游完泳在体育场里闲逛,里面很大,转着转着看到一个保龄球馆,怀着对未知事物的好奇,决定和某人去尝试一下。我和S同窗一人买了一局,按照说明,每一局分为10次,每一次有两次机会扔球。最后的比分就不说了,反正玩的很爽,最后也在边上一个厉害的大叔指点下,学会了基本的扔球姿式。 html
看到这你觉得这是一篇叙事文?那就错了,原由是从这里开始的,咱们的次数用完后,留在里面打台球(这里也有台球桌),看到不断有穿着队服一类东西的人进来,应该是来比赛的,同时又看到了赛道上面的牌子,有一个写着:289分。那分数是怎么计算的呢,怀着好奇心搜索起保龄球的积分规则来。在了解以后,我就在想一个问题:__若是是让我开发一个保龄球的游戏,那么计分程序要怎么写呢?__今天咱们就从这里提及。。。python
先简述一下保龄球的规则,这里引用百度知道的别人的回答,每一局比赛有10格,每格有两次击球机会,咱们这里关注它的得分状况,这里分为两种状况:apache
1-9格击球
每一格有3种可能:api
第10格击球
这一格有两种可能:数组
规则也了解了,下面就到了写代码的时候了,为了方便,这里选择Python,版本为3.6
考虑到直观性,这里没有用交互式的程序,而是直接将击中状况抽象成矩阵(数组),算出最后总分。
输入的数据大概是这个样子:网络
[[0, 3], [2, 6], [3, 6], [0, 3], [3, 0], [9, 1], [6, 3], [6, 2], [4, 6], [4, 2]]框架
10x2的数组,表明前10格每格的击倒瓶数,若是一格内不须要第二次击球,也算做0。这里先写一个简单的数据生成函数。dom
import random def top_10(): for i in range(10): for j in range(2): if j == 0 : a[i][j] = random.randint(0,10) else : a[i][j] = random.randint(0,10-a[i][j-1]) return a
同时,咱们注意到了,这个生成函数还少了点什么,没错,就是第十格的追加击球数。因此,这里再定义一个追加球生成函数
这里为了后面计算方便,也定义为[[x,y]]
这种格式函数
def addto_num(a): return [[random.randint(0,10),0]] if sum(a[9]) == 10 else [[0,0]]
原始数据的生成咱们完成了,接下来要定义计算函数了,计算总分数学习
def calc_total(top): sums = 0 index = 0 for x in top: if x[0] == 10: sums += 10 if top[index+1][0] == 10: sums += 10 + top[index+2][0] else: sums += sum(top[index+1]) elif sum(x) == 10: sums += 10 + top[index+1][0] else: sums += sum(x) index+=1 if index == 9: break sums += sum(top[8]+top[9]+top[10]) return sums
代码写的不是很好看,你们请谅解啊,不过整个完整的功能是作完了,咱们能够写个方法测试下
tmp1 = top_10() add1 = addto_num(tmp1) c = calc_total(tmp1+add1) print(c)
78
想必你们也了解,当下最火的就是AI,而做为实现AI的其中一种手段,深度学习必不可少。最近也在学习这方面的知识(ps:给沐神疯狂打call,强烈推荐他的深度学习课程,连接你们本身去搜,就不作广告了),虽说本身连入门都算不上,但仍是想实现一下本身版本的。
因而就有了这个:
深度学习版本的保龄球得分计算方法
这里咱们用到了mxnet这个深度学习框架,最基础的部分的两个库ndarray和autograd
首先,咱们是基于线性回归这个最简单也是最基础的神经网络实现的,模型看起来就像这样
$$\boldsymbol{\hat{y}} = X \boldsymbol{w} + b$$
同时定义它的损失函数,也就是计算预测值和实际值的差距,这里用两个的平方偏差来计算,模型是这样
$$\sum_{i=1}^n (\hat{y}_i-y_i)^2.$$
首先,咱们要__建立数据集__
由于咱们以前定义的是Python的list,因此在这里要转换成mxnet的内置数组ndarray
不过在此以前咱们要先改进下咱们的生成函数,以前是由两个函数组成,如今为了方便,咱们合成一个。同时,计算方法改形成ndarray版本的。
from mxnet import ndarray as nd from mxnet import autograd def init_data(): for i in range(0,10): for j in range(0,2): if j == 0 : a[i][j] = random.randint(0, 10) else : a[i][j] = random.randint(0,10-a[i][j-1]) return a+[[random.randint(0,10),0]] if sum(a[9]) == 10 else a+[[0,0]] def calc_total_nd(top): sums = 0 index = 0 for x in top: if x[0].asscalar() == 10: sums += 10 if top[index+1][0].asscalar() == 10: sums += 10 + top[index+2][0].asscalar() else: sums += nd.sum(top[index+1]).asscalar() elif nd.sum(x).asscalar() == 10: sums += 10 + top[index+1][0].asscalar() else: sums += nd.sum(x).asscalar() index+=1 if index == 9: break sums += nd.sum(top[8]+top[9]+top[10]).asscalar() return sums num_inputs = 22 num_examples = 1000 X = nd.zeros(shape=(num_examples,11,2)) for i in X: i[:] = nd.array(init_data()) y = nd.array([calc_total_nd(i) for i in X])
而后是定义 数据读取方法
目的是在后面训练时随机遍历咱们的数据集,这里参考了沐神教程里的方法。
import random batch_size = 10 def data_iter(): # 产生一个随机索引 idx = list(range(num_examples)) random.shuffle(idx) for i in range(0, num_examples, batch_size): j = nd.array(idx[i:min(i+batch_size,num_examples)]) yield nd.take(X, j), nd.take(y, j)
尝试着读取一个
for data, label in data_iter(): print(data, label) break
[[[ 2. 0.] [ 7. 0.] [ 1. 7.] [ 2. 2.] [ 6. 2.] [ 0. 5.] [ 0. 5.] [ 7. 1.] [ 6. 4.] [ 3. 0.] [ 0. 0.]] [[ 6. 3.] [ 4. 2.] [ 2. 4.] [ 8. 2.] [ 4. 6.] [ 6. 3.] [ 2. 6.] [ 6. 3.] [ 2. 3.] [ 8. 2.] [ 7. 0.]] [[ 10. 0.] [ 8. 0.] [ 2. 2.] [ 8. 2.] [ 0. 3.] [ 10. 0.] [ 10. 0.] [ 6. 3.] [ 10. 0.] [ 1. 7.] [ 0. 0.]] [[ 5. 1.] [ 6. 2.] [ 10. 0.] [ 3. 6.] [ 8. 2.] [ 10. 0.] [ 4. 4.] [ 2. 4.] [ 2. 0.] [ 7. 3.] [ 10. 0.]] [[ 6. 2.] [ 8. 0.] [ 0. 0.] [ 9. 0.] [ 6. 4.] [ 5. 3.] [ 5. 0.] [ 1. 6.] [ 0. 1.] [ 4. 4.] [ 0. 0.]] [[ 5. 5.] [ 6. 3.] [ 0. 7.] [ 2. 8.] [ 10. 0.] [ 4. 0.] [ 1. 5.] [ 1. 2.] [ 1. 2.] [ 0. 2.] [ 0. 0.]] [[ 10. 0.] [ 0. 3.] [ 3. 7.] [ 3. 1.] [ 8. 1.] [ 4. 2.] [ 8. 1.] [ 6. 4.] [ 10. 0.] [ 5. 0.] [ 0. 0.]] [[ 8. 2.] [ 10. 0.] [ 6. 0.] [ 10. 0.] [ 1. 4.] [ 2. 6.] [ 9. 0.] [ 5. 5.] [ 7. 1.] [ 5. 1.] [ 0. 0.]] [[ 9. 1.] [ 7. 1.] [ 6. 3.] [ 0. 5.] [ 7. 3.] [ 7. 1.] [ 6. 3.] [ 3. 1.] [ 3. 3.] [ 10. 0.] [ 6. 0.]] [[ 0. 10.] [ 4. 3.] [ 2. 6.] [ 2. 6.] [ 4. 1.] [ 8. 1.] [ 5. 4.] [ 3. 6.] [ 6. 4.] [ 4. 2.] [ 0. 0.]]] <NDArray 10x11x2 @cpu(0)> [ 73. 104. 133. 118. 70. 87. 107. 118. 105. 99.] <NDArray 10 @cpu(0)>
数据准备好了,如今要定义一个__初始化的模型参数__
这里随机生成一个就行了,后面咱们会经过训练,慢慢学习完善这个参数,这也是深度学习的目的
w = nd.random_normal(shape=(num_inputs, )) b = nd.random_normal(shape=(1,)) params = [w, b] print(params)
[ [ 0.50869578 -0.16038011 0.91511744 0.84187603 -0.49177799 -1.00553632 -1.55609238 3.13221502 -0.15748753 -0.4358989 -0.52664566 -0.49295077 -0.17884982 1.43718672 0.43164727 -0.31814137 0.46760127 -0.16282491 0.17287086 0.6836102 0.76158988 1.61066961] <NDArray 22 @cpu(0)>, [ 9.91063134e-05] <NDArray 1 @cpu(0)>]
而后附上梯度,也就是让后面autograde能够对这个函数求导
for param in params: param.attach_grad()
定义模型和损失函数
这里要注意的是:咱们的维度不是1,因此要把数组的维度reshape一下变成一维数组
def net(X): return nd.dot(X.reshape((-1,num_inputs)), w) + b def square_loss(yhat, y): return (yhat - y.reshape(yhat.shape)) ** 2
而后是优化方法,也就是学习方法,让函数去学习参数
def SGD(params, lr): for param in params: param[:] = param - lr * param.grad
最后就是__训练__了
epochs = 5 learning_rate = .0001 for e in range(epochs): total_loss = 0 for data, label in data_iter(): with autograd.record(): output = net(data) loss = square_loss(output, label) loss.backward() SGD(params, learning_rate/batch_size) total_loss += nd.sum(loss).asscalar() print("Epoch %d, average loss: %f" % (e, total_loss/num_examples))
Epoch 0, average loss: 82.049488 Epoch 1, average loss: 82.009441 Epoch 2, average loss: 81.810044 Epoch 3, average loss: 82.243776 Epoch 4, average loss: 82.023799
最后来验证下咱们的预测结果
for data, label in data_iter(): print("实际分数") print(label) print("预测分数") print(net(data)) break
实际分数 [ 108. 77. 102. 115. 85. 110. 76. 124. 78. 87.] <NDArray 10 @cpu(0)> 预测分数 [ 107.43678284 86.52748871 101.92710114 116.50645447 90.5655899 115.31760406 80.10424805 118.94145203 84.49520111 95.17882538] <NDArray 10 @cpu(0)>
参考:
动手学深度学习