# 多层神经网络(xor 问题)

# 代码展示

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

window.onload = async () => {
  /*构造数据*/
  const data = getData(400);
  console.log('data', data);

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

  /*初始化模型*/
  const model = tf.sequential();

  /*添加隐藏层*/
  model.add(
    tf.layers.dense({
      units: 4, // 神经元个数
      inputShape: [2], // // 神经元(输入)接受的形状
      activation: 'relu', // 这个激活函数可以带来一些非线性的变化,如果这里不要激活函数相当于这个隐藏层没有用,因为无论设置多少个层没有激活函数输出都是线性的
    })
  );

  /*添加输出层*/
  model.add(
    tf.layers.dense({
      // sequential为连续输出模型所以这里不需要设置inputshape
      units: 1, // 只需要输出一个概率
      activation: 'sigmoid', // 输出一个0-1的概率只能选sigmoid
    })
  );

  /*新增损失函数和优化器*/
  model.compile({
    loss: tf.losses.logLoss,
    optimizer: tf.train.adam(0.1),
  });

  /*设置tensor数据*/
  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: 10,
    callbacks: tfvis.show.fitCallbacks(
      // 可视化模拟训练过程
      { name: '训练过程' },
      ['loss'] // 制定度量单位(制作损失曲线)
    ),
  });

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

# 重点笔记

  • 神经网络层数并不是越多越好,比如本示例中 2 层就比三层好,层数过少无法解决复杂问题,层数过多可能出现过拟合

  • 神经元个数可调,但并不是越多越好,少了可能不能计算复杂情况,多了会导致计算缓慢

  • 激活函数只需要带来非线性变化的就行,不必过于纠结使用哪个激活函数

  • 输出层的神经元只需要设为 1,因为只需要输出一个概率

  • 对于全连接层只有第一层需要设置 inputShape,因为后面层的输入是上一层的输出

  • 如果不添加激活函数,那么变化则始终为线性,所有的线性加起来还是线性