本文主要介紹如何通過預先設定好的語法規則以及單詞,通過Python來自動生成一些句子。
解析語法
在生成句子之前,我們需要先告訴機器生成句子的語法。
因此,我們先定義一個簡單的語法:
simple_grammar = """
sentence => noun_phrase verb_phrase
noun_phrase => Article Adj* noun
Adj* => null | Adj Adj*
verb_phrase => verb noun_phrase
Article => 一個 | 這個
noun => 女人 | 籃球 | 桌子 | 小貓
verb => 看着 | 坐在 | 聽着 | 看見
Adj => 藍色的 | 好看的 | 小小的
"""
上面的語法其實可以表示成一棵語法樹(Syntax Tree),對此感興趣的話可以參考以下文章:
語法說明
sentence => noun_phrase verb_phrase:表示一個sentence由noun_phrase和verb_phrase組成。Adj => 藍色的 | 好看的 | 小小的:表示Adj一共有藍色的 | 好看的 | 小小的3個選擇。
基於上面的兩條規說明,我們就能夠讀懂上面的語法規則。
觀察上面的語法,我們可以發現一共有兩類詞匯,左邊的詞匯是可以繼續拓展的,右邊的詞匯如果不在左邊,那么是不可拓展的。
我們先以adj為例,編寫代碼。Adj語法如下:
adj_grammar = """
Adj* => null | Adj Adj*
Adj => 藍色的 | 好看的 | 小小的
"""
根據上面的adj_grammar字符串語法規則,我們將其解析成字典格式:
# 解析語法
def create_grammar(grammar_str, split = '=>', line_split = '\n'):
grammar = {}
for line in grammar_str.split(line_split):
if not line.strip():
continue # 跳過空行
else:
exp, stmt = line.split(split)
grammar[exp.strip()] = [s.split() for s in stmt.split('|')]
return grammar
結果如下:

接着我們再將上面的simple_grammar語法規則解析成字典格式:

至此,我們已經能夠使用代碼解析出上面的語法規則了。
生成句子
最后,我們編寫代碼根據上面的語法規則,自動生成句子:
# 生成句子
def generate(gram, target):
if target not in gram:
return target # means target is a terminal expression
# target in gram 意味着target是可以繼續拓展下去的
else:
expaned = [generate(gram, t) for t in random.choice(gram[target])]
return ''.join([e if e!='/n' else '\n' for e in expaned if e != 'null'])
利用編寫的代碼,隨機生成一個句子:

我們可以再定義兩個語法,看一下效果如何:
# 在西部世界里
# 一個”人類“的語言可以定義為:
human = """
human = 自己 尋找 活動
自己 = 我 | 俺 | 我們
尋找 = 找找 | 想找點
活動 = 樂子 | 玩的
"""
# 一個“接待員”的語言可以定義為
host = """
host = 寒暄 報數 詢問 業務相關 結尾
報數 = 我是 數字 號 ,
數字 = 單個數字 | 數字 單個數字
單個數字 = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
寒暄 = 稱謂 打招呼 | 打招呼
稱謂 = 人稱 ,
人稱 = 先生 | 女士 | 小朋友
打招呼 = 你好 | 您好
詢問 = 請問你要 | 您需要
業務相關 = 玩玩 具體業務
玩玩 = null
具體業務 = 喝酒 | 打牌 | 打獵 | 賭博
結尾 = 嗎?
"""
“人類”和“接待員”各自隨機生成5個句子:

可以看到,我們已經成功地通過自定義的語法規則來讓計算機自動生成句子了。
小結
本文主要通過解析語法樹,自動生成句子等方法,實現了一個自動生成句子的程序。
看完這篇文章,大家也可以設計實現自己的句子生成器。
上面生成了許多句子,但是,我們如何判斷哪些句子更為合理(符合人類說話習慣),哪些句子不合理?這時候就需要用到語言模型了,關於語言模型的相關概念及代碼實現,將會在后面的文章講到,敬請期待。
