为什么Spark的Word2Vec返回向量?

Meh*_*ran 3 java machine-learning apache-spark word2vec apache-spark-ml

运行Word2VecSpark示例,我意识到它接受了一个字符串数组并给出了一个向量。我的问题是,它不应该返回矩阵而不是向量吗?我期望每个输入单词一个向量。但是它返回一个向量周期!

或者也许它应该接受字符串,而不是一个字符串数组(一个单词)作为输入。然后,可以,它可以返回一个向量作为输出。但是接受一个字符串数组并返回一个向量对我来说没有任何意义。

[更新]

根据@Shaido的请求,以下是我的微小更改以打印输出模式的代码:

public class JavaWord2VecExample {
    public static void main(String[] args) {
        SparkSession spark = SparkSession
                .builder()
                .appName("JavaWord2VecExample")
                .getOrCreate();

        // $example on$
        // Input data: Each row is a bag of words from a sentence or document.
        List<Row> data = Arrays.asList(
                RowFactory.create(Arrays.asList("Hi I heard about Spark".split(" "))),
                RowFactory.create(Arrays.asList("I wish Java could use case classes".split(" "))),
                RowFactory.create(Arrays.asList("Logistic regression models are neat".split(" ")))
        );
        StructType schema = new StructType(new StructField[]{
                new StructField("text", new ArrayType(DataTypes.StringType, true), false, Metadata.empty())
        });
        Dataset<Row> documentDF = spark.createDataFrame(data, schema);

        // Learn a mapping from words to Vectors.
        Word2Vec word2Vec = new Word2Vec()
                .setInputCol("text")
                .setOutputCol("result")
                .setVectorSize(7)
                .setMinCount(0);

        Word2VecModel model = word2Vec.fit(documentDF);
        Dataset<Row> result = model.transform(documentDF);

        for (Row row : result.collectAsList()) {
            List<String> text = row.getList(0);
            System.out.println("Schema: " + row.schema());
            Vector vector = (Vector) row.get(1);
            System.out.println("Text: " + text + " => \nVector: " + vector + "\n");
        }
        // $example off$

        spark.stop();
    }
}
Run Code Online (Sandbox Code Playgroud)

它打印:

Schema: StructType(StructField(text,ArrayType(StringType,true),false), StructField(result,org.apache.spark.ml.linalg.VectorUDT@3bfc3ba7,true))
Text: [Hi, I, heard, about, Spark] => 
Vector: [-0.0033279924420639875,-0.0024428479373455048,0.01406305879354477,0.030621735751628878,0.00792500376701355,0.02839711122214794,-0.02286271695047617]

Schema: StructType(StructField(text,ArrayType(StringType,true),false), StructField(result,org.apache.spark.ml.linalg.VectorUDT@3bfc3ba7,true))
Text: [I, wish, Java, could, use, case, classes] => 
Vector: [-9.96453288410391E-4,-0.013741840076233658,0.013064394239336252,-0.01155538750546319,-0.010510949650779366,0.004538436819400106,-0.0036846946126648356]

Schema: StructType(StructField(text,ArrayType(StringType,true),false), StructField(result,org.apache.spark.ml.linalg.VectorUDT@3bfc3ba7,true))
Text: [Logistic, regression, models, are, neat] => 
Vector: [0.012510885251685977,-0.014472834207117558,0.002779599279165268,0.0022389178164303304,0.012743516173213721,-0.02409198731184006,0.017409833287820222]
Run Code Online (Sandbox Code Playgroud)

如果我错了,请纠正我,但输入是字符串数组,而输出是单个向量。我期望每个单词都可以映射到一个向量中。

des*_*aut 5

这是在此处证明Spark原理合理性的尝试,应将其作为对已经作为答案提供的漂亮编程说明的补充...

首先,在Word2Vec模型本身的原理特征语究竟是如何单独的嵌入应结合是不是(这大约是,好,个人的话),但关注的“高阶”的车型,如一个问题Sentence2Vec, Paragraph2Vec,Doc2VecWikipedia2Vec等(我想还可以再说几句)。

话虽如此,事实证明,组合词向量以获得较大文本片段(短语,句子,tweet等)的向量表示的第一种方法确实是对构成词的向量表示进行平均,如下所示: Spark ML可以。

从从业者社区开始,我们有:

如何连接词向量以形成句子向量(SO答案):

至少有三种常见的方式来组合嵌入向量。(a)求和,(b)求和与求平均或(c)串联。[...]见gensim.models.doc2vec.Doc2Vecdm_concatdm_mean-它允许你使用任何这些三个选项

Sentence2Vec:流行理论的评估?第一部分(单词向量的简单平均值)(博客文章):

因此,当您拥有单词向量并需要计算句子向量时,首先想到的是什么。

只是平均他们?

是的,这就是我们在这里要做的。 在此处输入图片说明

Sentence2Vec(Github仓库):

Word2Vec可以帮助查找具有相似语义的其他单词。但是,Word2Vec每次只能使用1个单词,而一个句子包含多个单词。为了解决这个问题,我编写了Sentence2Vec,它实际上是Word2Vec的包装。为了获得一个句子的向量,我只需获得句子中每个单词的平均向量和。

显然,至少对于从业者而言,单个单词向量的这种简单平均绝非意外。

预期此相反的论点是,博客文章和SO答案,可以说是不可靠的消息来源; 怎么样的研究人员和相关科学文献?好吧,事实证明,这种简单的平均在这里也很常见:

摘自句子和文档的分布式表示(Le&Mikolov,Google,ICML 2014):

在此处输入图片说明

来自NILC-USP在SemEval-2017上的任务4:用于Twitter情绪分析的多视图集合(SemEval 2017,第2.1.2节):

在此处输入图片说明


到现在为止应该很清楚,Spark ML中的特定设计选择绝不是任意的,甚至是罕见的。我已经写过博客,讲述了Spark ML中似乎荒唐的设计选择(请参见Spark 2.0中的分类:“输入验证失败”和其他奇妙的故事),但看来并非如此。