5

TL;DR; mllibツイートに対する予測のためにウィキ データ (テキストとカテゴリ) をトレーニングするに はどうすればよいですか?

トークン化された wiki データを変換して、 または のいずれかでトレーニングできるようにする方法がわかりませNaiveBayesLogisticRegression。私の目標は、訓練されたモデルをツイートと比較するために使用することです*。LR と for でパイプラインを使用してみましHashingTFIDFNaiveBayes、間違った予測を続けています。これが私が試したことです:

*ラベルにウィキ データの多くのカテゴリを使用したいことに注意してください...二項分類しか見たことがありません (あるカテゴリか別のカテゴリか)...自分のやりたいことを行うことは可能ですか?

パイプライン w LR

import org.apache.spark.rdd.RDD
import org.apache.spark.SparkContext
import org.apache.spark.ml.feature.HashingTF
import org.apache.spark.mllib.linalg.Vector
import org.apache.spark.ml.feature.RegexTokenizer

case class WikiData(category: String, text: String)
case class LabeledData(category: String, text: String, label: Double)

val wikiData = sc.parallelize(List(WikiData("Spark", "this is about spark"), WikiData("Hadoop","then there is hadoop")))

val categoryMap = wikiData.map(x=>x.category).distinct.zipWithIndex.mapValues(x=>x.toDouble/1000).collectAsMap

val labeledData = wikiData.map(x=>LabeledData(x.category, x.text, categoryMap.get(x.category).getOrElse(0.0))).toDF

val tokenizer = new RegexTokenizer()
  .setInputCol("text")
  .setOutputCol("words")
  .setPattern("/W+")
val hashingTF = new HashingTF()
  .setNumFeatures(1000)
  .setInputCol(tokenizer.getOutputCol)
  .setOutputCol("features")
val lr = new LogisticRegression()
  .setMaxIter(10)
  .setRegParam(0.01)
val pipeline = new Pipeline()
  .setStages(Array(tokenizer, hashingTF, lr))

val model = pipeline.fit(labeledData)

model.transform(labeledData).show

単純ベイズ

val hashingTF = new HashingTF()
val tf: RDD[Vector] = hashingTF.transform(documentsAsWordSequenceAlready)

import org.apache.spark.mllib.feature.IDF

tf.cache()
val idf = new IDF().fit(tf)
val tfidf: RDD[Vector] = idf.transform(tf)

tf.cache()
val idf = new IDF(minDocFreq = 2).fit(tf)
val tfidf: RDD[Vector] = idf.transform(tf)

//to create tfidfLabeled (below) I ran a map set the labels...but again it seems to have to be 1.0 or 0.0?

NaiveBayes.train(tfidfLabeled)
  .predict(hashingTF.transform(tweet))
  .collect
4

1 に答える 1

3

MLLogisticRegressionは多項式分類をまだサポートしていませんが、MLLibNaiveBayesLogisticRegressionWithLBFGS. 最初のケースでは、デフォルトで動作するはずです:

import org.apache.spark.mllib.classification.NaiveBayes

val nbModel = new NaiveBayes()
  .setModelType("multinomial") // This is default value
  .run(train)

ただし、ロジスティック回帰の場合は、いくつかのクラスを提供する必要があります。

import org.apache.spark.mllib.classification.LogisticRegressionWithLBFGS

val model = new LogisticRegressionWithLBFGS()
  .setNumClasses(n) // Set number of classes
  .run(trainingData)

前処理手順に関しては、非常に幅広いトピックであり、データにアクセスせずに有意義なアドバイスを提供することは難しいため、以下に示すものはすべて単なる推測です。

  • 私が理解している限り、あなたはトレーニングに wiki データを使用し、テストにツイートを使用しています。それが本当なら、それは一般的に悪い考えです。両方のセットが大幅に異なる語彙、文法、スペルを使用していると予想できます。
  • 単純な正規表現トークナイザーは、標準化されたテキストではかなりうまく機能しますが、私の経験からすると、ツイートのような非公式のテキストではうまく機能しません。
  • HashingTFはベースライン モデルを取得するための良い方法ですが、特にフィルタリング手順を適用しない場合は、非常に単純化されたアプローチになります。それを使用する場合は、少なくとも機能の数を増やすか、デフォルト値 (2^20) を使用する必要があります。

編集(IDF を使用した Naive Bayes のデータの準備)

  • ML パイプラインの使用:
import org.apache.spark.mllib.regression.LabeledPoint
import org.apache.spark.mllib.linalg.Vector
import org.apache.spark.ml.feature.IDF
import org.apache.spark.sql.Row

val tokenizer = ???

val hashingTF = new HashingTF()
  .setNumFeatures(1000)
  .setInputCol(tokenizer.getOutputCol)
  .setOutputCol("rawFeatures")

val idf = new IDF()
  .setInputCol(hashingTF.getOutputCol)
  .setOutputCol("features")

val pipeline = new Pipeline().setStages(Array(tokenizer, hashingTF, idf))
val model = pipeline.fit(labeledData)

model
 .transform(labeledData)
 .select($"label", $"features")
 .map{case Row(label: Double, features: Vector) => LabeledPoint(label, features)}
  • MLlib トランスフォーマーの使用:
import org.apache.spark.mllib.feature.HashingTF
import org.apache.spark.mllib.linalg.Vector
import org.apache.spark.mllib.feature.{IDF, IDFModel}

val labeledData = wikiData.map(x => 
  LabeledData(x.category, x.text, categoryMap.get(x.category).getOrElse(0.0)))

val p = "\\W+".r
val raw = labeledData.map{
    case LabeledData(_, text, label) => (label, p.split(text))}

val hashingTF: org.apache.spark.mllib.feature.HashingTF = new HashingTF(1000)
val tf = raw.map{case (label, text) => (label, hashingTF.transform(text))}

val idf: org.apache.spark.mllib.feature.IDFModel = new IDF().fit(tf.map(_._2))
tf.map{
  case (label, rawFeatures) => LabeledPoint(label, idf.transform(rawFeatures))}

注: トランスフォーマーは JVM アクセスを必要とするため、MLlib バージョンは PySpark では機能しません。Python を好む場合は、データ変換と zip を分割する必要があります。

編集(ML アルゴリズム用のデータの準備):

次のコードは一見有効に見えますが、

val categoryMap = wikiData
  .map(x=>x.category)
  .distinct
  .zipWithIndex
  .mapValues(x=>x.toDouble/1000)
  .collectAsMap

val labeledData = wikiData.map(x=>LabeledData(
    x.category, x.text, categoryMap.get(x.category).getOrElse(0.0))).toDF

MLアルゴリズムの有効なラベルは生成されません。

まず、MLラベルが (0.0, 1.0, ..., n.0) にあると想定します。ここで、n はクラスの数です。クラスの 1 つがラベル 0.001 を取得するサンプル パイプラインの場合、次のようなエラーが発生します。

エラー LogisticRegression: 分類ラベルは {0 から 0 にある必要があります 1 個の無効なラベルが見つかりました。

明らかな解決策は、マッピングを生成するときに除算を避けることです

.mapValues(x=>x.toDouble)

LogisticRegression他のアルゴリズムでは機能しMLますが、それでも失敗します。たとえば、次のRandomForestClassifierようになります

RandomForestClassifier に、指定されたクラス数なしで、無効なラベル列ラベルの入力が与えられました。StringIndexer を参照してください。

興味深いのは、 の ML バージョンはRandomForestClassifier、対応するものとは異なりMLlib、いくつかのクラスを設定するメソッドを提供していません。DataFrame列に特別な属性が設定されることを期待していることがわかりました。最も簡単な方法はStringIndexer、エラー メッセージに記載されているものを使用することです。

import org.apache.spark.ml.feature.StringIndexer

val indexer = new StringIndexer()
  .setInputCol("category")
  .setOutputCol("label")

val pipeline = new Pipeline()
  .setStages(Array(indexer, tokenizer, hashingTF, idf, lr))

val model = pipeline.fit(wikiData.toDF)
于 2015-09-19T22:48:58.687 に答える