当前位置:首页|资讯|机器学习|深度学习|编程

究极萌新的机器学习/深度学习之旅(1)

作者:井木木猪猪发布时间:2023-12-01

    靠,过于懒惰和颓废了,开学到现在属于是废学忘习,打游戏跟睡觉倒是不少。(最近刺客信条枭雄免费!这两天给我打爽了,顺便打折买了大革命,打完枭雄就打大革命!大革命!)

    上学期上大数据课随便写了个毫无营养的机器学习的专栏,我在那里头还立了个flag,说以后还得学机器学习。这学期因为各种阴差阳错,以后方向还真变成搞算法敲代码了,属于是被自己的回旋镖击中了(问题是我真一点不会敲代码啊╥﹏╥,大一的python基础早都忘完了╥﹏╥),所以说只能从头来了。而且也快期末考试(暂态和继保是真难学啊)和毕设开题了,我要重新做人!先写个简短的专栏,来一点点pytorch基础,没啥实质性内容,算是这个究极萌新系列的引子吧,也希望我未来能写一点更有技术含量的专栏。

    PyTorch中最基础的数据结构就是张量(Tensor),可以理解为一种多维数组。经过封装后的多维数组,可以很方便地进行多种科学运算操作

可微分张量

    PyTorch可以实现自动求导,自动求导由每一个**可微分**张量来实现。对于一个可微分张量,在其初始化时,需要设置其属性.requires_frad为True,使得之后所有依赖于它的各种运算中间结果的张量都是可微分的,从而能够进行**反向传播**过程。

    初始化可微分张量的代码如下:

    有了可微分张量,就可以进行反向求导,例如,求上面c对a的导数,可以用以下方式

    想要关闭自动求导有两种方法:

    1.使用torch.no_grad()环境,将代码包括起来,停止自动求导。代码如下:

    2.将计算流程中的一个中间计算变量,从自动求导的流程图中排除出去,调用.detach_()方法

    在我尝试这段代码时,发现不能够连续两次调用 tensor.backward()。

    在尝试运行以上代码时,出现了以下报错:

      翻译下就是:

a%20%5Crightarrow%20b%20%5Crightarrow%20c%20%5Crightarrow%20d。在完成一次反向求导d.backward()后,这张流程图就被删掉了,不能再次使用这张图对已经保存的张量进行反向求导了。

    给出的解决方案是:“在使用d.backward()时,设定参数retain_graph = True,即改成d.backward(retain_graph = True)”。

    这样,就会在完成一次反向求导之后,仍然保留原来的流程图,也就能进行第二次反向求导操作了。


### Function:实现自动微分的基础


    在前面所述的对Tensor数据类型的简单探索中,我们发现,当我们利用print()函数输出一个tensor时,输出的结果除了这个张量自身,还会附带有“grad_fn=”的信息。这是PyTorch的自动微分引擎在计算每个Tensor时,记录其对应的求导函数。所以,实际上的自动求导也并非完全“自动”,二十通过逐步回溯用前向函数对应的反向传播函数来实现的。在Pytorch中就是通过Function这个类实现的,这个类也可以被集成,并且允许我们自定义。

    例如下面的代码    

    这串代码的输出结果是下面这样:

    根据输出结果,我们对Pytorch中的自动求导的实现过程就可以有更进一步的理解了(写在了代码注释中)。

    可以看到,在前向过程中,通过ctx.save_for_backward(w, b)语句,自定义保存在反向传播过程中需要用到的张量。而在反向过程中,通过ctx.saved_tensors语句,调用在前向传播过程中保存的张量。

    在反向过程中,参数grad_outputs有点奇怪,这是个什么东西呢?根据输出的结果来看,这个值是1,让我们更改代码,将loss = y.sum()改成loss = y**2,看看会发生什么。

    更改后的输出结果如下:    

    可以看到,这次的grad_outputs变成了10,也就是2y。

    还可以经过多次更改loss的定义,看看会发生什么变化。

grad%5C_%20outputs%3D%5Cfrac%7B%5Cpartial%20loss%7D%7B%5Cpartial%20y%7D。例如在这个代码中,我们用到的反向传播是loss.backward(),想要求的是loss对x、w、b的导数,但前向过程所输出的结果是y。相当于要对复合函数求导,即:%5Cfrac%7B%5Cpartial%20loss%7D%7B%5Cpartial%20x%7D%3D%5Cfrac%7B%5Cpartial%20loss%7D%7B%5Cpartial%20y%7D%20%5Cfrac%7B%5Cpartial%20y%7D%7B%5Cpartial%20x%7D。但我们只定义了y对x的导数,所以要多乘一步grad_outputs加以修正。

    还有一点就是,这里使用Function类,反向传播过程的求导公式是我们自己定义的:

    这就很奇怪,明明之前说pytorch可以自动求导,导数还要自己定义,这也太不自动了吧,明明之前不定义Function类的时候就能够自动求导了,现在还要自己先算一遍再定义到反向传播函数里,这难道不是画蛇添足?

    的确,如果我们只需要在反向传播过程中求取因变量对自变量的导数,定义申明Function类的确是不必要的。但了解Function类还是有必要的,在未来的实际应用中,我们在反向传播过程中不一定只用到求导这一种操作,可能需要自定义新的函数(例如在BP算法中可能我们要定义新的误差函数),这时候自然就需要应用Function类进行操作了。



Copyright © 2024 aigcdaily.cn  北京智识时代科技有限公司  版权所有  京ICP备2023006237号-1