Lind
@Lind (Lind Taylor)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

CNNで正解率、適合率、再現率の結果がすべて同じ値になる

解決したいこと

例)
cnnでvgg16のファインチューニングを使って、二値分類を行おうとしたときに起こったバグです。正解率0.5,再現率0.5,適合率0.5というように値がすべて同じになってしまします。4エポック以降の結果はここには記載していませんが、100EPOCHくらいまで様子を見ても変わりません。結果から推測するに、正解率と適合率が同じという点から、一種類のデータしか判別できていないように思われますが、原因はよくわかりません。

発生している問題・エラー

出ているエラーメッセージを入力

Epoch 2/200
239/239 [==============================] - 18s 74ms/step - loss: 0.6935 - accuracy: 0.4927 - precision: 0.4927 - recall: 0.4927 - val_loss: 0.6932 - val_accuracy: 0.5000 - val_precision: 0.5000 - val_recall: 0.5000
Epoch 3/200
239/239 [==============================] - 18s 74ms/step - loss: 0.6934 - accuracy: 0.4958 - precision: 0.4958 - recall: 0.4958 - val_loss: 0.6932 - val_accuracy: 0.5000 - val_precision: 0.5000 - val_recall: 0.5000
Epoch 4/200
239/239 [==============================] - 18s 74ms/step - loss: 0.6934 - accuracy: 0.4948 - precision: 0.4948 - recall: 0.4948 - val_loss: 0.6931 - val_accuracy: 0.5000 - val_precision: 0.5000 - val_recall: 0.5000

例)

NameError (uninitialized constant World)

または、問題・エラーが起きている画像をここにドラッグアンドドロップ

該当するソースコード

import keras
import glob
import numpy as np
import tensorflow as tf
from sklearn.model_selection import train_test_split
# from keras.preprocessing.image import load_img, img_to_array #ここでエラーとなるので以下のコードに変更
from tensorflow.keras.utils import load_img, img_to_array
from keras import models
from keras.applications import VGG16 
from keras.models import Sequential, load_model, model_from_json 
from keras.layers import Conv2D, MaxPooling2D
from keras.layers import Dense, Dropout, Flatten
#from keras.utils import plot_model #これはKerasのバージョンなどにより使えないのでコメントアウト
 #keras.utils.to_categoricalでエラーが出るので追加
# from keras.optimizers import Adam # ここでエラーとなるので以下のコードに変更
from tensorflow.keras.optimizers import Adam # 「tensorflow.」を追加
from sklearn.metrics import confusion_matrix
from tensorflow.keras.callbacks import TensorBoard 
from tensorflow.keras.metrics  import Precision 
import matplotlib.pyplot as plt
import time


#2 各種設定 



image_size = 180 

color_setting = 3  #ここを変更。データセット画像のカラー:「1」はモノクロ・グレースケール。「3」はカラー。

folder = ['desease', 'health']

class_number = len(folder)
print('今回のデータで分類するクラス数は「', str(class_number), '」です。')


#3 データセットの読み込みとデータ形式の設定・正規化・分割 

X_image = []  
Y_label = [] 
for index, name in enumerate(folder):
  read_data = train_data_path + '/' + name
  files = glob.glob(read_data + '/*.jpg') #ここを変更。png形式のファイルを利用する場合のサンプルです。
  print('--- 読み込んだデータセットは', read_data, 'です。')

  for i, file in enumerate(files):  
    if color_setting == 1:
      img = load_img(file, color_mode = 'grayscale' ,target_size=(image_size, image_size))  
    elif color_setting == 3:
      img = load_img(file, color_mode = 'rgb' ,target_size=(image_size, image_size))
    array = img_to_array(img)
    X_image.append(array)
    Y_label.append(index)

X_image = np.array(X_image)
Y_label = np.array(Y_label)

X_image = X_image.astype('float32') / 255
Y_label = keras.utils.to_categorical(Y_label, class_number) 

train_images, valid_images, train_labels, valid_labels = train_test_split(X_image, Y_label, test_size=0.10)
x_train = train_images
y_train = train_labels
x_test = valid_images
y_test = valid_labels


#4 機械学習(人工知能)モデルの作成 – 畳み込みニューラルネットワーク(CNN)・学習の実行等

epochs = 200 
batch_size = 16 
vgg_conv = VGG16(weights='imagenet', include_top=False, input_shape=(image_size, image_size, 3)) 
last = vgg_conv.output 
mod = Flatten()(last) 
mod = Dense(1024, activation='relu')(mod) 
mod = Dropout(0.5)(mod) 
preds = Dense(2, activation='softmax')(mod) 
model = models.Model(vgg_conv.input, preds) 

model.summary() 

model.compile(loss='categorical_crossentropy', 
              optimizer=Adam(), 
              metrics=['accuracy' , Precision(), tf.keras.metrics.Recall()]) 

    
    # モデルの訓練
history = model.fit(x_train, y_train,
                      batch_size=batch_size, 
                      epochs=epochs, 
                      validation_data=(x_test, y_test), 
                      shuffle=True) 

model.save(f"model_potato_test.h5") 
print(history.history) 

# Calculate TP, FP, FN, TN
y_true = np.argmax(y_test, axis=1)
y_pred = model.predict(x_test)
y_pred = np.argmax(y_pred, axis=1)
cm = confusion_matrix(y_true, y_pred)
tn, fp, fn, tp = cm.ravel()

print(f'フォールド {i + 1} TP(真陽性): {tp}')
print(f'フォールド {i + 1} FP(偽陽性): {fp}')
print(f'フォールド {i + 1} FN(偽陰性): {fn}')
print(f'フォールド {i + 1} TN(真陰性): {tn}')

model.summary()


model.compile(loss='categorical_crossentropy',
              optimizer=Adam(),
              metrics=['accuracy'])

model.save(f"model_potato_{i+1}.h5")

start_time = time.time()




plt.plot(history.history['accuracy'])
plt.plot(history.history['val_accuracy'])
plt.title('Model accuracy')
plt.ylabel('Accuracy')
plt.xlabel('Epoch')
plt.grid()
plt.legend(['Train', 'Validation'], loc='upper left')
plt.show()

plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.grid()
plt.legend(['Train', 'Validation'], loc='upper left')
plt.show()


#model.save('cnn_model_weight.h5')
score = model.evaluate(x_test, y_test, verbose=0)
print('Loss:', score[0], '(損失関数値 - 0に近いほど正解に近い)') 
print('Accuracy:', score[1] * 100, '%', '(精度 - 100% に近いほど正解に近い)') 
print('Computation time(計算時間):{0:.3f} sec(秒)'.format(time.time() - start_time))

### 自分で試したこと

最終出力層のDence(1, sigmoid)、損失関数をbinary crossentropyに変更しても解決しませんでした。モデル構造に原因があるような気がしますが、解決方法のご教授をよろしくお願いします。
0

3Answer

結果から推測するに、正解率と適合率が同じという点から、一種類のデータしか判別できていないように思われます

これは「一種類のデータしか判別できていない」というわけではなく,$n$値分類の際の正解率が$\frac{1}{n}$である場合,ランダムに答えを言っているだけのモデルができたことを意味します12

つまり,モデルは何ら学習してないに等しいです.

  1. じゃんけんの手を映した画像に対して必ず勝つような手を提案する,みたいなモデルを作った場合,適当に答えるだけでも勝率は1/3です.

  2. 正しくは,$n$種類あるデータがそれぞれ$a_1$件,$a_2$件,..,$a_n$件ずつあった場合,1種類目のデータに対する正解率は$\frac{a_1}{\sum_i a_i}$です.つまり,全ての種類の件数が等しい場合,ランダム推測の正解率は$\frac{1}{n}$というわけです.

1Like

Comments

  1. @Lind

    Questioner

    ありがとうございます。
    とりあえず、ハイパーパラメーターをいじって解決を試みるしかないのでしょうか?

  2. それも一手ですが,最初は教師データの見直し,すなわちデータエンジニアリングを行うのが先ですね.
    教師データとして与えているx_train, y_trainのペアは本当に正しいペアなのか,人間が見て判別することのできる事例なのか等々から始めましょう

すみませんもう一点気になったことがあります。最適化関数をSGDにすると、LOSSがnanと表示されるのですがこれの原因も分かりますでしょうか?

0Like

Comments

  1. デフォルトの学習率をそのまま使ったからnanになった.とかでしょうか.

model.summary()

のすぐ上に下記を追加したら、どうなりますでしょうか?

for layer in model.layers[:-3]:
   layer.trainable = False
for layer in model.layers[-3:]:
   layer.trainable = True
0Like

Your answer might help someone💌