有不同的方法
将数据集分块(在未来节省时间,但需要初始时间投入)
分块可以让您简化许多操作,例如洗牌等。
确保每个子集/块都代表整个数据集。每个块文件应该有相同数量的行。
这可以通过将一行追加到一个又一个文件来完成。很快,您就会意识到打开每个文件并写一行是低效的。尤其是在同一个驱动器上读写时。
-> 添加适合内存的写入和读取缓冲区。
选择适合您需要的块大小。我选择这个特定的大小是因为我的默认文本编辑器仍然可以相当快地打开它。
较小的块可以提高性能,特别是如果您想获得类分布等指标,因为您只需遍历一个代表性文件即可获得对整个数据集的估计,这可能就足够了。
较大的块文件确实可以更好地表示每个文件中的整个数据集,但您也可以只浏览 x 个较小的块文件。
我确实为此使用了 c#,因为我在那里更有经验,因此我可以使用完整的功能集,例如将任务 reading / processing / writing 拆分到不同的线程。
如果您有使用 python 或 r 的经验,我怀疑应该也有类似的功能。在如此大的数据集上,并行化可能是一个重要因素。
可以将分块数据集建模为一个交错数据集,您可以使用张量处理单元对其进行处理。这可能会产生最好的性能之一,并且可以在本地以及真正大型机器上的云中执行。但这需要对 tensorflow 进行大量学习。
使用阅读器,逐步阅读文件
您不想做all_of_it = file.read() 之类的事情,而是想使用某种流阅读器。以下函数逐行读取其中一个块文件(或整个 300gb 数据集)以计算文件中的每个类。通过一次处理一行,您的程序不会溢出内存。
您可能想要添加一些进度指示,例如 X 行/秒或 X MBbs,以便估算总处理时间。
def getClassDistribution(path):
classes = dict()
# open sample file and count classes
with open(path, "r",encoding="utf-8",errors='ignore') as f:
line = f.readline()
while line:
if line != '':
labelstring = line[-2:-1]
if labelstring == ',':
labelstring = line[-1:]
label = int(labelstring)
if label in classes:
classes[label] += 1
else:
classes[label] = 1
line = f.readline()
return classes
我使用分块数据集和估计的组合。
性能缺陷
-
尽可能避免嵌套循环。另一个循环中的每个循环都会将复杂度乘以 n
-
尽可能一次性处理数据。每个循环一个接一个地增加 n 的复杂度
- 如果您的数据采用 csv 格式,请避免使用
cells = int(line.Split(',')[8]) 等预制函数,这将很快导致内存吞吐量瓶颈。一个恰当的例子可以在getClassDistribution找到,我只想得到标签。
以下 C# 函数将 csv 行快速拆分为元素。
// Call function
ThreadPool.QueueUserWorkItem((c) => AnalyzeLine("05.02.2020,12.20,10.13").Wait());
// Parralelize this on multiple cores/threads for ultimate performance
private async Task AnalyzeLine(string line)
{
PriceElement elementToAdd = new PriceElement();
int counter = 0;
string temp = "";
foreach (char c in line)
{
if (c == ',')
{
switch (counter)
{
case 0:
elementToAdd.spotTime = DateTime.Parse(temp, CultureInfo.InvariantCulture);
break;
case 1:
elementToAdd.buyPrice = decimal.Parse(temp);
break;
case 2:
elementToAdd.sellPrice = decimal.Parse(temp);
break;
}
temp = "";
counter++;
}
else temp += c;
}
// compare the price element to conditions on another thread
Observate(elementToAdd);
}
创建数据库并加载数据
在处理类似 csv 的数据时,您可以将数据加载到数据库中。
数据库是为容纳大量数据而设计的,您可以期待非常高的性能。
与原始数据相比,数据库可能会占用更多磁盘空间。这就是我不再使用数据库的原因之一。
硬件优化
如果您的代码优化得当,您的瓶颈很可能是硬盘吞吐量。
- 如果数据适合您的本地硬盘驱动器,请在本地使用它,因为这样可以消除网络延迟(假设本地网络中的每条记录需要 2-5 毫秒,远程位置需要 10-100 毫秒)。
- 使用现代硬盘。一个 1tb NVME SSD 今天的成本约为 130(intel 600p 1tb)。 nvme ssd 使用 pcie,比普通 ssd 快 5 倍,比普通硬盘快 50 倍,尤其是在快速写入不同位置时(分块数据)。近年来,SSD 的容量已经大大增加,对于这样的任务来说,这将是野蛮的。
以下屏幕截图提供了在同一台机器上使用相同数据进行 tensorflow 训练的性能比较。一次只保存在本地标准 ssd 上,一次保存在本地网络中的网络附加存储(普通硬盘)上。