百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

搭建和部署手语AI识别系统

toyiye 2024-06-06 22:12 9 浏览 0 评论

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

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

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

手语介绍

我们先来简单了解一下手语,它由 3 个主要部分组成:

  • 手指拼写:这是一种手动的交流方式,用双手和手指拼写单词。每个字母都用指定的手位置表示。
  • 单词级符号词汇:这是一个大型视频数据集,用于识别单词或字母的整个手势。
  • 非手部特征:包括任何面部表情、嘴巴、舌头或身体姿势。

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

深度学习与计算机视觉

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

CNN 网络有着如上图所示的网络结构,典型的结构包括卷积层、池化层、激活层、全连接层等,对于输入图像,可以有效抽取图像内容表征,并进行分类或其他处理。卷积层等特殊结构,可以在控制参数量的前提下,保证良好的图像特征提取能力。

小试牛刀,打通流程

我们来构建一个 CNN 识别的流程,会分成以下基础步骤:

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

① 导入相关库

我们在这里主要使用 TensorFlow 构建网络与训练,会使用 Numpy 做数据计算与处理,以及使用 Matplotlib 进行简单可视化。

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

# 导入工具库
import stringimport pandas as pd
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as pltfrom tensorflow 
import kerasfrom functools 
import partialfrom tensorflow.keras.preprocessing.image 
import ImageDataGenerator, array_to_img

② 读取数据集

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

下面我们加载训练集与测试集并切分特征与标签:

# 读取数据
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存在梯度消失的问题。

非常深的神经网络的梯度会很快变为零(反向传播的梯度连乘带来的问题),这最终会使整个梯度下降变慢。有一些特殊结构的神经网络,可以大程度缓解这个问题,比如最著名的 ResNet,当然,大家可以借助 ResNet 预训练模型快速迁移学习应用在我们当前的手语识别问题上,为了让大家对ResNet 细节更清晰,我们在这里手动搭建 ResNet-50(即50层的ResNet网络)来训练和做效果对比。

② 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
# 导入OpenCV
import 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)。

相关推荐

说冲A就冲A,这个宝藏男孩冯俊杰我pick了

爱奇艺新上架了一部网剧叫《最后一个女神》。有个惊人的发现,剧里男三居然是《青春有你》的训练生冯俊杰。剧组穷,戏服没几件,冯俊杰几乎靠一件背背佳撑起了整部剧。冯俊杰快速了解一下。四川人,来自觉醒东方,人...

唐山打人嫌犯陈继志去医院就医的背后,隐藏着三个精心设计的步骤

种种迹象表明,陈继志这帮人对处理打人之后的善后工作是轻车驾熟的,他们想实施的计划应该是这样的:首先第一步与伤者进同一家医院做伤情鉴定,鉴定级别最好要比对方严重,于是两位女伤者被鉴定为轻伤,他们就要求医...

熬夜会造成神经衰弱,别再熬夜了(熬夜会加重神经衰弱吗)

长时间熬夜会出现神经衰弱,皮肤受损,超重肥胖,记忆力下降等现象……熬夜了能补回来吗?每天少睡一两个小时算熬夜吗?必须上夜班怎么办?如何减少熬夜伤害?戳图转给爱熬夜的TA!via央视新闻来源:河北省文...

落叶知秋的图片爬取(落叶知秋的图片有哪些?)

importrequestsfrombs4importBeautifulSoupimporttimeimportjsonpathimportjsonfromurllib.parsei...

小心有毒!长沙海关查获藏匿在“巧克力威化涂层”中的大麻

来源:海关发布近日,长沙黄花机场海关对一票申报为“巧克力威化涂层”的进境快件进行机检查验时,在包裹内查获封装于各独立威化饼干包装袋中的大麻230克。另从其他申报为“巧克力、儿童早餐谷物”的快件中查获藏...

钧正平:编造传播这种谣言,荒谬(钧正公司)

来源:钧正平工作室官方微博【钧评编造传播这种谣言,荒谬!】目前,乌克兰安全形势还在迅速变化之中,各方面安全风险上升。相关事件网上热度极高,倍受瞩目。然而,有一些人却借机大肆制造散播一些低级谣言,比如...

幸运角色过去了,谈一谈DNF起源的元素

总的来说伤害比上个版本强太多了,打卢克每日和团本明显能感觉的到。目前打团B套+圣耀稍微打造下应该都能随便二拖了。组队基本上都是秒秒秒(以前得强力辅助,现在随便带个毒奶都行)。单刷除了王座和顶能源阿斯兰...

DNF元素超大凉打桩测试(把括号的伤害加起来好像比较正常)

最近修练场的二觉老是很奇怪,发现以前都是习惯性先减抗然后丢二觉,结果伤害。。。直接丢二觉就正常了下面是其他技能伤害,没达到BUG线,估计问题不大。装备打造方面:全身红字加起来353(41*5+74*2...

ANSYS接触和出图技巧(ansys rough接触)

1.ANSYS后处理时如何按灰度输出云图?1)你可以到utilitymenu-plotctrls-style-colors-windowcolors试试2)直接utilitymenu-plotctr...

ANSYS有限元使用经验总结-后处理(4)

28.求塑性极限荷载时,结构的变形应该较大,建议把大变形打开。...

CFopen21.1、CFopen21.2都来了(cfile open)

[呲牙][赞][加油]

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

取消回复欢迎 发表评论:

请填写验证码