首页/文章/ 详情

PyTorch | 计算高阶导数

1年前浏览2069

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>
classNet :public torch::nn::Module
{
public:
    at::Tensor forward(at::Tensor x, at::Tensor y)

return torch::sin(x) * torch::cos(y); 
    }
};
intmain(int argc, char* argv[])
{
// 构造输入数据
constint 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)
        {
constfloat posiX = xT[i][j].item<float>();
constfloat posiY = yT[i][j].item<float>();
constfloat sinX = std::sin(posiX);
constfloat cosX = std::cos(posiX);
constfloat sinY = std::sin(posiY);
constfloat 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();
return0;
}
.
     
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工程师 签名征集中
获赞 48粉丝 98文章 45课程 9
点赞
收藏
未登录
还没有评论
课程
培训
服务
行家
VIP会员 学习 福利任务 兑换礼品
下载APP
联系我们
帮助与反馈