2017年12月26日

Recurrent Convolutional Neural Networks (RCNN) を使ってテキスト分類してみた

エモトです.みなさん,ガルパン最終章は見られましたか?私はまだ二回しか見に行けてません.また近いうちに爆音上映をキメたいです.

深層学習(ディープラーニング)といえば,まず先に画像を対象にしたものが例に挙がりますが,今回はテキスト分類をしてみたいと思い,ちょっと試してみました.

手法

テキストは画像と比較しても,可変長であり,単語(語彙)を順々に書き繋いでいくので,Recurrent Neural Networks (RNN) ベースの手法が良いだろうと思い,Siwei Lai らが2015年に発表した Recurrent Convolutional Neural Networks for Text Classification から,Recurrent Convolutional Neural Networks (RCNN) を用いて,検証しました.

RCNN

RCNN は名前が示す通りに,recurrent と convolutional を組み合わせた NN です.時系列データの扱いにかけた recurrent に,画像分類でその性能を発揮した convolutional の組合せ,なにか強そうですね.ここで,同様な手法として,Bradbury らが提案した QRNN というのがあります.こちらは RCNN が組み合わせに対して,CNN を擬似的に recurrent にするように設計されています.対照実験を行いたいところですが,時間の都合上,今回は RCNN のみになります.

RCNN の実装は Keras で行いました.実装したモデルを下図になります.冒頭に深層学習と言いましたが,それほど層は深くないです.ここで,vanilla RNN ではなく LSTM を使用して,入力テキストの形態素解析には MeCab (mecab-ipadic-NEologd) ,埋め込み層には fastText を使用しました.

keras-rcnn.png


実験

アマゾンのレビューデータを用いて,コメントをレーティングごとに分類しました.商品286個のレビューデータ115,349件を用意して,以下のように分けて実験を行いました.

trainvaltesttotal
83051922823070115349

結果

学習したモデルでテストデータを分類した結果を下記に示します(レーティングは1から始まりますが,計算の都合上0からになっています).この表は行ごとに予測したレーティングが実際はどのレーティングだったかをカウントしています(対角行列のように対角線上に数値が集まっていれば良い結果といえます).

予測\正解01234
05051671318197
11401831798863
2101193634439394
3529647420271665
49891597250112074

予想と実際のレーティングが一致したのは,約68% となりました.少し精度が低いと思いましたが,表を見ると,低いレーティングは低く,高いレーティングは高く,正解に近く分布しているので,完全一致という指標でなければ,まずまずの結果のように見えます.言い訳をしておくと,レーティングという人間の曖昧さ,日本語という自然言語の難しさから,この手法と結果は悪くはないと思っています.

まとめ

RCNNでテキスト分類を行い,比較的良い結果が得られました.しかしながら,レーティングのようにラベル付けがはっきりしているデータは多くはなく,文章はメディア(媒体)や書き手によって,いくつもの形になります.銀の弾丸はないと.テキスト分離は難しい分野ですが,それゆえ面白いですね.

最後に,本年,本ブログへの訪問,ありがとうございました.来年もよろしくお願いします.
posted by Seesaa京都スタッフ at 10:37| Comment(0) | 機械学習 | このブログの読者になる | 更新情報をチェックする

2017年10月06日

fastTextをPythonで使いたいならpyfasttext

エモトです.今週末はついにRWBY volume 4の公開.あの3のラストからどう展開するのか楽しみです.2,3は難波まで遠征してましたが,4は地元京都でも上映されるので,テンションあがってます.

(ここから追記)
この記事に関して,こちら に追加情報を書いてます
(ここまで追記)

最近,word2vecfastText などの語彙のベクトル化に興味あります.それらの説明はまたいつかとして,語彙を意味のあるベクトルデータに変換して,
king - man + women = queen
の加算計算をやってしまうとは,素直にすごいの一言です.

個人的にfastTextでWikipediaの全データを使って,学習させて,
ザク + 赤い = シャア専用ザク
の計算ができたのは,本当に感動しました.

ただ,加算だけでは満足できませんよね.他のサービスと連携させたいですよね.やっぱり,いまはPythonですよね.ということで,fastTextをPythonで動かす方法を色々調べました.

fasttext 0.8.3
現在,「fasttext python」で検索したらトップに表示される,各種技術ブログ等でも紹介されている,おそらく誰もがこれを使おうと思うでしょう.しかしながら,fasttext 0.8.3 と本家fastTextとバージョンが異なるようで,うまく動作しません.その fasttext 0.8.3 で学習や推論をすれば問題ないですが,最新のfastTextは使いたいですよね.ブランチ内で0.9.0が公開されているのですが,これも上手く動きません.

gensim
これは本家fastTextで学習したモデルを読むことができます.しかしながら,なぜかベクトル化した数値や類似した語句の結果が本家と異なります.gensimの学習は本家fastTextをコマンドラインで実行しており,学習したものと推論で異なってしまってはつかえません.

Pythonからコマンドラインを呼び出して無理に連携してしまおうかと思いましたが,現時点で利用可能なライブラリがありました.

pyfasttext 0.4.0
本家fastTextで学習したモデルの読み込みも可能で.本家と同じ結果を出力することができました.インストールもコマンド1つですみます.
pip install pyfasttext
ただ私の環境では一発では入れられなかったので,ちょっと右往左往しました.しかし,本家と同じ結果を出すことができるので,pythonでfastTextを操作したいなら,このライブラリを使うのがオススメです.
posted by Seesaa京都スタッフ at 13:00| Comment(0) | 機械学習 | このブログの読者になる | 更新情報をチェックする

2017年09月11日

TensorFlow (Keras)とCore MLでリアルタイムの顔識別アプリを作ったぞい

エモトです.今週はAppleの発表会に,iOSDC JAPAN 2017と,iOS開発界隈のイベントが目白押しですね.iOSDCには弊社エンジニアも参加します.関係者各様よろしくお願いします.

n番煎じではありますが,機械学習フレームワーク TensorFlowKeras で学習したモデルを,iOS11 からの Core ML に与えて,簡単な顔識別のアプリを試作して見ました.試作ということで,データ収集など一部で手を抜いてます.ご了承ください.

IMG_0032 2.PNG


ただいま絶賛放送中のNEW GAME!!の9人の主要キャラを対象した識別アプリです.UI等は,先行実装された記事を参考しました.ありがとうございます.

1. TensorFlow (Keras)

画像データはGoogle画像検索およびTumblrタグ検索のスクリプトを書いて集めました.APIの使用制限上,各キャラごとに多くの画像は集められなかったのですが,比較的キャラの描き分けがされている,作画が安定しているからたぶん大丈夫だろうと思います.

使用したCNN(畳み込みニューラルネットワーク)は以下のような簡単なものを設定しました.

model = Sequential()
model.add(Conv2D(32,
                 kernel_size=(3, 3),
                 activation='relu',
                 input_shape=input_shape))
model.add(Conv2D(64, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(num_classes, activation='softmax'))

訓練およびテストデータには,取得した画像にOpenCVでアニメ顔検出を行い,顔領域を切り出したものを使用しました.Tumblrで取得した画像データを訓練,Googleで取得した画像データをテストとして,学習しました.

Epoch 81/1000
128/730 [====>.........................] - ETA: 2s - loss: 0.0728 - acc: 0.9609
256/730 [=========>....................] - ETA: 1s - loss: 0.0667 - acc: 0.9727
384/730 [==============>...............] - ETA: 1s - loss: 0.0610 - acc: 0.9766
512/730 [====================>.........] - ETA: 0s - loss: 0.0636 - acc: 0.9785
640/730 [=========================>....] - ETA: 0s - loss: 0.0629 - acc: 0.9797
730/730 [==============================] - 7s - loss: 0.0611 - acc: 0.9808 - val_loss: 0.4857 - val_acc: 0.8301
Epoch 00080: early stopping

学習結果は,EarlyStoppingを設定して,80回目の試行で切り上げ.テストデータに対して精度0.8となったので,遊び程度ならまあまあ良いかなと思います.本当はもっとデータ増やしたりやCNNを考えて,精度を上げたいところですが.

2. mlmodel へ変換

coremltools を使って,Kerasで学習したモデルをCore MLが読み込めるファイルへと変換します.convertのパラメータ調整もですが,Python2.7系にしか対応していないので,環境作成に時間かかりました.

import coremltools
coreml_model = coremltools.converters.keras.convert(h5_path, input_names = 'image', is_bgr = True, image_scale = 0.00392156863, image_input_names = 'image', class_labels = 'labels.txt')
coreml_model.save(mlmodel_path)

3. Core MLで識別

流れとしては,AVCaptureSessionでカメラから静止画像を順次受け取って顔画像を切り出して,Core MLで識別の流れです.ほかのブログ等では顔画像の識別はVisionフレームワークを使用してる例が多いですが.この試作アプリではアニメ顔ゆえにOpenCVで行なっています.本当は領域認識もRCNN,YOLOやSSDなどを実装して一緒に推論したいところですが,今回はそこまで手を回せられなかったので

    // 予測
    func predicate(image: UIImage) {
        
        let image = CIImage(cgImage: image.cgImage!)
        let handler = VNImageRequestHandler(ciImage: image)
        do {
            let model = try VNCoreMLModel(for: self.newGameModel.model)
            let req = VNCoreMLRequest(model: model, completionHandler: self.handleClassification)
            try handler.perform([req])
        } catch {
            self.isComputing = false
            print(error)
        }
    }
    
    // 推定結果
    private func handleClassification(request: VNRequest, error: Error?) {
        
        guard let observations = request.results as? [VNClassificationObservation] else { fatalError() }
        guard let best = observations.first else { fatalError() }
        
        DispatchQueue.main.async {
        	// 識別した結果に対応する処理など
            print("best identifier: " + best.identifier + ", prob: " + String(best.confidence*100))
            var str:String = ""
            for ob in observations {
                str = str + ob.identifier + ", " + String(ob.confidence*100) + "%" + "\n"
            }
            print(str)            
        }
    }

Core MLでリアルタイム識別を走らせてると,端末が熱くなってくるんですよね.画像一枚ごとに識別するシーンより,時系列データで連続したデータを識別するシーンがこれから多くなると思うので,ちょっと不安になります.近々発表される新型 iPhone はこの計算に耐えられる高スペックのCPUやGPUを積んでくるんでしょうか.楽しみです.
posted by Seesaa京都スタッフ at 11:37| Comment(0) | 機械学習 | このブログの読者になる | 更新情報をチェックする