深度学习之鸡兔同笼问题

前言

作为小学九大毒瘤之一的鸡兔同笼问题,在没学过二元一次方程之前估计是难倒了不少人,站在程序员的角度,鸡兔同笼问题其实有很多解法,最常见的有穷举法,公式法等,而今天我所要探索的方法是深度学习,估计我是第一个这么干的。

问题引入

我们总是会忽略某些习以为常的规则。想象一下,假设我们不知道鸡有两只脚,兔有四只脚,那么鸡兔同笼问题还会是那么容易吗?

深度学习的本质就是在有限的数据和结果之间寻找一条通用的逻辑算法,这个算法在生活中我们一般叫经验。因此本文以鸡兔同笼问题为例,给定数据和结果,让计算机在其中寻找通用的规则(鸡有两只脚,兔有四只脚)。

值得注意的是,不要妄想使用已有的规则来对神经元连接权重和阈值进行解释或修缮,否则只会适得其反。

第一次尝试(失败经验)

鉴于这种规则过于简单,神经网络我打算采用最简单的,一个输入层、一个隐藏层、一个输出层的简单结构,物理结构如下:
深度学习之鸡兔同笼问题
这么简单的神经网络结构用tensorflow做好像有点浪费,因此我最开始采用Excel表进行模拟,如下图:
深度学习之鸡兔同笼问题
通过不断替换权重数值和阈值(用宏做的),迭代301次之后,损失函数(误差平方)曲线如下:

深度学习之鸡兔同笼问题
因为逆向传播只使用了一组数据,所以损失函数曲线抖动是可以理解的。虽然图像直观地展示了学习的过程,但遗憾的是最终网络也未能收敛,且实际预测也具有较大误差,原因可能是数据没给够(只给了三组数据),或者是隐藏层神经元数量较少。

第二次实验

吸取了第一次的教训,这次我打算用python+tensorflow完成实验。

实验环境

  • python3.6
  • tensorflow2.5

神经网络物理结构

同样是三层结构,只是隐藏层的神经元数量变为了10个。
深度学习之鸡兔同笼问题

准备数据

深度学习最为依赖数据,因此数据的准备是开始深度学习的第一步,本文对此准备了10组数据,可根据需要自行添加更多数据。

def train_data():
    x = [[2, 6], [3, 8], [3, 10], [4, 10], [4, 12], [4, 14], [5, 12], [5, 14], [5, 16], [5, 18]]
    x = np.array(x)

    y = [[1, 1], [2, 1], [1, 2], [3, 1], [2, 2], [1, 3], [4, 1], [3, 2], [2, 3], [1, 4]]
    y = np.array(y)
    return (x, y)
x_train = train_data()[0]
y_train = train_data()[1]

搭建神经网络

# 神经网络结构,输入层2个神经元,隐藏层10个神经元,输出层2个神经元
model = keras.Sequential(
    [keras.layers.Flatten(input_shape=(2,)),
     keras.layers.Dense(10, activation='relu'),
     keras.layers.Dense(2)])

定义优化方法

# 定义损失函数和优化算法
model.compile(optimizer=keras.optimizers.Adam(0.001), loss='mse', metrics=['accuracy'])

模型训练与保存

# 模型训练,每训练20次进行一次验证,总共训练5000次
model.fit(x_train, y_train, epochs=5000, validation_data=val_data(), validation_freq=100)

# 保存模型
model.save("model.h5")

其中验证集来源于训练集,数量上有所减少而已。

def val_data():
    x = [[2, 6], [3, 8], [4, 10], [4, 12], [4, 14], [5, 14], [5, 18]]
    y = [[1, 1], [2, 1], [3, 1], [2, 2], [1, 3], [3, 2], [1, 4]]
    x = np.array(x)
    y = np.array(y)
    return (x, y)

训练效果如下图:
深度学习之鸡兔同笼问题

模型测试

训练好了模型,自然需要经过测试,而测试所用的数据是未在训练集中出现的。测试相关代码如下。

from tensorflow import keras
import numpy as np


# 测试所用数据
def test_data():
    x = [[6, 14], [6, 16], [6, 18], [6, 20], [6, 22]]
    y = [[5, 1], [4, 2], [3, 3], [2, 4], [1, 5]]
    return (np.array(x), np.array(y))


# 准备数据
x_test = test_data()[0]
y_test = test_data()[1]

# 加载模型
network = keras.models.load_model("model.h5")

# 获取结果
y_predict = network.predict(x_test)
print(y_predict)

输出结果如图:
深度学习之鸡兔同笼问题
可以看到,最终的结果与标签y的数据十分接近。

应用结果

模型测试通过,现在将模型应用于实际使用。

from tensorflow import keras
import numpy as np
import ast

# 加载模型
network = keras.models.load_model("model.h5")

# 封装数据
data = ast.literal_eval(input("请输入头和脚的数量:"))
data = [data]
data = np.array(data)

# 获取结果
y_predict = network.predict(data)
print("鸡有" + str(round(y_predict[0][0])) + "只,兔有" + str(round(y_predict[0][1])) + "只。")

最终实测效果如下图:
深度学习之鸡兔同笼问题
到此,采用深度学习解决鸡兔同笼问题完成。

附录

ANN.py

from tensorflow import keras
import numpy as np
import tensorflow as tf


def train_data():
    x = [[2, 6], [3, 8], [3, 10], [4, 10], [4, 12], [4, 14], [5, 12], [5, 14], [5, 16], [5, 18]]
    x = np.array(x)

    y = [[1, 1], [2, 1], [1, 2], [3, 1], [2, 2], [1, 3], [4, 1], [3, 2], [2, 3], [1, 4]]
    y = np.array(y)
    return (x, y)


def val_data():
    x = [[2, 6], [3, 8], [4, 10], [4, 12], [4, 14], [5, 14], [5, 18]]
    y = [[1, 1], [2, 1], [3, 1], [2, 2], [1, 3], [3, 2], [1, 4]]
    x = np.array(x)
    y = np.array(y)
    return (x, y)


# def test_data():
#     x = [[6, 14], [6, 16], [6, 18], [6, 20], [6, 22]]
#     y = [[5, 1], [4, 2], [3, 3], [2, 4], [1, 5]]
#     return (x, y)


# 第一步导入api
# 1.import tensorflow as tf
# #第二步分割数据集为训练集和测试集
# 2.train,test
# #第三步是搭建合适的网络模型(前向传播过程)
# 3.model=tf.keras.models.Sequential
# #第四步是配置训练方法,选择训练优化器,选择损失函数
# 4.model.compile
# #第五步在fit中执行训练过程
# 5.model.fit()
# #第六步输出训练结果的参数
# 6.model.summary

x_train = train_data()[0]
y_train = train_data()[1]

# 神经网络结构,输入层2个神经元,隐藏层10个神经元,输出层2个神经元
model = keras.Sequential(
    [keras.layers.Flatten(input_shape=(2,)),
     keras.layers.Dense(10, activation='relu'),
     keras.layers.Dense(2)])

# 定义损失函数和优化算法
model.compile(optimizer=keras.optimizers.Adam(0.001), loss='mse', metrics=['accuracy'])

# 模型训练,每训练20次进行一次验证,总共训练1000次
model.fit(x_train, y_train, epochs=5000, validation_data=val_data(), validation_freq=100)

# 保存模型
model.save("model.h5")

test.py

from tensorflow import keras
import numpy as np


# 测试所用数据
def test_data():
    x = [[6, 14], [6, 16], [6, 18], [6, 20], [6, 22]]
    y = [[5, 1], [4, 2], [3, 3], [2, 4], [1, 5]]
    return (np.array(x), np.array(y))


# 准备数据
x_test = test_data()[0]
y_test = test_data()[1]

# 加载模型
network = keras.models.load_model("model.h5")

# 获取结果
y_predict = network.predict(x_test)
print(y_predict)

prediction.py

from tensorflow import keras
import numpy as np
import ast

# 加载模型
network = keras.models.load_model("model.h5")

# 封装数据
data = ast.literal_eval(input("请输入头和脚的数量:"))
data = [data]
data = np.array(data)

# 获取结果
y_predict = network.predict(data)
print("鸡有" + str(round(y_predict[0][0])) + "只,兔有" + str(round(y_predict[0][1])) + "只。")

上一篇:Keras之ML~P:基于Keras中建立的简单的二分类问题的神经网络模型(根据200个数据样本预测新的5个样本)——概率预测


下一篇:Keras之DNN:基于Keras(sigmoid+binary_crossentropy+predict_proba)利用DNN实现分类预测概率——DIY二分类数据集&预测新数据点