如何在Python中加载并随机打乱8GB CSV数据(解决内存问题)
解决大尺寸CSV全局随机打乱的方案
咱太懂你这个痛点了——8GB的CSV全加载直接爆内存,分块单独打乱又跟没打乱差不多,原数据的顺序偏置根本没打破,对机器学习训练的影响可不小。我给你分享两个靠谱的解决方案,都是实际项目里验证过的:
方案一:纯Pandas+Numpy手动实现全局打乱
核心思路是先拿到全局数据的索引并打乱,再分批读取对应索引的行,这样就能真正实现全局随机,而不是块内随机。
步骤1:快速获取CSV总行数
不用加载全量数据,直接读文件行数就行:
import pandas as pd import numpy as np def get_total_rows(csv_path): with open(csv_path, 'r') as f: # 减1是跳过表头行 return sum(1 for _ in f) - 1 total_rows = get_total_rows('your_large_data.csv')
步骤2:生成全局打乱的索引
用numpy生成打乱的行索引数组,这个数组的内存占用其实很低(比如1亿行的话,int64类型也就800MB左右,大部分机器都能扛住):
# 生成0到total_rows-1的打乱索引(对应数据行的位置,表头是第0行) shuffled_indices = np.random.permutation(total_rows)
步骤3:分批读取打乱后的行
把打乱的索引分成小块,每次读取对应索引的行,这里要注意把索引转换成行号(加1,因为数据行从第1行开始),并把目标行号转成集合提升判断效率:
CHUNK_SIZE = 100000 # 根据你的内存情况调整,比如10万行一个块 # 把打乱的索引拆分成多个块 index_chunks = np.array_split(shuffled_indices, total_rows // CHUNK_SIZE + 1) for idx_chunk in index_chunks: # 转换为CSV里的实际行号(表头是第0行,数据行从1开始) rows_to_read = idx_chunk + 1 rows_set = set(rows_to_read) # 只读取目标行,跳过其他行 df_chunk = pd.read_csv( 'your_large_data.csv', skiprows=lambda x: x not in rows_set and x != 0, # 保留表头和目标行 nrows=len(idx_chunk) ) # 这里就可以用这个块做小批量训练了 # your_training_function(df_chunk)
如果需要重复使用打乱后的数据集,还可以把每个块写入单独的小CSV文件,之后训练时直接读取这些文件即可。
方案二:用Dask一键搞定(更简洁高效)
如果愿意引入Dask这个专门处理大数据集的库,那事情就简单多了——Dask会自动帮你处理内存、分块和全局打乱,代码量极少:
import dask.dataframe as dd # 用Dask读取CSV,自动分块 ddf = dd.read_csv('your_large_data.csv') # 全局打乱数据,frac=1表示使用全部数据,random_state固定随机种子 shuffled_ddf = ddf.sample(frac=1, random_state=42) # 迭代读取打乱后的块,直接用于训练 for df_chunk in shuffled_ddf.iterchunks(): # 小批量训练逻辑 # your_training_function(df_chunk)
为啥之前的分块单独打乱不行?
你之前的操作是把原数据切成连续块,然后每个块内打乱——但原数据的全局顺序(比如按类别、时间排序的偏置)依然存在,训练时还是会先集中学某一类数据,导致模型训练偏差。而上面的两种方案都是全局层面的随机打乱,每个训练batch里的数据都是从全量数据里随机抽取的,完全符合机器学习的训练要求。
内容的提问来源于stack exchange,提问作者Anh Pham
相关产品推荐
相关产品推荐

