質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.92%

kerasでaccuracy以外の精度指標を使いたい

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 4,605

---stax---

score 142

kerasを用いて画像の2値分類を行っています。
jupyter notebookで開発しています。

モデルの評価指標としてaccuracyだけを見て良いモデルか評価するのは良くないと考え
ROC曲線、ROC AUCなどを取得できるようにしたいです。

kerasにはROC曲線、ROC AUCなどは標準でサポートされている評価指標に含まれていないので自分で作成する必要があるのですが何から手をつけてよいか分からず良き詰まっています。

自分なりに調べてみて以下のリンクを真似て記述してみたのですが
How to compute Receiving Operating Characteristic (ROC) and AUC in keras?
関数aucとroc_aucでは処理は行われるのですが結果が違っており、tensolflowなどの記述も含まれていて今の自分には理解ができないので使ったことのあるscikit-learnを使おうと考えたのですが上手く行きません。

コード内の
model.compile
部分で以下のエラーとなります。
compileは設定だけだと理解しているのですがエラーを見た感じは引数の不備かと思うのですが、まだ学習もさせていないので何が現状渡されようとしているのか分からず、関数auc, roc_aucに関してはなぜ問題ないのか分かりません。

自分の記述している内容はどこが間違っているでしょうか?
また、metricsの中でscikit-learnを使う場合はどのように記述すればよいでしょうか?
フレームワークの挙動もまだ理解できておらず初歩的な質問ですがアドバイス頂きたいです。

以下コードになります。
データのフォルダ分けなどの部分は省略させて頂いています。

from keras import layers
from keras import models
from keras import optimizers

model = models.Sequential()
#layers.Conv2D(32, (3, 3))
#input_shape = CNNの入力テンソルが(image_height, image_width, image_channels)(バッチ次元を含まない)の形状
model.add(layers.Conv2D(32, (3, 3), activation='relu', input_shape=(300, 300, 3)))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(64, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Conv2D(128, (3, 3), activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))

model.add(layers.Flatten())
model.add(layers.Dense(512, activation='relu'))
model.add(layers.BatchNormalization())
model.add(layers.Dense(1, activation='sigmoid'))

import keras.backend as K
from sklearn.metrics import roc_auc_score
import tensorflow as tf
from sklearn.metrics import precision_score
from sklearn.metrics import roc_curve

def auc(y_true, y_pred):
    auc = tf.metrics.auc(y_true, y_pred)[1]
    K.get_session().run(tf.local_variables_initializer())
    return auc

def roc_auc(y_true, y_pred):
    roc_auc = tf.py_func(roc_auc_score, (y_true, y_pred), tf.double)
    return roc_auc

def roc(y_true, y_pred):
    fpr, tpr, thresholds = roc_curve(y_true, y_pred)
    return fpr, tpr, thresholds


model.compile(loss='binary_crossentropy',
                      optimizer=optimizers.RMSprop(lr=1e-4),
                      metrics=['acc', auc, roc_auc, roc])

from keras.callbacks import EarlyStopping, ModelCheckpoint, TensorBoard
from keras.preprocessing.image import ImageDataGenerator

fpath = r'C:\Users\python file\model\DA_BN\{epoch:02d}-{acc:.2f}-{loss:.2f}-{val_acc:.2f}-{val_loss:.2f}.hdf5'
check_point = ModelCheckpoint(filepath = fpath, monitor='val_loss', verbose=0, save_best_only=True, mode='min', period=1)

train_datagen = ImageDataGenerator(
    rescale=1./255,
    brightness_range=[0.4, 1.0],
    channel_shift_range=3.,
)

test_datagen = ImageDataGenerator(rescale=1./255)

predict_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
    train_dir, #ターゲットディレクトリ
    target_size = (300, 300), #全ての画像サイズを150*150に変更
    batch_size = 20, #バッチサイズ
    class_mode = 'binary', #binary_crossentropyを使用するため2値のラベルが必要,
    save_to_dir = r'C:\Users\python file\dataAugmentation_DA_BN',
    save_prefix = 'generated',
    save_format = 'png')

validation_generator = test_datagen.flow_from_directory(
    val_dir,
    target_size = (300, 300),
    batch_size = 20,
    class_mode = 'binary')   

predict_generator = predict_datagen.flow_from_directory(
    test_dir,
    target_size = (300, 300),
    batch_size = 20,
    class_mode = 'binary')   

history = model.fit_generator(train_generator,
                             steps_per_epoch = 1,
                             epochs = 5,
                             validation_data = validation_generator,
                             validation_steps = 1,
                             callbacks=[check_point])
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-38-b90f05e5f8e8> in <module>()
      3 model.compile(loss='binary_crossentropy',
      4                       optimizer=optimizers.RMSprop(lr=1e-4),
----> 5                       metrics=['acc', auc, roc_auc,  roc])

~\AppData\Local\Continuum\anaconda3\lib\site-packages\keras\engine\training.py in compile(self, optimizer, loss, metrics, loss_weights, sample_weight_mode, weighted_metrics, target_tensors, **kwargs)
    438                 output_metrics = nested_metrics[i]
    439                 output_weighted_metrics = nested_weighted_metrics[i]
--> 440                 handle_metrics(output_metrics)
    441                 handle_metrics(output_weighted_metrics, weights=weights)
    442 

~\AppData\Local\Continuum\anaconda3\lib\site-packages\keras\engine\training.py in handle_metrics(metrics, weights)
    407                     metric_result = weighted_metric_fn(y_true, y_pred,
    408                                                        weights=weights,
--> 409                                                        mask=masks[i])
    410 
    411                 # Append to self.metrics_names, self.metric_tensors,

~\AppData\Local\Continuum\anaconda3\lib\site-packages\keras\engine\training_utils.py in weighted(y_true, y_pred, weights, mask)
    401         """
    402         # score_array has ndim >= 2
--> 403         score_array = fn(y_true, y_pred)
    404         if mask is not None:
    405             # Cast the mask to floatX to avoid float64 upcasting in Theano

<ipython-input-37-3b434b12b89b> in roc(y_true, y_pred)
     41 
     42 def roc(y_true, y_pred):
---> 43     fpr, tpr, thresholds = roc_curve(y_true, y_pred)
     44     return fpr, tpr, thresholds

~\AppData\Local\Continuum\anaconda3\lib\site-packages\sklearn\metrics\ranking.py in roc_curve(y_true, y_score, pos_label, sample_weight, drop_intermediate)
    532     """
    533     fps, tps, thresholds = _binary_clf_curve(
--> 534         y_true, y_score, pos_label=pos_label, sample_weight=sample_weight)
    535 
    536     # Attempt to drop thresholds corresponding to points in between and

~\AppData\Local\Continuum\anaconda3\lib\site-packages\sklearn\metrics\ranking.py in _binary_clf_curve(y_true, y_score, pos_label, sample_weight)
    313     """
    314     # Check to make sure y_true is valid
--> 315     y_type = type_of_target(y_true)
    316     if not (y_type == "binary" or
    317             (y_type == "multiclass" and pos_label is not None)):

~\AppData\Local\Continuum\anaconda3\lib\site-packages\sklearn\utils\multiclass.py in type_of_target(y)
    242     if not valid:
    243         raise ValueError('Expected array-like (array or non-string sequence), '
--> 244                          'got %r' % y)
    245 
    246     sparseseries = (y.__class__.__name__ == 'SparseSeries')

ValueError: Expected array-like (array or non-string sequence), got <tf.Tensor 'dense_2_target_8:0' shape=(?, ?) dtype=float32>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • ---stax---

    2019/03/14 22:21

    回答ありがとうございます。
    なるほど...tf.Tensorという型になっているのですね。
    参考に貼っていただいたようにlossが小さいモデルをまずはスナップショットして、そのモデルに対してのROC AUCを出したほうが実現は可能な気がします。
    これに関しても、私の質問文にあります変数predict_generator(テストデータ)は flow_from_directoryで作られたジェネレータ型なので戻り値であるタプルをデータとラベルに分割してnumpy配列に直す処理がないとscikit-learnの関数に渡せないという理解でよろしかったでしょうか?
    flow_from_directoryはラベルを付けてくれたりすごく便利なのですが理解していないとすごく難しいですね...。

    キャンセル

  • tiitoi

    2019/03/14 22:57

    > flow_from_directoryで作られたジェネレータ型なので戻り値であるタプルをデータとラベルに分割してnumpy配列に直す処理がないとscikit-learnの関数に渡せないという理解でよろしかったでしょうか?

    必要なのは、ジェネレータ型の戻り値のラベル (正解ラベル) と学習したモデルの推論結果 (予測ラベル) の2つですね。
    この2つを numpy 配列として得られれば、あとは sklearn の関数にわたすことで各種評価指標の算出が行えます。

    キャンセル

  • ---stax---

    2019/03/14 23:26

    回答ありがとうございます。
    勘違いしておりました。自分で予測したデータと正解ラベルですね。
    数日間この部分で完全にストップしていたので明日は少し前進できそうです。
    いつも丁寧に答えていただいて本当にありがとうございます。

    キャンセル

回答 1

check解決した方法

0

以下の実装で対応
コードは抜粋

①各エポックでROC AUCを求める

def roc_auc(y_true, y_pred):
    roc_auc = tf.py_func(roc_auc_score, (y_true, y_pred), tf.double)
    return roc_auc

#上記関数をmetricsパラメータの中で指定する
model.compile(loss='binary_crossentropy',
                      optimizer=optimizers.RMSprop(lr=1e-4),
                      metrics=['acc', roc_auc])

②各エポックで最も良かったモデルをスナップショットし、そのモデルに対して評価する
(各エポックで求めるのではなく、学習後のモデルで評価する)

from keras.models import load_model
from sklearn.metrics import roc_curve
from sklearn.metrics import auc
from sklearn.metrics import roc_auc_score
import numpy as np
import matplotlib.pyplot as plt

#モデルの読み込み
#①のようにmetricsに自作の関数を使う場合custom_objectsパラメータを指定しないと読み込めない
model_test = load_model(r'C:\Users\python file\model\DA_BN\17-1.00-0.00-0.95-0.10.hdf5', custom_objects={'roc_auc':roc_auc})

score = model_test.evaluate_generator(predict_generator)
print('loss={}, accuracy={}, ROC_AUC={}'.format(*score))

#テストデータジェネレータ
predict_data = predict_generator[0]
predicts = []
labels = []

for i in range(40):
    input_data = predict_data[0][i]
    input_data = input_data.reshape(1, *input_data.shape)
    #predict = predict.reshape((1,) + predict.shape)
    predicts.append(input_data) 

    labels.append(predict_data[1][i])

predicts = np.concatenate(predicts, axis=0)

y_pred_keras = model_test.predict(predicts).ravel()
print('予測結果:', y_pred_keras)

fpr_keras, tpr_keras, thresholds_keras = roc_curve(labels, y_pred_keras, drop_intermediate=False)
print('閾値:', thresholds_keras)

auc_keras = auc(fpr_keras, tpr_keras)
print('aucスコア:', auc_keras)

print(fpr_keras, tpr_keras)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.92%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る