当前位置:首页>python>Day 3: Python GPU编程 - PyTorch入门

Day 3: Python GPU编程 - PyTorch入门

  • 2026-09-06 15:03:05
Day 3: Python GPU编程 - PyTorch入门

📚 学习目标

今天我们将进入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文档。

最新文章

随机文章