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

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, 0
    for X, y in data_iter:          # X是特征,y是标签
        y = y.astype("float32")
        # 取出每一行中数字最大的那一项的索引号,并和标签y比较,再对该次小批量求和,累加
        acc_sum += (net(X).argmax(axis=1) == y).sum().asscalar()
        n += y.size
    return 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, 0
        for 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.size
        test_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.1
	train_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)
相关推荐
道友老李2 分钟前
【自然语言处理(NLP)】生成词向量:ELMo(Embedded from Language Models)原理及应用
人工智能·自然语言处理
江瀚视野5 分钟前
苹果再度砍掉AR眼镜项目?AR真的是伪风口吗?
人工智能
我的青春不太冷8 分钟前
【OpenCV实战】基于 OpenCV 的多尺度与模板匹配目标跟踪设计与实现
人工智能·opencv·目标跟踪·模板匹配
shanks6611 分钟前
【PyQt】lambda函数,实现动态传递参数
开发语言·python·pyqt
有续技术22 分钟前
DEEPSEKK GPT等AI体的出现如何重构工厂数字化架构:从设备控制到ERP MES系统的全面优化
人工智能·gpt·重构
番茄灭世神26 分钟前
使用DeepSeek的技巧笔记
人工智能·ai·deepseek
relis38 分钟前
编程AI深度实战:大模型哪个好? Mistral vs Qwen vs Deepseek vs Llama
人工智能·llama·qwen·ai大模型·开源模型·deepseek·codestral
bug404_1 小时前
AI-ISP论文Learning to See in the Dark解读
人工智能·计算机视觉·接口隔离原则
火云牌神1 小时前
本地大模型编程实战(08)自制聊天机器人(2)
python·llama·chatbot·deepseek
North_D1 小时前
【DeepSeek-01】无需 Docker,千元级电脑本地部署 Ollama + DeepSeek-R1:14b + Chatbox
人工智能·深度学习·神经网络·机器学习·chatgpt·deepseek·deepseek r1