You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spark环境下生成类Gensim风格Bigrams的技术方案问询

实现Spark下的Gensim风格全局Bigrams

要在Spark中生成和Gensim一致的全局高频Bigrams,核心是先基于全量语料筛选出符合频率/PMI阈值的双词组合,再将这些组合合并回原文本,而不是每条文本单独生成所有二元组。以下分两种方案实现:

方案一:Spark NLP实现(优先推荐)

步骤1:全局统计并筛选高频Bigrams

首先生成所有可能的双词组合,统计全局出现频率,再通过min_count和PMI(点互信息)筛选出符合条件的Bigrams(对齐Gensim的Phrases逻辑):

from sparknlp.annotator import NGramGenerator
from sparknlp.base import DocumentAssembler, Finisher
from pyspark.sql.functions import explode, col, log, split
from pyspark.sql.types import StringType

# 假设你的预处理DataFrame名为`process`,包含`lemmatized`列(Array[String]类型的分词结果)
# 1. 将分词转换为Spark NLP的Document格式,生成所有可能的Bigrams
document_assembler = DocumentAssembler() \
    .setInputCol("lemmatized") \
    .setOutputCol("document")

ngram_generator = NGramGenerator() \
    .setInputCols(["document"]) \
    .setOutputCol("ngrams") \
    .setN(2) \
    .setDelimiter("_") \
    .setEnableCumulative(False)

finisher = Finisher() \
    .setInputCols(["ngrams"]) \
    .setOutputCols(["all_bigrams"]) \
    .setOutputAsArray(True)

# 生成包含所有Bigrams的DataFrame
bigram_df = document_assembler.transform(process)
bigram_df = ngram_generator.transform(bigram_df)
bigram_df = finisher.transform(bigram_df)

# 2. 统计每个Bigram的全局出现次数,过滤掉低频组合(对应Gensim的min_count=5)
bigram_counts = bigram_df.select(explode(col("all_bigrams")).alias("bigram")) \
    .groupBy("bigram") \
    .count() \
    .filter(col("count") >= 5)

# 3. 计算PMI(点互信息)筛选有意义的Bigrams(对应Gensim的threshold)
# 先统计单个词的全局频率
word_counts = process.select(explode(col("lemmatized")).alias("word")) \
    .groupBy("word") \
    .count() \
    .withColumnRenamed("count", "word_count")

# 拆分Bigram为两个独立词
bigram_split = bigram_counts.withColumn("words", split(col("bigram"), "_")) \
    .withColumn("word1", col("words")[0]) \
    .withColumn("word2", col("words")[1]) \
    .drop("words")

# 计算PMI:PMI = log( (Bigram出现次数 * 总词数) / (词1出现次数 * 词2出现次数) )
total_words = word_counts.agg(sum("word_count")).first()[0]

bigram_pmi = bigram_split.join(word_counts, bigram_split.word1 == word_counts.word, "left") \
    .withColumnRenamed("word_count", "count1") \
    .drop("word") \
    .join(word_counts, bigram_split.word2 == word_counts.word, "left") \
    .withColumnRenamed("word_count", "count2") \
    .drop("word") \
    .withColumn("pmi", log( (col("count") * total_words) / (col("count1") * col("count2")) )) \
    .filter(col("pmi") >= 4.0)  # 阈值可根据语料调整,Gensim默认threshold=100对应PMI约4-5

# 得到最终要合并的Bigrams列表
selected_bigrams = bigram_pmi.select("bigram").rdd.map(lambda x: x[0]).collect()

步骤2:将筛选出的Bigrams合并回原文本

使用广播变量传递Bigrams集合,通过UDF高效合并原分词中的连续双词:

from pyspark.sql.functions import udf
from pyspark.sql.types import ArrayType

# 将Bigrams集合转为广播变量,避免重复加载到每个Task
bigrams_broadcast = spark.sparkContext.broadcast(set(selected_bigrams))

def merge_bigrams(tokens):
    merged = []
    i = 0
    token_len = len(tokens)
    while i < token_len:
        # 检查当前词和下一个词是否组成目标Bigram
        if i < token_len - 1 and f"{tokens[i]}_{tokens[i+1]}" in bigrams_broadcast.value:
            merged.append(f"{tokens[i]}_{tokens[i+1]}")
            i += 2
        else:
            merged.append(tokens[i])
            i += 1
    return merged

# 注册UDF并生成处理后的列
merge_udf = udf(merge_bigrams, ArrayType(StringType()))
tweets_with_bigrams = process.withColumn("lemmatized_with_bigrams", merge_udf(col("lemmatized")))

方案二:Spark MLlib实现

如果无法使用Spark NLP,可通过MLlib的NGram组件实现相同逻辑:

步骤1:统计筛选Bigrams

from pyspark.ml.feature import NGram
from pyspark.sql.functions import explode, col, split, log

# 生成所有可能的Bigrams
ngram = NGram(n=2, inputCol="lemmatized", outputCol="all_bigrams")
bigram_df = ngram.transform(process)

# 统计频率并过滤低频组合
bigram_counts = bigram_df.select(explode(col("all_bigrams")).alias("bigram")) \
    .groupBy("bigram") \
    .count() \
    .filter(col("count") >= 5)

# 后续PMI计算和筛选逻辑与Spark NLP方案完全一致,最终得到selected_bigrams列表

步骤2:合并Bigrams到原文本

使用和Spark NLP方案相同的广播变量+UDF逻辑即可,代码完全复用。

为什么你之前的方法无效?

  • Spark NLP的NGramGenerator:仅生成单条文本内的所有连续双词,没有基于全量语料筛选高频组合,因此得到的是所有可能的二元组,而非有意义的Bigrams。
  • UDF调用Gensim Phrases:按行单独训练Phrases模型,仅利用当前行的分词数据,完全没有用到全局语料,生成的Bigrams不具备通用性,不符合需求。

内容的提问来源于stack exchange,提问作者Criscas05

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 08:51:06