打开用于机器学习的ROOT NTuple最快,最节省内存的方法是什么?

我正在使用scikit Learn建立一个机器学习项目。输入的数据是平面ROOT NTuples。

过去,我一直使用root_numpy将NTuples转换为保存在h5文件中的pandas.DataFrame。

我想知道是否可以使用uproot来
a)完全跳过h5转换?
b)使用的内存不如从h5加载到DataFrame中一样?

我幼稚的第一次尝试看起来像这样:

'''
Runs preselection,keeps only desired variables in DataFrame
'''
def dropAndKeep(df,dropVariables=None,keepVariables=None,presel=None,inplace=True):

    if ((presel is not None) and (not callable(presel))):
        print("Please either provide a function to 'presel' or leave blank")
        raise ValueError

    if callable(presel):
        if not(inplace):
            df = df.drop(df[~presel(df)].index,inplace=False)
        else:
            df.drop(df[~presel(df)].index,inplace=True)

    if keepVariables is not None:
        dropthese = list( set(df.columns) - set(keepVariables) )
        return df.drop(columns=dropthese,inplace=inplace)

    if dropVariables is not None:
        return df.drop(columns=dropVariables,inplace=inplace)

'''
Loads a TTree from ROOT file into a DataFrame 
'''
def load_root(inFile,key,presel=None):
    df = uproot.open(inFile)[key].pandas.df()
    dropAndKeep(df,dropVariables,keepVariables,presel=presel,inplace=True)
    return df


inFile = "path/to/file.root"
key = "ntuple"
df = load_root(inFile,key)

这需要很长时间。有更好的方法吗?

dangdangheta 回答:打开用于机器学习的ROOT NTuple最快,最节省内存的方法是什么?

请注意,每次对uproot.open(...)file [key]的调用都使用纯Python(uproot最慢的部分)加载TFile和TTree元数据。如果多次调用此方法,请尝试保留TFile和/或TTree对象并重新使用它们。

另外,看来您的dropAndKeep函数只是删除行(事件),但是如果我读错了并且它在做列(分支),则使用uproot的branches参数数组读取功能仅发送所需的分支。由于ROOT文件中的数据是按列排列的,因此您无法避免读取不需要的事件-您必须在事实发生之后(在任何框架中)将其剪切掉。

接下来,请注意,对于简单的操作(如过滤事件),Pandas比NumPy慢得多。如果要加快速度,请使用TTree.arrays而不是TTree.pandas.df获取数组,构造一个NumPy布尔数组供您选择,然后将其应用于{{1} }返回。然后,您可以使用Pandas的DataFrame构造函数将它们全部放入DataFrame中(如果您真的需要Pandas的话)。

的确,您不需要通过HDF5,也不需要通过Pandas。您的机器学习框架(TensorFlow?Torch?)几乎可以肯定有一个接口,该接口可以接受零拷贝(或到GPU的一个拷贝)的NumPy数组。强调HDF5或Pandas的教程之所以这样做,是因为对于大多数用户(非HEP)来说,这些是最方便的界面。他们的数据可能已经在HDF5或Pandas中了;我们的数据可能在ROOT中。

如果您的机器学习将在GPU上进行,也许您也希望在GPU上进行事件选择。 CuPy是一个NumPy克隆,它完全在GPU上分配和运行,并且您的TensorFlow / Torch张量可能与CuPy数组具有零拷贝接口。原则上,如果将CuPy数组用作asarray interpretation的目标,则uproot应该能够直接从ROOT文件写入CuPy数组。不过,我还没有尝试过。

如果您可以控制要处理的ROOT文件,请尝试使它们的存储篮变大(增加刷新大小),并简化其数据结构(例如,纯数字或数字的数组/向量,再不深入)。也许最重要的是,使用像lz4这样的轻量级压缩,而不是使用重量级的Luke lzma。

Uproot可以并行读取篮子,但这仅在它需要执行许多非Python计算(例如对lzma进行解压缩)时才有用。

如果要一遍又一遍地读取这些数组,则可能要使用numpy.save写入中间文件,该文件实际上只是磁盘上的原始字节。这意味着回读时没有反序列化,这与解码ROOT或HDF5文件所需的工作相反。因为它是一种简单的格式,所以您甚至可以使用numpy.memmap来读回它,因为它会从磁盘延迟加载数据,从而偷看操作系统的页面缓存,甚至删除字节的显式副本。

并非所有这些技巧都将同样有用。我尝试将最重要的代码放在首位,但是在进行大型代码重写之前进行了实验,可能并没有太大的不同。有些技巧无法与其他技巧结合使用,例如CuPy和memmap(memmap总是延迟加载到主内存中,而不是GPU内存中)。但是某些组合可能会富有成果。

祝你好运!

,

我的另一个观点。我在连根拔起-> hdf5营地;这样,我可以做一次较慢的部分(将文件读入hdf5),并合并较小的文件并进行一些处理。我还保持压缩较低或关闭。这可能需要4到5分钟的连根读取许多文件,然后将其读取到

我要补充的一点是,如果您有“锯齿状”的数据(例如真相信息),则可以直接使用AwkwardArray(对hdf5具有本机支持)来很好地工作。我使用h5py处理HDF5文件。您可以在这里查看我的工作:https://gitlab.cern.ch/LHCb-Reco-Dev/pv-finder

以前也以这种方式进行设计,因为我没有一个可以同时在其中使用ROOT和ML工具在任何地方运行的环境,但是现在我使用Conda使用了单个environment.yml文件-forge ROOT和ML工具(PyTorch等)。

,

每个人都忘记了明显的成分:RDataFrame.AsNumpy(),例如https://root.cern.ch/doc/master/df026__AsNumpyArrays_8py.html

这样,就不需要临时文件,也不需要将所有内容加载到内存中。阅读以本机C ++速度进行。很高兴看到关于https://root-forum.cern.ch上效果更好的报告!

,

Jim似乎对选项提供了很好的概述,所以我将提供一种专门的策略:

由于您似乎正在执行“预选择”步骤,所以我认为您可以从以NumPy,HDF5或Parquet格式保存中间文件中受益。这样,您避免了每次在磁盘上处理数据时都重复选择计算(将这些格式加载到NumPy或pandas中与保存它们一样简单)。因此,我的建议是一次加载ROOT风格的数据(并且仅读取您感兴趣的分支),执行预选择步骤,并保存中间文件以备后用。我会举一个更具体的例子:

我的工作流程包括三个选择。我们可以将它们表示为pandas.DataFrame.eval / pandas.DataFrame.query字符串。 (如果可用,numexpr会加速pandas.eval)。这些类似于TTree::Draw选择。这是一个任意示例,其中我的树上有[electron_pt,regionA,regionB,regionC]列。

selectA = "(electron_pt >= 25) & (regionA == True)"
selectB = "(electron_pt >= 30) & (regionB == True)"
selectC = "(electron_pt >= 35) & (regionC == True)"

我可以一次将数据加载到数据框中并应用选择:

keep_columns = [......] # some list of branches to keep,must contain selection branches
df = uproot.open("file.root").get("tree").pandas.df(branches=keep_columns)

selections = {
    "A": selectA,"B": selectB,"C": selectC
}

现在我们可以遍历选择,查询数据框,并保存仅包含特定选择的中间格式。

for name,selection in selections.items():
    df.query(selection).to_hdf("file_selection{}.h5".format(name),name)
    # or save to parquet (if pyarrow is installed):
    # df.query(selection).to_parquet("file_section{}.parquet".format(name))

然后使用pandas.read_hdfpandas.read_parquet将文件读回到内存中。

过去,当我针对来自共同来源的数据训练ML分类器时,这种策略对我来说非常有效,但是需要将其分类为几个不同的选择。

本文链接:https://www.f2er.com/3117912.html

大家都在问