Este guia pressupõe que você já leu o guia de modelos e camadas .
No TensorFlow.js, há duas maneiras de treinar um modelo de aprendizado de máquina:
- usando a API Layers com
LayersModel.fit()
ouLayersModel.fitDataset()
. - usando a API principal com
Optimizer.minimize()
.
Primeiro, veremos a API Layers, que é uma API de nível superior para construção e treinamento de modelos. A seguir, mostraremos como treinar o mesmo modelo usando a API Core.
Introdução
Um modelo de aprendizado de máquina é uma função com parâmetros que podem ser aprendidos que mapeia uma entrada para uma saída desejada. Os parâmetros ideais são obtidos treinando o modelo nos dados.
O treinamento envolve várias etapas:
- Obtendo um lote de dados para o modelo.
- Pedir ao modelo para fazer uma previsão.
- Comparando essa previsão com o valor "verdadeiro".
- Decidir quanto alterar cada parâmetro para que o modelo possa fazer uma previsão melhor no futuro para aquele lote.
Um modelo bem treinado fornecerá um mapeamento preciso da entrada até a saída desejada.
Parâmetros do modelo
Vamos definir um modelo simples de 2 camadas usando a API Layers:
const model = tf.sequential({
layers: [
tf.layers.dense({inputShape: [784], units: 32, activation: 'relu'}),
tf.layers.dense({units: 10, activation: 'softmax'}),
]
});
Nos bastidores, os modelos têm parâmetros (geralmente chamados de pesos ) que podem ser aprendidos por meio do treinamento em dados. Vamos imprimir os nomes dos pesos associados a este modelo e suas formas:
model.weights.forEach(w => {
console.log(w.name, w.shape);
});
Obtemos a seguinte saída:
> dense_Dense1/kernel [784, 32]
> dense_Dense1/bias [32]
> dense_Dense2/kernel [32, 10]
> dense_Dense2/bias [10]
Existem 4 pesos no total, 2 por camada densa. Isso é esperado, pois as camadas densas representam uma função que mapeia o tensor de entrada x
para um tensor de saída y
por meio da equação y = Ax + b
onde A
(o kernel) e b
(o viés) são parâmetros da camada densa.
NOTA: Por padrão, as camadas densas incluem uma tendência, mas você pode excluí-la especificando
{useBias: false}
nas opções ao criar uma camada densa.
model.summary()
é um método útil se você deseja obter uma visão geral do seu modelo e ver o número total de parâmetros:
Camada (tipo) | Forma de saída | Parâmetro # |
denso_Dense1 (denso) | [nulo,32] | 25120 |
denso_Dense2 (denso) | [nulo,10] | 330 |
Parâmetros totais: 25450 Parâmetros treináveis: 25450 Parâmetros não treináveis: 0 |
Cada peso no modelo é backend por um objeto Variable
. No TensorFlow.js, uma Variable
é um Tensor
de ponto flutuante com um método adicional assign()
usado para atualizar seus valores. A API Layers inicializa automaticamente os pesos usando as práticas recomendadas. Para fins de demonstração, poderíamos substituir os pesos chamando assign()
nas variáveis subjacentes:
model.weights.forEach(w => {
const newVals = tf.randomNormal(w.shape);
// w.val is an instance of tf.Variable
w.val.assign(newVals);
});
Otimizador, perda e métrica
Antes de fazer qualquer treinamento, você precisa decidir sobre três coisas:
- Um otimizador . A tarefa do otimizador é decidir quanto alterar cada parâmetro do modelo, dada a previsão atual do modelo. Ao usar a API Layers, você pode fornecer um identificador de string de um otimizador existente (como
'sgd'
ou'adam'
) ou uma instância da classeOptimizer
. - Uma função de perda . Um objetivo que o modelo tentará minimizar. Seu objetivo é fornecer um único número para “quão errada” estava a previsão do modelo. A perda é calculada em cada lote de dados para que o modelo possa atualizar seus pesos. Ao usar a API Layers, você pode fornecer um identificador de string de uma função de perda existente (como
'categoricalCrossentropy'
) ou qualquer função que receba um valor previsto e um valor verdadeiro e retorne uma perda. Veja uma lista de perdas disponíveis em nossos documentos da API. - Lista de métricas. Semelhante às perdas, as métricas calculam um único número, resumindo o desempenho do nosso modelo. As métricas são geralmente calculadas sobre todos os dados no final de cada época. No mínimo, queremos monitorar se nossa perda está diminuindo com o tempo. No entanto, muitas vezes queremos uma métrica mais amigável, como a precisão. Ao usar a API Layers, você pode fornecer um identificador de string de uma métrica existente (como
'accuracy'
) ou qualquer função que receba um valor previsto e um valor verdadeiro e retorne uma pontuação. Veja uma lista de métricas disponíveis em nossos documentos de API.
Quando você decidir, compile um LayersModel
chamando model.compile()
com as opções fornecidas:
model.compile({
optimizer: 'sgd',
loss: 'categoricalCrossentropy',
metrics: ['accuracy']
});
Durante a compilação, o modelo fará algumas validações para garantir que as opções escolhidas sejam compatíveis entre si.
Treinamento
Existem duas maneiras de treinar um LayersModel
:
- Usando
model.fit()
e fornecendo os dados como um grande tensor. - Usando
model.fitDataset()
e fornecendo os dados por meio de um objetoDataset
.
modelo.fit()
Se o seu conjunto de dados cabe na memória principal e está disponível como um único tensor, você pode treinar um modelo chamando o método fit()
:
// Generate dummy data.
const data = tf.randomNormal([100, 784]);
const labels = tf.randomUniform([100, 10]);
function onBatchEnd(batch, logs) {
console.log('Accuracy', logs.acc);
}
// Train for 5 epochs with batch size of 32.
model.fit(data, labels, {
epochs: 5,
batchSize: 32,
callbacks: {onBatchEnd}
}).then(info => {
console.log('Final accuracy', info.history.acc);
});
Nos bastidores, model.fit()
pode fazer muito por nós:
- Divide os dados em um conjunto de treinamento e validação e usa o conjunto de validação para medir o progresso durante o treinamento.
- Embaralha os dados, mas somente após a divisão. Para estar seguro, você deve embaralhar previamente os dados antes de passá-los para
fit()
. - Divide o tensor de dados grandes em tensores menores de tamanho
batchSize.
- Chama
optimizer.minimize()
enquanto calcula a perda do modelo em relação ao lote de dados. - Ele pode notificá-lo no início e no final de cada época ou lote. No nosso caso, somos notificados ao final de cada lote usando a opção
callbacks.onBatchEnd
. Outras opções incluem:onTrainBegin
,onTrainEnd
,onEpochBegin
,onEpochEnd
eonBatchBegin
. - Ele cede ao thread principal para garantir que as tarefas enfileiradas no loop de eventos JS possam ser tratadas em tempo hábil.
Para obter mais informações, consulte a documentação de fit()
. Observe que se você optar por usar a API Core, terá que implementar essa lógica sozinho.
modelo.fitDataset()
Se seus dados não couberem inteiramente na memória ou estiverem sendo transmitidos, você poderá treinar um modelo chamando fitDataset()
, que usa um objeto Dataset
. Aqui está o mesmo código de treinamento, mas com um conjunto de dados que envolve uma função geradora:
function* data() {
for (let i = 0; i < 100; i++) {
// Generate one sample at a time.
yield tf.randomNormal([784]);
}
}
function* labels() {
for (let i = 0; i < 100; i++) {
// Generate one sample at a time.
yield tf.randomUniform([10]);
}
}
const xs = tf.data.generator(data);
const ys = tf.data.generator(labels);
// We zip the data and labels together, shuffle and batch 32 samples at a time.
const ds = tf.data.zip({xs, ys}).shuffle(100 /* bufferSize */).batch(32);
// Train the model for 5 epochs.
model.fitDataset(ds, {epochs: 5}).then(info => {
console.log('Accuracy', info.history.acc);
});
Para obter mais informações sobre conjuntos de dados, consulte a documentação de model.fitDataset()
.
Previsão de novos dados
Depois que o modelo for treinado, você pode chamar model.predict()
para fazer previsões sobre dados não vistos:
// Predict 3 random samples.
const prediction = model.predict(tf.randomNormal([3, 784]));
prediction.print();
API principal
Anteriormente, mencionamos que existem duas maneiras de treinar um modelo de aprendizado de máquina no TensorFlow.js.
A regra geral é tentar usar a API Layers primeiro, uma vez que ela é modelada a partir da bem adotada API Keras. A API Layers também oferece várias soluções prontas para uso, como inicialização de peso, serialização de modelo, treinamento de monitoramento, portabilidade e verificação de segurança.
Você pode querer usar a API Core sempre que:
- Você precisa de flexibilidade ou controle máximo.
- E você não precisa de serialização ou pode implementar sua própria lógica de serialização.
Para obter mais informações sobre esta API, leia a seção "API Core" no guia Modelos e Camadas .
O mesmo modelo acima escrito usando a API Core se parece com isto:
// The weights and biases for the two dense layers.
const w1 = tf.variable(tf.randomNormal([784, 32]));
const b1 = tf.variable(tf.randomNormal([32]));
const w2 = tf.variable(tf.randomNormal([32, 10]));
const b2 = tf.variable(tf.randomNormal([10]));
function model(x) {
return x.matMul(w1).add(b1).relu().matMul(w2).add(b2);
}
Além da API Layers, a API Data também funciona perfeitamente com a API Core. Vamos reutilizar o conjunto de dados que definimos anteriormente na seção model.fitDataset() , que embaralha e agrupa em lote para nós:
const xs = tf.data.generator(data);
const ys = tf.data.generator(labels);
// Zip the data and labels together, shuffle and batch 32 samples at a time.
const ds = tf.data.zip({xs, ys}).shuffle(100 /* bufferSize */).batch(32);
Vamos treinar o modelo:
const optimizer = tf.train.sgd(0.1 /* learningRate */);
// Train for 5 epochs.
for (let epoch = 0; epoch < 5; epoch++) {
await ds.forEachAsync(({xs, ys}) => {
optimizer.minimize(() => {
const predYs = model(xs);
const loss = tf.losses.softmaxCrossEntropy(ys, predYs);
loss.data().then(l => console.log('Loss', l));
return loss;
});
});
console.log('Epoch', epoch);
}
O código acima é a receita padrão ao treinar um modelo com a API Core:
- Faça um loop sobre o número de épocas.
- Dentro de cada época, faça um loop em seus lotes de dados. Ao usar um
Dataset
,dataset.forEachAsync()
é uma maneira conveniente de fazer um loop em seus lotes. - Para cada lote, chame
optimizer.minimize(f)
, que executaf
e minimiza sua saída calculando gradientes em relação às quatro variáveis que definimos anteriormente. -
f
calcula a perda. Ele chama uma das funções de perda predefinidas usando a previsão do modelo e o valor verdadeiro.