[51CTO.com クイック翻訳] 機械学習はデータサイエンスの頂点であり、教師あり学習は機械学習の頂点とも言えるものです。 背景数年前、ハーバード・ビジネス・レビュー誌は「データサイエンティスト:21世紀で最もセクシーな職業」という記事を掲載しました。この記事が発表された後、データサイエンスや統計学科は大学生の間で非常に人気となり、退屈なデータサイエンティストが初めて魅力的だと考えられるようになりました。 一部の業界では、データ サイエンティストが企業構造を変え、多くの意思決定を最前線の従業員に移行させています。データから実用的なビジネス洞察を得ることが、これまでになく容易になりました。 Andrew Ng 氏によると、教師あり学習アルゴリズムが業界に最も大きな価値をもたらしています。 教師あり学習がこれほど大きなビジネス価値を生み出す理由は疑う余地がありません。銀行はクレジットカード詐欺の検出にこれを使用、トレーダーはモデルに基づいて購入の決定を下すためにこれを使用、工場は生産ラインで不良部品をフィルタリングするためにこれを使用。 これらのビジネス シナリオには、次の 2 つの共通の特徴があります。 - バイナリ結果: 詐欺 vs. 詐欺ではない、購入する vs. 購入しない、不良品 vs. 不良品ではない。
- 不均等なデータ分布: 多数派グループと少数派グループ。
Andrew Ng 氏が最近指摘したように、少量データ、堅牢性、人的要因が AI プロジェクトの成功に対する 3 つの最大の障害です。ある意味では、少数派グループに関する稀なイベントの問題も、小さなデータの問題です。機械学習アルゴリズムは多数派グループからより多くの情報を学習し、少数派グループを簡単に誤分類する可能性があります。 ここで重要な疑問をいくつか挙げます。 - これらのまれなイベントに対して、どの機械学習手法がより優れたパフォーマンスを発揮するでしょうか?
- どのような指標ですか?
- 欠点は何ですか?
この記事では、完全な R 実装コードを添付して、5 つの機械学習手法を実際のデータ セットに適用し、上記の質問に答えようとします。 詳しい説明と元のデータセットについては、元のデータセットをご覧ください: https://archive.ics.uci.edu/ml/datasets/bank+marketing; 完全な R コードについては、私の Github をご覧ください: https://github.com/LeihuaYe/Machine-Learning-Classification-for-Imbalanced-Data。 ビジネス上の問題ポルトガルの銀行は、新しい銀行サービス (定期預金) のマーケティング戦略を実施しており、どのタイプの顧客がそのサービスに加入しているかを把握して、将来的に特定のグループをターゲットにマーケティング戦略を調整したいと考えています。データ サイエンティストは、営業チームやマーケティング チームと協力して、将来の加入者を特定するための統計ソリューションを考案しました。 R実装 以下はモデル選択プロセスと R 実装です。 1. インポート、データクリーニング、探索的データ分析 元のデータセットを読み込んでクリーンアップしましょう。 - #### データセットをロードする
- banking = read .csv("bank-additional- full .csv", sep = ";", header = T) ## 欠落データをチェックし、欠落データがないことを確認する
- banking[!complete.cases(banking),]#質的(因子)変数を数値に再コード化する
- banking$job= recode(banking$job, “’admin.’=1;’blue-collar’=2;’entrepreneur’=3;’housemaid’=4;’management’=5;’retired’=6;’self-employed’=7;’services’=8;’student’=9;’technician’=10;’unemployed’=11;’unknown’=12”)#変数を再度再コード化
- 銀行$婚姻歴 = recode(銀行$婚姻歴, “'離婚歴'=1;'既婚'=2;'独身'=3;'不明'=4”)銀行$教育歴 = recode(銀行$教育歴, “'basic.4y'=1;'basic.6y'=2;'basic.9y'=3;'高校'=4;'文盲'=5;'専門課程'=6;'大学の学位'=7;'不明'=8”)銀行$デフォルト= recode(銀行$デフォルト, “'いいえ'=1;'はい'=2;'不明'=3”)銀行$住宅歴 = recode(銀行$住宅歴, “'いいえ'=1;'はい'=2;'不明'=3”)銀行$ローン = recode(銀行$ローン, “'いいえ'=1;'はい'=2;'不明'= 3 ”) '=1;'はい'=2;'不明'=3")
- banking$contact = recode(banking$loan, “'cellular'=1;'telephone'=2;”)banking$ month = recode(banking$ month , “'mar'=1;'apr'=2;'may'=3;'jun'=4;'jul'=5;'aug'=6;'sep'=7;'oct'=8;'nov'=9;' dec '=10”)banking$day_of_week = recode(banking$day_of_week, “'mon'=1;'tue'=2;'wed'=3;'thu'=4;'fri'=5;”)banking$poutcome = recode(banking$poutcome, “'failure'=1;'nonexistent'=2;'success'=3;”)#変数 “pdays” はバリエーションがないので削除します
- banking$pdays = NULL #変数「pdays」はDVと同一線上にあるため削除
- 銀行$期間 = NULL
生データのクリーニングは、欠落している変数を再コード化し、質的変数を量的変数に変換する必要があるため、面倒に思えるかもしれません。実際のデータのクリーニングにはさらに長い時間がかかりました。 「データ サイエンティストは、時間の 80% をデータのクリーニングに費やし、時間の 20% をモデルの構築に費やしている」と言われています。 次のステップとして、結果変数の分布を調べることが役立つかもしれません。 - DVの#EDA
- plot(banking$y,main= "プロット1: 従属変数の分布" )
図1 関連する変数(サービス注文)は均等に分布しておらず、「はい」よりも「いいえ」の方が多いことがわかります。データの分布は最終的な統計モデルに影響を与えるため、不均衡な分布には何らかの警告サインが出るはずです。多数派のケースに基づいて開発されたモデルを使用すると、少数派のケースを誤分類してしまう可能性が高くなります。 2. データのセグメンテーション 次のステップとして、データセットをトレーニング セットとテスト セットの 2 つの部分に分割しましょう。通常、80 対 20 の分割に従います。つまり、80% がトレーニング セットで、20% がテスト セットです。時系列データの場合は、データの 90% に基づいてモデルをトレーニングし、残りの 10% のデータをテスト データセットとして使用します。 - #データセットをランダムにトレーニングセットとテストセットに分割する
- set .seed(1) # コードを実行するたびに同じ値を生成するようにシードを設定する# データを分割するためのインデックスを作成する: 80% トレーニングと20% テスト
- index = round(nrow(banking)*0.2,digits=0)#データセット全体をランダムにサンプリングし、合計数をindexの値と同じに保ちます
- test.indices = sample(1:nrow(banking), index ) #80% トレーニングセット
- banking.train=banking[-test.indices,] #20% テストセット
- banking.test=banking[test.indices,] # DV以外のトレーニングセットを選択
- YTrain = banking.train$y
- XTrain = banking.train %>% select (-y) # DV以外のテストセットを選択
- YTest = banking.test$y
- XTest = banking.test %>%選択(-y)
ここで、空のトレースレコードを作成しましょう。 - レコード = マトリックス(NA、nrow=5、ncol=2)
- colnames(レコード) <- c("train.error","test.error")
- rownames(レコード) <- c("ロジスティック","ツリー","KNN","ランダムフォレスト","SVM")
3. モデルのトレーニング このセクションでは、各機械学習モデルのトレーニングおよびテスト エラーを計算するために使用する新しい関数 (calc_error_rate) を定義します。 - calc_error_rate <-関数(予測値、真の値)
- { return (mean( true .value != expected .value))}
予測されたラベルが実際の値と一致しない場合、関数は比率を計算します。 #1 ロジスティック回帰モデル ロジスティック モデルの概要については、「機械学習 101」と「機械学習 102」の 2 つの記事をお読みください。 結果変数を除く他のすべての変数を含むロジスティック モデルを追加しましょう。結果はバイナリなので、モデルを二項分布(「ファミリー二項」)に設定します。 - glm.fit = glm(y ~ 年齢 + 因子 (職業) + 因子 (結婚) + 因子 (教育) + 因子 (デフォルト) + 因子 (住宅) + 因子 (ローン) + 因子 (連絡先) + 因子 (月) + 因子 (曜日) + キャンペーン + 前回 + 因子 (結果) + emp.var.rate + cons.price.idx + cons.conf.idx + euribor3m + nr.employed、データ = banking.train、家族 = 二項式)
次のステップはトレーニング エラーを取得することです。結果のタイプを予測し、多数決ルールを使用しているため、タイプを Responsive に設定します。事前確率が 0.5 以上の場合は結果を yes と予測し、それ以外の場合は no と予測します。 - prob.training = predict(glm.fit,type="response") banking.train_glm = banking.train %>% # 列車のすべての行を選択
- mutate(predicted.value= as .factor(ifelse(prob.training<=0.5, “ no ”, “yes”))) # mutateを使用して新しい変数を作成し、 ifelseを使用して多数決ルールを設定します# トレーニングエラーを取得します
- logit_training_error <- calc_error_rate(predicted.value=banking.train_glm$predicted.value, true .value=YTrain) # ロジスティックモデルのテストエラーを取得します
- prob.test = predict(glm.fit,banking.test,type="response") banking.test_glm = banking.test %>% #行を選択
- mutate(predicted.value2= as .factor(ifelse(prob.test<=0.5, “ no ”, “yes”))) # ルールを設定logit_test_error <- calc_error_rate(predicted.value=banking.test_glm$predicted.value2, true .value=YTest)# ロジスティックモデルのトレーニングエラーとテストエラーを書き留めます
- records[1,] <- c(logit_traing_error,logit_test_error)#最初の行に書き込む
#2 決定木 決定木の場合、クロス検証に従って最適な分割ノードを特定します。決定木の概要については、こちらの記事をご覧ください: https://towardsdatascience.com/decision-trees-in-machine-learning-641b9c4e8052。 - # 最適なノードを見つける
- #行の総数
- nobs = nrow(banking.train) #DT モデルを構築します。
- #DTモデルの構築については、このドキュメント(https://www.datacamp.com/community/tutorials/decision-trees-R)を参照してください。
- bank_tree = tree(y~., data = banking.train, na.action = na.pass,
- control = tree.control(nobs , mincut =2, minsize = 10, mindev = 1e-3)) #クロス検証でツリーを剪定する
- .seed(3)を設定する
- cv = cv.tree(bank_tree、FUN=prune.misclass、K=10)
- 履歴書#最適な履歴書を特定する
- best.size.cv = cv$サイズ[ which.min (cv$dev)]
- best.size .cv#best = 3bank_tree.pruned<-prune.misclass(bank_tree, best=3)
- 要約(bank_tree.pruned)
クロス検証に最適なサイズは 3 です。 - # bank_tree.prunedのトレーニングおよびテスト エラー
- pred_train = 予測(bank_tree.pruned、banking.train、type="class")
- pred_test = predict(bank_tree.pruned, banking.test, type="class") # トレーニングエラー
- DT_training_error <- calc_error_rate(predicted.value=pred_train, true .value=YTrain) # テストエラー
- DT_test_error <- calc_error_rate(predicted.value=pred_test, true .value=YTest) # エラーを書き留める
- レコード[2,] <- c(DT_training_error,DT_test_error)
#3 K近傍法(KNN) ノンパラメトリック手法である KNN では、分布に関する事前の知識は必要ありません。つまり、KNN は関連するセルに k 個の最も近い近傍を割り当てます。 概要については、この記事「R での K 近傍法の初心者向けガイド: 初心者からエキスパートまで」を参照してください: https://towardsdatascience.com/beginners-guide-to-k-nearest-neighbors-in-r-from-zero-to-hero-d92cd4074bdb。クロス検証と do.chunk 関数の詳細については、次の記事を参照してください: https://towardsdatascience.com/beginners-guide-to-k-nearest-neighbors-in-r-from-zero-to-hero-d92cd4074bdb。 クロスバリデーションを使用すると、k = 20 のときにクロスバリデーション エラーが最小になることがわかりました。 - 倍数 = 10
- set .seed(1)# cut() は範囲を複数の区間に分割します
- 折り畳み = seq.int (nrow(banking.train)) %>%
- cut(breaks = nfold, labels = FALSE ) %>%
- sampledo.chunk <-関数(chunkid、folddef、Xdat、Ydat、k) {
- train = (folddef!=chunkid)# トレーニング インデックスXtr = Xdat[train,] # インデックスによるトレーニングセットYtr = Ydat[train] # トレーニング セット内の実際のラベルXvl = Xdat[!train,] # テスト セットYvl = Ydat[!train] # テスト セット内の実際のラベルpredYtr = knn(train = Xtr, test = Xtr, cl = Ytr, k = k) # トレーニング ラベルを予測predYvl = knn(train = Xtr, test = Xvl, cl = Ytr, k = k) # テスト ラベルを予測data.frame(fold =chunkid, # k 個のフォールド
- train.error = calc_error_rate(predYtr, Ytr), # フォールドごとのトレーニング エラー
- val.error = calc_error_rate(predYvl, Yvl)) # フォールドごとのテストエラー
- }# 検証エラーを保存するにはerror.foldsを設定します
- error.folds = NULL # 10間隔でデータのシーケンスを作成する
- kvec = c(1, seq(10, 50, length.out = 5)) set .seed(1) for (j in kvec){
- tmp = ldply(1:nfold, do.chunk, # 各折り畳みにdo.関数を適用します
- folddef=folds、Xdat=XTrain、Ydat=YTrain、k=j) # 必須引数
- tmp$neighbors = j # 近隣の各値を追跡する
- error.folds = rbind(error.folds, tmp) # 結果を結合する
- }パッケージreshape2の#melt()は、ワイドフォーマットのデータをロングフォーマットのデータに変換します。
- errors = melt(error.folds, id.vars=c("fold","neighbors"), value.name = "error")
次に、検証エラーを最小化する最適な数値 K を見つけましょう。 - val.error.means = エラー %>%
- フィルター(変数== "val.error" ) %>%
- group_by(近隣, 変数) %>%
- summarise_each(関数(平均), 誤差) %>%
- グループ解除() %>%
- filter(error== min (error)) #最適な近傍数 = 20
- numneighbor =最大(val.error.means$neighbors)
- numneighbor## [20]
同じ手順に従って、トレーニング エラーとテスト エラーを見つけます。 - #トレーニングエラー
- .seed(20)を設定する
- pred.YTtrain = knn(train=XTrain、test=XTrain、cl=YTrain、k=20)
- knn_training_error <- calc_error_rate(predicted.value=pred.YTtrain, true .value=YTrain) #テストエラー =0.095 set.seed(20)
- pred.YTest = knn(トレーニング=XTrain、テスト=XTest、cl=YTrain、k=20)
- knn_test_error <- calc_error_rate(予測値=pred.YTest、 true .value=YTest) レコード[3,] <- c(knn_training_error、knn_test_error)
#4 ランダムフォレスト ランダム フォレスト モデルを構築するための標準的な手順に従います。ランダム フォレストの概要については、次の記事を参照してください: https://towardsdatascience.com/understanding-random-forest-58381e0602d2。 - #デフォルト設定でRFモデルを構築する
- .seed(1)を設定する
- RF_banking_train = randomForest(y ~ ., data=banking.train, importance= TRUE ) # トレーニングセットとテストセットを使用して結果クラスを予測する
- pred_train_RF = predict(RF_banking_train, banking.train, type="class") pred_test_RF = predict(RF_banking_train, banking.test, type="class") # トレーニングエラー
- RF_training_error <- calc_error_rate(predicted.value=pred_train_RF, true .value=YTrain) # テストエラー
- RF_test_error <- calc_error_rate(予測値=pred_test_RF、 true .value=YTest) レコード[4,] <- c(RF_training_error、RF_test_error)
#5 サポートベクターマシン ここでも、サポート ベクター マシンを構築するための標準的な手順に従います。アプローチの概要については、次の記事を参照してください: https://towardsdatascience.com/support-vector-machine-introduction-to-machine-learning-algorithms-934a444fca47。 - .seed(1)を設定する
- tune.out = tune(svm, y ~., data=banking.train,
- kernel="radial",ranges=list(cost=c(0.1,1,10)))# 最適なパラメータを見つける
- summary( tune.out )$best.parameters# 最適なモデル
- best_model = tune. out $best.modelsvm_fit=svm(y~., data=banking.train,kernel="radial",gamma=0.05555556,cost=1,probability= TRUE )# トレーニング/テスト セットを使用して結果クラスを予測する
- svm_best_train = 予測(svm_fit、banking.train、タイプ="class")
- svm_best_test = predict(svm_fit, banking.test, type="class") # トレーニングエラー
- svm_training_error <- calc_error_rate(predicted.value=svm_best_train, true .value=YTrain) # テストエラー
- svm_test_error <- calc_error_rate(予測値=svm_best_test、 true .value=YTest) レコード[5,] <- c(svm_training_error、svm_test_error)
4. モデルメトリクス モデル選択プロセスに従ってすべての機械学習モデルを構築し、トレーニング エラーとテスト エラーを取得しました。このセクションでは、いくつかのモデル メトリックを使用して最適なモデルを選択します。 4.1 トレーニング/テストエラー トレーニング/テストのエラーを使用して最適なモデルを見つけることができますか? それでは結果を見てみましょう。 記録 図2 ここでは、ランダム フォレストのトレーニング エラーが最も低くなっていますが、他の方法でも同様のテスト エラーがあります。トレーニング エラーとテスト エラーが非常に近いため、どちらが明確な勝者であるかを判断するのが難しいことに気付くかもしれません。 さらに、分類精度 (トレーニング エラーまたはテスト エラー) は、非常に不均衡なデータセットの測定基準としては使用すべきではありません。これは、データセットが多数の例で構成されており、ランダムに推測した場合でも 50% の精度が得られるためです。さらに悪いことに、高精度のモデルは少数の例に厳しい「ペナルティ」を課す可能性があります。それでは、別の指標である ROC 曲線を見てみましょう。 4.2 受信者動作特性(ROC)曲線 ROC は、分類モデルがすべての分類しきい値でどのように機能するかを示すグラフィカル表現です。他の分類器よりも速く 1 に近づく分類器を優先します。 ROC 曲線は、真陽性率と偽陽性率という 2 つのパラメータを同じグラフ内の異なるしきい値でプロットします。 TPR(リコール)= TP/(TP+FN) FPR = FP/(TN+FP) 図3 ROC 曲線は、分類精度を測定するだけでなく、TPR と FPR の間の適切なバランスも実現します。これはまれなイベントの場合に必要であり、多数例と少数例のバランスも実現したいためです。 - # ライブラリをロードする
- ライブラリ(ROCR)#追跡レコードの作成
- 曲線下面積 = 行列(NA、nrow=5、ncol=1)
- colnames(曲線下面積) <- c("AUC")
- rownames(Area_Under_the_Curve) <- c("Logistic","Tree","KNN","Random Forests","SVM")########### ロジスティック回帰 ############
- #ロシア
- prob_test <- predict(glm.fit, banking.test, type="response")
- pred_logit<- 予測(prob_test,banking.test$y)
- performance_logit <- performance(pred_logit,measure = "tpr", x.measure = "fpr")########### 決定木 ############
- #ロシア
- pred_DT<-predict(bank_tree.pruned、banking.test、type="vector")
- pred_DT <- 予測(pred_DT[,2],banking.test$y)
- performance_DT <- performance(pred_DT、measure = "tpr"、x.measure = "fpr")########### KNN ############
- #ロシア
- knn_model = knn(train=XTrain, test=XTrain, cl=YTrain, k=20,prob= TRUE )prob <- attr(knn_model, “prob”)
- 確率 <- 2*ifelse(knn_model == "-1", 確率,1-確率) — 1
- pred_knn <- 予測(確率、YTrain)
- performance_knn <- performance(pred_knn, “tpr”, “fpr”)########### ランダムフォレスト ###########
- #ロシア
- pred_RF<-predict(RF_banking_train、banking.test、type="prob")
- pred_class_RF <- 予測(pred_RF[,2],banking.test$y)
- performance_RF <- performance(pred_class_RF、measure = "tpr"、x.measure = "fpr")########### SVM ############
- #ロシア
- svm_fit_prob = 予測(svm_fit、type="prob",newdata=banking.test、確率= TRUE )
- svm_fit_prob_ROCR = 予測(attr(svm_fit_prob,”確率”)[,2],banking.test$y==”はい”)
- performance_svm <- パフォーマンス(svm_fit_prob_ROCR、"tpr"、"fpr")
ROC曲線を描いてみましょう。 ランダム割り当ての確率を示すために直線を追加します。私たちの分類器はランダムな推測よりも優れたパフォーマンスを発揮しますよね? - #ロジット
- plot(performance_logit,col=2,lwd=2,main="これら 5 つの分類方法のROC 曲線")legend(0.6, 0.6, c('logistic', 'Decision Tree', 'KNN','Random Forests','SVM'), 2:6)#decision tree
- プロット(performance_DT、col=3、lwd=2、 add = TRUE )#knn
- プロット(performance_knn、col=4、lwd=2、 add = TRUE )#RF
- plot(performance_RF,col=5,lwd=2, add = TRUE )# SVM
- プロット(performance_svm、col=6、lwd=2、 add = TRUE )abline(0,1)
図4 ここで勝者が決まります。 ROC 曲線によると、KNN (青線) は他のすべての方法よりも高くなっています。 4.3 曲線下面積(AUC) 名前が示すように、AUC は ROC 曲線の下の領域です。これは直感的な AUC 曲線の数学的表現です。 AUC は、分類器が可能な分類しきい値でどの程度うまく機能するかを組み合わせた結果を示します。 - ########### ロジット ###########
- auc_logit = パフォーマンス(pred_logit, "auc")@ y.values
- Area_Under_the_Curve[1,] <-c( as . numeric (auc_logit))########### 決定木 ###########
- auc_dt =パフォーマンス(pred_DT、"auc")@y.values
- 曲線下面積[2,] <- c( as . numeric (auc_dt))########### KNN ###########
- auc_knn < -パフォーマンス(pred_knn,"auc")@y.values
- Area_Under_the_Curve[3,] <- c( as . numeric (auc_knn))########### ランダムフォレスト ###########
- auc_RF =パフォーマンス(pred_class_RF、 "auc")@y.values
- 曲線下面積[4,] <- c( as . numeric (auc_RF))############ SVM ###########
- auc_svm<-performance(svm_fit_prob_ROCR,"auc")@ y.values [[1]]
- 曲線下面積[5,] <- c( as . numeric (auc_svm))
AUC値を確認してみましょう。 曲線の下の面積 図5 さらに、KNN は最大の AUC 値 (0.847) を持っています。 結論この論文では、ノンパラメトリック分類器である KNN がパラメトリック分類器よりも優れていることがわかりました。指標の観点から言えば、まれなイベントの分類精度よりも ROC 曲線を選択する方が合理的です。 原題: 5 つの機械学習アルゴリズムを使用してまれなイベントを分類する、著者: Leihua Ye [51CTOによる翻訳。パートナーサイトに転載する場合は、元の翻訳者と出典を51CTO.comとして明記してください] |