>  기사  >  백엔드 개발  >  상대적으로 메모리를 절약하는 희소 행렬 Python 저장소 솔루션

상대적으로 메모리를 절약하는 희소 행렬 Python 저장소 솔루션

高洛峰
高洛峰원래의
2016-10-18 09:54:261252검색

추천 시스템은 실제로 수학에서 희소 행렬인 user_id, item_id, rating과 같은 데이터를 처리해야 하는 경우가 많습니다. Scipy는 이 문제를 해결하기 위해 희소 모듈을 제공하지만 scipy.sparse에는 사용하기에 적합하지 않은 많은 문제가 있습니다. , data[i, ...], data[..., j], data[i, j] 빠른 슬라이싱을 동시에 지원할 수 없습니다. 2. 데이터가 메모리에 저장되므로 대용량 데이터를 잘 지원할 수 없습니다. .을 다루다.

데이터[i, ...], 데이터[..., j]의 빠른 슬라이싱을 지원하려면 i 또는 j의 데이터를 동시에 중앙에 저장해야 합니다. 대용량 데이터, 데이터도 필요합니다. 그 중 일부는 하드 디스크에 배치되고 메모리는 버퍼로 사용됩니다. 여기서 해결 방법은 비교적 간단합니다. 특정 i(예: 9527)의 경우 해당 데이터는 dict['i9527']에 저장됩니다. , 모든 데이터는 dict['j3306']에 저장됩니다. data[9527, ...]를 제거해야 하는 경우 dict['i9527']는 원래 dict 객체입니다. , 특정 j에 해당하는 값을 저장합니다. 메모리 공간을 절약하기 위해 이 dict를 바이너리 문자열 형식으로 저장하고 코드를 직접 입력합니다:

'''
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 텍스트 파일 가져오기, 메모리 dict를 사용하여 저장, 구성은 12분 안에 완료, 1.2G 메모리 소비, 에서 bdb 저장소 사용 샘플 코드 기준으로 20분이면 구축이 완료되는데, 캐시 크기보다 그리 크지 않은 300~400MB 정도의 메모리를 차지합니다. > 1.4788초를 소모하며, 데이터를 읽는 데 약 1.5ms가 소요됩니다.

import timeit
timeit.Timer('foo = __main__.data[9527, ...]', 'import __main__').timeit(number = 1000)
Dict 클래스를 사용하여 데이터를 저장하는 또 다른 장점은 메모리 Dict나 다른 형태의 DBM, 심지어 전설적인 Tokyo Cabinet을 사용할 수 있다는 것입니다...

좋아요, 하루만 기다려주세요. .

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.