# 过拟合和欠拟合

# 代码展示

import * as tf from '@tensorflow/tfjs';
import * as tfvis from '@tensorflow/tfjs-vis';
// import { getData } from '../xor/data';
import { getData } from './data';

window.onload = async () => {
  const data = getData(200, 5);
  console.log('data', data);

  tfvis.render.scatterplot(
    { name: '训练数据' },
    {
      values: [
        data.filter((p) => p.label === 1),
        data.filter((p) => p.label === 0),
      ],
    }
  );

  const model = tf.sequential();

  model.add(
    tf.layers.dense({
      units: 10, // 神经元个数
      inputShape: [2], //  神经元(输入)接受的形状
      activation: 'tanh', // 这个激活函数可以带来一些非线性的变化,如果这里不要激活函数相当于这个隐藏层没有用,因为无论设置多少个层没有激活函数输出都是线性的
      // kernelRegularizer: tf.regularizers.l2({ l2: 1 }), // l2正则化相当于设置权重衰减 l2为超参数
    })
  );

  /*使用丢弃法解决过拟合-添加一个dropout层*/
  model.add(tf.layers.dropout({ rate: 0.3 })); // 相当于10个神经元随机丢弃了N个

  model.add(
    tf.layers.dense({
      units: 1,
      inputShape: [2],
      activation: 'sigmoid',
    })
  );

  model.compile({
    loss: tf.losses.logLoss,
    optimizer: tf.train.adam(0.1),
  });

  const inputs = tf.tensor(data.map((p) => [p.x, p.y]));
  const labels = tf.tensor(data.map((p) => p.label));

  await model.fit(inputs, labels, {
    epochs: 200,
    validationSplit: 0.2, // 从训练集分出20%数据作为验证集
    callbacks: tfvis.show.fitCallbacks(
      { name: '训练效果' },
      ['loss', 'val_loss'],
      { callbacks: ['onEpochEnd'] }
    ),
  });

  window.predict = (form) => {
    const pred = model.predict(
      tf.tensor([[form.x.value * 1, form.y.value * 1]])
    );
    alert(`预测结果:${pred.dataSync()[0]}`);
  };
};

# 重点笔记

  • validationSplit: 0.2, 表示从训练集分出 20%数据作为验证集
await model.fit(inputs, labels, {
  epochs: 200,
  validationSplit: 0.2, // 从训练集分出20%数据作为验证集
  callbacks: tfvis.show.fitCallbacks(
    { name: '训练效果' },
    ['loss', 'val_loss'],
    { callbacks: ['onEpochEnd'] }
  ),
});
  • 欠拟合的解决方案是增加模型的复杂程度(层和神经元),也可能需要增加训练时间

  • 过拟合的应对法:早停法、权重衰减(l2 正则化)、丢弃法

早停法就是观察训练集的损失从哪里增长然后将训练停掉即可(但是如果刚开始训练集损失就猛增那么就相当于失败了,所以这种方式不推荐)

权重衰减简单来说就是把权重的复杂度作为损失的一部分,那么过于复杂的权重也在训练的过程中被衰减掉了

model.add(
  tf.layers.dense({
    units: 10,
    inputShape: [2],
    activation: 'tanh',
    kernelRegularizer: tf.regularizers.l2({ l2: 1 }), // l2正则化相当于设置权重衰减 l2为超参数
  })
);

丢弃法是在神经网络的隐藏层设置丢弃率,丢弃某几个神经元的权重,相当于把隐藏层的神经元个数减少了

model.add(
  tf.layers.dense({
    units: 10,
    inputShape: [2],
    activation: 'tanh',
  })
);

/*使用丢弃法解决过拟合(添加一个dropout层)*/
model.add(tf.layers.dropout({ rate: 0.3 })); // rate是丢弃率,相当于10个神经元随机丢弃了3个权重