训练-验证分割
TrainValidationSplit 模型为了选择最佳模型,会对输入数据集(训练数据集)进行随机分割,分成两个子集:较小的训练子集和验证子集。分割只执行一次。
在这个例子中,我们还将使用 ChiSqSelector 仅选择前五个特征,从而限制了我们模型的复杂性:
selector = ft.ChiSqSelector(numTopFeatures=5, featuresCol=featuresCreator.getOutputCol(), outputCol='selectedFeatures',labelCol='INFANT_ALIVE_AT_REPORT'
)
numTopFeatures 指定要返回的特征数量。我们将选择器放在 featuresCreator 之后,所以我们调用 featuresCreator 上的 .getOutputCol()。
我们之前已经介绍了如何创建 LogisticRegression 和 Pipeline,所以这里不再解释这些是如何创建的:
logistic = cl.LogisticRegression(labelCol='INFANT_ALIVE_AT_REPORT',featuresCol='selectedFeatures'
)
pipeline = Pipeline(stages=[encoder, featuresCreator, selector])
data_transformer = pipeline.fit(births_train)
TrainValidationSplit 对象的创建方式与 CrossValidator 模型相同:
tvs = tune.TrainValidationSplit(estimator=logistic, estimatorParamMaps=grid, evaluator=evaluator
)
和以前一样,我们将数据拟合到模型中,并计算结果:
tvsModel = tvs.fit(data_transformer \.transform(births_train)
)
data_train = data_transformer \.transform(births_test)
results = tvsModel.transform(data_train)
print(evaluator.evaluate(results, {evaluator.metricName: 'areaUnderROC'}))
print(evaluator.evaluate(results, {evaluator.metricName: 'areaUnderPR'}))
前面的代码输出了以下结果:
嗯,具有较少特征的模型肯定比完整模型表现得差,但差异并不是很大。最终,这是在更复杂的模型和不太复杂的模型之间的性能权衡。
PySpark ML 其他功能的实际应用
我们描述了 PySpark ML 库的大部分功能。在这一部分,我们将提供如何使用其中一些转换器和估计器的示例。
特征提取
我们已经使用了很多这个 PySpark 子模块的模型。在这一部分,我们将展示如何使用我们认为最有用的一些模型。
自然语言处理相关的特征提取器
正如前面所描述的,NGram 模型接受一个标记文本列表,并产生单词对(或 n-gram)。
在这个例子中,我们将引用 PySpark 文档的一部分,并展示如何在将文本传递给 NGram 模型之前进行清理。以下是我们数据集的样子(为了简洁起见,进行了缩写):
text_data = spark.createDataFrame([['''Machine learning can be applied to a wide variety of data types, such as vectors, text, images, and structured data. This API adopts the DataFrame from Spark SQL in order to support a variety of datatypes.'''],(...)['''Columns in a DataFrame are named. The code examples below use names such as "text," "features," and "label."''']
], ['input'])
我们单列 DataFrame 中的每一行只是一堆文本。首先,我们需要对这些文本进行标记。为此,我们将使用 RegexTokenizer 而不是 Tokenizer,因为我们可以指定我们希望文本在何处分割的模式:
tokenizer = ft.RegexTokenizer(inputCol='input', outputCol='input_arr', pattern='\s+|[,.\"]')
这里的模式根据任意数量的空格分割文本,同时去除逗号、句点、反斜杠和引号。标记器的输出的一行看起来像这样:
如你所见,RegexTokenizer 不仅将句子分割成单词,还将文本规范化,使每个单词都是小写。
然而,我们的文本中仍然有很多垃圾:像 be、a 或 to 这样的词在分析文本时通常不会提供任何有用的信息。因此,我们将使用 StopWordsRemover(...) 移除这些所谓的停用词:
stopwords = ft.StopWordsRemover(inputCol=tokenizer.getOutputCol(), outputCol='input_stop')
该方法的输出如下:
现在我们只有有用的单词。那么,让我们构建我们的 NGram 模型和 Pipeline:
ngram = ft.NGram(n=2, inputCol=stopwords.getOutputCol(), outputCol="nGrams")
pipeline = Pipeline(stages=[tokenizer, stopwords, ngram])
现在我们已经有了管道,我们按照之前非常相似的方式进行:
data_ngram = pipeline \.fit(text_data) \.transform(text_data)
data_ngram.select('nGrams').take(1)
前面的代码产生了以下输出:
就是这样。我们已经得到了我们的 n-gram,现在我们可以在进一步的 NLP 处理中使用它们。