ポイント: 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
Apache Sparkガイド
- Apache Sparkの機械学習パイプライン:入門
- Apache Sparkを用いたSGD線形回帰の例
- Spark決定木分類器
- ロジスティック回帰、Scala、およびSparkの使用
- Apache SparkでJupyterノートブックを使用する方法
- PythonとSpark機械学習を使用した分類
- PythonでSpark UDF(ユーザー定義関数)を書く方法
- Python Spark ML K-Meansの例
- サイバーセキュリティに機械学習を適用する方法
- Apache Spark:ストリームの操作
- Apache Sparkを用いたK-meansクラスタリング
- SparkをHiveと共に使用する
- 予防保全のためにApache Sparkを使用して予測を行う方法
これらの投稿は私自身の見解であり、BMCの立場、戦略、または意見を必ずしも表しているわけではありません。