如何使用 Keras 实现 CNN-LSTM

2023-12-10

我正在尝试实现一个 CNN-LSTM,对代表帕金森病/健康控制者语音的梅尔频谱图像进行分类。我正在尝试使用 LSTM 模型实现预先存在的模型 (DenseNet-169),但是遇到以下错误:ValueError: Input 0 of layer zero_padding2d is incompatible with the layer: expected ndim=4, found ndim=3. Full shape received: [None, 216, 1].谁能告诉我哪里出错了?

import librosa
import os
import glob
import IPython.display as ipd
from pathlib import Path
import timeit
import time, sys

%matplotlib inline
import matplotlib.pyplot as plt
import librosa.display

import pandas as pd
from sklearn import datasets, linear_model
from sklearn.model_selection import train_test_split
from matplotlib import pyplot as plt
import numpy as np
import cv2
import seaborn as sns

%tensorflow_version 1.x #version 1 works without problems
import tensorflow

from tensorflow.keras import models
from sklearn.preprocessing import LabelEncoder
from tensorflow.keras.utils import to_categorical
from tensorflow.keras.layers import TimeDistributed

import keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.callbacks import EarlyStopping
from sklearn.metrics import confusion_matrix, plot_confusion_matrix
from tensorflow.keras.layers import Conv2D, MaxPooling2D, Flatten, Dropout, Dense, BatchNormalization, Activation, GaussianNoise, LSTM
from sklearn.metrics import accuracy_score

DATA_DIR = Path('/content/drive/MyDrive/PhD_Project_Experiments/Spontaneous_Dialogue_PD_Dataset') 
diagnosis = [x.name for x in DATA_DIR.glob('*') if x.is_dir()]
diagnosis

def create_paths_ds(paths: Path, label: str) -> list:
    EXTENSION_TYPE = '.wav'
    return [(x, label) for x in paths.glob('*' + EXTENSION_TYPE)]

from collections import Counter

categories_to_use = [
    'Parkinsons_Disease',
    'Healthy_Control',
]

NUM_CLASSES = len(categories_to_use)

print(f'Number of classes: {NUM_CLASSES}')

paths_all_labels = []
for cat in categories_to_use:
    paths_all_labels += create_paths_ds(DATA_DIR / cat, cat)
 
X_train, X_test = train_test_split(paths_all_labels,test_size=0.1, stratify = [paths_all_labels[y][1] for y in range(len(paths_all_labels))] ) #fix stratified sampling for test data
X_train, X_val = train_test_split(X_train, test_size=0.2, stratify = [X_train[y][1] for y in range(len(X_train))] ) 

for i in categories_to_use:
  print('Number of train samples for '+i+': '+ str([X_train[y][1] for y in range(len(X_train))].count(i))) #checks whether train samples are equally divided
  print('Number of test samples for '+i+': '+ str([X_test[y][1] for y in range(len(X_test))].count(i))) #checks whether test samples are equally divided
  print('Number of validation samples for '+i+': '+ str([X_val[y][1] for y in range(len(X_val))].count(i))) #checks whether val samples are equally divided

print(f'Train length: {len(X_train)}')
print(f'Validation length: {len(X_val)}')
print(f'Test length: {len(X_test)}')

def load_and_preprocess_lstm(dataset, SAMPLE_SIZE = 30):
    IMG_SIZE = (216,128) 
    progress=0

    data = []
    labels = []
    for (path, label) in dataset:
        audio, sr = librosa.load(path)
        dur = librosa.get_duration(audio, sr = sr)
        sampleNum = int(dur / SAMPLE_SIZE)
        offset = (dur % SAMPLE_SIZE) / 2
        for i in range(sampleNum):
            audio, sr = librosa.load(path, offset= offset+i, duration=SAMPLE_SIZE)
            sample = librosa.feature.melspectrogram(audio, sr=sr)
            # print(sample.shape)
            sample = cv2.resize(sample, dsize=IMG_SIZE)
            sample = np.expand_dims(sample,-1)
            print(sample.shape)
            data += [(sample, label)]
            labels += [label]
        progress +=1
        print('\r Progress: '+str(round(100*progress/len(dataset))) + '%', end='')
    return data, labels

def retrieve_samples(sample_size, model_type):

    if model_type == 'cnn':
  
        print("\nLoading train samples")
        X_train_samples, train_labels = load_and_preprocess_cnn(X_train,sample_size)
        print("\nLoading test samples")
        X_test_samples, test_labels = load_and_preprocess_cnn(X_test,sample_size)
        print("\nLoading val samples")
        X_val_samples, val_labels = load_and_preprocess_cnn(X_val,sample_size)
        print('\n')

    elif model_type == 'lstm':

        print("\nLoading train samples")
        X_train_samples, train_labels = load_and_preprocess_lstm(X_train,sample_size)
        print("\nLoading test samples")
        X_test_samples, test_labels = load_and_preprocess_lstm(X_test,sample_size)
        print("\nLoading val samples")
        X_val_samples, val_labels = load_and_preprocess_lstm(X_val,sample_size)      
        print('\n')

    elif model_type == "cnnlstm":

        print("\nLoading train samples")
        X_train_samples, train_labels = load_and_preprocess_lstm(X_train,sample_size)
        print("\nLoading test samples")
        X_test_samples, test_labels = load_and_preprocess_lstm(X_test,sample_size)
        print("\nLoading val samples")
        X_val_samples, val_labels = load_and_preprocess_lstm(X_val,sample_size)      
        print('\n')

    print("shape: " + str(X_train_samples[0][0].shape))
    print("number of training samples: "+ str(len(X_train_samples)))
    print("number of validation samples: "+ str(len(X_val_samples)))
    print("number of test samples: "+ str(len(X_test_samples)))


    return X_train_samples, X_test_samples, X_val_samples

def create_cnn_lstm_model(input_shape):

    model = Sequential()
    cnn = tensorflow.keras.applications.DenseNet169(include_top=True, weights=None, input_tensor=None, input_shape=input_shape, pooling=None, classes=2)
    # define LSTM model
    model.add(tensorflow.keras.layers.TimeDistributed(cnn, input_shape=input_shape))
    model.add(LSTM(units = 512, dropout=0.5, recurrent_dropout=0.3, return_sequences = True, input_shape = input_shape))
    model.add(LSTM(units = 512, dropout=0.5, recurrent_dropout=0.3, return_sequences = False))
    model.add(Dense(units=NUM_CLASSES, activation='sigmoid'))#Compile

    model.compile(loss=tensorflow.keras.losses.binary_crossentropy, optimizer='adam', metrics=['accuracy'])
    print(model.summary())

    return model

def create_model_data_and_labels(X_train_samples, X_val_samples, X_test_samples):
    #Prepare samples to work for training the model
    labelizer = LabelEncoder()

    #prepare training data and labels
    x_train = np.array([x[0] for x in X_train_samples])
    y_train = np.array([x[1] for x in X_train_samples])
    y_train = labelizer.fit_transform(y_train) 
    y_train = to_categorical(y_train)

    #prepare validation data and labels
    x_val = np.array([x[0] for x in X_val_samples])
    y_val = np.array([x[1] for x in X_val_samples])
    y_val = labelizer.transform(y_val)
    y_val = to_categorical(y_val)

    #prepare test data and labels
    x_test = np.array([x[0] for x in X_test_samples])
    y_test = np.array([x[1] for x in X_test_samples])
    y_test = labelizer.transform(y_test)
    y_test = to_categorical(y_test)

    return x_train, y_train, x_val, y_val, x_test, y_test, labelizer


#Main loop for testing multiple sample sizes

#choose model type: 'cnn' or 'lstm'
model_type = 'cnnlstm'

n_epochs = 20
patience= 20
es = EarlyStopping(patience=20)
fragment_sizes = [5,10]
start = timeit.default_timer()

ModelData = pd.DataFrame(columns = ['Model Type','Fragment size (s)', 'Time to Compute (s)',  'Early Stopping epoch', 'Training accuracy', 'Validation accuracy', 'Test Accuracy']) #create a DataFrame for storing the results 

conf_matrix_data = []

for i in fragment_sizes:

    start_per_size = timeit.default_timer()

    print(f'\n---------- Model trained on fragments of size: {i} seconds ----------------')
    X_train_samples, X_test_samples, X_val_samples = retrieve_samples(i,model_type)
    x_train, y_train, x_val, y_val, x_test, y_test, labelizer = create_model_data_and_labels(X_train_samples, X_val_samples, X_test_samples)

    if model_type == 'cnn':
        model = create_cnn_model(X_train_samples[0][0].shape)
    elif model_type == 'lstm':
        model = create_lstm_model(X_train_samples[0][0].shape)
    elif model_type == 'cnnlstm':
        model = create_cnn_lstm_model(X_train_samples[0][0].shape)


    history = model.fit(x_train, y_train, 
              batch_size = 8, 
              epochs=n_epochs,
              verbose=1, 
              callbacks=[es],
              validation_data=(x_val, y_val))
    print('Finished training')


    early_stopping_epoch = len(history.history['accuracy'])
    training_accuracy = history.history['accuracy'][early_stopping_epoch-1-patience]
    validation_accuracy = history.history['val_accuracy'][early_stopping_epoch-1-patience]

    plot_data(history, i)

    predictions = model.predict(x_test)
    score = accuracy_score(labelizer.inverse_transform(y_test.argmax(axis=1)), labelizer.inverse_transform(predictions.argmax(axis=1)))

    print('Fragment size = ' + str(i) + ' seconds')
    print('Accuracy on test samples: ' + str(score))
    
    conf_matrix_data += [(predictions, y_test, i)]

    stop_per_size = timeit.default_timer()
    time_to_compute = round(stop_per_size - start_per_size)

    print ('Time to compute: '+str(time_to_compute))

    ModelData.loc[len(ModelData)] = [model_type, i, time_to_compute, early_stopping_epoch, training_accuracy, validation_accuracy, score] #store particular settings configuration, early stoppping epoch and accuracies in dataframe

stop = timeit.default_timer()
print ('\ntime to compute: '+str(stop-start))

我相信 input_shape 是 (128, 216, 1)

这里的问题是,您没有时间轴来时间分布 CNN (DenseNet169) 层。

在这一步中——

tensorflow.keras.layers.TimeDistributed(cnn, input_shape=(128,216,1)))

您将 128 维轴作为时间轴传递。这意味着每个 CNN (DenseNet169) 的输入形状为(216,1),它不是图像,因此会引发错误,因为它需要 3D 张量(图像)而不是 2D 张量。

您的输入形状需要是 4D 张量,例如 -(10, 128, 216, 1), 所以这样10成为时间轴(用于时间分布),并且(128, 216, 1)成为 CNN (DenseNet169) 的图像输入。


具有不规则张量和时间分布层的解决方案

IIUC,您的数据包含 n 个音频文件,每个文件包含可变数量的梅尔频谱图图像。

  1. 你需要使用tf.raggedtensors能够使用可变张量形状作为模型的输入
  2. 这需要您在其中设置的输入层的显式定义ragged=True
  3. 这允许您将每个音频文件作为单个样本传递,并带有可变图像,每个图像都将按时间分布。
  4. 你将不得不使用None作为定义模型时的时间分布轴形状

1. 创建虚拟数据集

让我们从一个示例数据集开始 -

import tensorflow as tf
from tensorflow.keras import layers, Model, utils, applications


#Assuming there are 5 audio files
num_audio = 5

data = []

#Create a random number of mel-spectrograms for each audio file
for i in range(num_audio):
    n_images = np.random.randint(4,10)
    data.append(np.random.random((n_images,128,216,1)))
    
print([i.shape for i in data])
[(5, 128, 216, 1), 
 (5, 128, 216, 1), 
 (9, 128, 216, 1), 
 (6, 128, 216, 1), 
 (4, 128, 216, 1)]

所以,您的数据应该看起来像这样。在这里,我有一个包含 5 个音频文件的虚拟数据集,第一个有 5 个形状图像(128,216,1),而最后一张有 4 个相同形状的图像。

2. 将它们转换为不规则张量

接下来,让我们转换并存储这些不规则张量。参差不齐的张量允许存储可变长度的对象,在本例中是可变数量的图像。了解更多关于他们的信息here.

enter image description here

#Convert each set of images (for each audio) to tensors and then a ragged tensor
tensors = [tensorflow.convert_to_tensor(i) for i in data]
X_train = tensorflow.ragged.stack(tensors).to_tensor()

#Creating dummy y_train, one for each audio files
y_train = tensorflow.convert_to_tensor(np.random.randint(0,2,(5,2)))

3. 创建模型

我正在使用一个functional API因为我发现它更具可读性并且与显式输入层配合使用效果更好,但是您可以在Sequential API以及。请随意将其转换为您的偏好。

请注意,我正在使用(None,128,216,1)作为输入形状。这将创建 5 个通道(批次的第一个隐式通道):(Batch, audio_files, h, w, channels)

我有一个虚拟 LSTM 层来展示该架构的工作原理,您可以随意堆叠更多层。另外,请注意,您的DenseNet169仅返回 2 个特征。因此你的TimeDistributed图层正在返回(None, None, 2)形张量,其中首先None是音频文件的数量,第二个None是图像数量(时间轴)。因此,请相应地选择下一层,因为 512 个 LSTM 单元可能太多了:)

#Create model
inp = layers.Input((None,128,216,1), ragged=True)

cnn = tensorflow.keras.applications.DenseNet169(include_top=True, 
                                                weights=None, 
                                                input_tensor=None, 
                                                input_shape=(128,216,1), #<----- input shape for cnn is just the image
                                                pooling=None, classes=2)


#Feel free to modify these layers!
x = layers.TimeDistributed(cnn)(inp)
x = layers.LSTM(8)(x)
out = layers.Dense(2)(x)

model = Model(inp, out)
model.compile(loss='binary_crossentropy', 
              optimizer='adam', 
              metrics='accuracy')

utils.plot_model(model, show_shapes=True, show_layer_names=False)

enter image description here

4. 训练!

下一步就是训练。请随意添加您自己的参数。

model.fit(X_train, y_train, epochs=2)
Epoch 1/2
WARNING:tensorflow:5 out of the last 5 calls to <function Model.make_train_function.<locals>.train_function at 0x7f8e55b4fe50> triggered tf.function retracing. Tracing is expensive and the excessive number of tracings could be due to (1) creating @tf.function repeatedly in a loop, (2) passing tensors with different shapes, (3) passing Python objects instead of tensors. For (1), please define your @tf.function outside of the loop. For (2), @tf.function has experimental_relax_shapes=True option that relaxes argument shapes that can avoid unnecessary retracing. For (3), please refer to https://www.tensorflow.org/guide/function#controlling_retracing and https://www.tensorflow.org/api_docs/python/tf/function for  more details.
1/1 [==============================] - 37s 37s/step - loss: 3.4057 - accuracy: 0.4000
Epoch 2/2
1/1 [==============================] - 16s 16s/step - loss: 3.3544 - accuracy: 0.4000

希望有帮助。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何使用 Keras 实现 CNN-LSTM 的相关文章

随机推荐

  • Asterisk-如何使用 ChanSpy() 或任何替代方法“耳语”音乐? [关闭]

    Closed 这个问题需要多问focused 目前不接受答案 我打算向其中一位会议参与者 耳语 音乐 消息文件 而不让其他会议参与者听到此消息 我浏览了 Asterisk Cookbook 中的音频操作章节 http ofps oreill
  • 如何在 Objective-C iphone sdk 中迭代嵌套字典

    您好 我有一个 json 字符串 已通过 JSON 框架转换为字典 我需要提取其内容 我怎样才能迭代到嵌套字典 我已经有了这段代码 可以让我查看字典 NSDictionary results responseString JSONValue
  • 如何使用 nav.popTo() (ionic 2)?

    我正在使用导航控制器 要返回 我可以使用 nav pop 但是如果我需要转到其他页面 不是最后一个页面 如何使用 nav popTo constructor nav NavController this nav nav this nav p
  • Numpy int 位长度

    我想找到以二进制表示无符号 numpy 整数 或整数数组中的每个元素 所需的位数 就像 python 的int bit length 确实如此 但 numpy 似乎没有等效的功能 例如 gt gt gt int 0b1000 bit len
  • 如何防止 Dockerfile 缓存 git clone

    我有一个 Dockerfile 试图将 Web 应用程序打包并部署到容器中 应用程序的代码在 Docker 镜像构建期间从 git 存储库获取 这是 Dockerfile 快照 RUN git clone depth 1 git repos
  • 在 openpyxl 中格式化图表数据标签

    我正在使用 Python 3 6 3 使用 openpyxl 2 4 9 编写一些 Excel 工作表 在图表数据上获取数据标签并不明显 但当我尝试格式化所述数据标签时 事情开始变得糟糕 我想要做的是改变他们的位置并改变他们的轮换 有人有什
  • PDO Mysql 语法错误 1064 [已关闭]

    这个问题不太可能对任何未来的访客有帮助 它只与一个较小的地理区域 一个特定的时间点或一个非常狭窄的情况相关 通常不适用于全世界的互联网受众 为了帮助使这个问题更广泛地适用 访问帮助中心 我运行以下代码 conn new PDO pdo at
  • PyQt 中的 QKeyPress 事件

    我的软件有问题 在我的钢琴软件中 如果我一直按下键盘上的某个键 那么它就会发出该特定键的多个重复的相同音调 但实际上我需要一个单一的音调 直到释放该特定的键 我提供了检测 keyPress 事件并调用相应方法的代码的一部分 那么我应该对我的
  • 用于 HTTPS 抓取的 Jsoup Cookie

    我正在尝试使用此网站在欢迎页面上收集我的用户名来学习 Jsoup 和 Android 使用以下代码 Connection Response res Jsoup connect http www mikeportnoy com forum l
  • 如何将 Spinner 默认值设置为 null?

    我正在尝试加载一个没有选定值的微调器 一旦用户选择了一个值 它就会将他们带到另一个页面 事实证明这是一个问题 因为目前页面只是在用户做出选择之前立即加载 我的 spinner 类的设置方式与 Google 的相同 http develope
  • 使用 document.body.innerHTML.replace 有多安全?

    正在运行类似的东西 document body innerHTML document body innerHTML replace 旧值 新值 危险的 我担心某些浏览器可能会搞乱整个页面 而且由于这是 JS 代码 这些代码将被放置在我无法控
  • 如何获取当前 .exe 的哈希值?

    SOLVED 我复制了该文件并在该副本上运行了哈希器 我需要我的应用程序来查找 EXE 当前的 MD5 我可以获取任何文件的MD5 但是 无论我做什么 我都无法获得 FileStream 来读取打开的 EXE 我尝试使用 FileOptio
  • 修改 google.translate.TranslateElement 结果中的元素

    我正在尝试将非常方便的 Google Translate 翻译元素嵌入到网页中 这非常简单并且效果很好 但我需要更改在生成的 HTML 中显示的默认文本 在使用过许多 Google API 和 js 库后 我认为这不会有问题 因为它几乎肯定
  • 创建 HuggingFace 数据集来训练 BIO 标记器

    我有一个字典列表 sentences text I live in Madrid labels O O O B LOC text Peter lives in Spain labels B PER O O B LOC text He lik
  • 使用处理程序从服务更新活动

    我想改变 a 的状态ToggleButton in my Activity当事件发生在service 任何人都可以帮我实现这个使用Handler 我的意思是我应该在哪里编写代码来创建处理程序以及如何从服务触发它 我读过类似的帖子 但是它们对
  • 地点选择器自动关闭

    我正在 android 中制作一个简单的地点选择器程序 问题是当我单击按钮时 地点选择器打开并在 2 3 秒后自动关闭 请帮忙 MainActivity java package com example akshay myapplicati
  • Android 发布高分辨率图像内存不足

    各位开发人员大家好 我正忙着让 android 从应用程序上传图像 我也让它工作了 代码如下 但是 当我发送大图像 10 兆像素 时 我的应用程序因内存不足异常而崩溃 解决方案是使用压缩 但如果我想发送全尺寸图像怎么办 我想也许与流有关 但
  • UINavigationController:每次转换后显示具有不同方向的嵌入式视图控制器?

    这是 StackOverflow 上的一个常见问题 但其他解决方案均无效 许多也是几年前写的 以下是一些考虑的帖子 UINavigationController 内的 viewController 是否可以有不同的方向 UINavigati
  • 我可以在返回 void 的函数中使用 return 语句吗?

    我必须返回到递归的上一级 下面的语法对吗 void f some code here return 是的 您可以从 void 函数返回 有趣的是 您还可以从 void 函数返回 void 例如 void foo return void 正如
  • 如何使用 Keras 实现 CNN-LSTM

    我正在尝试实现一个 CNN LSTM 对代表帕金森病 健康控制者语音的梅尔频谱图像进行分类 我正在尝试使用 LSTM 模型实现预先存在的模型 DenseNet 169 但是遇到以下错误 ValueError Input 0 of layer