""" 脑电分析系列[MNE-Python-5]| Python机器学习算法 随机森林判断睡眠类型 """ """ 案例介绍 本案例通过对多导睡眠图(Polysomnography,PSG) 数据进行睡眠阶段的分类来判断睡眠类型。 训练:对Alice的睡眠数据进行训练; 测试:利用训练结果对Bob的睡眠数据进行测试,判断其睡眠类型。 在分析之前,先简单介绍一下多导睡眠图 多导睡眠图(Polysomnography,PSG)又称睡眠脑电图。 主要用于睡眠和梦境研究以及抑郁症和睡眠呼吸暂停综合征的诊断。 多导睡眠图是通过不同部位的生物电或通过不同传感获得生物讯号, 经前置放大,输出为不同的电讯号,记录出不同的图形以供分析。 """ """ 数据集介绍 本案例用的数据是来自于PhysioNet上关于健康受试者的年龄对睡眠影响研究的公开数据集的一个子集。 mne.datasets.sleep_physionet.age.fetch_data可以下载PhysioNet数据集的子数据集。 该子数据集中包含20位受试者的实验数据,记录当时年龄为25-34岁的10位男性和10位女性的实验数据。 由于受试者13的第二个记录遗失了,所以除了受试者13以外,每个受试者都有两次夜间记录。 Sleep Physionet数据集使用8个标签进行标注,代表8各阶段: Wake (W), Stage 1, Stage 2, Stage 3, Stage 4, REM(R), Movement time(M), Stage(?). Stage(?)-(not scored) 唤醒-Wake(W)、第1阶段、第2阶段、第3阶段、第4阶段、 对应于从轻度睡眠到深度睡眠的范围;REM睡眠(R), 其中REM是Rapid Eye的缩写,表示快速眼运动睡眠, 运动(M)和阶段(?)的任何未得分部分。 """ # 第一步:导入工具库 import numpy as np import matplotlib.pyplot as plt import mne from mne.datasets.sleep_physionet.age import fetch_data from mne.time_frequency import psd_welch from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score from sklearn.metrics import confusion_matrix from sklearn.metrics import classification_report from sklearn.pipeline import make_pipeline from sklearn.preprocessing import FunctionTransformer """ 第二步:加载数据 """ """ 在这里,我们从两个主题下载数据, 最终目标是获得时间片段(epochs)及其相关的地面真理。 MNE为我们提供了 mne.datasets.sleep_physionet.age.fetch_data(), 可以方便地从Sleep Physionet数据集下载数据。 给定主题和记录的列表,提取程序将下载数据并为每个主题提供数据, 一对文件: -PSG.edf包含多导睡眠图。来自EEG头盔的原始数据, -Hypnogram.edf包含专家记录的注释。 然后,将这两个对象合并到mne.io.Raw对象中, 就可以根据注释的描述提取事件以获得时间片段(epochs)。 """ """ 可以通过 mne.datasets.sleep_physionet.age.fetch_data(subjects,recording,path) 来获取PhysioNet多导睡眠图数据集文件。 subjects:表示想要使用哪些受试者对象,可供选择的受试者对象范围为0-19。 recording:表示夜间记录的编号(索引),有效值为:[1]、[2]或[1、2]。 path:PhysioNet数据的存放地址,如果没有给定,则加载默认存放数据的地址; 如果默认存放数据集的地址不存在数据,则从网络中下载相关数据。 """ # mne.datasets.sleep_physionet.age.fetch_data(subjects,recording,path) # 选择两个受试者实验对象ALICE, BOB(该名字并非实验中的真实名,这里是为了方便才临时取的名字) ALICE, BOB = 0, 1 # 加载ALICE, BOB的实验数据文件 # path = 'mne_data'.replace("\", "/") [alice_files, bob_files] = fetch_data(subjects=[ALICE, BOB], recording=[1]) # 通道名称映射 mapping = {'EOG horizontal': 'eog', 'Resp oro-nasal': 'misc', 'EMG submental': 'misc', 'Temp rectal': 'misc', 'Event marker': 'misc'} #读取ALICE的edf文件,和其对应的注释文件 raw_train = mne.io.read_raw_edf(alice_files[0]) annot_train = mne.read_annotations(alice_files[1]) raw_train.set_annotations(annot_train, emit_warning=False) raw_train.set_channel_types(mapping) # 绘制空0s开始,时间窗口长度为40s的连续通道数据波形图 raw_train.plot(duration=40, scalings='auto') plt.show() """ 这里仅使用5个阶段:唤醒(W),阶段1,阶段2,阶段3/4和REM睡眠(R)。 为此,这里使用mne.events_from_annotations()中的event_id参数来选择我们感兴趣的事件, 并将事件标识符与每个事件相关联。 """ """ 睡眠表示与事件映射 """ annotation_desc_2_event_id = {'Sleep stage W': 1, 'Sleep stage 1': 2, 'Sleep stage 2': 3, 'Sleep stage 3': 4, 'Sleep stage 4': 4, 'Sleep stage R': 5} events_train, _ = mne.events_from_annotations( raw_train, event_id=annotation_desc_2_event_id, chunk_duration=30.) # 创建一个新的event_id以统一 阶段3和4 event_id = {'Sleep stage W': 1, 'Sleep stage 1': 2, 'Sleep stage 2': 3, 'Sleep stage 3/4': 4, 'Sleep stage R': 5} # 绘制事件数据 mne.viz.plot_events(events_train, event_id=event_id, sfreq=raw_train.info['sfreq']) # 保留颜色代码以便进一步绘制 stage_colors = plt.rcParams['axes.prop_cycle'].by_key()['color'] """ 根据注释中的事件从数据创建epochs(时间片段) """ tmax = 30. - 1. / raw_train.info['sfreq'] # tmax in included """ 所创建的是时间从tmin=0开始,到tmax为止的epochs """ epochs_train = mne.Epochs(raw=raw_train, events=events_train, event_id=event_id, tmin=0., tmax=tmax, baseline=None) print(epochs_train) """ 第三步:加载Bob的数据作为测试数据 按照上述相同的步骤来获取Bob的测试数据 """ raw_test = mne.io.read_raw_edf(bob_files[0]) annot_test = mne.read_annotations(bob_files[1]) raw_test.set_annotations(annot_test, emit_warning=False) raw_test.set_channel_types(mapping) events_test, _ = mne.events_from_annotations( raw_test, event_id=annotation_desc_2_event_id, chunk_duration=30.) epochs_test = mne.Epochs(raw=raw_test, events=events_test, event_id=event_id, tmin=0., tmax=tmax, baseline=None) print(epochs_test) """ 特征工程 观察不同睡眠阶段的功率谱密度(PSD)图,可以看到不同睡眠阶段具有不同的特征。 这些签名在Alice和Bob的数据中保持相似。 在本节的其余部分中,将基于特定频带中的相对功率来创建EEG特征, 以捕获数据中睡眠阶段之间的差异。 """ fig, (ax1, ax2) = plt.subplots(ncols=2) # iterate over the subjects stages = sorted(event_id.keys()) for ax, title, epochs in zip([ax1, ax2], ['Alice', 'Bob'], [epochs_train, epochs_test]): for stage, color in zip(stages, stage_colors): epochs[stage].plot_psd(area_mode=None, color=color, ax=ax, fmin=0.1, fmax=20., show=False, average=True, spatial_colors=False) ax.set(title=title, xlabel='Frequency (Hz)') ax2.set(ylabel='uV^2/hz (dB)') ax2.legend(ax2.lines[2::3], stages) plt.tight_layout() plt.show() """ 第四步:设计scikit-learn 转换器 创建一个函数,根据特定频带中的相对功率提取脑电图特征, 从而能够根据脑电图信号预测睡眠阶段。 """ def eeg_power_band(epochs): """脑电相对功率带特征提取 该函数接受一个""mne.Epochs"对象, 并基于与scikit-learn兼容的特定频带中的相对功率创建EEG特征。 Parameters ---------- epochs : Epochs The data. Returns ------- X : numpy array of shape [n_samples, 5] Transformed data. """ # 特定频带 FREQ_BANDS = {"delta": [0.5, 4.5], "theta": [4.5, 8.5], "alpha": [8.5, 11.5], "sigma": [11.5, 15.5], "beta": [15.5, 30]} psds, freqs = psd_welch(epochs, picks='eeg', fmin=0.5, fmax=30.) # 归一化 PSDs psds /= np.sum(psds, axis=-1, keepdims=True) X = [] for fmin, fmax in FREQ_BANDS.values(): psds_band = psds[:, :, (freqs >= fmin) & (freqs < fmax)].mean(axis=-1) X.append(psds_band.reshape(len(psds), -1)) return np.concatenate(X, axis=1) """ 第五步:根据 Alice的数据来预测Bob的睡眠阶段 使用scikit-learn进行多分类 下面展示了解决如何从爱丽丝的数据中预测鲍勃的睡眠阶段并尽可能避免重复样板代码的问题。 这里将利用sckit-learn的Pipeline和FunctionTransformer。 扩展:[Pipeline可以将许多算法模型串联起来, 可以用于把多个estamitors级联成一个estamitor, 比如将特征提取、归一化、分类组织在一起形成一个典型的机器学习问题工作流。 FunctionTransformer将python函数转换为与estamitor兼容的对象。] """ pipe = make_pipeline(FunctionTransformer(eeg_power_band, validate=False), RandomForestClassifier(n_estimators=100, random_state=42)) # 训练 y_train = epochs_train.events[:, 2] pipe.fit(epochs_train, y_train) # 预测 y_pred = pipe.predict(epochs_test) # 评估准确率 y_test = epochs_test.events[:, 2] acc = accuracy_score(y_test, y_pred) print("Accuracy score: {}".format(acc)) ''' 预测的准确精度为84.7% 查看分类报告做进一步分析 ''' print(classification_report(y_test, y_pred, target_names=event_id.keys())) """ 从分类报告中可以看出, Bob的每个阶段训练测试样本,以及对应的睡眠阶段的精度。 比如W阶段的精度为86%,测试样本为1856。测试总样本为2802。也可以看到其他一些指标比如召回率和F1值。 """问题1:由于我本地没有该实验所需数据集,默认从官网下载,可能由于我的网络问题,ssl一直拒绝下载,报错如下:
Using default location ~/mne_data for PHYSIONET_SLEEP... Error while fetching file https://physionet.org/physiobank/database/sleep-edfx/sleep-cassette//SC4001E0-PSG.edf. Dataset fetching aborted. Traceback (most recent call last): File "D:pythonliburllibrequest.py", line 1317, in do_open h.request(req.get_method(), req.selector, req.data, headers, File "D:pythonlibhttpclient.py", line 1230, in request self._send_request(method, url, body, headers, encode_chunked) File "D:pythonlibhttpclient.py", line 1276, in _send_request self.endheaders(body, encode_chunked=encode_chunked) File "D:pythonlibhttpclient.py", line 1225, in endheaders self._send_output(message_body, encode_chunked=encode_chunked) File "D:pythonlibhttpclient.py", line 1004, in _send_output self.send(msg) File "D:pythonlibhttpclient.py", line 944, in send self.connect() File "D:pythonlibhttpclient.py", line 1399, in connect self.sock = self._context.wrap_socket(self.sock, File "D:pythonlibssl.py", line 500, in wrap_socket return self.sslsocket_class._create( File "D:pythonlibssl.py", line 1040, in _create self.do_handshake() File "D:pythonlibssl.py", line 1309, in do_handshake self._sslobj.do_handshake() ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1108) During handling of the above exception, another exception occurred: Traceback (most recent call last): File "F:/mne_demo/mne_sklearn_random_trees_judge_sleeping_type.py", line 110, in最初我想这去报错中提到的这个链接https://physionet.org/physiobank/database/sleep-edfx/sleep-cassette//SC4001E0-PSG.edf将数据集下载下来并放到默认路径(./~/mne_data/physionet-sleep-data/,注fetch_data函数中path路径自定义的话写到physionet-sleep-data的上一层即可,该方法会自动给你自定义的path后面追加“/physionet-sleep-data/”)就ok了,后来发现我想的太简单了,失败后我尝试自己定义该函数的path,仍是无果,粗心的没有注意到这是报错的链接已经变了,换了一个链接,也就是说明数据集并非只有一个edf文件,昨晚尝试下载了两个之后放弃了pycharm。不得已只能上Anaconda平台试试 2.Anaconda上在jupyter notebook上使用代码与pycharm一致 昨晚报错:一直显示内核正忙,console也显示正在下载,重启后仍是如此,这校园网决绝子,真垃圾!同样这里我使用anaconda console上提供的链接前往下载,放到了anaconda 默认数据集路径,即c盘下当前用户名的mne_data/physionet-sleep-data/下,还是无果,此时仍没有注意到再次运行console的下载链接已改变,于是今晚只能放弃。[alice_files, bob_files] = fetch_data(subjects=[ALICE, BOB], File " ", line 24, in fetch_data File "D:pythonlibsite-packagesmnedatasetssleep_physionetage.py", line 122, in fetch_data psg_fname = _fetch_one(psg_records['fname'][idx].decode(), File "D:pythonlibsite-packagesmnedatasetssleep_physionet_utils.py", line 38, in _fetch_one _fetch_file(url, destination, print_destination=False, File " ", line 24, in _fetch_file File "D:pythonlibsite-packagesmneutilsfetching.py", line 117, in _fetch_file _get_http(url, temp_file_name, initial_size, timeout, verbose_bool) File "D:pythonlibsite-packagesmneutilsfetching.py", line 43, in _get_http response = request.urlopen(request.Request(url), timeout=timeout) File "D:pythonliburllibrequest.py", line 222, in urlopen return opener.open(url, data, timeout) File "D:pythonliburllibrequest.py", line 525, in open response = self._open(req, data) File "D:pythonliburllibrequest.py", line 542, in _open result = self._call_chain(self.handle_open, protocol, protocol + File "D:pythonliburllibrequest.py", line 502, in _call_chain result = func(*args) File "D:pythonliburllibrequest.py", line 1360, in https_open return self.do_open(http.client.HTTPSConnection, req, File "D:pythonliburllibrequest.py", line 1320, in do_open raise URLError(err) urllib.error.URLError: Process finished with exit code 1
但是当我今天重新打开电脑使用anaconda再次运行该程序,竟然网络下载成功了,在C:Userxxxmne_dataphysionet-sleep-data(注:这里的xxx是你当前windows开机登录的用户名)一共下载了4个edf文件,如下所示——
果然还是败坑爹的校园网所“赐”,我尝试把这四个数据集放到我的pycharm python项目文件所在位置的mne_dataphysionet-sleep-data,运行通过…
然后运行过一次之后,我删掉项目路径mne_dataphysionet-sleep-data的四个数据集文件,发现还是成功,重启pycharm还是成功,所以这里应该是fetch_data这个方法对path的应该有其他解读,一种就是自定义path或初始path为“./mne_data”,另一种就是使用当前user下的mne_data路径,所以我尝试把两处的数据集都删除,果然pycharm console又开始下载数据集,不得不吐槽以下pycharm下载数据集真的拉跨,一片红,而且今天第一次开机应该不能是网络的问题,否则anaconda上也会下载失败,又或许是我之前最初python做其他项目时对python package不太了解,无形中可能修改了某些属性导致,欢迎大家一起讨论pycharm使用体验或指出解决办法,灰常感谢!到此可以确认昨天真的是应为网络问题。
这里我初步怀疑时mne版本问题,想了以下我之前做mne官网的example是下载的0.24.0版本的mne package,查了pycharm的mne版本发现是0.21.2,所以尝试卸载重装mne,激活你的项目所在的anaconda虚拟环境后输入命令如下:pip install -U mne==0.21.2即可,
重新运行,通过 ->>>
这里还得提一下,关于有时候anaconda上同一段代码会生成两个一模一样的图的问题,如下所示:""" 这里仅使用5个阶段:唤醒(W),阶段1,阶段2,阶段3/4和REM睡眠(R)。 为此,这里使用mne.events_from_annotations()中的event_id参数来选择我们感兴趣的事件, 并将事件标识符与每个事件相关联。 """ """ 睡眠表示与事件映射 """ annotation_desc_2_event_id = {'Sleep stage W': 1, 'Sleep stage 1': 2, 'Sleep stage 2': 3, 'Sleep stage 3': 4, 'Sleep stage 4': 4, 'Sleep stage R': 5} events_train, _ = mne.events_from_annotations( raw_train, event_id=annotation_desc_2_event_id, chunk_duration=30.) # 创建一个新的event_id以统一 阶段3和4 event_id = {'Sleep stage W': 1, 'Sleep stage 1': 2, 'Sleep stage 2': 3, 'Sleep stage 3/4': 4, 'Sleep stage R': 5} # 绘制事件数据 mne.viz.plot_events(events_train, event_id=event_id, sfreq=raw_train.info['sfreq']) # 保留颜色代码以便进一步绘制 # stage_colors = plt.rcParams['axes.prop_cycle'].by_key()['color']上述代码运行结果—— 把上述代码最后一行注释取消后,运行结果才正常,希望有大佬能制出这其中的不同,感激不尽 到此,本次实验就结束了,如有不足,希望各位大佬批评指正,感谢 最后再次感谢微信公众号——脑际接口社区 分享的文章《脑电分析系列[MNE-Python-5]| Python机器学习算法随机森林判断睡眠类型》!!!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)