#1.機械学習の処理の手順
大まかな処理の流れは以下の8つのステップに分かれる
# | ステップ | 処理 | 使用ツール・ライブラリ |
---|---|---|---|
1 | データ入手 | スクレイピング、データの読み込み | Requests、BeautifulSoup、NumPy、Pandas |
2 | データ加工 | 前処理(欠損値の対応、データの整形、データの連結処理) | NumPy、Pandas |
3 | データ可視化 | データの可視化 | Matplotlib、Seaborn、Pandas |
4 | アルゴリズム選択 | 教師あり学習(分類・回帰)、教師なし学習(クラスタリング) | scikit-learn(train_test_split, SVM, DecisionTreeClassifier, RandomForestClassifier, LinearRegression, LogisticRegression, PCA, KMeans, AgglomerativeClustering) |
5 | 学習プロセス | ハイパーパラメータの最適化 | scikit-learn(train_test_split, GridSearchCV) |
6 | 精度評価 | ROC曲線、AUCの導出 | scikit-learn(roc_curve, roc_auc_score, classification_report) |
7 | 試験運用 | (割愛) | - |
8 | 結果利用 | (割愛) | - |
各ステップで使用するライブラリとその使い方を解説
##1-1.データ入手
###1-1-1.スクレイピング
####1-1-1-1.HTMLのtable要素の内容を取得(Pandas)
import pandas as pd
url = "https://example.com"
tables = pd.read_html(url)
df = tables[1] #サイト内の2つ目の表を抽出
df.head(20) #上位20項目を表示
####1-1-1-2.HTMLの任意の要素の内容を取得(Requests)
import requests
r = requests.get("https://example.com")
text =r.text #要素のInnerTextを取得
for line in text.split('\n'): #要素を改行して分割表示
if '</th>' in line or '</td>' in line:
print(line.strip()) #ブランク、空白文字を削除して出力
####1-1-1-3.HTMLを構文解析し、要素を取得する(BeautifulSoup)
import requests
from bs4 import BeautifulSoup
r = requests.get('')
soup = BeautifulSoup(r.text, 'html.parser')
###1-1-2.データの読み込み
import pandas as pd
#data変数に格納されているDataFrame化
df = pd.DataFrame(data)
#CSVファイルの読み込み
df = pd.read_csv("pokemon_galar.csv") #書き込みの場合はpd.to_csvを使用
#Excelファイルの読み込み
df = pd.read_excel("pokemon_galar.xlsx") #書き込みの場合はpd.to_excelを使用
#DataFrameファイルの読み込み
df = pd.read_pickle("pokemon_galar.pickle") #書き込みの場合はpd.to_pickleを使用
##1-2.データ加工
###1-2-1.前処理
####1-2-1-1.欠損値の対応
#####各要素が欠損値かどうかをBool値で返す(isnull)
df.isnull()
#####欠損値の行を削除(dropna)
df.dropna()
#####欠損値を別の値で補完する(fillna)
#1つ手前の値で補完する
df.fillna(method='ffill')
#平均値で補完する
df.fillna(df.mean()) #中央値の場合はdf.median()、最頻値の場合はdf.mode().iloc[0, :]
#####欠損値を別の値で補完する(SimpleImputer)
from sklearn.impute import SimpleImputer #バージョン0.20以降
#平均値で補完する
imp = SimpleIimputer(strategy='mean') #中央値の場合は'median'、最頻値の場合は'most_frequent'
imp.fit(df)
####1-2-1-2.データの整形
#####条件で抽出
df["hp"] >= 100
df.loc[:, "hp"] >= 100
df.query('hp >= 100 and sp <= 90')
#####データ型の変換
#データ型を確認する
df.dtypes
#int型をfloat型に変換する
import numpy as np
df.loc[:, "hp"] = df.loc[:, "hp"].astype(np.float32)
#####並べ替え
df.sort_values(by="hp", ascending=False) #デフォルトはTrue(昇順)
#####不要なカラムの削除
df = df.drop("hp", axis=1) #axis=1で列方向、axis=0で行方向
#####カラム名の変更
df = df.rename(column={'HP': 'hp'}) #{'変更前': '変更後'}
#####組み合わせデータの挿入
df.loc[:, "hp/total"] = df.loc[:, "hp"] / df.loc[:, "total"]
#####ダミー関数
import pandas as pd
df_mod = pd.get_dummies(df.loc[:, "hp_index"], prefix="hp")
#####データ連結
import pandas as pd
#行方向のデータ連結
df_merged = pd.concat([df_1, df_2], axis=0)
#列方向のデータ連結
df_merged = pd.concat([df, df_new_column], axis=1)
#####時系列データの扱い
import pandas as pd
#1か月分のデータ
dates = pd.date_range(start="2020-01-01", end="2020-01-31")
#1年分365日のデータ
dates = pd.date_range(start="2020-01-01". periods=365)
#任意の曜日のデータ
sunday = pd.date_range(start="2020-01-01", end="2020-01-31", freq='W-SUN') #日曜日のみのデータ
#月平均のデータ
df.groupby(pd.Grouper(freq='M')).mean()
##1-3.データ可視化
###1-3-1.基本統計量
#最大値
df.loc[:, "hp"].max()
#最小値
df.loc[:, "hp"].min()
#最頻値
df.loc[:, "hp"].mode()
#平均値
df.loc[:, "hp"].mean()
#中央値
df.loc[:, "hp"].median()
#標準偏差
df.loc[:, "hp"].std(ddof=0) #デフォルトはddof=1
#件数のカウント
df[df.loc[:, "hp"]>=100].count()
#要約
df.describe()
#相関係数
df.corr()
#DataFrame(Pandas)→ndarray(Numpy)変換
df.loc[:, ["hp", "sp"]].values
###1-3-2.グラフ描画
####1-3-2-1.散布図行列
from pandas.plotting import scatter_matrix
_ = scatter_matrix(df)
####1-3-2-2.Matplotlib
#####描画オブジェクト(fig)とサブプロット(ax)
import matplotlib.pyplot as plt
#2つのサブプロットを配置
fig, axes = plt.subplots(2)
plt.show()
#2行2列(4つ)のサブプロットを配置
fig, axes = plt.subplots(2, 2)
fig, axes = plt.subplots(nrows=2, ncols=2)
plt.show()
#####タイトル
import matplotlib.pyplot as plt
fig, axes = plt.subplots(ncols=2)
#描画オブジェクトにタイトル設定
fig.subtitle('figure title')
#サブプロットにタイトル設定
axes[0].set_title('subplot title 0')
axes[1].set_title('subplot title 1')
plt.show()
#####軸ラベル
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_xlabel('x label') #X軸にラベルを設定
ax.set_ylabel('y label') #Y軸にラベルを設定
plt.show()
#####凡例
import matplotlib.pyplot as plt
ax.plot(x, y, label='legend label')
ax.legend(loc='best') #データとの重なりが最小の場所に凡例を表示
plt.show()
#####ファイル出力
import matplotlib.pyplot as plt
fig.savefig('figure.png') #ファイル形式はpng, pdf, eps, svgが選択可能
####1-3-3-3.各種グラフの描画
#####折れ線グラフ
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x = df["hp"]
y = df["total"]
ax.plot(x, y)
plt.show()
#####棒グラフ
(i)複数の棒グラフを並べて描画する場合
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x1 = df["No."]
y1 = df["hp"]
y2 = df["speed"]
width = 0.4
ax.bar(x1, y1, width=width) #棒グラフの幅を0.4に設定
x2 = [num + width for num in x] #幅分ずらして棒グラフ2を描画
ax.bar(x2, y2, width=width)
plt.show()
(ii)積み上げ棒グラフを描画する場合
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x = df["No."]
y1 = df["at"]
y2 = df["sp_attack"]
y_total = [num1 + num2 for num1, num2 in zip(y1, y2)]
ax.bar(x, y_total)
ax.bar(x, y2)
plt.show()
#####散布図
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.scatter(df["hp"], df["total"], marker='v', label="hp-total") #下向き三角形
ax.scatter(df["attack"], df["total"], marker='^', label="attack-total") #上向き三角形
ax.scatter(df["defense"], df["total"], marker='s', label="defense-total") #正方形
ax.scatter(df["sp_attack"], df["total"], marker='*', label="sp_attack-total") #星形
ax.scatter(df["sp_defense"], df["total"], marker='x', label="sp_defense-total") #X
ax.legend()
plt.show()
#####ヒストグラム
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x = df["total"]
n, bins, patches = ax.hist(x, orientation='vertical')#デフォルトはvertical、横向きに描画したい場合はhorizontal
#n:各ビンの度数
#bins:ビンの境界値
#patches:ビンを描画するための情報
plt.show()
#####箱ひげ図
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x0 = df["hp"]
x1 = df["attack"]
x2 = df["defense"]
ax.boxplot(x0, x1, x2)
plt.show()
#####円グラフ
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
x0 = df.loc[0, "hp"] / df[0, "total"]
x1 = df.loc[0, "attack"] / df[0, "total"]
x2 = df.loc[0, "defense"] / df[0, "total"]
status = [x0, x1, x2]
ax.pie(status, startangle=90, counterclock=False) #上から時計回り
ax.axis('equal') #アスペクト比維持
plt.show()
##1-4.アルゴリズム選択
###1-4-1.分類(教師あり学習)
####1-4-1-1.テストデータと学習データの分割
from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3) #テストデータ:学習データ = 3:7に分割
####1-4-1-2.サポートベクタマシン
#####学習
from sklearn.svm import SVC
svc = SVC(kernel='linear', C=1e6) #線形分離、マージンの狭さ10^6
svc.fit(df["attack"], df["sp_attack"])
#####可視化
####1-4-1-3.決定木
#####学習
from sklearn.model_selection import train_test_split
from sklearn.tree import DecisionTreeClassifier
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
tree = DecisionTreeClassifier(max_depth=3) #木の最大の深さを"3"に指定
tree.fit(x_train, y_train)
#####可視化
from pydotplus import graph_from_dot_data
from sklearn.tree import export_graphviz
dot_data = export_graphviz(tree,
filled=True, #多く分類されたノードに着色する
rounded=True, #ノードの角を丸くする
class_names=['ordinary', 'semi-legendary', 'legendary'])
feature_names=['hp', 'attack', 'defense', 'sp_attack', 'sp_defense', 'speed']
out_file=None) #ファイルを介さずにデータを渡す
graph = graph_from_dot_data(dot_data)
graph.write_png('tree.png')
####1-4-1-4.ランダムフォレスト
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
forest = RandomForestClassifier(n_estimators=100) #決定木の個数
forest.fit(x_train, y_train)
y_pred = forest.predict(x_test)
y_pred
###1-4-2.回帰(教師あり学習)
データの読み込み・学習
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
df = pd.DataFrame(data)
x = df.loc[:, 0]
y = df.loc[:, 1]
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3)
lr = LinearRegression()
lr.fit(x_train, y_train)
y_pred = lr.predict(x_test)
可視化
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.scatter(y_pred, y_test) #x軸を予測値、y軸を実績値として予測の精度を散布図で可視化する
ax.plot((0, 50), (0, 50), linestyle='dashed', color='red') #45度線の描画
ax.set_xlabel('predicted value')
ax.set_ylabel('actual value')
plt.show()
###1-4-2-1.次元削減
###1-4-2-2.主成分分析
from sklearn.decomposition import PCA
pca = PCA(n_components=2) #新たな2変数に変換
x_pca = pca.fit_transform(df[:, "hp"], df[:, "total"])
fig, ax = plt.subplots()
ax.scatter(x_pca[:, 0], x_pca[:, 1])
plt.show()
###1-4-5.クラスタリング(教師なし学習)
####1-4-5-1.k-means
データの読み込み・学習
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
df = pd.read_csv("data.csv")
fig, ax = plt.subplots()
ax.scatter(df.loc[:, 0], df.loc[:, 1])
km = KMeans(n_cluster=2, init=ramdom, n_init=10)
#クラスタ数:2
#初期値:ランダムに乱数で生成
#k-means実行回数:10
y_km = km.fit_predict(df)
可視化
import numpy as np
fig, ax = plt.subplots()
#クラスタ1散布図
ax.scatter(df[y_km == 0, 0], df[y_km == 0, 1], s=50, edgecolor='black', marker='s', label='cluster1')
#クラスタ1中心
ax.scatter(np.mean(df[y_km == 0, 0]), np.mean(df[y_km == 0, 1]))
#クラスタ2散布図
ax.scatter(df[y_km == 1, 0], df[y_km == 1, 1], s=50, edgecolor='black', marker='o', label='cluster2')
#クラスタ2中心
ax.scatter(np.mean(df[y_km == 1, 0]), np.mean(df[y_km == 1, 1]))
ax.legend()
plt.show()
####1-4-5-2.階層的クラスタリング(凝集型)
データの読み込み
import pandas as pd
from sklearn.cluster import AgglomerativeClustering
df = pd.read_csv("data.csv")
ac = AgglomerativeClustering(n_cluster=3, affinity='euclidean', linkage='complete')
#最大クラスタ数:3
#リンケージ手法:complete
labels = ac.fit_predict(df)
labels
可視化
import numpy as np
from scipy.cluster.hierarchy import dendrogram #樹形図を描画するライブラリ
#子クラスタとの関係を抽出
children = ac.children_
#クラスタ間の距離を抽出
distance = np.arrange(children.shape[0])
#各データの観測番号
number_of_observations = np.arange(2, children.shape[0]+2)
#子クラスタ、クラスタ間の距離、観測番号を列方向に結合
linkage_matrix = np.hstack((children,
distance[:, np.newaxis],
number_of_observations[:, np.newaxis])).astype(float)
fig, ax = plt.subplots(figsize=(15, 3), dpi=300)
dendrogram(linkage_matrix, labels=np.arange(100), leaf_front_size=8, color_threshold=np.inf)
plt.show()
##1-5.学習プロセス
###1-5-1.ハイパーパラメータの最適化
####1-5-1-1.グリッドサーチ
##1-6.精度評価
###1-6-1.カテゴリの分類精度
指標 | 説明 | 計算式 | |
---|---|---|---|
適合率(precision) | 正例と予測したデータのうち、実際に正例の割合 | 真陽性/(真陽性+偽陽性) | 偽陽性をなくしたい際に求める |
再現率(recall) | 実際の正例のうち、正例と予測したものの割合 | 真陽性/(真陽性+偽陰性) | 偽陰性をなくしたい際に求める |
F値(F-Value) | 適合率と再現率の調和平均 | 2適合率再現率/(適合率+再現率) | |
正解率(accuracy) | 正例、負例にかかわらず、予測が的中した割合 | 真陽性+真陰性/全データ |
###1-6-2.分類精度の出力
from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred))
###1-6-3.交差検証
from sklearn.svm import SVC
from sklearn.model_selection import cross_val_score
svc = SVC()
x = df.loc[;, "hp"]
y = df.loc[:, "total"]
cross_val_score(svc, x, y, cv=10, scoring='precision')
#分割数:10
#評価指標:適合率(precision)
###1-6-4.ROC曲線の可視化
import numpy as np
import matplotlib.pyplot as plt
fpr = df.loc[:, "false_positive_rate"].values()/ n1 #n1:偽陽性のデータの個数
tpr = df.loc[:, "true_positive_rate"].values()/ n2 #n2:真陽性のデータの個数
fig, ax = plt.subplots()
ax.step(fpr, tpr) #ROC曲線を描画
ax.set_xlabel('false positive rate')
ax.set_ylabel('true positive rate')
plt.show()
###1-6-5.AUC
AUCとは...ROC曲線の下側の面積の合計。1に近づくほど正例と負例の区別がつきやすくなり、0に近づくほど区別がつかなくなる。