python – 自制国际象棋引擎的准确率极低(<1%)

huangapple go评论89阅读模式
英文:

python - EXTREMELY low accuracy (<1%) on homemade chess engine

问题

I am into chess. And no, I do not plan on using this engine for cheating. Right now, it has less than 1% accuracy. No doubt am I better than it. It generates the correct syntax I want it to generate, like d4e5 (piece on d4 captures e5). However, it is so innacurate that it often suggests illegal moves, like d6c7, when there is no piece on d6 or c7, or in other ways illegal. It has a 0.6% accuracy. Even 1980s chess bots could do better. So, how would I increase the accuracy on my model? My goal is at least 30%.

以下是代码部分,不需要翻译。

英文:

I am into chess. And no, I do not plan on using this engine for cheating. Right now, it has less than 1% accuracy. No doubt am I better than it. It generates the correct syntax I want it to generate, like d4e5 (piece on d4 captures e5). However, it is so innacurate that it often suggests illegal moves, like d6c7, when there is no piece on d6 or c7, or in other ways illegal. It has a 0.6% accuracy. Even 1980s chess bots could do better. So, how would I increase the accuracy on my model? My goal is at least 30%.

<code>

import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.svm import LinearSVC

df = pd.read_csv(&#39;train.csv&#39;)

train_df = df[:800]
test_df = df[800:]

vectorizer = TfidfVectorizer(ngram_range=(1, 2))
X_train_features = vectorizer.fit_transform(train_df[&#39;board&#39;])

clf = LinearSVC(random_state=0, tol=1e-5)
clf.fit(X_train_features, train_df[&#39;best_move&#39;])

X_test_features = vectorizer.transform(test_df[&#39;board&#39;])
predicted_moves = clf.predict(X_test_features)

correct_moves = predicted_moves == test_df[&#39;best_move&#39;]
accuracy = sum(correct_moves) / len(predicted_moves)
print(&quot;Accuracy:&quot;, accuracy)

import pickle
with open(&#39;model.pkl&#39;, &#39;wb&#39;) as f:
	pickle.dump(clf, f)
with open(&#39;vectorizer.pkl&#39;, &#39;wb&#39;) as f:
    pickle.dump(vectorizer, f)

with open(&#39;model.pkl&#39;, &#39;rb&#39;) as f:
    clf = pickle.load(f)
with open(&#39;vectorizer.pkl&#39;, &#39;rb&#39;) as f:
    vectorizer = pickle.load(f)

new_data = pd.DataFrame({&#39;board&#39;: [&#39;8/8/2B1p3/6k1/6P1/1K6/3P4/6bQ w - - 0 1&#39;]})

new_data_features = vectorizer.transform(new_data[&#39;board&#39;])

predicted_move = clf.predict(new_data_features)[0]

print(predicted_move)

</code>

答案1

得分: 2

以下是您要翻译的内容:

  • Input encoding: 让我们考虑两个类似的棋盘,都是白方走棋。

    第一个棋盘链接: https://lichess.org/analysis/4k3/6Q1/8/3p4/8/BK6/8/8_w_-_-_0_1?color=white

    在这里,最佳着法是 Qe7#。

    第二个棋盘链接: https://lichess.org/analysis/4k3/6Q1/3p4/8/8/BK6/8/8_w_-_-_0_1?color=white

    在这里,走 Qe7 会失去皇后。白方在那之后最多只能将比赛变成平局。

    现在,看一下每个棋盘状态的FEN(Forsyth–Edwards Notation):

    4k3/6Q1/8/3p4/8/BK6/8/8 w - - 0 1
    4k3/6Q1/3p4/8/8/BK6/8/8 w - - 0 1
    

    这两个FEN只有8和3p4标记的顺序不同。让我们看看它们在经过TfidfVectorizer转换后会发生什么:

    from sklearn.feature_extraction.text import TfidfVectorizer
    positions = [
        "4k3/6Q1/8/3p4/8/BK6/8/8 w - - 0 1",
        "4k3/6Q1/3p4/8/8/BK6/8/8 w - - 0 1",
    ]
    vectorizer = TfidfVectorizer(ngram_range=(1, 2))
    vectorizer.fit(documents)
    for position in positions:
        print(position, vectorizer.transform([position]).todense())
    

    输出:

    4k3/6Q1/8/3p4/8/BK6/8/8 w - - 0 1 [[0.37796447 0.37796447 0.37796447 0.37796447 0.37796447 0.37796447
      0.37796447]]
    4k3/6Q1/3p4/8/8/BK6/8/8 w - - 0 1 [[0.37796447 0.37796447 0.37796447 0.37796447 0.37796447 0.37796447
      0.37796447]]
    

    这两个棋盘向量化后得到相同的结果。无论您的模型有多好,它都无法区分这两个棋盘。TfidfVectorizer基于CountVectorizer,假定标记的顺序不重要。然而,这是国际象棋,棋子的顺序是重要的。(CountVectorizer似乎还删除了所有单字符标记,因此模型也不知道轮到谁走棋。)

  • Output encoding: 此代码使用LinearSVC来分类着法。如果最佳着法是在训练期间未出现的着法会发生什么?在这种情况下,模型无法输出该着法。

    我对来自Lichess上随机对局的1000万个着法进行了分析。其中有10%的着法从未出现在最受欢迎的800个着法中。(至少在代数表示法中如此。)如果您有800个训练示例,模型最多可以选择800个可能的着法。

    相反,我建议输出评估哪一方正在领先,以及领先多少。这将把它从具有800个输出的分类问题转变为具有1个输出的回归问题。您可以评估每个合法的着法,并选择模型认为最强的着法。

  • Game tree search: 游戏树搜索非常强大。很难想象有哪个引擎(除了作为笑话构建的引擎之外)不进行任何形式的游戏树搜索。如果您的目标是创建一个强大的国际象棋引擎,那么您应该考虑最小化或蒙特卡洛树搜索。另一方面,如果您的目标是写一个有趣的国际象棋引擎,那么它不必很强大,但仍然可以很有趣。

英文:

There are a number of choices here that mean that this model will essentially never do what you want.

Input encoding

Let's consider two similar boards, with white to move in both.

First board: https://lichess.org/analysis/4k3/6Q1/8/3p4/8/BK6/8/8_w_-_-_0_1?color=white

Here, the optimal move is Qe7#.

Second board: https://lichess.org/analysis/4k3/6Q1/3p4/8/8/BK6/8/8_w_-_-_0_1?color=white

Here, playing Qe7 will blunder the queen. White can, at best, draw the game after that.

Now, look at the FEN for each board state:

4k3/6Q1/8/3p4/8/BK6/8/8 w - - 0 1
4k3/6Q1/3p4/8/8/BK6/8/8 w - - 0 1

The FEN differs only by the order of the 8 and 3p4 tokens. Let's see what happens to them after being transformed by TfidfVectorizer:

from sklearn.feature_extraction.text import TfidfVectorizer
positions = [
    &quot;4k3/6Q1/8/3p4/8/BK6/8/8 w - - 0 1&quot;,
    &quot;4k3/6Q1/3p4/8/8/BK6/8/8 w - - 0 1&quot;,
]
vectorizer = TfidfVectorizer(ngram_range=(1, 2))
vectorizer.fit(documents)
for position in positions:
    print(position, vectorizer.transform([position]).todense())

Output:

4k3/6Q1/8/3p4/8/BK6/8/8 w - - 0 1 [[0.37796447 0.37796447 0.37796447 0.37796447 0.37796447 0.37796447
  0.37796447]]
4k3/6Q1/3p4/8/8/BK6/8/8 w - - 0 1 [[0.37796447 0.37796447 0.37796447 0.37796447 0.37796447 0.37796447
  0.37796447]]

These two boards vectorize to the same thing. No matter how good your model is, it can't tell the difference between these two boards. TfidfVectorizer is based on CountVectorizer, which assumes that the order of the tokens does not matter. However, this is chess, and the order of the pieces does matter. (CountVectorizer also seems to be dropping all single character tokens, so the model also doesn't know whose turn it is.)

Output encoding

This code is using a LinearSVC to classify moves. What happens if the best move is a move that did not appear during training? In this case, the model is incapable of outputting that move.

I did an analysis of 10 million moves from random games on Lichess. Of those moves, 10% of them never appeared in the most popular 800 moves. (At least, in algebraic notation.) If you have 800 training examples, you can have at most 800 possible moves that the model can choose.

Instead, I would suggest outputting an evaluation of which side is winning, and by how much. This changes it from a classification problem with 800 outputs to a regression problem with 1 output. You can evaluate each legal move, and pick the one the model thinks is strongest.

Game tree search is really strong. It's hard for me to think of any engine (aside from ones constructed as a joke) which don't do any kind of game tree search. If your goal is to create a strong chess engine, you should probably look into minimax or monte-carlo tree search. On the other hand, if your goal is to have fun writing a chess engine, then it doesn't need to be strong to be interesting.

huangapple
  • 本文由 发表于 2023年5月22日 03:11:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76301514.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定