ya9*_*9do 5 javascript node.js deep-learning tensorflow tensorflow.js
我正在使用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)
}
Run Code Online (Sandbox Code Playgroud)
我们可以在 上看到稳定的过程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
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
虽然tf.concatandArray.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]
Run Code Online (Sandbox Code Playgroud)
结果变量c是一个新的张量,而a和b没有改变。
Array.push
const a = [1,2];
a.push(3);
console.log(a); // Result: [1,2,3]
Run Code Online (Sandbox Code Playgroud)
a这里直接改变变量。
对于运行时速度,这意味着tf.concat在添加输入之前将所有张量值复制到新张量。需要复制的数组越大,这显然需要更多的时间。与此相反,Array.push不会创建数组的副本,因此无论数组有多大,运行时都会或多或少相同。
请注意,这是“设计使然”,因为张量是不可变的,因此对现有张量的每次操作总是创建一个新的张量。引用文档:
张量是不可变的,因此所有操作始终返回新的张量,并且永远不会修改输入张量。
因此,如果您需要从输入数据创建一个大张量,建议首先从文件中读取所有数据并将其与“vanilla”JavaScript 函数合并,然后再从中创建张量。
如果您的数据集太大,由于内存限制而需要分块处理,您有两种选择:
trainOnBatch功能该trainOnBatch函数允许对一批数据进行训练,而不是使用完整的数据集。因此,您可以在训练代码之前将代码分成合理的批次,这样您就不必一次将所有数据合并在一起。
另一个答案已经介绍了基础知识。这将允许您使用JavaScript 生成器函数来准备数据。我建议使用生成器语法而不是迭代器工厂(在另一个答案中使用),因为它是更现代的 JavaScript 语法。
示例(取自文档):
function* dataGenerator() {
const numElements = 10;
let index = 0;
while (index < numElements) {
const x = index;
index++;
yield x;
}
}
const ds = tf.data.generator(dataGenerator);
Run Code Online (Sandbox Code Playgroud)
然后,您可以使用该fitDataset函数来训练您的模型。
尽管创建张量的方法并不单一,但问题的答案在于如何处理创建的张量。
张量是不可变的,因此每次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, Run Code Online (Sandbox Code Playgroud)
<html>
<head>
<!-- Load TensorFlow.js -->
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@0.14.1"> </script>
</head>
<body>
</body>
</html>Run Code Online (Sandbox Code Playgroud)
正如我们从上面的代码片段中看到的,调用 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)
Run Code Online (Sandbox Code Playgroud)
虽然这是一个简单的解决方案,但并不总是可行。因为创建数组会将数据保存在内存中,而大数据条目很容易耗尽内存。因此,有时,最好不要创建整个 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);
Run Code Online (Sandbox Code Playgroud)
使用 tf.data 的优点是整个数据集是在model.fit调用过程中需要时批量创建的。
| 归档时间: |
|
| 查看次数: |
1641 次 |
| 最近记录: |