TensorFlow实战之猫狗识别

标签: 无 分类: 未分类 创建时间:2023-05-19 11:27:22 更新时间:2024-11-23 10:24:25

前言

为了更好的理解Tensorflow,我觉得在看教程的时候,最好还是要进行实战一下,我就以最简单的猫狗识别为例子进行学习好了。这里我假定已经安装配置好了 TensorFlow 的环境,我自己的安装配置可以参考 TensorFlow实战之环境搭建

大部分的训练模型的项目的目录结构如下,其实也有简单的,就是参考文章6,我跟着做了

  • data文件夹:包含 test 测试集 (有些可能叫 verify 验证集 )和 train 训练集
  • log文件夹:保存训练模型和参数
  • image文件夹: 存放训练图和预测结果图
  • input_data.py:负责实现读取数据,生成批次
  • model.py:负责实现神经网络模型
  • training.py:负责实现模型的训练以及评估
  • test.py: 从测试集中随机抽取一张图片, 进行预测是猫还是狗

先运行training.py来训练模型,训练完再运行test.py来测试图片进行预测猫或狗。

也有比较简单的,高准确率!TensorFlow猫狗识别案例 这篇文章的代码基本上都能运行,使用了VGG模型。这篇文章中我放弃了以往的 model.fit() 训练方法,改用model.train_on_batch方法。此外我也引入了进度条的显示方式,更加方便我们及时查看模型训练过程中的情况,可以及时打印各项指标。

参考文章:
1.cats_vs_dogs 这里用了tensorflow 1进行了一个训练和预测,里面的目录结构倒是可以参考。
2.CNN实现猫狗识别(Tensorflow版本) 包括 数据预处理、网络构建、模型训练 几个步骤。这里只有模型的训练过程,没有验证过程。
3.使用Tensorflow 2进行猫狗分类识别
4.Python——tensorflow2.8猫狗识别 跟着唐宇迪教程做了一遍,中间部分参数做了修改
5.tensorflow2.3实践 猫狗识别(详细步骤注释及93%的准确率) 这里也是使用了 VGG16 模型进行的训练
6.python如何识别猫狗?对狗和猫的照片进行分类(准确率为 97%) 这篇文章的达到了 97%,这里加入了 Dropout 正则化。
7.猫狗识别 这里也提供了代码,是TensorFlow 1.xx版本,有数据集的下载,文件夹分类说明。最终的模型和结果在这个猫狗分类问题上是符合期望的。在项目实施过程中有一点令我印象深刻,那就是任何机器学习问题,都免不了数据预处理、模型构建、模型训练、测试与验证等步骤;但预处理与模型调参可能会占用大部分时间和精力,数据组织方式也会对模型有较大影响。这篇文章也是我所参考的预测的代码。
8.基于卷积神经网络(CNN)的猫狗识别
9.深度学习100例-卷积神经网络(CNN)花朵识别 | 第4天
10.基于CNN的猫狗品种识别技术研究 这是用 CNN 和 PyTorch 实现的
11. lyhue1991 / eat_tensorflow2_in_30_days 这是Tensorflow教程,感觉写的还挺好的。

1.数据准备

下载数据,数据集链接:https://pan.baidu.com/s/1qBmmV-bhm5b1KWey5Zc_rw 提取码:e5rq 。也可以从谷歌上下载。下载之后放到 data 的 train 目录下就可以了。

2.数据预处理

数据预处理的过程,就是包括数据增强,图片变形,归一化等操作。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
"""查看数据"""
import tensorflow as tf
import pathlib
import matplotlib.pyplot as plt


data_dir = "./data/train"
data_dir = pathlib.Path(data_dir)
image_count = len(list(data_dir.glob('*/*')))
print("图片总数为:",image_count)

"""加载数据"""
batch_size = 8
img_height = 224
img_width = 224
"""
关于image_dataset_from_directory()的详细介绍可以参考文章:https://mtyjkh.blog.csdn.net/article/details/117018789
"""
train_ds = tf.keras.preprocessing.image_dataset_from_directory(
data_dir,
validation_split=0.2,
subset="training",
seed=12,
image_size=(img_height, img_width),
batch_size=batch_size)
"""
关于image_dataset_from_directory()的详细介绍可以参考文章:https://mtyjkh.blog.csdn.net/article/details/117018789
"""
val_ds = tf.keras.preprocessing.image_dataset_from_directory(
data_dir,
validation_split=0.2,
subset="validation",
seed=12,
image_size=(img_height, img_width),
batch_size=batch_size)



# 可以通过class_names输出数据集的标签。标签将按字母顺序对应于目录名称。
class_names = train_ds.class_names
print(class_names)

"""再次检查数据"""
for image_batch, labels_batch in train_ds:
print(image_batch.shape)
print(labels_batch.shape)
break


"""配置数据集"""
AUTOTUNE = tf.data.AUTOTUNE
def preprocess_image(image,label):
return (image/255.0,label)
# 归一化处理
train_ds = train_ds.map(preprocess_image, num_parallel_calls=AUTOTUNE)
val_ds = val_ds.map(preprocess_image, num_parallel_calls=AUTOTUNE)
train_ds = train_ds.cache().shuffle(1000).prefetch(buffer_size=AUTOTUNE)
val_ds = val_ds.cache().prefetch(buffer_size=AUTOTUNE)

"""可视化处理"""
def showImage():
plt.figure(figsize=(15, 10)) # 图形的宽为15高为10
for images, labels in train_ds.take(1):
for i in range(8):
ax = plt.subplot(5, 8, i + 1)
plt.imshow(images[i])
plt.title(class_names[labels[i]])
plt.axis("off")
plt.show()

3.模型构建

这里其实返回了一个VGG16的模型,里面用到了各种卷基层什么的。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
from keras import layers, models, Input
from keras.models import Model
from keras.layers import Conv2D, MaxPooling2D, Dense, Flatten, Dropout

def VGG16(nb_classes, input_shape):
input_tensor = Input(shape=input_shape)
# 1st block
x = Conv2D(64, (3,3), activation='relu', padding='same',name='block1_conv1')(input_tensor)
x = Conv2D(64, (3,3), activation='relu', padding='same',name='block1_conv2')(x)
x = MaxPooling2D((2,2), strides=(2,2), name = 'block1_pool')(x)
# 2nd block
x = Conv2D(128, (3,3), activation='relu', padding='same',name='block2_conv1')(x)
x = Conv2D(128, (3,3), activation='relu', padding='same',name='block2_conv2')(x)
x = MaxPooling2D((2,2), strides=(2,2), name = 'block2_pool')(x)
# 3rd block
x = Conv2D(256, (3,3), activation='relu', padding='same',name='block3_conv1')(x)
x = Conv2D(256, (3,3), activation='relu', padding='same',name='block3_conv2')(x)
x = Conv2D(256, (3,3), activation='relu', padding='same',name='block3_conv3')(x)
x = MaxPooling2D((2,2), strides=(2,2), name = 'block3_pool')(x)
# 4th block
x = Conv2D(512, (3,3), activation='relu', padding='same',name='block4_conv1')(x)
x = Conv2D(512, (3,3), activation='relu', padding='same',name='block4_conv2')(x)
x = Conv2D(512, (3,3), activation='relu', padding='same',name='block4_conv3')(x)
x = MaxPooling2D((2,2), strides=(2,2), name = 'block4_pool')(x)
# 5th block
x = Conv2D(512, (3,3), activation='relu', padding='same',name='block5_conv1')(x)
x = Conv2D(512, (3,3), activation='relu', padding='same',name='block5_conv2')(x)
x = Conv2D(512, (3,3), activation='relu', padding='same',name='block5_conv3')(x)
x = MaxPooling2D((2,2), strides=(2,2), name = 'block5_pool')(x)
# full connection
x = Flatten()(x)
x = Dense(4096, activation='relu', name='fc1')(x)
x = Dense(4096, activation='relu', name='fc2')(x)
output_tensor = Dense(nb_classes, activation='softmax', name='predictions')(x)

# 创建模型
model = Model(input_tensor, output_tensor)

# 显示模型参数
model.summary()

# 对模型进行编译
model.compile(optimizer="adam",
loss ='sparse_categorical_crossentropy',
metrics =['accuracy'])

return model

4.模型训练

这里训练模型,使用的就是分批次进行训练,最后保存模型为 model.h5 文件。

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import tensorflow as tf
from tqdm import tqdm
import keras.backend as K
import matplotlib.pyplot as plt

# 设置GPU
gpus = tf.config.list_physical_devices("GPU")
if gpus:
tf.config.experimental.set_memory_growth(gpus[0], True) #设置GPU显存用量按需使用
tf.config.set_visible_devices([gpus[0]],"GPU")
# 打印显卡信息,确认GPU可用
print(gpus)


import input_data
from model import VGG16

# 训练数据集
train_ds=input_data.train_ds
# 验证数据集
val_ds=input_data.val_ds
img_width=input_data.img_width
img_height=input_data.img_height

# 创建模型
model=VGG16(1000, (img_width, img_height, 3))

epochs = 10
lr = 1e-4

# 记录训练数据,方便后面的分析
history_train_loss = []
history_train_accuracy = []
history_val_loss = []
history_val_accuracy = []

for epoch in range(epochs):
train_total = len(train_ds)
val_total = len(val_ds)
"""
total:预期的迭代数目
ncols:控制进度条宽度
mininterval:进度更新最小间隔,以秒为单位(默认值:0.1)
"""
with tqdm(total=train_total, desc=f'Epoch {epoch + 1}/{epochs}',mininterval=1,ncols=100) as pbar:

lr = lr*0.92
K.set_value(model.optimizer.lr, lr)

for image,label in train_ds:
"""
训练模型,简单理解train_on_batch就是:它是比model.fit()更高级的一个用法

想详细了解 train_on_batch 的同学,
可以看看我的这篇文章:https://mtyjkh.blog.csdn.net/article/details/119506151
"""
history = model.train_on_batch(image,label)

train_loss = history[0]
train_accuracy = history[1]

pbar.set_postfix({"loss": "%.4f"%train_loss,
"accuracy":"%.4f"%train_accuracy,
"lr": K.get_value(model.optimizer.lr)})
pbar.update(1)
history_train_loss.append(train_loss)
history_train_accuracy.append(train_accuracy)

print('开始验证!')

with tqdm(total=val_total, desc=f'Epoch {epoch + 1}/{epochs}',mininterval=0.3,ncols=100) as pbar:
print("dddd")

for image,label in val_ds:

history = model.test_on_batch(image,label)

val_loss = history[0]
val_accuracy = history[1]

pbar.set_postfix({"loss": "%.4f"%val_loss,
"accuracy":"%.4f"%val_accuracy})
pbar.update(1)
history_val_loss.append(val_loss)
history_val_accuracy.append(val_accuracy)

print('结束验证!')
print("验证loss为:%.4f"%val_loss)
print("验证准确率为:%.4f"%val_accuracy)


"""评估模型"""
epochs_range = range(epochs)

plt.figure(figsize=(12, 4))
plt.subplot(1, 2, 1)

plt.plot(epochs_range, history_train_accuracy, label='Training Accuracy')
plt.plot(epochs_range, history_val_accuracy, label='Validation Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')

plt.subplot(1, 2, 2)
plt.plot(epochs_range, history_train_loss, label='Training Loss')
plt.plot(epochs_range, history_val_loss, label='Validation Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()


"""保存模型"""
model.save('model/model.h5')

这里可以看到,验证集的准确率都是 100% 了,感觉有些不正常,这个问题但是跑出的模型倒是能用,这个待以后再探讨。

多训练几次的话,每次的结果都不太一样。

5.测试和验证

原先的代码是从测试数据里面拿到多个图,然后分别判断每张图是什么,这里我直接改成了,给定一张图,判断这张图是什么分类。

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
32
33
34
35
36
37
38
39
40
41
42
import tensorflow as tf
import keras.utils as utils
from keras.models import load_model
import matplotlib.pyplot as plt
import numpy as np

# 加载模型
new_model = tf.keras.models.load_model('model/21_model.h5')

# 采用加载的模型(new_model)来看预测结果

plt.figure(figsize=(18, 3)) # 图形的宽为18高为5
plt.suptitle("预测结果展示")
# 支持中文
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号

filename='data/test/pensor_1.jpg'
img = utils.load_img(filename, target_size=(224, 224))
# 显示图片
plt.imshow(img)

# convert to array
img = utils.img_to_array(img)
# reshape into a single sample with 3 channels
# img = img.reshape(1, 224, 224, 3)
# center pixel data
img = img.astype('float32')

model = load_model('model/21_model.h5')


# 需要给图片增加一个维度
img_array = tf.expand_dims(img, 0)

# 使用模型预测图片中的人物
class_names=['cat','dog']
predictions = new_model.predict(img_array)
plt.title(class_names[np.argmax(predictions)])

plt.axis("off")
plt.show()
参考文章:
1.Tensorflow详解猫狗识别(4)–预测 这是tensorflow1.x版本的预测代码
2.利用Tensorflow训练好的模型,来预测或识别单张图片 这也是tensorflow1.xx的代码
3.[使用Tensorflow的Python Keras图像识别 model.predict返回[[0.]] 。](https://www.qiniu.com/qfans/qnso-49482561)
4.TensorFlow 2.0 model的predict()方法详解以及自定义predict实现预测数据和真实数据配对输出
5.Keras中predict()方法和predict_classes()方法的区别
6.tensorflow2加载图片
7.Keras model.predict输出的概率值转换为类别 predict函数训练后返回一个概率值数组,此数组的大小为n·k,第i行第j列上对应的数值代表模型对此样本属于某类标签的概率值,行和为1。例如预测结果为:[[0.66651809 0.53348191],代表预测样本的标签是0的概率为0.66651809,1的概率为0.53348191。这里还有一个需要注意的地方,本人在实际操作时发现,使用predict_classes()会出现报错:’Model’ object has no attribute ‘predict_classes’。这是因为本系统在搭建模型时,先进行了神经网络的编写,之后使用model = Model(input=inp, output=outp)方法,此时这种综合起来的方法无法使用predict_classes(),所以需要配合numpy.argmax()方法将样本最大概率归属类别的值转化为样本的预测数组。

6.迁移学习

小额赞助
本人提供免费与付费咨询服务,感谢您的支持!赞助请发邮件通知,方便公布您的善意!
**光 3.01 元
Sun 3.00 元
bibichuan 3.00 元
微信公众号
广告位
诚心邀请广大金主爸爸洽谈合作
每日一省
isNaN 和 Number.isNaN 函数的区别?

1.函数 isNaN 接收参数后,会尝试将这个参数转换为数值,任何不能被转换为数值的的值都会返回 true,因此非数字值传入也会返回 true ,会影响 NaN 的判断。

2.函数 Number.isNaN 会首先判断传入参数是否为数字,如果是数字再继续判断是否为 NaN ,不会进行数据类型的转换,这种方法对于 NaN 的判断更为准确。

每日二省
为什么0.1+0.2 ! == 0.3,如何让其相等?

一个直接的解决方法就是设置一个误差范围,通常称为“机器精度”。对JavaScript来说,这个值通常为2-52,在ES6中,提供了Number.EPSILON属性,而它的值就是2-52,只要判断0.1+0.2-0.3是否小于Number.EPSILON,如果小于,就可以判断为0.1+0.2 ===0.3。

每日三省
== 操作符的强制类型转换规则?

1.首先会判断两者类型是否**相同,**相同的话就比较两者的大小。

2.类型不相同的话,就会进行类型转换。

3.会先判断是否在对比 null 和 undefined,是的话就会返回 true。

4.判断两者类型是否为 string 和 number,是的话就会将字符串转换为 number。

5.判断其中一方是否为 boolean,是的话就会把 boolean 转为 number 再进行判断。

6.判断其中一方是否为 object 且另一方为 string、number 或者 symbol,是的话就会把 object 转为原始类型再进行判断。

每日英语
Happiness is time precipitation, smile is the lonely sad.
幸福是年华的沉淀,微笑是寂寞的悲伤。