本文首发于InfoQ公众号头条。
四年一度的世界杯又来了,作为没什么时间看球的码农,跟大家一样,靠买买足彩给自己点看球动力和乐趣,
然而总是买错球队,面对各种赔率也不知道怎么买才划算,足彩是不是碰大运的?如何提高自己的预测水平,成为预言帝,升职加薪赢取白富美走上人生巅峰?
本文采用机器学习方法,试图通过特定指标进行训练,对世界杯剩下的比赛胜负平做预测,并判断足彩给出的赔率是否值得买,以赢得博弈游戏的胜利。选取的数据量较少,仅提供一种思路,以下为我的程序的预测结果:
引言
本文主要写给跟我一样对机器学习的实际应用感兴趣的,但没有入门的小白程序员。初步尝试,抛砖引玉,如有大牛看出计算方法和数据的问题,欢迎指点讨论。
机器学习的一般步骤: 定义问题、数据预处理、特征工程、机器学习建模及训练、模型应用。为了定义问题,说清楚为什么要解决这个问题,我们首先需要先学习些基本概率赔率知识。
本文基于以下假设:
1、足彩靠服务费赚钱,选择的场次不存在假球因素
2、你的资金规模足以支撑长期买足彩
作为简化,不考虑几场组合串、让球胜平负、大小球、比分、进球数之类的场景。只考虑胜平负情况及赔率因素。本文不讨论庄家如何通过调整赔率和双方下注额获利,只讨论下注用户如何盈利。
概率赔率基本知识
我们用初中数学知识来学习EV知识,并绕过两个常见的赌场陷阱
1)赔率陷阱
我们先来看一个抛硬币的场景,假设正反面出现概率就是50%,你每次需要投入1元,你赢了给你1.8元( 即赔率1.8 ,你得到0.8元),输了就失去你的1元,这个游戏你应该玩吗?
这就要引入EV(期望值)公式了, EV就是事情出现的概率*盈亏的和
即 EV = 盈利*获胜率+损失*失败率 ,EV为正,长期来看你是盈利的,EV为负,长期看亏损。
具体到这个例子 就是 EV = (1.8-1)*0.5 + (- 1*0.5) = - 0.1 ,平均来说,你每轮会亏损 0.1元,所以你不应该玩这个。
赌场会有各种赔率陷阱,套用这个公式大体上都能识别出来,赌场就是靠这些赔率陷阱有了微弱的优势来盈利的,例如百家乐赌场优势是1.2%,21点赌场优势最小(0.58%),长期来看都是负EV。即使真有正EV的游戏,也会因为赌场庞大的资金量和你不对等的小资金量导致你在某次小概率时间里破产,所以理性看待,这里只做数学分析。
2)赌徒谬误陷阱
赌徒谬误大意是指将前后相互独立的随机事件当成有关联的事件,例如抛硬币时,无论抛几次,任意两次之间都是相互独立的,并不相互产生影响。道理虽简单易懂,但有时仍会糊涂。比如,当你连抛了5次正面时,到了第6次,你可能会认为这次正面出现的概率会更小了(< 1/2),反面出现的概率会更大(> 1/2)。
赌场中著名的输后加倍下注系统(Martingale)便是利用此心态的实例:赌徒第一次下注1元,如输了则下注2元,再输则变成4元,如此类推,直到赢出为止。赌徒误以为在连续输了多次之后,
胜出的概率会变大,所以愿意加倍又加倍地下注,殊不知其实概率是不变的,赌场的游戏机没有记忆,不会因为你输了就给你更多胜出的机会。
这时候就要用到1)的EV公式,某次的成功并不改变后果,你的期望值没有变,怎么做这种倍数努力都是没用的。 而且下注都有上限,如果你的资金无穷大且允许你下注无穷大,那么加倍下注确实能赢。可惜现实里做不到,小概率时间会把你弄的倾家荡产TT。
反等价鞅法则、凯利公式感兴趣的可以研究下,这里不再展开。我们只要知道正EV情况下我们才应该买足彩就好。
足彩赔率推导及返还率推导
买足彩如何获利的呢?
根据之前的赔率知识,当计算出我们获胜的胜率,再结合足彩给出的赔率,我们认为获胜时候EV为正,那么就可以下注。而不是说看赔率高就无脑以小博大,那样还是在赌博。
足彩的赔率又是怎么计算出来的呢?其实是根据胜率和返还率倒推的。
首先需要了解返还率这个指标 。一场比赛 假设 W、D、L各代表主胜、平赔、客胜的赔率,P为主胜的概率, 假设我们投入1元,在0EV状况下有:
(W-1) * 1 * P - 1* (1-P) = 0
W-1 为盈利, 1为输的时候的亏损。那么 求解方程 P = 1/W , 同理 平的概率是 1/D , 客胜的概率是 1/L ,加起来是100%
在没有干预的情况下: 返还金额/投注金额 = 1 /(1/W + 1/D + 1/L ) = 100%
可是足彩或者博彩公司是要盈利的,那么他们就会从总投注资金里抽取一部分, 于是返还金额就<投注金额。 于是就有 1/W*返还率 + 1/D*返还率 + 1/L*返还率 = 1 ,
例如胜场,赌场估计的概率就是 1/W*返还率 。 返还率 也可以用 1 /(1/W + 1/D + 1/L ) = 1/ ( (W*D+W*L+D*L) / (W*D*L)) = (W*D*L) / (W*D+W*L+D*L) 计算,更方便。
于是你看到 开出的 胜平负赔率 ,就可以算出 胜平负的概率和足彩的返还率了 。
例如胜 2.34 平 3.05 负 2.80 ,返还率为 (W*D*L) / (W*D+W*L+D*L) = 90%
博彩公司对该项比赛的概率估计为 主队胜 0.9/2.34 =0.384=38.4% 。 实际预算的时候是先有胜负概率再出赔率的,博彩公司根据各种因素指标,计算出了主队胜率, 然后主队的赔率就有了: W = 返还率/胜率 。 平和负的计算方法也是类似。
我们如何获胜呢?只能假设博彩公司概率估低,给出的赔率高了,这样套用EV公式 EV = (2.34-1) *P - (1-P) >0 , 即 P >1/W= 42.735% ,
即 足彩告诉我们他们计算主场胜率是38.4%,而我们认为主队胜率大于42.735%时候 就可以下注了 ,这时我们有微弱的优势,而且自行分析概率后就不容易被误导 ^^ 大家可以自行推倒二串一 三串一这种是否划算,不过早期比如提前几天的时候因为局势不明,是有可能有比较高的赔率。
定义问题,特征选取,数据抓取
绕了一大圈,其实简单来说,我们要做的就是自行计算胜平负的概率,然后套用EV公式看根据赔率投注是否划算。
这个问题抽象下,是一个预测类的问题。如果想要预测目标变量的值,可以选择监督学习算法,否则可以选择无监督算法。所以这个问题可以归类认为是机器学习的监督学习,可以用线性回归去解决这类问题。
简单地讲,浏览一些数据网站,我们可以选定一些特征和历史比赛结果样本,作为多元一次方程组去求解, 即 aX+bY+cZ = R 。 我们要做的就是求解参数a、b、c。这种方法在机器学习里就叫做多变量线性回归,有通用的解法。(ps 如果当年早知道,就可以顺利解决appstore排名预测问题了。。。)
特征选取:
根据某数据网站数据,这里选择主队获胜赔率、主队平局赔率、主队负赔率、主队世界排名、客队世界排名、主队近期胜率、客队近期胜率、主队信心指数、让球指数、让球后主胜赔率、让球后主平赔率、让球后主负赔率作为特征,结果集为最终胜平负赛果 ,简单起见,定为主胜 值为1(含平局)、主负值为0处理。
使用python3+BeautifulSoup 写爬虫抓数据示例:
# coding=utf-8
import urllib.parse
import urllib.request
import gzip
from bs4 import BeautifulSoup
class Game:
def __str__(self):
return '%s,%s,%s,%s,%s,%s,%s,%s,%s' %(self.race,self.turn,self.time,self.host,self.guest,self.rate_w,self.rate_tie,self.rate_lose,self.rate_back)
def getHtml(url,values):
user_agent='Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.82 Safari/537.36'
headers = {'User-Agent':user_agent}
data = urllib.parse.urlencode(values)
response_result = urllib.request.urlopen(url+'?'+data).read()
#压缩过的数据,一般不需要unzip 视具体网页决定
html = gzip.decompress(response_result)
html = html.decode('gbk')
return html
#return ""
#获取数据
def requestOdds(index):
print('向足彩数据网站请求数据')
url = 'http://odds.500.com/#!league=%5B40%2C109%2C352%5D'
value= {
}
result = getHtml(url,value)
return result
if __name__ == '__main__':
print("start parse")
text = requestOdds(1)
soup = BeautifulSoup(text, 'html.parser')
items = soup.find_all('tr', attrs={'': ''}, limit=1000)
size = 15
for item in items:
#print(item)
tds = item.find_all('td')
games = []
#只抓世界杯比赛
if len(tds)>=size and tds[1].string=='世界杯':
game = Game()
game.race = tds[1].string
game.turn = tds[2].string
game.time = tds[3].string
game.host = tds[4].string
game.guest = tds[6].string
game.rate_w = tds[11].string
game.rate_tie = tds[12].string
game.rate_lose = tds[13].string
game.rate_back= tds[14].string
games.append(game)
模型建立,程序回测,预测胜率
总共44场比赛,我们使用Logistic回归+Sigmoid函数分类方式处理(详情请阅读参考文献《机器学习实战》),训练算法为改进的随机梯度上升算法:
from numpy import * def sigmoid(inX): return 1.0/(1+exp(-inX)) def stocGradAscent1(dataMatrix, classLabels, numIter=500): m,n = shape(dataMatrix) weights = ones(n) #initialize to all ones for j in range(numIter): dataIndex = list(range(m)) for i in range(m): alpha = 4/(1.0+j+i)+0.0001 #apha decreases with iteration, does not randIndex = int(random.uniform(0,len(dataIndex)))#go to 0 because of the constant h = sigmoid(sum(dataMatrix[randIndex]*weights)) error = classLabels[randIndex] - h weights = weights + alpha * error * dataMatrix[randIndex] del(dataIndex[randIndex]) return weights
选择训练集和测试集都为这44场比赛,这时的错误率为28%,观察干扰数据:
足球的迷人地方,包含了韩国对德国这个超大冷门,我们的程序预测韩国胜率几乎为0。。。
用这个模型预测28日及以后的8场比赛, 步长改为更小,到时看看是否能达到72%的胜率:
时间场次, 主队胜率
日 本 VS 波 兰,0
塞内加 VS 哥伦比,0
英格兰 VS 比利时,0.95
巴拿马 VS 突尼斯,0.001
法 国 VS 阿根廷,0
乌拉圭 VS 葡萄牙,0.869032
俄罗斯 VS 西班牙,0.001
克罗地 VS 丹 麦,0.77
5、结论及展望
综上,我们看球预测时,需要较精确地估算出一个胜率,然后看足彩给的赔率是否合适,如果 胜率> 1/赔率,则适合下注,是个正EV的游戏。而不是十分肯定地说某某队会赢,毕竟庄家都不敢这么预测。如何估算胜率?我们可以选择一些特征值,进行线性回归,确定特征值对应的系数,然后预测下场比赛的赛果。
另外,必胜策略肯定是不存在的,不然庄家也太友好了。。。
本文模型选的特征值比较少,只依赖数据网站,缺少很多信息,准确率不高,回归系数没有完全收敛,也不能评估出胜负外的概率,需要调整参数。本人会继续改进。
代码git地址:https://github.com/sgp2004/world_cup_AI
参考文献
https://zhuanlan.zhihu.com/p/26929562
《机器学习实战》
课外阅读:
别赌球了,你只是“庄家必赢公式”的玩偶 https://mp.weixin.qq.com/s/ig06FpzhqE_9dFOtziCxKA
爆冷让人心慌?如何让「下注」稳赚不赔?https://mp.weixin.qq.com/s/baftxbdQQKTP-SEutM4uhw