这是吴恩达老师第四课第一周的编程练习,题目是分析图片中手势得到手所表示的数字。
数据集我传到github上,可以下载https://github.com/penguin219/WU_Lesson4_week1
特别要注意的是,如果你使用的是新版本的tensorflow,很有可能得到的结果和Coursera上的有所不同!
1. 先简单介绍一个CNN(直接看代码的请往下翻)
CNN全程是Convolutional Neural Networks,它更加适用于computer vision类的应用,这是因为CNN处理图像的效果要比神经网络的处理效果更好,CNN有三个基本组成部分,卷积层,池化层和全连接层。
1.1 卷积层
卷积层中有个很重要的概念,什么是卷积?理解了卷积对于池化层也会很好理解。为了方便说明,举个简单的例子。假设现在有一张(6*6)的灰度图像,要对它进行卷积操作,需要用到过滤器 Filter,这里我们用(3*3)的过滤器对这张灰度图像进行卷积,因为它们都是矩阵,所以记 A(6*6),B(3*3)
首先将(3*3)叠放在(6*6)的图像上,也就是A[0][0] 与B[0][0]是对应的,A[2][2] 与 B[2][2]对应,这里手画一下会更好理解,下一步要做的事情就是把对应位置的数字相乘,将得到的9个乘积再相加得到和,这里用sum1记录下来。
接着把B向右移动固定的单位,移动的单位数目,就称之为 步幅 Stride,我们将步幅设置为1,也就是向右移动1个单位,那么现在 A[0][1]对应B[0][0],继续上面的操作,对应位置相乘再相加得到和,记为sum2。
不难看出,B只能移动三次,加上初始位置得到的结果,一共得到4个和值,这四个值就作为完成卷积操作以后得到的矩阵的第一行,那么推测一下,第二行形式也是类似的,计算第二行时,B[0][0]对应的值就是A[1][0],然后再向右移动,得到第二行的所有值。B也只能向下移动三次,加上初始位置,也就是一共有4次,那么最后的结果就是一个(4*4)的矩阵。
这样就完成了一次卷积操作。
总结一下,一次卷积操作需要用到 Filter,也就是后面文中提到的参数,设置Stride,关于Filter再多说几句,一般来说,filter的选择可以是固定的,比如说很有名的sobel filter,scharr filter,不同的过滤器可以提取不同的特征,那么我们也可以将filter设置为随机的参数,通过喂给模型数据来训练这些参数,使得参数更好的符合我们的需求。
完成卷积的操作会使得图片缩小,并且存在一个问题,就是边缘的 像素点只会进行一次卷积操作,而中间的会进行很多次,就会在一定程度上降低边界信息的参考程度。解决办法就是padding,padding的意思是填充,在原图像的周围填充像素点,设置值为0,这样多添加的点也不会对结果造成影响。padding有两种形式,第一是Valid卷积,也就是不添加,p为0,第二种是SAME卷积,意思是卷积后的结果和原始图像尺寸是一样的,p=(f-1)/ 2。
最后要说的是,原始图像大小如果为(n*n),过滤器大小为(f*f),stride的值为s,padding的值为p
那么卷积的结果为(m*m)其中 m=(n+2p-f)/s +1 ,另m = n 就能解出上面p的值了
上面举的例子是灰度图像,是二维的,而我们一般使用的都是RGB图像,是三维。原始图像是几维对应的filter也是几维的,而filter的个数决定了卷积后得到图像的维度,保持一致!
1.2 池化层
池化层可对提取到的特征信息进行降维,使特征图变小,进行特征压缩,提取主要特征,简化网络计算复杂度并在一定程度上避免过拟合的出现。
池化层和卷积层非常类似,区别只在于,卷积层进行的是卷积操作(也就是相乘再相加)而池化层进行的是其他操作。
池化层有两种,第一种也是最常用的一种就是MAX Pooling,顾名思义,它进行的操作就是获取原图像中选取部分的最大值;
还有一种是Average Pool,计算选取部分的平均值。至于选取部分的大小也是由参数(x * x)决定的,但这里不是过滤器!
类似的池化层也有stride和padding的设置。
在tensorflow中关于padding的两种形式:https://blog.csdn.net/wuzqchom/article/details/74785643
1.3全连接层
其实全连接层和神经网络是很类似的,将最后一层的池化层得到的结果,全部进行flatten操作,也就是都合并成一个列向量,和后面的处理单元全部连接起来,直到最后一层网络,如果是多分类问题,就是连接到softmax。
2.CNN示例图
根据课程作业所用的CNN网络画出的示意图:
3.代码
导包:
import math
import numpy as np
import h5py
import matplotlib.pyplot as plt
import tensorflow as tf
获取数据:
#加载数据
def load_dataset():
train_dataset = h5py.File('d:/jupyProject/datasets/train_signs.h5','r')
#可以看看读取到的文件中包含的key值 ['list_classes', 'train_set_x', 'train_set_y']
#print(list(train_dataset.keys()))
train_x = np.array(train_dataset['train_set_x'][:]) #(1080, 64, 64, 3)
train_y = np.array(train_dataset['train_set_y'][:])
test_dataset = h5py.File('d:\jupyProject/datasets/test_signs.h5','r')
test_x = np.array(test_dataset['test_set_x'][:])
test_y = np.array(test_dataset['test_set_y'][:])
classes = np.array(test_dataset['list_classes'][:])
train_y = train_y.reshape(train_y.shape[0],1) #(1080,1)
test_y = test_y.reshape(test_y.shape[0],1)
#将上面的y集改为one-hot形式
label_num = len(classes)
train_y = np.eye(label_num)[train_y.reshape(-1)] #(1080, 6)
test_y = np.eye(label_num)[test_y.reshape(-1)]
train_x = train_x/255.
test_x = test_x/255.
return train_x,train_y,test_x,test_y,classes
占位符:
#新建占位符
def create_placeholders(n_H0,n_W0,n_C0,n_y):
X = tf.placeholder(shape=[None,n_H0,n_W0,n_C0],dtype = tf.float32)
Y = tf.placeholder(shape=[None,n_y],dtype = tf.float32)
return X,Y
初始化参数,也就是filter:
#初始化过滤器 这里用w[f,f,channels,numbers]来表示
def initialize_parameters():
tf.set_random_seed(1)
W1 = tf.get_variable('W1',[4,4,3,8],initializer = tf.contrib.layers.xavier_initializer(seed = 0))
W2 = tf.get_variable('W2',[2,2,8,16],initializer = tf.contrib.layers.xavier_initializer(seed = 0))
parameters = {
'W1':W1,
'W2':W2
}
return parameters
向前传播:
def forward_propagation(X,parameters):
W1 = parameters['W1']
W2 = parameters['W2']
#strides 表示对X的每个维度的步幅
Z1 = tf.nn.conv2d(X,W1,strides=[1,1,1,1],padding='SAME')
A1 = tf.nn.relu(Z1)
#ksize [1,f,f,1]表示用f*f大小的pool
P1 = tf.nn.max_pool(A1,ksize=[1,8,8,1],strides=[1,8,8,1],padding='SAME')
Z2 = tf.nn.conv2d(P1,W2,strides=[1,1,1,1],padding='SAME')
A2 = tf.nn.relu(Z2)
P2 = tf.nn.max_pool(A2,ksize=[1,4,4,1],strides=[1,4,4,1],padding='SAME')
P2 = tf.contrib.layers.flatten(P2)
#全连接会自动初始化参数并训练,所以不需要提前初始化参数
#注意不要在这里调用softmax函数 ,tf的sofmax函数和cost计算连接在一起
Z3 = tf.contrib.layers.fully_connected(P2,num_outputs=6,activation_fn=None)
return Z3
计算代价:
def compute_cost(Z3,Y):
cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(logits=Z3,labels=Y))
return cost
在每次迭代时获取不一样的batch数据,保证了每次迭代的数据组合顺序是不同的,更有利于算法的进步
def random_mini_batches(X, Y, mini_batch_size = 64, seed = 0):
"""
Creates a list of random minibatches from (X, Y)
Arguments:
X -- input data, of shape (input size, number of examples) (m, Hi, Wi, Ci)
Y -- true "label" vector (containing 0 if cat, 1 if non-cat), of shape (1, number of examples) (m, n_y)
mini_batch_size - size of the mini-batches, integer
seed -- this is only for the purpose of grading, so that you're "random minibatches are the same as ours.
Returns:
mini_batches -- list of synchronous (mini_batch_X, mini_batch_Y)
"""
m = X.shape[0] # number of training examples
mini_batches = []
np.random.seed(seed)
# Step 1: Shuffle (X, Y)
permutation = list(np.random.permutation(m))
shuffled_X = X[permutation,:,:,:]
shuffled_Y = Y[permutation,:]
# Step 2: Partition (shuffled_X, shuffled_Y). Minus the end case.
num_complete_minibatches = math.floor(m/mini_batch_size) # number of mini batches of size mini_batch_size in your partitionning
for k in range(0, num_complete_minibatches):
mini_batch_X = shuffled_X[k * mini_batch_size : k * mini_batch_size + mini_batch_size,:,:,:]
mini_batch_Y = shuffled_Y[k * mini_batch_size : k * mini_batch_size + mini_batch_size,:]
mini_batch = (mini_batch_X, mini_batch_Y)
mini_batches.append(mini_batch)
# Handling the end case (last mini-batch < mini_batch_size)
if m % mini_batch_size != 0:
mini_batch_X = shuffled_X[num_complete_minibatches * mini_batch_size : m,:,:,:]
mini_batch_Y = shuffled_Y[num_complete_minibatches * mini_batch_size : m,:]
mini_batch = (mini_batch_X, mini_batch_Y)
mini_batches.append(mini_batch)
return mini_batches
整合模型:
from tensorflow.python.framework import ops
def model(train_x,train_y,test_x,test_y,learning_rate = 0.0009,iteration=300,batch_size=64,print_cost=True):
# 返回模型无需重写参数
ops.reset_default_graph()
tf.set_random_seed(1)
costs = []
seed = 3
m,n_H0,n_W0,n_C0 = train_x.shape
n_y = train_y.shape[1]
X,Y = create_placeholders(n_H0,n_W0,n_C0,n_y)
parameters = initialize_parameters()
Z3 = forward_propagation(X,parameters)
cost = compute_cost(Z3,Y)
optimizer = tf.train.AdamOptimizer(learning_rate).minimize(cost)
with tf.Session() as sess:
sess.run(tf.global_variables_initializer())
for epoch in range(iteration):
batch_cost = 0.
batch_n = int(m/batch_size)
seed = seed+1
batches = random_mini_batches(train_x,train_y,batch_size,seed)
for batch in batches:
(mini_x,mini_y)=batch
_,temp_cost = sess.run([optimizer,cost],feed_dict={X:mini_x,Y:mini_y})
batch_cost += temp_cost / batch_n
if print_cost == True and epoch % 5 == 0:
print('Cost after epoch %i : %f' %(epoch,batch_cost))
if print_cost == True and epoch % 1 == 0:
costs.append(batch_cost)
#代价图像
plt.plot(np.squeeze(costs))
plt.ylabel('cost')
plt.xlabel('iterations per tens')
plt.show()
#做出预测
predict_op = tf.argmax(Z3,1)
correct_prediction = tf.equal(predict_op,tf.argmax(Y,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction,'float'))
train_accuracy = accuracy.eval({X:train_x,Y:train_y})
test_accuracy = accuracy.eval({X:test_x,Y:test_y})
print('train accuracy is :',train_accuracy)
print('test accuracy is :',test_accuracy)
model(train_x,train_y,test_x,test_y)
#结果
Cost after epoch 0 : 1.976589
Cost after epoch 5 : 1.895094
Cost after epoch 10 : 1.884952
Cost after epoch 15 : 1.862152
Cost after epoch 20 : 1.792553
Cost after epoch 25 : 1.659748
Cost after epoch 30 : 1.439652
Cost after epoch 35 : 1.210028
Cost after epoch 40 : 1.059912
Cost after epoch 45 : 0.950599
Cost after epoch 50 : 0.864589
Cost after epoch 55 : 0.779795
Cost after epoch 60 : 0.721491
Cost after epoch 65 : 0.659299
Cost after epoch 70 : 0.622481
Cost after epoch 75 : 0.587786
Cost after epoch 80 : 0.553721
Cost after epoch 85 : 0.515339
Cost after epoch 90 : 0.481323
Cost after epoch 95 : 0.464154
Cost after epoch 100 : 0.450780
Cost after epoch 105 : 0.419089
Cost after epoch 110 : 0.393920
Cost after epoch 115 : 0.381122
Cost after epoch 120 : 0.375946
Cost after epoch 125 : 0.361885
Cost after epoch 130 : 0.344810
Cost after epoch 135 : 0.328714
Cost after epoch 140 : 0.321064
Cost after epoch 145 : 0.299867
Cost after epoch 150 : 0.298625
Cost after epoch 155 : 0.284655
Cost after epoch 160 : 0.269399
Cost after epoch 165 : 0.264294
Cost after epoch 170 : 0.269712
Cost after epoch 175 : 0.267915
Cost after epoch 180 : 0.242613
Cost after epoch 185 : 0.228068
Cost after epoch 190 : 0.218832
Cost after epoch 195 : 0.213510
Cost after epoch 200 : 0.205735
Cost after epoch 205 : 0.200092
Cost after epoch 210 : 0.196989
Cost after epoch 215 : 0.187289
Cost after epoch 220 : 0.181605
Cost after epoch 225 : 0.186336
Cost after epoch 230 : 0.177865
Cost after epoch 235 : 0.174295
Cost after epoch 240 : 0.165915
Cost after epoch 245 : 0.168665
Cost after epoch 250 : 0.151124
Cost after epoch 255 : 0.157855
Cost after epoch 260 : 0.157312
Cost after epoch 265 : 0.150373
Cost after epoch 270 : 0.148940
Cost after epoch 275 : 0.139157
Cost after epoch 280 : 0.136293
Cost after epoch 285 : 0.127223
Cost after epoch 290 : 0.132367
Cost after epoch 295 : 0.118375
train accuracy is : 0.9759259
test accuracy is : 0.89166665