国产化环境实战:手把手教你在银河麒麟系统为QGIS 3.26添加自定义插件支持
2026/5/6 17:28:30
本文带你走进算子的神奇世界,用最生动的方式理解这个深度学习中最基础、最重要的概念
想象一下,你要用乐高积木搭建一座城堡。你不需要从零开始烧制每块积木,而是直接使用现成的积木块(长方形、正方形、门窗等),把它们组合起来。
算子(Operator)就是深度学习的乐高积木!
# 这是深度学习中的"乐高搭建"importtorch# 现成的"积木块"(算子)x=torch.randn(10,20)# 创建张量y=torch.randn(10,20)# 搭建过程(组合算子)z=x+y# 加法算子z=torch.relu(z)# 激活函数算子z=torch.matmul(z,w)# 矩阵乘法算子| 深度学习 | 厨房烹饪 | 说明 |
|---|---|---|
| 张量(Tensor) | 食材 | 待处理的数据 |
| 算子(Operator) | 厨具/操作 | 切菜刀、炒锅、烤箱 |
| 模型(Model) | 完整菜品 | 组合各种操作的结果 |
| 前向传播 | 烹饪过程 | 按顺序执行各个步骤 |
| 反向传播 | 调整食谱 | 根据结果调整操作 |
# 深度学习"做菜"流程input_data=...# 准备食材x=conv2d(input_data,weights)# 切菜(卷积)x=batch_norm(x)# 腌制(批归一化)x=relu(x)# 爆炒(激活)output=linear(x,weights)# 摆盘(全连接)f(x) = Wx + b # 线性变换 σ(x) = 1/(1+e^(-x)) # Sigmoid函数# PyTorch风格output=torch.nn.functional.linear(input,weight,bias)output=torch.sigmoid(input)# TensorFlow风格output=tf.layers.dense(input,units)output=tf.nn.sigmoid(input)// C++实现,考虑性能优化 Tensor linear_impl(const Tensor& input, const Tensor& weight) { // 各种优化:内存布局、并行计算、向量化 if (input.is_contiguous() && weight.is_contiguous()) { return optimized_matmul(input, weight); } else { return fallback_matmul(input, weight); } }; 针对特定硬件的指令 vfmadd231ps zmm0, zmm1, zmm2 ; AVX-512指令集让我们跟踪一个算子从定义到执行的完整过程:
# 1. 用户代码层(简单直观)importtorch x=torch.tensor([1.0,2.0,3.0])y=torch.tensor([4.0,5.0,6.0])z=x+y# 加法算子# 2. 框架调度层(自动选择最优实现)""" 当执行 x + y 时,PyTorch会: 1. 检查张量设备:CPU还是GPU? 2. 检查数据类型:float32还是float64? 3. 检查形状:是否支持广播? 4. 选择最优实现:调用对应的底层函数 """# 3. 算子分发层defadd_dispatch(input1,input2):ifinput1.is_cudaandinput2.is_cuda:returncuda_add(input1,input2)# GPU版本else:returncpu_add(input1,input2)# CPU版本# 4. 硬件执行层# CUDA Kernel 或 CPU指令实际执行计算# ---------- 基础算术算子 ----------x+y,x-y,x*y,x/y# 四则运算x @ y# 矩阵乘法x**2# 幂运算torch.sqrt(x)# 平方根# ---------- 张量操作算子 ----------torch.reshape(x,shape)# 改变形状torch.transpose(x,dim0,dim1)# 转置torch.cat([x,y],dim=0)# 拼接torch.split(x,split_size)# 分割# ---------- 神经网络算子 ----------torch.nn.Conv2d(in_channels,out_channels,kernel_size)torch.nn.Linear(in_features,out_features)torch.nn.BatchNorm2d(num_features)torch.nn.Dropout(p=0.5)# ---------- 激活函数算子 ----------torch.relu(x)# ReLUtorch.sigmoid(x)# Sigmoidtorch.tanh(x)# Tanhtorch.nn.functional.gelu(x)# GELU# ---------- 损失函数算子 ----------torch.nn.CrossEntropyLoss()torch.nn.MSELoss()# 均方误差torch.nn.L1Loss()# L1损失# ---------- 优化相关算子 ----------torch.optim.SGD(params,lr)# 优化器torch.autograd.grad(output,input)# 梯度计算# 纯Python算子(教学用,理解原理)defnaive_matmul(A,B):"""朴素的矩阵乘法"""m,n=A.shape n,p=B.shape C=torch.zeros(m,p)foriinrange(m):forjinrange(p):forkinrange(n):C[i,j]+=A[i,k]*B[k,j]returnC# 优化算子(生产用,极致性能)defoptimized_matmul(A,B):"""高度优化的矩阵乘法"""# 使用BLAS库、并行化、内存优化等returntorch.matmul(A,B)# 调用底层优化实现ReLU定义:f(x) = max(0, x)
defrelu_naive(tensor):"""最简单的ReLU实现"""result=tensor.clone()# 复制输入foriinrange(tensor.numel()):ifresult.view(-1)[i]<0:result.view(-1)[i]=0returnresult# 缺点:效率极低,Python循环慢defrelu_vectorized(tensor):"""向量化实现"""result=tensor.clone()result[result<0]=0returnresult# 优点:利用数组操作,速度快# PyTorch的ReLU底层实现(简化版)classReLUFunction(torch.autograd.Function):@staticmethoddefforward(ctx,input):# 前向传播:计算输出ctx.save_for_backward(input)# 保存输入供反向传播用output=input.clone()output[output<0]=0returnoutput@staticmethoddefbackward(ctx,grad_output):# 反向传播:计算梯度input,=ctx.saved_tensors grad_input=grad_output.clone()grad_input[input<0]=0# 输入<0的位置梯度为0returngrad_input# 封装成易用的函数defrelu_custom(input):returnReLUFunction.apply(input)// ReLU的CUDA Kernel实现 __global__ void relu_kernel(float* input, float* output, int n) { int idx = blockIdx.x * blockDim.x + threadIdx.x; if (idx < n) { output[idx] = input[idx] > 0 ? input[idx] : 0; } } // Python封装 def relu_cuda(tensor): output = torch.empty_like(tensor) n = tensor.numel() threads = 256 blocks = (n + threads - 1) // threads relu_kernel[blocks, threads](tensor.data_ptr(), output.data_ptr(), n) return output这是深度学习框架的魔法之源!算子不仅能计算前向传播,还能自动计算反向传播的梯度。
importtorch# 创建需要梯度的张量x=torch.tensor([1.0,2.0,3.0],requires_grad=True)w=torch.tensor([0.5,1.5,2.5],requires_grad=True)b=torch.tensor(0.1,requires_grad=True)# 前向传播(组合算子)y=x*w# 逐元素乘法算子z=y.sum()+b# 求和算子 + 加法算子loss=z**2# 平方算子# 魔法发生:自动计算所有梯度!loss.backward()print("x的梯度:",x.grad)# ∂loss/∂xprint("w的梯度:",w.grad)# ∂loss/∂wprint("b的梯度:",b.grad)# ∂loss/∂b背后的计算图:
输入: x, w, b │ ├─ 乘法算子 → y = x * w │ ├─ 求和算子 → s = y.sum() │ ├─ 加法算子 → z = s + b │ └─ 平方算子 → loss = z²# 常见模式:卷积 → 批归一化 → ReLUx=conv(x,weight)# 算子1:卷积x=batch_norm(x)# 算子2:批归一化x=relu(x)# 算子3:ReLU# 问题:需要3次内存读写,启动3个Kernel# 融合成一个算子:Conv+BN+ReLUx=fused_conv_bn_relu(x,weight,bn_weight,bn_bias)# 优点:1次内存读写,启动1个Kernel,极大提升性能# 原始GeLU:用多个基本算子实现defgelu_naive(x):returnx*0.5*(1.0+torch.erf(x/math.sqrt(2.0)))# 融合后的近似GeLU(速度更快)defgelu_fast(x):return0.5*x*(1+torch.tanh(math.sqrt(2/math.pi)*(x+0.044715*x**3)))# 不同抽象层次的算子调用# 高层次:nn.Module(最常用)importtorch.nnasnn layer=nn.Conv2d(3,64,kernel_size=3)output=layer(input)# 中层次:函数式接口importtorch.nn.functionalasF output=F.conv2d(input,weight,bias)# 低层次:直接调用底层output=torch._C._nn.conv2d(input,weight,bias,stride=1,padding=0)// C++中注册算子 TORCH_LIBRARY(my_ops, m) { // 注册一个名为"myop"的算子 m.def("myop(Tensor input) -> Tensor"); } // 实现算子 Tensor myop_impl(const Tensor& input) { // 具体实现 return output; } // 绑定实现 TORCH_LIBRARY_IMPL(my_ops, CPU, m) { m.impl("myop", myop_impl); }Swish定义:f(x) = x * sigmoid(βx),其中β是可学习参数
classSwishFunction(torch.autograd.Function):@staticmethoddefforward(ctx,x,beta=1.0):# 保存中间结果供反向传播使用ctx.save_for_backward(x)ctx.beta=beta# 计算 sigmoid(beta * x)sigmoid=torch.sigmoid(beta*x)# Swish = x * sigmoid(beta * x)returnx*sigmoid@staticmethoddefbackward(ctx,grad_output):x,=ctx.saved_tensors beta=ctx.beta# 计算sigmoid(beta*x)sigmoid=torch.sigmoid(beta*x)# Swish的导数公式# dSwish/dx = sigmoid(beta*x) + beta*x*sigmoid(beta*x)*(1-sigmoid(beta*x))swish_derivative=sigmoid+beta*x*sigmoid*(1-sigmoid)# 链式法则:乘以上游梯度grad_input=grad_output*swish_derivative# 对beta的梯度(如果需要学习beta)# dSwish/dbeta = x^2 * sigmoid(beta*x) * (1-sigmoid(beta*x))grad_beta=(x*x*sigmoid*(1-sigmoid)*grad_output).sum()returngrad_input,grad_betaclassSwish(nn.Module):def__init__(self,beta=1.0,learnable=False):super().__init__()iflearnable:self.beta=nn.Parameter(torch.tensor(float(beta)))else:self.beta=betadefforward(self,x):returnSwishFunction.apply(x,self.beta)# 使用方式model=nn.Sequential(nn.Linear(784,256),Swish(learnable=True),# 使用自定义算子!nn.Linear(256,10))importtorchimporttorch.profiler# 使用PyTorch性能分析器withtorch.profiler.profile(activities=[torch.profiler.ProfilerActivity.CPU,torch.profiler.ProfilerActivity.CUDA,],record_shapes=True,profile_memory=True,)asprof:# 运行模型output=model(input)print(prof.key_averages().table(sort_by="cuda_time_total"))# 不推荐的写法:使用Python循环result=torch.zeros_like(x)foriinrange(x.size(0)):result[i]=x[i]+y[i]# 低效!# 推荐的写法:使用向量化算子result=x+y# 高效!调用优化实现# 特殊情况:in-place操作减少内存分配x.add_(y)# 原地加法,不分配新内存# 使用TorchScript/JIT编译算子@torch.jit.scriptdefcustom_op(x:torch.Tensor,y:torch.Tensor)->torch.Tensor:returnx*torch.sigmoid(y)-y*torch.tanh(x)# 编译优化后运行更快compiled_op=torch.jit.optimize_for_inference(torch.jit.script(custom_op))# 使用Triton自动生成高效CUDA代码importtritonimporttriton.languageastl@triton.jitdeffused_attention_kernel(Q,K,V,output,# ... 自动生成优化代码):# Triton自动处理并行化、内存优化等pass# 一次编写,到处运行importtorchimporttorch_npu# 华为NPU支持importtorch_xla# Google TPU支持# 同一个算子在不同硬件上运行defuniversal_operator(x,y):z=torch.matmul(x,y)# 自动适配底层硬件returntorch.relu(z)# 错误:在循环中频繁调用小算子foriinrange(1000):x=small_operation(x)# 每次调用都有开销# 正确:合并操作,减少调用次数x=batch_operation(x)# 一次处理所有# in-place操作省内存,但可能破坏计算图x.add_(y)# 原地修改x# 非in-place操作安全,但消耗内存z=x+y# 创建新张量# 规则:训练时小心使用in-place,推理时可多用# 自动类型转换可能不高效x=torch.randn(10,dtype=torch.float32)y=torch.randn(10,dtype=torch.float64)z=x+y# x被提升为float64,可能低效# 显式统一类型x=x.to(y.dtype)# 或 y = y.to(x.dtype)z=x+y记住:深度学习本质是算子组合的艺术。每个复杂的模型都是由简单的算子构成的。掌握算子,你就掌握了构建智能系统的"乐高积木"!
importtorch# 用算子"搭积木"defmini_network(x):x=torch.relu(x)# 积木1:激活函数x=torch.matmul(x,w)# 积木2:线性变换x=torch.softmax(x,dim=1)# 积木3:归一化returnx# 试试自己组合!访问PyTorch源码,查看一个算子的实现:
https://github.com/pytorch/pytorch/tree/main/aten/src/ATen/native
在评论区分享:你最喜欢的算子是什么?为什么?
算子世界的大门已经打开,现在轮到你用这些"乐高积木"创造自己的AI杰作了!🚀