博客專欄

        EEPW首頁 > 博客 > Python實現對腦電數據情緒分析

        Python實現對腦電數據情緒分析

        發布人:AI科技大本營 時間:2021-08-07 來源:工程師 發布文章

        引言

        腦電波是一類由大腦中局部群體神經元同步放電所形成的具有時空特征的腦電活動電波。德國醫生漢斯·伯格(Hans Berger)在1924年首次在人的頭骨上記錄到腦電波圖(electroencephalography,EEG)。心理學研究表明,人類的認知和感知可以通過腦電波來表達。當大腦的嗅覺、聽覺、視覺、味覺及觸覺神經受到刺激時,其刺激反應信號可以通過腦電波表達出來,從而揭示感官和人員之間的心理關聯性。其中大量研究展示了使用腦電信號連續確定個人舒適感的可行性,并且可以得到更加客觀的數據。近來則有研究表明觸覺刺激與腦電波的θ,α,β這三個頻段均存在關聯性。

        傳統的情緒識別主要是基于面部特征、肢體動作和語音的研究,這些外在特征容易偽裝,并不能反應出真實的情緒,腦電信號可以反映大腦在加工情緒時所伴隨的神經電生理活動,能夠很好的彌補傳統研究方法的缺陷。

        1.png

        故本項目通過使用python語言搭建KNN機器學習算法實現對EEG腦電波數據的情緒分析分類。其最終實現的效果如下圖可見:

        2.png

        No.1 “基本介紹”

        環境要求

        本次環境使用的是python3.6.5+windows平臺。主要用的庫有:

        csv模塊。CSV庫在這里用來讀取CSV數據集文件。其中CSV文件是逗號分隔值文件,是一種常用的文本格式,用以存儲表格數據,包括數字或者字符。很多程序在處理數據時都會碰到csv這種格式的文件,它的使用是比較廣泛的。

        scipy模塊。scipy作為高級科學計算庫:和numpy聯系很密切,scipy一般都是操控numpy數組來進行科學計算、統計分析,所以可以說是基于numpy之上了。scipy有很多子模塊可以應對不同的應用,例如插值運算,優化算法等等。scipy則是在numpy的基礎上構建的更為強大,應用領域也更為廣泛的科學計算包。正是出于這個原因,scipy需要依賴numpy的支持進行安裝和運行。以Python為基礎的scipy的另一個好處是,它還提供了一種強大的編程語言,可用于開發復雜的程序和專門的應用程序。使用scipy的科學應用程序受益于世界各地的開發人員在軟件領域的許多小眾領域中開發的附加模塊。

        pathlib模塊。該模塊提供了一些使用語義表達來表示文件系統路徑的類,這些類適合多種操作系統。

        Pickle模塊。python的pickle模塊實現了基本的數據序列和反序列化。通過pickle模塊的序列化操作我們能夠將程序中運行的對象信息保存到文件中去,永久存儲;通過pickle模塊的反序列化操作,我們能夠從文件中創建上一次程序保存的對象。

        以及其他常用模塊,如OpenCV等不一一介紹了。

        KNN算法介紹

        其中k近鄰算法(k Nearest Neighbor,kNN)是一種理論上比較成熟的智能算法,最初由Cover和Hart于1968年提出。kNN算法思路簡單直觀:對于給定的測試樣本,基于某種距離度量找出訓練集中與其最靠近的k個訓練樣本,然后基于這k個“鄰居”的信息來進行預測。

        kNN方法可以實現分類任務和回歸任務。在分類任務中,一般使用“投****法”,即在k個“近鄰”樣本中出現最多的種類標記作為預測結果。在回歸任務中,一般使用“平均法”,即將這k個樣本的實值輸出標記的平均值作為預測結果。在定義了相似度評價準則后,還可以基于相似度的大小進行加權投****和加權平均。相似度越大的樣本權重越大。其中KNN算法分類流程如下圖:

        3.png

        No.2 “模型搭建”

        數據集準備

        首先我們使用官方提供的EEG數據集,放置data文件夾下:

        4.png

        數據特征提取

        首先我們使用官方提供的EEG數據集,放置data文件夾下:

        通過使用pickle實現對dat數據文件的讀取,獲取各個數據文件中特征向量,并在每個信道中進行fft。

        其中輸入:維數為N × M的通道數據,N為通道個數,M為每個通道的腦電圖數據個數。

        輸出:維度為N x M的FFT結果。N表示信道數,M表示每個信道的FFT數據數。

        關鍵代碼如下:

        for file in os.listdir("../data"):
              fname = Path("../data/"+file)
              x = cPickle.load(open(fname, 'rb'), encoding="bytes")
              for i in range(40):
                        num+=1
                        eeg_realtime = x[b'data'][i]
                        label = x[b'labels'][i]
                        if label[0] >6:
                            val_v = 3
                        elif label[0] < 4:
                            val_v = 1
                        else:
                            val_v = 2
                        if label[1] > 6:
                            val_a = 3
                        elif label[1] < 4:
                            val_a = 1
                        else:
                            val_a = 2
                        if i < 39:
                            va_label.write(str(val_v) + ",")
                            ar_label.write(str(val_a) + ",")
                        if num==1280:
                            va_label.write(str(val_v) )
                            ar_label.write(str(val_v) )
                        eeg_raw = np.reshape(eeg_realtime, (40, 8064))
                        eeg_raw = eeg_raw[:32, :]
                        eeg_feature_arr = self.get_feature(eeg_raw)
                        for f in range(160):
                            if f == 159:
                                fout_data.write(str(eeg_feature_arr[f]))
                            else:
                                fout_data.write(str(eeg_feature_arr[f]) + ",")
                        fout_data.write("\n")
                        print(file + " Video watched")

        然后從計算fft得到所有通道的頻率。輸入:維數為N × M的通道數據,N為通道個數,M為每個通道的腦電圖數據個數。輸出:每個通道的頻帶:Delta, Theta, Alpha, Beta和Gamma。

        # Length data channel
        L = len(all_channel_data[0])
        # Sampling frequency
        Fs = 128
        # Get fft data
        data_fft = self.do_fft(all_channel_data)
        # Compute frequencymotio
        frequency = map(lambda x: abs(x // L), data_fft)
        frequency = map(lambda x: x[: L // 2 + 1] * 2, frequency)
        f1, f2, f3, f4, f5 = itertools.tee(frequency, 5)
        # List frequency
        delta = np.array(list(map(lambda x: x[L * 1 // Fs - 1: L * 4 // Fs], f1)))
        theta = np.array(list(map(lambda x: x[L * 4 // Fs - 1: L * 8 // Fs], f2)))
        alpha = np.array(list(map(lambda x: x[L * 5 // Fs - 1: L * 13 // Fs], f3)))
        beta = np.array(list(map(lambda x: x[L * 13 // Fs - 1: L * 30 // Fs], f4)))
        gamma = np.array(list(map(lambda x: x[L * 30 // Fs - 1:L * 50 // Fs], f5)))

        模型預測

        從特征得到arousal值和valence值。其中Valence-Arousal分別代表情緒的正負向程度和激動程度,基于這兩個維度可以構成一個情感平面空間,任何一個情感狀態可以通過具體的Valence-Arousal數值,映射到VA平面空間中具體的一個點。

        輸入:來自所有頻帶和信道的特征(標準差和平均值),維度為1 × M(特征數量)。

        輸出:由每一種arousal和valence產生的1至3級情緒。1表示低,2表示中性,3表示高。

        self.train_arousal = self.train_arousal[40*(index-1):40*index]
        self.train_valence = self.train_valence[40*(index-1):40*index]
        self.class_arousal = np.array([self.class_arousal[0][40*(index-1):40*index]])
        self.class_valence = np.array([self.class_valence [0][40*(index-1):40*index]])
        distance_ar = list(map(lambda x: ss.distance.canberra(x, feature), self.train_arousal))
        # Compute canberra with valence training data
        distance_va = list(map(lambda x: ss.distance.canberra(x, feature), self.train_valence))
        # Compute 3 nearest index and distance value from arousal
        idx_nearest_ar = np.array(np.argsort(distance_ar)[:3])
        val_nearest_ar = np.array(np.sort(distance_ar)[:3])
        # Compute 3 nearest index and distance value from arousal
        idx_nearest_va = np.array(np.argsort(distance_va)[:3])
        val_nearest_va = np.array(np.sort(distance_va)[:3])
        # Compute comparation from first nearest and second nearest distance.、If comparation less or equal than 0.7, then take class from the first nearest distance. Else take frequently class.
        # Arousal
        comp_ar = val_nearest_ar[0] / val_nearest_ar[1]

        然后從特征中獲取情感類。

        輸入:來自所有頻段和信道的特征(標準偏差),尺寸為1 × M(特征數量)。

        輸出:根據plex模型,情緒在1到5之間的類別。

        class_ar, class_va = self.predict_emotion(feature,fname)

        print(class_ar, class_va)

        if class_ar == 2.0 or class_va == 2.0:

            emotion_class = 5

        if class_ar == 3.0 and class_va == 1.0:

            emotion_class = 1

        elif class_ar == 3.0 and class_va == 3.0:

            emotion_class = 2

        elif class_ar == 1.0 and class_va == 3.0:

            emotion_class = 3

        elif class_ar == 1.0 and class_va == 1.0:

            emotion_class = 4

        預測結果如下圖可見:

        5.png

        完整代碼

        鏈接:

        https://pan.baidu.com/s/1BX58sJv037eIJx9A8jWt3g

        提取碼:rwpe

        *博客內容為網友個人發布,僅代表博主個人觀點,如有侵權請聯系工作人員刪除。



        關鍵詞: Python

        相關推薦

        技術專區

        關閉
        主站蜘蛛池模板: 桑植县| 景谷| 新邵县| 南丹县| 光泽县| 新余市| 共和县| 上高县| 达孜县| 马公市| 阿瓦提县| 马山县| 惠东县| 荆州市| 福鼎市| 瑞昌市| 灌南县| 芮城县| 突泉县| 庄浪县| 邵阳市| 凉城县| 天柱县| 同江市| 邢台市| 东源县| 三河市| 阳信县| 泾源县| 耒阳市| 紫阳县| 石台县| 蕉岭县| 南溪县| 易门县| 屯昌县| 永昌县| 错那县| 施甸县| 新津县| 镇雄县|