[python] 機器學習 卷積神經網絡 用遷移學習實現人臉識別


項目簡介:

目標:識別全班61個人的人臉。

實現途徑:卷積神經網絡

  1. 用全班采集的照片訓練直接訓練自己的模型(圖片格式132*197,每人10張,8張加入訓練集,1張validation,1張test)
  2. 調用keras.application中的base_model(xception、inception、resnet50、VGG16、VGG19)做特征提取,更換我們自己的全鏈接層。
  3. 把basemodel的頂層的卷積層和池化層放開+全鏈接層

方法:用了第三種【不要放開太多層,否則提前用大量圖片訓練的模型就失去了效果,過擬合】,放開了block5。

人臉識別+圖像分析(打印每次訓練的損失函數和准確度)+打印圖片和測試是否正確+用opencv框出人臉

下面是帶着詳細注釋的代碼【其實我也沒有學懂——調包俠,調用opencv的代碼不一定正確】

# coding=utf-8
from keras import Sequential
from keras import backend as K
import os, shutil
import matplotlib.pyplot as plt
from keras import layers
from keras import models
from keras import optimizers
from keras.preprocessing.image import ImageDataGenerator
from keras.callbacks import ModelCheckpoint
from keras.applications import VGG16
import numpy as np
from PIL import Image, ImageDraw, ImageFont
#import cv2
K.tensorflow_backend._get_available_gpus()

base_dir = 'D:/program/project0701/tjuData/'
train_dir = os.path.join(base_dir, 'train')#os.path.join()函數:連接兩個或更多的路徑名組件
validation_dir = os.path.join(base_dir, 'validation')
test_dir = os.path.join(base_dir, 'test')

labels = []### 空列表list
fd = open('D:/program/project0701/lab.txt', 'r')#組號 英文名/n
for x in fd.readlines():
    labels.append(x[:-1])### 除了最后一個取全部,去掉最后一個回車
#print(labels) 
#print(len(labels))
labels = np.array(labels)


conv_base = VGG16(weights='imagenet',
                  include_top=False,  #不含頂部分類
                  input_shape=(132, 197, 3))
conv_base.summary()
"""
weight::告訴程序將網絡的卷積層和max pooling層對應的參數傳遞過來,將它們初始化成對應的網絡層次。
include_top::表示是否也要把Flatten()后面的網絡層也下載過來
VGG16對應的這層網絡用來將圖片划分到1000個不同類別中;
由於我們只用來區分貓狗兩個類別,因此我們去掉它這一層。
input_shape告訴網絡,我們輸入圖片的大小是132*197像素
"""
conv_base.trainable = True
#凍結卷積基——因為Dense層是層層隨機初始化conv_base.trainable = False
set_trainable = False
for layer in conv_base.layers:
    if layer.name == 'block5_conv1':
        set_trainable = True  #把第5層放開了
    if set_trainable:
        layer.trainable = True
    else:
        layer.trainable = False

model = models.Sequential()
model.add(conv_base)
model.add(layers.Flatten())
model.add(layers.Dense(256, activation='relu'))
model.add(layers.Dense(61, activation='softmax'))#sigmoid,二分類


model.compile(loss='categorical_crossentropy',  #多分類,交叉熵損失函數  
              optimizer=optimizers.RMSprop(lr=1e-4),  #lr學習率可以改 優化函數 α
              metrics=['acc'])


train_datagen = ImageDataGenerator(
      rescale=1./255,
      rotation_range=40,
      width_shift_range=0.2,
      height_shift_range=0.2,
      shear_range=0.2,
      zoom_range=0.2,
      horizontal_flip=True,
      fill_mode='nearest')

validation_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(## 以據文件夾路徑為參數,自動生成經過數提升/歸一化后的數據和標簽
        train_dir,                                  # 訓練數據路徑,train 文件夾下包含每一類的子文件夾
        target_size=(132,197),                      # 圖片大小resize成132*197
        batch_size=61,                              #批處理參數,它的極限值為訓練集樣本總數,當數據量比較少時,可以將batch_size值設置為全數據集
        class_mode='categorical')                   ## 使用多分類,返回1-D 的二值標簽
'''classes '''
validation_generator = validation_datagen.flow_from_directory(
        validation_dir,
        target_size=(132, 197),
        batch_size=61,  #
        class_mode='categorical')  


checkpointer = ModelCheckpoint(filepath='noface1-1.pretrained.finetuning.augmentation.model.weights.best.hdf5',
                               verbose=1,
                               save_best_only=True)
history = model.fit_generator(
      train_generator,
      steps_per_epoch=8,  #batch_size*steps_per_epoch=總訓練數據個數 防止訓練不均勻
      epochs=50,
      validation_data=validation_generator,#validation data並不用於更新權重,只是用是來檢測loss和accuracy等指標的
      validation_steps=1,  #
      callbacks=[checkpointer],
      verbose=2)
"""
Keras在使用.fit_generator訓練模型時的過程:
    Keras調用提供給.fit_generator的生成器函數(在本例中為aug.flow)
    生成器函數為.fit_generator函數生成一批大小為BS的數據
    .fit_generator函數接受批量數據,執行反向傳播,並更新模型中的權重
    重復該過程直到達到期望的epoch數量

    將訓練數據的總數除以批量大小的結果作為steps_per_epoch的值。
    一旦Keras到達這一步,它就會知道這是一個新的epoch。

verbose:日志顯示
verbose = 0 為不在標准輸出流輸出日志信息
verbose = 1 為輸出進度條記錄
verbose = 2 為每個epoch輸出一行記錄
"""

#從此開始的一段是繪圖分析
model.load_weights('noface1-1.pretrained.finetuning.augmentation.model.weights.best.hdf5')
#最近在訓練一個多位數字手寫體的模型,然后發現,我用ModelCheckpoint 保存了訓練過程中的結果最好一輪的參數。
# 后續用模型來預測新樣本的時候,就從直接本地加載訓練的模型

acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']

epochs = range(len(acc))
def smooth_curve(points, factor=0.8):
  smoothed_points = []
  for point in points:
    if smoothed_points:
      previous = smoothed_points[-1]
      smoothed_points.append(previous * factor + point * (1 - factor))
    else:
      smoothed_points.append(point)
  return smoothed_points

plt.plot(epochs,
         smooth_curve(acc), 'bo', label='Smoothed training acc')
plt.plot(epochs,
         smooth_curve(val_acc), 'b', label='Smoothed validation acc')
plt.title('Training and validation accuracy')
plt.legend()

plt.figure()

plt.plot(epochs,
         smooth_curve(loss), 'bo', label='Smoothed training loss')
plt.plot(epochs,
         smooth_curve(val_loss), 'b', label='Smoothed validation loss')
plt.title('Training and validation loss')
plt.legend()

plt.show()


test_datagen = ImageDataGenerator(rescale=1./255)
test_generator = test_datagen.flow_from_directory(
        test_dir,
        target_size=(132, 197),
        batch_size=61,
        shuffle = False,
        class_mode='categorical')
"""Whether to shuffle(調動,變換位置) the data (default: True)  
        If set to False, sorts the data in alphanumeric order. """
test_loss, test_acc = model.evaluate_generator(test_generator, steps=50)
print('test acc:', test_acc)
"""
def imgshow(img):
    image = Image.fromarray((img * 255).astype('uint8')).convert('RGB')
    return image

fig = plt.figure(figsize=(20, 12))
pred = model.predict_generator(test_generator, verbose=1, steps = 1)
test_datas, test_labels = test_generator.next()
lens = len(test_datas)
face_cascade = cv2.CascadeClassifier("D:/PySpace/face/haarcascade_frontalface_default.xml")

# print(print(test_labels))
for i in range(61):
    img = imgshow(test_datas[i])
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  #
    ax = fig.add_subplot(7, 10, i + 1, xticks=[], yticks=[])
    faces = face_cascade.detectMultiScale(gray, 1.2, 5)  # 1.3和5是特征的最小、最大檢測窗口,它改變檢測結果也會改變
    result = []
    for (x, y, width, height) in faces:
        result.append((x, y, x + width, y + height))
    draw_instance = ImageDraw.Draw(img)
    for (x1, y1, x2, y2) in faces:
        draw_instance.rectangle((x1, y1, x2, y2), outline=(255, 0, 0))
    ax.imshow(img)
    ax.set_title("{} \n{}".format(labels[np.argmax(pred[i])], labels[np.argmax(test_labels[i])]),
                 color=('green' if np.argmax(pred[i]) == np.argmax(test_labels[i]) else 'red'), size=20)
plt.show()

#在原圖像上畫矩形,框出所有人臉。
#調用Image模塊的draw方法,Image.open獲取圖像句柄,ImageDraw.Draw獲取該圖像的draw實例,然后調用該draw實例的rectangle方法畫矩形(矩形的坐標即
#detectFaces返回的坐標),outline是矩形線條顏色(B,G,R)。
#注:原始圖像如果是灰度圖,則去掉outline,因為灰度圖沒有RGB可言。drawEyes、detectSmiles也一樣。

def detectFaces(image_name):
    img = cv2.imread(image_name)
    face_cascade = cv2.CascadeClassifier("D:/PySpace/face/haarcascade_frontalface_default.xml")
    if img.ndim == 3:
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    else:
        gray = img #if語句:如果img維度為3,說明不是灰度圖,先轉化為灰度圖gray,如果不為3,也就是2,原圖就是灰度圖

    faces = face_cascade.detectMultiScale(gray, 1.2, 5)#1.3和5是特征的最小、最大檢測窗口,它改變檢測結果也會改變
    result = []
    for (x,y,width,height) in faces:
        result.append((x,y,x+width,y+height))
    return result
def drawFaces(image_name):
    faces = detectFaces(image_name)
    if faces:
        img = Image.open(image_name)
        draw_instance = ImageDraw.Draw(img)
        for (x1,y1,x2,y2) in faces:
            draw_instance.rectangle((x1,y1,x2,y2), outline=(255, 0,0))
        img.save('drawfaces_'+image_name)

"""

用自己訓練的模型的代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM