数据处理与数据增强模块总结
数据处理与数据增强模块
这个模块分为俩种,一种是dataset,一种是MindRecord,他们的最终目的都是为了加载和读取数据。
-
dataset可以加载常见的数据集、特定数据格式的数据集或自定义的数据集,一般在加载的时候要导入依赖的mindspore.dataset库
-
MindRecord是一种基于MIndSpore平台专门设计的一种加载数据集的库,MindSpore天然支持读取MindSpore数据格式(MindRecord存储的数据集),其在性能和特性上有更好的支持
可相互转化
MindSpore数据格式支持非标准的数据集和常见的数据集的转化,由此更加方便地加载到MindSpore中进行训练。
其实,MindSpore在部分场景做了性能优化,使用MindSpore数据格式可以获得更好的性能体验,所以推荐将数据转换成MindSpore数据格式(MindRecord存储的数据集),如果不想转化的话,就可以采用dataset模块去加载数据集,效果是等同的。
MindSpore数据格式具备的特征
-
实现多变的用户数据统一存储、访问,训练数据读取更简便。
-
数据聚合存储,高效读取,且方便管理、移动。
-
高效数据编解码操作,对用户透明、无感知。
-
灵活控制分区大小,实现分布式训练。
dataset支持的数据集
MindRecord支持的数据集
MindSpore数据格式的目标是归一化用户的数据集,并进一步通过MindDataset实现数据的读取,并用于训练过程
MindRecord文件的组成
一个MindRecord文件由数据文件和索引文件组成,且数据文件及索引文件暂不支持重命名操作:
-
数据文件
包含文件头、标量数据页、块数据页,用于存储用户归一化后的训练数据,且单个MindRecord文件建议小于20G,用户可将大数据集进行分片存储为多个MindRecord文件。
-
索引文件
包含基于标量数据(如图像Label、图像文件名等)生成的索引信息,用于方便的检索、统计数据集信息。
数据文件组成
-
文件头
文件头主要用来存储文件头大小、标量数据页大小、块数据页大小、Schema信息、索引字段、统计信息、文件分区信息、标量数据与块数据对应关系等,是MindRecord文件的元信息。
-
标量数据页
标量数据页主要用来存储整型、字符串、浮点型数据,如图像的Label、图像的文件名、图像的长宽等信息,即适合用标量来存储的信息会保存在这里。
-
块数据页
块数据页主要用来存储二进制串、NumPy数组等数据,如二进制图像文件本身、文本转换成的字典等。
格式转换可以参考:https://www.mindspore.cn/doc/programming_guide/zh-CN/r1.0/dataset_conversion.html
优秀代码解析
// 名称:计算()
// 描述:这个 Compute() 取 1 个 Tensor 并产生 1 个 Tensor。
// 派生类应该覆盖这个函数,否则会出错。
Status TensorOp::Compute(const std::shared_ptr<Tensor> &input, std::shared_ptr<Tensor> *output) {
IO_CHECK(input, output);
if (!OneToOne()) {
// 调用了错误的 Compute() 函数。 这不是 1-1 TensorOp
return Status(StatusCode::kMDUnexpectedError, "Wrong Compute() function is called. This is not 1-1 TensorOp.");
} else {
// 这是 TensorOp 1-1 吗? 如果是,请在派生类中实现这个 Compute()。
return Status(StatusCode::kMDUnexpectedError,
"Is this TensorOp 1-1? If yes, please implement this Compute() in the derived class.");
}
}
// 名称:计算()
// 描述:这个 Compute() 从不同的列中获取多个张量并产生多个张量。
// 派生类应该覆盖这个函数,否则会出错。
Status TensorOp::Compute(const TensorRow &input, TensorRow *output) {
IO_CHECK_VECTOR(input, output);
if (OneToOne()) {
// op是OneToOne,只能接受一个张量作为输入
CHECK_FAIL_RETURN_UNEXPECTED(input.size() == 1, "The op is OneToOne, can only accept one tensor as input.");
output->resize(1);
return Compute(input[0], &(*output)[0]);
}
// 这是 TensorOp 一对一吗? 如果不是,请在派生类中实现这个 Compute()。
return Status(StatusCode::kMDUnexpectedError,
"Is this TensorOp oneToOne? If no, please implement this Compute() in the derived class.");
}
Status TensorOp::Compute(const std::shared_ptr<DeviceTensor> &input, std::shared_ptr<DeviceTensor> *output) {
IO_CHECK(input, output);
// “调用了错误的 Compute() 函数。这是一个可以执行的运算符的函数”
// "不同的设备。如果是,请在派生类中实现。
return Status(StatusCode::kMDUnexpectedError,
"Wrong Compute() function is called. This is a function for operators which can be executed by"
"different device. If so, please implement it in the derived class.");
}
Status TensorOp::OutputShape(const std::vector<TensorShape> &inputs, std::vector<TensorShape> &outputs) {
if (inputs.size() != NumInput())
return Status(StatusCode::kMDUnexpectedError,
// 输入参数向量的大小与输入数量不匹配
"The size of the input argument vector does not match the number of inputs");
outputs = inputs;
return Status::OK();
}
Status TensorOp::OutputType(const std::vector<DataType> &inputs, std::vector<DataType> &outputs) {
if (inputs.size() != NumInput()