突击宝典:pytorch面试高频考点精析
pytorch模块
-
PyTorch的核心数据结构是什么?它与NumPy的主要区别是什么?
- 核心数据结构是张量(torch.Tensor)。
- 主要区别:PyTorch张量支持GPU加速和自动求导,NumPy数组仅用于CPU计算。
-
如何在CPU和GPU之间移动张量?
- 使用
.to(device)方法,例如tensor_gpu = tensor_cpu.to('cuda')。
- 使用
-
简述torch.Tensor和torch.tensor的区别?
torch.Tensor是类构造函数,默认创建浮点张量;torch.tensor是工厂函数,根据数据推断数据类型。
-
如何改变张量的形状?
- 使用
.view()、.reshape()或.resize_()方法。
- 使用
-
view和reshape有什么区别?
view要求张量内存连续,否则报错;reshape自动处理非连续张量,可能返回副本。
-
什么是张量的连续内存?torch.contiguous()是什么?
- 连续内存指元素在内存中顺序存储。
torch.contiguous()将张量转换为连续内存布局。
- 连续内存指元素在内存中顺序存储。
-
如何对张量进行切片、索引、高级索引操作?
- 类似NumPy,例如
tensor[0, :](切片)、tensor[mask](高级索引)。
- 类似NumPy,例如
-
简述torch.squeeze和torch.unsqueeze的作用?
squeeze移除维度为1的轴;unsqueeze在指定位置添加维度为1的轴。
-
如何连接两个张量?它们有什么区别?
- 使用
torch.cat()(沿现有维度连接)或torch.stack()(新建维度连接)。
- 使用
-
torch.mm、torch.matmul和@有什么区别?
mm仅用于矩阵乘法;matmul和@支持广播和不同维度的张量乘法。
-
如何实现张量的广播机制?
- 自动应用:当张量形状不同时,从小维度向大维度扩展(维度为1的轴可广播)。
-
如何在PyTorch和NumPy之间转换?
torch.from_numpy(ndarray)转张量;tensor.numpy()转NumPy数组。
-
将NumPy转成Torch需要注意什么?
- 共享内存(修改一个会影响另一个),需通过
.copy()避免。
- 共享内存(修改一个会影响另一个),需通过
-
简述什么是原地操作?原地操作有什么好处和风险?
- 原地操作(如
x.add_(y))直接修改张量,节省内存但会破坏原始数据,影响梯度计算。
- 原地操作(如
-
PyTorch的torch.Tensor类中,哪些属性与自动求导有关?
requires_grad:
- 这是一个布尔属性,用于指定张量是否需要计算梯度
- 默认值为False,表示不计算梯度
- 可通过
tensor.requires_grad_(True)方法修改 - 示例:创建需要梯度计算的张量
x = torch.tensor([1.0], requires_grad=True)
grad:
- 存储张量的梯度值
- 初始为None,调用
backward()后会自动填充 - 梯度会累积,每次反向传播会累加到当前梯度
- 可通过
zero_()方法清零梯度 - 示例:访问梯度
x = torch.tensor([1.0], requires_grad=True) y = x**2 y.backward() print(x.grad) # 输出: tensor([2.])
. grad_fn:
- 记录创建该张量的操作(即计算图中的边)
- 叶子节点的
grad_fn为None - 非叶子节点会保存对应的操作记录
- 示例:查看操作记录
x = torch.tensor([1.0], requires_grad=True) y = x**2 print(y.grad_fn) # 输出: <PowBackward0 object at 0x...>
应用场景:
- 训练神经网络时,通常将模型参数设为
requires_grad=True backward()会沿着grad_fn记录的路径反向传播梯度- 梯度清零是训练循环中的重要步骤,防止梯度累积影响参数更新 -
requires_grad、grad、grad_fn。
-
如何启动和禁止求导?
- 启动:设置
requires_grad=True;禁止:用torch.no_grad()或detach()。
- 启动:设置
-
简述调用backward()后,为何梯度会被累加?如何避免?
- 设计如此,便于RNN等模型的梯度累积。避免:在反向传播前调用
optimizer.zero_grad()。
- 设计如此,便于RNN等模型的梯度累积。避免:在反向传播前调用
-
简述如何从计算图中分离一个张量?detach()方法有什么用?
detach()返回无需梯度的新张量,将其从计算图中分离。
-
简述detach()和with torch.no_grad()有什么区别和联系?
detach()作用于单个张量;torch.no_grad()是上下文管理器,影响块内所有操作。
-
简述什么是梯度爆炸?什么是梯度消失?在PyTorch如何检测和缓解?
- 梯度爆炸:梯度值过大;梯度消失:梯度接近0。检测:打印梯度值;缓解:梯度裁剪、合适的初始化、BN层。
-
简述如何用torch.nn.utils.clip_grad_norm进行梯度裁剪?
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm)限制梯度范数。
-
简述如何自定义一个自动求导函数?
- 继承
torch.autograd.Function,实现forward和backward方法。
- 继承
-
简述PyTorch的数据加载的核心组件是什么?
Dataset(数据接口)、DataLoader(批加载、多进程等)。
-
简述如何实现一个自定义的Dataset类?
- 继承
torch.utils.data.Dataset,实现__len__和__getitem__方法。
- 继承
-
简述num_workers > 0的好处,可能带来什么问题?
- 好处:多进程加速数据加载;问题:内存占用增加,可能因资源限制出错。
-
简述什么是pin_memory?它什么时候能提升性能?
- 将数据固定到锁页内存,加速CPU到GPU的数据传输;在GPU训练时提升性能。
-
简述什么时候需要自定义collate_fn?
- 当样本大小不一致(如变长序列)或需特殊处理时。
-
简述如何定义一个神经网络模型?
- 继承
nn.Module,在__init__中定义层,在forward中实现前向传播。
- 继承
-
简述在__init__中定义层和forward方法中直接定义层有什么区别?
__init__中定义:层可复用;forward中定义:每次前向重新创建,不推荐。
-
简述nn.Parameter的作用?为什么需要它将张量标记为模型参数?
nn.Parameter将张量标记为模型参数,使其被optimizer识别并更新。
-
简述如何访问模型的所有参数?model.parameters()和model.named_parameters()有什么区别?
model.parameters()返回参数迭代器;model.named_parameters()额外返回参数名。
-
简述如何获取模型的某一层输出?
- 使用钩子(hook)或直接通过层名访问:
output = model.layer_name(input)。
- 使用钩子(hook)或直接通过层名访问:
-
简述什么是模型的前向传播和反向传播?
- 前向传播:输入通过网络计算输出;反向传播:根据损失计算梯度。
-
如何将模型移到GPU上?
model.to('cuda')或model.cuda()。
-
model.to(device)和model.cuda()有什么区别?
to(device)更灵活(支持CPU/GPU);cuda()仅移动到GPU。
-
什么是state_dict?它包含哪些内容?
- 模型参数字典,包含所有可学习参数(权重、偏置)。
-
如果在加载预训练模型时,结构不匹配怎么办?
- 忽略不匹配的键:
model.load_state_dict(pretrained_dict, strict=False)。
- 忽略不匹配的键:
-
如何选择分类损失函数?
- 二分类:
BCEWithLogitsLoss;多分类:CrossEntropyLoss;多标签:BCEWithLogitsLoss。
- 二分类:
-
简述nn.BCELoss和nn.BCEWithLogitsLoss有什么区别?为何推荐使用后者?
BCELoss需手动应用Sigmoid;BCEWithLogitsLoss内置Sigmoid且数值稳定。
-
如何选择回归损失函数?
- 常用
MSELoss(均方误差)或L1Loss(绝对误差)。
- 常用
-
简述torch.optim的作用?
- 实现优化算法(如SGD、Adam),用于更新模型参数。
-
简述什么是动量?在SGD中它怎么工作?
- 动量模拟惯性,加速收敛并减少震荡;SGD中加入历史梯度方向。
-
简述如何为模型的不同部分设置不同的学习率?
- 为不同参数组传递不同学习率给优化器:
optimizer = optim.SGD([{'params': model.base.parameters(), 'lr': 0.001},{'params': model.classifier.parameters(), 'lr': 0.01} ])
- 为不同参数组传递不同学习率给优化器:
-
简述如何使用optim.lr_scheduler来调整学习率?
- 包装优化器,例如:
scheduler = StepLR(optimizer, step_size=30, gamma=0.1)。
- 包装优化器,例如:
-
简述如何实现学习率热身?
- 使用
scheduler如LinearLR或自定义函数,在训练初期逐步增加学习率。
- 使用
-
为什么需要在每个batch之前调用optimizer.zero_grad()?
- 清除上一轮的梯度,避免梯度累加。
-
loss.backward()和optimizer.step()分别做了什么?
loss.backward():计算梯度;optimizer.step():更新参数。
-
如何将模型设置为训练或评估模式?
model.train()(训练模式);model.eval()(评估模式)。
-
简述在评估模式下,哪些层行为会发生改变?
Dropout层失效;BatchNorm层使用运行统计量而非批次统计量。
-
如何计算分类任务中的准确率?
- 比较预测标签与真实标签:
(preds == labels).float().mean()。
- 比较预测标签与真实标签:
-
简述什么是早停?如何实现?
- 早停:验证集性能不再提升时停止训练;实现:监控验证损失,设置耐心值。
-
如何冻结模型的某些层?
- 设置参数的
requires_grad=False。
- 设置参数的
-
简述Dropout和BN的原理?
- Dropout:随机失活神经元,防止过拟合;BN:规范化层输入,稳定训练。
-
什么是梯度累积?怎么实现?有什么作用?
- 多次小批量累积梯度后再更新参数;实现:多次
backward()后step();作用:模拟大批量训练。
- 多次小批量累积梯度后再更新参数;实现:多次
-
什么是迁移学习?迁移学习的典型步骤是?
- 迁移学习:复用预训练模型;步骤:加载预训练权重,微调最后几层。
-
如何处理多标签分类任务?
- 使用
BCEWithLogitsLoss,输出层每个节点独立应用Sigmoid。
- 使用
-
如何处理序列模型?
- 使用RNN、LSTM或Transformer层,处理变长序列(需填充或打包)。
-
TorchScript有什么作用?
- 将模型转换为静态图,便于部署(无Python依赖)。
-
如何通过torch.jit.trace和torch.jit.script转成TorchScript?这两者有什么区别?有什么局限性?
trace:通过示例输入记录执行路径;script:直接编译模型代码。区别:trace无法处理控制流;局限性:部分Python特性不支持。
-
简述ONNX格式?如何导出为ONNX?
- ONNX:开放模型交换格式;导出:
torch.onnx.export(model, input, "model.onnx")。
- ONNX:开放模型交换格式;导出:
-
导出ONNX会遇到哪些问题?
- 算子不支持、动态形状限制、版本兼容性问题。
-
如何在多个GPU上进行数据并行训练?
- 使用
nn.DataParallel或nn.parallel.DistributedDataParallel。
- 使用
-
简述nn.DataParallel和nn.parallel.DistributedDataParallel有什么区别?
DataParallel:单进程多线程,性能较低;DistributedDataParallel:多进程,效率更高。
-
用DistributedDataParallel的基本步骤?
- 初始化进程组;包装模型;使用
DistributedSampler。
- 初始化进程组;包装模型;使用
-
什么是混合精度训练?它为什么可以加速训练并减少内存占用?
- 混合精度:同时使用FP16和FP32;加速:利用Tensor Core;省内存:FP16占用的显存更少。
-
训练过程中,有哪些常见的内存泄露原因?如何调试?
- 原因:张量累积、循环引用、未释放缓存;调试:使用
torch.cuda.memory_summary()。
- 原因:张量累积、循环引用、未释放缓存;调试:使用
-
简述团队中,有哪些PyTorch代码的最佳实践?
- 模块化设计、使用配置文件、日志记录、版本控制、代码审查。
-
如何确保你的PyTorch实验是可复现的?
- 设置随机种子:
torch.manual_seed(42) torch.cuda.manual_seed_all(42) numpy.random.seed(42)
- 设置随机种子:
