在 BigQuery 中使用 javascript udf 进行 tf idf 计算时,UDF 工作程序在执行期间超时

Posted

技术标签:

【中文标题】在 BigQuery 中使用 javascript udf 进行 tf idf 计算时,UDF 工作程序在执行期间超时【英文标题】:UDF worker timed out during execution when using javascript udf in BigQuery for tf idf calculation 【发布时间】:2019-07-30 05:17:10 【问题描述】:

我尝试在 BigQuery 中实现一个查询,该查询使用 tf-idf 分数从更大的文档集合中查找文档的热门关键字。

在计算关键字的 tf-idf 分数之前,我清理文档(例如删除停用词和标点符号),然后从文档中创建 1、2、3 和 4-gram,然后在 n-克。

为了执行这种清理、n-gram 创建和词干提取,我使用了 javascript 库和 js udf。这是示例查询:

CREATE TEMP FUNCTION nlp_compromise_tokens(str STRING)
RETURNS ARRAY<STRUCT<ngram STRING, count INT64>> LANGUAGE js AS '''
  // creating 1,2,3 and 4 grams using compormise js
  // before that I remove stopwords using .removeStopWords
  // function lent from remove_stop_words.js
  tokens_from_compromise = nlp(str.removeStopWords()).normalize().ngrams(max:4).data()

  // The stemming function that stems
  // each space separated tokens inside the n-grams
  // I use snowball.babel.js here
  function stems_from_space_separated_string(tokens_string) 
    var stem = snowballFactory.newStemmer('english').stem;
    splitted_tokens = tokens_string.split(" ");
    splitted_stems = splitted_tokens.map(x => stem(x));
    return splitted_stems.join(" ")
  

  // Returning the n-grams from compromise which are 
  // stemmed internally and at least length of 2
  // alongside the count of the token inside the document
  var ngram_count = tokens_from_compromise.map(function(item) 
    return 
      ngram: stems_from_space_separated_string(item.normal),
      count: item.count
    ;
  );
  return ngram_count
'''
OPTIONS (
  library=["gs://fh-bigquery/js/compromise.min.11.14.0.js","gs://syed_mag/js/snowball.babel.js","gs://syed_mag/js/remove_stop_words.js"]);

with doc_table as (
  SELECT 1 id, "A quick brown 20 fox fox fox jumped over the lazy-dog" doc UNION ALL
  SELECT 2, "another 23rd quicker browner fox jumping over Lazier broken! dogs." UNION ALL
  SELECT 3, "This dog is more than two-feet away." #UNION ALL
),
  ngram_table as(
  select
    id,
    doc,
    nlp_compromise_tokens(doc) as compromise_tokens
  from
    doc_table),
n_docs_table as (
  select count(*) as n_docs from ngram_table
),
df_table as (
SELECT
  compromise_token.ngram,
  count(*) as df
FROM
  ngram_table, UNNEST(compromise_tokens) as compromise_token
GROUP BY
  ngram
),

idf_table as(
SELECT
  ngram,
  df,
  n_docs,
  LN((1+n_docs)/(1+df)) + 1 as idf_smooth
FROM
  df_table
CROSS JOIN
  n_docs_table),

tf_idf_table as (  
SELECT
  id,
  doc,
  compromise_token.ngram,
  compromise_token.count as tf,
  idf_table.ngram as idf_ngram,
  idf_table.idf_smooth,
  compromise_token.count * idf_table.idf_smooth as tf_idf
FROM
  ngram_table, UNNEST(compromise_tokens) as compromise_token
JOIN
  idf_table
ON
  compromise_token.ngram = idf_table.ngram)

SELECT
  id,
  ARRAY_AGG(STRUCT(ngram,tf_idf)) as top_keyword,
  doc
FROM(
  SELECT
    id,
    doc,
    ngram,
    tf_idf,
    ROW_NUMBER() OVER (PARTITION BY id ORDER BY tf_idf DESC) AS rn
  FROM
    tf_idf_table)
WHERE
  rn < 5
group by
  id,
  doc

示例输出如下所示:

在此示例中只有三个示例手工行。

当我尝试使用具有 1000 行的稍大一点的表执行相同的代码时,它再次运行良好,尽管需要更长的时间才能完成(仅 1000 行大约需要 6 分钟)。此示例表 (1MB) 可以是 json 格式的found here。

现在,当我在 a larger dataset (159K rows - 155MB) 上尝试查询时,查询在大约 30 分钟后耗尽,并显示以下消息:

错误:用户定义函数:UDF worker 在执行期间超时。 为 worker worker-109498 触发了意外中止:job_timeout (错误代码:超时)

我能否改进我的 udf 函数或整体查询结构,以确保它在更大的数据集(124,783,298 行 - 244GB)上平稳运行?

注意我已经对 google 存储中的 js 文件给予了适当的许可,以便任何人都可以访问这些 javascrip 来运行示例查询。

【问题讨论】:

【参考方案1】:

BigQuery UDF 非常方便,但计算量不大,不会让您的查询变慢或耗尽资源。有关限制和最佳实践,请参阅 doc reference。一般来说,您可以在本机 SQL 中转换的任何 UDF 逻辑都将更快并且使用更少的资源。

我会将您的分析分成多个步骤,将结果保存到每个步骤的新表中:

    清理文档(例如删除停用词和标点符号) 从文档中创建 1、2、3 和 4-gram,然后在 n-gram 内进行词干提取。 计算分数。

旁注:您可以使用多个 CTE 运行它来保存阶段,而不是将每个步骤保存到本机表中,但我不知道这是否会使查询超出资源限制。

【讨论】:

以上是关于在 BigQuery 中使用 javascript udf 进行 tf idf 计算时,UDF 工作程序在执行期间超时的主要内容,如果未能解决你的问题,请参考以下文章