CNNベースの顔認識(下)
Keras Deep Learning Application 1-Convolutional Neural Network(CNN)に基づく顔認識(パート1) 。前のブログでは、CNNの理論的基礎について詳しく説明しました。このブログでは、Kerasを使用して顔認識を実現するための特定のコードについて具体的に説明します。 。
コードのダウンロード
Githubソースコードのダウンロードアドレス:https:
//github.com/Kyrie-leon/CNN-FaceRec-keras
3、顔認識データセット
MegaFace顔データベースは、ワシントン大学によってリリースおよび維持されている100万レベルの顔データベースであり、現在、世界で最も権威のある顔認識パフォーマンス指標の1つです。顔データベースには、大量のデータと画像のランダム性という特徴があります。一部の顔データセットは年齢スパンが大きいため、認識が困難になります。このフェイスデータベースを使用するには、最初にアプリケーションを送信する必要があります。次に、ダウンロードアカウントとパスワードを取得した後、データセットをダウンロードできます。このペーパーでは、MegaFaceフェイスデータベースのいくつかのフェイスを実験データセットとして選択します。
MegaFace顔データセット公式ウェブサイト
リンク:https://pan.baidu.com/s/1aCjxQnKrnGY6hG-Mifdm2g抽出コード:w5wu
顔トレーニングセットの画像
リンクをダウンロード:https://pan.baidu.com/s/1rQEUk0toOSDYoG-frK4wBg抽出コード:73uyダウンロードフェイステストセット画像
3.1データセットの分割
この実験では、MegaFace顔データベースの40人の6127枚の画像を実験データセットとして選択します。これには、トレーニングセットの4742枚の画像、検証セットの1185枚の画像、テストセットの200枚の画像が含まれます。
Name_label = [] #姓名标签
path = './data/face/' #数据集文件路径
dir = os.listdir(path) #列出所有人
label = 0 #设置计数器
#数据写入
with open('./data/train.txt','w') as f:
for name in dir:
Name_label.append(name)
print(Name_label[label])
after_generate = os.listdir(path +'\\'+ name)
for image in after_generate:
if image.endswith(".png"):
f.write(image + ";" + str(label)+ "\n")
label += 1
上記のコードにより、実験データセットがtrain.txtファイルに書き込まれ、データの反復読み取りが実現されます。これにより、メモリ使用量が削減され、トレーニング速度が向上します。
# 打开数据集的txt
with open(r".\data\train.txt","r") as f:
lines = f.readlines()
#打乱数据集
np.random.seed(10101)
np.random.shuffle(lines)
np.random.seed(None)
# 80%用于训练,20%用于测试。
num_val = int(len(lines)*0.2) #
num_train = len(lines) - num_val #4715
データセットは上記のコードで分割され、同じ種類のデータが集中して実験の精度に影響を与えないようにランダムシードが設定されます。
3.2データの強化
元のトレーニングセットの顔の画像をぼかしたり、回転させたり、ノイズを追加したりすることで、さまざまな複雑な「環境」が作成され、元のデータに基づいてデータ量が拡張されるため、認識精度が向上します。劣悪な画像環境をシミュレートします。干渉だけでなく、さまざまな過酷な条件下での顔認識に対する畳み込み神経ネットワークの適応性が高いほど、コードは次のようになります。
def rand(a=0, b=1):
return np.random.rand()*(b-a) + a
def get_random_data(image, input_shape, random=True, jitter=.1, hue=.1, sat=1.2, val=1.2, proc_img=True):
h, w = input_shape
new_ar = w/h * rand(1-jitter,1+jitter)/rand(1-jitter,1+jitter)
scale = rand(.7, 1.3)
if new_ar < 1:
nh = int(scale*h)
nw = int(nh*new_ar)
else:
nw = int(scale*w)
nh = int(nw/new_ar)
image = image.resize((nw,nh), Image.BICUBIC)
# place image
dx = int(rand(0, w-nw))
dy = int(rand(0, h-nh))
new_image = Image.new('RGB', (w,h), (0,0,0))
new_image.paste(image, (dx, dy))
image = new_image
# flip image or not
flip = rand()<.5
if flip:
image = image.transpose(Image.FLIP_LEFT_RIGHT)
# distort image
hue = rand(-hue, hue)
sat = rand(1, sat) if rand()<.5 else 1/rand(1, sat)
val = rand(1, val) if rand()<.5 else 1/rand(1, val)
x = rgb_to_hsv(np.array(image)/255.)
x[..., 0] += hue
x[..., 0][x[..., 0]>1] -= 1
x[..., 0][x[..., 0]<0] += 1
x[..., 1] *= sat
x[..., 2] *= val
x[x>1] = 1
x[x<0] = 0
image_data = hsv_to_rgb(x)*255 # numpy array, 0 to 1
return image_data
効果画像:
第4に、CNNネットワークモデルを構築します
4.1CNNモデルを構築する
この記事で構築した畳み込みニューラルネットワークモデルを図に示します。このモデルには、入力レイヤー1つ、畳み込みレイヤー3つ、最大プーリングレイヤー3つ、フラット化レイヤー1つ、完全接続レイヤー1つ、完全接続レイヤー1つで構成される合計10のレイヤーがあります。 Softmaxレイヤーで構成されています。
まず、入力レイヤーの入力データは200×200×3の画像マトリックス、最初のコンボリューションレイヤーのコンボリューションカーネルは3×3、デフォルトのスライドステップサイズは1ピクセルです。マトリックスがコンボリューションカーネルのコンボリューション操作を受けた後、198×198×64のマトリックスデータが形成され、次に、サイズが2×2でデフォルトのスライディングステップが2の最大プーリングレイヤーがプーリング処理に使用され、特徴はに縮小されます。 99×99×64マトリックスデータ。
次に、第1の最大プーリング層によって出力された99×99×64マトリックスデータが第2の畳み込み層の入力データとして使用され、それは1回の畳み込みのスライディングステップで第2の3×3サイズの畳み込みカーネルを通過する。層が畳み込まれた後、97×97×32のマトリックスデータが取得され、次にマトリックスデータが2×2サイズの最大プーリング層と2のスライディングステップサイズでプールされ、特徴が48×48に縮小されます。 ×32の特徴マトリックス、特徴マトリックスはドロップアウトレイヤーに渡されます。ドロップアウトレイヤーは、モデルのオーバーフィットを抑制するために使用され、設定された確率は25%です。
畳み込み層の最後の層と最大プーリング層では、畳み込みとプーリングのプロセスは前の2つの層と同様です.48×48×32の特徴マトリックスは、3×3サイズの畳み込みカーネルで畳み込まれ、 46×46×32のマトリックスデータは、最大のプーリング操作のために2×2サイズの最大プーリングレイヤーに渡され、フィーチャは最終的に23×23×3のフィーチャマトリックスに縮小され、マトリックスを通過する確率は25%です。パラメータを更新するためのドロップアウトレイヤー。
データが上記のコンボリューションプーリングプロセスを通過した後、出力データは2次元ベクトルであり、モデルはコンボリューション操作の結果を分類する必要があるため、2次元マトリックスを1次元マトリックスに縮小する必要があります。ここではFalttenを使用します。多次元入力データを1次元にするレイヤーは、畳み込みレイヤーから完全に接続されたレイヤーへの遷移を提供します。このステップでは、23×23×32のフィーチャマトリックスが「フラット化」されて、合計16,928個の固有値を含む1次元マトリックスのセットになり、これらの固有値が完全に接続されたレイヤーに渡されます。
このペーパーで設計された完全に接続されたレイヤーには128のニューロンが含まれ、16928の機能値は128のニューロンと完全に接続され、128の機能データはrelu関数のアクティブ化処理後に生成され、50%の確率でドロップアウトレイヤーに渡されます。 Softmaxレイヤーの入力データ。
最後に、この論文では、完全に接続されたレイヤーの128の特徴データが分類ラベルの数と完全に接続され、顔の分類と認識がSoftmaxレイヤーを介して実行されます。実装コードは次のとおりです。
def MmNet(input_shape, output_shape):
model = Sequential() # 建立模型
# 第一层
model.add(Conv2D(64, kernel_size=(3, 3), activation='relu', input_shape=input_shape))
model.add(MaxPooling2D(pool_size=(2, 2)))
# 第二层
model.add(Conv2D(32, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
# 第三层
model.add(Conv2D(32, (3, 3), activation='relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))
# 全连接层
model.add(Flatten())
model.add(Dense(128, activation='relu'))
model.add(Dropout(0.5))
# 全连接层
model.add(Dense(output_shape, activation='softmax'))
print("-----------模型摘要----------\n") # 查看模型摘要
model.summary()
return model
4.2モデルトレーニング
トレーニングの開始時に、トラバーサル方式を使用して各サブディレクトリの顔の画像を200×200ピクセルに正規化し、画像をマトリックスに変換してメモリに読み込みます。データセットはトレーニングセットとテストセットを作成し、顔認識用の分類ラベルをトレーニングセットとテストセットにそれぞれ追加します。
次に、構築されたニューラルネットワークモデルのトレーニングを開始します。この記事では、オプティマイザーとしてアダム降下法を選択し、学習率は0.01で、トレーニングにはGPU加速法を使用します。処理されたデータは、設定されたラベルに従ってニューラルネットワークに渡され、反復トレーニングが実行されます。各反復で、トレーニング結果が評価され、トレーニングされたネットワークモデルがローカルに保存されて、反復が中断されなくなります。実装コードは次のとおりです。
#3. 训练模型
def train(model, batch_size):
model = model #读取模型
#定义保存方式,每三代保存一次
checkpoint_period1 = ModelCheckpoint(
log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
monitor='acc',
save_weights_only=False,
period=3
)
#学习率下降方式,acc三次不下降就下降学习率继续训练
reduce_lr = ReduceLROnPlateau(
monitor='val_acc',
patience=3,
verbose=1
)
#val_loss一直不下降意味模型基本训练完毕,停止训练
early_stopping = EarlyStopping(
monitor='val_loss',
min_delta=0,
patience=10,
verbose=1
)
#交叉熵
model.compile(loss = 'categorical_crossentropy',
optimizer =Adam(lr=1e-4),
metrics=['accuracy'])
#Tebsorboard可视化
tb_Tensorboard = TensorBoard(log_dir="./model", histogram_freq=1, write_grads=True)
#开始训练
history = model.fit_generator(generate_arrays_from_file(lines[:num_train], batch_size, True),
steps_per_epoch=max(1, num_train//batch_size),
validation_data=generate_arrays_from_file(lines[num_train:], batch_size, False),
validation_steps=max(1, num_val//batch_size),
verbose = 1,
epochs=10,
initial_epoch=0,
callbacks=[early_stopping, checkpoint_period1, reduce_lr, tb_Tensorboard])
return history, model
4.3認識される面をロードします
このセクションのコード実装プロセスは、トレーニング済みのニューラルネットワークモデルをロードし、認識が必要な顔の画像をロードし、認識し、認識結果を印刷して、ウィンドウに顔の写真を表示します。ウィンドウのタイトルは、画像のパス名です。特定のコードの実装
import os
import glob
import h5py
import keras
import numpy as np
from tkinter import *
from tkinter import ttk
from PIL import Image,ImageTk
from keras.models import load_model
from keras.preprocessing.image import load_img, img_to_array
from keras.applications.imagenet_utils import preprocess_input
from keras.models import Model
from Name import *
img_rows = 300 # 高
img_cols = 300 # 宽
def letterbox_image(image, size):
iw, ih = image.size
w, h = size
scale = min(w/iw, h/ih)
nw = int(iw*scale)
nh = int(ih*scale)
image = image.resize((nw,nh), Image.BICUBIC)
new_image = Image.new('RGB', size, (0,0,0))
new_image.paste(image, ((w-nw)//2, (h-nh)//2))
return new_image
def test_accuracy(lines, model):
t = 0
n = len(lines)
i = 0 #计数器
# 遍历测试集数据
for i in range(n):
name = lines[i].split(';')[0] #人脸图像名字xxx_xxx.png
label_name = (lines[i].split(';')[1]).strip('\n') #人脸数字标签0-39
label_name = int(label_name)
file_name = str(Name.get(label_name)) #对应人名str
# 从文件中读取图像
img = Image.open(r".\data\test" +"\\"+ name)
img = np.array(letterbox_image(img,[img_rows,img_cols]),dtype = np.float64)
img = preprocess_input(np.array(img).reshape(-1,img_cols,img_rows,3))
pre_name = model.predict_classes(img) # 返回预测的标签值
print(int(pre_name),label_name)
if int(pre_name) == label_name:
t+=1
print(t/n)
def main():
#获取模型权重h5文件
model = load_model('./logs/easy1.h5')
with open('./data/test.txt','r') as f:
lines = f.readlines()
for img_location in glob.glob('./data/test/*.png'): # 限制测试图像路径以及图像格式
img = load_img(img_location)
img = img_to_array(img)
#图像处理
img = preprocess_input(np.array(img).reshape(-1,img_cols,img_rows,3))
img_name = (img_location.strip("face\\")).rstrip(".png")
pre_name = model.predict_classes(img) # 返回预测的标签值
print(pre_name)
pre = model.predict(img)
for i in pre_name:
for j in pre:
name = Name.get(i)
#print(name)
# if name != "Audrey_Landers":
acc = np.max(j) * 100
print("\nPicture name is [%s]\npPredicted as [%s] with [%f%c]\n" %(img_name, name, acc, '%'))
MainFrame = Tk()
MainFrame.title(img_name)
MainFrame.geometry('300x300')
img = Image.open(img_location)
img = ImageTk.PhotoImage(img)
label_img = ttk.Label(MainFrame, image = img)
label_img.pack()
MainFrame.mainloop()
if __name__ == '__main__':
main()