我正在使用tf.Tensor
和tf.concat()
处理大量的训练数据,
而且我发现连续使用tf.concat()
会很慢。
将大数据从文件加载到tf.Tensor
的最佳方法是什么?
我认为这是Javascript中按数组处理数据的常用方法。 要实现这一目标,这是要执行的粗略步骤。
Array.push()
将该对象添加到数组中所以我想我可以像上面一样使用tf.concat()
。
tf.concat()
将张量添加到原始张量这里有一些代码可以同时测量Array.push()
和tf.concat()
的速度
import * as tf from "@tensorflow/tfjs"
let t = tf.tensor1d([1])
let addT = tf.tensor1d([2])
console.time()
for (let idx = 0; idx < 50000; idx++) {
if (idx % 1000 == 0) {
console.timeEnd()
console.time()
console.log(idx)
}
t = tf.tidy(() => t.concat(addT))
}
let arr = []
let addA = 1
console.time()
for (let idx = 0; idx < 50000; idx++) {
if (idx % 1000 == 0) {
console.timeEnd()
console.time()
console.log(idx)
}
arr.push(addA)
}
我们可以看到Array.push()
上的流程稳定,
但在tf.concat()
default: 0.150ms
0
default: 68.725ms
1000
default: 62.922ms
2000
default: 23.199ms
3000
default: 21.093ms
4000
default: 27.808ms
5000
default: 39.689ms
6000
default: 34.798ms
7000
default: 45.502ms
8000
default: 94.526ms
9000
default: 51.996ms
10000
default: 76.529ms
11000
default: 83.662ms
12000
default: 45.730ms
13000
default: 89.119ms
14000
default: 49.171ms
15000
default: 48.555ms
16000
default: 55.686ms
17000
default: 54.857ms
18000
default: 54.801ms
19000
default: 55.312ms
20000
default: 65.760ms
default: 0.009ms
0
default: 0.388ms
1000
default: 0.340ms
2000
default: 0.333ms
3000
default: 0.317ms
4000
default: 0.330ms
5000
default: 0.289ms
6000
default: 0.299ms
7000
default: 0.291ms
8000
default: 0.320ms
9000
default: 0.284ms
10000
default: 0.343ms
11000
default: 0.327ms
12000
default: 0.317ms
13000
default: 0.329ms
14000
default: 0.307ms
15000
default: 0.218ms
16000
default: 0.193ms
17000
default: 0.234ms
18000
default: 1.943ms
19000
default: 0.164ms
20000
default: 0.148ms
答案 0 :(得分:1)
虽然tf.concat
和Array.push
函数的外观和行为相似,但有一个很大的区别:
tf.concat
从输入中创建一个新张量 Array.push
将输入添加到第一个数组 tf.concat
const a = tf.tensor1d([1, 2]);
const b = tf.tensor1d([3]);
const c = tf.concat([a, b]);
a.print(); // Result: Tensor [1, 2]
b.print(); // Result: Tensor [3]
c.print(); // Result: Tensor [1, 2, 3]
结果变量c
是新的张量,而a
和b
不变。
Array.push
const a = [1,2];
a.push(3);
console.log(a); // Result: [1,2,3]
在这里,变量a
被直接更改。
对于运行时速度,这意味着tf.concat
在添加输入之前将所有张量值复制到新的张量。显然,需要复制的数组越大,花费的时间就越多。与此相反,Array.push
不会创建数组的副本,因此无论数组有多大,运行时间都将大致相同。
请注意,这是“设计使然”的,因为张量是不可变的,因此对现有张量的每个操作始终会创建一个新的张量。引用docs:
张量是不可变的,因此所有操作总是返回新的张量,而从不修改输入张量。
因此,如果需要根据输入数据创建大张量,建议先从文件中读取所有数据,然后将其与“普通” JavaScript函数合并,然后再从中创建张量。
如果数据集太大,由于内存限制,需要分块处理它,则有两个选择:
trainOnBatch
函数 trainOnBatch
函数允许训练一批数据,而不是使用全部数据集。因此,您可以在训练它们之前将代码分成合理的批次,这样就不必一次将所有数据合并在一起。
另一个答案已经超出了基础知识。这将允许您使用JavaScript generator function来准备数据。我建议使用生成器语法而不是迭代器工厂(用于其他答案),因为它是更现代的JavaScript语法。
示例(摘自docs):
function* dataGenerator() {
const numElements = 10;
let index = 0;
while (index < numElements) {
const x = index;
index++;
yield x;
}
}
const ds = tf.data.generator(dataGenerator);
然后您可以使用fitDataset
函数来训练模型。
答案 1 :(得分:0)
尽管没有单一的方法来创建张量,但问题的答案在于如何处理创建的张量。
张量是不可变的,因此每次tf.concat
被称为创建新张量。
let x = tf.tensor1d([2]);
console.log(tf.memory()) // "numTensors": 1
const y = tf.tensor1d([3])
x = tf.concat([x, y])
console.log(tf.memory()) // "numTensors": 3,
<html>
<head>
<!-- Load TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.14.1"> </script>
</head>
<body>
</body>
</html>
从上面的片段中我们可以看到,调用tf.concat时创建的张量数量为 3 而不是 2 。的确,tf.tidy
将处理未使用的张量。但是,随着创建的张量越来越大,这种创建和处理张量的操作将变得最昂贵。这既是内存消耗又是计算的问题,因为创建新的张量将始终委托给后端。
现在了解性能问题了,最好的处理方法是什么?
for (i= 0; i < data.length; i++) {
// fill array x
x.push(dataValue)
}
// create the tensor
tf.tensor(x)
尽管这是微不足道的解决方案,但并非总是可行。因为创建数组会将数据保留在内存中,所以我们很容易用大数据条目耗尽内存。因此有时,最好不要创建整个javascript数组来创建数组块,并从这些数组块中创建张量,并在创建后立即开始处理这些张量。如有必要,可以再次使用tf.concat
合并块张量。但这并不总是必需的。
例如,我们可以使用张量块重复调用model.fit(),而不必使用可能需要很长时间才能创建的大张量调用一次。在这种情况下,不需要连接块张量。
function makeIterator() {
const iterator = {
next: () => {
let result;
if (index < data.length) {
result = {value: dataValue, done: false};
index++;
return result;
}
return {value: dataValue, done: true};
}
};
return iterator;
}
const ds = tf.data.generator(makeIterator);
使用tf.data的优点是,整个数据集是在model.fit
调用期间在需要时分批创建的。