七月在线公开课笔记-十六-
七月在线公开课笔记(十六)
七月在线-深度学习集训营 第三期[2022] - P4:在线视频:01-当下最好的语言模型BERT介绍 - 程序员技术手札 - BV1gW4y1x7j7
ok ok那么咱们今天的课程就正式开始了啊,首先给大家做一个自我介绍,我是这边深圳线下的一个负责人啊,我叫jo,然后主要是负责a2 b方向的,今天的话就由我为大家来介绍一下这个目前比较火。
效果也比较好的一个b r t模型啊,今天这个模型呢其实在结构方面还是蛮复杂的。
首先第一点呢就是这个tention机制,而这一块的话可能大家在之前的课可能已经学过了,我这边再带着大家简单回忆一下好吧,第二块的话就是transformer。
transformer的话其实就是啊bt最核心的一个,一个结构啊,说白了b r t其实就是由transformer组合而来的,所以这一块大家是肯定要了解的,然后第三块的话我会带着大家去了解。
然后第四块的话我会为大家再介绍一下,就从b r t从去年年底推出到现在,又推出了哪些新的类似于或者说超越了bt的这些模型,再给大家简单介绍一下,然后最后一块的话就是一个b i t的实战。
我会采用一个文本相似度的这个任务,教大家啊,怎么by two,怎么微调,这个比压线好吧,那咱们就啊正式开始了,首先第一块的话还是在带着大家回忆一下这个tention机制哦,对提一点。
就是说我在讲课的过程中,大家如果有什么问题一定要啊及时给提出来好吧,因为每一块它都是有相关性的,如果你前面没有听懂,例如attention没有听懂,那transformer你可能就听不懂。
然后transformer和bt也是息息相关的,如果transformer那一块你没有弄明白的话啊,b i t这一块你可能就很难理解了好吧,所以大家中途如果有什么问题就及时提出来。
我这边都会第一时间给大家解答,好吧啊,实训作业有的会有几个简答题,还有就是一个bt的一个微调的一个作业,待会上完课,我会把它发到群里好吧,那咱们就正式开始。
首先来大家再回忆了解一下这个attention机制啊,啊我这边的话也带带着大家简单回忆一下这一块,嗯,对于序列生成类的任务来说的话,通常都会采用这个sequence to sequence这个模型。
例如像对对联啊啊文本摘要啊,机器翻译啊,其实都是采用这个sequence to sequence这样一个结构的模型来做的,我们简单回忆一下啊,像我们ppt当中的这个动图。
左边这个绿色的这一块其实就是sequence to sequence,sequence的这个encoder阶段,那紫色这一块呢其实就是它的这个decoder层,其中不管是encode还是抵扣的。
其中的每一个节点,就我说啊这个绿色的圈圈,或者说这个紫色的这一块,它其实都是一个或者说是一个rn的变体啊,那我们简单再回忆一下这个sequence to sequence。
这个模型整体的这个流程是什么样子的,我们这个动图当中呢其实是一个机器翻译的一个小任务,这里的这个例子是把这个法语翻译成了这个英文,咱们简单看一下啊,它这个流程到底是什么样子的。
首先呢在这个encode阶段,就是第一个节点会首先输入一个法语的词,比如这里他输入了一个法语的词之后的节,第一个部分的话是下一个词,第二个部分的话是上一个节点的啊,hidden sat。
那直到这个in code结束的这个地方呢,它输出的表示的就是in code阶段,整个的这个context,这里大家理解吧,这块应该大家很熟悉了,所以这一块我可能不会讲得特别详细。
那对于这个context呢,它又会作为抵扣的阶段,第一个节点的这个初始化的一个hidden state,那第一个你考的阶段,第一个节点的一个输入值呢,其实通常是会采用一个开始符来作为它的输入啊。
每经过一个decoder的节点,就会输入一个翻,译出来的词,例如这里的这个输出了一个i,到了他的第二个节点,就会把这个i作为它的一个输入。
并且把上一个节点的hidden state作为它当前节点的这个啊keen state,再进行一个输入,但是这个模型呢有一个致命的缺点,大家知不知道啊,嗯它致命的缺点是什么,没错就是如果序列特别长。
他可能会丢失一些信息啊,我举一个例子啊,例如在文本摘要这样子的一个任务,就include阶段,它可能序列会很长,可能有上百个字,上千个字都有可能对吧,但是在抵扣的阶段,它可能就是一个简单的标题啊。
或者是一个啊是几十个文字,对于这样的情况呢,include阶段靠前的一部分的一些文本信息,它就很容易丢失,那为了解决这个问题呢,attention就应运而生了,我讲到这里,如果有问题就及时给提出来好吧。
我要第一时间给大家解答,那咱们就继续讲这个attention,turn其实和sequence to sequence啊,这么说吧,attention其实它不是一个模型,它是一种机制。
咱们考虑这么一个场景啊,现在我在做一个英文题目的阅读理解啊,我可能有很多个题目,首先我在做第一个题目的时候,我并不会说我去把整篇文章都读一遍,然后去从中找这个答案,还是说我会去选择这篇文章中的某个段落。
就和当前这个问题相关的某个段落,然后去详细仔细的去阅读那个段落,来得到当前这个题目的答案,那我做下一个题目的时候,我也是去选择一个最适合当前这个题目的那个段落,去详细阅读。
这样才能把这个题目的答案给解决出来对吧,那pension是什么意思呢,例如这个tension,它其实参考的就是我们做阅读理解的时候,只关注整篇段落的一部分,而不会去关注它的一个整体,那咱们看一下啊。
如果要采用tention机制是什么样子的,咱们仔细对比一下啊,啊这边这咱们仔细对比一下这两个动图啊,对于第一个sequence to sequence的模型来说。
这个动图它其实encode阶段它输出的就只有一个context对吧,那我们再看一下这个attention,它输出的他ino的阶段输出的是什么东西,咱们看一下这个动图。
仔细看是不是它输出的是前面三个节点呃,所有的隐藏状态,也就是说他输出了三个节点的隐藏状态,那我在抵扣的阶段的时候,我会把这三个啊encode输出的hidden state作为一个选择,咱们做个类比。
对于抵扣的层,第一个这个嗯稍等一下,对于抵扣的阶段,第一个这个节点就我们类比一下,类比成我们现在在做阅读理解的第一个题,我是不是会从这边这三个段落里面,选择一个最适合当前这个节点的一个段落。
然后详细的去读这个段落,那attention其实咱们类比一下,就是这样的一个机制,从这三个hidden sit里面选一个最适合自己的,然后做了一个加权,到了我们下一个节点,同样是一一啊,一个道理。
还是会做一个加权的选择,那其中这个加权的选择就每一步到底是怎么加权的,咱们就在下一页的ppt给大家讲好吧,咱们继续下去看一下这个呢左边这一块是啊。
上图当中提到的encode阶段输出的三个hidden feat,那这个紫色这一块呢,如果是针对的是第一个节点,我们通常会随机初始化一个这个紫色的这个啊向量。
然后我们要把这个向量和这三个对三个keen state作为一个相乘,就会得到这样的三个值,再把这三个值做一个soft max的操作,就会得到这样的一个权重。
那我们把这个权重呢再和上面这个三个hidden state进行一个相乘,也就是说我考虑考虑到了当前对于当前这个节点来说,我从上面这三个hidden sat选择了一个权重最高的。
你看我们这里计算出来的三个选手分别是0。96,0。02,0。02,也就是说对于我decoder的第一个节点来说啊,0。96这个对应的这个kden state它的权重是最高的,那对啊。
映射到我们的阅读理解当中来说,也就是说我们在做第一个节点这个题的时候,我们发现第一个段落写的相关性是最大的,所以我们会给它设一个较大的权重,那我们把这个权重和这三个hidden state做一个相乘。
最后再把这三个权重进行一个相加,就可以拿到我们最终的这个结果了,啊这个结果的话就会作为一个新的hidden state输入到新的这个节点当中,大家看到这里有没有什么不明白的地方,有什么不明白的地方吗。
如果大家都明白,就扣个一好吧,扣个一让我知道大家都明白了,啊attention是一组固定的参数还是不用的样本,有不同的参数呢,其实tension它只是一种机制,它在不同的场景,它的使用方式是不一样的。
我这里介绍的只是一种最简单的一个tension,那像这里介绍的这种情况,其实这个地方的这个值,那不同的节点,例如我现在到了第二个节点对吧,那第二个节点的其实这个值和上一个节点的这个值肯定是不一样的。
因为我们我们还是类比到这个阅读理解当中啊,我做第一个题考虑的可能是第一个段落,但是我做第二个题的时候,考虑的就不是第一个段落了,考虑就是可能就是第二个或者是第三个段落了,对吧,嗯大家如果有什么问题的话。
直接在那个就是直播间发出来就好了,因为那个qq的话,我这边看起来不是特别方便好吧,我看一下qq这边的问题,为什么是输出三个h啊,你指的三个h,你的意思是说我的影扣encode的阶段为什么会输出三个h吗。
啊大家有什么问题在那个直播间这边提问好吧,可能会唱嗯,我来我先看一下问题啊,新的headset要和原来的state做拼接吗,啊是这样子的,同上有很多种方式,有的方式是不用拼接的啊,有的方式是需要拼接的。
然后这个问题的话,我待会在后面的p p t会讲,好吧啊,比如说decode有四个节点,一共只有四个tension节点对吧,如果decoder是有四个节点的话,那对应的attention就是四次。
对你们说的没错,才四个腾讯节点,每一个就那么几个参数,是的这个是是这样子的,怎么能保证抵扣的权重都是对的呢,是这样子的,我这个权重其实是在反向传播的时候不停的更新的,因为五要最小化的那个损失函数嘛。
我在啊反向传播的过程中就会不停的求导,然后不停的去更新这个权重,所以最后这个权重就是正确的,啊是你说的这个原文各种情况都有,的确会有这种情况,所以就是说啊这个模型因为比较简单嘛对吧,我刚才也说了。
对于这样的模型,如果是attention,为什么会有transformer,为什么会有更比attention这种机制更复杂的一些模型,就是因为它并不能把这些所有的情况都考虑进去。
也就是说它的特征提取效果还没有想象中的那么完美,只是说呃tention他这种机制有一定的效果,那肯定不能做到特别完美对吧,特摄只是一种机制,咱们讲到后面,然后可能对这个某某同学应该也会有更多的理解。
好吧,咱们继续后面的内容啊,主要是tension这种思想,大家这里要掌握好啊,我这边再做一个简单的总结,到底什么时候tention说白了他其实就是一个加权,针对于前面的这个hidden state。
你看我们针对这里前面这三个hidden state,我们做了一个加权,然后把最后进行了一个相加,而腾讯说白了就是一个加权的机制,如何选择最符合当前的这个idc,好吧,大家记住了,他是一个加权机制。
那如果大家都清楚了,我就继续下面的内容了,还有什么不明白的地方吗,如果attention机制不了解的话啊,说到transformer的话,大家可能会有点懵,那我就继续讲transform嘛,好吧。
哦哦对,这里就是刚才的那个图,咱们看一下,刚才不是有同学提说啊,有的是拼接嘛对吧,其实tention这里罗列的一个一个图,这个图上面其实就是一些比较常见的一个tention机制,你看下第一个这个啊。
active attention,这个tention其实就是把隐藏层隐藏节点给拼接了起来,然后做了一个类似于全连接层的操作,然后外面套了一个tan h的激活函数啊。
还有这个scared dot product attention,其实所以说attention其实很多有很多种思想,很多种变体,咱们这里关键是要掌握它的这个,核心思路它就是一个加权。
ok咱们就继续讲好吧,看一下这个所谓传说中的transformer到底是个什么东西,ok咱们先来简单看一下这个transformer的一个整体的一个结构,是什么样子的啊。
transformer呢它其实也是一个encode decode的阶段,咱们看一下,就左边这一块,左边这一块它是它的encode啊,我圈一下左边这一块是他的encoder。
然后右边这一块的话就是它的一个decoder啊,咱们先看这个encode阶段,encode阶段的话它又分为了两个子层,第一个子层叫做market tension。
然后第二个子层叫做feed forward,其实就是一个全连接层啊,它每一个子层之后呢,又接了一个类似于那个残缺残缺网络,大家清楚吧,也就是一个residual connection,一个残缺模块嗯。
能有效防止梯度消失,经过这个残缺模块之后,他还会经过一个leon normalization层,这个layer normalization呢其实和那个h normalization是很类似的。
咱们再来看一下这个decoder层啊,decoder层其实和那个encoder层很类似啊,只是咱们看一下这一块,这一块其实是完全和那个encoder层是一模一样的对吧,就下面这里又多了一块这一块。
它其实是一个mask marty had tension,那这个东西是什么东西呢,咱们也会在后面给大家详细的讲,然后最后如果我的课程结束了,有transformer的讲完了,大家如果有空的话。
也去读一下这个transformer的这个论文,因为如果自己去真的读过这个论文了,可能会有一些更多一些新的理解好吧,然后这个ppt里面我也要把这个论文的地址地址给给了出来,大家有空的话去看一下。
接下来的话我就会详细给大家讲一下到底什么是mari had a tension,然后什么是这个musket,mari had a attention,好吧嗯,在开始讲那个ml的tension之前。
要给大家说另外一个东西,就是scared dot product tension,这个东西呢我们在之前的ppt有说过,就是这里的这个给他打了,它是attention的一种变体。
咱们来好好看一下这个scared dot product attention到底是什么东西,好吧,其实常用的喷射呢是有两种。
就首先是这个additive tension和这个dot product tension,就是我刚才那张图提到的,咱们再看一下啊,就是第一个active attention,我换一个颜色的笔。
这个dattention还有一个是,然后这个scared dot product tension呢是咱们transformer用到的,咱们就再回顾一下这个editive分成什么呀。
把两个隐藏状态拼在了一起,然后过一个全连接层,外面套一个tan h聚合函数,然后外面再加一个权重,说白了就是两个激活那个两个全连接层啊,这个dotable dog tension呢就很简单了。
它其实就是把两个呃sat进行了一个相乘矩阵乘法,所以说这个additive tension和product tension,dot product tension,其实两者效果是很接近的啊。
但是dot product tension呢它会更简单一些,咱们刚才也看到了嘛,其实就是一个矩阵乘法对吧,所以计算起来会比较简单。
所以呢transformer也采用的是这个dot product tension,也就是说在这个marty hard tention当中,它采用的是这个啊dot product attention。
但是他为什么不用这个dot product tension啊,而是要用了一个cared dot product tension,加了一个啊,咱们看下面这个公式啊,如果我们把这个缩放因子啊。
下面这个缩放因子给去了,它其实就是一个dot product tension,加上这个缩放因子之后,它就叫做scared dot product or tension,接。
下来咱们就看一下为什么要有这个缩放因子啊,首先我们来理解一下这个公式啊,这个q k v到底是什么东西啊,我们还是类比一开始的那个,类比到这个图上面来啊。
咱们看一下这个三个三个h它其实就是可以理解为就是这个k,那这个q呢咱们就可以理解为这个紫色的这个抵扣的这个这个东西,我们用这个抵扣的和这三个进行一个相传就可以计算出嗯。
回顾回顾一下啊,你看我们把这个这个三个和这个紫色进行一个相乘对吧,得到了13 九九这个值,然后做一个soft max的操作,再和原值进行一个相乘,最后拿到最终的结果。
所以说其实我们这里的这个v和就等于这个东西,也就是说我们的这个啊k和v是相等的,在,这个我们刚才讲的这个情况,下面k和v是相等的,这样的话大家这个公式应该就理解了吧。
如果我们先把这个我们先把这个缩放因子给遮住,只看这个q k a soft max乘以这个v,然后结合这个图,这里大家理解吧。
最后这个图。
那为什么要除以这个缩放因子呢,是这样子的,就假如q与k的初始化的值它是满足啊,均值为零,方差为一,那么点乘后的结果呢,它的均值就会为零,然后方差是dk啊,这里的d可以表示的是这个qq的这个维度。
也就是说咱们这里这个h一的这个维度,但是如果这个维度很大的话,就会出现一个问题啊,因为啊咱们也看到了这里点成之后的结果,它反差是dk,也就是说如果我们这个dk特别大,那它这个反差很大。
也就是说不同的数之间它相差的值就会很大对吧,然后我们又只得知道这个soft max它的输出值和是一,如果有一个输出值很大,那么其他单元都会受到一个抑制,咱们再看一下,回顾一下之前的这个图。
你看咱们这里h1 h2 h3 对应的是13 九的九,但是经过这个soft max之后,13对应的变成了0。96,而九对应的是0。02,它其实九和13比起来嗯,没有相差很多对吧,但是它的权重已经很小了。
但是13的权重就很大,那如果我们这里除以了一个啊这个dk的话,就会把这个soft max这个值尽量的放小一些,不会导致所有的值都会变大,就是这个原因,所以要除以这个值啊,我看一下这个同学的问题啊。
k也是矩阵,每个节点都有一个tension,可是这样子的,我这里看我这里可以表示的是h1 h2 h3 合在一起,是这样子的啊,你指的说错了,是你说的是哪一块,说错了呢,啊某某同学针对一个抵扣的节点。
针对一个你的意思是说啊,是是向量对它是一个向量,不是的,如果是针对于当前这个情况的话,它其实就只是一个单纯只是一个向量,它不是一个向量,对它只是一个向量,ok那咱们继续好吧,嗯咱们说到了刚才那里。
为什么要加一个缩放因子啊,为什么要加一个缩放因子,大家这里清楚了吗,啊我再简单的说一下啊,加上缩放因子之后,就会防止soft max 3中某一个值过大,而导致当前这个值一直到了其他的值。
从而防止出现全盘通吃的这个情况,咱们再回忆一下这个图,13对应的是0。9,六九对应的是0。02,13和九相差的不多,但是13站的权重特别大,我们点成的均值为零,方差是dk,如果没有这个缩放因子。
因为反差很大,所以其中的值它相差的会很大,大的值就会全盘通吃,大家这里清楚了吧,如果清楚的话,咱们就继续看后面的内容,嗯清楚了吗,清楚的同学打个一好吧,如果有不明白的地方,大家及时提出来好吧。
那咱们就继续了,明白了这个scared dot product tension之后,咱们就可以来看一下这个smart的tension到底是什么东西了啊。
我们再看一下上面这个对于上面这个tension来说,它其实啊只能输出一种情况的值对吧,但是marin hart tension是什么意思呢,就是说我这个tension如果只有一个腾讯的情况的话。
我可能只会考虑到一个维度对吧,但是对于n m p这样比较复杂的任务来说,它就可能会有很多个维度需要去考虑啊,就好比这个cn大陆的这个卷积层,它的这个卷积核你也不可能就只单单的设置一个对吧。
咱们这里做个类,比就可以把这个类比成这个,咱们再就只单单的设置一个对吧,咱们这里做个类比,就可以把这个类比成这个,咱们再提在采用这个cn卷积层的时候啊,卷积核的个数不够啊。
所以咱们是不是也可以用一种类似的方式把这个藤审就是多加几种,就是考虑到不同的维度,那怎么加呢,这就是咱们之后面要讲的问题,那对于这种如果多个不同维度的attention在一起,这种情况的话。
就被称为马号的tension,好吧,为啥这里的tension有三个参数呢,其实是这样子的啊,attention,我刚才也没有说过,这个这里的这个tension的k和v。
它其实表示的是一个值。
我们再简单看一下啊,q表示的是这个紫色的啊,k和v表示的是这三个黄色的,它其实有些情况下它虽然是三个参数,但是口某些参数它可能是相同的,它三个值都是相同的,好ok那咱们继续讲后面的内容,好吧啊。
玛丽哈的tension就说了,刚才一个tension考虑的维度不高,那咱们就多加几个,那怎么样才能多加几个呢,咱们看再看一下原来的这个这个公式啊,这个公式你看我们q k v都是固定的呀对吧。
嗯都是固定的,那我们怎么才能去考虑多个维度呢,这个时候其实我们就需要引入一个东西叫做,w q w k和w v我每次在计算q kv的时候,我不会再用它的这个直接一开始得到的值。
而是用我们这个初始化的值和这个矩阵进行一个相乘,而得到的值才会作为他的q,同样的这个w k也是用这个输入值和这个矩阵进行相乘,然后得到k这个w v也是一样的,啊我先在这边看一下同学的问题啊。
k和v可以不一样吗,为什么要写两遍,啊q和v可能有的情况下是不一样的,因为我刚才也有也有说过这个attention,它其实情况有很多种,咱们在这在这里也看过attention,其实情况有很多种。
从x怎么得到q,从x怎么得到q是指这个图上的吗,啊接下来我会讲好吧,啊我就是在回答一下这个q k v的问题啊,q k v的问题,他有的时候他可能三个值都会相等,然后也有可能某些部分知识相等的。
具体的话要考虑具体的一个场景,就像我刚才说的一样,attention它不是一个模型,它是一种机制,它是一种加权的方式,不同的场景下,它的这个加群方式可能是不一样的,你可以是直接加啊。
doctor da ten成这样,直接进行一个相乘,你也可以像activattention里面一样,作为一个线性的一个操作,然后加一个激活函数好吧,所以说attention操作其实真的很多。
咱们关键要学的是他的这个思路,这种加权的这种思想啊,接下来咱们就看一下这个同学提到的就是怎么从这个x得到q,咱们这里这个x其实可以类比到上面这个这三个黄色的。
这三个就是它的encode hidden state的,咱们可以做个这样的类比。
只不过这里啊取名的是这个x,首先呢我们会初始化三个这样的一个矩阵,这三个矩阵都是随机初始化的,然后把这个x和这三个矩阵分别做一个乘法,然后得到三个向量,而这三个向量呢就会作为新的这个q k v。
那这三个矩阵也是在这个反向传播的过程中不停地更新,然后才会更新到最适合生成q k v的这个矩阵,得到我们这个q k v之后,就是接下来的操作就和上面的这个这个公式很类似了,先把q和k进行一个相乘。
然后计算它的这个啊缩放因子,而soft max再把soft max的值和这个v相乘,最后再做一个累加,把所有的情况相加,就可以得到我们最终的这个输出的这个值了,q是指decoder中某个t的h是个向量。
kv都是隐code中所有t的h拼起来的矩阵对吗,对q通常是指的是decoder中的某个,你说的这个某个时间点的这个get set没问题,这一点说的是没问题的。
然后kv都是抵扣的中所有时间的h拼起来的一个矩阵,对你说的这个是也是没问题的,完全没问题,咱们再继续看这个马丽哈的tension啊,看到这一步,大家有不明白的地方吗,就是从这个q乘以k最后得到的这个v。
然后v在累加求和得到的这个z就到这一步,大家有不明白的地方吗,咱们再简单回忆一下啊,对于这种情况,我们q k v是固定的,所以我们没法考虑多个维度,那怎么办呢,我们乘以一个随机初始化的一个矩阵。
这样的话就会得到一个新的q,把这个新的q k v进行一个刚才的一个计算,就可以得到我们最终的这个雷一,不是q是decode的,这个同学叫某某同学,q是抵code的,qq它其实的意思是query。
然后k是key,然后wish指的是value啊,market head体现在哪呢,稍等一下我就会给大家解释到底体现在哪里好吧,关键先把这一步给看明白,dk为啥是八啊。
ok我把给大家解释下这个dk dk的话通常是对,就是维度,然后就是取一个开方,因为啊在transformer这个里面,它默认的维度是64 64,所以这里提到的是一个八,对transformer。
他的确是要更新这三个矩阵,咱们先继续好吧,讲一下这个mti的到底体现在什么地方,啊如果我们把我们看一下。
这里其实就是一个x嘛对吧,类比到上面的话。
但是对于一个序列来说,他肯定不止一个x一对吧,它可能会有多个,所以我们就把它这里可能还有多个,它可能是一个矩阵的形式,所以我们这里以一个矩阵的形式来表示它的话,最终其实我们只得到这样的一个式子。
这里大家是能理解的吧,接下来我们来看一下它这个marty到底是体现在了什么地方啊,自己看哦,关键的点来了,这是第一种情况的这个啊权重矩阵,那如果我们引入多个情况的权重矩阵呢,这种这是一套对吧。
左边这里是一套,我们再引入一套唉,这样的时候我们是不是就可以得得到得到多个z,在transformer当中,它默认是引入了八套八套参数,所以对最后他就会得到这样八个这样子的z。
那他最后我们就把这个bug z进行一个拼接,然后再乘以一个wo矩阵,这边这个矩阵主要的目的是用来降维的,然后我们就可以得到最终的这个z,这个z的维度其实和你看和这个z的维度其实是一样的。
所以这边这个w其实就是用来降维的,保证我们输出的这个z和每一个这种小z它的维度是相同的,那marin呢体现在就是我们生成了多套这个权重矩阵,明白了吧,这位啊这位同学,317136。
这就是mhy的体现的地方,啊说到这里大家看有没有什么不明白的地方,transformer的目标其实就是不停地更新所有的钱选装矩阵,如果我们有一共有八套权重矩阵的话,那其实就是24个嘛对吧。
我们要更新24个权重矩阵,当然这个mt号的团审它只是其中一个部分,哦对忘记提一点,我们再看一下这个transer的一个整体的一个图啊,我们我们只看这个encode的阶段啊。
encode阶段它其实输入的只是一个句子对吧,那这个attention他只有一个序列,这个tension是和谁啊,tention呀,是不是只能和自己的tension,所以,马号的这个encode阶段。
这个madht tension,它其实这里写的不是特别准确,它应该叫做micht sattention,也就是说他这个tension是自己和自己做一个tention,大家注意啊。
这里应该叫马里哈的cf tention,自己和自己做的tension,啊这个c就是体现在我输入的,我在include阶段,我输入的是句子本身,它没有其他序列,我抵扣的阶段才会涉及到另外一个序列。
我引扣的阶段只是自己和自己做一个弹射,也就是说我这里的这个在seer tension当中,这个q k v3 个值是相等的,你看我这个q和k是相等的,那是不是就是自己和自己做这个和腾蛇了,这就体现在了。
这就是cf体现的一个位置,明白了吧,然后这边的话就是它的一个整体的一个图,咱们再简单回顾一下啊,这是输入的x这里是一个矩阵,因为它是它不止一个序列,多个矩多个序列,然后和多个啊矩阵进行一个相乘。
得到dotq k v,然后q k v经过啊,咱们刚才的这个tension的这个公式,最终得到多个z,多个z组合在一起,和一个w的矩阵进行相乘,进行一个缩放,就能得到我们最终的这个z。
ok咱们看到这里有没有什么不明白的地方,然后大家也来看一下这个图啊,心里面回忆一下它整个流程是什么样子的,咱们给大家两分钟的时间好吧。
因为mha tension其实也是transformer的一个最重要的一个结构点,只要这一个地方啊了解清楚了,那其他都是小k4 了,为什么人口的阶段也要做tention呢。
其实啊你从句子当中去提取特征的时候,你肯定我举我举个最简单的例子,为什么词向量的效果不如b加t直接生成趋向量的效果好呢,因为就单纯的一个word actor,它没法考虑一个上下文的语境。
他就直接把这个单词给的这个词向量生成了对吧,那对于或者说我不不去比较这个比亚t,我去比较a某就用word和omo做一个比较,恶魔所考虑的就是一个上下文的语境。
它会根据上下文的语境去动态生成这个词的词向量,那为什么欧盟的效果比我connect的效果好呢,就是因为它考虑了上下文呀对吧,那我们赢,那对比到这个创新当中一样的,我只要提取特征的时候。
那我就自己和自己做一个弹声啊,这样我才能把就是上下文语境这样的一些特征给提取出来,明白了吧,那这个cf腾讯就讲到这里,咱们就继续下面内容了好吧。
接下来咱们再看一下这个encode整个的in code include,就是刚才说的一个money has cattention,然后加了一个residual connection。
再评上一个layer normalization,neo normalization和那个beach normalization是很类似的,它主要是用来繁殖过拟合的啊,接下来的话就过了一个全连接层。
然后又是一个research connection和那个layer normalization,这就是一个include整个的这个结构了,然后在transformer当中呢,它in扣的其实是这样。
六个这样的一个子模块堆叠在了一起,整个这个六个字模块成为它的一个进口的,知道了吧,接下来咱们就来看这个抵扣的阶段了啊,抵扣的阶段刚才也说了这两啊,前面这两块是一样的,主要看一下它的它的最下面这一块。
这个musket到底是什么东西啊,咱们来看一下,我们现在有一个这样子的一个序列啊,叫做i have a dream,我们把这个序列就做一个cf attention的时候。
就是说白了就是q和k进行一个点传,你看假如把这个当做当做q,然后把这边这个啊当做可以,我做一个矩阵的乘法,它其实它是不是会生成一个tension的矩阵,就这样的一个矩阵对吧,就一个4x4的一个矩阵。
那对于这个对于这个抵扣的阶段来说,咱们来看一下啊,如果要生成一个新的序列,你如果都让我啊已经看到当前我要生成的这个序列后面的内容,然后再来生成序列,那其实是不是就没意义了,能理解我这句话吗。
就是说假如我现在要把这个i have a dream,把它翻译成中文,或者说啊我把中文翻译成英文,就是我有一个梦想,我要翻译成i have a dream。
我引扣的阶段输入的是i have a dream,这个时候啊我在训练阶段,我的那个decoder,首先我要输入的是i对吧,我要输入的是i,但是如果我在输入i的时候。
把这个have a dream后面的内容也输也输入了,那我输出的输出的时候,我都看到后面的内容了对吧,那就没意义了,我们要做的就是说我看不到后面的内容,那怎么办呢,就是建立一个三角上三角矩阵。
然后把也就是右边这个图灰色的这一块,就灰色的这一块,把这一块全部置为负无穷,这样我在做soft max的时候,就会把这一块的值指得特别小,你看我这个i对应的它只会检测到和i相关的值。
i和have a dream这三个词的时候,因为它是负无穷嘛,所以so soft max之后它的值会很小,也就是说i就拿不到have a dream这三个词的一些相关的一些特征。
而对于have这个词的话,它是可以拿到i和have两个词的对吧,同理到最后这个dream他就可以看到所有序列的一些相关的特征了,这就是这个灰色的部分指的就是这边说的这个musket。
说白了就是把嗯后面的内容给遮住了,不让他提前看到,那其他部分的话就和前面的是一样的了,所以说在这个抵扣的阶段,只有这一块不一样,咱们做了一个遮挡的操作啊。
这个musket money hattention,大家明白了吗,有没有什么不清楚的地方,嗯有不明白的地方吗,这个mosket,如果明白了我就继续讲下面的内容了啊。
最后咱们再看一下这个mari had tension在transformer当中的三个地方,我们看继续看这个图啊,这个market的tension在这个transformer当中有三个地方应用的。
这三个地方到底有哪些地方是不一样的啊,首先呢这个encoder阶段就是一个标准的马里海的特射,对于啊抵扣的阶段,这个musket咱们也讲了,那最后我们来说一下这个啊中间的这个马里海的tension。
这个时候它其实就不是一个cf的tension了,大家知道为什么吗,为什么这里的不是一个cf产生,为什么这个蓝色的这个mod had tension不是一个sample tention。
其实这里你看他的收入是不是啊,transformer encode的一个输入,它不单单是那个它自身的一个输入,所以它的k这里对应的k,是encode输出的一个值,知道了吧,这就是一个。
所以说这里它这里不是excel的同时,一个是一个encode阶段的一个值,还有一个是它自身的一个值,你看这个箭头其实就可以类比到咱们这个呃,sequence to sequence这样的一个机制。
你看我这个抵扣的阶段,首先输入的是一个include阶段的一个值,其次是抵扣的阶段的一个输入值,这就是这个马里海的tension和这个include阶段,这个cf tension的一个区别。
这里有明白吗,大家,最后咱们再来看一个动图啊,这个动图就是嗯也是一个法语的翻译,他会把这个我是一个学生的法语,把它翻译成一个英文,我们看一下这个流程到底是什么样子的呀,啊首先把这三个向量进行一个输入。
这个是encode的hien se,把这个kds和这个矩阵相乘,要深入到抵扣的阶段,然后抵扣的把这个值输出,哎这个同学这个时候可能会有同学有疑问啊,哎为什么嗯,我第一个词好像没有。
其实没有那个用不到那个masket腾讯的那一部分的输入,而是只有这个encode阶段的一个输入,是的,因为我们第一个阶段,我们啊抵扣的阶段,它的输入其实是要等到第一轮的这个输出值,这里有输入。
输出了一个i,我要把这个i作为一个输入输入到这个位置,这个时候,我们这个musket attention才开始真正的运作,咱们看一下第二部分,第二部分把这个i也输入进来,咱们看一下这个动图。
然后把这个m拿到之后,再输入这个这个这个阶段其实就已经和那个sequence to sequence,那个阶段很接近了对吧,这大概就是一个transform的一个整的一个流程。
嗯看到流程这里大家有没有什么不明白的地方,咱们再简单看一下啊,首先我们需要的是这个encode阶段的一个输入,得到第一个输出值,再把第一个输出值输入到带有musket的马里奥的tension。
然后再不停的输出解析的一个结果,直到遇到一个结束图,咱们整个生成的这个流程就结束了,有没有什么不明白的地方,没有的话,咱们就继续下面的内容了。
啊到下面的话就要开始讲这个bt了。
这样吧,咱们稍微休息一会好吧啊,休息十分钟,然后大家也把这个整个流程再回忆一下,然后看有哪里不明白的地方,我给大家再解答一下,啊趁这个时间点,大家也把自己想问的问题,还有不清楚的地方可以啊,打在公屏上。
然后待会我给大家统一解答好吧,咱们稍微学计划,嗯大家看得怎么样,整个流程有没有熟悉一些了,那大家有什么问题尽管提出来好吧,咱们再过一分钟吧,再过一分钟,我们把这几个问题回答一下。
咱们就正式开始下面的内容,ok ok大家都回来了吗,都在吧都在吧,我准备就给这几个问题进行一个答疑,嗯蓝色框起来。
那个阶段的输入是将encoder和decoder的self attention的输出值加一起,输入到最后的什么,是这样子啊,咱们看第二个动图啊。
首先我们会把include阶段的这个输出值和这个矩阵进行一个相乘,拿到q和v对吧,然后我们会和这边的这个输入值,这边的输入值其实就是一个q,明白了是这边这个值又做了一个cf那个腾讯的操作。
然后输出了最后的结果,并不是说就把它相加,明白了吧,这个317136,这位同学,啊为什么说它不是sattention呢,因为它这里输入他这个q k v并不是相同的呀,在encode的阶段。
它只有一个序列,它是自身和自身做了一个c和腾升,但是在那个抵扣的阶段,它有新的输入,还有新的一个输入,它有另外一个序列,所以严格意义上来说,它并不能很严谨的叫做cf腾讯。
啊然后看一下a2 l这位同学的问题,k和v都是encode阶段的pen sk吗,啊不能叫这里其实已经不能叫黑等词的,他应该说叫他的encode阶段的一个输出值,如果准确的时候应该是叫它的输出值。
咱们再看一下一开始那个图,你看这里它其实输出的是一个首先一个全连接层嘛,一个residuous action要经过ler ler ler malization之后的一个输出值,ok明白了吧。
还有什么问题吗,这里啊如果没有什么问题,在公屏上打个一好吧,ok ok ok,那就继续下面的内容了,反正大家都不是特别积极呀,希望大家都能一起互动进来好吧,让我知道各位的这个掌握的一个情况。
因为如果你穿不了解的话,b r t嗯就等于是学不好了。
ok咱们接下来就来看这个transformer,哦,是那个b r t,这个b e r t到底是个什么东西,对对对,transformer,transformer这个模型也是我自己觉得最复杂的一个模型了。
就相比于c c n和rn来说真的很复杂,所以大家听完这次的课下来之后,还要自己再消化一下,多看一下这个ppt好吧,或者说把我这个课的录播再拿过来看一下,这个模型真的很复杂。
但是没关系啊,大家不用特别担心,因为我们这个b e r t啊。
它其实并不是transformer的全部的一个阶段,我们看一下b加c的全名是什么,be directional encoder,representation from transformer。
什么意思呢,他用的只是transformer的encoder,大家是不是觉得transformer是这个抵扣的阶段更复杂一些,抵扣的阶段其实就是一个cf tension对吧。
复杂的点就在这个cf层上对吧,那这个b i t是什么东西呢,它其实就是这个transformer encoder层,然后把这个encoder层进行了一个堆叠在一起,对于b i t有两种啊。
一个是base,还有一个是挖掘啊,贝斯的话它其实是12层的这个encode叠在了一起,然后它的维度是输出值的维度是768。
然后这个a表示的就是marty had tension的这个head的个数它是12,然后这个very large的话是24层include叠在一起。
然后输出值的维度是以1024money had attention的个数是这个16,然后这边的话我也给了大家这个啊b r t这个模型的一个论文,大家如果transformer理解的差不多了。
然后transformer的这篇论文也看过了,那就可以来看这个b r g的这篇论文,当然也可以先看我的视频,或者看我的这个p p t,如果都看明白了,再去看论文好吧,那bt呢它其实是一个预训练的模型。
接下来我给大家简单介绍一下到底什么是语训练,到底是什么是微调啊,就是所谓的反to啊,异性恋呢其实就是我事先使用了一批数据,然后训练了一个模型,最终这个模型的这个漫画能力会很强。
然后呢我再用这个事先训练好的这个模型,在一个自己特定的任务上,并且使用自己特定的一个数据集,这个是这个阶段呢就要做一个微调,也就是fq,那前一个阶段就是一个预训练,那对于bt来说呢。
它的这个预训练阶段到底是什么样子的啊,接下来就为大家详细介绍一下它的这个运镜链的阶段到底是怎么做的,嗯在开始之前咱们再确认一下啊,这个bt的结构大家都明白了吧。
就是transformer的encoder堆叠在了一起,这里大家清楚了吧啊它没有抵扣的,只有营口的好吧,大家不要觉得这个b e r t有多复杂,其实也没有大家想象那么复杂。
就是transformer的encode堆叠在了一起,ok咱们接下来就看这个bt的运行列阶段到底是什么样子的,这个bt的预训练阶段呢做了两个任务,一个是musket language model。
还有一个是next centers prediction,第一啊,这两个任务我在下面给大家介绍,咱们先看一下这个bt和其他的一些预训练的模型有什么区别,首先是这个g b t,还有一个是某。
咱们先看这个a某某,它其实采用的是内部采用的是一个l s t m,我们看一下啊,采用的是一个lsd m,然后b r t呢采用的是这个transformer的encode。
然后g b t呢和这个比亚t其实是很类似的,都是采用的这个transformer encode,但是唯一的不同点在于b r t采用的是一个双向的啊,这个gp呢采用的是一个单向的,为什么说它这里是双向啊。
g b t是单向的,咱们看下面的这个musket language mode,看完这个鱼情节的任务,大家就明白了,好吧,我们先看一下这个musket language model到底是什么东西。
其实呢这个任务就可以理解为一个完形填空啊,首先呢就是把所有的文本当中15%的词,用这个mask mask这个遮罩把它给遮住了,就这个东西被遮住了,啊就是这里有一个例子啊,my dog is harry。
他把这个这个这个单词用这个mask给遮住了,然后呢,我把my dog is这三个字作为输入,然后来预测这个词,但是这样的话其实是有一个很大的问题的,什么问题呢,就是说如果我把所有的这个训练语言当中。
我们15%的词都给遮住,那是不是很多次他就可能从来没有见过呢,就在白白天阶段和那个预期练阶段,他可能有些词性从来没见过嘛对吧,有可能有些词数量它本来就不是特别多,结果你还把它给遮住了。
那我完全就不知道那个词是什么东西了对吧,啊所以呢在b i t c当中呢,他就采用了三个方法,就首先呢他把这15%的词,有80%的概率是用这个mask给它遮住。
然后10%的是用一个随机的词来代替这个mask的词,例如这里meal with harry,他换成了一个apple,还有的10%的情况呢,它是保持不变的啊,那问你为什么这里这个随机输入一个词。
为什么不会对,这个就是说为什么会不会对整体的这个结果造成一定的影响呢,毕竟其实这个apple说白了他其实的势力已经引入了一些人为误差了对吧,所以说啊到底这个引入了这样子的一个随机死了,有没有影响的啊。
在b r t里面,它其实论文当中有一有解释,就是说随机引入的这些词带来的负面影响,他认为啊因为只有15%的词啊,并且是10%的概率,所以最后他其实只有1。5%的概率,他觉得这个影响特别小。
所以他认为这样的带来的一些负面影响是可以忽略不计的,所以它的这个训练就采用了这三个点来对这个单词进行一个预测,说白了就是完形填空嘛,对吧啊,他为什么要做成这样的一个完形填空的一个任务呢。
还有一个原因就是因为因为雨量特别大,如果我采用其他一些任务的话,可能需要做一些数据标注,但是这样子的一个任务,我们不需要打标签,我们语料越多,我们可以训练的这个啊这个语料就是数据集,就是对应的训练集。
还有这个目标,就是label,就是x和y就特别多嘛对吧,我们就可以不停的训练,不停的更新我们刚才所谓的那个穿梭er当中的那个矩阵对吧,除了这个啊完形填空这个任务啊。
bt b r t它还有一个任务叫做next and prediction,什么意思呢,就是说选择一些句子对a和b其中有50%的数据,b是a的下一条句子,然后剩余50%的数据b是语料库中随机选取的。
就是说我有一个段落啊,我一个啊两个句子如果是连在一起的,那我就把这个标签标为一,如果两个句子啊,然后我再选一个句子,后面这个句子呢随意机选一个句子,这样的句子它肯定是没有上下文的一个相关性的对吧。
这个时候呢我就把这个句子给表为复原吧,例如下面这里下面这里有是有一,个例子,说白了他其实就是一个啊自然语言推理的一个人物,就判断下文是不是上文的一个下文,就是说能不能从上文推理出一个下文。
为什么要加入这样的一个任务呢,官方给出的解释就是说,他是他认为其实我们会有很多类似这样子的一个,就是在微调阶段,可能会有很多类似这样的一个任务,就可能会有类似这样子的一个任务。
所以它引入这样的一个任务来提升一个总体的一个效果,其次呢他这个输入数据当中,他会,把那个序列的头部加一个cl这个c a r s啊,一方面是表示它的一个起始符,其次在我们嗯通常也会用这个c l s来表示。
当前这个句子的一个局限量,然后中间的这个s e p是一个分隔符,用来分割它的第一个序列的,这是第一个序列,这个是第二个训练,s e p用来做分割的,然后末尾它也会加一个s e p的这样的一个符号。
那这里关键就是记住一下他三这两个符号有什么用,有什么区别,好吧,这就是他的一个第二个任务,但是这个第二个任务在cha cha net当中并没有采用这个任务啊。
当时给出的原因就是说这个任务对总体的一个提升效果并不理想,就是说有没有基本上啊影响不大,所以在xi net当中并没有采用这个任务,而是只保留了这个musket language model。
这个异性恋的人物,然后大家看这两个预训练的时候的这两个任务有什么问题吗,有什么问题就提出来好吧,嗯保持10%保持不变,是不是很大概率这个词永远训练不到呢,嗯其实这个10%和这个随机取一个词来代替的话。
其实情况是一样的嘛,它只有1。5%的概率,所以说它的概率是真的很小,而且b r t它的训练语料很大,我还有点要跟大家解释,为什么b r t的训练语言大,其中一个原因就是因为它模型足够复杂。
只有模型足够复复杂,他就要足够的语料,就好比我们从小到大啊,从一个小孩长到一个成年人,我们其实智商是慢慢的越来越高的对吧,这就类比到模型当中,诶,模型从一个比较简单的结构变成了一个比较复杂的结构啊。
当我们啊从小孩子长成一个成年人的时候,那类比到b r t当中也是一样的,模型更复杂,就是这个道理,ok这就是b r t的两个预训练的一个任务,这两个任务大家有没有什么不明白的地方嗯。
同样啊这个b e r t这个nsence prediction,他也不需要打标签对吧,然后咱们就继续后面的内容啊,我这边再再重复提一下这个cs,这个cl它其实也一方面是用来表示它这个开始符。
其次这个是也可以用来表示它整个个的一个局限量,而两个任务是合在一起训练的,bt属于一个多任务模型,就是我会把这两个函数的这个loss,就是这个损失值,就可能上面这个是有一个loss一嘛对吧。
然后下面这个loss啊,我把这两个loss相加,然后同时对它进行一个反向求导,啊官方是这么解释的,就是说我们我们我们这个只是指的是在那个微调阶段啊,微调阶段它其实我们输入的时候是一个假如。
假如我们输入的只有一个句子,我们输入是一个c l s,然后是一个序列对吧,然后结尾是一个s e p,那官方的解释就是说你可以用这个cl s来表示这整个极限量,当然目前也有一些啊。
就不采用这个c r s的,然后就是把后面这一块做一个类似磁化的一个操作,然后再生成一个局限量,那通常如果你要采用的是cl s来作为一个局限量的话,其实说白了就是一个在微调的时候把生成计算量之后。
我后面再加一个全连接层啊,或者jack soft max做一个分类和反向传播的时候,它其实也是在不停的更新嘛,对吧,ok这就是他的一个预训练阶段的两个任务,所以模型预训练的任务是预测出马死单词。
并且判断下对对对对,是的,这两个任务就是两个任务嗯,嗯然后接下来要说一下这个b r t的这个输入值的部分啊,在之前讲那个transform transformer的时候,并没有去讲这个输入值的内容啊。
第一个部分是它的这个token be啊,第二个部分就是那个position in biding,这个token ben,说白了它嗯大家就可以理解为一个词向量。
然后这个position in bedding表示的是因为transformer它没有位置信息,它不像i n或者r n的变体,它有一个位置信息对吧,它是按序列输入的,但是transformer没有。
它是一次性,就像cn一样,它是一次性就输入进去了,所以它引入了一个position be,这个position embedding的这个计算公式,我罗列在了前面,大家简单看一下就ok了啊。
这个token bedding和这个pos是eb都是transformer的一个输入的输入值,但是在b r t当中,它还多了一个segment embedding,就是表示是个段落向量,什么意思呢。
因为我们有一个next sentence prediction这样的一个任务对吧,我要把这两个这个这两个序列给区分开,所以呢这里就引入了一个segment dei,你看我们从这里分隔开。
左边这个是一个segment embedding,然后右边这个是一个segment be,然后这个具体的值呢通常就是啊如果是第一个的话,就全是一,然后这边全是零就ok了,主要是要把这两个序列给区分开。
他会在微调的时候,我也会有日志,大家也可以看到这个sigming betty,在日志当中可以看到这个就是它的一个输入过程,咱们再看一下token bdsignment ebd。
还有个position ebd啊,最终是把这三个值进行一个相加累加,然后把它进行作为输入输入到transformer当中,明白了吧,这就是bt的一个全部的一个内容了,还有它的一个运行连接段。
大家有什么问题啊,这里讲完之后,后面的内容的话就是带着大家再讲一下啊。
目前b r t的一些改进的一些方面的知识。
最后的话就是咱们的实战一个微调,所以说啊看到这里大家有什么问题,有问题的话提出来好吧,对对对,是固定的,就是对于当前这个序列来说是固定的,啊b r t当中是采用的分子,没有用词。
对对只有只有这个头盔应该是变换的,在那个bt当中,它其实输入就是一个index,就是肉,而不是有一个词典啊,有词典我里面有一个100个10万吧,我有10万个词,那如果第一个词在第一个位置。
那我就是以我第二个词在第十个位置,那就是十,然后这样的一个数值,这就是一个token be,啊对我现在基本上用不到work sector,我现在基本上能上能上b r t的,都会上b r t。
只是因为效果好,其次就是说啊大家都用这个东西嘛对吧,你说你又去写一些其他比较复杂的模型,然后又用这个word vector,反而效果不如b r t,那还不如直接上b b r t,而且它有个最大的问题。
它容易出现ov的问题,就是毕竟中文词特别多嘛对吧,你有些词难免收入不进去,那对于那种情况你就没法解决了,所以我现在很少用这个word that,ok咱们就继续下面内容好吧。
呃接下来的话就简单为大家介绍一下,就从去年年底,然后到现在又有哪些新的模型。
但是呢就虽然从去年年啊那个年底到现在又推出了很多新的模型,但是目前大家用的多的还是这个啊b i t为什么呢,可能最大的一个原因啊,这些预训练的模型很多都是只有提供了英文版的,它没有提供中文版的。
虽然这个百度它自家的那个,而你他提供中文版了,但是呃据我了解,如果你要用early的话,好像是要用他自己的那个pd拍的那个框架,所以说啊很多情况你说你自己的一个工程项目对吧,一个线上的项目。
如果你要把这个novlog框架也换成了这个片头开头,它其实难免可能会引起一些很大的问题对吧,所以说风险点也很高,并且这个而你相对于bt提升也并没有说达到了特别夸张的程度。
所以我们目前用的多的还是这个抵押题嗯,接下来的话就简单为大家介绍一下,就目前几个啊,新的就是效果比bt好了一些那个预训练的模型哦,对我还忘记解释一个东西啊,刚才看到这个图的时候不是说了吗。
b r t它是一个双向的啊,啊这个g b t它是一个单向的,为什么这个b r t是双向,g b t是单线的啊,咱们还是看这个任务啊,啊假如对于这个这个序列,我要预测的是dog。
那我会把my is harry,第三个单词输入进去,来预测这个dog,那如果是g p t这个模型呢,它要预测dog这个词的时候,他只会输入my,它不会输入后面的内容,明白了吧。
这就是为什么bgr t是双向,gbt是单向的原因,啊g b t其实但是效果也特别好,但是g p t那个团队就是也不会搞什么大新闻,所以出来好像关注的人也不是特别多,直到谷歌那个比亚迪出来之后。
这种真正的预训练模型才彻底火了起来,然后到了g p t二点啊,g p二点就特别有意思啊,但是因为gb t提出的这个组织叫open a open ai嘛,他们其实并不是一个以盈利为目的一个组织啊。
他们关键是open,他们要开语言,但是到了g p二点的时候,他们并没有把这个模型开源,他们是这么解释,他说我们这个gp二点太了,如果开源的话,可以有一些啊非法分子拿去生成什么假新闻的话。
对社会会造成很多不良影响,最后网上就很多人喷嘛,说这个gp二点其实根本没有他们说的这么夸张,怎么怎么的这个啊这个题外话,那么咱们就回到正题上面来,这个gp二点相比于这个b r t到底有什么区别呢。
其实gb t二点,和g p d一点零差不多,只是g p t二点。
它的这个transformer的这个层数更多了啊。
g b t一点零,它的base版本和b t一样,也是12层,但是二点好像是翻到了48层还是多少,其实不是特别清楚了,说白了就是网络结构更复杂了,然后we的数据也更多了啊。
后面的话就是百度提出了这个early,就是百度这个early它是什么情况呢,我举个简单的例子啊,啊那个深圳是广东的一个城市,那如果我们采用的是这个啊b r t来训练它。
可能嗯mask的时候他并不会把深圳这个词同时mask mask,它只会可能只会把深圳的正这个字给mask了,然后用其他字来预测这个字,那百度这个而你是干什么呢,他mask的时候。
它会把深圳这个词同时给mask了,就是说它能一定程度学到这个深圳和广东这个词的一个,相关的一个关系,而不是学这个字和这个其他字的一个关系,就是这样子的说法,就是增强模型的一个语义表示能力。
后面呢清华他也提出了一个叫earning的模型,这个是融入的一些知识图谱的内容,到后面啊,微软又提出了这个mt d n,他这个其实没有什么新的东西,就是加了更多的这,个预训练的任务我们知道吗。
一个musket language model,还有一个就说起来就是一个完形填空,然后一个自然语言推理对吧,但是微软这个mt d n n那又加了很多就乱七八糟的,各种英雄性的任务都加进去了。
然后最终效果有一定的提升,后面的话也是最近又提了一个叉叉a net这个模型的话,它一方面是解决了这个mask带来的一些误差嘛,咱们刚才也说了,在做完形填空的时候,这个mask可能会引起一些问题对吧。
它都会引起一些,虽然这个,可以忽略不计,但毕竟他也是一些人为误差,那这个超i net呢,它一方面就是用了一些方法来解决这个mask的问题,其次呢把它引入了这个双流注意力机制。
也就是说他把这个attention那个机制稍微改了一下,做了一个优化,最后一点的话,他是把那个transformer换成了这个传送差,这穿former x一样的,其实训练速度比这个传送门快很多。
我记得船是快了1800倍,而且transformer x是用来处理这个长序列的啊,b r t这个模型它只能支持最长512的一个序列,但是这个穿插a就毕业了,它支持的序列长,度是b r t的就很多倍。
具体几倍我也不记得,不是特别清楚的啊,说白了它是一个对于一个长序列问题来说提出的一种解决方案,我可能是输入的是一篇文章对吧,一篇文章可能一两千个字,那b押题完全不能满足这个情况。
那对于如果我想用bt来做这种长文本的处理啊,有一种方案就是我把这个一两千个字的序列分成一段一段的,然后分别来生成它的这个向量,但是这种很明显嘛,你不同的一个段落之间,完全就把那个上下文的一些信息。
给丢失了对吧,所以也就是说它这个x样net引入transformer char来之后,也是进一步把这个性能给提升了,好像也是在类似微软那个又加了特别多的一个任务,好像是啊robert还是叫什么东西。
我记得不是特别清楚了,那个是呃好像也是采用了更多的一个语料还是什么,总之都是一些对bt的一些小的改进,关键感性多的点,我觉得还是这个x2 net,大家如果,bt之后熟悉的差不多了。
就可以来熟悉一下这个tri ne,我看他官方的那个it tab也说,之后可能这个child net会也会推出这个中文的这个预训练的模型,ok这就是大概是一个啊。
bt从去年年底到现在要推出的一些新的一些模型,其实目前的这个是啊b r t的时代,我们完全是没有办法去训练这些模型的,例如像那个xi net它的一个训练成本好像是6万还是8万美元啊,它训练成本真的很高。
就对于我们来说是完全没办法的,只能说把这个模型拿过来,然后做一个白t做一个微调,当然最重,要的还是要理解它的原理嘛对吧,那接下来咱们就看一下,既然我们不能去运训练哎。
那微调我们总得会吧对吧,接下来就进入今晚的实战。
咱们来看一下这个bt到底怎么用好吧,啊这边有几个地址啊。
稍等一下。
给大家看一下这几个是干嘛的,首先是第一个地址啊,对然后这边的话是我自己的一个博客啊,发给大家,这些都有讲,这里还有一个transformer的一个源码的分析。
然后后面也是我待会会给他讲的一个白兔子怎么做,然后我把这篇博客发给大家了,如果大家看ppt有些地方写的不够全的话,大家也可以直接来看我这篇博客就ok了,嗯然后这个是bt的一个源码,我们看一下。
我这边也发出来吧,这个是它的一个源码啊,get大家都会用吧,就把这个复制过来,然后克隆一下就能到本地了,这个是它的一个源码,然后待会我会带着大家看一个比较关键的这个run class file。
这个这个python文件就行了,其他大家不用看,只用看这一个就ok了,然后它的预训练的模型啊,大家待会直接点这个就好了,从这里就可以下载到,待会我们都会放到那个我发给发给这边老师的。
然后老师应该会上传到那个服务器,待会我都会统一发的,然后这个预训练模型的地址在这边也有,是在哪里来着看一下啊,就这因为大家找不到地址的话,也可以直接点这里,啊我这边已经提前下载好了。
给大家看一下,啊ok ok ok,我把这个啊,我待会发群里好吧,我这边没有上qq,我待会直播结束了吧,统一发群里,啊这边的话就是下下来的一个bt的一个代码。
就是这边说的。
然后这个是一个预训练的模型,把它解压一下,然后放到这个同级目录就ok了,放到同级目录啊,不放到同级录目录也ok,待会我会我会解释的,然后咱们主要微调的话,就看这一个这个py文件就行了。
run class file到py啊,咱们先从梦函数收起好吧,它其实这个的话它咱们主要是要采用那个命令行的形式来执行,这个的py文件,就是在大概看一下,就是要python。
然后one class file这样的执行,但是他这里其实有几个参数啊,tname这些,这里这个a mark black as required,意思就是说我在执行这个到py文件的时候。
我必须要体验这五个参数,也就是说我后面要怎么分一下,所以是d i l这等于什么,我这里要要把它写进去,同样的一个task name也是等于什么,要把它写进去,这个是执行这个文件必填的五个参数。
但是它有一些选填的参数,这个参数的定义,这个参数的定义在这个在前面,待会带着大家再看一下,然后是这个啊,tf的这个如果它调用这个方法的话,它其实会执行音这里的这个默认函数,这个梦函数看着很长。
但其实啊很简单,然后我们关注的地方也不会特别特别多,这个地方需要关注,然后咱们先看一下刚才说的,除了这几个b填的参数外,还有哪些参数可以填,其他那些参数都是选填啊,你看它其实一开始是定义在这里的。
就是dh d r r这,里也有说明这个dt di r到底是干嘛的,啊这是几个必填的参数,这里有写必填参数,嗯解释一下参数,好的,我先给大家解释一下参数。
原来是这个dd i d i r date dr r,就是咱们的那个输入数据的这个地址啊。
我这边ppt上面也有,你看现在dr我采用的是这个啊,这个是我在上面定义好的,就是这个地址,这个是我自己服务器的地址啊,我是把我是把这个数据放在了我自己的这个服务器上面的这个地址。
啊然后这个是不是config file的地址,也就是说这个我们在这个预训练的这个模型当中有这几个文件,我们先看看这几个文件啊,首先这三个文件是那个tensorfloor生成的一个模型文件啊。
然后这个的话就是刚才说的一个词典,你看这是我们的c l s,这是我们的s e p,这是mask,这些都解释过了对吧,这个表示的是unknown,就是说如果那个字没有。
或者说那个符号那个字符没有出现在这个字典里面的话,它就会替换成这个,然后再解释给大家解释一下,这个按use是干嘛的,这里一共有看一下,一共有100个吧,哎还是九个啊,99个,哎,不对不对不对,99啊。
99个对的,一共有99个,这个on use什么意思呢,就是如果你要在自己的这个模型当中添加一些额外的符号,例如我要加一个q也不好,那你就把这个按use换成这个东西就ok了,明白了吧,那。
或者说你遇到了一些比较陌生的字,在这个里面没有,哎,那你把这个比如比较复杂的什么招财进宝是吧,乱七八糟替换成这个就ok了,就这个意思啊,ok咱们继续解释这边的词,还有这个这个配置文件还没说。
然后这个是那个bt的一个配置文件,也就是说他在预青年阶段,他模型的一些超参数,headset size啊,这是768,这也是为什么模型它输出的一个维度是768,还有你看什么词典的大小啊。
说白了就是一些超参数,它定义的在这个位置啊,那咱们继续回到这五个b减参数上面啊,这是bt config file。
就是说这个文件的一个路径,咱们看一下这边啊,一个confile pay也是你。
就是这个config文件的一个路径,然后是这个task name,task name的话,待会给大家解释好吧,然后是这个啊字典就这个东西这个东西的路径,而最后是一个output。
就说你训练好了这个模型之后,你要把这个啊运行那个微调之后的这个模型存在什么地方,那你就要填这个参数,这就是五个必填参数了啊,其他选填的参数就大家自己来看一下,这里就啊不说了。
啊简单说一说几个吧,像you try,就是说我要不要训练,要不要预测。
还有一个啊,do predict吧,我记得对那个do predict,就说你要不要做预测,然后还有什么咱们看一下啊。
啊这个是一个模型的地址,然后是这个是指的就是说你输入的最大序列的一个长度,如果你这个大家应该都清楚吧,如果你的序列最大长度说的是32,然后我们输入的序列只有20个20的长度。
那他就会把后面12个位置不零啊,然后这个是which size。
这是训练造轮,这大概就是一些参数,接下来的话就给大家看一下啊,怎么微调,我们先看一下这个整体的一个结构啊,它这里面其实有好几个这个波带走,那我就把这个给删了吧,他这里有好几个processor。
这个processor就是它给的一个例子,然后它每一个procel它其实都是继承于这个啊,stprocessor训练语料是要准备成什么哦,对我还没有说这个任务是什么,我们今天的这个任务。
就是说我要对这个数据判断它的一个是否是相似句,我举个例子啊,啊这是我们这里有给大家准备的数据啊,这是训练的数据,这是验证集,这是测试节,咱们看一下,如果这两个序列是同一局,那这个标签就是一。
如果这两个句子不是同一句,这个标签就是雷,就这就是判断啊语义相似度的一个训练的一个数据集,知道了吧,咱们继续回到这边啊,这边的这些processor是那个比亚迪自己提供的个例子啊。
我们的任务呢其实也是写一个这个自己的这个我再走,然后继承于这个啊dcel,咱们看一下这个data processor,这个data processor它其实有四个方法需要我们实现,一个是获取的训练数据。
然后获取验证集,获取测试集,然后还有一个是获取它的标签,那这里的话是一个读取一个tsv文件里面的数据,这个方法你可以自己写,也可以直接调用它的,就关键还是要看你具体的这个数据格式是什么样子的。
那对于我们来说的话,我们其实关键就是只需要把这四个方法再实现一下就ok了,那对于这四个方法到底该怎么实现呢,我们可以参考一下下面的它提供的这个小例子啊,首先从这个tsv文件里面读取一个数据。
然后这边定义了一个列表,调用它这个分字的方法啊,然后把对应的每一行的数据,或者说每一个字段的数据进行一个分子,然后把这个标签也取出来,最后,定义啊。
他这里初始化了一个input的一个example这个对象,然后把这四个参数给传了进去,这是第一个,这是text a,这个是text b,那这是一个label,这个g u i d是以这种形式生成就ok了。
按照他这个格式来,不需要咱们去啊,用其他方式,但你也可以用其他方式,但是你要保证这个gu id是唯一的,咱们就可以完全按照他这个方式来就ok了,不过因为我们这里可能数据格式和他不太一样吧。
我们这里就用我们用tas来读一下,好吧,嗯简单写一下,rc f v,input,这里是tree,那我们就写tr,这就是我们读出来的这个it f,然后呢,这里输出画了一个列表。
咱们可以直接把这个数给copy过来,这里的话就是循环的是df的values,012没什么问题,然后咱们要把这两行代码不需要其他代码都是一样的,这就是一个读训练题的一个方式啊,读测试集和验证集啊。
读测试对测试集和验证集是不是一样的,这是我这里的啊,名字不一样嘛,这里换成第一位,到这里换成,就ok了,最后面就是这个get label了,咱们也可以参考一下他这个他这个是一个三分类对吧。
三分类我们这里换成一个二分类就ok了啊,他一个二分类对,一开始咱们先这样写啊,如果这样写的话是会有问题的好吧,咱们这里一定要以这串的形式来算,所以说我们在这里注意啊,啊给大家提一下,这是重点啊。
很容易报错的这个地方,如果你不把它用字符串的形式传的话,它这里读出来是一个整形的形式,那它就会报错,这里大家记着一定要换成一个字符串的形式,这样的话咱们这个自定义的这个processor就写好了。
那这个自定义的procel到底该怎么加进去呢,咱们看一下它那个mmin函数啊,这样说的这个m函数嗯,这个这四个就是他自己定义的一个procel,如果我们要把我们的processor加进去,很简单啊。
首先取一个名字啊,假如我这里叫jo,然后这时候咱们刚才定义的这个我才走,这样的话就可以把咱们这个自定义的这个procel给加进来了,刚才我也说了,我们有一个参数不是没有介绍吗。
就是这个task name,那这个task name就是就是我们定义在这个位置的啊,我这里定义的其实就是一个只有明白了吧。
然后如果你在输入参数的时候啊,就要看你那里定义的什么,如果定义的是只有now,这里就选择就ok了。
就是这样子的啊,这边的话我已经把那个代码已经上传到我自己的这个服务器上了。
然后我运行给大家看一下,要简单看一下它的日志,嗯这次实训作业有答案的没啊,作业的话是这样子的,会有几个简答题,简答题的主要原因是想让大家再熟悉一下transformer,它内部的结构到底是什么样子好吧。
一些比较关键的点,也是一些面试很好问的问题,大家去把那几个简单题都做一下,其次的话就是今天就今天晚上我给大家讲的这个白t的任务,大家去把这个模型给跑一下,就是一定要学会怎么拍to好吧,这个很重要。
然后咱们这里给他by two哦,我之前已经发过了,所以他那个模型已经保存好了,他就不会去再进行一个训练了,然后最最最终他在这个验证集的准确率可以达到0。94啊,咱们主要是看一下它的那个输出的一些日志。
嗯咱们就看这个吧,这个是z a r s一开始符,然后这是第一个序列,这个是分隔符,然后第二个序列,最后还有一个啊增值服,首先把这些字符做一个分词啊对吧。
咱们对应一下刚才的那个源码,我们自己的这个脉搏。
在走这边做一个分子分子的操作,分完字之后,它会针对于词典当中这个字在词典的一个位置,我们看一下这个cl它是在101的位置。
咱们看一下是不是啊,c l s102 ,因为咱们就是从一级开始嘛对吧。
默认应该是从零开始,所以这里是101,没什么问题,这些也是把自己对应的位置转换出来就ok了,然后这个是一个mask啊,这个mask啊并不是咱们transformer里面的那个mask。
而是指的是那个padding mask,就是说我补偿,你看我这个序列,我们刚刚说的对,上去就是32对吧,我这个明显没有32,那我在没有的位置就不灵,这个mask表示的是我哪些位置是正式指。
哪些位置是我补丁的位置,然后如果是真实值的话,那就是一如果不是的话,就补零,明白了吧,然后最后的话就是它的这个段落向量了,段落向量,你看第一个段落全是零。
第二个段落全是一,然后最后面这边是不灵的,这就是它的一个输出值,ok后面的一些咱们再看一下,还有没有什么有意义的日志,简单看一下,这边的话就是它的一个模型结构了,看一下啊,一共12层,从零开始的。
一共12层,你看它最后有一层是那个雷电zation segment id,没懂是吧,ok我再说一下,还是以看那个例子来吧,segment,它表示的是第一个序列还是第二个序列,零表示的是第一个序列。
你看我第一个序列对应的全是零,如果是第二个序列的话,我对应的全是一,就是这个意思,后面后面这边是又是另外一个序列,因为它后面补零了嘛对吧,他这个长度不够,所以我补零了,如果是另外一个序列的话。
我就再补零就ok了,说白了就是要把这两个序列给区分开来,所以一个是零,一个是一。
啊咱们在那个这里的时候也有提吧。
这里有题吧,嗯这个segment前面你看好这里写反了,我这里写的是一,其实前面应该是雷,后面应该是一样,不过即使你前面写一,后面写零也没有什么关系,你只要能把这两个序列给区分开来就ok了。
这个任务不需要做mask吗,啊是这样子的,mask它只出现在运行年阶段,我们现在做的是by two微调好吧,我刚才讲这个的时候也说了嘛,这个mask有个有一个最大的问题,因为我们mask了15%的词。
这样在法庭阶段可能有些丑的,从来没有见过,这也是这个引起的一些人为误差,是两个任务啊,同学一个是预训练阶段的任务,预训练阶段是啊,完形填空,还有自然语言推理,然后by two阶段就是你针对自己的任务。
自己的数据作为一个训练就ok了,明白了吧,就没有马克这个东西了。
大概这就是整个的一个白的一个任务了。
然后我博客里面有一个那个,有个这个啊,如果大家到时候有些地方不太清楚的话,也可以按照我这篇博客来做,我觉得还挺清楚的。
啊数据这边不需要预处理,我已经全部准备好了,没有什么需要预处理的,就直接这么读取就ok了,句号那些,其实他都是在字典里面有存在的,增加语料的话,要怎么集训iq呢,集训是什么意思,就继续训练吗。
就默认首先就b r t它其实已经把你封装好了,如果你之前有保修过啊,他会把那个模型给保存好,如果你有新的语要来了,你想再发帖的话,他会把之前你训练好的这个模型先加载进来,然后再把q。
这些异常的符号标点符号也要去面对,准确啊,不会有影响,因为你这些符号它最终还是可以转换成这个对应的这个数值嘛对吧,所以是不会有影响的,但是如果你的语料啊就是不太不太工整。
然后里面有很多一些乱七八糟的符号,建议还是先用那个正则或者什么方式把这些异常的符号先给去除,但这种情况就是说发挥很多,你看像我给这份数据,其实就日常值很少,可能会有一点,但是很少这种情况的话就没有什么。
你看像这里三井,其实这种其实问题不大,这种就没必要处理了,就如果他特别多那种异常符号特别多,那就做个正则什么的处理一下。
用bt的啊,均向量怎么取出来是吧,机向量的话啊,今天时间也不是特别多了,也可以来参考一下这篇博客好吧,他其实你在微调的阶段的时候,他已经把那个极限量给取出来了。
然后只是说你感受不到,他已经帮你把这些东西全部封装好了,你只需要把那个输入的这个结合写一下,就是你把为数据的这一部分的逻辑写一下就ok了,它其实内部已经把你全部封装好了。
啊大家首先第一步先去把今天晚上这个发帖的这个代码弄清楚,如果大家有还有时间的话啊,可以去我这里啊,自己还有一份代码诶,怎么找不到了,然后我这边还有一份代码,这个代码就很简单啊。
就一行代码就可以生成就向量了,大家可以来看一下我这个怎么写的,后面的话还有一些啊,也是一个fight fight,也是我把他做了一个那个就是封装了一下,就是你不需要去在命令行又输这些参数什么的啊。
不需要我直接帮你写成文本,写到那个配置文件里面了,我这里有一个这个吧,这个是啊,你直接在这边写一写记好了,然后就三行代码就可以做好微调了。
但是看我这个封装的代码之前,大家一定要先自己动手,把我今天晚上讲的这个微调的过程弄清楚好吧,啊我建议大家先来用源码把它这个过程给过一遍。
弄清楚了,然后再去看封装的,你看我这个封装也可以,然后你也去看,你也可以去看那个close的一个,克斯也有人封装好了,啊这个也很简单啊,你看他这个调用它的这个工资,这边是分子。
他这个也不需要配置什么参数,然后直接掉就好了,然后他这个可以直接用p i p把这个你看直接p i p一下。
但是你也要把这个预训练这个模型提前准备好。
然后按照他的这个训练就ok了,不过他这个就是完全是用那个cvs的api在训练啊,啊这个也是另外一个封装,我这边也有一个中文文档,大家如果英文看的不太懂的话,来看这个中文的也ok好吧。
这个就是kiss风服装。
ok今天完了全部的任务内容大概就是这些了啊,啊大家看还有没有什么问题,有问题的话就把它打到公屏上,不同的下游任务法to是不是都是写不同的,对对对对对,是这样子的啊,ppt群里面应该有。
群里面有群里面的是一个那个pdf格式的。
群里面有这份好吧,大家看这一份就ok了,哦动图看不了是吧,稍等一下,我把那个动图给发一下啊,这样吧,待会我统一整理一下那个动图,还有那个pdf重新整理一份。
然后发到群里,我这边要把一起发到群里,还有那个预训练好的模型,还有代码,我觉得都全部一起发到群里好吧,ok ok大家看看有没有什么问题,待会我把那个比亚t的源码,然后它的预训练的模型,还有pdf。
还有动图,就全部一起发群里,哦对了,还有作业好吧,大概十分钟五分钟之后吧,好吧,我这边再稍微整理一下啊,如果没有什么问题的话,那今天的直播大概就到这里了,如果大家之后还有什么问题。
就在群里艾特我就ok了,好吧啊,简单的总结一下好吧,简单总结一下,首先是那个transformer art marty号的同时一定要弄清楚,多看几遍,我当年学transform的时候。
其实也学了蛮长时间的,所以大家多花点时间来看一下,然后是bt的两个运行链阶段的两个任务,一个完形填空,一个自然语言推理。
好吧,看一下,最后就是他的一个微调阶段,ok那今天的直播就到这里了。
大家有什么问题就在群里艾特我就ok了好吧,各位就早点休息,嗯嗯好拜拜拜拜拜拜。
七月在线-深度学习集训营 第三期[2022] - P5:在线视频:02-生成式对抗网络(GAN) - 程序员技术手札 - BV1gW4y1x7j7
ok那我们开始今天的内容呃,今天内容是这个这个gg,就是我们的这个生成式对抗网络,然后呢在讲今天的这个生成式对抗网络,讲完之后呢,啊我看看时间啊,估计会给大家出几道,这个我们在啊面试或者笔试中的时候。
我们看吧,好不好,我们先看我们先来看啊,今天的这个主要的部分内容啊,我的这个准备应该是是准备了一些的哈,是要说要说gg的生成式对抗网络,这节课的内容啊,我们不得不先来讲一个啊。
几个预备的这样一个知识点啊,那先跟大家先说明一下啊,就是想要听明白深沉式对抗网络,这么这部分的内容啊,今天的内容一定会是偏啊,偏原理一部分啊,因为就是如果要弄清楚这部分的,这个细节的话,就部分逃不掉。
就有可能有些同学,如果说比如说对这个写数学或者高等数学,线性代数稍微有一些惧怕的同学,那可能要这个做好心理准备啊,因为干这部分内容,其实视频原理会比较多一些啊,但是啊我尽量会把它就是代码公式等啊。
公式相关的一些东西,我尽量能够去弱化它,给大家一些更加直观的理解啊,帮助大家去去克服这样的一个,恐惧的心理吧,但是这个不管是谁讲啊,可能我讲或者其他老师讲,我觉得这部分其实是逃不掉的话,好。
那我们开始今天的这个正式的这个内容了,那刚才说了,我们要讲一些预备知识,对不对,预备知识的第一个点就是kl,什么是chao divergence呢,care divergence。
divergence其实就是说白了去衡量两个分布,数据分布之间的差异性,你就记住这一个点就好了,它其实就是衡量两个数据分布的distance,或者说差异性,然后它的这么一个定义呢,就在这里。
k l p和q是什么东西呢,p和q就是两个data distribution啊,两个这个数据的这个不同的分布,然后你看一下这个distance定义什么呢,定义p x log p x除以q x对x的积分。
对x的积分诶我问大家一个问题啊,这个情况下是假设x是一个连续的,这个这个变量对不对,那假设x是一个不连续的变量,你们说这个distance的形式应该是什么呀,这是我问的第一个问题啊。
有同学可以在群里面回答一下吗,没错啊,当一个变量不再是连续的时候,而是离散的情况下,我们所有的积分通通就会变成mation求和啊,ok那如果是这样一个公式的,这么一个形式的话。
大家想想看什么时候这个divergence,或者说这个距离kl这个距离最小,想想看什么时候最小,两种两种嘛对吧,两种两种这个观点一什么时候最小,那很简单嘛,我问你两张图片相似度啊,那个距离什么时候最小。
两张图片的距离最小,其实就说明它们的相似度最大,什么时候两张图片的相似度最大,这张图片和另外一张图片,他们是重复的时候,这两张图片的距离一定是为零,它的相似度一定是最大,对不对,它其实不是相似度。
它就是一样的嘛,好,那现在两个数据分布什么时候距离是最小,那就是exact一模一样的时候,也就是说px等于q x完全重合的时候,它的距离当然最小了,对不对,这是一种二,你根据它的这样一个公式的性质。
你看这公式性质是不是有一个log东东存在,那你想想什么时候,rog的一个东东是等于零呢,那高中数学告诉我们,rog的函数的曲线是不是这么画的,跟x轴的交集sorry,那x轴的交集是不是。
其实x轴为一的情况下,对吧啊,x为一的情况下,那这个时候x又是啥呢,是不是就是p x除上q x在这呢,那px除上q x等于一,那不就是px等于q x的时候啊,可以这样,ok试试。
刚才给大家这个啊热身了一下对吧,然后接下来跟大家说明一件事情,kl距离这个东西,你们说啊,它是有具有对称性吗,换句话说看这个东西,kl p和q是否等于kl q跟p,大家思考一下这个问题。
它是否具有对称性呢,我想直观上,我就觉得大家应该要觉得它不具有对称性,对不对,它应该是有一个非对称性质的,没错,那为什么呢,很简单啊,我们来举个例子啊,举个例子来看一下,我们假设这个p跟q的数据分布。
是如上两种形式啊,如上两种形式,那我们来算一下p和q的ko和,以及q和p的,还记得吧,刚才为什么姜文老师给大家做一个铺垫,为什么这个x的变量,当它是连续分布的时候,变成离散分布的时候。
我们的那个积分会变成求和的时候啊,这个时候我们就要用上了,对不对,那p跟q的kl divergence怎么算呢,前面那个公式我们再回过头看一下,他说是p x乘上rog p x跟q x的比值。
然后对x的积分,那这里积分改成求和,那是p x乘上log p x除上q x,然后每一个分段进行求和不就ok了吗,p跟q那就是刚才这个东西对不对,刚才这个东西,那首先px我们来看一下。
当都是为这个的时候啊,这个的时候三段嘛啊,零的时候是多少呢,0。36对吧,px p0 是等于0。36,那q0 是等于0。33,那所以当x等于零的时候,它就是这一段,对不对。
那一和二的时候呢也一模一样喽对吧,一模一样,然后加起来就是这个东东,这个东东是多少呢,我们来看一下是0。0852996,这数字真好,996,然后再看另外一个q跟p在这,q和p那这个时候就是q在前面了。
对不对,然后比值是q x比上p x那就是反过来喽,啊刚才一样的这个求法,那这个时候得出来的结果是,所以说它是具有不对称性质的啊,p跟q的kl是不等于q跟p啊,那个q跟p的ko,那我们可以在对啊。
根据这个啊啊tao divergence,divergence来进行啊,在深1度的理解,我们看假设这个是p x分布啊,假设这是q x的这个分布,你们说k l p跟q的这么一个分布,会是什么样子的呢。
也就是说,px和q x其实是两个高斯的概率分布,只是他们的命不一样对吧,均值不一样,p x应该是一个这个标准的,然后q x是有一个平移的,然后现在问p跟q的divergence分布。
大家想想看会画出来是什么样子,画成右边这个样子,大家看看能不能够理解,你要理解这张图,或者说理解p跟q的这么一个,divergence的这样一个分布,那很简单,怎么想呢,大家这么想。
你把x从远到近拉过来,去看一下px跟q x的那个呃,这个kl divergence的那个定义,你来算一下,想象一下它是什么样的这么一个曲线,给大家一分钟的时间啊,自己想一下,画画也ok,就从这里开始啊。
需要大家动动笔啊,动动这个脑子,那是不是说首先你这么想,当x比较小的时候,和x比较大的时候,p跟q的kl divergence会是多少呢,因为当x比较小的时候,或者当x比较大的时候,px和q x都是零。
对不对,那所以它基本上也就是零对吧,那不断的当x往这边移移的时候啊,移的时候那一开始他们都是零,都是重合的,对不对,都是为零嘛,啊,然后当出现在比如说-4,这个点左右的时候啊。
到-3这个的时候开始慢慢的有,对不对,开始慢慢的有有什么呢,开始慢慢的有差异性开始存在了,对不对,之前都感觉不到嘛,所以他应该是慢慢慢慢上升的,这么一个状态,对不对,然后当x挪到这根线的时候。
这个线的时候,你们说他们的kl div认识是多少,这两个东西都相交集了,对不对,那我的kl divergence一定是零了对吧,所以这个点呢就应该一定是,就是这个点的时候肯定是为零,对吧啊,肯定是为零。
而在这个当中当中,刚才我们说了,随着它的这样一个推进,那kl diversions慢慢慢慢慢的就会增大,增大到某一个点的时候就应该是最大了吧,因为它这里一定是一个有限的范围,对不对,然后最后又要回到零。
那就是慢慢慢慢慢慢又变角变变小了,对不对,直观上来你就可以这样理解,对不对啊,最简单的例子你就看这根线嘛,它的这样差异一定当某一个点的时候,是有一个最大的情况,对不对,然后慢慢慢慢的又变回来了。
所以大家想想看是不是它的这样一个分布,大概就是这个样子的对吧,那后面我就不说了,只是一样啊,都是一样,只是反一反,那我们看下面这张图啊,下面这张图,给出更多的不同的颜色的分布啊,这张图能不能够理解。
就是kl的距离是对应的这样一个情况,为什么呢,为什么呢,你只要想一个case就好了,为什么这里的绿颜色啊,这个绿颜色对应到这里啊,为什么这个绿颜色啊,当q x是绿颜色的时候,我的kl的距离要增加。
是增加嘛对吧,因为你的这个时候看这张图,绿颜色的的这样一个啊,波波峰,对不对,更加的陡峭,那为什么呢,很简单嘛,因为绿颜色的这个数据的分布,离它要来的更加的远,对不对,当x从这边往这边挪的时候。
那当然我的距离是来的更加的远了,是不是啊,这样想就能想通了,对不对,好这个是kl divergence,刚才说了,kl divergence是具有不对称性的对吧。
那ok那接下来我们来再介绍一个js divergence,js divergence的定义如下,它是建立在什么呢,pl的divergence的基础之上,它其实是p跟q p加q除以二。
以及q与p加q除以二的这么一个,divergence的这么一个平均的值,ok这个定义应该没有问题了,对不对,好我们举个例子,这个例子就在这里,我们看左上角这个图啊,还是刚才我们看的tx跟q x。
那接下来大家画一下p跟q以及q跟p的,这两个kl的这样一个距离的分布,刚才都已经告诉大家画了,对不对,尤其是这根线,我们已经刚才跟大家分析过了,对不对啊,分析过了好,现在只是把另外一半啊。
另外一部分给补过来啊,q跟p的这一个kl diver,而这个呢是p跟m的这样一个,这个红颜色是p跟m的这样kl divergence,然后蓝颜色的是p,q跟divergence的这么一根线,大家想想看。
为什么这个的分布跟那个是一样,是因为p加q除以二,我的这么一个拟合出来的这样一个形状,大概就是这个样子,对不对,我觉得可能会比较低一点的,是不是啊,所以你这样一个形状。
但是只是它的这样一个坐标的范围不一样,那这两个相加起来是多少呢,加起来就是这个js的kl分布,想想看是不是这个理由是什么,理由是我是这两个东西相加,对不对,是不是这两个东西相加,这两个东西相加。
你看看这个东西已经超过了0。10,对不对,喏看它的这个最大的这样一个,而这个东西是在负的0。05,对不对,所以这两个东西加一加,就应该是一个正的东西,对不对,所以在这儿啊,那边也是一样,能够理解吗。
这样讲是不是大家可以可以消化一下,所以为什么说啊,就是需要大家有一些数学的sense啊,逻辑的这么一些sense,这个就考验大家高中数学和这个啊,高等数学的一些图形的这么一个,理解的能力,没问题吧。
这还都只是前菜啊,这是我们讲game必备的这两个距离有问题吗,同学们没有的话,我们继续,好那我们继续了啊,好接下来要开始进入干的部分了,那说这个gg其实是由两个东东的组成,哪两个东东呢。
我们一个称之为叫discriminator,一个称为叫generator,也其实就是大家说的,一个叫判别器,和一个叫生成器的这么一个概念,所以,啊这个,discriminator是什么呢。
都说了是判别器,对不对啊,所以discriminator其实就是用来判断某一章,某一个data,它到底是真的data还是生成出来的假的data,就好比某一个这个是,比如说是一个验钞机对吧,有一个验钞机。
这个验钞机呢,其实是来判断你给出来的这个人民币啊,这个头像百元大钞到底是真的还是假的,如果是真真的的话,如果对于是这个啊真币的话,那我要让他过,对不对,假币的话,那我就要给出警告。
那放到我们的这个game这个里面,那对于如果是real data是真实的一张data,来自于真实data的一张某一个data,那,么我要输出的这样一个概率是一个很高的,这样一个概率应该是输出一对吧。
然后如果是对于某一个生成的啊,数据集是一个人造的假的这样一个数据,那我要给他派林,这个就是discrimination要做的事情,generator要做什么呢,generator是这样的。
generator是要generator的,这个刚才说了d的输入输出,对不对,d的输入是某一个data,比如说如果是图片的话,那其实就是一个这个,比如说299x299x3,这么一张图片啊。
它的这样一个输入啊,输出应该是一个零或者一啊,或者当然是0~1的这么一个概率分布了,对不对,啊当然我希望对于real data,他输直接输出这一了,对于假的fake data是输出零。
那generator的输入输出是什么,generator的输出很好理解,大部分同学都能够理解输出,因为是一个data嘛,因为生成器对不对,如果我是一个图片的生成器,那我就是生成一张输出一张图片的。
那generator输入是什么,大家想想这个会稍微稍微有一点点trick,就是generator,你总归东西就是generator,输出一张图片,对不对,你总归要喂给他一个什么东西。
什么样的一个一个信号吧,比如说你要是输入输出一张狗的图片,ok那,你要给他一点什么样的提示,让他输出口,比如说啊这个狗是一个黄颜色毛的对吧,它得有一个啊,这个这个这个这个尾巴是长的。
总归要有一些信号量给了哈,对不对啊,那所以generator g的输入呢,通常是一个column vector,是一个列向量,比如说是100维的这样一个列向量啊,一开始可能可以是随机分布啊。
随机分布的一个情况,所以generator的这个输入,输出输入是一个向量,是一个向量空间,是一个一维的向量空间,比如说100为200位,然后输出呢是一个高维,如果是图像的话,那就是一个高维的这么一。
个呃像素点的空间,那generator和discriminator,我们把它放在一起,就组成了我们的game的这么一个网络,的这么一个结构,首先我们先来看一下discriminate的部分。
这个很好理解,刚才我们说了,已经说了,对不对啊,上面的这半部分就是discrimination的部分,给定任何一个sample data,无论这个data是来自于真实的数据分布,还是假的数据分布。
那discrimination是输出一个scaler,一个标量输出,这个data到底是不是真的,还是假的的这么一个概率值,generator generator,其实它的输入是刚才看到没有。
一个latent space,其实就是一个向量啊,100或者200位的,然后输出呢,输出是一个产生的这么一组fake data,那fake data他要做什么样的呢,它其实就是想要什么呢。
他的目的是想要骗过这个真discriminator,让他输出是真的这么一个结论,所以好比我们拿刚才的这样一个,验钞机的这样一个例子,对于验钞机来说呢,它必须它的职能是说,对不对,这是他做的事情。
generator,就对应到产生产这个验钞机的那一群,对不对,因为他都要去生产这个验钞机了,你说他们是不是这个就怕流氓有文化,对不对,类似这种,虽然他去骗过一些这个呃这个这个啊。
这个discriminator,有一些真假验钞的这样一个手段,就好比我要不断的去升级,我的这样一个验钞机啊,一代不行,那不行,那我要上二代,三代等等,大概就是这样啊,大概就是这样。
所以这个就是我们discriminator和generator,的这么一个啊职能,对于d来说,我要非常有能力的去区分真和假,而对于g来说,我必须什么呢,我必须要让我的这样一个造假的能力,越来越登峰造极。
去骗过我的这样一个地,所以d和g呢,其实就是正好吃这么一对啊对抗,所以为什么叫生成式对抗网络呢,它的对抗性质就在这啊,这是从宏观的这样一个层面,把这么一个framework给搞定啊,这么这一个呃给啊。
讲完了,好那我们接下来从这个模型的层面啊,从这个数学定义的键啊层面,再把刚才这件事情给对应起来啊,首先我们给三个这么一个变量啊,哪三个变量呢,pg pg和pr jz是什么呢。
p z就是刚才我们看到那个latent space,那个空间的,还记得吧,就是我说的100或者200位,未给generator的那个输入,假设pz是这个刚才那个输入的,这样一个啊数据分布。
通常来说啊是一个这个啊uniform,然后pg是什么呢,pg其实就是generator的这样一个数据分布,就是造假器啊,生成器这么一个数据分布,r pr呢就是real date。
就是真实的这样一个数据分布好,这是我们三个这样一个概念,所以呢对应到刚才我们说discriminator,d判别器,他做什么呢,他对真实的那个数据啊,真实的那个贝塔x我要尽可能让它最大,对不对。
它的这样一个输输出最大,给它的这样一个school最大,因为刚才我们说了嘛,所以他要做的这个事情,就是他的目的是maximize这个东东对吧,这个没问题,而对于这个假的data来说。
也就是生成器产生的data来说,jz jz是什么呢,z是什么呢,z是由刚才那个,这个东西对不对,是由pc的这么一个数据分布给产生的,所以jz z输到g里面去,jz输出是什么,jz输出是一个data。
对不对,是一个x啊,这个x是一个fake的x,然后对于d来说,对于general嗯,那个判别器来说,我要去把jz去判为零,对不对,这是我作为判别器的使命啊,我要对于jz我要去判零,而对于x呢。
我要去判这个一,这是我要做的事情,所以如果说对于jz我要去判零那事,事实上是不是其实我要做这样一件事情,我要maximize一减d g z,对不对,看一下这个公式,因为我要最大maximus这个东东。
那我又是一减东西,那我要减的那个东西,我要让它最小,对不对,最小是多少零,所以这个就是对于站在判别期的角度上,他要做这件事情,刚才我们说的,只是说站在判别期的角度做这件事情,对不对。
那接下来我们尝试站在生成期的角度,来看这件事情,那生成器的角度是什么呢,啊生成器的角度是说ok我有了一个z对吧,有了一个z,然后产生了一个g z,那我希望什么呢。
我希望把这个jz这个data产生这个data,比如说是x一撇对吧,这个x一撇我要送到d判别器d里面去,我要让dx一撇什么呢,大对不对,因为这样我就骗过了嘛,啊骗过了这样一个判别器嘛。
所以我希望d j z要来的更加的什么呢,要来的更加的大,所以恰好这两个东东呢就是一个对抗,所以这个是从把刚才我们从这个概念上的,这么一个理解,转化到数学表达式的这么一个理解。
所以我们在随着刚才的这样一个思路,往后面再走一步,所以刚才我们说了pg pg和pr,那我把这些东西都揉在一起啊,揉在一起,我们写成生成式对抗网络的这么一个啊,这个啊目标函数他要做什么呢。
哦他要做这样一件事情,对于d来说,对于判别器来说,我希望什么呢,我要我希望那些真实的这个data,他的这个分数越高越高越好,这是对于我判别器来说,他做的这件事情对不对啊,做的事啊,对于生成器来说呢。
啊对于生成器来说,我要做一件什么样的事情,我希望能够去骗过判别器,对不对,所以对于生成器来说,它从z里面取一个分布,jz是一个人造的这么一个data x一撇,那d x一撇,就是把这个生成的这个x一撇。
送到判别器里面去,看它分数对吧,我希望这个分数越高越好,越高越好,那么用一减去,它其实是不是就是越贪越低越好呀,ok所以对于d来说,我希望它越高越好,对于g来说,我希望它越低越好。
所以这个就是我们的mamax啊,一个是命,一个是max,所以有时候我们的这个生成式对抗网络的,这个对抗也称为叫mean max game,而这个公式我又可以写成下面这一坨公式,原因是什么。
原因是看下标啊,看下标前面这一坨没有任何变化,对不对,好刚才这个下标是z从pz取出来的,对不对,而pz我们说啊,那个sorry z是从pc出来的,而jz其实就是一个生成的一个dx一撇,对不对。
所以我可以把它写成下面这个公式,当x一撇从pg x的分布里面取出来的时候,那其实那个jz其实就是我们的这个x嘛,对吧,它可以写成这样的一个公式啊,更加简洁一点,如果我大家刚才这样一个从啊大的这样一。
个盖的那个概念性的这样一个框架,到刚才的目标函数的这样一个啊映射关系,这个映射没有任何问题的话,那接下来我们就可以玩生成式对抗网络的,这样一个更深的这么一个层面去卖一步了,那我们来考虑一下。
刚才我们说判别器这件事情很重要,对不对,很重要,你好,就好像你的这样一个呃这个验钞机,那他能不重要嘛对吧,它它的这么一个啊地位是非常重要的嘛,那ok我们来看一下什么情况下,我们的这样一个验钞机。
或者说我们的,这样一个判别器能够达到一个不错的,这么一个值,就是他什么时候我的这样一个d是ok的啊,不错的,那刚才我们把这个东东看到没有,刚才我们把这个东东,展开来,也就是我现在已经把生成式。
对抗网络的loss函数写成啊,x从pr x里面取log dx啊,再从这个x从pgx里面取log一减dx,它把它写成我们展开的这么一个形式,是不是就是它,我把刚才的这样一个期望,把它用这个积分来表示。
没有任何毛病,对不对,这里是p i x,这里是p gx,好我们做一个变量的这么一个代换,我们令什么呢,x tale上面有个小波浪号,视为大dx判别器,我们用a呢表示pr x,用b呢表示p gx。
那ok那刚才的这么一个,公式我是不是可以把它写成这个东东,对吧,两件代换嘛,a乘上log x tale,加上这个b乘以log一减x ta好,那这个公式我就写到这儿来了,那刚才不是说我要去求一个。
最优的这么一个d,就什么时候我要看我的判别器,d达到不错的一个结果对吧,那求一个,最优的这么一个值啊,求这么一个极值,在高等数学里面是不是,其实就是令它的导数为零,然后我看他是什是什么渠道,什么样的值。
对不对,那令它的导数为零,那o,那不就是a这里是x分之一,对不对,然后这里减b然后一减x分之一,对吧啊,所以一直在这,当然这个东西是一个常数了,不要都ok对不对啊,不要都ok,所以令它为零。
令它为零很简单嘛,这个时候问dx是多少,那不就相当于刚才dx是啥,dx在这里我认为x tale了,对不对,也就是刚才这里的自变量,那这个时候问它等于零的时候,excel的这样一个表达式你会写吧。
那你肯定会写嘛,就是我们的初中数学,对不对啊,你把这个东西写出来就好了,那excel是多少呢,excel是不是,上面这一坨为零,那就是a除以a加b a是什么呢,a我们来看一下,a是这里的p r x。
b是这里的pgx,ok我们来看一下,所以我们写成了px与px加pgx的直,a就在这里,那什么时候,它取到一个最优的这么一个解呢,很明显,当px等于pgx的时候,dx渠道最优的一个解1/2。
这个是什么意思,这个意思是说,p i x是一个固定的一个东东,对不对,只是我们不知道它是什么吗,tx是一个固定的动作,因为它是真实数据的这么一个分布,他是继承的这么一个事实。
所以pgx是我们不断的去去改变的,一个东西,对不对啊,我要去去去去得到的这么一个东西,pg x与pr x相等的时候,就说明当我不断进行调整调整,调整这个z或者调整其他什么的时候,我能调出来一个。
我generator生成的这么一个数据分布,达到一个什么样的境界呢,达到一个跟真实分布一模一样的境界,那这个时候我的dx其实是对,我的real data和fake data。
其实基本上我是没有什么分辨率的,所以我就靠猜,只能猜这么一个,50%的这么一个几率,这个就是dx新的这么一个值,所以他背后其实是在说这么一句话,d的这么一个optimal。
这么一个value其实就是1/2,什么时候取到呢,当pg和pg是相等的时候,它取到,我们用一张图把刚才这个这个概念,我们再试图去理解一下啊,试图去理解一下,我们看这里有四张图,这四张图是什么意思呢。
ok我们先来看一下下面这个z啊,就是我们刚才看到的那个,100为200位的那个latent space vector啊,那个东东啊,这个x呢是我们的那个数据分布,z到x大家看这里有一个映射的过程。
对不对,那这个时候映射的过程,其实就是那个jz的那么一个映射的过程,对不对,我要从一个100 200位的这么一个空间,去映射到一个啊这个data的这么一个空间,而这个绿颜色的线是什么呢。
绿颜色绿色的线就是我们的gx这根线,也就是我生成器的这么一个数据分布,这个黑颜色点点的线呢,是我真实的这么一个数据分布,p小i x,然后这个蓝颜色的线线是什么意思呢,蓝颜色的现象就是g are。
sorry,是你的d判别器dx,所以大家看a这张图,a这张图说明什么呢,大家说a上图说明什么呢,a这张图你说问大家一个问题啊,a这张图你得出来的这个信息dx是否可用,姜文老师就问这么一句话。
大家想想看dx是否可用,我通过这个问题可以把,问一下大家的这样一个理解力,没有同学回答吗,我觉得有问题啊,你看你的逻辑,你说dx对real样本数分数高,而fake样本分数低,很好区分,因此不可用。
那前半段话,你说的这样一个现象,和后半段话这样一个结论,为什么是矛盾的呀,你看既然dx对real data分数很高,而fake分数很低,说明我要对真超的这么一个,这个输出的这样一个结果很高。
那我来到阈值,对d我问的是对d可不可以用,对对判别器而言可不可用,所以大家听清楚问题啊,对判别器是可用,对不对,嗯没错,是可用的,唉李彪同学,你不是说看不懂吗,你怎么又看懂了,你到底看不看得懂,没错。
对判别器而言,这个是可用的,其他同学有没有看懂这个很重要啊,这个对大家理解很重要,没有不懂的吧,如果没有不懂,我就当做我们的这个同学们留下来的,这个单身的同学们,你们都是精英啊,芒果同学不懂是吧。
好芒果同学不懂,我再讲一遍啊,为芒果同学,你在这个啊讲一遍,得亏你是单身对吧,这里有z x还有这个绿色的天线,黑色的线线,还有蓝色的线线,对吧啊,这么几个东西,z是生成器的输入部分。
也就是刚才那个100维的这么一个输入的,这么一个列向量,而x呢是我们的real data和fake,data的这么一个数据分布,而z到x的这么一个非线性的,这么一个映射关系啊,其实就是在说有了一个z。
那我就会有一个jz,也就是生成的这么一个x或者说x一撇,刚才我举的这个例子,而这个黑颜色的线呢,说的是真实数据的分布,所以真实的数据分布,黑颜色的这根线线你是不能动的,对不对。
还它就是一个客观事实存在的,这个没问题吧,然后绿颜色的这个线线是什么意思呢,大家只要看a图啊,不要去看其他图,我们现在只看a图,绿颜色这个线线是我们生成器生成的,这么一个数据分布,所以对当前而言。
g这个函数有了,对不对,p gx你知道了,然后p i x你有了,所以我们还差一个判别去判别器d对不对,然后我们现在假设判别器是如蓝颜色,这个图这根曲线画画的这么一张图为例。
我的问题是当前的判别器是否可用,是否可用,你就要去看什么呢,去看dx对什么呢,对real data和对fig data它是否有区分度,对不对,那有没有区分度吗,有没有区分度,其实就在这嘛,对这边而言。
对于x是属于我的,这个大部分是为这个real data而言,那我的生成器啊,我的判别器分数比较高,对不对,而,当时我的这个x是来自于fake分布,那我输出的这样一个判别器分数比较低,对不对。
所以它是有很好的区分度对吧,所以这个时候是可用好,注意这个时候是可用啊,可用好,b这张图是什么意思,d这张图的意思是,我对dx进行一次优化迭代,生成了这么一张图,这张图跟左边a图相比而言。
是不是我的decision boundary,更加的平滑和清楚了,我是不是得出这个信息,从这张图案里面是不是,芒果同学就问你是不是,那说明什么呢,说明我dx判别器朝着这个前进的方向,走了一步。
优化了一步对吧,往更好的这样一个柱子上迈了一步,这是对于d而言,c是什么意思呢,c是说o你判别器牛逼了对吧啊,判别器你进步了,ok,那这个时候我要对我的gx,也要去优化一遍。
因为刚才我们是maximize什么呢,maximize那个刚才要用的那个千万别忘了,刚才在我们的损失函数里面,是一个mini max的游戏,对不对,in mac mini max的这么一个落函数。
我还要对g来进行优化呢,对不对啊,对g也就是对这个生成器优化了一步,那生成器优化了一步,那就到c图,c图跟b图区别是什么,是不是c图跟d b boy图,唯一的区别就在于,我是生成器的那个gx啊。
gz朝道朝着什么呢,朝着real data方向去靠近了,也就是他优化了一下,他进步了一下,随着刚才我的这样一个gx和dx,不断的交替来回去优化,那优化到一个好的第一个情况的时候,是不是我的gx和dx。
我的这两个数据的分布几乎都是一致了,也就是你造假什么呢,造假的这样一个能力炉火纯青了对吧,所以这个时候你的判别器,你的dx,对吧,所以你输出的概率是10。5/2,好期待。
这位同学对刚才的这样一个这这张图,就对刚才我们说的这些事情的一个啊,几何上的这么一个总结,现在各位看懂了吗,看明白了吗,看明白了,给我个信号,所以这张图是一个非常非常好的总结啊。
我觉得是一个非常好的总结,就是对刚才我们的这样一个,dx和gx生成器判别器啊,怎么去优化,怎么去配合优化的这么一个,然后达到一个什么样的这样一个啊,结果是一个很好的总结啊,所以大家一定要弄懂。
好我们继续,刚才我们说了,我们的这个生成判别器和生成器,这样一个博弈的过程,对不对,什么时候到达了一个好好的这么一个结果,好我们的结论是px啊,p p g小g x和p小r x,当生成的系去拟合啊。
逼近到这个真实数据分布的时候,诶我的这样一个dx是1/2诶,这很好理解对好那这个时候我们来看一下,当dx是1/2,这个时候我的loss函数是什么,那我的loss函数是不是刚才是这个形式,对吧,这个形式。
dx是1/2,那一减dx不也是1/2吗,所以这两个1/2我把它提出来,在这,p小x对x的积分和,p小gx对x的积分是多少,是多少,快问快答,所以我的结论写出来就是-2,有个二,老干。
这个其实就是啊dx取到最优的情况下,这个1/2的情况下,我的啊目标函数的这么一个最优解,是-2的log 2,好,我们从另外一个角度来再理解一下,我们的目标函数,大家还记得我们的什么呢。
我们的这个js的这么一个distance对吧,刚才开课一讲就讲了kl和dl等等,对不对,跟大家讲了这两个预备知识,dx啊,那个js的这样一个distance,我把它写出来。
用1/2 kl和1/2 klpg,的这么一个形式写出来,没毛病,对不对,没有任何的毛病好,我把这个公式再把它拆下成下面这个公式,能看懂吗,我无非是把这个logo的这么一个1/2,把它提出来了,对不对。
因为kl的这个公式的表达是不是,px乘上log p x除上q x对x的积分,那就是这个东东哦,只是这个时候有一个1/2,然后我把它拎到前面来了,对不对,那下面一个也是一样,所以这两个logo啊。
我把它写成在一起是什么,这两个logo不就是log 4吗,我把它写到这里,对不对,而后面的这两个东东加起来是什么,后面这两个东西加起来,看它的这样一个分子,是不是其实就是。
log d x和log一减dx,是不是,其实就是刚才我们的目标函数,是不是就这个,所以当把js的这样一个distance,写成这个东东的时候,我进行等量变换一下,是不是可以得到这个公式,也就是目标函数。
肝的目标函数是等于什么呢,两倍的js d s啊,js的distance减去两倍的log 2,现在问什么时候,我的loss函数取到一个最优的一个解,什么时候,那很显然什么时候呢,后面是一个常数项,对不对。
跟我没关系,而前面这个东西是一个distance的距离,对不对,那distant这样一个距离,其实就是,当p小i x和p小gx为重合的时候,我的这样一个距离一定是最小的对吧。
而这个时候我的要函数的最优解,是不是就前面的-2楼干,就是后面这一串东西嘛,前面就为零了嘛,所以这是从另外一个角度维度来去看待,我们的这样一个当dx是,啊这个这个确定的时候。
我们的这个捞函数的这样一个optimal value,这两个角度能看明白吗,这个就是js distance和我们的这个loss函数,的这么一个关系,啊这里我们再给大家补充一下,gain的一些变种。
就是其实会有很多的一些不同的这个,生成式对抗网络啊,啊他们有一些变化在哪里呢,其实就在于啊判别器输出啊,判别器的这么一个输出的这么一个啊东东,什么意思呢,就是刚才我们说了。
我们的判别器只输出零或者一两类,对不对,判断它到底是真实的data还是假的,data a,其实在那个实际的这一个应用情况下来说,我其实data判断那个dx啊,我们其实很多c n n其实可以输出更多。
标签更丰富的一个情况,就比如说大力是狗还是猫还是轮船等等啊,这些情况,其实这个时候我的dx还可以输出什么呢,还可以输出一到k减一这个东东啊,一共k类对吧,同时呢啊我还会输出dk类,dk类是什么意思啊。
dk类其实就是假的数据那一类嘛,就前面k k总是这个黑客这样一个类别,是我这个啊,真实的这样一个数据分布标签对吧,猫狗轮穿啊,最后这个dk呢啊,k加一类的,这样一个k是一个假的,这样一个类别。
所以对于这个判别器而言,我希望我的real data是属于我们这样一个啊,前面开个任意一个嘛,他对于甲的那个数据而言呢,比如说我希望它输出k啊,这是有一些变种,讲到这里,我们稍微休息一下吧。
啊稍微稍微有一些啊,这个复杂,是不是大家稍微轻松轻松一下,那个消化一下,我们一会稍后回,各位同学,我们回来了啊,这个刚才有一个同学叫杨宝,同学问你一个问题,这个问题他说嗯为什么在啊,把这个调过来啊。
啊养马同学问了这样一个问题,就是为什么啊,这个公式到下面,这个公式可以这么去进行变换,ok这是他的问题,当然你想想看啊,为什么可以这样变换,这里变换的差异在哪里,变换的差异。
是不是在z到pz的这么一个取值,以及变成了x从什么呢,从pg x取出来,这是第一个点,对不对,第二个点是g啊,那刚才我我们是不是其实说过这样一段话,生成器gx啊,gz它的这么一个输入输出是什么。
jz生成器的输入是那个z,就是latent space vector,你看这张图,这是我记的这个输入呀,对不对,这是g的输入,而g的输出是什么,g的输出是某一个data,对不对。
哪个data呢是一个fake data,因为是我通过人造出来的这么一个数据啊,generate的这么一个data,所以它的输入输出就比较明确了,jz jz不就是把z送给generator。
生成器里面去它的输出嘛,所以jz是一个data,对不对,它是生成的某一个data,可能是一个图片,可能是一段话啊,我们就以图片为例啊,它可能就是一个图片,是一个fake的图片,那ok你明白了。
jz是一个fake的图片对吧,没问题,那所以呢公式二就做了这么一个变化,什么变化呢,从pg x取出来,pg x是什么呢,是fake data的数据分布,对不对,因为是p小于gx嘛。
所以这个时候你的x是一个data,对不对,是一个fake data,所以这个时候我把它写成dx就没问题了,所以第一个加号,这这个公式有两项,第一项的x和第二项的x不是一件事情,对不对。
为什么不是一件事情,因为它是来自不同的分布,第一项是来自真实数据的分布,而第二项是来自fake的数据的分布,就能听明白了吗,现在杨宝同学,好明白就ok好,我们继续,刚才我们讲到了哪呢。
ok我们讲到了这对吧,ok讲到这好,接下来我们讲game的几个这几个点吧,几个问题好吧,讲几个问题,第一个点是说嗯其实刚才说了,我要去最大化这个啊dx,然后我要最小化gx,其实这句话说说是容易。
其实其实还是比较难的,我们举个例子啊,我们举个例子,假设我们要这个mini买这个东东,然后我们要呢mini max这个第二个函数啊,其中第一个是x,第二个是y啊,这两个变量,所以对于第一个而言。
函数一对x的梯度是y,函数二对y的这样一个梯度是负x,ok那大家想想看,只要x和y它的符号是1号的,那么这个时候什么呢,这个时候我的这个啊优化,是不是一下子把他拉过来,一下子又把它推过去。
所以其实还是挺难去达到一个比较平衡的,这么一个值,对不对,比较找到一个比较啊u的这么一个值,既既满足于啊函数一,又满足函数二,所以啊,有时候去做这个mmx的这样一个优化,并不是你想的那么简单。
这张图其实就代表了刚才我们说的啊,这个意思就是,你看一下,就是随着我的这样一个不断的迭代,我的这样一个把他拉过来和把它推过去的,这样一个差异会越来越大,越来越大,第二个,那我们要先说一个概念。
这个概念第一是流行流行,没没什么好说的,大家都知道就manifold,其实你可以把它想成一个n维的,这么一个空间就ok了,然后第二个概念是一个support支撑集,那什么是支撑级呢。
说白了其实你就可以理解为,支撑集是一个函数的,非零部分的这么一个子集,你可以看到这么一个定义,说白了它其实就是这件事情好,那刚才我们说了p小g x和p啊,小rx对不对,这两件事情好,第一个啊。
第一个我们要说的这样的事情,p小rx看起来是比较高的,这么一个高维的这么一个流行,有可能是一个高维的这么一个情况,但事实上是不是这样的,p小i x是什么,其实就是真实的那些数据的分布,对不对啊。
我们看起来真实的那个数据分布,比如说啊我的这样一个啊,是4096维的那个feature vector,或者说我是一个229x29,299x299x3维的这么一个pixel data。
你想想看你所有的这个像素值,它真的是啊,299x299,这么大小的这么一个数据分布吗,也就是说它的维度真的是一定是这么高吗,事实上可能没有那么高,对不对,为什么呢,是因为你要去产生一个狗,一个猫。
一个轮船,一张图片,它其实有非常非常多的约束,对不对啊,因为你要显然是一个毛,是一个狗啊,你至少你的这样一个是是是轮船的话,你的这个图片里面不可能应该是有毛发啊,不应该有其它,它其实有非常多的一些形状。
有有这个海水啊天空,然后又有怎么样的一个呃,轮船的这么一个外形等等,它其实啊可变的东西没有那么多,其实它相比于我们赋予它的,这么一个维度而言,它其实维度是没有那么高的,对不对,p小g x。
p小gx其实它的维度也没有那么高啊,为什么呢,因为虽然p小gx我是要去啊,生成的一个就是啊图片啊,一个data,但是千万不要忘了,p小g x图片是怎么来的,换句话说我问大家生成器的输入是什么。
生成器的输入是不是那个latent vector,是那个z,对不对,那个z通常来说是一个低位的空间,是一个100或者200的这么一个东东,所以p小gx这个东东,也不是一个高维的东东啊,就是啊这件事情。
所以这是p小于gx和p小于x等等,那再来我们看了一下,什么是一个低维的这么一个支撑机,那其实是说p小r x和p小gx,其实是一个高维分布状态,里面的两个低维的形态,那什么意思呢。
左边这张图其实就是我们想要表述的东西,高维的形态,其实这里你可以理解为是一个299乘,299x3这么一个像素点的空间,这么一个动作,或者409位六位的这么一个啊,高位的这样一个特征。
向量的这么一个表达啊,这是他俩的数据分布,但事实上呢,这两个p小gx和p小于x,是两个低位的形态,因为刚才我们说了,它有种种种种的约束啊,比如说100跟这个4096或者100根啊。
229299x299x3,这么一个大小的这么一个啊区别,那这是什么意思呢,这个意思就是这两个数据分布,其实它的这样一个交集的可能性会比较低,就好比在三维空间里面,你画两条线。
它相交的这样一个概率要比三维空间里面,你画两个平面,它相交的概率要来得低得多得多的嘛,何况这个维度的差异,而不仅是三跟一和三跟二对吧,很更更多,所以说白了小gx和p,小gx和p小rx是两个高为负分布。
里面的两个低位的这么一个支撑几,那这又怎么样呢,有同学会说,so what so what so what,一会我们就知道了,最大的一个点,就是最大的一个disadvantage。
不好的这么一个点就是说什么呢,啊大家看这个公式啊,你们就能get到了,就是你在算loss函数的时候,哈喽哈喽哈喽,同学们掉线了吗,好我们继续吧啊好刚才我们说so what对吧。
为什么p小rx和p角减x是高维流行分,那个这个高维这个流行分布里面的,两个低位的这么一个支撑集,so what,那又怎么样呢,其实原因就是你看这个东西你就知道了,我们的目标函数是写成两个,概率分布啊。
数据分布之间的distance,而数据分布的这样一个distance,大家想想看,当p小g和p小r没有交集的时候,是不是,其实你这个distance就没就就就是啊,非常大了,是不是这个意思。
如果是非常大了,都没有交集了,那你怎么去算它的梯度呢,那所以你的这样一个梯度下降算法,你怎么去优化,你的d和g是不是就无法去进行优化了,所以他其实说的这件事情就是在这里啊,就是一旦我的p小g和p小r。
是两个高维流形分布里面的,一个低位的这么一个形态来表示,那我很难去做优化,那这个说的是说当这个呃,我的这个discriminate discriminator,就是当我的这样一个判别器啊。
是好的时候是是比较好的时候,那越优化优化着,优化着,那我的这样一个啊,这个gradient其实就会越来越小,那很正常嘛,因为你的这样一个啊,都已经是一个比较好的,这么一个状态的时候。
所以当你的随着你往这个方,后面的进一步的优化,当然你的梯度就越来越小了,你的梯度越来越小,其实你再往后面去优化的这么一个空间,当然越来越低落,这个其实就是一个gradient。
managing的这么一个问题啊,我觉得这个很好理解,所以总结下来我们的这个啊,这个这个啊gg其实就面临这两个问题,第一个问题是说,假如我的这个判别器啊,如果说他不足够好不够好,我的这个呃这个这个这个。
loss函数啊,当然你无法去给他去这个提供好一个,比较好的这么一个方向,让我的这个generator去进一步优化,对不对,而如果你的判别器啊非常强了,那你后面的这样一个梯度的这么一个值。
那也就越来越小了,所以其实你也很难去往后面再走,所以什么时候,你的这样一个game是一个比较好的时候呢,就是当你的discriminator,当你的判别器可能训练的不好不坏的时候,唉。
这个时候对你的g和d其实能够提供一个,比较好的这么一个梯度去进行优化,我靠什么叫不好不坏,这个火候就很难掌握,对不对,所以正因为是这个原因,而我的gun,我们的gg其实是非常难训练的啊。
这是啊它的这么一个原因的本质,那gai还有一个啊臭名昭著的一个问题,叫model collapse,什么叫model collapse呢,其实就是说啊我因为我的这样一个gag啊,很难训练,对不对。
所以通常来说我的ga,他会去倾向于选择一些安全的这些样本啊,不倾向于去选择一些啊多变性,就好比我们大家去吃饭去点菜的时候啊,有些同学会会这个啊,会只一味的去点那几个菜啊,因为他他这个呃。
怕这个吃一些青菜的时候啊,和不合自己的胃口嘛,所以他为了避免这样一个情况,他躲在自己的一个舒适安全区,但有些同学不一样,就是我喜欢尝新的花样对吧,很有可能我会扩大我自己这个后面的这样。
一个喜欢的这样一个菜的品种,所以前面那一种同学,其实就是啊这个啊这样的情况啊,所以因为这样的情况,所以我会产生这个model collapse,其实就是说经常会生成一种类似一些,重复的啊一些样本啊。
啊不to,而不会去生成一些多样性的这些样本,那还有一个问题,其实就是啊我general的这个game,无法去提供一个比较好的,这么一个度量的这么一个指标,就是告诉我什么时候我这个生成器啊。
或者这个判别器啊,训练的是不是够好了,就是在原始的这个概里面呢,我是没有这么一个美metric,所以我只能不断的去打印出来,我的那个generator,我人人工的看a什么时候。
我的这个啊生成器生成的是不错的,这个data只能这样去干,有没有一个很好的这样一个度量,度量的这么一个指标,所以呢为了应对刚才说的那几点的问题啊,我们有这么几个啊,训练干的这么一个技巧吧。
啊我们来分享一下,第一个是说我们称之为叫feature match,什么叫feature matching,feature match,其实说的是这样一件事情,就是我是说我要把啊,我在这个训练的时候啊。
我不仅是要把这个呃,这个刚才我那个训练的啊损失啊,或者目标函数把它给写下来,我其实还是希望有一个什么呢,有一个更加多维的这么一个维度来进行啊,real date和这个fake data的这么一个表征。
比如说什么呢,比如说我希望能够把啊,数据里面的一些的mean或者medium啊,这些统计信息加进来,就不仅只是学纯数据的一些东西,就是纯pixel,比如说拿这个pixel啊为例,就是拿数据。
拿那个图像为例,不仅是pixel的这么一个最后的这么一个情况,我还是希望能够有这么一些数据分布的,这些信息,所以这个称之为叫feature mach。
还有呢叫这个mini batch的这个discrimination啊,这个意思就是我在衡量某一个点的时候,某一个data的时候判断的时候,我希望能够去看,不仅是这个单一的这样一个data。
我希望能够看这个batch里面其他的啊,比如说近邻的这个data一起来判断诶,看这个data到底是不是一个real或者fake data,然后还有一个,trick。
是说用这个historical的这样一个average,这个是什么意思呢,这个很简单理解,就是说你在训练的这样一个过程中啊,你会把那些参数的历史的信息给融合进来,这个的意思其实就是说。
当你某一个这个啊参数进行突然跳变的时,候,你要给他一些惩罚,就是你希望你的这样一个啊,进行优化和变动的时候,你的这个model的参数还是能够平滑地进行一,次,进行这个啊,就比如说优化的啊。
不要去进行一些节约性,跳变性的一些动作,这个很有可能是一个noise,对不对,还有就是我们用到一些啊smoothing的啊,这个技巧smooing的意思其实就是,很多时候其实是用来cnn我们的控。
控制过拟合的一些trick,这个意思其实很简单,就是比如说你的编码是100,对不对,然后因为你这个时候,你这个称之为是一个hard encoding嘛,是一个hard的形式,那hard形式很容易。
就是把所有的集体的信息分量,都集中在这个维度上了,那这个soft那个smooing的意思,其实就是一个更加soft的表达,我就把他的编码形式会写成0。9,然后接下来不是我还是要把它。
有一个这个加起来为一嘛,还是一个概率分布,那接下来比如说这三都是,它是接下来的0。1的这么一个平均,比如说都是0。030。033,0。033啊,这么一个编码的形式就叫one啊。
这个label smooth啊,这个virtu virtual虚的虚拟的这么一个b n啊,稍微有点复杂,这就不用说了啊,暂时略过了,然后还有两个trick是这样的,就是其中一个是加noise。
什么叫加noise呢,这里一个很好的直观的解释,就是刚才我们不是说,p小g x和p小i x,是高为流行分布中的两个啊,低维的这么一个支撑集嘛,然后正因为此,他俩没有交交叠,对不对,很难去有交叠。
所以它的这么一个啊js distance啊,会比较大啊,会可能是无穷大,那这个时候你无法提供良好的这么一个,梯度的信息去优化d和g,那这个noise,其实就是,你要把刚才的那个p小gx和p小x。
把它的这么一个分布往大了去去挪,如果加了更多的noise,他俩去去joint,他俩有交集的这个可能性就增加了,这个是加noise的这么一个,本质的这么一个原因,还有一个是说用更好的这个matrix啊。
后面其实我们就要讲这一点,就是我们会有一个we game,去表征一个更好的这么一个呃,这个这个啊evaluation的这么一个metric,所以接下来我们来看什么是we game干的。
全称是这个vs的这么一个生成式对抗网络,为什么叫这个呢,一会儿我们可以看到啊,was suing的这么一个东东,其实是一个距离啊,他提了一个新的这么一个距离,叫啊w distance。
那提这么一个距离的本质是产生的,这样一个源头是什么呢,其实就是在这,当px的形态是这个情况的时候啊,q4 个是这个时候的时候,那大家想p那个kl的这样一个距离,或者js的这样一个距离。
是不是其实是一个正无穷,因为他俩没有交集嘛,对不对,他俩没有交集,一个是这样的一个分布,还有一个是这样,这样这样这样这样一个分布,他俩没有交集,那这个wasisting的这样一个distance。
是说它的这样一个距离,要来得更加的平滑和稳定,它会提供一个距离就这么简单,所以当某一个这个啊啊,数据分布不是不为零,另外一个数据分布为零的时候,在kl distance和jazz distance。
它其实是有问题的,而对于这个w distance呢是没有这个问题的,这就是啊啊这张图其实就图解了,刚才这样一件意思就是当我的p x这样,然后不断的q x越来越往右边靠。
然后这两个之间的这样一个distance,是这个啊会有问题,对不对,因为随着你的这个距离往右边打,那你的这么一个,就是它的这样一个distance要越来越大,而当这个的情况下,我们的这个啊w game。
也就是刚才我们想说的这个新的一个干,跟这个original game,那它的这么一个优势就体现出来了,这张图大家啊现在看的不是特别清楚,因为它其实只是一个导引了。
就是告诉大家we game有一个很好的性质,是什么意思呢,就是当你的这个看这根线是discriminator,对红颜色这根线,这个时候当左边是这个real data,看蓝颜色这根线。
然后右边这根线是你的啊,fake data,那如果对于这个情况下,是不是左边这个东东你都是比较高的值,然后对于右边这个数据分布,你都是比较低的值,这个时候你的discriminator。
也就是你的判别器是比较好的情况下,很牛逼的情况下,对不对,然后对于之前的那些data也说,对于啊之前的那个game,那你的这样一个gradients,其实是几乎是没有的啊,对于新的这么一个game。
就是w ga,我还是会有一些它的这样一个梯度,来指示你进行进一步的训练,这个就是它的这么一个优势啊,这是一个导引了,我们一会就可以看一下它的这样一个,为什么可以做到这一点,好,先来介绍一下。
刚才说的这个wasted的这么一个距离,w distance什么是这个东西,w distance又称为什么呢,em distance,em distance是什么意思呢。
叫a mover distance,其实就叫做推土机的这么一个距离好,这个听上去会比较麻烦啊,听上去比较这个这个未必是不是很好理解,对不对,我们来看一下什么叫推土机距离呢,其实说白了就是这个意思。
当左边这张图它是一个数据分布的时候,比如说它是一个砖对吧,几块砖,然后我要把它搬到右边的这个形态的时候,请问这个时候,我移动的最小的这样一个能量是什么,或者说我移动的最小的这个砖块是什么。
千万别忘了我移动的时候,我要把距离算上去,对不对,举个例子,当box一从这个位置挪到这个位置的时候,我需要多少份能量,消耗多少份能量,那它的距离是不是从1~7了,其实它移到了这六个距离,对不对。
所以假设每一个距离,它都是单位一的这么一个能量消耗的话,我把box一挪了这样一个位置,我消耗的这么一个能量就是六对吧,所以我可以统计出来什么呢,统计出来把这里面的每一个元素。
每一个box都移动到右边的这个啊,每一个位置的时候,它都会有一个能量的这么一个消耗,对不对,所以我可以去统计出不同的这个移动的,这么一个策略对吧,这是我可以做的这件事情,em distance是什么呢。
em distance是说我所有这些策略里,面移动的这样一个消耗能量,里面中最小的那个部分是什么,这个就叫做worth啊,那个推土机距离,听明白了,到另外一个数据分布啊,或者另外一堆图。
它当中消耗的这部分的能量最小是什么,这个就是wasim的这样一个距离,所以千万不要忘了它的这一个东东,一定是一个最小的这样一个能量消耗,比如说啊同样是这两个东东对吧,这两个东东实现的这两个东东。
我要把它变成虚线,这两个东东一个是左边这个东西移过来,一个右边这个东西移过来,一个消耗两个,当然你也可以这个东西到这儿对吧,这个东西到这儿啊,你消耗六个,那这个时候你的distance是多,少是二。
好我们把刚才的这么一个概念性的东东,用数学形式的表达式写出来,就在这儿了,这两个东西的这么挪过来的这么一个距离,就是这样一个表达,然后取什么呢,这里面取p i x和p gx的,这样一个联合分布。
ok取什么呢,取它的下确界,这是刚才把我们说的那个东东,翻译成数学形式,就这么简单,刚才这个东西啊,如果是这个啊,有的这么一个一个一个权重的话,我要把它给放上来,对不对,还有一个权重,所以总而言之。
这里我们强调是最小的那样一个能量消耗,那kl距离或者js距离,和刚才我们说的这样一个w距离,有一些什么样的区别呢,我们举一个例子,大家都能看明白了,我们假设什么呢,我们假设有两个数据分布,p和q。
其中红颜色的数据分布是p,而蓝颜色的数据分布是q,我们来看一下红颜色数据分布是什么,还有两个维度,x轴为零,而y呢是0~1的这么一个uniform的分布,而x呢是啊啊啊。
对于那个qq的这样一个数据分布呢,x是theta,然后这个西塔是01的这样一个区间,然后y是01的这么一个,uniform的这么一个分布好,它画出来就两个蓝东东,对不对,好我们考虑一个情况。
什么情况呢,当sa不为零的情况下,当c它不为零的情况下,你把kl distance和js distance写一下,用他的这么一个概念啊,用它的这么一个定义带进去对吧,带进去是啥,p和q的一个答啊。
kl distance,那是不是px乘上log px除上q x,对吧,你这个时候x是为零的嘛,所以这个时候正无穷正无穷,而对于p w p q呢,我的距离是多少,为什么是sa,因为这两个东西相差一个c。
我只要移动sa的距离就ok了对吧,而当c等于零的时候呢,那当然kl和js the distance都为零了,因为这两个数据分布完全重合了,对不对啊,这个时候我的w的distance也是为零。
其实也就是sa,所以通过这么一个例子,大家就会看出,我w distance要来的比js和ko的这样一个,distance来的更加的稳定和平滑,对不对,不会不会带来这些啊梯度啊,然后啊又是这个啊无穷啦。
这样的话对于我的这样一个训练,是有比较难度啊,有难度的这个一个考验嘛,刚才我们说了,在原始的这样一个general的这么一,个干的情况下,我的loss函数其实等同于最小。
它其实就等同于我的那个js tesc,对不对,所以这个时候我们的w distance啊,更加的smooth,这个就是啊我们跟大家说的推土机距离,em distance好。
接下来我们要把它放到get里面去用,对不对,ok我们看它怎么进行融合的,刚才我们说什么呢,我们说distance,刚才这两个东东是什么呢,是这样的一个定义,这个定义对,我可以把它用一个这个数学定理。
把它做一个数学上的这样一个变化,这个数学定理比较复杂,大家先不用管它啊,因为他其实在那个论文里面啊,花了一些篇幅去进行论证的,但是这里我们不是讲纯数学的东西,对不对,也不是讲理论的这样一个计算。
我们先讲一个结论,就是说他用了这么一个性质啊,队友的这么一个性质,把刚才的这么一个,把刚才的这么一个啊distance呢,写成了这么一个东东,我们都知道所谓的duality就是对偶性质。
其实就是他的这么一个对偶性嘛,就是说最小把它切到这个最大,然后minimize maximize,然后下学界这个时候变成一个上学界,对不对,然后变成什么样的一个情况呢。
其实是变成这个fx从这个pr里面取,以及这个x是从pgx里面取的,这么一个形式,其中f的这样一个l范数是小于等于k的,其实这个不叫这个这个,这个这个f的l的范数小于等于k,这句话的意思其实就是要什么呢。
要这个函数保持一个叫k liberties的,这么一个连续性,这个说起来可能又有点复杂了,对不对,就是又是一个什么数学的一个东东,没错是一个东东啊,但是大家先可以啊,这么理解。
就是所谓的这么一个数学的这个性质,其实就是这个数学的公式的定义,在这里看起来比较复杂,对不对啊,你这么去理解就ok了,姜文老师尝试帮你去解析一下,这个东西的东东,你把它变一下什么呢,变一下是不是。
其实就是这玩意儿,应该把这个东西挪过来嘛,啊挪过来其实说白了是什么呢,说白了其实就是fx的这样一个东东,的这样一个梯度的这样一个局部的变化,不应该是一个没有限制的动作,其实说白了就是fx的这样一个梯度。
它的这样一个局部的这么一个啊,变化的幅度是属于有限的啊,像小于等于k g k是多少,我不关心,只要是有一个限制就ok了,这个就是k libertus的这么一个,延续性的这么一个性质,所以总而言之。
他把刚才那个东东写成了这个东西,这个东西,那大家想一想这个东西我怎么去优化,就是我怎么把它从一个数学的东东,深度深度神经网络的这么一个角度去理解,去进行建模,那其实我是不是在做这样一件事情。
结果是不是可以把它转换成这么一个东东,就是什么呢,就是ok你要找我的上学界,对不对,我先找一个最大的这么一个值,最大的这个值哪来的呢,最大的这个东东我是来自来自于一组参数,这个参数是什么呢。
这个参数是由一个神经网络,是建模的一个东东,有同学会说,你用一个是一组神经网络建模的,去拟合刚才那个东东,o不ok,首先你要明白一个神经网络的拟合的能力,是非常非常强大的对吧。
我们都知道有一个这个推论和定理,就是用一个啊一层的这样一个mlp,可以和任意一个这样一个函数,然后这个时候呢,因为你要去得到它的这么一个上学键嘛,但很有可能你得到的这个结果不一定是。
但是通过你神经网络的这样一个建模,你可以取到一个逼近的那样一个结果对吧,其实你可以去,得到一个非常非常好的这么一个近似的结,果,可能差0。0001,那你so what呢,怎么样呢,对吧啊。
我其实从这个计算机的角度来说,我不一定要找到那个最精准的那个值,我要找到一个可用的这么一个值就ok了,这个其实就是计算机的同学的思想,和数学的思想是完全是不一样,我们是实用性质的嘛。
所以说白了我可以通过啊,这个神经网络的这么一个套路,去把它给建模和实现了,那千万不要忘了,刚才我们是不是有一个限制,这个限制是说,我要满足这样一个函数性质,对不对,k liptures这么一个函数。
限制这个函数性质的本身,是不是说我这个f的这样一个函数,其实就是我的神经网络对吧,因为我是用神经网络来进行拟合嘛,我要满足他的这么一个kk的这样一个,libertus这么一个连续性。
也就是我应该是它是有一个有界的嘛,就是它的这样一个啊变动的这么一个幅度,不是一个无穷的,但是这个在神经网络怎么做呢,是不是啊,你们的这个n o p的老师告诉大家,我为了防止,什么呢,防止我的梯度爆炸。
我要对我的参数w进行一个什么样的操作,大家还记得吗,ok的老师怎么跟大家讲的,来回忆一下我在里面,我要防止我的梯度爆炸,我会做一件什么样的事情,b n,对b n它是这跟他没关系嘛。
其实我对那个参数会有一个,什么样的一个约束吗,哎记得吗,是不是有一个clip的操作,大家想想看,是不是有一个leeping的操作,这是为了防止我的梯度的爆炸,我每一次呢这个w梯度,比如说超过很大的时候。
我要强行把它放到,比如说负的0。1到正的0。1,负的0。1到正的0。101之间,大家还记得这个操作吗,在安安里面,有印象吗,大家,有没有印象有没有印象,就是这是i n里面一个很重要的一个。
一个一个一个一个一个trick,大家怎么能忘记呢,这个trick呢也会用到,就是get里面训练,为什么呢,因为我要保证什么呢,我要保证你每一次我的这样一个f,它是一个有界的这么一个变动,对不对。
那ok那f的产生的原因其实就是啊输入嘛,我只要把输入变成一个有界的一个性质啊,一就是比较小的这么一个,有界的这么一个区间,那么f我一定就会在一个有限的,这样一个区间,至于这个区间k是多少。
我们不care对不对,因为k的这样一个大小,它不会影响我梯度的变化,对不对,只是它一个绝对数字的大小,我只要它是有界就ok了,不在乎它的这样一个界是大事小,所以我通过这样一个check。
就可以去去这个把刚才的教练啊啊,k lipches,这么一个连续性给它进行保证了,所以数学上的一些啊一些条件,我在,计算机的这样一个同学们的这样一个角度,里面,看起来。
有时候其实没有那么难去去进行实现啊,其实可能就一行代码就可以进行实现了,刚才我们讲了这个w干的这样一个几,几个点,对不对,w distance啊,几个点,用这个啊,ing啊等等这些东西去对它进行建模。
那刚才看到我们的这样一个loss函数啊,这个写成这样一个形式,对不对,写成这么一个东东,这个东东大家看看我的损失函数,写成这个东东的话,这里面还有log吗,首先这里面已经没有log了,对不对。
是不是没有log了啊,都没有log,所以我有哪些点要进行改动呢,就是盖里面原来哪些点有没有感动呢啊,ok其实他只要改动四个地方其实就ok了,就是说w game这个东西的引入,对于原始的那个干。
几乎不要去改动很多的东西,但是我只要去动几个点,我就能够实现wegame,第一个点,就是我把sk mory的那个激活函数给挪走,因为原来的那个东东,我是要去判断dx这个东西对不对。
我要去判断零和一的这么一个区间,我要去给real data判一对,fake data判零,但在we game这件事情里面来说,我其实是一个回归的任务,对不对,刚才我们说的是一个回归任务。
所以我sigma就不需要有了,第二我要对我的这样一个gradient,参数的这样一个update进行一个creeping的操作,我每次要对他去cp,为了满足刚才的k liptures,这样一个连续性。
然后在这个呃w k里面呢,我还不用去取log,就是刚才我们啊对,说到那个点对吧,我们不需要取log,然后接下来是一个一个经验上的一个,trick,就是说在w盖里面。
我通常会用i m s proper的这么一个,optimizer去训练,会来的更加的稳定,就这么几点,这里跟大家在这个补充一下啊,前面我们是不是提到w这件事情。
要来的比general的game来的更加的好的一个点,就是它有一个指示器,这个指示器是说我的这个lost的这样一个made,a metric,是可以告诉我训练的是不是足够好,我就不用。
每次都是通过可视化的那个生成器,每一次去可说完那个深圳去看,哎,我这个数据到底是不是训练的好了啊,不用去看这一点,因为我的那个distance就是w那个distance,其实是一个天然的这样一个指示器。
告诉我pg和pr的这么一个分布,对不对,所以这个distance谁可以告诉我,训练的好还是不好,所以我们把w干在每一个loss的这样一个,不同的这个点去可视化来,我会发现随着我loss的下降越来越小。
我的图像生成的质量就越来越高,所以这个点是有保证的,就符合刚才我们说的这样一个直觉,这个就是刚才说的这件事情,那在w盖里面呢,还有一个非常什么呢,非常这个比较新出来的这么一个东东叫gp。
叫gradient penalty,来更好地帮助我的we game去进行训练,这个是什么意思呢,这个很简单,这个很简单嗯,就刚才我们是说啊leeping那件事情可以帮,助我们一定程度上去满足。
刚才那个看数学条件,对不对,但是这里有一个问题,就是leeping的这样一个参数,那个c呀我可能会比较难调,尽管我可以通过cross validation,把它当做一个超餐去进行调整。
但是这里啊很多时候啊,过小和过大我都会产生一些问题嘛,这个不好调整,所以gp就是应对这样一个啊,这个难点去产生的,就是,ok,刚才我们不是说,我们要满足k lipches的这么一个条件,那ok啊。
那我就让你满足one lipches,这样一个条件呗,那满足one liptures这样的条件,就是我希望什么呢,我希望每一次gradient的这样一个,nm就是它的这样一个梯度,的这样一个大小啊。
尽可能的这个接近于一,这样的话呢,我就会把那些梯度远离一的地方,我给他进行一次惩罚就ok了,这个就是gradient penalty的这么一个想法,就是我把它控制在这个一这么一个附近。
这是这个gradient penalty,的这么一个过程啊,其实没有什么太大的改变啊,因为啊之前全都是我们之前要做的事情,然后在这里呢,其实就添加了这么一个东东啊,有了这么一个jz,有了这么一个dx。
我生成一个新的这一个x一撇,然后看这个东东,把这个东西加到我的loss函数里面去,这个就是gradient penalty要做的事情,最后啊最后一页没放出来,对不对,大家自己看一下吧,就是我们的这个啊。
gradient的这样一个clipping,以及加上这个gp带来的更加的好好,今天就是跟大家讲的gag和we game,然后在这个这节课刚开始的时候,是不是要跟大家说了,有时间给大家出一下这个。
给大家看一下这个真题,对不对,我们有时候面试的这样一个真题,尽管抄了一些时间,但是我觉得今天这个七夕节嘛,我想在上课的同学也不着急,对不对,你们也不着急,这个去去去干嘛,所以我们来看一下啊。
送大家一份七夕大礼,大家看一下这个题,再读一下,花一分钟时间读一下这个题干,选什么,然后把答案打在群里面,其他同学呢,有同学就说选a,有没有不同同学的意见,有同学选b,到底是a还是b呢,真理就是掌握在。
多数人的手中答案就是b对吧,肯定是b嘛对吧,因为你要加了这个gradient penalty gp,其实就是说我会把我的这样一个gradient,nm去放到什么呢,放到这个呃跟一相关的这么一个呃。
呃呃就是接近的这么一个地方,那这里又说是一个log scale,对,log一是多少对吧,所以是b是蓝色这根线好,再看一道题,选什么,江夏陈仓同学说选a,其他同学呢,有没有不同的意见,答案就是a。
为什么是a呢,大家想想看,它,其实这道题让你去判断leeping和gradient,penalty的两个位置的这么一个形状的分布,对不对,那大家要知道creeping和grading penalty。
虽然它都是在解决k lptures,连续性的这样一个问题,但是大家想想看,一个是用强制性的这样一个暴力的clip,而另外一个是用什么呢,是用一个更加soft的这样一个就是这个啊。
这个你可以理解为是一个这个回归的,这么一个惩罚的这么一个情况啊,这么一个lost的情况,那当然通过penalty的gradient penal形式,penalty的这么一个情况的这样一个分布。
要来得更加的平滑,对不对,因为你clip的这个情况,通常来说,可能可能,什么,超过0。01,和超过-0。01的概率会比较大嘛,对不对,因为你啊整个数据分布,你想想看处于-零点点,负的0。01和正的0。
01,这样的一个概率,要来的,比处于其他的这样一个数据段的,这样一个概率要来的更加的小嘛,所以一旦大于0。01,和小于负的0。01的数据出现了,那我就会强制把它clip,不到0。01和负的0。01嘛。
所以在正-0。01的这个情况下,我的数据分布会比较高,啊就是这个意思,然后另外一个啊penalty的情况呢,会相对来说更加的这样一个平滑,所以理解clipping和理解gp这两个题目。
就帮助大家这个很直观的去get到,去检测大家能不能理解,这个w跟这样一个核心的这么一个,约束条件的这么一个实现,好了,今天就是这个啊,今天的这个课程的部分的内容,如果没记错的话,我应该是就是最后一次。
这应该是最后一次线上的这么一个课程吧,就是跟大家上,当然可能还有一次线下的课程,肯定要后面跟大家还有一次课程,然后那今天就这样咯,就祝大家这个节日快乐好吗。
七月在线-深度学习集训营 第三期[2022] - P6:在线视频:03-深度学习在推荐系统里的应用场景 - 程序员技术手札 - BV1gW4y1x7j7
好我们开始。
嗯主要分为三部分,然后呢我们再看一下就是推荐特有的网络结构,也就是交叉交叉层,这个呢是专门为推荐系统这个问题定制的,网络结构也是这个就是相当于推荐系统里面原创的吧,然后这里面这里面讲到的大部分的模型呢。
都可以在下面这个这个综述里面找到,所以大家感兴趣的话,可以去看一下这篇综述,写得非常全,也就是d n c r n,还有我们其实现在不太常用的这个自编码器,auto encoder。
怎么样把这把这些模型用到推荐系统中呢,我们来依次来看一下,首先叫叫蔫儿蔫儿这个协同过滤的这样一个模型,它其实就是直接把推进的数据放到一个dna里面来去去而已,因为在推荐里面呢,我们的数据是分为两部分。
就是至少至少分为两部分,一部分是用户端的特征数据,另外一部分是就是物品端的特征,所以呢在这个模型里再输入层有用户端的特征和物品端的特征。
然后他in binding之后变成了用户的特征向量和物品的特征向量之后,他们就接一个多层的全连接的神经网络,所以这个这个模型没有任何改变,只是特征换成了推荐系统里面的数据的特征,只是这样而已。
这是最直接最简单的一个应用方式,那这个简单的模型呢嗯就是跟原来原来推荐系统里面,就是早些年推荐系统里面常用的l r模型结合在一起呢,就变成了一个非常著名的模型。
叫wide and deep learning,这个大家应该都知道,它的模型思想非常简单,再一看像搭积木一样,左边一个右边吧,右边就是我刚刚提到的l那个那个dna模型,把特征分别ebending之后。
然后跑一个全连接的d,然后左边呢是把特征直接连到了输出,所以如果只看左边的话,实际上相当于一个什么模型,左边这部分呢实际上相当于一个l r模型对吧。
所以这个y and deep它就是l r加上一个一个那个丁,所以这个模型结构非常简单,但是呢它又特别就是在实际的效果比较好,在工业界呢特别喜欢这样的模型,就是实现起来特别简单。
但是呢效果就是效果比较明显的模型,这样的模型在工业界里面就会比较就传的比较比较广,嗯这个模型大家之前应该学过呃,不知道现在还有没有问题,以及他为什么用这样的网络结构来解决这个推荐推荐领域的问题。
然后关于它的就是它的效果,我们在这在这个课上不做讨论,因为你知道那个实验效果是跟数据有关的,这些模型之间这个效果是我们是不好比较的,它有不同的这个应用场景。
所以呢我们主要是讨论这个模型的结构和它的就是它的出发点嗯,现在这个y等def模型呢,它从结构上很简单,就是l2 加一个dna解决了什么问题呢,它其实就是分别解决了除以l2 和纯dna的就是缺点。
因为l r模型呢它比较简单,按照论文里的说法呢,l r模型是记忆性比较强,他能够记住过去用户比较明确的兴趣爱好,比如说用户说就是通过历史用户的浏览历史,发现用户特别特别不喜欢看动作片,比如说电影推荐吧。
那这个这边这个浅层的l r模型是很好的,可以对这个建模的,那如果是用这个dna模型呢,因为它经过了很多层的非线性变换,所以呢优酷的这个这这个兴趣爱好就被模糊掉了,模糊掉了,他就不会有这么强。
那反过来dna的好处呢,它是可以发现用户其他的潜在兴趣爱好,所以从模型的性能的角度,就是它用过通过简单的并行那个模型,然后分别解决了l2 模型,记忆性强,但是它的泛化性能不太好,因为它就偏向于记忆。
你看到的结果并不能去很好的建模用户一些潜在的兴趣爱好,但是对一些用户比较明显的,然后就是已经明确的兴兴趣爱好点呢,它会把它模糊掉,所以两个模型结合在一起呢,从直观上来讲,它会有一些比较好的效果。
啊这是dna的一个呃一个呃实际应用,接下来我们看一下gcn做的模型,大家看左边呢,这个就是就是看那个左边这个图的左半边,它是用户的特征,经过一个声音之后,因为这这里这里的推荐场景应该是文本推荐。
然后用户的特征跟物品的特征呢,原始特征都是文本,然后这两两部分文本通过一个dna,不是dcn建模之后得到一个输出的特征向量,然后这边物品也通过一个声音得到一个隐藏的向量。
这两个引擎向上呢我们可以做一个相似度计算,比如说这个内积最终得到一个预测结果,所以这也是一个比较直接的方式,相当于我把sn呢用做了提取领特征的一个模型,对物品对用户和物品通过分别用cn来提取与特征。
然后呢在输出层对这两个隐藏呢做一个相似度的计算,能得出我们的预测值,右边呢是一个类似的图,它只不过是换了一种画法,强调的也是对于物品端,我们做做成一个影像量,这边就是一个标准c。
然后这里的u呢是用户的隐藏的隐藏的向量,v是物品的隐藏的向量,然后呢这个图跟这个稍微就是不一样的地方在哪呢,是他得出的这个就是物品端的这个隐藏的向量呢,它还放进了其他的一些特征,放进其他一些特征。
就是其他的物品特征,两个特征一起,最终得出用户的这个隐藏向量,这个这个得到这一步的方法,你可以是简单的相加求平均,大家看这个cn的模型有没有问题,好接下来我们看一下g2 n的做法,i呢是一个序列模型。
这个图好像是不太清晰,刚才这个图不太清晰,然后这个图应该应该应该应该好多了,好我们继续吧,i呢比如说常用的l s tm它是一个序列模型,那我们把怎样把这个序列模型用在推荐里面呢。
也有一个非常简单直接的用法,因为我们在做这种rna的训练的时候呢,我的训练样本同样是很很很多篇文章或者是很多个句子,然后一个句子是一个训练样本,然后句子呢是由词的序列组成的。
所以呢句词是句子里面的组成元素,那我怎么样把推荐场景用到这个里面来呢,我把用户看成句子,用户的购买记录或者是观看记录,形成了一个序列,看作句子里面的词,就是用户是句子,然后呢物品是词。
每个用户都有一个我购买过的物品序列,或者说我看电影的时候观看的电影序列对吧,那天然呢就构成了一个符合cn的训练样本,那i能够做什么呢,能够做根据之前的序列去预测下一个词加一个词,那相对应的推荐里面。
我就是根据用户之前的购买历史预测下一个他要购买的物品对吧,所以说只不过把东西换了模型,一一个一个一和一个一个代码都不用改,就可以拿来跑推荐了,对吧,大家看这个有没有问题。
就是我现在讲的都是就是最直接最直接的应用,就是那个推荐系统的那个数据上,所以说rm本来看起来跟推荐没有关系,但是你这样一处理完之后呢,它天然的形成了预预测下一个用户,下一个想购买物品的这样一个模型。
这一看有没有问题,也就是说在这个图里面呢,我每个节点是一个物品,就是他先买了这个,再买这个再买这个,然后呢我最终要预测他下一个要买什么东西,然后最后呢我们再看来看一下自编码器嗯,所以我们简单过一下。
都被一个就是就是编码器和解码的这样一个结构来代替了,嗯所以像这种原始的这个自编码器的结构呢就有点有点落伍了,我们简单看一下它的思想是什么,大家看它的输入是用户的评分,这是一个稀疏向量。
然后呢这每一个点代表的用户对这个物品的评分,然后比如说他对第一个物品评分是五分,然后第二个没有评分,第三个评分是三分,然后这样构成了一个用户的原始向量。
自编码器的作用呢是通过一个非线性变换得到一个隐藏层,隐藏层呢再通过非线性变换得到一个输出层,我自编码器想要做做到的效果就是让我的输出层和隐藏层尽量的相似,也就是说我能够在这个隐藏层节点里面。
少量的隐藏层节点里面尽量的保留住我,原始数据的信息,我的原始数据呢就是用户的整个评分历史,用户的评分历史其实就代表了用户的兴趣爱好,然后通过中间层的我可以认为是embedding或者是压缩或者是降维。
得到一个这个特征向量,再通过输出存档,也才那个非呃那个变换呢得到了这样一个输出,注意啊,输出的维度跟输入的维度是一样的,就是输出,比如说我有1万个物品,那我这输入是一个1万维的系数向量。
那输出呢也是一个以万为的系数向量,就是他要跟它一对应,然后使这两个两个向量呢最近的误差极小化,然后拿这个模型拿这个东西来做预测呢,怎么预测呢,因为你输入的时候,这些这些特征有些是缺失的。
就是说这些白色的是用户没有看过的,没有买过的物品,那当然我输出的时候呢是可以预测出所有所有节点值的,我输出的时候,所有节点植物都可以预测出来,所以那些未知的节点呢上面也是有直的。
所以我就可以拿这个词来作为这个我的我的预测结果,对用户做推荐,所以这是这个自编码器,就是用用单层的自编码器直接用来做推荐的这样一个方法,然后我们把它扩展一下呢,可以得到右边的这个模型,右边的这个模型嗯。
他对用户和物品分别做了自编码,嗯我最左边这个这个自编码器里面的数据呢,我只用到了用户的评分数据,用户的其他特征我都没有用到,物品的其他特征我也没有用到,比如说用户的年龄,性别标签,这些特征我都没有用到。
那在这个右边这个图里面呢,我相当于把用用户的所有特征,把用户特征画画在右边,把用户的所有特征做一个自编码,然后就是说输入是用户的原所有原始特征,输出呢也是对应用户的原始特征。
中间得到一个降维的隐藏的向量,然后物品呢也一样,这样得到两个用户和物品等等隐身的向量,然后我们对这两个隐藏的限量呢做一个相似度计算,比如说还是刚才说的那几,就会得到我最终的预测结果。
这里举的两个例子呢,我是是呃,在在那个就是就是在推荐里面,把澳洲影通通eqa完全的作为提取特征的工具,完全的作为提取特征的工具,跟刚才的那个图是是对应的。
大家看这个图里面,我是把sn作为提取物品短语特征的工具,那在这个图里面呢,这是用户端的隐特征优势,它一般都代表用户端的隐特征,v呢是物品端的隐特征,这个物品单的一当中呢,这篇文章讲的是文本推荐。
所以这是相当于是我今天要要这个要推荐的文本的隐藏的限量,他怎么得到这个隐藏的限量呢,是通过一个自编码器来得到这个他这里的x2 分之l就是这个文本的,通过自编码器得到了隐藏的限量。
所以说相当于它纯粹是把这个作为提取特征的工具,然后把它放到推荐的模型里面去,大家看一下到目前为止有没有问题,我们来总结一下嗯,知目前为止提到的模型,刚才提到的模型呢。
在模型层面呢基本上是没有什么创新和大的改动,然后稍也就是稍微的简单组合一下,比如说我刚才说的r n rn这个用来做推荐,我我连一个一行代码都不用改,我只要把我的数据处理成rn需要的格式就可以了。
比如说用户当做句子,然后呢用户看过的这个就是买过的物品系这个序列当做一个词的序列,直接放进去就好了,然后呢第第一个举的例子,dna的例子也一样,也是我直接把用户和物品单的特征都处理好了之后,直接倒。
直接接一个深,就是全连接的dna就可以了,没有任何改变,然后这是第一就是我刚才我们看到的模型的第一种,第一种用法,第二种用法呢就把它当做一个提取特征的工具,就纯粹把它当作输入层提取特征的工具。
继续的特征之后呢,我我推荐还有用原来用什么模型,还有什么模型,比如说刚才c和那个自编码器的例子,cn auto encoder,我都直接拿来作为提取特征的工具。
然后接下来呢我们看一下今天比较就是最核心的部分,就是这个叫交叉层,就是为推荐系统专门定制的网络结构,可以说是专门为解决推荐系统的问题而产生的,在讲这个之前呢,我们必须提到一个推荐系统里面做。
就是有里程碑作用的模型,就是fm,也就是分解机,中中文的话叫分解机,嗯,我们来看一下这个呃也是挺有名的模型,叫deep fm,这个模型呢也是分为了左右两边,我们分开来看,先看右半边,从中间这画开。
可以看到右半边是什么模型,就是一个点跟刚才wide and deep里面的右半边是一模一样的,就是把用户用户和电影的,比如就是物品的特征分别处理完之后呢,接到一个全连接的网络里面去。
所以右半边呢单看就是一个电影,那左半边呢就是它不一样的地方了,大家看一下左半边的结构,输入层是稀疏的用户,用户和物品原始特征,他这边呢是分域对它进行做banon,这个玉呢可以是按就是特征。
比如说就是呃年龄做一个预性别,作为一个玉,也可以把所有用户的特征作为一个域,然后物品端的特征作为一个域,这个都没有关系,就是把相同类别的特征呢放在一起,然后做了embedding之后呢。
得到一个隐藏的向量诶,这里得到一个隐藏的向量,这里得到一个隐藏的向量之后就跟右边分到分成左,分到左边去,左边在干什么呢,大家看它定义了一个这个操作,这个差上面就行了,叫做做内积,做内基。
然后呢你看这个做内积的单元呢,它连接的是第一个隐藏的向量和第二个隐藏的向量,这个虚线连接的,然后第二个单元呢它连接的是第一个和第三个隐藏的向量,所以呢这里面所有的乘号。
这个单元呢它是相当于对下面的这个隐藏的向量两两做内积,两两做内积,两两都做做内积得到的值之后呢,我们再做一个相加,两两内积做相加,这个就是这个这个运算呢其实就是fm的核心运算,我们看一下。
下面是fm的这个模型的,就是它的数学定义,一个呃常数项加一个线性项啊,到这里,如果如果没有其他的话,这就是一个l2 ,这是一个l2 的定义,但是fm多了一个,二次项二四项呢是一个交,它是一个交叉项。
它对于特征两两之间做了交叉,交叉的这个权重值呢,它定义成了这两个特征,引特征向量的就是引向量的内积,零向量的内积,所以这是如果下面是用公式来表达对这个式子的。
就是说大家看这个对所有的这个内积求和的这一项,如果这是用公式表达的话呢,那这里就是用一个网络来表达,它们是完全一样的含义,完全一样的含义,我是只是用一个图来表示两两做内积而已。
所以它定义了这样一个做内积的交叉层,所以说像tina flow里面可能都就他不会有这样一个层,这就是api函数,所以你要自己去写,大家看看这个图有没有问题,然后呢呃如果没有的话呢。
大家可以想一下这个加号代表什么意思,这个加号这个单元注意连接的是输入层,它是用这个这个实现连接的输入层单元,这个加号这个单元其实可以跟下面的公式里面,某某一个部分对对应起来,得想想是哪个部分。
嗯对这个这个求这个加号单元连接的部分,这个这一部分组合在一起呢,其实对应的就是这个线形象对吧,现象很简单,就是把输入乘上一个权重加起来,这是输入值,然后这个边上是权重,然后乘起来加在一起。
不就这个单元吗,为啥要embedding之前的呀,因为现印象是直接跟输入输,就是原始的输入做成鸡蛋,不是它不是模仿残差网络,它就是一个线性项,就是模型定义而已。
所以啊这里这里这个因因为因为常数项应该都一般都会被省掉,所以我我我我左边的这个网络它就完完全全等价于这个式子,所以它为什么叫deep fm呢,它其实叫deep fm,但是它只是一个只是一个搭积木。
就是呃更就是更深的组合在一起,它只是左边一个fm,右边一个电音,然后最终把把两部分的结果再求个和而已,所以大家再理解一下,这个式子跟这个图里面的左半边网络是完全等价的,完全等价的,好没有问题的话呢。
我们再接着看,而有了这样一个交叉层之后呢,我们就可以做别的事情了,就是我们可以做排列组合的,刚才这个deep fm呢是把这个fm点左右拼接在一起,那我可可可不可以上下拼接呢,答案是可以。
这就是一个上下拼接的模型,怎么样上下拼接呢,用户就这这也是用户的特征,然后物品的特征,然后我做直接先来牵连听,先连dna之后呢,经过很多层的非线性变换,得得到一个上层的隐藏的向量。
大家看这上面呢也是一个两两做内积的过程,然后两者做内积之后呢,我们再得到一个组合的输出,所以这个模型可以认为是上面是一个fm,然后下面是一个电影,就相当于把这两个积木呢做了一个上下的组合。
一个是左右的组合,一个上下的组合,那从直观上来讲呢,它们分别是就是建模了,有有有什么样的一个不同的建模的呃,就是表达能力呢,从直观上来讲,我们最终做内积的引擎的向量,因为内积其实就是一个相似度计算。
我做内机的隐藏的限量代表了用户的兴趣爱好,我用户兴趣爱好都相当于ebending到这个向量里去了,那如果是我们之前的fm呢,在得到隐藏的向量的之后之后,我实际上是用了一个线性变换。
那线性变换呢可能表达的就比较弱,我最终得到了用户的兴趣爱好呢就比较直白,比较直白的意思,我举个例子,就是说我就知道用户啊喜欢多有多喜欢看动作片,然后有的喜欢看喜剧片这种比较直白的爱好。
但是如果又会有一些就是特别复杂的兴趣爱好的描述呢,复杂的比如说啊我喜欢看他看某某人演的,然后又什么导演是谁,然后然后再在什么时间段放的,然后还跟谁一起看呢,就这种复杂的东西就是复杂的兴趣爱好呢。
在这样一个线性变化里面,你可能表达不出来,所以就从直观上呢,我先用一些非线性变换,然后得到一个能够表达这个用户潜在的隐藏的兴趣爱好的隐藏,的向量就特别浅淡,一点都不直白,你用就是潜在到。
你用用话语描述不出来的这样一个这样一个一个一个点,得到这样一个形成的向量之后呢,我再做这些内积运算,所以说你可以认为是直观上这个模型的,就是嗯就是它的一个建模思想,但效果呢我们就不好说了。
因为有些问题它里面就不需要去描述用户这么深层次的结构,有时候会让反而把问题告诉答了,这里的bin都不是单独训练的,像这整个模型是是合并在一起训练的,是完全合并在一起训练的,到这里之后呃。
到这里呢我们暂停一下,然后我们找一个代码来看一下,可以更更直观的理解理解这个这这个dpfm模型。
我这边有一个deep fm的实现,代码也非常非常简短。
使用test flow做的。
好我们主要来看一下他建这个网络的过程,也就是建这个图的过程,刚开始呢都是一些输入的特征了,这些都是输入特征,然后定一些权重,然后这个东西我们就先忽略掉网络结构。
从哪开始呢,从这儿开始。
我们会发现这个代码跟刚刚那个图其实是一一对应的,首先第一部分,就看代码其实没必要看太仔细,你你看那个注释就行了,就这一层呢它是做emon,然后用的是传统flow提供的这个函数。
然后这个函数呢这个具体细节我们就不在这讲了,你只要知道它做的作用就是做一个ebending,线性的ebending emin之后呢,就得到了这个embedding这个词。
这个层这个层就是呃就是就是对应的是对应的图里面的这些dancing bing。
然后,他就开始开始呃进行下面的计算了,下面呢先做了一个first order,first order,就是这个线性项对吧,大家看现印象呢,它其实就是把输入层跟那个权重做了一个乘积,然后加在一起。
然后这个有个有个drop out,这个这个可以忽略掉,跟结构没有关系,所以呢这是线性项,而这一部分就是先印象这前面两个呢都没什么,都不是都没有什么呃,就是问题都不是很关键的。
这个模型最关键的部分就是它的二次项,也就是交叉交叉层的定义,嗯这六行就是整个模型最核心的部分,它定义了deep fm的交叉层,但是呢你要你如果看这个东西的话,它跟公式里面是不是对应的,不是对应的。
因为你如果用他的flow去实现这个这个操作的话呢,你其实挺麻烦,因为tfo里面没有对应的api,大家可以想一下,我有我有我有一些向量,我要对他们两两做内积,两两做内积。
你来想用torflow api怎么实现,不好实现了对吧,那怎么办呢,其实我们有一个呃就是很好的方法,不止对于实现上,而且对于性能上都有很大的提升,这个方法呢在呃我想想。
因为大家可能不是不是专专门做推荐的,然后如果是专门想专门做推荐的话呢。
像这个fm的原始论文,那是一定要看的,是属于必读论文。
这个漏用力呢有一个最关键的推导。
推导它是把这个二四项经过一系列变换之后呢,变成了下面这个式子,看这个推导呢就是有点太细了,显得很很冗长,嗯,这里要做的事情呢就是我想得到一个就是计算这个东西既快,然后呢实现起来又方便的方法。
好我们现在不看这个式子,我来给大家打一个,我来告诉大家他在做什么。
我来告诉大家他在做什么。
因为我们石家要求的是呢是一对二四项的内积,即二四项的内积呢,其实就是很多项相乘,就是比如说我们用abc 3个向量,这abc 3个向量呢我们把它简化成一维的,所以我喜欢求的东西。
就是a b一加上bc加上ac对吧,我要求这个东西,然后呢,如果我如果拿这个式子直接写代码的话,大概要写一个双重循环,我大概要写一个双重循环去便利这些a b c之间所有可能组合对吧。
但这个东西呢它其实等于什么呀,a加b加c的平方减去a的平方,加上b的平方,加上c的平方,前面要除个1/2,前面数1/2,对这就是刚才那个式子简化到一维的情况,所以说是个非常简单的公式。
但是呢就是一个小技巧trick,但是我来写这个用这个来实现的话呢,大家看它的时间复杂,都变成了a an了,左边是n的平方,因为你要写个二重循环,但右边不用,我先把所有的值加在一起,求个平方,这是一个。
这是一个线性的,然后呢再把所有的平方加在一起,求个和,这也是线性的,然后再求个减法,所以这整个计算呢实际上是一个线性的过程,然后把它替换成向量,也是也是一样的道理。
我就不再写了。
啊大家看这个式子有没有问题,哦没有问题的话,我们回去看一下代码嗯,注意记住啊,这里是做什么,先求和,然后再求个平方减去呢,先求平方再求和。
那代码里面做了什么呢,代码里面看注释就明白他做什么了,这是先求和再求平方的部分,所以呢正好两行语句一个是求和,一个是求平方,对吧,这样就跟那个狮子完全对应起来了,然后下一部分是先求平方再求和。
一样先求平方再求和,这里就没有那种两两匹配的操作了,因为因为他的朋友在语法里面做,你做两两的,这个就是是不好写出来的,很麻烦,那这样就直接写出来了,写完之后呢,我知道把这两项做一个相减就得到我最终值了。
大家看它是一个减法操作,同时乘个0。5,因为是有二二倍的吗,我刚才是在里面稍稍写那个再除以二,就是我们要做的这个交叉项。
对这是dfm的最核心的部分,第六行六行语句写完了,然后呢它的地地图部分呢其实很简单,就是你看它就是一个循环,有多少有多少有多少层,我就加多少层的全链接,就是最简单最基础的dna,就我就不多讲了。
然后最后把这两部分加在一起,就是我们看到的deep fm模型。
大家看这块有没有问题。
好没问题的话。
我们继续,然后对fm本身没有做任何改变,大家看没有任何改变,我那个图跟下面的fm的原始的模型定义公式是完全等价的,诶这个模型呢就做了创新,就是我定义了新的交叉层,也就是相似度,我一般呢是用内积来做计算。
然后内积呢相当于把用户的引擎了向量嗯,比如说这是用户的隐藏的向量,然后呢这是物品的隐藏的向量,它们两两之间依次做做做相乘,然后相加得到一个相似度,从直观上来讲呢,大概什么意思呢,就是说这个引擎的向量。
第一个第一维度可能代表用户喜欢看动作片的程度,这个数值代表了用户喜欢看动作片的程度,然后第二那个那个我们用电影举例子,就是电影的电影的这个隐藏的向量,第一个维度可能代表了这个电影动作元素的多少。
或者说动作元素动动作元素的精彩程度,那也就意味着呢这个维度对于用户来说,电影来说,它的含义是一样的,它的语义是一样的,所以你对他们两个这两个值做相乘的时候呢,它就能代表用户潜在喜欢这个电影的程度。
然后第二个维度呢可能代表用户喜欢看喜剧片的这个程度,然后相对应的第二位在电影端呢,代表这个电影喜剧元素的多少,对吧,这就是整个做内积的它的含义,但是你会发现用户的第一为隐特征跟电影的第二位影特征之间。
是没有做交互的,对吧,就是用户喜欢看动作片的程度跟这个电影喜剧元素多少,这两个事情呢他没有做交互,这个家伙有没有用呢,不好说,对吧不好说,所以呢这个这个deep connetwork呢。
他的思想就是把用户和物品就是或者电影它的引擎声呢,他不做内机,做一个叫做外籍,相当于把这个这个特征跟这个特征呢这样相乘,这样相乘呢会得到是一个矩阵,3x3的矩阵。
这个三角矩阵会把这两个向量里面两两之间都做一个乘积,都做一个成绩,内积得到是一个数值,我把它反过来,这样就是所谓的求外积之后呢,算出来是一个矩阵,这个矩阵每一项是这个用户隐特征跟物品与特征里面。
就任意两个特征之间的两个点能匹配,匹配完了之后,因为得到的是个矩阵,因为得到这个矩阵,我们为了往后输出呢,我再把这个矩阵乘上一个一个权重向量,最终得到得到的是一个呃数数值啊,这个图是从右往左画的。
它的输出呢是这两个用户的隐特征向量跟物品的隐身向量,先做一个外机,然后再乘上一个权重,这个权重呢你可以认为是为了把他这个举证给他,就是拉平成一个向量,因为你只有拉平的一个向量才好做下面的下面的运算嘛。
好这就是这个模型最大的创新,它定义了一个这样一个就是cosplayer,他把这个两个引擎之间两两之间都做交叉,它不但这一块做到了极致,他还做到一个什么事情呢,就这一层输入和输出的结构是一样的。
大家看这一层输入是向量,输入是这两个向量,输出呢也是向量,那我就可以把这个交叉进行很多次了对吧,我交叉层的输入是一个一个向量输入,输入也是一个向量,但输出的向量呢我可以继续做用这个东西做交叉。
所以呢我这个跟dna一样,我可以把交叉层呢诶摞在一起,吴磊在一起做很多层的这样的交叉,然后右边呢它就是一个它叫definite work,它就是一个一个dna。
所以呢它就相当于是把dna和它定义的这个叫cosnet work拼在了一起,啊这个这个模型呢在模型上是有很就是相当于做的,就是呃跟之前模型比最大的一个创新。
但是实际效果呢这个模型好像效果在很多场景下效果并不好,就是我们刚才讲的,你刚才那把用户用户电影不同的影子能拿来做交叉,到底有没有含义呢,有没有意义,这个就不好说了,大家看这个模型有没有问题。
对他是一个对称的。
它是一个对称的,它是一个对称阵。
有一八是多余的,因为因为你是相乘嘛,那那那a乘b跟b乘a是一样的,像这种处理层的这种这种都是呢可以说是技巧,也可以说一些,因为你如果把这个矩阵直接摊平,直接摊平的话呢,也可是是可以的。
直接摊平呢就变成一个久违的向量了,那如果如果如果这个层这样来定义的话呢,它它也是达到的效果,就输入是一个向量,输出是一个向量,但是你会发现输入的向量就爆就爆发了,本来是三变成九了,然后你再到下一层的。
再再再叠加的时候,那就变成就变成二,就变成又变成九的平方了,所以呢嗯我先把这个这个矩阵呢先把信息压缩了,压缩到跟原来一样的三个三维,所以我这个我这个交叉层就可以无限的叠加。
所以它这个从结构上呢是比较比较优美的一个结构,就是我特别针对这个推荐的交叉定位的一个层,就是说这个层呢就是可以像那个比如说cn里面的ping层啊,然后这一层做一个标准化的,作为标准化的操作。
关于关于这个交叉层呢,我们就主要看这两个例子,因为呃目前在交叉上做的尝试呢其实就是太复杂的,交叉上做的尝试呢都是不太成功的,就这个deep cos network,虽然它在模型上呢就是有他的。
有他的优美之处,但是在效果上呢就不是很理想。
对这就是你说的,我理解不了做危机的意义在哪里,也就是说隐特征两个隐特征向量我有没有必要它不同维度之间做交叉,有没有必要呢,他的出发点呢就在于我认为只是两两相,就是就是对齐的隐藏之间做交叉呢。
他觉得不充分,就是推荐里面就是大家一直在考虑这个问题,我就是觉得特征之间交叉不充分,我觉得不充分,我得来点非线性的交叉,最近交叉呢我觉得也不充分,我我还得来个就是这种错位的交叉。
所以呢他都是在往这个交叉的充分性上做做文章,但是你要非得说有什么直观含义的话,确实很难讲,所以说这也是他为什么在实际中表现呢并不太好的原因。
嗯我下面举的一个例子呢,呃这是给他们一个一个开源的项目,然后这个例子呢其实它代表了一个思想,就是我我想用这个例子代表一个思想,就是端到端的思想,其实都是为了呃,主要是为了体现一个端到端训练的思想。
什么叫端到端呢,就是我从最原始的输入到最原始的输出,都在模型里解决,我不在手工或者额外做一些特征变换和特征提取,就是输入越原始越好,比如说我们之前用如果用l2 来做推荐的话,我要手工的去做一些交叉特征。
手工做一些交叉特征对吧,有了fm之后,哎我的输入不用自己去手工交叉出来一些特征了,因为fm会在模型内部帮我做交叉,啊这就是fm的进步,但是fm解决不了,就说一些复杂的输入的问题,比如说图片。
我要推荐图片,推荐文本,那你如果直接把文本,然后这原始的特征放进去的话,文本原始特征那就是那就是磁带模型嘛,你把它原始特征放进去的话,效果不会好。
因为因为它有专门适合处理文本和图片的这样一个这样的这样的模型,所以呢我们这个端到端的话,就是你输入是什么样子的,我就先找一个适合处理你这种类型输入的模型,把它接到。
或者就是作作为作为将来输入端的in bani也好,或者你把它理解为特征提取的一部分也好,把它先接着把这个数据接住,接住之后后面再想办法做交叉,所以你看这个呢这个模型呢体现了之前好几个思想。
在用户端的这些特征呢,他全部做了bending之后,然后接了好多层的全连接层,先做了线性变换,然后电影端呢也是先把离散特征做了一些嵌入接全连接,然后关键的是这边,他把电影名这样一个文本特征呢做了一个新。
然后得到了这个电影文本特征,就是只要做了这第一步的这个这个之后呢,这些特征就一视同仁了,一视同仁,然后接到全连接层,最终得到一个电影最后的一个隐藏的限量,然后这是用户的隐藏的向量,然后他们做内积。
所以说这一步你只要一看到这种结构呢,其实就是fm的思想,就是其实就是在做fm,所以它是相当于这个模型。
这个模型跟刚才这个。
跟刚才这个是一个含义啊,就是先把特征做分析变换,在上面做内积,现在分页变换上面做内积是是是一样的,它多在它这个比这个模型多在什么地方呢,多在他用c接住了这文本特征。
这个时候你发现我在输入端最底层的输入端呢,我全部是原始特征,我自己都没有做任何处理,电影名我直接用,我直接用那个磁带编码的,就是就是磁带模型输入进去就好了,或者是如果这个地方我不用sin的话。
就是我我用我用那个用rn的话,我直接把这个序列原始的序列放在这里就可以了,如果你还有其他类型的特征的话,诶都拿适合处理这个特征的模型放在前面,把它给接住,接触之后得到隐藏的限量,最后进入fm的思想。
做一个内积就可以了,所以我们总结一下呃,这第二部分,第二部分呢是在模型上有创新的,这个创新就是推荐系统特有的,其其之前的那些问题就是dsn不同的网络结构,不管是最基础的fm,还有对fm的扩展。
就是cos network,它都是增加了一个之前的模型都没有的这样一个层,这个层叫交叉层,然后在实际中呢我们选择什么样的交叉层,比如说先做线性变换,先接电音再交叉,还是先交叉再接dn。
这个真的要实际问具体问题具体分析,在很多实验里面效果不太理想,但说不定呢说不定哪天找到一,个场景它效果就特别好,然后最后一点是端到端的训练,就是如果你真的要把嗯,就模型用在推荐里的话。
你要贯彻的一个思想就是端到端你尽量对原始特征少做,少做一些预处理,少做预处理,比如说用户画像,然后物品端的一些提前的特征提取,这些东西就是它都是无监督的,然后5000度的提取的特征呢。
它效果就不会是最最优的,最优的肯定是我们做一个端到端的训练,大家看这块有没有问题,啊没有问题的话呢,我们看一下第三部分,第三部分呢我们举几个比较新的论文,看一下这个推荐里面的最新的进展,主要是两方面的。
一个是注意力机制,就是attention,嗯这两个呢是近几年是比较火的方向,所以呢其实只要是火的东西,就是很快的会被运用到推荐里面去,而且有时候呢确实会会有比较明显的效果嗯。
然后我们现在来看一下第一篇把注意力机制用到推荐里面的文章,给大家一分钟,先看一下这个这个模型,然后我们再来讲它是横着画的,啊嗯我们先看一下这个模型,从中间左半边在干嘛呢,左边的你如果仔细看的话。
他其实是在画一个fm,这是原始特征,然后呢这是相当于把这个原始特征乘上了权重的车,因为这里v是权重嘛,然后第三层两两交叉,看到没有,就是两两交叉,两两交叉得到的这个项,看到没有,就是大家回想一下。
就是fm的求和二次项求和的那个项,我给你,我再把那个公式给复制过来,这是pdf,能复制不过来,就这二次项目,二次项求和就是这个样子的对吧,两两做内积,然后这是原始的输入。
唉这个地方因为有时候我们会省略掉,为什么省略掉呢,因为我们大部分特征都是离散特征,离散特征我做文化的编码,文化编码之后呢,这些特征都值都是一,就x i x j都是一,所以呢乘起来就就没没有了。
所以一般二次项就只有这个内积而已,但在通用的情况下呢,x i x j可以不是一这这个图呢就是画的就是这种通用的情况,就是原始的特征呢,它有不是一的0。2,0。4,好这是线性项,两两端对齐之后就是二次项。
注意啊,这个attention net如果不考虑的话,他接下来做的事情就是把这些二次项a加在一起,最终得到这个值,就注意把这个a去掉啊,然后就是fm,所以它是一个横着画的fm。
那attention机制怎么加进去呢,在这个在这个模型里面,他加的方法特别简单呃,特别直接,但是我但是可能有有同学之前没有了解过特训机制,我再简单介绍介绍一下本身机制。
就是说我们在人类在解决一个问题的时候呢,他的关注点不是整个样本,比如说我们在做图片,比如我看一个图片里面,这是人还是动物还是一个场景的时候,你的你的关注点不会是图片上每一个元素,你一定会注意到这个图片。
比如说显著的地方,比如说人的话就是人脸对吧,你会注意到这个图的某一个部分,然后如果用到文本,比如说翻译机器翻译的话呢,我在翻译一个句子的时候,我在具体翻译每一个词,每一个词的时候。
我最关注的肯定不是整条句子,就是说我注意力不可能平均分配在整个句子的每个词上,当前这个词应该怎么翻译,那起主要作用的肯定是这个词最相近的这些词,就说我在解决这个问题的时候呢,我的注意力是有分配的。
那回到这个fm里面,我想在哪些地方产生注意力的分配呢,就是这些24项,二次项我们刚才讲了一个二次项呢,代表了一个兴趣爱好的匹配,代表一个兴趣爱好的匹配,那这所有的匹配呢代表了一个相当于一个嗯怎么说呢。
就是这因为我们之前呢是对这些所有的匹配呢一视同仁的,所有的匹配匹配值算出来之后,我们是直接求和的,那这里如果引入注意力思想呢,它是这么引入的,我不把这些东西一视同仁的一视同仁,那直观要怎么理解呢。
就是说我可能这个人呢特别喜欢看动作片,我只要这个电影动作部分精彩啊,我其实可以忽略其他部分电影,比如说就演员,演员请的时候我也不管了啊,有没有什么喜剧元素我也不管了,只要动作过于精彩,我就肯定喜欢对吧。
这时候我的注意力全部集中在动作内为隐特征的交叉上,对吧,那另外一个人可能就相反了,另外人我就我只要个电影好笑,那我就是害了他,其他的部分再烂我也不管对吧,所以每个人对这个关注点不一样。
所以这个模型的思想就是,我用一个权重来表示这每一个匹配值之间的重要程度,所以这就是ai键,a i j的值呢a i键是用一个函数,这这群中寻求学的每一个网络都是一个函数,这个函数呢你有很多种定义方法。
我们这里就不就不展开来讲了,比如说最简单的你就可以算相似度,也是基于类似相对的方法呢算出一个函数值,这个值呢代表了当前这个匹配的重要程度,就我刚刚举的例子,我特别看重动作元素。
那只要动作元素匹配上了其他的匹配我就不管了,那大家看这个模型有没有问题,这个模型这样论文里嗯,就是给出的结果呢实施还是非常惊艳的,就是其实模型实现起来很简单,对我我我跟你说,比如说简单怎么做。
简单怎么做,因为我最终最终得到了这这个每一项不是一个值吗,就说这一项没分别是值对吧,分别是值,那我直接比如说算一个soft max,soft max,就可以输出概率了。
就说因为我注意力分配也可以可以认为是一个概率,那我在这个纸上,如果softmax作为我的这个这个这个注意力函数的话,它含义是什么呢,就是我只关注那些最大的值值,越值越大,就是就负责越富。
唉就就就是能够体现出我刚才说的那个效果,我就只关注我,好没有问题的话,我们再看下一个模型,下个模型呢画的比较复杂了,我先讲讲他在干什么事情,就是呃去年去年的一篇文章。
他叫coattention networks,他要做的问题呢是怎么样结合的评论来呃,进就提升推荐效果,因为现在好多网站不管是什么,不管是新闻啦还是那个呃就是电影啊,下面都会有用户评论。
所以说评论呢是很重要的一部分信息,而且信息量很大,比如说我住一个酒店,我特别关注他,比如说卫生情况,那如果有一个评论说它卫生很好对吧,我就我可能就住的概率就非常大了。
所以说评论对于推荐这个是是现在是起的越来越重要的作用,那这个问题这个这个文章是解决什么问题呢,它是把用户的历史评论当作用户的特征,然后呢在这个物品的历史评论当做物品的特征,就是在他这个模型里面。
就说这是很学术的做法了,因为他没有,他都没有考虑其他的什么用户的,其他的其他的就是原始特征。
用户的年龄性别,嗯我先看一下每一个节点的输出都是对对,刚才那个模型呢,每个节点输出都是值,因为他画的特别细。
对这里每个节点输出都是值了,你发现没有,因为这是内积吗,内积是一个数值x24 40最原始的10。2,所以这每个都是数值,这数值你就可以直接拿来算smax,我只是举个例子,就肯定不是最优的。
就可以得到一个a2 s了,我可以用其他的函数来计算这个相似度,我不是相似度来计算,我这个注意力分配,然后这个模型呢开始把用户的所有历史评论拿出来做用户的输入特征。
然后把物品的所有历史评论拿出来作为物品的输入特征,所以所以这就是输入层,为什么是这样的,两边都是评论,然后平时做ebending之后呢,还有一个叫cootion,科尔tension。
它这里面要找出来的是用户评论和物品评论之间的,那个就是最匹配的部分,为什么叫cover才是呢,就是它是说用户的这个评论体现了用户的关注点,然后呢这个物品的评论呢是体现了物品的一些关注点。
但是我怎么匹配起来呢,就通过这样一个cootation,它这块,呢其实就算的是相似度,大家可以去看一下,它是的,它是用户特征,有两点之间做相似度,加入读了之后呢,他做了一个max pooling。
max pooling,大家知道选选的是最匹配的那个,那这里的直观含义是什么呢,就找出用户最重要的一个评论,最看重的一个评论和这个电影最重要的一个和这个物品最重要的评论,找出两条这样的评论。
那什么意思呢,就是说这个我这个用户最重要的评论里面,比如说我给一个给一个就是经常评论的是哇这个这个酒店卫生太差了,或者说这几天卫生很好,那你会发现这个用户很关注卫生情况。
那这个电影的这个这个这个酒店其他的评论呢,就是其他用户给他的评论可能也有这些值,所以你来这两这两个评论列表,就是用户的历史评论列表,跟电影的历史评论列表拿来你做匹配呢,哎你会发现对于这个用户来说。
我可能关注点需要放在这个就是关于卫生情况的这个评论下,所以第一层呢实际上是通过这个两两匹配去,我就是就是决定我的注意力该放在哪个评论上,就是评论层的attention。
最终得到了评论层的attention之后,嗯,他也做了,就是做了一些变换之后,然后又有一个词词这个层次的开始像他做的就是事情跟下面差不多,我我知道这两个简单评论,这就是我都是我用户的关注点啊。
那在评论具体哪一个词是我就是我,我应该我再我再重说一遍,就是我的注意力应该放到哪两条评论上了,我接下来要确定我在这评论这两这个评论里面,我注意力该放在哪个词上好,经过匹配之后。
我发现我注意力其实就只注意就专注在卫生情况这样一个词上,那我如果确定了这个这这个事情之后,那我的预测就比较准了对吧,所以呢他这两层筛选就是第一层是筛选我看中的那个评论,第二层呢是筛选我看中的词。
就评论里面看中的词,就是我最终会被哪些测,就是这个数据影响,最终最后呢大家看也是接了一个fm层,所以呢fm这样一个交叉层,了基本的构成,基本的过程元素,大家看这个有没有问题,海滩曲怎么在梯度下降啊。
你用探测辅助写一个模型,你还用管它怎么算吗,你只要把公式写出来,它就它就就就都用梯度下降就可以算出来了,对吧,好关于注意力机制呢,我们就就是看一下呃,主要介绍这两篇文章,大家都了解吗。
就是了解同学可以敲个一,不了解的话可以敲个零。
好我这里呢也主要是介绍就是这个这个模型的核心思想,就是直观思想嗯,那它是什么意思呢,我们举个例子就明白了,比如说我给我一个输入输出是什么,然后我有正确答案,就比如说我做评分预测,我给我一个用户一个电影。
它是最能评多少分,我数据集里面有这个答案,然后呢五金的学历是没有答案的,就是我给你一堆数据,你给我做聚类,就是说你相当于发现这个数据里面有什么潜在规律,但是我没有什么答案,我啥都没有。
我也不知道这数据分几类,我也不知道这个数据到底能不能分类,他不给你正确答案,他不给你正确答案,但是呢它也不是完全没有答案,他给你一个惩罚函数,这个呢就举个例子,就是说比如说我再做一个数学题。
有正确答案的,我这一步该怎么写,然后会有人告诉我正确答案,就是这一步该怎么算怎么算怎么算,但是我能告诉你每一步走的对不对,或者说你这一步就是说做的好不好,比如说你再算一个数学题,你每写一部嗯。
然后旁边有人说诶这一步写的不错,你写一部,然后就说哎你这个公式用的不太好,就有人告诉你这些事情,但是就就永远不告诉你正确答案,不告诉你该怎么写,该怎么算,然后呢你要跟这样一个这样一个人长期的就是做交互。
就是你写一步,他告诉你怎么样,或者换到下棋的例子,也也嗯嗯就是像像那个比如下围棋也是这样,我下一步你告诉我好奇坏棋,但是他永远不会告诉你这一步该往哪儿走,就是不会告诉你答案,就是当前棋盘放在这里。
我该放在哪个位置,这是正确答案,这一段不会告诉你,你先自己下题目,你先自己做,做完了我告诉你好不好,最终能学到一个最优效果,那把这样一个思想用在推荐里面会有什么优势,会不会有什么优势呢。
是一个序列的交互,他是跟环境不断的交互,我每走一步都会有人告诉我好不好,但其实推荐里面呢恰恰就是这样一个场景,就是我每做出一个推荐的时候,就是我算法每做这个推荐结果的时候,其实是会有人告诉我好不好的。
这告诉我好不好的人,就是动态交互,这这这样的优势呢就就体现出来了,它可以很好地对用户的动态进行建模,因为因为很多时候用户的兴趣爱好是有动态变化的,我之前可能很喜欢什么什么样的,就是一些东西。
那突然比如说我我最近结婚了,那我可能就兴趣转向了一些结婚的产品,我装修了对吧,等装修完了,我可能生小孩了,我就开始关注一些这些母婴用品对吧,所以兴趣爱好这种动态变化呢可以被这个模型天然的很好的去捕捉到。
因为我没有正确答案,长期收益就是说我关注的是长期我得到的奖赏,勾勾高,那就放在推荐里面,它对应的什么呢,如果我只关注短期收益的话呢,其实就就对应的是用户这一次点还是不点,点就是正确答案。
不点就代表是错误答案,那这样有什么坏处呢,因为在推荐里面,其实点击率不是唯一的屏幕指标,如果是把点击率作为最重要的屏幕指标呢,像今日头条,那你就天天推荐三组就行了,你只要推荐三人组,点击率就高。
但是你会发现用户边点边骂你low,对吧,所以点击率不是你的唯一屏幕指标,你有很多其他的潜在的潜在的屏幕指标,但是不好量化,不好量化,不好量化,我就没不好作为正确答案,你明白吗。
如果用监督模型的框架训练的话,我就我的正确答案一定是要确定的,比如说这个推荐用户点了,它就是一没点就是零,这是之前的做法,但是他点了不一定好,他点了固定好,这个事情我怎么评估呢。
我是没办法知道正确答案的,所以你在没办法知道正确答案的情况下呢,这个第第二个特性,我其实不需要准确的正确答案,我只要能告诉你好还是不好就行了,就是有一个比较靠谱的,我告诉你好和不好的函数。
就是我不断地产生推荐结果,然后呢用户给我告诉我一些反馈,就是转化我的奖赏函数,再加上函数可以把很多用户考虑进来,比如用户点了肯定是一个奖励,但是呢用户点完之后很快退出去了,很快退出去了对吧。
这就是一个惩罚,那还有很多其他的呢,那比如说用户点完我这个东西之后,就直接离开我的app了,很长时间都不会再回来了对吧,很多很多运动都会考虑进去,这些东西可以构成一个复杂的奖赏函数。
但是你构不成一个正确答案,明白吧,就是我我有很多的状态,然后呢我还要决定在当前状态下我的行动,就代表我当前给用户推荐哪些东西,当前的上下文,当前用户的状态,所有的所有的那些就是当前的这些就是所有的情况。
然后我要做的行动呢是就是下一步该推荐哪一个,那我的行动呢就100个,我推荐哪个就是100个,当然如果你推荐一个list的话,你可以选择的行动更多,然后你每选择一个新的推荐结果。
经常你做了一个action,做完action之后呢,环境环境就是用户会告诉你你做的好不好,这样不断交互,就是你做一个action,用户给你一个reward,就是告诉你一个奖赏,然后最终将不断的交互。
不断交互,然后学到一个比较比较优的结果,然后如果关于就是在细的细节,大家可以去去研究一下这篇文章。
我说了。
七月在线—算法coding公开课 - P1:实战动态规划(直播coding) - 七月在线-julyedu - BV1YW411K7va
大家好,欢迎大家观看7月算法公开课。我们这堂课的主要内容是动态规划实战。😊,在之前的课程中,我们讲述过动态规划的理论知识。本堂课我将通过三个例题,通过实际代码向大家进一步讲述动态规划。
最后我会做一下总结与思考。我们先来看例一,给定一个M行N列的二维数组,就是一个矩阵,每个元素是一个非负整数。我们从左上角走到右下角,每次只能朝右或者朝下走,不能走出矩阵。😊。
要求就是我们使得这个路径上经过的所有数总和尽可能小,这是lid code上第64题。最直接的思路当然是枚举。😊,M行N列的矩阵,我们从左上角走到右下角,需要走M加N减2步,这其中有M减一步是朝下走的。
N减一步是朝右走的,所以路径总条数是CM加N减2M减1。我们显然可以把这些路径全都枚举出来,选一条和最小的。😊,理论上可行,实际上不可行,因为CM加N减2M减1非常大。
大家可以试一下M等于N等于50的时候,这个数有多大。😊,实施动态规划吧。我们用DPIJ表示,从左上角走到DI行DJ列的时候,所有和的最小值。我们考虑一下我们如何求DTIJ。我们要到达DI行DJ列。
要先到达DI减一行DJ列。再朝下走一步,或者先到达DI行DJ减一列,再朝右走一步。我们只有这两种方式到达DI行DJ列。那么我们到达第I行DJ列的最优值取决于这两种方式的最优值。
所以我们先到DI减一行DJ列的最优值,或者到第I行DJ减一列的最优值,这两的时候取最优,再加上DI行DJ列的值,就是我们到达DI行DJ列的最优值。其实这也是一种枚举,只是我们枚举了方案。
代替了枚举具体的路径。反正无论如何,我们要么先到这个,要么先到这个只有两个位置可以到达DI航TJ列。这实际上是一个递推式,有了递推式还不够,我们还需要有初值。😊,因为递推式是通过I减1G减一来推的。
所以初值就是它们等于零的时候相关的值。初值一般很好求,比方说DP00。我们设下标从零开始,DP00,因为还没有开始走,它在起点。😊,他只能是。原来矩阵里面第零个元素的这个位置的值。那么我们对于首行元素。
J大于零的时候,它只能从左边过来,所以它只能是DP0J减1加上A0J,也就是我们只有一种方式。同理,对于最左边那一列的时候。我们发现它也只有一种方式,也就是只能从上面过来。所以它等于DPI减10。
加上AI0。也就是说,在特殊的时候,我们只有一种方式。在一般情况下,我们是有两种方式取最小,所以这个就是初值了。那么时间复杂度呢,显然是M乘以N。因为我我们要打出一张M乘以N的表来,空间也是M乘以N。
我们可以优化一下空间。注意到BPIJ值与I减1J和IJ减1有关。我们循环I的时候正向循环J这个时候。我们只用异尾,也就是DPGDPG等于这个值。我们这个时候DPJ减一已经更新过了。
所以它实际上是DPIJ减1DPJ正打算更新,所以等号左边的DPJ实际上是DPIJ。😊,等号右面的DPJ是旧的值,还是DPI减1J。所以我们这个式子就是刚才的那个式子,只不过省掉了第一维而已。
这样我们空间复杂度就省掉了一些。当然,时间复杂度是不变的。😊,举一个贪心的反例,贪心就是我们看哪边小就往哪边走。那么最开始我们在零的时候发现一比2小,我们先走到了一。😊,当我们走到一的时候。
发现全是100。😊,所以我们至少要经过1个100。如果我们一开始放弃一走二的话,我们发现有0211这么一条路,显然比走过1个100要好很多。所以贪心是个错的。下面我们来上li code提交一下这个问题。
因为代码是现场写的,所以。可能会有一些错误,我会及时的修改。我们先来求出这个M和N,因为它给的是vector。这样我们就得到了MN就是M行N列。😊,那么我们下面来构造那个矩阵DP。因为它是一个二维数组。
我这里面用vector。它是M行。恩烈的。这样vector我就可以当普通的二维数组使用了。我们循环。因为要求DPIJ。根据我们刚才的式子,零的时候需要。特殊处理。那么I和J都是零的时候。
我们发现BTIJ就是起点的那个数,当然可以写gradereade00,也可以写gradereIJ。😊,那如果I等于0,这不等于0。显然,他只能从左边过来。也就是。不等于0,那一为可以减掉一。
这个是没得选的。如果I不等于0,这还是零的话,那么它只能从上面过来,所以这个也是没得选的。那如果不是这些情况,就是个刚才说的一般情况,它等于什么呢?就等于我们刚才说的那个式子。就是这两种情况。取最小。
再加上这个位置的值。那最终答案是什么?因为我们要走到右下角,最终答案就是。右下角就是最后一个元素M减1N减1,我们来提交一下这个代码。呃,可见是正确的。我再来做一下那个优化的空间优化的那个例子。
这个时候我们只需要一味的。书组只需要N,其实正规的做法可能需要取M和N较小的那个那样空间会更优。但是如果M小于N的话,我们MN相当于要交换一下这个矩阵要转置一下,这样稍微有点麻烦。
我这里面就简单的取了个N,那么。😊,我们直接把第一位去掉,这个是没有问题的,因为都是I嘛。这个也是没有问题的。注意新值和旧值,虽然我把第一位去掉了。
这个左面相当于是DTIJ右面因为我是正向循环的JJ减一的值已经被更新过了,所以J减一是DTIJ减1。但是这个时候DPJ还是IDPI减1J,所以我把这位直接去掉是没有问题的。
那么最后返回的就是最后那个N减1,这样我们就省掉了一位复杂度,我再提交一次。😊,还是AC的那我们解决了这个问题,我们再来看下一个问题。最大子数组合这个问题也被提过很多次了,给定一个整数数组。😊。
我们要求一个飞空的子数组,也就是连续的一段数,使得它的和最大,这是lead code的第53题。题目意思很简单,我们有若干种思路,最简单的思路就是暴力枚举。我们我们要求一个子数组嘛。
我们就先枚举它的起点0到N减1,枚举终点I到N减1,再循环一次求I到J之间所有元素的和,进而求最小值。这个相当于枚举了所有的飞空的子数组时间复杂度是N的3次方。😊,稍微聪明一点的枚举。
我没举起点0到N减1终点一样,我们在没举终点的时候,顺便求和。之前的那个方法是把终点没举出来,再写一个循环求和。😊,我们注意到J从I循环的时候,我们J每增加一,我们把和加上去就可以了。
所以可以在每举J的时候,顺便求和,就降低了以为时间复杂度,变成了ON方的。😊,那么还有一种方法是分制的方法,复杂度会更低。我们把一个子数一个数组切成两个近乎相等的子数组,每个长度近乎是2分之N。
那么最终的解要么在左面那一段,要么在最右面那一段,要么就是跨越中心点的最大子数组。所以。这个分的时候就是分别求和的时候,我们再循环一次求跨越中心点的那个最大子数组的和。😊,这个要ON的复杂度。
那么总时间复杂度就是TN等于2倍的T2分之N加上ON通过递归数推出来,时间复杂度是N logN的。我们再来看第四个方案。BP。BPI表示,以AI结尾的最大子数组的和。那么以AI结尾的最大指数组合。
其实有两种方案,一种就是只包含AI1个数。这个很简单,不包含AI减1。那么它的和就是AI要么我就把AI减1也囊括上,我要包含AI减1。😊,我要包含AI减一的时候。
显然包含了一个以AI减一结尾的最大的子数组的和,所以它就是DPI减1加上AI在这两种情况下,我取一个最大值就是DPI。所以这也是一种枚举,只不过我没有枚举子数组,而是枚举了一个方案。😊。
我要么包含AI减1,要包含AI减一,显然是一个子数组,包含AI减一的以AI减一结尾的一个最大子数组,要么就不包含AI减1,那就是AI本身啊,这两个取最大,这是一个递推式。初值是什么呢?
初值是DP0等于A0。因为最开始第一个数没有前面的那个数,所以这两种情况只能取这个第一种情况就是。呃,只能取第二种情况,就是只包含AI的这个情况。那么答案是什么呢?
答案是DP0到DPN减一里面最大的那个值。因为我们最后求的那个子数组一定要以某一项结尾。虽然我们不知道以哪一项结尾,我们只能循环求一下最大值。😊,那么这个算法的时间复杂度是ON,空间复杂度也是ON。
因为存了1个DP数组。空间可以用化吗?我们考虑一下DPI只与DPI减一有关,我们要把DPI存下来吗?其实没有必要,我们可以考虑and here,这里面and here实际上是DPI减1。
它加上AI之后再再和AI取一个最大得到一个新的and here。这个and here其实就是等号左边的就是DPI。所以这个是DPI减1。我通过一一步更新,更新成DPI。
那么anser也在循环的时候不断更新就可以了。😊,这个空间复杂度就做到了O一。当然,时间是无法优化的。还有一种其实也是一个思路,它也是线性的。我在这里面大概讲一下,我们定一个和sI表示原来那个数组。😊。
前I项的和,这里I是大于等于零的。那么我们定一下s负一等于0,这是为了方便。一个。显然的事实就是说。我任意一个子数组,从I加到J,这个子数组,它就等于s j减去sI减1。这个是显然的。
因为这个子数组先求前这项,减去前I减一项的和,就是I到J的这个子数组的和。😊,那么如果我们没举G的话。对于假设sG已经定了。那么我怎么求相当于这个子数组的终点已经定了,我怎么求起点呢?
起点显然是越小越好,因为它越小,这个差值才越大,因为J已经定了。那么实际上我们可以在求得s j的时候,顺便记录一下比J小的那些s值的最小值。也就是说我们用一个变量记录s的最小值和当前的sam减一下。😊。
取一个最大就是我们要的这个后面也会有代码展示,这个和DP的思路不一样,但是复杂度是一样的,时间也是线性的,空间也是常数的。那么下面我们来实现这几种方法。因为时间关系,我只打算实现分制算法,还有DP算法。
还有最后那个枚举算法,我们先来看分制。😊,如果这个是递归的退出条件。如果只有一项,显然就只能取A0了,否则至少有两项。那么我们取一下中点。就是N除以2啊。那么我们认为左边是。0到幂的。右边是。
幂的到N减1。好,幂的加一到N减1。这个区域到幂的减一吧,因为N等于二的时候,密的等于一,密的等于一的时候,右边会为空。所以我们这样左边是零到幂的减1。右面是me的到N减1,这儿分成两个两段。
那么对于两段的话,我可以分别求。最终的结果是什么呢?是左面的。左面有显然左面有密的像。左面有密的项,右面有。N简密的项。注意这里要偏移,因为。我右边的起始位置是me。这个是N解密的项,左边是密的项。
那么最终的结果可能是这两部分里面最大的,这个是分制分的部分。那么怎么和呢?和的部分我一定跨越了中心点。我假设它跨越了幂的减一。😊,那么当前的和就是。你的减一。ma是当前和的最大的时候。
那么我们从幂的减一往前走。就是往左边走。那么们不断更新这个妹。这个时候就是往左走的最大值,我们放到了这个妹里面,那么我们还要继续往右走,这时候再把no浮成妹。因为。当前左边已经固定下来了。
一样的那每次加上AI就是从n这个值再往右走。那么最终结果是什么呢?就是answer和ma里面去较大的那个。我们提交一下这个代码。是一个正确的。那么这个是分制算法,它的时间复杂度是N log N。
那么我们再来演示一下刚才说的那个DP算法。同样我先展示是空间复杂度是ON的那个算法。那么DT0是。A0这里显然假设N是大于零的。就是这个数组起码不是空的。我们怎么算DPI呢?刚才说了DPI等于。
要么就是前面那一项加上AI,要么就是AI。那最终呢结果就是。最大的那个。显然,我这里面answer还没有定义。先让它等于BP0。然后return的就是一个answer。提交稍微有一些慢。
这也是一个正确的。那么我再来展示一下空间复杂度是O一的,就是长数空间的那个算法。显然我把这个改成。And here。这也是一个变量了。然后呢,answer。等于A0最开始。
那么这个更新其实就是我用and here自己更新and here。就是新的and here等于旧的and here,加上AIAI那么answer呢就等于。这里面最大的。
最好还是return answer sir,我再提交一下。也是1个AC的算法。那么最后呢我再演示一下刚才说的那种求s的方法。😊,这个sam表示。当前的和,然后呢还有一个mins表示最小值。
因为s负一等于0,所以目前的最小值就是0。嗯。然后我就做一下。其实它应该取s负一和s零的最小值。sam0就是s。那么。对AI来讲。这里面answer还得求一下answer等于s,或者说等于A0。
那么我要求以AI结尾的。子数组最大子数组的和。我这个s实际上就是前I项的和了,那么它减的应该是之前更小的那一项的和。😊,所以answer等于。因为要取最大。它减的呢sam减去之前的最小的sam。
那同时我这个sam要更新一下,注意更新的顺序。我这mins一定要后更新。mean sum就是所有s的最小值,s就是前I项的和answer,就是我们最后要求的,最后还是return一个anser。
再提交一下。也是1个AC的算法。所以这是我讲了分制动态规划以及那个线性的另外一种理解的枚举。这个三种方法做的这个题目。那么下面我们再来看第三个题目。就是编辑距离问题。给定两个字符串,S和T。
通常我们叫S和T。因为S是source,就是原始的串。T是target,就是我们要变成的串。我们要求把S变成T所需要的操作次数,最少那什么叫操作呢?操作,包括我在S的任意位置上增加一个字符。
增加什么字符都无所谓。这算一次操作,或者我去掉任何一个字符,这个也叫一次操作,或者我修改一个字符,也就是说我S可以添一个字符,删一个字符,或者改一个字符,这都叫一次操作,最少经过多少次操作。
把S变成T呢?😊,这是一个li code上的第72题。之前我讲过BFS,我是说当题目里面有最少或者最小等等这种字样的话,它是BFS的一个关键词。那么当然这个题目我们是可以用BFS做的,当然只是理论上。
😊,因为我们用BFS做,至少要搜索Lance S,加上ls t呃,至多要搜索Lance S,加上Ls替。因为最差情况就是我们把S里面的字符全都删了,再都添加上T里面一个一个字符,这是一个上界。😊。
因为这个实际的具体的数很难分析,但至少这个这个上界的步数对我们来讲实在太大了。因为每一步有三种可能,起码是一个指数的算法。所以BFS理论可行,实际不可行。那么我们想想DP就是动态规划的思路。😊。
我们考虑到这个问题在于,因为我们可以从S里面增字符山字符,有这种操作非常非常麻烦。我们把它变为另外一个等价的问题来看一看。😊,我这里面举了个例子。
比方说FS等于ABCFT等于BBFG我们把它变为字符串对齐的问题。什么叫对齐呢?我们来看一下。😊,这个S等于ABCFT呢等于BBFG我可以添加一种特殊的字符,在任意位置添加都可以。
我要求这种对齐对齐就是一个对一个对齐之后,如果他们两个相等,这个就没事了。如果不相等就要扣一分。比方说我这样对齐之后发现。😊,这个A和D不相等,需要扣一分。这个C和这种特殊字符不相等,扣一分。
这个特殊字符和G不相等,也需要扣一分,所以至少需要扣3分。😊,那么这个和编辑距离有什么关系呢?我们可以看一下,首先两个特殊字符,这个没必要对应。因为如果S是一个特殊字符,这边T我也加一个特殊字符。
这两个相等也没有任何意义。因为它没有任何实际的意义。所以我们规定加一个规定,就是这种特殊字符只能和一个实际的字符对应。😊,两个特殊字符对应是没有任何意义的。那这些代表了什么呢?😊,S。
的特殊字符代表增加字符什么意思?就是说如果我T的G和S加了一个特殊字符来对应的话,我实际上是代表在S的这个位置把G添上,这实际上是一次操作,所以要扣一分。😊,那T位置如果我添了一个这个。
比方说这个位置我添了一个特殊字符S对应的C,我代表把S的这个C,最终我要删掉。😊,所以这种删除操作也是扣一分的。那对应位置相同的是不扣分的。比方说这两个B。这个是不扣分的,因为它根本就没有编辑。
那对于这个A和D对应,实际上是要扣一分的。我相当于要把S的这个A改成D。总而言之就是。😊,如果。T里面是一个实际的字符,S里面跟它们长得不一样的话,那就要把S的对应位置改成T的这个位置。
比方说这个位置改成G。😊,这个位置改成D。那如果T是一个特殊字符的话,就要表示把S对应字符删了。那我们的目标就是在S和T中分别添加一些这种特殊字符,然后把它们两个弄得一样长,弄得一样长之后。
对齐之后扣分最少。这个扣分最少的问题,就等价于我们的那个最短边辑距离的问题。这个是对问题做了一个转化。为了方便理解。😊,那这个问题其实很像最长公共子系列啊等等这种问题。都是就是字符串操作的相关问题。
我们来看一下怎么解它。😊,我们用DPIJ表示S的前I个位置和T的前这个位置对齐能产生的最少扣分啊,这里面得分就是扣分的,也就是最少得分。那么。我们考虑一下DPIJ它可能是哪几种情况。
第一种就是我就要让S的第I个字符和T的第这个字符对齐,无论他们怎么样,我反正我让他们对齐了。那么S的前I减一个位置和T的前J减一个位置。😊,也是要对齐的,这个对齐再加上它们两个之间是否相等。
这个CYG表示S的第X个字符和T的第这个字符是否相等。如果相等的话,我取零,因为相等的话不得分,或者说不扣分,不相等的话,我取一,因为不相等的话,就需要把S的第X个字符替换成T的第这个字符。
这个实际上对应了。😊,那个位置是两个实际字符的位置,让他们实际匹配。那还有一种显然就是我让S的第I个字符和。😊,这个让J那边让T那边加一个特殊字符,或让这两个对齐。
那么S第I个字符和一个特殊字符对齐意味着什么?意味着。😊,T的第这个字符在之前已经匹配过了,也就是说T的第这个字符和S的前I减一个字符已经match上了。那么对于S的第X个字符来讲。
我让它和特殊字符对齐,这个显然要扣一分,这个就表示删掉S的第X个字符,同理。😊,我可以让T的第这个字符和。😊,S中的一个特殊字符来对齐,这意味着什么?这意味着。😊,I的就是S的前X字符。
已经和T的这减一个字符已经对齐过了。那么T的比这个字符没有地方放,所以只能和一个特殊字符对齐。这样的话也是扣一分,这意味着在S中加上T的第这个字符。😊,那么只有这三种情况对齐这个S的前I位和T的前J位。
那这三种情况取一下,最小就是我们要求的结果。那实际上我们就得出来这么一个地推式。有了地推式还不够,初值是什么?初值是。等于零的时候,因为都是和减一来对的,等于零的时候。
第一个串的前零位和第二个串的前J位如果对齐的话,最少的代价只能是J。这个意味着第一个串什么都没有,也就是S什么都没有T,我要有J位的话,我只能在S中添加上T的那J位是最少的。第二个意思是一样的。
如果S有I位T什么都没有的话,我只能从S中把这I位都删掉。😊,注意这两个初值实际上是有一个重合的,就是I和G都等于零的时候,这两个式子走哪个都一样。因为DT00等于0,就两个都是空的时候。
对齐是不需要任何操作的那时间复杂度通过这个式子也能看出来,就是两个长度的乘积。😊,空间复杂度也是两个程长度的乘积。同样我们发现。DPIJ和DTI减1J减1,还有DTI减1J和DTIJ减J减1相关。
我们实际上可以用同样的方法可以省掉以为呃空间复杂度。但是要注意这里面有一个DTI减1J减1这个值我们在更新I的时候,已经变成了DTIJ减1了。所以我们需要临时把这个值存下来。后面代码中会有体现。😊。
那么下面我们来看一下,就是通过lid code实际做一下这个题。那么根据我的习惯,我先把两个字符串串的长度取出来。就是一个长度为。一个长度为M,一个长度为N。然后我们写那个递推式。
其实就是把那个递推式变成代码。注意刚才的那个方式哦,DP还没有定义。它是一个M加一行。N加一行的这么一个N加一列的这么一个数组。因为我所有下标要从I0到M,所有下标要从0到N,那么。I等于零的时候。
刚才说了。J等于零的时候也说了。关键就是两个都不是0,它等于什么呢?一个就是。S的第I位和J的第I位和和呃就是word一的。DI减一位或者说DI位,因为是长度为I。
所以它那个应该减一和word2的DJ减一位是否相等?相等的话,我们取零不相等的话,我们取一。这是一个可行值。还有一种情况呢是。另外两种就是。简单了,就是IG减1加上1。当然我可以这样写。
因为两个都有加一。还是写到一起。这个这两种情况取一下面,一共是三种情况,取一下最小值就可以了。那么最终的结果就是。显然是DTMN,因为是第一个串的长度和第二个串的长度。
注意这里面下标和那个长度之间有一个简易的关系。那么我提交一下。它是1个AC的代码。那下面我来考虑如何省掉那一位复杂度。同理这个实际上也只是考虑可以考虑M和N哪个小。但是在这里面我为了简单。
我就直接把M那一枚省掉了。😊,那么这个是不变的。这个也是不变的。所以这要注意。这我要考虑DTI减1J减1这个值我没有,我们目前认为它是last。然后DPIJ减1DPIJ减1是我们前一步已经更新的。
这个是有的。那么DPI减1JDPI减1J等号左边和等号右边不一样,都是DTJ这个是DPI减1这是要更新的。问题是这个last是什么?😊,这个last刚才说了是DTI减1J减1。
我们看一下怎么得到这个last。那对于DPI减1J减1的时候,我们试图得到这个last。其实我们在这儿。在更新DPG的时候,我们发现。😊,这个时候当然这个last要在I这边定义。在更新DPG的时候。
这个时候是。😊,DPJ是DPIJ那么在更新之前就是DPI减1J。所以这时候lastt我们已经有值了。那么。这个时候。在更新之前,实际上是DPI减1G。然后更新之后执行了这句话之后,这个G变成了DPIJ。
那么实际上我用temple把旧的这个DPG存下来。这个temple实际上是。DPI减1J,那么我们把last复成tle。这个时候last实际上是DPI减1J当J增一加一的时候。
这个last就变成了DPI减1J减1。所以我们这里面只要用一些小技巧把这个旧的那个DP值存下来就可以了。那最终返回的是什么?返回的是DPN。😊,那么我再提交一下。它也是1个AC的代码。
请大家仔细理解这个代码。那最后我就做一下总结。对于DP问题,实际上我之前讲的自优子文体结构啊等等,那些只是理论上的。😊,我们真正解1个DDP问题。最关键的是要有DP的递推式,但是光有地P式递推式还不够。
我们需要有地推式的初值,这样才能完全解出来这个DP问题。实际上DP本质是一种递推,那注意空间优化。我讲的这几个问题都可以通过空间上的优化省掉一位。另外其实还是需要多练习,多思考,才能有感觉。😊。
那么下面举出了一些little code上DP题的题号,这些都是li code上经典的DP问题。😊,请大家勤思考,勤练习。啊,我今天这堂课就到这里,这是我联系方式,希望大家给予我批评指正,谢谢大家。😊。
七月在线—算法coding公开课 - P2:排序查找实战(直播coding) - 七月在线-julyedu - BV1YW411K7va
大家好,欢迎观看7月算法公开课。我们这堂课的主要内容是排序查找实战。我将通过三个例题讲述本课内容,最后总结结束本课。第一个问题比较简单,我会快速的讲一下算法,把主要精力放在写代码上啊。
就是杨式矩阵的查找。所谓杨氏矩阵就是每一行每一列都单调递增的这么一个矩阵。我们的任务是查找一个数是否在这个矩阵中出现,这是li code上第74题。我们有三种方法。第一种方法是,假设要查找的数是Y。
我们每次试图删掉矩阵中的一行或者一列,保证Y不在这一行或者一列中出现,把问题放在子矩阵上面去。我们要找的数是Y当前在第一行最后一列这个数的位置上,也就是X。如果X小于Y,根据杨氏矩阵的性质。
所有数第一行的所有数都是小于Y的。所以我们可以安全的删掉第一行。如果X大于Y,则说明最后一列的所有数都大于Y,所以我们可以安全的删掉最后一列,这样无论哪一种情况,我们都可以通过减少矩阵的一行或者一列。
我们把精力永远放在剩余的那个子矩阵的第一行最后一列这个位置。这个算法的时间复杂度就是OM加NM是行数,N是列数。根据大O级号的定义和概念,我们知道OM加N其实就是O maxaxMN。这是第一种方法。😊。
第二种方法是分制法,它同样利用了杨氏矩阵的性质。我们取中心点,画个时差,取中心点X。要找的元素同样同样是Y。如果X小于Y,我们发现这个A矩阵这个位置不可能出现Y,因为A矩阵里面所有值都小于Y。
如果X大于Y同理这个D矩阵,所有值都是大于Y的。我们可以把D矩阵删掉。这样哪种情况,我们要么删掉A矩阵,要么删掉D矩阵,那时间复杂度其实就是。TN等于TN除以2,加上TN除以4加O1。
这里要注意我们删掉一个矩阵之后,剩余的比方说我们把A删了,我们可以把BD看成一个矩阵,它的大小是2分之N。那C也是一个矩阵,它大小是4分之NO一是我们比较XY的时间复杂度。
那这个N注意是呃矩阵里面所有元素的个数,也就是小M乘以小N。所以我用大N表示它的复杂度不是很好分析,介于根号N到N之间。那第三种方法其实也是分制。我们在中线上查找,因为中线是这个杨氏矩阵中间的那一行。
中间的那一行同样满足单调递增的这个性质。我们可以在中间的那一行找到最后一个小于Y的值,我们称之为X1,之所以说它是最后一个,就是说它下面那一项,恰好刚刚大于Y。所以我们找到了实际上是一个突变的临界点。
X一小于YX2大于Y这两个点在中线上是相邻的。那么A这个矩阵是以X1为右下角的。D这个矩阵是以X2为左上角的。根据刚才的分析,A中所有的元素都小于Y。D中所有的元素都大于Y。我们可以把A和D同时删掉。
剩余两个矩阵B和C在递归的查找。那删掉这个之后,复杂度其实还不是很好分析。因为这两个矩阵的规模取决于这个临界点的位置。那么在一种情况下,就是恰好评分的时候。
它实际上恰好分成了两个剩余的大小为4分之N的矩阵。所以这个恰好评分的时候,复杂度是这样的,这个logN是我们二分查找X一位置的这个时间复杂度。注意这个N是小N,也就是列数。因为我们只在这一行里面查找。
😊,那这个时间复杂度最后推出来是根号N的。这个是三种分制法,下面我们在let code上写一下这个问题。
这个。我们先考虑第一种做法,我先得到这个矩阵的行和列。这是行。这是列。就是M行N列。刚才的方法是说,我只要放在第一行的最后一列就可以了。满足的条件就是。行数不能超出范围,当然C要大于等于0。
因为我这个是增加一行,减少一列呃,减少一行减少一列的。那么我们判断。这个值。和target大小。如果它大于target,就说明这个最后一列是没有用的。如果它小于他gi。就说明第一行是没有用的。
否则就是他和他给的相等。相等的时候,我们直接return一个处。然后在循环外面就说明我们把行和或者列都删完了,那么显然是找不到这个元素的。我提交一下这个代码。这是1个AC代码。那么我们再考虑。
第二种方法就是我讲的那个分制法,找到中心点的那那种分制。这种分制法呢,我们需要一个。另外一个支持它的函数,这个函数我们叫做finidefiide就是从这个矩阵里面。给定一个子矩阵。
子矩阵显然最开始是它本身就是左上角坐标和右下角坐标。我们要找的数候还是tart。那么现在关键问题就是看一下我们。这个find函数如何实现?find我们的传入参数是一个矩阵。为了方便。
我把这个参数名字改改短一点,就叫做A。X1Y1是左上角,X2Y2是右下角。carget是我们要找的这个元素。那么如果这个矩阵为空的时候。就是。X1大于X2或者Y1大于Y2,这个矩阵是空的。
显然我们要找的数不在空矩阵里面,否则的话这个矩阵非空的时候,我们就求一下。中心点的位置所谓中心点就是幂的X和幂的Y就是。把横坐标取一下中,纵坐标也取一下中。然后呢,如果。我们要找的这个元素。
恰好在中心点。显然我们return一个处就可以了。那如果它不是中性电,我们就要根据它的大小。他和这个我们要找的这个元素。的大小来决定一下。它所在的位置,一种情况呢是。就是如果tt比我们要找的这个元素小。
那说明右下角那个矩阵是没有用的。根据刚才的分析,我们要仔细算一下剩余的那个矩阵,两个矩阵的左上角和右下角。就是这是一个矩阵,另外那个矩阵应该是Ame的XY1。那个应该是X2。米的外。减1吧,减1。
好像要减1。他给他。这个是。呃,其中的一个矩阵。另外一个矩另外的一种情况就是target比我们要找的这个值要大的话,它实际上就是左上角那个矩阵是没有用的。同样我们重新算一下这个坐标是。X2Y2。看给的。
另外那边是A那个X加1。问1X2。密度。晚。大概检查一下这个坐标的写的,想一下写的对不对?这个坐标要仔细算一下。然后我可以提交一下。它也是一个正确的代码。那么下面再讲一下我们那个分支法的第三种方法。
就是当然这个主的都不变了,我们需要。一个二分的算法来算。我们要找到临界点的位置。那么我们怎么二分呢?我们要算一个这么一个东西,就是在中间那一行找到一个我们想要的东西,找到一个临界点。
这个临界点实际上我们称为这个我写一个二分的函数叫做。叫做help。这个函数是目的是从A密的XY1到Y2这个区间里面。找到一个。小于等于targe的最后一个值。那我们现现在来实现一下这个函数。
他叫help。它的传入参数实际上是一行。当然他限定了这个。Y一列到Y2列。我们要找到一个最后一个小于等于target的这么一个值。那么怎么写呢?我们就写一个传统的二分,把left定下来。
right也定下来,然后判断一下。reft小于等于right的话,我们就算一下中间的那个元素。然后看一下,如果A密的小于等于。Cararge。F不等于幂的加一,否则。
right于幂的减注意这里return实际上是left减一。这里要注意,如果这种所有元素都大于tagi,也就是这种元素不存在的话,返left是从来不会变的,left就是Y1,最后返回的是Y1减1。
所以我们根据返回值如果比Y一要小的话,就说明这个元素不存在。否则的话就说明这个元素存在。现在我们找到了这样一个inY,我们怎么用它呢?😊,我们先要判断一下in theY。是不是。
就是me的这me X这一行inY这一列有没有我们要的这个target。这是因为刚才说了,大于等于Y一表明这这种情况是存在的。这种情况存在就是说我们可以找到一个小于等于target的点。
那么既然是小于等于,我们先看一下它是不是等于。😊,如果等于我们就直接反馈处了,就省事了。否则就说明我们找到了临界点,这个到in的Y这个位置是小于的。那么到这个in的Y加一的那个位置是大于的。这样的话。
以in的Y为右下角那个矩阵和以inY加一为左上角那个矩阵,我们都可以扔掉,这样就剩余了两个矩阵,同样要仔细算一下这个坐标,应该是A。Yes。这是一个矩阵。另外那个是。幂的X加1。
我要从这两个子矩阵里面找他给。啊,注意这个时候,即使in的Y小与Y一也没有关系。因为我们在后面会判断出这个矩阵为空,直接返回for再提交一下。它也是一个正确的代码,这个是我们要讲的第一题。
那么下面继续讲第二个问题。它是li code上第四题就是两个有序数组的中位数。这里面因为两个数组是有序的,求中位数其实就是求排好序之后,中间那个元素。当然它和奇数和偶数有关系。
我们现在把它作为一个更一般的问题,就是求两个有序数组规并之后的第K小的元素。那么第一个方法,我们自然能想到的就是按照规并排序那么做,就是规并,因为两个数组已经有序了。当然我们只规并到前K个元素就可以了。
这个时间复杂度是O的。显然。😊,那另外一种方法还是分制,我们用规并的还是用规并排序的思想。我们从A和B中一共拿K个数看一下。因为我们假设A长度稍微短一些,我们取。A中取PAA个元素,B中取PB个元素。
一共取K个元素。注意这个呃2分之K和这个A的长度要取一个较小的值,因为A一共就这么多元素了。B因为是较长一点的,一定能取够这么多个元素。关键问题在这儿,如果APA减1,因为下标从零开始。
PA减一就是A的数组中目前取的最后一个元素,它比B的PB减一要小的话,这说明我们在规并的时候,A数组已经走到了终点,剩余的是用B的数凑足了K个。换句话说。
这说明总的最大值DK大的值不会出现在0到PA减1里面。这是因为这个假设我们再多加一些A中的元素的话,那么这个DK小的值有可能是出现在这个PA减一后面的。😊,对于B来讲,我们再多加数也没有用。
因为光A和B光取这些元素已经凑够了Kg了。所以。大家仔细理解一下,会发现B数组PB后面的那些数对我们来讲没有意义。A数组PA之前的那些数对我们来讲没有意义。
对于APPA减一大于等于BPV减1来讲是对称的。所以这个结论请大家仔细用规并排序的思想想一下,哪些数是没有意义的那最终的结论就是我们可以扔掉这个较小值数组的前一部分。
同时我们可以扔掉较大值数组的后一部分。也就是这种情况来讲,我们可以把A的前前半部分。以及数组B的后半部分都扔掉,继续做。关于这个的时间复杂度,我们可以证明它是logK的。并且。
主要一个原因就是K每次几乎减少一半。具体我们可以看一下代码。
下面写一下这个问题,这个问题是这个问题。因为这里面实际上是要返回这个两个数组的中位数,取决于呃M和N的奇偶性的。所以我在主程序里面实际上写了一个更复杂的东西,就是判断一下。这个东西是奇数还是偶数?
那如果是奇数的话,显然它中间的元素就是一个固定的。就是AB。MN相当于我取M加N加1,我取这个第这个小的元素对于偶数的话稍微有点复杂。这个还是ABMN这边的元元素数实际上是。M加N。除以2。再加上。
另外那边实际上是M加N。除以2。再加一。我取这个。这两个数的和。乘以。5,就是它对中位数的定义是如果是偶数个就取中间两个元素,然后这个再取一下中位数,再取一下平均数,就是我们要求的这个元素。
所以我们这是取了两个数,然后再乘点5,我把它用括号括起来。那么我们的第一种方法实际上是算斐 K。我们用规并的方法。这里面重新命名一下参数,把它改成小写。这个是A的长度和B的长度。然后当然还有一个K。
我们的第一种方法就是按照规并排序的思想来写。那么我现在大概写一下。Yes。规并排序就是说哪个数组没用完,我就用哪个数组。当然这个时候要判断一下这个。当前的因为我K每次减少一的。
我需要判断一下AI和BJ的这个大小关系。如果AI小于BJ,显然我就要考虑AI了。当然我们不用真的规并,我们只是考虑一下现在在哪个元素那儿就可以了。如果K是零的话,我们就可以。返回AI了。
否则就我们就可以考虑是BJBJ同样要判断K是不是0。如果K是零的话,就把BJ直接返回。再否则的话我们就。加加这。嗯,这个否则写不写都可以在那写上吧,否则的话我们就加加J。那么这个退出的时候。这里面啊。
这里面这个。加加I应该写到这里了。然后从逻辑上来讲,如果K等于0。就返回BG,否则的话我就把J加一,然后这个循环结束。那么在循环外面,实际上我们是看一下是哪个数组被耗尽了。如果是A数组被耗尽了,显然。
B数组还有元素,我就返回这个这个值。如果否则的话,就像说明B数组被靠近了,我就返回A这个值。然后这个可以提交一下。它是一个正确的代码。然后我们再来看一下我刚才说的第二种方法。第二种方法比较麻烦。
同样还是要实现这个find K。那么我们怎么实现find K呢?我们要假设先确定一下A和B的长度。如果lance A大于LsB。我们就把它交换一下,这样因为为了后面好分析。就把它交换一下。从这以后呢。
我们就知道lenice A是小于等于lenance B的。然后就按照当然有些特殊情况,我们要提前考虑。比方说如果lessance A已经空了,因为A比较小,luice A已经空了。
显然我们就返回B那部分的值就好了。就是B的DK减一个元素,否则的话呢。还有一个比较简单的情况,就是如果K恰好是一。我们显然返回的就是A和B中比较小的那个,因为K等于一相当于返回最小的元素。
那否则呢我们就从A中取。尽可能取一半个元素,当然有可能取不到一半。B中呢取剩下的那些元素,然后我们去考虑一下,根据刚才说的,我们考虑一下这个值。He。B的PB减一的值的大小。那如果A的这个小呢。
我们刚才已经说了这个小说明。呃,就是A的前半部分是没用的。我们可以把精力放到A的后半部分,A的后半部分偏移一下,就是A加PA。那么B呢就在这儿。那么。A的长度,实际上我们把PA个较小的数已经扔掉了。
那么对于B的长度,我们就限制在PB上了。因为PB的后半部分是没有用的,然后K呢减少了PA个数,因为我们把A的较小的PA个数已经扔掉了。那对于第二种情况呢,实际上跟它是对称的,A不变。
那么B呢B的前半部分被扔掉了。那么A的长度变成了PA。B的长度变成Lance B减去PB,那么K呢也减去PB是因为我们把较小的这个PB的数扔掉了。那么我们再提交一下。这也是一个正确的代码。
那么下面我们来分析信析啊,这个算法的时间复杂度。首先关于这一步,如果lenance A比Lance B大的话,我们交换这一步充其量,使我们的递归层数扩大了2倍,而二是一个常数。
所以这一步我们暂时不用考虑。那这个。这个是递归的推出条件,我们都不用考虑。我们关键看K的变化。我们可以看一下,对于第二种情况,就是说取后半部分的情况,我们可以看一下。
因为K减PB实际上就是PA而PA是小于K的一半的,小于等于K的一半的。所以对于第二种情况,我们K的这个变化最多变成了K的一半。😊,所以说K至多减少了一呃,至少变成了原来的一半。那么对于第一种情况。
我们再看一下。如果这个PA取到了2分之K,就是近乎K的一半的话,那么K减PA也变成一半。关键一个问题是说LA不够长。所以PA没有取成2分之K这种值,而PA取成了LsA,因为它取的是较小值。
那么我们的K的减小值实际上可能就没有到一半,剩的可能更多了。但问题在于,当PA等于LanceA的时候,我们发现这个值是0。这个值是零,在递归过来的时候,我们会发现下一个L A已经变成零了。
因为这边传过来的是L A减PA。那么对于lan A等于0,我们在下一层会直接退出,所以这种情况不用考虑。那也就是说我们只考虑这个PA等于2分之K的时候,PA等于2分之K的时候。
这一减K减少了还是减少了一半。所以无论哪种情况,K至多变成原来的一半左右。所以当K等于一的时候就退出了。所以这个时间复杂度是logK的。当然刚才说的。
如果PA取到了Lance A这个值在下一层就直接退出了,所以这种特殊情况也也会满足这个logK的时间复杂度。那么对于logK来讲,其实我们传的值实际上是M和M加N啊,或者说2分之MN这么大的。
所以这个复杂度实际上是呃logM加N也就是它要求的这种时间复杂度。这个是我要讲的第二个问题。😊。
下面继续我要讲的第三个问题。就是说这是荷兰国旗问题。所谓荷兰国旗是说荷兰的那个国旗实际上是有三种颜色的布条构成的。我们要通过交换对012进行排序。其实这里面012代表三种不同的颜色。我们试图通过交换。
把零放到一起,把一放到一起,把二放到一起。这个数组只有这三种元素,强调一下,要通过交换。这是因为其实我们可以数一下有多少个零多少个一多少个2,最后呃按照顺序,比方说有三个02两个11个2。
我们就把数组前三个复成0,后接着两个复成1,最后一个复成2就可以了。但是这里面它要求通过交换。所以不是我们简单的计数,这么简单的这种事情。这是lea code上第75题。啊。
其实我想讲的一个事情就是所谓的循环不变式。我们证明我们把K作为循环变量,然后我们证明一个结论始终存在。当K等于零的时候,它是正确的。在K增加一之后,它还是正确的,这样就证明了K不断的变化的时候。
它总是成立的,这个有点像数学归纳法的思想。我们要证明什么事呢?就是这么一个事儿,就是先设I等于0,这等于N。然后呢。我们要说0到A减1这个区间,这是B区间。
这个是指的下标下标在0到A减1的这个区间里面全是0。然后下标在I到K减一里面全是一。下标在K到J减一里面是我们没有考虑到,或者说目前还没有看到的值,下标在J到N减一里面全是2。
我们看一下为什么这个结论在K等于零的时候是成立的。K等于0时候,我们看一下。😊,K等于0,这个是0到-1这么个区间,这个区间是个空的区间。我们认为空的区间是永远成立的。所以我说空区间里面全是零是成立的。
那么对于一来讲也一样,因为K等于0I等于0,这个是0-1,这也是个空区间。对于J来讲,这是个N,然后这个到N减1,这也是个空区间。所以012区域都是空的。而K等于零的时候,这等于N,也就是0到N减1。
就是全部的N个数,我们都是未探测的状态。所以这四个这个条件是成立的。我们通过归纳法证明,当K变化的时候,我们对于AK做一下处理,仍满足这些条件仍然成立。比方说我们发现当前的AK等于0,我们怎么处理呢?
我们把AK和AI做一下交换,同时I加一,这是为什么?因为我们知道I的这个位置实际上是零和一的分界点,它是第一个一的位置。那么因为AK等于0,我把AK和AI交换了一下,新的AK实际上就变成一了。
旧的AI就变成零了,同时I加一,实际上我们把这个零的区间延伸出来了一个0一的区间这个往右延伸出来了一个一,所以I加了一。😊,然后我们把K再增加一的时候,这些条件显然也成立。那么对于AK等于一呢。
我们不做任何操作,这是因为I到K减一是1K增加一的时候,这个I到新的K减一仍然是一,这个没有关系。那么对于二其实和这个对于零的处理是对称的。😊,我们把这个AJ减一和AK做一下交换。
但是别忘了这里面这个K要减少一,这是因为这个。AJ这个值其实是我们AJ减一这个值是我们没有探测到的值。所以我们把它交换了之后,新的AK这个位置。尽管我们把AK换过去,换到AJ那个位置是2。
但是新的AK这个位置是旧的AJ减1AJ减一是个未探测值。所以我们这个K要先减少一,因为这个K在循环变量里面,我把它先减少一再增加一之后,这K是没有变的。那么它的时间复杂度为什么是ON的呢?这是因为。😊。
我这个K是循环的,每次都加一的。对于这个AK等于0的时候,K增加一没有关系。对于AK等于一的时候,相当于也是K增加一没有关系。但是对于AK等于2的时候,虽然K先减少了一,又增加了一,相当于K不变。
但是这减少了一。所以我们这个循环的区间实际上在不断的减小了。每次要么K增加了一,要么这减少了一。所以这个对我们来讲,这个区间总在一个一个减少,所以它是1个ON的时间复杂度。😊。
讲这个是为了让大家理解一下这个呃循环不变是。下面我写一下这个问题。
是这个。按照我们的定义。我们看一下。刚才说了等于零的时候,我们就把。A爱。和AK做下交换。等于一的时候没操作,所以我们只要看一下哦,这里面是K。我们只要看一下这个。等于二的时候就可以了,等于二的时候。
我们要把A减减J和AK减减做交换。因为这里面是K减减,然后这边又加了,所以K是不变的,J是减少一的哦,这个循环变量要循环到N呃循环到G,因为这个是循环到J减一的。那么这个题就写完了,再提交一下。
然后他也是一个正确的代码。
然后大概总结一下,其实查找和排序呃涉及到的相关知识挺多的。比方说我们要熟悉各种排序算法,比方说冒泡啊、规并啊,规并排序对于列表比较有效,希尔排序啊快速排序啊。
比方说荷兰国际问题就是快速排序partition的那个过程的一个一个变种吧。它正好分成了三堆。包括有些快速排序那个parttition,实际上也是分成三堆的。我们把和比分区元素小和分区元素相等。
以及把分区元素大,比分区元素大的这三种元素分到了三个不同的区间里面,就跟012排序是一样的。还有堆排序啊、统排序啊、技数排序啊、技术技术排序啊等等,这些都是很常见很重要的排序算法。
那另外查找查找的本质是利用序。如果没有顺序的话,我们查找只能是顺序查找,也就是一个一个找。正因为它有了顺序,我们可以采用某种分制的方法,通常是二分,也有三分的这种可能。
那么其实它的本质是我们利用了某些特殊的顺序。每次确定一下,我们要找到那个元素,在我们的全集里面的某一部分的一个子集里面,也就呃通常就是在这个全集的一半的某个集合里面,也就是所谓的二分把集合分成两部分。
我们确定一下,它在呃A部分还是B部分,从而就每次可以扔到一半达到logN的复杂度。这个就在写那个杨式矩阵查找的时候,的第三个算法用到了二分的这种思想。因为其中有一步要确定那个连界点。我就是二分出来的。
好,今天的课就到这里。希望大家。他给我提出批评指正,谢谢大家。
七月在线—算法coding公开课 - P3:链表实战(直播coding) - 七月在线-julyedu - BV1YW411K7va
欢迎收看7月算法公开课。我们这堂课的主要内容是链表问题实战。从li code上选了4个题,或者说5个题,难度适中,不太难也不太容易的题,比较典型,跟链表相关。来给大家讲述一下。
第一个题就是单链表是否有环儿。那么这是一个比较经典的问题。普通的单链表是没有环的,最后一个节点指向的是空。那么如果有环的话,最后一个节点指向了前面的某一个节点。注意这个有环可能不是简单的一个圈。
可能是这种阿拉伯数字6这种形式,前面有一条线段,或者说有个丙,后面有一个圈是这样的链表。他问你首先问你有没有环。然后如果有环的话,起点在哪,这个是上142题。那么最直接的方法。
我们用一个map或者说用一个set,用一个集合来记录经过的节点,然后一个一个走,每次查一下它是否是否在之前出现过。如果我们走到了一个之前的节点,则说明有环,并且走到这个节点就是环的起点。
那么这种方法的弊端也很显然,我们多用了一个集合,这个集合可能有查找的这种时间的效率。另外还有ON的空间这种复杂度。这个查找呢,当然我们可以用哈希等等等等,把它优化到O一,但是空间是避免不了的。我想讲的。
😊,方法也是比较经典的,是双指针,这个其实就是比较简单数学上的这个追积问题。大家要仔细理解。那么我们假设有两个指针,X呢每次从头开始走一步,Y呢每次从头开始走两步。那么如果有环的话。
我们证明X和Y一定会相遇。当然没有环的话,因为Y走的快,Y先走到链表的这个末尾,这就直接退出去了。有环的话,我们假设环呢,这个环的大小,这个环里面包含了A个节点。刚才说了这个饼的长度,线段的长度。
我们认为是B个节点,注意B可能为0,那就是简单的一个圈。那当然这个我们统一考虑,那我们假设因为X走的慢,X一步一步走,它刚走完那个B这么多个节点的时候。😊,也就是说X刚进入圈的时候,Y呢,因为Y走的快。
它肯定已经进入圈了。我们认为Y在C这个位置,在圈里面,C这个位置距圈的起点是C。那注意C可能为0,我们无论为不为0,我们后面会考虑其实为零的情况更简单。啊,C是小于A的。因为一圈才是A嘛。
我们把这个圈想象成。😊,一个线段式的东西,C是小于A的。我们认为这个其实C等于零的时候,它们是刚好相遇的。因为这个X走到了圈的起点,Y不管不管绕了多少圈,也走到了圈的起点。
但是我们不能证明就是呃第一次相遇的位置就是圈的起点。我们后面来看,我们认为Y领先了XC个位置。因为X刚进入圈,Y已经走了C了。但是由于它是一个圈,我们反着看,我们可以认为Y在追XX领先YA减C个位置。
😊,那么由于这个Y的速度是X的2倍,X每走一步。Y会走两步,所以每经过一步的时候,XY的距离会缩小一。所以经过A减C步的时候,他们一定相遇,这样就证明了这个如果有圈的话,在X进入圈的时候,再过A减C步。
X和Y就会相遇。那注意C等于零的时候,这个其实就是零部的时候,他们就相遇,这个后面可以统一处理掉。那么关键是如果第一次相遇的时候,圈的起点在哪里?C等于零的时候,第一次相遇就是圈的起点。
但是C不等于零的时候,第一次相遇的时候不一定是圈的起点。我们怎么找到圈的起点呢?😊,那么相遇的时候,我们考虑假设C等于0。这是简单的情况。那么X走了B,这是显然的它只走了那条线段的长度。
而Y呢它显然是走了一个线段的长度,又转了若干个圈,所以是B加上K乘以A,这个K是任意一个整数,我们可以不考虑它。那么。😊,由于Y的速度是X的2倍,所以Y走的距离还可以理解成2乘以B,所以这是一个等式。
那么我们就知道B等于K乘以A。也就是说那条线段的长度恰好就是若干个圈的长度。或者说饼的长度恰好是圈长的整数倍。那么。相遇的时候。他们其实是在圈的起点相遇的,相遇的时候,我们把X拉回起点。
Y呢还在这个圈的起点的位置。那么X再走一个饼长的时候,Y又转了若干个圈,这个和一开始是一样的,X没走一个B,Y就转若干圈。那么。😊,第二次相遇的时候,他们一定在圈的起点相遇。其实第一次已经试了。
我们不能确认我们把X拉回去,让X走一个线段的长度。那么Y在圈里面恰好也转了若干个圈,他们再次相遇的时候,就是圈起点的长度。😊,那么。C大于零小于A的时候。我们可以用类似的方法推出来,这个把X拉回去。
让X走一个线段的长度,Y又回到了圈的极点,为什么呢?😊,同样的分析,那么X走了多少?X走了,B加上A减C。刚才说了,在经过A减C的时候,他们再次相遇。而Y呢显然Y走的就是2乘以B加上A减C嘛。😊。
那么它又是什么呢?它又是B加上A减C。走了若干圈,因为YBX一定多走了若干个圈,因为他们在圈里面的同一个位置相遇。那么这个和这个是类似的,我们可以推出来B加上A减C是圈长的整数倍。什么意思呢?
注意这个第一次相遇地点距离圈起点的位置是A减C。我们在这个位置上,如果我们再走B的话,那么它恰好就应该到圈的起点。因为从A减C这个位置再走B的话,恰好经过了若干圈。所以这个意思是说。😊。
我把Y放在A减C这个位置,把X拉回到起点。那么X再走一个B,Y呢也走一个B,注意这时候把Y的速度变为和X一样,每次都走一步。X走BY也走B,注意Y前面先走了A减C,所以它走了A减C加B。
它恰好走了若干个圈的整数位。所以X走B的时候刚进入圈。😊,Y从A减C的位置也走B的时候,会回到圈的起点,他们再次相遇的时候,就是圈的起点。所以无论C等于零也好,不等于0也好。
我们都证明了从第一次相遇之后,我们把X拉回到这个最开始的位置。然后XY统一一步一步走,再次相遇的时候,他们会在圈的起点相遇。这样问题就简单了。代码写出来非常简单。大家重点是看分析,我们来写一下。😊。
先写那个。就是说用set的那个方法。我们保存这个之前的所有节点,再建立一个吧,一般列表不要丢头。T就表示这个T不是空。然后这个时候如果我们发现它。在集合里的话,那么这个。我们就找到了。
return零就是return空。那么这个写起来非常简单。啊,这里忘了一个把它加进来,注意千万不要忘了把它加到集合里面。啊,这是一个正确代码。那这个有一个空间复杂呃,空间复杂度是ON的。
时间复杂度是N log n的。因为这有个查询。那如果我们想把时间复杂度降低一点的话,可以用一个an order set,这个就相当于哈希了,查找是O一的,但是空间是免不了的。也是一个正确的。
关键刚才说的第二种方法。我们刚才说了。这个。定义两个变量。从开头开始。怎么做?刚才说X每次走一步,Y每次走两步。当然,为了防止这个空引用,因为一上来就有可能是空。我们把这些都避免掉。拜呢。外每次走两步。
每一次都要看它是不是空。就是每次X走一步,Y走两步,直到呢他们能相遇。这个时候退出循环的时候,X和Y就相等了,相等了就说明有圈有圈怎么办?刚才说了,把X拉回起点。那我就这么写吧。X如果和Y不相等的话。
X走一步。外呢也走一步。那别忘了,最后return的就是X也好,Y也好,再次相遇的时候,那个地点。也是一个正确代码。那么这个还是比较简单的一个问题。
那我们来看第二个题。
这个其实呃相对比较简单了。但是它比较新,就是把这个奇数和偶数位置节点分开,然后把这个偶数节点的位置放到奇数节点后面,关键是怎么做,先把奇数的放到一起,然后偶数的放到一起,再把偶数的接到奇数的后面。
关键是每个列表都可能是空。我们要记录头记录尾,记录空,记录各种情况。
其实写起来并不难,关键是一些这个细节上的处理。这个I就表示它是第几个那个什么第几个位置。真真爱。然后呢,我们把基数我们定义一些节点,基数位置的头。基数位置的尾。偶数位置的头。偶数位置的尾。
这个是基数位置,基数位置我就要把它接到基数位置上面去。那么怎么接?注意,一开始列表可能是空的。如果这个t一就是基数位置的尾不是空的,那么说明这个列表有东西。我就可以把它。接上来。
否则呢注意这个ele一是空的的时候,had一一定也是空的,表示没有节点,我把它复过来。那边是对称的。如果t2不是空的,那么把它接上来,注意要更新尾,所以这个顺序是很重要。
这个最后一个节点的next的先等于它,然后再把最后一个节点复过来。否则呢一样的headd2等于t2等于headd。这样我就把它拆分为两个列表了,这里面还有一些细节,注意这个假设两个列表都不是空的话。
t一的nex的值我要指向hat2,然后t2的nex值我要指向空,这些我都还没有做,所以还是要做。如果他有一。tele一不是空,表示第一个列表不是空。那我们把t一的nex指指向headide2。
如果TO一是空呢。说明第一个列表是空的。那么我就把第一个列表的头指向第二个列表的头。那么这样我最后结果就是这个headd一了。那么别忘了把最后一个节点的nex值付成空。那么最后一个节点是什么呢?
无论如何最后一个节点都应该是t2,我们看一下。如果t2不是空,t2的nex值浮成0。那如果t2是空,说明headd二也是空,那么我们把t一的nex复成0,在这已经负了。所以这是很多细节问题。
最后returnhead一。这是一个正确的代码。
那么这个问题也不是特别特别难,比较难的。其实我个人觉得还是例一,然后主要考虑的就是这种啊边界问题啊,一些细节问题。那么另外一个也非常简单,就是单列表的排序。那么。
这个要求我们是Nlog的时间,还要求我们O一的空间。当然我们空间暂且不考虑我们。注意快速排序,在理想的情况下,或者说在平均的情况下可以达到N log根,它并不能保证Nlog根。
所以这个题如果写一个快速排序的话,我并不能就是说认为它一定是对的。当然这个过还是能过的。我建议写的还是归并排序,那么规并排序,当然有递归和非递归非递归我打的星号不太容易实现。但是递归的话。
这个它有这个递归对栈的空间,要不要计算的问题,它空间是不是O一的问题,这也是一些细节。这个我就写一个递归的这个非递归的留给大家自己去思考。那么我来写这个递归的呃,链表单链表的排序。
那么显然还是有些边界条件。
那如果单链表是空。或者呢。单列表的。只有一个元素,那么就是它的nex是空。我就把它直接返回去,这样边界条件我就处理完了,否则呢。😊,我希望把单链表从中间劈开。当然这个P开其实我可以数一下它的长度。
但是呢我用另外一种方法,什么什么方法呢?就是跟刚才讲的那个XY很相像,我用A和B,然后呢。B每次走两步。A呢每次走一步。这样B每次走了两步,A呢每次走一步。这样当B走到结尾的时候,A恰好在终点。
这个时候。注意这个退出的时候,这个要么B的nex是空,要么B的nex next是空。但无论如何,B走到了这个链表的结尾,那么A呢恰好在终点。那么我们对这个。A的 next做一下排序。相当于把列表切开了。
切成了A的 next和had这两部分。我们把A的 next进行一下排序,别忘了把A的 next复成0。这时候列表就分成两部分,一部分是以had开头的,一部分是以B开头的。注意这是一个低归调用。
我们假设对A的nex排好序返回表的头,我们又付给B了。因为这个B走到结尾,这个B的这个指针就没有用了。那么下面我们要做的就是。head和B是两个有序的列表。我们这个。呃。
这个下面我要我们要做的是B是排好序的,害的还没有排序。我们把这个。ha也排好序,这时候A和B就是两个有序的链表。我们下面要做的就是墨着它。两个有序练表的合并还是就是不是特别难的。我们看一下。如果。
较A较小。注意每次都要考虑这个tail的这个是不是空。这个和刚才是类似的,如果失空的,hi的别忘了复过来。否则呢。是。哦,这里别忘了A等于A nextex,不然死循环了。这个就是哪个小就把它接上来。
然后把它指向下一个。那么最后还有一些尾巴要处理,就是A嘛,有可能是A,也有可能是B,但是不可能既是A又是B。因为这个wellA和B这个至少有一个是空的时候才会退出来。这个也是把A整个这个列表接上去。
别忘了return。
这也是一个正确代码,这是一个经典的规并排序的过程。
那么。
这是我想讲第三个题。第四个题,其实就是这个有序的单列表的节点去重,它是立的 code上82题和83题。这个去重的方法这个不太一样,一个是重复的节点一个都不一要,只要是重复的,我就扔。
另外一个是这个重复的节点,我只保留一个。嗯,无论是哪种方法,跟刚才的方法就是思想是类似的,保存一个列表头,一个列表尾注意就是空啊等等这些情况,我们来看一下。
这个是liical82题,他说的什么?只要是重复的,我就这个把它扔掉。那我们来看一下这个怎么做。我还是用head一t有一。那我们看一下。关键是我们怎么找到这些就是重复的节点。如果害的不为空。
我们把一连串的重复的节点都弄进来。这个是value6。这两个条件同时满足的话。就说明这个。就说明我重复了。注意有这个。把它定义到外面了。我们用一个节点顺着这个had走,had next如果和到相等的话。
那么它是重复。如果是重复,我就一直的走走到不重复为止。那么这个时候别忘了这个had等于temple,它一定要等于到下一个节点上面去,这是最后要做的工作。如果不重复。那么刚才说了。
我们就把这个headd接过来怎么做这个。跟刚才做法一样,如果t一不是空,t一等于t一的next等于head。如果它有E是空,had一等于t有一等于had。然后我们就把它接上来。
然后并且呢这个headd在这里面会更新。然后最后别忘了嗯,tll一的next要指向。空ong。return是ha一。先编译一下。哦,好像是唱试的有点快。这也是一个正确的代码。那么对于那种问题。
其实和这个是相像的,我们就是。可以copy这个代码。关键是这个是重复的,我只我只做一次。那么其实这个做法更简单,因为链表本身是有序的嘛,我可以不要这个。那么。对于had的话。
我判断它和之前的那个tail是不是相等就好了。我们让当前这个ha和前面那个tail不相等的话。我们就把它接上,就是这个时候要么它是空,要么就是它的value和head的 value不相等。
我们就把它接上。那具体怎么接,还是刚才那个过程。如果t一不是空,t一等于t一的next。We had。如果他由一是空的。ha一等于t有一。什么我想编一下。啊,这也是一个正确的代码。
今天的题目其实并不是特别的难,最后大概做一下总结。
这个链表和数组不一样,列表可以拆开。比方说刚才讲的那个什么奇奇偶位置分开啊,包括刚才讲的规并排序,我自己可以新建一个头,什么叫新建一个头?刚才比较典型的那个基偶这个节点分开。
我可以把基数位置放到一个小列表里面,偶数位置放到另外一个小列表里面。总而言之,我可以把一个列表按照我自己的意愿拆成若干段,这个和数组不一样,每一段单独建一个头。
那么一般的思路其实考虑起来都是考虑如果插入的话,考虑插入前的位置和插入后的位置,哪些指针需要修改。无论单列表双链表循环列表都是这个思路删除也一样,都是考虑当前位置前后的这个指针,哪些指针需要修改。
那么比较特殊的,一个是列表头,一个是列表尾,还有一个当然是这个空,就是列表里面的空值。那注意要避免map呀set这种东西,基本上列表的题目用不着这么高级的数据结构。但当然也有例外,比方说这个。😊。
LRU catch这个146题,这比较难的,它可以用链表加上map等等一系列复杂的结构来实现。那么还有一个是复杂列表复制,今天时间关系没来得及讲。这个其实lea code上列表相关的问题都比较多。
那么这个大家可以搜一下,比方说关键词是link或者list,这样题非常多,我大概列了几个表星号的比较难。那前面呢还是比较容易的。当然第一个不是let code的问题,大家可以想一下。
这个在面试中也有可能出现,也就是这个循环列表的插入有序,就是说我给你一个圈,我已经告诉你它是一个圈了。😊,并且它是有序的,它有可能是单链表,也有可能是双链表,它是个循环链表。那么比方说12345。
我给你一个0应该插入到哪,我给你一个6,应该插入到哪儿,我给你一个普通的数。4、比方说应该插入到那儿,那插入的位置有可能是就是圈的起点,当是圈是没有起点,就是最小值的前面,最大值的后面。
其实和最小值的前面是一个位置,这个是循环列表的这个特性。那么。多个有序列表的合并。其实刚才讲规并的时候讲的是两个有序列表的合并。那么多个大家考虑一下,是不是可以两两合并,还是就是呃一2合并起来。
三四合并起来,5六合并起来,这样一层一层合并,还是一二合并起来,再把三放进去,再把四放进去,就是一次性的这样合并起来。其实这两个复杂度是不是就不太一致的。大家可以考虑一下,其实按层来分会好一些。
当然还有一种方法就是用一个堆把每个列表的这个开头都存起来,一个一个再拿出来。那么链表翻转,这是比较简单的问题,这是列表这个比较典型的let头 code上的问题。😊,好,今天的课就到这里。
欢迎大家给我提出批评指正,谢谢大家。😊。