关于具有 2 个特征的 Tensorflow 分类模型的问题

Question about Tensorflow classification model with 2 features

我正在尝试在 Tensorflow.js 中构建模型以 class 验证 2 个特征。

Input (training) data screenshot for clarity <- "Class" 有 A 和 B 特征输入的标签。

我的结果如下所示: Results where "Pred-Class" is predicted by model

因此,根据结果,我想向社区询问下一个问题:

1. 我正在为模型提供下一个张量 classification 的 One Hot 值:

[[0, 1, 0],
[0, 0, 0],
[0, 0, 0],
[1, 0, 0],
[0, 1, 0]]

但是我收到的模型的预测答案是浮动的:

[[0.3534753, 0.4548116, 0.1917132],
 [0.3060284, 0.5562349, 0.1377369],
 [0.2464814, 0.6586764, 0.094842 ],
 [0.321316 , 0.5279192, 0.1507648],
 [0.3391353, 0.4934992, 0.1673654]]

你能帮我理解这是正确的方法还是我的设置有错误?

2。我是否正确地将 One Hot 转换回 Tensor

decodedPred = tf.argMax(preds, axis=1)

Link 到行 https://github.com/webjema/TF-PH-AB-SIGMOID/blob/master/script.js#L67

3。一般来说,数据 and/or 模型设置中有什么大错误吗? 找不到 tf.js 示例来 class 将 n 个输入验证为一个 class(其中 n > 1).

此测试项目在 GitHub - https://github.com/webjema/TF-PH-AB-SIGMOID(带有 Docker 以便于检查)。

主要脚本:

async function getHealthData() {
  const healthDataReq = await fetch('healthData.json');
  const healthData = await healthDataReq.json();
  const cleanedHealthData = healthData.map(d => ({
    featureA: d.A,
    featureB: d.B,
    label: d.Class
  })).filter(d => (d.featureA != null && d.featureB != null && d.label != null));
  return cleanedHealthData;
}

async function getTestData() {
  const testDataReq = await fetch('testData.json');
  const testData = await testDataReq.json();
  const cleanedTestData = testData.map(d => ({
    featureA: d.A,
    featureB: d.B
  })).filter(d => (d.featureA != null && d.featureB != null));
  return cleanedTestData;
}

function createModel() {
  // Create a sequential model
  const model = tf.sequential();
  // Add an input layer
  model.add(tf.layers.dense({ inputShape: [2], units: 1, useBias: true }));
  model.add(tf.layers.dense({ units: 15, activation: 'relu' }));
  model.add(tf.layers.dense({ units: 10, activation: 'relu' }));

  // Add an output layer
  model.add(tf.layers.dense({ units: 3, activation: 'softmax' }));
  return model;
}

async function trainModel(model, inputs, labels) {
  // Prepare the model for training.  
  model.compile({
    optimizer: tf.train.adam(),
    loss: tf.losses.meanSquaredError, //categorical_crossentropy? how?
    metrics: ['acc'],
  });
  const batchSize = 10;
  const epochs = 140;
  const oneHot = tf.oneHot(labels, 3);
  console.log("Train input:"); inputs.print();
  console.log("Labels oneHot:"); oneHot.print(); // debug
  return await model.fit(inputs, oneHot, {
    batchSize,
    epochs,
    shuffle: true,
    callbacks: tfvis.show.fitCallbacks(
      { name: 'Training Performance' },
      ['loss', 'label'],
      { height: 200, callbacks: ['onEpochEnd'] }
    )
  });
}

function testModel(model, inputData, min, max) {
  const { inputs, labels } = inputData;
  const unNormInput = inputs
    .mul(max.sub(min))
    .add(min);
  console.log("Test data:");unNormInput.print(); // debug
  const preds = model.predict(inputs);
  console.log("Predict:"); preds.print(); // debug
  decodedPred = tf.argMax(preds, axis=1);
  console.log("Decoded Predict:"); decodedPred.print(); // debug
  const decodedPredArray = decodedPred.arraySync();

  // show output data table
  const headers = ['Feature A', 'Feature B', 'Pred-Class'];
  const values = unNormInput.arraySync().map((e, i) => e.concat(decodedPredArray[i]));
  const surface = { name: 'Output health data table', tab: 'Data analisys' };
  tfvis.render.table(surface, { headers, values });
}

async function run() {
  // Load and plot the original input data that we are going to train on.
  const healthData = await getHealthData();
  const testData = await getTestData();
  const { min, max } = getMinMax(healthData, testData);
  // show input data table
  const headers = ['Feature A', 'Feature B', 'Class'];
  const values = healthData.map(d => [d.featureA, d.featureB, d.label]);
  const surface = { name: 'Input health data table', tab: 'Data analisys' };
  tfvis.render.table(surface, { headers, values });
  // Create the model
  const model = createModel();
  tfvis.show.modelSummary({ name: 'Model Summary' }, model);
  // Convert the data to a form we can use for training.
  const { inputs, labels } = convertToTensor(healthData, min, max);
  // Train the model  
  await trainModel(model, inputs, labels);
  console.log('Done Training');
  // Test model
  const testTensorData = convertToTensor(testData, min, max);
  testModel(model, testTensorData, min, max);
  console.log('Done Testing');
}

document.addEventListener('DOMContentLoaded', run);

/**
* Convert the input data to tensors that we can use for machine 
* learning. We will also do the important best practices of _shuffling_
* the data and _normalizing_ the data
*/
function convertToTensor(data, min, max) {
  // Wrapping these calculations in a tidy will dispose any 
  // intermediate tensors.
  // Step 1. Shuffle the data    
  tf.util.shuffle(data);
  // Step 2. Convert data to Tensor
  const inputs = data.map(d => [d.featureA, d.featureB])
  const labels = data.map(d => d.label);
  const inputTensor = tf.tensor2d(inputs, [inputs.length, 2]);
  const normalizedInputs = inputTensor.sub(min).div(max.sub(min));
  return {
    inputs: normalizedInputs,
    labels: labels
  }
}

function getMinMax(healthData, testData) {
  const inputs1 = healthData.map(d => [d.featureA, d.featureB])
  const inputs2 = testData.map(d => [d.featureA, d.featureB])
  const all = inputs1.concat(inputs2);
  const inputTensor = tf.tensor2d(all, [all.length, 2]);
  const inputMax = inputTensor.max();
  const inputMin = inputTensor.min();
  return { min: inputMin, max: inputMax }
}

对于模型设置,我使用了 https://stackabuse.com/tensorflow-2-0-solving-classification-and-regression-problems/

中的提示

你把它们转成一个hot的时候标签有很大错误

const oneHot = tf.oneHot(labels, 3);

因为您的 class 标签 https://i.stack.imgur.com/XYKAn.png 值为 [-1, 0 , 1]。我做一个快速测试。如果标签是 [ 0, 1, 2],tf.one_hot 给出您所期望的:

lables = [0, 1, 2]
depth = 3

tf.one_hot(lables , depth) 
<tf.Tensor: shape=(3, 3), dtype=float32, numpy=
array([[1., 0., 0.],
       [0., 1., 0.],
       [0., 0., 1.]], dtype=float32)>

但是,如果您的 class 标签是 [-1,0 , 1]。你的一热转换会出错

labels = [-1, 0, 1]
depth = 3

tf.one_hot(labels, depth) 
<tf.Tensor: shape=(3, 3), dtype=float32, numpy=
array([[0., 0., 0.],
       [1., 0., 0.],
       [0., 1., 0.]], dtype=float32)>

你的损失函数有误

你class一个热标签的化,你需要使用categorical_crossentropy

解决这 2 个问题应该会开始给你合理的结果