Javascript Tensorflow.js内存泄漏:如何清理未使用的张量?

Javascript Tensorflow.js内存泄漏:如何清理未使用的张量?,javascript,node.js,machine-learning,memory-leaks,tensorflow.js,Javascript,Node.js,Machine Learning,Memory Leaks,Tensorflow.js,我在写一个脚本,有时候会泄露张量。这可能发生在多种情况下,例如,当我训练神经网络时,但训练崩溃了。在这种情况下,训练被中断,无法正确处理张量。这会导致内存泄漏,我正试图通过处理未使用的张量来清除内存泄漏 示例 在下面的代码片段中,我正在训练两个(非常简单的)模型。第一次运行将起作用,不会导致张量泄漏(训练前的张量数量=训练后的张量数量)。第二次,我使用了一个无效的重塑层在训练期间强制崩溃。因此,将抛出一个错误,并且数据集中的张量(我猜?)将无法正确处理。该代码是一个示例,说明了张量是如何泄漏的

我在写一个脚本,有时候会泄露张量。这可能发生在多种情况下,例如,当我训练神经网络时,但训练崩溃了。在这种情况下,训练被中断,无法正确处理张量。这会导致内存泄漏,我正试图通过处理未使用的张量来清除内存泄漏

示例

在下面的代码片段中,我正在训练两个(非常简单的)模型。第一次运行将起作用,不会导致张量泄漏(训练前的张量数量=训练后的张量数量)。第二次,我使用了一个无效的
重塑
层在训练期间强制崩溃。因此,将抛出一个错误,并且数据集中的张量(我猜?)将无法正确处理。该代码是一个示例,说明了张量是如何泄漏的

异步功能序列(shouldCrash){
log(`Training,shouldcash=${shouldcash}`);
constDataSet=tf.data.zip({//setup data
xs:tf.data.array([[1],[1]]),
ys:tf.data.array([1]),
}).批次(1);
const model=tf.sequential({//设置模型
图层:[
密集({units:1,inputShape:[1]}),
tf.layers.reformate({targetShape:[(shouldCrash?2:1)]}),//崩溃时使用无效形状
],
});
compile({optimizer:'sgd',loss:'meanSquaredError'});
log('Tensors before:',tf.memory().numTensors);
试一试{
const history=await model.fitDataset(数据集,{epochs:1});
}捕捉(错误){
log(`Error:${err.message}`);
}
log('Tensors after:',tf.memory().numTensors);
}
(异步()=>{
等待训练(错误);//正常训练
等待训练(正确);//错误训练
})();

根据文档,提供给tf.tidy的函数“不得返回承诺”。在内部,tf后端处理拟合模型时使用的所有张量。这就是为什么不应将
tf.fit
放在
tf.tidy
内的原因。要处理崩溃的模型,可以对模型调用
tf.dispose

的确,当前似乎存在内存泄漏,但在定义模型期间发生模型崩溃是一个糟糕的实现。这不应该发生在适当的场景中,因为可以测试给定的参数是否与层的输入匹配。例如,在构建模型之前,可以避免重新塑造2到1的形状,以防止内存泄漏

异步功能序列(shouldCrash){
log(`Training,shouldcash=${shouldcash}`);
constDataSet=tf.data.zip({//setup data
xs:tf.data.array([[1],[1]]),
ys:tf.data.array([1]),
}).批次(1);
const model=tf.sequential({//设置模型
图层:[
密集({units:1,inputShape:[1]}),
tf.layers.reformate({targetShape:[(shouldCrash?2:1)]}),//崩溃时使用无效形状
],
});
compile({optimizer:'sgd',loss:'meanSquaredError'});
log('Tensors before:',tf.memory().numTensors);
试一试{
const history=await model.fitDataset(数据集,{epochs:1});
}捕捉(错误){
log(`Error:${err.message}`);
}
log('Tensors after:',tf.memory().numTensors);
回归模型
}
(异步()=>{
const m1=等待列车(错误);//正常培训
tf.处置(m1)
const m2=等待训练(真);//训练出错
tf.处理(m2)
tf.可处置资产()
log('Tensors after:',tf.memory().numTensors);
})();

清除异步代码中未使用的张量的方法是将创建它们的代码包装在startScope()和endScope()调用之间

tf.engine().startScope()
//做你的事

tf.engine().endScope()
我知道我所展示的代码“实现不佳”,但正如我所说的,它只是用来演示内存泄漏。
tf.disposeVariables
函数看起来非常有用,我甚至不知道可以将模型传递给
tf.dispose
。谢谢!:)我从这个方法中找到了答案。TFjs也在测试中使用它是的,这太棒了!它解决了我的记忆问题。谢谢谢谢,太好了,这创造了奇迹!它清除了开始和结束范围之间的所有张量:)是否有此API的文档?TensorFlow.js的维护者建议使用
.dispose()
而不是上述方法:。如果您有多个承诺同时运行,或者如果您在承诺中创建承诺(这在异步/等待代码中很常见),则上述方法可能会有问题:这种情况可能会导致来自不同承诺的交叉微任务,这可能会导致以下执行:
startScope
startScope
endScope
endScope
。第一个
endScope
可以处理将在另一个promise中使用的张量。