LoginSignup
noname20220504
@noname20220504

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Conv1Vで画像分類を試していますが、lossが0のまま変化しません。

解決したいこと

2種類の画像データを読み取って、Cov1Dで分類するコードを書きましたが、lossが0のまま変わらないため、正しく判定することができません。

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

accuracyは学習とともに値が変わっているのに対し、lossは0のまま変化がありません。

例)

Epoch 50/50
13/13 [==============================] - 5s 369ms/step - loss: 0.0000e+00 - accuracy: 0.7087 - val_loss: 0.0000e+00 - val_accuracy: 0.0000e+00

該当するソースコード

# -*- coding: utf-8 -*-
"""
Created on Thu Mar 10 19:46:13 2022

@author: 
"""
from tensorflow.keras.layers import Input,Dense
from tensorflow.keras.models import Model
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from PIL import Image
import os
import pprint
from sklearn.metrics import confusion_matrix
from sklearn import metrics
from matplotlib import cm
import tensorflow as tf
from tensorflow import keras
from scipy.special import boxcox, inv_boxcox
from scipy import stats

img_width, img_height =64, 64 

# 学習用のデータを読み取る.
image_list_train = []
label_list_train = []

# ./data/train 以下のcosmosディレクトリ以下の画像を読み込む。
for dir in os.listdir("data/train"):
    if dir == ".DS_Store":
        continue

    dir1 = "data/train/" + dir 
    label = 0

    if dir == "cosmos": # cosmosはラベル0
        label = 0
    elif dir == "sakura": # sakuraはラベル1 
        label = 1

    for file in os.listdir(dir1):
        if file != ".DS_Store":
            # 配列label_listに正解ラベルを追加(cosmos:0 sakura:1)
            label_list_train.append(label)
            filepath = dir1 + "/" + file
            # 画像をn×n(n=50)pixelに変換し、1要素が[R,G,B]3要素を含む配列のnxnの2次元配列として読み込む。
            # [R,G,B]はそれぞれが0-255の配列。
            image = Image.open(filepath).resize((img_width, img_height))
            image = image.convert("RGB")
            image = np.array(image)
            # 配列を変換し、[[Redの配列],[Greenの配列],[Blueの配列]] のような形にする。
            # さらにフラットな1次元配列に変換。最初の1/3はRed、次がGreenの、最後がBlueの要素がフラットに並ぶ。
            image = image.reshape(1, image.shape[0] * image.shape[1] * image.shape[2]).astype("float32")[0]
            # 出来上がった配列をimage_listに追加。
            image_list_train.append(image / 255.)

# kerasに渡すためにnumpy配列に変換。
x_input = np.array(image_list_train)
print("x_input",x_input.shape)

# ラベルの配列を1と0からなるラベル配列に変更
#dataset_ytr=np.array(label_list_train)
y_input=np.array(label_list_train)
print("y_input",y_input.shape)

# 判定用のデータを読み取る.
image_list_test= []
label_list_test = []

# ./data/test 以下のcosmos,sakuraディレクトリ以下の画像を読み込む。
for dir in os.listdir("data/test"):
    if dir == ".DS_Store":
        continue

    dir1 = "data/test/" + dir 
    label = 0

    if dir == "cosmos": # cosmosはラベル0
        label = 0
    elif dir == "sakura": # sakuraはラベル1
        label = 1

    for file in os.listdir(dir1):
        if file != ".DS_Store":
            # 配列label_listに正解ラベルを追加(cosmos:0, sakura:1)
            label_list_test.append(label)
            filepath = dir1 + "/" + file
            # 画像をn×n(n=50)pixelに変換し、1要素が[R,G,B]3要素を含む配列のnxnの2次元配列として読み込む。
            # [R,G,B]はそれぞれが0-255の配列。
            image = Image.open(filepath).resize((img_width, img_height))
            image = image.convert("RGB")
            image = np.array(image)
            # 配列を変換し、[[Redの配列],[Greenの配列],[Blueの配列]] のような形にする。
            # さらにフラットな1次元配列に変換。最初の1/3はRed、次がGreenの、最後がBlueの要素がフラットに並ぶ。
            image = image.reshape(1, image.shape[0] * image.shape[1] * image.shape[2]).astype("float32")[0]
            # 出来上がった配列をimage_listに追加。
            image_list_test.append(image / 255.)

# kerasに渡すためにnumpy配列に変換。
x_test = np.array(image_list_test)
#x_test = np.transpose(0, 1, 2)
print("x_test", x_test.shape)
  
# ラベルの配列を1と0からなるラベル配列に変更
y_test= np.array(label_list_test)
print("y_test", y_test.shape)

from keras.layers.convolutional import Conv1D, MaxPooling1D
from keras.models import Sequential
from tensorflow.keras import layers
from keras.layers.core import Dense, Dropout, Activation, Flatten
from keras import optimizers

model = Sequential()

model.add(Conv1D(8, kernel_size=3, input_shape=(12288,1)))
model.add(Activation('relu'))
model.add(Flatten())
model.add(Dense(800))
model.add(Activation('relu'))
model.add(Dense(400))
model.add(Activation('relu'))
model.add(Dense(200))
model.add(Activation('relu'))
model.add(Dense(100))
model.add(Activation('relu'))
model.add(Dense(1))
model.add(Activation('sigmoid'))
model.summary()

model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['accuracy'])
history = model.fit(x_input, y_input, batch_size=8, epochs=50,validation_split=0.1, verbose = 1)

y_pred = model.predict(x_test)
print("y_pred",y_pred.shape)
np.savetxt('y_pred.csv',y_pred)

from sklearn.metrics import accuracy_score
from sklearn.metrics import precision_score
from sklearn.metrics import recall_score
from sklearn.metrics import f1_score
Accuracy = accuracy_score(y_test, y_pred)*100
Precision = precision_score(y_test, y_pred)*100
Recall = recall_score(y_test, y_pred)*100
F1 = f1_score(y_test, y_pred)*100
report_list = ['Accuracy', 'Precision', 'Recall', 'F1']
report_value = [Accuracy, Precision, Recall, F1]

# Confution_matrix
cm = confusion_matrix(y_test, y_pred)
print(cm)

print("Accuracy", Accuracy)
print("Precision", Precision)
print("Recall", Recall)
print("F1", F1)

# ROC曲線
cnt=0
point=[0,0]
roc=[]
roc.append([0,0])
rank=list(reversed(np.argsort(y_test)))
error=np.sum((y_test.astype("int")==1))
for ridx in rank:
    if y_test[ridx]==1:
        point[1]=point[1]+1
        cnt=cnt+1
    else:
        point[0]=point[0]+1
    roc.append([point[0],point[1]])
    if cnt==error:
        roc.append([y_test.shape[0],point[1]])
        break
roc=np.array(roc)
np.savetxt("roc.txt",roc)
plt.plot(roc[:,0]/y_test.shape,roc[:,1]/error)
plt.plot([0,1],color='black',linestyle='dashed')
plt.savefig("ROC_curve.png")
plt.show()

# Plot training & validation loss values
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title('Model loss')
plt.ylabel('Loss')
plt.xlabel('Epoch')
plt.legend(['Train', 'Test'], loc='upper left')
plt.savefig("loss-epoch.png")
plt.show()

例)

def greet
  puts Hello World
end

自分で試したこと

optimizerをadamからsgdにしたところ、lossがNanになりました。

0

2Answer

This answer has been deleted for violation of our Terms of Service.

Comments

  1. @noname20220504

    Questioner

    64(画像縦方向のピクセル数)×64(画像横方向のピクセル数)×(原色数)(=12288)からなる3次元の画像データセット(テンソル)を1次元のデータセット(12288,1)(ベクトル)に変換しています。

  2. なるほど,image = image.reshape(1, image.shape[0] * image.shape[1] * image.shape[2]).astype("float32")[0]という操作で12288のサイズを実現しているわけですね.
    では,

    # 配列を変換し、[[Redの配列],[Greenの配列],[Blueの配列]] のような形にする。
    # さらにフラットな1次元配列に変換。最初の1/3はRed、次がGreenの、最後がBlueの要素がフラットに並ぶ。
    image = image.reshape(1, image.shape[0] * image.shape[1] * image.shape[2]).astype("float32")[0]
    

    の箇所でのreshapeは,コメントの通りに動作していないことはご存知でしょうか.
    具体的には,[Rの配列,Gの配列,Bの配列]の順ではなく,[Rの0番目,Gの0番目,Bの0番目,Rの1番目,Gの1番目,Bの1番目,...]となっているということです.これをコメントの通りに実装するには,現状のChannel Lastの形式からChannel Firstの形式にtransposeしてからreshapeするのがベストです.

    弊環境で再現するにあたり色々聞きたいところですが,直感的にはモデルでConv1Dが1レイヤしか採用されていない点やbatch_sizeが小さい点,optimizer = 'adam'としていて,学習率をデフォルトのまま利用している点,loss = 'sparse_categorical_crossentropy'が悪さしている可能性を否定できません.特に治すならloss = 'binary_crossentropy'にしてみたり,学習率を小さくすることでNaNが出ることを防ぐ可能性があります.

    そもそも画像処理CNNの常套手段であるConv2Dを使わずConv1Dなのか教えていただけるとモデル改善を提案することもできます.現状,1つのkernelがkernel_size=3,strides=1[R0, G0, B0, R1, G1, B1, ...]の配列を畳み込むことを考えてみるとモデルが破綻していることがよくわかると思います.

  3. 先ほど検証したところ,loss='sparse_categorical_crossentropy'であることが悪さしているようです.lossがNaNになりました.したがって今回のタスクに適さない損失関数です.loss = 'binary_crossentropy'にしましょう.ちなみに精度はランダム推測と同等の50%を記録しました.これはモデルの改善で修正可能です.

  4. @noname20220504

    Questioner

    ご教示いただきありがとうございます。Conv1Dにしたのはあくまでも練習用で、Conv2Dが実装できたのでConv1Dも試してみようという感じで行ったものです。
    バッチサイズが小さいのは、もとのデータ数が100個程度しかないため、このように設定しました。
    また、ご教示いただいた通り、損失関数をbinary_crossentropyにしたところ、lossがNaNになるという事象はなくなりました。
    学習率などその他のパラメータについても見直したいと思います。

  5. なるほど,非常に良い試みだと思います.まずは前処理から見直しましょう.

    従来Conv2Dで処理されるような元々の画像バッチ(N, W, H, C)であったのをConv1Dで処理するために(N, W * H, C)形式にしたりするところを,貴コードでは(N, W * H * C, 1)にしてしまっており精度の低下に繋がっています.

    余力があればViT等に使われるようにパッチをあてたtoken形式にして入力データとしましょう.

  6. @noname20220504

    Questioner

    レスポンスが遅くなってしまいすみません。前処理についてあれこれ試してみたのですが、自分のコーディング能力のなさのせいがエラーが出てしまい上手くいきませんでした。
    代わりに、学習・判定のところで、
    nb_class = 2
    nb_features = 12288
    model = Sequential()
    model.add(Conv1D(filters=16, kernel_size=2, input_shape=(nb_features,1)))
    model.add(Activation('relu'))
    model.add(Flatten())
    #model.add(Dropout(0.4))
    model.add(Dense(512, activation='relu'))
    #model.add(Dense(128, activation='relu'))
    model.add(Dense(nb_class))
    model.add(Activation('sigmoid'))

    model.summary()

    #model=Model(inputs=input_layer,outputs=output_layer)
    model.compile(loss='sparse_categorical_crossentropy',optimizer='adam', metrics=['accuracy'])
    #earstop=EarlyStopping(monitor='val_loss',min_delta=0.0,patience=5,)

    history= model.fit(x_input,y_input,batch_size=8,epochs=100,validation_split=0.05)#,callbacks=[earstop])

    y_pred = model.predict(x_test)
    print("y_pred",y_pred.shape)
    np.savetxt('y_pred.csv',y_pred)

    a=[]
    for i in range(123):
    if y_pred[i][0] > y_pred[i][1]:
    a.append(0)
    else:
    a.append(1)
    #print("a",a)
    np.savetxt('a.csv', a)
    y_pred=a
    としたところ、正解率・F値ともに90%を超え、高い学習性能を示しました。

  7. 正解率・F値はどのように算出したのでしょうか.
    貴モデルは性能が良すぎて過学習するはずです

  8. @noname20220504

    Questioner

    正解率=(TP+TN)/(TP+FP+TN+FN)
    F値:適合率と再現率の調和平均
    で算出しました。ここでは陽性:コスモス、陰性:桜としました。

  9. @noname20220504

    Questioner

    ちなみに、損失関数と学習状況は下記の通りとなりました。
    今回の計算では、教師データと評価データが非常によく似ていたので、このような精度になってもおかしくはないのかなと思っていたのですが、やはり過学習でしょうか・・・
    image.png
    image.png

  10. やはりval_lossがずっと0,val_accuracyが1であり続けているのが気になります.
    trainingデータ(x_input, y_input)は何個ありますでしょうか.validation_split=0.05ということは,len(x_input)*0.05件がvalidationに使われます.100件しかないのでしたらたった5件ということになってしまいます(これは一般的な配分ではありません)

    教師データと評価データが非常によく似ていた

    というのは,具体的にどのような状態なのでしょうか.
    「画角を少しだけ変えて同じ花を再撮影した.」とかなのでしょうか

  11. @noname20220504

    Questioner

    trainデータは全部で120個になります。validation_splitについては、当初0.20や0.10としていたのですが、ためしに0.05でやったところ正解率・F値が改善されたので、その値としていました。ただ、仰るように、全体のデータ数に比して小さすぎたかもしれません。
    教師データと評価データが非常によく似ていたというのは、仰る通りで相違ございません。

  12. 力不足で申し訳ないのですが,手元のCIFAR10のデータでcatとdogのみ抜き出して分類を試みても再現できません.
    一応,cross validationをやってみて評価するということはできると思います.

  13. @noname20220504

    Questioner

    レスポンスが遅くなってしまい申し訳ありません。
    再現できないとのこと承知しました。こちらでも交差検証とあわせ、データを変えて試してみたいと思います。

Your answer might help someone💌