機械学習をやってみる話
前回
第2章は「機械学習」
前回の顧客データを使って機械学習を行う。
機械学習には、
クラスタリング:同じような特徴ベクトルに分類すること
分類:すでに分類されているものを分析して、新たな対象がどの分類に属するか予想すること
回帰:特徴ベクトルが時系列の場合にその時系列パターンを予想すること
すでに分類、予測する方法が決まっている分類と回帰を「教師あり学習」、分類すること自体を決める「教師なし学習」という。
長いので目次作った。
【類似度計算】
pd.DataFrame[].drop(消去の対象, axis=)
DataFrameの対象の部分を消去する。
axis=0で行、axis=1で列を消去
#データ読み込み
df_info = pd.read_csv("accomodation_info.csv", index_col=0, parse_dates=[0])
#毎月の利用回数を特徴ベクトルとする。
x_0 = df_info.resample("M").count()#月別利用回数
x_0 = x_0.drop(x_0.columns.values, axis=1)#カラムの列を全て消去(つまりインデックスだけになる)
pd.concat([データ1,データ2],axis=).fillna(埋める数字)
データ1と2を連結、axisはつなげる向き。
fillna()は欠損値NaNに数字を入れる。
#順位の決定
i_rank = 1
j_rank = 2
#顧客IDの抽出
i_id = df_info["顧客ID"].value_counts().index[i_rank]#1位のID
j_id = df_info["顧客ID"].value_counts().index[j_rank]#2位のID
#月ごとの利用回数を特徴量として算出
x_i = df_info[df_info["顧客ID"]==i_id].resample("M").count()
x_j = df_info[df_info["顧客ID"]==j_id].resample("M").count()
#欠損値があった場合の穴埋め→0の月があったときにって感じ
x_i = pd.concat([x_0,x_i],axis=1).fillna(0) #X_0とX_1を連結 axis=0で縦方向、1で横方向 fillnaでNaNに0を入れる。
x_j = pd.concat([x_0,x_j],axis=1).fillna(0)
iloc[行のインデックス,列のインデックス]
特定の列(or行)を抽出
np.linalg.norm():ノルム計算
デフォルトではL2ノルムを計算する。
#類似度計算
dx = x_i.iloc[:,0].values - x_j.iloc[:,0].values #0列目を抽出して引く(0列目以降も値は同じだけどね)
#ノルム計算
n = np.linalg.norm(dx) #ノルム計算のデフォルトはL2ノルム
#次元による正規化
num_dim = len(x_i)
d = n/num_dim
まずは2つのデータで類似度を計算したが、こんな感じで多次元の特徴ベクトルがデータ数分ある。
次元数が多いとグラフ(二次元)で書けないので、"次元削減"が必要になる。
【次元削減】
主成分分析
今回は「主成分分析」を用いる
主成分分析は多次元の特徴ベクトルのばらつきが最大になる次元を決める次元削減法。
まずは特徴ベクトルの作成
#空のリスト
list_vector = []
#人数設定
num = 100
for i_rank in range(num):
i_id = df_info["顧客ID"].value_counts().index[i_rank]#ID抽出
x_i = df_info[df_info["顧客ID"] == i_id].resample("M").count()#月ごとの利用回数
x_i = pd.concat([x_0, x_i], axis=1).fillna(0)#穴埋め
list_vector.append(x_i.iloc[:,0].values.tolist())#リストに追加(シリーズなのでtolistで変換)
シリーズをリストに打ち込むときはvalues.tolist()を使う。らしい。
主成分分析は
from sklearn.decomposition import PCA
を使う。
※モジュールをインストールする時、「sklearn」をインストールするとうまくいかない。(というか入れるなというふうに書いている)
「scikit-learn」を入れること!
#特徴ベクトル
features = np.array(list_vector) #リストを行列に
#主成分分析
pca = PCA()
pca.fit(features)
主成分分析でベクトルが決まったら、それに合わせて特徴ベクトルを変換する。
#特徴ベクトルを主成分ベクトルに変換
transformed = pca.fit_transform(features)
【クラスタリング】
k-means法
上で作成した特徴ベクトルを、クラスタリングする。今回はk-means法を用いる。
k-means法は
①適当にクラスタを割り当てる。
②各クラスタの重心を決める
③各点の所属するクラスタを一番近い重心のクラスタに変更する。
④①〜③をくりかえすといい感じに分けられる。らしい。
from sklearn.cluster import KMeans
こうして、
#k-means法
#クラスター数の決定
num_of_cluster = 4
#クラスタリング
model = KMeans(n_clusters=num_of_cluster, random_state=0,)
model.fit(features)
pred_class = model.labels_
こう。すごいなーパソコンくん。
【分類アルゴリズム】
未知のデータが、k-means法で分類したクラスターのどれに分類されるか予想する。
決定木
分類アルゴリズムの一つ、決定木を使う。
この〜木なんの木気になる気になる〜♪っていうCM昔あったよね、あれなんのCMだったんだろ。
決定木はある一つの次元で2つに分類するという動作を繰り返すことでモデルを作る
なお、可視化するのに使うdtreevizでめちゃくちゃ苦戦した。
#分析したいクラスターを決定
target_class = 1
#目的変数の決定
num = len(pred_class)
#クラスター1以外は0の行列作成
data_o = np.zeros(num)
for i in range (num):
if pred_class[i]==target_class:
data_o[i] = True
else:
data_o[i] = False
#説明変数の決定
data_e = features
#決定木モデルの構築
clf = DecisionTreeClassifier(max_depth=2)
#max_depthを上げるとより細かく分類できるので、スコアも上がる
clf = clf.fit(data_e, data_o)
# indexの抽出
x_0 = df_info.resample('M').count()
x_0 = x_0.drop(x_0.columns.values,axis=1)
time_index = x_0.index
#注意!dtreevizのバージョンを1.4.1にすること!
#graphvizのPATHを通す
import os
os.environ["PATH"] += os.pathsep + '/opt/homebrew/bin'
from dtreeviz.trees import dtreeviz
# 決定木を描画
m = dtreeviz(clf,
data_e,
data_o,
target_name='Class',
feature_names=time_index,
class_names=['False','True'],)
m.view()
テスト用分けてなかった。
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(features,data_o) #いつもの。
#決定木モデルの構築
clf = DecisionTreeClassifier(max_depth=2)
clf = clf.fit(x_train,y_train)
pred_tree = clf.predict(x_test)
#スコアと混同行列
score = clf.score(x_test, y_test) #何%合ってるか
cm = confusion_matrix(y_test, pred_tree) #混同行列([TP,FN],[FP,TN])
print(score,cm)
これ、毎回分割がランダムだから結果変わるんだね、、
大体5−60%くらい?なんか本よりだいぶ低い、、、
max depth増やしても7-80%くらいなのか、、、
ランダムフォレスト
一次元ごとに分類する決定木を集めて複数次元の分類を組み合わせて予測する分類器
特徴量の重要度がわかるところがメリット
#ランダムフォレスト
from sklearn.ensemble import RandomForestClassifier
RFC_model = RandomForestClassifier(bootstrap=True, n_estimators=10, max_depth=None, random_state=1)
clf = RFC_model.fit(x_train,y_train)
# 特徴量の重要度を出力
importances = clf.feature_importances_
# 特徴量の重要度をグラフに出力
n_features = len(time_index)
plt.figure(figsize=(12, 8))
plt.barh(range(n_features), importances , align='center')
plt.yticks(np.arange(n_features), time_index)
plt.show()
サポートベクトルマシ(SVM)
多次元の中で空間的に分類する方法
これが一番精度高かった。
【回帰】
svmを用いて時系列データを曲線にフィットさせて、時系列予測を行う。
np.stack():リスト同士を行列に結合
np.arrange(A,B):A~Bの間の整数のリストを作成
np.ravel():多次元配列を一次元配列に
data_target = data_e[data_o==1]
data_y = data_target
data_x = np.stack([np.arange(0,len(data_target[0])) for _ in range(len(data_target))], axis=0) #[0〜データ個数]の行列をデータ個数分作って行列化
#多次元配列を一次元配列に
data_y = np.ravel(data_y)
data_x = np.ravel(data_x)
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(data_x,data_y)
#SVM回帰
from sklearn.svm import SVR
svm_model = SVR(kernel="rbf", C=1)#Cは正規化の強度
reg = svm_model.fit(x_train.reshape(-1,1),y_train.reshape(-1,1))
#予測曲線
x_pred = np.arange(len(data_target[0])).reshape(-1,1)
y_pred = svm_model.predict(x_pred)
plt.plot(data_x,data_y, "k.")
plt.plot(x_pred,y_pred, "r.-")
plt.show()
reg.score(x_test.reshape(-1,1), y_test.reshape(-1,1))
ちょっと最後は置いていかれた。
svmについての説明はあんまりなかったから他で勉強するか、、、
なお、カーネル関数についてよくわからなかったのでGPTに聞いてみた
カーネル関数は、SVM(サポートベクターマシン)において用いられる関数で、データの非線形分類や回帰を実現するための手法です。
SVMは、線形分類器として知られており、2つのクラスを分離する超平面を見つけることによって、データを分類します。しかし、データが複雑な形をしている場合や、データの次元数が非常に高い場合、線形分離することが困難な場合があります。そこで、カーネル関数を用いて、データを高次元の特徴空間に写像することで、線形分離することが可能になります。
カーネル関数は、2つのデータ点間の距離を計算する関数であり、主に以下の3種類が用いられます。
1. 線形カーネル関数
- 2つのデータ点の内積を計算する関数
- 線形分類に用いられる2. 多項式カーネル関数
- 線形カーネル関数に次数を付加した関数
- 非線形分類に用いられる3. RBFカーネル関数(ガウシアンカーネル関数)
- 2つのデータ点の距離をガウス分布で表した関数
- 非線形分類に用いられるこれらのカーネル関数は、SVMの訓練時に用いられ、データを高次元の特徴空間に写像することで、非線形分類や回帰を実現します。
よくわかんねえ。まぁこの辺はまた次の機会に、、、