Advertisement

机器学习算法:通过语音诊断帕金森氏病

阅读量:

本文描述了一个使用Python机器学习库来识别帕金森病患者声音特征的项目。项目目标是通过声音测量和机器学习模型,区分帕金森病患者与健康者的音频。主要方法包括数据预处理、特征提取(如声调、 shimmer 和 jitter 测量)以及使用逻辑回归模型进行分类。实验结果表明,模型在测试集上的准确率为63%,数据集有限,结果令人满意。此外,项目提供了可扩展性,通过增加帕金森病患者的音频样本可以进一步提高模型性能。

该算法将被我们开发出来,它是一个相对简单的Python机器学习方案。该方案旨在通过声音来判断该患者是否为患者。

基于一组音频文件库,我们通过对其音频进行一系列测量,用于构建我们的机器学习数据集,该音频文件库包含健康个体和帕金森病患者。

在生成机器学习数据集后,我们将采用SciKit Learn库来训练线性回归模型。最终,我们将开发一个Python库,该库能够方便地集成到其他应用程序中。

数据集

首先,主要任务是生成一个包含音频测量值以及患者健康状况标识的表格。

我们将要使用的音频文件(https://zenodo.org/record/2867216#.Xp4kVsgzaUl )。

让我们从导入必要的Python库开始。

复制代码
    import glob
    import numpy as np
    import pandas as pd
    import parselmouth
    from parselmouth.praat import call

接下来,我们将创建一个函数,该函数将允许您对输入的音频文件进行各种复杂的测量。这些测量由 parselmouth 库实现,并将在 Python 代码中使用 Praat 进行执行。

https://parselmouth.readthedocs.io/en/stable/

复制代码
    def measurePitch(voiceID, f0min, f0max, unit):
    sound = parselmouth.Sound(voiceID) # read the sound
    pitch = call(sound, "To Pitch", 0.0, f0min, f0max)
    pointProcess = call(sound, "To PointProcess (periodic, cc)", f0min, f0max)#create a praat pitch object
    localJitter = call(pointProcess, "Get jitter (local)", 0, 0, 0.0001, 0.02, 1.3)
    localabsoluteJitter = call(pointProcess, "Get jitter (local, absolute)", 0, 0, 0.0001, 0.02, 1.3)
    rapJitter = call(pointProcess, "Get jitter (rap)", 0, 0, 0.0001, 0.02, 1.3)
    ppq5Jitter = call(pointProcess, "Get jitter (ppq5)", 0, 0, 0.0001, 0.02, 1.3)
    localShimmer =  call([sound, pointProcess], "Get shimmer (local)", 0, 0, 0.0001, 0.02, 1.3, 1.6)
    localdbShimmer = call([sound, pointProcess], "Get shimmer (local_dB)", 0, 0, 0.0001, 0.02, 1.3, 1.6)
    apq3Shimmer = call([sound, pointProcess], "Get shimmer (apq3)", 0, 0, 0.0001, 0.02, 1.3, 1.6)
    aqpq5Shimmer = call([sound, pointProcess], "Get shimmer (apq5)", 0, 0, 0.0001, 0.02, 1.3, 1.6)
    apq11Shimmer =  call([sound, pointProcess], "Get shimmer (apq11)", 0, 0, 0.0001, 0.02, 1.3, 1.6)
    harmonicity05 = call(sound, "To Harmonicity (cc)", 0.01, 500, 0.1, 1.0)
    hnr05 = call(harmonicity05, "Get mean", 0, 0)
    harmonicity15 = call(sound, "To Harmonicity (cc)", 0.01, 1500, 0.1, 1.0)
    hnr15 = call(harmonicity15, "Get mean", 0, 0)
    harmonicity25 = call(sound, "To Harmonicity (cc)", 0.01, 2500, 0.1, 1.0)
    hnr25 = call(harmonicity25, "Get mean", 0, 0)
    harmonicity35 = call(sound, "To Harmonicity (cc)", 0.01, 3500, 0.1, 1.0)
    hnr35 = call(harmonicity35, "Get mean", 0, 0)
    harmonicity38 = call(sound, "To Harmonicity (cc)", 0.01, 3800, 0.1, 1.0)
    hnr38 = call(harmonicity38, "Get mean", 0, 0)
    return localJitter, localabsoluteJitter, rapJitter, ppq5Jitter, localShimmer, localdbShimmer, apq3Shimmer, aqpq5Shimmer, apq11Shimmer, hnr05, hnr15 ,hnr25 ,hnr35 ,hnr38

然后,为每种测量类型制作一个列表,并创建一个用于标记病人的健康状况的列表。在数据填入完毕后,利用这些列表构建机器学习数据集。

复制代码
    localJitter_list = [] #measure
    localabsoluteJitter_list = [] #measure
    rapJitter_list = [] #measure
    ppq5Jitter_list = [] #measure
    localShimmer_list =  [] #measure
    localdbShimmer_list = [] #measure
    apq3Shimmer_list = [] #measure
    aqpq5Shimmer_list = [] #measure
    apq11Shimmer_list =  [] #measure
    hnr05_list = [] #measure
    hnr15_list = [] #measure
    hnr25_list = [] #measure
    parkinson_list = [] #Parkinson(1) or healthy(0)

现在,可以调用预先生成的函数,通过明确的音频测量操作来填充列表项。为了完成填充任务,我们需要设计并实施4个for循环结构。

复制代码
    for wave_file in glob.glob("audio/SpontaneousDialogue/PD/*.wav"):
    sound = parselmouth.Sound(wave_file)
    (localJitter, localabsoluteJitter, rapJitter, ppq5Jitter, localShimmer, localdbShimmer, apq3Shimmer, aqpq5Shimmer, apq11Shimmer, hnr05, hnr15 ,hnr25 ,hnr35 ,hnr38) = measurePitch(sound, 75, 1000, "Hertz")
    file_list.append(wave_file) # make an ID list
    localJitter_list.append(localJitter) # make a mean F0 list
    localabsoluteJitter_list.append(localabsoluteJitter) # make a sd F0 list
    rapJitter_list.append(rapJitter)
    ppq5Jitter_list.append(ppq5Jitter)
    localShimmer_list.append(localShimmer)
    localdbShimmer_list.append(localdbShimmer)
    apq3Shimmer_list.append(apq3Shimmer)
    aqpq5Shimmer_list.append(aqpq5Shimmer)
    apq11Shimmer_list.append(apq11Shimmer)
    hnr05_list.append(hnr05)
    hnr15_list.append(hnr15)
    hnr25_list.append(hnr25)
    parkinson_list.append(1) #1 because parkinson file
    ​
    for wave_file in glob.glob("audio/ReadText/PD/*.wav"):
    sound = parselmouth.Sound(wave_file)
    (localJitter, localabsoluteJitter, rapJitter, ppq5Jitter, localShimmer, localdbShimmer, apq3Shimmer, aqpq5Shimmer, apq11Shimmer, hnr05, hnr15 ,hnr25 ,hnr35 ,hnr38) = measurePitch(sound, 75, 1000, "Hertz")
    file_list.append(wave_file) # make an ID list
    localJitter_list.append(localJitter) # make a mean F0 list
    localabsoluteJitter_list.append(localabsoluteJitter) # make a sd F0 list
    rapJitter_list.append(rapJitter)
    ppq5Jitter_list.append(ppq5Jitter)
    localShimmer_list.append(localShimmer)
    localdbShimmer_list.append(localdbShimmer)
    apq3Shimmer_list.append(apq3Shimmer)
    aqpq5Shimmer_list.append(aqpq5Shimmer)
    apq11Shimmer_list.append(apq11Shimmer)
    hnr05_list.append(hnr05)
    hnr15_list.append(hnr15)
    hnr25_list.append(hnr25)
    parkinson_list.append(1) #1 because parkinson file
    ​
    for wave_file in glob.glob("audio/SpontaneousDialogue/HC/*.wav"):
    sound = parselmouth.Sound(wave_file)
    (localJitter, localabsoluteJitter, rapJitter, ppq5Jitter, localShimmer, localdbShimmer, apq3Shimmer, aqpq5Shimmer, apq11Shimmer, hnr05, hnr15 ,hnr25 ,hnr35 ,hnr38) = measurePitch(sound, 75, 1000, "Hertz")
    file_list.append(wave_file) # make an ID list
    localJitter_list.append(localJitter) # make a mean F0 list
    localabsoluteJitter_list.append(localabsoluteJitter) # make a sd F0 list
    rapJitter_list.append(rapJitter)
    ppq5Jitter_list.append(ppq5Jitter)
    localShimmer_list.append(localShimmer)
    localdbShimmer_list.append(localdbShimmer)
    apq3Shimmer_list.append(apq3Shimmer)
    aqpq5Shimmer_list.append(aqpq5Shimmer)
    apq11Shimmer_list.append(apq11Shimmer)
    hnr05_list.append(hnr05)
    hnr15_list.append(hnr15)
    hnr25_list.append(hnr25)
    parkinson_list.append(0) #0 because healthy file
    ​
    for wave_file in glob.glob("audio/ReadText/HC/*.wav"):
    sound = parselmouth.Sound(wave_file)
    (localJitter, localabsoluteJitter, rapJitter, ppq5Jitter, localShimmer, localdbShimmer, apq3Shimmer, aqpq5Shimmer, apq11Shimmer, hnr05, hnr15 ,hnr25 ,hnr35 ,hnr38) = measurePitch(sound, 75, 1000, "Hertz")
    file_list.append(wave_file) # make an ID list
    localJitter_list.append(localJitter) # make a mean F0 list
    localabsoluteJitter_list.append(localabsoluteJitter) # make a sd F0 list
    rapJitter_list.append(rapJitter)
    ppq5Jitter_list.append(ppq5Jitter)
    localShimmer_list.append(localShimmer)
    localdbShimmer_list.append(localdbShimmer)
    apq3Shimmer_list.append(apq3Shimmer)
    aqpq5Shimmer_list.append(aqpq5Shimmer)
    apq11Shimmer_list.append(apq11Shimmer)
    hnr05_list.append(hnr05)
    hnr15_list.append(hnr15)
    hnr25_list.append(hnr25)
    parkinson_list.append(0) #0 because healthy file

最后,我们利用pandas和numpy库,需要将这些列表分组到一个表中,以便将它们转换为机器学习数据集。

复制代码
    pred = pd.DataFrame(np.column_stack([parkinson_list,localJitter_list, localabsoluteJitter_list, rapJitter_list, ppq5Jitter_list, localShimmer_list, localdbShimmer_list, apq3Shimmer_list, aqpq5Shimmer_list, apq11Shimmer_list, hnr05_list, hnr15_list, hnr25_list]),
                               columns=["Parkinson","Jitter_rel","Jitter_abs","Jitter_RAP","Jitter_PPQ","Shim_loc","Shim_dB","Shim_APQ3","Shim_APQ5","Shi_APQ11", "hnr05", "hnr15", "hnr25"])  #add these lists to pandas in the right order
    ​
    pred['hnr25'].fillna((parkinson['hnr25'].mean()), inplace=True) #Data cleaning because they may be NaN values
    pred['hnr15'].fillna((parkinson['hnr15'].mean()), inplace=True) #Data cleaning because they may be NaN values
    ​
    pred.to_csv("processed_results.csv", index=False) # Write out the updated dataset
制作机器学习模型

我们采用SciKit learn库中的线性回归算法,这种算法能够基于几个参数(measures)对标签(0或1)进行分类。在上文中,我们讨论了如何通过几个参数(measures)对标签(0或1)进行分类。

在训练过程中,我们通过设定特定的参数值和明确的标签值来训练我们的机器学习模型。

复制代码
    parkinson = pd.read_csv("processed_results.csv") #Loading CSV dataset
    ​
    predictors=["Jitter_rel","Jitter_abs","Jitter_RAP","Jitter_PPQ","Shim_loc","Shim_dB","Shim_APQ3","Shim_APQ5","Shi_APQ11","hnr05","hnr15", "hnr25"] #Listing predictors
    ​
    for col in predictors: # Loop through all columns in predictors
    if parkinson[col].dtype == 'object':  # check if column's type is object (text)
        parkinson[col] = pd.Categorical(parkinson[col]).codes  # convert text to numerical
    
    from sklearn.model_selection import train_test_split
    X_train, X_test, y_train, y_test = train_test_split(parkinson[predictors], parkinson['Parkinson'], test_size=0.25, random_state=1)
    
    from sklearn.linear_model import LogisticRegression
    ​
    clf = LogisticRegression()
    clf.fit(X_train, y_train)
    ​
    train_score = clf.score(X_train, y_train)
    test_score = clf.score(X_test, y_test)
    ​
    print ('train accuracy =', train_score)
    print ('test accuracy =', test_score)
    ​
    #train accuracy = 0.6666666666666666
    #test accuracy = 0.631578947368421

我们获得0.63的精度,考虑数据集的数据如此有限,这个结果还是令人满意的。

导出机器学习模型的Python代码如下:

复制代码
    import joblib
    ​
    clf.fit(X_train, y_train)
    ​
    joblib.dump(clf, "trainedModel.sav")
制作库

请记住,我们的主要目标是获取一个能够被另一个程序所使用的核心库。Python实现代码如下:

复制代码
    import joblib
    import parselmouth
    from parselmouth.praat import call
    import pandas as pd
    import numpy as np
    import sklearn
    ​
    ​
    def loadModel(PATH):
    clf = joblib.load(PATH)
    return clf
    ​
    def measurePitch(voiceID, f0min, f0max, unit):
    sound = parselmouth.Sound(voiceID) # read the sound
    pitch = call(sound, "To Pitch", 0.0, f0min, f0max)
    pointProcess = call(sound, "To PointProcess (periodic, cc)", f0min, f0max)#create a praat pitch object
    localJitter = call(pointProcess, "Get jitter (local)", 0, 0, 0.0001, 0.02, 1.3)
    localabsoluteJitter = call(pointProcess, "Get jitter (local, absolute)", 0, 0, 0.0001, 0.02, 1.3)
    rapJitter = call(pointProcess, "Get jitter (rap)", 0, 0, 0.0001, 0.02, 1.3)
    ppq5Jitter = call(pointProcess, "Get jitter (ppq5)", 0, 0, 0.0001, 0.02, 1.3)
    localShimmer =  call([sound, pointProcess], "Get shimmer (local)", 0, 0, 0.0001, 0.02, 1.3, 1.6)
    localdbShimmer = call([sound, pointProcess], "Get shimmer (local_dB)", 0, 0, 0.0001, 0.02, 1.3, 1.6)
    apq3Shimmer = call([sound, pointProcess], "Get shimmer (apq3)", 0, 0, 0.0001, 0.02, 1.3, 1.6)
    aqpq5Shimmer = call([sound, pointProcess], "Get shimmer (apq5)", 0, 0, 0.0001, 0.02, 1.3, 1.6)
    apq11Shimmer =  call([sound, pointProcess], "Get shimmer (apq11)", 0, 0, 0.0001, 0.02, 1.3, 1.6)
    harmonicity05 = call(sound, "To Harmonicity (cc)", 0.01, 500, 0.1, 1.0)
    hnr05 = call(harmonicity05, "Get mean", 0, 0)
    harmonicity15 = call(sound, "To Harmonicity (cc)", 0.01, 1500, 0.1, 1.0)
    hnr15 = call(harmonicity15, "Get mean", 0, 0)
    harmonicity25 = call(sound, "To Harmonicity (cc)", 0.01, 2500, 0.1, 1.0)
    hnr25 = call(harmonicity25, "Get mean", 0, 0)
    harmonicity35 = call(sound, "To Harmonicity (cc)", 0.01, 3500, 0.1, 1.0)
    hnr35 = call(harmonicity35, "Get mean", 0, 0)
    harmonicity38 = call(sound, "To Harmonicity (cc)", 0.01, 3800, 0.1, 1.0)
    hnr38 = call(harmonicity38, "Get mean", 0, 0)
    return localJitter, localabsoluteJitter, rapJitter, ppq5Jitter, localShimmer, localdbShimmer, apq3Shimmer, aqpq5Shimmer, apq11Shimmer, hnr05, hnr15 ,hnr25 ,hnr35 ,hnr38
    ​
    ​
    def predict(clf, wavPath):
    file_list = []
    localJitter_list = []
    localabsoluteJitter_list = []
    rapJitter_list = []
    ppq5Jitter_list = []
    localShimmer_list = []
    localdbShimmer_list = []
    apq3Shimmer_list = []
    aqpq5Shimmer_list = []
    apq11Shimmer_list = []
    hnr05_list = []
    hnr15_list = []
    hnr25_list = []
    hnr35_list = []
    hnr38_list = []
    ​
    sound = parselmouth.Sound(wavPath)
    (localJitter, localabsoluteJitter, rapJitter, ppq5Jitter, localShimmer, localdbShimmer, apq3Shimmer, aqpq5Shimmer,
     apq11Shimmer, hnr05, hnr15, hnr25, hnr35, hnr38) = measurePitch(sound, 75, 1000, "Hertz")
    localJitter_list.append(localJitter)  # make a mean F0 list
    localabsoluteJitter_list.append(localabsoluteJitter)  # make a sd F0 list
    rapJitter_list.append(rapJitter)
    ppq5Jitter_list.append(ppq5Jitter)
    localShimmer_list.append(localShimmer)
    localdbShimmer_list.append(localdbShimmer)
    apq3Shimmer_list.append(apq3Shimmer)
    aqpq5Shimmer_list.append(aqpq5Shimmer)
    apq11Shimmer_list.append(apq11Shimmer)
    hnr05_list.append(hnr05)
    hnr15_list.append(hnr15)
    hnr25_list.append(hnr25)
    hnr35_list.append(hnr35)
    hnr38_list.append(hnr38)
    ​
    toPred = pd.DataFrame(np.column_stack(
        [localJitter_list, localabsoluteJitter_list, rapJitter_list, ppq5Jitter_list, localShimmer_list,
         localdbShimmer_list, apq3Shimmer_list, aqpq5Shimmer_list, apq11Shimmer_list, hnr05_list, hnr15_list,
         hnr25_list]),
                         columns=["Jitter_rel", "Jitter_abs", "Jitter_RAP", "Jitter_PPQ", "Shim_loc", "Shim_dB",
                                  "Shim_APQ3", "Shim_APQ5", "Shi_APQ11", "hnr05", "hnr15",
                                  "hnr25"])  # add these lists to pandas in the right order
    ​
    resp = clf.predict(toPred)
    resp = str(resp)
    ​
    if resp == "[1.]":
        return True
    else:
        return False

调用上述库的Python代码如下:

复制代码
    from RecognitionLib import *
    ​
    path = "../trainedModel.sav" #Model path
    clf = loadModel(path) #Model loading
    ​
    print(predict(clf, "../../audio/ok.wav"))#Predicition

通过获取更多的数据样本来提升机器学习模型的性能,从而提高模型的预测精度,即通过从帕金森氏病患者中获取更多的音频样本。

Python圈子

文源网络,仅用于学习参考,侵权删除。

在学习Python的过程中,可能会遇到各种挑战,但不要担心,我这里为你准备了一份详尽的学习资源。

这份学习资料共包含40+本电子书和800+个教学视频,涵盖Python基础、爬虫、框架、数据分析、机器学习等多个领域。

需要进一步了解的朋友,可以直接访问链接《Python学习资料》获取完整版本。

关注公众号【Python圈子

file

全部评论 (0)

还没有任何评论哟~