文章目录
1.复写keras_faster rcnn
1.1 解析xml信息
import xml.etree.ElementTree as ET
def parse_label(xml_file):
#建立一个实例
tree = ET.parse(xml_file) # parse解析
#建立跟路径
root = tree.getroot()
width = root.find('size').find('width').text # 一张照片这3个量都为1个不变
height = root.find('size').find('height').text
image_name = root.find('filename').text
#数据列表,一列为一组数据
category = []
xmin = []
ymin = []
xmax = []
ymax = []
for object in root.findall('object'): # 因为object有多个
for x in object.iter('name'): # name就是category多个
category.append(x.text)
xmax.append(object.find('bndbox').find('xmax').text)
ymax.append(object.find('bndbox').find('ymax').text)
xmin.append(object.find('bndbox').find('xmin').text)
ymin.append(object.find('bndbox').find('ymin').text)
#列表组合
ground_truth_box = [list(box) for box in zip(xmin,ymin,xmax,ymax)]
return image_name,(width,height),category,ground_truth_box
import glob # 读文件夹也可以:for i in os.dir glob可以带路径输出
for name in glob.glob('/Users/mikegao/Desktop/Annotation/*'):
print (parse_label(name),'\n')
或
:
import pandas as pd
list_table =[]
for i in range(10,20):
date = {'name':i+1,"age":i}
list_table.append(date) # 将data append进去
print (list_table)
# 下面data来自list_table
data_frame = pd.DataFrame(data=list_table,columns=['age','name'])
data_frame.to_csv('321.csv',index=False,mode='a',header=True)
输出的 list_table:
输出的 data_frame:
1.2 Anchor生成
下图box[0]为x,box[1]为y
结果为:(9,256), 所以有256个centerx(中心点),每个里面有9个框,256*9=2304
1.3 多输出多输入(函数式)
1.4 resnet
下图stage1里conv和maxpool都会做一次下采样
import keras.layers as KL
from keras.models import Model
import keras.backend as K
import tensorflow as tf
from keras.utils import np_utils
from keras.utils.vis_utils import plot_model
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
import os
%matplotlib inline
from keras.datasets import mnist
#创建resnet网络结构
def building_block(filters,block): #filters数量根据stage会增加,不能写死在网络里
#判断block1和2
if block != 0: # 不=0的话就是block2,枚举
stride = 1
else: #如果等于0 采用stride 2,两倍下采样 也就是 如果是 building1 使用2倍下采样
stride = 2
def f(x):
#主路
y = KL.Conv2D(filters=filters,kernel_size=(1,1),strides=stride)(x)
# 原文一开始kernel_size=(3,3),但28*28图卷积没了
y = KL.BatchNormalization(axis=3)(y) #[-1,28,28,3] axis=3对应下标3即3
y = KL.Activation('relu')(y)
#注意第二层没有stride使用padding same就是保证size相同
y = KL.Conv2D(filters=filters, kernel_size=(3, 3), padding='same')(y)
y = KL.BatchNormalization(axis=3)(y)
y = KL.Activation('relu')(y)
y = KL.Conv2D(filters=4*filters,kernel_size=(1,1))(y)
y = KL.BatchNormalization(axis=3)(y)
#副路
#判断是哪个block 设定不同的 shortcut支路参数
if block == 0 : #如果是0 那么就是block1的通路
shortcut = KL.Conv2D(filters=4*filters,kernel_size=(1,1),strides=stride)(x)
shortcut = KL.BatchNormalization()(shortcut)
else:
#如果不等于0 那就是block2 那么就直接接input的tensor
shortcut = x
#主通路和副路shortcut 相加
y = KL.Add()([y,shortcut]) #y主 shortcut支路 直接通过add层相加
y = KL.Activation('relu')(y)
return y
return f
#resnet fp提取
def ResNet_Extractor(X_train, Y_train,X_test,Y_test):
# 头部,TOP:
# customize your top input
input = KL.Input([28,28,1])
x = KL.Conv2D(filters=64,kernel_size=(3,3),padding='same')(input)
x = KL.BatchNormalization(axis=3)(x)
x = KL.Activation('relu')(x)
# 身体,主要部分:控制building_block
# 每个stage要有不同的 block 12222的数量 ,还有第一个Block1 输入维度后边要迭代(stage)
filters = 64
block = [2,2] # stage0里有2个block,stage1里也有2个block
for i,block_num in enumerate(block):
print ('---stage--', str(i) ,'---')
for block_id in range(block_num):
print('---block--', str(block_id) ,'---')
x = building_block(filters=filters,block=block_id)(x)
filters *= 2 #每个stage double filter个数
# 注意这里的输出就是我们常规的featuremap的输出x,不包含下面尾部
# 尾部,输出:
x = KL.AveragePooling2D(pool_size=(2, 2))(x)
x = KL.Flatten()(x)
x = KL.Dense(units=10, activation='softmax')(x
# 定义输入输出
model = Model(inputs=input, outputs=x)
# 打印网络
print (model.summary())
plot_model(model,to_file='resMnist6-9-2.png',show_shapes=True)
# 编译网络
model.compile(loss='categorical_crossentropy',
optimizer='adam',
metrics=['accuracy'])
history = model.fit( # 想要打印出来所有做个变量history
X_train,
Y_train,
epochs=6,
batch_size=200,
verbose=1,
validation_data = (X_test,Y_test),
)
model.save('./mnistRES2.h5')
#打印graph
plt.plot(history.history['loss'])
plt.plot(history.history['val_loss'])
plt.title("model loss")
plt.ylabel("loss")
plt.xlabel("epoch")
plt.legend(["train","test"],loc="upper left")
plt.show()
plt.plot(history.history['acc'])
plt.plot(history.history['val_acc'])
plt.title('model accuracy')
plt.ylabel('accuracy')
plt.xlabel('epoch')
plt.legend(['train', 'test'], loc='upper left')
plt.show()
return model
def main():
(X_train,Y_train),(X_test,Y_test) = mnist.load_data()
print(X_train.shape)
X_train = X_train.reshape(-1,28,28,1)
X_test = X_test.reshape(-1,28,28,1)
print (X_train.shape)
#-set type into float32 设置成浮点型
X_train = X_train.astype('float32') #astype SET AS TYPE into
X_test = X_test.astype('float32')
X_train = X_train/255.0
X_test /=255.0
#Class vectors [0,0,0,0,0,0,0,1(7),0,0] #转成二进制
Y_test = np_utils.to_categorical(Y_test,10) #定义LABEL类数量
Y_train = np_utils.to_categorical(Y_train,10)
ResNet_Extractor(X_train, Y_train,X_test,Y_test)
main()
输出:
测试:
from keras.models import load_model
import matplotlib.image as processimage
import matplotlib.pyplot as plt
import numpy as np
model = load_model('mnistRES2.h5')
image = processimage.imread('pred_image/3.jpg')
# plt.imshow(image)
# plt.show()
image_to_array = np.array(image)
image_to_array = image_to_array.reshape(-1,28,28,1)
prediction = model.predict(image_to_array)
Final_prediction = [result.argmax() for result in prediction]
print (Final_prediction,prediction)
1.5 rpn
import keras.layers as KL
from keras.models import Model
import keras.backend as K
import keras
import tensorflow as tf
from keras.utils import plot_model
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
%matplotlib inline
def building_block(filters,block):
# import random
# bn = random.randint(100,300,)
#判断block1和2
if block != 0: #如果不等于0 那么使用 stride=1
stride = 1
else: #如果等于0 采用stride 2 两倍下采样 也就是 如果是 building1 使用2倍下采样
stride = 2
def f(x):
#主通路结构
y = KL.Conv2D(filters=filters,kernel_size=(1,1),strides=stride)(x)
y = KL.BatchNormalization(axis=3)(y)
y = KL.Activation('relu')(y)
y = KL.Conv2D(filters=filters, kernel_size=(3, 3), padding='same')(y) #注意这里没有stride使用padding same就是保证size相同
y = KL.BatchNormalization(axis=3)(y)
y = KL.Activation('relu')(y)
#主通路输出
y = KL.Conv2D(filters=4*filters,kernel_size=(1,1))(y)
y = KL.BatchNormalization(axis=3)(y)
#判断是哪个block 设定不同的 shortcut支路参数
if block == 0 : #如果是0 那么就是block1的通路
shortcut = KL.Conv2D(filters=4*filters,kernel_size=(1,1),strides=stride)(x)
shortcut = KL.BatchNormalization(axis=3)(shortcut)
else:
#如果不等于0 那就是block2 那么就直接接input的tensor
shortcut = x
#主通路和shortcut 相加
y = KL.Add()([y,shortcut]) #y主 shortcut支路 直接通过add层相加
import random
y = KL.Activation('relu',name='last'+str(random.randint(100,300)))(y)
return y
return f
#resnet 主输入函数
def ResNet_Extractor(inputs):
x = KL.Conv2D(filters=64,kernel_size=(3,3),padding='same')(inputs)
x = KL.BatchNormalization(axis=3)(x)
x = KL.Activation('relu')(x)
#控制调用网络结构feature map 特征图
#每个stage要有不同的 b12的数量 ,还有 第一个Block1 输入维度后边要迭代(stage)
filters = 64
block = [2,2,2]
for i,block_num in enumerate(block):
for block_id in range(block_num):
x = building_block(filters=filters,block=block_id)(x)
filters *= 2 #每个stage double filter个数
return x
#share map 和 anchor提取
def RpnNet(featuremap, k=9):
#特征图到共享层,大小没变
shareMap = KL.Conv2D(filters=256,kernel_size=(3,3),padding='same',name='SSharemap')(featuremap)
shareMap = KL.Activation('linear')(shareMap)
#计算rpn分类前后景
rpn_classification = KL.Conv2D(filters=2*k,kernel_size=(1,1))(shareMap)
#无法reshape上面这个rpn_classification tensor,加个自定义层,tf.reshape(x为目标,[]为格式)
rpn_classification = KL.Lambda(lambda x:tf.reshape(x,[tf.shape(x)[0],-1,2]))(rpn_classification)
#上行[0]是取batch,-1是anchor数量每个图大小不一样ancho的r数量也不一样,输出维度2:每个anchor输出是或不是,x就是rpn_classification
rpn_classification = KL.Activation('linear',name='rpn_classification')(rpn_classification)
#上面输出前后景,下面还要输出前后景的概率值
rpn_probability = KL.Activation('softmax',name='rpn_probability')(rpn_classification)
#计算回归修正
rpn_position = KL.Conv2D(filters=4*k,kernel_size=(1,1))(shareMap)
rpn_position = KL.Activation('linear')(rpn_position)
rpn_BoundingBox =KL.Lambda(lambda x:tf.reshape(x,[tf.shape(x)[0],-1,4]),name='rpn_POS')(rpn_position)
return rpn_classification,rpn_probability,rpn_BoundingBox
# x = KL.Input((64,64,3))
# featureMap = ResNet_Extractor(x)
# rpn_classification,rpn_probability,rpn_BoundingBox = RpnNet(featureMap,k=9)
# model = Model(inputs = [x],outputs=[rpn_classification,rpn_probability,rpn_BoundingBox])
# model.summary()
# plot_model(model=model,to_file='siezemap test.png',show_shapes=True)
1.5.1 rpn分类loss
def RPNClassLoss(rpn_match,rpn_Cal): #rpn_match原始输入值
rpn_match = tf.squeeze(rpn_match,axis=-1) #axis=-1指最后一维
#tf.where(bool型:T/F)结果为[行,列]
indices = tf.where(K.not_equal(x=rpn_match,y=0))
#1=1前景, 0 and -1 = 0后景
anchor_class = K.cast(K.equal(rpn_match,1),tf.int32) #return Ture = 1 False = 0
anchor_class = tf.gather_nd(params=anchor_class ,indices=indices) #这个是我们原始样本结果
rpn_cal_class = tf.gather_nd(params=rpn_Cal,indices=indices) # 这个我们rpn计算值结果
# .sparse解析就不需one—hot了
loss = K.sparse_categorical_crossentropy(target=anchor_class,output=rpn_cal_class,from_logits=True)
# if then else
loss = K.switch(condition=tf.size(loss)>0,then_expression=K.mean(loss),else_expression=tf.constant(0.0))
#>0取平均,<0取0
return loss
1.5.2 rpn回归loss
#小工具提取
def batch_pack(x,counts,num_rows):
output = []
for i in range(num_rows):
output.append(x[i,:counts[i]])
return tf.concat(output,axis=0)
#位置loss
def RpnBBoxLoss(target_bbox,rpn_match,rpn_bbox):
rpn_match = tf.squeeze(input=rpn_match,axis=-1)
indice = tf.where(K.equal(x = rpn_match,y=1)) #正样本位置
rpn_bbox = tf.gather_nd(params=rpn_bbox,indices=indice) #rpn预测值 找到=1的位置
batch_counts = K.sum(K.cast(K.equal(x = rpn_match,y=1),tf.int32),axis=-1)
target_bbox = batch_pack(x= target_bbox,counts=batch_counts,num_rows=10)
#loss 计算
diff = K.abs(target_bbox-rpn_bbox)
less_than_one = K.cast(K.less(x = diff, y=1.0),tf.float32)
loss = less_than_one * 0.5 * diff**2 + (1 - less_than_one)*(diff-0.5)
loss = K.switch(condition=tf.size(loss)>0,then_expression=K.mean(loss),else_expression=tf.constant(0.0))
return loss
#确定input
input_image = KL.Input(shape=[64,64,3],dtype=tf.float32)
input_bbox = KL.Input(shape=[None,4],dtype=tf.float32)
input_class_ids = KL.Input(shape = [None],dtype=tf.int32) # map {'dog':0,'cat':1}
input_rpn_match = KL.Input(shape=[None,1],dtype=tf.int32)
input_rpn_bbox = KL.Input(shape=[None,4],dtype=tf.float32)
#in out put
feature_map = ResNet_Extractor(input_image)
rpn_classification,rpn_probability,rpn_BoundingBox = RpnNet(feature_map,k=9)
loss_rpn_class = KL.Lambda(lambda x:RPNClassLoss(*x),name='classloss')([input_rpn_match,rpn_classification])
loss_rpn_bbox = KL.Lambda(lambda x:RpnBBoxLoss(*x),name='bboxloss')([input_rpn_bbox,input_rpn_match,rpn_BoundingBox])
model = Model(inputs=[input_image,input_bbox,input_class_ids,input_rpn_match,input_rpn_bbox],
outputs = [rpn_classification,rpn_probability,rpn_BoundingBox,loss_rpn_class,loss_rpn_bbox] )
#自定义loss 输入
loss_layer1 = model.get_layer('classloss').output
loss_layer2 = model.get_layer('bboxloss').output
model.add_loss(tf.reduce_mean(loss_layer1))
model.add_loss(tf.reduce_mean(loss_layer2))
model.compile(loss=[None]*len(model.outputs),
optimizer=keras.optimizers.SGD(lr=0.00003))
model.summary()
2.kaggle猫狗分类
2.1 数据准备
猫狗图下载:https://www.kaggle.com/c/dogs-vs-cats/data ,原始数据集包含25,000张猫狗图(每个类别12,500个)大小为543MB(压缩后)。Keras己内建好的预训练模型进行图像分类, 包括:VGG16
,VGG19
,ResNet50
,InceptionV3
,InceptionResNetV2
,Xception
,MobileNet
from keras.applications import VGG16
# 实例化一个VGG16模型
conv_base = VGG16(weights='imagenet',
include_top=False, # 在这里告诉 keras我们只需要卷积基底的权重模型资讯
input_shape=(150, 150, 3)) # 宣告我们要处理的图像大小与颜色通道数
向构造函数传递了三个参数:
1weights
, 指定从哪个权重检查点初始化模型
2.include_top
, 指定模型最后是否包含密集连接分类器。默认情况下,这个密集连接分类器对应于ImageNet的1000个类别。因为我们打算使用自己的分类器(只有两个类别:cat和dog),所以不用包含。
3.input_shape
, 输入到网络中的图像张量(可选参数),如果不传入这个参数,那么网络可以处理任意形状的输入
以下是VGG16“卷积基底conv_base”
架构细节:
conv_base.summary() # 打印一下模型资讯
最后这个特征图形状为(4, 4, 512),这个特征上面添加一个密集连接分类器。
2.2 不用数据增强快速特征提取(计算代价低)
运行ImageDataGenerator实例,将图像及其标签提取为Numpy数组,调用conv_base模型的predict方法
从这些图像的中提取特征。
#特征提取
import os
import numpy as np
from keras.preprocessing.image import ImageDataGenerator
base_dir = 'data/cats_and_dogs_small'
train_dir = os.path.join(base_dir, 'train')
validation_dir = os.path.join(base_dir, 'validation')
test_dir = os.path.join(base_dir, 'test')
datagen = ImageDataGenerator(rescale=1./255) # 产生一个"图像资料产生器"物件
batch_size = 20 # 设定每次产生的图像的数据批量
# 提取图像特征
def extract_features(directory, sample_count): # 影像的目录, 要处理的图像数
features = np.zeros(shape=(sample_count, 4, 4, 512)) # 根据VGG16(卷积基底)的最后一层的轮出张量规格
labels = np.zeros(shape=(sample_count)) # 要处理的图像数
# 产生一个"图像资料产生器"实例(资料是在档案目录中), 每呼叫它一次, 它会吐出特定批次数的图像资料
generator = datagen.flow_from_directory(
directory,
target_size=(150, 150), # 设定图像的高(height)与宽(width)
batch_size=batch_size, # 设定每次产生的图像的数据批量
class_mode='binary') # 因为我们的目标资料集只有两类(cat & dog)
# 让我们把训练资料集所有的图像都跑过一次
i = 0
for inputs_batch, labels_batch in generator:
features_batch = conv_base.predict(inputs_batch) # 透过“卷积基底”来提取图像特征
features[i * batch_size : (i + 1) * batch_size] = features_batch # 把特征先存放起来
labels[i * batch_size : (i + 1) * batch_size] = labels_batch #把标签先存放起来
i += 1
if i * batch_size >= sample_count:
# Note that since generators yield data indefinitely in a loop,
# we must `break` after every image has been seen once.
break
print('extract_features complete!')
return features, labels
train_features, train_labels = extract_features(train_dir, 2000) # 训练资料的图像特征提取
validation_features, validation_labels = extract_features(validation_dir, 1000) # 验证资料的图像特征提取
test_features, test_labels = extract_features(test_dir, 1000) # 测试资料的图像特征淬取
2.2.1 flatten后接分类器
提取的特征当前是(样本数,4,4,512)的形状。我们将它们喂给一个密集连接(densely-connected)的分类器,所以首先我们必须把它们压扁(flatten)成(样本数, 8192):
train_features = np.reshape(train_features, (2000, 4 * 4 * 512))
validation_features = np.reshape(validation_features, (1000, 4 * 4 * 512))
test_features = np.reshape(test_features, (1000, 4 * 4 * 512))
下面定义一个密集连接分类器,并在刚刚保存好的数据和标签上训练分类器:
from keras import models
from keras import layers
from keras import optimizers
# 产生一个新的密集连接层来做为分类器
model = models.Sequential()
model.add(layers.Dense(256, activation='relu', input_dim=4 * 4 * 512))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation='sigmoid')) # 因为我的资料集只有两类(cat & dog)
model.compile(optimizer=optimizers.RMSprop(lr=2e-5),
loss='binary_crossentropy',
metrics=['acc'])
# 把预处理的卷积基底所提取的特征做为input来进行训练
history = model.fit(train_features, train_labels,
epochs=30,
batch_size=20,
validation_data=(validation_features, validation_labels))
训练速度快,只需要处理两个Dense层。看一下训练过程中的损失和精度曲线:
import matplotlib.pyplot as plt
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
从图中可以看出,验证精度达到了约90%,比之前从一开始就训练小型模型效果要好很多,但是从图中也可以看出,虽然dropout比率比较大,但模型从一开始就出现了过拟合
。这是因为本方法没有使用数据增强
,而数据增强对防止小型图片数据集过拟合非常重要。
2.3 用数据增强的特征提取(计算代价高)
这种方法速度更慢,计算代价更高,但是可以在训练期间使用数据增强。这种方法是:扩展conv_base模型,然后在输入数据上端到端的运行模型(这种方法计算代价很高,必须在GPU上运行)
from keras import models
from keras import layers
model = models.Sequential() # 产生一个新的网络模型结构
model.add(conv_base) # 把预训练的卷积基底叠上去
model.add(layers.Flatten()) # 打平
model.add(layers.Dense(256, activation='relu')) # 叠上新的密集连接层来做为分类器
model.add(layers.Dense(1, activation='sigmoid')) # 因为我的资料集只有两类(cat & dog)
model.summary()
VGG16的“卷积基底”
有14,714,688个参数,非常大。上面添加的分类器有200万个参数。在编译和训练模型之前,需要冻结卷积基
。冻结一个或多个层是指在训练过程中保持其权重不变(如果不这么做,那么卷积基
之前学到的表示
将会在训练
过程中被修改
)。因为其上添加的Dense是随机初始化的,所以非常大的权重更新会在网络中进行传播
,对之前学到的表示
造成很大破坏。在Keras中,冻结网络的方法是将其trainable属性设置为False:
# 看一下“冻结前”有多少可以被训练的权重
print('This is the number of trainable weights '
'before freezing the conv base:', len(model.trainable_weights))
# “冻结”卷积基底
conv_base.trainable = False
# 再看一下“冻结后”有多少可以被训练的权重
print('This is the number of trainable weights '
'after freezing the conv base:', len(model.trainable_weights))
2.3.1 冻结后用数据增强训练
如此设置之后,只有添加的两个Dense层的权重才会被训练,总共有4个权重张量,每层2个(主权重矩阵和偏置向量),注意的是,如果想修改权重属性trainable,那么应该修改好属性之后再编译模型。下面,我们可以训练模型了,并使用数据增强的办法:
from keras.preprocessing.image import ImageDataGenerator
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')
# 请注意: 验证测试用的资料不要进行资料的增强
test_datagen = ImageDataGenerator(rescale=1./255)
train_generator = train_datagen.flow_from_directory(
# 图像资料的目录
train_dir,
# 设定图像的高(height)与宽(width)
target_size=(150, 150),
batch_size=20,
# 因为我们的目标资料集只有两类(cat & dog)
class_mode='binary')
validation_generator = test_datagen.flow_from_directory(
validation_dir,
target_size=(150, 150),
batch_size=20,
class_mode='binary')
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=2e-5),
metrics=['acc'])
history = model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=30,
validation_data=validation_generator,
validation_steps=50,
verbose=2)
model.save('cats_and_dogs_small_3.h5') # 把模型储存到档案
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
验证精度到了将近96%,而且减少了过拟合(在训练集上好,验证测试集上差)
2.4 微调模型
以上0和1都属于特征提取
,下面使用模型微调
进一步提高模型性能,步骤如下:
(1)在已经训练好的基网络(base network)上添加自定义网络
(2)冻结基网络
(3)训练所添加的部分
(4)解冻基网络
的一些层
(5)联合训练解冻的这些层和添加的部分
在做特征提取
的时候已经完成了前三个步骤。我们继续第四个步骤,先解冻conv_base,然后冻结其中的部分层。
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
input_1 (InputLayer) (None, 150, 150, 3) 0
_________________________________________________________________
block1_conv1 (Conv2D) (None, 150, 150, 64) 1792
_________________________________________________________________
block1_conv2 (Conv2D) (None, 150, 150, 64) 36928
_________________________________________________________________
block1_pool (MaxPooling2D) (None, 75, 75, 64) 0
_________________________________________________________________
block2_conv1 (Conv2D) (None, 75, 75, 128) 73856
_________________________________________________________________
block2_conv2 (Conv2D) (None, 75, 75, 128) 147584
_________________________________________________________________
block2_pool (MaxPooling2D) (None, 37, 37, 128) 0
_________________________________________________________________
block3_conv1 (Conv2D) (None, 37, 37, 256) 295168
_________________________________________________________________
block3_conv2 (Conv2D) (None, 37, 37, 256) 590080
_________________________________________________________________
block3_conv3 (Conv2D) (None, 37, 37, 256) 590080
_________________________________________________________________
block3_pool (MaxPooling2D) (None, 18, 18, 256) 0
_________________________________________________________________
block4_conv1 (Conv2D) (None, 18, 18, 512) 1180160
_________________________________________________________________
block4_conv2 (Conv2D) (None, 18, 18, 512) 2359808
_________________________________________________________________
block4_conv3 (Conv2D) (None, 18, 18, 512) 2359808
_________________________________________________________________
block4_pool (MaxPooling2D) (None, 9, 9, 512) 0
_________________________________________________________________
block5_conv1 (Conv2D) (None, 9, 9, 512) 2359808
_________________________________________________________________
block5_conv2 (Conv2D) (None, 9, 9, 512) 2359808
_________________________________________________________________
block5_conv3 (Conv2D) (None, 9, 9, 512) 2359808
_________________________________________________________________
block5_pool (MaxPooling2D) (None, 4, 4, 512) 0
=================================================================
Total params: 14,714,688
Trainable params: 14,714,688
Non-trainable params: 0
回顾这些层,我们将微调最后三个卷积层
,直到block4_pool之前所有层都应该被冻结,后面三层来进行训练。为什么不调整更多层? 为什么不调整整个“卷积基底”? 我们可以,但是我们需要考虑:
1.
“卷积基底”较前面的神经层所学习到的特征表示更加通用(generic)
,更具有可重复使用的特征,而较高层次的特征表示则聚焦独特的特征
。微调这些聚焦独特的特征的神经层则更为有用。
2.
我们训练
的参数越多,我们越有可能的过拟合(overfitting)。VGG16的“卷积基底”具有1千5百万的参数,因此尝试在小数据集上进行训练是有风险的。
conv_base.trainable = True # 解冻 "卷积基底"
# 所有层直到block4_pool都应该被冻结,而 block5_conv1,block5_conv2, block5_conv3 及 block5_pool则被解冻
layers_frozen = ['block5_conv1','block5_conv2', 'block5_conv3', 'block5_pool']
for layer in conv_base.layers:
if layer.name in layers_frozen:
layer.trainable = True
else:
layer.trainable = False
# 把每一层是否可以被"trainable"的flat打印出来
for layer in conv_base.layers:
print("{}: {}".format(layer.name, layer.trainable))
2.4.1 微调并保存模型
现在可微调网络了,我们将使用学习率非常小的RMSProp优化器来实现。之所以让学习率很小,是因为对于微调网络的三层表示,我们希望其变化范围不要太大,太大的权重可能会破坏这些表示。
model.compile(loss='binary_crossentropy',
optimizer=optimizers.RMSprop(lr=1e-5), # 使用小的learn rate
metrics=['acc'])
history = model.fit_generator(
train_generator,
steps_per_epoch=100,
epochs=100,
validation_data=validation_generator,
validation_steps=50)
model.save('cats_and_dogs_small_4.h5')
acc = history.history['acc']
val_acc = history.history['val_acc']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'bo', label='Training acc')
plt.plot(epochs, val_acc, 'b', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'bo', label='Training loss')
plt.plot(epochs, val_loss, 'b', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
这些曲线看起来包含噪音。为了让图像更具有可读性,可以让每个损失和精度替换为指数移动平均,从而让曲线变得更加平滑,下面用一个简单实用函数来实现:
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()
通过指数移动平均,验证曲线变得更清楚了。精度提高了1%,约从96%提高到了97%。
在测试数据上最终评估这个模型:
test_generator = test_datagen.flow_from_directory(
test_dir,
target_size=(150, 150),
batch_size=20,
class_mode='binary')
test_loss, test_acc = model.evaluate_generator(test_generator, steps=50)
print('test acc:', test_acc)
得到了差不多97%的测试精度,在关于这个数据集的原始Kaggle竞赛中,这个结果是最佳结果之一。我们只是用了一小部分训练数据(约10%)就得到了这个结果。训练20000个样本和训练2000个样本还是有很大差别的。
3.Caffe_SSD三字码识别
3.1 check List
1.
检查 CUDA nvcc -V环境是否安装正常 如果不正常则去安装 NVIDIA ,CUDA ,CUDNN (版本搭配)
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2019 NVIDIA Corporation
Built on Sun_Jul_28_19:07:16_PDT_2019
Cuda compilation tools, release 10.1, V10.1.243
由于Python2 即将落幕 所以我们这次在Ubuntu18.04 自带的 python3.6上进行
2.
sudo ldconfig 检查是否有软连接没有生效
3.2 正式安装
1.
依赖解决:
sudo apt-get install libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler
sudo apt-get install --no-install-recommends libboost-all-dev
sudo apt-get install libopenblas-dev liblapack-dev libatlas-base-dev
sudo apt-get install libgflags-dev libgoogle-glog-dev liblmdb-dev
sudo apt-get install git cmake build-essential
从源代码编译Opencv,进入官网 : http://opencv.org/releases.html , 下载 3.x系列 解压到你要安装的位置,命令行进入已解压的文件夹
mkdir build # 创建编译的文件目录
cd build
cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/usr/local ..
make -j8 #编译 注意自己的核数
在执行 make -j8 命令编译到 92% 时可能会出现以下错误,是由于opecv3.1与cuda8.0不兼容导致的。解决办法:修改 /opencv-3.1.0/modules/cudalegacy/src/graphcuts.cpp 文件内容,如图:
编译成功后安装:sudo make install
安装完成后通过查看 opencv 版本验证是否安装成功:pkg-config --modversion opencv
2.
安装caffe-SSD
git clone https://github.com/weiliu89/caffe.git
cd caffe
git checkout ssd
再次检查依赖
sudo apt-get install libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler
sudo apt-get install --no-install-recommends libboost-all-dev
sudo apt-get install libatlas-base-dev python-dev
sudo apt-get install libgflags-dev libgoogle-glog-dev liblmdb-dev
3.
修改Makefile.config文件,复制一份的原因是编译 caffe 时需要的是 Makefile.config 文件,而Makefile.config.example 只是caffe 给出的配置文件例子,不能用来编译 caffe。
cp Makefile.config.example Makefile.config
sudo vim Makefile.config
应用 cudnn
将第5行的 # 取消
#USE_CUDNN := 1
修改成:
USE_CUDNN := 1
应用 opencv 版本
将第21行的 # 取消
#OPENCV_VERSION := 3
修改为:
OPENCV_VERSION := 3
使用 python 接口
将第89行的 # 取消
#WITH_PYTHON_LAYER := 1
修改为
WITH_PYTHON_LAYER := 1
修改 python 路径
将 92/93行的 代码修改如下
INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include
LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib
修改为:
INCLUDE_DIRS := $(PYTHON_INCLUDE) /usr/local/include /usr/include/hdf5/serial
LIBRARY_DIRS := $(PYTHON_LIB) /usr/local/lib /usr/lib /usr/lib/x86_64-linux-gnu /usr/lib/x86_64-linux-gnu/hdf5/serial
注释Python2 切换python3
代码修改如下 否则会编译Python.h and numpy/arrayobject.h. 出错
将 67/68行的 实际行数稍微有些出路可能
PYTHON_INCLUDE := /usr/include/python2.7 \
/usr/lib/python2.7/dist-packages/numpy/core/include
修改为:
# PYTHON_INCLUDE := /usr/include/python2.7 \
# /usr/lib/python2.7/dist-packages/numpy/core/include
将77/78 行的 注释解除 并更新 为Python3.6 实际行数稍微有些出路可能
# PYTHON_LIBRARIES := boost_python3 python3.5m
# PYTHON_INCLUDE := /usr/include/python3.5m \
# /usr/lib/python3.5/dist-packages/numpy/core/include
PYTHON_LIBRARIES := boost_python3 python3.6m
PYTHON_INCLUDE := /usr/include/python3.6m \
/usr/lib/python3.6/dist-packages/numpy/core/include
如果最后提示不支持compute_20,就把这句删掉,最后效果是
nvcc fatal : Unsupported gpu architecture ‘compute_20’
Makefile:588: recipe for target ‘.build_release/cuda/src/caffe/solvers/sgd_solver.o’ failed
make: *** [.build_release/cuda/src/caffe/solvers/sgd_solver.o] Error 1
make: *** Waiting for unfinished jobs…
建议显卡直接改成如下:
CUDA_ARCH := -gencode arch=compute_30,code=sm_30
-gencode arch=compute_35,code=sm_35
-gencode arch=compute_50,code=sm_50
-gencode arch=compute_52,code=sm_52
-gencode arch=compute_60,code=sm_60
-gencode arch=compute_61,code=sm_61
-gencode arch=compute_61,code=compute_61
然后修改caffe 目录下的 Makefile 文件: 注意不是Makefile.config文件
sudo vim Makefile
将第409行 替换为如下
NVCCFLAGS +=-ccbin=$(CXX) -Xcompiler-fPIC $(COMMON_FLAGS)
替换为:
NVCCFLAGS += -D_FORCE_INLINES -ccbin=$(CXX) -Xcompiler -fPIC $(COMMON_FLAGS)
将:181行替换为如下
LIBRARIES += glog gflags protobuf boost_system boost_filesystem m hdf5_hl hdf5
改为:
LIBRARIES += glog gflags protobuf boost_system boost_filesystem m hdf5_serial_hl hdf5_serial
将:265行 替换如下(我的环境太新除了这个问题【可选项】当你出现了
.build_release/lib/libcaffe.so: undefined reference to `boost::re_detail_106501::put_mem_block(void*)')
LIBRARIES += boost_thread stdc++
改为:
LIBRARIES += boost_thread stdc++ boost_regex
4.
下载python 环境依赖包 :
去到caffe根目录中的python目录中运行 …/caffe/python
5.
设置系统python环境 在末尾添加环境变量
vim ~/.bashrc
export PYTHONPATH="/opt/build/caffe/python" # 此处为caffe 的rootdir 目录
source ~/.bashrc
6.
编译caffe保存 开始编译,在 caffe 目录下执行 如果出错 建议修改完毕使用 make clean 继续Try
make all -j32 代表几核并行编译 请与自己电脑量力而行 后续将不再重复声明哇
make test -j32
make pycaffe
make runtest -j32
“/ usr / bin / ld:在Caffe编译中找不到-lopenblas”错误
即使在克隆OpenBlas之后包括基本包,并且将在14.04和16中链接相应的库。
apt install liblapack-dev liblapack3 libopenblas-base libopenblas-dev
apt install liblapack-dev liblapack3 libopenblas-base libopenblas-dev
到此 安装caffe 结束
7.
更改caffe 源码: https://blog.csdn.net/sinat_14916279/article/details/56489601
安装参考:https://blog.csdn.net/lukaslong/article/details/81390276