ホームページ  >  記事  >  バックエンド開発  >  比較的メモリを節約する疎行列 Python ストレージ ソリューション

比較的メモリを節約する疎行列 Python ストレージ ソリューション

大家讲道理
大家讲道理オリジナル
2016-11-07 17:15:161206ブラウズ

レコメンデーション システムでは、実際には数学におけるスパース行列である user_id、item_id、評価などのデータを処理する必要があることがよくありますが、Scipy はこの問題を解決するためのスパース モジュールを提供していますが、scipy.sparse には使用に適さない多くの問題があります。 data[i, ...]、data[..., j]、data[i, j] の高速スライスを同時にサポートできません。 2. データはメモリに保存されるため、十分にサポートできません。大量のデータ処理。

data[i, ...]、data[..., j] の高速スライスをサポートするには、大量のデータを保存するために、i または j のデータを同時に一元的に保存する必要があります。のデータもハードディスク上に配置する必要があり、メモリをバッファとして使用します。ここでの解決策は比較的単純です。特定の i (9527 など) については、そのデータは dict['i9527'] に保存されます。 、そのすべてのデータは dict['j3306'] に格納されています。data[9527, ...] を取り出す必要がある場合は、dict['i9527'] を取り出すだけです。元々は dict オブジェクトです。 、特定の j に対応する値を保存します。メモリ領域を節約するために、この辞書をバイナリ文字列の形式で保存し、コードを直接入力します:

'''
Sparse Matrix
'''
import struct
import numpy as np
import bsddb
from cStringIO import StringIO
  
class DictMatrix():
    def __init__(self, container = {}, dft = 0.0):
        self._data  = container
        self._dft   = dft
        self._nums  = 0
  
    def __setitem__(self, index, value):
        try:
            i, j = index
        except:
            raise IndexError('invalid index')
  
        ik = ('i%d' % i)
        # 为了节省内存,我们把j, value打包成字二进制字符串
        ib = struct.pack('if', j, value)
        jk = ('j%d' % j)
        jb = struct.pack('if', i, value)
  
        try:
            self._data[ik] += ib
        except:
            self._data[ik] = ib
        try:
            self._data[jk] += jb
        except:
            self._data[jk] = jb
        self._nums += 1
  
    def __getitem__(self, index):
        try:
            i, j = index
        except:
            raise IndexError('invalid index')
  
        if (isinstance(i, int)):
            ik = ('i%d' % i)
            if not self._data.has_key(ik): return self._dft
            ret = dict(np.fromstring(self._data[ik], dtype = 'i4,f4'))
            if (isinstance(j, int)): return ret.get(j, self._dft)
  
        if (isinstance(j, int)):
            jk = ('j%d' % j)
            if not self._data.has_key(jk): return self._dft
            ret = dict(np.fromstring(self._data[jk], dtype = 'i4,f4'))
  
        return ret
  
    def __len__(self):
        return self._nums
  
    def __iter__(self):
        pass
  
    '''
    从文件中生成matrix
    考虑到dbm读写的性能不如内存,我们做了一些缓存,每1000W次批量写入一次
    考虑到字符串拼接性能不太好,我们直接用StringIO来做拼接
    '''
    def from_file(self, fp, sep = 't'):
        cnt = 0
        cache = {}
        for l in fp:
            if 10000000 == cnt:
                self._flush(cache)
                cnt = 0
                cache = {}
            i, j, v = [float(i) for i in l.split(sep)]
  
            ik = ('i%d' % i)
            ib = struct.pack('if', j, v)
            jk = ('j%d' % j)
            jb = struct.pack('if', i, v)
  
            try:
                cache[ik].write(ib)
            except:
                cache[ik] = StringIO()
                cache[ik].write(ib)
  
            try:
                cache[jk].write(jb)
            except:
                cache[jk] = StringIO()
                cache[jk].write(jb)
  
            cnt += 1
            self._nums += 1
  
        self._flush(cache)
        return self._nums
  
    def _flush(self, cache):
        for k,v in cache.items():
            v.seek(0)
            s = v.read()
            try:
                self._data[k] += s
            except:
                self._data[k] = s
  
if __name__ == '__main__':
    db = bsddb.btopen(None, cachesize = 268435456)
    data = DictMatrix(db)
    data.from_file(open('/path/to/log.txt', 'r'), ',')

テスト 4500W 定格データ (整数、整数、浮動小数点形式) の場合、922MB のテキスト ファイルがインポートされ、メモリ辞書を使用すると、1.2G のメモリを消費して構築が完了します。サンプル コードで bdb ストレージを使用すると、構築は 12 分で完了します。 20 分、約 300 ~ 400 MB のメモリを占有します。これはキャッシュサイズよりもそれほど大きくありません。テストを受けてください:

import timeit
timeit.Timer('foo = __main__.data[9527, ...]', 'import __main__').timeit(number = 1000)

は 1.4788 秒を消費し、1 つのデータを読み取るのに約 1.5 ミリ秒かかります。

Dict クラスを使用してデータを保存するもう 1 つの利点は、メモリ Dict やその他の形式の DBM、さらには伝説的な Tokyo Cabinet さえも使用できることです…

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。