03 基于kNN分类算法的语音情感识别

1. 实验目的

  • 掌握k近邻分类算法的原理
  • 掌握基于k近邻分类算法的情感识别基本过程
  • 利用MATLAB、python实现该情感识别过程

2. 实验原理

KNN分类的思想是给定一个在特征空间中的待分类样本,如果其附近的K个训练样本大多数属于一个类别,那么他也是这个类别。已知特征集为 {X1,X2,...,XK} ,待分类样本为 X ​,计算​ X 与​ Xl 的距离 D(X,Xl)=∑i=1N[X(i)−Xl(i)]2 , min{D(X,Xl)} 是最近邻,选出 D(X,Xl) 最小的K个,然后投票决定。

  • 提取特征,构造特征向量 X1,X2,...,Xn
  • 设定K的值
  • 提取待识别样本的向量 X ,计算​ X 与​ Xl 的距离
  • 选出 D(X,Xl) 最小的K个,然后投票决定。

3. 实验步骤

3.1 特征提取

阿木:02 语音情感识别数据集及特征提取,得到5个140*50的不同情绪文件。

3.2 k值设定与训练集、测试集划分

通过k折交叉验证选取k值,或因为该数据集比较小,所以可以令k从1取到设定的最大值,比较不同k值下的识别准确率。

KMAX = 30;
NumberOfTrain=size(fearVec,2)* 0.5; %一半测试用,一半训练用
trainVector=[fearVec(:,1:NumberOfTrain),hapVec(:,1:NumberOfTrain),neutralVec(:,1:NumberOfTrain),sadnessVec(:,1:NumberOfTrain),angerVec(:,1:NumberOfTrain)]; % 构建训练样本集
testVector=[fearVec(:,(NumberOfTrain+1):size(fearVec,2)),hapVec(:,(NumberOfTrain+1):size(hapVec,2)),neutralVec(:,(NumberOfTrain+1):size(neutralVec,2)),sadnessVec(:,(NumberOfTrain+1):size(sadnessVec,2)),angerVec(:,(NumberOfTrain+1):size(angerVec,2))]; % 构建测试样本集

K = 9
data = np.hstack((fear, happy, neutral, sadness, anger))
y = np.array([[i] * 50 for i in range(5)]).flatten()
per = np.random.permutation(250)
data_train = data[:, per[:180]]
label_train = y[per[:180]]
data_test = data[:, per[180:]]
label_test = y[per[180:]]
label_pred = np.zeros(250 - 180)
j = 0

3.3 计算待识别样本与已知样本距离

distanceMatrix=zeros(size(trainVector,2),size(testVector,2)); % 每一列表示某个测试语音与所有训练集样本的距离
%% 计算每个测试样本和训练样本集各样本的距离
for i=1:size(testVector,2)
    for j=1:size(trainVector,2)
        distanceMatrix(j,i)=norm(testVector(:,i)-trainVector(:,j)); %计算欧氏距离
    end
end
python版本:
for test in data_test.T:
    scores = np.zeros(len(data_train.T))
    for i in range(len(data_train.T)):
        scores[i] = np.sum(np.power(test - data_train[:, i], 2))
    pos = np.argsort(scores)[:K]
    result = label_train[pos]
    label = get_most_label(result)
    label_pred[j] = label
    j += 1
def get_most_label(result):
    rst = {}
    for r in result:
        if r not in rst.keys():
            rst[r] = 1
        else:
            rst[r] += 1
    m = sorted(rst.items(), key=lambda x: x[1], reverse=True)
    return m[0][0]

3.4 投票表决

%% 统计分类结果 (根据相应的特征向量在数组trainVector或testVector中所处的位置来辨别类型)
totalTestNumber=size(fearVec,2)-NumberOfTrain;
n1=NumberOfTrain;
n2=n1+NumberOfTrain;
n3=n2+NumberOfTrain;
n4=n3+NumberOfTrain;
n5=n4+NumberOfTrain;
p1=size(fearVec,2)-NumberOfTrain;
p2=p1+size(hapVec,2)-NumberOfTrain;
p3=p2+size(neutralVec,2)-NumberOfTrain;
p4=p3+size(sadnessVec,2)-NumberOfTrain;
p5=p4+size(angerVec,2)-NumberOfTrain;
if(n5~=size(trainVector,2)||p5~=size(testVector,2))
    disp('data error')
    return;
end
acc = zeros(1,KMAX);
for k=1:KMAX
    emtionCounter=zeros(1,5);
    for i=1:size(distanceMatrix,2)
        flag=zeros(1,5);
        [sortVec,index]=sort(distanceMatrix(:,i));
        % 统计K个近邻中各类别的数量
        for j=1:k
            if(n1>=index(j)&&index(j)>=1)
                flag(1)=flag(1)+1;
            elseif(n2>=index(j)&&index(j)>n1)
                flag(2)=flag(2)+1;
            elseif(n3>=index(j)&&index(j)>n2)
                flag(3)=flag(3)+1;
            elseif(n4>=index(j)&&index(j)>n3)
                flag(4)=flag(4)+1;
            else
                flag(5)=flag(5)+1;
            end
        end
        [~,index1]=sort(flag);
        % 如果K个近邻中数量最多的类别与该样本实际的类别一致,则认为算法识别正确,相应counter加一。
        if((p1>=i&&i>=1)&&index1(5)==1)
            emtionCounter(index1(5))=emtionCounter(index1(5))+1;
            
        elseif((p2>=i&&i>p1)&&index1(5)==2)
            emtionCounter(index1(5))=emtionCounter(index1(5))+1;
            
        elseif((p3>=i&&i>p2)&&index1(5)==3)
            emtionCounter(index1(5))=emtionCounter(index1(5))+1;
            
        elseif((p4>=i&&i>p3)&&index1(5)==4)
            emtionCounter(index1(5))=emtionCounter(index1(5))+1;
            
        elseif((p5>=i&&i>p4)&&index1(5)==5)
            emtionCounter(index1(5))=emtionCounter(index1(5))+1;
        end
    end
    acc(k) = sum(emtionCounter)/(totalTestNumber*5);
end

[acc2,index2]=sort(acc);
k=index2(KMAX);
acc_max = acc2(KMAX);
emtionCounter=zeros(1,5);

4. 实验结果

%% 显示结果
ratio=emtionCounter./totalTestNumber;
bar(ratio);
ylim([0,1])
set(gca,'XTickLabel',{'惊恐','高兴','中性','高兴','生气'});
n=1:5;
for i = 1:length(ratio)
    text(n(i)-0.18,ratio(i)+0.02,num2str(ratio(i)));
end
title(strcat('KNN 算法识别结果\_ ',strcat('K = ',num2str(k))));
xlabel('情感类别')
ylabel('识别率')
disp('识别率为');
disp(acc_max);

k取不同值时不同情绪的识别率:

识别率为0.7840

def confusion_matrix_info(y_true, y_pred, labels=['fear', 'happy', 'neutr', 'sad', 'anger'],
                          title='confusion matrix'):
    """
    计算混淆矩阵以及一些评价指标,并将混淆矩阵绘图出来
    :param y_true: 真实标签,非one-hot编码
    :param y_pred: 预测标签,非one-hot编码
    :param labels: 标签的含义
    :param title: 绘图的标题
    :return:
    """
    C2 = confusion_matrix(y_true, y_pred)
    C = pd.DataFrame(C2, columns=labels, index=labels)
    m, _ = C2.shape
    for i in range(m):
        precision = C2[i, i] / sum(C2[:, i])
        recall = C2[i, i] / sum(C2[i, :])
        f1 = 2 * precision * recall / (precision + recall)
        print('In class {}:\t total samples: {}\t true predict samples: {}\t'
              'acc={:.4f},\trecall={:.4f},\tf1-score={:.4f}'.format(
            labels[i], sum(C2[i, :]), C2[i, i], precision, recall, f1))
    print('-' * 100, '\n', 'average f1={:.4f}'.format(f1_score(y_true, y_pred, average='micro')))

    f, ax = plt.subplots()
    sns.heatmap(C, annot=True, ax=ax, cmap=plt.cm.binary)
    ax.set_title(title)
    ax.set_xlabel('predict')
    ax.set_ylabel('true')
    plt.show()

5. 参考文献

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值