📚 学习目标
今天我们将进入Python GPU编程的世界,学习使用PyTorch框架进行GPU加速计算。通过今天的学习,你将:
1. 理解PyTorch的核心概念:张量(Tensor)
2. 掌握CPU与GPU之间的数据传输
3. 学会使用PyTorch进行基本的GPU加速计算
4. 理解自动微分机制及其在优化中的作用
5. 实现一个完整的GPU加速图像处理示例
预计学习时间: 5小时(理论2小时 + 实践3小时)
🎯 为什么选择PyTorch?
在Day 2中,我们学习了CUDA C/C++编程,虽然性能强大,但编程复杂度较高。PyTorch提供了更友好的Python接口,同时保持了GPU加速的优势。
PyTorch vs CUDA C/C++
特性 | CUDA C/C++ | PyTorch |
编程语言 | C/C++ | Python |
学习曲线 | 陡峭 | 平缓 |
开发效率 | 较低 | 高 |
性能 | 最优 | 优秀(接近原生CUDA) |
适用场景 | 底层优化、极致性能 | 快速原型、深度学习 |
调试难度 | 困难 | 容易 |
生活类比:
●CUDA C/C++就像开手动挡赛车,性能极致但操作复杂
●PyTorch就像开自动挡跑车,性能优秀且易于驾驶
对于初学者,我们推荐先掌握PyTorch,在需要极致性能时再深入CUDA优化。
📖 核心概念讲解
1. 张量(Tensor):PyTorch的基础数据结构
1.1 什么是张量?
简单理解: 张量就是一个多维数组,类似于NumPy的ndarray,但可以在GPU上运行。
维度说明:
●0维张量:标量(scalar),如 5
●1维张量:向量(vector),如 [1, 2, 3]
●2维张量:矩阵(matrix),如 [[1, 2], [3, 4]]
●3维张量:如RGB图像 [高度, 宽度, 通道]
●4维张量:如批量图像 [批量大小, 通道, 高度, 宽度]
生活类比: 想象一个仓库管理系统:
●标量:记录温度(一个数字)
●向量:记录货架上的一排物品(一维列表)
●矩阵:记录一个货架层的物品(二维表格)
●3维张量:记录整个货架的物品(立体结构)
●4维张量:记录整个仓库的物品(包含多个货架)
1.2 创建张量
import torch# 创建张量的多种方式# 1. 从列表创建x = torch.tensor([1, 2, 3, 4])print(f"从列表创建: {x}")# 2. 创建全零张量zeros = torch.zeros(3, 4)# 3行4列print(f"全零张量:\n{zeros}")# 3. 创建全一张量ones = torch.ones(2, 3, 4)# 2个3行4列的矩阵print(f"全一张量形状: {ones.shape}")# 4. 创建随机张量random_tensor = torch.randn(3, 3)# 标准正态分布print(f"随机张量:\n{random_tensor}")# 5. 创建单位矩阵eye = torch.eye(4)print(f"单位矩阵:\n{eye}")# 6. 从NumPy数组创建import numpy as npnp_array = np.array([1, 2, 3])tensor_from_numpy = torch.from_numpy(np_array)print(f"从NumPy创建: {tensor_from_numpy}")
输出示例:
从列表创建: tensor([1, 2, 3, 4])全零张量:tensor([[0., 0., 0., 0.],[0., 0., 0., 0.],[0., 0., 0., 0.]])全一张量形状: torch.Size([2, 3, 4])随机张量:tensor([[ 0.5432, -0.1234,1.2345],[-0.6789,0.9876, -0.4321],[ 1.1111, -0.2222,0.3333]])单位矩阵:tensor([[1., 0., 0., 0.],[0., 1., 0., 0.],[0., 0., 1., 0.],[0., 0., 0., 1.]])从NumPy创建: tensor([1, 2, 3], dtype=torch.int32)
2. GPU加速:设备管理
2.1 检查GPU可用性
import torch# 检查CUDA是否可用cuda_available = torch.cuda.is_available()print(f"CUDA可用: {cuda_available}")if cuda_available:# 获取GPU数量gpu_count = torch.cuda.device_count()print(f"GPU数量: {gpu_count}")# 获取当前GPUcurrent_device = torch.cuda.current_device()print(f"当前GPU索引: {current_device}")# 获取GPU名称gpu_name = torch.cuda.get_device_name(current_device)print(f"GPU名称: {gpu_name}")# 获取GPU显存信息total_memory = torch.cuda.get_device_properties(current_device).total_memory / 1024**3print(f"GPU总显存: {total_memory:.2f} GB")else:print("未检测到GPU,将使用CPU运行")
输出示例:
CUDA可用: TrueGPU数量: 1当前GPU索引: 0GPU名称: NVIDIA GeForce RTX 3090GPU总显存: 24.00 GB
2.2 设备选择与数据传输
import torch# 定义设备device = torch.device("cuda" if torch.cuda.is_available() else "cpu")print(f"使用设备: {device}")# 创建张量并移动到GPUx = torch.randn(3, 3)print(f"张量初始设备: {x.device}")# 方法1: 使用.to()x_gpu = x.to(device)print(f"移动后设备: {x_gpu.device}")# 方法2: 直接在GPU上创建y_gpu = torch.randn(3, 3, device=device)print(f"直接创建在GPU: {y_gpu.device}")# 方法3: 使用.cuda()z_gpu = torch.randn(3, 3).cuda()print(f"使用.cuda(): {z_gpu.device}")# 从GPU移回CPUx_cpu = x_gpu.cpu()print(f"移回CPU: {x_cpu.device}")
输出示例:
使用设备: cuda张量初始设备: cpu移动后设备: cuda:0直接创建在GPU: cuda:0使用.cuda(): cuda:0移回CPU: cpu
⚠️ 重要提示:
●GPU上的张量只能与GPU上的张量运算
●CPU上的张量只能与CPU上的张量运算
●混合运算会导致错误
# ❌ 错误示例:混合设备运算x_cpu = torch.randn(3, 3)y_gpu = torch.randn(3, 3, device='cuda')# result = x_cpu + y_gpu# 这会报错!# ✅ 正确做法:统一设备y_cpu = y_gpu.cpu()result = x_cpu + y_cpu# 正确
3. 张量运算:GPU加速的核心
3.1 基本运算
import torchimport time# 创建大矩阵进行性能测试size = 5000# CPU版本a_cpu = torch.randn(size, size)b_cpu = torch.randn(size, size)start = time.time()c_cpu = torch.matmul(a_cpu, b_cpu)# 矩阵乘法cpu_time = time.time() - startprint(f"CPU矩阵乘法时间: {cpu_time:.4f}秒")# GPU版本device = torch.device('cuda')a_gpu = a_cpu.to(device)b_gpu = b_cpu.to(device)# 预热GPU(第一次运行较慢)_ = torch.matmul(a_gpu, b_gpu)torch.cuda.synchronize()# 等待GPU完成start = time.time()c_gpu = torch.matmul(a_gpu, b_gpu)torch.cuda.synchronize()# 等待GPU完成gpu_time = time.time() - startprint(f"GPU矩阵乘法时间: {gpu_time:.4f}秒")# 加速比speedup = cpu_time / gpu_timeprint(f"加速比: {speedup:.2f}x")
输出示例:
CPU矩阵乘法时间: 1.2345秒GPU矩阵乘法时间: 0.0123秒加速比: 100.37x
💡 为什么需要预热GPU?GPU首次运行需要初始化CUDA上下文和编译核函数,会花费额外时间。预热后,后续运行会快很多。
3.2 常用运算
import torchx = torch.randn(3, 4, device='cuda')print(f"原始张量:\n{x}")# 逐元素运算print(f"绝对值: {torch.abs(x)}")print(f"指数: {torch.exp(x)}")print(f"平方根: {torch.sqrt(torch.abs(x))}")# 聚合运算print(f"求和: {torch.sum(x)}")print(f"均值: {torch.mean(x)}")print(f"最大值: {torch.max(x)}")print(f"最小值: {torch.min(x)}")# 按维度聚合print(f"按行求和: {torch.sum(x, dim=1)}")print(f"按列求和: {torch.sum(x, dim=0)}")# 矩阵运算A = torch.randn(3, 4, device='cuda')B = torch.randn(4, 5, device='cuda')C = torch.matmul(A, B)# 矩阵乘法print(f"矩阵乘法结果形状: {C.shape}")# 转置print(f"转置后形状: {A.T.shape}")# 重塑x_flat = x.reshape(12)# 展平print(f"展平后形状: {x_flat.shape}")
4. 自动微分:优化的核心
4.1 什么是自动微分?
简单理解: 自动微分是PyTorch自动计算函数梯度的功能,无需手动推导导数公式。
生活类比: 想象你在山上(目标函数),想下山到最低点(最小化损失)。自动微分就像一个GPS导航,自动告诉你每个方向的坡度(梯度),你只需要沿着坡度下降的方向走。
为什么重要?
●ILT优化需要计算损失函数对掩模的梯度
●手动推导复杂函数的梯度非常困难
●自动微分让优化变得简单
4.2 使用自动微分
import torch# 创建需要梯度的张量x = torch.tensor([2.0], requires_grad=True)print(f"x = {x}")# 构建计算图y = x ** 2# y = x^2print(f"y = x^2 = {y}")z = y + 3# z = y + 3 = x^2 + 3print(f"z = y + 3 = {z}")# 反向传播,计算梯度z.backward()# 查看梯度: dz/dxprint(f"dz/dx = {x.grad}")# 应该是 2*x = 4.0
输出示例:
x = tensor([2.], requires_grad=True)y = x^2 = tensor([4.], grad_fn=)z = y + 3 = tensor([7.], grad_fn=)dz/dx = tensor([4.])
计算过程:
z = x^2 + 3dz/dx = 2x在 x=2 时,dz/dx = 4
4.3 复杂示例:优化问题
import torchimport matplotlib.pyplot as plt# 优化目标:找到使 f(x) = (x-3)^2 + (x+1)^2 最小的x# 理论解: f'(x) = 2(x-3) + 2(x+1) = 4x - 4 = 0 => x = 1# 初始化参数x = torch.tensor([0.0], requires_grad=True)# 记录优化过程x_history = [x.item()]# 梯度下降learning_rate = 0.1for i in range(20):# 前向传播:计算损失loss = (x - 3) ** 2 + (x + 1) ** 2# 反向传播:计算梯度loss.backward()# 更新参数(使用梯度下降)with torch.no_grad():# 不记录梯度x -= learning_rate * x.grad# 清零梯度(重要!)x.grad.zero_()# 记录x_history.append(x.item())if i % 5 == 0:print(f"迭代{i}: x = {x.item():.4f}, loss = {loss.item():.4f}")print(f"\n最终结果: x = {x.item():.4f}")print(f"理论最优解: x = 1.0000")# 可视化plt.figure(figsize=(10, 6))plt.plot(x_history, 'o-', label='优化路径')plt.axhline(y=1.0, color='r', linestyle='--', label='最优解 x=1')plt.xlabel('迭代次数')plt.ylabel('x值')plt.title('梯度下降优化过程')plt.legend()plt.grid(True)plt.savefig('optimization_path.png', dpi=150, bbox_inches='tight')print("\n优化路径图已保存为 optimization_path.png")
输出示例:
迭代0: x = 0.4000, loss = 10.0000迭代5: x = 0.9328, loss = 4.0450迭代10: x = 0.9991, loss = 4.0000迭代15: x = 0.9999, loss = 4.0000最终结果: x = 1.0000理论最优解: x = 1.0000优化路径图已保存为 optimization_path.png
⚠️ 重要注意事项:
1. requires_grad=True: 需要计算梯度的张量必须设置
2. backward(): 反向传播计算梯度
3. grad.zero_(): 每次迭代前清零梯度,否则梯度会累加
4. with torch.no_grad(): 更新参数时不需要记录梯度
5. 实战:GPU加速图像处理
5.1 图像卷积基础
什么是卷积?卷积是一种图像处理操作,通过一个小的核(kernel/filter)在图像上滑动,计算局部区域的加权和。
生活类比: 想象你用一个放大镜看图像,每次只看一小块区域,然后根据特定规则计算这一块的平均值、边缘等特征,最后得到一个新的图像。
卷积核示例:
●均值滤波: 核全为1,用于模糊/平滑
●边缘检测: 核检测亮度变化,用于提取轮廓
●锐化: 核增强对比度,用于突出细节
5.2 GPU加速卷积实现
import torchimport numpy as npfrom PIL import Imageimport matplotlib.pyplot as plt# 读取图像def load_image(image_path):"""读取图像并转换为张量"""img = Image.open(image_path).convert('L')# 转为灰度图img_array = np.array(img, dtype=np.float32) / 255.0# 归一化到[0,1]return torch.from_numpy(img_array).unsqueeze(0).unsqueeze(0)# 添加批次和通道维度# 定义卷积核def get_kernels():"""定义常用卷积核"""kernels = {}# 均值滤波(模糊)kernels['blur'] = torch.ones(1, 1, 3, 3) / 9.0# 边缘检测(Sobel算子)kernels['edge_x'] = torch.tensor([[[[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]]]], dtype=torch.float32)kernels['edge_y'] = torch.tensor([[[[-1, -2, -1],[0,0,0],[1,2,1]]]], dtype=torch.float32)# 锐化kernels['sharpen'] = torch.tensor([[[[0, -1, 0],[-1, 5, -1],[0, -1, 0]]]], dtype=torch.float32)return kernels# GPU卷积def gpu_convolution(image_tensor, kernel, device='cuda'):"""使用GPU进行卷积"""# 移动到GPUimage_gpu = image_tensor.to(device)kernel_gpu = kernel.to(device)# 使用conv2d进行卷积result = torch.nn.functional.conv2d(image_gpu, kernel_gpu, padding=1)return result.cpu().squeeze()# 创建测试图像(如果没有真实图像)def create_test_image(size=256):"""创建测试图像:带有几何图形"""image = np.zeros((size, size), dtype=np.float32)# 添加矩形image[50:100, 50:150] = 1.0# 添加圆形center = (180, 180)radius = 40y, x = np.ogrid[:size, :size]mask = (x - center[0])**2 + (y - center[1])**2 <= radius**2image[mask] = 1.0# 添加三角形for i in range(50):image[150+i, 20+i*2:40+i*2] = 1.0return torch.from_numpy(image).unsqueeze(0).unsqueeze(0)# 主程序if __name__ == "__main__":# 检查GPUdevice = torch.device('cuda' if torch.cuda.is_available() else 'cpu')print(f"使用设备: {device}")# 创建测试图像print("创建测试图像...")image_tensor = create_test_image(256)# 获取卷积核kernels = get_kernels()# 应用不同卷积核results = {}for name, kernel in kernels.items():print(f"应用 {name} 卷积核...")results[name] = gpu_convolution(image_tensor, kernel, device)# 特殊处理:边缘检测合并edge_magnitude = torch.sqrt(results['edge_x']**2 + results['edge_y']**2)# 可视化fig, axes = plt.subplots(2, 3, figsize=(15, 10))# 原图axes[0, 0].imshow(image_tensor.squeeze(), cmap='gray')axes[0, 0].set_title('原始图像')axes[0, 0].axis('off')# 模糊axes[0, 1].imshow(results['blur'], cmap='gray')axes[0, 1].set_title('均值滤波(模糊)')axes[0, 1].axis('off')# 锐化axes[0, 2].imshow(results['sharpen'], cmap='gray')axes[0, 2].set_title('锐化')axes[0, 2].axis('off')# 边缘检测Xaxes[1, 0].imshow(torch.abs(results['edge_x']), cmap='gray')axes[1, 0].set_title('边缘检测(X方向)')axes[1, 0].axis('off')# 边缘检测Yaxes[1, 1].imshow(torch.abs(results['edge_y']), cmap='gray')axes[1, 1].set_title('边缘检测(Y方向)')axes[1, 1].axis('off')# 边缘检测合并axes[1, 2].imshow(edge_magnitude, cmap='gray')axes[1, 2].set_title('边缘检测(合并)')axes[1, 2].axis('off')plt.tight_layout()plt.savefig('image_processing_results.png', dpi=150, bbox_inches='tight')print("\n处理结果已保存为 image_processing_results.png")plt.show()
💻 项目实战:GPU加速图像滤波器
项目说明
今天的项目将实现一个完整的GPU加速图像处理工具,包括:
1. 读取和处理图像
2. 多种卷积核滤波
3. CPU与GPU性能对比
4. 实时预览效果
项目结构
code_2026_03_26/├── README.md# 项目说明文档├── image_filter_gpu.py# 主程序:GPU图像滤波器├── tensor_operations.py# 张量操作练习├── autograd_demo.py# 自动微分示例├── performance_benchmark.py# 性能基准测试├── test_image.png# 测试图像(程序自动生成)└── expected_outputs.txt# 预期输出示例
核心代码解析
详细代码请查看 code_2026_03_26/ 目录下的各个文件。
📊 性能对比分析
让我们对比一下CPU和GPU在不同任务上的性能差异:
任务 | 数据规模 | CPU时间 | GPU时间 | 加速比 |
矩阵乘法 | 1000×1000 | 0.15s | 0.003s | 50x |
矩阵乘法 | 5000×5000 | 1.23s | 0.012s | 103x |
图像卷积 | 1024×1024 | 0.45s | 0.008s | 56x |
图像卷积 | 4096×4096 | 2.34s | 0.032s | 73x |
结论:
●GPU在大规模矩阵运算上优势明显
●数据规模越大,GPU加速效果越好
●PyTorch性能接近原生CUDA代码
🔍 深入理解:PyTorch vs NumPy
特性 | NumPy | PyTorch |
设备 | 仅CPU | CPU + GPU |
自动微分 | 不支持 | 支持 |
深度学习 | 不支持 | 完整支持 |
性能 | 快 | 更快(GPU) |
内存管理 | 手动 | 自动 |
API相似度 | - | 90%兼容 |
转换示例:
import torchimport numpy as np# NumPy转PyTorchnp_array = np.array([1, 2, 3])torch_tensor = torch.from_numpy(np_array)# PyTorch转NumPytorch_tensor = torch.tensor([1, 2, 3])np_array = torch_tensor.numpy()# GPU张量需要先移回CPUgpu_tensor = torch.tensor([1, 2, 3], device='cuda')np_array = gpu_tensor.cpu().numpy()# 注意:需要.cpu()
🎯 实践任务清单
任务1:张量基础操作(30分钟)
●创建不同形状的张量(标量、向量、矩阵、3D张量)
●练习张量索引和切片
●实现张量的基本运算(加减乘除、矩阵乘法)
●测试CPU和GPU性能差异
预期输出: 运行 tensor_operations.py,完成所有练习并记录结果
任务2:自动微分练习(45分钟)
●实现一个简单的优化问题(如:y = x^2,找到最小值)
●尝试不同学习率,观察收敛速度
●实现多变量优化(如:minimize x^2 + y^2)
●可视化优化路径
预期输出: 运行 autograd_demo.py,生成优化路径图
任务3:图像处理实战(60分钟)
●创建或加载一张测试图像
●实现至少3种不同的卷积核(模糊、锐化、边缘检测)
●对比CPU和GPU处理时间
●保存处理结果图像
预期输出: 运行 image_filter_gpu.py,生成处理后的图像
任务4:性能基准测试(45分钟)
●测试不同规模矩阵乘法的CPU/GPU性能
●绘制性能曲线图
●分析何时使用GPU更划算
●编写测试报告
预期输出: 运行 performance_benchmark.py,生成性能对比图表
📚 扩展阅读
官方文档
1. PyTorch官方教程
2. PyTorch自动微分详解
3. CUDA语义文档
进阶主题
1. 混合精度训练: 使用torch.cuda.amp加速训练
2. 多GPU训练: 使用DataParallel和DistributedDataParallel
3. 内存优化: 使用torch.no_grad()和梯度检查点
4. JIT编译: 使用torch.jit.script优化性能
与光刻技术的联系
虽然今天我们学习的是通用的PyTorch技能,但这些技术直接应用于ILT:
1. 张量运算: ILT中的光刻仿真是大规模张量运算
2. 自动微分: ILT优化需要计算损失函数对掩模的梯度
3. GPU加速: ILT计算密集,GPU加速是必须的
4. 图像处理: 掩模和晶圆图像都是图像数据
✅ 学习检查清单
完成今天的学习后,你应该能够:
理论理解
●理解张量的概念和维度
●理解CPU与GPU的内存模型差异
●理解自动微分的工作原理
●理解卷积操作的数学原理
实践技能
●能创建和操作PyTorch张量
●能在CPU和GPU之间传输数据
●能使用自动微分进行优化
●能实现GPU加速的图像卷积
性能优化
●理解GPU预热的重要性
●知道何时使用GPU更高效
●能分析GPU程序性能瓶颈
●能进行基本的性能对比测试
工程实践
●能编写规范的PyTorch代码
●能正确处理设备转移
●能调试GPU相关错误
●能阅读和理解PyTorch文档
🎉 总结
今天我们学习了PyTorch GPU编程的基础知识,包括:
1. 张量: PyTorch的核心数据结构,类似NumPy但支持GPU
2. 设备管理: CPU与GPU之间的数据传输
3. GPU加速: 大规模运算的显著性能提升
4. 自动微分: 优化问题的利器
5. 图像处理: 实际应用案例
关键收获:
●PyTorch提供了友好的GPU编程接口
●GPU加速可以带来几十到几百倍的性能提升
●自动微分让复杂优化问题变得简单
●掌握PyTorch为后续学习ILT打下坚实基础
下一步预告: 明天(Day 4)我们将学习数学基础回顾与工具准备,包括:
●线性代数在光刻中的应用
●傅里叶变换与频域分析
●优化算法基础
●数值计算工具
这些数学工具是理解光刻成像模型和ILT算法的核心基础!
📝 课后练习
练习1:张量操作
# 创建一个5x5的随机矩阵,计算其特征值和特征向量import torchA = torch.randn(5, 5, device='cuda')eigenvalues, eigenvectors = torch.linalg.eig(A)print(f"特征值: {eigenvalues}")print(f"特征向量: {eigenvectors}")
练习2:优化问题
# 实现梯度下降求解: min (x-3)^2 + 2(y+1)^2import torchx = torch.tensor([0.0], requires_grad=True, device='cuda')y = torch.tensor([0.0], requires_grad=True, device='cuda')for i in range(50):loss = (x - 3)**2 + 2*(y + 1)**2loss.backward()with torch.no_grad():x -= 0.1 * x.grady -= 0.1 * y.gradx.grad.zero_()y.grad.zero_()if i % 10 == 0:print(f"迭代{i}: x={x.item():.4f}, y={y.item():.4f}, loss={loss.item():.4f}")print(f"最终结果: x={x.item():.4f}, y={y.item():.4f}")# 理论解: x=3, y=-1
练习3:自定义卷积核
# 实现一个自定义的浮雕效果卷积核# 浮雕核: [[-2,-1,0],[-1,1,1],[0,1,2]]import torchemboss_kernel = torch.tensor([[[[-2, -1, 0],[-1,1, 1],[ 0,1, 2]]]], dtype=torch.float32, device='cuda')# 应用到图像上(参考项目代码)
恭喜你完成Day 3的学习! 🎊
记住:实践是最好的老师,多运行代码,多尝试不同的参数,你会发现PyTorch的强大之处!
有问题随时查看 code_2026_03_26/ 目录下的代码示例和README文档。