【动手学PaddlePaddle2.0系列】手把手带你实现表情分类

手把手带你实现表情分类

任务:本次识别是一个图像二分类任务,利用卷积神经网络实现图像中表情的分类

实践平台:百度AI实训平台-AI Studio、python3.7+飞桨2.0.0
【动手学PaddlePaddle2.0系列】手把手带你实现表情分类_第1张图片

卷积神经网络(CNN)

卷积神经网络(Convolution Neural Network,简称CNN),CNN 其实可以看作 DNN 的一种特殊形式。它跟传统 DNN 标志性的区别在于两点,Convolution Kernel 以及 Pooling。

【动手学PaddlePaddle2.0系列】手把手带你实现表情分类_第2张图片

数据集介绍

网上公开的人脸表情图像数据集:

  • 包含positive和negative两种表情,共7200余张图片
  • 图片为16464,灰度图像
  • 本次实验中,取其中的10%作为测试集,90%作为训练集

【动手学PaddlePaddle2.0系列】手把手带你实现表情分类_第3张图片

# 解压数据集

!cd 'data/data71015' && unzip -q face_data.zip
# 导入所需要的包

import os
import pandas as pd
import numpy as np
from PIL import Image

import paddle
import paddle.nn as nn
from paddle.io import Dataset
import paddle.vision.transforms as T
from paddle.metric import Accuracy

import warnings
warnings.filterwarnings("ignore")


all_file_dir = 'data/data71015/face_data'

img_list = []
label_list = []

label_id = 0

class_list = [c for c in os.listdir(all_file_dir) if os.path.isdir(os.path.join(all_file_dir, c))]

# print(class_list)
for class_dir in class_list:

    image_path_pre = os.path.join(all_file_dir, class_dir)

    for img in os.listdir(image_path_pre):
        # print(img)
        # f.write("{0}\t{1}\n".format(os.path.join(image_path_pre, img), label_id))
        img_list.append(os.path.join(image_path_pre, img))
        label_list.append(label_id)
    label_id += 1

img_df =  pd.DataFrame(img_list)
label_df = pd.DataFrame(label_list)

img_df.columns = ['images']
label_df.columns = ['label']

df = pd.concat([img_df, label_df], axis=1)
# 打乱数据
df = df.reindex(np.random.permutation(df.index))

df.to_csv('face_data.csv', index=0)

# 读取数据
train_images = pd.read_csv('face_data.csv', usecols=['images','label'])

# 划分训练集和校验集
all_size = len(train_images)
print(all_size)
train_size = int(all_size * 0.8)
train_image_path_list = train_images[:train_size]
val_image_path_list = train_images[train_size:]

print(len(train_image_path_list))
print(len(val_image_path_list))
7281
5824
1457
# 构建Dataset
class MyDataset(paddle.io.Dataset):
    """
    步骤一:继承paddle.io.Dataset类
    """
    def __init__(self, train_list, val_list, mode='train'):
        """
        步骤二:实现构造函数,定义数据读取方式
        """
        super(MyDataset, self).__init__()
        self.data = []
        # 借助pandas读取csv文件
        self.train_images = train_list
        self.test_images = val_list
        if mode == 'train':
            # 读train_images.csv中的数据
            for row in self.train_images.itertuples():
                # self.data.append(['data/data71799/lemon_lesson/train_images/'+getattr(row, 'images'), getattr(row, 'label')])
                self.data.append([getattr(row, 'images'), getattr(row, 'label')])
        else:
            # 读test_images.csv中的数据
            for row in self.test_images.itertuples():
                # self.data.append(['data/data71799/lemon_lesson/train_images/'+getattr(row, 'images'), getattr(row, 'label')])
                self.data.append([getattr(row, 'images'), getattr(row, 'label')])
    def load_img(self, image_path):
        # 实际使用时使用Pillow相关库进行图片读取即可,这里我们对数据先做个模拟
        image = Image.open(image_path)

        return image

    def __getitem__(self, index):
        """
        步骤三:实现__getitem__方法,定义指定index时如何获取数据,并返回单条数据(训练数据,对应的标签)
        """
        image = self.load_img(self.data[index][0])
        label = self.data[index][1]

        return data_transforms(image), np.array(label, dtype='int64')

    def __len__(self):
        """
        步骤四:实现__len__方法,返回数据集总数目
        """
        return len(self.data)
#预处理

data_transforms = T.Compose([
    T.Resize([28, 28]),
    T.ToTensor(),
])

#train_loader
train_dataset = MyDataset(train_list=train_image_path_list, val_list=val_image_path_list, mode='train')
train_loader = paddle.io.DataLoader(train_dataset, places=paddle.CPUPlace(), batch_size=64, shuffle=True, num_workers=0)

#val_loader
val_dataset =MyDataset(train_list=train_image_path_list, val_list=val_image_path_list, mode='test')
val_loader = paddle.io.DataLoader(val_dataset, places=paddle.CPUPlace(), batch_size=64, shuffle=True, num_workers=0)

#定义卷积神经网络

import paddle
import paddle.nn as nn

class LeNet(nn.Layer):

    def __init__(self, num_classes=10):
        super(LeNet, self).__init__()
        self.num_classes = num_classes
        self.features = nn.Sequential(
            nn.Conv2D(
                1, 6, 3, stride=1, padding=1),
            nn.ReLU(),
            nn.MaxPool2D(2, 2),
            nn.Conv2D(
                6, 16, 5, stride=1, padding=0),
            nn.ReLU(),
            nn.MaxPool2D(2, 2))

        if num_classes > 0:
            self.fc = nn.Sequential(
                nn.Linear(400, 120),
                nn.Linear(120, 84), nn.Linear(84, num_classes))

    def forward(self, inputs):
        x = self.features(inputs)

        if self.num_classes > 0:
            x = paddle.flatten(x, 1)
            x = self.fc(x)
        return x
# 模型封装
model = LeNet(num_classes=2)
model = paddle.Model(model)

# 定义优化器
optim = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())
model.summary((128, 1, 28, 28))
---------------------------------------------------------------------------
 Layer (type)       Input Shape          Output Shape         Param #    
===========================================================================
   Conv2D-1      [[128, 1, 28, 28]]    [128, 6, 28, 28]         60       
    ReLU-1       [[128, 6, 28, 28]]    [128, 6, 28, 28]          0       
  MaxPool2D-1    [[128, 6, 28, 28]]    [128, 6, 14, 14]          0       
   Conv2D-2      [[128, 6, 14, 14]]   [128, 16, 10, 10]        2,416     
    ReLU-2      [[128, 16, 10, 10]]   [128, 16, 10, 10]          0       
  MaxPool2D-2   [[128, 16, 10, 10]]    [128, 16, 5, 5]           0       
   Linear-1         [[128, 400]]          [128, 120]          48,120     
   Linear-2         [[128, 120]]          [128, 84]           10,164     
   Linear-3         [[128, 84]]            [128, 2]             170      
===========================================================================
Total params: 60,930
Trainable params: 60,930
Non-trainable params: 0
---------------------------------------------------------------------------
Input size (MB): 0.38
Forward/backward pass size (MB): 14.05
Params size (MB): 0.23
Estimated Total Size (MB): 14.67
---------------------------------------------------------------------------






{'total_params': 60930, 'trainable_params': 60930}
# 配置模型
model.prepare(
    optim,
    paddle.nn.CrossEntropyLoss(),
    Accuracy()
    )

# vdl可视化

from visualdl import LogReader, LogWriter

args={
     
    'logdir':'./vdl',
    'file_name':'vdlrecords.model.log',
    'iters':0,
}

# 配置visualdl
write = LogWriter(logdir=args['logdir'], file_name=args['file_name'])
#iters 初始化为0
iters = args['iters'] 

#自定义Callback
class Callbk(paddle.callbacks.Callback):
    def __init__(self, write, iters=0):
        self.write = write
        self.iters = iters

    def on_train_batch_end(self, step, logs):

        self.iters += 1

        #记录loss
        self.write.add_scalar(tag="loss",step=self.iters,value=logs['loss'][0])
        #记录 accuracy
        self.write.add_scalar(tag="acc",step=self.iters,value=logs['acc'])
`./vdl/vdlrecords.model.log` is exists, VisualDL will add logs to it.
# 模型训练与评估
model.fit(train_loader,
        val_loader,
        epochs=30,
        callbacks=Callbk(write=write, iters=iters),
        verbose=1,
        )


The loss value printed in the log is the current step, and the metric is the average value of previous step.
Epoch 1/30
step 91/91 [==============================] - loss: 0.4497 - acc: 0.7648 - 39ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.3698 - acc: 0.8332 - 37ms/step        
Eval samples: 1457
Epoch 2/30
step 91/91 [==============================] - loss: 0.2299 - acc: 0.8453 - 40ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.2713 - acc: 0.8648 - 36ms/step        
Eval samples: 1457
Epoch 3/30
step 91/91 [==============================] - loss: 0.4113 - acc: 0.8583 - 38ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.4222 - acc: 0.8668 - 36ms/step        
Eval samples: 1457
Epoch 4/30
step 91/91 [==============================] - loss: 0.1887 - acc: 0.8686 - 40ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.1599 - acc: 0.8799 - 42ms/step        
Eval samples: 1457
Epoch 5/30
step 91/91 [==============================] - loss: 0.2030 - acc: 0.8728 - 38ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.4274 - acc: 0.8710 - 37ms/step        
Eval samples: 1457
Epoch 6/30
step 91/91 [==============================] - loss: 0.2805 - acc: 0.8798 - 39ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.3917 - acc: 0.8703 - 37ms/step        
Eval samples: 1457
Epoch 7/30
step 91/91 [==============================] - loss: 0.2474 - acc: 0.8846 - 41ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.1723 - acc: 0.8895 - 37ms/step        
Eval samples: 1457
Epoch 8/30
step 91/91 [==============================] - loss: 0.2656 - acc: 0.8889 - 38ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.4242 - acc: 0.8730 - 37ms/step        
Eval samples: 1457
Epoch 9/30
step 91/91 [==============================] - loss: 0.2926 - acc: 0.8937 - 41ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.2314 - acc: 0.8826 - 37ms/step        
Eval samples: 1457
Epoch 10/30
step 91/91 [==============================] - loss: 0.2662 - acc: 0.9004 - 38ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.2651 - acc: 0.8922 - 36ms/step        
Eval samples: 1457
Epoch 11/30
step 91/91 [==============================] - loss: 0.3136 - acc: 0.9016 - 39ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.3480 - acc: 0.8895 - 36ms/step        
Eval samples: 1457
Epoch 12/30
step 91/91 [==============================] - loss: 0.1664 - acc: 0.9045 - 39ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.1817 - acc: 0.8895 - 38ms/step        
Eval samples: 1457
Epoch 13/30
step 91/91 [==============================] - loss: 0.2565 - acc: 0.9076 - 39ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.2876 - acc: 0.8847 - 37ms/step        
Eval samples: 1457
Epoch 14/30
step 91/91 [==============================] - loss: 0.2123 - acc: 0.9056 - 39ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.2496 - acc: 0.8909 - 37ms/step        
Eval samples: 1457
Epoch 15/30
step 91/91 [==============================] - loss: 0.2957 - acc: 0.9133 - 38ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.1879 - acc: 0.8881 - 36ms/step        
Eval samples: 1457
Epoch 16/30
step 91/91 [==============================] - loss: 0.2162 - acc: 0.9154 - 38ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.3824 - acc: 0.8895 - 36ms/step        
Eval samples: 1457
Epoch 17/30
step 91/91 [==============================] - loss: 0.3214 - acc: 0.9157 - 38ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.3840 - acc: 0.8970 - 38ms/step        
Eval samples: 1457
Epoch 18/30
step 91/91 [==============================] - loss: 0.1565 - acc: 0.9190 - 38ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.3724 - acc: 0.8964 - 36ms/step        
Eval samples: 1457
Epoch 19/30
step 91/91 [==============================] - loss: 0.2747 - acc: 0.9188 - 38ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.4975 - acc: 0.8888 - 36ms/step        
Eval samples: 1457
Epoch 20/30
step 91/91 [==============================] - loss: 0.2160 - acc: 0.9190 - 38ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.2133 - acc: 0.8950 - 37ms/step        
Eval samples: 1457
Epoch 21/30
step 91/91 [==============================] - loss: 0.2422 - acc: 0.9246 - 38ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.1167 - acc: 0.8868 - 37ms/step        
Eval samples: 1457
Epoch 22/30
step 91/91 [==============================] - loss: 0.2355 - acc: 0.9260 - 38ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.1651 - acc: 0.8909 - 37ms/step        
Eval samples: 1457
Epoch 23/30
step 91/91 [==============================] - loss: 0.2197 - acc: 0.9279 - 39ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.3063 - acc: 0.8854 - 38ms/step        
Eval samples: 1457
Epoch 24/30
step 91/91 [==============================] - loss: 0.2055 - acc: 0.9174 - 39ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.1573 - acc: 0.8888 - 36ms/step        
Eval samples: 1457
Epoch 25/30
step 91/91 [==============================] - loss: 0.2945 - acc: 0.9263 - 39ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.5323 - acc: 0.8964 - 40ms/step        
Eval samples: 1457
Epoch 26/30
step 91/91 [==============================] - loss: 0.2485 - acc: 0.9301 - 41ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.3039 - acc: 0.8868 - 37ms/step        
Eval samples: 1457
Epoch 27/30
step 91/91 [==============================] - loss: 0.1851 - acc: 0.9360 - 40ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.2341 - acc: 0.8868 - 36ms/step        
Eval samples: 1457
Epoch 28/30
step 91/91 [==============================] - loss: 0.1346 - acc: 0.9322 - 39ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.1907 - acc: 0.8826 - 36ms/step        
Eval samples: 1457
Epoch 29/30
step 91/91 [==============================] - loss: 0.1109 - acc: 0.9334 - 38ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.4592 - acc: 0.8895 - 36ms/step        
Eval samples: 1457
Epoch 30/30
step 91/91 [==============================] - loss: 0.3149 - acc: 0.9353 - 39ms/step        
Eval begin...
The loss value printed in the log is the current batch, and the metric is the average value of previous step.
step 23/23 [==============================] - loss: 0.4777 - acc: 0.8854 - 36ms/step        
Eval samples: 1457
# 模型保存

model.save('Hapi_MyCNN', False)
import os, time
import matplotlib.pyplot as plt
import paddle
from PIL import Image
import numpy as np

def load_image(img_path):
    '''
    预测图片预处理
    '''
    img = Image.open(img_path)
    plt.imshow(img)          #根据数组绘制图像
    plt.show()               #显示图像
    
    #resize
    img = img.resize((28, 28), Image.BILINEAR) #Image.BILINEAR双线性插值
    img = np.array(img).astype('float32')

    return img

def infer_img(path, model_file_path, use_gpu):
    '''
    模型预测
    '''
    paddle.set_device('gpu:0') if use_gpu else paddle.set_device('cpu')
    model = paddle.jit.load(model_file_path)
    model.eval() #训练模式

    #对预测图片进行预处理
    infer_imgs = []
    infer_imgs.append(load_image(path))
    infer_imgs = np.array(infer_imgs)
    label_list = ['0:Negative', '1:Positive']

    for i in range(len(infer_imgs)):
        data = infer_imgs[i]
        dy_x_data = np.array(data).astype('float32')
        dy_x_data = dy_x_data[np.newaxis, np.newaxis, : ,:]
        img = paddle.to_tensor(dy_x_data)
        out = model(img)

        print(out[0])
        print(paddle.nn.functional.softmax(out)[0]) # 若模型中已经包含softmax则不用此行代码。

        lab = np.argmax(out.numpy())  #argmax():返回最大数的索引
        print("样本: {},被预测为:{}".format(path, label_list[lab]))

    print("*********************************************")
############################################################################################

image_path = []

for root, dirs, files in os.walk('data/data71015/face_data/Negative/'):
    # 遍历work/文件夹内图片
    for f in files:
        image_path.append(os.path.join(root, f))

for i in range(len(image_path)):
    infer_img(path=image_path[i], use_gpu=True, model_file_path="Hapi_MyCNN")
    # time.sleep(0.5) #防止输出错乱
_data/Negative/'):
    # 遍历work/文件夹内图片
    for f in files:
        image_path.append(os.path.join(root, f))

for i in range(len(image_path)):
    infer_img(path=image_path[i], use_gpu=True, model_file_path="Hapi_MyCNN")
    # time.sleep(0.5) #防止输出错乱
    break

你可能感兴趣的:(飞桨炼丹童子的成长之路,深度学习,计算机视觉,paddlepaddle)