【SAS】glmselectプロシジャ_Lasso回帰、ElasticNet回帰

前回 hpgenselect プロシジャで罰則付きロジスティック回帰を実行してみた。
【SAS】hpgenselect プロシジャ_罰則付きロジスティック回帰(LASSO) - こちにぃるの日記

目的変数が連続値の時のLASSO回帰をやってなかったのでまとめてみる。
ついでにElasticNet回帰もやってみる。

【目次】


使用データ


SASHELP にあるベースボールデータを使用する。選手のヒット数、エラー数などの成績から年俸(対数)を予測してみる。

*-- データ;
data ads;
    set sashelp.baseball;
    if cmiss(of n:, of Cr:, logSalary, Division, League) = 0;
    if Division="West"   then Division_West  =1; else Division_West  =0; *One-hot encoding;
    if League="American" then League_American=1; else League_American=0; *One-hot encoding;
run;


データ分割


ベースボールデータの2割をテストデータとする。
ホールドアウト法用に残り8割を 7 : 3 = 訓練データ : バリデーションデータ に細分する。

*-- データ分割;
proc surveyselect data=ads rate=0.2 out=ads outall method=srs seed=19;
run;
data train(drop=selected) test(drop=selected);
    set ads;
    if selected=1 then output test; else output train;
run;
 
proc surveyselect data=train rate=0.3 out=train outall method=srs seed=19;
run;
 
proc format;
    value rollf 0="train-data" 1="validation-data" 2="test-data";
run;
data alldata;
    format selected selected2 rollf.; *-- フォーマット適用;
    set train test(in=in1);
    if in1 then do;
         selected =2; *-- テストデータに番号振る;
         selected2=2;
    end;
    else selected2=0;
run;


LASSO回帰 Hold-out法


ホールドアウト法においては、partition statement で訓練データと検証データを指定できる。
なお、model statement の option に stb を入れると偏回帰係数を出してくれる。

*-- ラッソ回帰;
ods graphics on;
proc glmselect data=train plots=all seed=19;
    partition roleVar=selected(train='0' validate='1');
    model logSalary = nAtBat nHits nHome nRuns nRBI nBB nOuts nAssts nError
                      CrAtBat CrHits CrHome CrRuns CrRbi CrBB
                      Division_West League_American
                      / details=all stats=all
                        selection=lasso(choose=validate stop=none) stb;
    code file="glmselect_lasso.sas";
run;
ods graphics off;
 
data pred_lasso;
    format selected selected2 logSalary P_logSalary;
    set alldata;
    %include glmselect_lasso;
run;

f:id:cochineal19:20210711162118p:plain:w300

LASSO回帰 Cross-validation法


クロスバリデーションでは model statementselection=lasso(choose=cv) cvmethod=random(10) とする。
random(k) で k-fold cross-validation になる。なお、seedも指定可能。

*-- ラッソ回帰 CV;
ods graphics on;
proc glmselect data=train plots=all seed=19;
    model logSalary = nAtBat nHits nHome nRuns nRBI nBB nOuts nAssts nError
                      CrAtBat CrHits CrHome CrRuns CrRbi CrBB
                      Division_West League_American
                      / details=all stats=all
                        selection=lasso(choose=cv stop=none) cvmethod=random(10) stb;
    score out=score_lasso_cv predicted residual;
    code file="glmselect_lasso_cv.sas";
run;
ods graphics off;
 
data pred_lasso_cv;
    format selected2 logSalary P_logSalary;
    set alldata;
    %include glmselect_lasso_cv;
run;

f:id:cochineal19:20210711162331p:plain:w300

ElasticNet回帰 Hold-out法


ElasticNetもLassoと基本は同じ。
L2正則化項の係数を指定でき、グリッドサーチも可能。selection=elasticnet(choose=validate l2search=grid showl2search) とする。
showl2search を入れると最終決定値を表示できる。

もちろん、係数を直接指定することもできる。例:selection=elasticnet(choose=validate l2=0.5)

*-- ElasticNet回帰 ;
ods graphics on;
proc glmselect data=train plots=all seed=19;
    partition roleVar=selected(train='0' validate='1');
    model logSalary = nAtBat nHits nHome nRuns nRBI nBB nOuts nAssts nError
                      CrAtBat CrHits CrHome CrRuns CrRbi CrBB
                      Division_West League_American
                      / details=all stats=all
                        selection=elasticnet(choose=validate l2search=grid showl2search stop=none) stb ;
    score out=score_elasticnet predicted residual;
    code  file="glmselect_elasticnet_l2grid.sas";
run;
ods graphics off;
 
data pred_elasticnet_l2grid;
    format selected selected2 logSalary P_logSalary;
    set alldata;
    %include glmselect_elasticnet_l2grid;
run;

f:id:cochineal19:20210711163023p:plain:w250 f:id:cochineal19:20210711163109p:plain:w300

ElasticNet回帰 Cross-Validation法


クロスバリデーションについても Lassoと同じ。

*-- ElasticNet回帰;
ods graphics on;
proc glmselect data=train plots=all seed=19;
    model logSalary = nAtBat nHits nHome nRuns nRBI nBB nOuts nAssts nError
                      CrAtBat CrHits CrHome CrRuns CrRbi CrBB
                      Division_West League_American
                      / details=all stats=all
                        selection=elasticnet(choose=cv l2search=grid showl2search stop=none) cvmethod=random(10) stb;
    score out=score_elasticnet predicted residual;
    code  file="glmselect_elasticnet_cv_l2grid.sas";
run;
ods graphics off;
 
data pred_elasticnet_cv_l2grid;
    format selected selected2 logSalary P_logSalary;
    set alldata;
    %include glmselect_elasticnet_cv_l2grid;
run;

f:id:cochineal19:20210711163316p:plain:w250 f:id:cochineal19:20210711163352p:plain:w300

今回は L2=0 でLasso回帰と等価になった。

モデル評価


モデル評価指標を複数並べたいので自作する。

*-- 評価指標;
%macro _Scores(inds=, act=logSalary, pred=P_logSalary, group=);
    proc sql;
        create table &inds._Scores as
            select
                 %if &group.^="" %then %do; &group. , %end;
                 count(*) as N
                ,sum(abs(&pred. - &act.)) / count(*) as MAE label="平均絶対誤差(MAE:Mean Absolute Error)"
                ,sum((&pred. - &act.)**2) / count(*) as MSE label="平均二乗誤差(MSE:Mean Squared Error)"
                ,sqrt(calculated MSE)                as RMSE label="MSEの平方根(RMSE:Root Mean Squared Error)"
                ,sum(abs((&pred. - &act.) / &act.)) / count(*) as MAPE label="平均絶対誤差(MAE:Mean Absolute Error)"
                ,sum(((&pred. - &act.) / &act.)**2) / count(*) as MSPE label="平均二乗パーセント誤差(MSPE:Mean Squared Percentage Error)"
                ,sqrt(calculated MSPE) as RMSE as RMSPE label="平均二乗パーセント誤差の平方根(RMSPE:Root Mean Squared Percentage Error)"
            from &inds.
             %if &group.^="" %then %do; group by &group. %end;
        ;
    quit;
    
    title "&inds.";
    proc print data=&inds._Scores; run;
    title "";
%mend _Scores;
%_Scores(inds=pred_lasso,                group=selected);
%_Scores(inds=pred_lasso_cv,             group=selected2);
%_Scores(inds=pred_elasticnet_l2grid,    group=selected);
%_Scores(inds=pred_elasticnet_cv_l2grid, group=selected2);

f:id:cochineal19:20210711163511p:plain:w500

RMSEで見ると今回はクロスバリデーションで作ったモデルが良好な予測性能。

参考


https://support.sas.com/documentation/onlinedoc/stat/131/glmselect.pdf

【SAS】hpgenselect プロシジャ_罰則付きロジスティック回帰(LASSO)

特徴量選択でSTEPWISE法などのほか、LASSO(L1正則化)を視野に入れる人もいる(だろう)。
LASSOについては以前 Python の記事で触れた。
cochineal19.hatenablog.com
今回は「SASで使ったみたい」&「ロジスティック回帰で使ってみたい」という視点でまとめる。(需要は低いかもしれないが、)SAS機械学習に踏み込める!という点は面白い。

【目次】


Pythonでの実装コード


Pythonでは sklearn でめちゃくちゃ簡単に実装できる。
使用するプログラム言語に制約がないならPythonが一番使いやすい。

from sklearn.linear_model import LogisticRegression
model1 = LogisticRegression(C=lambda, penalty="l1", solver='liblinear')
model1.fit(train_X, train_y)
model1.predict(valid_X)


Rでの実装コード


Rでも glmnet で簡単に実行できる。
RではElasticNetの土台があり、パラメタ調整で LassoElasticNetRidge を使い分ける。

library(glmnet)

vals <- c("特徴量1","特徴量2",...,"特徴量p")
train_X <- data.matrix(train[vals])
train_y <- train$目的変数
valid_X <- data.matrix(valid[vals])
valid_y <- valid$目的変数
 
model1 <- glmnet(train_X, train_y, family=binomial(link=logit), alpha=1, lambda=lamda)
coef(model1)
pred1 <- predict.glmnet(model1, valid_X, type="response")
 
    #-- alpha:  1=Lasso, 0=Ridge、0~1の間=ElasticNet
    #-- lambda: PythonのCと同じ。
    #--    lamda=cv.lasso$lambda.min でMSE最小のLassoモデル
    #--    lamda=cv.lasso$lambda.1se でSE最小のLassoモデル


SASでの実装コード


ここから本題のSASでの実装。
難しいのかなと思ったが、hpgenselect プロシジャで比較的簡単に実装できる。

*-- モデル作成;
proc hpgenselect data=train lassorho=0.8 lassosteps=20;
    partition roleVar=selected(train='0' validate='1');
    model 目的変数(event="1")=特徴量1, ..., 特徴量p / distribution=binary link=logit;
    selection method=lasso(choose=validatestop=none) details=all;
    performance details;
    code file="MyCode.sas" residual; *-- 予測用のPGM生成;
    ods output ParameterEstimates=_Coef_lasso; *-- 回帰係数;
    output data=_Pred_lasso role=ROLE pred=PRED resid=RESID;
run;
 
*-- 構築モデルで予測(hpgenselectプロシジャのcodeステートメントで出力したPGMを実行);
data test_pred;
   set test;
   %include MyCode;
run;


hpgenselect プロシジャは、バリデーションデータを用いた評価に対応でき、partition statement で指定する。

また、新しいデータで予測したい場合、code statement で作成モデルのPGMコード(SASファイル)を出力し、それをdataステップで実行( %include )すれば良い。
この構成は Python, R と異なり面白い。

なお、hpgenselect のガイド を見ると Model statement の option に NOCENTER があり、Requests that continuous main effects not be centered and scaled とあるので、おそらく内部処理で標準化してくれている。

もし標準化できていないのなら stdize プロシジャで標準化することもできる。

proc stdize data=train out=train2;
    var 特徴量;
run;


SASでお試し(前処理)


お試しに、Kaggleチュートリアルタイタニックデータで分析してみる。

  • 目的変数: Survived(死亡有無)

  • 特徴量: Pclass(乗客の階級)Sex(性別)Age(年齢)Fare(乗船料金)Embarked(乗船港)SibSp(兄弟・配偶者数)Parch(両親・子供の数)


欠損値処理は 前の記事 と同じ(前回から SibSpParch 追加)。
stdize プロシジャでゼロ、中央値、平均値など色々埋めれるようで便利。

*-- 数値型:欠損値を中央値に置き換え;
proc stdize data=train out=train method=median reponly;
    var age;
run;
 
*-- カテゴリ型:欠損値を最頻値に置き換え;
data train;
    set train;
    Embarked=coalescec(Embarked, "S");
run;


データ分割は surveyselect プロシジャで実装できる。シード指定も簡単。 今回は訓練データ、バリデーションデータ、テストデータに分けてみる。

*-- 訓練データとテストデータに 8:2 で分割;
proc surveyselect data=train rate=0.2 out=train outall method=srs seed=19;
run;
data train(drop=selected) test(drop=selected);
    set train;
    if selected=1 then output test; else output train;
run;
 
*-- 訓練データを訓練:バリデーション=8:2 に再分割;
proc surveyselect data=train rate=0.2 out=train outall method=srs seed=19;
run;
 
*-- まとめたデータ作る; 
proc format;
    value rollf 0="train-data" 1="validation-data" 2="test-data";
run;
data alldata;
   set train test(in=in1);
   format selected rollf.; *-- フォーマット適用;
   if in1 then selected=2; *-- テストデータに番号振る;
run;


SASでお試し(LASSO)


お待ちかねのLASSO。

*-- ラッソを使ったロジスティック回帰;
proc hpgenselect data=train;
    partition roleVar=selected(train='0' validate='1');
    class Pclass(ref="1") Sex(ref="male") Embarked(ref="S") / param=ref;
    model Survived(event="1")=Pclass Sex Age Fare Embarked SibSp Parch / distribution=binary link=logit;
    selection method=lasso(choose=validate stop=none) details=all;
    performance details;
    code file="hpgenselect_lasso.sas" residual; *-- 予測用のPGM生成;
    *ods output ParameterEstimates=_Coef_lasso; *-- 回帰係数;
    *output data=_Pred_lasso role=ROLE pred=PRED resid=RESID;
run;
data Prediction_lasso;
   set alldata;
   %include hpgenselect_lasso; *-- 予測用PGM実行(構築モデルで予測);
run;


特徴量は Sex(性別)Age(年齢)Fare(乗船料金) が残ったみたい。

f:id:cochineal19:20210710160738p:plain:w250

SASでお試し(STEPWISE)


hpgenselect はSTEPWISE法にも対応しているので、比較がてら実行してみる。

proc hpgenselect data=train;
    partition roleVar=selected(train='0' validate='1');
    class Pclass(ref="1") Sex(ref="male") Embarked(ref="S") / param=ref; 
    model Survived(event="1") = Pclass Sex Age Fare Embarked SibSp Parch SibSp*Parch / distribution=binary link=logit;
    selection method=stepwise(slentry=0.05 slstay=0.05);
    performance details;
    code file="hpgenselect_stepwise.sas" residual; *-- 予測用のPGM生成;
    *ods output ParameterEstimates=_Coef_stepwise; *-- 回帰係数;
    *output data=_Pred_stepwise role=ROLE pred=PRED resid=RESID;
run;
data Prediction_stepwise;
   set alldata;
   %include hpgenselect_stepwise; *-- 予測用PGM実行(構築モデルで予測);
run;


STEPWISEでは、Pclass(乗客の階級)Sex(性別)Age(年齢) が残った。

f:id:cochineal19:20210710160928p:plain:w400

SASでお試し(モデル評価)


hpgenselect は評価指標を算出してくれないので自作してみる。
なお、二値分類問題の評価指標は、以前の記事で解説している。
【統計】ロジスティック回帰分析(ホスマー・レメショウ検定、ROC、AUC等) - こちにぃるの日記

%macro _Scores(inds=, tlabel=Survived, pred=U_Survived, group=selected);
    proc sql;
        create table &inds._Scores as
            select
                 %if &group.^="" %then %do; &group. , %end;
                 count(*) as N
                ,sum(case when &tlabel.=1 and &pred.=1 then 1 else 0 end) as TP
                ,sum(case when &tlabel.=1 and &pred.=0 then 1 else 0 end) as FN
                ,sum(case when &tlabel.=0 and &pred.=1 then 1 else 0 end) as FP
                ,sum(case when &tlabel.=0 and &pred.=0 then 1 else 0 end) as TN
                ,(calculated TP + calculated TN) / count(*)      as Accuracy    /*正診率(Accuracy)*/
                ,calculated TP / (calculated TP + calculated FN) as Recall      /*検出率(Recall) 、類語:感度*/
                ,calculated TN / (calculated FP + calculated TN) as Specificity /*特異度(Specificity)*/
                ,calculated TP / (calculated TP + calculated FP) as Precision   /*適合率(Precision)*/
                ,2 * calculated Recall * calculated Precision / (calculated Recall + calculated Precision) as f1_score /*F1スコア*/
            from &inds.
             %if &group.^="" %then %do; group by &group. %end;
        ;
    quit;
    
    title "&inds.";
    proc print data=&inds._Scores; run;
    title "";
%mend _Scores;
%_Scores(inds=_Pred_lasso);
%_Scores(inds=_Pred_stepwise);


今回、F1スコアで評価すると、わずかだが LASSOでの構築モデルが良好だった。
f:id:cochineal19:20210710161015p:plain

なお、正則化に関わるLASSOのパラメタ(lassorho lassosteps)はデフォルトのままでいじってない。ここをいじるとさらに良くなるかもしれない。
グリッドサーチやランダムサーチが簡単にできるとありがたいが、どうなのだろうか。

参考


https://support.sas.com/documentation/onlinedoc/stat/142/hpgenselect.pdf
https://www.mwsug.org/proceedings/2017/AA/MWSUG-2017-AA02.pdf
https://www.lexjansen.com/sesug/2016/SD-253_Final_PDF.pdf
Solved: HPGENSELECT - LASSO- LOGISTIC - SAS Support Communities
sklearn.linear_model.LogisticRegression — scikit-learn 0.24.2 documentation
https://cran.r-project.org/web/packages/glmnet/glmnet.pdf

【SASオンデマンド】CSVファイル等を自分のPCからアップロードする

地味に困ったのでメモ(きっと他にも困る人がいるはず)。

自分のPCからアップロード


SASオンデマンド上で

  • [サーバーファイルとフォルダ] > [odaws##-XXXX##] > [ファイル(ホーム)] を選択。

  • ツールバーの [アップロード] コマンド(上矢印のアイコン)が有効になるのでクリック。
    (もしくは [ファイル(ホーム)] を右クリックして、ポップアップから [ファイルのアップロード] を選択)

  • [ファイルのアップロード] 画面が表示されるのでアップロード。


Work領域にインポート


アップロード後、次のコードでWork領域へ。
エンコーディングはデータに合わせる。例示はCSVファイル)

filename file1 '/home/ユーザー名/ファイル名.csv' encoding='utf-8';
proc import datafile=file1
            out=work.ads
            dbms=csv;
            getnames=yes;
run;


参考


Importing Data into SAS OnDemand for Academics - SAS Tutorials - LibGuides at Kent State University

【Kaggle】Titanicデータを XGBoost, LightGBM, CatBoost で分析してみる

GBDT(Gradient Boosting Decision Tree)を使ってみたかったのでメモ。
KaggleのTitanicデータを予測してみる。

scikit-learnに準拠した model.fit(データ) で記載。
あまりパラメタいじらず&Cross Validateせずでも Catboost は Score:0.78 が出た。
n_estimaternum_boost_round )はでっかい値にして、early_stopping_rounds で切った。

前処理


前回記事と同じ。Embarkedはダミー変数に変更。

import numpy as np 
import pandas as pd
from scipy import stats
from sklearn import model_selection
 
#-- import
train = pd.read_csv("../input/titanic/train.csv")
test = pd.read_csv("../input/titanic/test.csv")
###--- 欠損処理
train["Age"] = train["Age"].fillna(train["Age"].median()) #--中央値で補完
train["Embarked"] = train["Embarked"].fillna(train["Embarked"].mode().iloc(0)) #-- 最頻値で補完 
test["Age"] = test["Age"].fillna(test["Age"].median()) #--中央値で補完
test["Fare"] = test["Fare"].fillna(test["Fare"].median()) #--中央値で補完
 
###--- カテゴリの数値化
train["Sex"] = train["Sex"].map({'male':0, 'female':1})
train = pd.get_dummies(train, columns=['Embarked'])
test["Sex"] = test["Sex"].map({'male':0, 'female':1})
test = pd.get_dummies(test, columns=['Embarked'])
###--- データ分割
Xval = ["Pclass", "Sex", "Age", "Fare", "Embarked_C", "Embarked_Q", "Embarked_S"]
df_y = train["Survived"].values
df_X = train[Xval].values
train_X, valid_X, train_y, valid_y = model_selection.train_test_split(df_X, df_y, random_state=19)
test_X = test[Xval].values


XGBoost (eXtreme Gradient Boosting)


XGBoost Python Package — xgboost 1.5.0-dev documentation

import xgboost as xgb
from sklearn.metrics import accuracy_score
 
#-- モデル
xgb = xgb.XGBClassifier(max_depth = 10
                        ,learning_rate = 0.01
                        ,n_estimators = 100000
                        ,objective = "binary:logistic"
                        ,eval_metric = "logloss"
                        ,random_state = 19
                       )
 
#-- 学習
xgb.fit(train_X, train_y
        ,eval_set = [(valid_X, valid_y)]
        ,early_stopping_rounds = 100, verbose = 10
       )
 
#-- バリデーションデータで予測&正解率算出
pred = xgb.predict(valid_X)
print('score', accuracy_score(valid_y, pred))
 
#-- テストデータで予測
pred_y = xgb.predict(test_X)


Score
- Valid: 0.8565022421524664
- Test: 0.75837

LightGBM


lightgbm.LGBMClassifier — LightGBM 3.2.1.99 documentation

import lightgbm as lgb
from sklearn.metrics import accuracy_score
 
#-- モデル
lgb = lgb.LGBMClassifier(n_estimators = 100000, objective = 'binary')
 
#-- 学習
gbm.fit(train_X, train_y
        ,eval_set = [(valid_X, valid_y)]
        ,early_stopping_rounds = 100, verbose = 10
       )
 
#-- バリデーションデータで予測&正解率算出
pred = gbm.predict(valid_X, num_iteration = gbm.best_iteration_)
print('score', accuracy_score(valid_y, pred))
 
#-- テストデータで予測
pred_y = gbm.predict(test_X, num_iteration = gbm.best_iteration_)


Score
- Valid: 0.8565022421524664
- Test: 0.76315


CatBoost


Usage examples - CatBoost. Documentation

from catboost import CatBoostClassifier
from sklearn.metrics import accuracy_score
 
#-- モデル
cbc = CatBoostClassifier(num_boost_round = 10000
                         ,depth = 10
                         ,learning_rate = 0.01 
                         ,eval_metric = 'Accuracy'
                         ,loss_function = 'Logloss'
                        )
 
#-- 学習
cbc.fit(train_X, train_y
        ,eval_set = [(valid_X, valid_y)]
        ,early_stopping_rounds = 100, verbose = 10
       )
 
#-- バリデーションデータで予測&正解率算出
pred = cbc.predict(valid_X)
print('score', accuracy_score(valid_y, pred))
 
#-- テストデータで予測
pred_y = cbc.predict(test_X)


Score
- Valid: 0.8565022421524664
- Test: 0.78229


参考


【Python覚書】XGBoostで多値分類問題を解いてみる | ポテサラ
【Python覚書】アンサンブル学習:XGBoost、LightGBM、CatBoostを組み合わせる(その1) | ポテサラ
XGBoost論文を丁寧に解説する(1) - Qiita
XGBoost論文を丁寧に解説する(2): ShrinkageとSubsampling - Qiita
GBDT系の機械学習モデルであるXGBoost, LightGBM, CatBoostを動かしてみる。 - Qiita
GBDT系の機械学習モデルのパラメータチューニング奮闘記 ~ CatBoost vs LightGBM vs XGBoost vs Random Forests ~ その1 - Qiita
XGBoostのScikit-Learn APIでearly stoppingを利用する - おおかみ山
XGBoostパラメータのまとめとランダムサーチ実装 - Qiita
なぜn_estimatorsやepochsをパラメータサーチしてはいけないのか - 天色グラフィティ
Python: CatBoost を使ってみる - CUBE SUGAR CONTAINER
PythonでCatBoostの解説 – S-Analysis
Pythonでデータ分析:XGboost - データサイエンティスト(仮)
Pythonでデータ分析:Catboost - データサイエンティスト(仮)
Pythonでcatboostを使ってみる - Qiita

【Kaggle】タイタニックの分析をやってみる

Kaggleに登録してみたので備忘録を。
初心者ということもあり、まずは王道のタイタニックデータを使った分析をしてみた。

データの前処理


データはKaggle内にあり、TrainとTestにあらかじめ分けられている。
生存有無を予測する2値分類問題であり有名なデータ。

import numpy as np
import pandas as pd
from scipy import stats
 
#-- import
train = pd.read_csv("../input/titanic/train.csv")
test = pd.read_csv("../input/titanic/test.csv")


このデータは欠損があるようなので、今回は諸先輩方のブログを参考に中央値と最頻値で補完する。 また、カテゴリ変数は数値化しておく。

###--- 欠損処理
train["Age"] = train["Age"].fillna(train["Age"].median()) #--中央値で補完
train["Embarked"] = train["Embarked"].fillna("S") #-- 最頻値で補完 train["Embarked"].mode()

test["Age"] = test["Age"].fillna(test["Age"].median()) #--中央値で補完
test["Fare"] = test["Fare"].fillna(test["Fare"].median()) #--中央値で補完
 
###--- カテゴリの数値化
train["Sex"] = train["Sex"].map({'male':0, 'female':1})
train["Embarked"] = train["Embarked"].map({'S':0, 'C':1, 'Q':2})

test["Sex"] = test["Sex"].map({'male':0, 'female':1})
test["Embarked"] = test["Embarked"].map({'S':0, 'C':1, 'Q':2})


※あとから気づいたが、Embarked はダミー変数化した方が良かった。
pd.get_dummies(train, columns=['Embarked'])

バリデーションデータの準備


Kaggleでは、テストデータ(正解ラベルなし)があらかじめ準備されているため、次の手順で分析してみる。
* 訓練データを訓練&検証データに分割
* F1-スコアでモデル評価
* ベストモデルを用いてテストデータで予測

from sklearn import model_selection

Xval = ["Pclass", "Sex", "Age", "Fare", "Embarked"]

df_y = train["Survived"].values
df_X = train[Xval].values

train_X, valid_X, train_y, valid_y = model_selection.train_test_split(df_X, df_y, random_state=19)

test_X = test[Xval].values


モデル作成


初めてなので色々なモデルを試してみた(グリッドサーチ、ランダムサーチなどはしていない)。
順番に決定木、ランダムフォレスト、ロジスティック回帰(L2正則化、L1正則化、両方0.5ずつ)、線形サポートベクターマシーン(L2正則化、L1正則化)、サポートベクターマシーン(カーネル:Linear、rbf、poly、sigmoid)。

from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC, SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix, f1_score
 
modfnc = [DecisionTreeClassifier(random_state=19)
          ,RandomForestClassifier(random_state=19)
          ,LogisticRegression(random_state=19, penalty="l2")
          ,LogisticRegression(random_state=19, penalty="l1", solver='liblinear')
          ,LogisticRegression(random_state=19, penalty="elasticnet", solver='saga', l1_ratio=0.5)
          ,LinearSVC(random_state=19)
          ,LinearSVC(random_state=19, penalty='l1', loss='squared_hinge', dual=False)
          ,SVC(kernel='linear', random_state=19)
          ,SVC(kernel='rbf', random_state=19)
          ,SVC(kernel='poly', random_state=19)
          ,SVC(kernel='sigmoid', random_state=19)
         ]
 
score=0
 
for modfnc1 in modfnc:
    model = modfnc1
    model.fit(train_X, train_y)
    pred_y = model.predict(valid_X)
    print("----",modfnc1,"-----")
    #print(model.score(valid_X, valid_y))
    print(f1_score(valid_y, pred_y, average="micro"))
    #print(confusion_matrix(valid_y, pred_y))
    #print(classification_report(valid_y, pred_y))
    #print()
    
    if score < f1_score(valid_y, pred_y, average="micro"):
        best_model = modfnc1
        score = f1_score(valid_y, pred_y, average="micro")
 
print("---- best model ----")
print(best_model)
print(score)


このうち、F1-スコアでの評価ではランダムフォレストが最も良好だった。

---- DecisionTreeClassifier(random_state=19) -----
0.7713004484304933
---- RandomForestClassifier(random_state=19) -----
0.8430493273542601
---- LogisticRegression(random_state=19) -----
0.8071748878923767
---- LogisticRegression(penalty='l1', random_state=19, solver='liblinear') -----
0.8251121076233184
---- LogisticRegression(l1_ratio=0.5, penalty='elasticnet', random_state=19,
                   solver='saga') -----
0.6905829596412556
---- LinearSVC(random_state=19) -----
0.7309417040358744
---- LinearSVC(dual=False, penalty='l1', random_state=19) -----
0.8251121076233184
---- SVC(kernel='linear', random_state=19) -----
0.8116591928251121
---- SVC(random_state=19) -----
0.6636771300448431
---- SVC(kernel='poly', random_state=19) -----
0.6278026905829597
---- SVC(kernel='sigmoid', random_state=19) -----
0.5919282511210763
 
---- best model ----
RandomForestClassifier(random_state=19)
0.8430493273542601


テストデータで予測


ベストモデル(ランダムフォレスト)を使ってテストデータで予測を行う。
Kaggleのテストデータには正解ラベルが入っておらず、予測結果をKaggleの指定ページに提出してスコアを出す仕組みである(カンニングできない)。

best_model.fit(train_X, train_y)
pred_y2 = best_model.predict(test_X)
 
submission = pd.DataFrame({'PassengerId':test['PassengerId'], 'Survived':pred_y2})
submission.to_csv('submission.csv', index = False)


実際に結果を提出してみたところ、Score : 0.75119(44,476位)だった。

ハイパーパラメータを何もいじらずだったので、次はグリッドサーチやランダムサーチを使ってベストモデルを探してみたい。
もしくはポピュラーな LightGBMXGboost の実装をしてみたい(こっちの興味の方が強い)。

参考


【Kaggle超初心者向け】Titanicにチャレンジしてみた - Qiita
Kaggleコンペの提出データをKernelから提出する - Qiita
機械学習初心者がKaggleのTitanic課題でモデルを作る | 4番は司令塔

当ブログの関連記事:
【統計】ロジスティック回帰分析 - こちにぃるの日記
【機械学習_Python】Ridge回帰、Lasso回帰、Elastic Net回帰 - こちにぃるの日記
【機械学習_Python】決定木とランダムフォレスト - こちにぃるの日記
【機械学習_Python】サポートベクターマシーン - こちにぃるの日記

【SAS】call execute

executeの使い方の備忘録。

やりたいこと:
動的に生成したコードを実行したい。

call execute(コード) でいける。

data _null_;
  call execute("data a; a=1; run;");
run;


マクロ変数を投入することもできる。

%let CODE1 = '
    proc sql;
        create table a (
        col1 char 20 label="Columns 1"
        ,col2 num 8 label="Columns 2" format=8.1
        );
    quit;
';
data _null_;
  call execute(&CODE1.);
run;


execute内でコードを組み立てることもできる。

%let CODE2 = '
             col1 char 20 label="Columns 1"
            ,col2 num 8 label="Columns 2" format=8.1
    ';
data _null_;
  call execute(cats('proc sql; create table a (', &CODE., '); quit;'));
run;

【SAS】retainステートメント

文字列に関する記事はあまりなさそうだったので備忘録がてら。
個人メモで解説しないです。

やりたいこと:
* 文字型 ⇒ 複数行を1行にまとめる。
* 数値型 ⇒ 累積値を計算する。

サンプルデータ

data a;
  input a $ b;
  cards;
AA 1
BB 2
CC 3
;
run;


コード

data b;
  set a end=end;
  length a2 $200;
  retain a2 "" b2 0;      /* 初期値 */
  a2 = catx(" ", a2, a);  /* 文字列:空白スペースで連結 */
  b2 = b2 + b;            /* 数値型:累積 */
  if end then do;         /* 最終行をマクロ変数にする */
    call symputx("txt1", a2);
    call symputx("txt2", b2);
  end;
run;


output

%put "&txt1.";
 "AA BB CC"
 
%put "&txt2.";
 "6"
本ブログは個人メモです。 本ブログの内容によって生じた損害等の一切の責任を負いかねますのでご了承ください。