You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于大量大型字典构建大型稀疏矩阵的优化方案咨询

高效构建大规模稀疏矩阵并执行矩阵向量乘法

问题背景

我有一系列格式为dict_k:{(i,j):d_ij}的字典,其中i、j是待构建稀疏矩阵的整数索引,d_ij为浮点数,每个字典最多包含100万键值对。共有约160个这类字典,每个大小约16MB,字典间可能存在键值重复(如不同字典中都有(0,0):1.25)。需要构建包含所有键值对的稀疏矩阵,最终用于执行矩阵向量乘法,矩阵规模约为500万×500万。

初始方案是构建巨型字典再转COO矩阵,但过程导致电脑卡顿:

bigDict={}
for i in range(160):
    with open(dictionary_path, "rb") as fp:   
        bigDict.update(pickle.load(fp))

优化方案

1. 跳过巨型字典,直接收集数据构建矩阵

构建巨型字典会产生极高的内存峰值(仅字典结构本身就会占用额外内存),直接拆分每个字典的键值到三个列表(行索引、列索引、值)是更高效的方式,同时可按需处理重复键:

场景A:重复键的值需要累加

这是矩阵构建的常规需求,直接收集所有条目后转COO再转CSR(CSR是矩阵向量乘法效率最高的格式),SciPy会自动合并相同(i,j)位置的值:

import numpy as np
from scipy.sparse import coo_matrix, csr_matrix

rows = []
cols = []
vals = []

# 遍历所有字典文件
for idx in range(160):
    dict_path = f"your_dict_{idx}.pkl"
    with open(dict_path, "rb") as fp:
        current_dict = pickle.load(fp)
        # 拆分键值对到列表
        for (r, c), v in current_dict.items():
            rows.append(r)
            cols.append(c)
            vals.append(v)

# 构建COO矩阵并转换为CSR
coo = coo_matrix((vals, (rows, cols)), shape=(5_000_000, 5_000_000))
csr_matrix = coo.tocsr()

# 执行矩阵向量乘法
result = csr_matrix.dot(your_vector)

场景B:重复键保留最后一次出现的值

如果需要用后读取字典的值覆盖之前的重复键,需先去重再构建矩阵:

import numpy as np
from scipy.sparse import csr_matrix

rows = []
cols = []
vals = []

for idx in range(160):
    dict_path = f"your_dict_{idx}.pkl"
    with open(dict_path, "rb") as fp:
        current_dict = pickle.load(fp)
        rows.extend(r for r, c in current_dict.keys())
        cols.extend(c for r, c in current_dict.keys())
        vals.extend(current_dict.values())

# 转换为numpy数组便于处理
rows_np = np.array(rows, dtype=np.int64)
cols_np = np.array(cols, dtype=np.int64)
vals_np = np.array(vals, dtype=np.float64)

# 按行、列排序,保留最后一个重复项
sorted_indices = np.lexsort((cols_np, rows_np))
sorted_rows = rows_np[sorted_indices]
sorted_cols = cols_np[sorted_indices]
sorted_vals = vals_np[sorted_indices]

# 生成唯一掩码,筛选最后出现的条目
unique_mask = np.concatenate([[True], (sorted_rows[1:] != sorted_rows[:-1]) | (sorted_cols[1:] != sorted_cols[:-1])])
final_rows = sorted_rows[unique_mask]
final_cols = sorted_cols[unique_mask]
final_vals = sorted_vals[unique_mask]

# 构建CSR矩阵
csr_matrix = csr_matrix((final_vals, (final_rows, final_cols)), shape=(5_000_000, 5_000_000))

2. 分块处理降低内存峰值

如果内存仍紧张,可分批次处理字典,每批构建一个小CSR矩阵后合并:

from scipy.sparse import csr_matrix
import pickle

total_csr = None
block_size = 10  # 每批处理10个字典

for idx in range(0, 160, block_size):
    batch_rows = []
    batch_cols = []
    batch_vals = []
    # 处理当前批次的字典
    for batch_idx in range(idx, min(idx + block_size, 160)):
        dict_path = f"your_dict_{batch_idx}.pkl"
        with open(dict_path, "rb") as fp:
            current_dict = pickle.load(fp)
            for (r, c), v in current_dict.items():
                batch_rows.append(r)
                batch_cols.append(c)
                batch_vals.append(v)
    # 构建批次CSR矩阵
    batch_csr = csr_matrix((batch_vals, (batch_rows, batch_cols)), shape=(5_000_000, 5_000_000))
    # 合并到总矩阵(累加场景)
    if total_csr is None:
        total_csr = batch_csr
    else:
        total_csr += batch_csr

3. 替换pickle为numpy二进制存储(长期优化)

如果可以重新生成字典文件,建议直接存储为numpy数组(.npy),避免pickle的序列化开销,读取速度更快:

生成文件时:

import numpy as np

# 假设current_dict是当前要存储的字典
rows = np.array([r for r, c in current_dict.keys()], dtype=np.int64)
cols = np.array([c for r, c in current_dict.keys()], dtype=np.int64)
vals = np.array(list(current_dict.values()), dtype=np.float64)

np.save(f"rows_{idx}.npy", rows)
np.save(f"cols_{idx}.npy", cols)
np.save(f"vals_{idx}.npy", vals)

读取构建矩阵时:

import numpy as np
from scipy.sparse import csr_matrix

rows_list = []
cols_list = []
vals_list = []

for idx in range(160):
    rows_list.append(np.load(f"rows_{idx}.npy"))
    cols_list.append(np.load(f"cols_{idx}.npy"))
    vals_list.append(np.load(f"vals_{idx}.npy"))

# 合并所有数组
rows_np = np.concatenate(rows_list)
cols_np = np.concatenate(cols_list)
vals_np = np.concatenate(vals_list)

csr_matrix = csr_matrix((vals_np, (rows_np, cols_np)), shape=(5_000_000, 5_000_000))

关键注意事项

  • 矩阵向量乘法优先使用CSR格式,其在稀疏矩阵-稠密向量乘法上的效率远高于COO、CSC等格式。
  • 500万×500万的矩阵需确保索引值不超出范围,避免构建矩阵时出错。

内容的提问来源于stack exchange,提问作者DarkBulle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 04:45:35