CSV ファイルを読み込んで機械学習と予測をする 【scikit-learn】

Machine Learning

CSV ファイルを読み込んで機械学習と予測を行うサンプルプログラムについてまとめておきます。

以下の環境で動作確認をしています。
環境: Windows パソコン、Anaconda、Python 3.x (scikit-learn、pandas、numpy)のインストール済み

背景

Python を使って機械学習の技術検討、活用をしています。
画像処理や物体認識なども動かしていますが、現状の機械学習の技術レベルで、実用上すぐに使える用途といえば、測定データ、ログデータの解析、予測ではないかと思います。

例えば、ネットからのスクレイピングなどにより、天気、株価など、時系列でデータを取得します。温度、湿度などの測定デバイスがあるときは、時系列で測定したデータでもよいです。
こうして取得したデータ(ログデータ)に対し、別途、機械学習と予測を行うことで、所定時刻に自動的に判断や処理を実行する、といったケースです。
ここで、データ用のファイル(ログデータ)のフォーマットとして典型的なのは、多くは、CSV ファイルかテキストファイルとなると思います。

ところが、ネット検索をしてみると、ログデータを機械学習でどうやって扱うか、ポイントが整理されたサイトがあまり見当たらないようです。
作業内容はほとんど定型処理です。しかし、その都度、ソースコードを読み込んで、プログラムを検討しなければならないというのも効率が悪いです。

そこで、機械学習を行う場合のログデータの例とサンプルプログラムについてまとめておくことにします。
CSV ファイルを差し替えてヘッダー部分を修正すれば、任意のデータについて機械学習や予測ができるよう意図しています。
なお、機械学習のプログラムとしては、現在、最も広く使われていると思われる Python の scikit-learn を使います。Python と scikit-learn のインストールは済ませておいてください。

使い方

① パソコンに任意のフォルダを作成し、そのフォルダの中に以下の4つのテキストファイルを作成してください。4つのファイルには、末尾のサンプルコードを貼りつけて保存してください。
学習と予測の2つを実行するのであれば、いずれにせよ4つの要素(ファイル)が必要になると思います。

learn1.py  機械学習を実行する python スクリプト
機械学習の学習段階で使う。
csv1.csv  学習のための csv ファイル
入力データ(col1 ~ col8)と正解データ(col9)の両方が入っている。
predict1.py  予測用の python スクリプト
機械学習の実際の使用段階で使う。
csv2.csv  予測のための csv ファイル
入力データ(col1 ~ col8)のみが入っている。正解データ(col9)は入っていない(使用しない)。

② コンソールから、学習用のスクリプト learn1.py を実行してください。正解データの入ったファイル csv1.csv を読み込んで、機械学習を実行します。
学習後、学習済みのパラメータが保存されます。また、正解率のテストが実行され、結果が表示されます。
③ 予測用のスクリプト predict1.py を実行してください。②の段階では使用していないデータ csv2.csv を用いて、予測を行います。予測結果はファイル名 csv3.csv で保存されます。

→ うまく動いたら、CSVデータを差し替えるなど、自由にカスタマイズしてください。

スクリプトの説明

学習用データ csv1.csv について

csv1.csv は、機械学習をさせるための学習用データです。スクリプト learn1.py で使います。
添付したデータは、実際に私が取得しているログデータをそのまま抜粋したものです。

この学習用データには、機械学習に使う入力用のデータと、正解データの2つが入っています。
この例では、csv ファイルのヘッダーcol1 ~ col8 を入力用データとし、col9 を正解データとして予測させるようにしています。
プログラムを書き換えることで、どの列を入力とするか、いくつ入力するか、どの列を正解データとするか、任意に設定できます。
csv1.csv に示したようなログデータ(正解データを含む)を自動で取得していけば、(csv2.csv に示したような)正解データのない場合でも、予測ができるようになります。

学習用スクリプト learn1.py について

学習用のスクリプト learn1.py は、以下のようになっています。
・ 最初に import … として、機械学習に必要なパッケージを読み込んでいます。
・ def としたところで、機械学習に必要な各ステップを関数で定義しています。
具体的には、csv ファイルの読み込み、csv ファイルの分割((入力用データ、出力用データ) × (学習用データ、テスト用データ)に分割)、機械学習の実行(fit)、テスト用の予測と正解の評価(predict_score1)の4つを定義しています。
・ path1 としたところで、フォルダのあるパスを設定しています。以降、すべてのファイルの読み書きは、このフォルダ内で行います。
・ read_csv1 関数を実行し、上記の CSV ファイル(csv1.csv)の読み込みを行います。
・ header1 は、csv1.csv の中で、入力データにしたい列のタイトルを定義しています。header2 は、正解データとする列のタイトルです。header1 は複数の列を指定できますが、header2 は1列しか定義できません。
この設定により、CSV ファイルの中で、col1 ~ col8 の8つの列の数値を入力とし、col9 の数値を予測するよう機械学習をさせるということになります。
・ つぎに、上記の入力と出力を分割し、かつ、訓練用データとテスト用データに分割します。具体的には、読み込んだ学習用データに対し、split1() 関数を実行し、csv1.csv のデータを4つに分けます。
x1_xxxx としたものが入力データ、y1_xxxx としたものが出力データ、xx_train としたものが訓練用データ、xx_test としたものがテスト用データです。
・ 例えば、x1_train, y1_train は、機械学習の学習用に使うトレーニングデータで、x1_train を入力したら、y1_train が出力されるように(パラメータを振って)機械学習を行う、という意味です。
・ 機械学習は、fit1() とした関数で実行しています。
学習後のデータ(モデル)は model1 として取得しています。fitting_model1.sav は、学習後のデータを保存するときのファイル名です。
このファイルを保存しておき、使う段階でこの学習済みデータを読み込むことで、予測ができるようになります。
・ また、n1 = 7 としたところで、機械学習を行う際に使うモデルを指定しています。具体的にはサポートベクターマシン (n1 = 2)、ランダムフォレスト(n1 = 6, 7)などです。詳細は、def fit() で定義している部分を参照してください。
CSV データを使って予測をさせたとき、さらに精度を上げたいと思ったときは、この機械学習に使うモデルを変更してみて、取得したデータに一番あったモデルを選択するようにします。
・ その後、predict_score1() 関数で、テスト用データを使った予測と評価を行います。具体的には、学習後のモデル model1 にテスト用のデータ x2_test を与えて予測させ、正解データ y2_test と比較して、正解率 score を評価します。
・ 最後に評価結果を表示します。80% 等の正解率が出力されるようにしています。
スクリプトを実行すると、正解率がわかるようになっています。実行後、n1 = 7 等とした数値を n1 = 2, 3, 4, 5, 6 等に変えて、学習モデルを変えてみてください。正解率が変わると思います。機械学習の最初の学習段階で、学習対象(学習に使う CSV ファイル)に応じて、最適な学習モデルが変わるため、最適なモデルと学習用データを探すことになります。
サンプルコードでは、学習に必要な入力データは col1 ~ col8 としました。しかし、情報が多ければ多いほどよいとは限りません。情報が多すぎるとかえって、的確な判断には不要であるノイズが増える可能性があります。
予測をするのに必要な情報のみとなるよう列を絞ったほうが的確な判断ができる可能性があります。
また、正解を得るための情報が不足しているのであれば、さらに入力する情報を増やす、または、より適切なデータに差し替える必要があります。そこで、入力データの数 (col1 ~ col8)と種類は、増減させてみて、実際に使う段階では調整、最適化をしてください。

実用段階でのデータ csv2.csv について

csv2.csv は、実際の使用段階を想定したデータです。
具体的には、上述の学習用データで正解データのない(正解を使用しない)データとします。
かつ、csv1.csv とは内容が被らないデータ(学習済みモデルからすると、学習では用いていない初めてのデータ)になっています。

実用段階用のスクリプト predict1.py について

・ 最初に import … として、機械学習に必要なパッケージを読み込んでいます。
・ def としたところで、予測を行う関数を定義しています。具体的には、使用段階用の csv ファイル(csv2.csv)を読み込んで、入力に必要な列(col1 ~ col8)を取得します。入力用の列は、学習段階と同じ(header1が同じ)になるようそろえておく必要があります。
・ つぎに、学習段階で生成した学習済みモデル(fitting_model1.sav)を model1 として読み込みます。このモデルを使って予測 model1.predict() を行います。
予測したデータ y1 は、入力前のデータ x1 と結合して、値 xy1 を返します。
予測を行ったら、print() 関数で xy1 を表示し、予測した結果を csv ファイル csv3.csv として保存します。

うまく動いたら

・ うまく動いたら、学習に使う列の数(col1 ~ col8)を変えてみるなどしてみてください。
列を増減させた場合は、スクリプトの header1 、predict1() 内の col9 (正解とする列)を修正してください。
・ また、実際に使っている機器のログデータ、スクレイピングをした時系列データに差し替えてみてください。
・ ここで挙げているサンプルにおいて、CSV ファイルの要件としては、1行目にヘッダーを入れておく必要があります。また、学習に使うデータと、正解とするデータの列は、整数としてください。小数点を使う場合は、100倍して int() に入れるなどして、整数化してください。
・ データを差し替えたら、入力データの列の数を調整し、うまく学習できるか確認してみてください。
うまく動くようであれば、さらに、スクリプト内で設定している学習モデルを切り替えてみて、正解率が上がるかどうか確認してみてください。入力するデータの特徴に応じて、学習モデル側も切り替えて最適化することで、正解率をさらに改善できると思います。
・ 正解率が上がらないときは、入力データが足りているかどうか、欲しい正解に関係のないデータを使っていないか、学習モデルは妥当かどうか、などを見直していくことになると思います。私が過去に作った事例では、正解率 80% を超え、実用上十分使える程度の機械学習モデルを作ることができました。
・ 単語/言語を扱いたい場合は、数値に置きなおすなどの前処理を入れることで対応できると思います。
・ また、Python のスクリプトは、コマンドプロンプトなどから実行できますが、パソコンのスケジューラなどに登録しておけば自動実行も可能となります。つまり、すでに測定器などを持っていて、ログデータが自動的に取得できているのであれば、そのログデータから、定期的に/自動的に予測をすることも可能となる、ということになります。

まとめ

CSV ファイルを読み込み、Python の scikit-learn を用いて、機械学習と予測を行うサンプルプログラムをまとめました。
任意の csv ファイルに差し替えて、入力用の列、出力用(正解データ)の列を設定すれば、機械学習と予測ができることになります。

これで、機械学習の中身をブラックボックスとして扱えるようになります。

なお、ログデータなどのファイルについて、一括で移動させる方法などについてポイントをまとめています。
また、機械学習のプログラムが動いたら、つぎに、学習に使う元データをどう集めるかが課題となります。一例として、天気予報のデータをネットから収集してテキストや HTML 形式で出力するサンプルコードについてもまとめています。
もしも関心があるようでしたら、参考にしてみてください。

関連リンク
・ 機械学習で株価予測 【Python】
・ フルーツでの物体検出をやってみた 【YOLOv5】
・ LinearRegression の予測結果を整数で出力する 【scikit-learn】
・ ファイルを一括で移動する 【Python】
・ バッチファイルで Anaconda から Python を実行する方法 【Windows 10】
・ 天気予報を取得してHTMLで出力する 【Python】

スクリプト

learn1.py

import os 
import sklearn 
import numpy as np1
import pandas as pd1
import joblib 
from sklearn.linear_model import LinearRegression
from sklearn.svm import SVC
from sklearn.svm import LinearSVC 
from sklearn.linear_model import LogisticRegression 
from sklearn.neighbors import KNeighborsClassifier 
from sklearn.ensemble import RandomForestClassifier 
from sklearn.model_selection import train_test_split 

def read_csv1( file1 ): 
    return pd1.read_csv( file1 ) 

def split1( csv1, header1, header2 ): 
    # csv1 = csv1.reindex( np1.random.permutation( csv1.index ) ).reset_index( drop=True )    # randomize 
    # print( csv1 )
    x1 = csv1.loc[:, header1 ]
    y1 = csv1.loc[:, header2 ]
    x1_train, x2_test, y1_train, y2_test = train_test_split( x1, y1, random_state=0 )
    y1_train = y1_train.values.ravel() 
    y2_test  = y2_test.values.ravel() 
    return x1_train, x2_test, y1_train, y2_test

def fit1( x0_train, y0_train, file1, n1 ): 
    if os.path.exists( file1 ): 
        model0 = joblib.load( file1 )
    else: 
        model0 = "" 
    all_models1 = [ 
        model0,                                                                       # 0: only read existing model 
        LinearRegression(),                                                           # 1: linear regression 
        SVC(),                                                                        # 2: support vector machine 
        LinearSVC( max_iter=1000 ),                                                   # 3: linear suppoort vector machine 
        LogisticRegression( max_iter=10000 ),                                         # 4: logistic regression 
        KNeighborsClassifier( n_neighbors = 1 ),                                      # 5: kneighbors classifier 
        RandomForestClassifier(),                                                     # 6: random forest 1 
        RandomForestClassifier( min_samples_leaf=2, random_state=0, max_depth=100 )   # 7: random forest (customized) 
    ] 
    model1 = all_models1[ n1 ]                   # fitting model 
    model1.fit( x0_train, y0_train )             # learning 
    joblib.dump( model1, file1 )                 # save the model 
    return model1 

def predict_score1( model1, x0_test, y0_test ): 
    y1_pred = model1.predict( x0_test ) 
    # print( "predict" )
    # print( y1_pred ) 
    # print( type( y1_pred ) ) 
    # print( "answer" )
    # print( y0_test.T )
    # print( y0_test.to_numpy().T[0])
    # print( type( y0_test ) )
    # score, % 
    return model1.score( x0_test, y0_test )

path1 = os.path.dirname(__file__) + "/" 

file1 = path1 + "csv1.csv" 
csv1 = read_csv1( file1 )                                                 # read csv file 

header1 = ["col1","col2","col3","col4","col5","col6","col7","col8"]       # csv header 1 
header2 = ["col9"]                                                        # csv header 2 
x1_train, x2_test, y1_train, y2_test = split1( csv1, header1, header2 )   # split 

file1 = path1 + 'fitting_model1.sav' 
n1 = 7                                                                    # fitting model 0-7 
model1 = fit1( x1_train, y1_train, file1, n1 )                            # training 
s1 = predict_score1( model1, x2_test, y2_test )                           # test 
print( "score: " + str( 100.0*s1 ) + " %"  ) 

#for n1 in range( 1, 8 ): 
#   print( n1 ) 
#   model1 = fit1( x1_train, y1_train, file1, n1 )   # training 
#   s1 = test1( model1, x2_test, y2_test )                                # test 
#   print( "model:" + str(n1) + " score: " + str( 100.0*s1 ) + " %"  ) 

predict1.py



import os 
import sklearn 
import pandas as pd1
import joblib 

def predict1( file1, file2, header1 ): 
    x1 = pd1.read_csv( file1 ).loc[:, header1] 
    model1 = joblib.load( file2 )                                    # load model 
    y1 = model1.predict( x1 )                                        # predict 
    # y1 = ( y1+0.5 ).astype(int) 
    xy1 = x1.assign( col9 = y1 )                                     # predicted data 
    return xy1 

path1 = os.path.dirname(__file__) + "/" 

header1 = ["col1","col2","col3","col4","col5","col6","col7","col8"]  # csv header 1 
file1 = path1 + "csv2.csv" 
file2 = path1 + 'fitting_model1.sav' 
xy1 = predict1( file1, file2, header1 )                              # predict 
print( xy1 ) 

file2 = path1 + "csv3.csv"                                           # save the predicted result 
xy1.to_csv( file2 ) 

csv1.csv

col1,col2,col3,col4,col5,col6,col7,col8,col9
19,09,30,07,47,04,0,0,12
19,09,30,07,52,05,0,0,12
19,09,30,07,57,05,0,0,12
19,09,30,08,02,04,0,0,11
19,09,30,08,07,04,0,0,11
19,10,01,07,47,04,1,0,12
19,10,01,07,52,04,1,0,12
19,10,01,07,57,04,1,0,12
19,10,01,08,02,04,1,0,11
19,10,01,08,07,04,1,0,11
19,10,02,07,47,04,2,0,11
19,10,02,07,52,04,2,0,12
19,10,02,07,57,04,2,0,12
19,10,02,08,02,05,2,0,11
19,10,02,08,07,05,2,0,11
19,10,03,07,47,04,3,0,12
19,10,03,07,52,04,3,0,12
19,10,03,07,57,04,3,0,12
19,10,03,08,02,04,3,0,11
19,10,03,08,07,04,3,0,11
19,10,04,07,47,04,4,0,12
19,10,04,07,52,04,4,0,12
19,10,04,07,57,04,4,0,12
19,10,04,08,02,04,4,0,11
19,10,04,08,07,05,4,0,11
19,10,05,07,47,04,5,0,11
19,10,05,07,52,04,5,0,11
19,10,05,07,57,04,5,0,11
19,10,05,08,02,04,5,0,10
19,10,05,08,07,05,5,0,10
19,10,06,07,47,04,6,0,11
19,10,06,07,52,05,6,0,11
19,10,06,07,57,04,6,0,11
19,10,06,08,02,05,6,0,10
19,10,06,08,07,04,6,0,10
19,10,07,07,42,04,0,0,12
19,10,07,07,47,04,0,0,12
19,10,07,07,52,04,0,0,12
19,10,07,07,57,05,0,0,12
19,10,07,08,02,05,0,0,11
19,10,07,08,07,04,0,0,11
19,10,08,07,42,05,1,0,12
19,10,08,07,47,05,1,0,12
19,10,08,07,52,04,1,0,12
19,10,08,07,57,05,1,0,12
19,10,08,08,02,04,1,0,11
19,10,08,08,07,04,1,0,11
19,10,09,07,42,04,2,0,12
19,10,09,07,47,04,2,0,12
19,10,09,07,52,04,2,0,12
19,10,09,07,57,04,2,0,12
19,10,09,08,02,04,2,0,11
19,10,09,08,07,04,2,0,11
19,10,10,07,42,04,3,0,12
19,10,10,07,47,05,3,0,12
19,10,10,07,52,05,3,0,12
19,10,10,07,57,05,3,0,12
19,10,10,08,02,04,3,0,11
19,10,10,08,07,05,3,0,11
19,10,11,07,42,04,4,0,12
19,10,11,07,47,05,4,0,12
19,10,11,07,52,04,4,0,12
19,10,11,07,57,05,4,0,12
19,10,11,08,02,05,4,0,11
19,10,11,08,07,04,4,0,11
19,10,12,07,42,04,5,0,11
19,10,12,07,47,05,5,0,11
19,10,12,07,52,05,5,0,11
19,10,12,07,57,04,5,0,11
19,10,12,08,02,05,5,0,10
19,10,12,08,07,05,5,0,10
19,10,13,07,42,10,6,0,11
19,10,13,07,47,10,6,0,11

csv2.csv

col1,col2,col3,col4,col5,col6,col7,col8
20,10,08,07,57,05,3,0
20,10,08,08,02,05,3,0
20,10,08,08,07,06,3,0
20,10,08,08,12,04,3,0
20,10,08,08,17,05,3,0
20,10,08,08,22,05,3,0
20,10,10,07,42,07,5,0
20,10,10,07,47,06,5,0
20,10,10,07,52,06,5,0
20,10,10,07,57,04,5,0
20,10,10,08,02,07,5,0
20,10,10,08,07,05,5,0
20,10,10,08,12,07,5,0
20,10,10,08,17,06,5,0
20,10,10,08,22,05,5,0
タイトルとURLをコピーしました