ロジスティック回帰、Scala、およびSparkの使用

ロジスティック回帰、Scala、およびSparkの使用

  • 人工知能(AI)
6分で読めます 更新日 Jun 9, 2017

ポイント: Apache Sparkを使用したロジスティック回帰は、二値の結果を予測することが示され、特に乳がん患者データに適用されます。このチュートリアルでは、データ準備、モデルのトレーニング、および変数間の関係の評価をカバーします。

  • ロジスティック回帰は二値の結果に対する確率を予測します。
  • 乳がん患者データをモデル化に利用します。

ここでは、Apache Sparkを使用してロジスティック回帰を行う方法を説明します。

ロジスティック回帰(LR)は線形回帰に密接に関連しています。独立した入力値が与えられたときに従属値を予測するのではなく、確率と二値、はいまたはいいえの結果を予測します。

変数間に何らかの関係があると信じるときに、線形またはロジスティック回帰を使用します。たとえば、勉強する時間は成績と明らかに相関しています。したがって、それに対して線形またはロジスティック回帰を使用できます。

ロジスティック回帰は0(偽)または1(真)を出力します。慣例として、イベントの確率が50%を超える場合、LRは1の値を割り当てます。

これをどのように使用できるかを示すために、マサチューセッツ大学での乳がん患者の研究データを単純なLRモデルに入力します。

乳がんデータ

データセットの説明はこちらです。Excel形式のデータはこちらです。そしてテキストファイル形式のデータはこちらです。

説明やExcelファイルを見てください。これらは乳腺疾患と診断された50人の患者と、診断されなかった150人(対照群)です。要因には年齢、流産の数などが含まれます。診断(1=はい、0=いいえ)はD列にあり、列見出しはFNDXです。

良いことは、すべてのデータが数値であり、LRモデルに特化してレイアウトされていることです。したがって、必要なデータ構造を作成してSpark ML LRモデルに供給するだけで済みます。

始めるには、.datファイルをダウンロードして編集し、イントロダクションテキストと列見出しを削除します。コードでスペースを取り除き、欠損値のある行(患者記録)も削除します。不完全なデータをモデルに投入することはできません。

データに関するコメント

ほとんどの人がこのようなプログラミングを教えるとき、入力データを2つの部分に分けてトレーニングデータセットとテストデータセットに分けます。しかし、それは必ずしも論理的ではありません。

インターネットからダウンロードした.datファイルの目的はモデルをトレーニングすることです。言い換えれば、最小二乗法やその他の手法を使用して、入力と最も近く一致する線を見つけます。

統計の用語で言えば、係数と切片を計算してy = mx + bのような式を作成します。ここでmは係数、bは切片、yは予測値です。しかし、ここではxだけでなく、さらに多くの入力変数があります。

そのモデルを構築した後、医者のオフィスに来た患者からの1つの記録をプラグインして使用します。そうすれば、その患者が乳がんの可能性が高いかどうかを予測できます。すでに医者のオフィスにはまさにそのような作業を行うコンピュータがあります。

以下はScalaコードです。コマンドラインインタープリタを使用するにはspark-shellを実行するか、Zeppelinノートブックを使用します。コードには説明のためのコメントを入れています。

Scalaコード

Scalaコード

    import org.apache.spark.sql
import org.apache.spark.sql._
import org.apache.spark.sql.types._
import org.apache.spark.ml.classification.LogisticRegression
import org.apache.spark.mllib.linalg.{Vector, Vectors}
import org.apache.spark.ml.evaluation.BinaryClassificationEvaluator
import org.apache.spark.ml.feature.{VectorAssembler, StringIndexer}
/**
*  Missing values show up as a dot.  The dot function below
*  returns -1 if there is a dot or blank value. And it converts strings to double.
* Then later we will
*  delete all rows that have any -1 values.
*/
def dot (s: String) : Double = {
if (s.contains(".") || s.length == 0) {
return -1
} else {
return s.toDouble
}
}
/**
*  We are going to use a Dataframe.  It requires a schema.
* So we create that below.  We use the same column names
*  as are in the .dat file.
*/
val schema = StructType (
StructField("STR", DoubleType, true) ::
StructField("OBS", DoubleType, true) ::
StructField("AGMT", DoubleType, true) ::
StructField("FNDX", DoubleType, true) ::
StructField("HIGD", DoubleType, true) ::
StructField("DEG",DoubleType, true) ::
StructField("CHK", DoubleType, true) ::
StructField("AGP1", DoubleType, true) ::
StructField("AGMN", DoubleType, true) ::
StructField("NLV", DoubleType, true) ::
StructField("LIV", DoubleType, true) ::
StructField("WT", DoubleType, true) ::
StructField("AGLP", DoubleType, true) ::
StructField("MST", DoubleType, true) ::  Nil)
/**
*  Read in the .dat file and use the regular expression
*  \s+ to split it by spaces into an RDD.
*/
val readingsRDD = spark.sparkContext.textFile("/home/walker/bbdm13.dat")
val RDD = readingsRDD.map(_.split("\\s+"))
/**
*   Run the dot function over every element in the RDD to convert them
*   to doubles, since that if the format requires by the Spark ML LR model.
*   Note that we skip the first one since that is just a blank space.
*/
val rowRDD = RDD.map(s => Row(dot(s(1)),dot(s(2)),dot(s(3)),dot(s(4)),dot(s(5)),dot(s(6)),
dot(s(7)),dot(s(8)),dot(s(9)),dot(s(10)),dot(s(11)),dot(s(12)),
dot(s(13)),dot(s(14))))
/**
* Now create a dataframe with the schema we described above,
*
*/
val readingsDF = spark.createDataFrame(rowRDD, schema)
/**
*  Create a new dataframe dropping all of those with missing values.
*/
var cleanDF = readingsDF.filter(readingsDF("STR") > -1 && readingsDF("OBS") > -1 && readingsDF("AGMT")  > -1  && readingsDF("FNDX") > -1 && readingsDF("HIGD") > -1  && readingsDF("DEG") > -1 && readingsDF("CHK") > -1 && readingsDF("AGP1") > -1  && readingsDF("AGMN") > -1  && readingsDF("NLV") > -1  && readingsDF("LIV") > -1 && readingsDF("WT") > -1 && readingsDF("AGLP") > -1 && readingsDF("MST") > -1)
/**
*  Now comes something more complicated.  Our dataframe has the column headings
*  we created with the schema.  But we need a column called “label” and one called
* “features” to plug into the LR algorithm.  So we use the VectorAssembler() to do that.
* Features is a Vector of doubles.  These are all the values like patient age, etc. that
* we extracted above.  The label indicated whether the patient has cancer.
*/
val featureCols = Array("STR" , "OBS" , "AGMT" , "HIGD" , "DEG" , "CHK" , "AGP1" , "AGMN" , "NLV" , "LIV" , "WT" , "AGLP",  "MST" )
val assembler = new VectorAssembler().setInputCols(featureCols).setOutputCol("features")
val df2 = assembler.transform(cleanDF)
/**
* Then we use the StringIndexer to take the column FNDX and make that the label.
*  FNDX is the 1 or 0 indicator that shows whether the patient has cancer.
* Like the VectorAssembler it will add another column to the dataframe.
*/
val labelIndexer = new StringIndexer().setInputCol("FNDX").setOutputCol("label")
val df3 = labelIndexer.fit(df2).transform(df2)
/**
*   Now we declare the LR model and run fit and transform to make predictions.
*/
val model = new LogisticRegression().fit(df3)
val predictions = model.transform(df3)
/**
*  Now we print it out.  Notice that the LR algorithm added a “prediction” column
*  to our dataframe.   The prediction in almost all cases will be the same as the label.  That is
* to be expected it there is a strong correlation between these values.  In other words
* if the chance of getting cancer was not closely related to these variables then LR
* was the wrong model to use.  The way to check that is to check the accuracy of the model.
*  You could use the BinaryClassificationEvaluator Spark ML function to do that.
* Adding that would be a good exercise for you, the reader.
*/
predictions.select ("features", "label", "prediction").show()
+--------------------+-----+----------+
|            features|label|prediction|
+--------------------+-----+----------+
|[1.0,1.0,39.0,9.0...|  1.0|        1.0|
|[1.0,2.0,39.0,10....|  0.0|        0.0|
|[1.0,3.0,39.0,11....|  0.0|        0.0|
|[1.0,4.0,39.0,12....|  0.0|        0.0|
|[2.0,2.0,38.0,12....|  0.0|        0.0|
|[2.0,3.0,38.0,9.0...|  0.0|        0.0|
|[2.0,4.0,38.0,13....|  0.0|        0.0|
|[3.0,1.0,38.0,9.0...|  1.0|        1.0|
|[3.0,2.0,38.0,10....|  0.0|        0.0|
|[3.0,3.0,38.0,15....|  0.0|        0.0|
|[3.0,4.0,38.0,12....|  0.0|        0.0|
|[4.0,1.0,38.0,15....|  1.0|        1.0|
|[4.0,2.0,38.0,15....|  0.0|        0.0|
|[4.0,3.0,38.0,12....|  0.0|        0.0|
|[4.0,4.0,38.0,12....|  0.0|        0.0|
|[5.0,1.0,38.0,12....|  1.0|        1.0|
|[5.0,2.0,38.0,12....|  0.0|        0.0|
|[5.0,4.0,38.0,13....|  0.0|        0.0|
|[6.0,1.0,38.0,13....|  1.0|        1.0|
|[6.0,2.0,38.0,12....|  0.0|        0.0|
+--------------------+-----+----------+
only showing top 20 rows

これらの投稿は私自身の見解であり、BMCの立場、戦略、または意見を必ずしも表しているわけではありません。

人工知能(AI)の関連記事

The Briefを購読する

このオファーに登録することで、あなたはBMCのプライバシーポリシーを読み、同意したことを確認します。

ありがとう

あなたはThe Briefに登録されました
The Briefはメインフレームとエンタープライズオーケストレーション—BMC AMIとControl-M—および、両者に関するアナリストの研究、顧客の展開、イベントをカバーしています。