澳大利亚的大堡礁美不胜收,是全球最大的珊瑚礁,也是多种多样的海洋生物栖息的家园。不幸的是,珊瑚礁面临蚕食珊瑚的棘冠海星(COTS)的威胁。为了控制COTS爆发,珊瑚礁管理人员使用一种名为Manta Tow勘查的方法,将潜水员拖在船后,目测评估珊瑚礁的各个部分。然而,这种方法在效率、数据质量和可扩展性方面存在局限性。
为了改善对COTS的监测和控制,大堡礁基金会启动了一项创新计划。一个关键部分就是使用水下摄像机采集珊瑚礁图像,并运用AI自动检测COTS。
为了针对这种基于视频的大规模勘查开发机器学习技术,澳大利亚国家科学机构CSIRO与谷歌进行合作。他们的目标是创建系统,以便能够准确高效地分析大量图像,从而近乎实时地查明大堡礁的COTS爆发情况。这将大大有助于环保工作,并有助于确保珊瑚礁生态系统受到长期保护。
总而言之,基于AI的图像分析正被应用于从大堡礁拍摄画面中自动检测棘冠海星,从而以大幅提高的效率和规模更好地监测和控制,以保护珊瑚礁。这里旨在建立一个使用水下珊瑚礁视频训练的对象检测模型,实时准确地识别海星。我们在这篇博文中将构建一个机器学习管道,以分析珊瑚礁的图像,并预测棘冠海星的存在和位置。
我们将使用Cleanvision(这个开源软件包使用以数据为中心的AI来清理图像数据中的问题)、Tensorflow、KerasCV以及YOLOv8,后者用于计算机视觉任务,比如对象检测和图像分类。
!pip install -q cleanvision keras-cv kaggle
由于数据量巨大,我们直接使用Kaggle下载并运行教程。为此,我们需要配置您的Kaggle凭据,并授予适当的权限,点击这里阅读更多(https://www.kaggle.com/discussions/general/74235?ref=hackernoon.com)。
! cp kaggle.json ~/.kaggle/
! chmod 600 ~/.kaggle/kaggle.json
下载和提取Kaggle数据集
!kaggle competitions download -c tensorflow-great-barrier-reef
!unzip /content/tensorflow-great-barrier-reef.zip
使用Cleanvision清理数据
CleanVision是一个以数据为中心的开源AI软件包,可以自动识别可能对计算机视觉系统产生负面影响的有问题的图像。它扫描图像数据集并检测常见的数据问题,比如模糊、光照不足和重复图像等。
在开发机器学习模型以确保可靠性之前,解决训练数据中的这些问题是必不可少的第一步。
CleanVision提供了一个简单的统一界面,仅运行相同的几行Python代码就可以审查任何图像集合,无论大小或来源如何。这使得清理脏的图像数据异常容易。
通过预先主动标记低质量、重复及其他不需要的图像,CleanVision在构建计算机视觉应用程序时有助于节省大量时间,并改善结果。
这是在运用机器学习处理基于图像的任务之前做好数据干净这项先决条件的快速方法。
from cleanvision import Imagelab
# Specify path to folder containing the image files in your dataset
imagelab = Imagelab(data_path="train_images/")
# Automatically check for a predefined list of issues within your dataset
imagelab.find_issues()
# Produce a neat report of the issues found in your dataset
imagelab.report()
import os
for st in imagelab.info['exact_duplicates']['sets']:
os.remove(st[0])
导入相关库
import pandas as pd
# show images inline
%matplotlib inline
import keras
import tensorflow
# import miscellaneous modules
import matplotlib.pyplot as plt
import cv2
import os
import numpy as np
import time
import tensorflow as tf
准备数据集进行预处理
我们将读取数据集,然后对其进行预处理,以便能够将其用于模型训练。我们先加载train CSV文件,并将注释由原始JSON格式转换为整数格式。
df_train = pd.read_csv("train.csv")
df_train=df_train.loc[df_train["annotations"].astype(str) != "[]"]
df_train['annotations'] = df_train['annotations'].apply(eval)
df_train['image_path'] = "train_images/video_" +
df_train['video_id'].astype(str) + "/" +
df_train['video_frame'].astype(str) + ".jpg"
df_extrain=df_train.explode('annotations') # Single annotation per row
df_extrain.reset_index(inplace=True)
df_extrain.head()
df_extrain_main=pd.DataFrame(pd.json_normalize(df_extrain['annotations']),
columns=['x', 'y', 'width', 'height']).join(df_extrain)
df_extrain_main['class']=0
df_extrain_main=df_extrain_main[['image_path','x','y','width','height','cl
ass','video_id','video_frame']]
df_extrain_main.head(10)
在CSV处理之后,我们尝试将CSV拆分成一个数据集,并使用适当的边界框格式、类和图像路径,以创建准备进一步处理的初步数据集。
def create_tf_example(rowss,data_df):
xmins = []
xmaxs = []
ymins = []
ymaxs = []
# Convert ---> [xmin,ymin,width,height] to [xmins,xmaxs,ymins,ymaxs]
xmin = rowss['x']
xmax = rowss['x']+rowss['width']
ymin = rowss['y']
ymax = rowss['y']+rowss['height']
#main_data.append((rowss['image_path'],xmins,xmaxs,ymins,ymaxs))
return rowss['image_path'],xmin,ymin,xmax,ymax
from PIL import Image, ImageDraw
paths = []
bboxes = []
classes = []
for index, row in df_extrain_main.iterrows():
if index % 1000 == 0:
print('Processed {0} images.'.format(index))
image_path,xmins,ymins,xmaxs,ymaxs=create_tf_example(row,df_extrain_main)
paths.append(image_path)
bboxes.append([[float(xmins),float(ymins),float(xmaxs),float(ymaxs)]])
classes.append([0])
这里,我们使用tf.rag.constant由bbox和classes列表创建不规则张量。不规则张量是一类张量,可以沿一个或多个维度处理不同长度的数据。这在处理具有可变长度序列的数据(如文本或时间序列数据)时非常有用。
在这种情况下,bbox和classes列表对于每个图像有不同的长度,这取决于图像中对象的数量以及相应的边界框和类。为了应对这种变化,我们使用不规则张量而不是规则张量。
稍后,这些不规则张量使用from_tensor_slices方法用于创建tf.data.Dataset。该方法通过沿第一个维度对输入张量进行切片来创建一个数据集。
通过使用不规则张量,数据集可以为每个图像处理不同长度的数据,并为进一步处理提供灵活的输入管道。
bbox = tf.ragged.constant(bboxes)
classes = tf.ragged.constant(classes)
image_paths = tf.ragged.constant(paths)
data = tf.data.Dataset.from_tensor_slices((image_paths, classes, bbox))
num_val = int(bbox.shape[0] * 0.2)
# Split the dataset into train and validation sets
val_data = data.take(num_val)
train_data = data.skip(num_val)
KerasCV包括流行的计算机视觉数据集(比如ImageNet、COCO和Pascal VOC)的预训练模型,可用于迁移学习。KerasCV还提供了一系列可视化工具,用于检查模型所学到的中间表示,以及直观显示对象检测和分割任务的结果。
在这个特定的笔记本中,我们使用YOLOV8,因此我们必须根据YOLOV8模型以兼容的格式格式化数据集。边界框输入格式应该如下所示:
bounding_boxes = {
# num_boxes may be a Ragged dimension
'boxes': Tensor(shape=[batch, num_boxes, 4]),
'classes': Tensor(shape=[batch, num_boxes]) }
字典有两个键box和classes,每个键都映射到TensorFlow RaggedTensor或Tensor对象。boxes张量的形状为[batch, num_boxes, 4],其中batch是批处理中的图像数量,num_boxes是任何图像中边界框的最大数量。4表示定义边界框所需的四个值:xmin、ymin、xmax和ymax。
classes张量的形状为[batch, num_boxes],其中每个元素代表boxes张量中相应边界框的类标签。num_boxes维度可能是不规则的,这意味着边界框的数量在批处理中的图像当中可能不同。
最终的模型输入看起来像这样:
{"images": images, "bounding_boxes": bounding_boxes}
def load_image(image_path):
image = tf.io.read_file(image_path)
image = tf.image.decode_jpeg(image, channels=3)
return image
def load_dataset(image_path, classes, bbox):
# Read Image
image = load_image(image_path)
bounding_boxes = {
"classes": tf.cast(classes, dtype=tf.float32),
"boxes": tf.cast(bbox, dtype=tf.float32),
}
return {"images": tf.cast(image, tf.float32), "bounding_boxes":
bounding_boxes}
数据增强
在构建对象检测模型时,运用有效的数据增强至关重要,但又具有挑战性。像裁切和翻转等转换必须正确地更新边界框坐标。手动执行此操作既复杂又容易出错。
KerasCV提供了专门的层来处理感知边界框的增强。它提供了广泛的转换,可以自动调整bboxes以匹配增强的图像。
借助KerasCV,这种集成的边界框处理可以轻松地将强大的增强功能整合到对象检测管道中。利用tf.data管道,增强可以在训练期间实时完成。
借助KerasCV强大的bbox感知增强功能,开发人员可以在对象检测模型中实现更多样化更有用的训练数据和改进的泛化,同时避免繁琐的坐标手动处理。这些层在幕后处理这种复杂性。
augmenter = keras.Sequential(
layers=[
keras_cv.layers.RandomFlip(mode="horizontal",
bounding_box_format="xyxy"),
keras_cv.layers.RandomShear(
x_factor=0.2, y_factor=0.2, bounding_box_format="xyxy"
),
keras_cv.layers.JitteredResize(
target_size=(640, 640), scale_factor=(0.75, 1.3),
bounding_box_format="xyxy"
),
]
)
resizing = keras_cv.layers.JitteredResize(
target_size=(640, 640),
scale_factor=(0.75, 1.3),
bounding_box_format="xyxy",
)
借助增强训练数据集
我们正在借助增强为对象检测模型准备训练数据集。我们先将batch_size设置为4,然后使用load_dataset函数来加载数据。
接下来,我们对数据进行大清洗,以确保我们的模型不会在特定类型的数据上过拟合。然后,我们使用ragged_batch函数以固定大小4对数据进行批处理,并删除任何剩余的数据。
最后,我们对数据集运用augmenter函数进行数据增强,这有助于增加数据集的多样性,并提高模型的精度。
num_parallel_calls参数被设置为tf.data.AUTOTUNE,允许TensorFlow动态调整并行调用的数量,以提高性能。
BATCH_SIZE =4
train_ds = train_data.map(load_dataset, num_parallel_calls=tf.data.AUTOTUNE)
train_ds = train_ds.shuffle(BATCH_SIZE * 4)
train_ds = train_ds.ragged_batch(BATCH_SIZE, drop_remainder=True)
train_ds = train_ds.map(augmenter, num_parallel_calls=tf.data.AUTOTUNE)
验证数据集
在这个代码块中,我们通过将'load_dataset'函数映射到'val_data'集来创建一个验证数据集。该函数有助于从数据集加载数据,并为模型对其进行预处理。
我们还将'num_parallel_calls'设置为'tf.data.AUTOTUNE’,以启用并行机制,从而加快处理速度。
接下来,我们将验证数据集按“BATCH_SIZE * 4”的因子进行大清洗,以增强随机性并防止过拟合。最后,我们用'drop_remainder=True'创建一个'BATCH_SIZE'的'ragged_batch',以确保所有批次大小相同。
这有助于使模型在训练期间更高效更一致。
val_ds = val_data.map(load_dataset, num_parallel_calls=tf.data.AUTOTUNE)
val_ds = val_ds.shuffle(BATCH_SIZE * 4)
val_ds = val_ds.ragged_batch(BATCH_SIZE, drop_remainder=True)
边界框可视化
我们使用keras_cv库来实现边界框可视化。它导入必要的软件包,并为数据集定义类映射。visualize_dataset()函数接受数据集输入,并使用plot_bounding_box_gallery()函数来显示边界框被覆盖的图像。您可以通过train_ds和val_ds数据集看到这一点。
import keras_cv
from keras_cv import bounding_box
from keras_cv import visualization
class_mapping = {0:'fish'}
def visualize_dataset(inputs, value_range, rows, cols,
bounding_box_format):
inputs = next(iter(inputs.take(1)))
images, bounding_boxes = inputs["images"], inputs["bounding_boxes"]
visualization.plot_bounding_box_gallery(
images,
value_range=value_range,
rows=rows,
cols=cols,
y_true=bounding_boxes,
scale=5,
font_scale=0.7,
bounding_box_format=bounding_box_format,
class_mapping=class_mapping,
)
visualize_dataset(
train_ds, bounding_box_format="xyxy", value_range=(0, 255), rows=2,
cols=2
)
visualize_dataset(
val_ds, bounding_box_format="xyxy", value_range=(0, 255), rows=2,
cols=2
)
接下来,我们解压缩数据集,以便能够将其馈送到Keras API的模型训练函数。
def dict_to_tuple(inputs):
return inputs["images"], inputs["bounding_boxes"]
train_ds = train_ds.map(dict_to_tuple,
num_parallel_calls=tf.data.AUTOTUNE)
train_ds = train_ds.prefetch(tf.data.AUTOTUNE)
val_ds = val_ds.map(dict_to_tuple, num_parallel_calls=tf.data.AUTOTUNE)
val_ds = val_ds.prefetch(tf.data.AUTOTUNE)
模型构建
YOLOv8是流行的YOLO(你只看一次)系列模型的最新版本,用于对象检测和图像分类之类的计算机视觉任务。它是由Ultralytics在之前的YOLOv5模型的基础上创建的。
与以前的版本相比,YOLOv8集成了架构方面的众多升级,以提高精度和性能。创建者还旨在提升开发人员的整体体验。
作为业界领先的YOLO系列的最新版本,YOLOv8代表了对象检测等领域当前的最高水平。它结合了算法方面的进步,为从业人员不断提升CV能力和可用性方面的改进。
Keras-CV提供多种YOLOV8模型:YOLOv8n、YOLOv8s、YOLOv8m、YOLOv8l和YOLOv8x。就本例而言,我们选择YOLOV8s主力模型,并使用coco预训练权重来加载它。
接下来,我们使用YOLOV8 detector构建一个YOLOV8模型,接受特征提取器作为主干参数,num_classes参数根据class_mapping列表的大小指定要检测的对象类的数量,bounding_box_format参数告知模型数据集中bbox的格式。最后,特征金字塔网络(FPN)深度由fpn_depth参数指定。
backbone = keras_cv.models.YOLOV8Backbone.from_preset(
"yolo_v8_s_backbone_coco" # We will use yolov8 small backbone with
coco weights
)
yolo = keras_cv.models.YOLOV8Detector(
num_classes=1,
bounding_box_format="xyxy",
backbone=backbone,
fpn_depth=1,
)
编译模型
YOLOv8模型的训练过程涉及使用两种类型的损失:分类损失和边界框损失。
分类损失度量每个检测对象的预测类概率与真实类概率之间的差异。在这种情况下使用二元交叉熵损失,因为每个检测到的项目要么属于特定的类,要么不属于特定的类。该损失函数有助于优化模型以实现准确的对象分类。
另一方面,边界框损失计算预测的边界框与实际边界框之间的差异度。YOLOv8使用全面的IoU(CIoU)而不是基本的IoU来计算边界框损失。CIoU还考虑额外的因素,比如边界框的长宽比、中心距离和比例。如果查看更多的边界框属性(不仅限于重叠),这有助于更好地表示边界框的相似度。
通过在训练过程中针对对象分类和精确定位进行共同优化,分类损失和边界框损失帮助YOLOv8通过最小化跨类和边界框坐标的输出和标签数据之间的差异,生成高度准确的对象检测。
optimizer = tf.keras.optimizers.Adam(
learning_rate=10e-3,
)
yolo.compile(
optimizer=optimizer, classification_loss="binary_crossentropy",
box_loss="ciou"
)
训练模型
在每个轮次(epoch)期间,模型迭代式调整其参数,以实现预测输出与实际输出之间的差异最小化。这个过程帮助模型学习如何在珊瑚礁的水下视频中准确地检测到棘冠海星。
轮次的数量可以根据训练数据集的大小、模型的复杂性以及所需的精度加以调整。经过训练,模型可用于实时预测海星的存在和位置,帮助研究人员和环保主义者更有效地监测和控制COTS爆发。
yolo.fit(
train_ds,
epochs=3,
)
结论
在这个项目中,集成Cleanvision、TensorFlow、KerasCV和YOLOv8体现了AI在环保方面的强大功能和多功能性,特别是对大堡礁而言。
利用这些先进工具具有的优势,我们开发了一个强大的模型,能够在复杂的水下环境中准确识别棘冠海星。
这不仅标志着在保护世界自然奇观方面向前迈出了重要一步,还展示了AI在应对生态挑战方面具有的巨大潜力。
原文标题:How to Use TensorFlow and Cleanvision to Detect Starfish Threats in the Great Barrier Reef,作者:Aravind Putrevu
链接:
https://hackernoon.com/how-to-use-tensorflow-and-cleanvision-to-detect-starfish-threats-in-the-great-barrier-reef。