簡體   English   中英

使用Tensorflow.js和tf.Tensor處理大數據的最佳方法是什么?

[英]What is the best way to handle large data with Tensorflow.js and tf.Tensor?

我使用tf.Tensortf.concat()來處理大型訓練數據,我發現連續使用tf.concat()變慢了。 將大型數據從文件加載到tf.Tensor的最佳方法是什么?

背景

我認為這是在Javascript中通過數組處理數據的常用方法。 為實現這一目標,這是艱難的步驟。

將數據從文件加載到Array的步驟

  1. 從文件中讀取行
  2. 解析Javascript的對象行
  3. 通過Array.push()將該對象添加到數組
  4. 在完成讀到行的結束后,我們可以將該數組與for循環一起使用。

所以我認為我可以tf.concat()上面類似的方式使用tf.concat()

將數據從文件加載到tf.Tensor的步驟

  1. 從文件中讀取行
  2. 解析Javascript的對象行
  3. 將對象解析為tf.Tensor
  4. 通過tf.concat()將張量添加到原始張量
  5. 在讀完行后,我們可以使用那個tf.Tensor

一些代碼

這里有一些代碼來測量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()上它變慢了

對於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

對於Array.push()

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

雖然tf.concatArray.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是新的Tensor,而ab不變。

Array.push

const a = [1,2];
a.push(3);

console.log(a); // Result: [1,2,3]

這里,變量a直接改變。

對運行時的影響

對於運行時速度,這意味着在添加輸入之前, tf.concat所有張量值復制到新的張量。 這顯然需要更多時間,需要復制的數組越大。 與此相反, Array.push不會創建數組的副本,因此無論數組有多大,運行時都會或多或少相同。

請注意,這是“按設計”,因為張量是不可變的,因此現有張量上的每個操作總是會創建一個新的張量。 文檔引用:

張量是不可變的,因此所有操作總是返回新的張量並且永遠不會修改輸入張量。

因此,如果需要從輸入數據創建大張量,建議首先從文件中讀取所有數據,然后將其與“vanilla”JavaScript函數合並,然后再從中創建張量。

處理對於內存來說太大的數據

如果您有一個如此大的數據集,由於內存限制需要以塊的形式處理它,您有兩個選擇:

  1. 使用trainOnBatch功能
  2. 使用數據集生成器

選項1:trainOnBatch

trainOnBatch功能允許訓練一批數據,而不是使用完整的數據集。 因此,您可以在訓練之前將代碼拆分為合理的批次,這樣您就不必一次將所有數據合並在一起。

選項2:數據集生成器

另一個答案已經完成了基礎知識。 這將允許您使用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);

然后,您可以使用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, 
 <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將處置未使用的張量。 但是,隨着創建的張量變得越來越大,這種創建和處理張量的操作將變得最大且成本最高。 這既是內存消耗和計算的問題,因為創建新的張量將始終委托給后端。


從大數據創建張量

既然已經了解了性能問題,那么最好的方法是什么?

  • 在js中創建整個數組,當整個數組完成時,然后創建張量。
for (i= 0; i < data.length; i++) {
  // fill array x
  x.push(dataValue)
}
// create the tensor
tf.tensor(x)

雖然,這是微不足道的解決方案,但並不總是可行的。 因為創建一個數組會將數據保存在內存中,並且我們可以很容易地用大數據條目耗盡內存。 因此,有時候,最好不要創建整個javascript數組來創建數組,並從這些數組中創建一個張量,並在創建它們后立即開始處理這些張量。 如有必要,可以再次使用tf.concat合並塊張量。 但它可能並不總是必需的。

例如,我們可以使用張量塊重復調用model.fit(),而不是使用可能需要很長時間才能創建的大張量來調用它。 在這種情況下,不需要連接塊張量。

  • 如果可能,使用tf.data創建數據集。 如果我們接下來要使用數據模型,這是理想的解決方案。
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調用期間需要時,批量創建整個數據集。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM