DL4J:ETL用户指南
神经网络需要处理的数据有许多种不同的来源和类型,例如日志文件、文本文档、表格数据、图像、视频等。神经网络数据加工的目标是将各类数据转换为一系列存放于多维数组(Multi-Dimensional Array)中的值。
数据可能还需要进行各种预处理,包括转换、缩放、标准化、变换、合并、划分为定型与测试数据集、随机排序等。本页主要介绍目前可用的数据加工工具及其使用方法。
- 记录读取器
- 标准化器
- 转换
现有ETL路径的流程图
记录读取器
记录读取器是Skymind团队开发的ETL流程管理库DataVec中的一种类,名称为RecordReader
。
可用的记录读取器
名称 | 用途 | 概述 |
---|---|---|
BaseImageRecordReader | 图像数据 | 图像记录读取器的基础类 |
CodecRecordReader | 视频 | 编解码器记录读取器,用于解析:H.264(AVC)主流画质解码器、MP3编/解码器、Apple ProRes解码器和编码器、AAC编码器、H.264基本画质编码器、Matroska(MKV)解复用器和复用器、MP4(ISO BMF、QuickTime)解复用器/复用器及工具、MPEG 1/2解码器(支持隔行扫描)、MPEG PS/TS解复用器、Java player applet、VP8解码器、MXF解复用器;感谢jcodec提供基础解析器 |
CollectionRecordReader | 测试 | 集合记录读取器。主要用于测试。 |
CollectionSequenceRecordReader | 序列数据 | 用于序列的集合读取器。主要用于测试。 |
ComposableRecordReader | 合并后的数据 | 用于各个数据加工管道的RecordReader。单项记录是两个集合的串联。创建一个RecordReader来对RecordReader进行迭代并将其串联起来。hasNext为所有后续的RecordReader串联之和,对集合使用addAll返回一项记录 |
CSVNLinesSequenceRecordReader | 表格序列数据 | CSV序列记录读取器,使用条件:(a)所有时间序列位于单个文件中;(b)每个时间序列长度相等(在构造器中指定);(c)时间序列之间未使用分隔符。例如,若nLinesPerSequence=10,0~9行为第一个时间序列,10~19行为第二个时间序列,以此类推。 |
CSVRecordReader | 表格数据 | 简单的CSV记录读取器 |
CSVSequenceRecordReader | 表格序列数据 | CSV序列记录读取器,用于读取CSV格式的序列数据,其中每个序列各有一个文件(同时存在多个文件),文件中的每一行表示一个时间步 |
FileRecordReader | 文件 | 文件读写器 |
ImageRecordReader | 图像数据 | 图像记录读取器。读取本地文件系统,解析给定高度和宽度的图像。所有图像的高度、宽度及通道数量都会被缩放、变换至给定的值。也可以添加指定的标签(基于目录结构的one-of-k编码,根目录下的每个子目录都是一个索引标签) |
JacksonRecordReader | JSON、XML、YAML | 支持JSON、XML和YAML:每个文件仅限一项记录,通过Jackson ObjectMapper实现 |
LibSvmRecordReader | LibSVM内容 | 用于SVM(支持向量机)内容的记录读取器。 |
LineRecordReader | 文本 | 逐行读取文件 |
ListStringRecordReader | 文本 | 对一系列字符串进行迭代,返回一项记录。只接受@link {ListStringInputSplit}作为输入。 |
MatlabRecordReader | Matlab | Matlab记录读取器 |
RegexLineRecordReader | 带有正则表达式的文本 | RegexLineRecordReader:逐行读取一个文件,用一个正则表达式将其切分为字段。具体而言,我们采用的是Pattern和Matcher类。可加载整个文件。示例:格式如“2016-01-01 23:59:59.001 1 DEBUG First entry message!”的数据可用正则表达式字符串”(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) (\d+) ([A-Z]+) (.*)”切分为4个可写文本对象:[“2016-01-01 23:59:59.001”, “1”, “DEBUG”, “First entry message!”] |
RegexSequenceRecordReader | 文本序列数据正则表达式 | RegexSequenceRecordReader:逐行读取一个文件(作为序列),用一个正则表达式将其切分为字段。具体而言,我们采用Pattern和Matcher类来切分文件。示例:格式如“2016-01-01 23:59:59.001 1 DEBUG First entry message!”的数据可用正则表达式字符串”(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}) (\d+) ([A-Z]+) (.*)”切分为4个可写文本对象:[“2016-01-01 23:59:59.001”, “1”, “DEBUG”, “First entry message!”]注:RegexSequenceRecordReader支持多种错误处理模式,通过RegexSequenceRecordReader.LineErrorHandling实现。与指定的正则表达式不相匹配的无效文本可以导致异常(FailOnInvalid),可以无提示跳过(SkipInvalid),或者在跳过无效文本的同时记录警告(SkipInvalidWithWarning) |
SequenceRecordReader | 序列数据 | 用于一个记录序列。sequenceRecord()通常在本地使用。sequenceRecord(URI uri, DataInputStream dataInputStream)用于Spark等 |
SVMLightRecordReader | SVMLight | 改编自weka svmlight读取器2015年6月版-改编后能够识别HDFS式的数据块切分 |
TfidfRecordReader | 自然语言处理、词频-逆文档频率 | TF-IDF记录读取器(包装一个TF-IDF向量化器,用于传递标签并确保与记录读取器接口相符) |
VideoRecordReader | 视频 | 视频就是一系列变化的图片,处理时应当考虑到这一点。该方法对一个根目录进行迭代,返回 |
WavFileRecordReader | 音频 | Wav文件加载器 |
图像基础
为神经网络读取图像时,所有的图像数据在某一时刻必须都缩放至相同的尺寸。图像的初始缩放由ImageRecordReader
完成。
在定型、测试或推断之前加载一系列数据记录的方法如以下代码所示。读取灰阶图像时请将channels
设定为1。
ImageRecordReader recordReader = new ImageRecordReader(height,width,channels);
加载单幅图像用于推断:
NativeImageLoader loader = new NativeImageLoader(height, width, channels); \\ 加载和缩放
INDArray image = loader.asMatrix(file); \\ 创建INDarray
INDArray output = model.output(image); \\ 获得模型对图像的预测
图像数据增强
如果您的图像数据不足以完成神经网络的定型,您可以将现有的图像转换、采样或裁剪,生成额外的有效输入,增加可用的定型数据。
添加标签
在构建分类器时,标签是您想要预测的输出值,而与这些标签相关联的数据是输入。就CSV文件而言,标签可能就是数据记录本身的一部分,与相关输入紧邻着存放在同一行。CSVRecordReader让您可以把特定的字段指定为标签。
DataVec转换流程可将文本标签变换为数值。标签可能需要依据文件路径生成,比如图像分别存放在一系列目录中,目录名称代表了图像的标签。标签也有可能由文件名本身表示,此时就需要从文件所在的目录内部收集数据。
您可以用ParentPathLabelGenerator和PathLabelGenerator这两个DataVec中的类来添加标签。
依据父目录名称对图像进行标记的示例如下。
ParentPathLabelGenerator labelMaker = new ParentPathLabelGenerator();
ImageRecordReader recordReader = new ImageRecordReader(height, width, channels, labelMaker);
图像转换
图像会被系统作为像素值的数组读取。像素值通常是8比特,因此一幅包括一黑一白2个像素的图像的数组是[0,255]
。虽然神经网络可以直接用原始数据来定型,但最好还是先将数据标准化。“零均值单位方差”即让所有的值减去实际平均值,然后缩放至-1与1之间,以0为平均值。
图像定型数据可以通过旋转样例或倾斜图像来增强。
可用的图像转换方法
转换方法 | 具体说明 |
---|---|
BaseImageTransform | 基础类 |
ColorConversionTransform | 采用CVT(cvtcolor)的色彩变换 |
CropImageTransform | 以明确或随机方式裁剪图像 |
EqualizeHistTransform | 用于改善图像的对比度 |
FilterImageTransform | 用FFmpeg(libavfilter)过滤图像 |
FlipImageTransform | 以明确或随机方式翻转图像 |
ImageTransform | 基础类 |
MultiImageTransform | 应用多种转换方法 |
ResizeImageTransform | 调整图像尺寸,该转换方法适用于将整个加工管道中的图像强制调整为同样的尺寸。 |
RotateImageTransform | 以明确或随机方式旋转图像 |
ScaleImageTransform | 以明确或随机方式旋转图像 |
ShowImageTransform | 在屏幕上显示图像,仅用于可视化,不具备转换功能 |
WarpImageTransform | 以明确或随机方式对图像进行透视变换 |
数据转换
通过DataVec摄取数据时,可以用包含多个步骤的转换流程来转换数据。
DataVec现有的数据转换功能如下:
转换方法 | 具体说明 |
---|---|
BaseColumnsMathOpTransform | 多列数学运算的基础类。 |
BaseColumnTransform | “将单个列中的值映射至新的值。例如:对单个列进行字符串 -> 字符串,或空白 -> x类型的转换” |
BaseDoubleTransform | 基础类 |
BaseIntegerTransform | “抽象整数转换(单列)” |
BaseStringTransform | 抽象字符串列转换 |
BaseTransform | “基础转换:一种抽象转换列” |
CategoricalToIntegerTransform | 将分类数据转换为整数 |
CategoricalToOneHotTransform | 将分类数据转换为one-hot形式 |
ConditionalCopyValueTransform | “如果一项条件得到满足/为真,将一个特定列中的值替换为来自另一个列的新值。” |
ConditionalReplaceValueTransform | 基于条件的替换 |
DeriveColumnsFromTimeTransform | 转换流程 |
DoubleColumnsMathOpTransform | “新增一个double类型的列,由一个或多个其他列的值计算得出。” |
DoubleMathOpTransform | double类型数据的数学运算 |
DuplicateColumnsTransform | 复制一个或多个列。 |
IntegerColumnsMathOpTransform | “新增一个整数类型的列,由一个或多个其他列的值计算得出。” |
IntegerMathOpTransform | 整数数学运算 |
IntegerToCategoricalTransform | 将整数类型的列转换为分类数据列 |
Log2Normalizer | 标准化:scale * log2((in-columnMin)/(mean-columnMin) + 1) |
LongColumnsMathOpTransform | “新增一个长整数类型的列,由一个或多个其他列的值计算得出。” |
LongMathOpTransform | 让长整型列的值与一个长整型标量进行就地运算。 |
MapAllStringsExceptListTransform | 从映射表中剔除的列表 |
MinMaxNormalizer | 标准化方法之一,用于将(最小值到最大值)线性映射至(新最小值到新最大值)。 |
ReduceSequenceByWindowTransform | “对序列数据应用窗口函数,对数据窗口应用reduce函数” |
RemoveAllColumnsExceptForTransform | 保留指定的列 |
RemoveColumnsTransform | 删除指定的列 |
RemoveWhiteSpaceTransform | 删除空格 |
RenameColumnsTransform | 重命名列 |
ReorderColumnsTransform | 将列重新排序 |
ReplaceEmptyIntegerWithValueTransform | 将空缺的整数替换为指定的值 |
ReplaceEmptyStringTransform | 将空缺的字符串替换为指定的值 |
ReplaceInvalidWithIntegerTransform | 将无效值替换为整数 |
StandardizeNormalizer | “采用(x-mean)/stdev公式进行标准化。也称为标准分数、规范化等。” |
StringListToCategoricalSetTransform | 字符串转换为分类数据列表 |
StringMapTransform | 字符串转换为映射表 |
StringToCategoricalTransform | 字符串转换为分类数据 |
StringToTimeTransform | 由字符串生成数值时间 |
SubtractMeanNormalizer | 减去平均值 |
TimeMathOpTransform | 时间变换 |
缩放和标准化
通过RecordReader
获取的数据通常会传递给一个数据集迭代器,迭代器会遍历数据并对其进行预加工,以便输入神经网络。可以摄取的数据已经变为一个多维数组(INDarray),不再是用迭代器读取一个数据记录序列的形式。该阶段也有一系列转换和缩放的工具。由于数据已经是INDarray,此处介绍的工具都属于Skymind的科学计算库ND4J的一部分。文档参见此处
代码示例
DataNormalization scaler = new ImagePreProcessingScaler(0,1);
scaler.fit(dataIter);
dataIter.setPreProcessor(scaler);
可用的ND4J预处理器
ND4J数据集预处理器 | 用途 |
---|---|
ImagePreProcessingScaler | 指定最大值、最小值的缩放,可设定区间。像素值可以从0->255缩放至minRange->maxRange的区间内,默认minRange = 0,maxRange = 1 |
NormalizerMinMaxScaler | 指定最大值、最小值的缩放,可设定区间:X -> (X - min/(max-min)) * (given_max - given_min) + given_mi |
NormalizerStandardize | 标准缩放器,计算列的移动方差与均值 |
使用JavaCV、OpenCV和ffmpeg滤镜的图像转换
ffmpeg和OpenCV是用于过滤、转换图像及视频的开源库。在7.2及以上版本中获取ffmpeg过滤器的方法是向pom.xml文件中添加下列代码,将依赖项替换为当前版本。
<dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv-platform</artifactId> <version>1.3</version> </dependency>
文档
时间序列或序列数据
循环神经网络可用于分析序列和时间序列数据。DataVec提供的CSVSequenceReader
类可以从文件中读取序列数据。UCISequenceClassificationExample
就是一个很好的例子。
数据被分为测试和定型两个数据集,因此代码分别为每个数据集创建了一个迭代器。
这一数据集中共有六种标签。特征目录中每个包含数据的文件都有一个对应的标签文件,位于标签目录下。标签文件中仅有单个项,而特征文件则包含了相应设备的活动序列记录。
private static File baseDir = new File("src/main/resources/uci/");
private static File baseTrainDir = new File(baseDir, "train");
private static File featuresDirTrain = new File(baseTrainDir, "features");
private static File labelsDirTrain = new File(baseTrainDir, "labels");
private static File baseTestDir = new File(baseDir, "test");
private static File featuresDirTest = new File(baseTestDir, "features");
private static File labelsDirTest = new File(baseTestDir, "labels");
NumberedFileInputFormat
用String.Format
从文件名中提取索引。数据目录包含文件0.csv->449.csv
以下是读取特征及标签的代码。
SequenceRecordReader trainFeatures = new CSVSequenceRecordReader();
trainFeatures.initialize(new NumberedFileInputSplit(featuresDirTrain.getAbsolutePath() + "/%d.csv", 0, 449));
SequenceRecordReader trainLabels = new CSVSequenceRecordReader();
trainLabels.initialize(new NumberedFileInputSplit(labelsDirTrain.getAbsolutePath() + "/%d.csv", 0, 449));
摄取图像数据并输入预定型的模型
NativeImageLoader
可以读取一幅图像并将其转变为一个INDArray。请注意,您需要用网络定型时调整尺寸、缩放、标准化的方式来缩放和调整导入的图像。