任务:本次识别是一个图像二分类任务,利用卷积神经网络实现图像中表情的分类
实践平台:百度AI实训平台-AI Studio、python3.7+飞桨2.0.0
卷积神经网络(Convolution Neural Network,简称CNN),CNN 其实可以看作 DNN 的一种特殊形式。它跟传统 DNN 标志性的区别在于两点,Convolution Kernel 以及 Pooling。
网上公开的人脸表情图像数据集:
# 解压数据集
!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