通过数据集管理工程数据#
在VisionFlow中, 数据被分成: Parameter 参数和 Property 属性两种类型。本节会通过一些例子详细介绍如何访问、更新 property 属性类型的数据。
所有的属性数据 property 都被保存在数据库中并被组织成 SampleSet 数据集这种数据结构,通过高层接口的抽象隐藏了底层实现细节提升易用性。
通过数据集 SampleSet 获取数据 Sample#
样本集是以样本id和样本数据作为键值对组成的容器。样本集中包含什么样的属性类型是由工程(里的计算流程图)决定的。
样本是以属性节点id(ToolNodeId)和属性数据作为键值对组成的容器。
Note
样本集包含了访问数据库的句柄,在析构样本集对象时数据库的引用计数会减一。
上面提到了样本集里的属性类型是由工程决定的,以下列出 Project 中会影响样本集包含何种属性的接口:
这四个接口都会影响工程中的计算流程图的结构,所有每次调用这些接口都会改变样本集中包含的属性。
例如,给工程添加一个工具会将这个工具内部包含的属性节点添加到样本集中作为占位符(同时也会加入到数据库中)。
LazySample和Sample#
LazySample和Sample都可以获取到属性数据。它们的区别是:LazySample可以通过数据库获取属性数据,而Sample不能。
任何对LazySample和Sample的修改都不会影响到数据库中的数据,除非你通过接口 visionflow::data::SampleSet::update() 将这些修改提交.
LazySample#
当第一次访问或修改数据时, LazySample 会从数据库中读取,并将这次访问或修改缓存在内存中。 当再次访问之前已被访问或修改过的数据时, LazySample 会从缓存中读取数据,因此访问速度会更快。
除此之外,当更新一个 LazySample 到 SampleSet 时, LazySample 会检测出哪些属性数据被修改过, 只有这些被修改的属性会被更新到数据库中,这也会提高数据库的更新速度。
关于LazySample的更多信息,请查看 visionflow::LazySample.
Sample#
Sample 仅仅只是一个存放离线的属性数据的容器。你往里面设置什么数据,它存放的就是什么数据。 因此通过 Sample 得到的属性数据有可能是过时的(数据库被修改)。
一般而言,你应该通过 visionflow::LazySample::fetch() 接口得到一个 Sample 对象。
该 Sample 对象中存储了LazySample::fetch函数调用时刻、数据库存放的该样本的各个属性的数据。
查看Sample中存放的属性#
使用
visionflow::Sample::property_types()获取Sample中存储的全部属性名称和类型。使用
visionflow::Sample::get()获取Sample中某个属性的值。 此接口返回的是基类visionflow::props::IProperty的共享指针。 visionflow提供了 as 方法可以转换到特定的子类访问数据,你也可以通过 to_json、 to_xml 等序列化方法查看具体的值。
当更新一个 Sample 到 SampleSet 时, Sample 中的所有属性数据都会被更新到数据库中,即使某些属性并没有被更改过。 这会影响性能。
Note
更新数据库时请尽量使用LazySample。Sample更多的用途是存储一份离线数据。
关于Sample的更多信息,请查看 visionflow::Sample.
SampleSetIterator#
这是一个用于迭代 SampleSet 的迭代器类,提供了类似 std::literator 的接口和使用体验。
SampleSetIterator 的用法示例:
// get SampleSet from Project
visionflow::SampleSet sample_set = project->main_sample_set();
// print information of samples iteratively
for(visionflow::SampleSetIterator iter = sample_set.begin(); iter != sample_set.end(); ++iter){
std::cout << "sample id = " << iter.key() << std::endl;
std::cout << "sample created time = " << iter.descriptor().created_time << std::endl;
}
// find a sample with specific id
uint32_t id = 1;
auto iter = sample_set.find(id);
.. TODO
.. TODO
关于SampleSetIterator的更多信息,请查看 visionflow::data::SampleSetIterator
SampleDescriptor#
这是一个用于描述样本信息的数据结构。里面存储了样本的创建时间和样本标签。
通过接口 visionflow::data::ReadOnlySampleSet::sample_descriptor() 获取特定样本id的样本描述信息。
更新SampleDescriptor#
当你需要更新样本描述信息时,使用接口:
void visionflow::data::SampleSet::update(uint32_t id, const SampleDescriptor &data)
或者
void visionflow::data::SampleSet::update(const SampleSetIterator &iter, const ISample &data)
以下示例代码展示了如何给样本集中的第一个样本添加新的样本标签:
// get the SampleSet from Project
visionflow::SampleSet sample_set = project->main_sample_set();
//get the SampleSetIterator of the sample to be updated
auto iter = sample_set.begin();
//get the old SampleDescriptor
visionflow::SampleDescriptor desp = iter.descriptor();
// add a new_tag to descriptor
desp.tags.add("new_tag");
// update to SampleSet by SampleSetIterator
sample_set.update(iter, desp);
// you may also update by id of the sample
// sample_set.update(iter.key(), desp);
.. TODO
.. TODO
关于样本描述信息的更多信息,请查看 visionflow::SampleDescriptor.
添加Sample#
通过接口 visionflow::data::SampleSet::add_empty_sample() 可以向样本集中添加一个空的样本并获得其样本id。
该空样本中仅含有与样本集中一样的属性数量和类型,但是具体的属性数据是空的。
你也可以通过接口 visionflow::data::SampleSet::add() 向样本集中添加一个已存在的样本并获得其样本id。
Note
你需要确保样本和样本集所包含的属性数量和类型是一致的,这样样本才能被加入到样本集中,否则添加样本时会抛出异常 visionflow::excepts::PropertyTypeMismatch 。
假设我们已知 Input 工具的结构(通过 工具清单及详细流程图 查看更多工具详细结构图), 接下来的示例代码展示了如何为样本添加一个图片属性,并向样本集中添加四个这样的样本:
// add Input tool into Project
project->add_tool("Input");
// get the SampleSet from Project
visionflow::SampleSet sample_set = project->main_sample_set();
//create an empty sample
auto sample = sample_set.create_empty_sample();
// load an image from file
visionflow::props::Image img;
img.set_image(visionflow::img::Image::FromFile("D:/path/to/img_1.jpg"));
//set the Image property data
sample.set({"Input/image"},
std::make_shared<visionflow::props::Image>(img));
// add four samples into SampleSet
sample_set.add(sample); // sample id == 1
sample_set.add(sample); // sample id == 2
sample_set.add(sample); // sample id == 3
sample_set.add(sample); // sample id == 4
.. TODO
.. TODO
更新Sample#
可以将已有的样本通过样本id或 SampleSetIterator 迭代器更新到样本集中:
void visionflow::data::SampleSet::update(uint32_t id, const ISample &data)
Warning
SampleSet中必须存在样本id,并且被更新的样本的属性数量和类型需要与样本集一致,否则会抛出异常 visionflow::excepts::SampleNotFound 或 visionflow::excepts::PropertyTypeMismatch .
void visionflow::data::SampleSet::update(const SampleSetIterator &iter, const ISample &data)
Warning
SampleSetIterator需要指向一个在样本集中存在的样本, 并且被更新的样本的属性数量和类型需要与样本集一致,否则会抛出异常 visionflow::excepts::SampleNotFound 或 visionflow::excepts::PropertyTypeMismatch .
以下示例代码展示了如何通过更新接口将新的图片属性更新到样本集(接上文的示例代码):
// already add Input tool into Project
// add new empty sample into SampleSet and get its id
LazySample sample = sample_set.at(1);
// load new image from file
visionflow::props::Image img;
img.set_image(visionflow::img::Image::FromFile("D:/path/to/new_img.jpg"));
//set the Image property data
sample.set({"Input/image"},
std::make_shared<visionflow::props::Image>(img));
// update sample_set with LazySample
sample_set.update(new_id, sample);
// you may also update by iterator
// sample_set.update(sample_set.find(new_id), sample);
.. TODO
.. TODO
移除sample#
- 通过样本id从样本集中移除样本:
Warning
SampleSet中必须存在该样本id,否则会抛出异常 visionflow::excepts::SampleNotFound .
- 通过样本迭代器从样本集中移除样本:
SampleSetIterator visionflow::data::SampleSet::erase(const SampleSetIterator &iter)
Warning
SampleSetIterator必须指向一个样本集中存在的样本,否则会抛出异常 visionflow::excepts::InvalidIterator .
通过 PropertySet 访问 Property#
PropertySet是一个管理样本集中所有样本的单个同一属性数据的数据结构。样本集中所有样本的某个属性都存储在属性集中。
PropertySet同时还记录了所有样本的该属性的最后更新时间。
Note
与SampleSet类似,PropertySet同样也维护了一个数据库句柄,PropertySet析构时也会减少数据库的引用计数。
PropertySetIterator#
与SampleSet类似,PropertySet同样也提供了迭代器类型:
visionflow::data::PropertySetIterator。
以下示例代码展示了如何通过PropertySetIterator迭代获取样本集中所有样本的图片属性:
// get the PropertySet of Image
visionflow::data::PropertySet property_set = sample_set.property_set({"Input", visionflow::Input::image});
for (auto iter = property_set.begin(); iter.valid(); ++iter) {
std::cout << "sample id :" << iter.key() << std::endl;
// convert to props::Image ptr
auto img = iter.value()->as<visionflow::props::Image>();
// use img property to do some work ...
}
.. TODO
.. TODO
访问和修改PropertySet中的属性#
你可以通过样本id或者PropertySetIterator来访问或修改更新属性集中的属性。
Warning
样本id必须在样本集中存在,否则会抛出异常 visionflow::excepts::SampleNotFound .
PropertySetIterator必须指向属性集中存在的属性,否则会抛出异常 visionflow::excepts::InvalidIterator .
以下示例代码展示了如何更新属性集中的图片属性:
// get the PropertySet of Image
visionflow::data::PropertySet property_set = sample_set.property_set({"Input", visionflow::Input::image});
// get last update time
std::cout << "last time:" << property_set.last_update_time();
// get property type
assert(property_set.property_type() == "visionflow::props::Image");
// check sample not empty
assert(property_set.sample_empty() == false);
assert(property_set.data_exists(1) == true);
// get image property by sample id
auto img_of_sample_1 = property_set.at(1);
// reset a new image to the image property
img_of_sample_1->set_image(visionflow::img::Image::FromFile("D:/path/to/new_img.jpg"));
// update PropertySet by sample id
property_set.update(1, *img_of_sample_1);
// erase PropertySet by iterator
const auto&iter = property_set.find(4);
property_set.erase(iter);
// now the image property in sample 4 will be erased
.. TODO
.. TODO
WriteBatch提供更好的性能#
由于每次将样本的修改更新到数据库都会引发磁盘I/O,因此频繁的更新将会花费很多性能开销。
我们提供WriteBatch批量更新功能,允许将多次修改缓存到内存中,最后将这些修改一次性的更新到数据库并落盘,减少频繁的数据库访问以提升性能。
PropertyWriteBatch#
PropertyWriteBatch缓存所有对属性集的修改操作。
Note
对于同一个样本,只有最后的修改会生效(之前缓存的修改将被覆盖)。
Warning
你需要确保被修改的样本存在于样本集中,否则在更新PropertyWriteBatch时会抛出异常 visionflow::excepts::SampleNotFound 。
以下示例代码展示了如何使用 PropertyWriteBatch 批量更新属性集:
// get the PropertySet of Image
visionflow::data::PropertySet property_set = sample_set.property_set({"Input", visionflow::Input::image});
// firstly, create a PropertyWriteBatch
PropertyWriteBatch prop_write_batch = property_set.create_write_batch();
// catch all your modification on image PropertySet for different samples
// update sample 1 with img_101
visionflow::props::Image img_101;
img_101.set_image(visionflow::img::Image::FromFile("D:/path/to/img_101.jpg"));
prop_write_batch.update(1, img_101);
// update sample 2 with img_102
visionflow::props::Image img_102;
img_102.set_image(visionflow::img::Image::FromFile("D:/path/to/img_102.jpg"));
prop_write_batch.update(2, img_102);
// erase image property in sample 3
prop_write_batch.erase(3);
// erase image property in sample 2
// this will overwrite previous update operator
prop_write_batch.erase(2);
// update to database at once
property_set.write_batch(prop_write_batch);
// now all the modification are update to database
// image property in:
// sample 1 will update to img_101;
// sample 2 and sample 3 will be erased;
.. TODO
.. TODO
SampleWriteBatch#
SampleWriteBatch缓存了所有对样本集的修改操作,例如更新整个样本数据、更新某个样本里的某个属性数据或是更新样本描述信息。
Note
同样的,对于同一个样本的同一个数据,只有最后的修改会生效(之前缓存的修改将被覆盖)。
Warning
你需要确保被修改的样本存在于样本集中,否则在批量更新SampleWriteBatch时会抛出异常 visionflow::excepts::SampleNotFound 。
以下示例代码展示了如何使用 SampleWriteBatch 批量更新样本集:
// firstly, create a SampleWriteBatch
SampleWriteBatch sample_write_batch = sample_set.create_write_batch();
const visionflow::ToolNodeId img_prop_id = {"Input/image"};
// catch all your modification to SampleWriteBatch
// update sample 1 with img_101
visionflow::props::Image img_101;
img_101.set_image(visionflow::img::Image::FromFile("D:/path/to/img_101.jpg"));
sample_write_batch.update(
1, img_prop_id, std::make_shared<visionflow::props::Image>(img_101));
// update sample 2 with img_102
visionflow::props::Image img_102;
img_102.set_image(visionflow::img::Image::FromFile("D:/path/to/img_102.jpg"));
sample_write_batch.update(
2, img_prop_id, std::make_shared<visionflow::props::Image>(img_102));
// erase in sample 3
sample_write_batch.erase(3);
// erase image property in sample 2
// this will overwrite previous update operator
sample_write_batch.erase(2, img_prop_id);
// update to database at once
sample_set.write_batch(sample_write_batch);
// now all the modification are update to database
// image property in:
// sample 1 will update to img_101;
// sample 2 will be erased;
// and the whole sample 3 will be erased from SampleSet;
.. TODO
.. TODO
数据过滤#
SampleSet 和 PropertySet 都提供过滤功能,允许你通过设置自定义过滤条件来获得你想要的某些数据。 过滤条件是包含了一个函数的Python脚本字符串。
Note
- 为了能够正确的编写python脚本的过滤条件,你需要了解如下信息:
你需要十分注意缩进,缩进在Python中非常重要,错误的缩进会导致Python语法错误;
脚本中需要包含一个实现了具体的过滤逻辑的过滤函数,并返回一个布尔类型的结果作为过滤条件的结果的函数;
函数名必须为”vflow_filter”,并接受一个 Sample(ReadOnlySampleSetView) 或 Property(ReadOnlyPropertySetView) 作为参数;
你需要将引入正确的Visionflow模块,Visionflow的Python模块与C++中的命名空间相同。
Warning
Python脚本必须符合Python语法,否则执行时会抛出异常 visionflow::excepts::PythonSyntaxError 或 visionflow::excepts::PythonScriptError 。
ReadOnlySampleSetView#
ReadOnlySampleSetView允许过滤出满足过滤脚本条件的样本。
以下示例代码展示了如何使用ReadOnlySampleSetView过滤出图片大小在1024*1024到2048*2048之间的样本:
// firstly, write our filter script
const std::string filter_script = R"(
from visionflow import *
from visionflow.props import *
from visionflow.img import *
def vflow_filter(sample):
img_prop_id = ToolNodeId("Input", Input.image)
if not(sample.exist_property_data(img_prop_id)):
return False
img = sample.get(img_prop_id).image();
return 1024 <= img.size().w <= 2048 and 1024 <= img.size().h <= 2048 )";
auto sample_view = sample_set.filter(filter_script);
// if image in sample 1 meet the filter requirement, then its id will exist
// assert(sample_filter.exists(1));
// so to other samples
// assert(sample_filter.exists(2));
assert(sample_view.ids() == std::vector<uint32_t>{1, 2});
.. TODO
.. TODO
关于 ReadOnlySampleSetView 的更多信息,请查看 visionflow::data::ReadOnlySampleSetView.
ReadOnlyPropertySetView#
- ReadOnlyPropertySetView提供了两种过滤方式:
可以设置要过滤的样本id,将某些id的样本直接排除在外(此过滤方式是在样本层面直接过滤);
设置Python脚本过滤,该脚本以属性为参数,过滤出的样本中属性满足脚本的要求(此过滤方式是考察属性层面是否满足过滤条件);
ReadOnlyPropertySetView的过滤脚本要求与ReadOnlySampleSetView一致,唯一的不同就是其接受的参数为属性而不是样本。(Script Helper)
以下示例代码展示了如何使用ReadOnlyPropertySetView过滤出图片大小在1024*1024到2048*2048之间的样本,并且排除了样本id等于1的样本:
// firstly,
const visionflow::ToolNodeId img_prop_id = {"Input/image"};
// secondly, write our filter script
const std::string filter_script = R"(
from visionflow import *
from visionflow.props import *
from visionflow.img import *
def vflow_filter(prop):
img = prop.image();
return 1024 <= img.size().w <= 2048 and 1024 <= img.size().h <= 2048 )";
PropertySet img_prop_set = sample_set.property_set(img_prop_id);
ReadOnlyPropertySetView img_prop_view = img_prop_set.fitler(filter_script);
// if image in sample 1 meet the filter requirement of the python script, then its id will exist
assert(img_prop_view.sample_exists(1));
assert(img_prop_view.data_exists(1));
// if image in sample 2 not meet the filter requirement of the python script, then it will be filtered out
assert(img_prop_view.sample_exists(2));
assert(img_prop_view.data_exists(2) == false);
// all sample with filter ids will be filtered out
// no matter they meet the filter requirement of the python script or not
img_prop_view.set_filter_ids({1});
assert(img_prop_view.sample_exists(1) == false);
assert(img_prop_view.data_exists(1) == false);
.. TODO
.. TODO
关于 ReadOnlyPropertySetView 的更多信息,请查看 visionflow::data::ReadOnlyPropertySetView 。
ReadOnlyPropertySetView 过滤详解(以 ViewList 属性为例)#
以下示例均以 ViewList 属性为例,假设样本集中有三条样本:
id=1:含 2 个
kTest视图 + 1 个kTrain视图(共 3 个)id=2:仅含 1 个
kTrain视图(共 1 个)id=3:空 ViewList(0 个视图)
使用 set_filter_ids 按样本 id 排除
set_filter_ids 直接在样本层面排除指定 id,无论其属性内容如何,被排除的样本在视图中完全不可见。
auto prop_view = sample_set
.readonly_property_set({"Tool_1", "view_prop"})
.filter(""); // 暂不设置脚本过滤
// 排除 id=2 和 id=3
prop_view.set_filter_ids({2, 3});
assert(prop_view.data_exists(1)); // id=1 正常可见
assert(prop_view.data_exists(2) == false); // id=2 被排除
assert(prop_view.data_exists(3) == false); // id=3 被排除
.. TODO
使用 set_filter_script:返回 True / False
vflow_filter 返回 True 表示该样本通过过滤,返回 False 则将其排除。
适合只需要判断样本是否满足条件,不需要修改属性内容的场景。
// 过滤出视图数量 >= 2 的样本
auto prop_view = sample_set
.readonly_property_set({"Tool_1", "view_prop"})
.filter(R"(
import visionflow as vf
def vflow_filter(view_list):
return view_list.size() >= 2
)");
// id=1 含 3 个视图,通过
assert(static_cast<bool>(prop_view.at(1)));
// id=2 含 1 个视图,被过滤
assert(!static_cast<bool>(prop_view.at(2)));
// id=3 为空 ViewList,被过滤
assert(!static_cast<bool>(prop_view.at(3)));
.. TODO
使用 set_filter_script:返回 None
返回 None 与返回 False 等价,同样会排除该样本。
可用于”有条件地返回属性对象,否则不返回任何值”的写法,让逻辑更自然:
// 仅保留含 kTest 视图的样本;
// 若无 kTest 视图则不显式 return,Python 函数隐式返回 None
auto prop_view = sample_set
.readonly_property_set({"Tool_1", "view_prop"})
.filter(R"(
import visionflow as vf
def vflow_filter(view_list):
tagged = view_list.tagged_views(vf.kTest)
if tagged.size() > 0:
return tagged
# 无 kTest 视图时不返回任何值,等同于 return None,该样本将被排除
)");
// id=1 含 kTest 视图,通过,且属性内容已被裁剪为仅含 kTest 视图
assert(static_cast<bool>(prop_view.at(1)));
// id=2 无 kTest 视图,隐式返回 None,被排除
assert(!static_cast<bool>(prop_view.at(2)));
// id=3 空 ViewList,隐式返回 None,被排除
assert(!static_cast<bool>(prop_view.at(3)));
.. TODO
使用 set_filter_script:返回属性对象
vflow_filter 可以返回一个与输入同类型的属性对象,框架将用该返回值替换原始属性。
适合需要在保留样本的同时对属性数据进行裁剪过滤或修改的场景。
Warning
返回的属性对象必须与输入属性类型一致,否则会抛出异常
visionflow::excepts::PropertyTypeMismatch 。
// 裁剪 ViewList,只保留 kTest 视图
auto prop_view = sample_set
.readonly_property_set({"Tool_1", "view_prop"})
.filter(R"(
import visionflow as vf
def vflow_filter(view_list):
return view_list.tagged_views(vf.kTest)
)");
// id=1:原含 2 kTest + 1 kTrain,过滤后只剩 2 个 kTest 视图
auto prop = prop_view.at(1);
assert(static_cast<bool>(prop));
assert(prop->as<visionflow::props::ViewList>().tagged_views(vf::kTest).size() == 2);
assert(prop->as<visionflow::props::ViewList>().tagged_views(vf::kTrain).size() == 0);
// id=2:无 kTest 视图,tagged_views 返回空 ViewList(非 nullptr),
// 样本仍存在,但属性内容为空
assert(static_cast<bool>(prop_view.at(2)));
assert(prop_view.at(2)->as<visionflow::props::ViewList>().size() == 0);
// id=3:本身为空 ViewList,返回同样为空,样本仍存在
assert(static_cast<bool>(prop_view.at(3)));
.. TODO
综合示例:同时使用 set_filter_ids 和 set_filter_script
两种过滤方式可以叠加使用:set_filter_ids 先排除指定 id,剩余样本再经过脚本过滤。
auto prop_view = sample_set
.readonly_property_set({"Tool_1", "view_prop"})
.filter(R"(
import visionflow as vf
def vflow_filter(view_list):
return view_list.tagged_views(vf.kTest)
)");
// 在脚本过滤的基础上,额外排除 id=3
prop_view.set_filter_ids({3});
// id=1:通过脚本过滤,属性裁剪为仅含 kTest 视图
assert(static_cast<bool>(prop_view.at(1)));
// id=2:通过脚本过滤,返回空 ViewList(非 nullptr)
assert(static_cast<bool>(prop_view.at(2)));
// id=3:被 set_filter_ids 排除,不可见
assert(!static_cast<bool>(prop_view.at(3)));
.. TODO
ConfigureRuntimeProjectFilterAdapter#
ConfigureRuntimeProjectFilterAdapter 是在 Configure(训练/统计)流程 中使用的数据过滤适配器,
通过 filter_adapt 工厂函数创建。它将 Project 与 ConfigureRuntime 连接起来,
允许在不修改数据库原始数据的前提下,控制算法服务所能看到的数据范围。
与 ReadOnlyPropertySetView 的区别:
ReadOnlyPropertySetView用于 通用数据读取,直接从 PropertySet 构建过滤视图;ConfigureRuntimeProjectFilterAdapter用于 Configure 流程,过滤结果透明传入算法服务, 算法无需感知过滤的存在。
示例:分割工具的混淆矩阵计算
以分割工具为例:Segmentation::statistician 配置器负责计算混淆矩阵(精确率、召回率等指标),
其统计范围由 Segmentation::tagged_views 属性中的 ViewList 决定。
样本的 ViewList 中,每个视图除了有 kTest / kTrain 分集标签外,
还可以附加用户自定义标签(如 "front_face"、"back_face")来区分拍摄位置等信息。
默认情况下,Segmentation::statistician 会统计所有视图。
如果只想评估某个特定拍摄位置的模型效果,就需要将统计范围收窄到含有该标签的视图子集。
不设置过滤:所有视图均参与统计
// #include "visionflow/node_names.hpp" 包含各工具节点名的常量定义
const visionflow::ToolNodeId tagged_views_id{segment_id, visionflow::Segmentation::tagged_views};
const visionflow::ToolNodeId statistician_id{segment_id, visionflow::Segmentation::statistician};
const visionflow::ToolNodeId statistics_id{segment_id, visionflow::Segmentation::statistics};
auto config_rt = proj->create_config_runtime(statistician_id, opts);
auto adapter = visionflow::filter_adapt(proj.get(), config_rt);
// 不设置任何过滤,kTest 和 kTrain 的所有视图均参与统计
config_rt.initialize(adapter);
config_rt.execute(adapter);
adapter.commit();
const auto &metrics =
proj->get_param(statistics_id)->as<visionflow::param::ModelEvaluationMetrics>();
// metrics.table(kTestView) 包含所有 kTest 视图的统计结果
// metrics.table(kTrainView) 包含所有 kTrain 视图的统计结果
.. TODO
使用 MakeViewTagFilterScript 收窄统计范围
MakeViewTagFilterScript 根据指定标签自动生成过滤脚本,
将 Segmentation::tagged_views 裁剪为只含有 同时满足所有指定标签 的视图,无需手写 Python:
auto config_rt = proj->create_config_runtime(statistician_id, opts);
auto adapter = visionflow::filter_adapt(proj.get(), config_rt);
// 只统计带有 "front_face" 标签的视图,忽略其他拍摄位置的视图
visionflow::Tags filter_tags;
filter_tags.add("front_face");
adapter.set_filter_script(
tagged_views_id,
visionflow::ConfigureRuntimeProjectFilterAdapter::MakeViewTagFilterScript(
filter_tags));
config_rt.initialize(adapter);
config_rt.execute(adapter);
adapter.commit();
const auto &metrics =
proj->get_param(statistics_id)->as<visionflow::param::ModelEvaluationMetrics>();
// metrics.table(kTestView) 只包含带 "front_face" 标签的 kTest 视图
// 不含该标签的视图(如 "back_face")被排除在统计之外
// 传入空 Tags 时,MakeViewTagFilterScript 返回空字符串,等同于不过滤
visionflow::Tags empty_tags;
assert(visionflow::ConfigureRuntimeProjectFilterAdapter::MakeViewTagFilterScript(
empty_tags).empty());
.. TODO
使用 set_filter_ids 排除指定样本
在样本层面直接排除指定 id,无论其视图内容如何,被排除的样本对算法完全不可见:
auto adapter = visionflow::filter_adapt(proj.get(), config_rt);
// 排除已知质量不佳的样本
adapter.set_filter_ids({5, 10, 23});
config_rt.initialize(adapter);
config_rt.execute(adapter);
adapter.commit();
.. TODO
综合示例:set_filter_ids + MakeViewTagFilterScript
两者可叠加——先按 id 排除不参与统计的样本,再按视图标签收窄统计范围:
auto config_rt = proj->create_config_runtime(statistician_id, opts);
auto adapter = visionflow::filter_adapt(proj.get(), config_rt);
// 1. 排除异常样本
adapter.set_filter_ids({5, 10});
// 2. 只统计 "front_face" 拍摄位置的视图
visionflow::Tags filter_tags;
filter_tags.add("front_face");
adapter.set_filter_script(
tagged_views_id,
visionflow::ConfigureRuntimeProjectFilterAdapter::MakeViewTagFilterScript(
filter_tags));
config_rt.initialize(adapter);
config_rt.execute(adapter);
adapter.commit();
// 查看收窄统计范围后的混淆矩阵指标
const auto &metrics =
proj->get_param(statistics_id)->as<visionflow::param::ModelEvaluationMetrics>();
.. TODO
关于 ConfigureRuntimeProjectFilterAdapter 的更多信息,请查看
visionflow::ConfigureRuntimeProjectFilterAdapter 。