CS50 Introduction to Artificial Intelligence with Python Lecture 2
第二讲的主题是Knowledge,这里总结第二讲以及第二次作业。
课程地址:https://cs50.harvard.edu/ai/
备注:图片均来自课程课件。
这一讲介绍的知识是指逻辑知识,其目标是让AI具有逻辑推断能力。
Sentence
对世界的断言,该断言用知识语言描述。
命题符号
我们用大写字母表示命题符号,例如$P,Q,R$
逻辑连接词
模型
将真值赋值给每个命题逻辑,例如:
蕴含
当$\alpha$为true时,$\beta$总是true。
推断
从旧的sentence中推断新sentence的过程。
下面介绍一些推断算法。
Model Checking
- 要确定$\mathrm{KB} \vDash \alpha$:
- 列举所有可能的模型。
- 如果在每个模型中$\text{KB}$为真时$α$也为真,则$\text{KB}$蕴含$α$。
- 否则,$\text{KB}$不蕴含$α$。
Model Checking其实就是穷举法,来看一个具体例子:
推断法则
这里给出几个常用的推断法则:
条件 | 结论 |
---|---|
$\alpha \rightarrow \beta,\alpha$ | $\beta $ |
$\alpha \wedge \beta$ | $\alpha$ |
$\neg(\neg \alpha)$ | $\alpha $ |
$\alpha \rightarrow \beta$ | $\neg \alpha \vee \beta$ |
$\alpha \leftrightarrow \beta$ | $(\alpha \rightarrow \beta) \wedge(\beta \rightarrow \alpha)$ |
$\neg(\alpha \wedge \beta)$ | $\neg \alpha \vee \neg \beta$ |
$\neg(\alpha \vee \beta)$ | $\neg \alpha \wedge \neg \beta$ |
$(\alpha \wedge(\beta \vee \gamma))$ | $(\alpha \wedge \beta) \vee(\alpha \wedge \gamma)$ |
$(\alpha \vee(\beta \wedge \gamma))$ | $(\alpha \vee \beta) \wedge(\alpha \vee \gamma)$ |
利用推断法则,可以使用搜索的方法进行定理证明:
- 初始状态:起始知识库
- 动作:推理规则
- 转移模型:推断后的新知识库
- 目标测试:坚持我们试图证明的命题
- 路径成本函数:证明中的步骤数
Resolution
条件 | 结论 |
---|---|
$P \vee Q_{I} \vee Q_{2} \vee \ldots \vee Q_{n},\neg P$ | $Q_{I} \vee Q_{2} \vee \ldots \vee Q_{n}$ |
$\alpha \wedge \beta$ | $\alpha$ |
$P \vee Q ,\neg P \vee R$ | $Q \vee R$ |
$\alpha \rightarrow \beta$ | $\neg \alpha \vee \beta$ |
$P \vee Q_{I} \vee Q_{2} \vee \ldots \vee Q_{n},\neg P \vee R_{I} \vee R_{2} \vee \ldots \vee R_{m}$ | $Q_{I} \vee Q_{2} \vee \ldots \vee Q_{n} \vee R_{I} \vee R_{2} \vee \ldots \vee R_{m}$ |
clause
命题符号的析取:
conjunctive normal form
合取范式为clause的合取,例如
Inference by Resolution
- 要确定$\mathrm{KB} \vDash \alpha$:
- 将$(\mathrm{KB} \wedge \neg \alpha)$转换为合取范式。
- 检查是否可以使用Resolution来产生一个新的句子。
- 如果我们产生空子句(等价于False),我们产生矛盾,即$\mathrm{KB} \vDash \alpha$。
- 否则,如果我们无法添加新子句,则没有蕴含关系。
Project
Knights
注意这里一定要增加AKnight和AKnave有且仅有一个为True的条件。
AKnight = Symbol("A is a Knight")
AKnave = Symbol("A is a Knave")
BKnight = Symbol("B is a Knight")
BKnave = Symbol("B is a Knave")
CKnight = Symbol("C is a Knight")
CKnave = Symbol("C is a Knave")
# Puzzle 0
# A says "I am both a knight and a knave."
knowledge0 = And(
Or(And(AKnight, And(AKnight, AKnave)), And(AKnave, Not(And(AKnight, AKnave)))),
#有且只有一个为真
Or(And(AKnight, Not(AKnave)), And(AKnave, Not(AKnight))),
)
# Puzzle 1
# A says "We are both knaves."
# B says nothing.
knowledge1 = And(
Or(And(AKnight, And(AKnave, BKnave)), And(AKnave, Not(And(AKnave, BKnave)))),
#有且只有一个为真
Or(And(AKnight, Not(AKnave)), And(AKnave, Not(AKnight))),
Or(And(BKnight, Not(BKnave)), And(BKnave, Not(BKnight))),
)
# Puzzle 2
# A says "We are the same kind."
# B says "We are of different kinds."
knowledge2 = And(
Or(And(AKnight, BKnight), And(AKnave, BKnight)),
Or(And(BKnight, AKnave), And(BKnave, AKnave)),
#有且只有一个为真
Or(And(AKnight, Not(AKnave)), And(AKnave, Not(AKnight))),
Or(And(BKnight, Not(BKnave)), And(BKnave, Not(BKnight))),
)
# Puzzle 3
# A says either "I am a knight." or "I am a knave.", but you don't know which.
# B says "A said 'I am a knave'."
# B says "C is a knave."
# C says "A is a knight."
#Or(And(AKnight, AKnave), And(AKnave, AKnight))
knowledge3 = And(
Or(And(AKnight, Or(AKnight, AKnave)), And(AKnave, Not(Or(AKnight, AKnave)))),
Or(And(BKnight, Or(And(AKnight, AKnave), And(AKnave, AKnight))), And(BKnave, Not(Or(And(AKnight, AKnave), And(AKnave, AKnight))))),
Or(And(BKnight, CKnave), And(BKnave, CKnight)),
Or(And(CKnight, AKnight), And(CKnave, AKnave)),
#有且只有一个为真
Or(And(AKnight, Not(AKnave)), And(AKnave, Not(AKnight))),
Or(And(BKnight, Not(BKnave)), And(BKnave, Not(BKnight))),
Or(And(CKnight, Not(CKnave)), And(CKnave, Not(CKnight)))
)
Minesweeper
Sentence
这部分比较简单,只要按照提示来即可。
class Sentence():
"""
Logical statement about a Minesweeper game
A sentence consists of a set of board cells,
and a count of the number of those cells which are mines.
"""
def __init__(self, cells, count):
self.cells = set(cells)
self.count = count
def __eq__(self, other):
return self.cells == other.cells and self.count == other.count
def __str__(self):
return f"{self.cells} = {self.count}"
def known_mines(self):
"""
Returns the set of all cells in self.cells known to be mines.
"""
#raise NotImplementedError
if len(self.cells) == self.count:
if len(self.cells) != 0:
return self.cells
return None
def known_safes(self):
"""
Returns the set of all cells in self.cells known to be safe.
"""
#raise NotImplementedError
if self.count == 0:
if len(self.cells) != 0:
return self.cells
return None
def mark_mine(self, cell):
"""
Updates internal knowledge representation given the fact that
a cell is known to be a mine.
"""
#raise NotImplementedError
if cell in self.cells:
self.cells.remove(cell)
self.count -= 1
def mark_safe(self, cell):
"""
Updates internal knowledge representation given the fact that
a cell is known to be safe.
"""
#raise NotImplementedError
if cell in self.cells:
self.cells.remove(cell)
MinesweeperAI
注意对knowledge要进行去重处理。
class MinesweeperAI():
"""
Minesweeper game player
"""
def __init__(self, height=8, width=8):
# Set initial height and width
self.height = height
self.width = width
# Keep track of which cells have been clicked on
self.moves_made = set()
# Keep track of cells known to be safe or mines
self.mines = set()
self.safes = set()
# List of sentences about the game known to be true
self.knowledge = []
def mark_mine(self, cell):
"""
Marks a cell as a mine, and updates all knowledge
to mark that cell as a mine as well.
"""
self.mines.add(cell)
for sentence in self.knowledge:
sentence.mark_mine(cell)
def mark_safe(self, cell):
"""
Marks a cell as safe, and updates all knowledge
to mark that cell as safe as well.
"""
self.safes.add(cell)
for sentence in self.knowledge:
sentence.mark_safe(cell)
def add_knowledge(self, cell, count):
"""
Called when the Minesweeper board tells us, for a given
safe cell, how many neighboring cells have mines in them.
This function should:
1) mark the cell as a move that has been made
2) mark the cell as safe
3) add a new sentence to the AI's knowledge base
based on the value of `cell` and `count`
4) mark any additional cells as safe or as mines
if it can be concluded based on the AI's knowledge base
5) add any new sentences to the AI's knowledge base
if they can be inferred from existing knowledge
"""
#raise NotImplementedError
#1
self.moves_made.add(cell)
#2
self.mark_safe(cell)
#3
cells = []
for i in range(cell[0] - 1, cell[0] + 2):
for j in range(cell[1] - 1, cell[1] + 2):
# Ignore the cell itself
if (i, j) == cell:
continue
# Update count if cell in bounds and is mine
if 0 <= i < self.height and 0 <= j < self.width:
#已统计的无需考虑
if (i, j) in self.mines:
count -= 1
elif (i, j) not in self.safes:
cells.append((i, j))
self.knowledge.append(Sentence(cells, count))
#4
for sentence in self.knowledge:
safe = sentence.known_safes()
mine = sentence.known_mines()
if safe != None:
self.safes = self.safes.union(safe)
if mine != None:
self.mines = self.mines.union(mine)
#5
n = len(self.knowledge)
for i in range(n):
for j in range(i + 1, n):
s1 = self.knowledge[i]
s2 = self.knowledge[j]
if s1.cells.issubset(s2.cells):
cells = s2.cells - s1.cells
s = Sentence(s2.cells - s1.cells, s2.count - s1.count)
#判断是否重复
if s not in self.knowledge:
self.knowledge.append(s)
elif s2.cells.issubset(s1.cells):
cells = s1.cells - s2.cells
s = Sentence(s1.cells - s2.cells, s1.count - s2.count)
#判断是否重复
if s not in self.knowledge:
self.knowledge.append(s)
def make_safe_move(self):
"""
Returns a safe cell to choose on the Minesweeper board.
The move must be known to be safe, and not already a move
that has been made.
This function may use the knowledge in self.mines, self.safes
and self.moves_made, but should not modify any of those values.
"""
#raise NotImplementedError
for cell in self.safes:
if cell not in self.moves_made:
return cell
return None
def make_random_move(self):
"""
Returns a move to make on the Minesweeper board.
Should choose randomly among cells that:
1) have not already been chosen, and
2) are not known to be mines
"""
#raise NotImplementedError
for i in range(self.height):
for j in range(self.width):
if (i, j) not in self.moves_made and (i, j) not in self.mines:
return (i, j)
return None
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Doraemonzzz!
评论
ValineLivere