深度学习细节

Pytorch

nn内部的关系

  1. 叶子张量leaf tensor: 反向传播时,只保留属性requires_grad和is_leaf为真的导数

  2. requires_grad为真,is_leaf为假时,此张量的导数作为中间结果用于计算叶子节点的导数

  3. requires_grad为假,is_leaf为假时,此张量不参与求导。

  4. 叶子张量的作用:节省内存或者显存

  5. 叶子节点的grad_fn都为空

  6. 非叶子节点的grad_fn都不为空

  7. 如果想保留中间变量的导数,通过使用tensor.retain_grad()

  8. 如果我们只想debug,只需要输出中间变量的导数信息,而不需要保存他们,我们还可以使用tensor.register_hook

  9. inplace操作:在不更改变量内存地址的情况下,直接修改变量的值就叫做inplace操作。在autograd中记录的是变量的地址值

  10. pytorch通过tensor._version检测tensor发生了inplace操作

  11. 每次tensor进行inplace时,_version的值就会加1,在正向传播过程中,求导系统记录的b的version是0,但是反向传播过程中,求导系统发现b的version变成了1。
    对于requires_grad=True的叶子节点的值,在求梯度之前,是不允许修改的。

  12. 在backward之前,我们想修改叶子节点,必须按照一定的规则
    第一种方法:a.data.fill_()
    第二种方法:with torch.no_grad():

  13. nn.Conv2d如果后面加BN操作就不用加偏置,因为加不加偏置在BN之后效果一样,加入偏置后反而会增加显存

    numpy

  14. np.randm.choice(x,1)# 从x中随机选一个数

    Cuda编程

    在这里插入图片描述
    Grid管理Block,Block管理线程Thread

    cuda中threadIdx、blockIdx、blockDim和gridDim的使用

  15. threadIdx是一个uint3类型,表示一个线程的索引

  16. blockIdx是一个uint3类型,表示一个线程块的索引,一个线程块中通常有多个线程。

  17. blockDim是一个dim3类型,表示线程块的大小。

  18. gridDim是一个dim3类型,表示网格的大小,一个网格中通常有多个线程块。
    在这里插入图片描述

    pycuda

    gpuarray.to_gpu(cpu_data)把cpu的数据转到gpu上,但是之后直接去进行运算操作会比价慢。
    更快的方法为:
    gpu_2x_ker=ElementwiseKernel(
    “float in, float *out”,
    “out[i] = 2
    in[i];”,
    “gpu_2x_ker”
    )

import pycuda.autoinit
import pycuda.gpuarray as gpuarray
import numpy as np
import pbd

num = 4
A = np.random.rand(num)
B = np.random.rand(num)
A_GPU = gpuarray.to_gpu(A.astype(np.float32))
B_GPU = gpuarray.to_gpu(B.astype(np.float32))
C_GPU = A_GPU + B_GPU
C = C_GPU.get()
print('A=', A)
print('B=', B)
print('C=', C)

pycuda写hello world

# 对cuda初始化
import pycuda.autoinit
# SourceModule C++ 源码编译为python
from pycuda.compiler import SourceModule
kernel_code = r"""
__global__ void hello_from_gpu(void)
{
    printf("[%d,%d] Hello World from the GPU!\n,threadIdx.x,blockIdx.x");
}
__global__ void hello_from_gpu_2(void)
{
    printf("[%d,%d] Hello World from the GPU! the second func\n",threadIdx.x, blockIdx.x);
}
"""
# 编译
mod = SourceModule(kernel_code)
# 获取函数
hello_from_gpu = mod.get_function("hello_from_gpu_2")
# 利用多线程来调用我们定义的函数
hello_from_gpu(block=(1,1,1))
# 核函数的执行次数就是里面的数字的乘积
# 实现加法并行计算
import pycuda.autoinit
from pycuda.compiler import SourceModule
import pycuda.gpuarray as gpuarray
import numpy as np

# 使用核函数
# threadIdx:计算线程
# 第n号线程将x[n]与y[n]相加后存入z[n]
mod = SourceModule(r"""
void __global__ add(const float *x, const float *y, float *z)
{
    const int n = blockDim.x *blockIdx.x + threadIdx.x;
    z[n] = x[n] + y[n];
}
void __global__ mul(const float *x, const float *y, float *z)
{
    const int n = blockDim.x * blockIdx.x + threadIdx.x;
    z[n] = x[n]*y[n];
}
""")

add = mod.get_function("add")
mul = mod.get_functon("mul")
num  = 8
A = np.random.rand(num,num)
B = np.random.rand(num,num)
C = np.zeros([num,num])
A_GPU = gpuarray.to_gpu(A.astype(np.float32))
B_GPU = gpuarray.to_gpu(B.astype(np.float32))
C_GPU = gpuarray.to_gpu(B.astype(np.float32))
add(A_GPU, B_GPU,C_GPU,grid=(2,),block=(4,1,1))

  转载请注明: 叮当喵的Blog 深度学习细节

  目录