为什么我在kaggle平台上编写猫狗识别程序,一是因为我的个人电脑没有GPU,kaggle平台提供了GPU,使用GPU可以大大提高训练的效率;二是kaggle平台上提供了猫狗数据集。

1、部署kaggle开发环境,这部分不再描述,下面是我的设置情况。

pytorch cnn猫狗识别 基于kaggle猫狗数据集的识别_pytorch cnn猫狗识别

2、在kaggle平台上安装GPU版本的tensorflow2.3.0

pip install tensorflow-gpu==2.3.0

3、导入需要的包

import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
%matplotlib inline
import numpy as np
import glob
import os

4、读取数据集,获取数据集中每条数据的路径,路径存放到列表中。其中,train_image_path用于存放训练数据集,test_image_path用于存放验证数据集。

train_image_path = glob.glob('../input/cat-and-dog/training_set/training_set/*/*.jpg')
test_image_path = glob.glob('../input/cat-and-dog/test_set/test_set/*/*.jpg')

5、对训练数据进行乱序处理。

np.random.shuffle(train_image_path)

注意:对训练数据进行乱序处理很重要,我最初就是因为没有对训练数据集进行乱序处理,导致了在测试数据集上的识别精度始终为50%左右,无法提高。

6、获取数据集中的每条数据对应的标签。dog的标签值为0,cat的标签值为1。

#p为每张图片的路径
#p.split('/')[-2]作用是对路径进行切分,并取出倒数第2个元素,该元素的值为cat或者dog
#如果p.split('/')[-2]的值为cat则返回1,如果为dog则返回0
#train_image_label为每张图片的标签,图片为cat值为1,图片为dog值为0
train_image_label = [int(p.split('training_set/training_set/')[1].split('/')[0] == 'cats') for p in train_image_path]
test_image_label = [int(p.split('test_set/test_set/')[1].split('/')[0] == 'cats') for p in test_image_path]

7、查看一下训练数据集和测试数据集有多少条数据

len(train_image_path)

结果为8005

len(test_image_path)

结果为2023

8、定义图片的预处理函数

def load_preprosess_image(path, label):
    image = tf.io.read_file(path)
    image = tf.image.decode_jpeg(image, channels=3) #由于图像是彩色的,所以channels=3
    image = tf.image.resize(image, [360, 360]) #将图片大小值为360*360
    image = tf.image.random_flip_left_right(image)
    image = tf.image.random_flip_up_down(image)
    image = tf.cast(image, tf.float32) #图片像素值的默认类型为uint8,现在需要将数据类型转换为tf.float32
    image = image/255 #图片每一像素的值范围是0-255,现在需做归一化处理,所以除以255
    label = tf.reshape(label, [1]) #label的形式为[1,2,3],将label的形式转化为[[1], [2], [3]]
    return image, label

9、将图片的路径和标签组合列表中的一项

train_image_ds = tf.data.Dataset.from_tensor_slices((train_image_path, train_image_label))
test_image_ds = tf.data.Dataset.from_tensor_slices((test_image_path, test_image_label))

10、调用load_preprosess_image进行图片预处理。

AUTOTUNE = tf.data.experimental.AUTOTUNE #TensorFlow根据系统的实际情况,自动选择合适的值
#调用load_preprosess_image函数,进行图片处理。num_parallel_calls为TensorFlow选择的cpu的核心数。
train_image_ds = train_image_ds.map(load_preprosess_image, num_parallel_calls=AUTOTUNE)
#调用load_preprosess_image函数,进行图片处理。num_parallel_calls为TensorFlow选择的cpu的核心数。
test_image_ds = test_image_ds.map(load_preprosess_image, num_parallel_calls=AUTOTUNE)

11、下面可以看一下数据集的信息

test_image_ds

结果为<ParallelMapDataset shapes: ((360, 360, 3), (1,)), types: (tf.float32, tf.int32)>,表示每张图片的大小为360*360,厚度为3(即彩色图片),对应图片标签的长度为1。

train_image_ds

结果为<ParallelMapDataset shapes: ((360, 360, 3), (1,)), types: (tf.float32, tf.int32)>,表示每张图片的大小为360*360,厚度为3(即彩色图片),对应图片标签的长度为1。

12、设置数据集的相关属性等操作。训练时,每次从数据集中读取一批(即32条数据)。

BATCH_SIZE = 32
train_count = len(train_image_path)
test_count = len(test_image_path)

train_image_ds = train_image_ds.shuffle(500).batch(BATCH_SIZE)
train_image_ds = train_image_ds.prefetch(AUTOTUNE) #在训练期间,后台同时读取数据并进行转换操作,目的是提高效率
test_image_ds = test_image_ds.batch(BATCH_SIZE)
test_image_ds = test_image_ds.prefetch(AUTOTUNE) #在训练期间,后台同时读取数据并进行转换操作,目的是提高效率

13、下面从train_image_ds中读取一次数据

imgs, labels = next(iter(train_image_ds))

通过下面代码可以查看读取到的数据的形状

imgs.shape

结果为TensorShape([32, 360, 360, 3]),32代表一批次32张图片,每张图片的大小为360*360,厚度为3(即彩色图片)。

下面代码查看显示最后一张图片

plt.imshow(imgs[-1])

pytorch cnn猫狗识别 基于kaggle猫狗数据集的识别_人工智能_02

 

14、构建训练模型

model = keras.Sequential([
    tf.keras.layers.Conv2D(64, (3, 3), input_shape=(256, 256, 3), activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.Conv2D(64, (3, 3), activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.Conv2D(128, (3, 3), activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(256, (3, 3), activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.Conv2D(256, (3, 3), activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(512, (3, 3), activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.Conv2D(512, (3, 3), activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Conv2D(1024, (3, 3), activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.Conv2D(1024, (3, 3), activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.GlobalAveragePooling2D(),
    tf.keras.layers.Dense(256, activation='relu'),
    tf.keras.layers.BatchNormalization(),
    tf.keras.layers.Dense(1)
])

下面代码可以查看该模型的情况

model.summary()

部分结果为

pytorch cnn猫狗识别 基于kaggle猫狗数据集的识别_深度学习_03

15、执行下面代码,使用该模型就可以对图片进行预测。

pred = model(imgs)

看一下pred

pred.shape

结果为TensorShape([32, 1]),由于imgs是32张图片,预测的结果也是输出32项结果。

问题来了,该模型尚未进行训练,为什么能输出结果来,这是因为构建模型后,会分配给模型的相应的默认权重,因此也能输出结果,当然结果是十分不准确的。要想输出的结果准确,就需要训练,训练的过程就是逐步更新权重的过程。

16、更新权重的过程包含当前模型预测结果与标签值的比较,通过比较出来的误差来更新模型的权重。那么如何比较呢,通过二元交叉熵来比较。下面代码定义二元交叉熵。

ls = tf.keras.losses.BinaryCrossentropy()  #二元交叉熵

下面举例比较

ls([0., 0., 1., 1.], [1., 1., 1., 1.])

结果为<tf.Tensor: shape=(), dtype=float32, numpy=7.6666193>

ls([[0.], [0.], [1.], [1.]], [[1.], [1.], [1.], [1.]])

结果为<tf.Tensor: shape=(), dtype=float32, numpy=7.6666193>

也可以下面这样

#二元交叉熵, binary_crossentropy为小写,前面的BinaryCrossentropy为大写,区别就是小写的必须传入参数,如下。
tf.keras.losses.binary_crossentropy([0., 0., 1., 1.], [1., 1., 1., 1.])

17、定义优化器、平均损失、正确率等

optimizer = tf.keras.optimizers.Adam(learning_rate = 0.0001)#learning_rate = 0.01
epoch_loss_avg = tf.keras.metrics.Mean('train_loss') # 计算平均损失值
train_accuracy = tf.keras.metrics.Accuracy() #计算正确率
epoch_loss_avg_test = tf.keras.metrics.Mean('test_loss') # 计算平均损失值
test_accuracy = tf.keras.metrics.Accuracy() #计算正确率

18、定义每一步的训练函数

def train_step(model, images, labels):
    with tf.GradientTape() as t:
        pred = model(images)
        loss_step = tf.keras.losses.BinaryCrossentropy(from_logits=True)(labels, pred)
    grads = t.gradient(loss_step, model.trainable_variables)
    optimizer.apply_gradients(zip(grads, model.trainable_variables))
    epoch_loss_avg(loss_step)
    train_accuracy(labels, tf.cast(pred>0, tf.int32))

19、定义每一步的测试函数

def test_step(model, images, labels):
    pred = model(images, training=False)
    loss_step = tf.keras.losses.BinaryCrossentropy(from_logits=True)(labels, pred)
    epoch_loss_avg_test(loss_step)
    test_accuracy(labels, tf.cast(pred>0, tf.int32))

20、训练

num_epochs = 30

for epoch in range(num_epochs):
    for imgs_, labels_ in train_image_ds:
        train_step(model, imgs_, labels_)
        print('.', end='')
    print()
    
    train_loss_results.append(epoch_loss_avg.result())
    train_acc_results.append(train_accuracy.result())
    
    for imgs_, labels_ in test_image_ds:
        test_step(model, imgs_, labels_)
        
    test_loss_results.append(epoch_loss_avg_test.result())
    test_acc_results.append(test_accuracy.result())
    
    print('Epoch:{}: loss: {:.3f}, accuracy: {:.3f}, test_loss: {:.3f}, test_accuracy: {:.3f}'.format(
        epoch + 1,
        epoch_loss_avg.result(),
        train_accuracy.result(),
        epoch_loss_avg_test.result(),
        test_accuracy.result()
    ))
    
    epoch_loss_avg.reset_states()
    train_accuracy.reset_states()
    
    epoch_loss_avg_test.reset_states()
    test_accuracy.reset_states()

下面是部分的输出结果

pytorch cnn猫狗识别 基于kaggle猫狗数据集的识别_人工智能_04

pytorch cnn猫狗识别 基于kaggle猫狗数据集的识别_pytorch cnn猫狗识别_05

可以看出,随着训练次数的不断增多,在训练数据集和测试数据集上的识别准确率在不断的提高。