大容量データにも対応した特徴量エンジニアリング
FeatureWiz(Python)を使った特徴量選択(変数選択)

大容量データにも対応した特徴量エンジニアリングFeatureWiz(Python)を使った特徴量選択(変数選択)

機械学習系のモデルを構築するときに外せない作業の1つが、特徴量エンジニアリング(Feature Engineering)です。

特徴量エンジニアリングの主な作業は、特徴量生成特徴量選択(変数選択)です。

FeatureWizは、Python上で動くscikit-learn(sklearn)互換の特徴量エンジニアリング環境で、Pythonで記述された並列計算用のオープンソースライブラリーDaskを活用することで、非常に大きなデータセットに対しより良いパフォーマンスを発揮するようになりました。

今回は、「大容量データにも対応した特徴量エンジニアリングライブラリーFeatureWiz(Python)」のお話しを、一般的な使い方を説明しながら、大容量データセットに対する場合にどうすればいいのかを、簡単に説明します。

FeatureWiz の主な機能

  • 特徴量生成機能
    • interactions:乗算による特徴量生成
    • groupby:グループ集計(カテゴリカル変数で量的変数を集計)
    • target:カテゴリカル変数のエンコーディング
  • 特徴量選択機能
    • SULOV:相関係数ベースの変数選択
    • Recursive XGBoost:SULOV後にXGBoostを使用し最良の特徴量(変数)セットを探索

特徴量選択(変数選択)後、実務上どうしても必要な特徴量(変数)は、特徴量選択(変数選択)で弾かれたとしても採用してください。

 

FeatureWiz ライブラリーのインストール

コマンドプロンプト上で、pipでインストールするときのコードは以下です。

pip install xlrd
pip install featurewiz --ignore-installed --no-deps

 

実施すること

データセットの全ての特徴量(説明変数)を使った予測精度と、FeatureWizで特徴量選択(変数選択)し絞り込んだ特徴量(説明変数)を使った予測精度比較し、どの程度予測精度が悪化するのかを見ていきます。

  • データセットの全ての特徴量(説明変数)を使った場合
  • FeatureWizで特徴量選択(変数選択)し絞り込んだ特徴量(説明変数)を使った場合

 

今回は、木系の以下の3つの数理モデルを構築していきます。

  • 決定木(tree): DecisionTreeRegressor、DecisionTreeClassifier
  • ランダムフォレスト(RandomForest): RandomForestRegressor、RandomForestClassifier
  • アダブースト(AdaBoost): AdaBoostRegressor、AdaBoostClassifier

後ろにRegressorというワードが付いているのが回帰問題用のモデル(目的変数Yが量的変数)で、後ろにClassifierというワードが付いているのが回帰問題用のモデル(目的変数Yが質的変数)です。

ランダムフォレスト決定木ベースバギング系のアンサンブル学習モデルで、アダブースト決定木ベースブースティング系のアンサンブル学習モデルです。今回のアダブーストは、ランダムフォレストをベースに設定しています。

要は、アダブーストランダムフォレスト決定木の順に、予測精度が高いことが予想されます。

モデルの話しはこれ以上詳しくは説明いたしません。興味のある方は調べて頂ければと思います。

 

サンプルデータとしては、以下の2つを使い説明します。

  • Boston house pricesデータ:回帰問題用のサンプルデータ
  • 通信会社の顧客離反データ:分類問題用のサンプルデータ

 

Boston house prices(回帰問題)

Boston house prices|データセットの概要

回帰問題でよく登場するみんな大好き「ボストン住宅価格(BostonHousing)」データセットです。

以下、データ項目です。

  • CRIM: 犯罪率
  • ZN: 広い家の割合(25,000平方フィートを超える区画に分類される住宅地の割合)
  • INDUS: 非小売業の割合
  • CHAS: 川と隣接(川に隣接している場合は1、そうでない場合は0)
  • NOX: NOx濃度(0.1ppm単位)
  • RM: 平均部屋数
  • AGE: 古い家の割合(1940年より前に建てられた持ち家の割合)
  • DIS: 主要施設への距離(5つあるボストン雇用センターまでの加重距離)
  • RAD: 主要高速道路アクセス指数
  • TAX: 固定資産税率(10,000ドル当たり)
  • PTRATIO: 生徒と先生の比率
  • B: 1000×(黒人割合- 0.63)の二乗
  • LSTAT: 低所得者人口の割合
  • MEDV:住宅価格の中央値(1000ドル単位)

項目数は14。

  • 目的変数Y:一番下の「MEDV」(住宅価格)
  • 説明変数X(特徴量):残り13つのデータすべて

要するに、「MEDV」(住宅価格)を他の変数で当てる問題です。

 

CSVファイルは、以下からダウンロードできます。

BostonHousing.csv
https://www.salesanalytics.co.jp/0leq

 

Boston house prices|ライブラリー読み込み

先ずは、ライブラリーの読み込みです。

以下、コードです。

# ライブラリーの読み込み
import pandas as pd
import numpy as np

from featurewiz import featurewiz

from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import RandomForestRegressor
from sklearn.ensemble import AdaBoostRegressor

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import OrdinalEncoder
from sklearn.preprocessing import LabelEncoder

from sklearn.metrics import mean_absolute_percentage_error
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error

import matplotlib.pyplot as plt

 

Boston house prices|データセット読み込みと前処理

データセットを読み込みます。

以下、コードです。

# データセットの読み込み
data_path = 'https://www.salesanalytics.co.jp/0leq'
df = pd.read_csv(data_path)

df.head() #確認

 

以下、実行結果です。

 

データ状況を確認します。

以下、コードです。

# データセットの状況の確認
df.info()

 

以下、実行結果です。

 

変数「chas」(川の隣かどうか? 1:Yes、0:No)が0-1の整数値の量的データ(ニューメリカルデータ)になっていますが、質的データ(カテゴリカルデータ)です。型変換します。

以下、コードです。

# chasの値変換(1:Yes、0:No)
df = df.replace({'chas':{1:'Yes', 0:'No'}})

df.head() #確認

 

以下、実行結果です。

 

再度、データ状況を確認します。

以下、コードです。

# データセットの状況の確認
df.info()

 

以下、実行結果です。

 

変数「chas」(川の隣かどうか? 1:Yes、0:No)がObject型(カテゴリカルデータの型の1つ)に変換されていることが分かります。

 

次に、モデル構築用の学習データと、構築したモデルを検証するテストデータに分割します。

以下、コードです。

# データセットの分割(学習データとテストデータ)
df_train, df_test = train_test_split(df,
                                     test_size=0.3,
                                     random_state=123)

 

量的データ(ニューメリカルデータ)を標準化します。学習データ標準化モデルを作り、それを学習データテストデータに適用します。

以下、コードです。

# 標準化
scaler = StandardScaler()

df_train[df_train.drop(columns=["medv"]).select_dtypes("number").columns] = scaler.fit_transform(df_train.drop(columns=["medv"]).select_dtypes("number"))

df_test[df_test.drop(columns=["medv"]).select_dtypes("number").columns] = scaler.transform(df_test.drop(columns=["medv"]).select_dtypes("number"))

 

この学習データテストデータを使って、モデル構築精度検証を実施していきます。

 

Boston house prices|モデル設定

今回は、以下の3つの数理モデルを構築しますので、その設定を実施します。

  • 決定木(tree): DecisionTreeRegressor
  • ランダムフォレスト(RandomForest): RandomForestRegressor
  • アダブースト(AdaBoost): AdaBoostRegressor

以下、コードです。

# 構築するモデル設定
models = {
    'tree': DecisionTreeRegressor(random_state=123),
    'RandomForest': RandomForestRegressor(random_state=123),
    'AdaBoost': AdaBoostRegressor(RandomForestRegressor(random_state=123),
                                  learning_rate=0.1,
                                  n_estimators=100,
                                  random_state=123)
}

 

Boston house prices|学習とテスト(全ての特徴量を利用)

データセットの全ての特徴量(説明変数)を使った場合です。

特徴量(説明変数)目的変数に分け、質的データ(カテゴリカルデータ)を数値ラベル値に変換します。

以下、コードです。

# 学習データとテストデータ
X_train = df_train.drop(columns=["medv"])
y_train = df_train["medv"]
X_test = df_test.drop(columns=["medv"])
y_test = df_test["medv"]

# カテゴリカル変数をOrdinalEncoderで数値ラベルに変換
ordEnc = OrdinalEncoder()
X_train[X_train.select_dtypes("object").columns] \
    = ordEnc.fit_transform(X_train.select_dtypes("object"))
X_test[X_test.select_dtypes("object").columns] \
    = ordEnc.transform(X_test.select_dtypes("object"))

 

このデータを使い、学習データモデル構築し、テストデータ構築したモデルの検証を、実施します。

以下、コードです。

# 結果を格納するハコ
scores = {} 

# 予測モデル構築と精度検証
for model_name, model in models.items():
    model.fit(X_train, y_train)     #学習データで予測モデル構築
    y_pred = model.predict(X_test)  #テストデータの目的変数Yを予測
    scores[(model_name, 'RMSE')] = np.sqrt(mean_squared_error(y_test, y_pred))
    scores[(model_name, 'MAE')] = mean_absolute_error(y_test, y_pred)
    scores[(model_name, 'MAPE')] = mean_absolute_percentage_error(y_test, y_pred)
    scores[(model_name, 'R2')] = model.score(X_test,y_test)

# 出力
pd.Series(scores).unstack()

 

以下、実行結果です。

 

AdaBoostだけの結果を見ます。

  • MAE:2.14
  • MAPE:0.12
  • R2:0.87
  • RMSE:3.29

R2は決定係数です。

RMSE二乗平均平方根誤差、Root Mean Squared Error)とMAE平均絶対誤差、Mean Absolute Error)、MAPE平均絶対パーセント誤差、Mean absolute percentage error)の定義は以下です。 

以下の記号を使い精度指標の説明をします。

  • y_i^{actual} ・・・i番目の実測値
  • y_i^{pred} ・・・i番目の予測値
  • \overline{y^{actual}} ・・・実測値の平均
  • n ・・・実測値・予測値の数

■ 二乗平均平方根誤差RMSE、Root Mean Squared Error)

\sqrt{\frac{1}{n}\sum_{i=1}^n(y_i^{actual}-{y_i^{pred}})^2}

■ 平均絶対誤差MAE、Mean Absolute Error)

\frac{1}{n}\sum_{i=1}^n|y_i^{actual}-{y_i^{pred}}|

■ 平均絶対パーセント誤差MAPE、Mean absolute percentage error)

\frac{1}{n}\sum_{i=1}^n|\frac{y_i^{actual}-{y_i^{pred}}}{y_i^{actual}}|

 

最後に、AdaBoostモデルで予測したテストデータの目的変数の予測値と実測値の散布図(横:予測値、縦:実測値)を出力してみます。

以下、コードです。

# AdaBoost散布図
y_pred = model.predict(X_test)
plt.scatter(y_test, y_pred)
plt.xlabel("True(y_test)")
plt.ylabel("Predicted(y_pred)")
plt.grid(True)
plt.show()

 

以下、実行結果です。

 

Boston house prices|特徴量選択(変数選択)

FeatureWiz特徴量選択(変数選択)絞り込んだ特徴量(説明変数)を使った場合です。

では、FeatureWizを使い、自動特徴量エンジニアリングを実施します。

以下、コードです。

# 目的変数Y(ターゲット)の変数名
target = 'medv'

# 自動特徴量エンジニアリング実行
features = featurewiz(df_train,
                      target,
                      corr_limit=0.80, 
                      dask_xgboost_flag=False, 
                      nrows=None, 
                      verbose=1,
                      test_data=df_test
                     )

 

dask_xgboost_flag」を「True」にすると、Daskを使った高速化ができるようになります。データ行(サンプル数)が多いデータのとき、「nrows」にサンプリング数を指定すると、そのサンプル数で特徴量エンジニアリングが自動で実施されます。このようにして、大容量データに対し処理を高速化することができます。

以下、実行結果です。

 

特徴量(説明変数)13から11に減りました。

 

モデル構築したり予測精度検証したりするには、選択した特徴量(説明変数)のみのデータセットが必要になりますので、準備します。

以下、コードです。

# 選択した特徴量のみのデータ
df_train_selected, df_test_selected = features

 

それぞれのデータセットの中を見てみます。

学習データを見てみます。以下、コードです。

# 学習データ(選択した特徴量+説明変数Y)
df_train_selected.head()

 

以下、実行結果です。

 

テストデータを見てみます。以下、コードです。

# テストデータ(選択した特徴量)
df_test_selected.head()

 

以下、実行結果です。

 

Boston house prices|学習とテスト(選択特徴量を利用)

特徴量(説明変数)目的変数に分けます。

以下、コードです。

# 学習データとテストデータ
X_train = df_train_selected.drop(columns=["medv"])
y_train = df_train_selected["medv"]
X_test = df_test_selected
y_test = df_test["medv"]

 

このデータを使い、学習データでモデル構築し、テストデータで構築したモデルの検証を、実施します。

以下、コードです。

# 結果を格納するハコ
scores = {} 

# 予測モデル構築と精度検証
for model_name, model in models.items():
    model.fit(X_train, y_train)     #学習データで予測モデル構築
    y_pred = model.predict(X_test)  #テストデータの目的変数Yを予測
    scores[(model_name, 'RMSE')] = np.sqrt(mean_squared_error(y_test, y_pred))
    scores[(model_name, 'MAE')] = mean_absolute_error(y_test, y_pred)
    scores[(model_name, 'MAPE')] = mean_absolute_percentage_error(y_test, y_pred)
    scores[(model_name, 'R2')] = model.score(X_test,y_test)

# 出力
pd.Series(scores).unstack()

 

以下、実行結果です。

 

AdaBoostだけの結果を見ます。

  • MAE:2.14 → 2.15
  • MAPE:0.12 → 0.12
  • R2:0.87 → 0.87
  • RMSE:3.29 → 3.26

が、データセットの全ての特徴量(説明変数)を使った予測精度です。

が、FeatureWizで特徴量選択(変数選択)し絞り込んだ特徴量(説明変数)を使った予測精度です。

 

最後に、AdaBoostモデルで予測したテストデータの目的変数の予測値と実測値の散布図(横:予測値、縦:実測値)を出力してみます。

以下、コードです。

# AdaBoost散布図
y_pred = model.predict(X_test)
plt.scatter(y_test, y_pred)
plt.xlabel("True(y_test)")
plt.ylabel("Predicted(y_pred)")
plt.grid(True)
plt.show()

以下、実行結果です。

 

通信会社の顧客離反(分類問題)

通信会社の顧客離反|データセットの概要

通信会社の顧客の離反データ(Kaggleのサンプルデータ)を使います。以下のKaggleのページからCSVファイルをダウンロードしてお使いください。

Telco Customer Churn

目的変数Yは「離反」(Churn)です。

 

通信会社の顧客離反|ライブラリー読み込み

先ずは、ライブラリーの読み込みです。

以下、コードです。

# ライブラリーの読み込み
import pandas as pd
import numpy as np

from featurewiz import featurewiz

from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import AdaBoostClassifier

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import OrdinalEncoder
from sklearn.preprocessing import LabelEncoder

from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
from sklearn.metrics import plot_confusion_matrix

 

通信会社の顧客離反|データセット読み込みと前処理

データセットを読み込みます。

以下、コードです。

# データセットの読み込み
data_path = 'C:\dataset\WA_Fn-UseC_-Telco-Customer-Churn.csv'
df = pd.read_csv(data_path, index_col="customerID")

df.head() #確認

 

PCCドライブにあるフォルダ「dataset」に、「WA_Fn-UseC_-Telco-Customer-Churn.csv」というファイル名で格納した場合の例です。

以下、実行結果です。

 

データ状況を確認します。

以下、コードです。

# データセットの状況の確認
df.info()

 

以下、実行結果です。

 

TotalCharge」変数を数値型へ型変換し、「SeniorCitizen」の値変換(1:Yes、0:No)します。

以下、コードです。

# TotalChargesを型変換(数値型へ)
df.TotalCharges = pd.to_numeric(df.TotalCharges,
                                errors='coerce')

# SeniorCitizenの値変換(1:Yes、0:No)
df = df.replace({'SeniorCitizen':{1:'Yes', 0:'No'}})

df.head() #確認

 

以下、実行結果です。

 

再度、データ状況を確認します。

以下、コードです。

# データセットの状況の確認
df.info()

 

以下、実行結果です。

 

欠測データがありそうなので、確認してみます。

以下、コードです。

# 欠測状況の確認
df.isnull().sum()

 

以下、実行結果です。

 

欠測のある行を削除します。

以下、コードです。

# 欠測値のある行を削除する
df = df.dropna(how='any')

 

再度、欠測状況を確認します。

以下、コードです。

# 欠測状況の確認
df.isnull().sum()

 

以下、実行結果です。

 

次に、モデル構築用の学習データと、構築したモデルを検証するテストデータに分割します。

以下、コードです。

# データセットの分割(学習データとテストデータ)
df_train, df_test = train_test_split(df,
                                     test_size=0.3,
                                     random_state=123)

 

量的データ(ニューメリカルデータ)標準化します。学習データ標準化モデルを作り、それを学習データテストデータに適用します。

以下、コードです。

# 標準化
scaler = StandardScaler()

df_train[df_train.select_dtypes("number").columns] = scaler.fit_transform(df_train.select_dtypes("number"))

df_test[df_test.select_dtypes("number").columns] = scaler.transform(df_test.select_dtypes("number"))

 

この学習データテストデータを使って、モデル構築精度検証を実施していきます。

 

通信会社の顧客離反|モデル設定

今回は、以下の3つの数理モデルを構築しますので、その設定を実施します。

  • 決定木(tree): DecisionTreeClassifier
  • ランダムフォレスト(RandomForest): RandomForestClassifier
  • アダブースト(AdaBoost): AdaBoostClassifier

以下、コードです。

# 構築するモデル設定
models = {
    'tree': DecisionTreeClassifier(random_state=123),
    'RandomForest': RandomForestClassifier(random_state=123),
    'AdaBoost': AdaBoostClassifier(RandomForestClassifier(random_state=123),
                                   learning_rate=0.1,
                                   n_estimators=100,
                                   random_state=123)
}

 

Boston house prices|学習とテスト(全ての特徴量を利用)

データセットの全ての特徴量(説明変数)を使った場合です。

特徴量(説明変数)目的変数に分け、質的データ(カテゴリカルデータ)を数値ラベル値に変換します。

以下、コードです。

# 学習データとテストデータ
X_train = df_train.drop(columns=["Churn"])
y_train = df_train["Churn"]
X_test = df_test.drop(columns=["Churn"])
y_test = df_test["Churn"]

# 目的変数Yを数値ラベル化
labEnc = LabelEncoder()
y_train = labEnc.fit_transform(y_train)
y_test = labEnc.transform(y_test)

# カテゴリカル変数をOrdinalEncoderで数値ラベルに変換
ordEnc = OrdinalEncoder()
X_train[X_train.select_dtypes("object").columns] = ordEnc.fit_transform(X_train.select_dtypes("object"))
X_test[X_test.select_dtypes("object").columns] = ordEnc.transform(X_test.select_dtypes("object"))

 

このデータを使い、学習データモデル構築し、テストデータ構築したモデルの検証を、実施します。

以下、コードです。

# 結果を格納するハコ
scores = {} 

# 予測モデル構築と精度検証
for model_name, model in models.items():
    model.fit(X_train, y_train)     #学習データで予測モデル構築
    y_pred = model.predict(X_test)  #テストデータの目的変数Yを予測
    scores[(model_name, 'accuracy')] = model.score(X_test,y_test)
    scores[(model_name, 'precision')] = precision_score(y_test,y_pred)
    scores[(model_name, 'recall')] = recall_score(y_test,y_pred)
    scores[(model_name, 'f1')] = f1_score(y_test,y_pred)
    
# 出力
pd.Series(scores).unstack()

 

以下、実行結果です。

 

AdaBoostだけの結果を見ます。

  • accuracy(正答率):0.78
  • f1:0.55
  • precision(適合率):0.71
  • recall(再現率):0.26

Precisionは「正(例:離反)と予測したものが、どれだけ正しかったか?」を見る指標で、Recallは「実際に正(例:離反)であったもののうち、どれだけ正(例:離反)と予測できたか?」を見る指標で、F1PrecisionとRecallの調和平均です。

PrecisionRecallがトレードオフの関係があるので、そのバランスをとるときに採用する指標f1になります。

 

最後に、AdaBoostモデルで予測したテストデータの目的変数の予測値と実測値のCofution Matrix混同行列)を出力してみます。

以下、コードです。

# AdaBoost confusion matrix
plot_confusion_matrix(model, X_test, y_test)

 

以下、実行結果です。

 

通信会社の顧客離反|特徴量選択(変数選択)

FeatureWiz特徴量選択(変数選択)し絞り込んだ特徴量(説明変数)を使った場合です。

FeatureWizを使い、自動特徴量エンジニアリングを実施します。

以下、コードです。

# 目的変数Y(ターゲット)の変数名
target = 'Churn'

# 自動特徴量エンジニアリング実行
features = featurewiz(df_train,
                      target,
                      corr_limit=0.70, 
                      dask_xgboost_flag=False, 
                      nrows=None, 
                      verbose=1,
                      test_data=df_test
                     )

 

以下、実行結果です。

 

特徴量(説明変数)19から15に減りました。

 

モデル構築したり予測精度検証したりするには、選択した特徴量(説明変数)のみのデータセットが必要になりますので、準備します。

以下、コードです。

# 選択した特徴量のみのデータ
df_train_selected, df_test_selected = features

 

それぞれのデータセットの中を見てみます。

学習データを見てみます。以下、コードです。

# 学習データ(選択した特徴量+説明変数Y)
df_train_selected.head()

 

以下、実行結果です。

 

テストデータを見てみます。以下、コードです。

# テストデータ(選択した特徴量)
df_test_selected.head()

 

以下、実行結果です。

 

通信会社の顧客離反|学習とテスト(選択特徴量を利用)

特徴量(説明変数)目的変数に分けます。

以下、コードです。

# 学習データとテストデータ
X_train = df_train_selected.drop(columns=["Churn"])
y_train = df_train_selected["Churn"]
X_test = df_test_selected
y_test = LabelEncoder().fit_transform(df_test.Churn)

 

このデータを使い、学習データでモデル構築し、テストデータで構築したモデルの検証を、実施します。

以下、コードです。

# 結果を格納するハコ
scores = {} 

# 予測モデル構築と精度検証
for model_name, model in models.items():
    model.fit(X_train, y_train)     #学習データで予測モデル構築
    y_pred = model.predict(X_test)  #テストデータの目的変数Yを予測
    scores[(model_name, 'accuracy')] = model.score(X_test,y_test)
    scores[(model_name, 'precision')] = precision_score(y_test,y_pred)
    scores[(model_name, 'recall')] = recall_score(y_test,y_pred)
    scores[(model_name, 'f1')] = f1_score(y_test,y_pred)
    
# 出力
pd.Series(scores).unstack()

 

以下、実行結果です。

 

AdaBoostだけの結果を見ます。

  • accuracy(正答率):0.78 → 0.77
  • f1:0.56 → 0.54
  • precision(適合率):0.71 → 0.67
  • recall(再現率):0.46 → 0.44

が、データセットの全ての特徴量(説明変数)を使った予測精度です。

が、FeatureWizで特徴量選択(変数選択)し絞り込んだ特徴量(説明変数)を使った予測精度です。

 

最後に、AdaBoostモデルで予測したテストデータの目的変数の予測値と実測値のCofution Matrix混同行列)を出力してみます。

以下、コードです。

# AdaBoost confusion matrix
plot_confusion_matrix(model, X_test, y_test)

 

以下、実行結果です。

 

まとめ

今回は、「大容量データにも対応した特徴量エンジニアリングライブラリーFeatureWiz(Python)」のお話しを、一般的な使い方を説明しながら、大容量データセットに対する場合にどうすればいいのかを、簡単に説明しました。

基本、FeatureWiz特徴量選択(変数選択)機能だけを使いました。

ちなみに、FeatureWiz特徴量生成機能を使い特徴量を増やし実施してみましたが、膨大な特徴量が新たに作成され、結果的に非常に時間が掛かりました

いくつかのサンプルデータで試しましたが、劇的に精度が良くなることはありませんでした。ただ、良くなる可能性はあります。別の機会にお話しします。