0

ディスクからファイルを読み取って作成された大きな辞書に対して分析を行おうとしています。読み取り操作により、安定したメモリ フットプリントが得られます。次に、その辞書から一時的な辞書にコピーしたデータに基づいて計算を実行するメソッドがあります。これにより、すべてのコピーとデータの使用がメソッド内でスコープされ、メソッド呼び出しの最後に消えることを望んでいました。

悲しいことに、私は何か間違ったことをしています。customerdict の定義は次のとおりです (.py 変数の先頭で定義)。

customerdict = collections.defaultdict(dict)

オブジェクトの形式は {customerid: dictionary{id: 0||1}} です。

同様に定義された allids という辞書もあります。

以下の sim_pearson 距離を計算する方法があります (Programming Collective Intelligence のコードを変更したもの)。

def sim_pearson(custID1, custID2):
si = []

smallcustdict = {}
smallcustdict[custID1] = customerdict[custID1]
smallcustdict[custID2] = customerdict[custID2]

#a loop to round out the remaining allids object to fill in 0 values
for customerID, catalog in smallcustdict.iteritems():
    for id in allids:
        if id not in catalog:
            smallcustdict[customerID][asin] = 0.0

#get the list of mutually rated items
for id in smallcustdict[custID1]:
    if id in smallcustdict[custID2]:
        si.append(id) # = 1

#return 0 if there are no matches
if len(si) == 0: return 0

#add up all the preferences
sum1 = sum([smallcustdict[custID1][id] for id in si])
sum2 = sum([smallcustdict[custID2][id] for id in si])

#sum up the squares
sum1sq = sum([pow(smallcustdict[custID1][id],2) for id in si])
sum2sq = sum([pow(smallcustdict[custID2][id],2) for id in si])

#sum up the products
psum = sum([smallcustdict[custID1][id] * smallcustdict[custID2][id] for id in si])

#calc Pearson score
num = psum - (sum1*sum2/len(si))
den = sqrt((sum1sq - pow(sum1,2)/len(si)) * (sum2sq - pow(sum2,2)/len(si)))

del smallcustdict
del si
del sum1
del sum2
del sum1sq
del sum2sq
del psum

if den == 0:
    return 0

return num/den

sim_pearson メソッドをループするたびに、python.exe のメモリ フットプリントが無制限に増加します。「del」メソッドを使用して、ローカルスコープ変数を明示的に削除しようとしました。

タスクマネージャーを見ると、メモリが 6 ~ 10Mb ずつ増加しています。最初の customerdict がセットアップされると、フットプリントは 137Mb になります。

このようにしてメモリが不足している理由はありますか?

4

2 に答える 2

3

I presume the issue is here:

smallcustdict[custID1] = customerdict[custID1]
smallcustdict[custID2] = customerdict[custID2]

#a loop to round out the remaining allids object to fill in 0 values
for customerID, catalog in smallcustdict.iteritems():
    for id in allids:
        if id not in catalog:
            smallcustdict[customerID][asin] = 0.0

The dictionaries from customerdict are being referenced in smallcustdict - so when you add to them, you they persist. This is the only point that I can see where you do anything that will persist out of scope, so I would imagine this is the problem.

Note you are making a lot of work for yourself in many places by not using list comps, doing the same thing repeatedly, and not making generic ways to do things, a better version might be as follows:

import collections
import functools
import operator

customerdict = collections.defaultdict(dict)

def sim_pearson(custID1, custID2):

    #Declaring as a dict literal is nicer.
    smallcustdict = {
        custID1: customerdict[custID1],
        custID2: customerdict[custID2],
    }

    # Unchanged, as I'm not sure what the intent is here.
    for customerID, catalog in smallcustdict.iteritems():
        for id in allids:
            if id not in catalog:
                smallcustdict[customerID][asin] = 0.0

    #dict views are set-like, so the easier way to do what you want is the intersection of the two.
    si = smallcustdict[custID1].viewkeys() & smallcustdict[custID2].viewkeys()

    #if not is a cleaner way of checking for no values.
    if not si:
        return 0

    #Made more generic to avoid repetition and wastefully looping repeatedly.
    parts = [list(part) for part in zip(*((value[id] for value in smallcustdict.values()) for id in si))]

    sums = [sum(part) for part in parts]
    sumsqs = [sum(pow(i, 2) for i in part) for part in parts]
    psum = sum(functools.reduce(operator.mul, part) for part in zip(*parts))

    sum1, sum2 = sums
    sum1sq, sum2sq = sumsqs

    #Unchanged.
    num = psum - (sum1*sum2/len(si))
    den = sqrt((sum1sq - pow(sum1,2)/len(si)) * (sum2sq - pow(sum2,2)/len(si)))

    #Again using if not.
    if not den:
        return 0
    else:
        return num/den

Note that this is entirely untested as the code you gave isn't a complete example. However, It should be easy enough to use as a basis for improvement.

于 2012-11-10T01:07:57.047 に答える
1

次の 2 行を変更してみてください。

smallcustdict[custID1] = customerdict[custID1]
smallcustdict[custID2] = customerdict[custID2]

smallcustdict[custID1] = customerdict[custID1].copy()
smallcustdict[custID2] = customerdict[custID2].copy()

そうすれば、2 つの辞書に加えた変更は、関数が戻ったcustomerdictときに保持されません。sim_pearson()

于 2012-11-10T03:06:33.907 に答える