python 深度学习
返回主页编程菜鸡小徐震
博客园
首页
新随笔
联系
订阅
管理
python应用——keras深度学习
阅读目录
环境准备
一、对于数据的预处理
二、神经网络的建构
2.1Sequectial model
2.2Activation和Dense
三、神经网络模型的编译和运行
3.1optimizer
3.2learning rate(lr)
3.3loss function
3.4batch_size和epochs
3.5validation_split
3.6其他参数
四、神经网络用来预测
4.1关于test set
4.2生成test set
4.3预测过程实现
4.4预测结果最可能化
五、Confusion Matrix
5.1何为confusion matrix
5.2confusion matrix的应用
6.3Brief Summary
六、如何save和load model
6.1how to save
6.2how to load
七、convolutional neural network的数据预处理
7.1引入CNN所有需要的包
7.2对数据进行手动处理
7.3抽取样本数据
7.4规定路径
7.5进行预处理
7.6图片数据可视化
八、自己建立的CNN模型的训练和预测
8.1模型的建立
8.2CNN的编译和运行
8.3预测和混淆矩阵
九、使用Fine Tune调整vgg-16分类图像
9.1vgg-16模型准备
9.2编译和训练
9.3预测和confusion矩阵
十、便捷实用的MobileNet模型
10.1MobileNet模型简介
10.2数据预处理
10.3fine-tuning,编译和训练
10.4预测和混淆矩阵
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代表狗那么这张图片在神经网络中的结果就是10.75+00.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)#对二维矩阵来讲a0会有两个索引方向,第一个方向为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)#现在按照a0中的a1方向,即行方向搜索最大值,
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©#[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有三个方向a02
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有三个方向a02
35 #当axis=1时,是在a1方向上找最大值,即在列方向比较,此时就是指在每个矩阵内部的列方向上进行比较
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©
46 #[[1 2 0 1]
47 # [1 0 2 1]
48 # [0 1 2 1]]
49
50 d=np.argmax(a, axis=2)#对于三维度矩阵,a有三个方向a02
51 #当axis=2时,是在a2方向上找最大值,即在行方向比较,此时就是指在每个矩阵内部的行方向上进行比较
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.shape1)):
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’)
运行结果:
Confusion matrix, without normalization
[[400 20]
[ 20 400]]
有了这张图想必对于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的话会把所有数据以概率的形式表示,如下:
Normalized confusion matrix
[[0.95238095 0.04761905]
[0.04761905 0.95238095]]
回到顶部
六、如何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’)
所有使用load_model函数存储model的步骤也基本都是这两步,唯一不同的地方就是上载的地址。
然后我们就可以验证一下新旧两个模型的一致性:
1 new_model.summary()
2 new_model.get_weights()
3 new_model.optimizer
运行结果:
Model: “sequential_4”
Layer (type) Output Shape Param #
dense_12 (Dense) (None, 16) 32
dense_13 (Dense) (None, 32) 544
dense_14 (Dense) (None, 2) 66
Total params: 642
Trainable params: 642
Non-trainable params: 0
[array([[-0.08342516, -0.18501374, -0.4620674 , -0.02204102, -0.17732811,
-0.21860081, 0.652517 , -0.44338012, 0.0099441 , -0.22224525,
-0.5776169 , 0.31368536, -0.03883362, -0.12664345, -0.26365945,
-0.15359971]], dtype=float32),
array([ 0. , 0. , 0. , 0. , 0. ,
0. , -0.23546277, 0.29458967, -0.01728607, 0. ,
0. , 0.05643348, 0. , 0. , 0. ,
0. ], dtype=float32),
…
…
…
…
…
…
array([ 0.0646436 , -0.06464363], dtype=float32)]
<tensorflow.python.keras.optimizer_v2.adam.Adam at 0x22694ca50c8>
完全一致
成功!
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)
复制代码
运行结果:
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
[[1. 0.]
[1. 0.]
[1. 0.]
[1. 0.]
[1. 0.]
[0. 1.]
[1. 0.]
[1. 0.]
[1. 0.]
[1. 0.]]
对于图片数据可视化的流程基本就是这几步,可以直接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))
复制代码
运行结果:
True
False
True
True
<list_iterator object at 0x0000029FD3462B88>
1
2
3
这里的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 )
复制代码
之前都见过了,不再讲解。
运行结果:
Epoch 1/10
100/100 - 23s - loss: 18.1971 - accuracy: 0.5430 - val_loss: 10.0159 - val_accuracy: 0.5800
Epoch 2/10
100/100 - 21s - loss: 3.4772 - accuracy: 0.7120 - val_loss: 4.1437 - val_accuracy: 0.6350
Epoch 3/10
100/100 - 20s - loss: 0.9984 - accuracy: 0.8660 - val_loss: 4.2985 - val_accuracy: 0.6250
Epoch 4/10
100/100 - 19s - loss: 0.3579 - accuracy: 0.9310 - val_loss: 3.6414 - val_accuracy: 0.5950
Epoch 5/10
100/100 - 19s - loss: 0.0919 - accuracy: 0.9720 - val_loss: 3.3644 - val_accuracy: 0.5950
Epoch 6/10
100/100 - 20s - loss: 0.0132 - accuracy: 0.9960 - val_loss: 3.8086 - val_accuracy: 0.6150
Epoch 7/10
100/100 - 20s - loss: 0.0135 - accuracy: 0.9960 - val_loss: 3.4579 - val_accuracy: 0.6150
Epoch 8/10
100/100 - 20s - loss: 0.0022 - accuracy: 1.0000 - val_loss: 3.3740 - val_accuracy: 0.6200
Epoch 9/10
100/100 - 19s - loss: 2.9646e-04 - accuracy: 1.0000 - val_loss: 3.3881 - val_accuracy: 0.6000
Epoch 10/10
100/100 - 20s - loss: 2.0524e-04 - accuracy: 1.0000 - val_loss: 3.3896 - val_accuracy: 0.6050
<tensorflow.python.keras.callbacks.History at 0x22f19e0fec8>
我们可以看到,训练组的吻合程度很好但是评测组的准确度很低,只有60%,按照我们之前讲过的定义,这个模型显然是overfitting了。如果我们想要继续使用这个模型的话就必须优化来消除这个问题,但我们可以放弃这个模型,因为keras上面有许多准确的能给图片分类的模型,没必要自己建造,这里只是演示一下流程。
8.3预测和混淆矩阵
先上代码:
1 test_imgs, test_labels = next(test_batches)
2 plotImages(test_imgs)
3 print(test_labels)
这个代码的目的是打印出来测试组的一个batch和他们的lables,运行结果:
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
Clipping input data to the valid range for imshow with RGB data ([0…1] for floats or [0…255] for integers).
[[1. 0.] [1. 0.]
[1. 0.]
[1. 0.]
[1. 0.]
[1. 0.]
[1. 0.]
[1. 0.]
[1. 0.]
[1. 0.]]
下面这行代码是为了打印出来tset组的全部lables
1 test_batches.classes
运行结果:
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.shape1)):
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)
复制代码
运行结果:
Normalized confusion matrix
[[0.74 0.26]
[0.42 0.58]]
能看得出来,我们自己建立的模型准确度非常差,不过没关系,因为接下来我们将会使用现成的模型vgg-16来处理这些数据,我们将会看到vgg-16的强大能力。
回到顶部
九、使用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 )
复制代码
运行结果:
Epoch 1/5
100/100 - 108s - loss: 0.6291 - accuracy: 0.7710 - val_loss: 0.1753 - val_accuracy: 0.9250
Epoch 2/5
100/100 - 117s - loss: 0.1321 - accuracy: 0.9510 - val_loss: 0.1243 - val_accuracy: 0.9500
Epoch 3/5
100/100 - 124s - loss: 0.0845 - accuracy: 0.9660 - val_loss: 0.1109 - val_accuracy: 0.9500
Epoch 4/5
100/100 - 118s - loss: 0.0614 - accuracy: 0.9760 - val_loss: 0.1023 - val_accuracy: 0.9600
Epoch 5/5
100/100 - 125s - loss: 0.0475 - accuracy: 0.9830 - val_loss: 0.1004 - val_accuracy: 0.9550
<tensorflow.python.keras.callbacks.History at 0x244ebffb288>
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.shape1)):
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’)
复制代码
运行结果:
Confusion matrix, without normalization
[[48 2]
[ 1 49]]
可以看到,vgg-16模型只用了5次训练就达到了95.5%的准确率,完美完成了任务。
回到顶部
十、便捷实用的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 )
复制代码
运行结果:
Epoch 1/10
172/172 - 42s - loss: 0.0108 - accuracy: 0.9994 - val_loss: 0.0410 - val_accuracy: 0.9900
Epoch 2/10
172/172 - 42s - loss: 0.0149 - accuracy: 0.9977 - val_loss: 0.0185 - val_accuracy: 1.0000
Epoch 3/10
172/172 - 40s - loss: 0.0140 - accuracy: 0.9982 - val_loss: 0.0591 - val_accuracy: 0.9767
Epoch 4/10
172/172 - 40s - loss: 0.0107 - accuracy: 0.9982 - val_loss: 0.0164 - val_accuracy: 0.9967
Epoch 5/10
172/172 - 41s - loss: 0.0076 - accuracy: 1.0000 - val_loss: 0.0126 - val_accuracy: 1.0000
Epoch 6/10
172/172 - 42s - loss: 0.0055 - accuracy: 1.0000 - val_loss: 0.0125 - val_accuracy: 1.0000
Epoch 7/10
172/172 - 41s - loss: 0.0051 - accuracy: 0.9994 - val_loss: 0.0449 - val_accuracy: 0.9833
Epoch 8/10
172/172 - 41s - loss: 0.0058 - accuracy: 0.9988 - val_loss: 0.0331 - val_accuracy: 0.9867
Epoch 9/10
172/172 - 43s - loss: 0.0040 - accuracy: 0.9994 - val_loss: 0.0229 - val_accuracy: 0.9900
Epoch 10/10
172/172 - 41s - loss: 0.0034 - accuracy: 1.0000 - val_loss: 0.0179 - val_accuracy: 0.9933
<tensorflow.python.keras.callbacks.History at 0x166dbf71a88>
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.shape1)):
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’)
运行结果:
Confusion matrix, without normalization
[[5 0 0 0 0 0 0 0 0 0]
[0 5 0 0 0 0 0 0 0 0]
[0 0 5 0 0 0 0 0 0 0]
[0 0 0 5 0 0 0 0 0 0]
[0 0 0 0 5 0 0 0 0 0]
[0 0 0 0 0 5 0 0 0 0]
[0 0 0 0 0 0 5 0 0 0]
[0 0 0 0 0 0 0 5 0 0]
[0 0 0 0 0 0 0 0 5 0]
[0 0 0 0 0 0 0 0 0 5]]
完美!
好文要顶 关注我 收藏该文
编程菜鸡小徐震
粉丝 - 1 关注 - 0
+加关注
00
« 上一篇: python库——sklearn
» 下一篇: python库——pandas
posted @ 2021-08-21 10:38 编程菜鸡小徐震 阅读(315) 评论(0) 编辑 收藏 举报
刷新评论刷新页面返回顶部
登录后才能查看或发表评论,立即 登录 或者 逛逛 博客园首页
【推荐】腾讯云多款云产品1折起,买云服务器送免费机器
编辑推荐:
· 前端构建效率优化之路
· .NET性能优化-使用SourceGenerator-Logger记录日志
· 记一次 Linux server 偶发 CPU 飙升问题的跟进与解决
· 记一次 .NET 某智慧物流 WCS 系统 CPU 爆高分析
· 如何创建一个带诊断工具的 .NET 镜像
最新新闻:
· 3D石墨烯泡沫制成先进压力传感器
· 这个开挂都没法被封号的人,反手还能把别人的号封了。
· 盘活充电桩的闲钱:“私桩共享”能否解开新能源汽车充电难题?
· 交出“灾难性”财报后,英特尔要靠晶圆代工扳回一城?
· 腾讯电竞的蓝翔梦
» 更多新闻…
公告
昵称: 编程菜鸡小徐震
园龄: 11个月
粉丝: 1
关注: 0
+加关注
< 2022年8月 >
日 一 二 三 四 五 六
31 1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31 1 2 3
4 5 6 7 8 9 10
搜索
常用链接
我的随笔
我的评论
我的参与
最新评论
我的标签
随笔档案
2021年8月(5)
阅读排行榜
- python应用——keras深度学习(315)
- python库——sklearn(134)
- python库——pandas(73)
- python库——random(47)
- python库——numpy(36)
Copyright © 2022 编程菜鸡小徐震
Powered by .NET 6 on Kubernetes
目录导航
新的改变
我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:
- 全新的界面设计 ,将会带来全新的写作体验;
- 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
- 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
- 全新的 KaTeX数学公式 语法;
- 增加了支持甘特图的mermaid语法1 功能;
- 增加了 多屏幕编辑 Markdown文章功能;
- 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
- 增加了 检查列表 功能。
功能快捷键
撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G
合理的创建标题,有助于目录的生成
直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC
语法后生成一个完美的目录。
如何改变文本的样式
强调文本 强调文本
加粗文本 加粗文本
标记文本
删除文本
引用文本
H2O is是液体。
210 运算结果是 1024.
插入链接与图片
链接: link.
图片:
带尺寸的图片:
居中的图片:
居中并且带尺寸的图片:
当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。
如何插入一段漂亮的代码片
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
// An highlighted block
var foo = 'bar';
生成一个适合你的列表
- 项目
- 项目
- 项目
- 项目
- 项目1
- 项目2
- 项目3
- 计划任务
- 完成任务
创建一个表格
一个简单的表格是这么创建的:
项目 | Value |
---|---|
电脑 | $1600 |
手机 | $12 |
导管 | $1 |
设定内容居中、居左、居右
使用:---------:
居中
使用:----------
居左
使用----------:
居右
第一列 | 第二列 | 第三列 |
---|---|---|
第一列文本居中 | 第二列文本居右 | 第三列文本居左 |
SmartyPants
SmartyPants将ASCII标点字符转换为“智能”印刷标点HTML实体。例如:
TYPE | ASCII | HTML |
---|---|---|
Single backticks | 'Isn't this fun?' | ‘Isn’t this fun?’ |
Quotes | "Isn't this fun?" | “Isn’t this fun?” |
Dashes | -- is en-dash, --- is em-dash | – is en-dash, — is em-dash |
创建一个自定义列表
-
Markdown
- Text-to- HTML conversion tool Authors
- John
- Luke
如何创建一个注脚
一个具有注脚的文本。2
注释也是必不可少的
Markdown将文本转换为 HTML。
KaTeX数学公式
您可以使用渲染LaTeX数学表达式 KaTeX:
Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n−1)!∀n∈N 是通过欧拉积分
Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=∫0∞tz−1e−tdt.
你可以找到更多关于的信息 LaTeX 数学表达式here.
新的甘特图功能,丰富你的文章
- 关于 甘特图 语法,参考 这儿,
UML 图表
可以使用UML图表进行渲染。 Mermaid. 例如下面产生的一个序列图:
这将产生一个流程图。:
- 关于 Mermaid 语法,参考 这儿,
FLowchart流程图
我们依旧会支持flowchart的流程图:
- 关于 Flowchart流程图 语法,参考 这儿.
导出与导入
导出
如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。
导入
如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。
注脚的解释 ↩︎