【单层神经网络】softmax回归的从零开始实现(图像分类)

embedded/2025/2/6 6:49:46/

softmax回归

该回归分析为后续的多层感知机做铺垫

基本概念

softmax回归用于离散模型预测(分类问题,含标签)

softmax运算本质上是对网络的多个输出进行了归一化,使结果有一个统一的判断标准,不必纠结为什么要这么算

网络结构:

  1. 多输入多输出,单层神经网络,输出神经元与输入全连接
  2. 一组输入x为n维行向量,输入矩阵为a*n,a为样本个数
  3. 一组输出o为m维行向量,输出矩阵为a*m
  4. 偏置是m维行向量
  5. 权重矩阵W为n*m的矩阵(n行=输入个数,m列=输出个数)
  6. 网络输出:
    Y = softmax(O)
    O = X*W + b

交叉熵损失函数

核心:

  1. 交叉熵只关心对正确类别的预测概率
  2. 最小化交叉熵损失函数 等价于 最大化似然函数,二者出自不同学科,但在数学上描述了同一个对象
  3. 可以使用准确率评价模型表现

完整程序及注释

完整softmax图像分类实现

python">import d2lzh as d2l
from mxnet import autograd, nd
'''
基础参数声明
'''
batch_size = 256
# 创建两个迭代器,用于对mnist数据集进行小批量随机采样
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)num_inputs = 28*28  # 将二维图片变成一维向量后的长度
num_outputs = 10    # 输出数量
W = nd.random.normal(loc=0, scale=0.01, shape=(num_inputs, num_outputs))
b = nd.zeros(num_outputs)
W.attach_grad()
b.attach_grad()'''
训练必要方法声明
'''
# 指数化+归一化 = softmax运算
def softmax(X):X_exp = X.exp()partition = X_exp.sum(axis=1, keepdims=True)return X_exp / partition# 将二维图片变成一维向量(改变模型形式),并为每个特征分配权重和偏置
# nd.dot(X.reshape((-1, num_inputs)), W) + b 的输出是一个矩阵,行数等于小批量中的样本数,列数等于输出的类别数量
# 每一行中数值最大的那一项,代表模型认为该样本属于这一类的概率最大
def net(X):return softmax(nd.dot(X.reshape((-1, num_inputs)), W) + b)# y是正确的标签号,y_hat是样本对各个类型的预测概率
# 该函数返回各个概率中正确标签的概率值,并进行exp()运算
def cross_entropy(y_hat, y):return -nd.pick(y_hat, y).log() # .log()方法将pick()的返回的每个元素进行exp运算# 计算样本集的准确率
def accuracy(y_hat, y):return (y_hat.argmax(axis=1) == y.astype('float32')).mean().asscalar()# 小批量对data_iter进行遍历,最后统计准确率
def evaluate_accracy(data_iter, net):acc_sum, n = 0.0, 0for X, y in data_iter:          # X是特征,y是标签y = y.astype("float32")# 取出每一行中数字最大的那一项的索引号,并和标签y比较,再对该次小批量求和,累加acc_sum += (net(X).argmax(axis=1) == y).sum().asscalar()n += y.sizereturn acc_sum / n'''
训练函数
'''
def train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, params=None, lr=None, trainer=None):for epoch in range(num_epochs):train_l_sum, train_acc_sum, n = 0.0, 0.0, 0for X, y in train_iter:with autograd.record():y_hat = net(X)l = loss(y_hat, y).sum()        # 调用时, 传入交叉熵损失函数l.backward()if trainer == None:d2l.sgd(params, lr, batch_size)else:trainer.step(batch_size)y = y.astype("float32")train_l_sum += l.asscalar()         # 总损失train_acc_sum += (y_hat.argmax(axis=1) == y).sum().asscalar()   # 总准确率n += y.sizetest_acc = evaluate_accracy(test_iter, net)# 打印平均损失和平均准确率(训练集和测试集)print('epoch %d, loss %.4f, train_acc %.4f, test_acc %.4f'% (epoch+1, train_l_sum/n, train_acc_sum/n, test_acc))   '''
开始训练,一般在第9轮左右准确率逼近峰值,85%左右
想要更高的准确率,需要更好的模型和寻优算法
'''
if __name__ == "__main__":num_epochs, lr = 20, 0.1train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, batch_size, [W, b], lr)

查看数据集

python">from matplotlib import pyplot as plt
from mxnet.gluon import data as gdata# 展示FashionMNIST数据集
mnist_train = gdata.vision.FashionMNIST(train=True)
mnist_test = gdata.vision.FashionMNIST(train=False)
print(len(mnist_train))
print(len(mnist_test))feature, label = mnist_train[19]
print(label)
img = feature
# 将 MXNet NDArray 转换为 NumPy 数组,并调整维度顺序以匹配图像格式
img_np = img.asnumpy().squeeze() # 移除单维度条目plt.imshow(img_np, cmap='gray')
plt.axis('off')
plt.show()

呈现效果

我训练了8次就手动让它中止了
中断打印结果

具体程序解释

  1. net(X)

    X.reshape((-1, num_inputs))

    这是将X重新塑形的操作。这里的reshape函数用于改变X的形状而不改变其数据。

    参数(-1, num_inputs)告诉函数自动计算该维度的大小以使总元素数量保持不变,而另一维度则指定为num_inputs。

    -1在这里作为一个占位符,指示系统根据原张量中的元素数量和另一个指定的维度大小自动推断出这一维度的具体值

    简单来说,这段代码的作用是将输入数据X拉平成一维向量,以便与权重矩阵W进行点积运算。如果你的输入是一批二维图片,这一步会将每张图片转换为一维向量形式,使得它们能够通过全连接层

  2. nd.pick()

    用于从一个多维数组中根据指定的索引挑选元素
    举例:

    python">import mxnet as mx
    data = mx.nd.array([[1, 2], [3, 4]])
    index = mx.nd.array([1, 0])
    picked_elements = mx.nd.pick(data, index, axis=1)print(picked_elements.asnumpy())
    

    在这个例子中,data 是一个二维 NDArray,而 index 是一个一维数组,指示了在 axis=1(行)上希望选取的元素索引。

    nd.pick 函数会根据给定的索引数组 index,从 data 的指定维度(通过 axis 参数指定)中挑选出相应的元素。

    具体来说:mx.nd.array创建了两个行向量,用[1, 0]和axis=1索引的是 第0行中的第1个 和 第1行中的第0个 元素

  3. accuracy(y_hat, y)

    1. y_hat.argmax(axis=1)
      使用 argmax 函数找到 y_hat 中每一行(即每个样本)的最大值对应的索引。
    2. (y_hat.argmax(axis=1) == y.astype(‘float32’)):
      进行逐元素比较,判断预测的类别是否等于实际类别,返回bool类型
    3. .mean():
      计算上述布尔数组的平均值。由于 True 在数值计算中被视为 1,False 视为 0,因此平均值实际上就是预测正确的样本比例,即准确率。
    4. .asscalar():
      将结果从 NDArray 转换为 Python 标量(如 float)

http://www.ppmy.cn/embedded/159955.html

相关文章

Java NIO详解

一、NIO简介 NIO 中的 N 可以理解为 Non-blocking,不单纯是 New,是解决高并发、I/O高性能的有效方式。 Java NIO 是Java1.4之后推出来的一套IO接口,NIO提供了一种完全不同的操作方式, NIO支持面向缓冲区的、基于通道的IO操作。 …

unity学习26:用Input接口去监测: 鼠标,键盘,虚拟轴,虚拟按键

目录 1 用Input接口去监测:鼠标,键盘,虚拟轴,虚拟按键 2 鼠标 MouseButton 事件 2.1 鼠标的基本操作 2.2 测试代码 2.3 测试情况 3 键盘Key事件 3.1 键盘的枚举方式 3.2 测试代码同上 3.3 测试代码同上 3.4 测试结果 4…

第 1 天:UE5 C++ 开发环境搭建,全流程指南

🎯 目标:搭建 Unreal Engine 5(UE5)C 开发环境,配置 Visual Studio 并成功运行 C 代码! 1️⃣ Unreal Engine 5 安装 🔹 下载与安装 Unreal Engine 5 步骤: 注册并安装 Epic Game…

修复使用unplugin-auto-import和unplugin-vue-components后tsc-vue报错的问题

在使用NaiveUI的过程中,引入了unplugin-auto-import和unplugin-vue-components。 这两个组件能自动引入vue方法和vue组件,提升了开发者体验。 但是在vscode中,源码里未手动引用而直接用的方法和组件还是被标红,提示找不到&#x…

【llm对话系统】大模型 Llama 如何进行量化和推理

1. 写在前面 Llama 是 Meta AI 开源的一系列大型语言模型 (LLM),在各种 NLP 任务上表现出色。然而,这些模型通常具有庞大的参数量,需要大量的计算资源和内存才能进行推理。为了降低 Llama 模型的部署成本,并提高其推理速度,我们可以采用模型量化 (Quantization) 技术。 …

Mac M1 ComfyUI 中 AnyText插件安装问题汇总?

Q1:NameError: name ‘PreTrainedTokenizer’ is not defined ? 该项目最近更新日期为2024年12月,该时间段的transformers 版本由PyPI 上的 transformers 页面 可知为4.47.1. A1: transformers 版本不满足要求,必须降级transformors &#…

Linux系统 环境变量

环境变量 写在前面概念查看环境变量main函数的参数argc & argvenv bash环境变量 写在前面 对于环境变量,本篇主要介绍基本概念及三四个环境变量 —— PATH、HOME、PWD。其中 PATH 作为 “ 敲门砖 ”,我们会更详细讲解;理解环境变量的全局…

【PyQt】lambda函数,实现动态传递参数

为什么需要 lambda? 在 PyQt5 中,clicked 信号默认会传递一个布尔值(表示按钮是否被选中)。如果我们希望将按钮的文本内容传递给槽函数,需要通过 lambda 函数显式传递参数。 这样可以实现将按钮内容传递给槽函数&…