CS231n循环训练神经网络实例后续运行缓慢问题求助
问题分析与解决思路
你遇到的这个问题确实大概率是内存泄漏导致的——第一个模型训练时内存占用正常,但后续循环中,前一个模型/solver的内存没有被彻底释放,导致内存越用越多,系统开始频繁换页(swap),进而拖慢了代码执行速度。虽然你用了del语句,但Python的垃圾回收机制不会立刻回收内存,尤其是当对象之间存在引用循环(比如Solver持有Model的引用,Model又间接引用了Solver的某些配置)时,引用计数无法降到0,内存就会一直被占用。
下面是具体的验证方法和解决方案:
一、先验证是否真的是内存泄漏
可以通过监控内存使用情况来确认:
系统工具监控:
- Linux/macOS:打开
htop或top,观察Python进程的内存占用(RES列),如果每次循环后内存只升不降,就实锤内存泄漏。 - Windows:打开任务管理器,查看Python进程的内存使用量变化。
- Linux/macOS:打开
代码中打印内存使用:
用psutil库实时打印内存,先安装:pip install psutil,然后添加到你的循环中:import psutil import os def get_mem_usage_mb(): proc = psutil.Process(os.getpid()) return proc.memory_info().rss / (1024 ** 2) for lr in learning_rates: print(f"Before training lr={lr}: {get_mem_usage_mb():.2f} MB") # ... 你的模型训练代码 ... # del和gc代码 print(f"After training lr={lr}: {get_mem_usage_mb():.2f} MB")如果每次循环后内存没有回落,就说明内存没被正确回收。
二、解决方案
针对CS231n的Solver和FullyConnectedNet实现,这里有几个有效的解决方法:
方法1:手动斩断引用并强制垃圾回收
Python的del只是删除变量名的引用,但如果其他对象还持有这个对象的引用(比如Solver里的self.model),内存还是不会被释放。我们可以先手动清除这些引用,再强制触发垃圾回收:
修改你的循环代码:
import gc best_model = None best_val_acc = -1 hidden_dims = [100, 100, 100, 100, 100] reg= 0.0 std= 1e-2 learning_rates = [1e-4, 1e-5, 1e-6, 1e-7] for lr in learning_rates: model = FullyConnectedNet(hidden_dims, reg= reg, weight_scale= std, normalization= None) solver = Solver(model, data, num_epochs = 10, batch_size = 200, update_rule= 'adam', lr_decay = 0.95, optim_config= {'learning_rate': lr}, verbose = True, print_every= 1000) solver.train() val_acc = solver.check_accuracy(data['X_val'], data['y_val']) # 更新最佳模型 if val_acc > best_val_acc: # 先删除之前的最佳模型,避免内存占用 if best_model is not None: del best_model best_val_acc = val_acc best_model = model best_lr = lr else: # 不需要的模型直接删除 del model # 斩断Solver对Model的引用,再删除Solver solver.model = None del solver # 强制触发垃圾回收 gc.collect() print("Best learning rate found to be: ", best_lr)
关键改动:
- 先把
solver.model设为None,切断Solver和Model之间的引用 - 对不需要保留的Model直接
del - 调用
gc.collect()强制Python回收未使用的内存
方法2:用函数封装训练逻辑,利用作用域自动清理内存
Python的函数局部变量在函数执行结束后,引用计数会自动减少,垃圾回收更容易处理。把单个模型的训练逻辑封装到函数里:
import gc def train_single_model(lr, hidden_dims, reg, std, data): model = FullyConnectedNet(hidden_dims, reg= reg, weight_scale= std, normalization= None) solver = Solver(model, data, num_epochs = 10, batch_size = 200, update_rule= 'adam', lr_decay = 0.95, optim_config= {'learning_rate': lr}, verbose = True, print_every= 1000) solver.train() val_acc = solver.check_accuracy(data['X_val'], data['y_val']) return model, val_acc best_model = None best_val_acc = -1 hidden_dims = [100, 100, 100, 100, 100] reg= 0.0 std= 1e-2 learning_rates = [1e-4, 1e-5, 1e-6, 1e-7] for lr in learning_rates: model, val_acc = train_single_model(lr, hidden_dims, reg, std, data) if val_acc > best_val_acc: if best_model is not None: del best_model best_val_acc = val_acc best_model = model best_lr = lr else: del model gc.collect() print("Best learning rate found to be: ", best_lr)
函数执行完后,里面的solver和临时变量会自动脱离作用域,引用计数归零,内存更容易被回收。
方法3:清理Solver内部的缓存数据
CS231n的Solver类会在训练过程中保存loss_history、train_acc_history等数组,如果这些数组很大,训练结束后没清理也会占用内存。可以给Solver加一个清理方法:
在cs231n/solver.py里的Solver类中添加:
def clear_cache(self): self.loss_history = [] self.train_acc_history = [] self.val_acc_history = [] # 如果还有其他缓存的大数组,也在这里清空
然后在训练结束后调用:
solver.train() val_acc = solver.check_accuracy(...) solver.clear_cache() # 清理缓存 solver.model = None del solver
三、为什么第一个模型正常,后面变慢?
第一个模型训练时,系统内存充足,CPU/GPU可以高效执行;但后续模型训练时,内存被之前的模型数据占满,系统不得不使用磁盘swap空间(速度比内存慢几个数量级),导致代码执行速度骤降。这也是为什么不管换什么学习率,后续实例都会变慢——本质是内存不足导致的性能瓶颈,和学习率无关。
内容的提问来源于stack exchange,提问作者Ends of Invention

