奇人凡人が何か書くブログ

趣味とか投資とか、twitterに書きにくいことをこちらに書きます

【sklearnで機械学習】【分類】  プログラミングの話episode3

h05torです。
仕事の方でもちょっとした趣味としてもAIに触れおりまして、その中でPython機械学習ライブラリであるsklearnについて備忘録も兼ねて記事にしてみました。

sklearnといっても色々な関数が含まれており、まだほんの一部しか触れられていませんが、今回はロジスティック回帰による「分類」について
コードを貼るだけのページです。

・ライブラリのインポート

#エクセルやcsvファイルからデータをDataFrameとして読み込むためのライブラリ
import pandas as pd
#ロジスティック回帰
from sklearn.linear_model import LogisticRegression
#訓練データとテストデータに分割する
from sklearn.model_selection import train_test_split
#実際の分類に対し予測した分類がどのようになっているかを表す対照表を出力、学習モデルの正解率を計算
from sklearn.metrics import confusion_matrix,accuracy_score
#学習モデルを保存
import joblib

from sklearn * とすればsklearnに入っている関数などをまとめてインポートすることもできますが、その場合毎回例えばsklearn.model_selection.train_test_splitみたいな感じで長くなって長くなるので、sklearnに入っている関数のうち、使用するものだけをインポートしています。
インポート自体は最初にする必要はなく使用する箇所の前であれば後からでも可能です
・データの読み込み

#csvの場合
df = pd.read_csv('data.csv')
#csvの場合(上ので文字化けする場合)
df = pd.read_csv('data.csv',encoding="cp932")
#エクセルの場合
df = pd.read_excel('data.xlsx',sheet_name="Sheet1")

#DataFrameの上から10行を表示
print(df.head(10))

エクセルとcsvファイルの場合で少し異なります。
また、読み込んだデータは変数dfに格納されているのでprintで確認できます。(JupyterNotebookの場合はコード書くスペースの最後であればprint無しでdf.head(10)で可)

・読み込んだデータの加工

#列の追加(ここではaという列を追加し、a列のすべての行にAという値を入れる)
df["a"] = "A"
#列の削除(ここではbという列とcという列を削除)
df = df.drop(columns=['b','c'])
#列名の変更(列xをXに、列yをYに改名)
df = df.rename(columns={'x': 'X','y': 'Y'})
#列の並び替え(a,d,e,X,Y,Z,s,t,rの順にする)
df = df1.reindex(columns=['a','d','e','X','Y','Z','s','t','r'])
#列Zがnanの行を削除
df = df.dropna(subset=['Z'])
#特定の条件の行のみを抜き出し代入する(列Xが10以上の行を抜き出す)
df = df[df['X']>=10]
#行、列の範囲を指定して取得(この場合全範囲を取得)
df = df.iloc[:,:]
#加工後のデータをcsvに出力
df.to_csv('data2.csv',index=False)
#csvに出力したデータを再読み込み
df = pd.read_csv('data2.csv')

特定の条件の行を抜き出すところで、除外された行のインデックス番号が飛ぶため、この後にfor文で操作を行う際に問題が生じるため、
私は一旦csvに落として、再度read_csvで読み込んでインデックス番号を再構成しております。(もしかしたらもっと良い方法あるかも)

・データの分析

#列Zの各要素の出現回数(多い順に要素と回数を表示)
print(df['Z'].value_counts(sort=True))
#数値が入っている各列について、平均値、標準偏差、中央値などが確認できる
print(df.describe())
#

・ダミー変数化(文字列が入っている列Z,sをダミー変数化する)

df=pd.get_dummies(df, columns=["Z","s"])

機械学習は数値ベースで行われるため、文字列で構成される列はダミー変数を定義する必要があり、例えば列Zに存在する文字列がmとnの2種類であれば、
列Zが削除される代わりに列Z_mとZ_nが生成されある行で列Z_mには、列Zがmだった場合は1、nだった場合は0が入ります。
要するにダミー変数はその文字列であったかどうかなので1か0が入ります。

・訓練/テストデータの分割

#説明変数
X = df.iloc[:,1:100]
#目的変数(AAA、BBB、CCCに対し、それぞれ数値0,1,2を割り当てる)
y = df['a'].map({'AAA':0, 'BBB':1, 'CCC':2})
#訓練データとテストデータに分割する
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.3,random_state=30,stratify=y)

データの分割時の引数について、test_size=0.3は訓練データとテストデータを7:3の割合で分割していることを表します。状況にもよりますが、0.2~0.3ぐらいが適当です。
random_stateは乱数種で、同じ値だと毎回同じように分割されます。
stratifyは目的変数yを指定することで、訓練データとテストデータそれぞれの目的変数のデータの値の比率が一致するように分割されます。

・ロジスティック回帰法で学習

#学習モデルの定義
clf = LogisticRegression(penalty='l1',solver='liblinear',random_state=5)
#訓練データより、説明変数と目的変数をフィッティングさせる。ここで学習が行われる。
clf.fit(X_train,y_train)

・学習モデルの評価

#訓練データでフィッティングした結果と実際の値(y_train)がどのくらい合っているか正答率を表示
print('Train Accuracy:\n',clf.score(X_train,y_train))
#テストデータの説明変数について学習モデルを適用し、予測値を計算
pred = clf.predict(X_test)
#テストデータの実際の値(y_test)に対し、予測値がどのくらい合っているか正答地を表示
print('Test Accuracy:\n',accuracy_score(y_test,pred))
#混同行列を表示(実際の値y_testに対し予測値がどのような値になっているかを表示)
print('Confusion Matrix:\n',confusion_matrix(y_test,pred))

まず、訓練データを用いてフィッティングしたモデルが訓練データの目的変数に対してどの程度当たっているかを確認します。
これの値が低いとそもそもうまくフィッティングできていないので、学習モデルを他のものにする、パラメーターをいじる、そもそも学習に使用したデータの品質が低いなどが考えられます。
次にテストデータに対して学習モデルを適用し、訓練データ以外のデータに対してもちゃんと適用できているかを確認します。
一般にはテストデータに対する正答率の方が多少低くなりますが、訓練データに対する正答率に対してあまりにも低いと訓練データに過剰適合(過学習)になっており、データの分割時に
訓練データ:テストデータの比が99:1とかになっているとそのようになりやすいため、70:30~80:20ぐらいが良く、上の方でtest_size=0.2~0.3が適切としたのはそのためです。
混同行列を出力すると↓のような表が表示されます。行が実際の値で列が予測値です。合っていれば左上から右下に向かって対角線が引けます。

confusionmatrix
confusionmatrix


基本的には予測候補のなかから最も確率が高いと判定したものを予測値として出しているのですが、以下のようなコードで第2候補、第3候補も確認することができます。
テストデータ全部に対して予測値(第1候補)、第2候補、第3候補を出力していきます

#テストデータを第3候補までのスコアを見る
count=0
correct=0
correct2=0
correct3=0
for i in range(len(y_test)):
    X_test_=X_test.iloc[i:i+1,:]
    #print(type(X2))
    pred_2=clf.predict(X_test_)
    pred_2_proba=clf.predict_proba(X_test_)
    pred_2_proba=pred_2_proba[0]
    pro=[]
    for j in range(len(pred_2_proba)):
        pro.append([round(pred_2_proba[j],4),j])
    pro.sort(reverse=True)


    if pred_2[0]==y_test["a"][i]:
        correct+=1
    if pred_2[0]==y_test["a"][i] or pro[1][1]==y_test["a"][i]:
        correct2+=1
    if pred_2[0]==y_test["a"][i] or pro[1][1]==y_test["a"][i] or pro[2][1]==y_test["a"][i]:
        correct3+=1
        
    count+=1
    print("予測値:{}".format(pred_2[0]),"第2候補:{}".format(pro[1][1]),"第3候補:{}".format(pro[2][1]),"実際の値:{}".format(y_test["a"][i]),i)


print("正解率:{}%".format(100*correct/count))
print("第2候補までの正解率:{}%".format(100*correct2/count))
print("第3候補までの正解率:{}%".format(100*correct3/count))

・モデルの保存

#モデルの保存
filename = 'model.sav'
joblib.dump(clf, filename)

モデルをsavファイルで保存しておけば、他のファイルで読み込むことができ、学習モデルを利用して本格的な運用に使うことができるようになります。

                                                • -

この記事よりもちゃんとしたコードはネットで探せばいくらでも落ちていますが、とりあえず備忘録ということでご容赦ください。