首页/文章/ 详情

PyTorch | 计算高阶导数

1年前浏览2967


0

内容提要


 

计算图是机器学习/深度学习中非常重要的概念,可以更好地表示数学表达式,通过反向传播算法可求得其导数,本文将基于 PyTorch C++ 前端简要介绍相关计算过程。  

 

1

计算图


 
在本文中,我们用计算图来表达下面函数,

具体的计算图如下所示  

其 PyTorch C++ 实现代码如下


2

反向传播计算梯度


 

通过反向传播算法计算梯度的数学本质是链式法则(也称为复合函数求导)。 在上面计算图的表示形式上,如果我们想要求输出 z 对输入 x 和 y 的导数,便可以利用链式法则计算。


这里,我们引入下面两个符号

那么根据链式法则,反向看上面的计算图可以得到

同样的,我们对一阶导数使用链式法则求导便可求得二阶及更高阶导数。代码实现如下所示

3

结果分析


 

在此,我们在 [-5,5]x[-5,5] 的定义域上计算函数 z 的值及其导数,左侧为通过计算图计算得到的数值,右侧为解析结果,左右两侧图像均一致,函数值及导数图像如下所示。


  

 

4

完整代码

   
.

     
main.cpp
#include <iostream>
#include <fstream>
#include <vector>
#include <torch/torch.h>

class Net : public torch::nn::Module
{
public:
    at::Tensor forward(at::Tensor x, at::Tensor y) 
    

        return torch::sin(x) * torch::cos(y); 
    }
};

int main(int argc, char* argv[])
{
    // 构造输入数据
    const int numPointsPerDim = 101;
    std::vector<float> x;
    std::vector<float> y;
    for (int i = 0; i < numPointsPerDim; ++i)
    {
        for (int j = 0; j < numPointsPerDim; ++j)
        {
            x.push_back((i - numPointsPerDim / 2) * 0.1);
            y.push_back((j - numPointsPerDim / 2) * 0.1);
        }
    }
    // 转成张量
    torch::Tensor xT =
        torch::from_blob(x.data(), {numPointsPerDim, numPointsPerDim}, torch::kFloat)
            .requires_grad_(true);
    torch::Tensor yT =
        torch::from_blob(y.data(), {numPointsPerDim, numPointsPerDim}, torch::kFloat)
            .requires_grad_(true);
    torch::Tensor weight = torch::ones_like(xT);

    std::shared_ptr<Net> net = std::make_shared<Net>();

    // 函数值 z = sin(x) * cos(y) 
    torch::Tensor z = net->forward(xT, yT);

    // 一阶导数
    torch::Tensor dzdx = torch::autograd::grad({z}, {xT}, {weight}, truetruefalse)[0];
    torch::Tensor dzdy = torch::autograd::grad({z}, {yT}, {weight}, truetruefalse)[0];

    // 二阶导数
    torch::Tensor d2zdx2 = torch::autograd::grad({dzdx}, {xT}, {weight}, truetruefalse)[0];
    torch::Tensor d2zdy2 = torch::autograd::grad({dzdy}, {yT}, {weight}, truetruefalse)[0];
    torch::Tensor d2zdxy = torch::autograd::grad({dzdx}, {yT}, {weight}, truetruefalse)[0];
    torch::Tensor d2zdyx = torch::autograd::grad({dzdy}, {xT}, {weight}, truetruefalse)[0];

    // 输出数据
    std::ofstream os;
    os.open("results.csv");
    for (int i = 0; i < numPointsPerDim; ++i)
    {
        for (int j = 0; j < numPointsPerDim; ++j)
        {
            const float posiX = xT[i][j].item<float>();
            const float posiY = yT[i][j].item<float>();

            const float sinX = std::sin(posiX);
            const float cosX = std::cos(posiX);
            const float sinY = std::sin(posiY);
            const float cosY = std::cos(posiY);
            //
            os << posiX << ',' << posiY << ',';
            os << z[i][j].item<float>() << ',' << sinX * cosY << ',';

            os << dzdx[i][j].item<float>() << ',' << cosX * cosY << ',';
            os << dzdy[i][j].item<float>() << ',' << sinX * -sinY << ',';

            os << d2zdx2[i][j].item<float>() << ',' << -sinX * cosY << ',';
            os << d2zdy2[i][j].item<float>() << ',' << sinX * -cosY << ',';
            os << d2zdxy[i][j].item<float>() << ',' << cosX * -sinY << ',';
            os << d2zdyx[i][j].item<float>() << ',' << cosX * -sinY;
            //
            os << std::endl;
        }
    }
    os.close();

    return 0;
}

 

.

     
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)

project(autograd LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Torch REQUIRED PATHS "/opt/libtorch/share/cmake/Torch")
include_directories( ${TORCH_INCLUDE_DIRS} )

add_executable(autograd main.cpp)
target_link_libraries(${PROJECT_NAME} ${TORCH_LIBRARIES})
 
  
.

       
plot.py
#-*- coding:utf-8 -*-

import matplotlib.pyplot as plt 
import numpy as np

titles = ["z_numerical","z_analytical",
          "dzdx_numerical""dzdx_analytical",
          "dzdy_numerical""dzdy_analytical",
          "d2zdx2_numerical""d2zdx2_analytical",
          "d2zdy2_numerical""d2zdy2_analytical",
          "d2zdxy_numerical""d2zdxy_analytical",
          "d2zdyx_numerical""d2zdyx_analytical",
          ]

fig = plt.figure( figsize=(20,20) ) 
ax = plt.axes(projection='3d')

data = np.loadtxt("./results.csv", delimiter=",")

x = data[::,0]
y = data[::,1]
X = x.reshape(101,101)
Y = y.reshape(101,101

for idx in range(2,16):
    z = data[::,idx]
    Z = z.reshape(101,101)
    ax.plot_surface(X,Y,Z,cmap='rainbow'

    plt.title(titles[idx-2],fontsize=35)

    plt.savefig("FIGURE_"+str(idx+1)+".png", bbox_inches='tight')
    plt.cla()
 




来源:有限元术
科普理论python
著作权归作者所有,欢迎分享,未经许可,不得转载
首次发布时间:2023-01-10
最近编辑:1年前
寒江雪_123
硕士 | cae工程师 签名征集中
获赞 50粉丝 108文章 60课程 9
点赞
收藏
未登录
还没有评论
课程
培训
服务
行家
VIP会员 学习计划 福利任务
下载APP
联系我们
帮助与反馈