エンジニアリングとお金の話

都内で働くエンジニアの日記です。

【技術】初めて機械学習をやってみた

【SPONSORED LINK】

そろそろ機械学習について勉強したいと思っており、その一環として以下のコンテストに現在参加している。

deepanalytics.jp

内容としては、sansan社が主催のコンテストで名刺に書かれている項目に対してラベルを付与するアルゴリズムを作成するというもの。どうやらOCRを活用しても現在の技術では100%レイアウトを正確に解析するのは難しいらしい。

正直何から手を付ければいいのか分からなかったが、そんな初心者の為に画像解析のチュートリアルを準備しているので一通りやってみた。

コンテストチュートリアル(Sansan)

感想としては、pythonで画像を機械学習するやり方が何と無く分かってきた感じである。今回のチュートリアルでは機械学習を以下の手順にて行っている。

(画像データの取り込み)⇒(特徴抽出)⇒(モデルの学習)⇒(予測値の出力+評価)

なお、画像データの取り込みはPILを使用し、特徴の抽出はscikit-image、モデルの学習にはscikit-learnを使用している。これらのライブラリを活用すれば画像データを機械学習する事が出来るんだという事を学んだ。

サンプルコードを基にipython notebookで試してみたら、チュートリアル通りの結果が出力された。ただ、チュートリアルのコードはコメントが少なく、何をしているのか良く分からなかった。その為、自分で意味を考えてコードを並び替えてコメントを入れてみた。なお、コードで使用しているイメージ画像やtrain.csvはコンテストサイトからダウンロード出来る。

import pandas as pd
import os
import numpy as np
from PIL import Image
from skimage.feature import hog
from sklearn.cross_validation import train_test_split
from sklearn.linear_model import LogisticRegression

def main():
    #画像リサイズ用の設定値(216x72)
    img_shape = (216,72)

    #特徴量抽出用の値
    orientations = 6
    pixels_per_cell = (12,12)
    cells_per_block = (1, 1)

    #特徴量抽出 戻り値:X=特徴量配列、Y=画像項目情報配列
    X, Y = load_data('train.csv', 'train_images', img_shape, orientations, pixels_per_cell, cells_per_block)
    
    #XデータとYデータから学習用データと検証用データを作成する
    x_train, x_test, y_train, y_test = train_test_split(X, Y, test_size=0.5, random_state=1234)

    #ロジスティック回帰実施前の初期処理。今回は画像項目種類が9ある為、引数に9を設定
    model = MultiLabelLogistic(n_out = 9)  

    #学習用データにて学習を実施
    model.fit(x_train, y_train)

    #検証用データにて予想値を取得
    predictions = model.predict(x_test)


#特徴量抽出
def load_data(file_name, img_dir, img_shape, orientations, pixels_per_cell, cells_per_block):
    #画像の項目種類名(会社名や住所等全部で9分類)
    classes = ['company_name', 'full_name', 'position_name', 'address', 'phone_number', 'fax', 'mobile', 'email', 'url']
    
    #座標ファイルをデータフレームに格納
    df = pd.read_csv(file_name)
    
    #データフレームの件数を取得(23万件)
    n = len(df)
    
    #配列を初期化(2次元配列。行=23万、列=9)
    Y = np.zeros((n, len(classes)))
    
    #データフレームの件数分処理を実施(23万件)
    for i, row in df.iterrows():
        
        #ファイル名、各画像の座標を取得
        f, l, t, r, b = row.filename, row.left, row.top, row.right, row.bottom
        img = Image.open(os.path.join(img_dir, f)).crop((l,t,r,b)) # 項目領域画像を切り出す
        if img.size[0]<img.size[1]:                                # 縦長の画像に関しては90度回転して横長の画像に統一する
            img = img.transpose(Image.ROTATE_90)
        
        #切り取った画像をグレースケール変換
        img_gray = img.convert('L')
        
        #画像データをリサイズ(resizeの引数:img_shapeには画像サイズのタプルが入る)し、数値データへ変換
        img_gray = np.array(img_gray.resize(img_shape))/255.       # img_shapeに従った大きさにそろえる
        
        #HOG(Histogram of Oriented Gradients)にて特徴量を取得。(数値データ⇒ベクトルデータ)
        img = np.array(hog(img_gray,orientations = orientations,
                           pixels_per_cell = pixels_per_cell,
                           cells_per_block = cells_per_block))

        #データの読み込みが初回時
        if i == 0:
        #特徴量のベクトル数を取得
            feature_dim = len(img)
            
            #特徴量格納用配列を初期化(行=23万、列=特徴量のベクトル数)
            X = np.zeros((n, feature_dim))
        
        #X配列(特徴量格納用配列)へ特徴量データを設定
        X[i,:] = np.array([img])
        
        #画像に対応する項目種類情報を取得
        #※各画像毎に会社名や電話番号等の画像データが含んでいる項目に関する情報
        y = list(row[classes])
        
        #Y配列(画像項目情報格納用配列)へ項目種類情報を格納
        Y[i,:] = np.array(y)
    
    return X, Y

#ロジスティック回帰にて2値識別を行う
class MultiLabelLogistic():
    def __init__(self, n_out):
        self.n_out = n_out
        model_list = []
        
        #画像項目数分のインスタンスを作成
        #今回は9項目あるため、9個分のインスタンスを作成
        for l in range(self.n_out):
            model_list.append(LogisticRegression())
        self.models = model_list
    
    #特徴量配列と画像項目情報配列にて学習を実施
    def fit(self, X, Y):
        i = 0
        #画像項目数分処理を実施
        for model in self.models:
            #特徴量トレーニングデータと画像項目トレーニングデータにて学習を実施
            # ⇒恐らくこの処理にて、会社名の時はこの特徴量、電話番号の時はこの特徴量という分類が行われる想定。
            model.fit(X, Y[:,i])
            i += 1
    
    #学習内容より予測を実施
    def predict(self, X):
        i = 0
        #配列を初期化(2次元配列。行=検証データ件数、列=画像項目数)
        predictions = np.zeros((len(X), self.n_out))
        
        #画像項目数分処理を実施
        for model in self.models:
            #学習させた内容にて予測を実施。各画像項目毎に予想結果を配列に格納する
            predictions[:,i] = model.predict(X)
            i += 1        
        return predictions

if __name__ == '__main__':
    main()

今回はラベル分類を行うにあたり、ロジスティック回帰を使用してる。ただ、そもそもロジスティック回帰て何?な状態なのでそこから勉強して行こうと思う。先は長いが機械学習が使いこなせる様に頑張りたい。