运行Spark的Word2Vec示例时,我发现它接收一个字符串数组并输出一个向量。我的问题是,它不应该返回一个矩阵而不是一个向量吗?我原本期望每个输入词对应一个向量。但它返回的却只是一个向量!
或者,它应该接受单个字符串(一个词)作为输入,而不是字符串数组。这样的话,是的,它可以返回一个向量作为输出。但接受字符串数组并返回单个向量对我来说毫无意义。
[更新]
根据@Shaido的请求,这里是我的微小改动以打印输出模式的代码:
public class JavaWord2VecExample { public static void main(String[] args) { SparkSession spark = SparkSession .builder() .appName("JavaWord2VecExample") .getOrCreate(); // $example on$ // 输入数据:每行是一个句子或文档中的词袋。 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); // 学习从词到向量的映射。 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("模式: " + row.schema()); Vector vector = (Vector) row.get(1); System.out.println("文本: " + text + " => \n向量: " + vector + "\n"); } // $example off$ spark.stop(); }}
它打印出以下内容:
模式: StructType(StructField(text,ArrayType(StringType,true),false), StructField(result,org.apache.spark.ml.linalg.VectorUDT@3bfc3ba7,true))文本: [Hi, I, heard, about, Spark] => 向量: [-0.0033279924420639875,-0.0024428479373455048,0.01406305879354477,0.030621735751628878,0.00792500376701355,0.02839711122214794,-0.02286271695047617]模式: StructType(StructField(text,ArrayType(StringType,true),false), StructField(result,org.apache.spark.ml.linalg.VectorUDT@3bfc3ba7,true))文本: [I, wish, Java, could, use, case, classes] => 向量: [-9.96453288410391E-4,-0.013741840076233658,0.013064394239336252,-0.01155538750546319,-0.010510949650779366,0.004538436819400106,-0.0036846946126648356]模式: StructType(StructField(text,ArrayType(StringType,true),false), StructField(result,org.apache.spark.ml.linalg.VectorUDT@3bfc3ba7,true))文本: [Logistic, regression, models, are, neat] => 向量: [0.012510885251685977,-0.014472834207117558,0.002779599279165268,0.0022389178164303304,0.012743516173213721,-0.02409198731184006,0.017409833287820222]
如果我错了,请纠正我,但输入是一个字符串数组,输出是一个单一的向量。我原本期望每个词都被映射到一个向量中。
回答:
这是试图为Spark的这一设计提供合理的解释,并应被视为对已提供的出色编程解释的补充…
首先,如何精确地组合单个词嵌入并不是Word2Vec模型本身的特征(它是关于单个词的),而是“更高阶”模型关心的问题,如Sentence2Vec、Paragraph2Vec、Doc2Vec、Wikipedia2Vec等(我想你可以再列出几个…)。
话虽如此,事实证明,要获得更大文本片段(短语、句子、推文等)的向量表示,组合词向量的最初方法确实是像Spark ML那样简单地平均各个词的向量表示。
从实践者社区来看,我们有以下内容:
如何连接词向量以形成句子向量(Stack Overflow回答):
至少有三种常见的方法来组合嵌入向量;(a)求和,(b)求和并平均或(c)连接。 […] 参见
gensim.models.doc2vec.Doc2Vec
,dm_concat
和dm_mean
– 它允许你使用这三种选项中的任何一种
Sentence2Vec : 评估流行理论 – 第一部分(词向量的简单平均)(博客文章):
那么,当你有词向量并需要计算句子向量时,你脑海中首先想到的是什么?
只是平均它们?
Sentence2Vec(GitHub仓库):
Word2Vec可以帮助找到具有相似语义含义的其他词。然而,Word2Vec每次只能处理一个词,而一个句子由多个词组成。为了解决这个问题,我编写了Sentence2Vec,它实际上是对Word2Vec的封装。为了获得一个句子的向量,我只是简单地获取句子中每个词的向量和的平均值。
显然,至少对于实践者来说,这种单个词向量的简单平均绝非出乎意料。
这里预期的反驳是,博客文章和Stack Overflow的回答可能并不是那么可信的来源;那么研究人员和相关的科学文献呢?事实证明,这种简单的平均在这里也绝非罕见:
来自句子和文档的分布式表示(Le & Mikolov,Google,ICML 2014):
来自NILC-USP在SemEval-2017任务4:Twitter情感分析的多视图集成(SemEval 2017,第2.1.2节):
现在应该很清楚,Spark ML中的这一特定设计选择绝非随意,甚至并不罕见;我曾就Spark ML中看似荒谬的设计选择写过博客(见Spark 2.0中的分类:“输入验证失败”及其他奇妙的故事),但看起来这并不是这种情况…