乐趣区

关于cnn:AI识万物从0搭建和部署手语识别系统-⛵

💡 作者:韩信子 @ShowMeAI
📘 深度学习实战系列:https://www.showmeai.tech/tutorials/42
📘 计算机视觉实战系列: https://www.showmeai.tech/tutorials/46
📘 本文地址:https://www.showmeai.tech/article-detail/292
📢 申明:版权所有,转载请分割平台与作者并注明出处
📢 珍藏 ShowMeAI 查看更多精彩内容

据北京听力协会预估数据,我国听障人群数量已过千万。而在寰球范畴内有 4.66 亿人患有残疾性听力损失,约占全世界人口的 5%。聋哑人士很非凡,他们须要应用手语进行交换,其余与常人无异,我国存在特殊教育程度在各城市中倒退力度具备较大差别,国家通用手语推广水平浅,但不懂手语,与听力障碍者交流会十分艰难。

在本篇内容中,ShowMeAI 借助深度学习与神经网络技术,针对这个问题从 0 构建 1 个应用程序,检测手语并将其翻译给其他人进而突破手语隔膜。

搭建和部署实现后,你能够通过摄像头,轻松测试模型,如下图所示,快来一起试试吧。这个动图中的手势代表的单词,见文末哦!

💡 手语介绍

咱们先来简略理解一下手语,它由 3 个次要局部组成:

  • 手指拼写:这是一种手动的交换形式,用双手和手指拼写单词。每个字母都用指定的手地位示意。
  • 单词级符号词汇:这是一个大型视频数据集,用于辨认单词或字母的整个手势。
  • 非手部特色:包含任何面部表情、嘴巴、舌头或身材姿态。

在本文中,咱们先解决第①个局部的问题。咱们筹备应用的解决方案是基于视觉数据的神经网络

💡 深度学习与计算机视觉

人工智能和计算机视觉的最典型的模型是卷积神经网络(CNN),它在典型的计算机视觉利用中(如图像识别、指标检测等)利用宽泛。咱们在本次利用的核心技术也将采纳 CNN。

CNN 网络有着如上图所示的网络结构,典型的构造包含卷积层、池化层、激活层、全连贯层等,对于输出图像,能够无效抽取图像内容表征,并进行分类或其余解决。卷积层等非凡构造,能够在控制参数量的前提下,保障良好的图像特征提取能力。

对于卷积神经网络的具体常识能够参考 ShowMeAI 下述教程:

  • ShowMeAI 的 深度学习教程 | 吴恩达专项课程 · 全套笔记解读 中的文章 卷积神经网络解读
  • ShowMeAI 的 深度学习与计算机视觉教程 中的文章 卷积神经网络详解

💡 小试牛刀,买通流程

咱们来构建一个 CNN 辨认的流程,会分成以下根底步骤:

  • 数据读取与切分
  • 数据可视化及预处理
  • CNN 网络构建与训练

① 导入相干库

咱们在这里次要应用 TensorFlow 构建网络与训练,会应用 Numpy 做数据计算与解决,以及应用 Matplotlib 进行简略可视化。

对于这些工具库,ShowMeAI 都制作了快捷即查即用的速查表手册,大家能够在下述地位取得:

  • Tensorflow 速查手册
  • Numpy 速查手册
  • Matplotlib 速查手册

咱们先把这些工具库导入。

# 导入工具库
import string
import pandas as pd
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow import keras
from functools import partial
from tensorflow.keras.preprocessing.image import ImageDataGenerator, array_to_img

② 读取数据集

本数据集为手语字母对应的数据集,图片 size 不大,所以也叫做 sign_mnist 数据集(类比手写数字数据集 mnist),局部示例图片如下

数据集大家能够在 🏆Kaggle 平台对应数据集页面 下载,也能够通过 ShowMeAI 的百度网盘地址下载。

🏆 实战数据集下载(百度网盘):公众号『ShowMeAI 钻研核心』回复『实战 』,或者点击 这里 获取本文 [[5] 从 0 搭建基于神经网络的手语识别系统](https://www.showmeai.tech/art…)『sign_mnist 数据集

ShowMeAI 官网 GitHub:https://github.com/ShowMeAI-Hub

上面咱们加载训练集与测试集并切分特色与标签:

# 读取数据
test = pd.read_csv("sign_mnist_test.csv")
train = pd.read_csv("sign_mnist_train.csv")

# 输入根本信息
print("训练集维度", train.shape)
print("测试集维度", train.shape)
# 输入标签信息
labels = train["label"].value_counts().sort_index(ascending=True)
labels
# 切分特色与标签
train_x = train.drop(labels = "label", axis = 1)
train_y = train["label"]
test_x = test.drop(labels = "label", axis = 1)
test_y = test["label"]
train_x.head()
# 数据预处理与可视化

# 存储标签数据
test_classes= test_y
train_clasees = train_y

# 特色转为 numpy 格局
train_x = train_x.to_numpy()
test_x = test_x.to_numpy()

# 把数据转为 3 维图像数据(图片数量 * 宽 * 高,这里如果是灰度图,色彩通道为 1,省略)train_x = train_x.reshape(-1,28,28)
test_x = test_x.reshape(-1,28,28)
# 在训练集中取样 30 张图片,做可视化查看
def plot_categories(training_images, training_labels):
    fig, axes = plt.subplots(3, 10, figsize=(16, 15))
    axes = axes.flatten()
    letters = list(string.ascii_lowercase)

    for k in range(30):
        img = training_images[k]
        img = np.expand_dims(img, axis=-1)
        img = array_to_img(img)
        ax = axes[k]
        ax.imshow(img, cmap="Greys_r")
        ax.set_title(f"{letters[int(training_labels[k])]}")
        ax.set_axis_off()

    plt.tight_layout()
    plt.show()

plot_categories(train_x, train_y)

③ 卷积神经网络 CNN 搭建

咱们应用 TensorFlow 的 high level API(即 keras)搭建一个繁难 CNN 神经网络,并拟合一下数据

def create_model():
    model = tf.keras.models.Sequential([
    # 卷积层
    tf.keras.layers.Conv2D(32, (3,3), activation='relu', input_shape=(28, 28, 1)),
    # 池化层
    tf.keras.layers.MaxPooling2D(2,2),
    # 卷积层
    tf.keras.layers.Conv2D(32, (3,3), activation='relu'),
    # 池化层
    tf.keras.layers.MaxPooling2D(2,2),
    # 展平
    tf.keras.layers.Flatten(),
    # 全连贯层
    tf.keras.layers.Dense(512, activation='relu'),
    # softmax 分类
    tf.keras.layers.Dense(26, activation='softmax')])

    model.compile(
    optimizer='adam',  #优化器
    loss='sparse_categorical_crossentropy',  #损失函数
    metrics=['accuracy']) #评估准则
  
    return model
# 初始化模型
model = create_model()
# 拟合数据
history = model.fit(train_x, train_y, epochs=20, validation_data=(test_x, test_y))

咱们这里在全量数据集上迭代 20 个轮次,后果如下:

咱们能够看到,这里的数据并不特地简单,在本人从头搭建的 CNN 模型上,经过训练能够达到训练集 100% 验证集 92% 的准确率。

咱们再对训练过程中的「准确率」及「损失函数」变动值进行绘制,以理解模型状态。

# 获取准确率与损失函数状况
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']

# matplotlib 绘制训练过程中指标的变动情况
epochs = range(len(acc))

plt.plot(epochs, acc, 'r', label='Training accuracy')
plt.plot(epochs, val_acc, 'b', label='Validation accuracy')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()

plt.plot(epochs, loss, 'r', label='Training Loss')
plt.plot(epochs, val_loss, 'b', label='Validation Loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()

💡 问题与优化

① 深度网络与梯度隐没

一般来说,随着 CNN 网络层数变深,模型的学习能力会变强,也能学到更多的信息。但训练深度 CNN 存在梯度隐没的问题。

梯度隐没和梯度爆炸局部内容也能够参考 ShowMeAI 的对吴恩达老师课程的总结文章 📘 深度学习教程 | 深度学习的实用层面

十分深的神经网络的梯度会很快变为零(反向流传的梯度连乘带来的问题),这最终会使整个梯度降落变慢。有一些非凡构造的神经网络,能够大程度缓解这个问题,比方最驰名的 ResNet,当然,大家能够借助 ResNet 预训练模型疾速迁徙学习利用在咱们以后的手语辨认问题上,为了让大家对 ResNet 细节更清晰,咱们在这里手动搭建 ResNet-50(即 50 层的 ResNet 网络)来训练和做成果比照。

ResNet 的具体解说也能够参考 ShowMeAI 的 📘 深度学习教程 | 吴恩达专项课程 · 全套笔记解读 中的文章 📘 深度学习教程 | 经典 CNN 网络实例详解

② ResNet 模型简介

ResNet 是 Residual Networks 的简称,是迄今为止咱们看到的最风行和最胜利的深度学习模型之一。ResNets 由残差块组成,残差块的外围组件是『跳跃连贯 /skip-connection』。跳跃连贯,也称为快捷连贯,让神经网络跳过某些层并将一层的输入馈送到神经网络中另一层的输出。它能帮忙模型防止乘以两头跳过的那些层的权重,从而有助于解决梯度隐没的问题。

然而,应用 ResNet 和跳跃连贯,因为两头有卷积层和池化层,一层输入的维度可能与另一层的输入维度不同。为了解决这个问题,能够应用两种办法:

  • 快捷连贯填充多个零实体以减少其维度
  • 增加 1X1 卷积层来匹配维度。

然而,对于第二种办法,咱们须要在输入中增加一个额定的参数,而第一种办法不须要。

③ ResNet 为何无效

ResNet 的成果外围有 2 点:

  • ① 它应用咱们下面提到的跳跃连贯,它跳过层来解决梯度隐没的问题。
  • ② 它通过让模型学习恒等函数来确保最高层的性能至多与最低层一样好。

④ 构建 ResNet-50

上面咱们参考 keras 官网 ResNet 构建形式,构建一个 ResNet-50,如下所示,咱们先构建根本模块,再组装成最终的网络。

# Defining the identity block of the Resnet-50 Model. 
def identity_block(X, f, filters, training=True):
    # filter of the three convs 
    f1,f2,f3 = filters
    X_shortcut = X 
    
    # First Component 
    X = tf.keras.layers.Conv2D(filters = f1, kernel_size = 1, strides = (1,1), padding = 'valid')(X)
    X = tf.keras.layers.BatchNormalization(axis = 3)(X, training = training) # Default axis
    X = tf.keras.layers.Activation('relu')(X)
   
    # Second Component 
    X = tf.keras.layers.Conv2D(filters = f2, kernel_size = f, strides = (1,1), padding = 'same')(X)
    X = tf.keras.layers.BatchNormalization(axis = 3)(X, training = training) # Default axis
    X = tf.keras.layers.Activation('relu')(X)
   
    # Third Component 
    X = tf.keras.layers.Conv2D(filters = f3, kernel_size = 1, strides = (1,1), padding = 'valid')(X)
    X = tf.keras.layers.BatchNormalization(axis = 3)(X, training = training) # Default axis
    
    # Adding the two tensors 
    X = tf.keras.layers.Add()([X_shortcut,X])
    X = tf.keras.layers.Activation('relu')(X)
    
    # Returning the last output
    return X
# Defining the Convolution Block of the Resnet-50 Model. 
def convolutional_block(X, f, filters, s=2,training=True):
    # filter of the three convs 
    f1,f2,f3 = filters
    X_shortcut = X 
    
    # First Component 
    X = tf.keras.layers.Conv2D(filters = f1, kernel_size = 1, strides = (1,1), padding = 'valid')(X)
    X = tf.keras.layers.BatchNormalization(axis = 3)(X, training = training) # Default axis
    X = tf.keras.layers.Activation('relu')(X)
    
    # Second Component 
    X = tf.keras.layers.Conv2D(filters = f2, kernel_size = f, strides = (s,s), padding = 'same')(X)
    X = tf.keras.layers.BatchNormalization(axis = 3)(X, training = training) # Default axis
    X = tf.keras.layers.Activation('relu')(X)
    
    # Third Component 
    X = tf.keras.layers.Conv2D(filters = f3, kernel_size = 1, strides = (1,1), padding = 'valid')(X)
    X = tf.keras.layers.BatchNormalization(axis = 3)(X, training = training) # Default axis
    
    # Converting the Input Volume to the match the last output for addition. 
    X_shortcut =tf.keras.layers.Conv2D(filters = f3, kernel_size = 1, strides = (s,s), padding = 'valid')(X_shortcut)
    X_shortcut = tf.keras.layers.BatchNormalization(axis = 3)(X_shortcut, training = training)
    X = tf.keras.layers.Add()([X_shortcut,X])
    X = tf.keras.layers.Activation('relu')(X)
    
    # Adding the last two tensors
    X = tf.keras.layers.Add()([X, X_shortcut])
    X = tf.keras.layers.Activation('relu')(X)
    
    # Returning the output tensor
    return X
# Defining a modified Resnet-50 Model using the Identity and Convolution Blocks. 
def ResNet50(input_shape = (28, 28, 1), classes = 26):
    
    # Defining the input as a tensor with shape input_shape
    X_input = tf.keras.Input(input_shape)
    
    # Zero-Padding
    X = tf.keras.layers.ZeroPadding2D((3, 3))(X_input)
    
    # Stage 1
    X = tf.keras.layers.Conv2D(64, (5, 5), strides = (1, 1))(X)
    X = tf.keras.layers.BatchNormalization(axis = 3)(X)
    X = tf.keras.layers.Activation('relu')(X)
    X = tf.keras.layers.MaxPooling2D((3, 3), strides=(2, 2))(X)

    # Stage 2
    X = convolutional_block(X, f = 3, filters = [64, 64, 256], s = 1)
    X = identity_block(X, 3, [64, 64, 256])
    X = identity_block(X, 3, [64, 64, 256])
    
    # Add an Average Pool Layer
    X = tf.keras.layers.AveragePooling2D((2,2))(X)

    # Output Layer
    X = tf.keras.layers.Flatten()(X)
    X = tf.keras.layers.Dense(classes, activation='softmax')(X)
    
    # Create Model
    model = tf.keras.Model(inputs = X_input, outputs = X)

    return model

⑤ 训练 ResNet-50

上面咱们在数据集上,应用 ResNet-50 网络进行训练

# 初始化模型
model = ResNet50()

# 编译
model.compile(optimizer="adam",metrics=["accuracy"],loss = "sparse_categorical_crossentropy")

# 训练
history = model.fit(train_x, train_y, validation_data = (test_x, test_y), epochs =10)

失去如下后果

💡 优化成果比照

咱们对 ResNet-50 也绘制训练过程中准确率和损失函数的变动,如下

# 获取准确率与损失函数状况
acc = history.history['accuracy']
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']

# matplotlib 绘制训练过程中指标的变动情况
epochs = range(len(acc))

plt.plot(epochs, acc, 'r', label='Training accuracy')
plt.plot(epochs, val_acc, 'b', label='Validation accuracy')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()

plt.plot(epochs, loss, 'r', label='Training Loss')
plt.plot(epochs, val_loss, 'b', label='Validation Loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()

比照图如下:

咱们察看到,从简略的 CNN 模型换到 ResNet 模型时,测试集的准确率从 92% 到 97%。也阐明了,ResNet 的构造的确可能带来成果上的晋升。

💡 部署与实时测试

在这里咱们做一个简略的测试,应用 OpenCV 的视频录制性能,通过 python 收集咱们的摄像头的镜头采集的图像并进行实时预测。

ShowMeAI 给 OpenCV 工具库制作了快捷即查即用的 OpenCV 速查表手册,大家能够点击查看和下载。

具体的过程是,咱们解析捕捉的每一帧图像,将其解决为灰度图(相似于咱们模型的训练集),在图像核心抓取一个 400*400 像素的正方形区域(参见 x0,x1,y0,y1),将正方形调整为咱们最后的 28×28 大小并应用咱们的模型进行测试(之前保留到 .h5 文件)。

# 导入工具库
import keras
import numpy as np
from PIL import Image
import string
import pandas as pd
import tensorflow as tf
# 导入 OpenCV
import cv2
from matplotlib import pyplot

# 设定维度
dim = (28, 28) # 图像维度
letters = list(string.ascii_lowercase) # 辨认的字母

x0 = 1920 // 2 - 400 # 400px left of center
x1 = 1920 // 2 + 400 # 400px right of center
y0 = 1080 // 2 - 400 # 400px right of center
y1 = 1080 // 2 + 400 # 400px right of center

# 初始化视频捕捉
video=cv2.VideoCapture(0)

cv2.namedWindow('Webcam') # 构建 1 个窗口
cv2.moveWindow('Webcam',40,30) # 搁置窗口

while video.isOpened(): # 只有没有关掉实时摄像头
    ret,capture = video.read() # 抓取每个视频帧
    cropped = capture[y0:y1, x0:x1] # 截取
    img = cv2.cvtColor(cropped, cv2.COLOR_BGR2GRAY) # 转成灰度图
    img = cv2.GaussianBlur(img, (5, 5), 0) # 图像平滑
    img = cv2.resize(img, dim) # 图像大小缩放
    pyplot.imshow(img, cmap='gray') # 可视化展现图片
    pyplot.show() # 展现
    img = np.reshape(img, (1,img.shape[0],img.shape[1],1))
    img = tf.cast(img, tf.float32)
    pred=model.predict(img)

    # 可视化实时成果
    cv2.rectangle(capture, (x0,y0),(x1,y1),(255,0,0),2) # 为图片增加矩形框
    cv2.putText(capture,'{} res50'.format(letters[np.argmax(pred[0])]),(x0+25,y0+50),cv2.FONT_HERSHEY_SIMPLEX,0.9,(0,255,0),1) # 预测字母
    cv2.imshow('Webcam', capture) # 展现视频
    
    # 后果输入
    print(pred)
    print(letters[np.argmax(pred[0])])
    
    # 退出视频输出
    key = cv2.waitKey(1)
    if key == ord('q'):
        break
video.release()
cv2.destroyAllWindows()

为了更轻松地对预估后果查看,咱们把将预测的字母显示在实时画面上(请参阅上面的 gif 以测试单词 hello)。

💡 参考资料

  • 🏆 实战数据集下载(百度网盘):公众号『ShowMeAI 钻研核心』回复『实战 』,或者点击 这里 获取本文 [[5] 从 0 搭建基于神经网络的手语识别系统](https://www.showmeai.tech/art…)『sign_mnist 数据集
  • ShowMeAI 官网 GitHub:https://github.com/ShowMeAI-Hub
  • 📘 深度学习教程 | 吴恩达专项课程 · 全套笔记解读: https://www.showmeai.tech/tutorials/35
  • 📘 卷积神经网络解读: https://www.showmeai.tech/article-detail/221
  • 📘 深度学习的实用层面: https://www.showmeai.tech/article-detail/216
  • 📘 经典 CNN 网络实例详解: https://www.showmeai.tech/article-detail/222
  • 📘 深度学习与计算机视觉教程: https://www.showmeai.tech/tutorials/37
  • 📘 卷积神经网络详解: https://www.showmeai.tech/article-detail/264
  • 📘 Tensorflow 速查手册: https://www.showmeai.tech/article-detail/109
  • 📘 OpenCV 速查表手册: https://www.showmeai.tech/article-detail/112
  • 📘 Numpy 速查手册: https://www.showmeai.tech/article-detail/100
  • 📘 Matplotlib 速查手册: https://www.showmeai.tech/article-detail/103
  • 📘 https://arxiv.org/ftp/arxiv/papers/1905/1905.05487.pdf
  • 📘 https://www.analyticsvidhya.com/blog/2021/06/sign-language-recognition-for-computer-vision-enthusiasts
退出移动版