使用MLP神经网络模型预测质数

1、简介

本文仅是用于学习神经网络,用神经网络去判断一个数是否是质数没有意义。

2、模型概述

整个训练模型预测的过程分为四步:(1)获取数据。(2)定义模型。(3)使用数据训练模型。(4)使用模型预测。

3、代码实现

代码:【免费】使用MLP神经网络模型预测质数资源-CSDN下载

3.1、数据集

我这里是自己定义的数据,数据集就是"特征的矩阵"。

GetData.py

python 复制代码
import torch

# 数据
# (1)数值
# (2)是否是整数
# (3)是否大于0
# (4)是否等于1
# (5)是否大于1
# (6)是否能被1整除
# (7)是否能被自己整除
# (8)是否能被1和自己以外的数字整除

# [数值,是否是整数,是否大于0,是否等于1,是否大于1,是否能被1整除,是否能被自己整除,是否能被1和自己以外的数字整除]
x = torch.tensor([
    # 训练集
    [-52.9, 0, 0, 0, 0, 0, 1, 0],
    [-8.2, 0, 0, 0, 0, 0, 1, 0],
    ......
    [-60, 1, 0, 0, 0, 1, 1, 1],
    [-8, 1, 0, 0, 0, 1, 1, 1],
    [-76, 1, 0, 0, 0, 1, 1, 1],
    [-137, 1, 0, 0, 0, 1, 1, 0],
    ......
    [127, 1, 1, 0, 1, 1, 1, 0],
    [157, 1, 1, 0, 1, 1, 1, 0],
    [89, 1, 1, 0, 1, 1, 1, 0],
    ......
    [170, 1, 1, 0, 1, 1, 1, 1],

    [-0.7, 0, 0, 0, 0, 0, 1, 0],
    [-138.8, 0, 0, 0, 0, 0, 1, 0],
    ......
    [88, 1, 1, 0, 1, 1, 1, 1],
    [104, 1, 1, 0, 1, 1, 1, 1],
    ......
    [30, 1, 1, 0, 1, 1, 1, 1],
    [164, 1, 1, 0, 1, 1, 1, 1],

    [-177.2, 0, 0, 0, 0, 0, 1, 0],
    ......
    [138, 1, 1, 0, 1, 1, 1, 1]
], dtype=torch.float)

# 标签    0 表示非质数,1表示是质数
y = torch.tensor([
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1,
    1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1,
    1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,

    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
    1,
    1, 1, 1, 1, 1,
    1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0,
    0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
])


num_nodes = len(y)
split_size = num_nodes // 3
# 全部初始化为 False  True表示这一条数据属于这个集合    False表示不属于
train_mask = torch.zeros(num_nodes, dtype=torch.bool)
val_mask = torch.zeros(num_nodes, dtype=torch.bool)
test_mask = torch.zeros(num_nodes, dtype=torch.bool)

# 前1/3     前三分之一数据属于训练集
train_mask[:split_size] = True
# 中间1/3   中间三分之一数据属于验证集
val_mask[split_size:2 * split_size] = True
# 后1/3     后三分之二数据属于测试集
test_mask[2 * split_size:] = True

# 输入维度
num_features = x.shape[1]
num_classes = len(torch.unique(y))  # num_classes = 2 因为只有true false两种

print(num_features)
print(num_classes)

3.2、模型

随机数种子的作用是让程序在每次运行时的初始参数都保持一致,从而确保每次训练结果的可复现性。比如每次运行程序时,都会去初始化每层全连接层(线性层)的初始参数,比如self.lin1和self.lin2的weight(权重)和bias(偏置)。

weight:每个输入特征的重要程度。告诉神经元哪个特征更重要?哪个特征应该放大?哪个特征应该减弱,用于描述输入特征对当前神经元输出的影响方向和影响程度。

bias:y=Wx+b,bias就是b,是一个常数。表示神经元自身的偏移量,它使模型即使输入全为0,也可以输出非0,从而提高模型表达能力。

weight和bias在训练的时候会不断更新调整,从而使模型达到最优的学习效果。

线性层公式:y=Wx+b

x:输入,比如用于训练的数据。数据里面会包含各个特征。结合3.1的GetData里的定义的数据,输入就是,x = 数值,是否整数,是否\>0,是否==1,是否\>1,能否被1整除,能否被自己整除,是否能被其它数字整除

W:权重,告诉神经元每个特征的重要程度,用于描述输入特征对当前神经元输出的影响方向和影响程度。结合3.1的GetData里的定义的数据,权重可能是,0.2,1.3,-0.5,0.7,0.9,0.4,0.8,-3.1

b:偏置,整体把结果往上移一点或者往下移一点。让模型具有更大的表达能力。

y:输出,线性层计算后的输出,作为下一层的输入或最终分类依据。

ReLU:激活函数,神经元可能输出负数即y=xW+b为负数,ReLU会把所有的负数都变成0。即ReLU(y)=0。

神经元的输出可以理解为:

**正数:**我认为我负责识别的这个特征存在,而且越大说明越明显。

**负数:**我认为这个特征不存在,或者与我要识别的模式相反。当前神经元认为该模式没有足够证据激活。

**ReLU:**把所有负响应都归零,只让"激活"的神经元继续向后传递信息。y>0是激活的神经元,y<=0是未激活的神经元。让没激活的神经元统一输出0方便告诉下一层我这里没有有价值的信息。

激活函数:赋予神经网络学习复杂非线性规律的能力。

常见激活函数:

Sigmoid:适用场景为二分类的最后一层。

Tanh:适用于RNN、LSTM、GRU。

ReLU:适用于MLP、CNN、GCN、GAT。

常见的神经网络

  • MLP:全连接网络(基础,处理一维向量)
  • CNN:卷积神经网络(网格结构数据:图像、时序)
  • GCN:图卷积网络(图数据,基于邻接矩阵做空域平滑)
  • GAT:图注意力网络(图数据,用注意力替代固定卷积权重)
python 复制代码
import torch
from torch.nn import Linear
import torch.nn.functional as F

from demo import GetData


class MLP(torch.nn.Module): # MLP:多层感知机   定义一个神经网络
    def __init__(self, hidden_channels):    # 初始化
        super().__init__()
        torch.manual_seed(12345)    # 固定随机数种子
        # 输入8维
        self.lin1 = Linear(GetData.num_features, hidden_channels)
        # 输出2个类别
        self.lin2 = Linear(hidden_channels, GetData.num_classes)

    # 定义神经网络前向传播(Forward Propagation)的过程
    # 定义数据如何流过这个模型(告诉pytorch:以后有人把数据送进来,请按照这个流程计算) 计算流程/数据流程
    # forward并不会更新参数,只是完成一次预测。
    def forward(self, x):
        x = self.lin1(x)    # 第一层全连接
        x = x.relu()        # relu激活函数
        x = F.dropout(x, p=0.5, training=self.training) # 随机失活  训练时会故意删掉一些神经元,防止模型过拟合
        x = self.lin2(x)    # 第二层全连接
        return x

# model = MLP(hidden_channels=16)
# print(model)

3.3、训练

Train.py

python 复制代码
import torch

from demo import GetData
from demo.MLP import MLP

model = MLP(hidden_channels=8)
criterion = torch.nn.CrossEntropyLoss()  # 损失函数,CrossEntropyLoss内部已经包含Softmax。
optimizer = torch.optim.Adam(model.parameters(), lr=0.01, weight_decay=5e-4)  # 优化器


# 训练模型
def train():
    model.train()  # 训练模式
    optimizer.zero_grad()  # 清空梯度
    out = model(GetData.x)  #
    loss = criterion(out[GetData.train_mask], GetData.y[GetData.train_mask])  # 计算损失函数
    loss.backward()  # Derive gradients. 反向传播 会影响更新参数
    optimizer.step()  # 基于梯度更新参数
    return loss.item()  # 本轮训练的损失值


# 验证模型:验证模型不能更新参数,因此不用loss.backward()和optimizer.step()
@torch.no_grad()  # 让pytorch不计算梯度,速度会更快
def validate():
    model.eval()  # 切换到评估模式
    out = model(GetData.x)
    pred = out.argmax(dim=1)
    correct = (
            pred[GetData.val_mask]  # 预测的类别是否等于实际的类别
            ==
            GetData.y[GetData.val_mask]
    )
    acc = int(correct.sum()) / int(GetData.val_mask.sum())
    return acc


# 测试模型:和validate几乎一样,只是用的数据集不一样。
@torch.no_grad()
def test():
    model.eval()
    out = model(GetData.x)
    pred = out.argmax(dim=1)
    correct = (
            pred[GetData.test_mask]
            ==
            GetData.y[GetData.test_mask]
    )
    acc = int(correct.sum()) / int(GetData.test_mask.sum())
    return acc


# 训练代码,训练500次
for epoch in range(500):
    loss = train()
    val_acc = validate()
    print(
        epoch,
        loss,
        val_acc
    )

# 最后,只测试一次
test_acc = test()
print(
    "Test Accuracy:",
    test_acc
)

# 保存模型参数
torch.save(
    model.state_dict(),
    "model/prime_number.pth"
)

损失函数:计算预测结果和真实答案差多少。

梯度:告诉神经网络,每一个参数应该朝哪个方向修改。梯度是损失函数对权重的导数。每一次训练,都要清空一下梯度optimizer.zero_grad()。

反向传播:预测的时候是"输入------>第一层------>第二层------>loss"是"正向传播"。而计算梯度的时候,要反着来,"loss------>第二层------>第一层"。因为是反着来就叫"反向传播"。

损失函数负责评价,梯度负责指出哪里错,优化器负责修改参数。

优化器:优化器会根据梯度修改参数,不断的让loss越来越小。修改每个神经元y=Wx+b中的权重W和偏置b。

3.4、推理

复制代码
Predict.py
python 复制代码
from sympy.printing.pytorch import torch

from demo.MLP import MLP

# 创建模型(结构必须一致)
model = MLP(hidden_channels=8)

# 加载参数
model.load_state_dict(torch.load("model/prime_number.pth"))

# 进入预测模式
# eval把模型切换到评估模式,会自动关闭原来模型中的dropout
# dropout只是在训练的时候让神经元随机失活防止过拟合。但预测的时候需要全部神经元参与发挥模型的全部能力。
model.eval()    


new_method = torch.tensor([
    #[72, 1, 1, 0, 1, 1, 1, 1]
    # [47, 1, 1, 0, 1, 1, 1, 0]
    #[7, 1, 1, 0, 1, 1, 1, 0]
#[-52.9, 0, 0, 0, 0, 0, 1, 0]
    # [1009,1,1,0,1,1,1,0]
    [1003,1,1,0,1,1,1,1]
], dtype=torch.float)

with torch.no_grad():
    out = model(new_method)
    pred = out.argmax(dim=1)
print(pred)

输出0,表示1003不是质数。

4、总结

我的数据集设计其实存在一个作弊特征:(8)是否能被1和自己以外的数字整除。这个其实就是质数的定义。相当于在数据集的第8维就已经告诉了模型答案了。这个就是机器学习里面的"Data Leakage(数据泄露)"。模型看到了答案。因此设计的数据集中就不能包含这种数据。

我的训练集划分也不合理。应该是随机划分的,确保训练集、测试集、验证集数据随机均匀分布。

当然本次只是为了学习MLP,只是作为demo而已。

5、参考资料

https://chatgpt.com/

相关推荐
吴佳浩1 小时前
一文讲透AI算力单位:TFLOPS、PFLOPS、TOPS、稀疏算力,到底怎么算、怎么比?
人工智能·ai编程·gpu
guoyuhan1 小时前
用 OpenAI SDK 一行代码接入国产大模型:DeepSeek/Qwen/GLM 实战指南
人工智能
维基框架1 小时前
GitHub重构漏洞赏金计划 向AI批量报告说不
人工智能·重构·github
不加辣椒2 小时前
第5章:智能检索系统——从 Naive RAG 到 Agentic RAG
人工智能
程序员cxuan2 小时前
白嫖 Claude Max 20x 漏洞完整事件始末
人工智能·后端·程序员
猫头虎2 小时前
什么是ZCode for GLM-5.2?
开发语言·人工智能·python·科技·算法·ai编程·ai写作
用户874033739142 小时前
在 Ubuntu 22.04 最小化安装上部署与调优 Ollama 集群
人工智能
山林竹笋2 小时前
人工智能领域开源TOP20(2026.06.22-2026.06.28)
人工智能·开源·大模型·智能体·技术趋势
深圳佛手2 小时前
梁文锋说,AGI是首要目标。AGI和AI的区别是什么?
人工智能·机器学习