基于SVM的人脸图像性别分类

本来想用深度学习做的,但pytorch,tensorflow都死活装不上去,而且机器GPU也不行,所以就用传统SVM模型做了一个手写数字识别,又从网上下了数据,改成人脸性别识别。

数据:有20000张人脸图片,另有一个train.csv,共2列,分别为图片编号与性别(其中0为男,1为女)。

思路:
先手工剔除异常图片。读入数据,找到每张图片的性别,数据和性别分别存在两个数组里面,然后调用训练函数训练,训练后把模型用pickle保存起来以便下次使用。读入的数据也保存。
训练模型思路:先划分训练集和测试集,标准化数据,用SVM+poly核函数(经尝试比linear与rbf效果好)+随机搜索方法自动优化超参数(C,gamma和多项式系数)。
后来做成了命令行工具,代码如下
facereco.py

import train
TOTAL = 3000 # 要查找的文件数
IMG_HEIGHT = 50
IMG_SIZE = IMG_HEIGHT ** 2
dataX = np.empty((TOTAL,IMG_SIZE),dtype="int32") # 数据
dataY = np.empty((TOTAL,),dtype="uint8") # 标签,非0即1
retrain = False
Help=    "使用方法:\n"\
         "python facereco.py [filename_list] [-h -H] [-retrain]\n"\
         "filename_list:要进行识别的文件名列表\n"\
         "-h -H 显示此帮助\n"\
         "-retrain:重新训练模型然后应用\n"\
         "示例:python facereco.py me.jpg\n python facereco.py me.jpg you.jpg -retrain"
def initData(path):
    # 读取数据
    # 思路:先读取train.csv获取编号和值,再读图片,把图片文件名中的编号提取出来,查找对应的值
    traind = np.loadtxt("train.csv",dtype="int32",delimiter=',')
    fileList = os.listdir(path)
    count = 0
    for f in fileList:
        if count >= TOTAL:
            break
        if os.path.isdir(f):
            continue # 不递归搜索
        pic_index = int(f.replace(".jpg", '')) # 提取图片编号
        #print(pic_index)
        index = np.where(traind[:,0] == pic_index) # 找到图片编号对应train.csv的第几行(注意只在第0列找)
        #np.where返回值是一个数组,大小是n(找到的元素个数)*m(维数,此处为1)
        dataY[count] = traind[index[0][0]][1]
        dataX[count,:] = img2Vector(os.path.join(path, f))
        count += 1
    with open("dataX.np","wb") as fp: #由于数据量较大,将数据存入本地下次直接读取
        pickle.dump(dataX, fp)
    with open("dataY.np","wb") as fp:
        pickle.dump(dataY,fp)

def img2Vector(img_path:str):
    fp = open(img_path,"rb")
    img = Image.open(fp)
    img = img.resize((IMG_HEIGHT,IMG_HEIGHT)).convert("L") 
    imgData = np.array(img.getdata()).reshape(1,IMG_SIZE) # 转为行向量
    fp.close()
    return imgData
def parseArg():
    '''解析参数'''
    global retrain
    files = []
    argc = len(sys.argv)
    if argc == 1:
        print(Help)
        sys.exit(3)
    for arg in sys.argv[1:]:
        if arg.lower() == "-h":
            print(Help)
            sys.exit(0) # 存在-h/-H就输出帮助并退出
        if arg.lower() == "-retrain":
            retrain = True
        else:
            files.append(arg)
    return files
def trainModel():
    # 训练模型
    '''重新训练模型'''
    global dataX,dataY
    try:
        with open("dataX.np","rb") as fp: # 尝试加载数据
            dataX = pickle.load(fp)
        with open("dataY.np","rb") as fp:
            dataY = pickle.load(fp)
    except (OSError,pickle.UnpicklingError):
        initData("./train_data")
    svm,svmErr = train.SVM_train(dataX, dataY)
    print("测试集错误率%.3f"%svmErr)
    #print("用时%.3fs"%(time()-st))
    with open("model.pkl","wb") as fp:
        pickle.dump(svm,file=fp) #保存模型
    return svm
if __name__ == "__main__":
    files = parseArg()
    st = time()
    try:
        if retrain: # 如果参数中存在'retrain',则重新训练模型
            svm = trainModel()
        else: # 否则,尝试加载现有模型
            with open("model.pkl",mode="rb") as fp:
                svm = pickle.load(fp)
    except (OSError,pickle.UnpicklingError):
        print("未找到模型或模型损坏,将重新训练")
        svm = trainModel()
    # 开始用模型识别
    argc = len(files)
    for i in range(0,argc):
        dataX[i,:] = img2Vector(files[i])
    dataY = train.apply(svm, dataX[0:argc,:])
    for i in range(0,argc):
        gender = "男" if dataY[i] == 0 else "女"
        print("{:s} 识别结果为 {:s}".format(files[i],gender))
    print("用时%.3fs"%(time()-st))

train.py

try: #尝试读加载scaler
    _fp = open("scaler.pkl","rb")
    _scaler = pickle.load(_fp)
    _fp.close()
except:
    _scaler = MinMaxScaler()

def SVM_train(dataX,dataY):
    '''
    Parameters
    ----------
    dataX : ndarray  X\n
    dataY : ndarray  Y\n

    Returns
    -------
    tuple 训练的模型与在测试集上的误差

    '''
    global _scaler
    paramList = { #模型参数范围
        "C":np.random.uniform(0.6,6,size=(30,)),
        "gamma":np.random.uniform(0.001,0.05,size=(30,)),
        "degree":(3,4,5,6)
    }
    trainX,testX,trainY,testY = train_test_split(dataX,dataY,test_size=0.25)
    trainX = _scaler.fit(trainX).transform(trainX)
    testX = _scaler.transform(testX) # 注意在测试集上不能重新fit
    svm = RandomizedSearchCV(SVC(kernel="poly"),param_distributions=paramList,n_iter=6,n_jobs=3)
    svm = svm.fit(trainX,trainY)
    # 在测试集上测试
    result = svm.predict(testX)
    fp = open("scaler.pkl","wb")
    pickle.dump(_scaler,fp) #保存scaler,不然retrain==False时scaler不可用
    fp.close()
    return svm,get_error(result, testY)
def get_error(result,Y_test):
    same = 0
    _len = len(result)
    print("Confusion martix on test set")
    cm = confusion_matrix(result, Y_test) # 计算混淆矩阵
    print(cm)
    for i in range(0,cm.shape[0]): # 计算正确个数,对角线上是正确的
        same += cm[i][i]
    return (_len - same) / _len
def apply(svm,data:np.ndarray): #应用模型
    '''
    应用svm模型\n
    Parameters
    ------------
    svm:svm模型\n
    data: ndarray  要应用的数据\n
    Returns
    -----------
    ndarray 结果
    '''
    global _scaler
    _data = _scaler.transform(data)
    return svm.predict(_data)

在测试集上的准确率是87%左右。单次训练需要35~45s。
用未被选中的训练数据检测,取男女各6人,发现男的里又有1人检测为女性,女的里有1人检测为男性,我自拍一张也测对了,这个概率应该勉强可以吧。

数据集参见这篇文章里的链接

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
SVM(支持向量机)是一种常见的分类算法,可以用于图像分类。下面是一个基于SVM图像分类MATLAB代码示例: 1. 准备数据集 首先需要准备一个图像数据集,包括若干张图片和对应的标签(类别)。可以使用MATLAB自带的图像数据集或者自己收集整理。 2. 特征提取 将每张图片转化为特征向量,这里使用HOG特征。可以使用MATLAB自带的HOG函数进行提取。 3. 数据预处理 将特征向量归一化(normalize)或者标准化(standardize),以便更好地应用SVM分类器。 4. 训练SVM分类器 使用训练数据集训练一个SVM分类器。可以使用MATLAB自带的fitcsvm函数进行训练。 5. 测试 将测试数据集的图片转化为特征向量,然后使用训练好的SVM分类器进行分类。可以使用MATLAB自带的predict函数进行分类,并计算分类准确率。 下面是一个简单的示例代码: ```matlab % 准备数据集 data = imageDatastore('path/to/image/folder', 'IncludeSubfolders', true, 'LabelSource', 'foldernames'); [trainData, testData] = splitEachLabel(data, 0.8, 'randomized'); % 特征提取 featuresTrain = cellfun(@(x) extractHOGFeatures(x), trainData.Files, 'UniformOutput', false); featuresTest = cellfun(@(x) extractHOGFeatures(x), testData.Files, 'UniformOutput', false); % 数据预处理 XTrain = cell2mat(featuresTrain); XTest = cell2mat(featuresTest); YTrain = trainData.Labels; YTest = testData.Labels; [XTrain, mu, sigma] = zscore(XTrain); XTest = (XTest - mu) ./ sigma; % 训练SVM分类svm = fitcsvm(XTrain, YTrain); % 测试 YTest_pred = predict(svm, XTest); accuracy = sum(YTest == YTest_pred) / numel(YTest); fprintf('Testing accuracy: %.2f%%\n', accuracy * 100); ``` 注意,这只是一个简单的示例代码,实际应用中可能需要更多的数据预处理和参数调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值