关于利用数据列最值降低内存占用的作用及np.finfo返回值的疑问
嘿,我最近在学习一个入侵检测项目的代码,里面有一段用来降低内存占用的逻辑,我有点搞不懂其中的原理,还有个关于np.finfo()返回值的疑问,来跟大家聊聊~
先说说背景:我从一个入侵检测项目里看到这段内存优化代码,原以为能把内存降下来,但实际运行后发现初始和最终内存都是798.63MB,没变化。我打印了各列的最小值c_min和np.finfo(np.float32).min,发现后者比我数据里的最小值小很多,这就让我产生了两个疑问:
- 为什么作者要基于列的最大最小值来调整数据类型?
- 为什么
np.finfo(np.float32).min会比我数据列的实际最小值小这么多?
先贴一下这段内存优化的代码:
old_memory_usage = data.memory_usage().sum() / 1024 ** 2 print(f'Initial memory usage: {old_memory_usage:.2f} MB') for col in data.columns: col_type = data[col].dtype if col_type != object: c_min = data[col].min() # <-- retrieve c_min c_max = data[col].max() # Downcasting float64 to float32 if str(col_type).find('float') >= 0 and c_min > np.finfo(np.float32).min and c_max < np.finfo(np.float32).max: data[col] = data[col].astype(np.float32) # Downcasting int64 to int32 elif str(col_type).find('int') >= 0 and c_min > np.iinfo(np.int32).min and c_max < np.iinfo(np.int32).max: data[col] = data[col].astype(np.int32) new_memory_usage = data.memory_usage().sum() / 1024 ** 2 print(f"Final memory usage: {new_memory_usage:.2f} MB")
然后是我打印的列最小值和np.finfo(np.float32).min的结果:
Initial memory usage: 798.63 MB Fwd Packet Length Mean 0.0 -3.4028235e+38 Fwd Packet Length Std 0.0 -3.4028235e+38 Bwd Packet Length Mean 0.0 -3.4028235e+38 Bwd Packet Length Std 0.0 -3.4028235e+38 Flow Bytes/s -261000000.0 -3.4028235e+38 Flow Packets/s -2000000.0 -3.4028235e+38 Flow IAT Mean -13.0 -3.4028235e+38 Flow IAT Std 0.0 -3.4028235e+38 Fwd IAT Mean 0.0 -3.4028235e+38 Fwd IAT Std 0.0 -3.4028235e+38 Bwd IAT Mean 0.0 -3.4028235e+38 Bwd IAT Std 0.0 -3.4028235e+38 Fwd Packets/s 0.0 -3.4028235e+38 Bwd Packets/s 0.0 -3.4028235e+38 Packet Length Mean 0.0 -3.4028235e+38 Packet Length Std 0.0 -3.4028235e+38 Packet Length Variance 0.0 -3.4028235e+38 Average Packet Size 0.0 -3.4028235e+38 Avg Fwd Segment Size 0.0 -3.4028235e+38 Avg Bwd Segment Size 0.0 -3.4028235e+38 Active Mean 0.0 -3.4028235e+38 Active Std 0.0 -3.4028235e+38 Idle Mean 0.0 -3.4028235e+38 Idle Std 0.0 -3.4028235e+38 Final memory usage: 798.63 MB
先解答第一个疑问:为什么用列的最值来调整数据类型?
不同的数值类型占用的内存空间不一样,比如float64每个元素占8字节,float32只占4字节;int64占8字节,int32占4字节。直接把大类型转成小类型确实能省内存,但前提是数据里的所有值都能被小类型完全容纳,不然就会出现数据溢出(比如把一个比float32最大值还大的数转成float32,会变成无穷大inf,丢失真实数据)。
所以作者的逻辑是:先拿到列的最小值c_min和最大值c_max,然后对比目标类型(比如float32)的取值极限——如果列里的所有值都在这个极限范围内,就可以安全地把列转成更小的类型,既省内存又不会丢失数据。
再解答第二个疑问:为什么np.finfo(np.float32).min比我的c_min小?
np.finfo(np.float32).min是float32这个数据类型能表示的最小(最负)的数值,大概是-3.4e+38,而你的数据列里的实际最小值(比如Flow Bytes/s的-261000000.0)比这个值大得多,这说明你的数据完全在float32的容纳范围内。
至于为什么内存没变化,大概率是这些列本来就已经是float32类型了,所以转类型操作没有改变内存占用。你可以打印一下data.dtypes看看这些列的原始类型,就能验证这一点啦。
备注:内容来源于stack exchange,提问作者Christopher Chen

