深度学习算法学习(四):深度学习-最简单实现一个自行构造的找规律(机器学习)任务

Demo

基于pytorch框架编写模型训练

实现一个自行构造的找规律(机器学习)任务

规律:x是一个5维向量,如果第1个数>第5个数,则为正样本,反之为负样本

代码

python 复制代码
import time

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
from dask.bag.text import delayed

"""

基于pytorch框架编写模型训练
实现一个自行构造的找规律(机器学习)任务
规律:x是一个5维向量,如果第1个数>第5个数,则为正样本,反之为负样本

"""

# 生成一个样本, 样本的生成方法,代表了我们要学习的规律
# 随机生成一个5维向量,如果第一个值大于第五个值,认为是正样本,反之为负样本
def build_sample():
    # np.random.random(5):生成一个长度为5的一维数组,每个元素是[0,1)区间的随机浮点数
    # 例如,[0.3, 0.6, 0.1, 0.9, 0.2]
    x = np.random.random(5)
    # 四舍五入-保留小数点后3位
    x = np.around(x, 3)
    # 判断第一个数是否大于第五个数
    # 返回输入特征向量和标签
    # 也就是返回[0.3, 0.6, 0.1, 0.9, 0.2] 和 (1或0)
    if x[0] > x[4]:
        # 正样本
        return x, 1
    else:
        # 负样本
        return x, 0

# 生成total_sample_num个样本
def build_dataset(total_sample_num):
    X = []
    Y = []
    # 循环
    for i in range(total_sample_num):
        # 生成一个样本
        x, y = build_sample()
        X.append(x)
        Y.append([y])
    return torch.FloatTensor(X), torch.FloatTensor(Y)

# 总样本数
total_sample_num = 5000
train_x, train_y = build_dataset(total_sample_num)
print("本次预测集中共有%d个正样本,%d个负样本" % (sum(train_y), total_sample_num - sum(train_y)))


class TorchModel(nn.Module):
    def __init__(self, input_size):
        super(TorchModel, self).__init__()
        self.linear = nn.Linear(input_size, 1)  # 线性层
        self.activation = torch.sigmoid  # sigmoid归一化函数
        self.loss = nn.functional.mse_loss  # loss函数采用均方差损失

    # 当输入真实标签,返回loss值;无真实标签,返回预测值
    def forward(self, x, y=None):
        x = self.linear(x)  # (batch_size, input_size) -> (batch_size, 1)
        y_pred = self.activation(x)  # (batch_size, 1) -> (batch_size, 1)
        if y is not None:
            return self.loss(y_pred, y)  # 预测值和真实值计算损失
        else:
            return y_pred  # 输出预测结果

# 用来获取每轮模型的准确率
def evaluate(model):
    # 将模型切换到评估模式, 确保评估结果的一致性和可重复性
    model.eval()
    # 重新生成数据可以检验模型的泛化能力(处理新数据的能力), 避免模型只是"记住"了训练数据,而是真正学会了规律
    x, y = build_dataset(total_sample_num)
    print("本次预测集中共有%d个正样本,%d个负样本" % (sum(y), total_sample_num - sum(y)))
    # 初始化正确和错误计数
    correct, wrong = 0, 0
    # 使用torch.no_grad()上下文管理器,在该区块内关闭梯度计算, 这里不需要更新模型参数,因此不需要计算梯度
    with torch.no_grad():
        # 将测试数据 x输入模型,得到模型预测结果 等价于 model(x)
        y_pred = model.forward(x)
        # 与真实标签进行对比
        # 作用:同时遍历预测结果 y_pred和真实标签 y
        # zip()函数:将多个可迭代对象"打包"成元组序列
        # 示例:如果 y_pred = [0.8, 0.3], y = [1, 0],则循环两次:
        # 第一次:y_p = 0.8, y_t = 1
        # 第二次:y_p = 0.3, y_t = 0
        for y_p, y_t in zip(y_pred, y):
            # 负样本正确:预测概率 < 0.5 且真实标签 = 0
            if float(y_p) < 0.5 and int(y_t) == 0:
                correct += 1
            # 正样本正确:预测概率 ≥ 0.5 且真实标签 = 1
            elif float(y_p) >= 0.5 and int(y_t) == 1:
                correct += 1
            # 预测错误
            else:
                wrong += 1
    # 计算准确率:correct / (correct + wrong) 正确预测数占总数的比例
    print("正确预测个数:%d, 正确率:%f" % (correct, correct / (correct + wrong)))
    return correct / (correct + wrong)


def main():
    # 配置参数
    epoch_num = 20  # 训练轮数
    batch_size = 20  # 每次训练样本个数
    input_size = 5  # 输入向量维度
    learning_rate = 0.001  # 学习率
    # 建立模型
    # 实例化TorchModel类,input_size作为参数传入,定义模型结构
    model = TorchModel(input_size)
    # 选择优化器:使用Adam优化算法来更新模型参数,learning_rate为学习率
    optim = torch.optim.Adam(model.parameters(), lr=learning_rate)
    # 初始化一个空列表log,用于记录每一轮训练后的准确率(acc)和平均损失(loss)
    log = []
    # 训练过程
    # 循环epoch_num轮
    for epoch in range(epoch_num):
        # 将模型设置为训练模式。此模式会启用Dropout、BatchNorm等训练特有的层
        model.train()
        # 初始化一个空列表watch_loss,用于记录每个小批次(batch)损失值的一个列表
        watch_loss = []
        # 内层循环:将训练集分成多个batch进行训练,
        # batch的数量 = total_sample_num // batch_size 计算(总样本数量)除以(每次训练样本个数)后商的整数部分,小数会被舍弃计算
        for batch_index in range(total_sample_num // batch_size):
            # 拿到分页的训练集数据
            x = train_x[batch_index * batch_size : (batch_index + 1) * batch_size]
            y = train_y[batch_index * batch_size : (batch_index + 1) * batch_size]
            # 将数据输入模型, 计算损失, loss函数(前向传播),等价于 model(x, y)
            loss = model.forward(x, y)
            # 反向传播:计算损失函数关于模型参数的梯度
            loss.backward()
            # 优化器步进:根据梯度更新模型参数, 更新权重
            optim.step()
            # 清空优化器的梯度,防止梯度累积, 梯度归零
            optim.zero_grad()
            # 将当前batch的损失值添加到watch_loss列表中
            # loss.item()将张量转换为Python数字
            watch_loss.append(loss.item())
        # epoch + 1 当前第几轮训练
        # watch_loss 每个小批次(batch)损失值的一个列表
        # np.mean(watch_loss),np.mean(...)计算 watch_loss列表中所有数值的平均值
        print("=========\n第%d轮 平均loss:%f" % (epoch + 1, float(np.mean(watch_loss))))
        # 本轮模型准确率结果
        acc = evaluate(model)
        # 记录每一轮训练后的准确率(acc)和平均损失(loss)
        log.append([acc, float(np.mean(watch_loss))])
    # 保存模型
    # 将训练好的模型参数保存到文件"model.pt"
    # state_dict()只保存模型参数,不保存整个模型结构
    # 这样下次可以直接加载使用训练好的模型
    torch.save(model.state_dict(), "model.pt",)
    # 画图
    print(log)

    """
    len(log):获取log列表长度
    range():生成整数序列
    range(5)会生成一个类似于 [0, 1, 2, 3, 4]的序列
    
    [l[0] for l in log] 叫做列表推导式,是Python的简洁语法
    等价于:
    acc_list = []
    for l in log:
        acc_list.append(l[0])  # 取出每个子列表的第一个元素(acc)
        acc_list.append(l[1])  # 取出每个子列表的第二个元素(loss)
    """
    # 绘制准确率曲线:横轴是轮次,纵轴是准确率
    plt.plot(range(len(log)), [l[0] for l in log], label="acc")
    # 绘制损失曲线:横轴是轮次,纵轴是损失值
    plt.plot(range(len(log)), [l[1] for l in log], label="loss")
    # 显示图例(区分acc和loss曲线)
    plt.legend()
    # 显示图形窗口
    plt.show()
    return


"""
使用训练好的模型做预测

model_path:训练好的模型文件保存路径(如 "model.pt")
input_vec:要进行预测的输入数据(多个5维向量组成的列表)

"""
def predict(model_path, input_vec):
    # 定义输入向量的维度 因为测试的是5维输入向量,所以这里硬编码为5
    # 在实际项目中,这个值通常从模型配置或数据中动态获取
    input_size = 5
    # 创建一个新的模型实例
    model = TorchModel(input_size)
    # 加载训练好的权重
    model.load_state_dict(torch.load(model_path))
    # 打印模型的所有参数(权重和偏置)
    print("打印训练好的模型所有参数(权重和偏置):", model.state_dict())
    # 将模型切换到评估模式, 确保评估结果的一致性和可重复性
    model.eval()
    # 使用torch.no_grad()上下文管理器,在该区块内关闭梯度计算, 这里不需要更新模型参数,因此不需要计算梯度
    with torch.no_grad():
        # 模型预测
        # torch.FloatTensor(input_vec):将数组转换为PyTorch浮点数张量
        # model.forward(...):调用模型的前向传播方法进行预测
        result = model.forward(torch.FloatTensor(input_vec))
    # 同时遍历输入向量和预测值
    for vec, res in zip(input_vec, result):
        # float(res):将张量转换为Python
        # round(...):四舍五入到最接近的整数(0或1)
        """
            举例
            输入向量: [0.3, 0.9, 0.7, 0.6, 0.6]
            第一个数0.3 < 第五个数0.6(应该是负样本)  
            模型预测: 0.23
            round(0.23) = 0 → 预测类别: 0
        """
        print("输入:%s, 预测类别:%d, 预测值:%f" % (vec, round(float(res)), res))  # 打印结果


if __name__ == "__main__":
    # # 执行主函数训练保存模型
    # main()

    # 模拟一些数据,用上面主函数训练好的模型做预测
    test_vec = [[0.07889086, 0.15229675, 0.31082123, 0.03504317, 0.18920843],
                [0.94963533, 0.5524256, 0.95758807, 0.95520434, 0.84890681],
                [0.78797868, 0.67482528, 0.13625847, 0.34675372, 0.19871392],
                [0.79349776, 0.59416669, 0.92579291, 0.41567412, 0.1358894]]
    predict("model.pt", test_vec)
相关推荐
冰茶_1 天前
WPF路由事件:隧道与冒泡机制解析
学习·c#·.net·wpf·.netcore·mvvm
木头程序员1 天前
AI驱动的时序索引与查询优化:从存储检索到认知检索的跨越
人工智能·深度学习·时序数据库
hz_zhangrl1 天前
CCF-GESP 等级考试 2025年12月认证C++六级真题解析
c++·算法·青少年编程·程序设计·gesp·c++六级·gesp2025年12月
小沈同学呀1 天前
基于时间片划分的提醒算法设计与实现
服务器·数据库·算法
千金裘换酒1 天前
LeetCode 两数之和 Java
java·算法·leetcode
爱吃泡芙的小白白1 天前
Agent学习——并行化模式
学习·langchain·agent·google adk
汽车仪器仪表相关领域1 天前
光轴精准校准,安全检测基石——JZD-1/2前照灯检测仪用校准灯项目实战分享
数据库·算法·安全·汽车·压力测试·可用性测试
Mintopia1 天前
🌍 AI 自主决策:从文字到图像与声音的三元赋能之路
人工智能·算法·aigc
半夏知半秋1 天前
rust学习-探讨为什么需要标注生命周期
开发语言·笔记·学习·算法·rust