cs50ai3

这篇具有很好参考价值的文章主要介绍了cs50ai3。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

cs50ai3-------Optimization


  • cs50ai3-------Optimization
    • 基础知识
    • 课后题目
    • 代码实践
    • 学习链接
    • 总结

基础知识

这节课主要讲了一些优化问题对应的算法求解,其实具体使用时还是需要具体分析,看哪些问题能够转化为我们学习的算法能够求解的形式
local search与hill climbing与linear programming这三种算法都比较直观简单,这里就不多讲
值得一提的是,课上讲了爬山算法的几种变体,具体如下图所示:
cs50ai3
但是也不是变体就能一定解决陷入局部最优解的问题,也没有某种最好的方法,只有最适合的方法

接着是退火算法的介绍,顾名思义,这种算法避免陷入局部最优解的方法,是引入一个温度的概念,在刚开始的一段时间里温度较高,算法越有可能去做出随机的决策,随着时间流逝(算法迭代次数的增加),温度降低,做出随机决策的概率也降低
具体流程如下所示:
cs50ai3
从起始状态开始,我们随机的选择一个neighbor state,计算我们选择的state比现在的state好了多少,用ΔE来表示,如果ΔE大于0,我们就选择neighbor,如果小于0,我们也有 eΔE/T的概率去选择较差的neighbor,观察这个概率式子我们可以发现,ΔE<0时,温度越高,越有可能做出这样的选择,而ΔE越小,做出这样选择的概率越低

然后是关于约束满足问题的讨论,比如说对于下面这个问题:
cs50ai3

每个学生有A-G之间的几门课程,要求每个学生的课程之间不能在同一天考试,我们可以画出下面这样的图来表示它们之间的约束关系:
cs50ai3

此类约束满足问题大致有以下特征:
Set of variables (x₁, x₂, …, xₙ) 一组变量
Set of domains for each variable {D₁, D₂, …, Dₙ} 每个变量有对应的值域
Set of constraints C 一组约束

接着介绍了不同种类的约束:
A Hard Constraint is a constraint that must be satisfied in a correct solution.
A Soft Constraint is a constraint that expresses which solution is preferred over others.
A Unary Constraint is a constraint that involves only one variable. In our example, a unary constraint would be saying that course A can’t have an exam on Monday {A ≠ Monday}.
A Binary Constraint is a constraint that involves two variables. This is the type of constraint that we used in the example above, saying that some two courses can’t have the same value {A ≠ B}.

接着重点讨论了如何使问题满足arc consistency的算法
cs50ai3
对于两个变量之间的binary constraint与所有变量之间的binary constraint 我们可以分别采用以下算法来完成:
cs50ai3
cs50ai3
但是我们可以注意到ac-3算法是无法解决我们之前为学生安排考试的问题的,是因为它没有考虑到变量之间是如何连接的,我们可以使用之前search的思想来将这个csp问题转换为search问题:
cs50ai3

我们可以采用回溯搜索来解决csp问题:
cs50ai3
具体来说,对于之前的那个问题:
cs50ai3
我们从A的第一个value出发,设置A=mon,再加入B,B=mon不符合约束,所以B=tue,以此类推,直到整个assignment不再更新

我们可以对回溯搜索进一步的优化
首先,我们可以在每次加入var=value之后,都运行ac-3算法来维护整体的arc-consistency,这样就可以利用我们的knowledge来进行inference,删除某些节点的value,简化搜索过程:
cs50ai3
其次,在每次选择节点var时,我们可以优先选择domain中value最少的节点,在最开始value数目相同的时候,我们可以优先选择和其它节点连接最紧密的节点,即degree最大的节点,显然这样做可以最大的减少搜索深度与我们要搜索的空间:
cs50ai3
最后,在遍历每个节点domain的value时,我们可以优先选择受到最小约束的value,这样做我们找到问题的一个solution的概率可以最大化:
cs50ai3
cs50ai3
比如对于上面这个问题,在选择c节点的value时,tue受到b,e,f三个节点的限制,wed受到b,e两个节点的限制,所以我们优先选择wed

课后题目

understanding:
该项目中有两个Python文件:crossword.py和generate.py。 第一个完全是为您编写的,第二个有一些功能留给您来实现。
首先,我们来看看 crossword.py。 该文件定义了两个类:Variable(代表填字游戏中的变量)和 Crossword(代表填字游戏本身)。
请注意,要创建变量,我们必须指定四个值:其行 i、列 j、其方向(常量 Variable.ACROSS 或常量 Variable.DOWN)及其长度。
Crossword 类需要两个值来创建新的纵横字谜游戏:一个定义谜题结构的 Structure_file(_ 用于表示空白单元格,任何其他字符表示不会被填充的单元格)和一个定义了 Words_file 用于拼图词汇的单词列表(每行一个)。 每个文件的三个示例都可以在项目的数据目录中找到,也欢迎您创建自己的文件。
特别注意,对于任何填字游戏对象填字游戏,我们存储以下值:
crossword.height 是一个整数,表示填字游戏的高度。
crossword.width 是一个整数,表示填字游戏的宽度。
crossword.struct 是一个表示谜题结构的 2D 列表。 对于任何有效的第 i 行和第 j 列,如果单元格为空白(必须在其中填充字符),crossword.struct[i][j] 将为 True,否则为 False(该单元格中不填充任何字符) 。
crossword.words 是构建填字游戏时要从中提取的所有单词的集合。
crossword.variables 是拼图中所有变量的集合(每个变量都是一个 Variable 对象)。
crossword.overlaps 是一个字典,将一对变量映射到它们的重叠部分。 对于任何两个不同的变量 v1 和 v2,如果两个变量没有重叠,则 crossword.overlaps[v1, v2] 将为 None;如果变量重叠,则为一对整数 (i, j)。 (i, j) 对应解释为 v1 值的第 i 个字符必须与 v2 值的第 j 个字符相同。
Crossword 对象还支持 Neighbors 方法,该方法返回与给定变量重叠的所有变量。 也就是说,crossword.neighbors(v1) 将返回与变量 v1 相邻的所有变量的集合。
接下来,看一下generate.py。 在这里,我们定义了一个 CrosswordCreator 类,我们将用它来解决填字游戏。 创建 CrosswordCreator 对象时,它会获得一个应该是 Crossword 对象的填字游戏属性(因此具有上述所有属性)。 每个 CrosswordCreator 对象还获得一个domains属性:一个字典,它将变量映射到变量可能采用的一组可能的单词作为值。 最初,这组单词是我们词汇表中的所有单词,但我们很快就会编写函数来限制这些域。
我们还为您定义了一些函数来帮助您测试代码: print 将在终端上打印给定作业的填字游戏的表示形式(在该函数和其他地方,每个作业都是一个将变量映射到其对应的字典) 字)。 同时,保存将生成与给定作业相对应的图像文件(如果您还没有使用此功能,则需要 pip3 安装 Pillow)。 letter_grid 是 print 和 save 都使用的辅助函数,它会生成一个二维列表,其中包含给定作业的适当位置的所有字符
最后,注意solve函数。 该函数做了三件事:首先,它调用enforce_node_consistency来强制填字游戏中的节点一致性,确保变量域中的每个值都满足一元约束。 接下来,该函数调用 ac3 来强制弧一致性,确保满足二进制约束。 最后,该函数对最初为空的赋值(空字典 dict())调用回溯,以尝试计算问题的解决方案。
不过,force_node_consistency、ac3 和 backtrack 函数(以及其他函数)尚未实现。 这就是留给你完成的地方

specification:
在generate.py中完成enforce_node_consistency、revise、ac3、assignment_complete、consistent、order_domain_values、selected_unassigned_variable和backtrack的实现,以便您的AI在可能的情况下生成完整的填字游戏。

force_node_consistency 函数应该更新 self.domains 以使每个变量都是节点一致的。
回想一下,当对于每个变量,其域中的每个值都与变量的一元约束一致时,就实现了节点一致性。 在填字游戏中,这意味着确保变量域中的每个值的字母数量与变量的长度相同。
要从变量 v 的域中删除值 x,由于 self.domains 是将变量映射到值集的字典,因此可以调用 self.domains[v].remove(x)。
该函数不需要返回值。

revise函数应该使变量x与变量y一致。
x 和 y 都将是代表拼图中变量的 Variable 对象。
回想一下,当 x 域中的每个值在 y 域中都有一个不会引起冲突的可能值时,x 与 y 是弧一致的。 (填字游戏中的冲突是指两个变量在其重叠的地方的value不一致。)
为了使 x 弧与 y 一致,您需要从 x 域中删除在 y 域中没有对应可能值的任何值。
回想一下,您可以访问 self.crossword.overlaps 来获取两个变量之间的重叠(如果有)。
y 的定义域应保持不变。
如果对 x 的域进行了修改,则该函数应返回 True; 如果没有进行修改,它应该返回 False。

ac3 函数应该使用 AC3 算法来强制解决问题的弧一致性。 回想一下,当每个变量域中的所有值都满足该变量的二进制约束时,就实现了弧一致性。
回想一下,AC3 算法维护一个要处理的弧队列。 该函数采用一个名为 arcs 的可选参数,表示要处理的初始弧列表。 如果 arcs 为 None,则您的函数应从问题中所有弧的初始队列开始。 否则,您的算法应从仅包含弧列表中的弧的初始队列开始(其中每个弧都是变量 x 和不同变量 y 的元组 (x, y))。
回想一下,要实现 AC3,您将一次修改队列中的每个弧。 不过,每当您对域进行更改时,您可能需要向队列中添加额外的弧,以确保其他弧保持一致。
您可能会发现在 ac3 的实现中调用 revise 函数很有帮助。
如果在强制弧一致性的过程中,您从域中删除了所有剩余值,则返回 False(这意味着无法解决问题,因为变量不再有可能的值)。 否则,返回 True。
您无需担心在此函数中强制执行单词唯一性(您将在一致函数中实现该检查。)

assignment_complete 函数应该(顾名思义)检查给定的分配是否完成。
assignment是一个字典,其中键是 Variable 对象,值是表示这些变量将采用的单词的字符串。
如果每个填字游戏变量都分配了一个值(无论该值是什么),则分配完成。
如果赋值完成,该函数应返回 True,否则返回 False。

consistent函数应该检查给定的分配是否一致。
如果满足问题的所有约束,则赋值是一致的:也就是说,所有值都是不同的,每个值都是正确的长度,并且相邻变量之间不存在冲突。
如果赋值一致,该函数应返回 True,否则返回 False。

order_domain_values 函数应返回 var 域中所有值的列表,并根据最小约束值启发式排序。
var 将是一个 Variable 对象,代表拼图中的变量。
回想一下,最小约束值启发法是根据相邻未分配变量排除的值的数量来计算的。
请注意,assignment中存在的任何变量如果已经有一个值,在计算排除相邻未赋值变量的值的数量时不应将其计算在内。
对于消除了的域值相邻变量的可能选择数量相同,任何顺序都是可以接受的。
回想一下,您可以访问 self.crossword.overlaps 来获取两个变量之间的重叠(如果有)。
首先通过以任意顺序返回值列表来实现此函数可能会有所帮助(这仍然应该生成正确的填字游戏)。 一旦您的算法开始工作,您就可以返回并确保以正确的顺序返回值。
您可能会发现根据特定键对列表进行排序很有帮助:Python 包含一些有助于实现此目的的有用函数。

select_unassigned_variable 函数应根据最小剩余值启发式和度启发式返回填字游戏中尚未通过赋值进行分配的单个变量。
赋值是一个字典,其中键是 Variable 对象,值是表示这些变量将采用的单词的字符串。 您可能会认为分配不会完成:并非所有变量都会出现在分配中。
您的函数应该返回一个 Variable 对象。 您应该返回其域中剩余值数量最少的变量。 如果变量之间存在联系,则应选择这些变量中度数最大(具有最多邻居)的变量。 如果两种情况都存在平局,您可以在平局变量中任意选择。
首先通过返回任意未分配的变量(仍应生成正确的填字游戏)来实现此函数可能会有所帮助。 一旦您的算法开始工作,您就可以返回并确保根据启发式返回一个变量。
您可能会发现根据特定键对列表进行排序很有帮助:Python 包含一些有助于实现此目的的有用函数。

backtrack函数应接受部分赋值作为输入,并使用回溯搜索,返回完整的令人满意的变量值赋值(如果可能的话)。

代码实践

本次crossword作业总体上就是一个csp问题的实践,用到的算法以及优化的思想在前面的基础知识部分均有涉及

首先是unary consistency与arc consistency的维护:

    def enforce_node_consistency(self):
        """
        Update `self.domains` such that each variable is node-consistent.
        (Remove any values that are inconsistent with a variable's unary
         constraints; in this case, the length of the word.)
        """
        for x in self.crossword.words:
            for v in self.domains.keys():
                if len(x) != v.length:
                    self.domains[v].remove(x)

    def revise(self, x, y):
        """
        Make variable `x` arc consistent with variable `y`.
        To do so, remove values from `self.domains[x]` for which there is no
        possible corresponding value for `y` in `self.domains[y]`.

        Return True if a revision was made to the domain of `x`; return
        False if no revision was made.
        """
        loc = self.crossword.overlaps[x, y]
        remove_words = []
        if loc != None:
            i, j = loc
            revised = False
            for word in self.domains[x]:
                flag = True
                for candi in self.domains[y]:
                    if word[i] == candi[j]:
                        flag = False
                        break
                if flag:
                    remove_words.append(word)
                    revised = True
            for w in remove_words:
                self.domains[x].remove(w)
            return revised
        return False

    def ac3(self, arcs=None):
        """
        Update `self.domains` such that each variable is arc consistent.
        If `arcs` is None, begin with initial list of all arcs in the problem.
        Otherwise, use `arcs` as the initial list of arcs to make consistent.

        Return True if arc consistency is enforced and no domains are empty;
        return False if one or more domains end up empty.
        """
        if not arcs:
            queue = []
            for variable1 in self.domains:
                for variable2 in self.crossword.neighbors(variable1):
                    if self.crossword.overlaps[variable1, variable2] is not None:
                        queue.append((variable1, variable2))

        while len(queue) > 0:
            x, y = queue.pop(0)
            if self.revise(x, y):
                if len(self.domains[x]) == 0:
                    return False
                for neighbour in self.crossword.neighbors(x):
                    if neighbour != y:
                        queue.append((neighbour, x))
            return True

其次是检测当前纵横字谜是否已经完成,并且要检测填入的单词是否符合我们的限制,比如说单词的长度,单词与单词之间不能相同,重叠部分的单词应该相同等等

    def assignment_complete(self, assignment):
        """
        Return True if `assignment` is complete (i.e., assigns a value to each
        crossword variable); return False otherwise.
        """
        for variable in self.domains:
            if variable not in assignment:
                return False
        return True

    def consistent(self, assignment):
        """
        Return True if `assignment` is consistent (i.e., words fit in crossword
        puzzle without conflicting characters); return False otherwise.
        """
        # 检查长度是否满足谜语的长度
        for v, word in assignment.items():
            if v.length != len(word):
                return False
        # 检查每个单词是否不同
        if len(assignment) != len(set(assignment.values())):
            return False
        # 检查重叠的部分单词是否一致
        for var_pair, loc in self.crossword.overlaps.items():
            if loc != None:
                x, y = var_pair
                i, j = loc
                if x in assignment and y in assignment and assignment[x][i] != assignment[y][j]:
                    return False
        return True


然后是我们之前提到过的优化部分,第一部分就是在遍历每个变量的值域时,我们会选择受到最小约束的那部分,但是这里我搞了半天也没有看懂代码究竟是怎么实现的,于是作罢,第二部分比较简单,就是在选择变量时,选择值域较小与度较大的变量

    def order_domain_values(self, var, assignment):
        """
        Return a list of values in the domain of `var`, in order by
        the number of values they rule out for neighboring variables.
        The first value in the list, for example, should be the one
        that rules out the fewest values among the neighbors of `var`.
        """
        return list(self.domains[var])

    def select_unassigned_variable(self, assignment):
        """
        Return an unassigned variable not already part of `assignment`.
        Choose the variable with the minimum number of remaining values
        in its domain. If there is a tie, choose the variable with the highest
        degree. If there is a tie, any of the tied variables are acceptable
        return values.
        """
        # 获取所有未分配的变量
        unassigned = set(self.domains.keys()) - set(assignment.keys())
        # 如果没有未分配的变量,则返回 None
        if not unassigned:
            return None
        # 定义一个函数,用于比较变量的剩余值数量和度
        def variable_score(variable):
            # 获取变量的剩余值数量
            remaining_values = len(self.domains[variable])
            # 获取变量的度(与其他变量相邻的数量)
            degree = len(self.crossword.neighbors(variable))
            # 返回一个元组,包含剩余值数量和度,用于排序
            return (remaining_values, -degree)  # 使用负数表示度越高越好
        # 使用 variable_score 函数对未分配的变量进行排序
        sorted_variables = sorted(unassigned, key=variable_score)
        # 返回排序后的第一个变量,即剩余值数量最少且度最高的变量
        return sorted_variables[0]

最后就是之前的backtrack函数的伪代码的具体实现,利用了我们之前完成的一些函数即可:

    def backtrack(self, assignment):
        """
        Using Backtracking Search, take as input a partial assignment for the
        crossword and return a complete assignment if possible to do so.

        `assignment` is a mapping from variables (keys) to words (values).

        If no assignment is possible, return None.
        """
        if self.assignment_complete(assignment):
            return assignment
        unassigned_var = self.select_unassigned_variable(assignment)
        domain = self.order_domain_values(unassigned_var, assignment)
        for word in domain:
            new_assignment = assignment.copy()
            new_assignment[unassigned_var] = word
            if self.consistent(new_assignment):
                result = self.backtrack(new_assignment)
                if result != None:
                    return result
        return None

学习链接

参考代码链接:https://github.com/wbsth/cs50ai
https://github.com/PKUFlyingPig/cs50_ai
视频链接(b站中文机翻字幕): https://www.bilibili.com/video/BV1AQ4y1y7wy/?p=5&vd_source=23b9ed7e58fa7e510011caaf2e7e3320
课程官网(全套资源):https://cs50.harvard.edu/ai/2023/

总结

cs50ai这门课拖了好久,其实课程note已经完成了,只是感觉project做起来太费劲,太懒,准备抓紧,然后将重心转向umich cv文章来源地址https://www.toymoban.com/news/detail-710361.html

到了这里,关于cs50ai3的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 第二章:AI大模型基础知识 2.1 机器学习基础

    随着计算机技术的飞速发展,人工智能已经成为了当今科技领域的热门话题。在这个过程中,机器学习作为人工智能的一个重要分支,扮演着至关重要的角色。本文将从机器学习的基本概念、核心算法原理、具体操作步骤、实际应用场景等方面进行详细讲解,帮助读者更好地

    2024年02月21日
    浏览(57)
  • UE4的AI行为树基础知识

            在制作游戏时,会制作敌人、怪物、NPC等不被玩家所操作的对象,那么制作这些对象,就需要通过使用AI行为树来为他们编写各自的一些行为逻辑,比如敌人会寻找主角并攻击、怪物会在自己的领域巡逻等等。 NavMeshBoundsVolume:导航网格体边界体积,用作导航寻路,会

    2024年02月11日
    浏览(38)
  • AI模型部署基础知识(一):模型权重与参数精度

    一般情况来说,我们通过收集数据,训练深度学习模型,通过反向传播求导更新模型的参数,得到一个契合数据和任务的模型。这一阶段,通常使用pythonpytorch进行模型的训练得到pth等类型文件。AI模型部署就是将在python环境中训练的模型参数放到需要部署的硬件环境中去跑,

    2024年01月20日
    浏览(50)
  • AIGC内容分享(二十):「AI视频生成」技术核心基础知识和模型应用

    目录 何为AI视频? 一、技术发展概况 二、代表模型及应用​​​​​​​ 三、仍存在许多技术难点 「 AI 视频」 通常指的是由人工智能(AI)技术生成或处理的视频。这可能包括使用深度学习、计算机视觉和其他相关技术来改善视频的质量、内容或生成全新的视频内容。一

    2024年01月18日
    浏览(56)
  • 实时AI绘画模型SDXL Turbo核心基础知识详解 | 【算法兵器谱】

    Rocky Ding 公众号:WeThinkIn 【算法兵器谱】栏目专注分享AI行业中的前沿/经典/必备的模型论文,并对具备划时代意义的模型论文进行全方位系统的解析。也欢迎大家提出宝贵的优化建议,一起交流学习💪 大家好,我是Rocky。 如果说2022年,Stable Diffusion横空出世,成为AI行业从传

    2024年01月16日
    浏览(54)
  • 控制系统中的AI、AO、DI、DO是什么意思——控制系统基础知识

      控制系统中AI、AO、DI、DO是集散控制系统中模块上常见的一些基本标注,好处就是便于分清什么类型量的设备,方便前期的产品选型及后期的维修与保养。   同时将现场模拟量仪表和开关量设备等进行清晰分类,便于后期仪表和设备的弱电信号接线。 其实很简单,AI、

    2024年01月20日
    浏览(45)
  • 【STM32】基础知识 第五课 C 语言基础知识

    stdint.h 是从 C99 中引进的一个标准 C 库的文件. 路径: “D:MDK5.34ARMARMCCinclude” 运算符 含义 运算符 含义 按位与 ~ 按位取反 | 按位或 左移 ^ 按位异或 右移 按位与: num1 运算符 num2 结果 0 0 0 1 0 0 0 1 0 1 1 1 按位或: num1 运算符 num2 结果 0 | 0 0 1 | 0 1 0 | 1 1 1 | 1 1 按位异或: num1 运算符

    2024年02月13日
    浏览(74)
  • 数字电路基础知识系列(六)之LC滤波器的基础知识

    LC滤波器,是指将电感(L)与电容器 ©进行组合设计构成的滤波电路,可去除或通过特定频率的无源器件。电容器具有隔直流通交流,且交流频率越高越容易通过的特性。而电感则具有隔交流通直流,且交流频率越高越不易通过的特性。因此,电容器和电感是特性完全相反的被

    2024年02月03日
    浏览(89)
  • Unity | Shader基础知识(第九集:shader常用单词基础知识速成)

    目录 一、顶点(Vertex)和法线(Normal) 二、UV信息 三、 基础数据种类 1 基础数据种类 2 基础数据数组 3 基础数据数组的赋值 4 对数据数组的调用 四、 基础矩阵 1 基础矩阵种类  2 对矩阵数组的调用 2.1对一个数据的调用  2.2对多个数据的调用  2.3对数据的赋值 五、基础纹理种

    2024年02月01日
    浏览(73)
  • Opengl入门基础-基础知识

    通过之前的教程,我们已经拥有了开发环境,但是在真正开发程序之前,我们首先了解下Opengl的基本概念。 Opengl是什么? 通常网上会说Opengl是一种规范,一种接口,但是这种说法有点抽象,我们不妨先看看下面这个简单的gl流程 代码中可能有人对GLFW_OPENGL_PROFILE这类参数感到

    2024年02月11日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包