この項ではKaggle House Prices住宅価格予測をSparkMLというライブラリーを使って実施していきます。
タスクと表記のあるセクションは自分で調べながら実装して下さい。
House Pricesデータセットの用意
data_description.txt
train.csv
data_description.txt, train.csvをダウンロードして、zeppelin/data/ ディレクトリ下の任意の場所に配置します。
これらのデータはKaggleのHouse Prices住宅価格予測というコンペティションで使用されたデータです。
Kaggleに会員登録、ログインをすることで、そちらからもダウンロードすることが出来ます。
1. データ準備
1.1. ライブラリのimport
今回の処理に必要なライブラリをimportします。
1 2 3 4 5 6 7 8 9 10 11 |
import org.apache.spark.sql.SparkSession import org.apache.spark.sql.DataFrame import org.apache.spark.sql.types._ import org.apache.spark.sql.functions._ import org.apache.spark.ml.feature.{StringIndexer, OneHotEncoder, OneHotEncoderModel} import org.apache.spark.ml.Pipeline import org.apache.spark.ml.PipelineModel import org.apache.spark.ml.feature.VectorAssembler import org.apache.spark.ml.feature.StandardScaler import org.apache.spark.ml.regression.LinearRegression import org.apache.spark.ml.evaluation.RegressionEvaluator |
1.2. データの読み込み
まずは、データをSpark data frameとして読み込みます。
1 2 |
val sparkSession = SparkSession.builder().getOrCreate() val df = sparkSession.read.option("header", "true").option("inferSchema", "true").csv("/data/housePrices/train.csv") |
下記のコマンド等を使ってデータの中身を把握しましょう。
1 2 3 |
df.printSchema() df.show() df.groupBy("column_name").count().show() |
また、data_description.txtを開き各カラムの意味を大まかに確認しましょう。
確認出来たら、下記のコードでトレーニングデータとテストデータに分割しましょう
1 |
val Array(df_train, df_test) = df.randomSplit(Array(0.70, 0.30), 12345L) |
2.前処理
このコンテンツではLinearRegression
(線形回帰)をつかって住宅価格を予測していきたいと思います。
下記のヒントを元にデータの前処理を調べながら実装してみましょう!
タスク1:下準備
線形回帰の予測を行うためには全てのカラムを数値データ(Numerical Columns)にする必要があります。
その為、数値データ、文字列データそれぞれで必要な前処理が異なります。
まずは、数値データのみを含むデータフレーム、文字列データフレームに分けてみましょう。
タスク2:数値データの特徴量エンジニアリング
数値データの特徴量エンジニアリングを行っていきます。
下記3点調べながら実装してみて下さい。
- 特徴量の作成
例)YearBuiltカラム(当初の建築年)とYrSoldカラム(販売年)から、築年数を表すカラムを追加するetc
- 欠損値処理:null, NAなどの欠損値を平均値や特定の値に置き換える
- 特徴量改善:数値の大小が価格に直接関係するかわからないカラムを除く
タスク3:文字列データの特徴量エンジニアリング
続いて、以下の文字列のカラムを数値変換(エンコード)してみましょう
- RoofStyl
- ExterQual
- ExterCond
- BsmtQual
- BsmtCond
- HeatingQC
ヒント:One-Hot Encoding, Label Encoding
チャレンジ1
前処理に使用したコードを関数化して可読性とメンテナンス性を高めてみましょう
3. 線形回帰モデルでの価格予測
価格の予測に使用するカラムを一つのベクトル列featuresカラムに統合します。訓練データと検証データのそれぞれに対して行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import org.apache.spark.ml.feature.VectorAssembler val featureCols = df_train_processed .drop("Id", "SalePrice") .columns val assembler = new VectorAssembler() .setInputCols(featureCols) .setOutputCol("features") val assembledData_train = assembler .transform(df_train_processed) .select("Id", "SalePrice", "features") |
同様の処理を検証データに対しても実行します。
1 2 3 |
val assembledData_test = assembler .transform(df_test_processed) .select("Id", "SalePrice", "features") |
線形回帰モデルのインスタンスを作成し、訓練データでモデルを作成します。
1 2 3 4 5 6 7 |
import org.apache.spark.ml.regression.LinearRegression val lr = new LinearRegression() .setLabelCol("SalePrice") .setFeaturesCol("features") val model_lr = lr.fit(assembledData_train) |
作成した予測モデルで検証データの価格を予測します。
1 |
val test_predictions_lr = model_lr.transform(assembledData_test) |
test_predictions_lr に予測結果のpredictionカラムが追加されます。
4. モデルの評価
タスク4:RMSEによるモデルの評価
予測モデルの性能を評価するための指標の1つに RMSE があります。これを使ってモデルの性能評価をしてください。
タスク5:線形回帰モデルの係数の確認
線形回帰モデルでは、特徴量(features)と目的変数(SalePrice)の間の線形な関係を仮定しています。
この係数は、各特徴量が目的変数に対してどのように寄与するかを示す数値です。
各カラムごとの係数をプリントして確認してください。
タスク6 標準化をした場合の予測結果・モデルの評価・係数の値
各カラムが異なるスケールをもっているため、係数の値同士を直接比較することは出来ません。 標準化(または正規化)を行うことで直接比較することが可能になります。
ヒント:VectorAssembler()で特徴量を統合後、StandardScaler()で標準化し、再度モデルの係数を確認してみましょう