引言:
卷積神經網絡
- 卷積神經網絡最早是為了解決圖像識別的問題,現在也用在時間序列數據和文本數據處理當中,卷積神經網絡對于數據特征的提取不用額外進行,在對網絡的訓練的過程當中,網絡會自動提取主要的特征.
- 卷積神經網絡直接用原始圖像的全部像素作為輸入,但是內部為非全連接結構.因為圖像數據在空間上是有組織結構的,每一個像素在空間上和周圍的像素是有關系的,和相距很遠的像素基本上是沒什么聯系的,每個神經元只需要接受局部的像素作為輸入,再將局部信息匯總就能得到全局信息. 權值共享和池化兩個操作使網絡模型的參數大幅的減少,提高了模型的訓練效率.
卷積神經網絡主要特點
- 權值共享: 在卷積層中可以有多個卷積核,每個卷積核與原始圖像進行卷積運算后會映射出一個新的2D圖像,新圖像的每個像素都來自同一個卷積核.這就是權值共享.
- 池化: 降采樣,對卷積(濾波)后,經過激活函數處理后的圖像,保留像素塊中灰度值最高的像素點(保留最主要的特征),比如進行 2X2的最大池化,把一個2x2的像素塊降為1x1的像素塊.
進入內容
首先項目
實現架構和結果圖
1.訓練模型
train.py
"""
訓練模型
"""
from keras.callbacks import TensorBoard, ModelCheckpoint
from generator import get_train_test, create_generator # 數據切分,創建生成器
from model_framework.frontend import get_model # 導入模型def train():"""功能:訓練模型,并保存模型"""model = get_model((64, 64, 3)) # 獲取模型X_train, X_test, y_train, y_test= get_train_test('data/dataset') # 完成數據集切分tbCallBack = TensorBoard( log_dir='./logs')model_checkpoint = ModelCheckpoint(f'trained_model/checkpoint_model.h5',monitor='val_loss',verbose=0,save_weights_only=False,save_best_only=True) # 注:save_weights_only = False,表示保存的由ModelCheckpoint()保存的模型既可以用load_model加載,也可以用load_weights加載model.fit_generator(create_generator( X_train, y_train, 40),steps_per_epoch=50,epochs=30,validation_data=create_generator( X_test,y_test,40),validation_steps=10,callbacks=[tbCallBack, model_checkpoint]) # TODO完成訓練模型代碼model.save_weights('trained_model/weight.h5') # 保存權重model.save( './trained_model/model.h5')# 完成保存模型文件代碼if __name__ == '__main__':train()
?
2.數據處理相關功能
import os
import cv2
import numpy as np
from sklearn.model_selection import train_test_splitdef read_img(path):"""功能:讀取圖片# 參數:path: 數據集的路徑# 返回:res: 不同人的圖片。"""res = []for (root, dirs, files) in os.walk(path):if files:tmp = []files = np.random.choice(files, 4)for f in files:img = os.path.join(root, f)image = cv2.imread(img)image = cv2.resize(image, (64, 64),interpolation=cv2.INTER_CUBIC)image = np.array(image, dtype='float32')image /= 255.tmp.append(image)res.append(tmp)return resdef get_paris(path):"""功能:構造用于訓練的成對數據# 參數:path: 數據集的路徑# 返回:sm1: 一對數據中的第一個對象sm2: 一對數據中的第二個對象y1: 成對數據的標簽,相同為1,不同為0."""sm1, sm2, df1, df2 = [], [], [], []res = read_img(path)persons = len(res)for i in range(persons):for j in range(i, persons):p1 = res[i]p2 = res[j]if i == j:for pi in p1:for pj in p2:sm1.append(pi)sm2.append(pj)else:df1.append(p1[0])df2.append(p2[0])df1 = df1[:len(sm1)]df2 = df2[:len(sm2)]y1 = list(np.zeros(len(sm1)))y2 = list(np.ones(len(df1)))sm1.extend(df1)sm2.extend(df2)y1.extend(y2)return sm1, sm2, y1def create_generator(x, y, batch):"""功能:構造數據生成器# 參數:x: 數據y: 標簽batch: 數據生成器每次生成數據的個數# 返回:[x1, x2]: 成對數據yb: 數據標簽"""while True:index = np.random.choice(len(y), batch) # 每次選擇batch個數據索引x1, x2, yb = [], [], []for i in index:x1.append(x[i][0])x2.append(x[i][1])yb.append(y[i])x1 = np.array(x1)x2 = np.array(x2)yield [x1, x2], ybdef get_train_test(path):"""功能:切分數據集# 參數:path: 數據集的路徑# 返回:X_train: 用于訓練的數據X_test: 用于測試的數據y_train: 用于訓練的標簽y_test: 用于測試的標簽"""im1, im2, y = get_paris(path) # 構造成對數據im = list(zip(im1, im2))X_train, X_test, y_train, y_test = train_test_split(im, y, test_size=0.33) # 利用sklearn切分數據集return X_train, X_test, y_train, y_test
3.工具包相關內容
"""
工具包:提取特征、圖像處理
"""
import cv2
import numpy as np
from model_framework.frontend import get_model,contrastive_loss
from keras.models import Model,load_modelfrom keras.applications.mobilenet import relu6,DepthwiseConv2Ddef get_feature_model(url,mode = 'weight'):"""功能:獲取提取特征模型# 參數:url:模型或模型權重的路徑mode:'weight'或'model_framework'# 返回:feat_model: 返回提取特征模型"""if mode == 'weight': # 加載權重model = get_model((64, 64, 3),plot_model_path='data/images/face_net.png')model.load_weights(url)elif mode == 'model': # 加載模型 注:加載模型時,若包含自定義層或自定義對象時,需要使用custom_objects參數。model = load_model(url,custom_objects={'contrastive_loss': contrastive_loss,'relu6': relu6,'DepthwiseConv2D':DepthwiseConv2D})feat_model = Model(inputs=model.get_layer('model_1').get_input_at(0),outputs=model.get_layer('model_1').get_output_at(0))return feat_modeldef process_image(img):"""功能:預處理圖像# 返回:image: 處理后的圖像"""image = cv2.resize(img, (64, 64),interpolation=cv2.INTER_CUBIC) # 重新設置圖像大小image = np.array(image, dtype='float32') # 將圖像格式改為arrayimage /= 255. # 歸一化image = np.expand_dims(image, axis=0) # 增加維度return image
4.用到兩個模型框架
siamese network 和 MobileNet v2 模型框架的具體使用如下
"""
前端模型,siamese network
功能:該網絡使得相似特征距離更近,否則盡可能遠。
"""
from .backend import MobileNetv2 # 后端模型,用來提取人臉特征。
import keras.backend as K
from keras.layers import Input, Lambda
from keras.models import Model
from keras.optimizers import Adamdef euclidean_distance(inputs):"""歐氏距離功能:該函數計算兩個特征之間的歐氏距離。# 參數:inputs: 兩個特征,list類型.# 返回:Output: 歐氏距離,double類型."""u, v = inputsreturn K.sqrt(K.sum((K.square(u - v)), axis=1, keepdims=True))def contrastive_loss(y_true, y_pred):"""對比損失功能:計算對比損失.# 參數:y_true:表示樣本是否匹配的標簽,y_true = 1 表示匹配,0表示不匹配.整數類型。y_pred:歐氏距離,double類型.# ReturnsOutput:contrastive loss,double類型."""margin = 1. # 閾值return K.mean((1. - y_true) * K.square(y_pred) + y_true * K.square(K.maximum(margin - y_pred, 0.)))def get_model(shape,plot_model_path='data/images/face_net.png'):"""人臉識別網絡功能:該網絡使得相似特征距離更近,否則盡可能遠。# 參數:shape: 輸入圖像input的形狀,彩色圖像或灰度圖像.# 返回:模型model."""mn = MobileNetv2(shape) # 后端模型,用來提取特征。im1 = Input(shape=shape)im2 = Input(shape=shape)feat1 = mn(im1) # 提取特征,feat1和feat2分別為提取到的特征。feat2 = mn(im2)distance = Lambda(euclidean_distance)([feat1, feat2])# Lambda層,在此處用于歐氏距離的計算,該方式為函數式編程。face_net = Model(inputs=[im1, im2], outputs=distance) # 構造siamese network模型adam = Adam(lr = 0.0012,beta_1=0.9, beta_2=0.999)face_net.compile(optimizer=adam, loss=contrastive_loss) # 編譯模型,損失函數為contrastive_lossreturn face_net
"""
MobileNet v2 模型框架
"""
from keras.models import Model
from keras.layers import Input, Conv2D, GlobalAveragePooling2D
from keras.layers import Activation, BatchNormalization, add, Reshape
from keras.regularizers import l2
from keras.layers.advanced_activations import LeakyReLU
from keras.applications.mobilenet import relu6, DepthwiseConv2D
from keras import backend as Kdef _conv_block(inputs, filters, kernel, strides):channel_axis = 1 if K.image_data_format() == 'channels_first' else -1x = Conv2D(filters, kernel, padding='same', strides=strides)(inputs)x = BatchNormalization(axis=channel_axis)(x)return Activation(relu6)(x)def _bottleneck(inputs, filters, kernel, t, s, r=False):channel_axis = 1 if K.image_data_format() == 'channels_first' else -1tchannel = K.int_shape(inputs)[channel_axis] * tx = _conv_block(inputs, tchannel, (1, 1), (1, 1))x = DepthwiseConv2D(kernel, strides=(s, s),depth_multiplier=1, padding='same')(x)x = BatchNormalization(axis=channel_axis)(x)x = Activation(relu6)(x)x = Conv2D(filters, (1, 1), strides=(1, 1), padding='same')(x)x = BatchNormalization(axis=channel_axis)(x)if r:x = add([x, inputs])return xdef _inverted_residual_block(inputs, filters, kernel, t, strides, n):x = _bottleneck(inputs, filters, kernel, t, strides)for i in range(1, n):x = _bottleneck(x, filters, kernel, t, 1, True)return xdef MobileNetv2(input_shape):"""MobileNetv2框架# 參數:input_shape: 輸入值的shape# 返回:model_framework:MobileNetv2模型"""inputs = Input(shape=input_shape, name='single_input')x = _conv_block(inputs, 32, (3, 3), strides=(2, 2))x = _inverted_residual_block(x, 64, (3, 3), t=5, strides=2, n=2)x = _inverted_residual_block(x, 128, (3, 3), t=5, strides=2, n=2)x = _inverted_residual_block(x, 256, (3, 3), t=5, strides=1, n=1)x = _conv_block(x, 1280, (1, 1), strides=(1, 1))x = GlobalAveragePooling2D()(x)x = Reshape((1, 1, 1280))(x)x = Conv2D(512, (1, 1), padding='same', kernel_regularizer=l2(5e-4))(x)x = LeakyReLU(alpha=0.1)(x)x = Conv2D(128, (1, 1), padding='same', kernel_regularizer=l2(5e-4))(x)output = Reshape((128,), name='feat_out')(x)model = Model(inputs, output)return model
5.結果輸入進行可視化操作
"""
結果可視化
"""
import os
import cv2
import numpy as np
import matplotlib.pyplot as plt
from sklearn.manifold import TSNE
from utils.utils import get_feature_model, process_imagedef plot_reduce_dimension(model):"""Plot reduced dimension result wiht t-SNE.功能:用t-SNE算法對預測結果降維,并可視化顯示# 參數:model_framework: Model, 提取特征的模型"""outputs = []n = 5 # 類別paths = 'data/dataset'dirs = np.random.choice(os.listdir(paths), n) # 隨機選擇n個類別for d in dirs:p = os.path.join(paths, str(d))files = os.listdir(p)if files:for f in files:img = os.path.join(p, f) # 獲取圖像urlimage = cv2.imread(img) # 讀取圖像image = process_image(image) # 圖像預處理output = model.predict(image)[0] # 顯示預測結果outputs.append(output)embedded = TSNE(2).fit_transform(outputs) # 進行數據降維,降成兩維colors = ['b', 'g', 'r', 'k', 'y']for i in range(n):m, n = i * 20, (i + 1) * 20plt.scatter(embedded[m: n, 0], embedded[m: n, 1],c=colors[i], alpha=0.5)plt.title('T-SNE')plt.grid(True)plt.show()def compare_distance(model,paths):"""功能:對比人與人之間的不同,即計算歐氏距離并可視化# 參數:model_framework: 特征提取模型"""dists = []outputs = []paths = paths # 預測數據的地址for img in os.listdir(paths):# img = paths + img + '.jpg' # 獲取圖片路徑img = os.path.join(paths,img)image = cv2.imread(img) # 讀取圖片image = process_image(image) # 圖片預處理output = model.predict(image) # 預測結果outputs.append(output)vec1 = outputs[0]for vec2 in outputs:dist = np.linalg.norm(vec1 - vec2) # 計算L2范數,即歐氏距離dists.append(dist)print(dists[1:])plt.bar(range(1, 6), (dists[1:]), color='lightblue')plt.xlabel('Person')plt.ylabel('Euclidean distance')plt.title('Similarity')plt.grid(True)plt.show()if __name__ == '__main__':# model_framework = get_feature_model(url = 'trained_model/weight.h5',mode='weight') # 加載模型model = get_feature_model(url='trained_model/model.h5 ',mode = 'model') # 完成加載模型代碼# TODO完成可視化提取的樣本特征代碼plot_reduce_dimension(model)# TODO完成可視化人臉的相似度compare_distance(model,'./data/images/person')
以上就是本文的全部內容,希望對大家的學習有所幫助。
?
?
https://github.com/Liu0330
?
http://39.106.94.21/article/Github/#?
http://39.106.94.21/article/python0/