前言
虽然提供了很多Estimator/Transformer, 正如这篇文章所显示的,如何基于SDL+TensorFlow/SK-Learn开发NLP程序,处理的代码依然是很多的,能不能进一步简化呢?其实还是有办法的。我们先来看个示例。
示例代码
我Fork了 Spark-Deep-Learning,并且在他的基础上做了一些开发,具体参看release分支。编译和使用参考这篇文章如何基于SDL+TensorFlow/SK-Learn开发NLP程序。
documentDF = self.session.createDataFrame([
("Hi I heard about Spark", "Hi I heard about Spark", 2.0, 3.0, 1, 2),
("I wish Java could use case classes", "I wish Java could use case classes", 3.0, 4.0, 0, 4),
("Logistic regression models are neat", "Logistic regression models are neat", 4.0, 5.0, 2, 5)
], ["title", "body", "f1", "f2", "preds", "i1"])
ef = EasyFeature(numFeatures=10, outputCol="features", wordMode="tfidf",
discretizerFields={"f1": 2})
df = ef.transform(documentDF)
df.select("features").show(truncate=False)
运行结果是:
我们看到,EasyFeature生成了一个20009维的向量,那么他是如何怎么产生的呢?EasyFeature是根据什么原理去生成这个向量的呢?
如果你把wordMode 设置为 embdding,这个时候,会额外有两个字段title_text_ EasyFeature, body_text__EasyFeature,他们是一串数字序列,主要是为了方便给CNN/LSTM等算法使用,其他字段会被拼接成features字段。
设计原理
为了实现自动特征化,核心是四点:类型,规则,统计,先验。
类型
所谓类型指的是Spark DataFrame 的数据是强类型的,常见类型有String,Int, Double, Float, Array, VectorUDF等,他们其实可以给我们提供一定的信息,比如String一般而言有两种可能性:
- 需要分词的字段,一般而言会转化tf/idf 或者word sequence(LSTM/CNN)形式。
- 不需要分词的字段,一般其实就是分类字段。
Int 我们可以求一个distinct值,如果很少,很可能是一个分类字段,比如性别,年龄等。Double,Float等则可能是连续的,比如可能是金额等。
规则
字段的名字也能给我们一定的启发,通常如果类型是String,并且名字还是title,body,sentence,summary之类的,一般是需要分词的字段。Int类型而且还是age,gender之类的名字,则必定是个分类字段。在类型的基础上,让我们更好的确认,该如何特征化某个字段。
统计
当规则无法给我们帮助时,我们仅仅知道某个字段是一个int,我们该怎么办,这个时候统计就起作用了,如果某个字段只有少数几个类型,比如性别,我恩统计只有两种可能性,这么少的可能性,那我们就可以对待为分类属性,可以进行one-hot化了。如果发现有几十万个种类,可能就是售价之类的,那么就自然当做连续值即可,当时我们可以做一些缺失值处理。
先验
当然,我们可以通过人工干预,比如明确告知系统哪些是需要分词的字段,哪些是字段需要离散化,这些作为系统的先验知识。系统自动识别这种规则,然后自动进行处理,你唯一需要做的就是告知哪些字段要做什么处理。
目前的规则集
EasyFeature 是主要是利用周末开始开发的,所以还有待完善,尤其是其中的规则,需要大量有经验的算法工程师参与进来,提供更好的规则,从而更好的自动化抽取特征。目前EasyFeature的处理方式为:
- 把所有字段分成 整数类型,浮点类型,字符类型
- 对浮点类型做缺失值处理
- 对整数做分类和连续值的区分,分类的会被做one-hot化处理
- 对字符类型区分为分词和不分词。不分词会转化为分类字段,分词根据配置,会转化为tf/idf vector或者word sequence & word embedding形态。如果是word sequence/word embedding,则不会拼接到最后的输出字段中。
- 用户可以指定哪些字段进行离散化处理
- 拼接所有字段,形成最后的feature
作用
可以毫不费劲的就把算法跑起来,从而看到基准线在哪,如果觉得不满意,你可以通过内置的先验系统告知系统应该对哪些字段做哪些处理,也就是进行tunning.