추천 시스템은 실제로 수학에서 희소 행렬인 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.5ms가 걸립니다.
import timeit timeit.Timer('foo = __main__.data[9527, ...]', 'import __main__').timeit(number = 1000)