基于朴素贝叶斯分类器的语音性别识别 高斯分布
具体题目参考
https://www.kaggle.com/primaryobjects/voicegender
U201714600
数据集概述
集合中共有 3168 条数据,男女各 1584 条,每条数据可视
作一个长度为 21 的一维数组。其中前 20 个数值是这条语音的 20 个特征值,这些特征值包括了语音信号的长度、基频、标准差、频带中值点/一分位频率/三分位频率等;最后一个数值是性别标记。元数据集中直接以字符串,即 male 和 female 进行标注。
问题分析
使用 7:3 划分数据集。通过朴素贝叶斯方法,可以先对所有特征值做统计,并且通过连续性参数估计(高斯分布)方法得到参数。之后使用预测函数预测测试集。
打开voice.csv文件,20个特征值分类为F1,F2 ,F3 …,F20。
朴素贝叶斯方法,对于某测试数据,Fi’代表其在Fi特征的值
则P(男|F1’×F2’×F3’....×F20’)=P(男)*P(F1’|男)*P(F2’|男).....*P(F20’|男)/
[P(F1)*P(F2’).....*P(F20’)]
则可以通过比较P(男)*P(F1’|男)*P(F2’|男).....*P(F20’|男)和P(女)*P(F1’|女)*P(F2’|女).....*P(F20’|女)的大小给出性别。
方法本质上是概率的叠乘,20个特征,多个特征的概率相乘,乘积的结果将会非常小,从而影响实验结果。所以这里统一取对数。
考虑到数据缺失的问题,发现mode 和 dfrange 等列都有为0 的值,说明数据有缺失,若采用高斯分布,则其概率可能接近0,导致总概率非常低,所以对缺失的值,采取该特征平均值代替。结合高斯分布,同时避免了某个概率接近0,则总的概率接近0的情况。
原数据集中直接以字符串male和female进行标注,本次实验用1表示男性、0表示女性。
原数据中男女并不是随机分布,而是前半部分男后半部分女,所以要先打乱或者随机抽取训练数据。
1.3设计与分析
设计流程图如图1.1
图1.1
1.4结果分析(包括实际运行结果截图,性能结果图、表等)
由于每次随机划分测试与训练数据,结果不同,取四次运行结果,如图
测试集数量: 1056 正确数量: 941 正确率: 0.8910984848484849
测试男数量: 530 正确数量: 512 正确率: 0.9660377358490566
测试女数量: 526 正确数量: 429 正确率: 0.8155893536121673
图1.2
图1.3
图1.4
图1.5
综合以上情况得到平均情况如下表1.1
表1.1
综合正确率 0.89 |
综合错误率 0.11 |
男生正确率 0.97 |
男生错误率 0.03 |
女生正确率 0.80 |
女生错误率 0.20 |
其它:实验测试过程中发现取对数与否,处理值元数据中值为0的数据与否 对结果影响不大。
1.5思考与总结
朴素贝叶斯与那里比较简单,实验难度在于对numpy掌握不熟练。
朴素贝叶斯假设特征独立,但实验实际中有些数据关联比较大,如三分频和中间值。
实验结果表明对男生,女生的测试准确度有很大差别,原因可能是女声的声调(pitch) 变化范围比男性的大,准确说是女性语音的基频更高,而训练数据集不够大而不能很好的覆盖这些变化。
实验给我留下了一个问题,如何将一段人声处理成这20个特征,解决这个问题就能实现一个有实际作用的应用。
1.6代码附录
import numpy as np
#csv 文件格式
import csv
import matplotlib.pyplot as plt
import math
from scipy.stats import norm
#处理数据得到训练和测试的特征和标签 返回四个列表
def LoadFileToSet(file_name):
with open(file_name) as f:
rawCharacteristic = [] # 特征
trainCharacteristic = []
testCharacteristic = []
trainLable = []
testLable = []
f_csv=csv.DictReader(f)
lableName =list(f_csv.fieldnames)
#print(lableName)
#
for line in f_csv.reader:
rawCharacteristic.append(line)
# 转换标签 male 0 female 1
for i in range(len(rawCharacteristic)):
rawCharacteristic[i][20]=0 if rawCharacteristic[i][20]=='male' else 1
#打乱
np.random.shuffle(rawCharacteristic)
# for line in rawCharacteristic:
# print(line)
#特征列数 20
# 求每一个特征的平均值
data_mat = np.array(rawCharacteristic).astype(float)
count_vector = np.count_nonzero(data_mat, axis=0)
sum_vector = np.sum(data_mat, axis=0)
mean_vector = sum_vector / count_vector
## 数据缺失的地方 用 平均值填充 其实去掉对结果没啥影响
for row in range(len(data_mat)):
for col in range (0,20):
if data_mat[row][col] == 0.0:
data_mat[row][col] = mean_vector[col]
#划分训练集 测试集 特征和标签
num1 = int(len(data_mat) / 3 * 2) #2112
for i in range(num1):
trainCharacteristic.append(data_mat[i][:20])
trainLable.append(data_mat[i][20])
for i in range(num1,len(data_mat)):
testCharacteristic.append(data_mat[i][:20])
testLable.append(data_mat[i][20])
#不能data_mat[:num][:20] shape 20,21 ???
trainLable=np.array(trainLable).astype(int)
testLable=np.array(testLable).astype(int)
return trainCharacteristic,testCharacteristic,trainLable,testLable
#num1 是2/3数量 2112
trainCharacteristic,testCharacteristic,trainLable,testLable=LoadFileToSet("./voice.csv")
#测试结果标签
TestResultlable=[]
# 训练集性别概率 先验概率
pmale = trainLable.sum(axis=0) / len(trainLable)
pfamle = 1 - pmale
# 训练集性别前提下的特征
trainMaleCharacteristic=[]
trainFemaleCharacteristic=[]
for i in range(len(trainLable)):
if trainLable[i]==0: trainMaleCharacteristic.append(trainCharacteristic[i])
else: trainFemaleCharacteristic.append(trainCharacteristic[i])
#MaleMeanVector=np.sum(trainMaleCharacteristic,axis=0)/len(trainMaleCharacteristic)
#训练男女各个特征的均值和标准差 var方差 2*20
MaleMeanVector=np.mean(trainMaleCharacteristic,axis=0)
MaleDeviationVector=np.sqrt(np.var(trainMaleCharacteristic,axis=0))
FemaleMeanVector=np.mean(trainFemaleCharacteristic,axis=0)
FemaleDeviationVector=np.sqrt(np.var(trainFemaleCharacteristic,axis=0))
#计算概率 过程取对数
BeMaleRrobablityVector=[]
BeFemaleRrobablityVector=[]
AccurateResualtNum =0
MaleAccurateResualtNum =0
FemaleAccurateResualtNum =0
for i in range(len(testLable)):
BeMaleRrobablity = math.log2(pmale)
BeFemaleRrobablity = math.log2(pfamle)
for j in range(0,20):
BeMaleRrobablity+=math.log2(norm.cdf(testCharacteristic[i][j],MaleMeanVector[j],MaleDeviationVector[j]))
BeFemaleRrobablity+=math.log2(norm.cdf(testCharacteristic[i][j],FemaleMeanVector[j],FemaleDeviationVector[j]))
# #不取对数
# BeMaleRrobablity = (pmale)
# BeFemaleRrobablity = (pfamle)
# for j in range(0,20):
# BeMaleRrobablity*=norm.cdf(testCharacteristic[i][j],MaleMeanVector[j],MaleDeviationVector[j])
# BeFemaleRrobablity*=(norm.cdf(testCharacteristic[i][j],FemaleMeanVector[j],FemaleDeviationVector[j]))
#print(BeMaleRrobablity,BeFemaleRrobablity)
#BeMaleRrobablityVector.append(BeMaleRrobablity)
#BeFemaleRrobablityVector.append(BeFemaleRrobablity)
lable1=0 if BeMaleRrobablity>BeFemaleRrobablity else 1
TestResultlable.append(lable1)
if lable1==testLable[i]:
AccurateResualtNum +=1
if lable1==0: MaleAccurateResualtNum+=1
else: FemaleAccurateResualtNum+=1
print("测试集数量: "+ str(len(testLable))+" 正确数量: "+ str(AccurateResualtNum)+ " 正确率: "+str(AccurateResualtNum/len(testLable)))
print("测试男数量:",len(testLable)-np.count_nonzero(testLable)," 正确数量: ",MaleAccurateResualtNum," 正确率: ",MaleAccurateResualtNum/(len(testLable)-np.count_nonzero(testLable)))
print("测试女数量:",np.count_nonzero(testLable)," 正确数量: ",FemaleAccurateResualtNum," 正确率: ",FemaleAccurateResualtNum/np.count_nonzero(testLable))