Добавление классификатора машинного обучения в конце слоя CNN

Вопрос или проблема

Я хотел использовать CNN в качестве извлекателя признаков для моих изображений, а затем передать эти признаки некоторым классификаторам машинного обучения, таким как SVM, дерево решений и KNN. Однако, когда я пытался использовать SVM, я получил следующее сообщение об ошибке:

File “C:\Users\Afef-\Anaconda3\lib\site-packages\sklearn\svm\base.py”, line 521, in _validate_targets
” class” % len(cls))
ValueError: Количество классов должно быть больше одного; получен 1 класс

Вот мой код:

import os
import numpy as np
from sklearn.metrics import confusion_matrix
from plot_metrics import plot_accuracy, plot_loss, plot_roc_curve
from keras.models import Sequential
from keras.layers import Dense, Dropout, Activation, Flatten
from keras.layers import Conv2D, MaxPooling2D
from keras.utils import np_utils
from keras import backend as K
K.set_image_dim_ordering('th')
np.random.seed(15)  


"""
Используя бэкэнд Theano и порядок размерностей изображения Theano:
(# каналов, # изображений, # строк, # столбцов)
(1, 3040, 513, 125)
"""

def preprocess(X_train, X_test):
    """
    Преобразовать из float64 в float32 и нормализовать до децибел
    относительно полного масштаба (dBFS) для 4-секундного фрагмента.
    """
    X_train = X_train.astype('float32')
    X_test = X_test.astype('float32')

    X_train = np.array([(X - X.min()) / (X.max() - X.min()) for X in X_train])
    X_test = np.array([(X - X.min()) / (X.max() - X.min()) for X in X_test])
    return X_train, X_test


def prep_train_test(X_train, y_train1, X_test, y_test1, nb_classes):
    """
    Подготовить образцы и метки для ввода Keras, нормализуя и преобразовывая
    метки в категориальное представление.
    """
    print('Обучение на {} образцах, валидация на {}'.format(X_train.shape[0],
                                                           X_test.shape[0]))

    # нормализовать до dBfS
    X_train, X_test = preprocess(X_train, X_test)

    # Преобразовать векторы классов в бинарные матрицы классов
    Y_train1 = np_utils.to_categorical(y_train, nb_classes)
    Y_test1 = np_utils.to_categorical(y_test, nb_classes)

    return X_train, X_test, Y_train1, Y_test1


def keras_img_prep(X_train, X_test, img_dep, img_rows, img_cols):
    """
    Изменить размер матриц признаков для ожидаемых входных размеров Keras.
    Для 'th' (Theano) порядок размерностей ожидает размеры:
    (# каналов, # изображений, # строк, # столбцов).
    """
    if K.image_dim_ordering() == 'th':
        X_train = X_train.reshape(X_train.shape[0], 1, img_rows, img_cols)
        X_test = X_test.reshape(X_test.shape[0], 1, img_rows, img_cols)
        input_shape = (1, img_rows, img_cols)
    else:
        X_train = X_train.reshape(X_train.shape[0], img_rows, img_cols, 1)
        X_test = X_test.reshape(X_test.shape[0], img_rows, img_cols, 1)
        input_shape = (img_rows, img_cols, 1)
    return X_train, X_test, input_shape


def cnn(X_train, y_train1, X_test, y_test1, batch_size,
        nb_classes, epochs, input_shape):
    """
    Архитектура свёрточной нейронной сети для классификации аудиофрагментов
    как нормальные (0) или депрессивные (1).
    """
    model = Sequential()

    model.add(Conv2D(32, (3, 3), padding='valid', strides=1,
                     input_shape=input_shape, activation='relu'))

    model.add(MaxPooling2D(pool_size=(4, 3), strides=(1, 3)))

    model.add(Conv2D(32, (1, 3), padding='valid', strides=1,
              input_shape=input_shape, activation='relu'))

    model.add(MaxPooling2D(pool_size=(1, 3), strides=(1, 3)))

    model.add(Flatten())
    model.add(Dense(512, activation='relu'))
    model.add(Dense(512, activation='relu'))
    model.add(Dropout(0.5))

    model.add(Dense(nb_classes))
    model.add(Activation('softmax'))

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

    history = model.fit(X_train, y_train1, batch_size=batch_size, epochs=epochs,
                        verbose=1, validation_data=(X_test, y_test1))


    # Оценка точности на тестовых и тренировочных наборах
    score_train = model.evaluate(X_train, y_train1, verbose=0)
    print('Точность на обучающей выборке:', score_train[1])
    score_test = model.evaluate(X_test, y_test1, verbose=0)
    print('Точность на тестовой выборке:', score_test[1])
#    print(X_train.shape, X_test.shape, y_train.shape, y_test.shape)
    return model, history



if __name__ == '__main__':


    print('Извлечение локально')
    X_train = np.load('E:/depression detection/data/processed/train_samples.npz')
    y_train = np.load('E:/depression detection/data/processed/train_labels.npz')
    X_test = np.load('E:/depression detection/data/processed/test_samples.npz')
    y_test = np.load('E:/depression detection/data/processed/test_labels.npz')

    X_train, y_train, X_test, y_test = \
        X_train['arr_0'], y_train['arr_0'], X_test['arr_0'], y_test['arr_0']

    # Параметры CNN
    batch_size = 32
    nb_classes = 2
    epochs = 7

    # нормализовать данные и подготовить для Keras
    print('Обработка изображений для Keras...')
    X_train, X_test, y_train1, y_test1 = prep_train_test(X_train, y_train,
                                                       X_test, y_test,
                                                       nb_classes=nb_classes)

    # 513x125x1 для спектрограммы с размером обрезки 125 пикселей
    img_rows, img_cols, img_depth = X_train.shape[1], X_train.shape[2], 1

    # изменить размер входного изображения для Keras
    # использован порядок размерностей Theano (th), (# каналов, # изображений, # строк, # столбцов)
    X_train, X_test, input_shape = keras_img_prep(X_train, X_test, img_depth,
                                                  img_rows, img_cols)


    # запустить CNN
    print('Обучение модели...')
    model, history = cnn(X_train, y_train1, X_test, y_test1, batch_size,
                         nb_classes, epochs, input_shape)



    for l in range(len(model.layers)):
      print(l, model.layers[l])

     # слой извлечения признаков
    getFeature = K.function([model.layers[0].input, K.learning_phase()],
                       [model.layers[7].output])
# классификационный слой
    getPrediction = K.function([model.layers[8].input, K.learning_phase()],
                           [model.layers[9].output])

    exTrain = getFeature([X_train[:30], 0])[0]
    exTest = getFeature([X_test[:30], 0])[0]
    y_train00 = y_train[:30]
    y_test00 = y_test[:30]
    from sklearn.svm import SVC
    clf = SVC(gamma="auto")
    clf.fit(exTrain, y_train00)

Проверьте, чтобы y_test[:30] содержал более одного класса.

Ответ или решение

Вам нужно решить проблему с использованием классификатора SVM после слоя извлечения признаков из вашей сверточной нейронной сети (CNN). Ошибка, которую вы получаете:

ValueError: The number of classes has to be greater than one; got 1 class

указывает на то, что в вашем обучающем наборе данных, который вы передаёте SVM для обучения, присутствует только один класс. Это может произойти по нескольким причинам. Давайте рассмотрим шаги, которые можно предпринять, чтобы устранить эту проблему.

1. Проверка классов в данных

Сначала убедитесь, что в ваших метках присутствует более одного класса. Это можно сделать, используя следующую проверку:

print(np.unique(y_train00))  # Посмотрим на уникальные классы в y_train00

Если в результате вы увидите только один класс, вам нужно проверить, правильно ли вы передали данные и насколько хорошо вы их разбили на обучающий и тестовый наборы.

2. Убедитесь в наличии достаточного количество образцов для каждого класса

Ваша выборка для обучения должна содержать достаточно экземпляров для каждого класса. Если у вас имеется дисбаланс в классах, это может привести к ситуации, когда вы не получите представленных всех классов в небольшом подмножестве, используемом для обучения или тестирования.

3. Проверьте процесс подготовки данных

Убедитесь, что на этапе подготовки данных вы правильно составляете ваши метки. В функции prep_train_test, передавайте корректные метки и проверьте, правильно ли осуществляется их преобразование в категориальный формат.

4. Убедитесь, что вы извлекаете признаки корректно

Вы извлекаете признаки с помощью:

exTrain = getFeature([X_train[:30], 0])[0]

Здесь вы берете только первые 30 образцов. Это может привести к тому, что имеющиеся классы не будут представлены, если все 30 образцов относятся к одному классу. Попробуйте использовать больше экземпляров для извлечения, например, весь обучающий набор:

exTrain = getFeature([X_train, 0])[0]
y_train00 = y_train  # Используйте полный y_train

5. Подготовка и обучение модели

Теперь, когда вы удостоверились, что в метках более одного класса и что в обучающем наборе данных правильно представлены классы, теперь можете снова попытаться обучить классификатор SVM:

from sklearn.svm import SVC

# Обучение SVM только если в y_train больше одного класса
if len(np.unique(y_train00)) > 1:
    clf = SVC(gamma="auto")
    clf.fit(exTrain, y_train00)
else:
    print("Ошибка: недостаточно классов для обучения SVM.")

Заключение

Данные шаги должны помочь вам устранить ошибку, с которой вы столкнулись. Убедитесь, что ваши обучающие данные хорошо сбалансированы и правильно структурированы, и вы сможете успешно использовать SVM после извлечения признаков из CNN. Если будут возникать дополнительные проблемы, рекомендую проверить не только сами данные, но и как именно происходит их обработка и классификация.

Оцените материал
Добавить комментарий

Капча загружается...