作者:韩信子@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 stringimport pandas as pdimport numpy as npimport tensorflow as tfimport matplotlib.pyplot as pltfrom tensorflow import kerasfrom functools import partialfrom 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_ytrain_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),将正方形调整为咱们最后的 28x28 大小并应用咱们的模型进行测试(之前保留到 .h5 文件)。
# 导入工具库import kerasimport numpy as npfrom PIL import Imageimport stringimport pandas as pdimport tensorflow as tf
# 导入OpenCVimport cv2from matplotlib import pyplot# 设定维度dim = (28, 28) # 图像维度letters = list(string.ascii_lowercase) # 辨认的字母x0 = 1920 // 2 - 400 # 400px left of centerx1 = 1920 // 2 + 400 # 400px right of centery0 = 1080 // 2 - 400 # 400px right of centery1 = 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'): breakvideo.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