はじめに
Aidemyのデータ分析講座6か月を受講しまして、最後の成果課題として、kaggleのデータを用いた「社員の離職予測」をしてみました。
私はAidemyの講座を受講しようと決めてからパソコンを購入したくらいの初心者です。自分の知らない世界に飛び込んでみたい気持ちからプログラミングに挑戦してみました。学んでいく中で「こんなこともできるのか」と驚くことばかりでした。AIのことを知ることができただけでも貴重な経験となりました。
本成果物の方針と環境
- 初学者向けの内容となります。講座で学んだやり方を軸に、わからない点は調べて補っています。
- 分析環境としてGoogle Colaboratoryを使用します。
- Kaggleから以下のデータセットを使用しました。
こちらは、従業員の離職につながる要因を明らかにするための架空のデータセットです。流れとして、社員の離職(Attrition)がYesかNoのどちらに分類されるか予測する二項分類となります。Attritionが目的変数となり、そのほかは説明変数としています。今回はAttritionがYesかNoのどちらに分類されるか予測するモデルを複数作り、テストデータで予測することが目的となります。
本編
1.データの確認
2.ベースラインモデル構築
3.不均衡データであるため、ダウンサンプリングで均衡をとる。
4.ハイパーパラメータのチューニング
5.まとめ
6.終わりに
基本的なライブラリのインポート
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
#ロジスティック回帰モデルをインポート
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score
#線形SVMをインポート
from sklearn.svm import LinearSVC
#非線形SVMをインポート
from sklearn.svm import SVC
#ランダムフォレストのインポート
from sklearn.ensemble import RandomForestClassifier
#k-NNのインポート
from sklearn.neighbors import KNeighborsClassifier
#ダウンサンプリング実装
from imblearn.under_sampling import RandomUnderSampler
#ランダムサーチのモジュールをインポート
from sklearn.model_selection import RandomizedSearchCV
#scipy.statをインポート
import scipy.stats
#データ読み込み
df=pd.read_csv('WA_Fn-UseC_-HR-Employee-Attrition.csv')
1.データの確認
データの確認
1470行35列(次元)でobject型データが散見される。欠損値は無いことを確認。df.info()
1 Age 年齢
2 Attrition 離職の有無
3 BusinessTravel 出張の頻度
4 DailyRate 日給レート
5 Department 所属部署
6 DistanceFromHome 自宅ー職場間の距離 (マイル)
7 Education 学歴
8 EducationField 学生時代の専門分野
9 EmployeeCount グループを構成する従業員の数
10 EmployeeNumber 従業員番号
11 EnvironmentSatisfaction 環境への満足度(4段階)
12 Gender ジェンダー
13 HourlyRate 時給レート
14 JobInvolvement 仕事への関与のレベル(4段階)
15 JobLevel 仕事のレベル(5段階)
16 JobRole 仕事上の役割(職種、役職)
17 JobSatisfaction 仕事への満足度(4段階)
18 MaritalStatus 婚姻の状況
19 MonthlyIncome 月収
20 MonthlyRate 月収レート
21 NumCompaniesWorked これまでに勤務した企業数
22 Over18 18歳以上か?
23 OverTime 残業の有無
24 PercentSalaryHike 昇給率
25 PerformanceRating パフォーマンスレート(4段階)
26 RelationshipSatisfaction 人間関係への満足度(4段階)
27 StandardHours 標準労働時間
28 StockOptionLevel ストックオプションレベル
29 TotalWorkingYears トータルの勤続年数
30 TrainingTimesLastYear 過去1年間のトレーニング受講時間
31 WorkLifeBalance ワークライフバランス(従業員の実感、4段階)
32 YearsAtCompany この会社での勤続年数
33 YearsInCurrentRole 現在の役職についてからの年数
34 YearsSinceLastPromotion 最後の昇進からの年数
35 YearsWithCurrManager 現在のマネージャーになってからの年数
統計量確認後、以下のデータは今回の分析目的には不要と判断した。
・従業員数 EmployeeCount
・従業員番号 EmployeeNumber
・標準労働時間 StandardHours
・18歳以上であるか Over18
なぜなら、同じ値しか示さなかったり、社員の離職には関係がないためである。
そのため、今回はデータから除外することにした。
df=df.drop(columns=['EmployeeCount','EmployeeNumber', 'StandardHours','Over18'])
#データの行列数確認
df.shape
オブジェクト型データの確認
object型データは”Attrition”も含めると全部で9次元であった。全次元で最大でも9種類の値と小さいため、カテゴリカルデータとして扱う。
for column in df.columns:
if df[column].dtype == object:
print(f"{column}: Cardinality {df[column].nunique()}")
print("-" * 50)
各次元とターゲットデータAttritionの関係を可視化↓
可視化によって確認できた、Attritionの傾向が相対的に強いもの
- BusinessTravelがTravel_Frequently
- JobRoleがSales Representative
- MaritalStatusがSingle
- OverTimeがYes
#object型データ
for column in df.columns:
if df[column].dtype == object:
sns.countplot(x=column, hue="Attrition", data=df)
plt.xticks(rotation=45)
plt.show()
数値型データの確認
各次元とターゲットデータAttritionの関係を可視化↓
可視化によって確認できた、Attritionの傾向が相対的に強いもの
- JobLevelが低い
- MonthlyIncomeが低い
- StockOptionLevelが低い
- TotalWorkingYearsが短い
- YearAtCompanyが短い
- YearsInCurrentRoleが短い
- YearsSinceLastPromotionが短い
- YearsWithCurrManagerが短い
#数値型データ
for column in df.columns:
if df[column].dtype != object and df[column].nunique() <= df["Age"].nunique():
sns.countplot(x=column, hue="Attrition", data=df)
plt.show()
# Cardinalityが大きいデータは棒グラフでは見づらいので以下のヒストグラムでbinsを調整した上で可視化
fig = sns.FacetGrid(df, col="Attrition", hue="Attrition", height=4)
fig.map(sns.histplot, "Age", bins=5, kde=False)
plt.show()
データの調整(ラベルエンコーダー)
・object型データを数値型データに変換する。
まずはターゲットデータである”Attrition”を変換する。
今回は二項分類なのでLabelEncoderで0と1にエンコードする。
print("before : ", df["Attrition"].dtype)
print("before : ", df["Attrition"].unique())
df["Attrition"] = LabelEncoder().fit_transform(df["Attrition"])
print("after : ", df["Attrition"].dtype)
print("after : ", df["Attrition"].unique())
#カテゴリカル変数のダミー変数化
df_cleansing = pd.get_dummies(df, columns=df.select_dtypes(include="object").columns)
df_cleansing.info()
相関関係の確認
Attritionに対してほぼ相関がないように思える。強いていうのであれば通勤距離が長いと離職につながるようである。
plt.figure(figsize=(20,20))
sns.heatmap(df.corr(),vmax=1,vmin=-1,annot=True)
plt.show()
2.ベースラインモデル構築
データの前処理
#先ほどダミー変数に格納した"df_cleansing"を用いる。
#正解ラベルの作成
target = df_cleansing['Attrition']
target
#df_cleansingにAttrition(正解ラベル)が含まれているのでAttritionを削除
df_cleansing= df_cleansing.drop(['Attrition'], axis = 1)
df_cleansing
#データを8:2で分割
train_X, test_X, train_y, test_y = train_test_split(df_cleansing, target, test_size = 0.2, random_state = 42)
#分割前1470行、分割後1176行(80%に訓練データに分割されている。
print(train_X.shape)
print(train_y.shape)
print(test_X.shape)
print(test_y.shape)
- 学習と評価↓
どのモデルも正解率が良くても、F値がかなり低かったり、その逆の現象が起きたりしていた。現時点でバランスが良いものは「非線形モデル」であった。改善の余地ありと判断した。
ロジスティック回帰モデルで予測
model = LogisticRegression(max_iter=10000)
model.fit(train_X, train_y)
#訓練データに対して予測
y_pred = model.predict(train_X)
#テストデータに対して予測
y_test_pred = model.predict(test_X)
#モデルの予測結果の評価指標
print("訓練データ正解率:{:.3f}".format(accuracy_score(train_y, y_pred)))
print("訓練データ精度/適合率:{:.3f}".format(precision_score(train_y, y_pred)))
print("訓練データ再現率:{:.3f}".format(recall_score(train_y, y_pred)))
print("訓練データF値:{:.3f}".format(f1_score(train_y, y_pred)))
print()
print("テストデータ正解率:{:.3f}".format(accuracy_score(test_y, y_test_pred)))
print("テストデータ精度/適合率{:.3f}".format(precision_score(test_y, y_test_pred)))
print("テストデータ再現率:{:.3f}".format(recall_score(test_y, y_test_pred)))
print("テストデータF値:{:.3f}".format(f1_score(test_y, y_test_pred)))
#max_iterを10000にすると下記のwarningの表示が消えた
サポートベクタマシンで予測(線形SVMモデル)
model = LinearSVC(max_iter=1000000)
model.fit(train_X, train_y)
#訓練データに対して予測
y_pred = model.predict(train_X)
#テストデータに対して予測
y_test_pred = model.predict(test_X)
#モデルの予測結果の評価指標
print("訓練データ正解率:{:.3f}".format(accuracy_score(train_y, y_pred)))
print("訓練データ精度/適合率:{:.3f}".format(precision_score(train_y, y_pred)))
print("訓練データ再現率:{:.3f}".format(recall_score(train_y, y_pred)))
print("訓練データF値:{:.3f}".format(f1_score(train_y, y_pred)))
print()
print("テストデータ正解率:{:.3f}".format(accuracy_score(test_y, y_test_pred)))
print("テストデータ精度/適合率{:.3f}".format(precision_score(test_y, y_test_pred)))
print("テストデータ再現率:{:.3f}".format(recall_score(test_y, y_test_pred)))
print("テストデータF値:{:.3f}".format(f1_score(test_y, y_test_pred)))
以下Warningが残り続けた。
/usr/local/lib/python3.10/dist-packages/sklearn/svm/_base.py:1244: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
warnings.warn(
サポートベクタマシンで予測(非線形SVMモデル)
model = SVC()
model.fit(train_X, train_y)
#訓練データに対して予測
y_pred = model.predict(train_X)
#テストデータに対して予測
y_test_pred = model.predict(test_X)
#モデルの予測結果の評価指標
print("訓練データ正解率:{:.3f}".format(accuracy_score(train_y, y_pred)))
print("訓練データ精度/適合率:{:.3f}".format(precision_score(train_y, y_pred)))
print("訓練データ再現率:{:.3f}".format(recall_score(train_y, y_pred)))
print("訓練データF値:{:.3f}".format(f1_score(train_y, y_pred)))
print()
print("テストデータ正解率:{:.3f}".format(accuracy_score(test_y, y_test_pred)))
print("テストデータ精度/適合率{:.3f}".format(precision_score(test_y, y_test_pred)))
print("テストデータ再現率:{:.3f}".format(recall_score(test_y, y_test_pred)))
print("テストデータF値:{:.3f}".format(f1_score(test_y, y_test_pred)))
ランダムフォレストで予測
model = RandomForestClassifier()
model.fit(train_X, train_y)
#訓練データに対して予測
y_pred = model.predict(train_X)
#テストデータに対して予測
y_test_pred = model.predict(test_X)
#モデルの予測結果の評価指標
print("訓練データ正解率:{:.3f}".format(accuracy_score(train_y, y_pred)))
print("訓練データ精度/適合率:{:.3f}".format(precision_score(train_y, y_pred)))
print("訓練データ再現率:{:.3f}".format(recall_score(train_y, y_pred)))
print("訓練データF値:{:.3f}".format(f1_score(train_y, y_pred)))
print()
print("テストデータ正解率:{:.3f}".format(accuracy_score(test_y, y_test_pred)))
print("テストデータ精度/適合率{:.3f}".format(precision_score(test_y, y_test_pred)))
print("テストデータ再現率:{:.3f}".format(recall_score(test_y, y_test_pred)))
print("テストデータF値:{:.3f}".format(f1_score(test_y, y_test_pred)))
k近傍法で予測
model = KNeighborsClassifier()
model.fit(train_X, train_y)
#訓練データに対して予測
y_pred = model.predict(train_X)
#テストデータに対して予測
y_test_pred = model.predict(test_X)
#モデルの予測結果の評価指標
print("訓練データ正解率:{:.3f}".format(accuracy_score(train_y, y_pred)))
print("訓練データ精度/適合率:{:.3f}".format(precision_score(train_y, y_pred)))
print("訓練データ再現率:{:.3f}".format(recall_score(train_y, y_pred)))
print("訓練データF値:{:.3f}".format(f1_score(train_y, y_pred)))
print()
print("テストデータ正解率:{:.3f}".format(accuracy_score(test_y, y_test_pred)))
print("テストデータ精度/適合率{:.3f}".format(precision_score(test_y, y_test_pred)))
print("テストデータ再現率:{:.3f}".format(recall_score(test_y, y_test_pred)))
print("テストデータF値:{:.3f}".format(f1_score(test_y, y_test_pred)))
3.不均衡データであるため、ダウンサンプリングで均衡をとる。
- 不均衡データによる原因により、ベースラインモデルによる学習のスコアが不安定であると仮定して、ダウンサンプリングを試みた。
ダウンサンプリング後、カテゴリカル変数のダミー変数化、学習までの準備
#目的変数の各値の個数を確認
target.value_counts()
#Attrition が0のデータを抽出
df_Attrition_0 = df.query('Attrition == 0')
df_Attrition_0
#Attritionが0のデータをダウンサンプリング
df_sample_0 = df_Attrition_0.sample(237, random_state=0)
df_sample_0
#同じようにAttritionが1の行のみ抽出
df_Attrition_1 = df.query('Attrition == 1')
df_Attrition_1
#抽出したデータを結合
df3=pd.concat([df_sample_0,df_Attrition_1],ignore_index=True)
df3
#結合した結果の可視化
sns.countplot(x='Attrition', data=df3)
#カテゴリカル変数のダミー変数化
df3 = pd.get_dummies(df3)
df3
#正解ラベルの作成
target2 = df3['Attrition']
target2
#df3に(正解ラベル)が含まれているのでAttritionを削除
df3= df3.drop(['Attrition'], axis = 1)
df3
#scikit-learnをインポートし、データを分割(8:2)
train_X, test_X, train_y, test_y = train_test_split(df3, target2, test_size = 0.2, random_state = 42)
print(train_X.shape)
print(train_y.shape)
print(test_X.shape)
print(test_y.shape)
ダウンサンプリング後の学習と評価
- 適合率、再現率が大幅に改善され、正解率の信頼性が高くとれる結果となった。現時点でバランスが良いものは「ロジスティック回帰モデル」となった。
ロジスティック回帰モデルで予測
model = LogisticRegression(max_iter=10000)
model.fit(train_X, train_y)
#訓練データに対して予測
y_pred = model.predict(train_X)
#テストデータに対して予測
y_test_pred = model.predict(test_X)
#モデルの予測結果の評価指標
print("訓練データ正解率:{:.3f}".format(accuracy_score(train_y, y_pred)))
print("訓練データ精度/適合率:{:.3f}".format(precision_score(train_y, y_pred)))
print("訓練データ再現率:{:.3f}".format(recall_score(train_y, y_pred)))
print("訓練データF値:{:.3f}".format(f1_score(train_y, y_pred)))
print()
print("テストデータ正解率:{:.3f}".format(accuracy_score(test_y, y_test_pred)))
print("テストデータ精度/適合率{:.3f}".format(precision_score(test_y, y_test_pred)))
print("テストデータ再現率:{:.3f}".format(recall_score(test_y, y_test_pred)))
print("テストデータF値:{:.3f}".format(f1_score(test_y, y_test_pred)))
サポートベクタマシンで予測(線形SVMモデル)
model = LinearSVC(max_iter=1000000)
model.fit(train_X, train_y)
#訓練データに対して予測
y_pred = model.predict(train_X)
#テストデータに対して予測
y_test_pred = model.predict(test_X)
#モデルの予測結果の評価指標
print("訓練データ正解率:{:.3f}".format(accuracy_score(train_y, y_pred)))
print("訓練データ精度/適合率:{:.3f}".format(precision_score(train_y, y_pred)))
print("訓練データ再現率:{:.3f}".format(recall_score(train_y, y_pred)))
print("訓練データF値:{:.3f}".format(f1_score(train_y, y_pred)))
print()
print("テストデータ正解率:{:.3f}".format(accuracy_score(test_y, y_test_pred)))
print("テストデータ精度/適合率{:.3f}".format(precision_score(test_y, y_test_pred)))
print("テストデータ再現率:{:.3f}".format(recall_score(test_y, y_test_pred)))
print("テストデータF値:{:.3f}".format(f1_score(test_y, y_test_pred)))
以下のWarningが表示された。
/usr/local/lib/python3.10/dist-packages/sklearn/svm/_base.py:1244: ConvergenceWarning: Liblinear failed to converge, increase the number of iterations.
warnings.warn(
ダウンサンプリング後は訓練データの「正解率」「精度/適合率、テストデータの「精度/適合率」以外の数値が上がった。
サポートベクタマシンで予測(非線形SVMモデル)
model = SVC()
model.fit(train_X, train_y)
#訓練データに対して予測
y_pred = model.predict(train_X)
#テストデータに対して予測
y_test_pred = model.predict(test_X)
#モデルの予測結果の評価指標
print("訓練データ正解率:{:.3f}".format(accuracy_score(train_y, y_pred)))
print("訓練データ精度/適合率:{:.3f}".format(precision_score(train_y, y_pred)))
print("訓練データ再現率:{:.3f}".format(recall_score(train_y, y_pred)))
print("訓練データF値:{:.3f}".format(f1_score(train_y, y_pred)))
print()
print("テストデータ正解率:{:.3f}".format(accuracy_score(test_y, y_test_pred)))
print("テストデータ精度/適合率{:.3f}".format(precision_score(test_y, y_test_pred)))
print("テストデータ再現率:{:.3f}".format(recall_score(test_y, y_test_pred)))
print("テストデータF値:{:.3f}".format(f1_score(test_y, y_test_pred)))
ランダムフォレストで予測
model = RandomForestClassifier()
model.fit(train_X, train_y)
#訓練データに対して予測
y_pred = model.predict(train_X)
#テストデータに対して予測
y_test_pred = model.predict(test_X)
#モデルの予測結果の評価指標
print("訓練データ正解率:{:.3f}".format(accuracy_score(train_y, y_pred)))
print("訓練データ精度/適合率:{:.3f}".format(precision_score(train_y, y_pred)))
print("訓練データ再現率:{:.3f}".format(recall_score(train_y, y_pred)))
print("訓練データF値:{:.3f}".format(f1_score(train_y, y_pred)))
print()
print("テストデータ正解率:{:.3f}".format(accuracy_score(test_y, y_test_pred)))
print("テストデータ精度/適合率{:.3f}".format(precision_score(test_y, y_test_pred)))
print("テストデータ再現率:{:.3f}".format(recall_score(test_y, y_test_pred)))
print("テストデータF値:{:.3f}".format(f1_score(test_y, y_test_pred)))
k近傍法で予測
model = KNeighborsClassifier()
model.fit(train_X, train_y)
#訓練データに対して予測
y_pred = model.predict(train_X)
#テストデータに対して予測
y_test_pred = model.predict(test_X)
#モデルの予測結果の評価指標
print("訓練データ正解率:{:.3f}".format(accuracy_score(train_y, y_pred)))
print("訓練データ精度/適合率:{:.3f}".format(precision_score(train_y, y_pred)))
print("訓練データ再現率:{:.3f}".format(recall_score(train_y, y_pred)))
print("訓練データF値:{:.3f}".format(f1_score(train_y, y_pred)))
print()
print("テストデータ正解率:{:.3f}".format(accuracy_score(test_y, y_test_pred)))
print("テストデータ精度/適合率{:.3f}".format(precision_score(test_y, y_test_pred)))
print("テストデータ再現率:{:.3f}".format(recall_score(test_y, y_test_pred)))
print("テストデータF値:{:.3f}".format(f1_score(test_y, y_test_pred)))
4.ハイパーパラメータのチューニング
- さらに精度をあげるために、「ランダムサーチ」によって、精度の良いパラメータの組み合わせを探索する。
ロジスティック回帰のハイパーパラメーターの値の候補を設定
model_param_set_random = {
LogisticRegression() : {
"penalty" : ["l1", "l2"],
"C" : scipy.stats.uniform(0.00001, 1000),
"solver" : ["newton-cg", "lbfgs", "liblinear", "sag", "saga"],
"multi_class" : ["ovr", "multinomial", "auto"],
"max_iter" : [2000],
"random_state": [42]
}
}
max_score = 0
best_param = None
#ランダムサーチでハイパーパラメーター探索
for model, param in model_param_set_random.items():
clf = RandomizedSearchCV(model, param)
clf.fit(train_X, train_y)
pred_y = clf.predict(test_X)
score = accuracy_score(test_y, pred_y)
if max_score < score:
max_score = score
best_param = clf.best_params_
print("ハイパーパラメーター:{}".format(best_param))
print("ベストスコア:", max_score)
LogisticRegression = LogisticRegression()
LogisticRegression.fit(train_X, train_y)
print()
print('調整なし')
print(LogisticRegression.score(test_X, test_y))
非線形svmのハイパーパラメーターの値の候補を設定
model_param_set_random = {
SVC() : {
"kernel": ["linear", "poly", "rbf", "sigmoid"],
"C": scipy.stats.uniform(0.00001, 1000),
"decision_function_shape": ["ovr", "ovo"],
"random_state": [42]
}
}
max_score = 0
best_param = None
#ランダムサーチでハイパーパラメーター探索
for model, param in model_param_set_random.items():
clf = RandomizedSearchCV(model, param)
clf.fit(train_X, train_y)
pred_y = clf.predict(test_X)
score = accuracy_score(test_y, pred_y)
if max_score < score:
max_score = score
best_param = clf.best_params_
print("ハイパーパラメーター:{}".format(best_param))
print("ベストスコア:", max_score)
svm = SVC()
svm.fit(train_X, train_y)
print()
print('調整なし')
print(svm.score(test_X, test_y))
ランダムフォレストのハイパーパラメーターの値の候補を設定
model_param_set_random = {
RandomForestClassifier() : {
"n_estimators": [i for i in range(1, 21)],
"criterion": ["gini", "entropy"],
"max_depth":[i for i in range(1, 5)],
"random_state": [42]
}
}
max_score = 0
best_param = None
#ランダムサーチでハイパーパラメーター探索
for model, param in model_param_set_random.items():
clf = RandomizedSearchCV(model, param)
clf.fit(train_X, train_y)
pred_y = clf.predict(test_X)
score = accuracy_score(test_y, pred_y)
if max_score < score:
max_score = score
best_param = clf.best_params_
print("ハイパーパラメーター:{}".format(best_param))
print("ベストスコア:", max_score)
RandomForest = RandomForestClassifier()
RandomForest.fit(train_X, train_y)
print()
print('調整なし')
print(RandomForest.score(test_X, test_y))
k近傍法のハイパーパラメーターの値の候補を設定
model_param_set_random = {
KNeighborsClassifier() : {
"n_neighbors": [i for i in range(1, 51)],
"weights": ["uniform" , "distance"],
"algorithm":["ball_tree", "kd_tree", "brute", "auto"]
}
}
max_score = 0
best_param = None
#ランダムサーチでハイパーパラメーター探索
for model, param in model_param_set_random.items():
clf = RandomizedSearchCV(model, param)
clf.fit(train_X, train_y)
pred_y = clf.predict(test_X)
score = accuracy_score(test_y, pred_y)
if max_score < score:
max_score = score
best_param = clf.best_params_
print("ハイパーパラメーター:{}".format(best_param))
print("ベストスコア:", max_score)
KNeighborsClassifier = KNeighborsClassifier()
KNeighborsClassifier.fit(train_X, train_y)
print()
print('調整なし')
print(KNeighborsClassifier.score(test_X, test_y))
5まとめ
チューニングをすることで、どのモデルのスコアも上昇する結果となった。最終的に最も高いベストスコアを出したモデルは、ランダムフォレスト(0.821052...)となった。
6.終わりに
・反省
ターゲットデータに対して相関関係にあるものが薄く、どのように進めていけばよいかかなり迷いました。そのためすべてターゲットデータと掛け合わせ、視覚化しましたが、正直考察しきれていない部分が多いように感じます。今後も引き続き様々なコードを見て理解し、その考察に注目して、手段を増やすことで分析力を向上させていきたいと思います。
・やってみて感じたこと 今後について
Aidemyのカリキュラムをもとに、コードを書いたり、先人の方の二項分類のコードを参考に、どのような分析をしているかをひたすら眺めては調べ考えたりすることで改めてコードの認識を深めるとともに、多種多様な方法があることを知りました。ハイパーパラメータに関してはしっかりと仕組みを理解するのにかなり時間がかかると感じました。今後もコードに触れていく環境を維持し、コードの意味を理解することを意識し、いずれコンペ等に意欲的に参加していきたいと考えております。
アイデミーのチューターさんに感謝申し上げます。ありがとうございました。