用于数据集准备的Python多处理

用于数据集准备的Python多处理,python,multithreading,process,multiprocessing,queue,Python,Multithreading,Process,Multiprocessing,Queue,我正在寻找为机器学习任务准备数据集的较短方法。我发现多处理库可能会有所帮助。然而,因为我是多道处理的新手,我找不到合适的方法 我首先编写了如下代码: 类数据读取程序: 定义初始化(自): self.data\u list=从\u文件读取\u数据 self.data=[] def_就绪_数据(自身、ex、idx): #一些需要几分钟的复杂函数 定义数据集创建者(自身、队列): 对于idx,枚举中的ex(自数据列表): queue.put(self.\u ready\u data(ex,idx))

我正在寻找为机器学习任务准备数据集的较短方法。我发现多处理库可能会有所帮助。然而,因为我是多道处理的新手,我找不到合适的方法

我首先编写了如下代码:

类数据读取程序:
定义初始化(自):
self.data\u list=从\u文件读取\u数据
self.data=[]
def_就绪_数据(自身、ex、idx):
#一些需要几分钟的复杂函数
定义数据集创建者(自身、队列):
对于idx,枚举中的ex(自数据列表):
queue.put(self.\u ready\u data(ex,idx))
定义数据集消费者(自身、队列):
总平均值=0.0

t=tqdm(range(self.num_data),total=self.num_data,desc='Building Dataset',bar_format='{desc}:{percentage:3.0f}%({n_fmt}/{total_fmt})[{appeased}我试图勾勒出一个具有多处理池的解决方案是什么样子。我完全摆脱了消费者进程,因为它看起来就像父进程在等待一样(最终需要数据)这样它就可以成为消费者。因此,我设置了一个池,并使用
imap\u unordered
处理将数据传递给工作者的过程

我猜想数据处理实际上根本不需要DatasetReader,并将其移到了自己的函数中。在Windows上,要么整个
DataReader
对象被序列化到子进程(包括您不想要的数据),要么对象的子版本不完整,在您尝试使用它时可能会崩溃

无论哪种方式,对子进程中的
DatasetReader
对象所做的更改在父进程中都看不到。如果父进程依赖于该对象中的更新状态,这可能是意外的。我认为,最好严格地将子进程中发生的情况括起来

from multiprocessing import Pool, get_start_method, cpu_count

# moved out of class (assuming it is not class dependent) so that
# the entire DatasetReader object isn't pickled and sent to
# the child on spawning systems like Microsoft Windows

def _ready_data(idx_ex):
    idx, ex = idx_ex
    # Some complex functions that take several minutes
    result = complex_functions(ex)
    return (idx, result)


class DatasetReader:

    def __init__(self):
        self.data_list = Read_Data_from_file
        self.data = [None] * len(data_list)

    def _ready_data_fork(self, idx):
        # on forking system, call worker with object data
        return _ready_data((idx, self.data_list[idx]))

    def run(self):

        t = tqdm(range(self.num_data), total=self.num_data, desc='Building Dataset ',
            bar_format='{desc}:{percentage:3.0f}% ({n_fmt}/{total_fmt}) '
                '[{elapsed}<{remaining},{rate_fmt}{postfix}]')

        pool = Pool(min(cpu_count, len(self.data_list)))
        if get_start_method() == 'fork':
            # on forking system, self.data_list is in child process and
            # we only pass the index
            result_iter = pool.imap_unordered(self._ready_data_fork, 
                    (idx for idx in range(len(data_list))),
                    chunksize=1)
        else:
            # on spawning system, we need to pass the data
            result_iter = pool.imap_unordered(_ready_data,
                    enumerate(self.data_list,
                    chunksize=1)

        for idx, result in result_iter:
            next(t)
            self.data[idx] = result

        pool.join()
从多处理导入池,获取启动方法,cpu计数
#移出类(假设它不依赖于类),以便
#整个DatasetReader对象未被pickle并发送到
#生成系统(如Microsoft Windows)上的子系统
def_就绪_数据(idx_ex):
idx,ex=idx_ex
#一些需要几分钟的复杂函数
结果=复杂函数(ex)
返回(idx,结果)
类DatasetReader:
定义初始化(自):
self.data\u list=从\u文件读取\u数据
self.data=[None]*len(数据列表)
def_就绪_数据_分叉(自身,idx):
#在分叉系统上,使用对象数据调用worker
返回_ready_数据((idx,self.data_列表[idx]))
def运行(自):
t=tqdm(范围(self.num_数据),总计=self.num_数据,desc='Building Dataset',
条形图格式=“{desc}:{百分比:3.0f}%({n_fmt}/{total_fmt})”

“[{appead}随机思维-你在过程中失败了18-一次运行更少的数据可能会有所帮助。
从文件中读取数据
是你从文件中读取的一堆数据吗?你能在过程中完成读取部分吗?
\u ready\u Data
的结果是否很大?它的计算是用python还是在一些子系统中完成的或者发布GIL后在C中工作的
scipy
?gc.collect
可能没有多大作用,除非你有很多循环引用数据。但是如果
t
inx
很大,最好在处理完它们后立即删除它们。@tdelaney我认为我可以生成有限数量的进程,s用数据的编号和处理部分来分割数据。也许我应该这样做。因为整个数据都在一个文件中,我认为除非我分割数据文件,否则很难分割数据读取部分。
\u ready\u data
的结果相当大(大约200~300KB)。我想所有的计算都是用python完成的。我想我不再需要
gc.collect
。谢谢你的评论!:)这是一个有趣的问题。这是mac/linux系统中的进程分叉,还是windows系统中的进程分叉?如果分叉,您不必将'ex'放在队列中,它已经在子进程空间中了。@tdelaney我目前使用的是Centos,它可能是linux系统,但我无法理解分叉和分叉之间的区别(您能分享一些参考或一些可能有用的东西吗?谢谢您的帮助!在fork上,子进程在创建进程时具有父内存的“写时拷贝”视图。因此不要传递
self.data\u list[idex]
对于子级,它已经存在。在spawn上,python必须pickle父级状态并在子级中取消pickle。因此,您必须传递
self.data\u list[idx]
。但请小心,当您创建该子流程并想要使用
self
时,您可能会发现它复制了所有
数据列表
。谢谢!我终于明白您试图解释的原因了!另一方面,目前可能很难应用
fork
ing功能,因为
\u ready\u data
使用了大量f
self
函数和变量:(也许我应该检查一下是否可以将其移出类,这样我就可以避免对整个DatasetReader对象进行酸洗。无论如何,感谢您的帮助,进行了友好的解释!在分叉系统上使用self并不一定不好,只需知道您在分叉时正在处理对象的快照。一个问题是长期维护-以子流程中不起作用的方式更新代码很容易。您可以通过将数据计算方法提取到不同的类中来划分类。它们可能只是为了方便起见才出现在这个类中。通过创建更集中的类来分离关注点可能是一件好事。我不知道您的环境,只是笼统地说。
from multiprocessing import Pool, get_start_method, cpu_count

# moved out of class (assuming it is not class dependent) so that
# the entire DatasetReader object isn't pickled and sent to
# the child on spawning systems like Microsoft Windows

def _ready_data(idx_ex):
    idx, ex = idx_ex
    # Some complex functions that take several minutes
    result = complex_functions(ex)
    return (idx, result)


class DatasetReader:

    def __init__(self):
        self.data_list = Read_Data_from_file
        self.data = [None] * len(data_list)

    def _ready_data_fork(self, idx):
        # on forking system, call worker with object data
        return _ready_data((idx, self.data_list[idx]))

    def run(self):

        t = tqdm(range(self.num_data), total=self.num_data, desc='Building Dataset ',
            bar_format='{desc}:{percentage:3.0f}% ({n_fmt}/{total_fmt}) '
                '[{elapsed}<{remaining},{rate_fmt}{postfix}]')

        pool = Pool(min(cpu_count, len(self.data_list)))
        if get_start_method() == 'fork':
            # on forking system, self.data_list is in child process and
            # we only pass the index
            result_iter = pool.imap_unordered(self._ready_data_fork, 
                    (idx for idx in range(len(data_list))),
                    chunksize=1)
        else:
            # on spawning system, we need to pass the data
            result_iter = pool.imap_unordered(_ready_data,
                    enumerate(self.data_list,
                    chunksize=1)

        for idx, result in result_iter:
            next(t)
            self.data[idx] = result

        pool.join()