目录
1. 惰性加载 vs 立即加载
特性 | 惰性加载 (Dask) | 立即加载 (Pandas) |
数据读取时机 | 只记录操作指令,不立即读文件 | 调用时直接加载全部数据到内存 |
内存占用 | 仅存储任务描述,内存占用极低 | 数据全部存入内存,占用高 |
执行触发条件 | 遇到 .compute() 或 .persist() 才执行 | 代码执行到该行立即加载 |
适用场景 | 超大数据集(超过内存容量) | 小中型数据集(内存可容纳) |
特性 | 惰性加载 (Dask) | 立即加载 (Pandas) |
只记录操作指令,不立即读文件 | 调用时直接加载全部数据到内存 | |
仅存储任务描述,内存占用极低 | 数据全部存入内存,占用高 | |
遇到 或 才执行 | 代码执行到该行立即加载 | |
超大数据集(超过内存容量) | 小中型数据集(内存可容纳) |
示例对比:
# 惰性加载(Dask) ratings = dd.read_csv("big_file.csv") # 此时仅创建任务计划,不读文件 result = ratings.groupby('user').mean().compute() # 触发实际计算 # 立即加载(Pandas) ratings = pd.read_csv("small_file.csv") # 立即读取全部数据到内存 |
2. Dask 如何实现惰性加载?
- 任务图(Task Graph):Dask 会先构建一个计算任务的逻辑图(记录 "要做什么"),但不执行。例如:
# 以下代码仅构建任务图,无实际计算 ddf = dd.read_csv("data.csv") ddf = ddf[ddf['rating'] > 3] mean_rating = ddf['rating'].mean() |
- 触发执行:调用 .compute() 时,Dask 才会:
- 按任务图分块读取数据
- 逐步执行过滤、计算等操作
- 返回最终结果
3. 为什么在推荐系统中使用惰性加载?
- 处理超大规模评分数据:用户评分数据通常量级极大(如数百万条),直接加载到内存会崩溃。
- 内存控制:惰性加载允许按需分块处理数据,避免内存溢出(OOM)。
- 并行计算优化:Dask 可以自动将任务分配到多核 / 多机执行,加速预处理。
4. 代码中的具体惰性操作
在原始代码中,这些操作是惰性的:
ratings = dd.read_csv(self.ratings_path) # 惰性读取 ratings = ratings.drop_duplicates(...) # 惰性记录去重任务 user_ids = ratings['userId'].unique() # 惰性记录唯一值计算 |
直到调用 .compute() 才真正执行:
self.user_ids = ratings['userId'].unique().compute() # 触发实际计算 |
5. 惰性加载的优缺点
优点 | 缺点 |
节省内存,处理超大数据 | 调试困难(无法直接查看中间结果) |
支持并行和分布式计算 | 需要手动触发计算(.compute ()) |
可优化计算流程(合并重复操作) | 对小数据反而更慢(额外调度开销) |
6. 典型应用场景
- 大数据预处理:清洗 / 过滤超过内存的 CSV 文件
- 特征工程:逐步生成大规模机器学习特征
- 推荐系统 / 广告系统:处理用户行为日志(点击、评分等)
在推荐系统中,惰性加载让系统能够处理百万级用户和千万级评分记录,而不会因内存不足崩溃。