2011-02-18 21 views
8

Tôi đang viết chỉ mục đảo ngược cho công cụ tìm kiếm trên bộ sưu tập tài liệu. Ngay bây giờ, tôi đang lưu trữ chỉ mục làm từ điển từ điển. Tức là, mỗi từ khóa ánh xạ tới một từ điển docID-> vị trí xuất hiện.Sử dụng cPickle để tuần tự hóa từ điển lớn gây ra MemoryError

Mô hình dữ liệu trông giống như sau: {từ: {DOC_NAME: [location_list]}}

Xây dựng chỉ số trong bộ nhớ hoạt động tốt, nhưng khi tôi cố gắng serialize vào đĩa, tôi nhấn một MemoryError. Đây là mã của tôi:

# Write the index out to disk 
serializedIndex = open(sys.argv[3], 'wb') 
cPickle.dump(index, serializedIndex, cPickle.HIGHEST_PROTOCOL) 

Ngay trước khi tuần tự hóa, chương trình của tôi đang sử dụng khoảng 50% bộ nhớ (1,6 Gb). Ngay sau khi tôi thực hiện cuộc gọi tới cPickle, bộ nhớ sử dụng bộ nhớ của tôi lên tới 80% trước khi bị rơi.

Tại sao cPickle sử dụng quá nhiều bộ nhớ để tuần tự hóa? Có cách nào tốt hơn để tiếp cận vấn đề này không?

Trả lời

10

cPickle cần sử dụng nhiều bộ nhớ bổ sung vì nó phát hiện chu kỳ. Bạn có thể thử sử dụng mô-đun nguyên soái nếu bạn chắc chắn dữ liệu của mình không có chu kỳ

+1

Làm việc như một sự quyến rũ. Sửa chữa cực kỳ đơn giản - về cơ bản chỉ thay đổi "dưa" để "soái" và đã được thực hiện. Tôi đã không nhận ra cPickle thực hiện chu kỳ phát hiện. Bằng cách sử dụng marshal thay vào đó, ghi vào đĩa mất một vài giây như trái ngược với 20 phút, và giảm tiêu thụ bộ nhớ từ 30% và giảm xuống gần 0%. Cảm ơn! –

+0

Giải pháp đơn giản cộng với lời giải thích ngắn gọn, tuyệt vời 100%. – mitchus

+0

Điều cần biết, cảm ơn @gnibbler! –

0

Có thư viện dưa khác mà bạn có thể thử. Cũng có thể có một số cài đặt cPickle bạn có thể thay đổi.

Các tùy chọn khác: Chia từ điển của bạn thành các phần nhỏ hơn và cPickle từng phần. Sau đó, đặt chúng lại với nhau khi bạn tải mọi thứ vào.

Xin lỗi điều này là mơ hồ, tôi chỉ đang viết ra khỏi đỉnh đầu. Tôi nghĩ rằng nó vẫn có thể hữu ích vì không ai khác trả lời.

0

Bạn cũng có thể đang sử dụng công cụ sai cho công việc này. Nếu bạn muốn tồn tại một lượng lớn dữ liệu được lập chỉ mục, tôi khuyên bạn nên sử dụng cơ sở dữ liệu trên đĩa SQLite (hoặc, tất nhiên, chỉ là một cơ sở dữ liệu bình thường) với một ORM như SQLObject hoặc SQL Alchemy.

Đây sẽ chăm sóc trong những điều trần tục như khả năng tương thích, tối ưu hóa định dạng cho mục đích, chứ không phải giữ tất cả dữ liệu trong bộ nhớ cùng một lúc để bạn chạy ra khỏi bộ nhớ ...

Added: Bởi vì tôi là Tuy nhiên, chủ yếu là vì tôi là một người tốt, đây là bản demo xuất hiện để làm những gì bạn cần (nó sẽ tạo một tệp SQLite trong thư mục hiện tại của bạn và xóa nó nếu một tệp với tên đã tồn tại, vì vậy hãy đặt nó ở đâu đó trống trước):

import sqlobject 
from sqlobject import SQLObject, UnicodeCol, ForeignKey, IntCol, SQLMultipleJoin 
import os 

DB_NAME = "mydb" 
ENCODING = "utf8" 

class Document(SQLObject): 
    dbName = UnicodeCol(dbEncoding=ENCODING) 

class Location(SQLObject): 
    """ Location of each individual occurrence of a word within a document. 
    """ 
    dbWord = UnicodeCol(dbEncoding=ENCODING) 
    dbDocument = ForeignKey('Document') 
    dbLocation = IntCol() 

TEST_DATA = { 
    'one' : { 
     'doc1' : [1,2,10], 
     'doc3' : [6], 
    }, 

    'two' : { 
     'doc1' : [2, 13], 
     'doc2' : [5,6,7], 
    }, 

    'three' : { 
     'doc3' : [1], 
    }, 
}   

if __name__ == "__main__": 
    db_filename = os.path.abspath(DB_NAME) 
    if os.path.exists(db_filename): 
     os.unlink(db_filename) 
    connection = sqlobject.connectionForURI("sqlite:%s" % (db_filename)) 
    sqlobject.sqlhub.processConnection = connection 

    # Create the tables 
    Document.createTable() 
    Location.createTable() 

    # Import the dict data: 
    for word, locs in TEST_DATA.items(): 
     for doc, indices in locs.items(): 
      sql_doc = Document(dbName=doc) 
      for index in indices: 
       Location(dbWord=word, dbDocument=sql_doc, dbLocation=index) 

    # Let's check out the data... where can we find 'two'? 
    locs_for_two = Location.selectBy(dbWord = 'two') 

    # Or... 
    # locs_for_two = Location.select(Location.q.dbWord == 'two') 

    print "Word 'two' found at..." 
    for loc in locs_for_two: 
     print "Found: %s, p%s" % (loc.dbDocument.dbName, loc.dbLocation) 

    # What documents have 'one' in them? 
    docs_with_one = Location.selectBy(dbWord = 'one').throughTo.dbDocument 

    print 
    print "Word 'one' found in documents..." 
    for doc in docs_with_one: 
     print "Found: %s" % doc.dbName 

Đây là chứng nhận không phải là cách duy nhất (hoặc nhất thiết là cách tốt nhất) để làm điều này. Việc các bảng Tài liệu hoặc Word có phải là các bảng riêng biệt khỏi bảng Vị trí hay không phụ thuộc vào dữ liệu của bạn và cách sử dụng thông thường. Trong trường hợp của bạn, bảng "Word" có thể là một bảng riêng biệt với một số cài đặt bổ sung để lập chỉ mục và tính duy nhất.

+0

Cảm ơn đề xuất của bạn. Bây giờ, tôi sẽ sử dụng nguyên soái thay vì dưa, nhưng tôi có thể xem lại và di chuyển sang một giải pháp dựa trên db trong tương lai. Chúc mừng! –

+0

@Stephen Poletto - đó là mát mẻ, nếu marhsal hoạt động, nó hoạt động, và điều này có thể vẫn ở đây cho hậu thế :) – detly

Các vấn đề liên quan