距離完成我的三十天鐵人賽剩下最後的七篇文章了。我打算用一個遊戲--Hangman猜字遊戲來結束這個系列。昨天說到語言模型,語言模型可以以字詞為單位作N-gram,也可以以字母為單位。最後這個系列就要以語言模型為底層技術來寫一個猜字遊戲的AI。
Hangman game的玩法是透過一個人寫下一個字,讓另一個人來猜。對方一次會猜一個英文字母,猜對的話就把字母寫到正確的格子上,猜錯的話則會記錄下來。若是猜錯的次數達到一個數字,則猜字的一方輸。
這裡我們先開發一個簡易版:
def hangman(secret_word, guesser, max_mistakes=8, verbose=True, **guesser_args):
"""
secret_word是將要被猜的字、guesser是我們之後會陸續寫進來的猜字模型(真人猜字或AI)、max_mistakes是最多可以錯誤的次數、
verbose為True時表示互動性猜字(AI猜字時可以改False)、guesser_args是一個keyword argument。
"""
secret_word = secret_word.lower()
mask = ['_'] * len(secret_word) # 把要被猜的字轉成暗文
guessed = set()
if verbose:
print("開始猜字遊戲,提示:", ' '.join(mask), '長度為', len(secret_word))
mistakes = 0
while mistakes < max_mistakes:
if verbose:
print("你還有", (max_mistakes-mistakes), "次機會。")
guess = guesser(mask, guessed, **guesser_args)
if verbose:
print('你猜了:', guess)
if guess in guessed:
if verbose:
print('這個字母已經猜過了!')
mistakes += 1
else:
guessed.add(guess)
if guess in secret_word:
for i, c in enumerate(secret_word):
if c == guess:
mask[i] = c
if verbose:
print('猜對了,', ' '.join(mask))
else:
if verbose:
print('抱歉,再猜猜')
mistakes += 1
if '_' not in mask:
if verbose:
print('恭喜你贏了!')
return mistakes
if verbose:
print('沒有機會了,正確字是', secret_word)
return mistakes
這裡我們先寫一個人機互動猜字版:
def human(mask, guessed, **kwargs):
"""
可以手動遊玩
"""
print('請輸入你要猜的字:')
return input().lower().strip()
interactive = True
試玩遊戲:
if interactive:
hangman('algorithm', human, 8, True)
開始寫我們的AI猜字之前,要先準備訓練集和測試集。從這篇文章中可以看到,照著統計學來猜字是個不錯的做法,因此我們也自己來訓練一組模型,照著出現的頻率為順序進行猜字。
我們使用之前用過的Brown Corpus來訓練AI猜字演算法,為了刻意增加猜字的難度,AI在測試時的字和訓練的字會是不一樣的(把訓練集和測試集拆開),如此才能訓練出更通用的模型。
我們使用 nltk.corpus.Brown
裡的 words()
方法,從中將非英文字母的文字去除,同時將每個字小寫化。之後,我們用 numpy.random.shuffle
來隨機刷取文集中的字,以分成訓練集和測試集。測試集裡有1000字,剩下的字都會到訓練集裡面。
from nltk.corpus import brown
import numpy as np
np.random.seed(12345)
# word_set 儲存Brown Corpus中所有獨特的字型
word_set = []
# test_set 儲存1000個字的測試集
test_set = []
# training_set 將剩下的字存到訓練集中
training_set = []
word_s = set([])
for word in brown.words():
if word.isalpha():
word = word.lower()
if word not in word_s:
word_s.add(word)
word_set = list(word_s)
np.random.shuffle(word_set)
test_set = word_set[:1000]
training_set = word_set[1000:]
print(len(word_set))
print(len(test_set))
print(len(training_set))
這裡一來就不是一個人寫一個字讓對方猜了,而是電腦和人類的對決:
if interactive:
hangman(np.random.choice(test_set), human, 8, True)
大家可以自己玩玩看~今天的Jupyter Notebook在這裏。