吴恩达第四课第二周分析

import numpy as np
from keras import layers
from keras.layers import Input, Dense, Activation, ZeroPadding2D, BatchNormalization, Flatten, Conv2D
from keras.layers import AveragePooling2D, MaxPooling2D, Dropout, GlobalMaxPooling2D, GlobalAveragePooling2D
from keras.models import Model
from keras.preprocessing import image
from keras.utils import layer_utils
from keras.utils.data_utils import get_file
from keras.applications.imagenet_utils import preprocess_input
import pydot
from IPython.display import SVG
from keras.utils.vis_utils import model_to_dot
from keras.utils import plot_model
import kt_utils 

import keras.backend as K
K.set_image_data_format('channels_last')
import matplotlib.pyplot as plt
from matplotlib.pyplot import imshow

%matplotlib inline

#加载数据集
X_train_orig, Y_train_orig, X_test_orig, Y_test_orig, classes = kt_utils load_dataset()
# Normalize image vectors
X_train = X_train_orig/255.
X_test = X_test_orig/255.

# Reshape
Y_train = Y_train_orig.T
Y_test = Y_test_orig.T

print ("number of training examples = " + str(X_train.shape[0]))
print ("number of test examples = " + str(X_test.shape[0]))
print ("X_train shape: " + str(X_train.shape))
print ("Y_train shape: " + str(Y_train.shape))
print ("X_test shape: " + str(X_test.shape))
print ("Y_test shape: " + str(Y_test.shape))

#数据集的细节如下:

#图像维度:(64,64,3)
#训练集数量:600
#测试集数量:150

-------1.2使用Keras框架构建模型--------

def model(input_shape):
	'''
	模型大纲
	'''
	#定义一个tensor的placeholder,维度为input_shape
		X_input = Input(input_shape)#创建一个向量,其第一个参数为维度

	#使用0填充:X_input的周围填充0
	X = ZeroPadding2D((3,3))(X_input)#zeropadding层,参数是填充的维度, 第一个是行填充,第二个是列填充,表示行填充上下三行,列填充左右三行,维度变为(X_input+a[0]*2,X_input + a[1]*2)
	
	#对X使用CONV-> BN -> RELU 块 ,,相当于完整一层!!
	X = Conv2D(32,(7,7),strides = (1,1),name = 'conv0')(X)#卷积层,第一个为过滤器个数,第二个为单个过滤器大小,第三个为过滤器步长,第四个为过滤器名字。
	X = BatchNormalization(axis = 3,name = 'bn0')(X)#batch归一化层,可以使函数拥有统一的均值和标准差,axis = 特征轴。
	X = Activation('relu')(X)#激活函数。

	#最大池化层
	X = MaxPooling2D((2,2),name = 'max_pool')(X)#第二层,池化层,池化格子为2 x 2

	#降维,矩阵转化为向量 + 全连接层
	X = Flatten()(X) #卷积层和向量层间的交互方式,将卷积层的数据格式转化为全连接层的数据格式。
	X = Dense(1,activation = 'sigmoid',name = 'fc')(X)#常用的全连接层,所实现的运算是output = activation(dot(input,kernel)+bias)

	#创建模型,将会创建一个模型的实体,我们可以用它来训练、测试。
	model = Model(inputs = X_input,outputs = X,name = 'HappyModel')

	return model

'''
回顾整个创建模型的过程:
创建模型其实无非就是:
[卷积层(ZeroPadding -> Conv2D -> BatchNormalization -> Activation,卷积,归一化再激活) -> 池化层(MaxPooling2D or MeanPooling2D,取最大值或均值的层,会缩放数据)] -> Flatten(平坦化数据,卷积层数据转换成全连接层) -> Dense(创建全连接层的函数,output = activation(dot(input,kernel) + bias))
-> 当Dense为一维后,创建Model(inputs = 原始输入,outputs = 最后一个Dense层的输出,name = 'mingzi')

现在我们已经设计好了我们的模型了,要训练并测试模型我们需要这么做:

  1. 创建一个模型实体。#这里创建的模型是没有输入的,真正的输入输入是在训练模型,而创建model仅仅需要创建出一个神经网络的结构
  2. 编译模型,可以使用这个语句:model.compile(optimizer = '...' #优化方法#,loss == '...'#损失函数#,metrics = ['accuracy'])。
  3. 训练模型:model.fit(x = ...,y = ...,epochs = ...,batch_size = ...)。
  4. 评估模型:model.evaluate(x = ...,y = ...)
 #创建模型
 happy_model = HappyModel(X_train.shape[1:])
 
 #编译模型
 happy_model.compile('adam','binary_crossentropy',metrics = ['accuracy'])

 #训练模型
 happy_model.fit(X_train,Y_train,epochs = 40,batch_size = 50)

 #评估模型
 preds = happy_model.evaluate(X_test,Y_test,batch_size = 32,verbose = 1,sample_weight = None)
 print('误差值 = ' + str(preds[0]))
 print('准确度 = ' + str(preds[1]))
  • 你可以在每个块后面使用最大值池化层,它将会减少宽、高的维度。
  • 改变优化器,这里我们使用的是Adam
  • 如果模型难以运行,并且遇到了内存不够的问题,那么就降低batch_size(12通常是一个很好的折中方案)
  • 运行更多代,直到看到有良好效果的时候。

#测试你的图片

 img_path = 'images/smile.jpeg #路径
 
 img = image.load_img(img_path,target_size = (64,64))#加载图标,并按尺寸输出
 imshow(img)#显示图片
 
 x = image.img_to_array(img)#将图片转换为向量矩阵
 x = np.expand_dims(x,axis = 0)#expand_dims用于给函数添加维度,相当与在x前添加一个维度,将x变为3维向量,其实就是添加一个m(数量)的维度
 x = preprocess_input(x)

 print(happy_model.predict(x))

---------1.5其他功能-----------

happy_model.summary() #打印出你的每一层的大小细节
#这一步是绘制图,需要用到Graphviz,安装可参考我前面的文献。
%matplotlib inline
plot_model(happy_model,to_file = 'happy_model.png')
SVG(model_to_dot(happy_model).create(prog = 'dot',format = 'svg'))

----------2.残差网络的搭建----------
这里我们将学习怎样使用残差网络搭建一个非常深的卷积网络,理论上越深的网络越能够实现复杂的功能,但是在实际上却非常难以训练。‘残差网络’就是为了解决网络的难以训练的问题的。
在下面这一步中,我们将:

  • 实现基本的残差块。
  • 将这些残差块放在一起,实现并训练用于图像分类的神经网络。

本次试验将使用‘Keras框架’
导入库函数:

import numpy as np
import tensorflow as tf

from keras import layers
from keras.layers import Input,Add,Dense,Activation,ZeroPadding2D,BatchNormalization,Flatten,Conv2D,AveragePooling2D,MaxPooling2D,GlobaMaxPooling2D
from keras.models import Model,load_model
from keras.preprocessing import image
from keras.utils import layer_utils
from keras.utils.data_utils import get_file
from keras.applications.imagenet_utils import preprocess_input
from keras.utils.vis_utils import model_to_dot
from keras.utils import plot_model
from keras.initializers import glorot_uniform

import pydot
from IPython.display import SVG
import scipy.misc
from matplotlib.pyplot import imshow
import keras.backend as K
K.set_image_data_format('channels_last')
K.set_learning_phase(1)

import resnets_utils

-----------------2.1深层网络的麻烦-----------------
使用深层网络最大的好处就是它能够完成很复杂的功能,它能够从边缘(浅层)到非常复杂的特征(深层)中不同的抽象层次的特征中学习。然而,使用比较深的网络通常没有什么好处,会造成梯度消失(非常深的网络通常会有一个梯度信号,该信号会迅速的消退,从而使得梯度下降变得非常缓慢。更具体的说,在梯度下降的过程中,当你从最后一层回到第一岑过的时候,你在每个步骤上乘以权重矩阵,梯度值可以迅速的指数式地减少到0),极少数的情况下会迅速增长,造成梯度爆炸。
在训练过程中,你可能会看到开始几层梯度的大小(或范数)迅速下降到0,如下图:
在前几层随着迭代次数的增加,学习的速度会下降的非常快。

-----------------2.2构建一个残差网络-----------------
在残差网络中,一个’捷径(shortcut)‘或者说’跳跃连接(skip connection)’ 允许梯度直接反向传播到更浅的层。

-----2.2.1恒等块-----
恒等块是残差网络使用的标准块,对应于输入的激活值(比如a[l]) 与输出激活值(比如a[l+1])具有相同的维度,为了具象化残差块的不同步骤,我们来看看下面的图吧。
在实践中,我们要做一个跳跃连接会跳过3个隐藏层而不是两个。
每个步骤如下:
1.主路径的第一部分:
1.第一个Conv2D有F1个过滤器,其大小为(1,1),步长为(1,1),使用填充方式为‘valid’(不填充),命名规则为:conv_name_base+'2a' 使用0作为随机种子为其初始化。 #这里的conv_name_base的意思是第几个残差块,也就是残差块的名字。
2.第一个BatchNorm是通道的轴归一化,其命名规则为bn_name_base + '2a'
3.接着使用ReLU函数,它没有命名也咩有超参数
2.主路径的第二部分:
1.第二个Conv2D有F2个过滤器,其大小为(f,f),步长为(1,1),使用填充方式为’same’,命名规则为conv_name_base + '2b',使用0作为随机种子为其初始化。
2.第二个BatchNorm是通道的轴归一化,其命名规则为bn_name_base + '2b'
3.接着使用ReLU激活函数,它没有命名也没有超参数。
3.主路径的第三部分:
1.第三个Conv2D有F3个过滤器,其大小为(1,1),步长为(1,1),使用填充方式为’valid’,命名规则为conv_name_base + '2c' ,使用0作为随机种子为其初始化。
2.第三个BatchNorm是通道的轴归一化,其命名规则为bn_name_base + '2c'.
3.注意这里没有ReLU函数
4.最后一步:
1.将捷径与输入加在一起。
2.使用ReLU激活函数,它没有命名也没有超参数。

#其实上述就是卷积(1,1) – 卷积(f,f) – 卷积(1,1)(在激活前与第一个卷积连接)

def identity_block(X,f,filters,stage,block):
	#命名规则
	conv_name_base = 'res' + str(stage) + block + '_branch'
	bn_name_base = 'bn' + str(stage) + block + '_branch'
	F1,F2,F3 = filters # 填充层

	X_shortcut = X #捷径其实就是最初的输入
	
	#主路径的第一部分
	##卷积层
	X = Conv2D(filters = F1,kernel_size = (1,1),strides = (1,1),padding = 'valid',name = conv_name_base + '2a',kernel_initializer = glorot_uniform(seed = 0))(X)
	##归一化
	X = BatchNormalization(axis = 3,name = bn_name_base + '2a')(X)
	##激活函数ReLU
	X = Activation('relu')(X)

	#主路径的第二部分
	##卷积层
	X = Conv2D(filters = F2,kernel_size = (f,f),strides = (1,1),padding = 'same',name = conv_name_base + '2b',kernel_initializer = glorot_uniform(seed = 0))(X)
	##归一化层
	X = BatchNormalization(axis = 3,name = bn_names_base + '2b')(X)
	X = Activation('relu')(X)

	#主路径的第三部分
	##卷积层
	X = Conv2D(filters = F3,kernel_size = (1,1),strides = (1,1),padding = 'valid',name = conv_name_base + '2c',kernel_initializer = glorot_uniform(seed = 0))(X)
	##归一化层
	X = BatchNormalization(axis = 3,name = bn_name_base + '2c')(X)
	##先不要Relu

	#最后一步
	##将捷径与输入加在一起
	X = Add()([X,X_shortcut])
	##使用ReLU激活函数
	X = Activation('relu')(X)

	return X

测试码:

tf.reset_default_graph()#清除系统已经生成的全部空间,#with函数每次执行都会生成空间使用上面这个函数可以全部清零,就无内存消耗了。
with tf.Session() as test:
	np.random.seed(1)
	A_prev = tf.placeholder('float',[3,4,4,6])
	X = np.random.randn(3,4,4,6)
	A = identity_block(A_prev,f=2,filters = [2,4,6],stage =1,block = 'a')

	test.run(tf.global_variables_initializer())
	out = test.run([A],feed_dict = {A_prev:X,K.learning_phase():0})
	print('out = ' + str(out[0][1][1][0]))
	
	test.close()

---------2.2.2卷积块---------
我们已经实现了残差网络的恒等块,现在,残差网络的卷积块是另一种类型的残差块,它适用于输入输出的维度不一致的情况,它不同于上面的恒等块,与之区别在于,捷径中有一个CONV2D层。
捷径中的卷积层将把输入x卷积为不同的维度,因此在主路径最后那里需要适配捷径中的维度。比如:把激活值中的宽高减少两倍,我们可以使用1*1,步伐为2。捷径上的卷积层不适用任何非线性激活函数,它的主要作用是仅仅应用(学习后的)线性函数来减少输入的维度,以便在后面的加法步骤中的维度相匹配。
每个步骤如下:
1.主路径的第一部分:
1.第一个Conv2D有F1个过滤器,其大小为(1,1),步长为(1,1),使用填充方式为‘valid’(不填充),命名规则为:conv_name_base+'2a' 使用0作为随机种子为其初始化。 #这里的conv_name_base的意思是第几个残差块,也就是残差块的名字。
2.第一个BatchNorm是通道的轴归一化,其命名规则为bn_name_base + '2a'
3.接着使用ReLU函数,它没有命名也咩有超参数
2.主路径的第二部分:
1.第二个Conv2D有F2个过滤器,其大小为(f,f),步长为(1,1),使用填充方式为’same’,命名规则为conv_name_base + '2b',使用0作为随机种子为其初始化。
2.第二个BatchNorm是通道的轴归一化,其命名规则为bn_name_base + '2b'
3.接着使用ReLU激活函数,它没有命名也没有超参数。
3.主路径的第三部分:
1.第三个Conv2D有F3个过滤器,其大小为(1,1),步长为(1,1),使用填充方式为’valid’,命名规则为conv_name_base + '2c' ,使用0作为随机种子为其初始化。
2.第三个BatchNorm是通道的轴归一化,其命名规则为bn_name_base + '2c'.
3.注意这里没有ReLU函数
4.捷径:
1.此卷积层有F3个过滤器,其维度为(1,1),步伐为(s,s),使用‘valid’的填充方式,命名规则为conv_name_base + '1'
2.此规范层是通道的轴归一化,其命名规则是bn_name_base + '1'
5.最后一步:
1.将捷径与输入加在一起
2.使用ReLU激活函数

def convolutional_block(X,f,filters,stage,block,s=2):
	#定义命名规则
	conv_name_base = 'res' + str(stage) + block + '_branch'
	bn_name_base = 'bn' + str(stage) + block + '_branch'

	#获取过滤器数量
	F1,F2,F3 = filters

	#保存输入数据
	X_shortcut = X

	#主路径
	##主路径第一部分
	X = Conv2D(filters = F1,kernel_size = (1,1),strides = (s,s),padding = 'valid',name = conv_name_base + '2a',kernel_initializer = glorot_uniform(seed = 0))(X)
	X = BatchNormalization(axis = 3,name = bn_name_base + '2a',kernel_initializer = glorot_uniform(seed = 0))(X)
	X = Activation('relu')(X)
	
	##主路径第二部分
	X = Conv2D(filters = F2,kernel_size = (f,f),strides = (1,1),padding = 'same',name = conv_name_base + '2b',kernel_initializer = glorot_uniform(seed = 0))(X)
	X = BatchNormalization(axis = 3,name = bn_name_base + '2b')(X)
	X = Activation('relu')(X)

	##主路径第三部分
	X = Conv2D(filters = F3,kernel_size = (1,1),strides = (1,1),padding = 'valid',name = conv_name_base + '2c',kernel_initializer = glorot_uniform(seed = 0))(X)
	X = BatchNormalization(axis = 3,name = bn_name_base + '2b')(X)
	
	##捷径
	X_shortcut = Conv2D(filters = F3,kernel_size = (1,1),strides = (s,s),padding = 'valid',name = conv_name_base + '1')(X_shortcut)
	X_shortcut = BatchNormalization(axis = 3,name = bn_name_base + '1')(X_shortcut)
	
	#最后一步
	X = Add()([X,X_shortcut])
	X = Activation('relu')(X)

	return X

测试码

tf.reset_default_graph()

with tf.Session() as test:
	np.random.seed(1)
	A_prev = tf.placeholder('float',[3,4,4,6])
	X = np.random.randn(3,4,4,6)

	A = convolutional_block(A_prev,f=2,filters = [2,4,6],stage = 1,block = 'a')
	test.run(tf.global_variables_initializer())
	
	out = test.run([A],feed_dict = {A_prev:X,K.learning_phase():0})
	print('out = ' + str(out[0][1][1][0]))

	test.close()
def ResNet50(input_shape = (64,64,3),classes = 6):
	'''
	实现ResNet50
	Conv2D -> BatchNorm -> Relu -> Maxpool -> convblock -> IDblock*3 -> CONVBLCK -> IDBLOCK*5 -> CONVBLOCK -> IDBLOCK*2 -> AVGPOOL -> TOPLAYER
	'''
	X_input = Input(input_shape)
	X = ZeroPadding((3,3))(X_input)

	#stage1
	X = Conv2D(filters = 64,kernel_size = (7,7),strides = (2,2),name = 'conv1',kernel_initializer = glorot_uniform(seed = 0))(X)
	X = BatchNormalization(axis = 3,name = 'bn_conv1')(X)
	X = Activation('relu')(X)
	X = MaxPooling2D(pool_size = (3,3),strides = (2,2))(X)

	#stage2
	X = convolutional_block(X,f=3,filters = [64,64,256],stage = 2,block = 'a',s = 1)
	X = identity_block(X,f=3,filters = [64,64,256],stage = 2,block = 'b')
	X = identity_block(X,f=3,filters = [64,64,256],stage = 2,block = 'c')
	X = identity_block(X,f=3,filters = [64,64,256],stage = 2,block = 'd')

	#stage3
	X = convolutional_block(X,f=3,filters = [128,128,512],stage = 3,block = 'a',s = 2)
	X = identity_block(X,f=3,filters = [128,128,512],stage = 3,block = 'b')
	X = identity_blcok(X,f=3,filters = [128,128,512],stage = 3,block = 'c')
	X = identity_block(X,f=3,filters = [128,128,512],stage = 3,block = 'd')
	
	#stage4
	X = convolutional_block(X,f=3,filters = [256,256,1024],stage = 4,block = 'a',s = 2)
	X = identity_block(X,f=3,filters = [256,256,1024],stage = 4,block = 'b')
	X = identity_block(X,f=3,filters = [256,256,1024],stage = 4,block = 'c')
	X = identity_block(X,f=3,filters = [256,256,1024],stage = 4,block = 'd')
	X = identity_block(X,f=3,filters = [256,256,1024],stage = 4,block = 'e')
	X = identity_block(X,f=3,filters = [256,256,1024],stage = 4,block = 'f')

	#stage5
	X = convolutional_block(X,f=3,filters = [512,512,2048],stage = 5,block = 'a',s = 2)
	X = identity_block(X,f=3,filters = [512,512,2048],stage = 5,block = 'b')
	X = identity_block(X,f=3,filters = [512,512,2048],stage = 5,block = 'c')

	#均值池化层
	X = AveragePooling2D(pool_size = (2,2),padding = 'same')(X)

	#输出层
	X = Flatten()(X)
	X = Dense(classes,activation = 'softmax',name = 'fc' + str(classes),kernel_initializer = glorot_uniform(seed = 0))(X)

	#创建模型
	model = Model(inputs = X_input,outputs = X,name = 'ResNet50')
	return model

模型实体化和编译工作:

model = ResNet50(input_shape = (64,64,3),classes = 6)
model.compile(optimizer = 'adam',loss = 'categorical_crossentropy',metrics = ['accuracy'])

加载训练集进行训练:

X_train_orig,Y_train_orig,X_test_orig,Y_test_orig,classes = resnets_utils.load_dataset()

X_train = X_train_orig / 255.
X_test = X_test_orig / 255.

Y_train = resnets_utils.convert_to_one_hot(Y_train_orig,6).T
Y_test = resnets_utils.convet_to_one_hot(Y_test_orig,6).T
print("number of training examples = " + str(X_train.shape[0]))
print("number of test examples = " + str(X_test.shape[0]))
print("X_train shape: " + str(X_train.shape))
print("Y_train shape: " + str(Y_train.shape))
print("X_test shape: " + str(X_test.shape))
print("Y_test shape: " + str(Y_test.shape))

评估模型:

preds = model.evaluate(X_test,Y_test)

print('误差值 = ' + str(preds[0]))
print('准确率 = ' + str(preds[1]))

------------2.4使用自己的图片做测试------------

from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

%matplotlib inline

img_path = 'images/fingers_big/2.jpg'
my_image = image.load_img(img_path,target_size = (64,64))
my_image = image.img_to_array(my_image)

my_image = np.expand_dims(my_image,axis = 0)
my_image = preprocess_input(my_image)

print('my_image.shape = ' + str(my_image.shape))

print('class prediction vector [p(0), p(1), p(2), p(3), p(4), p(5)] = ')
print(model.predict(my_image))

my_image = scipy.misc.imread(img_path)
plt.imshow(my_image)
model.summary()
plot_model(model,to_file = 'model.png')
SVG(model_to_dot(model).create(prog = 'dot',format = 'svg'))
发布了31 篇原创文章 · 获赞 0 · 访问量 676

猜你喜欢

转载自blog.csdn.net/ballzy/article/details/105447134