数据地址为:https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition/data

只需下载其中的train.zip文件,下载解压后,图片文件存在一个名称为train的文件夹下,内容如下图显示。都为jpg格式文件,以cat或dog开头,之后是图片的索引号(从0到12499),即猫和狗的图片各有12500张。
20211118214555.jpg

:::info 本节的数据都放在了同一文件夹下,文件名称中直接含有标签,数据准备阶段会比较容易。第27章的数据准备阶段遇到更复杂的问题,感兴趣的读者可以关注27.1节的内容。 :::

26.1.1 获取文件列表和标签

图片文件夹位于当前数据文件中,名称为“train”,由于后续的操作要多次用到,设置该文件夹路径为IMAGE_DIR,代码如下所示:

  1. static final String DATA_DIR = Utils.ROOT_DIR + "dog_cat" + File.separator;
  2. static final String IMAGE_DIR = DATA_DIR + "train/";

所有图片文件都在IMAGE_DIR文件夹下,只需使用File的list方法,就可以获取全部文件名;构造MemSourceBatchOp,将文件列表作为其唯一的数据,并设置列名称为“relative_path”;随后,从文件名称中是否包含dog或者cat,来确定对应的标签值,标签列名称为“label”;然后打印出10条,确定一下操作效果;最后,保存到”list_all.ak”。具体代码如下:

  1. new MemSourceBatchOp(new File(IMAGE_DIR).list(), "relative_path")
  2. .select("relative_path, REGEXP_EXTRACT(relative_path, '(dog|cat)') AS label")
  3. .lazyPrint(10)
  4. .link(
  5. new AkSinkBatchOp()
  6. .setFilePath(DATA_DIR + "list_all.ak")
  7. .setOverwriteSink(true)
  8. );
  9. BatchOperator.execute();

运行结果如下,

  1. relative_path|label
  2. -------------|-----
  3. dog.8011.jpg|dog
  4. cat.5077.jpg|cat
  5. dog.7322.jpg|dog
  6. cat.2718.jpg|cat
  7. cat.10151.jpg|cat
  8. cat.3406.jpg|cat
  9. dog.1753.jpg|dog
  10. cat.4369.jpg|cat
  11. cat.7660.jpg|cat
  12. dog.5535.jpg|dog

26.1.2 划分训练、测试集

我们随机抽取几张图片进行查看,图片的尺寸并不是统一的,在分类计算前,一定要“Reshape”为相同的尺寸。在本章的计算中,我们会将图片统一到32X32的尺寸,在个人电脑使用CPU就可以进行实验;也会将图片统一到96X96的尺寸,这样可以符合TF Hub的EfficientNet模型对输入数据的要求。

在实际操作中,我们会先从文件总表中划分出训练集和预测集各自的文件列表,然后,对于训练集的文件列表,分别以32X32的尺寸和96X96的尺寸,读取图像文件数据为Tensor形式,并分别保存到TRAIN_32_FILE和TRAIN_96_FILE;对于测试集也做同样的操作。这样保证了不同尺寸训练、测试集都对应着同样的原始图片训练、测试集。

从文件总表中划分出训练集和预测集的代码如下:

  1. Utils.splitTrainTestIfNotExist(
  2. new AkSourceBatchOp().setFilePath(DATA_DIR + "list_all.ak"),
  3. DATA_DIR + "list_train.ak",
  4. DATA_DIR + "list_test.ak",
  5. 0.9
  6. );

26.1.3 读取图片文件

本节需要用到图片列转换为张量组件:ReadImageToTensorStreamOp,该组件的参数说明如下:

名称 描述 类型 是否必须?
outputCol 输出结果列的列名 String
relativeFilePathCol 相对根目录的文件路径列的列名 String
rootFilePath 文件根目录路径 String
reservedCols 算法保留列 String[]
imageWidth 图片宽度 Integer
imageHeight 图片高度 Integer

更多介绍参见该组件的文档 https://www.yuque.com/pinshu/alink_doc/readimagetotensorstreamop

对于训练集的文件列表,分别以32X32的尺寸和96X96的尺寸,读取图像文件数据为Tensor形式,并分别保存到TRAIN_32_FILE和TRAIN_96_FILE,具体代码如下:

  1. new AkSourceStreamOp()
  2. .setFilePath(DATA_DIR + "list_train.ak")
  3. .link(
  4. new ReadImageToTensorStreamOp()
  5. .setRelativeFilePathCol("relative_path")
  6. .setRootFilePath(IMAGE_DIR)
  7. .setImageWidth(32)
  8. .setImageHeight(32)
  9. .setOutputCol("tensor")
  10. )
  11. .link(
  12. new AkSinkStreamOp()
  13. .setFilePath(DATA_DIR + TRAIN_32_FILE)
  14. .setOverwriteSink(true)
  15. );
  16. StreamOperator.execute();
  17. new AkSourceStreamOp()
  18. .setFilePath(DATA_DIR + "list_train.ak")
  19. .link(
  20. new ReadImageToTensorStreamOp()
  21. .setRelativeFilePathCol("relative_path")
  22. .setRootFilePath(IMAGE_DIR)
  23. .setImageWidth(96)
  24. .setImageHeight(96)
  25. .setOutputCol("tensor")
  26. )
  27. .link(
  28. new AkSinkStreamOp()
  29. .setFilePath(DATA_DIR + TRAIN_96_FILE)
  30. .setOverwriteSink(true)
  31. );
  32. StreamOperator.execute();

对于测试集的文件列表,分别以32X32的尺寸和96X96的尺寸,读取图像文件数据为Tensor形式,并分别保存到TEST_32_FILE和TEST_96_FILE,具体代码如下:

  1. new AkSourceStreamOp()
  2. .setFilePath(DATA_DIR + "list_test.ak")
  3. .link(
  4. new ReadImageToTensorStreamOp()
  5. .setRelativeFilePathCol("relative_path")
  6. .setRootFilePath(IMAGE_DIR)
  7. .setImageWidth(32)
  8. .setImageHeight(32)
  9. .setOutputCol("tensor")
  10. )
  11. .link(
  12. new AkSinkStreamOp()
  13. .setFilePath(DATA_DIR + TEST_32_FILE)
  14. .setOverwriteSink(true)
  15. );
  16. StreamOperator.execute();
  17. new AkSourceStreamOp()
  18. .setFilePath(DATA_DIR + "list_test.ak")
  19. .link(
  20. new ReadImageToTensorStreamOp()
  21. .setRelativeFilePathCol("relative_path")
  22. .setRootFilePath(IMAGE_DIR)
  23. .setImageWidth(96)
  24. .setImageHeight(96)
  25. .setOutputCol("tensor")
  26. )
  27. .link(
  28. new AkSinkStreamOp()
  29. .setFilePath(DATA_DIR + TEST_96_FILE)
  30. .setOverwriteSink(true)
  31. );
  32. StreamOperator.execute();