棋牌AI(RL-Card)
RL-Card
起因:最初由于斗地主需要引入AI使用,由于不是由我负责的,也没太了解过这方面东西。
后来做麻将的时候需要进一步优化AI逻辑,需要做一些优化尝试。
1 | |
以上是较早版本的优化思路
- 仍旧是对源代码中的出牌策略进行细分调整,优化过程中发现,优化的策略参数调整往往达不到满意的程度(各种对局情况下,出牌效果不稳定,会出现这局这样优化是好的,下一局使用这个策略算法却是负优化)于是,在调整一段时间后,确定一个相对稳定的版本,便不再在此思路上费功夫。
- 麻将向听前进算法,思路源于麻将胡牌中最早接触的日式查表法。想着是不是可以计算需要几手才能胡,于是查找相关资料,确有人这么尝试过。理论上来讲,此算法若是稳定的话,时间复杂度上会好很多。结果是不稳定,具体过程不太记得了,就此略过。
- 思路参考于:机器自学习。想着是不是可以实时获取用户出牌数据,动态调整出牌数据库。止步于理论思考阶段,并未真正实现此逻辑(数据量会与日俱增,用户数据不可控,单一从胜率来看,数据浮动太大)
- RL-Card代码训练模型,然后加载模型提供接口,程序中调用。
关于RL-Card最早了解于项目中斗地主的AI,源于斗零(DouZero)快手出品。
后续了解到RL-Card棋牌AI训练。
最初的时候使用run-rl.py 执行1万次,训练出模型加载代入已有麻将使用,效果感觉和版本1的策略细分差不多,甚至有时候还不如,后续因为别的工作安排便暂停了这方面研究。
最近又有大把时间可以研究下其他的,于是又开始学习线性代数(学的头大,不想学了)、了解大模型知识(也挺头大,专业名次太多)、刷题(刷不动,只好跟着官解一步步写程序逻辑)、跑RL-Card程序,如下记录:
1 | |
以下详细介绍一下过程:
运行run_rl.py程序;
1
2
3
4/**使用python3执行
查看报错,安装对应的引用包程序
执行命令,大概这样子*/
run_rl.py --env mahjong --num_episodes 10000 --log_dir experiments/mahjong_dqn_result_10000/加载模型,实现python接口(以下代码依赖RL-Card代码);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from http.server import HTTPServer, BaseHTTPRequestHandler
from examples.experiments.mahjong_dqn_result.mahjong import MahjongModel as Model
from rlcard.games.mahjong.card import MahjongCard as Card
# hand = []
# hand.append(Card('bamboo','1'))
# hand.append(Card('bamboo','2'))
# hand.append(Card('characters','3'))
# # hand.append(Card('bamboo','4'))
# # hand.append(Card('bamboo','5'))
# # hand.append(Card('bamboo','6'))
# # hand.append(Card('bamboo','7'))
# # hand.append(Card('bamboo','8'))
# # hand.append(Card('bamboo','9'))
# # hand.append(Card('bamboo','1'))
# # hand.append(Card('bamboo','2'))
# # hand.append(Card('bamboo','3'))
# hand.append(Card('characters','9'))
# hand.append(Card('characters','9'))
# pile = []
# pile.append(Card('bamboo','5'))
# pile.append(Card('bamboo','5'))
# pile.append(Card('bamboo','5'))
# piles = [[pile],[],[],[]]
# table = []
# table.append(Card('characters','5'))
# table.append(Card('characters','5'))
# table.append(Card('characters','5'))
# action = Model().eval(hand,piles,table)
# print("action:",action)
class Request(BaseHTTPRequestHandler):
#通过类继承,新定义类
timeout = 5
server_version = 'Apache'
def do_GET(self):
#在新类中定义get的内容(当客户端向该服务端使用get请求时,本服务端将如下运行)
self.send_response(200)
self.send_header("type","get") #设置响应头,可省略或设置多个
self.end_headers()
msg = 123 #要返回给客户端的信息
msg = str(msg).encode() #转为str再转为byte格式
self.wfile.write(msg) #将byte格式的信息返回给客户端
def do_POST(self):
#在新类中定义post的内容(当客户端向该服务端使用post请求时,本服务端将如下运行)
data = self.rfile.read(int(self.headers['content-length'])) #获取从客户端传入的参数(byte格式)
json_data = data.decode() #将byte格式转为str格式
print(type(json_data),json_data)
model = Model()
hand, piles, table = model.decode_str(json_data)
action = model.eval(hand,piles,table)
action_str = model.action2str(action)
print("action",action,action_str)
self.send_response(200)
self.send_header("type","post") #设置响应头,可省略或设置多个
self.end_headers()
msg = action_str
msg = str(msg).encode() #转为str再转为byte格式
self.wfile.write(msg) #将byte格式的信息返回给客户端
host = ('0.0.0.0',8888) #设定地址与端口号,'localhost'等价于'127.0.0.1'
server = HTTPServer(host, Request) #根据地址端口号和新定义的类,创建服务器实例
server.serve_forever() #开启服务1
2// 测试接口
curl -H 'content-type:application/json' -X POST -d "{\"hand\":[\"bamboo-1\",\"bamboo-2\",\"bamboo-3\",\"characters-9\",\"characters-9\"],\"piles\":[[\"bamboo-5\",\"bamboo-5\",\"bamboo-5\"],[],[],[]],\"table\":[\"characters-5\",\"characters-5\",\"characters-5\"]}" "http://192.168.1.106:8888"cuda模式全面启用;
1 | |
- 加参数 -cuda 0 启动GPU加速
1 | |
发现速度较慢,nvidia-smi dmon 命令实时查看sm(GPU占用率)一直在10%左右波动。
证明:未充分释放GPU性能。
解决办法:
1 | |
代码中添加如下设置:
1 | |
最终使用命令训练(Gpu初始利用率在50左右浮动,后续在50、60、70波动)
1 | |