Pytorch
nn内部的关系
叶子张量leaf tensor: 反向传播时,只保留属性requires_grad和is_leaf为真的导数
requires_grad为真,is_leaf为假时,此张量的导数作为中间结果用于计算叶子节点的导数
requires_grad为假,is_leaf为假时,此张量不参与求导。
叶子张量的作用:节省内存或者显存
叶子节点的grad_fn都为空
非叶子节点的grad_fn都不为空
如果想保留中间变量的导数,通过使用tensor.retain_grad()
如果我们只想debug,只需要输出中间变量的导数信息,而不需要保存他们,我们还可以使用tensor.register_hook
inplace操作:在不更改变量内存地址的情况下,直接修改变量的值就叫做inplace操作。在autograd中记录的是变量的地址值
pytorch通过tensor._version检测tensor发生了inplace操作
每次tensor进行inplace时,_version的值就会加1,在正向传播过程中,求导系统记录的b的version是0,但是反向传播过程中,求导系统发现b的version变成了1。
对于requires_grad=True的叶子节点的值,在求梯度之前,是不允许修改的。在backward之前,我们想修改叶子节点,必须按照一定的规则
第一种方法:a.data.fill_()
第二种方法:with torch.no_grad():nn.Conv2d如果后面加BN操作就不用加偏置,因为加不加偏置在BN之后效果一样,加入偏置后反而会增加显存
numpy
np.randm.choice(x,1)# 从x中随机选一个数
Cuda编程
Grid管理Block,Block管理线程Threadcuda中threadIdx、blockIdx、blockDim和gridDim的使用
threadIdx是一个uint3类型,表示一个线程的索引
blockIdx是一个uint3类型,表示一个线程块的索引,一个线程块中通常有多个线程。
blockDim是一个dim3类型,表示线程块的大小。
gridDim是一个dim3类型,表示网格的大小,一个网格中通常有多个线程块。
pycuda
gpuarray.to_gpu(cpu_data)把cpu的数据转到gpu上,但是之后直接去进行运算操作会比价慢。
更快的方法为:
gpu_2x_ker=ElementwiseKernel(
“float in, float *out”,
“out[i] = 2in[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))