在本节中,将针对飞机场的飞机卫星图片和湖泊卫星图片进行卷积,使用CNN卷积神经网络实现图片的识别。这是一个较为完整的实战案例。从图片和标签的读取到神经网路的建立以及模型的训练。最后使用几张网上的图片进行概率预测。

图片数据集

读取图片路径和标签

import tensorflow as tf
from tensorflow import keras
import matplotlib.pyplot as plt
import numpy as np
import glob
import random

# 获取所有的图片路径,使用glob
# 获取E:\\BaiduNetdiskDownload\\2_class下所有文件夹下的所有jpg后缀的路径
# glob.glob()可同时获取所有的匹配路径
all_image_path = glob.glob("E:\\BaiduNetdiskDownload\\2_class\\*\\*.jpg")

# 对图片进行乱序,希望每次学习的时候都能看到不一样的数据
# 这样可以获得更好的训练效果,只需要把all_image_path打乱即可
random.shuffle(all_image_path)

# 设置标签
# 这里设置airplane为0,lake为1,这是给计算机看的
# 计算机不能用字符串进行训练
label_to_index = {'airplane':0, 'lake':1}

# 对上面的字典进行反转
# 给人看的不能是0和1,而是字符串“airplane”或者“lake”
index_to_label = dict((v,k) for k,v in label_to_index.items())

# 使用split进行切分img路径
all_labels = [label_to_index.get(img.split("\\")[-2])  for img in all_image_path ]

#  未完待续.......

运行的结果

知识点补充:glob以及glob.glob

glob模块Python中的通配符标准库,一般可以用来方便地获取文件路径。只要安装了Python就会自带这个库,不需要单独安装

glob模块常用来查找文件目录和文件,并将搜索的到的结果返回到一个列表中,常见的两个方法有glob.glob()和glob.iglob(),可以和常用的find功能进行类比,glob支持*?[]这三种通配符。glob.glob()可同时获取所有的匹配路径,而glob.iglob()一次只能获取一个匹配路径。

  • *代表0个或多个字符
  • ?代表一个字符
  • []匹配指定范围内的字符,如[0-9]匹配数字
#1、定义一个函数,搜索C盘下的所有文件和目录,并返回一个列表(这里使用import glob)
import glob
def search():
    f = glob.glob(r'c:\*')  # glob.glob表示glob模块下的glob函数
    print(f)
if __name__ == "__main__":
    search()


#2、定义一个函数查询E盘中以.exe结尾的文件有哪些(这里使用from glob import glob):
from glob import glob#导入模块的同名函数
def search():
    f = glob(r'e:\*.exe')#直接调用函数
        for i in f:
            print(i)
if __name__ == "__main__":
    search()

图像的读取和解码

图像的读取和解码在TensorFlow中有内置的函数,分别为tf.io.read_file(img)tf.image.decode_jpeg(img_2),decode_jpeg是指针对jpg或者jpeg格式的图片进行解码,也有decode_png、decode_image等函数。建议针对固定格式使用对应的函数。

这里,可以定义一个函数来进行处理。

def load_image(path):
    img_raw = tf.io.read_file(path)                        # 读取图片,转化为二进制数据
    img_tensor = tf.image.decode_jpeg(img_raw, channels=3) # 解码二进制,转换为tensor类型的数据,彩色图片,通道为3
    img_tensor = tf.image.resize(img_tensor, [256,256]) # 对图片大小进行限制,规范到256*256,对于图片大小不一的很有用
    img_tensor = tf.cast(img_tensor, tf.float32)        # 转化数据类型
    img_tensor = img_tensor/255                         # 归一化进行处理
    return img_tensor                                   # 返回转换后的值

tf.cast()函数:执行 tensorflow 中张量数据类型转换,比如读入的图片如果是int8类型的,一般在要在训练前把图像的数据格式转换为float32。Tensor默认的数据类型是32位float类型。

创建Dataset

tf.data模块怎么用?创建Dataset以及基本操作(dataset.shuffle、dataset.batch、dataset.repeat)

map()函数以迭代的方式将提供的功能应用于每个项目。

# 创建img的dataset
img_ds = tf.data.Dataset.from_tensor_slices(all_image_path)  
img_ds = img_ds.map(load_image)

# 创建label的dataset
label_ds = tf.data.Dataset.from_tensor_slices(all_labels)

# 将其进行合并,使用zip
img_label_ds = tf.data.Dataset.zip((img_ds,label_ds)) 

划分训练和测试dataset

我们想要同时又训练数据集,也有测试数据集。

假如使用20%的进行测试,80%的进行训练。这是可以使用take进行划分训练数据集和测试数据集。

image_count = len(all_image_path)       # 所有图片的数量
test_count = int(image_count*0.2)       # 拿出20%的量进行测试
train_count = image_count - test_count  # 剩下的80%进行训练

train_ds = img_label_ds.skip(test_count)
test_ds = img_label_ds.take(test_count)

# 训练数据:使用repeat进行数据重复,并且进行乱序和batch设置
train_ds = train_ds.repeat().shuffle(100).batch(16)
# 测试数据:设置batch
test_ds = test_ds.batch(16)

模型的创建

model = tf.keras.Sequential()
model.add(tf.keras.layers.Conv2D(64,(3,3),
                                 input_shape=(256,256,3),
                                 activation='relu'))   
# 这里设置64个卷积核,每一个卷积核都会扫过图片,形成一个特征层.核的大小是3X3
# 在第一层需要告知输入的层的大小,为256*256,3通道,RGB
# 激活函数是relu
# 再添加一个卷积层
model.add(tf.keras.layers.Conv2D(64,(3,3),activation='relu'))   
model.add(tf.keras.layers.MaxPool2D())

model.add(tf.keras.layers.Conv2D(128,(3,3),activation='relu'))   
model.add(tf.keras.layers.Conv2D(128,(3,3),activation='relu'))   
model.add(tf.keras.layers.MaxPool2D())

model.add(tf.keras.layers.Conv2D(256,(3,3),activation='relu'))   
model.add(tf.keras.layers.Conv2D(256,(3,3),activation='relu'))   
model.add(tf.keras.layers.MaxPool2D())
# 卷积核是依次按照两倍的数量增长,有更好的特征提取的效果

model.add(tf.keras.layers.Conv2D(512,(3,3),activation='relu'))   
model.add(tf.keras.layers.Conv2D(512,(3,3),activation='relu')) 
model.add(tf.keras.layers.Conv2D(512,(3,3),activation='relu')) 

# 再添加一个全局平均池化,将四维数组[batch height width channel]转化为二维的[batch imageCharacter]
# 这是为了在Dense层进行读取数据
model.add(tf.keras.layers.GlobalAveragePooling2D())

# 添加Dense层
model.add(tf.keras.layers.Dense(1024, activation='relu'))
model.add(tf.keras.layers.Dense(256, activation='relu'))
model.add(tf.keras.layers.Dense(1, activation='sigmoid'))   # 逻辑回归,二分类

查看模型的大小:

Model: "sequential_1"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
conv2d (Conv2D)              (None, 254, 254, 64)      1792      
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 252, 252, 64)      36928     
_________________________________________________________________
max_pooling2d (MaxPooling2D) (None, 126, 126, 64)      0         
_________________________________________________________________
conv2d_2 (Conv2D)            (None, 124, 124, 128)     73856     
_________________________________________________________________
conv2d_3 (Conv2D)            (None, 122, 122, 128)     147584    
_________________________________________________________________
max_pooling2d_1 (MaxPooling2 (None, 61, 61, 128)       0         
_________________________________________________________________
conv2d_4 (Conv2D)            (None, 59, 59, 256)       295168    
_________________________________________________________________
conv2d_5 (Conv2D)            (None, 57, 57, 256)       590080    
_________________________________________________________________
max_pooling2d_2 (MaxPooling2 (None, 28, 28, 256)       0         
_________________________________________________________________
conv2d_6 (Conv2D)            (None, 26, 26, 512)       1180160   
_________________________________________________________________
conv2d_7 (Conv2D)            (None, 24, 24, 512)       2359808   
_________________________________________________________________
conv2d_8 (Conv2D)            (None, 22, 22, 512)       2359808   
_________________________________________________________________
global_average_pooling2d (Gl (None, 512)               0         
_________________________________________________________________
dense (Dense)                (None, 1024)              525312    
_________________________________________________________________
dense_1 (Dense)              (None, 256)               262400    
_________________________________________________________________
dense_2 (Dense)              (None, 1)                 257       
=================================================================
Total params: 7,833,153
Trainable params: 7,833,153
Non-trainable params: 0

模型的编译核训练

不使用optimizer="adma"来设置优化器,而是使用optimizer=tf.keras.optimizers.Adam()可以更加灵活设置相关参数。这里建议使用较小的学习速率或者是使用默认的速率。optimizer="adma"这种方式就是全部使用默认参数且不可更改。

使用实例来设置优化器

model.compile(optimizer=tf.keras.optimizers.Adam(0.0001),
              loss = tf.keras.losses.BinaryCrossentropy(),
              metrics=['acc']
             )

# 在前面,设置了train_ds = train_ds.repeat().shuffle(100).batch(16)
# repeat就是重复输入,那么需要告知重复多少次是训练完了一轮
BATCH_SIZE = 16
step_per_epoch = train_count // BATCH_SIZE
val_step = test_count // BATCH_SIZE

history = model.fit(train_ds,epochs = 10,
                    steps_per_epoch = step_per_epoch,
                    validation_data = test_ds,
                    validation_steps = val_step
                   )

训练结果

可以将结果绘制出来,更加清晰:

plt.plot(history.epoch, history.history.get('acc'),label='acc')
plt.plot(history.epoch, history.history.get('val_acc'),label='val_acc')

plt.plot(history.epoch, history.history.get('loss'),label='loss')
plt.plot(history.epoch, history.history.get('val_loss'),label='val_loss')
plt.legend()

绘制正确率和loss

这里的建立模型的时候没有使用Dropout进行抑制过拟合,有需要的可以去看下面这篇文章:

什么是过拟合?什么是欠拟合?过拟合解决方法之使用Dropout抑制

使用模型进行预测

不能将用来测试的图片直接进行进行预测(predict),因为图片的维度不够。

解释为什么维度不够

扩展维度

path1 = 'E:\\test01.png'
path2 = 'E:\\test02.png'

# 加载图片函数(上面已经进行了封装)
image_test1 = load_image(path1)
image_test2 = load_image(path2)

image_test1 = tf.expand_dims(image_test1, axis=0)
image_test2 = tf.expand_dims(image_test2, axis=0)

pred = model.predict(image_test1)
if 1>=pred>0.5:
    print("This is leak")
if 0.5>pred>0:
    print("This is airplane")

pred = model.predict(image_test2)
if 1>=pred>0.5:
    print("This is leak")
if 0.5>pred>0:
    print("This is airplane")

预测效果

可以将这个预测函数进行封装,定义一个函数,只需要传入预测图片的路径即可:

def pre_img(img_path):
    image_test = load_image(img_path)  # 加载图片函数load_image(上面已经进行了封装)
    image_test = tf.expand_dims(image_test, axis=0)
    pred = model.predict(image_test)
    if 1>=pred>0.5:
        print("This is leak")
    if 0.5>pred>0:
        print("This is airplane")

我再补充几张图片来进行预测:

预测结果

数据集下载

解压密码:www.52txr.cn

卫星图像数据识别压缩包(含测试test).zip

压缩包


版权声明 ▶ 本网站名称:陶小桃Blog
▶ 本文链接:https://www.52txr.cn/2022/lakeplane.html
▶ 本网站的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,请联系站长进行核实删除。
▶ 转载本站文章需要遵守:商业转载请联系站长,非商业转载请注明出处!!
▶ 站长邮箱 [email protected][email protected] ,如不方便留言可邮件联系。

最后修改:2022 年 09 月 27 日
如果觉得我的文章对你有用,请随意赞赏