Недостаточно памяти после пары вызовов decodeJpeg - PullRequest
1 голос
/ 25 мая 2020

Я хочу обработать пару изображений (на самом деле довольно много) с помощью Tensorflow, используя tf js -node.

К сожалению, на моем Raspberry Pi я быстро испытываю ошибку нехватки памяти. Это фрагмент в Typescript:

import * as tfnode from '@tensorflow/tfjs-node'
import * as fs from 'fs'
import * as path from 'path'
import * as process from 'process'

let counter = 0

setInterval(() => {
    const imageBuffer = fs.readFileSync(path.join(__dirname, 'samples', 'image.jpg'))

    const tfimage = tfnode.node.decodeJpeg(imageBuffer)

    console.log(`${++counter} - ${process.memoryUsage().rss / 1024 / 1024} MB`)
}, 100)

Это выводит что-то вроде

1 - 216.390625 MB
2 - 357.78515625 MB
3 - 499.421875 MB
4 - 641.5703125 MB
5 - 782.9453125 MB
6 - 924.3203125 MB
7 - 1066.7265625 MB
8 - 1208.09765625 MB
9 - 1349.4765625 MB
10 - 1491.625 MB
11 - 1633.2578125 MB
2020-05-25 22:36:12.101809: W tensorflow/core/framework/op_kernel.cc:1651] OP_REQUIRES failed at cast_op.cc:109 : Resource exhausted: OOM when allocating tensor with shape[3024,4032,3] and type int32 on /job:localhost/replica:0/task:0/device:CPU:0 by allocator cpu
/home/pi/develop/tftest/build/node_modules/@tensorflow/tfjs-core/dist/engine.js:404
            throw ex;
            ^

Error: Invalid TF_Status: 8
Message: OOM when allocating tensor with shape[3024,4032,3] and type int32 on /job:localhost/replica:0/task:0/device:CPU:0 by allocator cpu
    at NodeJSKernelBackend.executeSingleOutput (/home/pi/develop/tftest/build/node_modules/@tensorflow/tfjs-node/dist/nodejs_kernel_backend.js:193:43)
    at NodeJSKernelBackend.cast (/home/pi/develop/tftest/build/node_modules/@tensorflow/tfjs-node/dist/nodejs_kernel_backend.js:1141:21)
    at engine_1.ENGINE.runKernelFunc.x (/home/pi/develop/tftest/build/node_modules/@tensorflow/tfjs-core/dist/ops/array_ops.js:139:78)
    at /home/pi/develop/tftest/build/node_modules/@tensorflow/tfjs-core/dist/engine.js:542:55
    at /home/pi/develop/tftest/build/node_modules/@tensorflow/tfjs-core/dist/engine.js:388:22
    at Engine.scopedRun (/home/pi/develop/tftest/build/node_modules/@tensorflow/tfjs-core/dist/engine.js:398:23)
    at Engine.tidy (/home/pi/develop/tftest/build/node_modules/@tensorflow/tfjs-core/dist/engine.js:387:21)
    at kernelFunc (/home/pi/develop/tftest/build/node_modules/@tensorflow/tfjs-core/dist/engine.js:542:29)
    at /home/pi/develop/tftest/build/node_modules/@tensorflow/tfjs-core/dist/engine.js:553:27
    at Engine.scopedRun (/home/pi/develop/tftest/build/node_modules/@tensorflow/tfjs-core/dist/engine.js:398:23)

Я пробовал библиотеки рук из https://github.com/Qengineering/TensorFlow-Raspberry-Pi и https://github.com/yhwang/node-red-contrib-tf-model, оба ведут себя одинаково.

Затем я попробовал этот самый тест фрагмента на Windows и получил очень удивительные результаты. Потребление памяти быстро увеличивается до 8 ГБ, а затем неустойчиво колеблется между 800 МБ и 8 ГБ. Конечно, это работает, поскольку в системе намного больше памяти, чем у RPi.

Но что я могу сделать с RPi? Могу ли я контролировать управление памятью Tensorflow?

Ответы [ 2 ]

1 голос
/ 26 мая 2020

Виноват ли setInterval?

Может быть проблема с памятью, связанная с тем, как используется setInterval, но обычно это связано с обратным вызовом setInterval с использованием замыкания, сохраняющего ссылку на определенные объекты. При этом сборщик мусора G C в движках javascript улучшился настолько, что он может обнаруживать эти недостижимые коды и удалять большинство из них. setInterval явно не является причиной проблемы. for или while l oop могли бы вызвать проблему.

Почему заполнена память?

Основная причина в том, что слишком много создаются тензоры.

const tfimage = tfnode.node.decodeJpeg(imageBuffer)

Тензоры неизменяемы. Каждый раз, когда появляется новое назначение, создается новый тензор. Следовательно, объем памяти увеличивается с увеличением числа тензоров. По-другому все работает со встроенными объектами js. Однако следующий код требует некоторой памяти - для создания массива - не вызовет утечки памяти. Поскольку G C знает, что для каждой итерации while l oop предыдущая переменная v больше не будет использоваться и, следовательно, будет собираться мусор.

while(true) {
    const v = Array.from({length: 1000000}, k => k+1)
}

Как сделать решить проблему

Каждый созданный тензор должен быть явно помещен в конец блока while. tf.dispose поможет избавиться от одного тензора.

setInterval(() => {
    const imageBuffer = fs.readFileSync(path.join(__dirname, 'temp.png'))

    const tfimage = tfnode.node.decodeJpeg(imageBuffer)
    tfnode.dispose(tfimage)

    console.log(`${++counter} - ${process.memoryUsage().rss / 1024 / 1024} MB`)
}, 100)

В случае, если нужно избавиться от многих тензоров, их можно использовать в блоке tf.tidy

setInterval(() => {
    tfnode.tidy(() => {
        const imageBuffer = fs.readFileSync(path.join(__dirname, 'temp.png'))

        const tfimage = tfnode.node.decodeJpeg(imageBuffer)
        console.log(`${++counter} - ${process.memoryUsage().rss / 1024 / 1024} MB`)
    })
}, 100)
1 голос
/ 26 мая 2020

На самом деле я раньше не работал с TensorFlow, но у меня есть представление о части setInterval.

Я думаю, что, когда setInterval l oop выполняет новую операцию, старая операция, которая выполнялась ранее, не завершена и может создать слишком много сложенных незавершенных операций с большим количеством памяти, которая уже используется используется старыми и все еще активными операциями.

Таким образом, вы можете сделать это последовательно, как при использовании традиционного while или для l oop, чтобы не блокировать событие l oop, как метод setImmediate.

Я не уверен в своем решении, но дай ему шанс.

...