首先刚接手这种关于信号的分类问题,以下可能会有不对的地方,接下去通过学习会对不正确的地方进行更正或者补充。

心电信号分类参考文献:Cardiologist-LevelArrhythmiaDetectionwithConvolutionalNeuralNetworks
目标:对传感器采集的某一维信号数据(time-amplitude)进行分类,比如通过一段一维心音信号进行正常/异常的分类。

方法:这里采用端到端通过卷积神经网络的方式对一段时域的一维心音信号得到正常/异常的类别诊断(本质是分类模型的搭建),卷积神经网络使用参考文献中的,结构如下所示:

网络结构参数:

输入shape:(batch_size,steps,1),steps必须是256的倍数,对应的label shape:(batch_size,steps/256,class_number),由于网络下采样256倍,这样最后feature map的输出shape(无Flatten层,所以输出有3个维度):(batch_size,steps/下采样倍数256,softmax_class_number)与label shape才能匹配上。从这里可以看出,对应输入的steps个采样点,每256个采样点有一个label,那么steps个采样点就有steps/256个label

网络从input->output共下采样256倍(每两个resnet block进行2倍下采样)

共16个resnet block,每四个resnet block增加2倍滤波器的数量,分别为32,64,128,256

卷积核:1*1 kernel size

Maxpooling中设置padding=‘same’

具体方法:

1、心音数据预处理:

原因:不同数据集的音频数据制作标准会有差异,这种差异包括:音频采样率,通道数,长短,降噪法,尺度等,所以在对所有数据集进行融合之前需要最大可能地统一这些标准。

 2、特征提取:

心音信号有时域特征和频域特征,由于这里借鉴的是心电信号的分类模型,所以这里只采用一维的时域特征进行分类模型的搭建,即直接拿一维心音信号的采样点强度作为输入(输入的steps必须是256的倍数,且每256个采样点有一个label)。

代码实现:

(1)分类模型的搭建:

from keras import backend as Kdef _bn_relu(layer, dropout=0, **params):from keras.layers import BatchNormalizationfrom keras.layers import Activationlayer = BatchNormalization()(layer)layer = Activation(params["conv_activation"])(layer)if dropout > 0:from keras.layers import Dropoutlayer = Dropout(params["conv_dropout"])(layer)return layerdef add_conv_weight(layer,filter_length,num_filters,subsample_length=1,**params):from keras.layers import Conv1D layer = Conv1D(filters=num_filters,kernel_size=filter_length,strides=subsample_length,padding='valid',kernel_initializer=params["conv_init"])(layer)return layerdef add_conv_layers(layer, **params):for subsample_length in params["conv_subsample_lengths"]:layer = add_conv_weight(layer,params["conv_filter_length"],params["conv_num_filters_start"],subsample_length=subsample_length,**params)layer = _bn_relu(layer, **params)return layerdef resnet_block(layer,num_filters,subsample_length,block_index,**params):from keras.layers import Add from keras.layers import MaxPooling1Dfrom keras.layers.core import Lambdadef zeropad(x):y = K.zeros_like(x)return K.concatenate([x, y], axis=2)def zeropad_output_shape(input_shape):shape = list(input_shape)assert len(shape) == 3shape[2] *= 2return tuple(shape)shortcut = MaxPooling1D(pool_size=subsample_length, padding='same')(layer)zero_pad = (block_index % params["conv_increase_channels_at"]) == 0 \and block_index > 0if zero_pad is True:shortcut = Lambda(zeropad, output_shape=zeropad_output_shape)(shortcut)for i in range(params["conv_num_skip"]):if not (block_index == 0 and i == 0):layer = _bn_relu(layer,dropout=params["conv_dropout"] if i > 0 else 0,**params)layer = add_conv_weight(layer,params["conv_filter_length"],num_filters,subsample_length if i == 0 else 1,**params)layer = Add()([shortcut, layer])return layerdef get_num_filters_at_index(index, num_start_filters, **params):return 2**int(index / params["conv_increase_channels_at"]) \* num_start_filtersdef add_resnet_layers(layer, **params):layer = add_conv_weight(layer,params["conv_filter_length"],params["conv_num_filters_start"],subsample_length=1,**params)layer = _bn_relu(layer, **params)for index, subsample_length in enumerate(params["conv_subsample_lengths"]):num_filters = get_num_filters_at_index(index, params["conv_num_filters_start"], **params)layer = resnet_block(layer,num_filters,subsample_length,index,**params)layer = _bn_relu(layer, **params)return layerdef add_output_layer(layer, **params):from keras.layers.core import Dense, Activationfrom keras.layers.wrappers import TimeDistributedlayer = TimeDistributed(Dense(params["num_categories"]))(layer)return Activation('softmax')(layer)def add_compile(model, **params):from keras.optimizers import Adamoptimizer = Adam(lr=params["learning_rate"],clipnorm=params.get("clipnorm", 1))model.compile(loss='categorical_crossentropy',optimizer=optimizer,metrics=['accuracy'])def build_network(**params):from keras.models import Modelfrom keras.layers import Inputinputs = Input(shape=params['input_shape'],dtype='float32',name='inputs')if params.get('is_regular_conv', False):layer = add_conv_layers(inputs, **params)else:layer = add_resnet_layers(inputs, **params)output = add_output_layer(layer, **params)model = Model(inputs=[inputs], outputs=[output])if params.get("compile", True):add_compile(model, **params)return model

(2)训练脚本

from __future__ import print_function
from __future__ import division
from __future__ import absolute_importimport argparse
import json
import keras
import numpy as np
import os
import random
import timeimport network
import load
import utilMAX_EPOCHS = 100def make_save_dir(dirname, experiment_name):start_time = str(int(time.time())) + '-' + str(random.randrange(1000))save_dir = os.path.join(dirname, experiment_name, start_time)if not os.path.exists(save_dir):os.makedirs(save_dir)return save_dirdef get_filename_for_saving(save_dir):return os.path.join(save_dir,"{val_loss:.3f}-{val_acc:.3f}-{epoch:03d}-{loss:.3f}-{acc:.3f}.hdf5")def train(args, params):print("Loading training set...")train = load.load_dataset(params['train'])print("Loading dev set...")dev = load.load_dataset(params['dev'])print("Building preprocessor...")preproc = load.Preproc(*train)print("Training size: " + str(len(train[0])) + " examples.")print("Dev size: " + str(len(dev[0])) + " examples.")save_dir = make_save_dir(params['save_dir'], args.experiment)util.save(preproc, save_dir)params.update({"input_shape": [None, 1],"num_categories": len(preproc.classes)})model = network.build_network(**params)stopping = keras.callbacks.EarlyStopping(patience=8)reduce_lr = keras.callbacks.ReduceLROnPlateau(factor=0.1,patience=2,min_lr=params["learning_rate"] * 0.001)checkpointer = keras.callbacks.ModelCheckpoint(filepath=get_filename_for_saving(save_dir),save_best_only=False)batch_size = params.get("batch_size", 32)if params.get("generator", False):train_gen = load.data_generator(batch_size, preproc, *train)dev_gen = load.data_generator(batch_size, preproc, *dev)model.fit_generator(train_gen,steps_per_epoch=int(len(train[0]) / batch_size),epochs=MAX_EPOCHS,validation_data=dev_gen,validation_steps=int(len(dev[0]) / batch_size),callbacks=[checkpointer, reduce_lr, stopping])else:train_x, train_y = preproc.process(*train)dev_x, dev_y = preproc.process(*dev)model.fit(train_x, train_y,batch_size=batch_size,epochs=MAX_EPOCHS,validation_data=(dev_x, dev_y),callbacks=[checkpointer, reduce_lr, stopping])if __name__ == '__main__':parser = argparse.ArgumentParser()parser.add_argument("config_file", help="path to config file")#config_file为神经网络的参数,保存成.json文件parser.add_argument("--experiment", "-e", help="tag with experiment name",default="default")args = parser.parse_args()params = json.load(open(args.config_file, 'r'))train(args, params)

(3)batch_data,batch_label的生成

from __future__ import print_function
from __future__ import division
from __future__ import absolute_importimport json
import keras
import numpy as np
import os
import random
import scipy.io as sio
import tqdmSTEP = 256def data_generator(batch_size, preproc, x, y):num_examples = len(x)examples = zip(x, y)examples = sorted(examples, key = lambda x: x[0].shape[0])end = num_examples - batch_size + 1batches = [examples[i:i+batch_size]for i in range(0, end, batch_size)]random.shuffle(batches)while True:for batch in batches:x, y = zip(*batch)yield preproc.process(x, y)class Preproc:def __init__(self, ecg, labels):self.mean, self.std = compute_mean_std(ecg)self.classes = sorted(set(l for label in labels for l in label))self.int_to_class = dict( zip(range(len(self.classes)), self.classes))self.class_to_int = {c : i for i, c in self.int_to_class.items()}def process(self, x, y):return self.process_x(x), self.process_y(y)def process_x(self, x):x = pad(x)x = (x - self.mean) / self.stdx = x[:, :, None]return xdef process_y(self, y):# TODO, awni, fix hack pad with noise for cincy = pad([[self.class_to_int[c] for c in s] for s in y], val=3, dtype=np.int32) y = keras.utils.np_utils.to_categorical(y, num_classes=len(self.classes))return ydef pad(x, val=0, dtype=np.float32):max_len = max(len(i) for i in x)padded = np.full((len(x), max_len), val, dtype=dtype)for e, i in enumerate(x):padded[e, :len(i)] = ireturn paddeddef compute_mean_std(x):x = np.hstack(x)return (np.mean(x).astype(np.float32),np.std(x).astype(np.float32))def load_dataset(data_json):with open(data_json, 'r') as fid:data = [json.loads(l) for l in fid]labels = []; ecgs = []for d in tqdm.tqdm(data):labels.append(d['labels'])ecgs.append(load_ecg(d['ecg']))return ecgs, labelsdef load_ecg(record):if os.path.splitext(record)[1] == ".npy":ecg = np.load(record)elif os.path.splitext(record)[1] == ".mat":ecg = sio.loadmat(record)['val'].squeeze()else: # Assumes binary 16 bit integerswith open(record, 'r') as fid:ecg = np.fromfile(fid, dtype=np.int16)trunc_samp = STEP * int(len(ecg) / STEP)return ecg[:trunc_samp]if __name__ == "__main__":data_json = "examples/cinc17/train.json"train = load_dataset(data_json)preproc = Preproc(*train)gen = data_generator(32, preproc, *train)for x, y in gen:print(x.shape, y.shape)break

(4)预测的时候用到(归一化),保存训练数据集中一维心音信号的均值,标准差,类别等信息

import os
import cPickle as pickledef load(dirname):preproc_f = os.path.join(dirname, "preproc.bin")with open(preproc_f, 'r') as fid:preproc = pickle.load(fid)return preprocdef save(preproc, dirname):preproc_f = os.path.join(dirname, "preproc.bin")with open(preproc_f, 'w') as fid:pickle.dump(preproc, fid)

(5)模型预测(batch_data的预测)

from __future__ import print_functionimport argparse
import numpy as np
import keras
import osimport load
import utildef predict(data_json, model_path):preproc = util.load(os.path.dirname(model_path))dataset = load.load_dataset(data_json)x, y = preproc.process(*dataset)model = keras.models.load_model(model_path)probs = model.predict(x, verbose=1)return probsif __name__ == '__main__':parser = argparse.ArgumentParser()parser.add_argument("data_json", help="path to data json")parser.add_argument("model_path", help="path to model")args = parser.parse_args()probs = predict(args.data_json, args.model_path)

小结:

       心音信号的分类不能像心电信号一样处理,心电信号采样频率一般是200Hz,输入拿256个采样点,那么也有1.28s,且每1.28s有一个label,这对于通过1.28s心电信号的特征进行疾病诊断是合理的,而网络输入更加是同一个人连续30s的心电信号,那么就有200Hz*30=6000个采样点,由于256个采样点一个label,网络下采样倍数为256倍,所以输入为int(6000/256)*256个采样点。而心音信号的采样频率一般是4000Hz,数据预处理的时候一般下采样到1000Hz或2000Hz,如果也像心电一样拿256个采样点,那么时间只有0.256s或0.128s,而通过这不到1s的时间就要进行疾病诊断是不合理的,所以如果是做心音信号分类的话就不能采取如上的网络结构,可以修改的方法有如下:

(1)改变以上网络的输出结构,采用Flatten层,输出class_number个节点的softmax层,由于心音信号输入网络一般是3s左右, 是原始音频经过slice(切割)处理的,那么根据固定采样频率2000Hz来算的话,输入网络的采样点个数就为2000Hz*3s=6000个采样点

(2)采用频域特征,将一维时域信号转变成二维的时频信号再输入神经网络

(3)采用循环神经网络(这个具体没尝试过,接下去研究一下)

从心电信号分类过渡到心音信号分类相关推荐

  1. 论文研读笔记_基于优化的SVM心音信号分类算法的研究

    论文研究_基于优化的SVM心音信号分类算法的研究 先存起来,持续更新 摘要 心音信号采集易混入杂音,影响判断: 经验式模态分解算法分析特征分布:心音信号集中于低频.噪音集中于高频: 利用切比雪夫滤波器 ...

  2. 【项目实战-MATLAB】:基于CNN的心音信号分类

    心脏听诊是先天性心脏病(简称:先心病,CHD)初诊和筛查的主要手段.本项目对先心病心音信号进行分析和分类识别研究,提出了一种基于卷积神经网络的先心病分类算法.本文算法基于临床采集的已确诊先心病心音信号 ...

  3. 基于连续小波变换和GoogLeNet的PCG心音信号分类算法

    心音信号(PCG)是人体重要的生理信号,携带大量生理特征,但这些微弱信号的提取极易受到外界的干扰,关于心音信号处理相关的文章,可参考下面几篇博士论文,讲的非常详细,因此,不再赘述. [1]陈尧. 心音 ...

  4. abb工业机器人指令lf怎么用_工业机器人课程:ABB知识点27 | I/O控制指令

    本节要点 · 了解I/O 信号分类 · 掌握Set.Reset.WaitDI.SetDO.PulseDO 指令的使用 要点解析 (1)I/O 信号分类. I/O 信号分类见下表. I/O 信号分类 ( ...

  5. 嵌入式系统课程设计题目

    简介:一些嵌入式系统课程设计题目,可以当做对你学习ARM的一个检测. 嵌入式系统课程设计-选题要求及课题 1.嵌入式系统课程设计时长两星期,要求学生分组进行课程设计,每组学生人数为2-3人(可在不超过 ...

  6. 基于PTB-XL数据集的深度学习心电信号分类

    1.摘要: ECG Signal Classification Using Deep Learning Techniques Based on the PTB-XL Dataset 心电信号的分析与处 ...

  7. 【算法竞赛学习】心跳信号分类预测-特征工程

    Task3 特征工程 此部分为零基础入门数据挖掘-心跳信号分类预测的 Task3 特征工程部分,带你来了解时间序列特征工程以及分析方法,欢迎大家后续多多交流. 赛题:零基础入门数据挖掘-心跳信号分类预 ...

  8. 【直播】张晋:心跳信号分类模型融合

    心跳信号分类模型融合 目前 Datawhale第23期组队学习 正在如火如荼的进行中.为了大家更好的学习,零基础入门数据挖掘(心跳信号分类) 的课程设计者张晋,将为大家带来一场直播分享--心跳信号分类 ...

  9. 【直播】鱼佬:心跳信号分类赛高分突破

    心跳信号分类赛高分突破 目前 Datawhale第23期组队学习正在如火如荼的进行中.为了大家更好的学习,零基础入门数据挖掘(心跳信号分类) 的课程设计者王贺,将为大家带来一场直播分享--心跳信号分类 ...

  10. 【组队学习】【23期】Datawhale零基础入门数据挖掘(心跳信号分类)

    零基础入门数据挖掘(心跳信号分类) 开源内容:https://github.com/datawhalechina/team-learning-data-mining/tree/master/Heart ...

最新文章

  1. 利用VS+MFC+Opencv显示图像和视频所需添加类(CvvImage.h和CvvImage.cpp的源码)。
  2. 什么是维度诅咒?如何评估降维算法在当前任务数据集上的效果?
  3. mysql 恢复 二进制,mysql通过二进制日志恢复数据库
  4. 利用python爬虫(part1)--urllib.request模块
  5. 深入浅出单实例Singleton设计模式
  6. 题解 【国家集训队2011】数颜色
  7. 题解 P5065 【[Ynoi2014]不归之人与望眼欲穿的人们】
  8. SpringCloud学习笔记022---SpringBoot中集成使用MongoDb进行增删改查
  9. “编程能力差,90%会输在这点上!”谷歌开发:方法不对,努力也白费
  10. 联想y50p70换固态硬盘_换了固态硬盘电脑就变快?告诉你:固态硬盘不是万能药...
  11. PostSQL编写经验(利用坐标值,创建空间要素字段)
  12. 个性化域名邮箱的配置云阿里云域名解析详细教程
  13. iis 程序池设置及详解
  14. Java父亲节贺卡,父亲节贺卡祝福语语句
  15. Foxmail登录不上163邮箱。。。
  16. MATLAB--微分方程
  17. AI强度相关的研究:MCTS 蒙特卡洛树搜索 Monte Carlo Tree Search
  18. 同时支持手机号、用户名、邮箱登录
  19. contactform7 ajax,Wordpress contact_form_7_v5.0.3 插件 权限提升、任意文件读取漏洞分析...
  20. 成都理工大学计算机考研经历,09计算机考研的小小体会~

热门文章

  1. php sql注入教程,PHP简单高效防御sql注入的方法分享
  2. 计算机制作ppt教程,ppt怎么做?手机电脑超全PPT制作教程_教你做出完整的PPT
  3. uni-app框架简介
  4. 基于java水果网店管理系统计算机毕业设计源码+系统+lw文档+mysql数据库+调试部署
  5. [PowerQuery]Excel 如何开启PowerQuery组件
  6. fastboot刷机工具_红魔3/3S 刷机教程
  7. WPS国际版(安卓)
  8. 手机网络延迟测试软件,手机网速延迟测试在线(手机网络延迟测试工具)
  9. win10添加组策略命令
  10. Mac系统下编译并使用ijkplyer播放器