python应用——keras深度学习
keras学习笔记与具体实现中的python应用
本博客将持续更新!!!
环境准备
最近开始学习keras来写深度学习算法,同时也是为了自己的实习测试做准备hhhhhh
所需要的工具:Windows系统、python3.5~3.8的版本、tensorflow、anaconda、cuda、cudnn、keras(必须先安装tensorflow再安装keras)
这里面各种软件的安装非常的麻烦并且费时费力,我自己安装的时候花了五天时间,中间无数次重装、下载、出错,并且网上也没有一个放之四海而皆准的安装指南,我认为比较系统教授安装流程的是B站上的DeepLizard关于keras算法的教程,里面有一集专门教你怎么下载这些软件,更重要的是怎么配置GPU运行,直接上B站搜索即可。
一、对于数据的预处理
对数据的预处理是深度学习过程中很重要的一环,数据处理做的不到位的话很有可能使得后期的学习过程出现很多不准确的结果,本节将以一个例子来初步展现如何预处理数据。
1 import numpy as np
2 from random import randint
3 from sklearn.utils import shuffle
4 from sklearn.preprocessing import MinMaxScaler
首先引入各种必须的库和库函数,本文涉及到的库和库函数在我的其他博客上都有详细解释,不明白的话请移步。
1 train_lables=[],train_samples=[]
设置训练样本和训练样本的标签。
故事的背景是这样的:
1050个年轻人和1050个老年人分别被注射了一种试验药物 ( 以65岁为分界 ) ,95%的老人出现了副作用,5%的年轻人出现了副作用,其他人均无副作用。
1 for i in range(50):
2 random_younger=randint(13,64)
3 train_samples.append(random_younger)
4 train_lables.append(1)
5
6 random_older=randint(65,100)
7 train_samples.append(random_older)
8 train_lables.append(0)
9 for i in range(1000):
10 random_younger=randint(13,64)
11 train_samples.append(random_younger)
12 train_lables.append(0)
13
14 random_older=randint(65,100)
15 train_samples.append(random_older)
16 train_lables.append(1)
生成训练样本和其标签
第一个for循环分别形成了50个年轻人和老年人的数据,第二个for循环分别形成了1000个年轻人和老年人的数据。range(start,end.step)函数表示从起始点到终止点每过一个步长取一个数字的过程,range(50)=range(0,50,1),相当于进行了50次循环。
注意,在本部分代码进行之前, train_lables=[],train_samples=[]都只是空列表,
train_samples.append(random_younger)
此语句是向列表中添加,random_younger这个元素。
在代码进行完成之后,两个列表中的元素都是特殊的5%群体在前面,95%群体在后面,这样的顺序显然是不自然的,因此使用shuffle函数打乱。
1 train_lables=np.array(train_lables)
2 train_samples=np.array(train_samples)
3 train_lables,train_samples=shuffle(train_lables,train_samples)
先用了np.array()函数转化为numpy.array形式,因为keras处理的数组和矩阵一般都是numpy形式。
1 scaler=MinMaxScaler(feature_range=(0,1))
2 scaled_train_samples=scaler.fit_transform(train_samples.reshape(-1, 1))
这里使用了MinMaxScaler函数将train_samples[]进行归一化。注意强调的部分,如果没有这部分reshape将会报错,因为MinMaxScaler函数期望的是一个二维数组,而我们输入了一个一维数组,所以用这种方式转换为二维数组。此处只是固定搭配而已,背下来即可。
最后打印验证:
1 for i in scaled_train_samples:
2 print(i)
运行结果:
[0.57471264] [0.62068966] [0.67816092] [0.50574713] [0.1954023] [0.70114943] [0.81609195] [0.36781609] [0.2183908] [0.95402299] [0.83908046] [0.09195402] [0.71264368] [0.72413793] [0.36781609] [0.22988506]
............
1 for i in train_samples:
2 print(i)
运行结果:
55 81 27 74 47 88 24 67 53
......
1 for i in train_lables:
2 print(i)
运行结果:
1
0
1
0
1
0
1
0
......
二、神经网络的建构
接下来是一个神经网络建构的简单实例,一起来看一下代码:
1 import tensorflow as tf
2 from tensorflow import keras
3 from tensorflow.keras.models import Sequential
4 from tensorflow.keras.layers import Activation,Dense
5 from tensorflow.keras.optimizers import Adam
6 from tensorflow.keras.metrics import categorical_crossentropy
1 model=Sequential([
2 Dense(units=16,input_shape=(1,),activation='relu'),
3 Dense(units=32,activation='relu'),
4 Dense(units=2,activation='softmax')
5 ])
2.1Sequectial model
Sequectial model是一个十分常用的神经网络模型,中文名叫顺序模型,除了此模型keras还提供许多其他的模型,可以在keras官网上搜索到。
2.2Activation和Dense
Dense是keras提供的一种常用的layer,“Dense(units=32,activation='relu')”这句代码的意思是这一个layer中的神经节点类型都是Dense类型,这一层共有32个节点,使用的activation也就是激活函数名称是relu。
activation是激活函数。在一个神经元在接收到信息之后,将使用activation来处理前置信息,将处理之后的信息向下一个层进行传递。
需要特别注意的是这一句代码“input_shape=(1,)” 这表明了接受层接受的数据规模是什么。什么叫做数据规模呢?要知道keras的神经网络要接收的信息都是以维数不同的数组形式接收的,数组的维数就是规模,比如说[1,2,3]该数组的规模就是(3,);[[1,2,3],[3,4,5]]的规模就是(2,3)[[[1,2],[2,3]],[[3,4],[4,5]]]的规模就是(2,2,2)。
三、神经网络模型的编译和运行
终于到了最激动人心的时刻了,在这一节,我们将会运行我们自己的第一个简单的神经网络模型,那么它会成功吗?先来看代码。
1 model.compile(optimizer=Adam(lr=0.001),loss='sparse_categorical_crossentropy',metrics='accuracy')
2 model.fit(x=scaled_train_samples,y=train_lables,validation_split=0.2,batch_size=10,epochs=30,verbose=2)
是的,本节只有这短短的两行代码,但是每行代码里面都包含了好几个我们所说的hyper parameter,中文叫超参数,这些参数决定了我们训练神经网络的速度、质量以及是否会出现overfitting和underfitting。超参数对于神经网络具体的影响我们会在以后的章节中涉及到,这节我们先简要的介绍一下hyper parameter。
3.1optimizer
optimizer中文名叫做优化器,优化器的作用就是在神经网络不断训练的过程中改变网络本身使其达到预测的结果与实际的结果差距越来越小的目的。SGD是一种常见的optimizer,这里我们用的Adam就是SGD的一种变形。
3.2learning rate(lr)
learning rate中文名叫做学习率,它一般是一个在0.001到0.01之间的float型数据。有一个形象的比喻是说,如果神经网络是一个人,我们要达到的最佳点就是这个人要到达的目的地,optomizer决定了这个人行走的方向,learning rate就是这个人的步伐大小。步伐较大时,到达最佳点附近的时间会变短,但是我们可能会越过最佳点,误差比较大。步伐较小时,虽然到达最佳点的时间会变长,但是相应的,误差也会变小。
3.3loss function
loss function中文名叫做损失函数,它的作用是计算每个样本通过神经网络之后产生的loss,即每个样本在神经网络中产生的结果与它们真实的结果也就是lable的差距。举个例子来说,如果我们想要我们的神经网络区分猫和狗的照片,神经网络会对每一个照片做一个判断,比如说照片A是猫的概率是75%狗的概率是25%;以1代表猫,0代表狗那么这张图片在神经网络中的结果就是1*0.75+0*0.25=0.75。但实际上,这张照片就是猫,它的lable是1,所以这张照片产生的loss就是1-0.75=0.25。
3.4batch_size和epochs
batch_size是一次传入神经网络的数据个数,而epochs是所有训练样本要传入神经网络的次数。
3.5validation_split
这里涉及到了validation set(测评组)的概念。我们知道,神经网络训练时使用的是training set,并通过自身矫正不断缩减与training lables的差距。同样的,validation set也会通过神经网络,只是神经网络只计算validation set中的loss值,而不会根据loss值调整自己。当validation的loss值趋于无限小时,我们就可以知道这个神经网络对于training set之外的数据也有了一定的普适性。如果training set的loss很小,但是validation set的loss很大就说明我们的模型只适用于training set而不具有普适性,即over-fitting过吻合。如果training set的loss很大则说明此模型根本没有生效,叫做under-fitting低吻合。
validation_split=0.2就是指在训练集中抽取20%的样本进行适当的变换之后作为评测集输入到神经网络中。
需要注意的一点是,validation_split选取的x%数据是训练集的后x%数据。这告诉我们什么呢?虽然每次在训练之前,训练集都会被fit函数shuffle,但是在fit函数的shuffle功能起作用之前validation_split就已经将一部分数据分离出来了,也就是说使用validation_split分离的评测集会取没有shuffle的数据,这也就是为什么我们要在模型compile之前手动shuffle,为的就是让评测集从已经shuffle过的测试集里面选取,避免出现一些奇怪的结果。
3.6其他参数
x,y分别是我们之前预处理过的samples和lables。verbose=2是每次训练必须设置的值,记住即可。metrics='accuracy'代表我们在训练过程中将以精准度作为标准,训练完成之后将会输出每一个epoch的精准度,供我们评估这个模型的优劣。
运行结果:
Epoch 1/30 168/168 - 0s - loss: 0.6313 - accuracy: 0.6161 - val_loss: 0.5559 - val_accuracy: 0.7714 Epoch 2/30 168/168 - 0s - loss: 0.4796 - accuracy: 0.8202 - val_loss: 0.4021 - val_accuracy: 0.8643 Epoch 3/30 168/168 - 0s - loss: 0.3640 - accuracy: 0.8964 - val_loss: 0.3043 - val_accuracy: 0.9238 Epoch 4/30 168/168 - 0s - loss: 0.3089 - accuracy: 0.9274 - val_loss: 0.2632 - val_accuracy: 0.9619 Epoch 5/30
.........
.........
.........
.........
Epoch 27/30 168/168 - 0s - loss: 0.2467 - accuracy: 0.9440 - val_loss: 0.2061 - val_accuracy: 0.9452 Epoch 28/30 168/168 - 0s - loss: 0.2463 - accuracy: 0.9387 - val_loss: 0.2061 - val_accuracy: 0.9452 Epoch 29/30 168/168 - 0s - loss: 0.2463 - accuracy: 0.9405 - val_loss: 0.2051 - val_accuracy: 0.9452 Epoch 30/30 168/168 - 0s - loss: 0.2444 - accuracy: 0.9405 - val_loss: 0.2056 - val_accuracy: 0.9452
可以看到,我们的loss逐渐减小,accurary逐渐增大,最终达到了94.05%的精确度,表明我们的神经网络初步成功。
四、神经网络用来预测
4.1关于test set
test set 中文名叫测试集(注意validation set 叫做评估集)顾名思义,test set 就是我们要应用神经网络来预测其结果的数据集。在生产实践中,一般来说test set 就只包括test_samples而不包括test_lables原因也很好理解,如果我都有了test_lables那我还用你预测干嘛?神经网络不就是用来预测 lables 的吗?但是,有一般就有特殊,在特殊情况下test set也可以包括test_lables。比如说我们自己生成一个测试集,想要用它看看神经网络好不好使,这时候就需要lables了,没有lables怎么确定你预测的准不准啊?当然,有人就要问了,这样的话test set和validation set有啥区别呀?validation set是在fit函数中使用的,作用是观察我们的网络是否过拟合或者劣拟合;而test set是在predication中使用的,作用是看我们预测的准不准。这就是二者的区别了。
4.2生成test set
这部分代码和我们在数据预处理里面的过程一样,不再展开。
1 test_lables=[]
2 test_samples=[]
3 for i in range(50):
4 random_younger=randint(13,64)
5 test_samples.append(random_younger)
6 test_lables.append(1)
7
8 random_older=randint(65,100)
9 test_samples.append(random_older)
10 test_lables.append(0)
11 for i in range(1000):
12 random_younger=randint(13,64)
13 test_samples.append(random_younger)
14 test_lables.append(0)
15
16 random_older=randint(65,100)
17 test_samples.append(random_older)
18 test_lables.append(1)
19 test_lables=np.array(test_lables)
20 test_samples=np.array(test_samples)
21 test_lables,test_samples=shuffle(test_lables,test_samples)
22 scaler=MinMaxScaler(feature_range=(0,1))
23 scaled_test_samples=scaler.fit_transform(test_samples.reshape(-1, 1))
4.3预测过程实现
我们已经生成了test_samples和test_lables下面开始进行预测,首先看代码:
1 predictions=model.predict(x=scaled_test_samples,batch_size=10,verbose=0)
2 for i in predictions:
3 print(i)
关于model.predict函数:
1 predict(
2 x, batch_size=None, verbose=0, steps=None, callbacks=None, max_queue_size=10,
3 workers=1, use_multiprocessing=False
4 )
5
6 参数:
7 x:输入样本,格式可以是
8 - Numpy数组(或类似array的数组)或数组列表(如果模型具有多个输入)。
9 - TensorFlow张量或张量列表(如果模型具有多个输入)。
10 - tf.data数据集。
11 - 生成器或keras.utils.Sequence实例
12
13 batch_size:每个梯度更新的样本数。如果未指定,batch_size将默认为32
14 verbose:模型
15 steps:宣布预测回合完成之前的步骤总数(样本批次)。忽略默认值None
16 callbacks:预测期间应用的回调函数列表
17
18 max_queue_size=10:
19 仅用于generator或keras.utils.Sequence输入。
20 生成器队列的最大大小。
21 如果未指定,max_queue_size将默认为10
22
23 workers=1:
24 仅用于generator或keras.utils.Sequence输入。
25 使用基于进程的线程时,要启动的最大进程数。
26 如果未指定,worker将默认为1。如果为0,将在主线程上执行生成器
27
28 use_multiprocessing=False:
29 仅用于generator或keras.utils.Sequence输入。
30 如果为True,则使用基于进程的线程。
31 如果未指定,则use_multiprocessing将默认为False。
不必全都看懂,只需要明白我们例子中表示出来的参数都是什么意思就可以了。
model.predict函数结束之后将会返回神经网络模型每个输出类别的概率,这里设置verbose为0是因为output里面实在没什么可看的数据,想看的话可以设置为1自己看一下。
运行结果:
[0.0107724 0.98922765] [0.03459526 0.9654047 ] [0.9481422 0.05185788] [0.00780787 0.9921921 ] [0.9561656 0.04383443]
......
......
......
[0.95432866 0.04567139]
[0.02271039 0.97728956]
[0.00629709 0.9937028 ]
[0.07852411 0.9214759 ]
[0.9542845 0.04571553]
我们可以看到,输出结果里面包含两列分别是第0列和第1列。前面我们默认的设置是0代表没有副作用;1代表有副作用,这里面第0列就代表了该名测试者没有副作用的概率;第1列代表了该名测试者有副作用的概率。
4.4预测结果最可能化
先看代码:
1 rounded_predictions=np.argmax(predictions,axis=-1)
2 for i in rounded_predictions:
3 print(i)
运行结果:
1 1 0 1 0
......
......
......
0
1
1
1
0
这表示各个患者最有可能的结果。
关于np.argmax()函数:
1.对于一个一维向量:
1 import numpy as np
2 a = np.array([3, 1, 2, 4, 6, 1])
3 b=np.argmax(a)#取出a中元素最大值所对应的索引,此时最大值位6,其对应的位置索引值为4,(索引值默认从0开始)
4 print(b)#4
2.对于二维向量:
1 import numpy as np
2 a = np.array([[1, 5, 5, 2],
3 [9, 6, 2, 8],
4 [3, 7, 9, 1]])
5 b=np.argmax(a, axis=0)#对二维矩阵来讲a[0][1]会有两个索引方向,第一个方向为a[0],默认按列方向搜索最大值
6 #a的第一列为1,9,3,最大值为9,所在位置为1,
7 #a的第一列为5,6,7,最大值为7,所在位置为2,
8 #此此类推,因为a有4列,所以得到的b为1行4列,
9 print(b)#[1 2 2 1]
10
11 c=np.argmax(a, axis=1)#现在按照a[0][1]中的a[1]方向,即行方向搜索最大值,
12 #a的第一行为1,5,5,2,最大值为5(虽然有2个5,但取第一个5所在的位置),索引值为1,
13 #a的第2行为9,6,2,8,最大值为9,索引值为0,
14 #因为a有3行,所以得到的c有3个值,即为1行3列
15 print(c)#[1 0 2]
3.对于三维向量:
1 import numpy as np
2 a = np.array([
3 [
4 [1, 5, 5, 2],
5 [9, -6, 2, 8],
6 [-3, 7, -9, 1]
7 ],
8
9 [
10 [-1, 7, -5, 2],
11 [9, 6, 2, 8],
12 [3, 7, 9, 1]
13 ],
14 [
15 [21, 6, -5, 2],
16 [9, 36, 2, 8],
17 [3, 7, 79, 1]
18 ]
19 ])
20 b=np.argmax(a, axis=0)#对于三维度矩阵,a有三个方向a[0][1][2]
21 #当axis=0时,是在a[0]方向上找最大值,即两个矩阵做比较,具体
22 #(1)比较3个矩阵的第一行,即拿[1, 5, 5, 2],
23 # [-1, 7, -5, 2],
24 # [21, 6, -5, 2],
25 #再比较每一列的最大值在那个矩阵中,可以看出第一列1,-2,21最大值为21,在第三个矩阵中,索引值为2
26 #第2列5,7,6最大值为7,在第2个矩阵中,索引值为1.....,最终得出比较结果[2 1 0 0]
27 #再拿出三个矩阵的第二行,按照上述方法,得出比较结果 [0 2 0 0]
28 #一共有三个,所以最终得到的结果b就为3行4列矩阵
29 print(b)
30 #[[2 1 0 0]
31 #[0 2 0 0]
32 #[1 0 2 0]]
33
34 c=np.argmax(a, axis=1)#对于三维度矩阵,a有三个方向a[0][1][2]
35 #当axis=1时,是在a[1]方向上找最大值,即在列方向比较,此时就是指在每个矩阵内部的列方向上进行比较
36 #(1)看第一个矩阵
37 # [1, 5, 5, 2],
38 # [9, -6, 2, 8],
39 # [-3, 7, -9, 1]
40 #比较每一列的最大值,可以看出第一列1,9,-3最大值为9,,索引值为1
41 #第2列5,-6,7最大值为7,,索引值为2
42 # 因此对第一个矩阵,找出索引结果为[1,2,0,1]
43 #再拿出2个,按照上述方法,得出比较结果 [1 0 2 1]
44 #一共有三个,所以最终得到的结果b就为3行4列矩阵
45 print(c)
46 #[[1 2 0 1]
47 # [1 0 2 1]
48 # [0 1 2 1]]
49
50 d=np.argmax(a, axis=2)#对于三维度矩阵,a有三个方向a[0][1][2]
51 #当axis=2时,是在a[2]方向上找最大值,即在行方向比较,此时就是指在每个矩阵内部的行方向上进行比较
52 #(1)看第一个矩阵
53 # [1, 5, 5, 2],
54 # [9, -6, 2, 8],
55 # [-3, 7, -9, 1]
56 #寻找第一行的最大值,可以看出第一行[1, 5, 5, 2]最大值为5,,索引值为1
57 #第2行[9, -6, 2, 8],最大值为9,,索引值为0
58 # 因此对第一个矩阵,找出行最大索引结果为[1,0,1]
59 #再拿出2个矩阵,按照上述方法,得出比较结果 [1 0 2 1]
60 #一共有三个,所以最终得到的结果d就为3行3列矩阵
61 print(d)
62 # [[1 0 1]
63 # [1 0 2]
64 # [0 1 2]]
65 ###################################################################
66 #最后一种情况,指定矩阵a[0, -1, :],第一个数字0代表取出第一个矩阵(从前面可以看出a有3个矩阵)为
67 # [1, 5, 5, 2],
68 # [9, -6, 2, 8],
69 # [-3, 7, -9, 1]
70 #第二个数字“-1”代表拿出倒数第一行,为
71 # [-3, 7, -9, 1]
72 #这一行的最大索引值为1
73
74 # ,-1,代表最后一行
75 m=np.argmax(a[0, -1, :])
76 print(m)#1
77
78 #h,取a的第2个矩阵
79 # [-1, 7, -5, 2],
80 # [9, 6, 2, 8],
81 # [3, 7, 9, 1]
82 #的第3行
83 # [3, 7, 9, 1]
84 #的最大值为9,索引为2
85 h=np.argmax(a[1, 2, :])
86 print(h)#2
88 g=np.argmax(a[1,:, 2])#g,取出矩阵a,第2个矩阵的第3列为-5,2,9,最大值为9,索引为2
89 print(g)#2
五、Confusion Matrix
5.1何为confusion matrix
confusion matrix,中文名叫混淆矩阵。它是一种将test set中的真实lables与我们预测的lables相对比以得出模型准确性的可视化方法。
5.2confusion matrix的应用
我们用混淆矩阵分析我们上文中已经建立的神经网络模型。话不多说,先上代码:
引入必要的包:
1 %matplotlib inline
2 from sklearn.metrics import confusion_matrix
3 import itertools
4 import matplotlib.pyplot as plt
向混淆矩阵中传递真实数据和预测数据:
1 cm = confusion_matrix(y_true=test_lables, y_pred=rounded_predictions)
定义plot_confusion_matrix函数:
1 def plot_confusion_matrix(cm, classes,
2 normalize=False,
3 title='Confusion matrix',
4 cmap=plt.cm.Blues):
5 """
6 This function prints and plots the confusion matrix.
7 Normalization can be applied by setting `normalize=True`.
8 """
9 plt.imshow(cm, interpolation='nearest', cmap=cmap)
10 plt.title(title)
11 plt.colorbar()
12 tick_marks = np.arange(len(classes))
13 plt.xticks(tick_marks, classes, rotation=45)
14 plt.yticks(tick_marks, classes)
15
16 if normalize:
17 cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
18 print("Normalized confusion matrix")
19 else:
20 print('Confusion matrix, without normalization')
21
22 print(cm)
23
24 thresh = cm.max() / 2.
25 for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
26 plt.text(j, i, cm[i, j],
27 horizontalalignment="center",
28 color="white" if cm[i, j] > thresh else "black")
29
30 plt.tight_layout()
31 plt.ylabel('True label')
32 plt.xlabel('Predicted label')
打印混淆矩阵:
1 cm_plot_labels = ['no_side_effects','had_side_effects']
2 plot_confusion_matrix(cm=cm, classes=cm_plot_labels, title='Confusion Matrix')
运行结果:
5.3关于怎么使用plot_confusion_matrix函数
第一步:复制需要引入的包和plot_confusion_matrix函数的定义。
第二步:向矩阵中传递真实的标签和预测的标签。
第三步:写出每一列输出对应的类型名称例如“cm_plot_labels = ['AAAAAAA','BBBBBBB','CCCCCCC'......]”,AAAAAAA是第0列的名称,BBBBBBB是第一列的,依次类推。
第四步:引用plot_confusion_matrix函数,函数具体表示如下:
plot_confusion_matrix(cm, classes,title='Confusion matrix',normalize=True/False)
参数:
cm表示我们向矩阵中传递的两种标签。
classes就是上文中的“cm_plot_labels = ['AAAAAAA','BBBBBBB','CCCCCCC'......]”
title可以随便改
normalize是正则化的意思,选取true的话会把所有数据以概率的形式表示,如下:
六、如何save和load model
6.1how to save
save,顾名思义就是要把模型储存下来,在这里我们只介绍一种最全面也最方便的储存方式,另外几种读者可以自行上网搜索。话不多说,先上代码:
1 import os.path
2 if os.path.isfile('D:\model/medical_trial_model.h5')is False:
3 model.save('D:\model/medical_trial_model.h5')
所有使用model.save函数存储model的步骤基本都是这三步,唯一不同的地方就是存储的地址需要你自己选择。
6.2how to load
load,顾名思义就是把储存好的模型上载到keras上面,先上代码:
1 from tensorflow.keras.models import load_model
2 new_model=load_model('D:\model/medical_trial_model.h5')
6.3Brief Summary
目前为止我们已经基本了解了神经网络运行所有步骤,同时也构建了我们一个自己的简单的神经网络。接下来我们将会继续学习其他的model和neural network,它们的功能将会更加丰富,也会使你更加熟练地掌握深度学习和keras;但是不管是何种neural network,不变的是我们处理问题的步骤,从数据预处理到最后的存储和上载,这些步骤对每一种神经网络来说都必不可少。请时时回顾之前的内容,这些是深度学习的基础知识。下一节我们将会学习一种新的神经网络叫做convolutional neural network。
七、convolutional neural network的数据预处理
7.1引入CNN所有需要的包
1 import numpy as np
2 import tensorflow as tf
3 from tensorflow import keras
4 from tensorflow.keras.models import Sequential
5 from tensorflow.keras.layers import Activation, Dense, Flatten, BatchNormalization, Conv2D, MaxPool2D
6 from tensorflow.keras.optimizers import Adam
7 from tensorflow.keras.metrics import categorical_crossentropy
8 from tensorflow.keras.preprocessing.image import ImageDataGenerator
9 from sklearn.metrics import confusion_matrix
10 import itertools
11 import os
12 import shutil
13 import random
14 import glob
15 import matplotlib.pyplot as plt
16 import warnings
17 warnings.simplefilter(action='ignore', category=FutureWarning)
18 %matplotlib inline
这里涵盖的库包括了所有CNN处理所需要的库,不只是数据预处理哦。
7.2对数据进行手动处理
从kaggle上面下载dogs-vs-cats数据,也可以直接从CSDNcopy。将数据解压之后将dogs-vs-cats中除了train之外的文件夹都删除。将dogs-vs-cats/train/train中的图片文件放置到dogs-vs-cats/train中。将dogs-vs-cats放在D盘的data文件夹下面。
这里我只是提供了一种我自己处理文件的方式,如果读者有需求可以自行订制文件的放置路径。
7.3抽取样本数据
7.3.1代码
1 # Organize data into train, valid, test dirs
2 os.chdir('D:\data\dogs-vs-cats')
3 if os.path.isdir('train/dog') is False:
4 os.makedirs('train/dog')
5 os.makedirs('train/cat')
6 os.makedirs('valid/dog')
7 os.makedirs('valid/cat')
8 os.makedirs('test/dog')
9 os.makedirs('test/cat')
10
11 for i in random.sample(glob.glob('D:/data/dogs-vs-cats/train/cat*.jpg'), 500):
12 # 1、必须使用'/'而不是用'\',2、除了C盘和D盘,下属文件夹表示文件位置都用'/',只有C、D盘用':'3、后面是否加.jpg无所谓
13 shutil.move(i, 'train/cat')
14 for i in random.sample(glob.glob('D:/data/dogs-vs-cats/train/dog*'), 500):
15 shutil.move(i, 'train/dog')
16 for i in random.sample(glob.glob('D:/data/dogs-vs-cats/train/cat*'), 100):
17 shutil.move(i, 'valid/cat')
18 for i in random.sample(glob.glob('D:/data/dogs-vs-cats/train/dog*'), 100):
19 shutil.move(i, 'valid/dog')
20 for i in random.sample(glob.glob('D:/data/dogs-vs-cats/train/cat*'), 50):
21 shutil.move(i, 'test/cat')
22 for i in random.sample(glob.glob('D:/data/dogs-vs-cats/train/dog*'), 50):
23 shutil.move(i, 'test/dog')
24
25 os.chdir('../../')
这段代码一上来很多没有python基础的同志们可能会蒙,没关系,接下来我们就来讲解这里面的函数。
7.3.2os.chdir , os.path.isdir , os.makedirs
os库函数是python中关于路径处理的重要函数。这里os.chdir函数的作用是修改当前的工作路径,这里就修改为了'D:\data\dogs-vs-cats'。os.path.isdir函数的作用是查看在D:\data\dogs-vs-cats中是否存在D:\data\dogs-vs-cats\train\dog文件,如果有的话直接跳过这部分代码,如果没有则会在D:\data\dogs-vs-cats中分别创建test文件夹和valid文件夹,并且在test、train、valid文件夹中都设置cat和dog文件夹。os.makedirs函数的作用就是在当前路径里面创建一个之前不存在的文件,如果文件已经存在则会报错。需要注意的是,这里面除了第一个os.chdir函数使用的是绝对路径之外,其他os函数都是使用的相对于绝对路径的相对路径。
7.3.3random.sample , glob.glob , shutil.move
random.sample(LIST A,int a)函数的作用是,从LIST A中随机挑选a个样本返回。glob.glob函数的作用是从指定的位置中生成所有符合条件的文件路径组成的列表,在这里glob.glob('D:/data/dogs-vs-cats/train/cat*.jpg')语句就是指从绝对路径D:/data/dogs-vs-cats/train中挑选文件名是cat*.jpg的文件这里的星号可以代表一切,我们可以在train文件夹中看到小猫的照片都是“cat.一个数字.jpg”的形式,所以这句话也就是要挑选小猫的图片的意思。至于shutil.move函数的用法其实很简单:shutil.move('原文件完整路径(带文件名)','目标文件夹'),下面举例:比如说我要移动桌面的订单明细.xlsx文件到F:\bak文件夹下,代码如下:
1 shutil.move(r'C:\Users\dan\Desktop\订单明细.xlsx',r'F:\bak')
但是要注意,如果目标文件夹中文件已经存在或者目标文件夹不存在或者程序正在使用都会报错。
7.3.4总结
看完了上面的解释,我们就知道了这段代码到底在干什么了。其实就是创建了train/dog、train/cat、valid/dog、valid/cat、test/cat、test/dog六个文件夹,然后再train的每个文件夹里面装入500张猫或者狗的照片,valid则是100张,test是50张。
7.4规定路径
1 train_path = 'D:/data/dogs-vs-cats/train'
2 valid_path = 'D:/data/dogs-vs-cats/valid'
3 test_path = 'D:/data/dogs-vs-cats/test'
7.5进行预处理
7.5.1代码
1 train_batches = ImageDataGenerator(preprocessing_function=tf.keras.applications.vgg16.preprocess_input) \
2 .flow_from_directory(directory=train_path, target_size=(224,224), classes=['cat', 'dog'], batch_size=10)
3 valid_batches = ImageDataGenerator(preprocessing_function=tf.keras.applications.vgg16.preprocess_input) \
4 .flow_from_directory(directory=valid_path, target_size=(224,224), classes=['cat', 'dog'], batch_size=10)
5 test_batches = ImageDataGenerator(preprocessing_function=tf.keras.applications.vgg16.preprocess_input) \
6 .flow_from_directory(directory=test_path, target_size=(224,224), classes=['cat', 'dog'], batch_size=10, shuffle=False)
7.5.2对参数的解释
这里有很多参数,我们暂时不用知道它们的具体用处,只需要大致了解即可。
preprocessing_function指的是对图像进行预处理选择的方式,这里选择了vgg16方式。
directory参数用来指定要从哪个路径下选取图片数据。
target_size参数后面是指定的图片的大小,这个大小是由编程者自己制定的。
classes等于一个列表,列表里面是进入指定路径之后要从哪些类里面选取图片样本,注意虽然在本例中有cat和dog两个类,但是不是每次都只有两个类。读者自己应用的时候也可以设置三个及以上的类,只需要对于每个类在指定的路径下放置名字和类的名字一样的文件夹并在里面放入你想选取的图片文件即可。
batch_size是设置的一次输入CNN的样本数量,这一点之前我们就涉及到了。
shuffle在test组中等于False。当不指定shuffle参数时函数是默认True的。这里我们设置test组不打乱顺序是因为我们后面还要形成confusion matrix需要按照原来顺序的标签列表。
7.5.3当test set没有标签的时候
参见deeplizard的博客:
Note, in the case where you do not know the labels for the test data, you will need to modify the test_batches variable. Specifically, the change will be to set the parameters classes=None and class_mode=None in flow_from_directory().
7.5.4输出结果
Found 1000 images belonging to 2 classes.
Found 200 images belonging to 2 classes.
Found 100 images belonging to 2 classes.
7.6图片数据可视化
1 imgs, labels = next(train_batches)
2 def plotImages(images_arr):
3 fig, axes = plt.subplots(1, 10, figsize=(20,20))
4 axes = axes.flatten()
5 for img, ax in zip( images_arr, axes):
6 ax.imshow(img)
7 ax.axis('off')
8 plt.tight_layout()
9 plt.show()
10 plotImages(imgs)
11 print(labels)
运行结果:
对于图片数据可视化的流程基本就是这几步,可以直接copy。唯一需要讲解一下的就是next函数。首先我们要知道python中集合的数据类型有列表(list),元组(tuple),字典(dict),集合(set),字符串(str)等,这些都是可迭代的对象(Iterable),必须通过python内置方法iter()方法将这些可迭代的对象转化成迭代器对象(Iterator),进而可以使用for循环进行遍历。下面通过isinstance()方法判断其具体的类型。代码如下:
1 a=[1,2,3]
2 b=iter(a)
3 c1=isinstance(a,Iterable)
4 c2=isinstance(a,Iterator)
5 c3=isinstance(b,Iterable)
6 c4=isinstance(b,Iterator)
7 print(c1)
8 print(c2)
9 print(c3)
10 print(c4)
11 print(b)
12 print(next(b))
13 print(next(b))
14 print(next(b))
运行结果:
这里的next函数实际上就是从迭代器类型train_batches中选一个batch显示出来罢了
八、自己建立的CNN模型的训练和预测
8.1模型的建立
先上代码:
1 model = Sequential([
2 Conv2D(filters=32, kernel_size=(3, 3), activation='relu', padding = 'same', input_shape=(224,224,3)),
3 MaxPool2D(pool_size=(2, 2), strides=2),
4 Conv2D(filters=64, kernel_size=(3, 3), activation='relu', padding = 'same'),
5 MaxPool2D(pool_size=(2, 2), strides=2),
6 Flatten(),
7 Dense(units=2, activation='softmax')
8 ])
这里的模型依然是我们熟悉的Sequential Model但是我们可以看到里面的内容有了很大的不同。下面我们分层次解释里面这些参数的含义。
1.Conv2D Layer
Conv2D Layer是keras提供的一种很常见的使用卷积操作对图片进行作用的神经元层,这里的2D代表处理的是平面图片,它同样有3D和1D模式。 filters参数是指filter矩阵的数量;kernel_size是filter矩阵的长和宽;activation不解释了;这里的padding很重要,对于我们将图片边角处的数据充分利用以及保持图片大小不变有着重要意义,这里设置padding=same是说要保持图片大小不变,如果不添加这个参数,keras默认不进行padding,这会使得图片的大小变小;input_shape是指输入层中需要输入的数据格式,”224,224“指图片像素的数量是224×224,3代表图片按照三色轨道输入——即RGB轨道。
关于卷积的具体定义和filter矩阵到底是什么请查看deeplizard在B站上的深度学习基础课程。
2.MaxPool2D Layer
我们对于MaxPool的定义按下不表,只需要知道它是提取图片特征的操作,里面的参数copy即可。如果读者对于MaxPool的原理想要了解还是请查看deeplizard在B站上的深度学习基础课程。
3.Flatten Layer
这一层的作用是将2D的文件数据转换成一维的数据,便于输出结果。
8.2CNN的编译和运行
1 model.compile(optimizer=Adam(learning_rate=0.0001), loss='categorical_crossentropy', metrics=['accuracy'])
2 model.fit(x=train_batches,
3 steps_per_epoch=len(train_batches),
4 validation_data=valid_batches,
5 validation_steps=len(valid_batches),
6 epochs=10,
7 verbose=2
8 )
之前都见过了,不再讲解。
运行结果:
我们可以看到,训练组的吻合程度很好但是评测组的准确度很低,只有60%,按照我们之前讲过的定义,这个模型显然是overfitting了。如果我们想要继续使用这个模型的话就必须优化来消除这个问题,但我们可以放弃这个模型,因为keras上面有许多准确的能给图片分类的模型,没必要自己建造,这里只是演示一下流程。
8.3预测和混淆矩阵
先上代码:
1 test_imgs, test_labels = next(test_batches)
2 plotImages(test_imgs)
3 print(test_labels)
这个代码的目的是打印出来测试组的一个batch和他们的lables,运行结果:
运行结果:
array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
我们可以看到,里面是没有被shuffle的标签。
1 predictions = model.predict(x=test_batches, steps=len(test_batches), verbose=0)
2 np.round(predictions)
这里为什么我们没有像之前一样设置y=test_lables呢?因为test_batches已经被我们在这之前转化为ImageDataGenerator的形式,这种形式只要输入x参数就可以了。
第二行代码是为了打印出来预测的lables值。
运行结果:
array([[1., 0.],
[0., 1.],
[1., 0.],
[1., 0.],
[1., 0.],
[1., 0.],
......
......
......
[0., 1.],
[0., 1.],
[0., 1.],
[0., 1.],
[0., 1.]], dtype=float32)
下面来打印confusion matrix:
1 def plot_confusion_matrix(cm, classes,
2 normalize=False,
3 title='Confusion matrix',
4 cmap=plt.cm.Blues):
5 """
6 This function prints and plots the confusion matrix.
7 Normalization can be applied by setting `normalize=True`.
8 """
9 plt.imshow(cm, interpolation='nearest', cmap=cmap)
10 plt.title(title)
11 plt.colorbar()
12 tick_marks = np.arange(len(classes))
13 plt.xticks(tick_marks, classes, rotation=45)
14 plt.yticks(tick_marks, classes)
15
16 if normalize:
17 cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
18 print("Normalized confusion matrix")
19 else:
20 print('Confusion matrix, without normalization')
21
22 print(cm)
23
24 thresh = cm.max() / 2.
25 for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
26 plt.text(j, i, cm[i, j],
27 horizontalalignment="center",
28 color="white" if cm[i, j] > thresh else "black")
29
30 plt.tight_layout()
31 plt.ylabel('True label')
32 plt.xlabel('Predicted label')
33 cm = confusion_matrix(y_true=test_batches.classes, y_pred=np.argmax(predictions, axis=-1))
34 cm_plot_labels = ['cat','dog']
35 plot_confusion_matrix(cm=cm, classes=cm_plot_labels, title='Confusion Matrix',normalize=True)
运行结果:
九、使用Fine Tune调整vgg-16分类图像
9.1vgg-16模型准备
老规矩了,先上代码:
1 vgg16_model = tf.keras.applications.vgg16.VGG16()
运行结果:
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/vgg16/vgg16_weights_tf_dim_ordering_tf_kernels.h5
553467904/553467096 [==============================] - 55s 0us/step
这一步的目的就是引入vgg-16模型,如果之前没有使用过的话会先从网络上下载。
1 vgg16_model.summary()
运行结果:
Model: "vgg16" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 224, 224, 3)] 0 _________________________________________________________________ block1_conv1 (Conv2D) (None, 224, 224, 64) 1792 _________________________________________________________________ block1_conv2 (Conv2D) (None, 224, 224, 64) 36928 _________________________________________________________________ block1_pool (MaxPooling2D) (None, 112, 112, 64) 0 _________________________________________________________________ block2_conv1 (Conv2D) (None, 112, 112, 128) 73856 _________________________________________________________________ block2_conv2 (Conv2D) (None, 112, 112, 128) 147584 _________________________________________________________________ block2_pool (MaxPooling2D) (None, 56, 56, 128) 0 _________________________________________________________________ block3_conv1 (Conv2D) (None, 56, 56, 256) 295168 _________________________________________________________________ block3_conv2 (Conv2D) (None, 56, 56, 256) 590080 _________________________________________________________________ block3_conv3 (Conv2D) (None, 56, 56, 256) 590080 _________________________________________________________________ block3_pool (MaxPooling2D) (None, 28, 28, 256) 0 _________________________________________________________________ block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160 _________________________________________________________________ block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808 _________________________________________________________________ block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808 _________________________________________________________________ block4_pool (MaxPooling2D) (None, 14, 14, 512) 0 _________________________________________________________________ block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808 _________________________________________________________________ block5_pool (MaxPooling2D) (None, 7, 7, 512) 0 _________________________________________________________________ flatten (Flatten) (None, 25088) 0 _________________________________________________________________ fc1 (Dense) (None, 4096) 102764544 _________________________________________________________________ fc2 (Dense) (None, 4096) 16781312 _________________________________________________________________ predictions (Dense) (None, 1000) 4097000 ================================================================= Total params: 138,357,544 Trainable params: 138,357,544 Non-trainable params: 0 _________________________________________________________________
可以看出,vgg-16的模型比我们的模型复杂许多,那我们需要对于它做哪些修改呢?由于vgg-16本身就具有分类多种物品的能力,包括猫和狗,所以我们不需要对它的权重和偏见有任何修改,但是它的输出层有1000个类别而我们只需要识别两个类别——猫和狗,所以我们需要做的就是把最后一层改为两个输出神经元。
1 type(vgg16_model)
运行结果:
tensorflow.python.keras.engine.training.Model
这里可以看到vgg-16的模型不是顺序模型,如果是顺序模型,我们看到的结果应该是这样的:
tensorflow.python.keras.engine.sequential.Sequential
由于我们对于非顺序模型还不会操作所以我们需要把它转换成顺序模型。
1 model = Sequential()
2 for layer in vgg16_model.layers[:-1]:
3 model.add(layer)
这里运用了索引的形式,[start:end(:step)],[:-1]就是指从一开始到最后一层(不包括最后一层)的步长为1的索引。
把vgg-16中除了输出层的所有层都放进了model中。
1 for layer in model.layers:
2 layer.trainable = False
用这一步将除了最后一层之外的所有层之间的权重和偏见锁死,因为我们不希望它们改变。
1 model.add(Dense(units=2, activation='softmax'))
加入只有两个神经元的输出层
1 model.summary()
运行结果:
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
block1_conv1 (Conv2D) (None, 224, 224, 64) 1792
_________________________________________________________________
block1_conv2 (Conv2D) (None, 224, 224, 64) 36928
_________________________________________________________________
block1_pool (MaxPooling2D) (None, 112, 112, 64) 0
_________________________________________________________________
block2_conv1 (Conv2D) (None, 112, 112, 128) 73856
_________________________________________________________________
block2_conv2 (Conv2D) (None, 112, 112, 128) 147584
_________________________________________________________________
block2_pool (MaxPooling2D) (None, 56, 56, 128) 0
_________________________________________________________________
block3_conv1 (Conv2D) (None, 56, 56, 256) 295168
_________________________________________________________________
block3_conv2 (Conv2D) (None, 56, 56, 256) 590080
_________________________________________________________________
block3_conv3 (Conv2D) (None, 56, 56, 256) 590080
_________________________________________________________________
block3_pool (MaxPooling2D) (None, 28, 28, 256) 0
_________________________________________________________________
block4_conv1 (Conv2D) (None, 28, 28, 512) 1180160
_________________________________________________________________
block4_conv2 (Conv2D) (None, 28, 28, 512) 2359808
_________________________________________________________________
block4_conv3 (Conv2D) (None, 28, 28, 512) 2359808
_________________________________________________________________
block4_pool (MaxPooling2D) (None, 14, 14, 512) 0
_________________________________________________________________
block5_conv1 (Conv2D) (None, 14, 14, 512) 2359808
_________________________________________________________________
block5_conv2 (Conv2D) (None, 14, 14, 512) 2359808
_________________________________________________________________
block5_conv3 (Conv2D) (None, 14, 14, 512) 2359808
_________________________________________________________________
block5_pool (MaxPooling2D) (None, 7, 7, 512) 0
_________________________________________________________________
flatten (Flatten) (None, 25088) 0
_________________________________________________________________
fc1 (Dense) (None, 4096) 102764544
_________________________________________________________________
fc2 (Dense) (None, 4096) 16781312
_________________________________________________________________
dense (Dense) (None, 2) 8194
=================================================================
Total params: 134,268,738
Trainable params: 8,194
Non-trainable params: 134,260,544
_________________________________________________________________
我们可以看到,成功改变了模型。
9.2编译和训练
完全一致,不再赘述
1 model.compile(optimizer=Adam(learning_rate=0.0001), loss='categorical_crossentropy', metrics=['accuracy'])
2 model.fit(x=train_batches,
3 steps_per_epoch=len(train_batches),
4 validation_data=valid_batches,
5 validation_steps=len(valid_batches),
6 epochs=5,
7 verbose=2
8 )
运行结果:
9.3预测和confusion矩阵
同样,不再赘述
1 predictions = model.predict(x=test_batches, steps=len(test_batches), verbose=0)
2 def plot_confusion_matrix(cm, classes,
3 normalize=False,
4 title='Confusion matrix',
5 cmap=plt.cm.Blues):
6 """
7 This function prints and plots the confusion matrix.
8 Normalization can be applied by setting `normalize=True`.
9 """
10 plt.imshow(cm, interpolation='nearest', cmap=cmap)
11 plt.title(title)
12 plt.colorbar()
13 tick_marks = np.arange(len(classes))
14 plt.xticks(tick_marks, classes, rotation=45)
15 plt.yticks(tick_marks, classes)
16
17 if normalize:
18 cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
19 print("Normalized confusion matrix")
20 else:
21 print('Confusion matrix, without normalization')
22
23 print(cm)
24
25 thresh = cm.max() / 2.
26 for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
27 plt.text(j, i, cm[i, j],
28 horizontalalignment="center",
29 color="white" if cm[i, j] > thresh else "black")
30
31 plt.tight_layout()
32 plt.ylabel('True label')
33 plt.xlabel('Predicted label')
34 cm = confusion_matrix(y_true=test_batches.classes, y_pred=np.argmax(predictions, axis=-1))
35 cm_plot_labels = ['cat','dog']
36 plot_confusion_matrix(cm=cm, classes=cm_plot_labels, title='Confusion Matrix')
运行结果:
十、便捷实用的MobileNet模型
10.1MobileNet模型简介
前面我们已经看到过了vgg-16的强大功能,但是虽然vgg-16的优点很显著,它的缺点一样不容忽视,高达538MB的超大内存使得我们使用vgg-16异常的不方便。与vgg-16模型不同,MobileNet模型在保留了训练精度的情况下大大减少了内存需求,也因此MobileNet经常被应用在手机等移动设备的智能化上面。
10.2数据预处理
我们的数据集这次选用了从0-9的手势照片,这个照片集在GitHub上面可以下载ardamavi/Sign-Language-Digits-Dataset: Turkey Ankara Ayrancı Anadolu High School's Sign Language Digits Dataset (github.com),下载完成之后解压缩,将里面0-9十个文件夹放到D:\data\Sign-Language-Digits-Dataset-master中,删除Sign-Language-Digits-Dataset-master里面嵌套的同名Sign-Language-Digits-Dataset-master文件夹然后把Sign-Language-Digits-Dataset-master文件夹的名字中的master去掉。至此,手动操作完成,上代码:
引入所需的库
1 import numpy as np
2 import tensorflow as tf
3 from tensorflow import keras
4 from tensorflow.keras.layers import Dense, Activation
5 from tensorflow.keras.optimizers import Adam
6 from tensorflow.keras.metrics import categorical_crossentropy
7 from tensorflow.keras.preprocessing.image import ImageDataGenerator
8 from tensorflow.keras.preprocessing import image
9 from tensorflow.keras.models import Model
10 from tensorflow.keras.applications import imagenet_utils
11 from sklearn.metrics import confusion_matrix
12 import itertools
13 import os
14 import shutil
15 import random
16 import matplotlib.pyplot as plt
17 %matplotlib inline
将数据整理到train,valid,test文件夹中
1 # Organize data into train, valid, test dirs
2 os.chdir('D:/data/Sign-Language-Digits-Dataset')
3 if os.path.isdir('train/0/') is False:
4 os.mkdir('train')
5 os.mkdir('valid')
6 os.mkdir('test')
7
8 for i in range(0,10):
9 shutil.move(f'D:/data/Sign-Language-Digits-Dataset/{i}', 'train')
10 os.mkdir(f'D:/data/Sign-Language-Digits-Dataset/valid/{i}')
11 os.mkdir(f'D:/data/Sign-Language-Digits-Dataset/test/{i}')
12
13 valid_samples = random.sample(os.listdir(f'D:/data/Sign-Language-Digits-Dataset/train/{i}'), 30)
14 for j in valid_samples:
15 shutil.move(f'D:/data/Sign-Language-Digits-Dataset/train/{i}/{j}',f'D:/data/Sign-Language-Digits-Dataset/valid/{i}')
16
17 test_samples = random.sample(os.listdir(f'D:/data/Sign-Language-Digits-Dataset/train/{i}'), 5)
18 for k in test_samples:
19 shutil.move(f'D:/data/Sign-Language-Digits-Dataset/train/{i}/{k}', f'D:/data/Sign-Language-Digits-Dataset/test/{i}')
20 os.chdir('../..')
os.chdir的作用是指明当前的工作路径;os.path.isdir判断括号里的文件在当前的工作路径中是否存在;os.mkdir如果括号里面的文件夹不存在则创建该文件夹,如果存在会报错;shutil.move('需要移动的文件路径(包括该文件本身的名称)',‘要移动到的文件夹名称’),注意此处的文件名称前面有一个字母'f',这个'f'的作用是将后面文件名称中用大括号括起来的部分从被单引号确定的字符串中'救'出来,即——如果不加'f'这个路径就是指向一个名字叫做'i'这个字母的文件夹,但是加了'f'的话这个路径就是指向一个名称是'i'这个变量指代的数字的文件夹;os.listdir用于返回指定的文件夹包含的文件或文件夹的名字的列表,但有个很明显的缺点,它的默认顺序不是有序的或者说不是通常的顺序(不知道用啥排的),返回值是list类型。
还有一点需要注意的是:在python字符串中'\'是转义字符的标志,所以如果使用'\'来表示文件的从属关系的话需要连着使用两个,即——'\\'。
进行预处理
1 train_path = 'D:\\data\\Sign-Language-Digits-Dataset\\train'
2 valid_path = 'D:\\data\\Sign-Language-Digits-Dataset\\valid'
3 test_path = 'D:\\data\\Sign-Language-Digits-Dataset\\test'
4
5 train_batches = ImageDataGenerator(preprocessing_function=tf.keras.applications.mobilenet.preprocess_input).flow_from_directory(
6 directory=train_path, target_size=(224,224), batch_size=10)
7 valid_batches = ImageDataGenerator(preprocessing_function=tf.keras.applications.mobilenet.preprocess_input).flow_from_directory(
8 directory=valid_path, target_size=(224,224), batch_size=10)
9 test_batches = ImageDataGenerator(preprocessing_function=tf.keras.applications.mobilenet.preprocess_input).flow_from_directory(
10 directory=test_path, target_size=(224,224), batch_size=10, shuffle=False)
运行结果:
Found 1712 images belonging to 10 classes.
Found 300 images belonging to 10 classes.
Found 50 images belonging to 10 classes.
这里面的所有语法我们在之前都接触过,唯一不同的是我们预处理的方法现在不是vgg-16而是MobileNet了。
10.3fine-tuning,编译和训练
首先进行fine-tuning
下载模型
1 mobile = tf.keras.applications.mobilenet.MobileNet()
2 mobile.summary()
运行结果:
Downloading data from https://storage.googleapis.com/tensorflow/keras-applications/mobilenet/mobilenet_1_0_224_tf.h5 17227776/17225924 [==============================] - 4s 0us/step Model: "mobilenet_1.00_224" _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= input_1 (InputLayer) [(None, 224, 224, 3)] 0 _________________________________________________________________ conv1_pad (ZeroPadding2D) (None, 225, 225, 3) 0 _________________________________________________________________ conv1 (Conv2D) (None, 112, 112, 32) 864 _________________________________________________________________ conv1_bn (BatchNormalization (None, 112, 112, 32) 128 _________________________________________________________________
......
......
......
......
......
......
_________________________________________________________________
conv_pw_13_relu (ReLU) (None, 7, 7, 1024) 0
_________________________________________________________________
global_average_pooling2d (Gl (None, 1024) 0
_________________________________________________________________
reshape_1 (Reshape) (None, 1, 1, 1024) 0
_________________________________________________________________
dropout (Dropout) (None, 1, 1, 1024) 0
_________________________________________________________________
conv_preds (Conv2D) (None, 1, 1, 1000) 1025000
_________________________________________________________________
reshape_2 (Reshape) (None, 1000) 0
_________________________________________________________________
predictions (Activation) (None, 1000) 0
=================================================================
Total params: 4,253,864
Trainable params: 4,231,976
Non-trainable params: 21,888
_________________________________________________________________
进行fine-tuning
1 x = mobile.layers[-6].output
2 output = Dense(units=10, activation='softmax')(x)
我们可以看到,这里output层的形式和我们之前的模式明显是不一样的。这是因为我们这里调用的模型是functional model而不是sequential model简要介绍一下就是每一层都是一个函数,都有输入和输出,这里的x就是mobile.layers[-6].output的输出,而这个
输出也作为输入放到了下一层的参数中,也就是(x)。关于继续了解functional model,keras文档里面写的很清楚。快速开始函数式(Functional)模型 - Keras中文文档 (keras-cn.readthedocs.io)
第一行代码中有两个需要讲解的点。1.mobile.layers表示的是一个List型数据,是mobile模型的所有层组合而成的List。2.mobile.layers[-6].output先用索引的形式,从Layers这个List中找到了倒数第六层,然后调用.output函数找到这层的输出,也就是下一
层的输入,我们设为x。
1 model = Model(inputs=mobile.input, outputs=output)
这是functional model典型的定义方式:model=Model(inputs=... , outputs=... )
1 for layer in model.layers[:-23]:
2 layer.trainable = False
使用切片方式冻结除了最后23层之外的所有层。
1 model.compile(optimizer=Adam(lr=0.0001), loss='categorical_crossentropy', metrics=['accuracy'])
2 model.fit(x=train_batches,
3 steps_per_epoch=len(train_batches),
4 validation_data=valid_batches,
5 validation_steps=len(valid_batches),
6 epochs=10,
7 verbose=2
8 )
运行结果:
10.4预测和混淆矩阵
毫无新意,直接上代码:
1 test_labels = test_batches.classes
2 predictions = model.predict(x=test_batches, steps=len(test_batches), verbose=0)
1 def plot_confusion_matrix(cm, classes,
2 normalize=False,
3 title='Confusion matrix',
4 cmap=plt.cm.Blues):
5 """
6 This function prints and plots the confusion matrix.
7 Normalization can be applied by setting `normalize=True`.
8 """
9 plt.imshow(cm, interpolation='nearest', cmap=cmap)
10 plt.title(title)
11 plt.colorbar()
12 tick_marks = np.arange(len(classes))
13 plt.xticks(tick_marks, classes, rotation=45)
14 plt.yticks(tick_marks, classes)
15
16 if normalize:
17 cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]
18 print("Normalized confusion matrix")
19 else:
20 print('Confusion matrix, without normalization')
21
22 print(cm)
23
24 thresh = cm.max() / 2.
25 for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):
26 plt.text(j, i, cm[i, j],
27 horizontalalignment="center",
28 color="white" if cm[i, j] > thresh else "black")
29
30 plt.tight_layout()
31 plt.ylabel('True label')
32 plt.xlabel('Predicted label')
33 cm = confusion_matrix(y_true=test_labels, y_pred=predictions.argmax(axis=1))
1 test_batches.class_indices
这个函数可以让我们打印出来每个类名对应的序号。
运行结果:
{'0': 0,
'1': 1,
'2': 2,
'3': 3,
'4': 4,
'5': 5,
'6': 6,
'7': 7,
'8': 8,
'9': 9}
1 cm_plot_labels = ['0','1','2','3','4','5','6','7','8','9']
2 plot_confusion_matrix(cm=cm, classes=cm_plot_labels, title='Confusion Matrix')
运行结果:








浙公网安备 33010602011771号