前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >轻松学Pytorch – 年龄与性别预测

轻松学Pytorch – 年龄与性别预测

作者头像
OpenCV学堂
发布2020-06-19 10:33:15
2.3K1
发布2020-06-19 10:33:15
举报

大家好,上周太忙,没有更新Pytorch轻松学系列文章,但是我还是会坚定的继续走下去的,所谓有始有终,这个系列我会一直坚持写下去,希望大家继续支持我,积极给我反馈,当然也感谢大家的信任与点赞支持。

本文主要是基于公开数据集,完成了一个人脸的年龄与性别预测网络模型,以及模型训练与导出使用、本篇主要讲述的知识点有以下:

  • 如何实现卷积神经网络的多任务不同输出
  • 如何同时实现分类跟回归预测
  • 基于人脸年龄与性别的公开数据的数据制作
  • 使用多任务网络实现推理预测

数据集

本文使用的数据集来自这里

代码语言:javascript
复制
https://susanqq.github.io/UTKFace/

我使用的是已经对齐跟剪切之后的人脸数据,超过2W多张的标注数据,标注信息如下:

[age]_[gender]_[race]_[date&time].jpg

文件名称格式就是每张图像的标注信息

  • Age表示年龄,范围在0~116岁之间
  • Gender表示性别,0表示男性,1表示女性
  • Race表示人种,

基于Pytorch的dataset超类,重新完成了我的自定义数据集,代码如下:

代码语言:javascript
复制
class AgeGenderDataset(Dataset):
     def __init__(self, root_dir):
         self.transform = transforms.Compose([transforms.ToTensor()])
         img_files = os.listdir(root_dir)
         nums_ = len(img_files)
         # age: 0 ~116, 0 :male, 1 :female
         self.ages = []
         self.genders = []
         self.images = []
         index = 0
         for file_name in img_files:
             age_gender_group = file_name.split("_")
             age_ = age_gender_group[0]
             gender_ = age_gender_group[1]
             self.genders.append(np.float32(gender_))
             self.ages.append(np.float32(age_)/max_age)
             self.images.append(os.path.join(root_dir, file_name))
             index += 1

     def __len__(self):
         return len(self.images)

     def num_of_samples(self):
         return len(self.images)

     def __getitem__(self, idx):
         if torch.is_tensor(idx):
             idx = idx.tolist()
             image_path = self.images[idx]
         else:
             image_path = self.images[idx]
         img = cv.imread(image_path)  # BGR order
         h, w, c = img.shape
         # rescale
         img = cv.resize(img, (64, 64))
         img = (np.float32(img) /255.0 - 0.5) / 0.5
         # H, W C to C, H, W
         img = img.transpose((2, 0, 1))
         sample = {'image': torch.from_numpy(img), 'age': self.ages[idx], 'gender': self.genders[idx]}
         return sample

网络模型结构

卷积层作为基础模型,在卷积层之后使用最大全局池化,完成降维操作,然后再此基础上分为两路的全链接层,分布预测年龄跟性别分类,模型结构详细信息如下:

代码语言:javascript
复制
MyMulitpleTaskNet(
  (cnn_layers): Sequential(
    (0): Conv2d(3, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (1): ReLU()
    (2): BatchNorm2d(32, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (3): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (4): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (5): ReLU()
    (6): BatchNorm2d(64, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (7): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (8): Conv2d(64, 96, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (9): ReLU()
    (10): BatchNorm2d(96, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (11): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (12): Conv2d(96, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (13): ReLU()
    (14): BatchNorm2d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (15): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
    (16): Conv2d(128, 196, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))
    (17): ReLU()
    (18): BatchNorm2d(196, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
    (19): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  )
  (global_max_pooling): AdaptiveMaxPool2d(output_size=(1, 1))
  (age_fc_layers): Sequential(
    (0): Linear(in_features=196, out_features=25, bias=True)
    (1): ReLU()
    (2): Linear(in_features=25, out_features=1, bias=True)
    (3): Sigmoid()
  )
  (gender_fc_layers): Sequential(
    (0): Linear(in_features=196, out_features=25, bias=True)
    (1): ReLU()
    (2): Linear(in_features=25, out_features=2, bias=True)
  )
)

其中年龄是一个数值结果,所以通过回归来预测,这里使用sigmoid,需要注意的是在制作数据集的时候需要把年龄归一化到0~1之间。性别是二分类预测,使用softmax,实现预测。损失函数选择,对于年龄预测使用MSE,对于分类预测使用了交叉熵损失,总的损失函数是二值之和、权重相同

模型训练

模型训练的输入图像格式为BGR、NCHW = Nx3x64x64、每个批次N=16个样本,代码实现如下:

代码语言:javascript
复制
if train_on_gpu:
     model.cuda()

 ds = AgeGenderDataset("D:/python/pytorch_tutorial/UTKFace/")
 num_train_samples = ds.num_of_samples()
 bs = 16
 dataloader = DataLoader(ds, batch_size=bs, shuffle=True)

 # 训练模型的次数
 num_epochs = 25
 # optimizer = torch.optim.SGD(model.parameters(), lr=0.001)
 optimizer = torch.optim.Adam(model.parameters(), lr=1e-2)
 model.train()

 # 损失函数
 mse_loss = torch.nn.MSELoss()
 cross_loss = torch.nn.CrossEntropyLoss()
 index = 0
 for epoch in  range(num_epochs):
     train_loss = 0.0
     for i_batch, sample_batched in enumerate(dataloader):
         images_batch, age_batch, gender_batch = \
             sample_batched['image'], sample_batched['age'], sample_batched['gender']
         if train_on_gpu:
             images_batch, age_batch, gender_batch = images_batch.cuda(), age_batch.cuda(), gender_batch.cuda()
         optimizer.zero_grad()

         # forward pass: compute predicted outputs by passing inputs to the model
         m_age_out_, m_gender_out_ = model(images_batch)
         age_batch = age_batch.view(-1, 1)
         gender_batch = gender_batch.long()

         # calculate the batch loss
         loss = mse_loss(m_age_out_, age_batch) + cross_loss(m_gender_out_, gender_batch)

         # backward pass: compute gradient of the loss with respect to model parameters
         loss.backward()

         # perform a single optimization step (parameter update)
         optimizer.step()

         # update training loss
         train_loss += loss.item()
         if index % 100 == 0:
             print('step: {} \tTraining Loss: {:.6f} '.format(index, loss.item()))
         index += 1

         # 计算平均损失
     train_loss = train_loss / num_train_samples

     # 显示训练集与验证集的损失函数
     print('Epoch: {} \tTraining Loss: {:.6f} '.format(epoch, train_loss))

 # save model
 model.eval()
 torch.save(model, 'age_gender_model.pt')

网络模型结构

基于训练模型,实现人脸年龄与性别预测,人脸检测模型使用OpenCV DNN模块,代码实现如下:

代码语言:javascript
复制
 1def video_landmark_demo():
 2    cnn_model = torch.load("./age_gender_model.pt")
 3    print(cnn_model)
 4    # capture = cv.VideoCapture(0)
 5    capture = cv.VideoCapture("D:/images/video/example_dsh.mp4")
 6
 7    # load tensorflow model
 8    net = cv.dnn.readNetFromTensorflow(model_bin, config=config_text)
 9    while True:
10        ret, frame = capture.read()
11        if ret is not True:
12            break
13        frame = cv.flip(frame, 1)
14        h, w, c = frame.shape
15        blobImage = cv.dnn.blobFromImage(frame, 1.0, (300, 300), (104.0, 177.0, 123.0), False, False);
16        net.setInput(blobImage)
17        cvOut = net.forward()
18        # 绘制检测矩形
19        for detection in cvOut[0,0,:,:]:
20            score = float(detection[2])
21            if score > 0.5:
22                left = detection[3]*w
23                top = detection[4]*h
24                right = detection[5]*w
25                bottom = detection[6]*h
26
27                # roi and detect landmark
28                roi = frame[np.int32(top):np.int32(bottom),np.int32(left):np.int32(right),:]
29                rw = right - left
30                rh = bottom - top
31                img = cv.resize(roi, (64, 64))
32                img = (np.float32(img) / 255.0 - 0.5) / 0.5
33                img = img.transpose((2, 0, 1))
34                x_input = torch.from_numpy(img).view(1, 3, 64, 64)
35                age_, gender_ = cnn_model(x_input.cuda())
36                predict_gender = torch.max(gender_, 1)[1].cpu().detach().numpy()[0]
37                gender = "Male"
38                if predict_gender == 1:
39                    gender = "Female"
40                predict_age = age_.cpu().detach().numpy()*116.0
41                print(predict_gender, predict_age)
42
43                # 绘制
44                cv.putText(frame, ("gender: %s, age:%d"%(gender, int(predict_age[0][0]))), (int(left), int(top)-15), cv.FONT_HERSHEY_SIMPLEX, 0.75, (0, 0, 255), 1)
45                cv.rectangle(frame, (int(left), int(top)), (int(right), int(bottom)), (255, 0, 0), thickness=2)
46                # cv.putText(frame, "score:%.2f"%score, (int(left), int(top)), cv.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
47                c = cv.waitKey(10)
48                if c == 27:
49                    break
50                cv.imshow("face detection + landmark", frame)
51
52    cv.waitKey(0)
53    cv.destroyAllWindows()
54
55
56if __name__ == "__main__":
57    video_landmark_demo()

运行结果如下:

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-06-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 OpenCV学堂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
人脸识别
腾讯云神图·人脸识别(Face Recognition)基于腾讯优图强大的面部分析技术,提供包括人脸检测与分析、比对、搜索、验证、五官定位、活体检测等多种功能,为开发者和企业提供高性能高可用的人脸识别服务。 可应用于在线娱乐、在线身份认证等多种应用场景,充分满足各行业客户的人脸属性识别及用户身份确认等需求。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档