據北京聽力協會預估數據,我國聽障人群數量已過千萬。而在全球范圍內有4.66億人患有殘疾性聽力損失,約占全世界人口的5%。聾啞人士很特殊,他們需要使用手語進行交流,其他與常人無異,我國存在特殊教育水平在各城市中發(fā)展力度具有較大差異,國家通用手語推廣程度淺,但不懂手語,與聽力障礙者交流會非常困難。 在本篇內容中,ShowMeAI 借助深度學習與神經網絡技術,針對這個問題從 0 構建 1 個應用程序,檢測手語并將其翻譯給其他人進而打破手語隔閡。 搭建和部署完成后,你可以通過攝像頭,輕松測試模型,如下圖所示,快來一起試試吧。這個動圖中的手勢代表的單詞,見文末哦! 手語介紹我們先來簡單了解一下手語,它由 3 個主要部分組成:
在本文中,我們先解決第①個部分的問題。我們準備使用的解決方案是基于視覺數據的神經網絡 深度學習與計算機視覺人工智能和計算機視覺的最典型的模型是卷積神經網絡(CNN),它在典型的計算機視覺應用中(如圖像識別、目標檢測等)應用廣泛。我們在本次應用的核心技術也將采用 CNN。 CNN 網絡有著如上圖所示的網絡結構,典型的結構包括卷積層、池化層、激活層、全連接層等,對于輸入圖像,可以有效抽取圖像內容表征,并進行分類或其他處理。卷積層等特殊結構,可以在控制參數量的前提下,保證良好的圖像特征提取能力。 小試牛刀,打通流程我們來構建一個 CNN 識別的流程,會分成以下基礎步驟:
① 導入相關庫我們在這里主要使用 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),部分示例圖片如下 下面我們加載訓練集與測試集并切分特征與標簽:
# 輸出標簽信息labels = train['label'].value_counts().sort_index(ascending=True)labels
# 數據預處理與可視化# 存儲標簽數據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)
③ 卷積神經網絡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', #優(yōu)化器 loss='sparse_categorical_crossentropy', #損失函數 metrics=['accuracy']) #評估準則 return model
我們這里在全量數據集上迭代20個輪次,結果如下: 我們可以看到,這里的數據并不特別復雜,在自己從頭搭建的 CNN 模型上,經過訓練可以達到訓練集 100% 驗證集 92% 的準確率。 我們再對訓練過程中的「準確率」及「損失函數」變化值進行繪制,以了解模型狀態(tài)。 # 獲取準確率與損失函數情況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() 問題與優(yōu)化① 深度網絡與梯度消失一般來說,隨著 CNN 網絡層數變深,模型的學習能力會變強,也能學到更多的信息。但訓練深度CNN存在梯度消失的問題。 非常深的神經網絡的梯度會很快變?yōu)榱悖ǚ聪騻鞑サ奶荻冗B乘帶來的問題),這最終會使整個梯度下降變慢。有一些特殊結構的神經網絡,可以大程度緩解這個問題,比如最著名的 ResNet,當然,大家可以借助 ResNet 預訓練模型快速遷移學習應用在我們當前的手語識別問題上,為了讓大家對ResNet 細節(jié)更清晰,我們在這里手動搭建 ResNet-50(即50層的ResNet網絡)來訓練和做效果對比。 ② ResNet 模型簡介ResNet 是 Residual Networks 的簡稱,是迄今為止我們看到的最流行和最成功的深度學習模型之一。ResNets 由殘差塊組成,殘差塊的核心組件是『跳躍連接/skip-connection』。跳躍連接,也稱為快捷連接,讓神經網絡跳過某些層并將一層的輸出饋送到神經網絡中另一層的輸入。它能幫助模型避免乘以中間跳過的那些層的權重,從而有助于解決梯度消失的問題。 然而,使用 ResNet 和跳躍連接,由于中間有卷積層和池化層,一層輸出的維度可能與另一層的輸出維度不同。為了解決這個問題,可以使用兩種方法:
但是,對于第二種方法,我們需要在輸出中添加一個額外的參數,而第一種方法不需要。 ③ ResNet為何有效ResNet的效果核心有2點:
④ 構建ResNet-50下面我們參考 keras 官方 ResNet 構建方式,構建一個 ResNet-50,如下所示,我們先構建基本模塊,再組裝成最終的網絡。
# 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
⑤ 訓練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) 得到如下結果 優(yōu)化效果對比我們對ResNet-50也繪制訓練過程中準確率和損失函數的變化,如下
對比圖如下: 我們觀察到,從簡單的 CNN 模型換到 ResNet 模型時,測試集的準確率從92% 到 97% 。也說明了,ResNet 的結構確實能夠帶來效果上的提升。 部署與實時測試在這里我們做一個簡單的測試,使用 OpenCV 的視頻錄制功能,通過 python 收集我們的攝像頭的鏡頭采集的圖像并進行實時預測。
具體的過程是,我們解析捕獲的每一幀圖像,將其處理為灰度圖(類似于我們模型的訓練集),在圖像中心抓取一個 400*400 像素的正方形區(qū)域(參見 x0,x1,y0,y1),將正方形調整為我們最初的 28x28 大小并使用我們的模型進行測試(之前保存到 .h5 文件)。 # 導入工具庫import kerasimport numpy as npfrom PIL import Imageimport stringimport pandas as pdimport tensorflow as tf
為了更輕松地對預估結果查看,我們把將預測的字母顯示在實時畫面上(請參閱下面的 gif 以測試單詞 hello)。 |
|