【问题标题】:Faster way to store a NLTK FreqDict?存储 NLTK FreqDict 的更快方法?
【发布时间】:2016-03-01 03:06:54
【问题描述】:

我正在尝试加速我的应用程序,我发现下面的简单小函数 (compute_ave_freq) 实际上是最耗费时间的函数之一。罪魁祸首似乎是当它解开 NLTK FreqDist 时;这需要大量的时间。

当然,即使是这么长的时间也不到重新计算 FreqDist 所需时间的一半。有没有更好的方法来保存 NLTK FreqDist 对象?我尝试将它序列化为 JSON,但这会将它保存为一个简单的字典,从而丢失了我需要的许多 NLTK 功能。

代码如下:

def compute_ave_freq(word_forms):    
    fd = pickle.load(open("data/fd.txt", 'rb'))
    total_freq = 0
    for form in word_forms:
        freq = fd.freq(form)
        total_freq += freq
    try:
        ave_freq = total_freq/len(word_forms)
    except ZeroDivisionError:
        ave_freq = 0
    return ave_freq

这是 LineProfiler 的输出:

Total time: 0.197121 s
File: /home/username/development/appname/filename.py
Function: compute_ave_freq at line 25
Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
25                                           def compute_ave_freq(word_forms, debug=False):
26                                               # word_forms is a list of morphological variations of a word, such as
27                                               # ['كتبوا', 'كتبو', 'كتبنا', 'كتبت']
28                                           
29         1        78580  78580.0     79.1      fd = pickle.load(open("data/fd.txt", 'rb'))
30         1            3      3.0      0.0      total_freq = 0
31         5           10      2.0      0.0      for form in word_forms:
32         4        20676   5169.0     20.8          freq = fd.freq(form)
33         4            9      2.2      0.0          if debug==True:
34                                                       print(form, '\n', freq)
35         4            6      1.5      0.0          total_freq += freq
36         1            1      1.0      0.0      try:
37         1            3      3.0      0.0          ave_freq = total_freq/len(word_forms)
38                                               except ZeroDivisionError:
39                                                   ave_freq = 0
40         1            1      1.0      0.0      return ave_freq

谢谢!

【问题讨论】:

  • Unpickling 正在加载到 RAM 中,这是一个相当难以处理的问题,但是一旦加载它就可以了。可能将其放入某些数据库(例如 SQL/Mongo)中会是处理更大数据集的更好方法。否则,请稍等片刻,让它加载到 RAM 中。
  • 我认为一般规则可以是“如果您有一个数据集可以完全加载到 RAM 上而没有太大的压力,那么它并没有那么大,而且在索引/查询数据库上节省的时间可能不会是实质性的”。
  • fd = pickle.load(open("data/fd.txt", 'rb'))移到函数外,如果fd发生变化,只需将其传递给函数,即def compute_ave_freq(word_forms, fd):。否则,如果fd 不变,只需将fd 设为全局变量并加载一次即可。
  • 谢谢@alvas——我认为这可能是最好的解决方案!
  • 这确实奏效了——我将fd 设为全局变量,这将我的例句的解析时间从 7.2 秒提高到了 2.0 秒。我不知道为什么我以前没有想到这一点!

标签: python json serialization nltk pickle


【解决方案1】:

按照评论中的建议,将 fd 变量移到函数之外应该可以解决问题:

fd = pickle.load(open("data/fd.txt", 'rb'))

def compute_ave_freq(word_forms):    
    total_freq = 0
    for form in word_forms:
        freq = fd.freq(form)
        total_freq += freq
    try:
        ave_freq = total_freq/len(word_forms)
    except ZeroDivisionError:
        ave_freq = 0
    return ave_freq

但由于您要创建求和平均函数,所以这里有一个更简单的实现:

fd = pickle.load(open("data/fd.txt", 'rb'))

def compute_ave_freq(word_forms):
    try:
        return sum([fd.freq(form) for form in word_forms]) / len(word_forms)
    except ZeroDivisionError:
        return 0

或者:

fd = pickle.load(open("data/fd.txt", 'rb'))

def compute_ave_freq(word_forms):
    l = len(word_forms)
    if  l > 0:
        return sum([fd.freq(form) for form in word_forms]) / l
    else:
        return 0

或者更简单:

fd = pickle.load(open("data/fd.txt", 'rb'))

def compute_ave_freq(word_forms):
    l = len(word_forms)
    return sum([fd.freq(form) for form in word_forms]) / l if l > 0 else 0

或者lambda:

fd = pickle.load(open("data/fd.txt", 'rb'))
compute_ave_freq = lambda x: sum(fd.freq(i) for i in x)/len(x)
ave_freq = compute_ave_freq(word_forms) if len(word_forms) > 0 else 0

看看EAFP and LBYL

【讨论】:

  • 再次感谢您,也感谢您的参考。我使用了您建议的第一个变体(带有列表理解),因为lambdas 让我头疼... :-)
猜你喜欢
  • 2017-06-14
  • 1970-01-01
  • 1970-01-01
  • 2021-10-02
  • 2013-12-08
  • 1970-01-01
  • 2011-06-04
  • 2017-03-04
  • 2010-10-21
相关资源
最近更新 更多