【问题标题】:Improve speed of python algorithm提高python算法的速度
【发布时间】:2021-12-14 14:56:19
【问题描述】:

我已将 Sentiment140 数据集用于 twitter 进行情绪分析

代码:

从推文中获取单词:

tweet_tokens = []
[tweet_tokens.append(dev.get_tweet_tokens(idx)) for idx, item in enumerate(dev)]

从令牌中获取未知单词

words_without_embs = []
[[words_without_embs.append(w) for w in tweet if w not in word2vec] for tweet in tweet_tokens]
len(words_without_embs)

最后一部分代码,计算向量作为左右词(上下文)的均值

vectors = {} # alg
for word in words_without_embs:
  mean_vectors = []
  for tweet in tweet_tokens:
    if word in tweet:
      idx = tweet.index(word)
      try:
        mean_vector = np.mean([word2vec.get_vector(tweet[idx-1]), word2vec.get_vector(tweet[idx+1])], axis=0)
        mean_vectors.append(mean_vector)
      except:
        pass

    if tweet == tweet_tokens[-1]: # last iteration
      mean_vector_all_tweets = np.mean(mean_vectors, axis=0)
      vectors[word] = mean_vector_all_tweets

有 1058532 个单词,这段代码的最后一部分运行速度很慢,大约每分钟 250 个单词。

如何提高这个算法的速度?

【问题讨论】:

标签: python algorithm machine-learning nlp word2vec


【解决方案1】:

您的代码慢的主要原因之一是检查tweet_tokens 中每条推文的所有单词(接近 100 万个单词)是否存在。因此,您实现的时间复杂度为1e6 * |tweet_tokens|

1) 第一次改进(减少搜索和比较)

但是,您可以通过首先标记每个tweet,然后找到单词的索引来做得更好。如果你在现有的单词上构建了一本字典,你可以从字典中找到最多log(1e6) ~ 25比较的单词token的索引。因此,在这种情况下,时间复杂度最多为25 * |tweet_tokens|。因此,您可以将代码的性能提高1e6/25 = 40000 倍!

2) 二次改进(减少 Word2Vec 计算)

此外,您总是在计算不同推文中相同单词的向量。因此,每个单词的向量将被计算f-times f 是推文中单词的频率。一个合理的解决方案是计算一次words_without_embs 中所有单词的向量(可以是离线过程)。然后,例如,根据单词字典中的单词索引存储所有这些向量(以某种方式根据单词查询快速找到它们)。最终,只需从准备好的数据结构中读取它以进行平均计算。在这种情况下,除了提升 40000 倍之外,您还可以通过推文中所有单词的频率之和来提高代码的性能。

【讨论】:

    【解决方案2】:

    这看起来可以很好地与一些小的编辑并行。

    • 您能否将最后一个if tweet == tweet_tokens[-1]: 块移到一个级别并删除它的“if”语句?这本身只会略微提高速度,但有了它,您可以更有效地并行化代码。
    • 考虑将内部 for 循环设为自己的函数并通过多线程设置调用它。 (有关描述和示例,请参阅this site 的 ThreadPoolExecutor 和 thread_function()。)然后,您可以为单个机器上的每个处理器或分布式环境中的每个 VM 设置一个单独的线程。这应该可以实现更有效的扩展。
    • 以@DarrylG 的上述评论为基础,重构这些列表理解,以避免将 .append() 与现有列表一起使用。 .append() 比等效的列表理解要慢。例如,将[tweet_tokens.append(dev.get_tweet_tokens(idx)) for idx, item in enumerate(dev)] 更改为tweet_tokens = [dev.get_tweet_tokens(idx) for idx, item in enumerate(dev)]

    【讨论】:

    • 枚举有问题,最后一个索引不存在,出现Value错误,因此tweet_tokens =为空
    • 你能给出一个重现枚举问题的示例列表吗?
    【解决方案3】:

    处理未知单词的更常见(可能更好)的策略包括:

    • 训练/使用一个模型,例如 FastText,它可以为词汇外 (OOV) 单词提供猜测向量
    • 获取更多训练数据,因此可以从实际用法中学习更多未知单词的向量
    • 完全忽略未知单词

    您似乎决定通过平均所有直接邻居来为 OOV 词合成新向量。我认为这不会特别有效。在词向量的多种下游使用中,它只是倾向于加重词的上下文邻居——这也可以通过完全忽略未知词来非常简单/便宜地实现。

    但考虑到您想要做的事情,最好的方法是在识别words_without_embs 的同一遍中收集相邻的单词。

    例如,将words_without_embs 设置为dict(或者DefaultDict),其中每个键都是需要向量的单词,每个值是所有相邻单词的list'目前已经找到了。

    然后,在tweet_tokens 上的一个循环将使用需要向量的单词keys 填充words_without_embs,同时将所有相邻的values 填充- 到目前为止看到的单词。

    然后,在words_without_embs 键上的最后一个循环将简单地获取现有的相邻单词列表以进行平均。 (不再多次通过tweet_tokens。)

    但同样:所有这些工作可能不会超过简单地删除未知单词的基线实践。

    【讨论】:

    • 谢谢,找levenshtein distance,找一个相似的词怎么样?大多数未知单词是拼写错误
    • 可能是值得的——当然,这种“编辑距离”度量用于各种拼写或 OCR 校正算法。您可以尝试对包含超稀有或其他未知单词的数据进行传递,并将它们转换为已知或高于某个频率阈值的最接近编辑距离的单词。但你这是一种临时措施,它可能比它的价值更复杂——所以你应该能够定量地检查它是伤害还是帮助。
    • 请注意,如果有足够的数据,常见的拼写错误会经常出现,以至于模型可以从中学习合理的上下文词向量,这些词向量接近“预期”词。在像 FastText 这样的模型中,由于拼写错误共享带有“真实”拼写的 character-n-grams(片段),因此它们获得了比猜测更好的合成向量。你甚至可以采用非 FastText 词向量集,并尝试这种基于编辑距离的相似已知向量的混合——但这可能是大量的每个词的计算,而收益可以忽略不计。 (在别处分配相同的编码时间训练时间可能会更好。)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-09-18
    • 1970-01-01
    • 2015-09-27
    • 1970-01-01
    • 1970-01-01
    • 2019-08-01
    • 1970-01-01
    相关资源
    最近更新 更多