我应该如何在数据库中存储稀疏决策树(移动列表)?
我长期以来一直在考虑为棋盘游戏制作人工智能,最近我开始收集资源和算法。游戏是非随机的,并且大多数时候,存在 <<一名玩家 3 步,有时超过 20 步。我想存储关键的动作或不明确的动作,以便人工智能从错误中吸取教训,下次不会犯同样的错误。确定获胜或失败的棋步无需存储。所以我实际上有一个用于游戏开始的稀疏决策树。 我想知道如何将这个决策树存储在数据库中?数据库不需要是SQL,我不知道哪个数据库适合这个特定问题。
编辑:请不要告诉我将决策树解析到内存中,只是想象游戏像国际象棋一样复杂。
I have been thinking of making an AI for a board game for a long time, and recently I've started to gather resources and algorithms. The game is non-random, and most of the time, there < 3 moves for a player, sometimes, there are >20 moves. I would like to store critical moves, or ambiguous moves so that the AI learns from its mistakes and will not make a same mistake the next time. Moves that surely win or lose need not be stored. So I actually have a sparse decision tree for the beginning of games.
I would like to know how I should store this decision tree in a database? The database does not need to be SQL, and I do not know which database is suitable for this particular problem.
EDIT: Please do not tell me to parse the decision tree into memory, just imagine the game as complicated as chess.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
当您将遍历树时,neo4j 对我来说似乎是一个很好的解决方案。 SQL 不是一个好的选择,因为查询需要许多联接。据我了解这个问题,您正在寻求一种在数据库中存储一些图形的方法,而 Neo4j 是一个明确用于图形的数据库。为了稀疏性,您可以使用 PropertyContainers 将基元或字符串数组附加到图的边缘来编码移动序列(我说得对吗,通过节点的稀疏性和跳过,您的意思是您的树边是移动序列而不是单个移动序列)移动?)。
As you will be traversing the tree, neo4j seems like a good solution to me. SQL is no good choice because of the many joins you would need for queries. As i understand the question, you are asking for a way to store some graph in a database, and neo4j is a database explicitely for graphs. For the sparseness, you can attach arrays of primitives or Strings to the edges of your graph to encode sequences of moves, using PropertyContainers (am i right that by sparseness and skipping of nodes you mean your tree-edges are sequences of moves rather than single moves?).
首先,您尝试做的事情听起来像是基于案例的推理(CBR)问题,请参阅:http: //en.wikipedia.org/wiki/Case-based_reasoning#Prominent_CBR_systems。 CBR 将拥有一个决策数据库,理论上您的系统会选择可用的最佳结果。
因此我建议使用 neo4j,它是一个 nosql 图形数据库。 http://neo4j.org/
因此,为了表示您的游戏,每个位置都是图中的一个节点,并且每个节点应该包含从所述位置出发的潜在移动。您可以跟踪随着游戏进展而学习的评分指标,以便人工智能了解更多信息。
Firstly what you are trying to do sounds like a case based reasoning(CBR) problem see: http://en.wikipedia.org/wiki/Case-based_reasoning#Prominent_CBR_systems . CBR will have a database of decisions, your system would in theory pick the best outcomes available.
Therefore I would suggest using neo4j which is a nosql graph database. http://neo4j.org/
So to represent your game each position is a node in the graph, and each node should contain potential moves from said position. You can track scoring metrics which are learnt as games progress so that the AI is more informed.
我会使用像 RavenDB 这样的文档数据库(NOSQL),因为您可以在数据库中存储任何数据结构。
文档不像普通 SQL 数据库那样扁平,它允许您直接存储像树一样的分层数据:
这里您可以看到一个可以存储在 RavenDB 中的示例 JSON 树。
RavenDB 还内置了一个-in 查询分层数据的功能:
http://ravendb.net/faq/hierarchies
请查看 文档 以获取 RavenDB 如何工作的更多信息。
资源:
I would use a document database (NOSQL) like RavenDB because you can store any data structure in the database.
Documents aren't flat like in a normal SQL database and that allows you to store hierarchical data like trees directly:
Here you can see an example JSON tree which can be stored in RavenDB.
RavenDB also has a built-in feature to query hierarchical data:
http://ravendb.net/faq/hierarchies
Please look at the documentation to get more information how RavenDB works.
Resources:
您可以使用内存映射文件作为存储。
首先,创建“编译器”。该编译器将解析文本文件并将其转换为紧凑的二进制表示形式。主应用程序会将这个二进制优化文件映射到内存中。这将解决您的内存大小限制问题
You can use memory mapped file as storage.
First, create "compiler". This compiler will parse text file and convert it into compact binary representation. Main application will map this binary optimized file into memory. This will solve your problem with memory size limitation
从简单的数据库表设计开始。
决定:
当前状态二进制(57) |新状态二进制(57) | Score INT
CurrentState 和 NewState 是游戏状态的序列化版本。分数是给予 NewState 的权重(正分数是好的动作,负分数是坏的动作)你的 AI 可以适当地更新这些分数。
连珠,使用 15x15 棋盘,每个位置可以是黑色、白色或空,因此您需要 Ceiling( (2bits * 15*15) / 8 ) 字节来序列化棋盘。在 SQL 中,这将是 T-SQL 中的 BINARY(57)
你的 AI 会选择它存储的当前动作,例如...
你将从当前游戏状态中获得按最佳分数排序的所有存储的下一步动作的列表到最低分数。
您的表结构将在(CurrentState,NewState)上有一个复合唯一索引(主键),以方便搜索并避免重复。
这不是最好/最优化的解决方案,但由于您缺乏数据库知识,我相信它将是最容易实现的,并给您一个良好的开端。
Start with a simple database table design.
Decisions:
CurrentState BINARY(57) | NewState BINARY(57) | Score INT
CurrentState and NewState are a serialized version of the game state. Score is a weight given to the NewState (positive scores are good moves, negative scores are bad moves) your AI can update these scores appropriately.
Renju, uses a 15x15 board, each location can be either black, white or empty so you need Ceiling( (2bits * 15*15) / 8 ) bytes to serialize the board. In SQL that would be a BINARY(57) in T-SQL
Your AI would select the current moves it has stored like...
You'll get a list of all the stored next moves from the current game state in order of best score to least score.
Your table structure would have a Composite Unique Index (primary key) on (CurrentState, NewState) to facilitate searching and avoid duplicates.
This isn't the best/most optimal solution, but because of your lack of DB knowledge I beleive it would be the easiest to implement and give you a good start.
如果我与国际象棋引擎进行比较,它们是根据记忆进行游戏的,也许除了打开库之外。国际象棋太复杂,无法存储决策树。国际象棋引擎通过对潜在的和暂时的未来位置(而不是移动)分配启发式评估来进行游戏。未来的位置是通过某种有限深度搜索找到的,可能会在内存中缓存一段时间,但通常会在每一轮中重新计算,因为搜索空间太大,无法以比重新计算更快的方式进行存储。
If I compare with chess engines, those play from memory, maybe apart from opening libraries. Chess is too complicated to store a decinding decision tree. Chess engines play by assigning heuristic evaluations to potential and transient future positions (not moves). Future positions are found by some kind of limited depth search, may be cached for some time in memory, but often are plainly recalculated each turn as the search space is just too big to store in a way faster to look up than recalculating is possible.
你知道Chinook——解决跳棋问题的人工智能吗?它通过编译每个可能的结局的数据库来做到这一点。虽然这并不完全是您正在做的事情,但您可以从中学习。
Do you know Chinook — the AI that solves checkers? It does this by compiling a database of every possible endgame. While this is not exactly what you are doing, you might learn from it.
我无法清楚地想象您在树中处理的数据结构及其复杂性。
但这里有一些您可能感兴趣的想法:
I can't clearly conceive neither the data structures you handle in your tree nor their complexity.
But here are some thoughts which may interest you :
我会用国际象棋引擎中处理开局书的传统方式来解决这个问题:
查找移动 国际
象棋引擎通常通过 Zobrist 哈希,这是为游戏状态构建良好哈希函数的简单方法。
这种方法的一大优点是它可以处理换位,即如果可以通过备用路径到达相同的状态,则您无需担心这些备用路径,只需担心游戏状态本身。
国际象棋引擎是如何做到这一点的
大多数国际象棋引擎使用从记录的游戏编译而来的静态开局书籍,因此使用一个简单的二进制文件将这些哈希值映射到分数;例如
,然后按哈希对条目进行排序,并且借助操作系统缓存,进行简单的二进制搜索< /a> 通过文件会很快找到需要的条目。
更新分数
但是,如果你想让引擎不断学习,你将需要更复杂的数据结构;此时通常不值得您自己动手,您应该使用可用的库。我可能会使用 LevelDB,但是任何可以让你存储键值对的东西都是很好(Redis、SQLite、GDBM 等)
学习分数
更新分数的具体方式取决于您的游戏。在有大量可用数据的游戏中,一种简单的方法是有效的,例如仅存储导致位置的移动后获胜的游戏百分比;如果数据较少,您可以将从相关位置开始的博弈树搜索结果存储为分数。诸如Q学习之类的机器学习技术也是一种可能性,尽管我不知道一个在实践中真正做到这一点的程序。
I would approach this with the traditional way an opening book is handled in chess engines:
Looking up a move
Chess engines usually compute a hash function of the current game state via Zobrist hashing, which is a simple way to construct a good hash function for gamestates.
The big advantage of this approach is that it takes care of transpositions, that is, if the same state can be reached via alternate paths, you don't need to worry about those alternate paths, only about the game states themselves.
How chess engines do this
Most chess engines use static opening books that are compiled from recorded games and hence use a simple binary file that maps these hashes to a score; e.g.
The entries are then sorted by hash, and thanks to operating system caching, a simple binary search through the file will find the needed entries very quickly.
Updating the scores
However, if you want the engine to learn continously, you will need a more complicated data structure; at this point it is usually not worth doing yourself, and you should use an available library. I would probably use LevelDB, but anything that lets you store key-value pairs is fine (Redis, SQLite, GDBM, etc.)
Learning the scores
How exactly you update the scores depends on your game. In games with a lot of data available, a simple approach such as just storing the percentage of games won after the move that resulted in the position works; if you have less data, you can store the result of a game tree search from the position in question as score. Machine learning techniques such as Q learning are also a possibility, although I do not know of a program that actually does this in practice.
我假设您的问题是询问如何将决策树转换为串行格式,该格式可以写入某个位置,然后用于重建树。
尝试使用树的前序遍历,使用 toString() 函数(或其等效函数)将存储在决策树每个节点的数据转换为文本描述符。我所说的前序遍历是指实现一种算法,该算法首先在节点上执行 toString() 操作,并将输出写入数据库或文件,然后以指定的顺序在其子节点上递归地执行相同的操作。因为您正在处理稀疏树,所以您的 toString() 操作还应该包括有关子树是否存在的信息。
重建树很简单 - 第一个存储的值是根节点,第二个是左子树的根成员,依此类推。为每个节点存储的串行数据应提供有关下一个输入节点应属于哪个子树的信息。
I'm assuming your question is asking about how to convert a decision tree into a serial format that can be written to a location and later used to reconstruct the tree.
Try using a pre-order traversal of the tree, using a toString() function (or its equivalent) to convert the data stored at each node of the decision tree to a textual descriptor. By pre-order traversal, I mean implementing an algorithm that first performs the toString() operation on the node, and writes the output to a database or file, and then recursively performs the same operation on its child nodes, in a specified order. Because you are dealing with a sparse tree, your toString() operation should also include information about the existence or non-existence of subtrees.
Reconstructing the tree is simple - the first stored value is the root node, the second is the root member of the left subtree, and so on. The serial data stored for each node should provide information as to which subtree the next inputted node should belong to.