spark2.0特征提取转换选择之二:特征选择文本处理,以中文自然语言处理(情感分类)为例

大葱拌豆腐 大葱拌豆腐     2022-10-13     359

关键词:

特征选择

RFormula

RFormula是一个很方便,也很强大的Feature选择(自由组合的)工具。 
输入string 进行独热编码(见下面例子country) 
输入数值型转换为double(见下面例子hour) 
label为string,也用StringIndexer进行编号

RFormula produces a vector column of features and a double or string 
column of label. Like when formulas are used in R for linear regression, 
string input columns will be one-hot encoded, and numeric columns will be 
cast to doubles. If the label column is of type string, it will be first 
transformed to double with StringIndexer. If the label column does not exist 
in the DataFrame, the output label column will be created from the specified 
response variable in the formula.
如输入数据为
+---+-------+----+------+-------+
| id|country|hour|salary|clicked|
+---+-------+----+------+-------+
|  7|     US|  18|2500.0|    1.0|
|  8|     CA|  12|1500.0|    0.0|
|  9|     NZ|  15|1250.0|    0.0|
| 10|     US|  10|3200.0|    1.0|
+---+-------+----+------+-------+
使用RFormula:
RFormula formula = new RFormula()
                  .setFormula("clicked ~ country + hour + salary")
                  //clicked作为label,~之后的三个为选择的特征
                  .setFeaturesCol("features")
                  .setLabelCol("label");
+---------------------+-----+
|features             |label|
+---------------------+-----+
|[1.0,0.0,18.0,2500.0]|1.0  |
|[0.0,0.0,12.0,1500.0]|0.0  |
|[0.0,1.0,15.0,1250.0]|0.0  |
|[1.0,0.0,10.0,3200.0]|1.0  |
+---------------------+-----+   
[1.0,0.0]为"US"的独热编码,以此类推             

卡方独立检验

ChiSqSelector 
参考: 
http://www.blogjava.net/zhenandaci/archive/2008/08/31/225966.html 
里面举得例子很好理解(原文真的很通俗易懂,直接参考原文吧,瞬间明白的感觉)。

在Spark中似乎非常慢???

ChiSqSelector chiSqSelector=new ChiSqSelector()
                .setFeaturesCol("TF")
                .setOutputCol("features")
                .setLabelCol("label")
                .setNumTopFeatures(2);
Dataset<Row> wordsChiSq=chiSqSelector.fit(wordsTF).transform(wordsTF);

文本转换及特征提取

英文分词

中文分词

     中文分词工具比较多,Java,Python版本都有,这里以IKAnalyzer2012+Java版本为例说明。 
     使用时参考IKAnalyzer2012自带的中文帮助文档(有比较详细的用法)。 
     IKAnalyzer2012它 的 安 装 部 署 十 分 简 单 , 将 IKAnalyzer2012.jar 部 署 于 项 目 的 lib 目 录 中 ;IKAnalyzer.cfg.xml 与 stopword.dic 文件放置在 class 根目录。 
     依赖Lucene的类org.wltea.analyzer.luceneorg.wltea.analyzer,分词主类。

//创建分词对象  
Analyzer anal=new IKAnalyzer(true);       
StringReader reader=new StringReader(row.getString(1));  
//分词  
TokenStream ts=anal.tokenStream("", reader);  
CharTermAttribute term=(CharTermAttribute) ts
                    .getAttribute(CharTermAttribute.class);  
                    //遍历分词数据
                    String words="";
                    while(ts.incrementToken()){  
                        words+=(term.toString()+"|");
                    }  

正则表达式分词

word2vect

TF-IDF

去停用词

应用例子

下面是一个综合的实例子,用到了Spark的一些特征转换API。由于需要处理中文,还需要一个分词器。

准备语料

(I)首先准备一个数据集,谭松波老师收集的中文情感分析酒店评论语料 
从CSDN上可以下载:http://download.csdn.net/download/x_i_y_u_e/9273533 
1、-ChnSentiCorp-Htl-ba-2000: 平衡语料,正负类各1000篇。 
2、ChnSentiCorp-Htl-ba-4000: 平衡语料,正负类各2000篇。 
3、ChnSentiCorp-Htl-ba-6000: 平衡语料,正负类各3000篇。 
4、ChnSentiCorp-Htl-unba-10000: 非平衡语料,正类为7000篇。

(II) 在linux下是乱码的,需要转换: 
编码查看: 
%file -i neg.9.txt 
neg.9.txt: text/plain; charset=iso-8859-1 
需要转换为utf8 
%iconv -f gb18030 -t utf8 neg.9.txt -o neg.9.o.txt 
-f :from -t :to 
批量转如下: 
(1)复制文件目录 find ChnSen* -type d -exec mkdir -p utf/{} ; 
(2)转换 find ChnSen* -type f -exec iconv -f GBK -t UTF-8 {} -o utf/{} ; 
ChnSen*是单前文件夹下的目录,utf是输出目录

(III) python 处理文件,合并为一个文件,去掉一条评论中所有换行

# -*- coding: utf-8 -*-
#将所有评论读入到一个文件中,每个原始文件文件为一条评论,中间不要出现换行
#repalce("
"," "),是将^M(window下产生的 linux不认识的换行)去掉
#输出csv格式

path_out="E:/data/utf/ChnSentiCorp_htl_ba_2000/all.csv"
fw=open(path_out,w+)

#负样本
for i in range(999):    

    path1="E:/data/utf/ChnSentiCorp_htl_ba_2000/neg."+str(i+1)+".txt"
    fr=open(path1)
    lines=fr.readlines()

    fw.write("0.0,")#label    

    for line in lines:
        #repalce("
"," "),是将^M(window下产生的 linux不认识的换行)去掉
        #replace(",",""),是为输出CSV格式做准备
        line=line.replace("
",‘‘).strip().replace(",","")
        fw.write(line)

    fw.write("
")   
    fr.close()

#正样本
for i in range(999):   

    path2="E:/data/utf/ChnSentiCorp_htl_ba_2000/pos."+str(i+1)+".txt"
    fr=open(path2)

    fw.write("1.0,")#label    

    lines=fr.readlines()
    for line in lines:
        line=line.replace("
",‘‘).strip().replace(",","")
        fw.write(line)        
    fw.write("
")

    fr.close()

fw.close

完整流程

可参考论文(只是分词工具不同): 
基于 Spark 的文本情感分析 
http://www.ibm.com/developerworks/cn/cognitive/library/cc-1606-spark-seniment-analysis/index.html 
思路是一样的,不过我是用Java实现的,写起来远远不如Python简洁。

//初步完整的流程,还需要进一步优化
//IKAnalyzer2012分词->TF-IDF特征->NaiveBayes ML

package my.spark.ml.practice.classification;

import org.apache.spark.api.java.function.MapFunction;
import org.apache.spark.ml.classification.NaiveBayes;
import org.apache.spark.ml.classification.NaiveBayesModel;

import org.apache.spark.ml.evaluation.BinaryClassificationEvaluator;

import org.apache.spark.ml.feature.HashingTF;
import org.apache.spark.ml.feature.IDF;
import org.apache.spark.ml.feature.IDFModel;
import org.apache.spark.ml.feature.RegexTokenizer;
import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Encoder;
import org.apache.spark.sql.Encoders;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SparkSession;
import java.io.IOException;
import java.io.StringReader;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;

//引用IKAnalyzer2012的类
import org.wltea.analyzer.lucene.IKAnalyzer;

import my.spark.ml.practice.classification.LabelValue;;

//文本处理,酒店评论
public class myHotelTextClassifer3 {        

    public static void main(String[] args) throws IOException {

        SparkSession spark=SparkSession
                .builder()
                .appName("Chinese Text Processing")
                .master("local[4]")
                .config("spark.sql.warehouse.dir",
                        "file///:G:/Projects/Java/Spark/spark-warehouse" )
                .getOrCreate();  

        //屏蔽日志
        Logger.getLogger("org.apache.spark").setLevel(Level.WARN);
        Logger.getLogger("org.eclipse.jetty.server").setLevel(Level.OFF);
        //--------------------------(0)读入数据,数据预处理--------------------------------
        //原始数据文件包含两行,一行是label,一行是sentence,csv格式的
        Dataset<Row> raw=spark.read().format("csv")
                .load("E:/data/utf/ChnSentiCorp_htl_ba_2000/all.csv");        

        //去掉空值,不然一定后面一定抛出nullpointer异常
        //distinct数据去重 ,并打乱数据的顺序:不然数据是先负样本后正样本有规律排列的,预测效果偏高
        Dataset<Row> rawFilterNaN=raw
                .filter(raw.col("_c0").isNotNull())
                .filter(raw.col("_c1").isNotNull())
                .distinct();              

        //--------------------------(1)分词----------------------------------------
        //为Map自定义了Class LabelValue,见后面       
        Encoder<LabelValue> LongStringEncoder=Encoders.bean(LabelValue.class);
        Dataset<LabelValue> wordsDF=rawFilterNa.map(new MapFunction<Row,LabelValue>() {         
            @Override           
            public LabelValue call(Row row) throws Exception {  
                if (row!=null) {
                    LabelValue ret = new LabelValue();  
                    double Label=1.0;
                    if (row.getString(0).equals("0.0")) {
                        Label=0.0;
                    }else{
                        Label=1.0;
                    }                   
                    ret.setLabel(Label);

                    //-------------KAnalyzer分词--------------------
                    //创建分词对象  
                    Analyzer anal=new IKAnalyzer(true);       
                    StringReader reader=new StringReader(row.getString(1));  
                    //分词  
                    TokenStream ts=anal.tokenStream("", reader);  
                    CharTermAttribute term=(CharTermAttribute) ts
                            .getAttribute(CharTermAttribute.class);  
                    //遍历分词数据
                    String words="";
                    while(ts.incrementToken()){  
                        words+=(term.toString()+"|");
                    }  
                    ret.setValue(words);
                    reader.close();

                    return ret;                 
                }  
                else {
                    return null;
                }
            }

        }, LongStringEncoder);        


        //--------------------------(1)-2 RegexTokenizer分词器-----------------------------
        RegexTokenizer regexTokenizer = new RegexTokenizer()
                      .setInputCol("value")
                      .setOutputCol("words")
                      .setPattern("\|");

        Dataset<Row> wordsDF2 = regexTokenizer.transform(wordsDF); 


        //--------------------------(2) HashingTF训练词频矩阵---------------------------------       

        HashingTF tf=new HashingTF()
                .setInputCol("words")
                .setOutputCol("TF");
        Dataset<Row> wordsTF=tf.transform(wordsDF2).select("TF","label");  
        wordsTF.show();wordsTF.printSchema();
        Dataset<Row> wordsTF2=wordsTF
                .filter(wordsTF.col("TF").isNotNull())
                .filter(wordsTF.col("label").isNotNull());        


        //------------------------- (4)计算 TF-IDF 矩阵--------------------------------------
        IDFModel idf=new IDF()
                .setInputCol("TF")
                .setOutputCol("features")
                .fit(wordsTF2);
        Dataset<Row> wordsTfidf=idf.transform(wordsTF2);         


       //----------------------------(5)NaiveBayesModel ML---------------------
        Dataset<Row>[] split=wordsTfidf.randomSplit(new double[]{0.6,0.4});
        Dataset<Row> training=split[0];
        Dataset<Row> test=split[1];          

        NaiveBayes naiveBayes=new NaiveBayes()
                .setLabelCol("label")
                .setFeaturesCol("features");  
        NaiveBayesModel naiveBayesModel=naiveBayes.fit(training);

        Dataset<Row> predictDF=naiveBayesModel.transform(test);

        //自定义计算accuracy        
        double total=(double) predictDF.count();
        Encoder<Double> doubleEncoder=Encoders.DOUBLE();

        Dataset<Double> accuracyDF=predictDF.map(new MapFunction<Row,Double>() {            
            @Override
            public Double call(Row row) throws Exception {
                if((double)row.get(1)==(double)row.get(5)){return 1.0;}
                else {return 0.0;}
            }
        }, doubleEncoder);       

        accuracyDF.createOrReplaceTempView("view");
        double correct=(double) spark.sql("SELECT value FROM view WHERE value=1.0").count();
        System.out.println("accuracy "+(correct/total));

        //计算areaUnderRoc    
        double areaUnderRoc=new BinaryClassificationEvaluator()
                    .setLabelCol("label")
                    .setRawPredictionCol("prediction")
                    .evaluate(predictDF);
        //(areaUnderROC|areaUnderPR) (default: areaUnderROC)                            
        System.out.println("areaUnderRoc "+areaUnderRoc);  
   }
}



//结果分析
//accuracy 0.7957860615883307
//areaUnderRoc 0.7873761854583772
//应该还有提升的空间

//Class LabelValue
package my.spark.ml.practice.classification;

import java.io.Serializable;

public class LabelValue implements Serializable {
  private String value; 
  private double label;

  public String getValue() {
    return value;
  }

  public void setValue(String value) {
    this.value = value;
  }


  public double getLabel() {
        return label;
      }

  public void setLabel(double label) {
        this.label = label;
      }
}

先使用word2vect,然后将词产生的向量作为特征,分别用随机森林,GBTC, 
LogisticRegression,其中GBTC效果最好。但是普遍不如Naive-Bayes,可能还需要某些地方进行改进。

//转换为词向量,并进行标准化
Word2Vec word2Vec=new Word2Vec()
                .setInputCol("words")
                .setOutputCol("vect")
                .setVectorSize(10);
        Dataset<Row> vect=word2Vec
                .fit(wordsDF2)
                .transform(wordsDF2);
        //vect.show();vect.printSchema();
        //正则化
        Dataset<Row> vect2=new MinMaxScaler()
                .setInputCol("vect")
                .setOutputCol("features")
                .setMax(1.0)
                .setMin(0.0)
                .fit(vect)
                .transform(vect);   
        //vect2.show();vect2.printSchema();

//GBTC分类
GBTClassifier gbtc=new GBTClassifier()
                .setLabelCol("label")
                .setFeaturesCol("vect")
                .setMaxDepth(10)
                .setMaxIter(10)
                .setStepSize(0.1);
        Dataset<Row> predictDF=gbtc.fit(training0).transform(test0);
 //其余代码是一样的,可以尝试不同的参数组合。 

 

特征选择文本挖掘

】特征选择文本挖掘【英文标题】:FeatureSelectionTextMining【发布时间】:2014-01-1113:05:07【问题描述】:我们正在完成一项关于文本分类的任务,并且我们使用了一种无监督机器学习模型。在我们进行文本聚类之前,数据集必须经... 查看详情

什么是降维?特征选择或提取

】什么是降维?特征选择或提取【英文标题】:WhatisDimensionalityReduction?FeatureSelectionorextraction【发布时间】:2022-01-1602:11:36【问题描述】:据我所知,DR是一种将高维数据转换为低维数据的技术。但它是特征选择还是特征提取?这... 查看详情

sklearn学习:特征提取

...和图像等格式的数据集中提取机器学习算法支持的格式的特征。 注意:特征提取与特征选择非常不同:前者包括将任意数据(如文本或图像)转换为可用于机器学习的数值特征。后者是应用于这些功能的机器学习技 查看详情

特征工程之特征提取与特征选择区别

特征提取(FeatureEatraction)是在特征选择之前的,它是从原始数据中提取新特征的过程,这个提取过程通常是使用一定的算法(函数映射)来自动执行,将多维的或相关的原始特征通过数据转化或映射... 查看详情

特征选择--联合方法的特征提取

"""=================================================Concatenatingmultiplefeatureextractionmethods=================================================  Inmanyreal-worldexamples,therearemanywayst 查看详情

特征工程之特征提取与特征选择区别

特征提取(FeatureEatraction)是在特征选择之前的,它是从原始数据中提取新特征的过程,这个提取过程通常是使用一定的算法(函数映射)来自动执行,将多维的或相关的原始特征通过数据转化或映射... 查看详情

特征选择与特征提取

...式识别的大致流程如下:从图中我们可以知道,特征提取与选择是在分类器设计之前完成,它主要的工作是针对数据原始特征的缺陷,降低特征维数,提高分类器的设计与性能。原始特征模式识别中把每个对... 查看详情

SPARK 2.0:火花信息理论特征选择 java.lang.NoSuchMethodError:微风.linalg.DenseMatrix

】SPARK2.0:火花信息理论特征选择java.lang.NoSuchMethodError:微风.linalg.DenseMatrix【英文标题】:SPARK2.0:spark-infotheoretic-feature-selectionjava.lang.NoSuchMethodError:breeze.linalg.DenseMatrix【发布时间】:2018-09-0719:20:59【问题描述】:我正在尝试使用... 查看详情

scikit-learn:4.2.featureextraction(特征提取,不是特征选择)

http://scikit-learn.org/stable/modules/feature_extraction.html带病在网吧里。。。。。。写。求支持。。。1、首先澄清两个概念:特征提取和特征选择( Featureextractionisverydifferentfrom Featureselection)。theformerconsistsintransforming 查看详情

2类分类的建议无监督特征选择/提取方法?

】2类分类的建议无监督特征选择/提取方法?【英文标题】:Suggestedunsupervisedfeatureselection/extractionmethodfor2classclassification?【发布时间】:2016-04-3011:00:34【问题描述】:我有一组F功能,例如实验室色彩空间,熵。通过将所有特征连... 查看详情

文本分类的特征选择

】文本分类的特征选择【英文标题】:FeatureSelectionforTextClassification【发布时间】:2013-10-1318:39:35【问题描述】:我正在研究一个文本分类问题,其中选择100个最常用的词作为特征。我相信如果我使用更好的特征选择方法,结果... 查看详情

简单好用的特征选择器

featselectorfeatselector是一个基于统计分析和模型选择的特征选择器.Github:https://github.com/xiaorancs/feature-select背景特征过多会导致如下后果:引起维数灾难,模型推广能力差特征过于稀疏,模型效果不好很多冗余特征和相关性高... 查看详情

9主成分分析

一、用自己的话描述出其本身的含义:1、特征选择提取到的所有特征中选择和类标签有关的特征作为训练集特征,特征在选择前和选择后不改变值。2、PCAPCA即主成分技术,又称主分量分析。主成分分析也称主分量分析,旨在利... 查看详情

sklearn 特征选择

】sklearn特征选择【英文标题】:Sklearnfeatureselection【发布时间】:2017-04-0614:18:21【问题描述】:我无法使用任何Sklearn特征提取方法而没有收到以下错误:"TypeError:不能使用灵活类型执行reduce"从示例来看,特征提取方法似乎只适... 查看详情

ar学习笔记:配准特征点的选择(代码片段)

AR学习笔记(三):配准特征点的选择特征点选择思路用到的库OPENCVDLIB特征点选择测试根据唇部轮廓线选择特征点DLIB唇部识别测试根据牙齿的角点选择特征点边缘提取测试颜色分割测试轮廓检测提取特征点根据牙齿... 查看详情

automl学习---机器学习01

...所示: 2、框架分析 (1)数值特性:  ①连续特征:log1P、|x|、ex、归一化、离散化、顺序号等。  ②离散特征:频率、目标编码、One-hot编码、合并、Label-Encoder等。(2)特征提取(以文本为例):  特征特征提取... 查看详情

ar学习笔记:配准特征点的选择(代码片段)

AR学习笔记(三):配准特征点的选择特征点选择思路用到的库OPENCVDLIB特征点选择测试根据唇部轮廓线选择特征点DLIB唇部识别测试根据牙齿的角点选择特征点边缘提取测试颜色分割测试轮廓检测提取特征点根据牙齿... 查看详情

零基础学python--机器学习:特征提取(代码片段)

@TOC特征提取学习目标应用DictVectorizer实现对类别特征进行数值化、离散化应用CountVectorizer实现对文本特征进行数值化应用TfidfVectorizer实现对文本特征进行数值化说出两种文本特征提取的方式区别什么是特征提取呢?1.特征提取将... 查看详情